Replace inet_aton() with inet_pton() to parse IPs correctly (#8825)
[libi2ncommon] / src / tracefunc.cpp
... / ...
CommitLineData
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*/
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
38namespace I2n
39{
40namespace Tracer
41{
42
43using Logger::GlobalLogger;
44
45class PerThreadContainer
46{
47public:
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
57namespace
58{
59
60/// Internal TLS (thread local storage) data container
61boost::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 */
68static 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 */
92static 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 * All exceptions happening in here (from vector::reserve, vector::push_back, acquiring lock for logger)
142 * are caught. Will try to log only that problem occurred
143 */
144ScopeTracker::ScopeTracker(
145 const SourceLocation& loc) throw ()
146 : Location(loc)
147 , Depth(0)
148 , FuncDepth(0)
149 , PerThread(TLS_get_container())
150{
151 if (PerThread == NULL)
152 {
153 try_logging_error("Failed to get thread local storage");
154 return;
155 }
156
157 try
158 {
159 if (!PerThread->scope_tracker_list.empty())
160 {
161 ScopeTracker* last_tracker= PerThread->scope_tracker_list.back();
162 if (Location)
163 {
164 if (last_tracker->Location
165 && last_tracker->Location.File == Location.File
166 && last_tracker->Location.FunctionName == Location.FunctionName)
167 {
168 FuncDepth= last_tracker->FuncDepth+1;
169 }
170 }
171 Depth= last_tracker->Depth + 1;
172 }
173
174 ensure_indent_level(PerThread, Depth);
175 PerThread->scope_tracker_list.push_back(this);
176
177 // spit a message
178 if (Logger::has_log_level(Logger::LogLevel::Debug))
179 {
180 GlobalLogger.debug() << PerThread->indents[Depth] << "ENTER " << get_tag();
181 }
182 }
183 catch (...)
184 {
185 try_logging_error("Caught exception in constructor");
186 }
187} // eo ScopeTracker::ScopeTracker(const SourceLocation&)
188
189
190/**
191 * @brief create Tag if empty; return reference to it
192 *
193 * Moved this from constructor into own function to avoid creating Tag if it is
194 * not required (i.e. log level is not DEBUG) but still create it only once and
195 * ensure it is available if log level changes inside tracked function
196 */
197std::string& ScopeTracker::get_tag()
198{
199 if (Tag.empty())
200 {
201 if (Location.FunctionName.empty())
202 {
203 Tag = "<unknown> (global scope?)";
204 }
205 else
206 {
207 std::ostringstream ostr;
208 ostr << shorten_stl_types(Location.FunctionName);
209 if (FuncDepth>0)
210 {
211 ostr << "#" << FuncDepth+1;
212 }
213 Tag= ostr.str();
214 }
215 }
216 return Tag;
217}
218
219
220/**
221 * @brief destructor. emits a LEAVE message.
222 *
223 * All exceptions are caught, will try to log only that some problem occurred
224 */
225ScopeTracker::~ScopeTracker() throw ()
226{
227 if (PerThread == NULL)
228 {
229 try_logging_error("Failed to get thread local storage");
230 return;
231 }
232
233 try
234 {
235 // spit a message
236 if (Logger::has_log_level(Logger::LogLevel::Debug))
237 {
238 GlobalLogger.debug() << PerThread->indents[Depth] << "LEAVE " << get_tag();
239 }
240 if (PerThread->scope_tracker_list.empty())
241 {
242 return;
243 }
244 if (PerThread->scope_tracker_list.back() == this)
245 {
246 PerThread->scope_tracker_list.pop_back();
247 }
248 else
249 {
250 // oh hell, this should never be the case!
251 //TODO
252 GlobalLogger.error() << "inconsistency detected in scope tracker";
253 }
254 }
255 catch (...)
256 {
257 try_logging_error("Caught exception in destructor");
258 }
259} // eo ScopeTracker::~ScopeTracker()
260
261
262
263
264} // eo namespace Tracer
265} // eo namespace I2n