• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2020 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 #ifndef SRC_TRACE_PROCESSOR_TP_METATRACE_H_
18 #define SRC_TRACE_PROCESSOR_TP_METATRACE_H_
19 
20 #include <array>
21 #include <functional>
22 #include <vector>
23 
24 #include "perfetto/base/time.h"
25 #include "perfetto/ext/base/metatrace_events.h"
26 #include "perfetto/ext/base/string_view.h"
27 #include "perfetto/ext/base/thread_checker.h"
28 #include "perfetto/trace_processor/metatrace_config.h"
29 #include "protos/perfetto/trace_processor/metatrace_categories.pbzero.h"
30 
31 // Trace processor maintains its own base implementation to avoid the
32 // threading and task runners which are required by base's metatracing.
33 // Moreover, this metatrace also adds support for args which is missing
34 // from base's metatracing.
35 // On the other hand, this implementation is not (currently) thread-safe
36 // and is likely less performant than base's implementation.
37 namespace perfetto {
38 namespace trace_processor {
39 namespace metatrace {
40 
41 using Category = protos::pbzero::MetatraceCategories;
42 
43 // Stores whether meta-tracing is enabled.
44 extern Category g_enabled_categories;
45 
TraceTimeNowNs()46 inline uint64_t TraceTimeNowNs() {
47   return static_cast<uint64_t>(base::GetBootTimeNs().count());
48 }
49 
50 struct Record {
51   // Timestamp since boot in ns.
52   uint64_t timestamp_ns;
53 
54   // Duration of the event.
55   uint64_t duration_ns;
56 
57   // The name of the event.
58   // This is assumed to be a static/long lived string.
59   const char* event_name;
60 
61   // Extra context for some types of events.
62   // This buffer is leaked once per record - every time a record is
63   // reused, the old memory is released and a new allocation is performed.
64   char* args_buffer = nullptr;
65   uint32_t args_buffer_size = 0;
66 
67   // Adds an arg to the record.
AddArgRecord68   void AddArg(base::StringView key, base::StringView value) {
69 #if PERFETTO_DCHECK_IS_ON()
70     // |key| and |value| should not contain any '\0' characters as it
71     // messes with the |args_buffer| which uses '\0' to deliniate different
72     // arguments.
73     for (char c : key) {
74       PERFETTO_DCHECK(c != '\0');
75     }
76     for (char c : value) {
77       PERFETTO_DCHECK(c != '\0');
78     }
79 #endif
80     size_t new_buffer_size = args_buffer_size + key.size() + value.size() + 2;
81     args_buffer = static_cast<char*>(realloc(args_buffer, new_buffer_size));
82 
83     memcpy(&args_buffer[args_buffer_size], key.data(), key.size());
84     args_buffer[args_buffer_size + key.size()] = '\0';
85     memcpy(&args_buffer[args_buffer_size + key.size() + 1], value.data(),
86            value.size());
87     args_buffer[new_buffer_size - 1] = '\0';
88 
89     args_buffer_size = static_cast<uint32_t>(new_buffer_size);
90   }
91 
AddArgRecord92   void AddArg(base::StringView key, const std::string& value) {
93     AddArg(key, base::StringView(value));
94   }
95 
AddArgRecord96   void AddArg(base::StringView key, const char* value) {
97     AddArg(key, base::StringView(value));
98   }
99 };
100 
101 // Implementation of fixed-size ring buffer. The implementation of this
102 // class is modelled on the RingBuffer in metatrace.h of base but is different
103 // in a couple of ways:
104 //  1. This class is *not* thread safe.
105 //  2. The Record type stored in this class has the capability of storing
106 //     extra, event-specific context. For example, when tracing SQL query
107 //     execution, we store the query string.
108 //  3. The buffer is designed to be written continuously while meta-tracing
109 //     is enabled and read one-shot at the end of execution.
110 class RingBuffer {
111  public:
112   static constexpr uint32_t kDefaultCapacity = 256 * 1024;
113 
114   RingBuffer();
115   ~RingBuffer() = default;
116 
AppendRecord(const char * event_name)117   std::pair<uint64_t, Record*> AppendRecord(const char* event_name) {
118     PERFETTO_DCHECK_THREAD(thread_checker_);
119     PERFETTO_DCHECK(!is_reading_);
120 
121     uint64_t idx = write_idx_++;
122     Record* record = At(idx);
123     record->timestamp_ns = TraceTimeNowNs();
124     record->duration_ns = 0;
125     record->event_name = event_name;
126     record->args_buffer_size = 0;
127     return std::make_pair(idx, record);
128   }
129 
At(uint64_t idx)130   Record* At(uint64_t idx) { return &data_[idx % data_.size()]; }
131 
132   void ReadAll(std::function<void(Record*)>);
133 
GetInstance()134   static RingBuffer* GetInstance() {
135     static RingBuffer* rb = new RingBuffer();
136     return rb;
137   }
138 
IndexOf(Record * record)139   uint64_t IndexOf(Record* record) {
140     return static_cast<uint64_t>(std::distance(data_.data(), record));
141   }
142 
143   // Returns whether the record at the |index| has been overwritten because
144   // of wraps of the ring buffer.
HasOverwritten(uint64_t index)145   bool HasOverwritten(uint64_t index) {
146     return index + data_.size() < write_idx_;
147   }
148 
149   // Request the ring buffer to be resized. Clears the existing buffer.
150   void Resize(size_t requested_capacity);
151 
152  private:
153   bool is_reading_ = false;
154 
155   uint64_t start_idx_ = 0;
156   uint64_t write_idx_ = 0;
157   std::vector<Record> data_;
158 
159   PERFETTO_THREAD_CHECKER(thread_checker_)
160 };
161 
162 class ScopedEvent {
163  public:
164   ScopedEvent() = default;
165 
~ScopedEvent()166   ~ScopedEvent() {
167     if (PERFETTO_LIKELY(!record_))
168       return;
169     if (RingBuffer::GetInstance()->HasOverwritten(record_idx_))
170       return;
171     auto now = TraceTimeNowNs();
172     record_->duration_ns = now - record_->timestamp_ns;
173   }
174 
ScopedEvent(ScopedEvent && value)175   ScopedEvent(ScopedEvent&& value) {
176     record_ = value.record_;
177     record_idx_ = value.record_idx_;
178     value.record_ = nullptr;
179   }
180 
181   template <typename Fn = void(Record*)>
182   static ScopedEvent Create(
183       Category category,
184       const char* event_id,
185       Fn args_fn = [](Record*) {}) {
186     if (PERFETTO_LIKELY((category & g_enabled_categories) == 0))
187       return ScopedEvent();
188 
189     ScopedEvent event;
190     std::tie(event.record_idx_, event.record_) =
191         RingBuffer::GetInstance()->AppendRecord(event_id);
192     args_fn(event.record_);
193     return event;
194   }
195 
196  private:
197   ScopedEvent(const ScopedEvent&) = delete;
198   ScopedEvent& operator=(const ScopedEvent&) = delete;
199 
200   ScopedEvent& operator=(ScopedEvent&& value) = delete;
201 
202   Record* record_ = nullptr;
203   uint64_t record_idx_ = 0;
204 };
205 
206 // Enables meta-tracing of trace-processor.
207 void Enable(MetatraceConfig config = {});
208 
209 // Disables meta-tracing of trace-processor and reads all records.
210 void DisableAndReadBuffer(std::function<void(Record*)>);
211 
212 // Boilerplate to derive a unique variable name for the event.
213 #define PERFETTO_TP_METATRACE_UID2(a, b) a##b
214 #define PERFETTO_TP_METATRACE_UID(x) PERFETTO_TP_METATRACE_UID2(metatrace_, x)
215 
216 #define PERFETTO_TP_TRACE(...)                  \
217   auto PERFETTO_TP_METATRACE_UID(__COUNTER__) = \
218       ::perfetto::trace_processor::metatrace::ScopedEvent::Create(__VA_ARGS__)
219 
220 }  // namespace metatrace
221 }  // namespace trace_processor
222 }  // namespace perfetto
223 
224 #endif  // SRC_TRACE_PROCESSOR_TP_METATRACE_H_
225