Merge branch 'daemon-ext'
[libi2ncommon] / src / tracefunc.cpp
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 */
20 /** @file
21  * @brief implementation of tracing functionality.
22  *
23  * @copyright © Copyright 2008,2011 by Intra2net AG
24  * 
25  * Every thread gets its' own ScopeTracker stack via TLS (thread local storage)
26  *
27  */
28
29 #include "tracefunc.hpp"
30
31 #include <list>
32 #include <vector>
33 #include <sstream>
34 #include <boost/thread/tss.hpp>
35
36 #include <stringfunc.hxx>
37
38 namespace I2n
39 {
40 namespace Tracer
41 {
42
43 using Logger::GlobalLogger;
44
45 class PerThreadContainer
46 {
47 public:
48     typedef std::vector< ScopeTracker* > ScopeTrackerList;
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 };
56
57 namespace
58 {
59
60 /// Internal TLS (thread local storage) data container
61 boost::thread_specific_ptr<PerThreadContainer> thread_container;
62
63 /**
64  * Helper function to retrieve TLS pointer.
65  * Initializes the pointer if called for the first time.
66  * @return Pointer to the per thread container or NULL if error occurs
67  */
68 static PerThreadContainer *TLS_get_container() throw ()
69 {
70     // First call? Construct options container
71     if (thread_container.get() == NULL)
72     {
73         try
74         {
75             thread_container.reset(new PerThreadContainer());
76         }
77         catch (...)
78         {
79             return NULL;
80         }
81     }
82
83     PerThreadContainer *per_thread = thread_container.get();
84
85     return per_thread;
86 }
87
88 /**
89  * @brief ensures indent level string to exist up to the desired level.
90  * @param level the desired indent level.
91  */
92 static void ensure_indent_level(PerThreadContainer *per_thread, unsigned int level)
93                        throw(std::length_error, std::bad_alloc)
94 {
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)
106    {
107       per_thread->indents.push_back( per_thread->indents.back() + "  " );
108    }
109 } // eo ensure_indent_level(int)
110
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
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.
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
144  */
145 ScopeTracker::ScopeTracker(
146     const SourceLocation& loc) throw ()
147     : Location(loc)
148     , Depth(0)
149     , FuncDepth(0)
150     , PerThread(TLS_get_container())
151 {
152     if (PerThread == NULL)
153     {
154         try_logging_error("Failed to get thread local storage");
155         return;
156     }
157
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&)
189
190
191 /**
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 /**
222  * @brief destructor. emits a LEAVE message.
223  *
224  * All exceptions are caught, will try to log only that some problem occurred
225  */
226 ScopeTracker::~ScopeTracker() throw ()
227 {
228     if (PerThread == NULL)
229     {
230         try_logging_error("Failed to get thread local storage");
231         return;
232     }
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     }
260 } // eo ScopeTracker::~ScopeTracker()
261
262
263
264
265 } // eo namespace Tracer
266 } // eo namespace I2n