Use std::vector instead of std::list for active ScopeTracker management (we just...
[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 namespace I2n
37 {
38 namespace Tracer
39 {
40
41 using Logger::GlobalLogger;
42
43 class PerThreadContainer
44 {
45 public:
46     typedef std::vector< ScopeTracker* > ScopeTrackerList;
47
48     /// List of currently active scope trackers
49     ScopeTrackerList scope_tracker_list;
50
51     /// Precomputed indent levels
52     std::vector< std::string > indents;
53 };
54
55 namespace
56 {
57
58 /// Internal TLS (thread local storage) data container
59 boost::thread_specific_ptr<PerThreadContainer> thread_container;
60
61 /**
62  * Helper function to retrieve TLS pointer.
63  * Initializes the pointer if called for the first time.
64  * @return Pointer to the per thread container
65  */
66 static PerThreadContainer *TLS_get_container()
67 {
68     // First call? Construct options container
69     if (thread_container.get() == NULL)
70         thread_container.reset(new PerThreadContainer());
71
72     PerThreadContainer *per_thread = thread_container.get();
73
74     return per_thread;
75 }
76
77 /**
78  * @brief ensures indent level string to exist up to the desired level.
79  * @param level the desired indent level.
80  */
81 static void ensure_indent_level(PerThreadContainer *per_thread, int level)
82 {
83     // First initialization?
84     if (per_thread->indents.empty())
85     {
86          per_thread->indents.reserve(10);
87          if (level < 10)
88              level = 10;
89
90          per_thread->indents.push_back("");
91     }
92
93    while (per_thread->indents.size() <= level)
94    {
95       per_thread->indents.push_back( per_thread->indents.back() + "  " );
96    }
97 } // eo ensure_indent_level(int)
98
99 } // eo namespace <anonymous>
100
101
102 /*
103 ** implementation of ScopeTracker
104 */
105
106 /**
107  * @brief constructor. initializes object with a source location and emits a ENTER message.
108  * @param loc the source location.
109  *
110  * the message is indented according to the current depth of nested ScopeTracker instances.
111  */
112 ScopeTracker::ScopeTracker(
113     const SourceLocation& loc)
114     : Location(loc)
115     , Depth(0)
116     , FuncDepth(0)
117     , PerThread(TLS_get_container())
118 {
119     if (!PerThread->scope_tracker_list.empty())
120    {
121       ScopeTracker* last_tracker= PerThread->scope_tracker_list.back();
122       if (Location)
123       {
124          if (last_tracker->Location
125              && last_tracker->Location.File == Location.File
126              && last_tracker->Location.FunctionName == Location.FunctionName)
127          {
128             FuncDepth= last_tracker->FuncDepth+1;
129          }
130       }
131       Depth= last_tracker->Depth + 1;
132    }
133    ensure_indent_level(PerThread, Depth);
134    PerThread->scope_tracker_list.push_back(this);
135    {
136       std::ostringstream ostr;
137       if (Location.FunctionName.empty())
138       {
139          ostr << "<unknown> (global scope?)";
140       }
141       else
142       {
143          ostr << Location.FunctionName;
144          if (FuncDepth>0)
145          {
146             ostr << "#" << FuncDepth+1;
147          }
148       }
149       Tag= ostr.str();
150    }
151    // spit a message
152    if (Logger::has_log_level(Logger::LogLevel::Debug))
153    {
154       GlobalLogger.debug() << PerThread->indents[Depth] << "ENTER " << Tag;
155    }
156 } // eo ScopeTrcaker::ScopeTracker(const SourceLocation&)
157
158
159 /**
160  * @brief destructor. emits a LEAVE message.
161  *
162  *
163  */
164 ScopeTracker::~ScopeTracker()
165 {
166    // spit a message
167    if (Logger::has_log_level(Logger::LogLevel::Debug))
168    {
169       GlobalLogger.debug() << PerThread->indents[Depth] << "LEAVE " << Tag;
170    }
171    if (PerThread->scope_tracker_list.empty())
172    {
173       return;
174    }
175    if (PerThread->scope_tracker_list.back() == this)
176    {
177       PerThread->scope_tracker_list.pop_back();
178    }
179    else
180    {
181       // oh hell, this should never be the case!
182       //TODO
183       GlobalLogger.error() << "inconsistency detected in scope tracker";
184    }
185 } // eo ScopeTracker::~ScopeTracker()
186
187
188
189
190 } // eo namespace Tracer
191 } // eo namespace I2n