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