xref: /aosp_15_r20/external/perfetto/src/trace_processor/importers/systrace/systrace_parser.h (revision 6dbdd20afdafa5e3ca9b8809fa73465d530080dc)
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