Run shorten_stl_types (and a bit more) only if needed in scope tracker
[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
67  */
68 static PerThreadContainer *TLS_get_container()
69 {
70     // First call? Construct options container
71     if (thread_container.get() == NULL)
72         thread_container.reset(new PerThreadContainer());
73
74     PerThreadContainer *per_thread = thread_container.get();
75
76     return per_thread;
77 }
78
79 /**
80  * @brief ensures indent level string to exist up to the desired level.
81  * @param level the desired indent level.
82  */
83 static void ensure_indent_level(PerThreadContainer *per_thread, unsigned int level)
84 {
85     // First initialization?
86     if (per_thread->indents.empty())
87     {
88          per_thread->indents.reserve(10);
89          if (level < 10)
90              level = 10;
91
92          per_thread->indents.push_back("");
93     }
94
95    while (per_thread->indents.size() <= level)
96    {
97       per_thread->indents.push_back( per_thread->indents.back() + "  " );
98    }
99 } // eo ensure_indent_level(int)
100
101 } // eo namespace <anonymous>
102
103
104 /*
105 ** implementation of ScopeTracker
106 */
107
108 /**
109  * @brief constructor. initializes object with a source location and emits a ENTER message.
110  * @param loc the source location.
111  *
112  * the message is indented according to the current depth of nested ScopeTracker instances.
113  */
114 ScopeTracker::ScopeTracker(
115     const SourceLocation& loc)
116     : Location(loc)
117     , Depth(0)
118     , FuncDepth(0)
119     , PerThread(TLS_get_container())
120 {
121     if (!PerThread->scope_tracker_list.empty())
122    {
123       ScopeTracker* last_tracker= PerThread->scope_tracker_list.back();
124       if (Location)
125       {
126          if (last_tracker->Location
127              && last_tracker->Location.File == Location.File
128              && last_tracker->Location.FunctionName == Location.FunctionName)
129          {
130             FuncDepth= last_tracker->FuncDepth+1;
131          }
132       }
133       Depth= last_tracker->Depth + 1;
134    }
135    ensure_indent_level(PerThread, Depth);
136    PerThread->scope_tracker_list.push_back(this);
137
138    // spit a message
139    if (Logger::has_log_level(Logger::LogLevel::Debug))
140    {
141       GlobalLogger.debug() << PerThread->indents[Depth] << "ENTER " << get_tag();
142    }
143 } // eo ScopeTrcaker::ScopeTracker(const SourceLocation&)
144
145
146 /**
147  * @brief create Tag if empty; return reference to it
148  *
149  * Moved this from constructor into own function to avoid creating Tag if it is
150  * not required (i.e. log level is not DEBUG) but still create it only once and
151  * ensure it is available if log level changes inside tracked function
152  */
153 std::string& ScopeTracker::get_tag()
154 {
155     if (Tag.empty())
156     {
157         if (Location.FunctionName.empty())
158         {
159             Tag = "<unknown> (global scope?)";
160         }
161         else
162         {
163             std::ostringstream ostr;
164             ostr << shorten_stl_types(Location.FunctionName);
165             if (FuncDepth>0)
166             {
167                 ostr << "#" << FuncDepth+1;
168             }
169             Tag= ostr.str();
170         }
171     }
172     return Tag;
173 }
174
175
176 /**
177  * @brief destructor. emits a LEAVE message.
178  *
179  *
180  */
181 ScopeTracker::~ScopeTracker()
182 {
183    // spit a message
184    if (Logger::has_log_level(Logger::LogLevel::Debug))
185    {
186       GlobalLogger.debug() << PerThread->indents[Depth] << "LEAVE " << get_tag();
187    }
188    if (PerThread->scope_tracker_list.empty())
189    {
190       return;
191    }
192    if (PerThread->scope_tracker_list.back() == this)
193    {
194       PerThread->scope_tracker_list.pop_back();
195    }
196    else
197    {
198       // oh hell, this should never be the case!
199       //TODO
200       GlobalLogger.error() << "inconsistency detected in scope tracker";
201    }
202 } // eo ScopeTracker::~ScopeTracker()
203
204
205
206
207 } // eo namespace Tracer
208 } // eo namespace I2n