• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /* Copyright (c) 2013 The Chromium Authors. All rights reserved.
2  * Use of this source code is governed by a BSD-style license that can be
3  * found in the LICENSE file. */
4 
5 
6 /* XRay -- a simple profiler for Native Client */
7 
8 #include <assert.h>
9 #include <errno.h>
10 #include <stdarg.h>
11 #include <stdint.h>
12 #include <stdio.h>
13 #include <stdlib.h>
14 #include <string.h>
15 #include <sys/time.h>
16 #include <unistd.h>
17 #include "xray/xray_priv.h"
18 
19 #if defined(XRAY)
20 
21 /* GTSC - Get Time Stamp Counter */
22 #if defined(__amd64__) && !defined(XRAY_NO_RDTSC)
23 XRAY_INLINE uint64_t RDTSC64();
RDTSC64()24 uint64_t RDTSC64() {
25   uint64_t a, d;
26   __asm__ __volatile__("rdtsc" : "=a" (a), "=d" (d));
27   return ((uint64_t)a) | (((uint64_t)d) << 32);
28 }
29 #define GTSC(_x) _x = RDTSC64()
30 #elif defined(__i386__) && !defined(XRAY_NO_RDTSC)
31 #define GTSC(_x)      __asm__ __volatile__ ("rdtsc" : "=A" (_x));
32 #else
33 XRAY_INLINE uint64_t GTOD();
GTOD()34 uint64_t GTOD() {
35   struct timeval tv;
36   gettimeofday(&tv, NULL);
37   return (uint64_t)tv.tv_sec * 1000000 + (uint64_t)tv.tv_usec;
38 }
39 #define GTSC(_x) _x = GTOD();
40 #endif
41 
42 /* Use a TLS variable for cheap thread uid. */
43 __thread struct XRayTraceCapture* g_xray_capture = NULL;
44 __thread int g_xray_thread_id_placeholder = 0;
45 
46 
47 struct XRayTraceStackEntry {
48   uint32_t depth_addr;
49   uint64_t tsc;
50   uint32_t dest;
51   uint32_t annotation_index;
52 };
53 
54 
55 struct XRayTraceFrameEntry {
56   /* Indices into global tracebuffer */
57   int start;
58   int end;
59   uint64_t start_tsc;
60   uint64_t end_tsc;
61   uint64_t total_ticks;
62   int annotation_count;
63   bool valid;
64 
65 #ifndef XRAY_DISABLE_BROWSER_INTEGRATION
66   struct XRayTimestampPair start_time;
67   struct XRayTimestampPair end_time;
68 #endif
69 };
70 
71 
72 struct XRayTraceFrame {
73   struct XRayTraceFrameEntry* entry;
74   int head;
75   int tail;
76   int count;
77 };
78 
79 
80 struct XRayTraceCapture {
81   /* Common variables share cache line */
82   bool recording;
83   uint32_t stack_depth;
84   uint32_t max_stack_depth;
85   int buffer_index;
86   int buffer_size;
87   int disabled;
88   int annotation_count;
89   struct XRaySymbolTable* symbols;
90   bool initialized;
91   uint32_t annotation_filter;
92   uint32_t guard0;
93   struct XRayTraceStackEntry stack[XRAY_TRACE_STACK_SIZE] XRAY_ALIGN64;
94   uint32_t guard1;
95   uint32_t guard2;
96   char annotation[XRAY_ANNOTATION_STACK_SIZE] XRAY_ALIGN64;
97   uint32_t guard3;
98   struct XRayTraceBufferEntry* buffer;
99   struct XRayTraceFrame frame;
100 
101 #ifndef XRAY_DISABLE_BROWSER_INTEGRATION
102   int32_t thread_id;
103 #endif
104 } XRAY_ALIGN64;
105 
106 
107 #ifdef __cplusplus
108 extern "C" {
109 #endif
110 
111 #if defined(__pnacl__)
112 XRAY_NO_INSTRUMENT void __pnacl_profile_func_enter(const char* fname);
113 XRAY_NO_INSTRUMENT void __pnacl_profile_func_exit(const char* fname);
114 #else
115 XRAY_NO_INSTRUMENT void __cyg_profile_func_enter(void* this_fn,
116                                                  void* call_site);
117 XRAY_NO_INSTRUMENT void __cyg_profile_func_exit(void* this_fn,
118                                                 void* call_site);
119 #endif
120 
121 XRAY_INLINE int XRayTraceDecrementIndexInline(
122     struct XRayTraceCapture* capture, int index);
123 XRAY_INLINE int XRayTraceIncrementIndexInline(
124     struct XRayTraceCapture* capture, int index);
125 
126 
127 XRAY_NO_INSTRUMENT void __xray_profile_append_annotation(
128     struct XRayTraceCapture* capture,
129     struct XRayTraceStackEntry* se,
130     struct XRayTraceBufferEntry* be);
131 
132 #ifdef __cplusplus
133 }
134 #endif
135 
136 /* Asserts that the guard values haven't changed. */
XRayCheckGuards(struct XRayTraceCapture * capture)137 void XRayCheckGuards(struct XRayTraceCapture* capture) {
138   assert(capture->guard0 == XRAY_GUARD_VALUE_0x12345678);
139   assert(capture->guard1 == XRAY_GUARD_VALUE_0x12345678);
140   assert(capture->guard2 == XRAY_GUARD_VALUE_0x87654321);
141   assert(capture->guard3 == XRAY_GUARD_VALUE_0x12345678);
142 }
143 
144 /* Decrements the trace index, wrapping around if needed. */
XRayTraceDecrementIndexInline(struct XRayTraceCapture * capture,int index)145 int XRayTraceDecrementIndexInline(
146     struct XRayTraceCapture* capture, int index) {
147   --index;
148   if (index < 0)
149     index = capture->buffer_size - 1;
150   return index;
151 }
152 
153 /* Increments the trace index, wrapping around if needed. */
XRayTraceIncrementIndexInline(struct XRayTraceCapture * capture,int index)154 int XRayTraceIncrementIndexInline(
155     struct XRayTraceCapture* capture, int index) {
156   ++index;
157   if (index >= capture->buffer_size)
158     index = 0;
159   return index;
160 }
161 
162 /* Returns true if the trace entry is an annotation string. */
XRayTraceIsAnnotation(struct XRayTraceCapture * capture,int index)163 bool XRayTraceIsAnnotation(
164     struct XRayTraceCapture* capture, int index) {
165   struct XRayTraceBufferEntry* be = &capture->buffer[index];
166   char* dst = (char*)be;
167   return 0 == *dst;
168 }
169 
XRayTraceIncrementIndex(struct XRayTraceCapture * capture,int index)170 int XRayTraceIncrementIndex(struct XRayTraceCapture* capture, int index) {
171   return XRayTraceIncrementIndexInline(capture, index);
172 }
173 
XRayTraceDecrementIndex(struct XRayTraceCapture * capture,int index)174 int XRayTraceDecrementIndex(struct XRayTraceCapture* capture, int index) {
175   return XRayTraceDecrementIndexInline(capture, index);
176 }
177 
178 /* The entry in the tracebuffer at index is an annotation string. */
179 /* Calculate the next index value representing the next trace entry. */
XRayTraceSkipAnnotation(struct XRayTraceCapture * capture,int index)180 int XRayTraceSkipAnnotation(struct XRayTraceCapture* capture, int index) {
181   /* Annotations are strings embedded in the trace buffer. */
182   /* An annotation string can span multiple trace entries. */
183   /* Skip over the string by looking for zero termination. */
184   assert(capture);
185   assert(XRayTraceIsAnnotation(capture, index));
186   bool done = false;
187   int start_index = 1;
188   int i;
189   while (!done) {
190     char* str = (char*) &capture->buffer[index];
191     const int num = sizeof(capture->buffer[index]);
192     for (i = start_index; i < num; ++i) {
193       if (0 == str[i]) {
194         done = true;
195         break;
196       }
197     }
198     index = XRayTraceIncrementIndexInline(capture, index);
199     start_index = 0;
200   }
201   return index;
202 }
203 
204 
XRayTraceGetEntry(struct XRayTraceCapture * capture,int index)205 struct XRayTraceBufferEntry* XRayTraceGetEntry(
206     struct XRayTraceCapture* capture, int index) {
207   return &capture->buffer[index];
208 }
209 
210 /* Starting at index, return the index into the trace buffer */
211 /* for the next trace entry.  Index can wrap (ringbuffer) */
XRayTraceNextEntry(struct XRayTraceCapture * capture,int index)212 int XRayTraceNextEntry(struct XRayTraceCapture* capture, int index) {
213   if (XRayTraceIsAnnotation(capture, index))
214     index = XRayTraceSkipAnnotation(capture, index);
215   else
216     index = XRayTraceIncrementIndexInline(capture, index);
217   return index;
218 }
219 
XRayFrameGetTraceStartIndex(struct XRayTraceCapture * capture,int frame)220 int XRayFrameGetTraceStartIndex(struct XRayTraceCapture* capture, int frame) {
221   assert(capture);
222   assert(capture->initialized);
223   assert(!capture->recording);
224   return capture->frame.entry[frame].start;
225 }
226 
XRayFrameGetTraceEndIndex(struct XRayTraceCapture * capture,int frame)227 int XRayFrameGetTraceEndIndex(struct XRayTraceCapture* capture, int frame) {
228   assert(capture);
229   assert(capture->initialized);
230   assert(!capture->recording);
231   return capture->frame.entry[frame].end;
232 }
233 
234 /* Not very accurate, annotation strings will also be counted as "entries" */
XRayFrameGetTraceCount(struct XRayTraceCapture * capture,int frame)235 int XRayFrameGetTraceCount(
236     struct XRayTraceCapture* capture, int frame) {
237   assert(true == capture->initialized);
238   assert(frame >= 0);
239   assert(frame < capture->frame.count);
240   assert(!capture->recording);
241   int start = capture->frame.entry[frame].start;
242   int end = capture->frame.entry[frame].end;
243   int num;
244   if (start < end)
245     num = end - start;
246   else
247     num = capture->buffer_size - (start - end);
248   return num;
249 }
250 
251 /* Append a string to trace buffer. */
XRayTraceAppendString(struct XRayTraceCapture * capture,char * src)252 void XRayTraceAppendString(struct XRayTraceCapture* capture, char* src) {
253   int index = capture->buffer_index;
254   bool done = false;
255   int start_index = 1;
256   int s = 0;
257   int i;
258   char* dst = (char*)&capture->buffer[index];
259   const int num = sizeof(capture->buffer[index]);
260   dst[0] = 0;
261   while (!done) {
262     for (i = start_index; i < num; ++i) {
263       dst[i] = src[s];
264       if (0 == src[s]) {
265         dst[i] = 0;
266         done = true;
267         break;
268       }
269       ++s;
270     }
271     index = XRayTraceIncrementIndexInline(capture, index);
272     dst = (char*)&capture->buffer[index];
273     start_index = 0;
274   }
275   capture->buffer_index = index;
276 }
277 
278 /* Copies annotation from trace buffer to output string. */
XRayTraceCopyToString(struct XRayTraceCapture * capture,int index,char * dst)279 int XRayTraceCopyToString(
280     struct XRayTraceCapture* capture, int index, char* dst) {
281   assert(XRayTraceIsAnnotation(capture, index));
282   bool done = false;
283   int i;
284   int d = 0;
285   int start_index = 1;
286   while (!done) {
287     char* src = (char*) &capture->buffer[index];
288     const int num = sizeof(capture->buffer[index]);
289     for (i = start_index; i < num; ++i) {
290       dst[d] = src[i];
291       if (0 == src[i]) {
292         done = true;
293         break;
294       }
295       ++d;
296     }
297     index = XRayTraceIncrementIndexInline(capture, index);
298     start_index = 0;
299   }
300   return index;
301 }
302 
303 
304 /* Generic memory malloc for XRay */
305 /* validates pointer returned by malloc */
306 /* memsets memory block to zero */
XRayMalloc(size_t t)307 void* XRayMalloc(size_t t) {
308   void* data;
309   data = calloc(1, t);
310   if (NULL == data) {
311     printf("XRay: malloc(%d) failed, panic shutdown!\n", t);
312     exit(-1);
313   }
314   return data;
315 }
316 
317 
318 /* Generic memory free for XRay */
XRayFree(void * data)319 void XRayFree(void* data) {
320   assert(NULL != data);
321   free(data);
322 }
323 
324 
325 /* Main profile capture function that is called at the start */
326 /* of every instrumented function.  This function is implicitly */
327 /* called when code is compilied with the -finstrument-functions option */
328 #if defined(__pnacl__)
__pnacl_profile_func_enter(const char * this_fn)329 void __pnacl_profile_func_enter(const char* this_fn) {
330 #else
331 void __cyg_profile_func_enter(void* this_fn, void* call_site) {
332 #endif
333   struct XRayTraceCapture* capture = g_xray_capture;
334   if (capture && capture->recording) {
335     uint32_t depth = capture->stack_depth;
336     if (depth < capture->max_stack_depth) {
337       struct XRayTraceStackEntry* se = &capture->stack[depth];
338       uint32_t addr = (uint32_t)(uintptr_t)this_fn;
339       se->depth_addr = XRAY_PACK_DEPTH_ADDR(depth, addr);
340       se->dest = capture->buffer_index;
341       se->annotation_index = 0;
342       GTSC(se->tsc);
343       capture->buffer_index =
344         XRayTraceIncrementIndexInline(capture, capture->buffer_index);
345     }
346     ++capture->stack_depth;
347   }
348 }
349 
350 
351 /* Main profile capture function that is called at the exit of */
352 /* every instrumented function.  This function is implicity called */
353 /* when the code is compiled with the -finstrument-functions option */
354 #if defined(__pnacl__)
355 void __pnacl_profile_func_exit(const char* this_fn) {
356 #else
357 void __cyg_profile_func_exit(void* this_fn, void* call_site) {
358 #endif
359   struct XRayTraceCapture* capture = g_xray_capture;
360   if (capture && capture->recording) {
361     --capture->stack_depth;
362     if (capture->stack_depth < capture->max_stack_depth) {
363       uint32_t depth = capture->stack_depth;
364       struct XRayTraceStackEntry* se = &capture->stack[depth];
365       uint32_t buffer_index = se->dest;
366       uint64_t tsc;
367       struct XRayTraceBufferEntry* be = &capture->buffer[buffer_index];
368       GTSC(tsc);
369       be->depth_addr = se->depth_addr;
370       be->start_tick = se->tsc;
371       be->end_tick = tsc;
372       be->annotation_index = 0;
373       if (0 != se->annotation_index)
374         __xray_profile_append_annotation(capture, se, be);
375     }
376   }
377 }
378 
379 #ifndef XRAY_DISABLE_BROWSER_INTEGRATION
380 void XRayGetTSC(uint64_t* tsc) {
381   GTSC(*tsc);
382 }
383 
384 int32_t XRayGetSavedThreadID(struct XRayTraceCapture* capture) {
385   return capture->thread_id;
386 }
387 
388 struct XRayTimestampPair XRayFrameGetStartTimestampPair(
389     struct XRayTraceCapture* capture, int frame) {
390   return capture->frame.entry[frame].start_time;
391 }
392 
393 struct XRayTimestampPair XRayFrameGetEndTimestampPair(
394     struct XRayTraceCapture* capture, int frame) {
395   return capture->frame.entry[frame].end_time;
396 }
397 #endif
398 
399 /* Special case appending annotation string to trace buffer */
400 /* this function should only ever be called from __cyg_profile_func_exit() */
401 void __xray_profile_append_annotation(struct XRayTraceCapture* capture,
402                                       struct XRayTraceStackEntry* se,
403                                       struct XRayTraceBufferEntry* be) {
404   struct XRayTraceStackEntry* parent = se - 1;
405   int start = parent->annotation_index;
406   be->annotation_index = capture->buffer_index;
407   char* str = &capture->annotation[start];
408   XRayTraceAppendString(capture, str);
409   *str = 0;
410   ++capture->annotation_count;
411 }
412 
413 
414 
415 /* Annotates the trace buffer. no filtering. */
416 void __XRayAnnotate(const char* fmt, ...) {
417   va_list args;
418   struct XRayTraceCapture* capture = g_xray_capture;
419   /* Only annotate functions recorded in the trace buffer. */
420   if (capture && capture->initialized) {
421     if (0 == capture->disabled) {
422       if (capture->recording) {
423         char buffer[1024];
424         int r;
425         va_start(args, fmt);
426         r = vsnprintf(buffer, sizeof(buffer), fmt, args);
427         va_end(args);
428         {
429           /* Get current string ptr */
430           int depth = capture->stack_depth - 1;
431           struct XRayTraceStackEntry* se = &capture->stack[depth];
432           if (0 == se->annotation_index) {
433             struct XRayTraceStackEntry* parent = se - 1;
434             se->annotation_index = parent->annotation_index;
435           }
436           char* dst = &capture->annotation[se->annotation_index];
437           strcpy(dst, buffer);
438           int len = strlen(dst);
439           se->annotation_index += len;
440         }
441       }
442     }
443   }
444 }
445 
446 
447 /* Annotates the trace buffer with user strings.  Can be filtered. */
448 void __XRayAnnotateFiltered(const uint32_t filter, const char* fmt, ...) {
449   va_list args;
450   struct XRayTraceCapture* capture = g_xray_capture;
451   if (capture && capture->initialized) {
452     if (0 != (filter & capture->annotation_filter)) {
453       if (0 == capture->disabled) {
454         if (capture->recording) {
455           char buffer[XRAY_TRACE_ANNOTATION_LENGTH];
456           int r;
457           va_start(args, fmt);
458           r = vsnprintf(buffer, sizeof(buffer), fmt, args);
459           va_end(args);
460           {
461             /* get current string ptr */
462             int depth = capture->stack_depth - 1;
463             struct XRayTraceStackEntry* se = &capture->stack[depth];
464             if (0 == se->annotation_index) {
465               struct XRayTraceStackEntry* parent = se - 1;
466               se->annotation_index = parent->annotation_index;
467             }
468             char* dst = &capture->annotation[se->annotation_index];
469             strcpy(dst, buffer);
470             int len = strlen(dst);
471             se->annotation_index += len;
472           }
473         }
474       }
475     }
476   }
477 }
478 
479 
480 /* Allows user to specify annotation filter value, a 32 bit mask. */
481 void XRaySetAnnotationFilter(struct XRayTraceCapture* capture,
482                              uint32_t filter) {
483   capture->annotation_filter = filter;
484 }
485 
486 
487 /* Reset xray profiler. */
488 void XRayReset(struct XRayTraceCapture* capture) {
489   assert(capture);
490   assert(capture->initialized);
491   assert(!capture->recording);
492   capture->buffer_index = 0;
493   capture->stack_depth = 0;
494   capture->disabled = 0;
495   capture->frame.head = 0;
496   capture->frame.tail = 0;
497   memset(capture->frame.entry, 0,
498     sizeof(capture->frame.entry[0]) * capture->frame.count);
499   memset(&capture->stack, 0,
500     sizeof(capture->stack[0]) * XRAY_TRACE_STACK_SIZE);
501   XRayCheckGuards(capture);
502 }
503 
504 
505 /* Change the maximum stack depth captures are made. */
506 void XRaySetMaxStackDepth(struct XRayTraceCapture* capture, int stack_depth) {
507   assert(capture);
508   assert(capture->initialized);
509   assert(!capture->recording);
510   if (stack_depth < 1)
511     stack_depth = 1;
512   if (stack_depth >= XRAY_TRACE_STACK_SIZE)
513     stack_depth = (XRAY_TRACE_STACK_SIZE - 1);
514   capture->max_stack_depth = stack_depth;
515 }
516 
517 
518 int XRayFrameGetCount(struct XRayTraceCapture* capture) {
519   return capture->frame.count;
520 }
521 
522 int XRayFrameGetTail(struct XRayTraceCapture* capture) {
523   return capture->frame.tail;
524 }
525 
526 int XRayFrameGetHead(struct XRayTraceCapture* capture) {
527   return capture->frame.head;
528 }
529 
530 int XRayFrameGetPrev(struct XRayTraceCapture* capture, int i) {
531   i = i - 1;
532   if (i < 0)
533     i = capture->frame.count - 1;
534   return i;
535 }
536 
537 int XRayFrameGetNext(struct XRayTraceCapture* capture, int i) {
538   i = i + 1;
539   if (i >= capture->frame.count)
540     i = 0;
541   return i;
542 }
543 
544 bool XRayFrameIsValid(struct XRayTraceCapture* capture, int i) {
545   return capture->frame.entry[i].valid;
546 }
547 
548 uint64_t XRayFrameGetTotalTicks(struct XRayTraceCapture* capture, int i) {
549   return capture->frame.entry[i].total_ticks;
550 }
551 
552 int XRayFrameGetAnnotationCount(struct XRayTraceCapture* capture, int i) {
553   return capture->frame.entry[i].annotation_count;
554 }
555 
556 void XRayFrameMakeLabel(struct XRayTraceCapture* capture,
557                         int counter,
558                         char* label) {
559   snprintf(label, XRAY_MAX_LABEL, "@@@frame%d@@@", counter);
560 }
561 
562 
563 /* Scans the ring buffer going backwards to find last valid complete frame. */
564 /* Will mark whether frames are valid or invalid during the traversal. */
565 int XRayFrameFindTail(struct XRayTraceCapture* capture) {
566   int head = capture->frame.head;
567   int index = XRayFrameGetPrev(capture, head);
568   int total_capture = 0;
569   int last_valid_frame = index;
570   /* Check for no captures */
571   if (capture->frame.head == capture->frame.tail)
572     return capture->frame.head;
573   /* Go back and invalidate all captures that have been stomped. */
574   while (index != head) {
575     bool valid = capture->frame.entry[index].valid;
576     if (valid) {
577       total_capture += XRayFrameGetTraceCount(capture, index) + 1;
578       if (total_capture < capture->buffer_size) {
579         last_valid_frame = index;
580         capture->frame.entry[index].valid = true;
581       } else {
582         capture->frame.entry[index].valid = false;
583       }
584     }
585     index = XRayFrameGetPrev(capture, index);
586   }
587   return last_valid_frame;
588 }
589 
590 
591 /* Starts a new frame and enables capturing, and must be paired with */
592 /* XRayEndFrame()  Trace capturing only occurs on the thread which called */
593 /* XRayBeginFrame() and each instance of capture can only trace one thread */
594 /* at a time. */
595 void XRayStartFrame(struct XRayTraceCapture* capture) {
596   int i;
597   assert(NULL == g_xray_capture);
598   assert(capture->initialized);
599   assert(!capture->recording);
600   i = capture->frame.head;
601   XRayCheckGuards(capture);
602   /* Add a trace entry marker so we can detect wrap around stomping */
603   struct XRayTraceBufferEntry* be = &capture->buffer[capture->buffer_index];
604   be->depth_addr = XRAY_FRAME_MARKER;
605   capture->buffer_index =
606       XRayTraceIncrementIndex(capture, capture->buffer_index);
607   /* Set start of the frame we're about to trace */
608   capture->frame.entry[i].start = capture->buffer_index;
609   capture->disabled = 0;
610   capture->stack_depth = 1;
611 
612   /* The trace stack[0] is reserved */
613   memset(&capture->stack[0], 0, sizeof(capture->stack[0]));
614   /* Annotation index 0 is reserved to indicate no annotation */
615   capture->stack[0].annotation_index = 1;
616   capture->annotation[0] = 0;
617   capture->annotation[1] = 0;
618   capture->annotation_count = 0;
619   capture->recording = true;
620   GTSC(capture->frame.entry[i].start_tsc);
621   g_xray_capture = capture;
622 
623 #ifndef XRAY_DISABLE_BROWSER_INTEGRATION
624   capture->frame.entry[i].start_time = XRayGenerateTimestampsNow();
625 #endif
626 }
627 
628 
629 /* Ends a frame and disables capturing. Advances to the next frame. */
630 /* Must be paired with XRayStartFrame(), and called from the same thread. */
631 void XRayEndFrame(struct XRayTraceCapture* capture) {
632   int i;
633   assert(capture);
634   assert(capture->initialized);
635   assert(capture->recording);
636   assert(g_xray_capture == capture);
637   assert(0 == capture->disabled);
638   assert(1 == capture->stack_depth);
639   i = capture->frame.head;
640   GTSC(capture->frame.entry[i].end_tsc);
641   capture->frame.entry[i].total_ticks =
642     capture->frame.entry[i].end_tsc - capture->frame.entry[i].start_tsc;
643   capture->recording = NULL;
644   capture->frame.entry[i].end = capture->buffer_index;
645   capture->frame.entry[i].valid = true;
646   capture->frame.entry[i].annotation_count = capture->annotation_count;
647   capture->frame.head = XRayFrameGetNext(capture, capture->frame.head);
648   /* If the table is filled, bump the tail. */
649   if (capture->frame.head == capture->frame.tail)
650     capture->frame.tail = XRayFrameGetNext(capture, capture->frame.tail);
651   capture->frame.tail = XRayFrameFindTail(capture);
652   /* Check that we didn't stomp over trace entry marker. */
653   int marker = XRayTraceDecrementIndex(capture, capture->frame.entry[i].start);
654   struct XRayTraceBufferEntry* be = &capture->buffer[marker];
655   if (be->depth_addr != XRAY_FRAME_MARKER) {
656     fprintf(stderr,
657       "XRay: XRayStopFrame() detects insufficient trace buffer size!\n");
658     XRayReset(capture);
659   } else {
660     /* Replace marker with an empty annotation string. */
661     be->depth_addr = XRAY_NULL_ANNOTATION;
662     XRayCheckGuards(capture);
663   }
664   g_xray_capture = NULL;
665 
666 #ifndef XRAY_DISABLE_BROWSER_INTEGRATION
667   capture->frame.entry[i].end_time = XRayGenerateTimestampsNow();
668 #endif
669 }
670 
671 
672 /* Get the last frame captured.  Do not call while capturing. */
673 /* (ie call outside of XRayStartFrame() / XRayStopFrame() pair) */
674 int XRayGetLastFrame(struct XRayTraceCapture* capture) {
675   assert(capture);
676   assert(capture->initialized);
677   assert(!capture->recording);
678   assert(0 == capture->disabled);
679   assert(1 == capture->stack_depth);
680   int last_frame = XRayFrameGetPrev(capture, capture->frame.head);
681   return last_frame;
682 }
683 
684 
685 /* Disables capturing until a paired XRayEnableCapture() is called */
686 /* This call can be nested, but must be paired with an enable */
687 /* (If you need to just exclude a specific function and not its */
688 /* children, the XRAY_NO_INSTRUMENT modifier might be better) */
689 /* Must be called from same thread as XRayBeginFrame() / XRayEndFrame() */
690 void XRayDisableCapture(struct XRayTraceCapture* capture) {
691   assert(capture);
692   assert(capture == g_xray_capture);
693   assert(capture->initialized);
694   ++capture->disabled;
695   capture->recording = false;
696 }
697 
698 
699 /* Re-enables capture.  Must be paired with XRayDisableCapture() */
700 void XRayEnableCapture(struct XRayTraceCapture* capture) {
701   assert(capture);
702   assert(capture == g_xray_capture);
703   assert(capture->initialized);
704   assert(0 < capture->disabled);
705   --capture->disabled;
706   if (0 == capture->disabled) {
707     capture->recording = true;
708   }
709 }
710 
711 
712 
713 struct XRaySymbolTable* XRayGetSymbolTable(struct XRayTraceCapture* capture) {
714   return capture->symbols;
715 }
716 
717 
718 /* Initialize XRay */
719 struct XRayTraceCapture* XRayInit(int stack_depth,
720                                   int buffer_size,
721                                   int frame_count,
722                                   const char* mapfilename) {
723   struct XRayTraceCapture* capture;
724   capture = (struct XRayTraceCapture*)XRayMalloc(
725       sizeof(struct XRayTraceCapture));
726   int adj_frame_count = frame_count + 1;
727   size_t buffer_size_in_bytes =
728       sizeof(capture->buffer[0]) * buffer_size;
729   size_t frame_size_in_bytes =
730       sizeof(capture->frame.entry[0]) * adj_frame_count;
731   capture->buffer =
732       (struct XRayTraceBufferEntry *)XRayMalloc(buffer_size_in_bytes);
733   capture->frame.entry =
734       (struct XRayTraceFrameEntry *)XRayMalloc(frame_size_in_bytes);
735   capture->buffer_size = buffer_size;
736   capture->frame.count = adj_frame_count;
737   capture->frame.head = 0;
738   capture->frame.tail = 0;
739   capture->disabled = 0;
740   capture->annotation_filter = 0xFFFFFFFF;
741   capture->guard0 = XRAY_GUARD_VALUE_0x12345678;
742   capture->guard1 = XRAY_GUARD_VALUE_0x12345678;
743   capture->guard2 = XRAY_GUARD_VALUE_0x87654321;
744   capture->guard3 = XRAY_GUARD_VALUE_0x12345678;
745   capture->initialized = true;
746   capture->recording = false;
747   XRaySetMaxStackDepth(capture, stack_depth);
748   XRayReset(capture);
749 
750   /* Mapfile is optional; we don't need it for captures, only for reports. */
751   capture->symbols =
752       XRaySymbolTableCreate(XRAY_DEFAULT_SYMBOL_TABLE_SIZE);
753   if (NULL != mapfilename)
754     XRaySymbolTableParseMapfile(capture->symbols, mapfilename);
755 
756 #ifndef XRAY_DISABLE_BROWSER_INTEGRATION
757   /* Use the address of a thread local variable as a fake thread id. */
758   capture->thread_id = (int32_t)(&g_xray_thread_id_placeholder);
759 #endif
760 
761   return capture;
762 }
763 
764 
765 /* Shut down and free memory used by XRay. */
766 void XRayShutdown(struct XRayTraceCapture* capture) {
767   assert(capture);
768   assert(capture->initialized);
769   assert(!capture->recording);
770   XRayCheckGuards(capture);
771   if (NULL != capture->symbols) {
772     XRaySymbolTableFree(capture->symbols);
773   }
774   XRayFree(capture->frame.entry);
775   XRayFree(capture->buffer);
776   capture->initialized = false;
777   XRayFree(capture);
778 }
779 
780 #endif  /* XRAY */
781