/* * Copyright (C) 2018 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "perfetto/ext/base/metatrace.h" #include "perfetto/base/compiler.h" #include "perfetto/base/task_runner.h" #include "perfetto/base/time.h" #include "perfetto/ext/base/file_utils.h" #include "perfetto/ext/base/thread_annotations.h" namespace perfetto { namespace metatrace { std::atomic g_enabled_tags{0}; std::atomic g_enabled_timestamp{0}; // static members constexpr size_t RingBuffer::kCapacity; std::array RingBuffer::records_; std::atomic RingBuffer::read_task_queued_; std::atomic RingBuffer::wr_index_; std::atomic RingBuffer::rd_index_; std::atomic RingBuffer::has_overruns_; Record RingBuffer::bankruptcy_record_; constexpr uint16_t Record::kTypeMask; constexpr uint16_t Record::kTypeCounter; constexpr uint16_t Record::kTypeEvent; namespace { // std::function<> is not trivially de/constructible. This struct wraps it in a // heap-allocated struct to avoid static initializers. struct Delegate { static Delegate* GetInstance() { static Delegate* instance = new Delegate(); return instance; } base::TaskRunner* task_runner = nullptr; std::function read_task; }; } // namespace bool Enable(std::function read_task, base::TaskRunner* task_runner, uint32_t tags) { PERFETTO_DCHECK(read_task); PERFETTO_DCHECK(task_runner->RunsTasksOnCurrentThread()); if (g_enabled_tags.load(std::memory_order_acquire)) return false; Delegate* dg = Delegate::GetInstance(); dg->task_runner = task_runner; dg->read_task = std::move(read_task); RingBuffer::Reset(); g_enabled_timestamp.store(TraceTimeNowNs(), std::memory_order_relaxed); g_enabled_tags.store(tags, std::memory_order_release); return true; } void Disable() { g_enabled_tags.store(0, std::memory_order_release); Delegate* dg = Delegate::GetInstance(); PERFETTO_DCHECK(!dg->task_runner || dg->task_runner->RunsTasksOnCurrentThread()); dg->task_runner = nullptr; dg->read_task = nullptr; } // static void RingBuffer::Reset() { bankruptcy_record_.clear(); for (Record& record : records_) record.clear(); wr_index_ = 0; rd_index_ = 0; has_overruns_ = false; read_task_queued_ = false; } // static Record* RingBuffer::AppendNewRecord() { auto wr_index = wr_index_.fetch_add(1, std::memory_order_acq_rel); // rd_index can only monotonically increase, we don't care if we read an // older value, we'll just hit the slow-path a bit earlier if it happens. auto rd_index = rd_index_.load(std::memory_order_relaxed); PERFETTO_DCHECK(wr_index >= rd_index); auto size = wr_index - rd_index; if (PERFETTO_LIKELY(size < kCapacity / 2)) return At(wr_index); // Slow-path: Enqueue the read task and handle overruns. bool expected = false; if (RingBuffer::read_task_queued_.compare_exchange_strong(expected, true)) { Delegate* dg = Delegate::GetInstance(); if (dg->task_runner) { dg->task_runner->PostTask([] { // Meta-tracing might have been disabled in the meantime. auto read_task = Delegate::GetInstance()->read_task; if (read_task) read_task(); RingBuffer::read_task_queued_ = false; }); } } if (PERFETTO_LIKELY(size < kCapacity)) return At(wr_index); has_overruns_.store(true, std::memory_order_release); wr_index_.fetch_sub(1, std::memory_order_acq_rel); // In the case of overflows, threads will race writing on the same memory // location and TSan will rightly complain. This is fine though because nobody // will read the bankruptcy record and it's designed to contain garbage. PERFETTO_ANNOTATE_BENIGN_RACE_SIZED(&bankruptcy_record_, sizeof(Record), "nothing reads bankruptcy_record_") return &bankruptcy_record_; } // static bool RingBuffer::IsOnValidTaskRunner() { auto* task_runner = Delegate::GetInstance()->task_runner; return task_runner && task_runner->RunsTasksOnCurrentThread(); } } // namespace metatrace } // namespace perfetto