These facilities allow placing a break/trace on any method - even
primitives or methods of which no source code is available, can be
traced and/or breakpointed.
Also, the usual self halt
needs a recompilation of the inspected
method, while the facilities described below do not modify or recompile
any existing method. (thus leaving machine-code methods untouched -
instead of converting them into slower interpreted bytecode).
Finally, in contrast to placing a halt
into your methods,
breakpoints will not be reported in the "changes"
file
and removing breakpoints is much easier (simply evaluate 'MessageTracer cleanup').
(No longer leave any unintended halts in your program ;-)
Tracing is possible from both an instance or method viewpoint.
Currently, only trace and breakpoints on methods are supported by
the user interface; other facilities have to be enabled "manually",
by evaluating corresponding expressions in a workspace.
(late news: the new inspector allows traces/traps to be enabled too).
MessageTracer trap:anObject selector:aSelector
which arranges that the debugger is entered, whenever aSelector
is sent to anObject.
You can also specify a list of selectors,
as in:
or arrange for all messages (which are understood) with:
MessageTracer trap:anObject selectors:aCollectionOfSelectors
MessageTracer trapAll:anObject
Once in the debugger, use "step", "send" or
"continue" to resume execution in the breakpointed method.
The breakpoint is removed by:
to remove traps on individual selectors,
MessageTracer untrap:anObject selector:aSelector
or:
to remove all traps on anObject
(if you have traps on other selectors that you want to get rid of).
MessageTracer untrap:anObject
MessageTracer trace:anObject selector:aSelector
this arranges that a trace message is printed to the standard error output
(Stderr
),
both upon entry and exit to/from the method.
In analogy to traps, there are also:
to install tracers for more selectors, and:
MessageTracer trace:anObject selectors:aCollectionOfSelectors
to install tracers on all implemented selectors.
MessageTracer traceAll:anObject
The trace is removed with:
or:
MessageTracer untrace:anObject selector:aSelector
to remove all traces of anObject.
MessageTracer untrace:anObject
MessageTracer traceSender:anObject selector:aSelector
arranges that the sender is output the standard error, whenever some message is sent.
Use untrace:selector:
or untrace
(as described above), to remove the trace.
|arr|
arr := #(1 2 3 4 5).
MessageTracer traceSender:arr selector:#at:.
arr collect:[:e | ]
in contrast to:
|arr|
arr := #(1 2 3 4 5).
MessageTracer trace:arr selector:#at:.
arr collect:[:e | ]
MessageTracer
wrap:anObject
selector:aSelector
onEntry:entryBlock
onExit:exitBlock
where anObject and aSelector are as above,
entryBlock is a one-argument block to be evaluated on entry into the method.
It will get the current context passed as argument.
This allows conditional breakpoints or conditional tracing.
It can also be used
to implement pre- and post conditions a la Eiffel while debugging.
For example, you want to trace any sent to an object with a specific argument,
use something like:
(in the above example, the first argument is checked for being greater than 5;
if so, the debugger is entered)
|p|
p := Point new.
MessageTracer wrap:p selector:#x:
onEntry:[:con |
(con args at:1) > 5 ifTrue:[
Debugger
enter:con
withMessage:'hit breakPoint; arg > 5'
]
]
onExit:nil.
p x:4. "nothing happens"
p x:-1. "nothing happens."
p x:10. "bummm"
Postcondition checking can be implemented with this mechanism.
For example if you want to
check the range of some instance variable after every send of some selector,
use:
(in the above, replace getInstVar by an appropriate access selector
for the instance variable to be checked)
MessageTracer
wrap:someObject
selector:someSelector
onEntry:nil
onExit:[:con :retVal |
(con receiver getInstVar between:min and:max) ifFalse:[
Debugger
enter:con
withMessage:'postcondition violated'
]
]
MessageTracer
trapModificationsIn:anObject
and arranges that the debugger is entered, whenever any instance variable is changed in
anObject.
You can also specify a filter block, which gets the old value and new value of the object as arguments:
This kind of breakpoint is installed by:
arranges for a debugger to be entered if some instance variable is changed in anObject
AND the filterblock returns true.
Example:
MessageTracer
trapModificationsIn:anObject
filter:[:old :new | conditionForBreak]
Example (only trap modifications of x):
|p1 p2|
'creating points' printNL.
p1 := Point x:1 y:1. p2 := Point x:2 y:2.
'setting breakpoints' printNL.
MessageTracer trapModificationsIn:p1.
"p2 has no breakpoints set: - nothing happens"
'p2 x:' printNL. p2 x:22.
'p2 x' printNL. p2 x.
"now let it trap ..."
'p1 x:' printNL. p1 x:5.
'p1 x' printNL. p1 x.
'remove breakpoints' printNL.
MessageTracer untrap:p1.
"nothing happens now ..."
'p1 x:' printNL. p1 x:6.
|p|
'creating point' printNL.
p := Point x:1 y:1.
'setting breakpoints' printNL.
MessageTracer trapModificationsIn:p filter:[:old :new | old x ~~ new x].
"nothing happens for y"
'p y:' printNL. p y:22.
"now let it trap ..."
'p x:' printNL. p x:5.
'remove breakpoint' printNL.
MessageTracer untrap:p.
"nothing happens now ..."
'p x:' printNL. p x:6.
MessageTracer trapMethod:aMethod
this arranges that the debugger is entered, whenever aMethod
is about to be executed
(no matter, if for instances of the implementing class, or of any subclass).
The browser has a convenient menu function to set this kind of breakpoint, see below or the SystemBrowser documentation.
MessageTracer traceMethod:aMethod
Example:
MessageTracer traceMethod:(Integer compiledMethodAt:#factorial).
5 factorial.
MessageTracer untraceMethod:(Integer compiledMethodAt:#factorial)
MessageTracer traceMethodSender:aMethod
Example:
MessageTracer traceMethodSender:(Integer compiledMethodAt:#factorial).
5 factorial.
MessageTracer untraceMethod:(Integer compiledMethodAt:#factorial)
MessageTracer
wrapMethod:aMethod
onEntry:entryBlock
onExit:exitBlock
Example 1: catching a specific factorial invocation:
cleanup with:
MessageTracer wrapMethod:(Integer compiledMethodAt:#factorial)
onEntry:[:con |
con receiver == 3 ifTrue:[
Debugger
enter:con
withMessage:'3 factorial encountered'
]
]
onExit:[:con :val |
'leaving ' errorPrint.
con errorPrint. ' -> ' errorPrint.
val errorPrintNL.
].
5 factorial.
Example 2: tracing file-open of specific files:
MessageTracer unwrapMethod:(Integer compiledMethodAt:#factorial)
cleanup with:
MessageTracer wrapMethod:(FileStream compiledMethodAt:#openWithMode:)
onEntry:[:con |
(con receiver pathName endsWith:'.st') ifTrue:[
'opening ' errorPrint.
con receiver pathName errorPrintNL
]
]
onExit:nil.
"now, play around with systembrowser (look at methods source-code)
or look at files with the filebrowser ...
And watch the output in the xterm window."
Example 3: traping access to a specific file:
MessageTracer unwrapMethod:(FileStream compiledMethodAt:#openWithMode:)
cleanup with:
MessageTracer wrapMethod:(FileStream compiledMethodAt:#openWithMode:)
onEntry:[:con |
(con receiver name = 'test') ifTrue:[
Debugger
enter:con
withMessage:'opening file named ''test'''
]
]
onExit:nil.
"now, create a file named 'test' with the filebrowser ..."
MessageTracer unwrapMethod:(FileStream compiledMethodAt:#openWithMode:)
MessageTracer cleanup
to remove all trace points in the system,
MessageTracer untraceAllMethods
to remove all method traces.
Also, the Debuggers popupMenu (in the walkback list) offers an
untrace all entry, which does the above cleanup.
You may need this in case you get trapped by a persistent trap ;-)
example:
see whats sent to a StandardSystemView during its
startup ...
|v|
v := StandardSystemView new.
v inspect.
... now, set a trap ...
...
... then evaluate: 'self open'
... in the inspector
Float +
:
Smalltalk at:#MyCount put:0 .
MessageTracer
wrapMethod:(Float compiledMethodAt:#+)
onEntry:[:con | MyCount := MyCount + 1]
onExit:[:con :ret | ].
Try a few adds applied to a float, and have a look at the globals
count.
MessageTracer
unwrapMethod:(Float compiledMethodAt:#+)
or, simply:
MessageTracer
unwrapAllMethods
and don't forget to remove the global counter:
Smalltalk removeKey:#MyCount
In the above example, you will notice that a lot of float
additions happen without your explicit calls (for example, in
the window handling code). Thus, you may prefer to try it
with some other method (for example, sin
).
For example, placing a trace on Context>>sender
will lead to this situation,
because that method is indirectly called by the trace-code.
Thus the process will run into recursion overflow problems, and start up
a debugger; this debugger itself will run into the same problem, since during
its startup, it reads the context chain using Context>>sender
.
Finally, ST/X's runtime system will kill the (lightweight) process.
If you are lucky, the system is in a condition to enter the
MiniDebugger.
In this case, try to repair things with:
Notice: the Minidebugger of the newest release has a special command for this:
MiniDebugger> I <- I-command; gets you into a
line-by-line expression evaluator
MessageTracer unwrapAllMethods <- remove all breakpoints
<- empty line to leave the
expression evaluator
MiniDebugger> a <- abort
MiniDebugger> U <- U-command; unwrap all methods
Example:
ATTENTION: save your work before evaluating this
MessageTracer traceMethodSender:(Context compiledMethodAt:#sender).
thisContext sender.
MessageTracer untraceMethod:(Context compiledMethodAt:#sender).
after the above, you should repair things, by opening a workspace,
and evaluating:
MessageTracer unwrapAllmethods
(this will remove the tracers, and allow normal use of the debugger again).
Critical methods are all context methods, string concatenation and printing
methods.
To trace these critical methods, use the instance debugging facilities
described above, instead of method debugging - if possible
(it is not possible for context-related methods though).
It is difficult to offer a satisfying solution to this problem - the obvious fix (to simply check for recursion in the wrapper method) would prevent the tracing or breakpointing of recursive methods, such as the factorial-example above.
Future versions may provide better solutions - for now, be warned.
Copyright © 1995 Claus Gittinger Development & Consulting
<cg@exept.de>