blob: 582ed279ccce3e3a09bca84d267933fc9ede8b11 [file] [log] [blame]
// Copyright 2016 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "components/tracing/core/trace_ring_buffer.h"
#include "base/threading/platform_thread.h"
namespace tracing {
namespace v2 {
TraceRingBuffer::TraceRingBuffer(uint8_t* begin, size_t size)
: num_chunks_(size / kChunkSize),
num_chunks_taken_(0),
current_chunk_idx_(0) {
DCHECK_GT(num_chunks_, 0u);
DCHECK_EQ(0ul, reinterpret_cast<uintptr_t>(begin) % sizeof(uintptr_t));
chunks_.reset(new Chunk[num_chunks_]);
uint8_t* chunk_begin = begin;
for (size_t i = 0; i < num_chunks_; ++i) {
chunks_[i].Initialize(chunk_begin);
chunk_begin += kChunkSize;
}
}
TraceRingBuffer::~TraceRingBuffer() {}
TraceRingBuffer::Chunk* TraceRingBuffer::TakeChunk(uint32_t writer_id) {
base::AutoLock lock(lock_);
DCHECK_GT(num_chunks_, 0ul);
DCHECK_LT(current_chunk_idx_, num_chunks_);
for (size_t i = 0; i < num_chunks_; ++i) {
Chunk* chunk = &chunks_[current_chunk_idx_];
current_chunk_idx_ = (current_chunk_idx_ + 1) % num_chunks_;
if (!chunk->is_owned()) {
chunk->Clear();
DCHECK_NE(0u, writer_id);
chunk->set_owner(writer_id);
num_chunks_taken_++;
return chunk;
}
}
// Bankrupcy: there are more threads than chunks. All chunks were in flight.
if (!bankrupcy_chunk_storage_) {
bankrupcy_chunk_storage_.reset(new uint8_t[kChunkSize]);
bankrupcy_chunk_.Initialize(&bankrupcy_chunk_storage_.get()[0]);
}
bankrupcy_chunk_.Clear();
return &bankrupcy_chunk_;
}
void TraceRingBuffer::ReturnChunk(TraceRingBuffer::Chunk* chunk) {
// Returning a chunk without using it is quite odd, very likely a bug.
DCHECK_GT(chunk->used_size(), 0u);
// The caller should never return chunks which are part of a retaining chain.
DCHECK(!chunk->next_in_owner_list());
if (chunk == &bankrupcy_chunk_)
return;
// TODO(primiano): this lock might be removed in favor of acquire/release
// semantics on the two vars below. Check once the reader of these are landed.
base::AutoLock lock(lock_);
chunk->clear_owner();
num_chunks_taken_--;
}
bool TraceRingBuffer::IsBankrupcyChunkForTesting(const Chunk* chunk) const {
return chunk == &bankrupcy_chunk_;
}
size_t TraceRingBuffer::GetNumChunksTaken() const {
base::AutoLock lock(lock_);
return num_chunks_taken_;
}
TraceRingBuffer::Chunk::Chunk()
: begin_(nullptr), owner_(kNoChunkOwner), next_in_owner_list_(nullptr) {}
TraceRingBuffer::Chunk::~Chunk() {}
void TraceRingBuffer::Chunk::Clear() {
set_used_size(0);
set_next_in_owner_list(nullptr);
}
void TraceRingBuffer::Chunk::Initialize(uint8_t* begin) {
DCHECK_EQ(0ul, reinterpret_cast<uintptr_t>(begin) % sizeof(uintptr_t));
begin_ = begin;
}
} // namespace v2
} // namespace tracing