gdb.rb 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|
+
|