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 "perfetto/ext/base/optional.h"
20 #include "src/trace_processor/importers/common/event_tracker.h"
21 #include "src/trace_processor/importers/common/process_tracker.h"
22 #include "src/trace_processor/importers/common/slice_tracker.h"
23 #include "src/trace_processor/importers/common/track_tracker.h"
24
25 namespace perfetto {
26 namespace trace_processor {
27
SystraceParser(TraceProcessorContext * ctx)28 SystraceParser::SystraceParser(TraceProcessorContext* ctx)
29 : context_(ctx),
30 lmk_id_(ctx->storage->InternString("mem.lmk")),
31 screen_state_id_(ctx->storage->InternString("ScreenState")) {}
32
33 SystraceParser::~SystraceParser() = default;
34
ParsePrintEvent(int64_t ts,uint32_t pid,base::StringView event)35 void SystraceParser::ParsePrintEvent(int64_t ts,
36 uint32_t pid,
37 base::StringView event) {
38 systrace_utils::SystraceTracePoint point{};
39 switch (ParseSystraceTracePoint(event, &point)) {
40 case systrace_utils::SystraceParseResult::kSuccess:
41 ParseSystracePoint(ts, pid, point);
42 break;
43 case systrace_utils::SystraceParseResult::kFailure:
44 context_->storage->IncrementStats(stats::systrace_parse_failure);
45 break;
46 case systrace_utils::SystraceParseResult::kUnsupported:
47 // Silently ignore unsupported results.
48 break;
49 }
50 }
51
ParseZeroEvent(int64_t ts,uint32_t pid,int32_t flag,base::StringView name,uint32_t tgid,int64_t value)52 void SystraceParser::ParseZeroEvent(int64_t ts,
53 uint32_t pid,
54 int32_t flag,
55 base::StringView name,
56 uint32_t tgid,
57 int64_t value) {
58 systrace_utils::SystraceTracePoint point{};
59 point.name = name;
60 point.tgid = tgid;
61 point.value = value;
62
63 // The value of these constants can be found in the msm-google kernel.
64 constexpr int32_t kSystraceEventBegin = 1 << 0;
65 constexpr int32_t kSystraceEventEnd = 1 << 1;
66 constexpr int32_t kSystraceEventInt64 = 1 << 2;
67
68 if ((flag & kSystraceEventBegin) != 0) {
69 point.phase = 'B';
70 } else if ((flag & kSystraceEventEnd) != 0) {
71 point.phase = 'E';
72 } else if ((flag & kSystraceEventInt64) != 0) {
73 point.phase = 'C';
74 } else {
75 context_->storage->IncrementStats(stats::systrace_parse_failure);
76 return;
77 }
78 ParseSystracePoint(ts, pid, point);
79 }
80
ParseSdeTracingMarkWrite(int64_t ts,uint32_t pid,char trace_type,bool trace_begin,base::StringView trace_name,uint32_t,int64_t value)81 void SystraceParser::ParseSdeTracingMarkWrite(int64_t ts,
82 uint32_t pid,
83 char trace_type,
84 bool trace_begin,
85 base::StringView trace_name,
86 uint32_t /* tgid */,
87 int64_t value) {
88 systrace_utils::SystraceTracePoint point{};
89 point.name = trace_name;
90
91 // Hardcode the tgid to 0 (i.e. no tgid available) because
92 // sde_tracing_mark_write events can come from kernel threads and because we
93 // group kernel threads into the kthreadd process, we would want |point.tgid
94 // == kKthreaddPid|. However, we don't have acces to the ppid of this process
95 // so we have to not associate to any process and leave the resolution of
96 // process to other events.
97 // TODO(lalitm): remove this hack once we move kernel thread grouping to
98 // the UI.
99 point.tgid = 0;
100
101 point.value = value;
102 // Some versions of this trace point fill trace_type with one of (B/E/C),
103 // others use the trace_begin boolean and only support begin/end events:
104 if (trace_type == 0) {
105 point.phase = trace_begin ? 'B' : 'E';
106 } else if (trace_type == 'B' || trace_type == 'E' || trace_type == 'C') {
107 point.phase = trace_type;
108 } else {
109 context_->storage->IncrementStats(stats::systrace_parse_failure);
110 return;
111 }
112
113 ParseSystracePoint(ts, pid, point);
114 }
115
ParseSystracePoint(int64_t ts,uint32_t pid,systrace_utils::SystraceTracePoint point)116 void SystraceParser::ParseSystracePoint(
117 int64_t ts,
118 uint32_t pid,
119 systrace_utils::SystraceTracePoint point) {
120 switch (point.phase) {
121 case 'B': {
122 StringId name_id = context_->storage->InternString(point.name);
123 UniqueTid utid;
124 if (point.tgid == 0) {
125 utid = context_->process_tracker->GetOrCreateThread(pid);
126 } else {
127 utid = context_->process_tracker->UpdateThread(pid, point.tgid);
128 }
129 TrackId track_id = context_->track_tracker->InternThreadTrack(utid);
130 context_->slice_tracker->Begin(ts, track_id, kNullStringId /* cat */,
131 name_id);
132 break;
133 }
134
135 case 'E': {
136 // |point.tgid| can be 0 in older android versions where the end event
137 // would not contain the value.
138 UniqueTid utid;
139 if (point.tgid == 0) {
140 // If we haven't seen this thread before, there can't have been a Begin
141 // event for it so just ignore the event.
142 auto opt_utid = context_->process_tracker->GetThreadOrNull(pid);
143 if (!opt_utid)
144 break;
145 utid = *opt_utid;
146 } else {
147 utid = context_->process_tracker->UpdateThread(pid, point.tgid);
148 }
149 TrackId track_id = context_->track_tracker->InternThreadTrack(utid);
150 context_->slice_tracker->End(ts, track_id);
151 break;
152 }
153
154 case 'S':
155 case 'F': {
156 StringId name_id = context_->storage->InternString(point.name);
157 int64_t cookie = static_cast<int64_t>(point.value);
158 UniquePid upid =
159 context_->process_tracker->GetOrCreateProcess(point.tgid);
160
161 TrackId track_id = context_->track_tracker->InternAndroidAsyncTrack(
162 name_id, upid, cookie);
163 if (point.phase == 'S') {
164 context_->slice_tracker->Begin(ts, track_id, kNullStringId, name_id);
165 } else {
166 context_->slice_tracker->End(ts, track_id);
167 }
168 break;
169 }
170
171 case 'C': {
172 // LMK events from userspace are hacked as counter events with the "value"
173 // of the counter representing the pid of the killed process which is
174 // reset to 0 once the kill is complete.
175 // Homogenise this with kernel LMK events as an instant event, ignoring
176 // the resets to 0.
177 if (point.name == "kill_one_process") {
178 auto killed_pid = static_cast<uint32_t>(point.value);
179 if (killed_pid != 0) {
180 UniquePid killed_upid =
181 context_->process_tracker->GetOrCreateProcess(killed_pid);
182 context_->event_tracker->PushInstant(ts, lmk_id_, killed_upid,
183 RefType::kRefUpid);
184 }
185 // TODO(lalitm): we should not add LMK events to the counters table
186 // once the UI has support for displaying instants.
187 } else if (point.name == "ScreenState") {
188 // Promote ScreenState to its own top level counter.
189 TrackId track =
190 context_->track_tracker->InternGlobalCounterTrack(screen_state_id_);
191 context_->event_tracker->PushCounter(ts, point.value, track);
192 return;
193 }
194 // This is per upid on purpose. Some counters are pushed from arbitrary
195 // threads but are really per process.
196 UniquePid upid =
197 context_->process_tracker->GetOrCreateProcess(point.tgid);
198 StringId name_id = context_->storage->InternString(point.name);
199 TrackId track =
200 context_->track_tracker->InternProcessCounterTrack(name_id, upid);
201 context_->event_tracker->PushCounter(ts, point.value, track);
202 }
203 }
204 }
205
206 } // namespace trace_processor
207 } // namespace perfetto
208