15 #ifdef ENABLE_CALLSTACK_TRACKING
30 #include <unordered_map>
32 using namespace basis;
53 basis::mutex &callstack_tracker::__callstack_tracker_synchronizer()
56 return __global_synch_callstacks;
61 class tracker_hashing :
public std::unordered_map<pid_t, callstack_tracker *>
69 static tracker_hashing __global_tracker;
70 return __global_tracker;
84 pid_t my_thread_id = gettid();
85 #ifdef DEBUG_CALLSTACK_TRACKER
86 printf(
"looking for thread id %ld in callstack lists...\n", (
long)my_thread_id);
88 if (
auto seeker = thc.find(my_thread_id); seeker != thc.end()) {
89 my_thread_id = seeker->first;
90 pointy = seeker->second;
91 #ifdef DEBUG_CALLSTACK_TRACKER
92 printf(
"found existing callstack_tracker for thread %ld\n", (
long)my_thread_id);
95 #ifdef ENABLE_MEMORY_HOOK
102 #ifdef DEBUG_CALLSTACK_TRACKER
103 printf(
"adding new callstack_tracker for thread %ld\n", (
long)my_thread_id);
106 thc.insert({my_thread_id, pointy});
107 #ifdef ENABLE_MEMORY_HOOK
121 class callstack_records
138 callstack_tracker::callstack_tracker()
139 : _bt(new callstack_records),
155 const char *file,
int line)
158 #ifdef DEBUG_CALLSTACK_TRACKER
159 printf(
">> in, callstack pushframe depth=%d\n", _depth);
161 if (_unusable)
return false;
164 printf(
"callstack_tracker::push_frame: past limit at class=%s func=%s "
165 "file=%s line=%d\n", class_name, func, file, line);
169 if (_depth > _highest) _highest = _depth;
171 _bt->_records[_depth].assign(class_name, func, file, line);
172 #ifdef DEBUG_CALLSTACK_TRACKER
173 printf(
"<< out, callstack pushframe depth=%d\n", _depth);
181 #ifdef DEBUG_CALLSTACK_TRACKER
182 printf(
">> in, callstack popframe depth=%d\n", _depth);
184 if (_unusable)
return false;
188 printf(
"callstack_tracker::pop_frame stack underflow!\n");
191 _bt->_records[_depth].clean();
194 #ifdef DEBUG_CALLSTACK_TRACKER
195 printf(
"<< out, callstack popframe depth=%d\n", _depth);
204 if (_unusable)
return false;
205 if (!_depth)
return false;
206 _bt->_records[_depth]._line = line;
211 #define CHECK_SPACE_IN_BUFFER(desired_chunk) \
213 if (space_used_in_buffer + desired_chunk >= full_size_needed - 4) { \
214 printf("callstack_tracker::full_trace: failure in size estimation--we would have blown out of the buffer"); \
217 space_used_in_buffer += desired_chunk; \
224 if (_unusable)
return strdup(
"");
226 char *to_return = (
char *)malloc(full_size_needed);
227 #ifdef DEBUG_CALLSTACK_TRACKER
228 printf(
"fulltrace allocated %d bytes for trace.\n", full_size_needed);
237 char temp[initial_len];
238 int space_left_in_line;
240 int space_used_in_buffer = 0;
246 for (
int i = _depth; i >= 1; i--) {
248 strcat(to_return,
"\t");
249 space_left_in_line = initial_len;
251 int len_class = strlen(_bt->_records[i]._class);
252 int len_func = strlen(_bt->_records[i]._func);
253 if (space_left_in_line > len_class + len_func + 6) {
254 space_left_in_line -= len_class + len_func + 6;
255 sprintf(
temp,
"\"%s::%s\", ", _bt->_records[i]._class,
256 _bt->_records[i]._func);
258 strcat(to_return,
temp);
262 int len_file = strlen(_bt->_records[i]._file);
263 if (space_left_in_line > len_file + 4) {
264 space_left_in_line -= len_file + 4;
265 sprintf(
temp,
"\"%s\", ", _bt->_records[i]._file);
267 strcat(to_return,
temp);
271 sprintf(
temp,
"\"line=%d\"", _bt->_records[i]._line);
272 int len_line = strlen(
temp);
273 if (space_left_in_line > len_line) {
274 space_left_in_line -= len_line;
276 strcat(to_return,
temp);
280 strcat(to_return,
"\n");
290 if (_unusable)
return 0;
293 for (
int i = _depth; i >= 1; i--) {
298 int len_class = strlen(_bt->_records[i]._class);
299 int len_func = strlen(_bt->_records[i]._func);
300 this_line += len_class + len_func + 6;
302 int len_file = strlen(_bt->_records[i]._file);
303 this_line += len_file + 4;
317 const char *func,
const char *file,
int line,
bool add_frame)
318 : _frame_involved(add_frame),
325 #ifdef DEBUG_CALLSTACK_TRACKER
326 printf(
">> in, frame_tracking_instance ctor, class=%s func=%s\n", class_name, func);
329 #ifdef DEBUG_CALLSTACK_TRACKER
330 printf(
"<< out, frame_tracking_instance ctor\n");
337 : _frame_involved(false),
338 _class(to_copy._class? strdup(to_copy._class) :
NULL_POINTER),
339 _func(to_copy._func? strdup(to_copy._func) :
NULL_POINTER),
340 _file(to_copy._file? strdup(to_copy._file) :
NULL_POINTER),
350 #ifdef DEBUG_CALLSTACK_TRACKER
351 printf(
"frame_tracking_instance clean\n");
365 #ifdef DEBUG_CALLSTACK_TRACKER
366 printf(
">> in, frametrackinst assignment\n");
368 if (
this == &to_copy)
return *
this;
369 assign(to_copy._class, to_copy._func, to_copy._file, to_copy._line);
370 #ifdef DEBUG_CALLSTACK_TRACKER
371 printf(
"<< out, frametrackinst assignment\n");
377 const char *file,
int line)
389 #ifdef DEBUG_CALLSTACK_TRACKER
390 printf(
">> in, frame_tracking_instance update line num\n");
393 #ifdef DEBUG_CALLSTACK_TRACKER
394 printf(
"<< out, frame_tracking_instance update line num\n");
#define CHECK_SPACE_IN_BUFFER(desired_chunk)
This object can provide a backtrace at runtime of the invoking methods.
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".
bool pop_frame()
removes the last callstack frame off from our tracking.
virtual ~callstack_tracker()
char * full_trace() const
provides the current stack trace in a newly malloc'd string.
bool update_line(int line)
sets the line number within the current stack frame.
static basis::mutex & __callstack_tracker_synchronizer()
protects concurrent access.
int full_trace_size() const
this returns an estimated number of bytes needed for the full_trace().
a small object that represents a stack trace in progress.
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.
~frame_tracking_instance()
releases the information and this stack frame in the tracker.
void clean()
throws out our accumulated memory and pops frame if applicable.
char * _file
newly allocated copies.
bool _frame_involved
has this object been added to the tracker?
void assign(const char *class_name, const char *func, const char *file, int line)
similar to assignment operator but doesn't require an object.
auto_synchronizer simplifies concurrent code by automatically unlocking.
#define NULL_POINTER
The value representing a pointer to nothing.
#define program_wide_memories()
Implements an application lock to ensure only one is running at once.
callstack_tracker & thread_wide_stack_trace()
the provider of thread-wide (single instance per thread) callstack_trackers.
const char * emptiness_note
what we show when the stack is empty.
tracker_hashing & __tracker_hashing()
void update_current_stack_frame_line_number(int line)
sets the line number for the current frame in the global stack trace.
const int MAX_STACK_DEPTH
const int STACK_TRACKER_ELEMS
fairly generous thread allotment for callstacks, before we start bucketing.
The guards collection helps in testing preconditions and reporting errors.
void WHACK(contents *&ptr)
deletion with clearing of the pointer.