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

Generated by: LCOV version 1.14