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 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