1 /*
2 * Copyright (C) 2019 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_IMPORTERS_SYSTRACE_SYSTRACE_PARSER_H_
18 #define SRC_TRACE_PROCESSOR_IMPORTERS_SYSTRACE_SYSTRACE_PARSER_H_
19
20 #include <cstddef>
21 #include <cstdint>
22 #include <ostream>
23 #include <tuple>
24
25 #include "src/trace_processor/types/destructible.h"
26 #include "src/trace_processor/types/trace_processor_context.h"
27
28 #include "perfetto/base/logging.h"
29 #include "perfetto/ext/base/string_utils.h"
30 #include "perfetto/ext/base/string_view.h"
31 #include "src/trace_processor/storage/trace_storage.h"
32
33 namespace perfetto {
34 namespace trace_processor {
35
36 namespace systrace_utils {
37
38 // Visible for unittesting.
39 enum class SystraceParseResult { kFailure = 0, kUnsupported, kSuccess };
40
41 // Visible for unittesting.
42 struct SystraceTracePoint {
SystraceTracePointSystraceTracePoint43 SystraceTracePoint() {}
44
BSystraceTracePoint45 static SystraceTracePoint B(uint32_t tgid, base::StringView name) {
46 return SystraceTracePoint('B', tgid, std::move(name), 0, {});
47 }
48
ESystraceTracePoint49 static SystraceTracePoint E(uint32_t tgid) {
50 return SystraceTracePoint('E', tgid, {}, 0, {});
51 }
52
CSystraceTracePoint53 static SystraceTracePoint C(uint32_t tgid,
54 base::StringView name,
55 int64_t value) {
56 return SystraceTracePoint('C', tgid, std::move(name), value, {});
57 }
58
SSystraceTracePoint59 static SystraceTracePoint S(uint32_t tgid,
60 base::StringView name,
61 int64_t cookie) {
62 return SystraceTracePoint('S', tgid, std::move(name), cookie, {});
63 }
64
FSystraceTracePoint65 static SystraceTracePoint F(uint32_t tgid,
66 base::StringView name,
67 int64_t cookie) {
68 return SystraceTracePoint('F', tgid, std::move(name), cookie, {});
69 }
70
ISystraceTracePoint71 static SystraceTracePoint I(uint32_t tgid, base::StringView name) {
72 return SystraceTracePoint('I', tgid, std::move(name), 0, {});
73 }
74
NSystraceTracePoint75 static SystraceTracePoint N(uint32_t tgid,
76 base::StringView track_name,
77 base::StringView name) {
78 return SystraceTracePoint('N', tgid, std::move(name), 0,
79 std::move(track_name));
80 }
81
GSystraceTracePoint82 static SystraceTracePoint G(uint32_t tgid,
83 base::StringView track_name,
84 base::StringView name,
85 int64_t cookie) {
86 return SystraceTracePoint('G', tgid, std::move(name), cookie,
87 std::move(track_name));
88 }
89
HSystraceTracePoint90 static SystraceTracePoint H(uint32_t tgid,
91 base::StringView track_name,
92 int64_t cookie) {
93 return SystraceTracePoint('H', tgid, {}, cookie, std::move(track_name));
94 }
95
SystraceTracePointSystraceTracePoint96 SystraceTracePoint(char p,
97 uint32_t tg,
98 base::StringView n,
99 int64_t v,
100 base::StringView s)
101 : phase(p), tgid(tg), name(std::move(n)), int_value(v), str_value(s) {}
102
103 // Phase can be one of B, E, C, S, F, I, N, G, H.
104 char phase = '\0';
105
106 uint32_t tgid = 0;
107
108 // For phase = B, C, S, F, N, U, G.
109 base::StringView name;
110
111 // For phase = C (counter value) and B, S, F, N, G, H (async cookie).
112 int64_t int_value = 0;
113
114 // For phase = N, G, H (track name)
115 base::StringView str_value;
116
117 // Visible for unittesting.
118 friend std::ostream& operator<<(std::ostream& os,
119 const SystraceTracePoint& point) {
120 return os << "SystraceTracePoint{'" << point.phase << "', " << point.tgid
121 << ", \"" << point.name.ToStdString() << "\", " << point.int_value
122 << ", \"" << point.str_value.ToStdString() << "\""
123 << "}";
124 }
125 };
126
127 // We have to handle trace_marker events of a few different types:
128 // 1. some random text
129 // 2. B|1636|pokeUserActivity
130 // 3. E|1636
131 // 4. C|1636|wq:monitor|0
132 // 5. S|1636|frame_capture|123
133 // 6. F|1636|frame_capture|456
134 // 7. C|3209|TransfersBytesPendingOnDisk-value|0|Blob
135 // 8. I|4820|instant
136 // 9. N|1938|track_name|instant_name
137 // 10. G|1339|track_name|slice_name|789
138 // 11. H|6890|track_name|slice_name|135
139 // 12. H|6890|track_name|135
140 // Counters emitted by chromium can have a further "category group" appended
141 // ("Blob" in the example below). We ignore the category group.
ParseSystraceTracePoint(base::StringView str_untrimmed,SystraceTracePoint * out)142 inline SystraceParseResult ParseSystraceTracePoint(
143 base::StringView str_untrimmed,
144 SystraceTracePoint* out) {
145 *out = {};
146
147 // Strip trailing \n and \0. StringViews are not null-terminated, but the
148 // writer could have appended a stray \0 depending on where the trace comes
149 // from.
150 size_t len = str_untrimmed.size();
151 for (; len > 0; --len) {
152 char last_char = str_untrimmed.at(len - 1);
153 if (last_char != '\n' && last_char != '\0')
154 break;
155 }
156 base::StringView str = str_untrimmed.substr(0, len);
157
158 size_t off = 0;
159
160 // This function reads the next field up to the next '|', '\0' or end(). It
161 // advances |off| as it goes through fields.
162 auto read_next_field = [&off, &str, len]() {
163 for (size_t field_start = off;; ++off) {
164 char c = off >= len ? '\0' : str.at(off);
165 if (c == '|' || c == '\0') {
166 auto res = str.substr(field_start, off - field_start);
167 ++off; // Eat the separator.
168 return res;
169 }
170 }
171 };
172
173 auto f0_phase = read_next_field();
174 if (PERFETTO_UNLIKELY(f0_phase.empty()))
175 return SystraceParseResult::kFailure;
176 out->phase = f0_phase.at(0);
177
178 auto f1_tgid = read_next_field();
179 auto opt_tgid = base::StringToUInt32(f1_tgid.ToStdString());
180 out->tgid = opt_tgid.value_or(0);
181 const bool has_tgid = opt_tgid.has_value();
182
183 switch (out->phase) {
184 case 'B': { // Begin thread-scoped synchronous slice.
185 if (!has_tgid)
186 return SystraceParseResult::kFailure;
187 auto f2_name = str.substr(off); // It's fine even if |off| >= end().
188 if (f2_name.empty()) {
189 out->name = base::StringView("[empty slice name]");
190 } else {
191 out->name = f2_name;
192 }
193 return SystraceParseResult::kSuccess;
194 }
195 case 'E': // End thread-scoped synchronous slice.
196 // Some non-Android traces (Flutter) use just "E" (aosp/1244409). Allow
197 // empty TGID on end slices. By design they are thread-scoped anyways.
198 return SystraceParseResult::kSuccess;
199 case 'S': // Begin of async slice.
200 case 'F': { // End of async slice.
201 auto f2_name = read_next_field();
202 auto f3_cookie = read_next_field();
203 auto maybe_cookie = base::StringToInt64(f3_cookie.ToStdString());
204 if (PERFETTO_UNLIKELY(!has_tgid || f2_name.empty() || f3_cookie.empty() ||
205 !maybe_cookie)) {
206 return SystraceParseResult::kFailure;
207 }
208 out->name = f2_name;
209 out->int_value = *maybe_cookie;
210 return SystraceParseResult::kSuccess;
211 }
212 case 'I': { // Instant.
213 auto f2_name = read_next_field();
214 if (PERFETTO_UNLIKELY(!has_tgid || f2_name.empty())) {
215 return SystraceParseResult::kFailure;
216 }
217 out->name = f2_name;
218 return SystraceParseResult::kSuccess;
219 }
220 case 'N': { // Instant on track.
221 auto f2_track_name = read_next_field();
222 auto f3_name = read_next_field();
223 if (PERFETTO_UNLIKELY(!has_tgid || f2_track_name.empty() ||
224 f3_name.empty())) {
225 return SystraceParseResult::kFailure;
226 }
227 out->name = f3_name;
228 out->str_value = f2_track_name;
229 return SystraceParseResult::kSuccess;
230 }
231 case 'C': { // Counter.
232 auto f2_name = read_next_field();
233 auto f3_value = read_next_field();
234 auto maybe_value = base::StringToInt64(f3_value.ToStdString());
235 if (PERFETTO_UNLIKELY(!has_tgid || f2_name.empty() || f3_value.empty() ||
236 !maybe_value)) {
237 return SystraceParseResult::kFailure;
238 }
239 out->name = f2_name;
240 out->int_value = *maybe_value;
241 return SystraceParseResult::kSuccess;
242 }
243 case 'G': { // Begin of async slice on track.
244 auto f2_track_name = read_next_field();
245 auto f3_name = read_next_field();
246 auto maybe_cookie = base::StringToInt64(read_next_field().ToStdString());
247 if (PERFETTO_UNLIKELY(!has_tgid || f2_track_name.empty() ||
248 f3_name.empty() || !maybe_cookie)) {
249 return SystraceParseResult::kFailure;
250 }
251 out->name = f3_name;
252 out->str_value = f2_track_name;
253 out->int_value = *maybe_cookie;
254 return SystraceParseResult::kSuccess;
255 }
256 case 'H': { // End of async slice on track.
257 auto f2_track_name = read_next_field();
258 auto f3 = read_next_field();
259 auto f4 = read_next_field();
260 auto maybe_cookie_str = f4.empty() ? f3 : f4;
261 auto maybe_cookie = base::StringToInt64(maybe_cookie_str.ToStdString());
262 if (PERFETTO_UNLIKELY(!has_tgid || f2_track_name.empty() ||
263 !maybe_cookie)) {
264 return SystraceParseResult::kFailure;
265 }
266 out->str_value = f2_track_name;
267 out->int_value = *maybe_cookie;
268 return SystraceParseResult::kSuccess;
269 }
270 default:
271 if (str.find("trace_event_clock_sync:") == 0)
272 return SystraceParseResult::kUnsupported;
273 return SystraceParseResult::kFailure;
274 }
275 }
276
277 // Visible for unittesting.
278 inline bool operator==(const SystraceTracePoint& x,
279 const SystraceTracePoint& y) {
280 return std::tie(x.phase, x.tgid, x.name, x.int_value, x.str_value) ==
281 std::tie(y.phase, y.tgid, y.name, y.int_value, y.str_value);
282 }
283
284 } // namespace systrace_utils
285
286 class SystraceParser : public Destructible {
287 public:
GetOrCreate(TraceProcessorContext * context)288 static SystraceParser* GetOrCreate(TraceProcessorContext* context) {
289 if (!context->systrace_parser) {
290 context->systrace_parser.reset(new SystraceParser(context));
291 }
292 return static_cast<SystraceParser*>(context->systrace_parser.get());
293 }
294 ~SystraceParser() override;
295
296 void ParsePrintEvent(int64_t ts, uint32_t pid, base::StringView event);
297
298 // Parse a kernel event that mimics the systrace format.
299 void ParseKernelTracingMarkWrite(int64_t ts,
300 uint32_t pid,
301 char trace_type,
302 bool trace_begin,
303 base::StringView trace_name,
304 uint32_t tgid,
305 int64_t value);
306
307 // Parse a kernel "systrace/0" event which mimics the systrace format.
308 void ParseZeroEvent(int64_t ts,
309 uint32_t pid,
310 int32_t flag,
311 base::StringView name,
312 uint32_t tgid,
313 int64_t value);
314
315 private:
316 explicit SystraceParser(TraceProcessorContext*);
317 void ParseSystracePoint(int64_t ts,
318 uint32_t pid,
319 systrace_utils::SystraceTracePoint event);
320 void PostProcessSpecialSliceBegin(int64_t ts, base::StringView name);
321
322 TraceProcessorContext* const context_;
323 const StringId lmk_id_;
324 const StringId cookie_id_;
325 };
326
327 } // namespace trace_processor
328 } // namespace perfetto
329
330 #endif // SRC_TRACE_PROCESSOR_IMPORTERS_SYSTRACE_SYSTRACE_PARSER_H_
331