| /* |
| * 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) noexcept { |
| 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_ |