Topaz is currently the primary debugging tool for use with MagLev. Most of this document describes debugging with Topaz.
An alternate approach is to use GemTools, written in Squeak, for debugging. See debug from smalltalk
Run maglev with the -d
option to run in debug mode. Uncaught exceptions will land you in the Topaz prompt, where you can examine the stack etc. Here is a sample file:
$ cat debug_example.rb def raise_exception raise "Sample exception" end def another_method raise_exception end another_method
To debug, invoke MagLev with the -d
option:
$ maglev-ruby -d debug_example.rb _____________________________________________________________________________ | Configuration Files | | | | System File: /Users/pmclain/projects/maglev/git/etc/system.conf | | | | Executable File: /Users/pmclain/tmp/gem.conf | | Warning: File not found (errno=2,ENOENT, The file or directory specified cannot | be found) | | using defaults. | |_____________________________________________________________________________| (vmGc spaceSizes: eden init 2000K max 28128K , survivor init 336K max 4688K, vmGc old max 112496K, code max 30000K, perm max 15000K, pom 10 * 12504K = 125040K, vmGc remSet 2880K, meSpace max 143384K oopMapSize 1048576 ) _____________________________________________________________________________ | GemStone/S64 Object-Oriented Data Management System | | Copyright (C) GemStone Systems, Inc. 1986-2008. | | All rights reserved. | | covered by Patent Number 6,567,905 Generational Garbage Collector. | +-----------------------------------------------------------------------------+ | PROGRAM: topaz, Linear GemStone Interface (Linked Session) | | VERSION: 3.0.0, Tue Dec 2 17:07:56 2008 pmclain private build | | BUILD: 64bit-20633-PRIVATE | | BUILT FOR: Darwin (Mac) | | MODE: 64 bit | | RUNNING ON: 2-CPU cairo.gemstone.com i386 (Darwin 9.5.0 ) 2400MHz MacBook4,1| | 2048MB | | PROCESS ID: 16567 DATE: 12/04/08 18:01:15 PST | | USER IDS: REAL=pmclain (650) EFFECTIVE=pmclain (650) | |_____________________________________________________________________________| [Info]: LNK client/gem GCI levels = 801/801 ----------------------------------------------------- GemStone: Error Nonfatal Error, 'Sample exception' Error Category: 231169 [GemStone] Number: 2023 Arg Count: 1 Context : 91077889 Arg 1: [91078913 sz:16 cls: 74753 String] Sample exception topaz 1>
We are now at the Topaz prompt and can look at the stack with the where
command (abbreviated wh
):
topaz 1> wh ==> 1 Exception >> _pass:with: (envId 0) @8 line 23 [methId 2266369] 2 Exception >> pass (envId 0) @2 line 14 [methId 4062465] 3 [] in RubyFile >> load (envId 0) @4 line 18 [methId 53753089] 4 Exception >> _executeHandler: (envId 0) @2 line 7 [methId 2186753] 5 Exception >> _pass:with: (envId 0) @4 line 18 [methId 2266369] 6 Exception >> pass (envId 0) @2 line 14 [methId 4062465] 7 [] in RubyCompiler >> compileFileNamed:do: (envId 0) @4 line 26 [methId 52167937] 8 Exception >> _executeHandler: (envId 0) @2 line 7 [methId 2186753] 9 Exception >> signal (envId 0) @1 line 1 [methId 2180865] 10 Exception >> signal: (envId 0) @3 line 7 [methId 2161921] 11 Exception class >> signal: (envId 0) @3 line 4 [methId 2045953] 12 Object >> raise:: (envId 1) @2 line 2 [methId 72786689] 13 String >> signal (envId 1) @2 line 2 [methId 72575745] 14 String >> signal: (envId 1) @2 line 1 [methId 72632321] 15 Object >> raise: (envId 1) @2 line 2 [methId 72742913] 16 Object >> raise_exception (envId 1) @2 line 2 [methId 91096065] 17 Object >> another_method (envId 1) @2 line 2 [methId 91097089] 18 Object >> _compileFile (envId 1) @6 line 9 [methId 91098113] 19 [] in RubyCompiler >> compileFileNamed:do: (envId 0) @3 line 10 [methId 52139009] 20 [] in RubyCompiler >> loadFileNamed: (envId 0) @2 line 2 [methId 51476481] 21 [] in RubyCompiler >> compileFileNamed:do: (envId 0) @3 line 10 [methId 52141569] ... topaz 1>
To see the location in the ruby source file, we’ll look at stack frame 18, using the frame
command (abbreviated fr
):
topaz 1> fr 18 18 Object >> _compileFile (envId 1) @6 line 9 [methId 91098113] receiver [91094785 sz:0 cls: 72193 Object] anObject topaz 1>
And then list
the current frame’s source:
topaz 1> list def raise_exception raise "Sample exception" end def another_method raise_exception end another_method * ^6 ******* # method starts at line 1 of file /Users/pmclain/tmp/debug_example.rb topaz 1>
We can move down the stack with the down
command:
topaz 1> down 17 Object >> another_method (envId 1) @2 line 2 [methId 91097089] receiver [91094785 sz:0 cls: 72193 Object] anObject topaz 1>
And we can focus in on the current section of the stack frame by using where
with a parameter:
topaz 1> wh 10 17 Object >> another_method (envId 1) @2 line 2 [methId 91097089] 18 Object >> _compileFile (envId 1) @6 line 9 [methId 91098113] 19 [] in RubyCompiler >> compileFileNamed:do: (envId 0) @3 line 10 [methId 52139009] 20 [] in RubyCompiler >> loadFileNamed: (envId 0) @2 line 2 [methId 51476481] 21 [] in RubyCompiler >> compileFileNamed:do: (envId 0) @3 line 10 [methId 52141569] 22 ExecBlock >> on:do: (envId 0) @2 line 53 [methId 4329729] 23 [] in RubyCompiler >> compileFileNamed:do: (envId 0) @6 line 11 [methId 52148225] 24 ExecBlock >> ensure: (envId 0) @2 line 10 [methId 4310273] 25 [] in RubyCompiler >> compileFileNamed:do: (envId 0) @12 line 19 [methId 52168193] 26 ExecBlock >> on:do: (envId 0) @2 line 53 [methId 4329729] topaz 1> list def another_method raise_exception * ^2 ******* # method starts at line 5 of file /Users/pmclain/tmp/debug_example.rb topaz 1> down 16 Object >> raise_exception (envId 1) @2 line 2 [methId 91096065] receiver [91094785 sz:0 cls: 72193 Object] anObject topaz 1> list def raise_exception raise "Sample exception" * ^2 ******* # method starts at line 1 of file /Users/pmclain/tmp/debug_example.rb topaz 1>
Etc. Read the Topaz manual for all the details.
Often, it is convenient to debug when catching a specific exception. You can install a debug handler in your ruby code (Exception.install_debug_block
) that will be called for all ruby exceptions. You can then choose to drop down into topaz:
# Run this file with "maglev-ruby -d" to see how the debug block interacts # with the topaz debugger. # This debug block will catch all ruby exceptions raised by the program. # This example prints all the exceptions, and drops into topaz for # RuntimeErrors Exception.install_debug_block do |e| puts "-- Caught #{e}" # Print all exceptions case e when RuntimeError # drop into topaz if we get a RuntimeError nil.pause end # Since we did not re-raise the exception, the program will continue. end def raise_a_ruckus(exception) begin puts "-- About to raise #{exception}" raise exception rescue Exception # Rescue, just so we don't terminate end end # Raise some random exceptions, to see how [IOError.new, RuntimeError.new, NotImplementedError.new].each do |e| raise_a_ruckus(e) end
If you run the script above, with maglev-ruby -d
, then you’ll see each of the exceptions printed on stdout, and then you’ll be dropped into the topaz debugger when the RuntimeError
is raised. You can examine the stack, and then continue
, and the rest of the program will complete.
If you run in debug mode, you can pause and get the Topaz prompt using nil.pause
(or anything_else.pause
) in your ruby code:
nil.pause if rand(10) == 7 # if you're lucky, pause 10% of the time
Then you can continue with continue
:
Sometimes it is helpful to add debugging code in the src/kernel/* code. Since this is bootstrap code, you may want to turn off debugging code until after the bootstrap phase (since not all objects/methods are available). You can use the $MaglevInBootstrap
global variable to test the bootstrap state.
E.g., here we instrument Module#include to print out module inclusion:
# src/kernel/Module.rb # ... def include(*modules) # this variant gets bridge methods unless $MaglevInBootstrap # puts not available during bootstrap puts "======= (Module) #{self}.include(#{modules.inspect})" end modules.reverse.each do |a_module| a_module.append_features(self) a_module.included(self) end self end def include(a_module) # variant needed for bootstrap unless $MaglevInBootstrap # puts not available during bootstrap puts "======= (Module) #{self}.include(#{a_module})" end a_module.append_features(self) a_module.included(self) self end
We then reload primitives (to enable our debug code; rake maglev:reload_prims). When we run a program:
... ======= (Module) Gem::Version.include(Comparable) ======= (Module) Gem::Requirement.include(Comparable) ======= (Module) FileUtils.include(FileUtils::StreamUtils_) ======= (Module) FileUtils::Entry_.include(FileUtils::StreamUtils_) ======= (Module) FileUtils::Verbose.include(FileUtils) ======= (Module) FileUtils::NoWrite.include(FileUtils) ======= (Module) FileUtils::DryRun.include(FileUtils) ...
Most of the scripts and rake tasks are based on Topaz scripts and use the initialization files in $MAGLEV_HOME/etc/*
to log into Topaz and setup the MagLev Ruby environment.
The Topaz Programming Environment manual contains documentation on using Topaz with GemStone/S. However, that version of the manual is for the 2.3 version of GemStone/S, and MagLev runs on the 3.0 version of GemStone/S. GemStone/S 3.0 has not yet been publicly released. When 3.0 documentation becomes available, GemStone will update the link.
Topaz version 3.0 has online help, which includes the Ruby-specific commands that are not covered in the manual:
topaz 1> help TOPAZ - a programmer's interface to the GemStone system For an overview of Topaz, see the "Overview" help topic. Help is available for: HELP ABORT BEGIN BREAK C CATEGORY: CLASSMETHOD: COMMIT CONTINUE DEFINE DISPLAY DISASSEM DOIT DOWN [<anInteger>] EDIT ERRORCOUNT EXIT [<status>] EXITIFNOERROR EXPECTBUG EXPECTERROR EXPECTVALUE FILEOUT FRAME [<anInteger>] GCITRACE HIERARCHY IFERR IFERR_LIST IFERR_CLEAR IFERROR INPUT LEVEL LIMIT LIST LISTW LOADUA LOGIN LOGOUT LOOKUP METHOD: OBJECT OBJ1 OBJ2 OBJ1Z OBJ2Z Object_Specification_Formats OMIT OPAL OUTPUT PAUSEFORDEBUG PRINTIT PROTECTMETHODS UNPROTECTMETHODS RELEASEALL REMARK REMOVEALLMETHODS REMOVEALLCLASSMETHODS RUBYHIERARCHY RUBYLIST RUBYLOOKUP RUBYMETHOD RUBYCLASSMETHOD RUBYRUN RUN RUNENV INTERP INTERPENV NBRUN NBSTEP NBRESULT SEND SENDENV SET SHELL SPAWN STACK STACKWAITFORDEBUG STK STATUS STEP TEMPORARY THREAD THREADS TIME TOPAZPAUSEFORDEBUG TOPAZWAITFORDEBUG QUIT [<status>] UP [<anInteger>] WHERE <various options> Overview Blanks Break_Handling_in_Topaz Debugging_Smalltalk_DB_Code Initialization Local_Variables Multiple_Sessions Specifying_Objects Strings Linked_vs_RPC Network_Resource_String Topic?
press RETURN to continue…
exit
-
Exit Topaz
help
-
Get help
where
-
List current stack
where nn
-
Show only
nn
frames starting at current frame. where string
-
Show only frames containing
string
.string
cannot start with +, -, a decimal digit, or contain whitespace. Search is case-sensitive. Current frame is set to first frame matched by the search. where 'string'
-
Same as above, but
'string'
may contain digits and whitespace. frame
-
Show information about the current frame
list
-
List source code for the current frame
listw
-
List a window of source code around the current IP location. Useful if you have a long method, and just want to concentrate on the current location.
down
-
Move down one stack frame.
up
-
Move up one stack frame.
fr 5
-
See reason for failure where the “pause” for DEBUG_SPEC is
wh _co
-
Search for _compileFile frames
l
-
Window listing for first _compileFile frame
break method Foo bar @ 2
-
Break at step point 2 in Foo#bar.
continue
-
Continue after a break point.
set class Foo
-
Set the current class to
Foo
list step method: bar
-
List source code with possible break points shown. Use this to figure out which numeric parameter to pass via
@ nn
for the break method above. list breaks
-
List current break points
list breaks method: foo
-
List
display oops
-
Turn on display of object ids (necessary for examining variables)
obj1 @ NNN
-
Inspect the object with id
NNN
, one level deep obj2 @ NNN
-
Inspect the object with id
NNN
, two levels deep level N
-
Set the default display level to
N
.
You can get a Topaz prompt via rake (but you will not yet be logged into the VM).
$ cd $MAGLEV_HOME $ rake dev:topaz (in /Users/pmclain/maglev) topaz 1>
Type exit
to leave Topaz