gdb.rb 0.1.1 → 0.1.2
Sign up to get free protection for your applications and to get access to all the features.
- 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
|