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