new home directory
[feisty_meow.git] / nucleus / library / application / callstack_tracker.cpp
1
2
3
4 /*****************************************************************************\
5 *                                                                             *
6 *  Name   : callstack_tracker                                                 *
7 *  Author : Chris Koeritz                                                     *
8 *                                                                             *
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 \*****************************************************************************/
17
18 #ifdef ENABLE_CALLSTACK_TRACKING
19
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.
24
25 #include "callstack_tracker.h"
26
27 #include <malloc.h>
28 #include <stdio.h>
29
30 ////#undef new
31 //is this right way to clean that out.
32
33 const int MAX_STACK_DEPTH = 2000;
34   // beyond that many stack frames, we will simply refuse to add any more.
35
36 const int MAX_TEXT_FIELD = 1024;
37   // the most space we allow the class, function, and file to take up.
38
39 const char *emptiness_note = "Empty Stack\n";
40   //!< what we show when the stack is empty.
41
42 //////////////
43
44 class callstack_records
45 {
46 public:
47   frame_tracking_instance _records[MAX_STACK_DEPTH + 2];  // fudging room.
48 };
49
50 //////////////
51
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.
58
59 callstack_tracker::callstack_tracker()
60 : _bt(new callstack_records),
61   _depth(0),
62   _frames_in(0),
63   _frames_out(0),
64   _highest(0),
65   _unusable(false)
66 {
67 //printf("callstack ctor\n");
68 }
69
70 callstack_tracker::~callstack_tracker()
71 {
72 //printf("!!!!!!!!!!!!!!!!!!   callstack dtor in\n");
73   _unusable = true;
74   WHACK(_bt);
75 //printf("!!!!!!!!!!!!!!!!!!   callstack dtor out\n");
76 }
77
78 bool callstack_tracker::push_frame(const char *class_name, const char *func,
79     const char *file, int line)
80 {
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);
87     return false;
88   }
89   _depth++;
90   if (_depth > _highest) _highest = _depth;
91   _frames_in += 1;
92   _bt->_records[_depth].assign(class_name, func, file, line);
93 //printf("callstack pushframe depth=%d out\n", _depth);
94   return true;
95 }
96
97 bool callstack_tracker::pop_frame()
98 {
99 //printf("callstack popframe depth=%d in\n", _depth);
100   if (_unusable) return false;
101   if (_depth <= 0) {
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");
105     return false;
106   }
107   _bt->_records[_depth].clean();
108   _depth--;
109   _frames_out += 1;
110 //printf("callstack popframe depth=%d out\n", _depth);
111   return true;
112 }
113
114 bool callstack_tracker::update_line(int line)
115 {
116   if (_unusable) return false;
117   if (!_depth) return false;  // not as serious, but pretty weird.
118   _bt->_records[_depth]._line = line;
119   return true;
120 }
121
122 char *callstack_tracker::full_trace() const
123 {
124   if (_unusable) return strdup("");
125 //printf("fulltrace in\n");
126   char *to_return = (char *)malloc(full_trace_size());
127   to_return[0] = '\0';
128   if (!_depth) {
129     strcat(to_return, emptiness_note);
130     return to_return;
131   }
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.
139     temp[0] = '\0';
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);
147     }
148
149     temp[0] = '\0';
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);
155     }
156
157     temp[0] = '\0';
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);
163     }
164
165     strcat(to_return, "\n");  // we left space for this already.
166   }
167
168 //printf("fulltrace out\n");
169   return to_return;
170 }
171
172 int callstack_tracker::full_trace_size() const
173 {
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.
179
180     // all of these additions are completely dependent on how it's done above.
181
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;
185
186     int len_file = strlen(_bt->_records[i]._file);
187     this_line += len_file + 4;
188
189     this_line += 32;  // extra space for line number and such.
190
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;
194   }
195   return to_return;
196 }
197
198 //////////////
199
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),
206   _line(line)
207 {
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");
212   }
213 }
214
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),
221   _line(to_copy._line)
222 {
223 }
224
225 frame_tracking_instance::~frame_tracking_instance() { clean(); }
226
227 void frame_tracking_instance::clean()
228 {
229   if (_frame_involved) {
230 //printf("frametrackinst clean\n");
231     program_wide_stack_trace().pop_frame();
232   }
233   _frame_involved = false;
234   free(_class); _class = NULL_POINTER;
235   free(_func); _func = NULL_POINTER;
236   free(_file); _file = NULL_POINTER;
237   _line = 0;
238 }
239
240 frame_tracking_instance &frame_tracking_instance::operator =
241     (const frame_tracking_instance &to_copy)
242 {
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");
247   return *this;
248 }
249
250 void frame_tracking_instance::assign(const char *class_name, const char *func,
251     const char *file, int line)
252 {
253   clean();
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;
258   _line = line;
259 }
260
261 void update_current_stack_frame_line_number(int line)
262 {
263 //printf("frametrackinst updatelinenum in\n");
264   program_wide_stack_trace().update_line(line);
265 //printf("frametrackinst updatelinenum out\n");
266 }
267
268 #endif // ENABLE_CALLSTACK_TRACKING
269
270
271
272