Return NULL thread local storage instead of raising exception
[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 {
94     // First initialization?
95     if (per_thread->indents.empty())
96     {
97          per_thread->indents.reserve(10);
98          if (level < 10)
99              level = 10;
100
101          per_thread->indents.push_back("");
102     }
103
104    while (per_thread->indents.size() <= level)
105    {
106       per_thread->indents.push_back( per_thread->indents.back() + "  " );
107    }
108 } // eo ensure_indent_level(int)
109
110 /**
111  * @brief try logging that some problem occurred
112  *
113  * Keep in mind that problem could be with the logger itself, so wrap all this
114  * in a try-catch agin
115  */
116  static void try_logging_error(const std::string &message) throw ()
117 {
118     try
119     {
120         GlobalLogger.debug() << "Problem occurred in ScopeTracker: " << message;
121     }
122     catch (...)
123     {    // nothing more we can do
124     }
125 }
126
127
128 } // eo namespace <anonymous>
129
130
131 /*
132 ** implementation of ScopeTracker
133 */
134
135 /**
136  * @brief constructor. initializes object with a source location and emits a ENTER message.
137  * @param loc the source location.
138  *
139  * the message is indented according to the current depth of nested ScopeTracker instances.
140  */
141 ScopeTracker::ScopeTracker(
142     const SourceLocation& loc)
143     : Location(loc)
144     , Depth(0)
145     , FuncDepth(0)
146     , PerThread(TLS_get_container())
147 {
148     if (PerThread == NULL)
149     {
150         try_logging_error("Failed to get thread local storage");
151         return;
152     }
153     if (!PerThread->scope_tracker_list.empty())
154    {
155       ScopeTracker* last_tracker= PerThread->scope_tracker_list.back();
156       if (Location)
157       {
158          if (last_tracker->Location
159              && last_tracker->Location.File == Location.File
160              && last_tracker->Location.FunctionName == Location.FunctionName)
161          {
162             FuncDepth= last_tracker->FuncDepth+1;
163          }
164       }
165       Depth= last_tracker->Depth + 1;
166    }
167    ensure_indent_level(PerThread, Depth);
168    PerThread->scope_tracker_list.push_back(this);
169
170    // spit a message
171    if (Logger::has_log_level(Logger::LogLevel::Debug))
172    {
173       GlobalLogger.debug() << PerThread->indents[Depth] << "ENTER " << get_tag();
174    }
175 } // eo ScopeTrcaker::ScopeTracker(const SourceLocation&)
176
177
178 /**
179  * @brief create Tag if empty; return reference to it
180  *
181  * Moved this from constructor into own function to avoid creating Tag if it is
182  * not required (i.e. log level is not DEBUG) but still create it only once and
183  * ensure it is available if log level changes inside tracked function
184  */
185 std::string& ScopeTracker::get_tag()
186 {
187     if (Tag.empty())
188     {
189         if (Location.FunctionName.empty())
190         {
191             Tag = "<unknown> (global scope?)";
192         }
193         else
194         {
195             std::ostringstream ostr;
196             ostr << shorten_stl_types(Location.FunctionName);
197             if (FuncDepth>0)
198             {
199                 ostr << "#" << FuncDepth+1;
200             }
201             Tag= ostr.str();
202         }
203     }
204     return Tag;
205 }
206
207
208 /**
209  * @brief destructor. emits a LEAVE message.
210  *
211  *
212  */
213 ScopeTracker::~ScopeTracker()
214 {
215     if (PerThread == NULL)
216     {
217         try_logging_error("Failed to get thread local storage");
218         return;
219     }
220    // spit a message
221    if (Logger::has_log_level(Logger::LogLevel::Debug))
222    {
223       GlobalLogger.debug() << PerThread->indents[Depth] << "LEAVE " << get_tag();
224    }
225    if (PerThread->scope_tracker_list.empty())
226    {
227       return;
228    }
229    if (PerThread->scope_tracker_list.back() == this)
230    {
231       PerThread->scope_tracker_list.pop_back();
232    }
233    else
234    {
235       // oh hell, this should never be the case!
236       //TODO
237       GlobalLogger.error() << "inconsistency detected in scope tracker";
238    }
239 } // eo ScopeTracker::~ScopeTracker()
240
241
242
243
244 } // eo namespace Tracer
245 } // eo namespace I2n