Instrumenting Programs with Watches

Sometimes accesses to particular memory locations are of interest when instrumenting a program. For example, if we have a global data structure such as a scheduling queue, we might be interested in monitoring its activity. Or, if the program allocates buffers that may be modified through pointers in many parts of the program, it is more straightforward to instrument the buffer's memory locations rather than the code that might access the buffer through pointers.

Watch Example: The Stray Update

Watches are inserted at locations in the memory of an executing program, and implement an interface simulator to that implemented by a probe; the Simulator.Watch interface. This interface is nested inside the Simulator class and looks like this:

Suppose we are trying to track down a bug in our program where a variable is getting set to an incorrect value, but we aren't sure when it is happening, or where in the code. We saw how in the previous section that we can use probes in the program to insert instrumentation code in various places in the code. But this time, we aren't sure which part of the code might be the culprit--it might be a stray pointer, it might be a dark corner of the code someone else wrote. We can't simply insert probes into all possible locations in the code, because that would be extremely tedious, and we can't simply dump every memory access, because we'd have to wade through the output looking for writes to the variable we are interested in. What we need is a watch.

We can write a new Java class that implements the interface and inside its fireBeforeWrite() method, we have access to the instruction doing the update and its address, as well as the value being written. If it is an incorrect or invalid value, we can print a message, log it, and continue. The simulator doesn't lie--all updates to that memory location will go through the watch, and we can catch the offender red-handed!

Just as probes, watches can be as simple or as complex as your application needs. Watches can enable and disable other watches, probes, or events. For example, perhaps we have a global variable that controls the overall state of our program, and we only want to profile the program when it is in one of the states. We can insert a watch on that variable, and when that variable is written to the state that we are interested in, we can enable the rest of the profiling probes and watches. When the variable is written back to another state, we can disable the instrumentation code.

More information: