profiler.h
Go to the documentation of this file.
1 #ifndef SWRI_PROFILER_PROFILER_H_
2 #define SWRI_PROFILER_PROFILER_H_
3 
4 #include <algorithm>
5 #include <limits>
6 #include <unordered_map>
7 #include <atomic>
8 
9 #include <ros/time.h>
10 #include <ros/console.h>
12 
13 namespace swri_profiler
14 {
15 class SpinLock
16 {
17  std::atomic_flag locked_;
18  public:
19  SpinLock() : locked_(ATOMIC_FLAG_INIT) {}
20 
21  void acquire()
22  {
23  while (locked_.test_and_set(std::memory_order_acquire)) { ; }
24  }
25 
26  void release()
27  {
28  locked_.clear(std::memory_order_release);
29  }
30 };
31 
33 {
35 
36  public:
37  SpinLockGuard(SpinLock &lock) : lock_(lock) { lock_.acquire(); }
38  ~SpinLockGuard() { lock_.release(); }
39 };
40 
41 class Profiler
42 {
43  // OpenInfo stores data for profiled blocks that are currently
44  // executing.
45  struct OpenInfo
46  {
49  OpenInfo() : last_report_time(0) {}
50  };
51 
52  // ClosedInfo stores data for profiled blocks that have finished
53  // executing.
54  struct ClosedInfo
55  {
56  size_t count;
60  ClosedInfo() : count(0) {}
61  };
62 
63  // Thread local storage for the profiler.
64  struct TLS
65  {
66  // We support multiple threads by tracking the call stack
67  // independently per thread. We also track the stack depth to
68  // guard against problems from recursion.
69  size_t stack_depth;
70  std::string stack_str;
71  std::string thread_prefix;
72  };
73 
74  // open_blocks_ stores data for profiled blocks that are currently
75  // executing. It maps a thread_prefix + stack_address to an OpenInfo
76  // block. This is stored as a shared static variable instead of a
77  // TLS because the publishing thread needs to access it.
78  static std::unordered_map<std::string, OpenInfo> open_blocks_;
79 
80  // closed_blocks_ stored data for profiled blocks that have finished
81  // executing. It maps a stack_address to a ClosedInfo block. This
82  // map is cleared out regularly.
83  static std::unordered_map<std::string, ClosedInfo> closed_blocks_;
84 
85  // tls_ stores the thread local storage so that the profiler can
86  // maintain a separate stack for each thread.
87  static boost::thread_specific_ptr<TLS> tls_;
88 
89  // This spinlock guards access to open_blocks_ and closed_blocks_.
90  static SpinLock lock_;
91 
92  // Other static methods implemented in profiler.cpp
93  static void initializeProfiler();
94  static void initializeTLS();
95  static void profilerMain();
96  static void collectAndPublish();
97 
98  static bool open(const std::string &name, const ros::WallTime &t0)
99  {
100  if (!tls_.get()) { initializeTLS(); }
101 
102  if (name.empty()) {
103  ROS_ERROR("Profiler error: Profiled section has empty name. "
104  "Current stack is '%s'.",
105  tls_->stack_str.c_str());
106  return false;
107  }
108 
109  if (tls_->stack_depth >= 100) {
110  ROS_ERROR("Profiler error: reached max stack size (%zu) while "
111  "opening '%s'. Current stack is '%s'.",
112  tls_->stack_depth,
113  name.c_str(),
114  tls_->stack_str.c_str());
115  return false;
116  }
117 
118  tls_->stack_depth++;
119  tls_->stack_str = tls_->stack_str + "/" + name;
120 
121  std::string open_index = tls_->thread_prefix + tls_->stack_str;
122  {
123  SpinLockGuard guard(lock_);
124  OpenInfo &info = open_blocks_[open_index];
125  info.t0 = t0;
126  info.last_report_time = ros::WallTime(0,0);
127  }
128 
129  return true;
130  }
131 
132  static void close(const std::string &name, const ros::WallTime &tf)
133  {
134  std::string open_index = tls_->thread_prefix + tls_->stack_str;
135  {
136  SpinLockGuard guard(lock_);
137 
138  auto const open_it = open_blocks_.find(open_index);
139  if (open_it == open_blocks_.end()) {
140  ROS_ERROR("Missing entry for '%s' in open_index. Profiler is probably corrupted.",
141  name.c_str());
142  return;
143  }
144 
145  ros::WallDuration abs_duration = tf - open_it->second.t0;
146  ros::WallDuration rel_duration;
147  if (open_it->second.last_report_time > open_it->second.t0) {
148  rel_duration = tf - open_it->second.last_report_time;
149  } else {
150  rel_duration = tf - open_it->second.t0;
151  }
152  open_blocks_.erase(open_it);
153 
154  ClosedInfo &info = closed_blocks_[tls_->stack_str];
155  info.count++;
156  if (info.count == 1) {
157  info.total_duration = abs_duration;
158  info.max_duration = abs_duration;
159  info.rel_duration = rel_duration;
160  } else {
161  info.total_duration += abs_duration;
162  info.rel_duration += rel_duration;
163  info.max_duration = std::max(info.max_duration, abs_duration);
164  }
165  }
166 
167  const size_t len = name.size()+1;
168  tls_->stack_str.erase(tls_->stack_str.size()-len, len);
169  tls_->stack_depth--;
170  }
171 
172  private:
173  std::string name_;
174 
175  public:
176  Profiler(const std::string &name)
177  {
178  if (open(name, ros::WallTime::now())) {
179  name_ = name;
180  } else {
181  name_ = "";
182  }
183  }
184 
186  {
187  if (!name_.empty()) {
188  close(name_, ros::WallTime::now());
189  }
190  }
191 };
192 } // namespace swri_profiler
193 
194 // Macros for string concatenation that work with built in macros.
195 #define SWRI_PROFILER_CONCAT_DIRECT(s1,s2) s1##s2
196 #define SWRI_PROFILER_CONCAT(s1, s2) SWRI_PROFILER_CONCAT_DIRECT(s1,s2)
197 
198 #define SWRI_PROFILER_IMP(block_var, name) \
199  swri_profiler::Profiler block_var(name); \
200 
201 #ifndef DISABLE_SWRI_PROFILER
202 #define SWRI_PROFILE(name) SWRI_PROFILER_IMP( \
203  SWRI_PROFILER_CONCAT(prof_block_, __LINE__), \
204  name)
205 #else // ndef DISABLE_SWRI_PROFILER
206 #define SWRI_PROFILE(name)
207 #endif // def DISABLE_SWRI_PROFILER
208 
209 #endif // SWRI_PROFILER_PROFILER_H_
std::atomic_flag locked_
Definition: profiler.h:17
static bool open(const std::string &name, const ros::WallTime &t0)
Definition: profiler.h:98
static std::unordered_map< std::string, OpenInfo > open_blocks_
Definition: profiler.h:78
static boost::thread_specific_ptr< TLS > tls_
Definition: profiler.h:87
ros::WallDuration max_duration
Definition: profiler.h:59
static SpinLock lock_
Definition: profiler.h:90
Profiler(const std::string &name)
Definition: profiler.h:176
ros::WallDuration rel_duration
Definition: profiler.h:58
static std::unordered_map< std::string, ClosedInfo > closed_blocks_
Definition: profiler.h:83
ros::WallDuration total_duration
Definition: profiler.h:57
static WallTime now()
#define ROS_ERROR(...)
static void close(const std::string &name, const ros::WallTime &tf)
Definition: profiler.h:132
SpinLockGuard(SpinLock &lock)
Definition: profiler.h:37


swri_profiler
Author(s):
autogenerated on Fri Nov 27 2020 03:44:17