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;
	}
}