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