[Author Prev][Author Next][Thread Prev][Thread Next][Author Index][Thread Index]

[or-cvs] r10626: added ruby gdb-wrapper (in topf/trunk: lib utils)



Author: benedikt
Date: 2007-06-16 20:58:42 -0400 (Sat, 16 Jun 2007)
New Revision: 10626

Added:
   topf/trunk/lib/gdb.rb
   topf/trunk/utils/gdb-test.rb
Log:
added ruby gdb-wrapper

Added: topf/trunk/lib/gdb.rb
===================================================================
--- topf/trunk/lib/gdb.rb	                        (rev 0)
+++ topf/trunk/lib/gdb.rb	2007-06-17 00:58:42 UTC (rev 10626)
@@ -0,0 +1,129 @@
+# gdb.rb -- a simple wrapper around the GDB console to make inspecting
+# ruby processes less painful.
+# 
+# Author: Jamis Buck <jamis@xxxxxxxxxxxxx>
+#
+# This library is in the public domain. You are free to use it, modify
+# it, redistribute it, or ignore it, as you wish, with absolutely no
+# restrictions.
+#
+# The GDB module provides a "raw", low-level, language-independent
+# interface to GDB (GDB::Interface) as well as a higher-level
+# interface specifically for Ruby programs (GDB::Ruby). Theoretically,
+# GDB::Interface could be extended to support other languages
+# than Ruby.
+
+module GDB
+    # The basic interface for wrapping GDB. You could concievably use this
+    # to interace with arbitrary remote processes, though it is best used
+    # by extending it to add platform-specific functionality.
+    class Interface
+        # Creates a new GDB::Interface that connects to process-id and
+        # executable.
+        def initialize(options)
+            prompt = "gdb -q "
+
+            prompt << "-c " << options[:core]             if options[:core]
+            prompt << " " <<   options[:executable]       if options[:executable]
+            prompt << " " <<   options[:pid]              if options[:pid] 
+            prompt << " 2>&1"
+            @gdb = IO.popen(prompt, "r+")
+            read_to_prompt
+        end
+
+        # Returns the (32-bit) integer at the given address
+        def int_at(address)
+            command("x/1dw #{address}").first.split(/:/).last.to_i
+        end
+
+        # Returns the string located at the given address
+        def string_at(address)
+            command("x/1s *#{address}").first.match(/"(.*)"/)[1]
+        end
+
+        # Returns the (8-bit) double precision floating point value at
+        # the given address.
+        def double_at(address)
+            command("x/1fg #{address}").first.split(/:/).last.to_f
+        end
+
+        # Executes the given command and returns the output as an array
+        # of lines. The command must be in GDB's syntax. If $gdb_verbose
+        # is true, this will print the command to STDOUT as well.
+        def command(cmd)
+            puts "(gdb) #{cmd}" if $gdb_verbose
+            @gdb.puts(cmd.strip)
+            read_to_prompt
+        end
+
+        # Call a C function. The +function+ parameter must include any
+        # parameters, all of which must be in valid C syntax. +opts+
+        # should include a <code>:return</code> key, which indicates
+        # what the expected return type of the function is:
+        #
+        # * <code>:int</code>
+        # * <code>:string</code>
+        # * <code>:void</code>
+        #
+        # Currently, the default is <code>:void</code>. The method
+        # returns a value of the expected type.
+        def call(function, opts={})
+            cast = case opts[:return]
+                   when :int       then "int"
+                   when :string    then "char*"
+                   when nil, :void then "void"
+                   else Kernel.raise "unsupported return type #{opts[:return].inspect}"
+                   end
+
+            result = command("call (#{cast})#{function}")
+
+            case opts[:return]
+            when :int
+                if md = result.first.match(/= (-?\d+)/)
+                    md[1].to_i
+                else
+                    Kernel.raise "couldn't match integer result from #{result.inspect}"
+                end
+            when :string then result.first.match(/"(.*)"/)[1]
+            else nil
+            end
+        end
+
+        # Reads all lines emitted by GDB until a recognized prompt is encountered.
+        # Currently recognized prompts are:
+        #
+        # * "(gdb) "
+        # * " >"
+        #
+        # If your version of GDB uses a different kind of prompt, you should
+        # modify this method to include it.
+        #
+        # This method returns the lines read as an array. If $gdb_verbose is
+        # true, the lines will be echoed to STDOUT.
+        def read_to_prompt
+            lines = []
+            line = ""
+            while result = IO.select([@gdb])
+                next if result.empty?
+                c = @gdb.read(1)
+                break if c.nil?
+                line << c
+                break if line == "(gdb) " || line == " >"
+                if line[-1] == ?\n
+                    lines << line
+                    line = ""
+                end
+            end
+            puts lines.map { |l| "> #{l}" } if $gdb_verbose
+            lines
+        end
+
+        # A convenience method for executing GDB commands. Any unrecognized
+        # message is packaged up and invoked by GDB.
+        def method_missing(sym, *args)
+            cmd = sym.to_s
+            cmd << " #{args.join(' ')}" unless args.empty?
+            command(cmd)
+        end
+    end
+end

Added: topf/trunk/utils/gdb-test.rb
===================================================================
--- topf/trunk/utils/gdb-test.rb	                        (rev 0)
+++ topf/trunk/utils/gdb-test.rb	2007-06-17 00:58:42 UTC (rev 10626)
@@ -0,0 +1,20 @@
+require '../lib/gdb'
+
+# simple testcode that prints registers and backtrace of a given core file
+
+raise "please supply core file as argument" if !ARGV[0]
+
+options = {
+    :core => ARGV[0]
+}
+
+gdb = GDB::Interface.new(options)
+
+gdb.info("registers").each { |line| 
+    puts line
+}
+gdb.backtrace.each {|line|
+    puts line
+}
+
+gdb.quit