• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2018 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 #include "src/trace_processor/importers/systrace/systrace_parser.h"
18 
19 #include <optional>
20 
21 #include "perfetto/ext/base/string_utils.h"
22 #include "src/trace_processor/importers/common/async_track_set_tracker.h"
23 #include "src/trace_processor/importers/common/event_tracker.h"
24 #include "src/trace_processor/importers/common/process_tracker.h"
25 #include "src/trace_processor/importers/common/slice_tracker.h"
26 #include "src/trace_processor/importers/common/track_tracker.h"
27 #include "src/trace_processor/storage/trace_storage.h"
28 
29 namespace perfetto {
30 namespace trace_processor {
31 
SystraceParser(TraceProcessorContext * ctx)32 SystraceParser::SystraceParser(TraceProcessorContext* ctx)
33     : context_(ctx),
34       lmk_id_(ctx->storage->InternString("mem.lmk")),
35       oom_score_adj_id_(ctx->storage->InternString("oom_score_adj")),
36       screen_state_id_(ctx->storage->InternString("ScreenState")),
37       cookie_id_(ctx->storage->InternString("cookie")) {}
38 
39 SystraceParser::~SystraceParser() = default;
40 
ParsePrintEvent(int64_t ts,uint32_t pid,base::StringView event)41 void SystraceParser::ParsePrintEvent(int64_t ts,
42                                      uint32_t pid,
43                                      base::StringView event) {
44   systrace_utils::SystraceTracePoint point{};
45   switch (ParseSystraceTracePoint(event, &point)) {
46     case systrace_utils::SystraceParseResult::kSuccess:
47       ParseSystracePoint(ts, pid, point);
48       break;
49     case systrace_utils::SystraceParseResult::kFailure:
50       context_->storage->IncrementStats(stats::systrace_parse_failure);
51       break;
52     case systrace_utils::SystraceParseResult::kUnsupported:
53       // Silently ignore unsupported results.
54       break;
55   }
56 }
57 
ParseZeroEvent(int64_t ts,uint32_t pid,int32_t flag,base::StringView name,uint32_t tgid,int64_t value)58 void SystraceParser::ParseZeroEvent(int64_t ts,
59                                     uint32_t pid,
60                                     int32_t flag,
61                                     base::StringView name,
62                                     uint32_t tgid,
63                                     int64_t value) {
64   systrace_utils::SystraceTracePoint point{};
65   point.name = name;
66   point.int_value = value;
67   point.tgid = tgid;
68 
69   // The value of these constants can be found in the msm-google kernel.
70   constexpr int32_t kSystraceEventBegin = 1 << 0;
71   constexpr int32_t kSystraceEventEnd = 1 << 1;
72   constexpr int32_t kSystraceEventInt64 = 1 << 2;
73 
74   if ((flag & kSystraceEventBegin) != 0) {
75     point.phase = 'B';
76   } else if ((flag & kSystraceEventEnd) != 0) {
77     point.phase = 'E';
78   } else if ((flag & kSystraceEventInt64) != 0) {
79     point.phase = 'C';
80   } else {
81     context_->storage->IncrementStats(stats::systrace_parse_failure);
82     return;
83   }
84   // Note: for counter (C) events, we cannot assume that pid is within tgid.
85   // See ParseKernelTracingMarkWrite for rationale.
86   ParseSystracePoint(ts, pid, point);
87 }
88 
ParseKernelTracingMarkWrite(int64_t ts,uint32_t pid,char trace_type,bool trace_begin,base::StringView trace_name,uint32_t tgid,int64_t value)89 void SystraceParser::ParseKernelTracingMarkWrite(int64_t ts,
90                                                  uint32_t pid,
91                                                  char trace_type,
92                                                  bool trace_begin,
93                                                  base::StringView trace_name,
94                                                  uint32_t tgid,
95                                                  int64_t value) {
96   systrace_utils::SystraceTracePoint point{};
97   point.name = trace_name;
98   point.int_value = value;
99   point.tgid = tgid;
100 
101   // Some versions of this trace point fill trace_type with one of (B/E/C/I),
102   // others use the trace_begin boolean and only support begin/end events:
103   if (trace_type == 0) {
104     point.phase = trace_begin ? 'B' : 'E';
105   } else if (trace_type == 'B' || trace_type == 'E' || trace_type == 'C' ||
106              trace_type == 'I') {
107     point.phase = trace_type;
108   } else {
109     context_->storage->IncrementStats(stats::systrace_parse_failure);
110     return;
111   }
112 
113   // Note: |pid| is the thread id of the emitting thread, |tgid| is taken from
114   // the event payload. The begin/end event kernel atrace macros seem well
115   // behaved (i.e. they always put current->tgid into the payload). However the
116   // counter events have cases where a placeholder/unrelated pid is used (e.g.
117   // 0, 1, or a specific kthread, see g2d_frame_* counters for an example).
118   //
119   // Further, the counter events expect to be grouped at the process (tgid)
120   // level (multiple distinct pids will be emitting values for the same
121   // logical counter).
122   //
123   // Therefore we must never assume that pid is within tgid for counter events,
124   // but still trust that the tgid value is for a valid process (which will
125   // usually fall onto swapper/init or some kthread) to have a
126   // process_counter_track for the counter values.
127   ParseSystracePoint(ts, pid, point);
128 }
129 
130 // TODO(rsavitski): try to remove most special casing of tgid 0, as it is valid
131 // for kernel systrace points (due to systrace from interrupts).
ParseSystracePoint(int64_t ts,uint32_t pid,systrace_utils::SystraceTracePoint point)132 void SystraceParser::ParseSystracePoint(
133     int64_t ts,
134     uint32_t pid,
135     systrace_utils::SystraceTracePoint point) {
136   auto get_utid = [pid, &point, this]() {
137     if (point.tgid == 0)
138       return context_->process_tracker->GetOrCreateThread(pid);
139     return context_->process_tracker->UpdateThread(pid, point.tgid);
140   };
141 
142   switch (point.phase) {
143     case 'B': {
144       StringId name_id = context_->storage->InternString(point.name);
145       UniqueTid utid = get_utid();
146       TrackId track_id = context_->track_tracker->InternThreadTrack(utid);
147       context_->slice_tracker->Begin(ts, track_id, kNullStringId /* cat */,
148                                      name_id);
149       PostProcessSpecialSliceBegin(ts, point.name);
150       break;
151     }
152 
153     case 'E': {
154       // |point.tgid| can be 0 in older android versions where the end event
155       // would not contain the value.
156       UniqueTid utid;
157       if (point.tgid == 0) {
158         // If we haven't seen this thread before, there can't have been a Begin
159         // event for it so just ignore the event.
160         auto opt_utid = context_->process_tracker->GetThreadOrNull(pid);
161         if (!opt_utid)
162           break;
163         utid = *opt_utid;
164       } else {
165         utid = context_->process_tracker->UpdateThread(pid, point.tgid);
166       }
167       TrackId track_id = context_->track_tracker->InternThreadTrack(utid);
168       context_->slice_tracker->End(ts, track_id);
169       break;
170     }
171 
172     case 'S':
173     case 'F': {
174       StringId name_id = context_->storage->InternString(point.name);
175       int64_t cookie = point.int_value;
176       UniquePid upid =
177           context_->process_tracker->GetOrCreateProcess(point.tgid);
178 
179       auto track_set_id =
180           context_->async_track_set_tracker
181               ->InternAndroidLegacyUnnestableTrackSet(upid, name_id);
182 
183       if (point.phase == 'S') {
184         // Historically, async slices on Android did not support nesting async
185         // slices (i.e. you could not have a stack of async slices). If clients
186         // were implemented correctly, we would simply be able to use the normal
187         // Begin method and we could rely on the traced code to never emit two
188         // 'S' events back to back on the same track.
189         // However, there exists buggy code in Android (in Wakelock class of
190         // PowerManager) which emits an arbitrary number of 'S' events and
191         // expects only the first one to be tracked. Moreover, this issue is
192         // compounded by an unfortunate implementation of async slices in
193         // Catapult (the legacy trace viewer) which simply tracks the details of
194         // the *most recent* emitted 'S' event which leads even more inaccurate
195         // behaviour. To support these quirks, we have the special 'unnestable'
196         // slice concept which implements workarounds for these very specific
197         // issues. No other code should ever use |BeginLegacyUnnestable|.
198         tables::SliceTable::Row row;
199         row.ts = ts;
200         row.track_id =
201             context_->async_track_set_tracker->Begin(track_set_id, cookie);
202         row.name = name_id;
203         context_->slice_tracker->BeginLegacyUnnestable(
204             row, [this, cookie](ArgsTracker::BoundInserter* inserter) {
205               inserter->AddArg(cookie_id_, Variadic::Integer(cookie));
206             });
207       } else {
208         TrackId track_id =
209             context_->async_track_set_tracker->End(track_set_id, cookie);
210         context_->slice_tracker->End(ts, track_id);
211       }
212       break;
213     }
214 
215     case 'I': {
216       StringId name_id = context_->storage->InternString(point.name);
217       UniqueTid utid = get_utid();
218       TrackId track_id = context_->track_tracker->InternThreadTrack(utid);
219       context_->slice_tracker->Scoped(ts, track_id, kNullStringId, name_id, 0);
220       break;
221     }
222 
223     case 'N':
224     case 'G':
225     case 'H': {
226       StringId name_id = context_->storage->InternString(point.name);
227       StringId track_name_id = context_->storage->InternString(point.str_value);
228       UniquePid upid =
229           context_->process_tracker->GetOrCreateProcess(point.tgid);
230 
231       // Promote DeviceStateChanged to its own top level track.
232       AsyncTrackSetTracker::TrackSetId track_set_id;
233       if (point.str_value == "DeviceStateChanged") {
234         track_set_id = context_->async_track_set_tracker->InternGlobalTrackSet(
235             track_name_id);
236       } else {
237         track_set_id = context_->async_track_set_tracker->InternProcessTrackSet(
238             upid, track_name_id);
239       }
240 
241       if (point.phase == 'N') {
242         TrackId track_id =
243             context_->async_track_set_tracker->Scoped(track_set_id, ts, 0);
244         context_->slice_tracker->Scoped(ts, track_id, kNullStringId, name_id,
245                                         0);
246       } else if (point.phase == 'G') {
247         TrackId track_id = context_->async_track_set_tracker->Begin(
248             track_set_id, point.int_value);
249         context_->slice_tracker->Begin(ts, track_id, kNullStringId, name_id);
250       } else if (point.phase == 'H') {
251         TrackId track_id = context_->async_track_set_tracker->End(
252             track_set_id, point.int_value);
253         context_->slice_tracker->End(ts, track_id);
254       }
255       break;
256     }
257 
258     // Warning: counter event handling must never assume that the |pid| thread
259     // is within the |tgid| process due to kernel systrace quirks. If you need
260     // to change this, update ParseKernelTracingMarkWrite and ParseZeroEvent to
261     // pretend that pid is the same as tgid for C events.
262     case 'C': {
263       // LMK events from userspace are hacked as counter events with the "value"
264       // of the counter representing the pid of the killed process which is
265       // reset to 0 once the kill is complete.
266       // Homogenise this with kernel LMK events as an instant event, ignoring
267       // the resets to 0.
268       if (point.name == "kill_one_process") {
269         auto killed_pid = static_cast<uint32_t>(point.int_value);
270         if (killed_pid != 0) {
271           UniquePid killed_upid =
272               context_->process_tracker->GetOrCreateProcess(killed_pid);
273           TrackId track =
274               context_->track_tracker->InternProcessTrack(killed_upid);
275           context_->slice_tracker->Scoped(ts, track, kNullStringId, lmk_id_, 0);
276         }
277         // TODO(lalitm): we should not add LMK events to the counters table
278         // once the UI has support for displaying instants.
279       } else if (point.name == "ScreenState") {
280         // Promote ScreenState to its own top level counter.
281         TrackId track = context_->track_tracker->InternGlobalCounterTrack(
282             TrackTracker::Group::kDeviceState, screen_state_id_);
283         context_->event_tracker->PushCounter(
284             ts, static_cast<double>(point.int_value), track);
285         return;
286       } else if (point.name.StartsWith("battery_stats.")) {
287         // Promote battery_stats conters to global tracks.
288         StringId name_id = context_->storage->InternString(point.name);
289         TrackId track = context_->track_tracker->InternGlobalCounterTrack(
290             TrackTracker::Group::kPower, name_id);
291         context_->event_tracker->PushCounter(
292             ts, static_cast<double>(point.int_value), track);
293         return;
294       }
295 
296       // This is per upid on purpose. Some long-standing counters are pushed
297       // from arbitrary threads but expect to be per process (b/123560328).
298       // This affects both userspace and kernel counters.
299       StringId name_id = context_->storage->InternString(point.name);
300       UniquePid upid =
301           context_->process_tracker->GetOrCreateProcess(point.tgid);
302       TrackId track_id =
303           context_->track_tracker->InternProcessCounterTrack(name_id, upid);
304       context_->event_tracker->PushCounter(
305           ts, static_cast<double>(point.int_value), track_id);
306     }
307   }
308 }
309 
PostProcessSpecialSliceBegin(int64_t ts,base::StringView name)310 void SystraceParser::PostProcessSpecialSliceBegin(int64_t ts,
311                                                   base::StringView name) {
312   if (name.StartsWith("lmk,")) {
313     // LMK events introduced with http://aosp/1782391 are treated specially
314     // to parse the killed process oom_score_adj out of them.
315     // Format is 'lmk,pid,reason,oom adj,...'
316     std::vector<std::string> toks = base::SplitString(name.ToStdString(), ",");
317     if (toks.size() < 4) {
318       return;
319     }
320     auto killed_pid = base::StringToUInt32(toks[1]);
321     auto oom_score_adj = base::StringToInt32(toks[3]);
322     if (!killed_pid || !oom_score_adj) {
323       return;
324     }
325 
326     UniquePid killed_upid =
327         context_->process_tracker->GetOrCreateProcess(*killed_pid);
328     // Add the oom score entry
329     TrackId counter_track = context_->track_tracker->InternProcessCounterTrack(
330         oom_score_adj_id_, killed_upid);
331     context_->event_tracker->PushCounter(ts, *oom_score_adj, counter_track);
332 
333     // Add mem.lmk instant event for consistency with other methods.
334     TrackId track = context_->track_tracker->InternProcessTrack(killed_upid);
335     context_->slice_tracker->Scoped(ts, track, kNullStringId, lmk_id_, 0);
336   }
337 }
338 
339 }  // namespace trace_processor
340 }  // namespace perfetto
341