Instrumenting your code with X-Trace

This page introduces the X-Trace logging-like API. We use the Java API as an example, but the C++ API is similar.

Introduction

The X-Trace logging API makes instrumenting code with X-Trace as easy as using a logging API in the common case, while allowing you to capture complex annotation or execution structures with additional calls.

Simple Operation

When there is no concurrency in the computation (no “forks” or “joins”), all that is needed is to call the logging methods whenever you want to record an event in the execution. The class edu.berkeley.xtrace.XtrContext provides the API. It maintains a per-thread X-Trace context (task and operation ID) and reporting events. To use it:

  1. When communication is received, set the context using XtrContext.set().
  2. To record an operation, call XtrContext.logEvent(). Or, to add extra fields to the event report, call XtrContext.createEvent(), add fields to the returned XtrEvent object, and send it using XtrEvent.sendReport().
  3. When calling another service, get the current context using XtrContext.get() and send it as metadata. After receiving a reply, add an edge from both the reply's metadata and the current context in the report for the reply.
  4. Clear the context using XtrContext.clear().

Under the Covers

The X-Trace metadata carries enough information so that one event can link to the previous one. This is done through a combination of what information is passed from one event to the other – the metadata itself – and how this information is recorded – X-Trace reports. An X-Trace *task* is composed of many *events* or *operations* that are linked by edges indicating causality.

To register one event, an application has to perform the following sequence:

  1. Obtain the OpId from the last event
  2. Create a new OpId for the new event
  3. Report the new event, including the old and new OpIds (the “edge”), and any other information like nature of the event and a timestamp
  4. Provide a way for the following event to obtain the newly created OpId

This is what the XtrContext API provides. The XtrContext.set() and XtrContext.get() methods allow an implicit metadata to be carried with the computation, and the logEvent call does the proper propagation using this context. It assumes the previous event OpId was stored in the context with XtrContext.set(). It obtains this OpId with XtrContext.get(), and calls XtrContext.set() with the newly created metadata after it is done. XtrContext.logEvent() also sends a report that includes the edge between the previous and the current event. createEvent does the same propagation, creates a report but does not send it. Instead, it returns the XtrEvent created so you can add more information to it.

Capturing Parallelism

When there is a fork in the computation, optionally followed by a later join of the created parallel threads,1) you may want to capture this in a more structured way: the first events in each of the created threads should have the same parent, and if there is a join, they should join in the same event.

To have multiple events point to the same parent event, using the XtrContext API, you must store the common parent in a local variable. For example, suppose you want to launch several parallel computations as below:

    ...
    XtrContext.logEvent("my program", "A");
 
    XtrMetadata start_xtr = XtrContext.get(); 
 
    for (i = 0; i < N; i++) {
        XtrContext.set(start_xtr);
        XtrContext.logEvent("my program","start " + i);
        startSomething();
    }
    ...

Now suppose you have a method that is called when these threads finish. It should have access to an XtrEvent object that persists among calls, and add the relevant edges to it. As a simple example,

     ...
     XtrEvent xte = null;
     remaining = N;
     ...
 
     finishSomething() {
         if (remaining == 0)
            return;
         if (xte == null)
            xte = XtrContext.createEvent("my program", "finish something");
         else
            xte.addEdge(XtrContext.get());
         if ( --remaining == 0 ) {
            xte.sendReport();
            xte = null;
         }
     }
1) We use “forks”, “joins”, and “threads” here in the conceptual level, not necessarily using the corresponding Java calls. The computation can span multiple processes on different machines, for example.
 
docs/logging-api.txt · Last modified: 2007/11/21 17:38 by rfonseca
 
Except where otherwise noted, content on this wiki is licensed under the following license:CC Attribution-Noncommercial-Share Alike 3.0 Unported
Recent changes RSS feed Donate Powered by PHP Valid XHTML 1.0 Valid CSS Driven by DokuWiki