| /* Copyright (c) 2013 The Chromium Authors. All rights reserved. |
| * Use of this source code is governed by a BSD-style license that can be |
| * found in the LICENSE file. */ |
| |
| |
| /* XRay -- a simple profiler for Native Client */ |
| |
| #include <assert.h> |
| #include <errno.h> |
| #include <stdarg.h> |
| #include <stdint.h> |
| #include <stdio.h> |
| #include <stdlib.h> |
| #include <string.h> |
| #include <sys/time.h> |
| #include <unistd.h> |
| #include "xray/xray_priv.h" |
| |
| #if defined(XRAY) |
| |
| #define FORCE_INLINE __attribute__((always_inline)) |
| |
| /* GTSC - Get Time Stamp Counter */ |
| #if defined(__amd64__) && !defined(XRAY_NO_RDTSC) |
| FORCE_INLINE uint64_t RDTSC64() { |
| uint64_t a, d; |
| __asm__ __volatile__("rdtsc" : "=a" (a), "=d" (d)); |
| return ((uint64_t)a) | (((uint64_t)d) << 32); |
| } |
| #define GTSC(_x) _x = RDTSC64() |
| #elif defined(__i386__) && !defined(XRAY_NO_RDTSC) |
| #define GTSC(_x) __asm__ __volatile__ ("rdtsc" : "=A" (_x)); |
| #else |
| FORCE_INLINE uint64_t GTOD() { |
| struct timeval tv; |
| gettimeofday(&tv, NULL); |
| return (uint64_t)tv.tv_sec * 1000000 + (uint64_t)tv.tv_usec; |
| } |
| #define GTSC(_x) _x = GTOD(); |
| #endif |
| |
| /* Use a TLS variable for cheap thread uid. */ |
| __thread struct XRayTraceCapture* g_xray_capture = NULL; |
| __thread int g_xray_thread_id_placeholder = 0; |
| |
| |
| struct XRayTraceStackEntry { |
| uint32_t depth_addr; |
| uint64_t tsc; |
| uint32_t dest; |
| uint32_t annotation_index; |
| }; |
| |
| |
| struct XRayTraceFrameEntry { |
| /* Indices into global tracebuffer */ |
| int start; |
| int end; |
| uint64_t start_tsc; |
| uint64_t end_tsc; |
| uint64_t total_ticks; |
| int annotation_count; |
| bool valid; |
| |
| #ifndef XRAY_DISABLE_BROWSER_INTEGRATION |
| struct XRayTimestampPair start_time; |
| struct XRayTimestampPair end_time; |
| #endif |
| }; |
| |
| |
| struct XRayTraceFrame { |
| struct XRayTraceFrameEntry* entry; |
| int head; |
| int tail; |
| int count; |
| }; |
| |
| |
| struct XRayTraceCapture { |
| /* Common variables share cache line */ |
| bool recording; |
| uint32_t stack_depth; |
| uint32_t max_stack_depth; |
| int buffer_index; |
| int buffer_size; |
| int disabled; |
| int annotation_count; |
| struct XRaySymbolTable* symbols; |
| bool initialized; |
| uint32_t annotation_filter; |
| uint32_t guard0; |
| struct XRayTraceStackEntry stack[XRAY_TRACE_STACK_SIZE] XRAY_ALIGN64; |
| uint32_t guard1; |
| uint32_t guard2; |
| char annotation[XRAY_ANNOTATION_STACK_SIZE] XRAY_ALIGN64; |
| uint32_t guard3; |
| struct XRayTraceBufferEntry* buffer; |
| struct XRayTraceFrame frame; |
| |
| #ifndef XRAY_DISABLE_BROWSER_INTEGRATION |
| int32_t thread_id; |
| #endif |
| } XRAY_ALIGN64; |
| |
| |
| #ifdef __cplusplus |
| extern "C" { |
| #endif |
| |
| #if defined(__pnacl__) |
| XRAY_NO_INSTRUMENT void __pnacl_profile_func_enter(const char* fname); |
| XRAY_NO_INSTRUMENT void __pnacl_profile_func_exit(const char* fname); |
| #else |
| XRAY_NO_INSTRUMENT void __cyg_profile_func_enter(void* this_fn, |
| void* call_site); |
| XRAY_NO_INSTRUMENT void __cyg_profile_func_exit(void* this_fn, |
| void* call_site); |
| #endif |
| |
| XRAY_NO_INSTRUMENT void __xray_profile_append_annotation( |
| struct XRayTraceCapture* capture, |
| struct XRayTraceStackEntry* se, |
| struct XRayTraceBufferEntry* be); |
| |
| #ifdef __cplusplus |
| } |
| #endif |
| |
| /* Asserts that the guard values haven't changed. */ |
| void XRayCheckGuards(struct XRayTraceCapture* capture) { |
| assert(capture->guard0 == XRAY_GUARD_VALUE_0x12345678); |
| assert(capture->guard1 == XRAY_GUARD_VALUE_0x12345678); |
| assert(capture->guard2 == XRAY_GUARD_VALUE_0x87654321); |
| assert(capture->guard3 == XRAY_GUARD_VALUE_0x12345678); |
| } |
| |
| /* Decrements the trace index, wrapping around if needed. */ |
| XRAY_FORCE_INLINE int XRayTraceDecrementIndexInline( |
| struct XRayTraceCapture* capture, int index) { |
| --index; |
| if (index < 0) |
| index = capture->buffer_size - 1; |
| return index; |
| } |
| |
| /* Increments the trace index, wrapping around if needed. */ |
| XRAY_FORCE_INLINE int XRayTraceIncrementIndexInline( |
| struct XRayTraceCapture* capture, int index) { |
| ++index; |
| if (index >= capture->buffer_size) |
| index = 0; |
| return index; |
| } |
| |
| /* Returns true if the trace entry is an annotation string. */ |
| bool XRayTraceIsAnnotation( |
| struct XRayTraceCapture* capture, int index) { |
| struct XRayTraceBufferEntry* be = &capture->buffer[index]; |
| char* dst = (char*)be; |
| return 0 == *dst; |
| } |
| |
| int XRayTraceIncrementIndex(struct XRayTraceCapture* capture, int index) { |
| return XRayTraceIncrementIndexInline(capture, index); |
| } |
| |
| int XRayTraceDecrementIndex(struct XRayTraceCapture* capture, int index) { |
| return XRayTraceDecrementIndexInline(capture, index); |
| } |
| |
| /* The entry in the tracebuffer at index is an annotation string. */ |
| /* Calculate the next index value representing the next trace entry. */ |
| int XRayTraceSkipAnnotation(struct XRayTraceCapture* capture, int index) { |
| /* Annotations are strings embedded in the trace buffer. */ |
| /* An annotation string can span multiple trace entries. */ |
| /* Skip over the string by looking for zero termination. */ |
| assert(capture); |
| assert(XRayTraceIsAnnotation(capture, index)); |
| bool done = false; |
| int start_index = 1; |
| int i; |
| while (!done) { |
| char* str = (char*) &capture->buffer[index]; |
| const int num = sizeof(capture->buffer[index]); |
| for (i = start_index; i < num; ++i) { |
| if (0 == str[i]) { |
| done = true; |
| break; |
| } |
| } |
| index = XRayTraceIncrementIndexInline(capture, index); |
| start_index = 0; |
| } |
| return index; |
| } |
| |
| |
| struct XRayTraceBufferEntry* XRayTraceGetEntry( |
| struct XRayTraceCapture* capture, int index) { |
| return &capture->buffer[index]; |
| } |
| |
| /* Starting at index, return the index into the trace buffer */ |
| /* for the next trace entry. Index can wrap (ringbuffer) */ |
| int XRayTraceNextEntry(struct XRayTraceCapture* capture, int index) { |
| if (XRayTraceIsAnnotation(capture, index)) |
| index = XRayTraceSkipAnnotation(capture, index); |
| else |
| index = XRayTraceIncrementIndexInline(capture, index); |
| return index; |
| } |
| |
| int XRayFrameGetTraceStartIndex(struct XRayTraceCapture* capture, int frame) { |
| assert(capture); |
| assert(capture->initialized); |
| assert(!capture->recording); |
| return capture->frame.entry[frame].start; |
| } |
| |
| int XRayFrameGetTraceEndIndex(struct XRayTraceCapture* capture, int frame) { |
| assert(capture); |
| assert(capture->initialized); |
| assert(!capture->recording); |
| return capture->frame.entry[frame].end; |
| } |
| |
| /* Not very accurate, annotation strings will also be counted as "entries" */ |
| int XRayFrameGetTraceCount( |
| struct XRayTraceCapture* capture, int frame) { |
| assert(true == capture->initialized); |
| assert(frame >= 0); |
| assert(frame < capture->frame.count); |
| assert(!capture->recording); |
| int start = capture->frame.entry[frame].start; |
| int end = capture->frame.entry[frame].end; |
| int num; |
| if (start < end) |
| num = end - start; |
| else |
| num = capture->buffer_size - (start - end); |
| return num; |
| } |
| |
| /* Append a string to trace buffer. */ |
| void XRayTraceAppendString(struct XRayTraceCapture* capture, char* src) { |
| int index = capture->buffer_index; |
| bool done = false; |
| int start_index = 1; |
| int s = 0; |
| int i; |
| char* dst = (char*)&capture->buffer[index]; |
| const int num = sizeof(capture->buffer[index]); |
| dst[0] = 0; |
| while (!done) { |
| for (i = start_index; i < num; ++i) { |
| dst[i] = src[s]; |
| if (0 == src[s]) { |
| dst[i] = 0; |
| done = true; |
| break; |
| } |
| ++s; |
| } |
| index = XRayTraceIncrementIndexInline(capture, index); |
| dst = (char*)&capture->buffer[index]; |
| start_index = 0; |
| } |
| capture->buffer_index = index; |
| } |
| |
| /* Copies annotation from trace buffer to output string. */ |
| int XRayTraceCopyToString( |
| struct XRayTraceCapture* capture, int index, char* dst) { |
| assert(XRayTraceIsAnnotation(capture, index)); |
| bool done = false; |
| int i; |
| int d = 0; |
| int start_index = 1; |
| while (!done) { |
| char* src = (char*) &capture->buffer[index]; |
| const int num = sizeof(capture->buffer[index]); |
| for (i = start_index; i < num; ++i) { |
| dst[d] = src[i]; |
| if (0 == src[i]) { |
| done = true; |
| break; |
| } |
| ++d; |
| } |
| index = XRayTraceIncrementIndexInline(capture, index); |
| start_index = 0; |
| } |
| return index; |
| } |
| |
| |
| /* Generic memory malloc for XRay */ |
| /* validates pointer returned by malloc */ |
| /* memsets memory block to zero */ |
| void* XRayMalloc(size_t t) { |
| void* data; |
| data = calloc(1, t); |
| if (NULL == data) { |
| printf("XRay: malloc(%d) failed, panic shutdown!\n", t); |
| exit(-1); |
| } |
| return data; |
| } |
| |
| |
| /* Generic memory free for XRay */ |
| void XRayFree(void* data) { |
| assert(NULL != data); |
| free(data); |
| } |
| |
| |
| /* Main profile capture function that is called at the start */ |
| /* of every instrumented function. This function is implicitly */ |
| /* called when code is compilied with the -finstrument-functions option */ |
| #if defined(__pnacl__) |
| void __pnacl_profile_func_enter(const char* this_fn) { |
| #else |
| void __cyg_profile_func_enter(void* this_fn, void* call_site) { |
| #endif |
| struct XRayTraceCapture* capture = g_xray_capture; |
| if (capture && capture->recording) { |
| uint32_t depth = capture->stack_depth; |
| if (depth < capture->max_stack_depth) { |
| struct XRayTraceStackEntry* se = &capture->stack[depth]; |
| uint32_t addr = (uint32_t)this_fn; |
| se->depth_addr = XRAY_PACK_DEPTH_ADDR(depth, addr); |
| se->dest = capture->buffer_index; |
| se->annotation_index = 0; |
| GTSC(se->tsc); |
| capture->buffer_index = |
| XRayTraceIncrementIndexInline(capture, capture->buffer_index); |
| } |
| ++capture->stack_depth; |
| } |
| } |
| |
| |
| /* Main profile capture function that is called at the exit of */ |
| /* every instrumented function. This function is implicity called */ |
| /* when the code is compiled with the -finstrument-functions option */ |
| #if defined(__pnacl__) |
| void __pnacl_profile_func_exit(const char* this_fn) { |
| #else |
| void __cyg_profile_func_exit(void* this_fn, void* call_site) { |
| #endif |
| struct XRayTraceCapture* capture = g_xray_capture; |
| if (capture && capture->recording) { |
| --capture->stack_depth; |
| if (capture->stack_depth < capture->max_stack_depth) { |
| uint32_t depth = capture->stack_depth; |
| struct XRayTraceStackEntry* se = &capture->stack[depth]; |
| uint32_t buffer_index = se->dest; |
| uint64_t tsc; |
| struct XRayTraceBufferEntry* be = &capture->buffer[buffer_index]; |
| GTSC(tsc); |
| be->depth_addr = se->depth_addr; |
| be->start_tick = se->tsc; |
| be->end_tick = tsc; |
| be->annotation_index = 0; |
| if (0 != se->annotation_index) |
| __xray_profile_append_annotation(capture, se, be); |
| } |
| } |
| } |
| |
| #ifndef XRAY_DISABLE_BROWSER_INTEGRATION |
| void XRayGetTSC(uint64_t* tsc) { |
| GTSC(*tsc); |
| } |
| |
| int32_t XRayGetSavedThreadID(struct XRayTraceCapture* capture) { |
| return capture->thread_id; |
| } |
| |
| struct XRayTimestampPair XRayFrameGetStartTimestampPair( |
| struct XRayTraceCapture* capture, int frame) { |
| return capture->frame.entry[frame].start_time; |
| } |
| |
| struct XRayTimestampPair XRayFrameGetEndTimestampPair( |
| struct XRayTraceCapture* capture, int frame) { |
| return capture->frame.entry[frame].end_time; |
| } |
| #endif |
| |
| /* Special case appending annotation string to trace buffer */ |
| /* this function should only ever be called from __cyg_profile_func_exit() */ |
| void __xray_profile_append_annotation(struct XRayTraceCapture* capture, |
| struct XRayTraceStackEntry* se, |
| struct XRayTraceBufferEntry* be) { |
| struct XRayTraceStackEntry* parent = se - 1; |
| int start = parent->annotation_index; |
| be->annotation_index = capture->buffer_index; |
| char* str = &capture->annotation[start]; |
| XRayTraceAppendString(capture, str); |
| *str = 0; |
| ++capture->annotation_count; |
| } |
| |
| |
| |
| /* Annotates the trace buffer. no filtering. */ |
| void __XRayAnnotate(const char* fmt, ...) { |
| va_list args; |
| struct XRayTraceCapture* capture = g_xray_capture; |
| /* Only annotate functions recorded in the trace buffer. */ |
| if (capture && capture->initialized) { |
| if (0 == capture->disabled) { |
| if (capture->recording) { |
| char buffer[1024]; |
| int r; |
| va_start(args, fmt); |
| r = vsnprintf(buffer, sizeof(buffer), fmt, args); |
| va_end(args); |
| { |
| /* Get current string ptr */ |
| int depth = capture->stack_depth - 1; |
| struct XRayTraceStackEntry* se = &capture->stack[depth]; |
| if (0 == se->annotation_index) { |
| struct XRayTraceStackEntry* parent = se - 1; |
| se->annotation_index = parent->annotation_index; |
| } |
| char* dst = &capture->annotation[se->annotation_index]; |
| strcpy(dst, buffer); |
| int len = strlen(dst); |
| se->annotation_index += len; |
| } |
| } |
| } |
| } |
| } |
| |
| |
| /* Annotates the trace buffer with user strings. Can be filtered. */ |
| void __XRayAnnotateFiltered(const uint32_t filter, const char* fmt, ...) { |
| va_list args; |
| struct XRayTraceCapture* capture = g_xray_capture; |
| if (capture && capture->initialized) { |
| if (0 != (filter & capture->annotation_filter)) { |
| if (0 == capture->disabled) { |
| if (capture->recording) { |
| char buffer[XRAY_TRACE_ANNOTATION_LENGTH]; |
| int r; |
| va_start(args, fmt); |
| r = vsnprintf(buffer, sizeof(buffer), fmt, args); |
| va_end(args); |
| { |
| /* get current string ptr */ |
| int depth = capture->stack_depth - 1; |
| struct XRayTraceStackEntry* se = &capture->stack[depth]; |
| if (0 == se->annotation_index) { |
| struct XRayTraceStackEntry* parent = se - 1; |
| se->annotation_index = parent->annotation_index; |
| } |
| char* dst = &capture->annotation[se->annotation_index]; |
| strcpy(dst, buffer); |
| int len = strlen(dst); |
| se->annotation_index += len; |
| } |
| } |
| } |
| } |
| } |
| } |
| |
| |
| /* Allows user to specify annotation filter value, a 32 bit mask. */ |
| void XRaySetAnnotationFilter(struct XRayTraceCapture* capture, |
| uint32_t filter) { |
| capture->annotation_filter = filter; |
| } |
| |
| |
| /* Reset xray profiler. */ |
| void XRayReset(struct XRayTraceCapture* capture) { |
| assert(capture); |
| assert(capture->initialized); |
| assert(!capture->recording); |
| capture->buffer_index = 0; |
| capture->stack_depth = 0; |
| capture->disabled = 0; |
| capture->frame.head = 0; |
| capture->frame.tail = 0; |
| memset(capture->frame.entry, 0, |
| sizeof(capture->frame.entry[0]) * capture->frame.count); |
| memset(&capture->stack, 0, |
| sizeof(capture->stack[0]) * XRAY_TRACE_STACK_SIZE); |
| XRayCheckGuards(capture); |
| } |
| |
| |
| /* Change the maximum stack depth captures are made. */ |
| void XRaySetMaxStackDepth(struct XRayTraceCapture* capture, int stack_depth) { |
| assert(capture); |
| assert(capture->initialized); |
| assert(!capture->recording); |
| if (stack_depth < 1) |
| stack_depth = 1; |
| if (stack_depth >= XRAY_TRACE_STACK_SIZE) |
| stack_depth = (XRAY_TRACE_STACK_SIZE - 1); |
| capture->max_stack_depth = stack_depth; |
| } |
| |
| |
| int XRayFrameGetCount(struct XRayTraceCapture* capture) { |
| return capture->frame.count; |
| } |
| |
| int XRayFrameGetTail(struct XRayTraceCapture* capture) { |
| return capture->frame.tail; |
| } |
| |
| int XRayFrameGetHead(struct XRayTraceCapture* capture) { |
| return capture->frame.head; |
| } |
| |
| int XRayFrameGetPrev(struct XRayTraceCapture* capture, int i) { |
| i = i - 1; |
| if (i < 0) |
| i = capture->frame.count - 1; |
| return i; |
| } |
| |
| int XRayFrameGetNext(struct XRayTraceCapture* capture, int i) { |
| i = i + 1; |
| if (i >= capture->frame.count) |
| i = 0; |
| return i; |
| } |
| |
| bool XRayFrameIsValid(struct XRayTraceCapture* capture, int i) { |
| return capture->frame.entry[i].valid; |
| } |
| |
| int XRayFrameGetTotalTicks(struct XRayTraceCapture* capture, int i) { |
| return capture->frame.entry[i].total_ticks; |
| } |
| |
| int XRayFrameGetAnnotationCount(struct XRayTraceCapture* capture, int i) { |
| return capture->frame.entry[i].annotation_count; |
| } |
| |
| void XRayFrameMakeLabel(struct XRayTraceCapture* capture, |
| int counter, |
| char* label) { |
| snprintf(label, XRAY_MAX_LABEL, "@@@frame%d@@@", counter); |
| } |
| |
| |
| /* Scans the ring buffer going backwards to find last valid complete frame. */ |
| /* Will mark whether frames are valid or invalid during the traversal. */ |
| int XRayFrameFindTail(struct XRayTraceCapture* capture) { |
| int head = capture->frame.head; |
| int index = XRayFrameGetPrev(capture, head); |
| int total_capture = 0; |
| int last_valid_frame = index; |
| /* Check for no captures */ |
| if (capture->frame.head == capture->frame.tail) |
| return capture->frame.head; |
| /* Go back and invalidate all captures that have been stomped. */ |
| while (index != head) { |
| bool valid = capture->frame.entry[index].valid; |
| if (valid) { |
| total_capture += XRayFrameGetTraceCount(capture, index) + 1; |
| if (total_capture < capture->buffer_size) { |
| last_valid_frame = index; |
| capture->frame.entry[index].valid = true; |
| } else { |
| capture->frame.entry[index].valid = false; |
| } |
| } |
| index = XRayFrameGetPrev(capture, index); |
| } |
| return last_valid_frame; |
| } |
| |
| |
| /* Starts a new frame and enables capturing, and must be paired with */ |
| /* XRayEndFrame() Trace capturing only occurs on the thread which called */ |
| /* XRayBeginFrame() and each instance of capture can only trace one thread */ |
| /* at a time. */ |
| void XRayStartFrame(struct XRayTraceCapture* capture) { |
| int i; |
| assert(NULL == g_xray_capture); |
| assert(capture->initialized); |
| assert(!capture->recording); |
| i = capture->frame.head; |
| XRayCheckGuards(capture); |
| /* Add a trace entry marker so we can detect wrap around stomping */ |
| struct XRayTraceBufferEntry* be = &capture->buffer[capture->buffer_index]; |
| be->depth_addr = XRAY_FRAME_MARKER; |
| capture->buffer_index = |
| XRayTraceIncrementIndex(capture, capture->buffer_index); |
| /* Set start of the frame we're about to trace */ |
| capture->frame.entry[i].start = capture->buffer_index; |
| capture->disabled = 0; |
| capture->stack_depth = 1; |
| /* The trace stack[0] is reserved */ |
| memset(&capture->stack[0], 0, sizeof(capture->stack[0])); |
| /* Annotation index 0 is reserved to indicate no annotation */ |
| capture->stack[0].annotation_index = 1; |
| capture->annotation[0] = 0; |
| capture->annotation[1] = 0; |
| capture->annotation_count = 0; |
| capture->recording = true; |
| GTSC(capture->frame.entry[i].start_tsc); |
| g_xray_capture = capture; |
| |
| #ifndef XRAY_DISABLE_BROWSER_INTEGRATION |
| capture->frame.entry[i].start_time = XRayGenerateTimestampsNow(); |
| #endif |
| |
| } |
| |
| |
| /* Ends a frame and disables capturing. Advances to the next frame. */ |
| /* Must be paired with XRayStartFrame(), and called from the same thread. */ |
| void XRayEndFrame(struct XRayTraceCapture* capture) { |
| int i; |
| assert(capture); |
| assert(capture->initialized); |
| assert(capture->recording); |
| assert(g_xray_capture == capture); |
| assert(0 == capture->disabled); |
| assert(1 == capture->stack_depth); |
| i = capture->frame.head; |
| GTSC(capture->frame.entry[i].end_tsc); |
| capture->frame.entry[i].total_ticks = |
| capture->frame.entry[i].end_tsc - capture->frame.entry[i].start_tsc; |
| capture->recording = NULL; |
| capture->frame.entry[i].end = capture->buffer_index; |
| capture->frame.entry[i].valid = true; |
| capture->frame.entry[i].annotation_count = capture->annotation_count; |
| capture->frame.head = XRayFrameGetNext(capture, capture->frame.head); |
| /* If the table is filled, bump the tail. */ |
| if (capture->frame.head == capture->frame.tail) |
| capture->frame.tail = XRayFrameGetNext(capture, capture->frame.tail); |
| capture->frame.tail = XRayFrameFindTail(capture); |
| /* Check that we didn't stomp over trace entry marker. */ |
| int marker = XRayTraceDecrementIndex(capture, capture->frame.entry[i].start); |
| struct XRayTraceBufferEntry* be = &capture->buffer[marker]; |
| if (be->depth_addr != XRAY_FRAME_MARKER) { |
| fprintf(stderr, |
| "XRay: XRayStopFrame() detects insufficient trace buffer size!\n"); |
| XRayReset(capture); |
| } else { |
| /* Replace marker with an empty annotation string. */ |
| be->depth_addr = XRAY_NULL_ANNOTATION; |
| XRayCheckGuards(capture); |
| } |
| g_xray_capture = NULL; |
| |
| #ifndef XRAY_DISABLE_BROWSER_INTEGRATION |
| capture->frame.entry[i].end_time = XRayGenerateTimestampsNow(); |
| #endif |
| } |
| |
| |
| /* Get the last frame captured. Do not call while capturing. */ |
| /* (ie call outside of XRayStartFrame() / XRayStopFrame() pair) */ |
| int XRayGetLastFrame(struct XRayTraceCapture* capture) { |
| assert(capture); |
| assert(capture->initialized); |
| assert(!capture->recording); |
| assert(0 == capture->disabled); |
| assert(1 == capture->stack_depth); |
| int last_frame = XRayFrameGetPrev(capture, capture->frame.head); |
| return last_frame; |
| } |
| |
| |
| /* Disables capturing until a paired XRayEnableCapture() is called */ |
| /* This call can be nested, but must be paired with an enable */ |
| /* (If you need to just exclude a specific function and not its */ |
| /* children, the XRAY_NO_INSTRUMENT modifier might be better) */ |
| /* Must be called from same thread as XRayBeginFrame() / XRayEndFrame() */ |
| void XRayDisableCapture(struct XRayTraceCapture* capture) { |
| assert(capture); |
| assert(capture == g_xray_capture); |
| assert(capture->initialized); |
| ++capture->disabled; |
| capture->recording = false; |
| } |
| |
| |
| /* Re-enables capture. Must be paired with XRayDisableCapture() */ |
| void XRayEnableCapture(struct XRayTraceCapture* capture) { |
| assert(capture); |
| assert(capture == g_xray_capture); |
| assert(capture->initialized); |
| assert(0 < capture->disabled); |
| --capture->disabled; |
| if (0 == capture->disabled) { |
| capture->recording = true; |
| } |
| } |
| |
| |
| |
| struct XRaySymbolTable* XRayGetSymbolTable(struct XRayTraceCapture* capture) { |
| return capture->symbols; |
| } |
| |
| |
| /* Initialize XRay */ |
| struct XRayTraceCapture* XRayInit(int stack_depth, |
| int buffer_size, |
| int frame_count, |
| const char* mapfilename) { |
| struct XRayTraceCapture* capture; |
| capture = (struct XRayTraceCapture*)XRayMalloc( |
| sizeof(struct XRayTraceCapture)); |
| int adj_frame_count = frame_count + 1; |
| size_t buffer_size_in_bytes = |
| sizeof(capture->buffer[0]) * buffer_size; |
| size_t frame_size_in_bytes = |
| sizeof(capture->frame.entry[0]) * adj_frame_count; |
| capture->buffer = |
| (struct XRayTraceBufferEntry *)XRayMalloc(buffer_size_in_bytes); |
| capture->frame.entry = |
| (struct XRayTraceFrameEntry *)XRayMalloc(frame_size_in_bytes); |
| capture->buffer_size = buffer_size; |
| capture->frame.count = adj_frame_count; |
| capture->frame.head = 0; |
| capture->frame.tail = 0; |
| capture->disabled = 0; |
| capture->annotation_filter = 0xFFFFFFFF; |
| capture->guard0 = XRAY_GUARD_VALUE_0x12345678; |
| capture->guard1 = XRAY_GUARD_VALUE_0x12345678; |
| capture->guard2 = XRAY_GUARD_VALUE_0x87654321; |
| capture->guard3 = XRAY_GUARD_VALUE_0x12345678; |
| capture->initialized = true; |
| capture->recording = false; |
| XRaySetMaxStackDepth(capture, stack_depth); |
| XRayReset(capture); |
| |
| /* Mapfile is optional; we don't need it for captures, only for reports. */ |
| capture->symbols = |
| XRaySymbolTableCreate(XRAY_DEFAULT_SYMBOL_TABLE_SIZE); |
| if (NULL != mapfilename) |
| XRaySymbolTableParseMapfile(capture->symbols, mapfilename); |
| |
| #ifndef XRAY_DISABLE_BROWSER_INTEGRATION |
| /* Use the address of a thread local variable as a fake thread id. */ |
| capture->thread_id = (int32_t)(&g_xray_thread_id_placeholder); |
| #endif |
| |
| return capture; |
| } |
| |
| |
| /* Shut down and free memory used by XRay. */ |
| void XRayShutdown(struct XRayTraceCapture* capture) { |
| assert(capture); |
| assert(capture->initialized); |
| assert(!capture->recording); |
| XRayCheckGuards(capture); |
| if (NULL != capture->symbols) { |
| XRaySymbolTableFree(capture->symbols); |
| } |
| XRayFree(capture->frame.entry); |
| XRayFree(capture->buffer); |
| capture->initialized = false; |
| XRayFree(capture); |
| } |
| |
| #endif /* XRAY */ |