Home | Previous Page | Next Page   Creating User-Defined Routines > Developing a User-Defined Routine > Debugging a UDR >

Using Tracing

A tracepoint is a point within the code that can send special information about the current executing state of the UDR. Each tracepoint has the following parts:

Tip:
The IBM Informix BladeSmith development tool, which is part of the DataBlade Developer's Kit, automatically includes tracing statements in the C source code that it generates. For more information, see the IBM Informix: DataBlade Developer's Kit User's Guide.
Client Only

The DataBlade API tracing support is available only in C UDRs. Do not use this feature within client LIBMI applications.

End of Client Only

The mitrace.h header file defines the functions and data type structures of the tracing interface. The mi.h header file automatically includes the mitrace.h header file. You must include either mi.h or mitrace.h in any C UDR that uses a DataBlade API tracing function.

The DataBlade API provides the following tracing support.

Time of Use Tracing Support
At UDR-development time Adds tracepoints in the C UDR with associated trace-level thresholds
At UDR runtime Turns on different trace classes at specified trace levels

Adding a Tracepoint in Code

A user-defined tracepoint is a point within the code of a C UDR that can send special information about the current executing state of that routine.

To use a user-defined tracepoint
  1. Choose the trace class for the tracepoint.
  2. Put trace messages into the UDR code.

    A tracepoint contains a trace message whose text you want to output. You assign to each tracepoint a trace class and a threshold level. If a tracepoint threshold is not greater than the current trace level, the DataBlade API writes the associated trace message to the trace output file.

  3. Turn tracing on with an appropriate trace level for the tracepoints that you want to execute.

    You assign the current trace level when you turn on tracing with the mi_tracelevel_set( ) function.

Choosing a Trace Class

Trace messages are grouped into trace classes. A trace class enables you to set up categories of related tracepoints, which you can then turn on or turn off independently. Within your UDR, you can choose either type of trace class for your tracepoint:

Using the Built-In Trace Class

The DataBlade API provides a built-in trace class named __myErrors__. The __myErrors__ trace class writes out the full text of database server exceptions (errors and warnings) as they occur. You can set the trace level of __myErrors__ with mi_tracelevel_set( ), just as with any other trace class.

The __myErrors__ trace class provides the following trace levels:

Tip:
The __myErrors__ trace class does not appear in the systraceclasses system catalog table.
Creating a New Trace Class

To create your own trace class, define an entry for the trace class in the systraceclasses system catalog table. By default, all users can view this table, but only users with the DBA privilege can modify it. You can create as many trace classes as you like. The database server prevents you from creating a trace class name that is already in use.

Tip:
The BladeSmith of the DataBlade Developer's Kit (DBDK) can add trace messages to the systracemsgs system catalog table. For more information, see your BladeSmith documentation.

Figure 66 shows the INSERT statement that creates a trace class named funcEntry.

Figure 66. Creating the funcEntry Trace Class
INSERT INTO informix.systraceclasses(name)
   VALUES ('funcEntry');

When you insert a new trace class into systraceclasses, the database server assigns it a unique identifier, called a trace-class identifier. It stores this trace-class identifier in the classid column of systraceclasses.

Tip:
For more information on the columns of the systraceclasses system catalog table, see the IBM Informix: Guide to SQL Reference.

The built-in tracing that the DataBlade Developer's Kit (DBDK) provides assumes that you create a single trace class and that its name is the same as the name of your DataBlade module. For more information, see the IBM Informix: DataBlade Developer's Kit User's Guide.

Putting Trace Messages into Code

The DataBlade API supports the following types of tracepoints in a C UDR:

The DataBlade API provides the following tracing functions to insert U.S. English tracepoints into UDR code:

Tip:
The BladeSmith of the DataBlade Developer's Kit (DBDK) can create tracing routines and macros as part of the code it generates for a DataBlade module. For more information, see your BladeSmith documentation.
Using DPRINTF Macro

The DPRINTF macro directly marks a tracepoint in the code. It formats a trace message and specifies the threshold for the tracepoint. The syntax for DPRINTF is as follows:

DPRINTF(trace_class, threshold, (format [, arg]...));

These syntax elements have the following values:

trace_class
is either a trace-class name or the trace-class identifier (an integer value) expressed as a character string.
threshold
is a non-negative integer that sets the tracepoint threshold for execution.
format
is a printf-style output format that formats the trace message and can include print formatting directives.
arg
is an expression to be evaluated for output. It provides the value for a print formatting directive in the format argument.
Global Language Support

The DataBlade API also provides the GL_DPRINTF macro for formatting internationalized trace messages. For more information, see the IBM Informix: GLS User's Guide.

End of Global Language Support

The following example uses DPRINTF to insert a tracepoint of the funcEntry trace_class (which Figure 66 defines) after the doAbigPiece( ) function executes:

result = doAbigPiece(x, "x location");
DPRINTF("funcEntry", 50,
   ("After calling doAbigPiece with x = %d and %s \
      result = %f", x, "x location", result));

The trace message consists of the literal text, the print formatting directives, and the expressions for the print formatting directives.

To determine the value of a trace-class identifier, you can query the systraceclasses system catalog table for the classid column. The following SELECT statement obtains the trace-class identifier for the funcEntry trace class:

SELECT classid FROM informix.systraceclasses
WHERE name = 'funcEntry';

If the trace-class identifier for the funcEntry trace class is 42, the following DPRINTF call performs the same task as the preceding DPRINTF call:

result = doAbigPiece(x, "x location");
DPRINTF("42", 50,
   ("After calling doAbigPiece with x = %d and %s \
      result = %f", x, "x location", result));

The tracepoint threshold determines which tracepoints generate output, based on the current trace levels of the trace class. If a tracepoint threshold is not greater than the current trace level, the database server writes the associated trace message to the trace-output file.

The simplest tracing scheme is to have only two trace levels:

A more complex scheme could have four states: no tracing, light tracing, medium tracing, and heavy tracing. As an example, suppose you want to define the following trace levels for the funcEntry trace class.

Tracing
Level
Threshold
Values
Sample DPRINTF Call
None 0 None
Light 1 to 10 DPRINTF("funcEntry", 1, ("Entering doTheJob: the main function"));
Medium 11 to 20 DPRINTF("funcEntry", 11, ("Entering doAbigPiece: a top-level \help function"));
Heavy >= 20 DPRINTF("funcEntry", 21, ("Entering doAlittlePiece: an often-called \helper"));

The maximum number of trace levels is the largest non-negative integer representable on the platform.

Trace Blocks

In some cases, you might need to perform some computations before you decide whether to output certain trace data. To perform computations for a tracepoint, define a trace block. A trace block initially compares a specified threshold with the current trace level to determine whether to continue with the trace computations. Within the trace block, it can output a result in a trace message.

The DataBlade API provides the following tracing functions for use in a trace block:

Global Language Support

The DataBlade API also provides the gl_tprintf( ) function for formatting internationalized trace messages within a trace block. For more information on gl_tprintf( ), see the IBM Informix: GLS User's Guide.

End of Global Language Support

You can combine these trace-block functions with conventional C structures. The following example is typical:

/* Compare current trace level of "chck_consist" class and
 * with a tracepoint threshold of 20. Continue execution of
 * trace block if trace level >= 20
 */
if( tf("chck_consist", 20) ) 
   {
   x = fastScan(aList, y);
   consFactor = checkRconsit(x, bList);

   /* Generate trace message (in U.S. English) */
   tprintf("...in doWork: x = %f and consFactor = %f",
      x, consFactor);
   }
Defining Internationalized Trace Messages (GLS)

The DataBlade API also supports internationalized trace messages, which are trace messages that correspond to a particular non-English locale. The current database locale determines which code set the trace message uses. Based on the current database locale, a given tracepoint can produce an internationalized trace message. Internationalized tracing enables you to develop and test the same code in many different locales. For more information on how to use internationalized trace messages, see the IBM Informix: GLS User's Guide.

Using Tracing at Runtime

When your C UDR executes, you can use DataBlade API functions to perform the following tracing tasks.

Tracing Task DataBlade API Function
Turn on tracing for one or more trace classes mi_tracelevel_set( )
Set the trace-output file mi_tracefile_set( )

After the trace-output file is created, you can examine its content for information about the runtime behavior of your UDR.

Turning Tracing On

The mi_tracelevel_set( ) function sets the current trace level for one or more trace classes. You can use this function to perform the following tasks:

You pass to the mi_tracelevel_set( ) function a series of set commands, one set command for each trace class that you want set. A set command has the following format:

traceclass_name trace_level

The following example sets the trace class funcEntry (which Figure 66 defines) to a medium level (trace level of 14) and enables consistency checking with a trace class named chk_consist:

mi_integer ret;
...
ret = mi_tracelevel_set("chk_consist 1000 funcEntry 14");

You can also change the current trace level of a trace class with the mi_tracelevel_set( ) function. The following example changes the trace level of the funcEntry trace class from 14 (from the previous example) to a lower level of 5:

ret = mi_tracelevel_set("funcEntry 5");
Specifying the Trace-Output File

By default, the database server puts all trace messages in a system-defined trace-output file with a .trc file extension. For the name of this system-defined trace-output file, see the description of mi_tracefile_set( ) in the IBM Informix: DataBlade API Function Reference.

You can change the destination of trace messages with the mi_tracefile_set( ) function. With this function, you can specify the name of the trace-output file. For example, the following call to mi_tracefile_set( ) sets the trace file to a UNIX file named test14_may2.trc in the /d2/blades/tests directory:

mi_integer status;
...
status =
   mi_tracefile_set("/d2/blades/tests/test14_may2.trc");

For more information, see the description of the mi_tracefile_set( ) function.

Creating a UDR to Turn Tracing On

As a shortcut for debugging a UDR, you can create a UDR that automatically turns on tracing for your UDR. The registration of a sample user-defined procedure to perform such a task follows:

CREATE PROCEDURE trace_on(LVARCHAR, LVARCHAR)
EXTERNAL NAME '/usr/lib/udrs/my_tools.so(trace_on)'
LANGUAGE C;

The code for this user-defined procedure could be something like the following example:

void trace_on(trace_path, trace_cmds)
   mi_lvarchar *trace_path;
   mi_lvarchar *trace_level;
{
   mi_tracefile_set(mi_lvarchar_to_string(trace_path));
   mi_tracelevel_set(mi_lvarchar_to_string(trace_cmds));
};

After you register the trace-on procedure, you can turn on tracing for an SQL session with the following SQL statement:

EXECUTE PROCEDURE trace_on('trace_log_path', 
   'my_trace_class 20');

In the preceding statement, trace_log_path is the path to your trace log.

Alternatively, you could create a user-defined procedure to turn on a particular trace class. The following CREATE PROCEDURE statement registers a user-defined procedure to turn on the MyBlade trace class:

CREATE PROCEDURE traceset_myblade(LVARCHAR, INTEGER)
EXTERNAL NAME '/usr/lib/udrs/myblade.bld(db_trace_on)'
LANGUAGE C;

The following code implements such a user-defined procedure:

void db_trace_on(trace_path, trace_level)
   mi_lvarchar *trace_path;
   mi_integer trace_level;
{
   char[16] trace_cmd;

   mi_tracefile_set(mi_lvarchar_to_string(trace_path));
   sprintf(trace_cmd, "%s %d", "MyBlade", trace_level);
   mi_tracelevel_set(trace_cmd);
}

Now the following SQL statement turns on tracing for trace class MyBlade with a trace level of 20 whose tracing output goes in the UNIX file /u/dexter/udrs/myblade.trc:

EXECUTE PROCEDURE trace_on('/u/dexter/udrs/myblade.trc', 20);

Understanding Tracing Output

The DataBlade API tracing functions prepend each trace message with a time stamp to show the time that the trace message is written to the trace-output file. The time stamp enables you to associate trace output with other information, such as entries in the database server log file.

Suppose you use the DPRINTF macro to create the following tracepoints:

mi_string *udr_name = "myUDR";
...
DPRINTF("funcEntry", 15, ("%s: entering UDR", udr_name));
x = 9;
result = doSomething(x);
DPRINTF("funcEntry", 15,
   ("%s: after calling doSomething\(%d\), result = %f",
      udr_name, x, result));

If you set a trace level of 15 or greater and run the UDR at 8:56 A.M., the tracepoints generate the following lines in the trace-output file:

08:56:03  myUDR: entering UDR
08:56:03  myUDR: after calling doSomething(9), result = value

In the previous trace output, value would be the value that the function call of doSomething(9) returned.

When trace messages in the source of the UDR appear in English and the UDR uses the default locale as its server-processing locale, messages appear in English in the trace-output file. If the code set of the trace-message characters in the UDR source is different from (but compatible with) the code set of the server-processing locale, the database server performs the appropriate code-set conversion on these trace messages.

Global Language Support

To write an internationalized trace message to your trace-output file, the database server must locate a row in the systracemsgs system catalog table whose locale column matches (or is compatible with) the server-processing locale for your UDR. For more information, see the IBM Informix: GLS User's Guide.

End of Global Language Support
Home | [ Top of Page | Previous Page | Next Page | Contents | Index ]