# Unit Tracer reference

Unlike Perl, in &Cpp; the tracer is defined by inheriting from the class Unit::Tracer. The base class inherits from the Mtarget, and in the subclass you need at the minimum to define your virtual method:

    virtual void execute(Unit *unit, const Label *label, const Label
    	*fromLabel, Rowop *rop, TracerWhen when);

It gets called at the exactly same points as the Perl tracer (the &Cpp; part of the UnitTracerPerl forwards the calls to the Perl level). The arguments are also the same as described in the Perl docs:

-     unit

  is the unit being traced.

-     label

  is the current label being traced.

-     fromLabel

  is the parent label in the chaining (would be

      NULL

  if the current label is called directly, without chaining from anything).

-     rop

  is the current row operation.

-     when

  is a constant showing the point when the tracer is being called. It's value may be one of

      Unit::TW_BEFORE

  ,

      Unit::TW_AFTER

  ,

      Unit::TW_BEFORE_DRAIN

  ,

      Unit::TW_AFTER_DRAIN

  ,

      Unit::TW_BEFORE_CHAINED

  ,

      Unit::TW_AFTER_CHAINED

  ; the prefix

      TW

  stands for tracer when.

Here is a simple example of a tracer:

    class SampleTracer : public Unit::Tracer
    {
    public:
    	virtual void execute(Unit *unit, const Label *label,
    		const Label *fromLabel, Rowop *rop, Unit::TracerWhen when)
    	{
    		printf("trace %s label '%s' %c\n", Unit::tracerWhenHumanString(when),
    			label->getName().c_str(), Unit::tracerWhenIsBefore(when)? '{' : '}');
    	}
    };

The Unit methods shown are documented in &xrsp;.

The SampleTracer above was just printing the trace right away. Usually a better idea is to save the trace in the tracer object and return it on demand. Unit::Tracer provides the means to do that: an Erref object is used as a buffer, where the data can be added efficiently line-by-line, and later read back. The buffer is defined protected, so that the subclasses can access it but the end users can't:

    protected:
    	Erref buffer_; // buffer for collecting the trace

On each call the tracer's

    execute()

would build a string, and append it to the buffer:

    buffer_->appendMsg(false, traceString);

The public interface for the buffer is:

    virtual Erref getBuffer();
    virtual void clearBuffer();

These methods are virtual to let the subclasses define their own version if they want. The methods do exactly what their names say.

    getBuffer()

returns the reference to the buffer in

    buffer_

, after which the end user has the full access to it.

The pattern of reading the buffer contents works like this:

    string tlog = trace->getBuffer()->print();
    trace->clearBuffer();

The log string can then be printed, or used in any other way.

The clearing of the buffer is done by discarding the old one and allocating a new one. An interesting consequence is that if you call

    getBuffer()

, save the reference, then call

    clearBuffer()

, the original buffer is still available at your saved reference and won't be changed any more, for example:

    Erref buf = trace->getBuffer();
    trace->clearBuffer();
    string tlog = buf->print();

Of course, if a tracer subclass defines its own version of virtual methods, it may change this semantics.

It can be quite useful to include the row being processed into the trace, as the Perl examples show in &xrsp;. The &Cpp; part of Triceps doesn't provide a ready row printer yet but Unit::Tracer provides a way for you to use your own row printer. Even two ways. One is to re-define the public method

    printRow()

:

    virtual void printRow(string &res, const RowType *rt, const Row *row);

The job of this method is to append the information from the row

    row

of the type

    rt

to the result string

    res

. Append, not replace.

The second way is by providing a pointer to a simple C-style function of the type:

    typedef void RowPrinter(string &res, const RowType *rt, const Row *row);

The arguments and functionality are exactly the same as for the method

    printRow()

. The base class implementation of

    printRow()

works by calling this function pointer of it's not NULL. This pointer can be given to the Tracer constructor:

    Tracer(RowPrinter *rp = NULL);

In general, the subclasses should also take this argument and pass it through to the parent class. This allows the separation of the tracer itself and of the row printing: the user becomes capable of specifying any row printer. Since the default row printer is NULL, the row contents doesn't get printed by default.

Triceps provides a couple of stock tracer classes:

StringTracer

  Collects the trace in a buffer, identifying the objects as addresses. This is not exactly easy to read normally but may come useful if you want to analyze a core dump.

StringNameTracer

  Similar but prints the object identification as names. More convenient but prone to the duplicate names used for different objects.

Everything but the constructors of these classes follows the general Tracer interface. The constructors are:

    StringTracer(bool verbose = false, RowPrinter *rp = NULL);
    StringNameTracer(bool verbose = false, RowPrinter *rp = NULL);

The

    verbose

flag enables the tracing at all points, if it's set to

    false

then only the

    TW_BEFORE

points are traced. The argument

    rp

lets the end user supply a row printer function.

The tracing does not have to be used just for tracing. It can also be used as a breakpoint: check in your tracer for an arbitrary condition and stop if it has been met.

There is only one tracer per unit at a time. However if you want, you can implement the chaining in your own tracer (particularly useful if it's a breakpoint tracer): accept a reference to another tracer object, and after doing your own part, call that object's

    execute()

method.

Even though the Tracer class inherits from Mtarget, none of its methods have the synchronization in them. Thus each object must be used in only one thread. If you really want to use your tracer object from multiple threads, define your own subclass and re-implement all the virtual methods in it with synchronization.

Unit
tracing