mirror of
https://github.com/hermitcore/libhermit.git
synced 2025-03-09 00:00:03 +01:00
809 lines
24 KiB
C
809 lines
24 KiB
C
/* 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_priv.h"
|
|
|
|
#if defined(XRAY)
|
|
|
|
/* GTSC - Get Time Stamp Counter */
|
|
#if defined(__amd64__) && !defined(XRAY_NO_RDTSC)
|
|
XRAY_INLINE uint64_t RDTSC64();
|
|
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
|
|
XRAY_INLINE uint64_t GTOD();
|
|
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;
|
|
char frame_labels[XRAY_FRAME_LBL_BUFSIZE][XRAY_MAX_LABEL];
|
|
|
|
#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_INLINE int XRayTraceDecrementIndexInline(
|
|
struct XRayTraceCapture* capture, int index);
|
|
XRAY_INLINE int XRayTraceIncrementIndexInline(
|
|
struct XRayTraceCapture* capture, int index);
|
|
|
|
|
|
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. */
|
|
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. */
|
|
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)(uintptr_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;
|
|
}
|
|
|
|
uint64_t 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) {
|
|
if(counter < sizeof(capture->frame_labels) && capture->frame_labels[counter][0]) {
|
|
snprintf(label, XRAY_MAX_LABEL, "%s", capture->frame_labels[counter]);
|
|
} else {
|
|
snprintf(label, XRAY_MAX_LABEL, "frame_%i", counter);
|
|
}
|
|
}
|
|
void XRayLabelFrame(const char* fmt, ...)
|
|
{
|
|
char buffer[32];
|
|
int r;
|
|
va_list args;
|
|
|
|
va_start(args, fmt);
|
|
r = vsnprintf(buffer, sizeof(buffer), fmt, args);
|
|
if(r != 0) {
|
|
const int n = XRayFrameGetHead(g_xray_capture);
|
|
if(n < sizeof(g_xray_capture->frame_labels)) {
|
|
strcpy(g_xray_capture->frame_labels[n], buffer);
|
|
} else {
|
|
puts("XRay: Not enough entries in frame label buffer");
|
|
puts(" Add -DXRAY_FRAME_LBL_BUFSIZE=[n] to your CFLAGS");
|
|
}
|
|
}
|
|
va_end(args);
|
|
}
|
|
|
|
|
|
/* 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;
|
|
|
|
// initialize frame label
|
|
if(i < sizeof(capture->frame_labels)) {
|
|
capture->frame_labels[i][0] = 0;
|
|
}
|
|
|
|
#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 */
|