gdb.rb 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/README +229 -0
- data/bin/gdb.rb +17 -0
- data/ext/Makefile +2 -0
- data/ext/extconf.rb +71 -0
- data/ext/src/gdb-7.0.tar.bz2 +0 -0
- data/gdb.rb.gemspec +32 -0
- data/patches/gdb-breakpoints.patch +744 -0
- data/patches/gdb-eval.patch +57 -0
- data/patches/gdb-leak.patch +183 -0
- data/scripts/ruby-gdb.py +404 -0
- metadata +64 -0
@@ -0,0 +1,57 @@
|
|
1
|
+
diff --git a/python/py-value.c b/python/py-value.c
|
2
|
+
index 0146593..8f8c610 100644
|
3
|
+
--- a/python/py-value.c
|
4
|
+
+++ b/python/py-value.c
|
5
|
+
@@ -972,6 +972,27 @@ gdbpy_history (PyObject *self, PyObject *args)
|
6
|
+
return value_to_value_object (res_val);
|
7
|
+
}
|
8
|
+
|
9
|
+
+/* Returns value object for a given expression. */
|
10
|
+
+PyObject *
|
11
|
+
+gdbpy_eval (PyObject *self, PyObject *args)
|
12
|
+
+{
|
13
|
+
+ char *arg;
|
14
|
+
+ struct value *res_val = NULL; /* Initialize to appease gcc warning. */
|
15
|
+
+ volatile struct gdb_exception except;
|
16
|
+
+
|
17
|
+
+ if (! PyArg_ParseTuple (args, "s", &arg))
|
18
|
+
+ return NULL;
|
19
|
+
+
|
20
|
+
+ TRY_CATCH (except, RETURN_MASK_ALL)
|
21
|
+
+ {
|
22
|
+
+ res_val = parse_and_eval (arg);
|
23
|
+
+ }
|
24
|
+
+ GDB_PY_HANDLE_EXCEPTION (except);
|
25
|
+
+
|
26
|
+
+ return value_to_value_object (res_val);
|
27
|
+
+}
|
28
|
+
+
|
29
|
+
+
|
30
|
+
void
|
31
|
+
gdbpy_initialize_values (void)
|
32
|
+
{
|
33
|
+
diff --git a/python/python-internal.h b/python/python-internal.h
|
34
|
+
index 67a78af..fbf8247 100644
|
35
|
+
--- a/python/python-internal.h
|
36
|
+
+++ b/python/python-internal.h
|
37
|
+
@@ -68,6 +68,7 @@ extern PyObject *gdb_module;
|
38
|
+
extern PyTypeObject value_object_type;
|
39
|
+
|
40
|
+
PyObject *gdbpy_history (PyObject *self, PyObject *args);
|
41
|
+
+PyObject *gdbpy_eval (PyObject *self, PyObject *args);
|
42
|
+
PyObject *gdbpy_frame_stop_reason_string (PyObject *, PyObject *);
|
43
|
+
PyObject *gdbpy_selected_frame (PyObject *self, PyObject *args);
|
44
|
+
PyObject *gdbpy_lookup_type (PyObject *self, PyObject *args, PyObject *kw);
|
45
|
+
diff --git a/python/python.c b/python/python.c
|
46
|
+
index 254bd28..c96fa29 100644
|
47
|
+
--- a/python/python.c
|
48
|
+
+++ b/python/python.c
|
49
|
+
@@ -651,6 +651,8 @@ static PyMethodDef GdbMethods[] =
|
50
|
+
{
|
51
|
+
{ "history", gdbpy_history, METH_VARARGS,
|
52
|
+
"Get a value from history" },
|
53
|
+
+ { "eval", gdbpy_eval, METH_VARARGS,
|
54
|
+
+ "Get a value of an expression" },
|
55
|
+
{ "execute", execute_gdb_command, METH_VARARGS,
|
56
|
+
"Execute a gdb command" },
|
57
|
+
{ "parameter", gdbpy_parameter, METH_VARARGS,
|
@@ -0,0 +1,183 @@
|
|
1
|
+
diff --git a/python/py-value.c b/python/py-value.c
|
2
|
+
index 8f8c610..c3a6cbf 100644
|
3
|
+
--- a/python/py-value.c
|
4
|
+
+++ b/python/py-value.c
|
5
|
+
@@ -139,7 +139,7 @@ valpy_new (PyTypeObject *subtype, PyObject *args, PyObject *keywords)
|
6
|
+
}
|
7
|
+
|
8
|
+
value_obj->value = value;
|
9
|
+
- value_incref (value);
|
10
|
+
+ release_value_or_incref (value);
|
11
|
+
value_obj->address = NULL;
|
12
|
+
value_obj->type = NULL;
|
13
|
+
note_value (value_obj);
|
14
|
+
@@ -263,6 +263,7 @@ valpy_cast (PyObject *self, PyObject *args)
|
15
|
+
{
|
16
|
+
PyObject *type_obj;
|
17
|
+
struct type *type;
|
18
|
+
+ PyObject *res_obj = NULL;
|
19
|
+
struct value *res_val = NULL; /* Initialize to appease gcc warning. */
|
20
|
+
volatile struct gdb_exception except;
|
21
|
+
|
22
|
+
@@ -301,8 +302,10 @@ valpy_getitem (PyObject *self, PyObject *key)
|
23
|
+
{
|
24
|
+
value_object *self_value = (value_object *) self;
|
25
|
+
char *field = NULL;
|
26
|
+
+ PyObject *res_obj = NULL;
|
27
|
+
struct value *res_val = NULL;
|
28
|
+
volatile struct gdb_exception except;
|
29
|
+
+ struct value *mark = value_mark ();
|
30
|
+
|
31
|
+
if (gdbpy_is_string (key))
|
32
|
+
{
|
33
|
+
@@ -340,9 +343,13 @@ valpy_getitem (PyObject *self, PyObject *key)
|
34
|
+
}
|
35
|
+
|
36
|
+
xfree (field);
|
37
|
+
- GDB_PY_HANDLE_EXCEPTION (except);
|
38
|
+
+ GDB_PY_HANDLE_EXCEPTION_AND_CLEANUP (except, mark);
|
39
|
+
+
|
40
|
+
+ if (res_val)
|
41
|
+
+ res_obj = value_to_value_object (res_val);
|
42
|
+
|
43
|
+
- return res_val ? value_to_value_object (res_val) : NULL;
|
44
|
+
+ value_free_to_mark (mark);
|
45
|
+
+ return res_obj;
|
46
|
+
}
|
47
|
+
|
48
|
+
static int
|
49
|
+
@@ -423,8 +430,10 @@ enum valpy_opcode
|
50
|
+
static PyObject *
|
51
|
+
valpy_binop (enum valpy_opcode opcode, PyObject *self, PyObject *other)
|
52
|
+
{
|
53
|
+
+ PyObject *res_obj = NULL;
|
54
|
+
struct value *res_val = NULL; /* Initialize to appease gcc warning. */
|
55
|
+
volatile struct gdb_exception except;
|
56
|
+
+ struct value *mark = value_mark ();
|
57
|
+
|
58
|
+
TRY_CATCH (except, RETURN_MASK_ALL)
|
59
|
+
{
|
60
|
+
@@ -515,9 +524,13 @@ valpy_binop (enum valpy_opcode opcode, PyObject *self, PyObject *other)
|
61
|
+
break;
|
62
|
+
}
|
63
|
+
}
|
64
|
+
- GDB_PY_HANDLE_EXCEPTION (except);
|
65
|
+
+ GDB_PY_HANDLE_EXCEPTION_AND_CLEANUP (except, mark);
|
66
|
+
+
|
67
|
+
+ if (res_val)
|
68
|
+
+ res_obj = value_to_value_object (res_val);
|
69
|
+
|
70
|
+
- return res_val ? value_to_value_object (res_val) : NULL;
|
71
|
+
+ value_free_to_mark (mark);
|
72
|
+
+ return res_obj;
|
73
|
+
}
|
74
|
+
|
75
|
+
static PyObject *
|
76
|
+
@@ -680,6 +693,7 @@ valpy_richcompare (PyObject *self, PyObject *other, int op)
|
77
|
+
int result = 0;
|
78
|
+
struct value *value_other;
|
79
|
+
volatile struct gdb_exception except;
|
80
|
+
+ struct value *mark = value_mark ();
|
81
|
+
|
82
|
+
if (other == Py_None)
|
83
|
+
/* Comparing with None is special. From what I can tell, in Python
|
84
|
+
@@ -738,6 +752,7 @@ valpy_richcompare (PyObject *self, PyObject *other, int op)
|
85
|
+
break;
|
86
|
+
}
|
87
|
+
}
|
88
|
+
+ value_free_to_mark (mark);
|
89
|
+
GDB_PY_HANDLE_EXCEPTION (except);
|
90
|
+
|
91
|
+
/* In this case, the Python exception has already been set. */
|
92
|
+
@@ -858,7 +873,7 @@ value_to_value_object (struct value *val)
|
93
|
+
if (val_obj != NULL)
|
94
|
+
{
|
95
|
+
val_obj->value = val;
|
96
|
+
- value_incref (val);
|
97
|
+
+ release_value_or_incref (val);
|
98
|
+
val_obj->address = NULL;
|
99
|
+
val_obj->type = NULL;
|
100
|
+
note_value (val_obj);
|
101
|
+
diff --git a/python/python-internal.h b/python/python-internal.h
|
102
|
+
index fa4a62b..bc9c8c0 100644
|
103
|
+
--- a/python/python-internal.h
|
104
|
+
+++ b/python/python-internal.h
|
105
|
+
@@ -103,11 +103,17 @@ extern const struct language_defn *python_language;
|
106
|
+
/* Use this after a TRY_EXCEPT to throw the appropriate Python
|
107
|
+
exception. */
|
108
|
+
#define GDB_PY_HANDLE_EXCEPTION(Exception) \
|
109
|
+
+ GDB_PY_HANDLE_EXCEPTION_AND_CLEANUP(Exception, NULL)
|
110
|
+
+
|
111
|
+
+#define GDB_PY_HANDLE_EXCEPTION_AND_CLEANUP(Exception, mark) \
|
112
|
+
do { \
|
113
|
+
- if (Exception.reason < 0) \
|
114
|
+
+ if (Exception.reason < 0) { \
|
115
|
+
+ if (mark) \
|
116
|
+
+ value_free_to_mark (mark); \
|
117
|
+
return PyErr_Format (Exception.reason == RETURN_QUIT \
|
118
|
+
? PyExc_KeyboardInterrupt : PyExc_RuntimeError, \
|
119
|
+
"%s", Exception.message); \
|
120
|
+
+ } \
|
121
|
+
} while (0)
|
122
|
+
|
123
|
+
|
124
|
+
diff --git a/value.c b/value.c
|
125
|
+
index 589e03b..59a0196 100644
|
126
|
+
--- a/value.c
|
127
|
+
+++ b/value.c
|
128
|
+
@@ -675,7 +675,7 @@ free_all_values (void)
|
129
|
+
/* Remove VAL from the chain all_values
|
130
|
+
so it will not be freed automatically. */
|
131
|
+
|
132
|
+
-void
|
133
|
+
+int
|
134
|
+
release_value (struct value *val)
|
135
|
+
{
|
136
|
+
struct value *v;
|
137
|
+
@@ -683,7 +683,7 @@ release_value (struct value *val)
|
138
|
+
if (all_values == val)
|
139
|
+
{
|
140
|
+
all_values = val->next;
|
141
|
+
- return;
|
142
|
+
+ return 1;
|
143
|
+
}
|
144
|
+
|
145
|
+
for (v = all_values; v; v = v->next)
|
146
|
+
@@ -691,9 +691,21 @@ release_value (struct value *val)
|
147
|
+
if (v->next == val)
|
148
|
+
{
|
149
|
+
v->next = val->next;
|
150
|
+
- break;
|
151
|
+
+ return 1;
|
152
|
+
}
|
153
|
+
}
|
154
|
+
+
|
155
|
+
+ return 0;
|
156
|
+
+}
|
157
|
+
+
|
158
|
+
+/* Release VAL or increment its reference count if
|
159
|
+
+ it was released already */
|
160
|
+
+
|
161
|
+
+void
|
162
|
+
+release_value_or_incref (struct value *val)
|
163
|
+
+{
|
164
|
+
+ if (release_value (val) == 0)
|
165
|
+
+ value_incref (val);
|
166
|
+
}
|
167
|
+
|
168
|
+
/* Release all values up to mark */
|
169
|
+
diff --git a/value.h b/value.h
|
170
|
+
index 51e6960..4760c4a 100644
|
171
|
+
--- a/value.h
|
172
|
+
+++ b/value.h
|
173
|
+
@@ -594,7 +594,9 @@ extern void value_free (struct value *val);
|
174
|
+
|
175
|
+
extern void free_all_values (void);
|
176
|
+
|
177
|
+
-extern void release_value (struct value *val);
|
178
|
+
+extern int release_value (struct value *val);
|
179
|
+
+
|
180
|
+
+extern void release_value_or_incref (struct value *val);
|
181
|
+
|
182
|
+
extern int record_latest_value (struct value *val);
|
183
|
+
|
data/scripts/ruby-gdb.py
ADDED
@@ -0,0 +1,404 @@
|
|
1
|
+
import re
|
2
|
+
import gdb
|
3
|
+
|
4
|
+
class ZeroDict(dict):
|
5
|
+
def __getitem__(self, i):
|
6
|
+
if i not in self: self[i] = 0
|
7
|
+
return dict.__getitem__(self, i)
|
8
|
+
|
9
|
+
class Ruby (gdb.Command):
|
10
|
+
def __init__ (self):
|
11
|
+
super (Ruby, self).__init__ ("ruby", gdb.COMMAND_NONE, gdb.COMPLETE_COMMAND, True)
|
12
|
+
|
13
|
+
class RubyThreads (gdb.Command):
|
14
|
+
def __init__ (self):
|
15
|
+
super (RubyThreads, self).__init__ ("ruby threads", gdb.COMMAND_NONE)
|
16
|
+
|
17
|
+
def complete (self, text, word):
|
18
|
+
if text == word:
|
19
|
+
if word == '':
|
20
|
+
return ['trace', 'list']
|
21
|
+
elif word[0] == 't':
|
22
|
+
return ['trace']
|
23
|
+
elif word[0] == 'l':
|
24
|
+
return ['list']
|
25
|
+
|
26
|
+
def invoke (self, arg, from_tty):
|
27
|
+
if re.match('trace', arg):
|
28
|
+
self.trace()
|
29
|
+
else:
|
30
|
+
self.type = arg if arg == 'list' else None
|
31
|
+
self.show()
|
32
|
+
|
33
|
+
def trace (self):
|
34
|
+
self.type = 'list'
|
35
|
+
self.curr = None
|
36
|
+
self.main = gdb.eval('rb_main_thread')
|
37
|
+
|
38
|
+
self.height = gdb.parameter('height') or 26
|
39
|
+
self.unwind = gdb.parameter('unwindonsignal')
|
40
|
+
gdb.execute('set height 0')
|
41
|
+
gdb.execute('set unwindonsignal on')
|
42
|
+
|
43
|
+
gdb.execute('watch rb_curr_thread')
|
44
|
+
gdb.breakpoints()[-1].silent = True
|
45
|
+
num = gdb.breakpoints()[-1].number
|
46
|
+
|
47
|
+
try:
|
48
|
+
prev = None
|
49
|
+
while True:
|
50
|
+
gdb.execute('continue')
|
51
|
+
curr = gdb.eval('rb_curr_thread')
|
52
|
+
if curr == prev: break
|
53
|
+
self.print_thread(curr)
|
54
|
+
prev = curr
|
55
|
+
except KeyboardInterrupt:
|
56
|
+
None
|
57
|
+
|
58
|
+
gdb.execute('delete %d' % num)
|
59
|
+
gdb.execute('set unwindonsignal %s' % ('on' if self.unwind else 'off'))
|
60
|
+
gdb.execute('set height %d' % self.height)
|
61
|
+
|
62
|
+
def show (self):
|
63
|
+
self.main = gdb.eval('rb_main_thread')
|
64
|
+
self.curr = gdb.eval('rb_curr_thread')
|
65
|
+
self.now = gdb.eval('timeofday()')
|
66
|
+
|
67
|
+
try:
|
68
|
+
gdb.eval('rb_thread_start_2')
|
69
|
+
except RuntimeError:
|
70
|
+
self.is_heap_stack = False
|
71
|
+
else:
|
72
|
+
self.is_heap_stack = True
|
73
|
+
|
74
|
+
if self.main == 0:
|
75
|
+
print "Ruby VM is not running!"
|
76
|
+
else:
|
77
|
+
th = self.main
|
78
|
+
while True:
|
79
|
+
self.print_thread(th)
|
80
|
+
th = th['next']
|
81
|
+
if th == self.main: break
|
82
|
+
|
83
|
+
print
|
84
|
+
|
85
|
+
def print_thread (self, th):
|
86
|
+
if self.type != 'list': print
|
87
|
+
print th,
|
88
|
+
print 'main' if th == self.main else ' ',
|
89
|
+
print 'curr' if th == self.curr else ' ',
|
90
|
+
print "thread", " %s" % str(th['status']).ljust(16), "%s" % self.wait_state(th), " ",
|
91
|
+
if th != self.curr:
|
92
|
+
print "% 8d bytes" % th['stk_len']
|
93
|
+
else:
|
94
|
+
print
|
95
|
+
|
96
|
+
if self.type == 'list': return
|
97
|
+
|
98
|
+
if th == self.curr:
|
99
|
+
frame = gdb.eval('ruby_frame')
|
100
|
+
node = gdb.eval('ruby_current_node')
|
101
|
+
else:
|
102
|
+
frame = th['frame']
|
103
|
+
node = frame['node']
|
104
|
+
|
105
|
+
self.print_stack(th, frame, node)
|
106
|
+
|
107
|
+
def wait_state (self, th):
|
108
|
+
mask = th['wait_for']
|
109
|
+
state = list()
|
110
|
+
if mask == 0: state.append('WAIT_NONE')
|
111
|
+
if mask & 1: state.append('WAIT_FD(%d)' % th['fd'])
|
112
|
+
if mask & 2: state.append('WAIT_SELECT')
|
113
|
+
if mask & 4:
|
114
|
+
delay = th['delay']
|
115
|
+
time = delay-self.now
|
116
|
+
state.append('WAIT_TIME(%5.2f)' % time)
|
117
|
+
if mask & 8: state.append('WAIT_JOIN(%s)' % th['join'])
|
118
|
+
if mask & 16: state.append('WAIT_PID')
|
119
|
+
return ', '.join(state).ljust(22)
|
120
|
+
|
121
|
+
def print_stack (self, th, frame, node):
|
122
|
+
while True:
|
123
|
+
stk_pos = th['stk_pos']
|
124
|
+
stk_ptr = th['stk_ptr']
|
125
|
+
stk_len = th['stk_len']
|
126
|
+
addr = gdb.eval('(VALUE*)%s' % frame)
|
127
|
+
|
128
|
+
if not self.is_heap_stack and th != self.curr and stk_pos < addr and addr < (stk_pos+stk_len):
|
129
|
+
frame = (addr-stk_pos) + stk_ptr
|
130
|
+
frame = gdb.eval('(struct FRAME *)%s' % frame)
|
131
|
+
node = frame['node']
|
132
|
+
|
133
|
+
file = node['nd_file'].string()
|
134
|
+
line = gdb.eval('nd_line(%s)' % node)
|
135
|
+
type = gdb.eval('(enum node_type) nd_type(%s)' % node)
|
136
|
+
|
137
|
+
if frame['last_func']:
|
138
|
+
method = gdb.eval('rb_id2name(%s)' % frame['last_func']).string()
|
139
|
+
else:
|
140
|
+
method = '(unknown)'
|
141
|
+
|
142
|
+
print " ",
|
143
|
+
print str(type).lower().center(18), "%s in %s:%d" % (method, file, line)
|
144
|
+
|
145
|
+
if frame['prev'] == 0 or frame['last_func'] == 0: break
|
146
|
+
frame = frame['prev']
|
147
|
+
node = frame['node']
|
148
|
+
if node == 0: break
|
149
|
+
|
150
|
+
class RubyTrace (gdb.Command):
|
151
|
+
def __init__ (self):
|
152
|
+
super (RubyTrace, self).__init__ ("ruby trace", gdb.COMMAND_NONE, gdb.COMPLETE_NONE)
|
153
|
+
|
154
|
+
def setup (self):
|
155
|
+
commands = """
|
156
|
+
set $func = malloc(1)
|
157
|
+
p ((char*)$func)[0] = '\xc3'
|
158
|
+
p mprotect(($func&0xfffffffffffff000), 1, 0x7)
|
159
|
+
p rb_add_event_hook($func, RUBY_EVENT_C_CALL|RUBY_EVENT_CALL)
|
160
|
+
b *$func
|
161
|
+
""".split("\n")
|
162
|
+
|
163
|
+
for c in commands:
|
164
|
+
gdb.execute(c)
|
165
|
+
|
166
|
+
gdb.breakpoints()[-1].silent = True
|
167
|
+
self.func = gdb.eval('$func')
|
168
|
+
|
169
|
+
self.height = gdb.parameter('height') or 26
|
170
|
+
self.unwind = gdb.parameter('unwindonsignal')
|
171
|
+
gdb.execute('set height 0')
|
172
|
+
gdb.execute('set unwindonsignal on')
|
173
|
+
|
174
|
+
def teardown (self):
|
175
|
+
commands = """
|
176
|
+
finish
|
177
|
+
clear *$func
|
178
|
+
p mprotect(($func&0xfffffffffffff000), 1, 0x3)
|
179
|
+
p rb_remove_event_hook($func)
|
180
|
+
p free($func)
|
181
|
+
set $func = 0
|
182
|
+
""".split("\n")
|
183
|
+
|
184
|
+
for c in commands:
|
185
|
+
gdb.execute(c)
|
186
|
+
|
187
|
+
gdb.execute('set unwindonsignal %s' % ('on' if self.unwind else 'off'))
|
188
|
+
gdb.execute('set height %d' % self.height)
|
189
|
+
|
190
|
+
def invoke (self, arg, from_tty):
|
191
|
+
self.dont_repeat()
|
192
|
+
num = int(arg) if arg else 100
|
193
|
+
self.setup()
|
194
|
+
|
195
|
+
try:
|
196
|
+
while num > 0:
|
197
|
+
num -= 1
|
198
|
+
gdb.execute('continue')
|
199
|
+
|
200
|
+
frame = gdb.selected_frame()
|
201
|
+
if frame.pc() != self.func:
|
202
|
+
raise KeyboardInterrupt
|
203
|
+
|
204
|
+
node = gdb.eval('(NODE*) $rsi')
|
205
|
+
file = node['nd_file'].string()
|
206
|
+
line = gdb.eval('nd_line(%s)' % node)
|
207
|
+
method = gdb.eval('rb_id2name($rcx)')
|
208
|
+
method = method.string() if method > 0 else '(unknown)'
|
209
|
+
|
210
|
+
print "%s in %s:%d" % (method,file,line)
|
211
|
+
|
212
|
+
self.teardown()
|
213
|
+
except KeyboardInterrupt:
|
214
|
+
self.teardown()
|
215
|
+
except RuntimeError, text:
|
216
|
+
self.teardown()
|
217
|
+
if not re.search('signaled while in a function called from GDB', text):
|
218
|
+
raise
|
219
|
+
|
220
|
+
class RubyObjects (gdb.Command):
|
221
|
+
def __init__ (self):
|
222
|
+
super (RubyObjects, self).__init__ ("ruby objects", gdb.COMMAND_NONE)
|
223
|
+
|
224
|
+
def invoke (self, arg, from_tty):
|
225
|
+
self.height = gdb.parameter('height') or 26
|
226
|
+
gdb.execute('set height 0')
|
227
|
+
|
228
|
+
if arg == 'classes':
|
229
|
+
self.print_classes()
|
230
|
+
elif arg == 'nodes':
|
231
|
+
self.print_nodes()
|
232
|
+
elif arg == 'strings':
|
233
|
+
self.print_strings()
|
234
|
+
else:
|
235
|
+
self.print_stats()
|
236
|
+
|
237
|
+
gdb.execute('set height %d' % self.height)
|
238
|
+
|
239
|
+
def complete (self, text, word):
|
240
|
+
if text == word:
|
241
|
+
if word == '':
|
242
|
+
return ['classes', 'strings', 'nodes']
|
243
|
+
elif word[0] == 'c':
|
244
|
+
return ['classes']
|
245
|
+
elif word[0] == 'n':
|
246
|
+
return ['nodes']
|
247
|
+
elif word[0] == 's':
|
248
|
+
return ['strings']
|
249
|
+
|
250
|
+
def print_nodes (self):
|
251
|
+
nodes = ZeroDict()
|
252
|
+
|
253
|
+
for (obj, type) in self.live_objects():
|
254
|
+
if type == 0x3f:
|
255
|
+
nodes[ (int(obj['as']['node']['flags']) >> 12) & 0xff ] += 1
|
256
|
+
|
257
|
+
for (node, num) in sorted(nodes.items(), key=lambda(k,v):(v,k)):
|
258
|
+
print "% 8d %s" % (num, gdb.eval('(enum node_type) (%d)' % node))
|
259
|
+
|
260
|
+
def print_classes (self):
|
261
|
+
classes = ZeroDict()
|
262
|
+
|
263
|
+
for (obj, type) in self.live_objects():
|
264
|
+
if type == 0x2:
|
265
|
+
classes[ int(obj['as']['basic']['klass']) ] += 1
|
266
|
+
|
267
|
+
for (klass, num) in sorted(classes.items(), key=lambda(k,v):(v,k)):
|
268
|
+
print "% 8d %s" % (num, gdb.eval('rb_class2name(%d)' % klass).string())
|
269
|
+
|
270
|
+
def print_strings (self):
|
271
|
+
strings = ZeroDict()
|
272
|
+
bytes = 0
|
273
|
+
|
274
|
+
for (obj, type) in self.live_objects():
|
275
|
+
if type == 0x7:
|
276
|
+
s = obj['as']['string']
|
277
|
+
ptr = s['ptr']
|
278
|
+
if ptr:
|
279
|
+
bytes += s['len']
|
280
|
+
strings[ ptr.string() ] += 1
|
281
|
+
|
282
|
+
for (s, num) in sorted(strings.items(), key=lambda(k,v):(v,k)):
|
283
|
+
print "% 9d" % num, repr(s)
|
284
|
+
|
285
|
+
print
|
286
|
+
print "% 9d" % len(strings), "unique strings"
|
287
|
+
print "% 9d" % bytes, "bytes"
|
288
|
+
print
|
289
|
+
|
290
|
+
def print_stats (self):
|
291
|
+
total = live = free = 0
|
292
|
+
types = ZeroDict()
|
293
|
+
|
294
|
+
for (obj, flags) in self.all_objects():
|
295
|
+
if flags:
|
296
|
+
live += 1
|
297
|
+
types[ int(flags & 0x3f) ] += 1
|
298
|
+
else:
|
299
|
+
free += 1
|
300
|
+
|
301
|
+
total += 1
|
302
|
+
|
303
|
+
print
|
304
|
+
print " HEAPS % 9d" % self.heaps_used
|
305
|
+
print " SLOTS % 9d" % total
|
306
|
+
print " LIVE % 9d (%3.2f%%)" % (live, 100.0*live/total)
|
307
|
+
print " FREE % 9d (%3.2f%%)" % (free, 100.0*free/total)
|
308
|
+
print
|
309
|
+
|
310
|
+
for (type, num) in sorted(types.items(), key=lambda(k,v):(v,k)):
|
311
|
+
print " %s % 9d (%3.2f%%)" % (self.obj_type(type).ljust(8), num, 100.0*num/live)
|
312
|
+
|
313
|
+
print
|
314
|
+
|
315
|
+
def all_objects (self):
|
316
|
+
self.heaps_used = gdb.eval('heaps_used')
|
317
|
+
|
318
|
+
for i in xrange(self.heaps_used):
|
319
|
+
p = gdb.eval("(RVALUE*) heaps[%i].slot" % i)
|
320
|
+
pend = p + gdb.eval("heaps[%i].limit" % i)
|
321
|
+
|
322
|
+
while p < pend:
|
323
|
+
yield p, p['as']['basic']['flags']
|
324
|
+
p += 1
|
325
|
+
|
326
|
+
def live_objects (self):
|
327
|
+
for (obj, flags) in self.all_objects():
|
328
|
+
if flags:
|
329
|
+
yield obj, int(flags & 0x3f)
|
330
|
+
|
331
|
+
def obj_type (self, type):
|
332
|
+
return RubyObjects.TYPES.get(type, 'unknown')
|
333
|
+
|
334
|
+
Ruby()
|
335
|
+
RubyThreads()
|
336
|
+
RubyTrace()
|
337
|
+
RubyObjects()
|
338
|
+
|
339
|
+
macros = """
|
340
|
+
macro define R_CAST(st) (struct st*)
|
341
|
+
macro define RNODE(obj) (R_CAST(RNode)(obj))
|
342
|
+
macro define FL_USHIFT 12
|
343
|
+
macro define CHAR_BIT 8
|
344
|
+
macro define NODE_LSHIFT (FL_USHIFT+8)
|
345
|
+
macro define NODE_LMASK (((long)1<<(sizeof(NODE*)*CHAR_BIT-NODE_LSHIFT))-1)
|
346
|
+
macro define nd_line(n) ((unsigned int)(((RNODE(n))->flags>>NODE_LSHIFT)&NODE_LMASK))
|
347
|
+
macro define nd_type(n) ((int)(((RNODE(n))->flags>>FL_USHIFT)&0xff))
|
348
|
+
|
349
|
+
macro define T_MASK 0x3f
|
350
|
+
macro define BUILTIN_TYPE(x) (((struct RBasic*)(x))->flags & T_MASK)
|
351
|
+
|
352
|
+
macro define WAIT_FD (1<<0)
|
353
|
+
macro define WAIT_SELECT (1<<1)
|
354
|
+
macro define WAIT_TIME (1<<2)
|
355
|
+
macro define WAIT_JOIN (1<<3)
|
356
|
+
macro define WAIT_PID (1<<4)
|
357
|
+
|
358
|
+
macro define RUBY_EVENT_CALL 0x08
|
359
|
+
macro define RUBY_EVENT_C_CALL 0x20
|
360
|
+
""".split("\n")
|
361
|
+
|
362
|
+
for m in macros:
|
363
|
+
if len(m.strip()) > 0:
|
364
|
+
gdb.execute(m)
|
365
|
+
|
366
|
+
types = """
|
367
|
+
T_NONE 0x00
|
368
|
+
|
369
|
+
T_NIL 0x01
|
370
|
+
T_OBJECT 0x02
|
371
|
+
T_CLASS 0x03
|
372
|
+
T_ICLASS 0x04
|
373
|
+
T_MODULE 0x05
|
374
|
+
T_FLOAT 0x06
|
375
|
+
T_STRING 0x07
|
376
|
+
T_REGEXP 0x08
|
377
|
+
T_ARRAY 0x09
|
378
|
+
T_FIXNUM 0x0a
|
379
|
+
T_HASH 0x0b
|
380
|
+
T_STRUCT 0x0c
|
381
|
+
T_BIGNUM 0x0d
|
382
|
+
T_FILE 0x0e
|
383
|
+
|
384
|
+
T_TRUE 0x20
|
385
|
+
T_FALSE 0x21
|
386
|
+
T_DATA 0x22
|
387
|
+
T_MATCH 0x23
|
388
|
+
T_SYMBOL 0x24
|
389
|
+
|
390
|
+
T_BLKTAG 0x3b
|
391
|
+
T_UNDEF 0x3c
|
392
|
+
T_VARMAP 0x3d
|
393
|
+
T_SCOPE 0x3e
|
394
|
+
T_NODE 0x3f
|
395
|
+
""".split("\n")
|
396
|
+
|
397
|
+
RubyObjects.TYPES = {}
|
398
|
+
|
399
|
+
for t in types:
|
400
|
+
if len(t.strip()) > 0:
|
401
|
+
name, val = t.split()
|
402
|
+
gdb.execute("macro define %s %s" % (name, val))
|
403
|
+
RubyObjects.TYPES[int(val,16)] = name[2:].lower()
|
404
|
+
|