Use fixed size stack traces; unify getStackTrace
[folly.git] / folly / experimental / exception_tracer / StackTrace.h
index b7354498c3fc127bfebe609f8a6edccdea88d2df..4218c5a1ab1c791216f434bc6fe6584eac77a0a2 100644 (file)
 #ifndef FOLLY_EXPERIMENTAL_EXCEPTION_TRACER_STACKTRACE_H_
 #define FOLLY_EXPERIMENTAL_EXCEPTION_TRACER_STACKTRACE_H_
 
-#include <stddef.h>
-#include <stdint.h>
+#include <cassert>
+#include <cstddef>
+#include <cstdint>
 
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-typedef struct StackTrace {
-  uintptr_t* frameIPs;  /* allocated with malloc() */
-  size_t frameCount;
-} StackTrace;
-
-/**
- * Get the current stack trace, allocating trace->frameIPs using malloc().
- * Skip the topmost "skip" frames.
- * Return 0 on success, a negative value on error.
- * On error, trace->frameIPs is NULL.
- */
-int getCurrentStackTrace(size_t skip, StackTrace* trace);
+namespace folly { namespace exception_tracer {
 
-/**
- * Free data allocated in a StackTrace object.
- */
-void destroyStackTrace(StackTrace* trace);
+constexpr size_t kMaxFrames = 500;
 
-/**
- * A stack of stack traces.
- */
-typedef struct StackTraceStack {
-  StackTrace trace;
-  struct StackTraceStack* next;
-} StackTraceStack;
-
-/**
- * Push the current stack trace onto the stack.
- * Return 0 on success, a negative value on error.
- * On error, the stack is unchanged.
- */
-int pushCurrentStackTrace(size_t skip, StackTraceStack** head);
-
-/**
- * Pop (and destroy) the top stack trace from the stack.
- */
-void popStackTrace(StackTraceStack** head);
+struct StackTrace {
+  StackTrace() : frameCount(0) { }
 
-/**
- * Completely empty the stack, destroying everything.
- */
-void clearStack(StackTraceStack** head);
-
-/**
- * Move the top stack trace from one stack to another.
- * Return 0 on success, a negative value on error (if the source stack is
- * empty)
- */
-int moveTop(StackTraceStack** from, StackTraceStack** to);
-
-/**
- * Initialize the stack tracing code.
- */
-void initStackTrace();
+  size_t frameCount;
+  uintptr_t addresses[kMaxFrames];
+};
+
+// note: no constructor so this can be __thread.
+// A StackTraceStack MUST be placed in zero-initialized memory.
+class StackTraceStack {
+  class Node;
+ public:
+  /**
+   * Push the current stack trace onto the stack.
+   * Returns false on failure (not enough memory, getting stack trace failed),
+   * true on success.
+   */
+  bool pushCurrent();
+
+  /**
+   * Pop the top stack trace from the stack.
+   * Returns true on success, false on failure (stack was empty).
+   */
+  bool pop();
+
+  /**
+   * Move the top stack trace from other onto this.
+   * Returns true on success, false on failure (other was empty).
+   */
+  bool moveTopFrom(StackTraceStack& other);
+
+  /**
+   * Clear the stack.
+   */
+
+  void clear();
+
+  /**
+   * Is the stack empty?
+   */
+  bool empty() const { return !top_; }
+
+  /**
+   * Return the top stack trace, or nullptr if the stack is empty.
+   */
+  StackTrace* top();
+
+  /**
+   * Return the stack trace following p, or nullptr if p is the bottom of
+   * the stack.
+   */
+  StackTrace* next(StackTrace* p);
+
+ private:
+  // In debug mode, we assert that we're in zero-initialized memory by
+  // checking that the two guards around top_ are zero.
+  void checkGuard() const {
+#ifndef NDEBUG
+    assert(guard1_ == 0 && guard2_ == 0);
+#endif
+  }
 
-#ifdef __cplusplus
-}  /* extern "C" */
+#ifndef NDEBUG
+  uintptr_t guard1_;
 #endif
+  Node* top_;
+#ifndef NDEBUG
+  uintptr_t guard2_;
+#endif
+};
+
+}}  // namespaces
 
 #endif /* FOLLY_EXPERIMENTAL_EXCEPTION_TRACER_STACKTRACE_H_ */