• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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