StackBackTrace.h 7.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251
  1. //-------------------------------------------------------------------------------------------------------
  2. // Copyright (C) Microsoft. All rights reserved.
  3. // Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
  4. //-------------------------------------------------------------------------------------------------------
  5. #pragma once
  6. #ifdef STACK_BACK_TRACE
  7. #ifndef _WIN32
  8. #include <execinfo.h>
  9. #define CaptureStackBackTrace(FramesToSkip, FramesToCapture, buffer, hash) \
  10. backtrace(buffer, FramesToCapture)
  11. #endif // !_WIN32
  12. class StackBackTrace
  13. {
  14. public:
  15. static const ULONG DefaultFramesToCapture = 30;
  16. static StackBackTrace * Capture(char* buffer, size_t bufSize, ULONG framesToSkip = 0);
  17. template <typename TAllocator> _NOINLINE
  18. static StackBackTrace * Capture(TAllocator * alloc, ULONG framesToSkip = 0, ULONG framesToCapture = DefaultFramesToCapture);
  19. template <typename TAllocator> _NOINLINE
  20. static StackBackTrace * Create(TAllocator * alloc, ULONG framesToCaptureLater = DefaultFramesToCapture);
  21. size_t Print();
  22. template<typename Fn>void Map(Fn fn); // The Fn is expected to be: void Fn(void*).
  23. ULONG Capture(ULONG framesToSkip);
  24. ULONG GetRequestedFrameCount() { return this->requestedFramesToCapture; }
  25. template <typename TAllocator>
  26. void Delete(TAllocator * alloc);
  27. private:
  28. // We want to skip at lease the StackBackTrace::Capture and the constructor frames
  29. static const ULONG BaseFramesToSkip = 2;
  30. _NOINLINE StackBackTrace(ULONG framesToSkip, ULONG framesToCapture);
  31. _NOINLINE StackBackTrace(ULONG framesToCapture);
  32. ULONG requestedFramesToCapture;
  33. ULONG framesCount;
  34. PVOID stackBackTrace[];
  35. };
  36. template <typename TAllocator>
  37. StackBackTrace *
  38. StackBackTrace::Capture(TAllocator * alloc, ULONG framesToSkip, ULONG framesToCapture)
  39. {
  40. return AllocatorNewPlusZ(TAllocator, alloc, sizeof(PVOID) * framesToCapture, StackBackTrace, framesToSkip, framesToCapture);
  41. }
  42. template <typename TAllocator>
  43. StackBackTrace* StackBackTrace::Create(TAllocator * alloc, ULONG framesToCaptureLater)
  44. {
  45. return AllocatorNewPlusZ(TAllocator, alloc, sizeof(PVOID)* framesToCaptureLater, StackBackTrace, framesToCaptureLater);
  46. }
  47. template <typename TAllocator>
  48. void StackBackTrace::Delete(TAllocator * alloc)
  49. {
  50. AllocatorDeletePlus(TAllocator, alloc, sizeof(PVOID)* requestedFramesToCapture, this);
  51. }
  52. template <typename Fn>
  53. void StackBackTrace::Map(Fn fn)
  54. {
  55. for (ULONG i = 0; i < this->framesCount; ++i)
  56. {
  57. fn(this->stackBackTrace[i]);
  58. }
  59. }
  60. class StackBackTraceNode
  61. {
  62. public:
  63. template <typename TAllocator>
  64. static void Prepend(TAllocator * allocator, StackBackTraceNode *& head, StackBackTrace * stackBackTrace)
  65. {
  66. head = AllocatorNew(TAllocator, allocator, StackBackTraceNode, stackBackTrace, head);
  67. }
  68. template <typename TAllocator>
  69. static void DeleteAll(TAllocator * allocator, StackBackTraceNode *& head)
  70. {
  71. StackBackTraceNode * curr = head;
  72. while (curr != nullptr)
  73. {
  74. StackBackTraceNode * next = curr->next;
  75. curr->stackBackTrace->Delete(allocator);
  76. AllocatorDelete(TAllocator, allocator, curr);
  77. curr = next;
  78. }
  79. head = nullptr;
  80. }
  81. static void PrintAll(StackBackTraceNode * head)
  82. {
  83. // We want to print them tail first because that is the first stack trace we added
  84. // Reverse the list
  85. StackBackTraceNode * curr = head;
  86. StackBackTraceNode * prev = nullptr;
  87. while (curr != nullptr)
  88. {
  89. StackBackTraceNode * next = curr->next;
  90. curr->next = prev;
  91. prev = curr;
  92. curr = next;
  93. }
  94. // print and reverse again.
  95. curr = prev;
  96. prev = nullptr;
  97. while (curr != nullptr)
  98. {
  99. curr->stackBackTrace->Print();
  100. StackBackTraceNode * next = curr->next;
  101. curr->next = prev;
  102. prev = curr;
  103. curr = next;
  104. }
  105. Assert(prev == head);
  106. }
  107. private:
  108. StackBackTraceNode(StackBackTrace * stackBackTrace, StackBackTraceNode * next) : stackBackTrace(stackBackTrace), next(next) {};
  109. StackBackTrace * stackBackTrace;
  110. StackBackTraceNode * next;
  111. };
  112. //
  113. // In memory TraceRing
  114. //
  115. uint _trace_ring_next_id();
  116. //
  117. // A buffer of size "T[count]", dynamically allocated (!useStatic) or
  118. // statically embedded (useStatic).
  119. //
  120. template <class T, LONG count, bool useStatic>
  121. struct _TraceRingBuffer
  122. {
  123. T* buf;
  124. _TraceRingBuffer() { buf = HeapNewArray(T, count); }
  125. ~_TraceRingBuffer() { HeapDeleteArray(count, buf); }
  126. };
  127. template <class T, LONG count>
  128. struct _TraceRingBuffer<T, count, true>
  129. {
  130. T buf[count];
  131. };
  132. //
  133. // A trace ring frame, consisting of id, header, and optionally stack
  134. //
  135. template <class Header, uint STACK_FRAMES>
  136. struct _TraceRingFrame
  137. {
  138. uint id; // unique id in order
  139. Header header;
  140. void* stack[STACK_FRAMES];
  141. };
  142. //
  143. // Trace code execution using an in-memory ring buffer. Capture each trace
  144. // point with a frame, which contains of custom data and optional stack trace.
  145. // Useful for instrumenting source code to track execution.
  146. //
  147. // Header: Custom header data type to capture in each frame. Used to
  148. // capture execution state at a trace point.
  149. // COUNT: Number of frames to keep in the ring buffer. When the buffer
  150. // is filled up, capture will start over from the beginning.
  151. // STACK_FRAMES:
  152. // Number of stack frames to capture for each trace frame.
  153. // This can be 0, only captures header data without stack trace.
  154. // USE_STATIC_BUFFER:
  155. // Use embedded buffer instead of dynamically allocate. This may
  156. // be helpful to avoid static data initialization problem.
  157. // SKIP_TOP_FRAMES:
  158. // Top stack frames to skip for each capture (only on _WIN32).
  159. //
  160. // Usage: Following captures the last 100 stacks that changes
  161. // scriptContext->debuggerMode:
  162. // struct DebugModeChange {
  163. // ScriptContext* scriptContext;
  164. // DebuggerMode debuggerMode;
  165. // };
  166. // static TraceRing<DebugModeChange, 100> s_ev;
  167. // Call at every debuggerMode change point:
  168. // DebugModeChange e = { scriptContext, debuggerMode };
  169. // s_ev.Capture(e);
  170. //
  171. // Examine trace frame i with its call stack in debugger:
  172. // gdb:
  173. // p s_ev.buf[i]
  174. // windbg:
  175. // ?? &s_ev.buf[i]
  176. // dds/dqs [above address]
  177. //
  178. template <class Header, LONG COUNT,
  179. uint STACK_FRAMES = 30,
  180. bool USE_STATIC_BUFFER = false,
  181. uint SKIP_TOP_FRAMES = 1>
  182. class TraceRing:
  183. protected _TraceRingBuffer<_TraceRingFrame<Header, STACK_FRAMES>, COUNT, USE_STATIC_BUFFER>
  184. {
  185. protected:
  186. LONG cur;
  187. public:
  188. TraceRing()
  189. {
  190. cur = (uint)-1;
  191. }
  192. template <class HeaderFunc>
  193. void Capture(const HeaderFunc& writeHeader)
  194. {
  195. LONG i = InterlockedIncrement(&cur);
  196. if (i >= COUNT)
  197. {
  198. InterlockedCompareExchange(&cur, i % COUNT, i);
  199. i %= COUNT;
  200. }
  201. auto* frame = &this->buf[i];
  202. *frame = {};
  203. frame->id = _trace_ring_next_id();
  204. writeHeader(&frame->header);
  205. if (STACK_FRAMES > 0)
  206. {
  207. CaptureStackBackTrace(SKIP_TOP_FRAMES, STACK_FRAMES, frame->stack, nullptr);
  208. }
  209. }
  210. void Capture(const Header& header)
  211. {
  212. Capture([&](Header* h)
  213. {
  214. *h = header;
  215. });
  216. }
  217. // Capture a trace (no header data, stack only)
  218. void Capture()
  219. {
  220. Capture([&](Header* h) { });
  221. }
  222. };
  223. #endif // STACK_BACK_TRACE