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__