|  | /* | 
|  | * Copyright (C) 2020 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. | 
|  | */ | 
|  |  | 
|  | #ifndef SRC_TRACE_PROCESSOR_TP_METATRACE_H_ | 
|  | #define SRC_TRACE_PROCESSOR_TP_METATRACE_H_ | 
|  |  | 
|  | #include <array> | 
|  | #include <functional> | 
|  | #include <vector> | 
|  |  | 
|  | #include "perfetto/base/time.h" | 
|  | #include "perfetto/ext/base/metatrace_events.h" | 
|  | #include "perfetto/ext/base/string_view.h" | 
|  | #include "perfetto/ext/base/thread_checker.h" | 
|  | #include "perfetto/trace_processor/metatrace_config.h" | 
|  | #include "protos/perfetto/trace_processor/metatrace_categories.pbzero.h" | 
|  |  | 
|  | // Trace processor maintains its own base implementation to avoid the | 
|  | // threading and task runners which are required by base's metatracing. | 
|  | // Moreover, this metatrace also adds support for args which is missing | 
|  | // from base's metatracing. | 
|  | // On the other hand, this implementation is not (currently) thread-safe | 
|  | // and is likely less performant than base's implementation. | 
|  | namespace perfetto { | 
|  | namespace trace_processor { | 
|  | namespace metatrace { | 
|  |  | 
|  | using Category = protos::pbzero::MetatraceCategories; | 
|  |  | 
|  | // Stores whether meta-tracing is enabled. | 
|  | extern Category g_enabled_categories; | 
|  |  | 
|  | inline uint64_t TraceTimeNowNs() { | 
|  | return static_cast<uint64_t>(base::GetBootTimeNs().count()); | 
|  | } | 
|  |  | 
|  | struct Record { | 
|  | // Timestamp since boot in ns. | 
|  | uint64_t timestamp_ns; | 
|  |  | 
|  | // Duration of the event. | 
|  | uint64_t duration_ns; | 
|  |  | 
|  | // The name of the event. | 
|  | // This is assumed to be a static/long lived string. | 
|  | const char* event_name; | 
|  |  | 
|  | // Extra context for some types of events. | 
|  | // This buffer is leaked once per record - every time a record is | 
|  | // reused, the old memory is released and a new allocation is performed. | 
|  | char* args_buffer = nullptr; | 
|  | uint32_t args_buffer_size = 0; | 
|  |  | 
|  | // Adds an arg to the record. | 
|  | void AddArg(base::StringView key, base::StringView value) { | 
|  | #if PERFETTO_DCHECK_IS_ON() | 
|  | // |key| and |value| should not contain any '\0' characters as it | 
|  | // messes with the |args_buffer| which uses '\0' to deliniate different | 
|  | // arguments. | 
|  | for (char c : key) { | 
|  | PERFETTO_DCHECK(c != '\0'); | 
|  | } | 
|  | for (char c : value) { | 
|  | PERFETTO_DCHECK(c != '\0'); | 
|  | } | 
|  | #endif | 
|  | size_t new_buffer_size = args_buffer_size + key.size() + value.size() + 2; | 
|  | args_buffer = static_cast<char*>(realloc(args_buffer, new_buffer_size)); | 
|  |  | 
|  | memcpy(&args_buffer[args_buffer_size], key.data(), key.size()); | 
|  | args_buffer[args_buffer_size + key.size()] = '\0'; | 
|  | memcpy(&args_buffer[args_buffer_size + key.size() + 1], value.data(), | 
|  | value.size()); | 
|  | args_buffer[new_buffer_size - 1] = '\0'; | 
|  |  | 
|  | args_buffer_size = static_cast<uint32_t>(new_buffer_size); | 
|  | } | 
|  |  | 
|  | void AddArg(base::StringView key, const std::string& value) { | 
|  | AddArg(key, base::StringView(value)); | 
|  | } | 
|  |  | 
|  | void AddArg(base::StringView key, const char* value) { | 
|  | AddArg(key, base::StringView(value)); | 
|  | } | 
|  | }; | 
|  |  | 
|  | // Implementation of fixed-size ring buffer. The implementation of this | 
|  | // class is modelled on the RingBuffer in metatrace.h of base but is different | 
|  | // in a couple of ways: | 
|  | //  1. This class is *not* thread safe. | 
|  | //  2. The Record type stored in this class has the capability of storing | 
|  | //     extra, event-specific context. For example, when tracing SQL query | 
|  | //     execution, we store the query string. | 
|  | //  3. The buffer is designed to be written continuously while meta-tracing | 
|  | //     is enabled and read one-shot at the end of execution. | 
|  | class RingBuffer { | 
|  | public: | 
|  | static constexpr uint32_t kDefaultCapacity = 256 * 1024; | 
|  |  | 
|  | RingBuffer(); | 
|  | ~RingBuffer() = default; | 
|  |  | 
|  | std::pair<uint64_t, Record*> AppendRecord(const char* event_name) { | 
|  | PERFETTO_DCHECK_THREAD(thread_checker_); | 
|  | PERFETTO_DCHECK(!is_reading_); | 
|  |  | 
|  | uint64_t idx = write_idx_++; | 
|  | Record* record = At(idx); | 
|  | record->timestamp_ns = TraceTimeNowNs(); | 
|  | record->duration_ns = 0; | 
|  | record->event_name = event_name; | 
|  | record->args_buffer_size = 0; | 
|  | return std::make_pair(idx, record); | 
|  | } | 
|  |  | 
|  | Record* At(uint64_t idx) { return &data_[idx % data_.size()]; } | 
|  |  | 
|  | void ReadAll(std::function<void(Record*)>); | 
|  |  | 
|  | static RingBuffer* GetInstance() { | 
|  | static RingBuffer* rb = new RingBuffer(); | 
|  | return rb; | 
|  | } | 
|  |  | 
|  | uint64_t IndexOf(Record* record) { | 
|  | return static_cast<uint64_t>(std::distance(data_.data(), record)); | 
|  | } | 
|  |  | 
|  | // Returns whether the record at the |index| has been overwritten because | 
|  | // of wraps of the ring buffer. | 
|  | bool HasOverwritten(uint64_t index) { | 
|  | return index + data_.size() < write_idx_; | 
|  | } | 
|  |  | 
|  | // Request the ring buffer to be resized. Clears the existing buffer. | 
|  | void Resize(size_t requested_capacity); | 
|  |  | 
|  | private: | 
|  | bool is_reading_ = false; | 
|  |  | 
|  | uint64_t start_idx_ = 0; | 
|  | uint64_t write_idx_ = 0; | 
|  | std::vector<Record> data_; | 
|  |  | 
|  | PERFETTO_THREAD_CHECKER(thread_checker_) | 
|  | }; | 
|  |  | 
|  | class ScopedEvent { | 
|  | public: | 
|  | ScopedEvent() = default; | 
|  |  | 
|  | ~ScopedEvent() { | 
|  | if (PERFETTO_LIKELY(!record_)) | 
|  | return; | 
|  | if (RingBuffer::GetInstance()->HasOverwritten(record_idx_)) | 
|  | return; | 
|  | auto now = TraceTimeNowNs(); | 
|  | record_->duration_ns = now - record_->timestamp_ns; | 
|  | } | 
|  |  | 
|  | ScopedEvent(ScopedEvent&& value) { | 
|  | record_ = value.record_; | 
|  | record_idx_ = value.record_idx_; | 
|  | value.record_ = nullptr; | 
|  | } | 
|  |  | 
|  | template <typename Fn = void(Record*)> | 
|  | static ScopedEvent Create( | 
|  | Category category, | 
|  | const char* event_id, | 
|  | Fn args_fn = [](Record*) {}) { | 
|  | if (PERFETTO_LIKELY((category & g_enabled_categories) == 0)) | 
|  | return ScopedEvent(); | 
|  |  | 
|  | ScopedEvent event; | 
|  | std::tie(event.record_idx_, event.record_) = | 
|  | RingBuffer::GetInstance()->AppendRecord(event_id); | 
|  | args_fn(event.record_); | 
|  | return event; | 
|  | } | 
|  |  | 
|  | private: | 
|  | ScopedEvent(const ScopedEvent&) = delete; | 
|  | ScopedEvent& operator=(const ScopedEvent&) = delete; | 
|  |  | 
|  | ScopedEvent& operator=(ScopedEvent&& value) = delete; | 
|  |  | 
|  | Record* record_ = nullptr; | 
|  | uint64_t record_idx_ = 0; | 
|  | }; | 
|  |  | 
|  | // Enables meta-tracing of trace-processor. | 
|  | void Enable(MetatraceConfig config = {}); | 
|  |  | 
|  | // Disables meta-tracing of trace-processor and reads all records. | 
|  | void DisableAndReadBuffer(std::function<void(Record*)>); | 
|  |  | 
|  | // Boilerplate to derive a unique variable name for the event. | 
|  | #define PERFETTO_TP_METATRACE_UID2(a, b) a##b | 
|  | #define PERFETTO_TP_METATRACE_UID(x) PERFETTO_TP_METATRACE_UID2(metatrace_, x) | 
|  |  | 
|  | #define PERFETTO_TP_TRACE(...)                  \ | 
|  | auto PERFETTO_TP_METATRACE_UID(__COUNTER__) = \ | 
|  | ::perfetto::trace_processor::metatrace::ScopedEvent::Create(__VA_ARGS__) | 
|  |  | 
|  | }  // namespace metatrace | 
|  | }  // namespace trace_processor | 
|  | }  // namespace perfetto | 
|  |  | 
|  | #endif  // SRC_TRACE_PROCESSOR_TP_METATRACE_H_ |