gdb 0.3.0 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: 465e52470a227ee794a0ad1cbc6e643b6e7892d6
4
- data.tar.gz: 377b2acb9be71480b63999833ea24247dcceba1e
2
+ SHA256:
3
+ metadata.gz: fb429f1309a91731395b1a338e151f3d46431c9e060ffa067cb8cad51320d835
4
+ data.tar.gz: 249e06ec6ae0497951c7ed958e0da149694e76fc91696d839ebc8c3c6175e4a1
5
5
  SHA512:
6
- metadata.gz: efbff6edff5a76a1774bb4a5ba2300885cce78ff783730270fef4f0d5b8b1503539efa626f6cd56a220f4a9ccc0a2646c96eaa6d0392e7ff64511f9ef143f39b
7
- data.tar.gz: b91c6d713bc611fefcfa1898097ccfa402e18220f737f39ab1de6ee830603c760283a6a761ba7c745a4001255b316ccf8a9945df327781a7208335308d84ade0
6
+ metadata.gz: 0fdcb2dd789d8dbf3c29d46d0f0ba5b77373eeac74e4412d8136f65feaeedde0b82d95d48503c05edec8ea8f951264d798a4b99d2458b0833a48b164e032fa61
7
+ data.tar.gz: '09f569cd9e61aa079d0de7cf498220b010c529c4319f702f4a0d990ebf9714b8bce3f0a811254a2a9d788d6fd9fce8db93402bc1a5e98980cf84739a58c13b23'
data/README.md CHANGED
@@ -8,11 +8,16 @@
8
8
 
9
9
  # GDB-Ruby
10
10
 
11
- It's time for Ruby lovers to use Ruby in gdb, and... gdb in Ruby!
11
+ It's time for Ruby lovers to use Ruby in gdb and, gdb in Ruby!
12
+
13
+ Achieve two things in one gem:
14
+
15
+ 1. Launch Ruby interactive shell (pry) in gdb.
16
+ 2. A gdb Ruby-binding, i.e. communiate with gdb in a Ruby script.
12
17
 
13
18
  # Use Ruby in gdb
14
19
 
15
- We provide a binary `gdb-ruby` with usage exactly same as normal gdb,
20
+ We provide a binary `gdb-ruby` (a Ruby script actually) with usage exactly same as normal gdb,
16
21
  while has two extra commands: `ruby` and `pry`!
17
22
 
18
23
  See examples below:
@@ -51,14 +56,14 @@ Example:
51
56
 
52
57
  ## Integrate with other gdb extensions
53
58
 
54
- Completely NO effort if you want to use **gdb-ruby** with other gdb extension.
59
+ Completely *NO* effort if you want to use **gdb-ruby** with other gdb extensions.
55
60
 
56
61
  For example, I usually use [gef](https://github.com/hugsy/gef) for my gdb.
57
62
  And everything works as usual when integrated with **gdb-ruby**:
58
63
 
59
64
  Launching with `$ gdb-ruby -q bash`
60
65
 
61
- ![ruby-in-gef](https://i.imgur.com/WMxofSs.png)
66
+ ![ruby-in-gef](https://i.imgur.com/W8biCgP.png)
62
67
 
63
68
  # Use gdb in Ruby
64
69
 
@@ -138,6 +143,7 @@ gdb.interact
138
143
  # Installation
139
144
 
140
145
  Available on RubyGems.org!
146
+
141
147
  ```
142
148
  $ gem install gdb
143
149
  ```
@@ -158,5 +164,5 @@ Any feature requests and suggestions are welcome! :grimacing:
158
164
 
159
165
  # Growing up
160
166
 
161
- **gdb-ruby** is under developing, give it a star and [watching](https://github.com/david942j/gdb-ruby/subscription)
167
+ **gdb-ruby** is under developing, give it a star and [watch](https://github.com/david942j/gdb-ruby/subscription)
162
168
  for latest updates!
@@ -1,9 +1,17 @@
1
1
  #!/usr/bin/env ruby
2
2
 
3
- require 'gdb'
4
3
  require 'shellwords'
5
4
 
5
+ require 'gdb'
6
+ require 'gdb/util'
7
+
6
8
  argv = Shellwords.join(ARGV)
7
9
  Process.setproctitle('gdb-ruby ' + argv)
8
10
 
11
+ gdb = ::GDB::Util.find_gdb
12
+ if gdb.nil?
13
+ # No gdb installed
14
+ warn('[ERROR] No gdb installed in your system.')
15
+ exit 2
16
+ end
9
17
  GDB::GDB.new(argv).interact
@@ -6,11 +6,16 @@ require 'readline'
6
6
  require 'gdb/eval_context'
7
7
  require 'gdb/gdb_error'
8
8
  require 'gdb/tube/tube'
9
+ require 'gdb/util'
9
10
 
10
11
  module GDB
11
12
  # For launching a gdb process.
13
+ #
14
+ # @!macro [new] gdb_displayed
15
+ # @return [String]
16
+ # Returns what gdb displayed after executing this command.
12
17
  class GDB
13
- # Absolute path to python scripts.
18
+ # Absolute path to the python scripts.
14
19
  SCRIPTS_PATH = File.join(__dir__, 'scripts').freeze
15
20
 
16
21
  # To launch a gdb instance.
@@ -25,8 +30,11 @@ module GDB
25
30
  # gdb = GDB::GDB.new('-q -nh bash')
26
31
  # gdb = GDB::GDB.new('arm.elf', gdb: 'gdb-multiarch')
27
32
  def initialize(arguments, gdb: 'gdb')
33
+ gdb_bin = ::GDB::Util.which(gdb)
34
+ raise Errno::ENOENT, gdb if gdb_bin.nil?
35
+
28
36
  arguments = "--command=#{File.join(SCRIPTS_PATH, 'gdbinit.py')}" + ' ' + arguments # XXX
29
- @tube = spawn(gdb + ' ' + arguments)
37
+ @tube = spawn(gdb_bin + ' ' + arguments)
30
38
  pre = @tube.readuntil('GDBRuby:')
31
39
  @prompt = @tube.readuntil("\n").strip
32
40
  @tube.unget(pre + @tube.readuntil(@prompt))
@@ -37,8 +45,7 @@ module GDB
37
45
  # @param [String] cmd
38
46
  # Command to be executed.
39
47
  #
40
- # @return [String]
41
- # The execution result returned by gdb.
48
+ # @!macro gdb_displayed
42
49
  #
43
50
  # @example
44
51
  # gdb = GDB::GDB.new('bash')
@@ -48,22 +55,24 @@ module GDB
48
55
  # gdb.execute('print $rsi')
49
56
  # #=> "$1 = 0x7fffffffdef8"
50
57
  def execute(cmd)
58
+ # clear tube if not in interactive mode
59
+ @tube.clear unless interacting?
60
+
51
61
  @tube.puts(cmd)
52
62
  @tube.readuntil(@prompt).strip
53
63
  end
54
64
  alias exec execute
55
65
 
56
- # Set break point.
66
+ # Set breakpoints.
57
67
  #
58
- # This method some magic, see parameters or examples.
68
+ # This method does some magic, see examples.
59
69
  #
60
70
  # @param [Integer, String] point
61
71
  # If +Integer+ is given, will be translated as set break point
62
72
  # at address +point+, i.e. equivalent to +break *<integer>+.
63
73
  # If +String+ is given, equivalent to invoke +execute("break <point>")+.
64
74
  #
65
- # @return [String]
66
- # Returns what gdb displayed after set a break point.
75
+ # @!macro gdb_displayed
67
76
  #
68
77
  # @example
69
78
  # gdb = GDB::GDB.new('bash')
@@ -79,13 +88,12 @@ module GDB
79
88
  end
80
89
  alias b break
81
90
 
82
- # Run process.
91
+ # Run the process.
83
92
  #
84
93
  # @param [String] args
85
- # Arguments to pass to run command.
94
+ # Arguments to pass to +run+ command.
86
95
  #
87
- # @return [String]
88
- # Returns what gdb displayed.
96
+ # @!macro gdb_displayed
89
97
  #
90
98
  # @example
91
99
  # gdb = GDB::GDB.new('bash')
@@ -97,7 +105,7 @@ module GDB
97
105
  #
98
106
  # @note
99
107
  # If breakpoints are not set properly and cause gdb hangs,
100
- # this method will hang, too.
108
+ # this method hangs as well.
101
109
  def run(args = '')
102
110
  execute('run ' + args)
103
111
  end
@@ -133,7 +141,7 @@ module GDB
133
141
  end
134
142
  alias code_base text_base
135
143
 
136
- # Is process running?
144
+ # Is the process running?
137
145
  #
138
146
  # Actually judged by if {#pid} returns zero.
139
147
  #
@@ -149,11 +157,43 @@ module GDB
149
157
  # This method implemented by invoking +python print(gdb.selected_inferior().pid)+.
150
158
  #
151
159
  # @return [Integer]
152
- # The pid of process. If process is not running, zero is returned.
160
+ # The pid of the process. If the process is not running, zero is returned.
153
161
  def pid
154
162
  @pid = python_p('gdb.selected_inferior().pid').to_i
155
163
  end
156
164
 
165
+ # Execute +continue+ command.
166
+ #
167
+ # @!macro gdb_displayed
168
+ #
169
+ # @note
170
+ # This method may block the IO if no breakpoints are properly set.
171
+ def continue
172
+ check_alive!
173
+ execute('continue')
174
+ end
175
+
176
+ # Execute +info+ command.
177
+ #
178
+ # @param [String] args
179
+ # Arguments to pass to +info+ command.
180
+ #
181
+ # @!macro gdb_displayed
182
+ #
183
+ # @example
184
+ # gdb = GDB::GDB.new('spec/binaries/amd64.elf')
185
+ # gdb.break('main')
186
+ # gdb.run
187
+ # puts gdb.info('proc stat')
188
+ # # process 32537
189
+ # # cmdline = '/home/gdb-ruby/spec/binaries/amd64.elf'
190
+ # # cwd = '/home/gdb-ruby'
191
+ # # exe = '/home/gdb-ruby/spec/binaries/amd64.elf'
192
+ # gdb.close
193
+ def info(args = '')
194
+ execute('info ' + args)
195
+ end
196
+
157
197
  # Read current process's memory.
158
198
  #
159
199
  # @param [Integer, String] addr
@@ -243,6 +283,7 @@ module GDB
243
283
  #
244
284
  # @param [String] cmd
245
285
  # python command.
286
+ #
246
287
  # @return [String]
247
288
  # Execution result.
248
289
  def python_p(cmd)
@@ -250,10 +291,13 @@ module GDB
250
291
  end
251
292
 
252
293
  # Enter gdb interactive mode.
253
- # Gdb will be closed after interaction.
294
+ # GDB will be closed after interaction.
254
295
  #
255
296
  # @return [void]
256
297
  def interact
298
+ return if interacting?
299
+
300
+ @interacting = true
257
301
  $stdin.raw { @tube.interact(method(:output_hook)) }
258
302
  close
259
303
  end
@@ -263,6 +307,7 @@ module GDB
263
307
  # @return [void]
264
308
  def close
265
309
  return if @tube.closed?
310
+
266
311
  @tube.close
267
312
  Process.wait(@gdb_pid)
268
313
  nil
@@ -271,6 +316,10 @@ module GDB
271
316
 
272
317
  private
273
318
 
319
+ def interacting?
320
+ defined?(@interacting)
321
+ end
322
+
274
323
  # Raise {GDBError} if process is not running.
275
324
  #
276
325
  # @return [void]
@@ -278,11 +327,9 @@ module GDB
278
327
  raise GDBError, 'Process is not running' unless alive?
279
328
  end
280
329
 
281
- TIOCSWINSZ = 0x5414
282
-
283
330
  def spawn(cmd)
284
331
  output, input, @gdb_pid = PTY.spawn(cmd)
285
- output.ioctl(TIOCSWINSZ, [*IO.console.winsize, 0, 0].pack('S*'))
332
+ IO.console && output.winsize = IO.console.winsize
286
333
  ::GDB::Tube::Tube.new(input, output)
287
334
  end
288
335
 
@@ -290,15 +337,25 @@ module GDB
290
337
 
291
338
  # @param [String] output
292
339
  #
293
- # @return [String]
340
+ # @yieldparam [String] output
341
+ # @yieldreturn [void]
342
+ #
343
+ # @return [void]
294
344
  def output_hook(output)
295
345
  idx = output.index(COMMAND_PREFIX)
296
346
  return yield output.gsub(@prompt, '') if idx.nil?
347
+
297
348
  yield output.slice!(0, idx)
298
349
  cmd, args = output.slice(COMMAND_PREFIX.size..-1).split(' ', 2)
299
350
  # only support ruby and pry now.
300
- return yield output unless %w[ruby pry].include?(cmd)
301
- args = 'send(:invoke_pry)' if cmd == 'pry'
351
+ return yield output unless %w[ruby pry rsource].include?(cmd)
352
+
353
+ args = case cmd
354
+ when 'pry' then '__send__(:invoke_pry)'
355
+ when 'rsource' then File.read(File.expand_path(args.strip))
356
+ else args
357
+ end
358
+ args = '__send__(:invoke_pry)' if cmd == 'pry'
302
359
  # gdb by default set tty
303
360
  # hack it
304
361
  `stty opost onlcr`
@@ -313,7 +370,7 @@ module GDB
313
370
  end
314
371
 
315
372
  def eval_context
316
- @context ||= ::GDB::EvalContext.new(self)
373
+ @eval_context ||= ::GDB::EvalContext.new(self)
317
374
  end
318
375
  end
319
376
  end
@@ -36,7 +36,7 @@ class GDBRuby():
36
36
  return self.gdbruby_prompt + org
37
37
 
38
38
  def _gen_prompt(self):
39
- rnd = ''.join([random.choice(string.ascii_lowercase) for i in range(8)])
39
+ rnd = ''.join([random.choice(string.ascii_lowercase) for i in range(20)])
40
40
  return '(gdb-ruby-%s)' % rnd
41
41
 
42
42
  __commands__ = []
@@ -99,6 +99,17 @@ Example:
99
99
  """
100
100
  _cmdline_ = 'pry'
101
101
 
102
+ @register_command
103
+ class RSourceCommand():
104
+ _doc_ = """Source a Ruby script.
105
+
106
+ Syntax: rsource <file>
107
+
108
+ Example:
109
+ rsource ~/.gdbinit.rb
110
+ """
111
+ _cmdline_ = 'rsource'
112
+
102
113
 
103
114
  if not 'gdbruby' in globals():
104
115
  [GDBRubyCommand(c) for c in __commands__]
@@ -19,6 +19,7 @@ module GDB
19
19
  def <<(str)
20
20
  str = str.to_s.dup
21
21
  return self if str.empty?
22
+
22
23
  @data << str
23
24
  @size += str.size
24
25
  self
@@ -66,6 +67,7 @@ module GDB
66
67
  # @return [void]
67
68
  def unshift(str)
68
69
  return if str.nil? || str.empty?
70
+
69
71
  @data.unshift(str)
70
72
  @size += str.size
71
73
  end
@@ -32,6 +32,15 @@ module GDB
32
32
  @buffer.get(n)
33
33
  end
34
34
 
35
+ # Clear received data.
36
+ #
37
+ # @return [String]
38
+ # The data cleared.
39
+ # An empty string is returned if the buffer is already empty.
40
+ def clear
41
+ @buffer.get
42
+ end
43
+
35
44
  # Receive from +io+ until string +str+ appears.
36
45
  #
37
46
  # @param [String] str
@@ -63,8 +72,10 @@ module GDB
63
72
  #
64
73
  # @return [void]
65
74
  def puts(data)
75
+ return data.split("\n").each(&method(:puts)) if data.strip.include?("\n")
76
+
66
77
  @in.puts(data)
67
- readuntil(data)
78
+ readuntil("\n")
68
79
  end
69
80
 
70
81
  # Enter interactive mode.
@@ -83,11 +94,12 @@ module GDB
83
94
  io, = IO.select([$stdin, @out])
84
95
  @in.write($stdin.readpartial(READ_SIZE)) if io.include?($stdin)
85
96
  next unless io.include?(@out)
97
+
86
98
  begin
87
99
  recv = @out.readpartial(READ_SIZE)
88
100
  output_hook.call(recv) { |str| $stdout.write(str) }
89
101
  @out.ungetc(@buffer.get) unless @buffer.empty?
90
- rescue Errno::EIO
102
+ rescue Errno::EIO, EOFError
91
103
  break
92
104
  end
93
105
  end
@@ -0,0 +1,35 @@
1
+ module GDB
2
+ # Defines utility methods.
3
+ module Util
4
+ module_function
5
+
6
+ # Cross-platform way of finding an executable in the $PATH.
7
+ #
8
+ # @param [String] cmd
9
+ # @return [String?]
10
+ # @example
11
+ # which('ruby')
12
+ # #=> "/usr/bin/ruby"
13
+ def which(cmd)
14
+ exts = ENV['PATHEXT'] ? ENV['PATHEXT'].split(';') : ['']
15
+ ENV['PATH'].split(File::PATH_SEPARATOR).each do |path|
16
+ exts.each do |ext|
17
+ exe = File.join(path, "#{cmd}#{ext}")
18
+ return exe if File.executable?(exe) && !File.directory?(exe)
19
+ end
20
+ end
21
+ nil
22
+ end
23
+
24
+ # The name of gdb could be:
25
+ # - gdb
26
+ # - ggdb (macOS)
27
+ # @return [String?]
28
+ # @example
29
+ # find_gdb
30
+ # #=> 'gdb'
31
+ def find_gdb
32
+ %w[gdb ggdb].find { |n| which(n) }
33
+ end
34
+ end
35
+ end
@@ -1,4 +1,4 @@
1
1
  module GDB
2
2
  # The current version of GDB.
3
- VERSION = '0.3.0'.freeze
3
+ VERSION = '1.0.0'.freeze
4
4
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: gdb
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.0
4
+ version: 1.0.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - david942j
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2018-01-04 00:00:00.000000000 Z
11
+ date: 2019-02-10 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: pry
@@ -30,84 +30,84 @@ dependencies:
30
30
  requirements:
31
31
  - - "~>"
32
32
  - !ruby/object:Gem::Version
33
- version: 0.1.0
33
+ version: 0.1.1
34
34
  type: :runtime
35
35
  prerelease: false
36
36
  version_requirements: !ruby/object:Gem::Requirement
37
37
  requirements:
38
38
  - - "~>"
39
39
  - !ruby/object:Gem::Version
40
- version: 0.1.0
40
+ version: 0.1.1
41
41
  - !ruby/object:Gem::Dependency
42
- name: codeclimate-test-reporter
42
+ name: rake
43
43
  requirement: !ruby/object:Gem::Requirement
44
44
  requirements:
45
45
  - - "~>"
46
46
  - !ruby/object:Gem::Version
47
- version: '0.6'
47
+ version: '12.3'
48
48
  type: :development
49
49
  prerelease: false
50
50
  version_requirements: !ruby/object:Gem::Requirement
51
51
  requirements:
52
52
  - - "~>"
53
53
  - !ruby/object:Gem::Version
54
- version: '0.6'
54
+ version: '12.3'
55
55
  - !ruby/object:Gem::Dependency
56
- name: rake
56
+ name: rspec
57
57
  requirement: !ruby/object:Gem::Requirement
58
58
  requirements:
59
59
  - - "~>"
60
60
  - !ruby/object:Gem::Version
61
- version: '12.3'
61
+ version: '3.8'
62
62
  type: :development
63
63
  prerelease: false
64
64
  version_requirements: !ruby/object:Gem::Requirement
65
65
  requirements:
66
66
  - - "~>"
67
67
  - !ruby/object:Gem::Version
68
- version: '12.3'
68
+ version: '3.8'
69
69
  - !ruby/object:Gem::Dependency
70
- name: rspec
70
+ name: rubocop
71
71
  requirement: !ruby/object:Gem::Requirement
72
72
  requirements:
73
73
  - - "~>"
74
74
  - !ruby/object:Gem::Version
75
- version: '3.7'
75
+ version: '0.60'
76
76
  type: :development
77
77
  prerelease: false
78
78
  version_requirements: !ruby/object:Gem::Requirement
79
79
  requirements:
80
80
  - - "~>"
81
81
  - !ruby/object:Gem::Version
82
- version: '3.7'
82
+ version: '0.60'
83
83
  - !ruby/object:Gem::Dependency
84
- name: rubocop
84
+ name: simplecov
85
85
  requirement: !ruby/object:Gem::Requirement
86
86
  requirements:
87
87
  - - "~>"
88
88
  - !ruby/object:Gem::Version
89
- version: '0.52'
89
+ version: '0.15'
90
90
  type: :development
91
91
  prerelease: false
92
92
  version_requirements: !ruby/object:Gem::Requirement
93
93
  requirements:
94
94
  - - "~>"
95
95
  - !ruby/object:Gem::Version
96
- version: '0.52'
96
+ version: '0.15'
97
97
  - !ruby/object:Gem::Dependency
98
- name: simplecov
98
+ name: tty-platform
99
99
  requirement: !ruby/object:Gem::Requirement
100
100
  requirements:
101
101
  - - "~>"
102
102
  - !ruby/object:Gem::Version
103
- version: '0.15'
103
+ version: '0.1'
104
104
  type: :development
105
105
  prerelease: false
106
106
  version_requirements: !ruby/object:Gem::Requirement
107
107
  requirements:
108
108
  - - "~>"
109
109
  - !ruby/object:Gem::Version
110
- version: '0.15'
110
+ version: '0.1'
111
111
  - !ruby/object:Gem::Dependency
112
112
  name: yard
113
113
  requirement: !ruby/object:Gem::Requirement
@@ -122,7 +122,7 @@ dependencies:
122
122
  - - "~>"
123
123
  - !ruby/object:Gem::Version
124
124
  version: '0.9'
125
- description: 'It''s time for Ruby lovers to use Ruby in gdb, and... gdb in Ruby!
125
+ description: 'It''s time for Ruby lovers to use Ruby in gdb, and gdb in Ruby!
126
126
 
127
127
  '
128
128
  email:
@@ -141,6 +141,7 @@ files:
141
141
  - lib/gdb/scripts/gdbinit.py
142
142
  - lib/gdb/tube/buffer.rb
143
143
  - lib/gdb/tube/tube.rb
144
+ - lib/gdb/util.rb
144
145
  - lib/gdb/version.rb
145
146
  homepage: https://github.com/david942j/gdb-ruby
146
147
  licenses:
@@ -161,8 +162,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
161
162
  - !ruby/object:Gem::Version
162
163
  version: '0'
163
164
  requirements: []
164
- rubyforge_project:
165
- rubygems_version: 2.6.14
165
+ rubygems_version: 3.0.2
166
166
  signing_key:
167
167
  specification_version: 4
168
168
  summary: GDB Ruby-binding and Ruby command in GDB