1// Copyright 2016 the V8 project 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 "src/libplatform/tracing/trace-buffer.h"
6
7namespace v8 {
8namespace platform {
9namespace tracing {
10
11TraceBufferRingBuffer::TraceBufferRingBuffer(size_t max_chunks,
12                                             TraceWriter* trace_writer)
13    : max_chunks_(max_chunks) {
14  trace_writer_.reset(trace_writer);
15  chunks_.resize(max_chunks);
16}
17
18TraceObject* TraceBufferRingBuffer::AddTraceEvent(uint64_t* handle) {
19  base::MutexGuard guard(&mutex_);
20  if (is_empty_ || chunks_[chunk_index_]->IsFull()) {
21    chunk_index_ = is_empty_ ? 0 : NextChunkIndex(chunk_index_);
22    is_empty_ = false;
23    auto& chunk = chunks_[chunk_index_];
24    if (chunk) {
25      chunk->Reset(current_chunk_seq_++);
26    } else {
27      chunk.reset(new TraceBufferChunk(current_chunk_seq_++));
28    }
29  }
30  auto& chunk = chunks_[chunk_index_];
31  size_t event_index;
32  TraceObject* trace_object = chunk->AddTraceEvent(&event_index);
33  *handle = MakeHandle(chunk_index_, chunk->seq(), event_index);
34  return trace_object;
35}
36
37TraceObject* TraceBufferRingBuffer::GetEventByHandle(uint64_t handle) {
38  base::MutexGuard guard(&mutex_);
39  size_t chunk_index, event_index;
40  uint32_t chunk_seq;
41  ExtractHandle(handle, &chunk_index, &chunk_seq, &event_index);
42  if (chunk_index >= chunks_.size()) return nullptr;
43  auto& chunk = chunks_[chunk_index];
44  if (!chunk || chunk->seq() != chunk_seq) return nullptr;
45  return chunk->GetEventAt(event_index);
46}
47
48bool TraceBufferRingBuffer::Flush() {
49  base::MutexGuard guard(&mutex_);
50  // This flushes all the traces stored in the buffer.
51  if (!is_empty_) {
52    for (size_t i = NextChunkIndex(chunk_index_);; i = NextChunkIndex(i)) {
53      if (auto& chunk = chunks_[i]) {
54        for (size_t j = 0; j < chunk->size(); ++j) {
55          trace_writer_->AppendTraceEvent(chunk->GetEventAt(j));
56        }
57      }
58      if (i == chunk_index_) break;
59    }
60  }
61  trace_writer_->Flush();
62  // This resets the trace buffer.
63  is_empty_ = true;
64  return true;
65}
66
67uint64_t TraceBufferRingBuffer::MakeHandle(size_t chunk_index,
68                                           uint32_t chunk_seq,
69                                           size_t event_index) const {
70  return static_cast<uint64_t>(chunk_seq) * Capacity() +
71         chunk_index * TraceBufferChunk::kChunkSize + event_index;
72}
73
74void TraceBufferRingBuffer::ExtractHandle(uint64_t handle, size_t* chunk_index,
75                                          uint32_t* chunk_seq,
76                                          size_t* event_index) const {
77  *chunk_seq = static_cast<uint32_t>(handle / Capacity());
78  size_t indices = handle % Capacity();
79  *chunk_index = indices / TraceBufferChunk::kChunkSize;
80  *event_index = indices % TraceBufferChunk::kChunkSize;
81}
82
83size_t TraceBufferRingBuffer::NextChunkIndex(size_t index) const {
84  if (++index >= max_chunks_) index = 0;
85  return index;
86}
87
88TraceBufferChunk::TraceBufferChunk(uint32_t seq) : seq_(seq) {}
89
90void TraceBufferChunk::Reset(uint32_t new_seq) {
91  next_free_ = 0;
92  seq_ = new_seq;
93}
94
95TraceObject* TraceBufferChunk::AddTraceEvent(size_t* event_index) {
96  *event_index = next_free_++;
97  return &chunk_[*event_index];
98}
99
100TraceBuffer* TraceBuffer::CreateTraceBufferRingBuffer(
101    size_t max_chunks, TraceWriter* trace_writer) {
102  return new TraceBufferRingBuffer(max_chunks, trace_writer);
103}
104
105}  // namespace tracing
106}  // namespace platform
107}  // namespace v8
108