LCOV - code coverage report
Current view: top level - root/contrail/src/contrail-common/base - trace.h (source / functions) Hit Total Coverage
Test: OpenSDN C/C++ coverage (all TARGET_SET jobs) Lines: 110 161 68.3 %
Date: 2026-08-03 02:19:58 Functions: 17 34 50.0 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /*
       2             :  * Copyright (c) 2013 Juniper Networks, Inc. All rights reserved.
       3             :  */
       4             : 
       5             : #ifndef __TRACE_H__
       6             : #define __TRACE_H__
       7             : 
       8             : #include <atomic>
       9             : #include <mutex>
      10             : #include <map>
      11             : #include <vector>
      12             : #include <stdexcept>
      13             : 
      14             : #include <boost/function.hpp>
      15             : #include <boost/ptr_container/ptr_circular_buffer.hpp>
      16             : #include <boost/weak_ptr.hpp>
      17             : #include <boost/shared_ptr.hpp>
      18             : #include "base/util.h"
      19             : 
      20             : /// Manages a trace buffer's memory. A trace buffer is circular buffer with
      21             : /// the given size (count of records) and associated with the given name.
      22             : /// The type of records is specified during compilation using the template
      23             : /// parameter TraceEntryT. Trace buffers are organized into a table.
      24             : template<typename TraceEntryT>
      25             : class TraceBuffer {
      26             : public:
      27             : 
      28             :     /// The type defines how a map (a table) of trace buffers is stored.
      29             :     typedef std::map<const std::string,
      30             :                      boost::weak_ptr<TraceBuffer<TraceEntryT> > > TraceBufMap;
      31             : 
      32             :     /// Creates a new trace buffer with the given name and size (enable
      33             :     /// by default).
      34       47382 :     TraceBuffer(const std::string& buf_name, size_t size, bool trace_enable)
      35       47382 :         : trace_buf_name_(buf_name),
      36       47382 :           trace_buf_size_(size),
      37       47382 :           trace_buf_(trace_buf_size_),
      38       47382 :           write_index_(0),
      39       47382 :           read_index_(0),
      40       47382 :           wrap_(false) {
      41       47382 :         seqno_ = 0;
      42       47382 :         trace_enable_ = trace_enable;
      43       47382 :     }
      44             : 
      45             :     /// Destroys a trace buffer.
      46       47382 :     ~TraceBuffer() {
      47       47382 :         read_context_map_.clear();
      48       47382 :         trace_buf_.clear();
      49       47382 :     }
      50             : 
      51             :     /// Returns the name of the trace buffer.
      52     8780811 :     std::string Name() {
      53     8780811 :         return trace_buf_name_;
      54             :     }
      55             : 
      56             :     /// Enables the trace buffer.
      57           0 :     void TraceOn() {
      58           0 :         trace_enable_ = true;
      59           0 :     }
      60             : 
      61             :     /// Disables the trace buffer.
      62           0 :     void TraceOff() {
      63           0 :         trace_enable_ = false;
      64           0 :     }
      65             : 
      66             :     /// Determines whether the trace buffer is enabled or not.
      67     8782633 :     bool IsTraceOn() {
      68     8782633 :         return trace_enable_;
      69             :     }
      70             : 
      71             :     /// Returns the length (the maximum number of records) in the circular 
      72             :     /// buffer.
      73           3 :     size_t TraceBufSizeGet() {
      74           3 :         return trace_buf_size_;
      75             :     }
      76             : 
      77             :     /// Returns the length (the maximum number of records) in the circular 
      78             :     /// buffer.
      79           1 :     size_t TraceBufCapacityGet() {
      80           1 :         return trace_buf_.capacity();
      81             :     }
      82             : 
      83             :     /// Resets the size of the circular buffer.
      84           0 :     void TraceBufCapacityReset(size_t size) {
      85           0 :         trace_buf_.rset_capacity(size);
      86           0 :         trace_buf_size_ = size;
      87           0 :     }
      88             : 
      89             :     /// Writes the provided data into the circular buffer.
      90     8783141 :     void TraceWrite(TraceEntryT *trace_entry) {
      91     8783141 :         std::scoped_lock lock(mutex_);
      92             : 
      93             :         // Add the trace
      94     8784514 :         trace_buf_.push_back(trace_entry);
      95             : 
      96             :         // Once the trace buffer is wrapped, increment the read index
      97     8783938 :         if (wrap_) {
      98     6543214 :             if (++read_index_ == trace_buf_size_) {
      99        6166 :                 read_index_ = 0;
     100             :             }
     101             :         }
     102             : 
     103             :         // Increment the write_index_ and reset upon reaching trace_buf_size_
     104     8783938 :         if (++write_index_ == trace_buf_size_) {
     105        7522 :             write_index_ = 0;
     106        7522 :             wrap_ = true;
     107             :         }
     108             : 
     109             :         // Trace messages could be read in batches instead of reading
     110             :         // the entire trace buffer in one shot. Therefore, trace messages
     111             :         // could be added between subsequent read requests. If the
     112             :         // read_index_ [points to the oldest message in the trace buffer]
     113             :         // becomes same as the read index [points to the position in the
     114             :         // trace buffer from where the next trace message should be read]
     115             :         // stored in the read context, then there is no need to remember the
     116             :         // read context.
     117     8783938 :         ReadContextMap::iterator it = read_context_map_.begin();
     118     8783797 :         ReadContextMap::iterator next = it;
     119     8783797 :         for (size_t i = 0, cnt = read_context_map_.size(); i < cnt;
     120             :              i++, it = next) {
     121           0 :             ++next;
     122           0 :             if (*it->second.get() == read_index_) {
     123           0 :                 read_context_map_.erase(it);
     124             :             }
     125             :         }
     126     8783655 :     }
     127             : 
     128             :     /// Returns the next sequence number.
     129     8782812 :     uint32_t GetNextSeqNum() {
     130     8782812 :         uint32_t nseqno(seqno_.fetch_add(1));
     131             :         // Reset seqno_ if it reaches max value
     132     8782812 :         if (nseqno+1 >= kMaxSeqno) {
     133      264550 :             seqno_ = kMinSeqno;
     134             :         }
     135     8784740 :         return nseqno;
     136             :     }
     137             : 
     138             :     /// Reads the specified number of records from the buffer. Each records
     139             :     /// is submitted into the specified callback function.
     140           1 :     void TraceRead(const std::string& context, const int count,
     141             :             boost::function<void (TraceEntryT *, bool)> cb) {
     142           1 :         std::scoped_lock lock(mutex_);
     143           1 :         if (trace_buf_.empty()) {
     144             :             // No message in the trace buffer
     145           0 :             return;
     146             :         }
     147             : 
     148             :         // if count = 0, then set the cnt equal to the size of trace_buf_
     149           1 :         size_t cnt = count ? count : trace_buf_.size();
     150             : 
     151             :         size_t *read_index_ptr;
     152           1 :         typename ContainerType::iterator it;
     153             :         ReadContextMap::iterator context_it =
     154           1 :             read_context_map_.find(context);
     155           1 :         if (context_it != read_context_map_.end()) {
     156             :             // If the read context is present, manipulate the position
     157             :             // from where we wanna start
     158           0 :             read_index_ptr = context_it->second.get();
     159           0 :             size_t offset = *read_index_ptr - read_index_;
     160           0 :             offset = offset > 0 ? offset : trace_buf_size_ + offset;
     161           0 :             it = trace_buf_.begin() + offset;
     162             :         } else {
     163             :             // Create read context
     164           1 :             boost::shared_ptr<size_t> read_context(new size_t(read_index_));
     165           1 :             read_index_ptr = read_context.get();
     166           1 :             read_context_map_.insert(std::make_pair(context, read_context));
     167           1 :             it = trace_buf_.begin();
     168           1 :         }
     169             : 
     170             :         size_t i;
     171           1 :         typename ContainerType::iterator next = it;
     172           4 :         for (i = 0; (it != trace_buf_.end()) && (i < cnt); i++, it = next) {
     173           3 :             ++next;
     174           3 :             cb(&(*it), next != trace_buf_.end());
     175             :         }
     176             : 
     177             :         // Update the read index in the read context
     178           1 :         size_t offset = *read_index_ptr + i;
     179           1 :         *read_index_ptr = offset >= trace_buf_size_ ?
     180           0 :             offset - trace_buf_size_ : offset;
     181           1 :     }
     182             : 
     183             :     /// The member function is called to complete the reading of the
     184             :     /// circular buffer data.
     185           0 :     void TraceReadDone(const std::string& context) {
     186           0 :         std::scoped_lock lock(mutex_);
     187             :         ReadContextMap::iterator context_it =
     188           0 :             read_context_map_.find(context);
     189           0 :         if (context_it != read_context_map_.end()) {
     190           0 :             read_context_map_.erase(context_it);
     191             :         }
     192           0 :     }
     193             : 
     194             : private:
     195             : 
     196             :     /// Specifies the data type for storing records of the trace buffer.
     197             :     typedef boost::ptr_circular_buffer<TraceEntryT> ContainerType;
     198             : 
     199             :     /// Specifies the read context for the trace buffer.
     200             :     typedef std::map<const std::string, boost::shared_ptr<size_t> >
     201             :         ReadContextMap;
     202             : 
     203             :     /// Stores the name of the trace buffer.
     204             :     std::string trace_buf_name_;
     205             : 
     206             :     /// Stores the size of the trace buffer.
     207             :     size_t trace_buf_size_;
     208             : 
     209             :     /// Stores the records of the trace buffer.
     210             :     ContainerType trace_buf_;
     211             : 
     212             :     /// A flag to determine whether the trace buffer is enabled (ready
     213             :     /// for reading and writing).
     214             :     std::atomic<bool> trace_enable_;
     215             : 
     216             :     /// Points to the position in the trace buffer
     217             :     /// where the next trace message would be added
     218             :     size_t write_index_;
     219             : 
     220             :     /// Points to the position of the oldest
     221             :     /// trace message in the trace buffer
     222             :     size_t read_index_;
     223             : 
     224             :     /// Indicates if the trace buffer is wrapped
     225             :     bool wrap_;
     226             : 
     227             :     /// Stores the read context
     228             :     ReadContextMap read_context_map_;
     229             : 
     230             :     /// Stores the current sequence number.
     231             :     std::atomic<uint32_t> seqno_;
     232             : 
     233             :     /// Used to restrict simulateneous access to the trace buffer data
     234             :     /// from 2 threads
     235             :     std::mutex mutex_;
     236             : 
     237             :     /// Reserves max(uint32_t)
     238             :     static const uint32_t kMaxSeqno = ((2 ^ 32) - 1) - 1;
     239             : 
     240             :     /// Reserves 0
     241             :     static const uint32_t kMinSeqno = 1;
     242             : 
     243             :     DISALLOW_COPY_AND_ASSIGN(TraceBuffer);
     244             : };
     245             : 
     246             : /// The class is responsible for the destruction of a trace buffer.
     247             : template<typename TraceEntryT>
     248             : class TraceBufferDeleter {
     249             : public:
     250             : 
     251             :     /// A link to the trace buffers table type.
     252             :     using TraceBufMap = typename TraceBuffer<TraceEntryT>::TraceBufMap;
     253             : 
     254             :     /// Creates a new instance of this class using the given trace buffer table
     255             :     /// and a mutex object.
     256       47382 :     explicit TraceBufferDeleter(TraceBufMap &trace_buf_map, std::mutex &mutex) :
     257       47382 :             trace_buf_map_(trace_buf_map),
     258       47382 :             mutex_(mutex) {
     259       47382 :     }
     260             : 
     261             :     /// Performs the deletion of the trace buffer from the given map.
     262       47382 :     void operator()(TraceBuffer<TraceEntryT> *trace_buffer) const {
     263       47382 :         std::scoped_lock lock(mutex_);
     264       47382 :         for (typename TraceBufMap::iterator it = trace_buf_map_.begin();
     265      809104 :              it != trace_buf_map_.end();
     266      761722 :              it++) {
     267      809104 :             if (it->second.lock() == NULL) {
     268       47382 :                 trace_buf_map_.erase(it->first);
     269       47382 :                 delete trace_buffer;
     270       47382 :                 break;
     271             :             }
     272             :         }
     273       47382 :     }
     274             : 
     275             : private:
     276             : 
     277             :     /// A reference to the trace buffers table.
     278             :     TraceBufMap &trace_buf_map_;
     279             : 
     280             :     /// A reference to the mutex object.
     281             :     std::mutex &mutex_;
     282             : };
     283             : 
     284             : /// The table for managing trace buffers using a map between their names
     285             : /// and instances. The table is a singletone, the memory for its records
     286             : /// is managed by the user (only weak pointers are stored in the table).
     287             : template<typename TraceEntryT>
     288             : class Trace {
     289             : public:
     290             : 
     291             :     /// A link to the trace buffers table type.
     292             :     using TraceBufMap = typename TraceBuffer<TraceEntryT>::TraceBufMap;
     293             : 
     294             :     /// Returns a pointer to the trace buffers table instance.
     295     8874971 :     static Trace* GetInstance() {
     296     8874971 :         if (!trace_) {
     297         264 :             trace_ = new Trace;
     298             :         }
     299     8875140 :         return trace_;
     300             :     }
     301             : 
     302             :     /// Enables tracing for the table.
     303           0 :     void TraceOn() {
     304           0 :         trace_enable_ = true;
     305           0 :     }
     306             : 
     307             :     /// Disables tracing for the table.
     308           0 :     void TraceOff() {
     309           0 :         trace_enable_ = false;
     310           0 :     }
     311             : 
     312             :     /// Determines whether tracing is enabled for the table.
     313     8783522 :     bool IsTraceOn() {
     314     8783522 :         return trace_enable_;
     315             :     }
     316             : 
     317             :     /// Returns a pointer to the trace buffer associated with the given name.
     318             :     /// If there is no such a trace buffer, then an empty one is returned.
     319           4 :     boost::shared_ptr<TraceBuffer<TraceEntryT> > TraceBufGet(const std::string& buf_name) {
     320           4 :         std::scoped_lock lock(mutex_);
     321           4 :         typename TraceBufMap::iterator it = trace_buf_map_.find(buf_name);
     322           4 :         if (it != trace_buf_map_.end()) {
     323           4 :             return it->second.lock();
     324             :         }
     325           0 :         return boost::shared_ptr<TraceBuffer<TraceEntryT> >();
     326           4 :     }
     327             : 
     328             :     /// Adds a trace buffer with the given name and size and returns a
     329             :     /// reference to it. Returns a shared_ptr of the trace buffer for the
     330             :     /// memory management.
     331       91298 :     boost::shared_ptr<TraceBuffer<TraceEntryT> > TraceBufAdd(const std::string& buf_name, size_t size,
     332             :                      bool trace_enable) {
     333             :         // should we have a default size for the buffer?
     334       91298 :         if (!size) {
     335           0 :             return boost::shared_ptr<TraceBuffer<TraceEntryT> >();
     336             :         }
     337       91298 :         std::scoped_lock lock(mutex_);
     338       91298 :         typename TraceBufMap::iterator it = trace_buf_map_.find(buf_name);
     339       91298 :         if (it == trace_buf_map_.end()) {
     340       47382 :             boost::shared_ptr<TraceBuffer<TraceEntryT> > trace_buf(
     341       47382 :                 new TraceBuffer<TraceEntryT>(buf_name, size, trace_enable),
     342       47382 :                 TraceBufferDeleter<TraceEntryT>(trace_buf_map_, mutex_));
     343       47382 :             trace_buf_map_.insert(std::make_pair(buf_name, trace_buf));
     344       47382 :             return trace_buf;
     345       47382 :         }
     346       43916 :         return it->second.lock();
     347       91298 :     }
     348             : 
     349             :     /// Requests the list of trace buffers names from the table.
     350           0 :     void TraceBufListGet(std::vector<std::string>& trace_buf_list) {
     351           0 :         std::scoped_lock lock(mutex_);
     352           0 :         typename TraceBufMap::iterator it;
     353           0 :         for (it = trace_buf_map_.begin(); it != trace_buf_map_.end(); ++it) {
     354           0 :             trace_buf_list.push_back(it->first);
     355             :         }
     356           0 :     }
     357             : 
     358             :     /// Returns the capacity of the trace buffer with the given name.
     359           1 :     size_t TraceBufCapacityGet(const std::string& buf_name) {
     360           1 :         std::scoped_lock lock(mutex_);
     361           1 :         typename TraceBufMap::iterator it = trace_buf_map_.find(buf_name);
     362           1 :         if (it != trace_buf_map_.end()) {
     363           1 :             boost::shared_ptr<TraceBuffer<TraceEntryT> > trace_buf =
     364           1 :                                                              it->second.lock();
     365           1 :             return trace_buf->TraceBufCapacityGet();
     366           1 :         } else {
     367           0 :             return 0;
     368             :         }
     369           1 :     }
     370             : 
     371             :     /// Sets a new size of the trace buffer with the given name. If the trace
     372             :     /// buffer with the specified with given name is not found, then an empty
     373             :     /// trace buffer is returned.
     374           0 :     boost::shared_ptr<TraceBuffer<TraceEntryT> > TraceBufCapacityReset(
     375             :                                     const std::string& buf_name, size_t size) {
     376           0 :         std::scoped_lock lock(mutex_);
     377           0 :         typename TraceBufMap::iterator it = trace_buf_map_.find(buf_name);
     378           0 :         if (it != trace_buf_map_.end()) {
     379           0 :             boost::shared_ptr<TraceBuffer<TraceEntryT> > trace_buf =
     380           0 :                                                              it->second.lock();
     381           0 :             trace_buf->TraceBufCapacityReset(size);
     382           0 :             return trace_buf;
     383           0 :         }
     384           0 :         return boost::shared_ptr<TraceBuffer<TraceEntryT> >();
     385           0 :     }
     386             : 
     387             : private:
     388             : 
     389             :     /// Forbids the default ctor.
     390         264 :     Trace() {
     391         264 :         trace_enable_ = true;
     392         264 :     }
     393             : 
     394             :     /// Destroys the table.
     395             :     ~Trace() {
     396             : 
     397             :         delete trace_;
     398             :     }
     399             : 
     400             :     /// A pointer to the table (singleton) used in this program.
     401             :     static Trace *trace_;
     402             : 
     403             :     /// Determines if the tracing is enabled for the table.
     404             :     std::atomic<bool> trace_enable_;
     405             : 
     406             :     /// Stores the table of trace buffers.
     407             :     TraceBufMap trace_buf_map_;
     408             : 
     409             :     /// A mutex to protect the table from data races.
     410             :     std::mutex mutex_;
     411             : 
     412             :     DISALLOW_COPY_AND_ASSIGN(Trace);
     413             : };
     414             : 
     415             : #endif // __TRACE_H__

Generated by: LCOV version 1.14