Wrap Scopetracker constructor/destructor in big try-catch
[libi2ncommon] / src / tracefunc.cpp
CommitLineData
0e23f538
TJ
1/*
2The software in this package is distributed under the GNU General
3Public License version 2 (with a special exception described below).
4
5A copy of GNU General Public License (GPL) is included in this distribution,
6in the file COPYING.GPL.
7
8As a special exception, if other files instantiate templates or use macros
9or inline functions from this file, or you compile this file and link it
10with other works to produce a work based on this file, this file
11does not by itself cause the resulting work to be covered
12by the GNU General Public License.
13
14However the source code for this file must still be made available
15in accordance with section (3) of the GNU General Public License.
16
17This exception does not invalidate any other reasons why a work based
18on 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
38namespace I2n
39{
40namespace Tracer
41{
42
43using Logger::GlobalLogger;
44
f4fcef65
TJ
45class PerThreadContainer
46{
47public:
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
57namespace
58{
59
f4fcef65
TJ
60/// Internal TLS (thread local storage) data container
61boost::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 68static 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 92static 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 */
145ScopeTracker::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 */
198std::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 226ScopeTracker::~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