gdb 0.3.0 → 1.0.0

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