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 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 and ARGV[0] =~ /^\d+$/
5
- ARGV.unshift "#{Config::CONFIG['bindir']}/ruby"
6
- elsif ARGV.size == 2 and File.exist?(ARGV[0]) and ARGV[1] =~ /^\d+$/
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
- cmd = "#{File.dirname(__FILE__)}/../ext/dst/bin/gdb -ex 'py execfile(\"#{File.dirname(__FILE__)}/../scripts/ruby-gdb.py\")' #{ARGV.join(" ")}"
17
- exec(cmd)
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(' '))
@@ -19,14 +19,15 @@ if RUBY_VERSION >= "1.9"
19
19
  exit(1)
20
20
  end
21
21
 
22
- if `uname -a 2>&1` !~ /x86_64/
22
+ if RUBY_PLATFORM !~ /(i686|x86_64)-linux/
23
23
  STDERR.puts "\n\n"
24
24
  STDERR.puts "***************************************************************************************"
25
- STDERR.puts "********************* Only x86_64 linux is supported (for now) =( *********************"
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
@@ -1,7 +1,7 @@
1
1
  spec = Gem::Specification.new do |s|
2
2
  s.name = 'gdb.rb'
3
- s.version = '0.1.1'
4
- s.date = '2009-11-06'
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;
@@ -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 if arg == 'list' else None
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' % ('on' if self.unwind else 'off'))
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 = gdb.eval('timeofday()')
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 'main' if th == self.main else ' ',
86
- print 'curr' if th == self.curr else ' ',
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
- method = gdb.eval('rb_id2name(%s)' % frame['last_func']).string()
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' % ('on' if self.unwind else 'off'))
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) if arg else 100
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() if method > 0 else '(unknown)'
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.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-11-06 00:00:00 -08:00
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.4
60
+ rubygems_version: 1.3.5
60
61
  signing_key:
61
62
  specification_version: 3
62
63
  summary: gdb hooks for MRI