Commit | Line | Data |
---|---|---|
0e23f538 TJ |
1 | /* |
2 | The software in this package is distributed under the GNU General | |
3 | Public License version 2 (with a special exception described below). | |
4 | ||
5 | A copy of GNU General Public License (GPL) is included in this distribution, | |
6 | in the file COPYING.GPL. | |
7 | ||
8 | As a special exception, if other files instantiate templates or use macros | |
9 | or inline functions from this file, or you compile this file and link it | |
10 | with other works to produce a work based on this file, this file | |
11 | does not by itself cause the resulting work to be covered | |
12 | by the GNU General Public License. | |
13 | ||
14 | However the source code for this file must still be made available | |
15 | in accordance with section (3) of the GNU General Public License. | |
16 | ||
17 | This exception does not invalidate any other reasons why a work based | |
18 | on this file might be covered by the GNU General Public License. | |
19 | */ | |
b6fb82a2 RP |
20 | /** @file |
21 | * @brief implementation of tracing functionality. | |
22 | * | |
84eef65a | 23 | * @copyright © Copyright 2008,2011 by Intra2net AG |
f4fcef65 TJ |
24 | * |
25 | * Every thread gets its' own ScopeTracker stack via TLS (thread local storage) | |
b6fb82a2 | 26 | * |
b6fb82a2 RP |
27 | */ |
28 | ||
29 | #include "tracefunc.hpp" | |
30 | ||
31 | #include <list> | |
32 | #include <vector> | |
33 | #include <sstream> | |
f4fcef65 | 34 | #include <boost/thread/tss.hpp> |
b6fb82a2 | 35 | |
12481159 CH |
36 | #include <stringfunc.hxx> |
37 | ||
b6fb82a2 RP |
38 | namespace I2n |
39 | { | |
40 | namespace Tracer | |
41 | { | |
42 | ||
43 | using Logger::GlobalLogger; | |
44 | ||
f4fcef65 TJ |
45 | class PerThreadContainer |
46 | { | |
47 | public: | |
84eef65a | 48 | typedef std::vector< ScopeTracker* > ScopeTrackerList; |
f4fcef65 TJ |
49 | |
50 | /// List of currently active scope trackers | |
51 | ScopeTrackerList scope_tracker_list; | |
52 | ||
53 | /// Precomputed indent levels | |
54 | std::vector< std::string > indents; | |
55 | }; | |
b6fb82a2 RP |
56 | |
57 | namespace | |
58 | { | |
59 | ||
f4fcef65 TJ |
60 | /// Internal TLS (thread local storage) data container |
61 | boost::thread_specific_ptr<PerThreadContainer> thread_container; | |
b6fb82a2 | 62 | |
f4fcef65 TJ |
63 | /** |
64 | * Helper function to retrieve TLS pointer. | |
65 | * Initializes the pointer if called for the first time. | |
37799690 | 66 | * @return Pointer to the per thread container or NULL if error occurs |
f4fcef65 | 67 | */ |
37799690 | 68 | static PerThreadContainer *TLS_get_container() throw () |
f4fcef65 TJ |
69 | { |
70 | // First call? Construct options container | |
71 | if (thread_container.get() == NULL) | |
37799690 CH |
72 | { |
73 | try | |
74 | { | |
75 | thread_container.reset(new PerThreadContainer()); | |
76 | } | |
77 | catch (...) | |
78 | { | |
79 | return NULL; | |
80 | } | |
81 | } | |
b6fb82a2 | 82 | |
f4fcef65 | 83 | PerThreadContainer *per_thread = thread_container.get(); |
b6fb82a2 | 84 | |
f4fcef65 TJ |
85 | return per_thread; |
86 | } | |
b6fb82a2 RP |
87 | |
88 | /** | |
89 | * @brief ensures indent level string to exist up to the desired level. | |
90 | * @param level the desired indent level. | |
91 | */ | |
29bc7241 | 92 | static void ensure_indent_level(PerThreadContainer *per_thread, unsigned int level) |
096e680f | 93 | throw(std::length_error, std::bad_alloc) |
b6fb82a2 | 94 | { |
f4fcef65 TJ |
95 | // First initialization? |
96 | if (per_thread->indents.empty()) | |
97 | { | |
98 | per_thread->indents.reserve(10); | |
99 | if (level < 10) | |
100 | level = 10; | |
101 | ||
102 | per_thread->indents.push_back(""); | |
103 | } | |
104 | ||
105 | while (per_thread->indents.size() <= level) | |
b6fb82a2 | 106 | { |
f4fcef65 | 107 | per_thread->indents.push_back( per_thread->indents.back() + " " ); |
b6fb82a2 RP |
108 | } |
109 | } // eo ensure_indent_level(int) | |
110 | ||
37799690 CH |
111 | /** |
112 | * @brief try logging that some problem occurred | |
113 | * | |
114 | * Keep in mind that problem could be with the logger itself, so wrap all this | |
115 | * in a try-catch agin | |
116 | */ | |
117 | static void try_logging_error(const std::string &message) throw () | |
118 | { | |
119 | try | |
120 | { | |
121 | GlobalLogger.debug() << "Problem occurred in ScopeTracker: " << message; | |
122 | } | |
123 | catch (...) | |
124 | { // nothing more we can do | |
125 | } | |
126 | } | |
127 | ||
128 | ||
b6fb82a2 RP |
129 | } // eo namespace <anonymous> |
130 | ||
131 | ||
132 | /* | |
133 | ** implementation of ScopeTracker | |
134 | */ | |
135 | ||
136 | /** | |
137 | * @brief constructor. initializes object with a source location and emits a ENTER message. | |
138 | * @param loc the source location. | |
139 | * | |
140 | * the message is indented according to the current depth of nested ScopeTracker instances. | |
096e680f CH |
141 | * |
142 | * All exceptions happening in here (from vector::reserve, vector::push_back, acquiring lock for logger) | |
143 | * are caught. Will try to log only that problem occurred | |
b6fb82a2 RP |
144 | */ |
145 | ScopeTracker::ScopeTracker( | |
096e680f | 146 | const SourceLocation& loc) throw () |
f4fcef65 TJ |
147 | : Location(loc) |
148 | , Depth(0) | |
149 | , FuncDepth(0) | |
150 | , PerThread(TLS_get_container()) | |
b6fb82a2 | 151 | { |
37799690 CH |
152 | if (PerThread == NULL) |
153 | { | |
154 | try_logging_error("Failed to get thread local storage"); | |
155 | return; | |
156 | } | |
fdd09ae0 | 157 | |
096e680f CH |
158 | try |
159 | { | |
160 | if (!PerThread->scope_tracker_list.empty()) | |
161 | { | |
162 | ScopeTracker* last_tracker= PerThread->scope_tracker_list.back(); | |
163 | if (Location) | |
164 | { | |
165 | if (last_tracker->Location | |
166 | && last_tracker->Location.File == Location.File | |
167 | && last_tracker->Location.FunctionName == Location.FunctionName) | |
168 | { | |
169 | FuncDepth= last_tracker->FuncDepth+1; | |
170 | } | |
171 | } | |
172 | Depth= last_tracker->Depth + 1; | |
173 | } | |
174 | ||
175 | ensure_indent_level(PerThread, Depth); | |
176 | PerThread->scope_tracker_list.push_back(this); | |
177 | ||
178 | // spit a message | |
179 | if (Logger::has_log_level(Logger::LogLevel::Debug)) | |
180 | { | |
181 | GlobalLogger.debug() << PerThread->indents[Depth] << "ENTER " << get_tag(); | |
182 | } | |
183 | } | |
184 | catch (...) | |
185 | { | |
186 | try_logging_error("Caught exception in constructor"); | |
187 | } | |
188 | } // eo ScopeTracker::ScopeTracker(const SourceLocation&) | |
b6fb82a2 RP |
189 | |
190 | ||
191 | /** | |
fdd09ae0 CH |
192 | * @brief create Tag if empty; return reference to it |
193 | * | |
194 | * Moved this from constructor into own function to avoid creating Tag if it is | |
195 | * not required (i.e. log level is not DEBUG) but still create it only once and | |
196 | * ensure it is available if log level changes inside tracked function | |
197 | */ | |
198 | std::string& ScopeTracker::get_tag() | |
199 | { | |
200 | if (Tag.empty()) | |
201 | { | |
202 | if (Location.FunctionName.empty()) | |
203 | { | |
204 | Tag = "<unknown> (global scope?)"; | |
205 | } | |
206 | else | |
207 | { | |
208 | std::ostringstream ostr; | |
209 | ostr << shorten_stl_types(Location.FunctionName); | |
210 | if (FuncDepth>0) | |
211 | { | |
212 | ostr << "#" << FuncDepth+1; | |
213 | } | |
214 | Tag= ostr.str(); | |
215 | } | |
216 | } | |
217 | return Tag; | |
218 | } | |
219 | ||
220 | ||
221 | /** | |
b6fb82a2 RP |
222 | * @brief destructor. emits a LEAVE message. |
223 | * | |
096e680f | 224 | * All exceptions are caught, will try to log only that some problem occurred |
b6fb82a2 | 225 | */ |
096e680f | 226 | ScopeTracker::~ScopeTracker() throw () |
b6fb82a2 | 227 | { |
37799690 CH |
228 | if (PerThread == NULL) |
229 | { | |
230 | try_logging_error("Failed to get thread local storage"); | |
231 | return; | |
232 | } | |
096e680f CH |
233 | |
234 | try | |
235 | { | |
236 | // spit a message | |
237 | if (Logger::has_log_level(Logger::LogLevel::Debug)) | |
238 | { | |
239 | GlobalLogger.debug() << PerThread->indents[Depth] << "LEAVE " << get_tag(); | |
240 | } | |
241 | if (PerThread->scope_tracker_list.empty()) | |
242 | { | |
243 | return; | |
244 | } | |
245 | if (PerThread->scope_tracker_list.back() == this) | |
246 | { | |
247 | PerThread->scope_tracker_list.pop_back(); | |
248 | } | |
249 | else | |
250 | { | |
251 | // oh hell, this should never be the case! | |
252 | //TODO | |
253 | GlobalLogger.error() << "inconsistency detected in scope tracker"; | |
254 | } | |
255 | } | |
256 | catch (...) | |
257 | { | |
258 | try_logging_error("Caught exception in destructor"); | |
259 | } | |
b6fb82a2 RP |
260 | } // eo ScopeTracker::~ScopeTracker() |
261 | ||
262 | ||
263 | ||
264 | ||
265 | } // eo namespace Tracer | |
266 | } // eo namespace I2n |