1 /*
2 * Copyright (C) 2020 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_line_parser.h"
18
19 #include "perfetto/ext/base/flat_hash_map.h"
20 #include "perfetto/ext/base/string_splitter.h"
21 #include "perfetto/ext/base/string_utils.h"
22 #include "src/trace_processor/importers/common/args_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/importers/ftrace/binder_tracker.h"
28 #include "src/trace_processor/importers/ftrace/sched_event_tracker.h"
29 #include "src/trace_processor/importers/systrace/systrace_parser.h"
30 #include "src/trace_processor/types/task_state.h"
31
32 #include <cctype>
33 #include <cinttypes>
34 #include <string>
35
36 namespace perfetto {
37 namespace trace_processor {
38
SystraceLineParser(TraceProcessorContext * ctx)39 SystraceLineParser::SystraceLineParser(TraceProcessorContext* ctx)
40 : context_(ctx),
41 rss_stat_tracker_(context_),
42 sched_wakeup_name_id_(ctx->storage->InternString("sched_wakeup")),
43 sched_waking_name_id_(ctx->storage->InternString("sched_waking")),
44 cpufreq_name_id_(ctx->storage->InternString("cpufreq")),
45 cpuidle_name_id_(ctx->storage->InternString("cpuidle")),
46 workqueue_name_id_(ctx->storage->InternString("workqueue")),
47 sched_blocked_reason_id_(
48 ctx->storage->InternString("sched_blocked_reason")),
49 io_wait_id_(ctx->storage->InternString("io_wait")),
50 waker_utid_id_(ctx->storage->InternString("waker_utid")) {}
51
ParseLine(const SystraceLine & line)52 util::Status SystraceLineParser::ParseLine(const SystraceLine& line) {
53 auto utid = context_->process_tracker->UpdateThreadName(
54 line.pid, context_->storage->InternString(base::StringView(line.task)),
55 ThreadNamePriority::kFtrace);
56
57 if (!line.tgid_str.empty() && line.tgid_str != "-----") {
58 base::Optional<uint32_t> tgid = base::StringToUInt32(line.tgid_str);
59 if (tgid) {
60 context_->process_tracker->UpdateThread(line.pid, tgid.value());
61 }
62 }
63
64 base::FlatHashMap<std::string, std::string> args;
65 for (base::StringSplitter ss(line.args_str, ' '); ss.Next();) {
66 std::string key;
67 std::string value;
68 if (!base::Contains(ss.cur_token(), "=")) {
69 key = "name";
70 value = ss.cur_token();
71 args.Insert(std::move(key), std::move(value));
72 continue;
73 }
74 for (base::StringSplitter inner(ss.cur_token(), '='); inner.Next();) {
75 if (key.empty()) {
76 key = inner.cur_token();
77 } else {
78 value = inner.cur_token();
79 }
80 }
81 args.Insert(std::move(key), std::move(value));
82 }
83 if (line.event_name == "sched_switch") {
84 auto prev_state_str = args["prev_state"];
85 int64_t prev_state =
86 ftrace_utils::TaskState(prev_state_str.c_str()).raw_state();
87
88 auto prev_pid = base::StringToUInt32(args["prev_pid"]);
89 auto prev_comm = base::StringView(args["prev_comm"]);
90 auto prev_prio = base::StringToInt32(args["prev_prio"]);
91 auto next_pid = base::StringToUInt32(args["next_pid"]);
92 auto next_comm = base::StringView(args["next_comm"]);
93 auto next_prio = base::StringToInt32(args["next_prio"]);
94
95 if (!(prev_pid.has_value() && prev_prio.has_value() &&
96 next_pid.has_value() && next_prio.has_value())) {
97 return util::Status("Could not parse sched_switch");
98 }
99
100 SchedEventTracker::GetOrCreate(context_)->PushSchedSwitch(
101 line.cpu, line.ts, prev_pid.value(), prev_comm, prev_prio.value(),
102 prev_state, next_pid.value(), next_comm, next_prio.value());
103 } else if (line.event_name == "tracing_mark_write" ||
104 line.event_name == "0" || line.event_name == "print") {
105 SystraceParser::GetOrCreate(context_)->ParsePrintEvent(
106 line.ts, line.pid, line.args_str.c_str());
107 } else if (line.event_name == "sched_wakeup" ||
108 line.event_name == "sched_waking") {
109 auto comm = args["comm"];
110 base::Optional<uint32_t> wakee_pid = base::StringToUInt32(args["pid"]);
111 if (!wakee_pid.has_value()) {
112 return util::Status("Could not convert wakee_pid");
113 }
114
115 StringId name_id = context_->storage->InternString(base::StringView(comm));
116 auto wakee_utid = context_->process_tracker->UpdateThreadName(
117 wakee_pid.value(), name_id, ThreadNamePriority::kFtrace);
118
119 StringId event_name_id = line.event_name == "sched_wakeup"
120 ? sched_wakeup_name_id_
121 : sched_waking_name_id_;
122 InstantId instant_id = context_->event_tracker->PushInstant(
123 line.ts, event_name_id, wakee_utid, RefType::kRefUtid);
124 context_->args_tracker->AddArgsTo(instant_id)
125 .AddArg(waker_utid_id_, Variadic::UnsignedInteger(utid));
126
127 } else if (line.event_name == "cpu_frequency") {
128 base::Optional<uint32_t> event_cpu = base::StringToUInt32(args["cpu_id"]);
129 base::Optional<double> new_state = base::StringToDouble(args["state"]);
130 if (!event_cpu.has_value()) {
131 return util::Status("Could not convert event cpu");
132 }
133 if (!event_cpu.has_value()) {
134 return util::Status("Could not convert state");
135 }
136
137 TrackId track = context_->track_tracker->InternCpuCounterTrack(
138 cpufreq_name_id_, event_cpu.value());
139 context_->event_tracker->PushCounter(line.ts, new_state.value(), track);
140 } else if (line.event_name == "cpu_idle") {
141 base::Optional<uint32_t> event_cpu = base::StringToUInt32(args["cpu_id"]);
142 base::Optional<double> new_state = base::StringToDouble(args["state"]);
143 if (!event_cpu.has_value()) {
144 return util::Status("Could not convert event cpu");
145 }
146 if (!event_cpu.has_value()) {
147 return util::Status("Could not convert state");
148 }
149
150 TrackId track = context_->track_tracker->InternCpuCounterTrack(
151 cpuidle_name_id_, event_cpu.value());
152 context_->event_tracker->PushCounter(line.ts, new_state.value(), track);
153 } else if (line.event_name == "binder_transaction") {
154 auto id = base::StringToInt32(args["transaction"]);
155 auto dest_node = base::StringToInt32(args["dest_node"]);
156 auto dest_tgid = base::StringToUInt32(args["dest_proc"]);
157 auto dest_tid = base::StringToUInt32(args["dest_thread"]);
158 auto is_reply = base::StringToInt32(args["reply"]).value() == 1;
159 auto flags_str = args["flags"];
160 char* end;
161 uint32_t flags = static_cast<uint32_t>(strtol(flags_str.c_str(), &end, 16));
162 std::string code_str = args["code"] + " Java Layer Dependent";
163 StringId code = context_->storage->InternString(base::StringView(code_str));
164 if (!dest_tgid.has_value()) {
165 return util::Status("Could not convert dest_tgid");
166 }
167 if (!dest_tid.has_value()) {
168 return util::Status("Could not convert dest_tid");
169 }
170 if (!id.has_value()) {
171 return util::Status("Could not convert transaction id");
172 }
173 if (!dest_node.has_value()) {
174 return util::Status("Could not covert dest node");
175 }
176 BinderTracker::GetOrCreate(context_)->Transaction(
177 line.ts, line.pid, id.value(), dest_node.value(), dest_tgid.value(),
178 dest_tid.value(), is_reply, flags, code);
179 } else if (line.event_name == "binder_transaction_received") {
180 auto id = base::StringToInt32(args["transaction"]);
181 if (!id.has_value()) {
182 return util::Status("Could not convert transaction id");
183 }
184 BinderTracker::GetOrCreate(context_)->TransactionReceived(line.ts, line.pid,
185 id.value());
186 } else if (line.event_name == "binder_lock") {
187 BinderTracker::GetOrCreate(context_)->Lock(line.ts, line.pid);
188 } else if (line.event_name == "binder_locked") {
189 BinderTracker::GetOrCreate(context_)->Locked(line.ts, line.pid);
190 } else if (line.event_name == "binder_unlock") {
191 BinderTracker::GetOrCreate(context_)->Unlock(line.ts, line.pid);
192 } else if (line.event_name == "binder_transaction_alloc_buf") {
193 auto data_size = base::StringToUInt64(args["data_size"]);
194 auto offsets_size = base::StringToUInt64(args["offsets_size"]);
195 if (!data_size.has_value()) {
196 return util::Status("Could not convert data size");
197 }
198 if (!offsets_size.has_value()) {
199 return util::Status("Could not convert offsets size");
200 }
201 BinderTracker::GetOrCreate(context_)->TransactionAllocBuf(
202 line.ts, line.pid, data_size.value(), offsets_size.value());
203 } else if (line.event_name == "clock_set_rate" ||
204 line.event_name == "clock_enable" ||
205 line.event_name == "clock_disable") {
206 std::string subtitle =
207 line.event_name == "clock_set_rate" ? " Frequency" : " State";
208 auto rate = base::StringToUInt32(args["state"]);
209 if (!rate.has_value()) {
210 return util::Status("Could not convert state");
211 }
212 std::string clock_name_str = args["name"] + subtitle;
213 StringId clock_name =
214 context_->storage->InternString(base::StringView(clock_name_str));
215 TrackId track =
216 context_->track_tracker->InternGlobalCounterTrack(clock_name);
217 context_->event_tracker->PushCounter(line.ts, rate.value(), track);
218 } else if (line.event_name == "workqueue_execute_start") {
219 auto split = base::SplitString(line.args_str, "function ");
220 StringId name_id =
221 context_->storage->InternString(base::StringView(split[1]));
222 TrackId track = context_->track_tracker->InternThreadTrack(utid);
223 context_->slice_tracker->Begin(line.ts, track, workqueue_name_id_, name_id);
224 } else if (line.event_name == "workqueue_execute_end") {
225 TrackId track = context_->track_tracker->InternThreadTrack(utid);
226 context_->slice_tracker->End(line.ts, track, workqueue_name_id_);
227 } else if (line.event_name == "thermal_temperature") {
228 std::string thermal_zone = args["thermal_zone"] + " Temperature";
229 StringId track_name =
230 context_->storage->InternString(base::StringView(thermal_zone));
231 TrackId track =
232 context_->track_tracker->InternGlobalCounterTrack(track_name);
233 auto temp = base::StringToInt32(args["temp"]);
234 if (!temp.has_value()) {
235 return util::Status("Could not convert temp");
236 }
237 context_->event_tracker->PushCounter(line.ts, temp.value(), track);
238 } else if (line.event_name == "cdev_update") {
239 std::string type = args["type"] + " Cooling Device";
240 StringId track_name =
241 context_->storage->InternString(base::StringView(type));
242 TrackId track =
243 context_->track_tracker->InternGlobalCounterTrack(track_name);
244 auto target = base::StringToDouble(args["target"]);
245 if (!target.has_value()) {
246 return util::Status("Could not convert target");
247 }
248 context_->event_tracker->PushCounter(line.ts, target.value(), track);
249 } else if (line.event_name == "sched_blocked_reason") {
250 auto wakee_pid = base::StringToUInt32(args["pid"]);
251 if (!wakee_pid.has_value()) {
252 return util::Status("sched_blocked_reason: could not parse wakee_pid");
253 }
254 auto wakee_utid = context_->process_tracker->GetOrCreateThread(*wakee_pid);
255
256 InstantId id = context_->event_tracker->PushInstant(
257 line.ts, sched_blocked_reason_id_, wakee_utid, RefType::kRefUtid,
258 false);
259
260 auto inserter = context_->args_tracker->AddArgsTo(id);
261 auto io_wait = base::StringToInt32(args["iowait"]);
262 if (!io_wait.has_value()) {
263 return util::Status("sched_blocked_reason: could not parse io_wait");
264 }
265 inserter.AddArg(io_wait_id_, Variadic::Boolean(*io_wait));
266 context_->args_tracker->Flush();
267 } else if (line.event_name == "rss_stat") {
268 // Format: rss_stat: size=8437760 member=1 curr=1 mm_id=2824390453
269 auto size = base::StringToInt64(args["size"]);
270 auto member = base::StringToUInt32(args["member"]);
271 auto mm_id = base::StringToInt64(args["mm_id"]);
272 auto opt_curr = base::StringToUInt32(args["curr"]);
273 if (!size.has_value()) {
274 return util::Status("rss_stat: could not parse size");
275 }
276 if (!member.has_value()) {
277 return util::Status("rss_stat: could not parse member");
278 }
279 base::Optional<bool> curr;
280 if (!opt_curr.has_value()) {
281 curr = base::make_optional(static_cast<bool>(*opt_curr));
282 }
283 rss_stat_tracker_.ParseRssStat(line.ts, line.pid, *size, *member, curr,
284 mm_id);
285 }
286
287 return util::OkStatus();
288 }
289
290 } // namespace trace_processor
291 } // namespace perfetto
292