wow. that was easy: git mv core nucleus
[feisty_meow.git] / nucleus / library / application / callstack_tracker.h
diff --git a/nucleus/library/application/callstack_tracker.h b/nucleus/library/application/callstack_tracker.h
new file mode 100644 (file)
index 0000000..bb75ada
--- /dev/null
@@ -0,0 +1,156 @@
+#ifndef CALLSTACK_TRACKER_CLASS
+#define CALLSTACK_TRACKER_CLASS
+
+/*****************************************************************************\
+*                                                                             *
+*  Name   : callstack_tracker                                                 *
+*  Author : Chris Koeritz                                                     *
+*                                                                             *
+*******************************************************************************
+* Copyright (c) 2007-$now By Author.  This program is free software; you can  *
+* redistribute it and/or modify it under the terms of the GNU General Public  *
+* License as published by the Free Software Foundation; either version 2 of   *
+* the License or (at your option) any later version.  This is online at:      *
+*     http://www.fsf.org/copyleft/gpl.html                                    *
+* Please send any updates to: fred@gruntose.com                               *
+\*****************************************************************************/
+
+#include "definitions.h"
+
+#ifdef ENABLE_CALLSTACK_TRACKING
+
+#include "build_configuration.h"
+#include "root_object.h"
+
+namespace application {
+
+// forward.
+class callstack_records;
+class callstack_tracker;
+
+//////////////
+
+callstack_tracker BASIS_EXTERN &program_wide_stack_trace();
+  //!< a global object that can be used to track the runtime callstack.
+
+//////////////
+
+//! This object can provide a backtrace at runtime of the invoking methods.
+/*!
+  The callstack tracking is hooked in through the FUNCDEF macros used to
+  set function names for logging.  Thus it will only be visible if those
+  macros are used fairly carefully or if people invoke the stack frame addition
+  method themselves.
+*/
+
+class callstack_tracker
+{
+public:
+  callstack_tracker();
+  virtual ~callstack_tracker();
+
+  DEFINE_CLASS_NAME("callstack_tracker");
+
+  bool push_frame(const char *class_name, const char *func, const char *file,
+          int line);
+    //!< adds a new stack from for the "class_name" in "function" at the "line".
+    /*!< this function should be invoked when entering a new stack frame.  the
+    "file" can be gotten from the __FILE__ macro and the "line" number can come
+    from __LINE__, but the "class_name" and "func" must be tracked some other
+    way.  we recommend the FUNCDEF macro.  this function might return false if
+    there is no longer any room for tracking more frames; that is a serious
+    issue that might indicate a runaway recursion or infinite loop. */
+
+  bool pop_frame();
+    //!< removes the last callstack frame off from our tracking.
+
+  bool update_line(int line);
+    //!< sets the line number within the current stack frame.
+    /*!< the current frame can reside across several line numbers, so this
+    allows the code to be more specific about the location of an invocation. */
+
+  char *full_trace() const;
+    //!< provides the current stack trace in a newly malloc'd string.
+    /*!< the user *must* free() the string returned. */
+
+  int full_trace_size() const;
+    //!< this returns the number of bytes needed for the above full_trace().
+
+  int depth() const { return _depth; }
+    //!< the current number of frames we know of.
+
+  double frames_in() const { return _frames_in; }
+    //!< reports the number of call stack frames that were added, total.
+
+  double frames_out() const { return _frames_out; }
+    //!< reports the number of call stack frames that were removed, total.
+
+  double highest() const { return _highest; }
+    //!< reports the maximum stack depth seen during the runtime so far.
+
+private:
+  callstack_records *_bt;  //!< the backtrace records for current program.
+  int _depth;  //!< the current number of frames we know of.
+  double _frames_in;  //!< number of frame additions.
+  double _frames_out;  //!< number of frame removals.
+  double _highest;  //!< the most number of frames in play at once.
+  bool _unusable;  //!< object has already been destroyed.
+};
+
+//////////////
+
+//! a small object that represents a stack trace in progress.
+/*! the object will automatically be destroyed when the containing scope
+exits.  this enables a users of the stack tracker to simply label their
+function name and get the frame added.  if they want finer grained tracking,
+they should update the line number periodically through their function,
+especially when memory is about to be allocated or where something might go
+wrong. */
+
+class frame_tracking_instance
+{
+public:
+  // these are not encapsulated, but be careful with the contents.
+  bool _frame_involved;  //!< has this object been added to the tracker?
+  char *_class, *_func, *_file;  //!< newly allocated copies.
+  int _line;
+
+  frame_tracking_instance(const char *class_name = "", const char *func = "",
+      const char *file = "", int line = 0, bool add_frame = false);
+    //!< as an automatic variable, this can hang onto frame information.
+    /*!< if "add_frame" is true, then this actually adds the stack frame in
+    question to the tracker.  thus if you use this class at the top of your
+    function, such as via the FUNCDEF macro, then you can forget about having
+    to pop the frame later. */
+
+  frame_tracking_instance(const frame_tracking_instance &to_copy);
+
+  ~frame_tracking_instance();
+    //!< releases the information *and* this stack frame in the tracker.
+
+  frame_tracking_instance &operator =(const frame_tracking_instance &to_copy);
+
+  void assign(const char *class_name, const char *func, const char *file,
+          int line);
+    //!< similar to assignment operator but doesn't require an object.
+
+  void clean();
+    //!< throws out our accumulated memory and pops frame if applicable.
+};
+
+void update_current_stack_frame_line_number(int line);
+  //!< sets the line number for the current frame in the global stack trace.
+
+#else // ENABLE_CALLSTACK_TRACKING
+  // bogus replacements for most commonly used callstack tracking support.
+  #define frame_tracking_instance
+  #define __trail_of_function(p1, p2, p3, p4, p5) if (func) {}
+    // the above actually trades on the name of the object we'd normally
+    // define.  it must match the object name in the FUNCDEF macro.
+  #define update_current_stack_frame_line_number(line)
+#endif // ENABLE_CALLSTACK_TRACKING
+
+} //namespace.
+
+#endif // outer guard.
+