From 4bc311cf927208842650eacaed69eeacdd57f6df Mon Sep 17 00:00:00 2001 From: daniel-k Date: Wed, 18 May 2016 00:24:42 +0200 Subject: [PATCH] xray: import Xray profiler from Chromium Native Client --- hermit/usr/xray/LICENSE | 28 ++ hermit/usr/xray/browser.c | 163 +++++++ hermit/usr/xray/demangle.c | 25 ++ hermit/usr/xray/hashtable.c | 205 +++++++++ hermit/usr/xray/parsesymbols.c | 96 ++++ hermit/usr/xray/report.c | 209 +++++++++ hermit/usr/xray/stringpool.c | 94 ++++ hermit/usr/xray/symtable.c | 200 +++++++++ hermit/usr/xray/xray.c | 780 +++++++++++++++++++++++++++++++++ hermit/usr/xray/xray.h | 117 +++++ hermit/usr/xray/xray.odt | Bin 0 -> 52528 bytes hermit/usr/xray/xray_priv.h | 207 +++++++++ 12 files changed, 2124 insertions(+) create mode 100644 hermit/usr/xray/LICENSE create mode 100644 hermit/usr/xray/browser.c create mode 100644 hermit/usr/xray/demangle.c create mode 100644 hermit/usr/xray/hashtable.c create mode 100644 hermit/usr/xray/parsesymbols.c create mode 100644 hermit/usr/xray/report.c create mode 100644 hermit/usr/xray/stringpool.c create mode 100644 hermit/usr/xray/symtable.c create mode 100644 hermit/usr/xray/xray.c create mode 100644 hermit/usr/xray/xray.h create mode 100644 hermit/usr/xray/xray.odt create mode 100644 hermit/usr/xray/xray_priv.h diff --git a/hermit/usr/xray/LICENSE b/hermit/usr/xray/LICENSE new file mode 100644 index 000000000..29b40573f --- /dev/null +++ b/hermit/usr/xray/LICENSE @@ -0,0 +1,28 @@ +Copyright 2011, The Chromium Authors +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright +notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the following disclaimer +in the documentation and/or other materials provided with the +distribution. + * Neither the name of Google Inc. nor the names of its +contributors may be used to endorse or promote products derived from +this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/hermit/usr/xray/browser.c b/hermit/usr/xray/browser.c new file mode 100644 index 000000000..a150c5a3f --- /dev/null +++ b/hermit/usr/xray/browser.c @@ -0,0 +1,163 @@ +/* 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 */ + +#ifndef XRAY_DISABLE_BROWSER_INTEGRATION + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "ppapi/c/dev/ppb_trace_event_dev.h" +#include "xray/xray_priv.h" + + +#if defined(XRAY) +static PPB_Trace_Event_Dev* ppb_trace_event_interface = NULL; + +static const char* XRayGetName(struct XRaySymbolTable* symbols, + struct XRayTraceBufferEntry* e) { + uint32_t addr = XRAY_EXTRACT_ADDR(e->depth_addr); + struct XRaySymbol* symbol = XRaySymbolTableLookup(symbols, addr); + return XRaySymbolGetName(symbol); +} + +struct XRayTimestampPair XRayGenerateTimestampsNow(void) { + struct XRayTimestampPair pair; + assert(ppb_trace_event_interface); + + XRayGetTSC(&pair.xray); + pair.pepper = ppb_trace_event_interface->Now(); + return pair; +} + +/* see chromium/src/base/trace_event/trace_event.h */ +#define TRACE_VALUE_TYPE_UINT (2) +#define TRACE_VALUE_TYPE_DOUBLE (4) +#define TRACE_VALUE_TYPE_COPY_STRING (7) + +union TraceValue { + bool as_bool; + unsigned long long as_uint; + long long as_int; + double as_double; + const void* as_pointer; + const char* as_string; + }; + +void XRayBrowserTraceReport(struct XRayTraceCapture* capture) { + + const void* cat_enabled = ppb_trace_event_interface->GetCategoryEnabled( + "xray"); + struct XRaySymbolTable* symbols = XRayGetSymbolTable(capture); + + int32_t thread_id = XRayGetSavedThreadID(capture); + + int head = XRayFrameGetHead(capture); + int frame = XRayFrameGetTail(capture); + while(frame != head) { + + struct XRayTimestampPair start_time = XRayFrameGetStartTimestampPair( + capture, frame); + struct XRayTimestampPair end_time = XRayFrameGetEndTimestampPair( + capture, frame); + + double pdiff = (end_time.pepper - start_time.pepper); + double odiff = (end_time.xray - start_time.xray); + double scale_a = pdiff / odiff; + double scale_b = ((double)end_time.pepper) - (scale_a * end_time.xray); + printf("Xray timestamp calibration frame %d: %f %f\n", + frame, scale_a, scale_b); + + int start = XRayFrameGetTraceStartIndex(capture, frame); + int end = XRayFrameGetTraceEndIndex(capture, frame); + + struct XRayTraceBufferEntry** stack_base = XRayMalloc( + sizeof(struct XRayTraceBufferEntry*) * (XRAY_TRACE_STACK_SIZE + 1)); + struct XRayTraceBufferEntry** stack_top = stack_base; + *stack_top = NULL; + + uint32_t num_args = 0; + const char* arg_names[] = {"annotation"}; + uint8_t arg_types[] = {TRACE_VALUE_TYPE_COPY_STRING}; + uint64_t arg_values[] = {0}; + char annotation[XRAY_TRACE_ANNOTATION_LENGTH]; + + int i; + for(i = start; i != end; i = XRayTraceNextEntry(capture, i)) { + if (XRayTraceIsAnnotation(capture, i)) { + continue; + } + + uint32_t depth = XRAY_EXTRACT_DEPTH( + XRayTraceGetEntry(capture, i)->depth_addr); + + while(*stack_top && + XRAY_EXTRACT_DEPTH((*stack_top)->depth_addr) >= depth) { + struct XRayTraceBufferEntry* e = *(stack_top--); + ppb_trace_event_interface->AddTraceEventWithThreadIdAndTimestamp( + 'E', cat_enabled, + XRayGetName(symbols, e), + 0, thread_id, + (scale_a * e->end_tick) + scale_b, + 0, NULL, NULL, NULL, 0 + ); + } + + num_args = 0; + struct XRayTraceBufferEntry* e = XRayTraceGetEntry(capture, i); + uint32_t annotation_index = e->annotation_index; + if (annotation_index) { + XRayTraceCopyToString(capture, annotation_index, annotation); + + union TraceValue val; + val.as_string = (const char*)annotation; + + arg_values[0] = val.as_uint; + num_args = 1; + } + + ppb_trace_event_interface->AddTraceEventWithThreadIdAndTimestamp( + 'B', cat_enabled, + XRayGetName(symbols, e), + 0, thread_id, + (scale_a * e->start_tick) + scale_b, + num_args, arg_names, arg_types, arg_values, 0 + ); + + *(++stack_top) = e; + } + + while(*stack_top) { + struct XRayTraceBufferEntry* e = *(stack_top--); + ppb_trace_event_interface->AddTraceEventWithThreadIdAndTimestamp( + 'E', cat_enabled, + XRayGetName(symbols, e), + 0, thread_id, + (scale_a * e->end_tick) + scale_b, + 0, NULL, NULL, NULL, 0 + ); + } + + frame = XRayFrameGetNext(capture, frame); + XRayFree(stack_base); + } +} + +void XRayRegisterBrowserInterface(PPB_GetInterface interface) { + ppb_trace_event_interface = (PPB_Trace_Event_Dev*)interface( + PPB_TRACE_EVENT_DEV_INTERFACE); + assert(ppb_trace_event_interface); +} + +#endif /* XRAY */ +#endif /* XRAY_DISABLE_BROWSER_INTEGRATION */ \ No newline at end of file diff --git a/hermit/usr/xray/demangle.c b/hermit/usr/xray/demangle.c new file mode 100644 index 000000000..cf3cbc43d --- /dev/null +++ b/hermit/usr/xray/demangle.c @@ -0,0 +1,25 @@ +/* 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. */ + +#include "xray/xray_priv.h" + +/* Note name demangling requires linking against libstdc++ */ +/* If your platform does not support __cxa_demangle, re-compile XRay with: */ +/* -DXRAY_NO_DEMANGLE */ + +#if !defined(XRAY_NO_DEMANGLE) +extern +char* __cxa_demangle(const char* __mangled_name, char* __output_buffer, + size_t* __length, int* __status); +#endif + +const char* XRayDemangle(char* demangle, size_t size, const char* symbol) { +#if !defined(XRAY_NO_DEMANGLE) + int stat; + __cxa_demangle(symbol, demangle, &size, &stat); + if (stat == 0) + return demangle; +#endif + return symbol; +} diff --git a/hermit/usr/xray/hashtable.c b/hermit/usr/xray/hashtable.c new file mode 100644 index 000000000..45f2aa21c --- /dev/null +++ b/hermit/usr/xray/hashtable.c @@ -0,0 +1,205 @@ +/* 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. */ + + +/* Hashtable for XRay */ + +#include +#include +#include +#include +#include "xray/xray_priv.h" + +#if defined(XRAY) + +struct XRayHashTableEntry { + void* data; + uint32_t key; +}; + + +struct XRayHashTable { + int capacity; + int count; + struct XRayHashTableEntry* array; +}; + + +XRAY_NO_INSTRUMENT void XRayHashTableGrow(struct XRayHashTable* table); +XRAY_NO_INSTRUMENT uint32_t XRayHashTableHashKey(uint32_t key); +XRAY_NO_INSTRUMENT void XRayHashTableInit(struct XRayHashTable* table, + int32_t capacity); + +#define HASH_HISTO 1024 +int g_hash_histo[HASH_HISTO]; + + +/* Hashes a 32bit key into a 32bit value. */ +uint32_t XRayHashTableHashKey(uint32_t x) { + uint32_t y = x * 7919; + uint32_t z; + size_t c; + uint8_t* s = (uint8_t*)&y; + /* based on djb2 hash function */ + uint32_t h = 5381; + for (c = 0; c < sizeof(y); ++c) { + z = s[c]; + h = ((h << 5) + h) + z; + } + return h; +} + + +int XRayHashTableGetCapacity(struct XRayHashTable* table) { + return table->capacity; +} + + +int XRayHashTableGetCount(struct XRayHashTable* table) { + return table->count; +} + + +/* Looks up key in hashtable and returns blind data. */ +void* XRayHashTableLookup(struct XRayHashTable* table, uint32_t key) { + uint32_t h = XRayHashTableHashKey(key); + uint32_t m = table->capacity - 1; + uint32_t j = h & m; + uint32_t i; + int z = 1; + for (i = 0; i < m; ++i) { + /* An empty entry means the {key, data} isn't in the table. */ + if (NULL == table->array[j].data) { + ++g_hash_histo[0]; + return NULL; + } + /* Search for address */ + if (table->array[j].key == key) { + if (z >= HASH_HISTO) + z = HASH_HISTO - 1; + ++g_hash_histo[z]; + return table->array[j].data; + } + j = (j + 1) & m; + ++z; + } + /* Table was full, and there wasn't a match. */ + return NULL; +} + + +/* Inserts key & data into hash table. No duplicates. */ +void* XRayHashTableInsert(struct XRayHashTable* table, + void* data, uint32_t key) { + uint32_t h = XRayHashTableHashKey(key); + uint32_t m = table->capacity - 1; + uint32_t j = h & m; + uint32_t i; + for (i = 0; i < m; ++i) { + /* Take the first empty entry. */ + /* (the key,data isn't already in the table) */ + if (NULL == table->array[j].data) { + void* ret; + float ratio; + table->array[j].data = data; + table->array[j].key = key; + ++table->count; + ret = data; + ratio = (float)table->count / (float)table->capacity; + /* Double the capacity of the symtable if we've hit the ratio. */ + if (ratio > XRAY_SYMBOL_TABLE_MAX_RATIO) + XRayHashTableGrow(table); + return ret; + } + /* If the key is already present, return the data in the table. */ + if (table->array[j].key == key) { + return table->array[j].data; + } + j = (j + 1) & m; + } + /* Table was full */ + return NULL; +} + + +void* XRayHashTableAtIndex(struct XRayHashTable* table, int i) { + if ((i < 0) || (i >= table->capacity)) + return NULL; + return table->array[i].data; +} + + +/* Grows the hash table by doubling its capacity, */ +/* then re-inserts all the elements into the new table. */ +void XRayHashTableGrow(struct XRayHashTable* table) { + struct XRayHashTableEntry* old_array = table->array; + int old_capacity = table->capacity; + int new_capacity = old_capacity * 2; + int i; + printf("XRay: Growing a hash table...\n"); + XRayHashTableInit(table, new_capacity); + for (i = 0; i < old_capacity; ++i) { + void* data = old_array[i].data; + if (NULL != data) { + uint32_t key = old_array[i].key; + XRayHashTableInsert(table, data, key); + } + } + XRayFree(old_array); +} + + +void XRayHashTableInit(struct XRayHashTable* table, int32_t capacity) { + size_t bytes; + if (0 != (capacity & (capacity - 1))) { + printf("Xray: Hash table capacity should be a power of 2!\n"); + /* Round capacity up to next power of 2 */ + /* see http://aggregate.org/MAGIC/ */ + capacity--; + capacity |= capacity >> 1; + capacity |= capacity >> 2; + capacity |= capacity >> 4; + capacity |= capacity >> 8; + capacity |= capacity >> 16; + capacity++; + } + bytes = sizeof(table->array[0]) * capacity; + table->capacity = capacity; + table->count = 0; + table->array = (struct XRayHashTableEntry*)XRayMalloc(bytes); +} + + +/* Creates & inializes hash table. */ +struct XRayHashTable* XRayHashTableCreate(int capacity) { + struct XRayHashTable* table; + table = (struct XRayHashTable*)XRayMalloc(sizeof(*table)); + XRayHashTableInit(table, capacity); + memset(&g_hash_histo[0], 0, sizeof(g_hash_histo[0]) * HASH_HISTO); + return table; +} + + +/* Prints hash table performance to file; for debugging. */ +void XRayHashTableHisto(FILE* f) { + int i; + for (i = 0; i < HASH_HISTO; ++i) { + if (0 != g_hash_histo[i]) + fprintf(f, "hash_iterations[%d] = %d\n", i, g_hash_histo[i]); + } +} + + +/* Frees hash table. */ +/* Note: Does not free what the hash table entries point to. */ +void XRayHashTableFree(struct XRayHashTable* table) { + XRayFree(table->array); + table->capacity = 0; + table->count = 0; + table->array = NULL; + XRayFree(table); +} + +#endif /* XRAY */ + diff --git a/hermit/usr/xray/parsesymbols.c b/hermit/usr/xray/parsesymbols.c new file mode 100644 index 000000000..e2afd4413 --- /dev/null +++ b/hermit/usr/xray/parsesymbols.c @@ -0,0 +1,96 @@ +/* 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 +#include +#include +#include +#include +#include "xray/xray_priv.h" + +#if defined(XRAY) + +struct XRaySymbol* XRaySymbolTableCreateEntry(struct XRaySymbolTable* symtab, + char* line) { + uint32_t addr; + unsigned int uiaddr; + char symbol_text[XRAY_LINE_SIZE]; + char* parsed_symbol; + char* newln; + if (2 != sscanf(line, "%x %1023s", &uiaddr, symbol_text)) + return NULL; + if (uiaddr > 0x07FFFFFF) { + fprintf(stderr, "While parsing the mapfile, XRay encountered:\n"); + fprintf(stderr, "%s\n", line); + fprintf(stderr, + "XRay only works with code addresses 0x00000000 - 0x07FFFFFF\n"); + fprintf(stderr, "All functions must reside in this address space.\n"); + exit(-1); + } + addr = (uint32_t)uiaddr; + parsed_symbol = strstr(line, symbol_text); + newln = strstr(parsed_symbol, "\n"); + if (NULL != newln) { + *newln = 0; + } + return XRaySymbolTableAddByName(symtab, parsed_symbol, addr); +} + + +void XRaySymbolTableParseMapfile(struct XRaySymbolTable* symtab, + const char* mapfile) { + FILE* f; + char line[XRAY_LINE_SIZE]; + bool in_text = false; + bool in_link_once = false; + int in_link_once_counter = 0; + int num_symbols = 0; + + printf("XRay: opening mapfile %s\n", mapfile); + f = fopen(mapfile, "rt"); + if (0 == f) { + fprintf(stderr, "XRay: failed to open %s\n", mapfile); + return; + } + printf("XRay: parsing...\n"); + while (NULL != fgets(line, XRAY_LINE_SIZE, f)) { + if (line == strstr(line, " .text ")) { + in_text = true; + continue; + } + if (line == strstr(line, " .gnu.linkonce.t.")) { + in_link_once = true; + in_link_once_counter = 0; + continue; + } + if (line == strstr(line, " .text.")) { + in_link_once = true; + in_link_once_counter = 0; + continue; + } + if (line == strstr(line, " 0x")) { + if (in_text) { + XRaySymbolTableCreateEntry(symtab, line); + ++num_symbols; + } else if (in_link_once) { + if (in_link_once_counter != 0) { + if (NULL != XRaySymbolTableCreateEntry(symtab, line)) + ++num_symbols; + } else { + ++in_link_once_counter; + } + } + } else { + in_text = false; + in_link_once = false; + } + } + fclose(f); + printf("XRay: parsed %d symbols into symbol table\n", num_symbols); +} + +#endif // XRAY diff --git a/hermit/usr/xray/report.c b/hermit/usr/xray/report.c new file mode 100644 index 000000000..20e9e84ac --- /dev/null +++ b/hermit/usr/xray/report.c @@ -0,0 +1,209 @@ +/* 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include "xray/xray_priv.h" + +#if defined(XRAY) + +struct XRayTotal { + int index; + int frame; + uint64_t ticks; +}; + + +/* Dumps the trace report for a given frame. */ +void XRayTraceReport(struct XRayTraceCapture* capture, + FILE* f, + int frame, + char* label, + float percent_cutoff, + int ticks_cutoff) { + int index; + int start; + int end; + float total; + char space[257]; + struct XRaySymbolTable* symbols = XRayGetSymbolTable(capture); + memset(space, ' ', 256); + space[256] = 0; + if (NULL == f) { + f = stdout; + } + fprintf(f, + "====================================================================\n"); + if (NULL != label) + fprintf(f, "label %s\n", label); + fprintf(f, "\n"); + fprintf(f, + " Address Ticks Percent Function [annotation...]\n"); + fprintf(f, + "--------------------------------------------------------------------\n"); + total = XRayFrameGetTotalTicks(capture, frame); + start = XRayFrameGetTraceStartIndex(capture, frame); + end = XRayFrameGetTraceEndIndex(capture, frame); + index = start; + while (index != end) { + if (!XRayTraceIsAnnotation(capture, index)) { + const char* symbol_name; + char annotation[XRAY_TRACE_ANNOTATION_LENGTH]; + struct XRayTraceBufferEntry* e = XRayTraceGetEntry(capture, index); + uint32_t depth = XRAY_EXTRACT_DEPTH(e->depth_addr); + uint32_t addr = XRAY_EXTRACT_ADDR(e->depth_addr); + uint32_t annotation_index = e->annotation_index; + uint64_t ticks = + e->end_tick > e->start_tick ? e->end_tick - e->start_tick : 0; + float percent = 100.0f * (float)ticks / total; + if (percent >= percent_cutoff && ticks >= ticks_cutoff) { + struct XRaySymbol* symbol; + symbol = XRaySymbolTableLookup(symbols, addr); + symbol_name = XRaySymbolGetName(symbol); + if (0 != annotation_index) { + XRayTraceCopyToString(capture, annotation_index, annotation); + } else { + strcpy(annotation, ""); + } + fprintf(f, "0x%08X %12" PRIu64 " %5.1f %s%s %s\n", + (unsigned int)addr, ticks, percent, + &space[256 - depth], symbol_name, annotation); + } + } + index = XRayTraceNextEntry(capture, index); + } + fflush(f); +} + + +int qcompare(const void* a, const void* b) { + struct XRayTotal* ia = (struct XRayTotal*)a; + struct XRayTotal* ib = (struct XRayTotal*)b; + if (ib->ticks > ia->ticks) + return 1; + else if (ib->ticks < ia->ticks) + return -1; + return 0; +} + + +/* Dumps a frame report */ +void XRayFrameReport(struct XRayTraceCapture* capture, FILE* f) { + int i; + int head = XRayFrameGetHead(capture); + int frame = XRayFrameGetTail(capture); + int counter = 0; + int total_capture = 0; + struct XRayTotal* totals; + totals = (struct XRayTotal*) + alloca(XRayFrameGetCount(capture) * sizeof(struct XRayTotal)); + fprintf(f, "\n"); + fprintf(f, + "Frame# Total Ticks Capture size Annotations Label\n"); + fprintf(f, + "--------------------------------------------------------------------\n"); + while (frame != head) { + uint64_t total_ticks = XRayFrameGetTotalTicks(capture, frame); + int capture_size = XRayFrameGetTraceCount(capture, frame); + int annotation_count = XRayFrameGetAnnotationCount(capture, frame); + bool valid = XRayFrameIsValid(capture, frame); + char label[XRAY_MAX_LABEL]; + XRayFrameMakeLabel(capture, counter, label); + fprintf(f, " %3d %s %12" PRIu64 " %10d %10d %s\n", + counter, + valid ? " " : "*", + total_ticks, + capture_size, + annotation_count, + label); + totals[counter].index = counter; + totals[counter].frame = frame; + totals[counter].ticks = total_ticks; + total_capture += capture_size; + ++counter; + frame = XRayFrameGetNext(capture, frame); + } + fprintf(f, + "--------------------------------------------------------------------\n"); + fprintf(f, + "XRay: %d frame(s) %d total capture(s)\n", counter, total_capture); + fprintf(f, "\n"); + /* Sort and take average of the median cut */ + qsort(totals, counter, sizeof(struct XRayTotal), qcompare); + fprintf(f, "\n"); + fprintf(f, "Sorted by total ticks (most expensive first):\n"); + fprintf(f, "\n"); + fprintf(f, + "Frame# Total Ticks Capture size Annotations Label\n"); + fprintf(f, + "--------------------------------------------------------------------\n"); + for (i = 0; i < counter; ++i) { + int index = totals[i].index; + int frame = totals[i].frame; + uint64_t total_ticks = XRayFrameGetTotalTicks(capture, frame); + int capture_size = XRayFrameGetTraceCount(capture, frame); + int annotation_count = XRayFrameGetAnnotationCount(capture, frame); + char label[XRAY_MAX_LABEL]; + XRayFrameMakeLabel(capture, index, label); + fprintf(f, " %3d %12" PRIu64 " %10d %10d %s\n", + index, + total_ticks, + capture_size, + annotation_count, + label); + } + fflush(f); +} + + +/* Dump a frame report followed by trace report(s) for each frame. */ +void XRayReport(struct XRayTraceCapture* capture, + FILE* f, + float percent_cutoff, + int ticks_cutoff) { + int head = XRayFrameGetHead(capture); + int frame = XRayFrameGetTail(capture); + int counter = 0; + XRayFrameReport(capture, f); + fprintf(f, "\n"); + while (frame != head) { + char label[XRAY_MAX_LABEL]; + fprintf(f, "\n"); + XRayFrameMakeLabel(capture, counter, label); + XRayTraceReport(capture, f, frame, label, percent_cutoff, ticks_cutoff); + ++counter; + frame = XRayFrameGetNext(capture, frame); + } + fprintf(f, + "====================================================================\n"); +#if defined(XRAY_OUTPUT_HASH_COLLISIONS) + XRayHashTableHisto(capture, f); +#endif + fflush(f); +} + +/* Write a profile report to text file. */ +void XRaySaveReport(struct XRayTraceCapture* capture, + const char* filename, + float percent_cutoff, + int ticks_cutoff) { + FILE* f; + f = fopen(filename, "wt"); + if (NULL != f) { + XRayReport(capture, f, percent_cutoff, ticks_cutoff); + fclose(f); + } +} + +#endif /* XRAY */ diff --git a/hermit/usr/xray/stringpool.c b/hermit/usr/xray/stringpool.c new file mode 100644 index 000000000..bc8ac0956 --- /dev/null +++ b/hermit/usr/xray/stringpool.c @@ -0,0 +1,94 @@ +/* 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 string pool */ + +/* String pool holds a large pile of strings. */ +/* It is up to higher level data structures to avoid duplicates. */ +/* It is up to higher level data structures to provide fast lookups. */ + +/* _GNU_SOURCE must be defined prior to the inclusion of string.h + * so that strnlen is available with glibc */ +#define _GNU_SOURCE +#include +#include +#include "xray/xray_priv.h" + +#if defined(XRAY) + +struct XRayStringPoolNode { + struct XRayStringPoolNode* next; + char strings[XRAY_STRING_POOL_NODE_SIZE]; +}; + + +struct XRayStringPool { + struct XRayStringPoolNode* head; + struct XRayStringPoolNode* current; + int index; +}; + + +static struct XRayStringPoolNode* XRayStringPoolAllocNode() { + struct XRayStringPoolNode* s; + s = (struct XRayStringPoolNode *)XRayMalloc(sizeof(*s)); + s->next = NULL; + return s; +} + + +static int XRayStringPoolCurrentNodeSpaceAvail(struct XRayStringPool* pool) { + int i = pool->index; + return (XRAY_STRING_POOL_NODE_SIZE - i) - 1; +} + + +/* Append a string to the string pool. */ +char* XRayStringPoolAppend(struct XRayStringPool* pool, const char* src) { + /* Add +1 to STRING_POOL_NODE_SIZE to detect large strings */ + /* Add +1 to strnlen result to account for string termination */ + int n = strnlen(src, XRAY_STRING_POOL_NODE_SIZE + 1) + 1; + int a = XRayStringPoolCurrentNodeSpaceAvail(pool); + char* dst; + /* Don't accept strings larger than the pool node. */ + if (n >= (XRAY_STRING_POOL_NODE_SIZE - 1)) + return NULL; + /* If string doesn't fit, alloc a new node. */ + if (n > a) { + pool->current->next = XRayStringPoolAllocNode(); + pool->current = pool->current->next; + pool->index = 0; + } + /* Copy string and return a pointer to copy. */ + dst = &pool->current->strings[pool->index]; + strcpy(dst, src); + pool->index += n; + return dst; +} + + +/* Create & initialize a string pool instance. */ +struct XRayStringPool* XRayStringPoolCreate() { + struct XRayStringPool* pool; + pool = (struct XRayStringPool*)XRayMalloc(sizeof(*pool)); + pool->head = XRayStringPoolAllocNode(); + pool->current = pool->head; + return pool; +} + + +/* Free a string pool. */ +void XRayStringPoolFree(struct XRayStringPool* pool) { + struct XRayStringPoolNode* n = pool->head; + while (NULL != n) { + struct XRayStringPoolNode* c = n; + n = n->next; + XRayFree(c); + } + XRayFree(pool); +} + +#endif /* XRAY */ + diff --git a/hermit/usr/xray/symtable.c b/hermit/usr/xray/symtable.c new file mode 100644 index 000000000..1f0584c7f --- /dev/null +++ b/hermit/usr/xray/symtable.c @@ -0,0 +1,200 @@ +/* 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 symbol table */ + +#define _GNU_SOURCE +#include +#include +#include +#include + +#if defined(__GLIBC__) +#include +#endif + +#include "xray/xray_priv.h" +#define PNACL_STRING_OFFSET (0x10000000) + +#if defined(XRAY) + +bool g_symtable_debug = false; + +struct XRayFrameInfo { + int times_called; + int total_ticks; +}; + + +struct XRaySymbol { + const char* name; + struct XRayFrameInfo frames[XRAY_MAX_FRAMES]; +}; + + +struct XRaySymbolPoolNode { + struct XRaySymbolPoolNode* next; + struct XRaySymbol symbols[XRAY_SYMBOL_POOL_NODE_SIZE]; +}; + + +struct XRaySymbolPool { + struct XRaySymbolPoolNode* head; + struct XRaySymbolPoolNode* current; + int index; +}; + + +struct XRaySymbolTable { + int num_symbols; + struct XRayHashTable* hash_table; + struct XRayStringPool* string_pool; + struct XRaySymbolPool* symbol_pool; +}; + + +const char* XRaySymbolGetName(struct XRaySymbol* symbol) { + return (NULL == symbol) ? "(null)" : symbol->name; +} + + +struct XRaySymbol* XRaySymbolCreate(struct XRaySymbolPool* sympool, + const char* name) +{ + struct XRaySymbol* symbol; + symbol = XRaySymbolPoolAlloc(sympool); + symbol->name = name; + return symbol; +} + + +struct XRaySymbol* XRaySymbolPoolAlloc(struct XRaySymbolPool* sympool) { + struct XRaySymbol* symbol; + if (sympool->index >= XRAY_SYMBOL_POOL_NODE_SIZE) { + struct XRaySymbolPoolNode* new_pool; + new_pool = (struct XRaySymbolPoolNode*)XRayMalloc(sizeof(*new_pool)); + sympool->current->next = new_pool; + sympool->current = new_pool; + sympool->index = 0; + } + symbol = &sympool->current->symbols[sympool->index]; + ++sympool->index; + return symbol; +} + + +struct XRaySymbolPool* XRaySymbolPoolCreate() { + struct XRaySymbolPool* sympool; + struct XRaySymbolPoolNode* node; + sympool = (struct XRaySymbolPool*)XRayMalloc(sizeof(*sympool)); + node = (struct XRaySymbolPoolNode*)XRayMalloc(sizeof(*node)); + sympool->head = node; + sympool->current = node; + sympool->index = 0; + return sympool; +} + + +void XRaySymbolPoolFree(struct XRaySymbolPool* pool) { + struct XRaySymbolPoolNode* n = pool->head; + while (NULL != n) { + struct XRaySymbolPoolNode* c = n; + n = n->next; + XRayFree(c); + } + XRayFree(pool); +} + + +int XRaySymbolTableGetCount(struct XRaySymbolTable* symtab) { + return XRayHashTableGetCount(symtab->hash_table); +} + + +struct XRaySymbol* XRaySymbolTableAtIndex(struct XRaySymbolTable* symtab, + int i) { + return (struct XRaySymbol*)XRayHashTableAtIndex(symtab->hash_table, i); +} + +struct XRaySymbol* XRaySymbolTableAdd(struct XRaySymbolTable* symtab, + struct XRaySymbol* symbol, + uint32_t addr) { + struct XRaySymbol* sym = (struct XRaySymbol*) + XRayHashTableInsert(symtab->hash_table, symbol, addr); + symtab->num_symbols = XRayHashTableGetCount(symtab->hash_table); + return sym; +} + +struct XRaySymbol* XRaySymbolTableAddByName(struct XRaySymbolTable* symtab, + const char* name, uint32_t addr) { + char* recorded_name; + struct XRaySymbol* symbol; + char buffer[XRAY_LINE_SIZE]; + const char* demangled_name = XRayDemangle(buffer, XRAY_LINE_SIZE, name); + /* record the demangled symbol name into the string pool */ + recorded_name = XRayStringPoolAppend(symtab->string_pool, demangled_name); + if (g_symtable_debug) + printf("adding symbol %s\n", recorded_name); + /* construct a symbol and put it in the symbol table */ + symbol = XRaySymbolCreate(symtab->symbol_pool, recorded_name); + return XRaySymbolTableAdd(symtab, symbol, addr); +} + +struct XRaySymbol* XRaySymbolTableLookup(struct XRaySymbolTable* symtab, + uint32_t addr) { + void *x = XRayHashTableLookup(symtab->hash_table, addr); + struct XRaySymbol* r = (struct XRaySymbol*)x; + +#if defined(__pnacl__) + if (r == NULL) { + /* Addresses are trimed to 24 bits for internal storage, so we need to + * add this offset back in order to get the real address. + */ + addr |= PNACL_STRING_OFFSET; + const char* name = (const char*)addr; + struct XRaySymbol* symbol = XRaySymbolCreate(symtab->symbol_pool, name); + r = XRaySymbolTableAdd(symtab, symbol, addr); + } +#endif + +#if defined(__GLIBC__) + if (r == NULL) { + Dl_info info; + if (dladdr((const void*)addr, &info) != 0) + if (info.dli_sname) + r = XRaySymbolTableAddByName(symtab, info.dli_sname, addr); + } +#endif + return r; +} + + +/* Returns total number of symbols in the table. */ +int XRaySymbolCount(struct XRaySymbolTable* symtab) { + return symtab->num_symbols; +} + + +/* Creates and inializes a symbol table. */ +struct XRaySymbolTable* XRaySymbolTableCreate(int size) { + struct XRaySymbolTable* symtab; + symtab = (struct XRaySymbolTable*)XRayMalloc(sizeof(*symtab)); + symtab->num_symbols = 0; + symtab->string_pool = XRayStringPoolCreate(); + symtab->hash_table = XRayHashTableCreate(size); + symtab->symbol_pool = XRaySymbolPoolCreate(); + return symtab; +} + + +/* Frees a symbol table. */ +void XRaySymbolTableFree(struct XRaySymbolTable* symtab) { + XRayStringPoolFree(symtab->string_pool); + XRaySymbolPoolFree(symtab->symbol_pool); + XRayHashTableFree(symtab->hash_table); + symtab->num_symbols = 0; + XRayFree(symtab); +} + +#endif /* XRAY */ diff --git a/hermit/usr/xray/xray.c b/hermit/usr/xray/xray.c new file mode 100644 index 000000000..9e49591fe --- /dev/null +++ b/hermit/usr/xray/xray.c @@ -0,0 +1,780 @@ +/* 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include "xray/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; + +#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) { + 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 */ diff --git a/hermit/usr/xray/xray.h b/hermit/usr/xray/xray.h new file mode 100644 index 000000000..32feed0a0 --- /dev/null +++ b/hermit/usr/xray/xray.h @@ -0,0 +1,117 @@ +/* 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 */ + + +#ifndef LIBRARIES_XRAY_XRAY_H_ +#define LIBRARIES_XRAY_XRAY_H_ + +#include + +#ifndef XRAY_DISABLE_BROWSER_INTEGRATION +#include "ppapi/c/ppb.h" +#endif + +#if defined(__arm__) +#undef XRAY +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +#define XRAY_NO_INSTRUMENT __attribute__((no_instrument_function)) +#define XRAY_INLINE __attribute__((always_inline, no_instrument_function)) + +#if defined(XRAY) + +/* Do not call __XRayAnnotate* directly; instead use the */ +/* XRayAnnotate() macros below. */ +XRAY_NO_INSTRUMENT void __XRayAnnotate(const char* str, ...) + __attribute__ ((format(printf, 1, 2))); +XRAY_NO_INSTRUMENT void __XRayAnnotateFiltered(const uint32_t filter, + const char* str, ...) __attribute__ ((format(printf, 2, 3))); + +/* This is the beginning of the public XRay API */ + +/* Ok if mapfilename is NULL, no symbols will be loaded. On glibc builds, + * XRay will also attempt to populate the symbol table with dladdr() + */ +XRAY_NO_INSTRUMENT struct XRayTraceCapture* XRayInit(int stack_size, + int buffer_size, + int frame_count, + const char* mapfilename); +XRAY_NO_INSTRUMENT void XRayShutdown(struct XRayTraceCapture* capture); +XRAY_NO_INSTRUMENT void XRayStartFrame(struct XRayTraceCapture* capture); +XRAY_NO_INSTRUMENT void XRayEndFrame(struct XRayTraceCapture* capture); +XRAY_NO_INSTRUMENT void XRaySetAnnotationFilter( + struct XRayTraceCapture* capture, uint32_t filter); +XRAY_NO_INSTRUMENT void XRaySaveReport(struct XRayTraceCapture* capture, + const char* filename, + float percent_cutoff, + int cycle_cutoff); +XRAY_NO_INSTRUMENT void XRayReport(struct XRayTraceCapture* capture, + FILE* f, + float percent_cutoff, + int ticks_cutoff); + +#ifndef XRAY_DISABLE_BROWSER_INTEGRATION +XRAY_NO_INSTRUMENT void XRayBrowserTraceReport( + struct XRayTraceCapture* capture); +XRAY_NO_INSTRUMENT void XRayRegisterBrowserInterface( + PPB_GetInterface get_browser_interface); +#endif /* XRAY_DISABLE_BROWSER_INTEGRATION */ + + +#if defined(XRAY_ANNOTATE) +#define XRayAnnotate(...) __XRayAnnotate(__VA_ARGS__) +#define XRayAnnotateFiltered(...) __XRayAnnotateFiltered(__VA_ARGS__) +#else +#define XRayAnnotate(...) +#define XRayAnnotateFiltered(...) +#endif +/* This is the end of the public XRay API */ + +#else /* defined(XRAY) */ + +/* Builds that don't define XRAY will use these 'null' functions instead. */ + +#define XRayAnnotate(...) +#define XRayAnnotateFiltered(...) + +inline struct XRayTraceCapture* XRayInit(int stack_size, + int buffer_size, + int frame_count, + const char* mapfilename) { + return NULL; +} +inline void XRayShutdown(struct XRayTraceCapture* capture) {} +inline void XRayStartFrame(struct XRayTraceCapture* capture) {} +inline void XRayEndFrame(struct XRayTraceCapture* capture) {} +inline void XRaySetAnnotationFilter(struct XRayTraceCapture* capture, + uint32_t filter) {} +inline void XRaySaveReport(struct XRayTraceCapture* capture, + const char* filename, + float percent_cutoff, + int cycle_cutoff) {} +inline void XRayReport(struct XRayTraceCapture* capture, + FILE* f, + float percent_cutoff, + int ticks_cutoff) {} + +#ifndef XRAY_DISABLE_BROWSER_INTEGRATION +inline void XRayBrowserTraceReport(struct XRayTraceCapture* capture) {} +inline void XRayRegisterBrowserInterface( + PPB_GetInterface get_browser_interface) {} +#endif /* XRAY_DISABLE_BROWSER_INTEGRATION */ + + +#endif /* defined(XRAY) */ + +#ifdef __cplusplus +} +#endif + +#endif /* LIBRARIES_XRAY_XRAY_H_ */ diff --git a/hermit/usr/xray/xray.odt b/hermit/usr/xray/xray.odt new file mode 100644 index 0000000000000000000000000000000000000000..a7b1deee2e707604bc647092478553f5352938ba GIT binary patch literal 52528 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

VeEnr8l`^3{dfbMUzE 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 literal 0 HcmV?d00001 diff --git a/hermit/usr/xray/xray_priv.h b/hermit/usr/xray/xray_priv.h new file mode 100644 index 000000000..714c91daa --- /dev/null +++ b/hermit/usr/xray/xray_priv.h @@ -0,0 +1,207 @@ +/* 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 */ + +/* This header file is the private internal interface. */ + +#ifndef LIBRARIES_XRAY_XRAY_PRIV_H_ +#define LIBRARIES_XRAY_XRAY_PRIV_H_ + +#include +#include +#include +#include +#include "xray/xray.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#if defined(XRAY) + +#define XRAY_FORCE_INLINE __attribute__((always_inline, no_instrument_function)) + +#define XRAY_TRACE_STACK_SIZE (256) +#define XRAY_TRACE_ANNOTATION_LENGTH (2048) +#define XRAY_TRACE_BUFFER_SIZE (1048576) +#define XRAY_ANNOTATION_STACK_SIZE ((XRAY_TRACE_STACK_SIZE) * \ + (XRAY_TRACE_ANNOTATION_LENGTH)) +#define XRAY_STRING_POOL_NODE_SIZE (32768) +#define XRAY_FRAME_MARKER (0xFFFFFFFF) +#define XRAY_NULL_ANNOTATION (0x0) +#define XRAY_FUNCTION_ALIGNMENT_BITS (4) +#define XRAY_ADDR_MASK (0xFFFFFF00) +#define XRAY_ADDR_SHIFT (4) +#define XRAY_DEPTH_MASK (0x000000FF) +#define XRAY_SYMBOL_TABLE_MAX_RATIO (0.66f) +#define XRAY_LINE_SIZE (1024) +#define XRAY_MAX_FRAMES (60) +#define XRAY_MAX_LABEL (64) +#define XRAY_DEFAULT_SYMBOL_TABLE_SIZE (4096) +#define XRAY_SYMBOL_POOL_NODE_SIZE (1024) +#define XRAY_GUARD_VALUE_0x12345678 (0x12345678) +#define XRAY_GUARD_VALUE_0x87654321 (0x87654321) +#define XRAY_EXTRACT_ADDR(x) (((x) & XRAY_ADDR_MASK) >> XRAY_ADDR_SHIFT) +#define XRAY_EXTRACT_DEPTH(x) ((x) & XRAY_DEPTH_MASK) +#define XRAY_PACK_ADDR(x) (((x) << XRAY_ADDR_SHIFT) & XRAY_ADDR_MASK) +#define XRAY_PACK_DEPTH(x) ((x) & XRAY_DEPTH_MASK) +#define XRAY_PACK_DEPTH_ADDR(d, a) (XRAY_PACK_DEPTH(d) | XRAY_PACK_ADDR(a)) +#define XRAY_ALIGN64 __attribute((aligned(64))) + +struct XRayStringPool; +struct XRayHashTable; +struct XRaySymbolPool; +struct XRaySymbol; +struct XRaySymbolTable; +struct XRayTraceCapture; + +struct XRayTraceBufferEntry { + uint32_t depth_addr; + uint32_t annotation_index; + uint64_t start_tick; + uint64_t end_tick; +}; + + +/* Important: don't instrument xray itself, so use */ +/* XRAY_NO_INSTRUMENT on all xray functions */ + +XRAY_NO_INSTRUMENT char* XRayStringPoolAppend(struct XRayStringPool* pool, + const char* src); +XRAY_NO_INSTRUMENT struct XRayStringPool* XRayStringPoolCreate(); +XRAY_NO_INSTRUMENT void XRayStringPoolFree(struct XRayStringPool* pool); + +XRAY_NO_INSTRUMENT void* XRayHashTableLookup(struct XRayHashTable* table, + uint32_t addr); +XRAY_NO_INSTRUMENT void* XRayHashTableInsert(struct XRayHashTable* table, + void* data, uint32_t addr); +XRAY_NO_INSTRUMENT void* XRayHashTableAtIndex( + struct XRayHashTable* table, int i); +XRAY_NO_INSTRUMENT int XRayHashTableGetCapacity(struct XRayHashTable* table); +XRAY_NO_INSTRUMENT int XRayHashTableGetCount(struct XRayHashTable* table); +XRAY_NO_INSTRUMENT struct XRayHashTable* XRayHashTableCreate(int capacity); +XRAY_NO_INSTRUMENT void XRayHashTableFree(struct XRayHashTable* table); +XRAY_NO_INSTRUMENT void XRayHashTableHisto(FILE* f); + +XRAY_NO_INSTRUMENT struct XRaySymbol* XRaySymbolPoolAlloc( + struct XRaySymbolPool* sympool); +XRAY_NO_INSTRUMENT struct XRaySymbolPool* XRaySymbolPoolCreate(); +XRAY_NO_INSTRUMENT void XRaySymbolPoolFree(struct XRaySymbolPool* sympool); + +XRAY_NO_INSTRUMENT const char* XRayDemangle(char* demangle, size_t buffersize, + const char* symbol); + +XRAY_NO_INSTRUMENT const char* XRaySymbolGetName(struct XRaySymbol* symbol); +XRAY_NO_INSTRUMENT struct XRaySymbol* XRaySymbolCreate( + struct XRaySymbolPool* sympool, const char* name); +XRAY_NO_INSTRUMENT void XRaySymbolFree(struct XRaySymbol* symbol); +XRAY_NO_INSTRUMENT int XRaySymbolCount(struct XRaySymbolTable* symtab); + +XRAY_NO_INSTRUMENT struct XRaySymbol* XRaySymbolTableCreateEntry( + struct XRaySymbolTable* symtab, char* line); +XRAY_NO_INSTRUMENT void XRaySymbolTableParseMapfile( + struct XRaySymbolTable* symbols, const char* mapfilename); + +XRAY_NO_INSTRUMENT struct XRaySymbol* XRaySymbolTableAddByName( + struct XRaySymbolTable* symtab, const char* name, uint32_t addr); + +XRAY_NO_INSTRUMENT int XRaySymbolTableGetCount(struct XRaySymbolTable* symtab); +XRAY_NO_INSTRUMENT struct XRaySymbol* XRaySymbolTableLookup( + struct XRaySymbolTable* symbols, uint32_t addr); +XRAY_NO_INSTRUMENT struct XRaySymbol* XRaySymbolTableAtIndex( + struct XRaySymbolTable* symbols, int i); +XRAY_NO_INSTRUMENT struct XRaySymbolTable* XRaySymbolTableCreate(int size); +XRAY_NO_INSTRUMENT void XRaySymbolTableFree(struct XRaySymbolTable* symbtab); + +XRAY_NO_INSTRUMENT struct XRaySymbolTable* XRayGetSymbolTable( + struct XRayTraceCapture* capture); + +XRAY_NO_INSTRUMENT void XRayCheckGuards(struct XRayTraceCapture* capture); + +XRAY_NO_INSTRUMENT struct XRayTraceBufferEntry* XRayTraceGetEntry( + struct XRayTraceCapture* capture, int index); +XRAY_NO_INSTRUMENT int XRayTraceIncrementIndex( + struct XRayTraceCapture* capture, int i); +XRAY_NO_INSTRUMENT int XRayTraceDecrementIndex( + struct XRayTraceCapture* capture, int i); +XRAY_NO_INSTRUMENT bool XRayTraceIsAnnotation( + struct XRayTraceCapture* capture, int index); +XRAY_NO_INSTRUMENT void XRayTraceAppendString( + struct XRayTraceCapture* capture, char* src); +XRAY_NO_INSTRUMENT int XRayTraceCopyToString( + struct XRayTraceCapture* capture, int index, char* dst); +XRAY_NO_INSTRUMENT int XRayTraceSkipAnnotation( + struct XRayTraceCapture* capture, int index); +XRAY_NO_INSTRUMENT int XRayTraceNextEntry( + struct XRayTraceCapture* capture, int index); + +XRAY_NO_INSTRUMENT void XRayFrameMakeLabel(struct XRayTraceCapture* capture, + int counter, + char* label); +XRAY_NO_INSTRUMENT int XRayFrameFindTail(struct XRayTraceCapture* capture); + +XRAY_NO_INSTRUMENT int XRayFrameGetCount(struct XRayTraceCapture* capture); +XRAY_NO_INSTRUMENT int XRayFrameGetHead(struct XRayTraceCapture* capture); +XRAY_NO_INSTRUMENT int XRayFrameGetTail(struct XRayTraceCapture* capture); +XRAY_NO_INSTRUMENT int XRayFrameGetNext( + struct XRayTraceCapture* capture, int index); +XRAY_NO_INSTRUMENT uint64_t XRayFrameGetTotalTicks( + struct XRayTraceCapture* capture, int frame); +XRAY_NO_INSTRUMENT int XRayFrameGetTraceCount( + struct XRayTraceCapture* capture, int frame); +XRAY_NO_INSTRUMENT int XRayFrameGetTraceStartIndex( + struct XRayTraceCapture* capture, int frame); +XRAY_NO_INSTRUMENT int XRayFrameGetTraceEndIndex( + struct XRayTraceCapture* capture, int frame); +XRAY_NO_INSTRUMENT int XRayFrameGetAnnotationCount( + struct XRayTraceCapture* capture, int frame); +XRAY_NO_INSTRUMENT bool XRayFrameIsValid( + struct XRayTraceCapture* capture, int frame); + + +XRAY_NO_INSTRUMENT void XRayTraceReport(struct XRayTraceCapture* capture, + FILE* f, + int frame, + char* label, + float percent_cutoff, + int ticks_cutoff); +XRAY_NO_INSTRUMENT void XRayFrameReport(struct XRayTraceCapture* capture, + FILE* f); + +XRAY_NO_INSTRUMENT void* XRayMalloc(size_t t); +XRAY_NO_INSTRUMENT void XRayFree(void* data); + +XRAY_NO_INSTRUMENT void XRaySetMaxStackDepth( + struct XRayTraceCapture* capture, int stack_depth); +XRAY_NO_INSTRUMENT int XRayGetLastFrame(struct XRayTraceCapture* capture); +XRAY_NO_INSTRUMENT void XRayDisableCapture(struct XRayTraceCapture* capture); +XRAY_NO_INSTRUMENT void XRayEnableCapture(struct XRayTraceCapture* capture); +XRAY_NO_INSTRUMENT void XRayLoadMapfile( + struct XRayTraceCapture* capture, const char* mapfilename); + +struct XRayTimestampPair { + uint64_t xray; /* internal xray timestamp */ + int64_t pepper; /* corresponding timestamp from PPAPI interface */ +}; + +#ifndef XRAY_DISABLE_BROWSER_INTEGRATION +XRAY_NO_INSTRUMENT void XRayGetTSC(uint64_t* tsc); +XRAY_NO_INSTRUMENT int32_t XRayGetSavedThreadID( + struct XRayTraceCapture* capture); +XRAY_NO_INSTRUMENT struct XRayTimestampPair XRayFrameGetStartTimestampPair( + struct XRayTraceCapture* capture, int frame); +XRAY_NO_INSTRUMENT struct XRayTimestampPair XRayFrameGetEndTimestampPair( + struct XRayTraceCapture* capture, int frame); +XRAY_NO_INSTRUMENT struct XRayTimestampPair XRayGenerateTimestampsNow(void); +#endif + + +#endif /* defined(XRAY) */ + +#ifdef __cplusplus +} +#endif + +#endif /* LIBRARIES_XRAY_XRAY_PRIV_H_ */