|  | /* | 
|  | * 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<uint32_t> g_enabled_tags{0}; | 
|  | std::atomic<uint64_t> g_enabled_timestamp{0}; | 
|  |  | 
|  | // static members | 
|  | std::array<Record, RingBuffer::kCapacity> RingBuffer::records_; | 
|  | std::atomic<bool> RingBuffer::read_task_queued_; | 
|  | std::atomic<uint64_t> RingBuffer::wr_index_; | 
|  | std::atomic<uint64_t> RingBuffer::rd_index_; | 
|  | std::atomic<bool> RingBuffer::has_overruns_; | 
|  | Record RingBuffer::bankruptcy_record_; | 
|  |  | 
|  | #if !PERFETTO_IS_AT_LEAST_CPP17() | 
|  | constexpr size_t RingBuffer::kCapacity; | 
|  | constexpr uint16_t Record::kTypeMask; | 
|  | constexpr uint16_t Record::kTypeCounter; | 
|  | constexpr uint16_t Record::kTypeEvent; | 
|  | #endif | 
|  |  | 
|  | 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<void()> read_task; | 
|  | }; | 
|  |  | 
|  | }  // namespace | 
|  |  | 
|  | bool Enable(std::function<void()> 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 |