Home | History | Annotate | Download | only in trace_event
      1 // Copyright 2015 The Chromium Authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style license that can be
      3 // found in the LICENSE file.
      4 
      5 #include "base/trace_event/trace_buffer.h"
      6 
      7 #include <utility>
      8 #include <vector>
      9 
     10 #include "base/macros.h"
     11 #include "base/memory/scoped_ptr.h"
     12 #include "base/trace_event/trace_event_impl.h"
     13 
     14 namespace base {
     15 namespace trace_event {
     16 
     17 namespace {
     18 
     19 class TraceBufferRingBuffer : public TraceBuffer {
     20  public:
     21   TraceBufferRingBuffer(size_t max_chunks)
     22       : max_chunks_(max_chunks),
     23         recyclable_chunks_queue_(new size_t[queue_capacity()]),
     24         queue_head_(0),
     25         queue_tail_(max_chunks),
     26         current_iteration_index_(0),
     27         current_chunk_seq_(1) {
     28     chunks_.reserve(max_chunks);
     29     for (size_t i = 0; i < max_chunks; ++i)
     30       recyclable_chunks_queue_[i] = i;
     31   }
     32 
     33   scoped_ptr<TraceBufferChunk> GetChunk(size_t* index) override {
     34     // Because the number of threads is much less than the number of chunks,
     35     // the queue should never be empty.
     36     DCHECK(!QueueIsEmpty());
     37 
     38     *index = recyclable_chunks_queue_[queue_head_];
     39     queue_head_ = NextQueueIndex(queue_head_);
     40     current_iteration_index_ = queue_head_;
     41 
     42     if (*index >= chunks_.size())
     43       chunks_.resize(*index + 1);
     44 
     45     TraceBufferChunk* chunk = chunks_[*index].release();
     46     chunks_[*index] = NULL;  // Put NULL in the slot of a in-flight chunk.
     47     if (chunk)
     48       chunk->Reset(current_chunk_seq_++);
     49     else
     50       chunk = new TraceBufferChunk(current_chunk_seq_++);
     51 
     52     return scoped_ptr<TraceBufferChunk>(chunk);
     53   }
     54 
     55   void ReturnChunk(size_t index, scoped_ptr<TraceBufferChunk> chunk) override {
     56     // When this method is called, the queue should not be full because it
     57     // can contain all chunks including the one to be returned.
     58     DCHECK(!QueueIsFull());
     59     DCHECK(chunk);
     60     DCHECK_LT(index, chunks_.size());
     61     DCHECK(!chunks_[index]);
     62     chunks_[index] = std::move(chunk);
     63     recyclable_chunks_queue_[queue_tail_] = index;
     64     queue_tail_ = NextQueueIndex(queue_tail_);
     65   }
     66 
     67   bool IsFull() const override { return false; }
     68 
     69   size_t Size() const override {
     70     // This is approximate because not all of the chunks are full.
     71     return chunks_.size() * TraceBufferChunk::kTraceBufferChunkSize;
     72   }
     73 
     74   size_t Capacity() const override {
     75     return max_chunks_ * TraceBufferChunk::kTraceBufferChunkSize;
     76   }
     77 
     78   TraceEvent* GetEventByHandle(TraceEventHandle handle) override {
     79     if (handle.chunk_index >= chunks_.size())
     80       return NULL;
     81     TraceBufferChunk* chunk = chunks_[handle.chunk_index].get();
     82     if (!chunk || chunk->seq() != handle.chunk_seq)
     83       return NULL;
     84     return chunk->GetEventAt(handle.event_index);
     85   }
     86 
     87   const TraceBufferChunk* NextChunk() override {
     88     if (chunks_.empty())
     89       return NULL;
     90 
     91     while (current_iteration_index_ != queue_tail_) {
     92       size_t chunk_index = recyclable_chunks_queue_[current_iteration_index_];
     93       current_iteration_index_ = NextQueueIndex(current_iteration_index_);
     94       if (chunk_index >= chunks_.size())  // Skip uninitialized chunks.
     95         continue;
     96       DCHECK(chunks_[chunk_index]);
     97       return chunks_[chunk_index].get();
     98     }
     99     return NULL;
    100   }
    101 
    102   scoped_ptr<TraceBuffer> CloneForIteration() const override {
    103     scoped_ptr<ClonedTraceBuffer> cloned_buffer(new ClonedTraceBuffer());
    104     for (size_t queue_index = queue_head_; queue_index != queue_tail_;
    105          queue_index = NextQueueIndex(queue_index)) {
    106       size_t chunk_index = recyclable_chunks_queue_[queue_index];
    107       if (chunk_index >= chunks_.size())  // Skip uninitialized chunks.
    108         continue;
    109       TraceBufferChunk* chunk = chunks_[chunk_index].get();
    110       cloned_buffer->chunks_.push_back(chunk ? chunk->Clone() : NULL);
    111     }
    112     return std::move(cloned_buffer);
    113   }
    114 
    115   void EstimateTraceMemoryOverhead(
    116       TraceEventMemoryOverhead* overhead) override {
    117     overhead->Add("TraceBufferRingBuffer", sizeof(*this));
    118     for (size_t queue_index = queue_head_; queue_index != queue_tail_;
    119          queue_index = NextQueueIndex(queue_index)) {
    120       size_t chunk_index = recyclable_chunks_queue_[queue_index];
    121       if (chunk_index >= chunks_.size())  // Skip uninitialized chunks.
    122         continue;
    123       chunks_[chunk_index]->EstimateTraceMemoryOverhead(overhead);
    124     }
    125   }
    126 
    127  private:
    128   class ClonedTraceBuffer : public TraceBuffer {
    129    public:
    130     ClonedTraceBuffer() : current_iteration_index_(0) {}
    131 
    132     // The only implemented method.
    133     const TraceBufferChunk* NextChunk() override {
    134       return current_iteration_index_ < chunks_.size()
    135                  ? chunks_[current_iteration_index_++].get()
    136                  : NULL;
    137     }
    138 
    139     scoped_ptr<TraceBufferChunk> GetChunk(size_t* /* index */) override {
    140       NOTIMPLEMENTED();
    141       return scoped_ptr<TraceBufferChunk>();
    142     }
    143     void ReturnChunk(size_t /*index*/, scoped_ptr<TraceBufferChunk>) override {
    144       NOTIMPLEMENTED();
    145     }
    146     bool IsFull() const override { return false; }
    147     size_t Size() const override { return 0; }
    148     size_t Capacity() const override { return 0; }
    149     TraceEvent* GetEventByHandle(TraceEventHandle /* handle */) override {
    150       return NULL;
    151     }
    152     scoped_ptr<TraceBuffer> CloneForIteration() const override {
    153       NOTIMPLEMENTED();
    154       return scoped_ptr<TraceBuffer>();
    155     }
    156     void EstimateTraceMemoryOverhead(
    157         TraceEventMemoryOverhead* /* overhead */) override {
    158       NOTIMPLEMENTED();
    159     }
    160 
    161     size_t current_iteration_index_;
    162     std::vector<scoped_ptr<TraceBufferChunk>> chunks_;
    163   };
    164 
    165   bool QueueIsEmpty() const { return queue_head_ == queue_tail_; }
    166 
    167   size_t QueueSize() const {
    168     return queue_tail_ > queue_head_
    169                ? queue_tail_ - queue_head_
    170                : queue_tail_ + queue_capacity() - queue_head_;
    171   }
    172 
    173   bool QueueIsFull() const { return QueueSize() == queue_capacity() - 1; }
    174 
    175   size_t queue_capacity() const {
    176     // One extra space to help distinguish full state and empty state.
    177     return max_chunks_ + 1;
    178   }
    179 
    180   size_t NextQueueIndex(size_t index) const {
    181     index++;
    182     if (index >= queue_capacity())
    183       index = 0;
    184     return index;
    185   }
    186 
    187   size_t max_chunks_;
    188   std::vector<scoped_ptr<TraceBufferChunk>> chunks_;
    189 
    190   scoped_ptr<size_t[]> recyclable_chunks_queue_;
    191   size_t queue_head_;
    192   size_t queue_tail_;
    193 
    194   size_t current_iteration_index_;
    195   uint32_t current_chunk_seq_;
    196 
    197   DISALLOW_COPY_AND_ASSIGN(TraceBufferRingBuffer);
    198 };
    199 
    200 class TraceBufferVector : public TraceBuffer {
    201  public:
    202   TraceBufferVector(size_t max_chunks)
    203       : in_flight_chunk_count_(0),
    204         current_iteration_index_(0),
    205         max_chunks_(max_chunks) {
    206     chunks_.reserve(max_chunks_);
    207   }
    208 
    209   scoped_ptr<TraceBufferChunk> GetChunk(size_t* index) override {
    210     // This function may be called when adding normal events or indirectly from
    211     // AddMetadataEventsWhileLocked(). We can not DECHECK(!IsFull()) because we
    212     // have to add the metadata events and flush thread-local buffers even if
    213     // the buffer is full.
    214     *index = chunks_.size();
    215     chunks_.push_back(NULL);  // Put NULL in the slot of a in-flight chunk.
    216     ++in_flight_chunk_count_;
    217     // + 1 because zero chunk_seq is not allowed.
    218     return scoped_ptr<TraceBufferChunk>(
    219         new TraceBufferChunk(static_cast<uint32_t>(*index) + 1));
    220   }
    221 
    222   void ReturnChunk(size_t index, scoped_ptr<TraceBufferChunk> chunk) override {
    223     DCHECK_GT(in_flight_chunk_count_, 0u);
    224     DCHECK_LT(index, chunks_.size());
    225     DCHECK(!chunks_[index]);
    226     --in_flight_chunk_count_;
    227     chunks_[index] = chunk.release();
    228   }
    229 
    230   bool IsFull() const override { return chunks_.size() >= max_chunks_; }
    231 
    232   size_t Size() const override {
    233     // This is approximate because not all of the chunks are full.
    234     return chunks_.size() * TraceBufferChunk::kTraceBufferChunkSize;
    235   }
    236 
    237   size_t Capacity() const override {
    238     return max_chunks_ * TraceBufferChunk::kTraceBufferChunkSize;
    239   }
    240 
    241   TraceEvent* GetEventByHandle(TraceEventHandle handle) override {
    242     if (handle.chunk_index >= chunks_.size())
    243       return NULL;
    244     TraceBufferChunk* chunk = chunks_[handle.chunk_index];
    245     if (!chunk || chunk->seq() != handle.chunk_seq)
    246       return NULL;
    247     return chunk->GetEventAt(handle.event_index);
    248   }
    249 
    250   const TraceBufferChunk* NextChunk() override {
    251     while (current_iteration_index_ < chunks_.size()) {
    252       // Skip in-flight chunks.
    253       const TraceBufferChunk* chunk = chunks_[current_iteration_index_++];
    254       if (chunk)
    255         return chunk;
    256     }
    257     return NULL;
    258   }
    259 
    260   scoped_ptr<TraceBuffer> CloneForIteration() const override {
    261     NOTIMPLEMENTED();
    262     return scoped_ptr<TraceBuffer>();
    263   }
    264 
    265   void EstimateTraceMemoryOverhead(
    266       TraceEventMemoryOverhead* overhead) override {
    267     const size_t chunks_ptr_vector_allocated_size =
    268         sizeof(*this) + max_chunks_ * sizeof(decltype(chunks_)::value_type);
    269     const size_t chunks_ptr_vector_resident_size =
    270         sizeof(*this) + chunks_.size() * sizeof(decltype(chunks_)::value_type);
    271     overhead->Add("TraceBufferVector", chunks_ptr_vector_allocated_size,
    272                   chunks_ptr_vector_resident_size);
    273     for (size_t i = 0; i < chunks_.size(); ++i) {
    274       TraceBufferChunk* chunk = chunks_[i];
    275       // Skip the in-flight (nullptr) chunks. They will be accounted by the
    276       // per-thread-local dumpers, see ThreadLocalEventBuffer::OnMemoryDump.
    277       if (chunk)
    278         chunk->EstimateTraceMemoryOverhead(overhead);
    279     }
    280   }
    281 
    282  private:
    283   size_t in_flight_chunk_count_;
    284   size_t current_iteration_index_;
    285   size_t max_chunks_;
    286   ScopedVector<TraceBufferChunk> chunks_;
    287 
    288   DISALLOW_COPY_AND_ASSIGN(TraceBufferVector);
    289 };
    290 
    291 }  // namespace
    292 
    293 TraceBufferChunk::TraceBufferChunk(uint32_t seq) : next_free_(0), seq_(seq) {}
    294 
    295 TraceBufferChunk::~TraceBufferChunk() {}
    296 
    297 void TraceBufferChunk::Reset(uint32_t new_seq) {
    298   for (size_t i = 0; i < next_free_; ++i)
    299     chunk_[i].Reset();
    300   next_free_ = 0;
    301   seq_ = new_seq;
    302   cached_overhead_estimate_.reset();
    303 }
    304 
    305 TraceEvent* TraceBufferChunk::AddTraceEvent(size_t* event_index) {
    306   DCHECK(!IsFull());
    307   *event_index = next_free_++;
    308   return &chunk_[*event_index];
    309 }
    310 
    311 scoped_ptr<TraceBufferChunk> TraceBufferChunk::Clone() const {
    312   scoped_ptr<TraceBufferChunk> cloned_chunk(new TraceBufferChunk(seq_));
    313   cloned_chunk->next_free_ = next_free_;
    314   for (size_t i = 0; i < next_free_; ++i)
    315     cloned_chunk->chunk_[i].CopyFrom(chunk_[i]);
    316   return cloned_chunk;
    317 }
    318 
    319 void TraceBufferChunk::EstimateTraceMemoryOverhead(
    320     TraceEventMemoryOverhead* overhead) {
    321   if (!cached_overhead_estimate_) {
    322     cached_overhead_estimate_.reset(new TraceEventMemoryOverhead);
    323 
    324     // When estimating the size of TraceBufferChunk, exclude the array of trace
    325     // events, as they are computed individually below.
    326     cached_overhead_estimate_->Add("TraceBufferChunk",
    327                                    sizeof(*this) - sizeof(chunk_));
    328   }
    329 
    330   const size_t num_cached_estimated_events =
    331       cached_overhead_estimate_->GetCount("TraceEvent");
    332   DCHECK_LE(num_cached_estimated_events, size());
    333 
    334   if (IsFull() && num_cached_estimated_events == size()) {
    335     overhead->Update(*cached_overhead_estimate_);
    336     return;
    337   }
    338 
    339   for (size_t i = num_cached_estimated_events; i < size(); ++i)
    340     chunk_[i].EstimateTraceMemoryOverhead(cached_overhead_estimate_.get());
    341 
    342   if (IsFull()) {
    343     cached_overhead_estimate_->AddSelf();
    344   } else {
    345     // The unused TraceEvents in |chunks_| are not cached. They will keep
    346     // changing as new TraceEvents are added to this chunk, so they are
    347     // computed on the fly.
    348     const size_t num_unused_trace_events = capacity() - size();
    349     overhead->Add("TraceEvent (unused)",
    350                   num_unused_trace_events * sizeof(TraceEvent));
    351   }
    352 
    353   overhead->Update(*cached_overhead_estimate_);
    354 }
    355 
    356 TraceResultBuffer::OutputCallback
    357 TraceResultBuffer::SimpleOutput::GetCallback() {
    358   return Bind(&SimpleOutput::Append, Unretained(this));
    359 }
    360 
    361 void TraceResultBuffer::SimpleOutput::Append(
    362     const std::string& json_trace_output) {
    363   json_output += json_trace_output;
    364 }
    365 
    366 TraceResultBuffer::TraceResultBuffer() : append_comma_(false) {}
    367 
    368 TraceResultBuffer::~TraceResultBuffer() {}
    369 
    370 void TraceResultBuffer::SetOutputCallback(
    371     const OutputCallback& json_chunk_callback) {
    372   output_callback_ = json_chunk_callback;
    373 }
    374 
    375 void TraceResultBuffer::Start() {
    376   append_comma_ = false;
    377   output_callback_.Run("[");
    378 }
    379 
    380 void TraceResultBuffer::AddFragment(const std::string& trace_fragment) {
    381   if (append_comma_)
    382     output_callback_.Run(",");
    383   append_comma_ = true;
    384   output_callback_.Run(trace_fragment);
    385 }
    386 
    387 void TraceResultBuffer::Finish() {
    388   output_callback_.Run("]");
    389 }
    390 
    391 TraceBuffer* TraceBuffer::CreateTraceBufferRingBuffer(size_t max_chunks) {
    392   return new TraceBufferRingBuffer(max_chunks);
    393 }
    394 
    395 TraceBuffer* TraceBuffer::CreateTraceBufferVectorOfSize(size_t max_chunks) {
    396   return new TraceBufferVector(max_chunks);
    397 }
    398 
    399 }  // namespace trace_event
    400 }  // namespace base
    401