The Prosody API library implements tracing so
that you can see what an application is doing. Tracing is controlled by
the global variable TiNGtrace in the application. There are
at least five possible levels of trace, each of which causes more
information to be reported while including all information reported by
lower trace levels:
| Level | Effect |
|---|---|
| 0 | (default) No tracing is enabled. |
| 1 | Tracing of errors is enabled.
Whenever an API function returns an error code, a
line of output is printed (on standard error) showing
the error and the place where the error was generated
(a file name and line number withing the API library
itself). Since the API library is supplied as source
code, you can see why an error is being returned
(this is most useful with an error like
ERR_SM_BAD_PARAMETER since you can see
which parameter caused it to return the error).
|
| 2 | Tracing of most API calls is enabled. In addition to the trace produced by level 1, this reports every API function called (except those which read or write data) with the parameters passed and the result returned. This can be used to verify that channels have been set up correctly. |
| 3 | Tracing of all API calls is enabled. This is the same as level 2, except that all API functions are reported. |
| 4 | Tracing of all API calls is enabled and call-progess detection reports the list of simple tones it has receives and that it is comparing against the cadences in the call progress tone table. |
There may be more levels of trace available, depending on the variant of Prosody being used.
There are four ways to turn on tracing:
--TiNGtrace which contols
tracing. Specify this option with the desired level of trace.
For example, "--TiNGtrace 2".
apilib/TiNGtrace.c and link with the
application. This changes the default trace level to level 3.
#include "smdrvr.h" in your source and
write some code which assigns the desired trace level to
TiNGtrace. This is by far the best method as
it allows you to control trace using whatever style of
configuration is used for the rest of your application
(e.g. configuration file, command line parameters, etc). Ensure
that you permit any integer to be assigned to the trace level
so that future versions with more control over trace can use
the improved trace without having to modify the application.
TINGTRACE environment variable to the
desired trace level. Note that this merely initialises the
TiNGtrace global variable, so an application
which overwrites TiNGtrace cannot be traced by
this method, which is most useful for using on applications
which have made no provision for providing trace.
For example, the command:
detraw --TiNGtrace 2 -t48:0 -m1
produces trace like this:
init TiNGlib.o v498.
Every trace starts with the version of the API library. This ensures that if you are looking at the source code to find errors reported by file and line number that you know which version of source to look at.
Init card: Prosody_199 card Prosody_199: '199' 'm672. P1 v1.1 pr:3 att:4 sharcs:128k,128k,none,none sw=0x3'
This is a list of the Prosody cards found and some information on each which can be used by Aculab support to check the configuration. Since the modules are assigned numbers based on the order of the cards, this allows you to check that they are in the order you expect.
sm_channel_alloc(ty=0, fwid=0, grp=0, cmask=0x4) == 0, chan=4c130
Here's the first API call made. Its name is shown, and in brackets the
parameters which were passed in. Then the return code (the "==
0") and finally any values returned.
sm_switch_channel_input(chan=0x4c130, st=48, ts=0, ty=0) == 0 sm_listen_for(chan=0x4c130, tone=1, tid=0, t2d=0, cpt=0, gru=0, glat=0) == 0 smd_ev_create(chan=0x4c130, ty=3, bind=1) == 0, ev=5,0x2 sm_channel_set_event(chan=0x4c130, etyp=3, iss=1, ev=5:0x2) == 0 sm_get_recognised(chan=0x4c130)[msg c8330802 00000000 00000000][msg] == 0, chan=0x4c130, typ=0, p0=0, p1=0
The "[msg ...]" shows messages arriving from the
firmware.
sm_get_recognised(chan=0x4c130)[msg c8830802 00000000 00000400][msg] == 0, chan=0x4c130, typ=0, p0=0, p1=0 sm_get_recognised(chan=0x4c130)[msg c8830802 00000000 00000800][msg] == 0, chan=0x4c130, typ=0, p0=0, p1=0 exitting smd_ev_free(ev=5,0x2) == 0 sm_channel_release(chan=0x4c130) == 0
It is strongly recommended that when you write your own application you provide a suitable mechanism to set the trace level.
Since the trace information is simply written to standard output, if a program prints other status information, it will appear in sequence with the trace output.
Some applications run in an environment in which they have no standard
output. To allow trace to be gathered from these, there is a
mechanism whereby the trace can be redirected elsewhere. The global
variable TiNG_showtrace:
ACUAPI int (*TiNG_showtrace)(const char *fmt, va_list ap);
is a pointer to the function which is called to write out trace. By
default it points to vprintf.
By writing an alternative function and storing its address in
TiNG_showtrace you can redirect the trace. Since this
output function is called any time trace is produced, it will be
called from any thread which uses Prosody functions, and possibly
from threads which are internal to the Prosody API library.
Consequently, any replacement function must be written to be
appropriately thread-safe.
Note that the ability to obtain complete, unedited trace is often essential when obtaining support from Aculab, so it is important that any replacement function does not prevent trace being recorded and does not modify the lines being written (e.g. by truncating lines to some fixed maximum length).
Sometimes it can be hard to see what is being described by trace output because more than one thread is writing trace at the same time, so the trace consists of interleaved fragments of trace. By replacing the trace output function, you can put the trace for each thread into a separate file, ensuring that each is much easier to underastand.
An experimental, unsupported example of a different trace output
function to do this can be found in
$(TiNG)/test/Testlib/perthread_trace.*.
Separate files make it easier to see the actions of each
thread, but harder to see the interactions between threads, which are
more often the cause of problems.
Occasionally it may be useful to be able to attach to a running application and get trace from it. The trace output can be redirected to a TCP socket by replacing the trace output function, but a separate thread is required to listen for connections on that socket.
An (experimental, unsupported) example of a different trace output
function to do this can be found in
$(TiNG)/test/Testlib/remote_trace.*. To add this remote
trace facility to an application, create a dedicated thread which
calls remote_trace() specifying the port on which it is to
listen (as a string - either a service name or a port number). To
get remote trace from the application, connect to the port (for
example, using telnet or netcat), and enter
the desired trace level as a decimal number. The trace output will
appear. You can enter a different trace level at any time. If you
close the connection, trace output reverts to its previous
destination, but the trace level remains at the last level you set -
so you may want to set it to zero before closing the connection. If
the application exits, the connection will get end-of-file after the
last trace.
When debugging some problems, it can be convenient to record only the
trace generated just before the problem occurred. This may be because
the problem takes a long time to reproduce and the trace is too big
if collected continuously. An example of how to produce trace on
demand is provided in $(TiNG)/test/Testlib/tracehistory.*
which provides these functions:
int TiNGtrace_sethistory(size_t len);
len characters of trace. This trace data is kept in a
buffer in memory, which saves the overhead associated with writing to
a disk file. Configuring a length of zero disables the trace history
mechanism and restores the trace system to normal output.
int TiNGtrace_savehistory(FILE *histfile);
TiNGtrace_sethistory(). It then
clears the history buffer.
The application must set up the desired size of history buffer as well as enabling the appropriate level of trace in the normal way. When the application finds the problem has occurred, it can create a file, write the trace to it, and write any other information which may be useful, such as the time when the problem occurred, its own internal state, or trace from other parts of the application. Obviously, the application can use a separate file for each trace snapshot it takes, or leave te file open and write multiple snapshots into the same file (it must write a suitable marker to separate them in this case).
Here is an example of how the trace history might be saved:
void savetrace(char *why)
{
time_t now = time(0);
char filename[64];
FILE *tf;
sprintf(filename, "trace_%d", now); // assumes only one trace file per second
tf = fopen(filename, "w");
if (tf) {
fprintf(tf, "Trace saved at %s\n", ctime(now));
fprintf(tf, "Reason: %s\n", why);
fprintf(tf, "\n");
TiNGtrace_savehistory(tf);
fprintf(tf, "\n");
// save any other trace or relevant information
fclose(tf);
}
}