ATP - Using the ATP C API
ATP provides a user-accessible tracing API, to allow running programs to query and analyze program state without a crash. Note that this API is not required to use ATP’s normal crash-time analysis. To generate link and compile flags to use when compiling your program, use pkgconfig:
pkg-config libAtpUserTrace --cflags --libs
The C API header can be found in the ATP installation directory under include/libAtpUserTrace.h.
In this example, we’ll walk through a simple function to retrieve the program’s thread and stack state, and print it to standard out.
// Include the ATP C interface file.
// The compiler flags for the include path are generated by `pkg-config`
#include "libAtpUserTrace.h"
void print_process_state() {
// Variables used during process state dumping and parsing
char *process_state_path = NULL;
atp_process_state_t *process_state = NULL;
// Variables used for walking the stack
atp_thread_t **thread_cursor = NULL;
atp_thread_t *thread = NULL;
int thread_idx = 1;
atp_stack_frame_t **frame_cursor = NULL;
atp_stack_frame_t *frame = NULL;
// Dump the current process' thread, stack, and module information
// into the directory `./dump`.
// On success, `atp_dump_process_state` will return the path to the
// process state dump.
process_state_path = atp_dump_process_state("./dump");
// On failure, `atp_dump_process_state` will return a NULL pointer,
// and set `atp_error_msg` accordingly.
if (process_state_path == NULL) {
fprintf(stderr, "ATP failed to dump process state: %s\n",
(atp_error_msg()) ? atp_error_msg() : "No message");
goto cleanup_print_process_state;
}
// Parse the thread, stack, and module information from the process state
// path into a C object of type `atp_process_state_t`.
// On success, `atp_parse_process_state` will return an allocated process
// state object, to be freed with `atp_free_process_state`.
process_state = atp_parse_process_state(process_state_path);
// On failure, `atp_parse_process_state` will return a NULL pointer,
// and set `atp_error_msg` accordingly.
if (process_state == NULL) {
fprintf(stderr, "ATP failed to parse process state: %s\n",
(atp_error_msg()) ? atp_error_msg() : "No message");
goto cleanup_print_process_state;
}
// Threads are stored in a NULL-terminated list of `atp_thread_t` pointers.
for (thread_cursor = process_state->threads; *thread_cursor != NULL; thread_cursor++) {
// Dereference thread cursor
thread = *thread_cursor;
// Print header for this thread.
printf("Thread %d:\n", thread_idx);
// Stack frames are stored in a NULL-terminated list of `atp_stack_frame_t`
// pointers.
for (frame_cursor = thread->stack; *frame_cursor != NULL; frame_cursor++) {
// Dereference stack frame cursor
frame = *frame_cursor;
// Read the stack frame information and print it.
// Function names, file names, and line numbers are optional,
// sometimes ATP is not able to determine the full debug information.
// In that case, string-type fields such as `function_name` will be
// a NULL `char*`, while integer-type fields such as `source_line` will
// be set to 0.
printf(" Function: %s File: %s Line: %d\n",
(frame->function_name) ? frame->function_name : "(None)",
(frame->source_file_name) ? frame->source_file_name : "(None)",
frame->source_line);
}
// Increment thread index number
thread_idx++;
}
cleanup_print_process_state:
// Free the allocated memory for the process state object
// using `atp_free_process_state`.
if (process_state != NULL) {
atp_free_process_state(process_state);
process_state = NULL;
}
// The process state path returned by `atp_dump_process_state`
// is allocated on the stack, and must be freed by the calling code.
if (process_state_path != NULL) {
free(process_state_path);
process_state_path = NULL;
}
}