4 /*****************************************************************************\
6 * Name : callstack_tracker *
7 * Author : Chris Koeritz *
9 *******************************************************************************
10 * Copyright (c) 2007-$now By Author. This program is free software; you can *
11 * redistribute it and/or modify it under the terms of the GNU General Public *
12 * License as published by the Free Software Foundation; either version 2 of *
13 * the License or (at your option) any later version. This is online at: *
14 * http://www.fsf.org/copyleft/gpl.html *
15 * Please send any updates to: fred@gruntose.com *
16 \*****************************************************************************/
18 #ifdef ENABLE_CALLSTACK_TRACKING
20 // note: this object cannot be constructed when the memory_checker is still
21 // tracking memory leaks. it must be disabled so that this object can
22 // construct without being tracked, which causes an infinite loop. the code
23 // in the basis extern support takes care of that for us.
25 #include "callstack_tracker.h"
31 //is this right way to clean that out.
33 const int MAX_STACK_DEPTH = 2000;
34 // beyond that many stack frames, we will simply refuse to add any more.
36 const int MAX_TEXT_FIELD = 1024;
37 // the most space we allow the class, function, and file to take up.
39 const char *emptiness_note = "Empty Stack\n";
40 //!< what we show when the stack is empty.
44 class callstack_records
47 frame_tracking_instance _records[MAX_STACK_DEPTH + 2]; // fudging room.
52 // our current depth gives us our position in the array. we define our
53 // stack as starting at element zero, which is a null stack entry and
54 // corresponds to a depth of zero also. then, when the stack depth is one,
55 // we actually have an element in place and it resides at index 1. this
56 // scheme allows us to have an update to the line number just do nothing when
57 // there is no current stack.
59 callstack_tracker::callstack_tracker()
60 : _bt(new callstack_records),
67 //printf("callstack ctor\n");
70 callstack_tracker::~callstack_tracker()
72 //printf("!!!!!!!!!!!!!!!!!! callstack dtor in\n");
75 //printf("!!!!!!!!!!!!!!!!!! callstack dtor out\n");
78 bool callstack_tracker::push_frame(const char *class_name, const char *func,
79 const char *file, int line)
81 //printf("callstack pushframe depth=%d in\n", _depth);
82 if (_unusable) return false;
83 if (_depth >= MAX_STACK_DEPTH) {
84 // too many frames already.
85 printf("callstack_tracker::push_frame: past limit at class=%s func=%s "
86 "file=%s line=%d\n", class_name, func, file, line);
90 if (_depth > _highest) _highest = _depth;
92 _bt->_records[_depth].assign(class_name, func, file, line);
93 //printf("callstack pushframe depth=%d out\n", _depth);
97 bool callstack_tracker::pop_frame()
99 //printf("callstack popframe depth=%d in\n", _depth);
100 if (_unusable) return false;
102 // how inappropriate of them; we have no frames.
103 _depth = 0; // we don't lose anything useful by forcing it to be zero.
104 printf("callstack_tracker::pop_frame stack underflow!\n");
107 _bt->_records[_depth].clean();
110 //printf("callstack popframe depth=%d out\n", _depth);
114 bool callstack_tracker::update_line(int line)
116 if (_unusable) return false;
117 if (!_depth) return false; // not as serious, but pretty weird.
118 _bt->_records[_depth]._line = line;
122 char *callstack_tracker::full_trace() const
124 if (_unusable) return strdup("");
125 //printf("fulltrace in\n");
126 char *to_return = (char *)malloc(full_trace_size());
129 strcat(to_return, emptiness_note);
132 const int initial_len = MAX_TEXT_FIELD + 8;
133 char temp[initial_len];
134 int allowed_len = initial_len;
135 // space provided for one text line.
136 // start at top most active frame and go down towards bottom most.
137 for (int i = _depth; i >= 1; i--) {
138 strcat(to_return, "\t"); // we left space for this and \n at end.
140 int len_class = strlen(_bt->_records[i]._class);
141 int len_func = strlen(_bt->_records[i]._func);
142 if (allowed_len > len_class + len_func + 6) {
143 allowed_len -= len_class + len_func + 6;
144 sprintf(temp, "\"%s::%s\", ", _bt->_records[i]._class,
145 _bt->_records[i]._func);
146 strcat(to_return, temp);
150 int len_file = strlen(_bt->_records[i]._file);
151 if (allowed_len > len_file + 4) {
152 allowed_len -= len_file + 4;
153 sprintf(temp, "\"%s\", ", _bt->_records[i]._file);
154 strcat(to_return, temp);
158 sprintf(temp, "\"line=%d\"", _bt->_records[i]._line);
159 int len_line = strlen(temp);
160 if (allowed_len > len_line) {
161 allowed_len -= len_line;
162 strcat(to_return, temp);
165 strcat(to_return, "\n"); // we left space for this already.
168 //printf("fulltrace out\n");
172 int callstack_tracker::full_trace_size() const
174 if (_unusable) return 0;
175 if (!_depth) return strlen(emptiness_note) + 14; // liberal allocation.
176 int to_return = 28; // another hollywood style excess.
177 for (int i = _depth; i >= 1; i--) {
178 int this_line = 0; // add up parts for just this item.
180 // all of these additions are completely dependent on how it's done above.
182 int len_class = strlen(_bt->_records[i]._class);
183 int len_func = strlen(_bt->_records[i]._func);
184 this_line += len_class + len_func + 6;
186 int len_file = strlen(_bt->_records[i]._file);
187 this_line += len_file + 4;
189 this_line += 32; // extra space for line number and such.
191 // limit it like we did above; we will use the lesser size value.
192 if (this_line < MAX_TEXT_FIELD + 8) to_return += this_line;
193 else to_return += MAX_TEXT_FIELD + 8;
200 frame_tracking_instance::frame_tracking_instance(const char *class_name,
201 const char *func, const char *file, int line, bool add_frame)
202 : _frame_involved(add_frame),
203 _class(class_name? strdup(class_name) : NULL_POINTER),
204 _func(func? strdup(func) : NULL_POINTER),
205 _file(file? strdup(file) : NULL_POINTER),
208 if (_frame_involved) {
209 //printf("frametrackinst ctor in class=%s func=%s\n", class_name, func);
210 program_wide_stack_trace().push_frame(class_name, func, file, line);
211 //printf("frametrackinst ctor out\n");
215 frame_tracking_instance::frame_tracking_instance
216 (const frame_tracking_instance &to_copy)
217 : _frame_involved(false), // copies don't get a right to this.
218 _class(to_copy._class? strdup(to_copy._class) : NULL_POINTER),
219 _func(to_copy._func? strdup(to_copy._func) : NULL_POINTER),
220 _file(to_copy._file? strdup(to_copy._file) : NULL_POINTER),
225 frame_tracking_instance::~frame_tracking_instance() { clean(); }
227 void frame_tracking_instance::clean()
229 if (_frame_involved) {
230 //printf("frametrackinst clean\n");
231 program_wide_stack_trace().pop_frame();
233 _frame_involved = false;
234 free(_class); _class = NULL_POINTER;
235 free(_func); _func = NULL_POINTER;
236 free(_file); _file = NULL_POINTER;
240 frame_tracking_instance &frame_tracking_instance::operator =
241 (const frame_tracking_instance &to_copy)
243 //printf("frametrackinst tor = in\n");
244 if (this == &to_copy) return *this;
245 assign(to_copy._class, to_copy._func, to_copy._file, to_copy._line);
246 //printf("frametrackinst tor = out\n");
250 void frame_tracking_instance::assign(const char *class_name, const char *func,
251 const char *file, int line)
254 _frame_involved = false; // copies don't get a right to this.
255 _class = class_name? strdup(class_name) : NULL_POINTER;
256 _func = func? strdup(func) : NULL_POINTER;
257 _file = file? strdup(file) : NULL_POINTER;
261 void update_current_stack_frame_line_number(int line)
263 //printf("frametrackinst updatelinenum in\n");
264 program_wide_stack_trace().update_line(line);
265 //printf("frametrackinst updatelinenum out\n");
268 #endif // ENABLE_CALLSTACK_TRACKING