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__