gdb.rb 0.1.1 → 0.1.2
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 +20 -0
- data/bin/gdb.rb +16 -6
- data/ext/extconf.rb +5 -3
- data/gdb.rb.gemspec +3 -2
- data/patches/gdb-strings.patch +14 -0
- data/scripts/ruby-gdb.py +127 -10
- metadata +4 -3
data/README
CHANGED
@@ -1,6 +1,11 @@
|
|
1
1
|
gdb7 hooks for MRI
|
2
2
|
(c) 2009 Aman Gupta (tmm1)
|
3
3
|
|
4
|
+
=== Requirements
|
5
|
+
|
6
|
+
gdb.rb currently requires x86_64 linux, and a REE 1.8 binary. Support for other MRI builds and
|
7
|
+
platforms is in the works.
|
8
|
+
|
4
9
|
=== Usage
|
5
10
|
|
6
11
|
$ ps aux | grep deploy.rb
|
@@ -221,9 +226,24 @@ gdb7 hooks for MRI
|
|
221
226
|
85825 NODE_CALL
|
222
227
|
126739 NODE_ARRAY
|
223
228
|
|
229
|
+
|
224
230
|
=== TODO
|
225
231
|
|
226
232
|
`ruby where` for the current stack trace
|
227
233
|
`ruby print` to inspect ruby variables
|
228
234
|
`ruby breakpoint` to breakpoint on ruby methods
|
229
235
|
`ruby irb` for a simple interactive shell
|
236
|
+
|
237
|
+
|
238
|
+
=== Credits
|
239
|
+
|
240
|
+
Tom Tromey for his work on gdb python support (gdb-eval.patch, gdb-breakpoints.patch) and help
|
241
|
+
tracking down some memory leaks (gdb-leak.patch)
|
242
|
+
|
243
|
+
Joe Damato for writing parts of ruby-gdb.py and various gdb hacks to make gdb.rb possible.
|
244
|
+
|
245
|
+
|
246
|
+
=== License
|
247
|
+
|
248
|
+
Available under the Ruby License.
|
249
|
+
|
data/bin/gdb.rb
CHANGED
@@ -1,17 +1,27 @@
|
|
1
1
|
#!/usr/bin/env ruby
|
2
2
|
require 'rbconfig'
|
3
3
|
|
4
|
-
if ARGV.size == 1
|
5
|
-
ARGV
|
6
|
-
elsif ARGV.size ==
|
4
|
+
if ARGV.size == 1 && ARGV[0] =~ /^\d+$/
|
5
|
+
pid = ARGV[0]
|
6
|
+
elsif ARGV.size == 1 && ARGV[0] == 'none'
|
7
|
+
pid = nil
|
7
8
|
else
|
8
9
|
puts "Usage:"
|
9
10
|
puts
|
10
11
|
puts " gdb.rb <pid>"
|
11
|
-
puts " gdb.rb <path to ruby> <pid>"
|
12
12
|
puts
|
13
13
|
exit(1)
|
14
14
|
end
|
15
15
|
|
16
|
-
|
17
|
-
|
16
|
+
dir = File.expand_path(File.join(File.dirname(__FILE__), '..'))
|
17
|
+
|
18
|
+
args = []
|
19
|
+
args << "#{dir}/ext/dst/bin/gdb"
|
20
|
+
args << "-ex 'py execfile(\"#{dir}/scripts/ruby-gdb.py\")'"
|
21
|
+
if pid
|
22
|
+
args << "-ex 'attach #{pid}'"
|
23
|
+
else
|
24
|
+
args << "#{Config::CONFIG['bindir']}/#{Config::CONFIG['ruby_install_name']}"
|
25
|
+
end
|
26
|
+
|
27
|
+
exec(args.join(' '))
|
data/ext/extconf.rb
CHANGED
@@ -19,14 +19,15 @@ if RUBY_VERSION >= "1.9"
|
|
19
19
|
exit(1)
|
20
20
|
end
|
21
21
|
|
22
|
-
if
|
22
|
+
if RUBY_PLATFORM !~ /(i686|x86_64)-linux/
|
23
23
|
STDERR.puts "\n\n"
|
24
24
|
STDERR.puts "***************************************************************************************"
|
25
|
-
STDERR.puts "
|
25
|
+
STDERR.puts "*********************** Only x86 linux is supported (for now) =( **********************"
|
26
26
|
STDERR.puts "***************************************************************************************"
|
27
27
|
exit(1)
|
28
28
|
end
|
29
29
|
|
30
|
+
dir_config('python')
|
30
31
|
unless have_header('python2.5/Python.h') or have_header('python2.6/Python.h') or have_header('python2.4/Python.h')
|
31
32
|
STDERR.puts "\n\n"
|
32
33
|
STDERR.puts "***************************************************************************************"
|
@@ -55,6 +56,7 @@ Dir.chdir('src') do
|
|
55
56
|
gdb-eval
|
56
57
|
gdb-breakpoints
|
57
58
|
gdb-leak
|
59
|
+
gdb-strings
|
58
60
|
].each do |patch|
|
59
61
|
sys("patch -p1 < ../../../../patches/#{patch}.patch")
|
60
62
|
sys("git commit -am '#{patch}'") if ENV['DEV']
|
@@ -62,7 +64,7 @@ Dir.chdir('src') do
|
|
62
64
|
end
|
63
65
|
|
64
66
|
Dir.chdir(dir) do
|
65
|
-
sys("./configure --prefix=#{CWD}/dst/")
|
67
|
+
sys("./configure --prefix=#{CWD}/dst/ --with-python=#{with_config('python-dir') || 'yes'}")
|
66
68
|
sys("make")
|
67
69
|
sys("make install")
|
68
70
|
end
|
data/gdb.rb.gemspec
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
spec = Gem::Specification.new do |s|
|
2
2
|
s.name = 'gdb.rb'
|
3
|
-
s.version = '0.1.
|
4
|
-
s.date = '2009-
|
3
|
+
s.version = '0.1.2'
|
4
|
+
s.date = '2009-12-04'
|
5
5
|
s.rubyforge_project = 'gdb-rb'
|
6
6
|
s.summary = 'gdb hooks for MRI'
|
7
7
|
s.description = 'A set of gdb7 extensions for the MRI interpreter'
|
@@ -26,6 +26,7 @@ spec = Gem::Specification.new do |s|
|
|
26
26
|
"patches/gdb-eval.patch",
|
27
27
|
"patches/gdb-breakpoints.patch",
|
28
28
|
"patches/gdb-leak.patch",
|
29
|
+
"patches/gdb-strings.patch",
|
29
30
|
"scripts/ruby-gdb.py",
|
30
31
|
"gdb.rb.gemspec"
|
31
32
|
]
|
@@ -0,0 +1,14 @@
|
|
1
|
+
diff --git a/python/py-utils.c b/python/py-utils.c
|
2
|
+
index 49c0437..9cca10b 100644
|
3
|
+
--- a/python/py-utils.c
|
4
|
+
+++ b/python/py-utils.c
|
5
|
+
@@ -189,6 +189,9 @@ python_string_to_host_string (PyObject *obj)
|
6
|
+
PyObject *str;
|
7
|
+
char *result;
|
8
|
+
|
9
|
+
+ if (PyString_Check (obj))
|
10
|
+
+ return xstrdup (PyString_AsString (obj));
|
11
|
+
+
|
12
|
+
str = python_string_to_unicode (obj);
|
13
|
+
if (str == NULL)
|
14
|
+
return NULL;
|
data/scripts/ruby-gdb.py
CHANGED
@@ -1,11 +1,17 @@
|
|
1
1
|
import re
|
2
2
|
import gdb
|
3
|
+
import time
|
3
4
|
|
4
5
|
class ZeroDict(dict):
|
5
6
|
def __getitem__(self, i):
|
6
7
|
if i not in self: self[i] = 0
|
7
8
|
return dict.__getitem__(self, i)
|
8
9
|
|
10
|
+
class ListDict(dict):
|
11
|
+
def __getitem__(self, i):
|
12
|
+
if i not in self: self[i] = []
|
13
|
+
return dict.__getitem__(self, i)
|
14
|
+
|
9
15
|
class Ruby (gdb.Command):
|
10
16
|
def __init__ (self):
|
11
17
|
super (Ruby, self).__init__ ("ruby", gdb.COMMAND_NONE, gdb.COMPLETE_COMMAND, True)
|
@@ -24,10 +30,11 @@ class RubyThreads (gdb.Command):
|
|
24
30
|
return ['list']
|
25
31
|
|
26
32
|
def invoke (self, arg, from_tty):
|
33
|
+
self.dont_repeat()
|
27
34
|
if re.match('trace', arg):
|
28
35
|
self.trace()
|
29
36
|
else:
|
30
|
-
self.type = arg
|
37
|
+
self.type = arg == 'list' and arg or None
|
31
38
|
self.show()
|
32
39
|
|
33
40
|
def trace (self):
|
@@ -54,12 +61,12 @@ class RubyThreads (gdb.Command):
|
|
54
61
|
None
|
55
62
|
|
56
63
|
gdb.execute('delete %d' % num)
|
57
|
-
gdb.execute('set unwindonsignal %s' % (
|
64
|
+
gdb.execute('set unwindonsignal %s' % (self.unwind and 'on' or 'off'))
|
58
65
|
|
59
66
|
def show (self):
|
60
67
|
self.main = gdb.eval('rb_main_thread')
|
61
68
|
self.curr = gdb.eval('rb_curr_thread')
|
62
|
-
self.now =
|
69
|
+
self.now = time.time()
|
63
70
|
|
64
71
|
try:
|
65
72
|
gdb.eval('rb_thread_start_2')
|
@@ -82,8 +89,8 @@ class RubyThreads (gdb.Command):
|
|
82
89
|
def print_thread (self, th):
|
83
90
|
if self.type != 'list': print
|
84
91
|
print th,
|
85
|
-
print
|
86
|
-
print
|
92
|
+
print th == self.main and 'main' or ' ',
|
93
|
+
print th == self.curr and 'curr' or ' ',
|
87
94
|
print "thread", " %s" % str(th['status']).ljust(16), "%s" % self.wait_state(th), " ",
|
88
95
|
if th != self.curr:
|
89
96
|
print "% 8d bytes" % th['stk_len']
|
@@ -132,7 +139,10 @@ class RubyThreads (gdb.Command):
|
|
132
139
|
type = gdb.eval('(enum node_type) nd_type(%s)' % node)
|
133
140
|
|
134
141
|
if frame['last_func']:
|
135
|
-
|
142
|
+
try:
|
143
|
+
method = gdb.eval('rb_id2name(%s)' % frame['last_func']).string()
|
144
|
+
except:
|
145
|
+
method = '(unknown)'
|
136
146
|
else:
|
137
147
|
method = '(unknown)'
|
138
148
|
|
@@ -179,11 +189,11 @@ class RubyTrace (gdb.Command):
|
|
179
189
|
for c in commands:
|
180
190
|
gdb.execute(c)
|
181
191
|
|
182
|
-
gdb.execute('set unwindonsignal %s' % (
|
192
|
+
gdb.execute('set unwindonsignal %s' % (self.unwind and 'on' or 'off'))
|
183
193
|
|
184
194
|
def invoke (self, arg, from_tty):
|
185
195
|
self.dont_repeat()
|
186
|
-
num = int(arg)
|
196
|
+
num = arg and int(arg) or 100
|
187
197
|
self.setup()
|
188
198
|
|
189
199
|
try:
|
@@ -199,7 +209,7 @@ class RubyTrace (gdb.Command):
|
|
199
209
|
file = node['nd_file'].string()
|
200
210
|
line = gdb.eval('nd_line(%s)' % node)
|
201
211
|
method = gdb.eval('rb_id2name($rcx)')
|
202
|
-
method = method.string()
|
212
|
+
method = method > 0 and method.string() or '(unknown)'
|
203
213
|
|
204
214
|
print "%s in %s:%d" % (method,file,line)
|
205
215
|
|
@@ -216,25 +226,34 @@ class RubyObjects (gdb.Command):
|
|
216
226
|
super (RubyObjects, self).__init__ ("ruby objects", gdb.COMMAND_NONE)
|
217
227
|
|
218
228
|
def invoke (self, arg, from_tty):
|
229
|
+
self.dont_repeat()
|
219
230
|
if arg == 'classes':
|
220
231
|
self.print_classes()
|
221
232
|
elif arg == 'nodes':
|
222
233
|
self.print_nodes()
|
223
234
|
elif arg == 'strings':
|
224
235
|
self.print_strings()
|
236
|
+
elif arg == 'hashes':
|
237
|
+
self.print_hashes()
|
238
|
+
elif arg == 'arrays':
|
239
|
+
self.print_arrays()
|
225
240
|
else:
|
226
241
|
self.print_stats()
|
227
242
|
|
228
243
|
def complete (self, text, word):
|
229
244
|
if text == word:
|
230
245
|
if word == '':
|
231
|
-
return ['classes', 'strings', 'nodes']
|
246
|
+
return ['classes', 'strings', 'nodes', 'hashes', 'arrays']
|
232
247
|
elif word[0] == 'c':
|
233
248
|
return ['classes']
|
234
249
|
elif word[0] == 'n':
|
235
250
|
return ['nodes']
|
236
251
|
elif word[0] == 's':
|
237
252
|
return ['strings']
|
253
|
+
elif word[0] == 'h':
|
254
|
+
return ['hashes']
|
255
|
+
elif word[0] == 'a':
|
256
|
+
return ['arrays']
|
238
257
|
|
239
258
|
def print_nodes (self):
|
240
259
|
nodes = ZeroDict()
|
@@ -276,6 +295,53 @@ class RubyObjects (gdb.Command):
|
|
276
295
|
print "% 9d" % bytes, "bytes"
|
277
296
|
print
|
278
297
|
|
298
|
+
def print_hashes (self):
|
299
|
+
sample = ListDict()
|
300
|
+
hash_sizes = ZeroDict()
|
301
|
+
num_elems = 0
|
302
|
+
|
303
|
+
for (obj, type) in self.live_objects():
|
304
|
+
if type == 0xb:
|
305
|
+
h = obj['as']['hash']
|
306
|
+
tbl = h['tbl']
|
307
|
+
l = int(tbl['num_entries'])
|
308
|
+
|
309
|
+
num_elems += l
|
310
|
+
hash_sizes[l] += 1
|
311
|
+
if len(sample[l]) < 5: sample[l].append(h.address)
|
312
|
+
|
313
|
+
print " elements instances"
|
314
|
+
for (l, num) in sorted(hash_sizes.items()):
|
315
|
+
print "%9d" % l, num, "(", ', '.join([ str(i) for i in sample[l] ]), ")"
|
316
|
+
|
317
|
+
print
|
318
|
+
print "% 9d" % sum(hash_sizes.values()), "hashes"
|
319
|
+
print "% 9d" % num_elems, "member elements"
|
320
|
+
print
|
321
|
+
|
322
|
+
def print_arrays (self):
|
323
|
+
sample = ListDict()
|
324
|
+
array_sizes = ZeroDict()
|
325
|
+
num_elems = 0
|
326
|
+
|
327
|
+
for (obj, type) in self.live_objects():
|
328
|
+
if type == 0x9:
|
329
|
+
a = obj['as']['array']
|
330
|
+
l = int(a['len'])
|
331
|
+
|
332
|
+
num_elems += l
|
333
|
+
array_sizes[l] += 1
|
334
|
+
if len(sample[l]) < 5: sample[l].append(a.address)
|
335
|
+
|
336
|
+
print " elements instances"
|
337
|
+
for (l, num) in sorted(array_sizes.items()):
|
338
|
+
print "%9d" % l, num, "(", ', '.join([ str(i) for i in sample[l] ]), ")"
|
339
|
+
|
340
|
+
print
|
341
|
+
print "% 9d" % sum(array_sizes.values()), "arrays"
|
342
|
+
print "% 9d" % num_elems, "member elements"
|
343
|
+
print
|
344
|
+
|
279
345
|
def print_stats (self):
|
280
346
|
total = live = free = 0
|
281
347
|
types = ZeroDict()
|
@@ -320,10 +386,61 @@ class RubyObjects (gdb.Command):
|
|
320
386
|
def obj_type (self, type):
|
321
387
|
return RubyObjects.TYPES.get(type, 'unknown')
|
322
388
|
|
389
|
+
class RubyMethodCache (gdb.Command):
|
390
|
+
def __init__ (self):
|
391
|
+
super (RubyMethodCache, self).__init__ ("ruby methodcache", gdb.COMMAND_NONE)
|
392
|
+
|
393
|
+
def invoke (self, arg, from_tty):
|
394
|
+
self.dont_repeat()
|
395
|
+
cache = gdb.eval('cache')
|
396
|
+
size = 0x800
|
397
|
+
empty = 0
|
398
|
+
|
399
|
+
for i in xrange(size):
|
400
|
+
entry = cache[i]
|
401
|
+
if entry['mid'] != 0:
|
402
|
+
klass = gdb.eval('rb_class2name(%d)' % entry['klass'])
|
403
|
+
method = gdb.eval('rb_id2name(%d)' % entry['mid'])
|
404
|
+
print " %s#%s" % (klass and klass.string() or '(unknown)', method and method.string() or '(unknown)')
|
405
|
+
else:
|
406
|
+
empty += 1
|
407
|
+
|
408
|
+
print
|
409
|
+
print "%d empty slots (%.2f%%)" % (empty, empty*100.0/size)
|
410
|
+
print
|
411
|
+
|
412
|
+
class RubyPrint (gdb.Command):
|
413
|
+
def __init__ (self):
|
414
|
+
super (RubyPrint, self).__init__ ("ruby print", gdb.COMMAND_NONE)
|
415
|
+
|
416
|
+
def invoke (self, arg, from_tty):
|
417
|
+
self.dont_repeat()
|
418
|
+
|
419
|
+
type = int(gdb.eval("((struct RBasic *)(%d))->flags & 0x3f" % int(arg,0)))
|
420
|
+
rtype = RubyObjects.TYPES.get(type, 'unknown')
|
421
|
+
|
422
|
+
if rtype == 'array':
|
423
|
+
print rtype
|
424
|
+
elif rtype == 'hash':
|
425
|
+
print rtype
|
426
|
+
else:
|
427
|
+
print 'unknown'
|
428
|
+
|
429
|
+
class RubyEval (gdb.Command):
|
430
|
+
def __init__ (self):
|
431
|
+
super (RubyEval, self).__init__ ("ruby eval", gdb.COMMAND_NONE)
|
432
|
+
|
433
|
+
def invoke (self, arg, from_tty):
|
434
|
+
self.dont_repeat()
|
435
|
+
print gdb.eval("((struct RString*)rb_eval_string_protect(\"begin; (%s).inspect; rescue Exception => e; e.inspect; end\", 0))->ptr" % arg).string()
|
436
|
+
|
323
437
|
Ruby()
|
324
438
|
RubyThreads()
|
325
439
|
RubyTrace()
|
326
440
|
RubyObjects()
|
441
|
+
RubyMethodCache()
|
442
|
+
RubyPrint()
|
443
|
+
RubyEval()
|
327
444
|
|
328
445
|
macros = """
|
329
446
|
macro define R_CAST(st) (struct st*)
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: gdb.rb
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.
|
4
|
+
version: 0.1.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Aman Gupta
|
@@ -9,7 +9,7 @@ autorequire:
|
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
11
|
|
12
|
-
date: 2009-
|
12
|
+
date: 2009-12-04 00:00:00 -05:00
|
13
13
|
default_executable:
|
14
14
|
dependencies: []
|
15
15
|
|
@@ -30,6 +30,7 @@ files:
|
|
30
30
|
- patches/gdb-eval.patch
|
31
31
|
- patches/gdb-breakpoints.patch
|
32
32
|
- patches/gdb-leak.patch
|
33
|
+
- patches/gdb-strings.patch
|
33
34
|
- scripts/ruby-gdb.py
|
34
35
|
- gdb.rb.gemspec
|
35
36
|
has_rdoc: true
|
@@ -56,7 +57,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
56
57
|
requirements: []
|
57
58
|
|
58
59
|
rubyforge_project: gdb-rb
|
59
|
-
rubygems_version: 1.3.
|
60
|
+
rubygems_version: 1.3.5
|
60
61
|
signing_key:
|
61
62
|
specification_version: 3
|
62
63
|
summary: gdb hooks for MRI
|