profiler.h
Go to the documentation of this file.
00001 #ifndef SWRI_PROFILER_PROFILER_H_
00002 #define SWRI_PROFILER_PROFILER_H_
00003 
00004 #include <algorithm>
00005 #include <limits>
00006 #include <unordered_map>
00007 #include <atomic>
00008 
00009 #include <ros/time.h>
00010 #include <ros/console.h>
00011 #include <diagnostic_updater/diagnostic_updater.h>
00012 
00013 namespace swri_profiler
00014 {
00015 class SpinLock
00016 {
00017   std::atomic_flag locked_;
00018  public:
00019   SpinLock() : locked_(ATOMIC_FLAG_INIT) {}
00020     
00021   void acquire()
00022   { 
00023     while (locked_.test_and_set(std::memory_order_acquire)) { ; }
00024   }
00025 
00026   void release()
00027   {
00028     locked_.clear(std::memory_order_release);
00029   }
00030 };
00031 
00032 class SpinLockGuard
00033 {
00034   SpinLock &lock_;
00035   
00036  public:
00037   SpinLockGuard(SpinLock &lock) : lock_(lock) { lock_.acquire(); }
00038   ~SpinLockGuard() { lock_.release(); }
00039 };
00040 
00041 class Profiler
00042 {
00043   // OpenInfo stores data for profiled blocks that are currently
00044   // executing.
00045   struct OpenInfo
00046   {
00047     ros::WallTime t0;
00048     ros::WallTime last_report_time;
00049     OpenInfo() : last_report_time(0) {}
00050   };
00051 
00052   // ClosedInfo stores data for profiled blocks that have finished
00053   // executing.
00054   struct ClosedInfo
00055   {
00056     size_t count;
00057     ros::WallDuration total_duration;
00058     ros::WallDuration rel_duration;
00059     ros::WallDuration max_duration;  
00060     ClosedInfo() : count(0) {}
00061   };
00062 
00063   // Thread local storage for the profiler.
00064   struct TLS
00065   {
00066     // We support multiple threads by tracking the call stack
00067     // independently per thread.  We also track the stack depth to
00068     // guard against problems from recursion.
00069     size_t stack_depth;
00070     std::string stack_str;
00071     std::string thread_prefix;
00072   };
00073 
00074   // open_blocks_ stores data for profiled blocks that are currently
00075   // executing.  It maps a thread_prefix + stack_address to an OpenInfo
00076   // block.  This is stored as a shared static variable instead of a
00077   // TLS because the publishing thread needs to access it.
00078   static std::unordered_map<std::string, OpenInfo> open_blocks_;
00079 
00080   // closed_blocks_ stored data for profiled blocks that have finished
00081   // executing.  It maps a stack_address to a ClosedInfo block.  This
00082   // map is cleared out regularly.
00083   static std::unordered_map<std::string, ClosedInfo> closed_blocks_;
00084 
00085   // tls_ stores the thread local storage so that the profiler can
00086   // maintain a separate stack for each thread.
00087   static boost::thread_specific_ptr<TLS> tls_;
00088 
00089   // This spinlock guards access to open_blocks_ and closed_blocks_.
00090   static SpinLock lock_;
00091 
00092   // Other static methods implemented in profiler.cpp
00093   static void initializeProfiler();
00094   static void initializeTLS();
00095   static void profilerMain();
00096   static void collectAndPublish();
00097 
00098   static bool open(const std::string &name, const ros::WallTime &t0)
00099   {
00100     if (!tls_.get()) { initializeTLS(); }
00101 
00102     if (name.empty()) {
00103       ROS_ERROR("Profiler error: Profiled section has empty name. "
00104                 "Current stack is '%s'.",
00105                 tls_->stack_str.c_str());
00106       return false;
00107     }
00108     
00109     if (tls_->stack_depth >= 100) {
00110       ROS_ERROR("Profiler error: reached max stack size (%zu) while "
00111                 "opening '%s'. Current stack is '%s'.",
00112                 tls_->stack_depth,
00113                 name.c_str(),
00114                 tls_->stack_str.c_str());
00115       return false;
00116     }
00117 
00118     tls_->stack_depth++;
00119     tls_->stack_str = tls_->stack_str + "/" + name;
00120 
00121     std::string open_index = tls_->thread_prefix + tls_->stack_str;
00122     {
00123       SpinLockGuard guard(lock_);
00124       OpenInfo &info = open_blocks_[open_index];
00125       info.t0 = t0;
00126       info.last_report_time = ros::WallTime(0,0);
00127     }
00128 
00129     return true;
00130   }
00131   
00132   static void close(const std::string &name, const ros::WallTime &tf)
00133   {    
00134     std::string open_index = tls_->thread_prefix + tls_->stack_str;
00135     {
00136       SpinLockGuard guard(lock_);
00137 
00138       auto const open_it = open_blocks_.find(open_index);
00139       if (open_it == open_blocks_.end()) {
00140         ROS_ERROR("Missing entry for '%s' in open_index. Profiler is probably corrupted.",
00141                   name.c_str());
00142         return;
00143       }
00144       
00145       ros::WallDuration abs_duration = tf - open_it->second.t0;
00146       ros::WallDuration rel_duration;
00147       if (open_it->second.last_report_time > open_it->second.t0) {
00148         rel_duration = tf - open_it->second.last_report_time;
00149       } else {
00150         rel_duration = tf - open_it->second.t0;
00151       }
00152       open_blocks_.erase(open_it);
00153       
00154       ClosedInfo &info = closed_blocks_[tls_->stack_str];
00155       info.count++;
00156       if (info.count == 1) {
00157         info.total_duration = abs_duration;
00158         info.max_duration = abs_duration;
00159         info.rel_duration = rel_duration;
00160       } else {
00161         info.total_duration += abs_duration;
00162         info.rel_duration += rel_duration;
00163         info.max_duration = std::max(info.max_duration, abs_duration);
00164       }
00165     }
00166 
00167     const size_t len = name.size()+1;  
00168     tls_->stack_str.erase(tls_->stack_str.size()-len, len);
00169     tls_->stack_depth--;    
00170   }
00171 
00172  private:
00173   std::string name_;
00174   
00175  public:
00176   Profiler(const std::string &name)
00177   {
00178     if (open(name, ros::WallTime::now())) {
00179       name_ = name;
00180     } else {
00181       name_ = "";
00182     }
00183   }
00184   
00185   ~Profiler()
00186   {
00187     if (!name_.empty()) {
00188       close(name_, ros::WallTime::now());
00189     }
00190   }
00191 };  
00192 }  // namespace swri_profiler
00193 
00194 // Macros for string concatenation that work with built in macros.
00195 #define SWRI_PROFILER_CONCAT_DIRECT(s1,s2) s1##s2
00196 #define SWRI_PROFILER_CONCAT(s1, s2) SWRI_PROFILER_CONCAT_DIRECT(s1,s2)
00197 
00198 #define SWRI_PROFILER_IMP(block_var, name)             \
00199   swri_profiler::Profiler block_var(name);             \
00200 
00201 #ifndef DISABLE_SWRI_PROFILER
00202 #define SWRI_PROFILE(name) SWRI_PROFILER_IMP(      \
00203     SWRI_PROFILER_CONCAT(prof_block_, __LINE__),   \
00204     name)
00205 #else // ndef DISABLE_SWRI_PROFILER
00206 #define SWRI_PROFILE(name)
00207 #endif // def DISABLE_SWRI_PROFILER
00208 
00209 #endif  // SWRI_PROFILER_PROFILER_H_


swri_profiler
Author(s):
autogenerated on Sat Apr 27 2019 03:08:47