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