This page introduces the X-Trace logging-like API. We use the Java API as an example, but the C++ API is similar.
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.
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:
XtrContext.set().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().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.XtrContext.clear().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:
OpId from the last eventOpId for the new eventOpIds (the “edge”), and any other information like nature of the event and a timestampOpId
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.
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;
}
}