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