1*288bf522SAndroid Build Coastguard Worker /*
2*288bf522SAndroid Build Coastguard Worker * Copyright (C) 2018 The Android Open Source Project
3*288bf522SAndroid Build Coastguard Worker *
4*288bf522SAndroid Build Coastguard Worker * Licensed under the Apache License, Version 2.0 (the "License");
5*288bf522SAndroid Build Coastguard Worker * you may not use this file except in compliance with the License.
6*288bf522SAndroid Build Coastguard Worker * You may obtain a copy of the License at
7*288bf522SAndroid Build Coastguard Worker *
8*288bf522SAndroid Build Coastguard Worker * http://www.apache.org/licenses/LICENSE-2.0
9*288bf522SAndroid Build Coastguard Worker *
10*288bf522SAndroid Build Coastguard Worker * Unless required by applicable law or agreed to in writing, software
11*288bf522SAndroid Build Coastguard Worker * distributed under the License is distributed on an "AS IS" BASIS,
12*288bf522SAndroid Build Coastguard Worker * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13*288bf522SAndroid Build Coastguard Worker * See the License for the specific language governing permissions and
14*288bf522SAndroid Build Coastguard Worker * limitations under the License.
15*288bf522SAndroid Build Coastguard Worker */
16*288bf522SAndroid Build Coastguard Worker
17*288bf522SAndroid Build Coastguard Worker #include <memory>
18*288bf522SAndroid Build Coastguard Worker #include <queue>
19*288bf522SAndroid Build Coastguard Worker #include <string>
20*288bf522SAndroid Build Coastguard Worker #include <unordered_map>
21*288bf522SAndroid Build Coastguard Worker #include <vector>
22*288bf522SAndroid Build Coastguard Worker
23*288bf522SAndroid Build Coastguard Worker #include <android-base/file.h>
24*288bf522SAndroid Build Coastguard Worker #include <android-base/logging.h>
25*288bf522SAndroid Build Coastguard Worker #include <android-base/stringprintf.h>
26*288bf522SAndroid Build Coastguard Worker
27*288bf522SAndroid Build Coastguard Worker #include "SampleDisplayer.h"
28*288bf522SAndroid Build Coastguard Worker #include "command.h"
29*288bf522SAndroid Build Coastguard Worker #include "event_selection_set.h"
30*288bf522SAndroid Build Coastguard Worker #include "record.h"
31*288bf522SAndroid Build Coastguard Worker #include "record_file.h"
32*288bf522SAndroid Build Coastguard Worker #include "tracing.h"
33*288bf522SAndroid Build Coastguard Worker #include "utils.h"
34*288bf522SAndroid Build Coastguard Worker
35*288bf522SAndroid Build Coastguard Worker namespace simpleperf {
36*288bf522SAndroid Build Coastguard Worker namespace {
37*288bf522SAndroid Build Coastguard Worker
38*288bf522SAndroid Build Coastguard Worker using android::base::StringPrintf;
39*288bf522SAndroid Build Coastguard Worker
40*288bf522SAndroid Build Coastguard Worker struct SampleInfo {
41*288bf522SAndroid Build Coastguard Worker uint64_t timestamp; // the time when the kernel generates the sample
42*288bf522SAndroid Build Coastguard Worker uint64_t runtime_in_ns; // the runtime of the thread in the sample
SampleInfosimpleperf::__anona464bbee0111::SampleInfo43*288bf522SAndroid Build Coastguard Worker SampleInfo(uint64_t timestamp = 0, uint64_t runtime_in_ns = 0)
44*288bf522SAndroid Build Coastguard Worker : timestamp(timestamp), runtime_in_ns(runtime_in_ns) {}
45*288bf522SAndroid Build Coastguard Worker };
46*288bf522SAndroid Build Coastguard Worker
47*288bf522SAndroid Build Coastguard Worker struct SpinInfo {
48*288bf522SAndroid Build Coastguard Worker uint64_t spinloop_count = 0;
49*288bf522SAndroid Build Coastguard Worker double max_rate = 0;
50*288bf522SAndroid Build Coastguard Worker uint64_t max_rate_start_timestamp = 0;
51*288bf522SAndroid Build Coastguard Worker uint64_t max_rate_end_timestamp = 0;
52*288bf522SAndroid Build Coastguard Worker std::queue<SampleInfo> samples_in_check_period;
53*288bf522SAndroid Build Coastguard Worker uint64_t runtime_in_check_period = 0;
54*288bf522SAndroid Build Coastguard Worker };
55*288bf522SAndroid Build Coastguard Worker
56*288bf522SAndroid Build Coastguard Worker struct ThreadInfo {
57*288bf522SAndroid Build Coastguard Worker pid_t process_id = 0;
58*288bf522SAndroid Build Coastguard Worker pid_t thread_id = 0;
59*288bf522SAndroid Build Coastguard Worker std::string name;
60*288bf522SAndroid Build Coastguard Worker uint64_t total_runtime_in_ns = 0;
61*288bf522SAndroid Build Coastguard Worker SpinInfo spin_info;
62*288bf522SAndroid Build Coastguard Worker };
63*288bf522SAndroid Build Coastguard Worker
64*288bf522SAndroid Build Coastguard Worker struct ProcessInfo {
65*288bf522SAndroid Build Coastguard Worker pid_t process_id = 0;
66*288bf522SAndroid Build Coastguard Worker std::string name;
67*288bf522SAndroid Build Coastguard Worker uint64_t total_runtime_in_ns = 0;
68*288bf522SAndroid Build Coastguard Worker std::vector<const ThreadInfo*> threads;
69*288bf522SAndroid Build Coastguard Worker };
70*288bf522SAndroid Build Coastguard Worker
71*288bf522SAndroid Build Coastguard Worker class TraceSchedCommand : public Command {
72*288bf522SAndroid Build Coastguard Worker public:
TraceSchedCommand()73*288bf522SAndroid Build Coastguard Worker TraceSchedCommand()
74*288bf522SAndroid Build Coastguard Worker : Command("trace-sched", "Trace system-wide process runtime events.",
75*288bf522SAndroid Build Coastguard Worker // clang-format off
76*288bf522SAndroid Build Coastguard Worker "Records system-wide sched:sched_stat_runtime events, reports runtime taken\n"
77*288bf522SAndroid Build Coastguard Worker "by each process during recording, and optionally warns about processes which\n"
78*288bf522SAndroid Build Coastguard Worker "may have spinloops.\n"
79*288bf522SAndroid Build Coastguard Worker "Usage: simpleperf trace-sched [options]\n"
80*288bf522SAndroid Build Coastguard Worker "--duration time_in_sec Monitor for time_in_sec seconds. Here time_in_sec may\n"
81*288bf522SAndroid Build Coastguard Worker " be any positive floating point number. Default is 10.\n"
82*288bf522SAndroid Build Coastguard Worker "--check-spinloop check_period_in_sec\n"
83*288bf522SAndroid Build Coastguard Worker " Give warning for threads which may be spinning. A thread is\n"
84*288bf522SAndroid Build Coastguard Worker " thought of spinning on the CPU, when it takes more than\n"
85*288bf522SAndroid Build Coastguard Worker " [spin-rate] * [check_period] cpu time in any [check_period].\n"
86*288bf522SAndroid Build Coastguard Worker " [spin-rate] can be set by --spin-rate. Default check_period is 1 sec.\n"
87*288bf522SAndroid Build Coastguard Worker "--spin-rate spin-rate Default is 0.8. Vaild range is (0, 1].\n"
88*288bf522SAndroid Build Coastguard Worker "--show-threads Show runtime of each thread.\n"
89*288bf522SAndroid Build Coastguard Worker "--record-file file_path Read records from file_path.\n"
90*288bf522SAndroid Build Coastguard Worker // clang-format on
91*288bf522SAndroid Build Coastguard Worker ),
92*288bf522SAndroid Build Coastguard Worker duration_in_sec_(10.0),
93*288bf522SAndroid Build Coastguard Worker spinloop_check_period_in_sec_(1.0),
94*288bf522SAndroid Build Coastguard Worker spinloop_check_rate_(0.8),
95*288bf522SAndroid Build Coastguard Worker show_threads_(false) {}
96*288bf522SAndroid Build Coastguard Worker
97*288bf522SAndroid Build Coastguard Worker bool Run(const std::vector<std::string>& args);
98*288bf522SAndroid Build Coastguard Worker
99*288bf522SAndroid Build Coastguard Worker private:
100*288bf522SAndroid Build Coastguard Worker bool ParseOptions(const std::vector<std::string>& args);
101*288bf522SAndroid Build Coastguard Worker bool RecordSchedEvents(const std::string& record_file_path);
102*288bf522SAndroid Build Coastguard Worker bool ParseSchedEvents(const std::string& record_file_path);
103*288bf522SAndroid Build Coastguard Worker bool ProcessRecord(Record& record);
104*288bf522SAndroid Build Coastguard Worker void ProcessSampleRecord(const SampleRecord& record);
105*288bf522SAndroid Build Coastguard Worker std::vector<ProcessInfo> BuildProcessInfo();
106*288bf522SAndroid Build Coastguard Worker void ReportProcessInfo(const std::vector<ProcessInfo>& processes);
107*288bf522SAndroid Build Coastguard Worker
108*288bf522SAndroid Build Coastguard Worker double duration_in_sec_;
109*288bf522SAndroid Build Coastguard Worker double spinloop_check_period_in_sec_;
110*288bf522SAndroid Build Coastguard Worker double spinloop_check_rate_;
111*288bf522SAndroid Build Coastguard Worker bool show_threads_;
112*288bf522SAndroid Build Coastguard Worker std::string record_file_;
113*288bf522SAndroid Build Coastguard Worker
114*288bf522SAndroid Build Coastguard Worker StringTracingFieldPlace tracing_field_comm_;
115*288bf522SAndroid Build Coastguard Worker TracingFieldPlace tracing_field_runtime_;
116*288bf522SAndroid Build Coastguard Worker std::unordered_map<pid_t, ThreadInfo> thread_map_;
117*288bf522SAndroid Build Coastguard Worker };
118*288bf522SAndroid Build Coastguard Worker
Run(const std::vector<std::string> & args)119*288bf522SAndroid Build Coastguard Worker bool TraceSchedCommand::Run(const std::vector<std::string>& args) {
120*288bf522SAndroid Build Coastguard Worker if (!ParseOptions(args)) {
121*288bf522SAndroid Build Coastguard Worker return false;
122*288bf522SAndroid Build Coastguard Worker }
123*288bf522SAndroid Build Coastguard Worker TemporaryFile tmp_file;
124*288bf522SAndroid Build Coastguard Worker if (record_file_.empty()) {
125*288bf522SAndroid Build Coastguard Worker if (!RecordSchedEvents(tmp_file.path)) {
126*288bf522SAndroid Build Coastguard Worker return false;
127*288bf522SAndroid Build Coastguard Worker }
128*288bf522SAndroid Build Coastguard Worker record_file_ = tmp_file.path;
129*288bf522SAndroid Build Coastguard Worker }
130*288bf522SAndroid Build Coastguard Worker if (!ParseSchedEvents(record_file_)) {
131*288bf522SAndroid Build Coastguard Worker return false;
132*288bf522SAndroid Build Coastguard Worker }
133*288bf522SAndroid Build Coastguard Worker std::vector<ProcessInfo> processes = BuildProcessInfo();
134*288bf522SAndroid Build Coastguard Worker ReportProcessInfo(processes);
135*288bf522SAndroid Build Coastguard Worker return true;
136*288bf522SAndroid Build Coastguard Worker }
137*288bf522SAndroid Build Coastguard Worker
ParseOptions(const std::vector<std::string> & args)138*288bf522SAndroid Build Coastguard Worker bool TraceSchedCommand::ParseOptions(const std::vector<std::string>& args) {
139*288bf522SAndroid Build Coastguard Worker size_t i;
140*288bf522SAndroid Build Coastguard Worker for (i = 0; i < args.size(); ++i) {
141*288bf522SAndroid Build Coastguard Worker if (args[i] == "--duration") {
142*288bf522SAndroid Build Coastguard Worker if (!GetDoubleOption(args, &i, &duration_in_sec_, 1e-9)) {
143*288bf522SAndroid Build Coastguard Worker return false;
144*288bf522SAndroid Build Coastguard Worker }
145*288bf522SAndroid Build Coastguard Worker } else if (args[i] == "--check-spinloop") {
146*288bf522SAndroid Build Coastguard Worker if (!GetDoubleOption(args, &i, &spinloop_check_period_in_sec_, 1e-9)) {
147*288bf522SAndroid Build Coastguard Worker return false;
148*288bf522SAndroid Build Coastguard Worker }
149*288bf522SAndroid Build Coastguard Worker } else if (args[i] == "--spin-rate") {
150*288bf522SAndroid Build Coastguard Worker if (!GetDoubleOption(args, &i, &spinloop_check_rate_, 1e-9, 1.0)) {
151*288bf522SAndroid Build Coastguard Worker return false;
152*288bf522SAndroid Build Coastguard Worker }
153*288bf522SAndroid Build Coastguard Worker } else if (args[i] == "--show-threads") {
154*288bf522SAndroid Build Coastguard Worker show_threads_ = true;
155*288bf522SAndroid Build Coastguard Worker } else if (args[i] == "--record-file") {
156*288bf522SAndroid Build Coastguard Worker if (!NextArgumentOrError(args, &i)) {
157*288bf522SAndroid Build Coastguard Worker return false;
158*288bf522SAndroid Build Coastguard Worker }
159*288bf522SAndroid Build Coastguard Worker record_file_ = args[i];
160*288bf522SAndroid Build Coastguard Worker } else {
161*288bf522SAndroid Build Coastguard Worker ReportUnknownOption(args, i);
162*288bf522SAndroid Build Coastguard Worker return false;
163*288bf522SAndroid Build Coastguard Worker }
164*288bf522SAndroid Build Coastguard Worker }
165*288bf522SAndroid Build Coastguard Worker return true;
166*288bf522SAndroid Build Coastguard Worker }
167*288bf522SAndroid Build Coastguard Worker
RecordSchedEvents(const std::string & record_file_path)168*288bf522SAndroid Build Coastguard Worker bool TraceSchedCommand::RecordSchedEvents(const std::string& record_file_path) {
169*288bf522SAndroid Build Coastguard Worker if (!IsRoot()) {
170*288bf522SAndroid Build Coastguard Worker LOG(ERROR) << "Need root privilege to trace system wide events.\n";
171*288bf522SAndroid Build Coastguard Worker return false;
172*288bf522SAndroid Build Coastguard Worker }
173*288bf522SAndroid Build Coastguard Worker std::unique_ptr<Command> record_cmd = CreateCommandInstance("record");
174*288bf522SAndroid Build Coastguard Worker CHECK(record_cmd);
175*288bf522SAndroid Build Coastguard Worker std::vector<std::string> record_args = {"-e",
176*288bf522SAndroid Build Coastguard Worker "sched:sched_stat_runtime",
177*288bf522SAndroid Build Coastguard Worker "-a",
178*288bf522SAndroid Build Coastguard Worker "--duration",
179*288bf522SAndroid Build Coastguard Worker std::to_string(duration_in_sec_),
180*288bf522SAndroid Build Coastguard Worker "-o",
181*288bf522SAndroid Build Coastguard Worker record_file_path};
182*288bf522SAndroid Build Coastguard Worker if (IsSettingClockIdSupported()) {
183*288bf522SAndroid Build Coastguard Worker record_args.push_back("--clockid");
184*288bf522SAndroid Build Coastguard Worker record_args.push_back("monotonic");
185*288bf522SAndroid Build Coastguard Worker }
186*288bf522SAndroid Build Coastguard Worker return record_cmd->Run(record_args);
187*288bf522SAndroid Build Coastguard Worker }
188*288bf522SAndroid Build Coastguard Worker
ParseSchedEvents(const std::string & record_file_path)189*288bf522SAndroid Build Coastguard Worker bool TraceSchedCommand::ParseSchedEvents(const std::string& record_file_path) {
190*288bf522SAndroid Build Coastguard Worker std::unique_ptr<RecordFileReader> reader = RecordFileReader::CreateInstance(record_file_path);
191*288bf522SAndroid Build Coastguard Worker if (!reader) {
192*288bf522SAndroid Build Coastguard Worker return false;
193*288bf522SAndroid Build Coastguard Worker }
194*288bf522SAndroid Build Coastguard Worker const EventType* event = FindEventTypeByName("sched:sched_stat_runtime");
195*288bf522SAndroid Build Coastguard Worker const EventAttrIds& attrs = reader->AttrSection();
196*288bf522SAndroid Build Coastguard Worker if (attrs.size() != 1u || attrs[0].attr.type != event->type ||
197*288bf522SAndroid Build Coastguard Worker attrs[0].attr.config != event->config) {
198*288bf522SAndroid Build Coastguard Worker LOG(ERROR) << "sched:sched_stat_runtime isn't recorded in " << record_file_path;
199*288bf522SAndroid Build Coastguard Worker return false;
200*288bf522SAndroid Build Coastguard Worker }
201*288bf522SAndroid Build Coastguard Worker
202*288bf522SAndroid Build Coastguard Worker auto callback = [this](std::unique_ptr<Record> record) { return ProcessRecord(*record); };
203*288bf522SAndroid Build Coastguard Worker return reader->ReadDataSection(callback);
204*288bf522SAndroid Build Coastguard Worker }
205*288bf522SAndroid Build Coastguard Worker
ProcessRecord(Record & record)206*288bf522SAndroid Build Coastguard Worker bool TraceSchedCommand::ProcessRecord(Record& record) {
207*288bf522SAndroid Build Coastguard Worker switch (record.type()) {
208*288bf522SAndroid Build Coastguard Worker case PERF_RECORD_SAMPLE: {
209*288bf522SAndroid Build Coastguard Worker ProcessSampleRecord(*static_cast<SampleRecord*>(&record));
210*288bf522SAndroid Build Coastguard Worker break;
211*288bf522SAndroid Build Coastguard Worker }
212*288bf522SAndroid Build Coastguard Worker case PERF_RECORD_COMM: {
213*288bf522SAndroid Build Coastguard Worker const CommRecord& r = *static_cast<const CommRecord*>(&record);
214*288bf522SAndroid Build Coastguard Worker auto& thread = thread_map_[r.data->tid];
215*288bf522SAndroid Build Coastguard Worker thread.process_id = r.data->pid;
216*288bf522SAndroid Build Coastguard Worker thread.thread_id = r.data->tid;
217*288bf522SAndroid Build Coastguard Worker thread.name = r.comm;
218*288bf522SAndroid Build Coastguard Worker break;
219*288bf522SAndroid Build Coastguard Worker }
220*288bf522SAndroid Build Coastguard Worker case PERF_RECORD_FORK: {
221*288bf522SAndroid Build Coastguard Worker const ForkRecord& r = *static_cast<const ForkRecord*>(&record);
222*288bf522SAndroid Build Coastguard Worker auto& parent_thread = thread_map_[r.data->ptid];
223*288bf522SAndroid Build Coastguard Worker auto& child_thread = thread_map_[r.data->tid];
224*288bf522SAndroid Build Coastguard Worker parent_thread.process_id = r.data->ppid;
225*288bf522SAndroid Build Coastguard Worker parent_thread.thread_id = r.data->ptid;
226*288bf522SAndroid Build Coastguard Worker child_thread.process_id = r.data->pid;
227*288bf522SAndroid Build Coastguard Worker child_thread.thread_id = r.data->tid;
228*288bf522SAndroid Build Coastguard Worker child_thread.name = parent_thread.name;
229*288bf522SAndroid Build Coastguard Worker break;
230*288bf522SAndroid Build Coastguard Worker }
231*288bf522SAndroid Build Coastguard Worker case PERF_RECORD_TRACING_DATA:
232*288bf522SAndroid Build Coastguard Worker case SIMPLE_PERF_RECORD_TRACING_DATA: {
233*288bf522SAndroid Build Coastguard Worker const TracingDataRecord& r = *static_cast<const TracingDataRecord*>(&record);
234*288bf522SAndroid Build Coastguard Worker auto tracing = Tracing::Create(std::vector<char>(r.data, r.data + r.data_size));
235*288bf522SAndroid Build Coastguard Worker if (!tracing) {
236*288bf522SAndroid Build Coastguard Worker return false;
237*288bf522SAndroid Build Coastguard Worker }
238*288bf522SAndroid Build Coastguard Worker const EventType* event = FindEventTypeByName("sched:sched_stat_runtime");
239*288bf522SAndroid Build Coastguard Worker CHECK(event != nullptr);
240*288bf522SAndroid Build Coastguard Worker std::optional<TracingFormat> format = tracing->GetTracingFormatHavingId(event->config);
241*288bf522SAndroid Build Coastguard Worker if (!format.has_value()) {
242*288bf522SAndroid Build Coastguard Worker return false;
243*288bf522SAndroid Build Coastguard Worker }
244*288bf522SAndroid Build Coastguard Worker format.value().GetField("comm", tracing_field_comm_);
245*288bf522SAndroid Build Coastguard Worker format.value().GetField("runtime", tracing_field_runtime_);
246*288bf522SAndroid Build Coastguard Worker break;
247*288bf522SAndroid Build Coastguard Worker }
248*288bf522SAndroid Build Coastguard Worker }
249*288bf522SAndroid Build Coastguard Worker return true;
250*288bf522SAndroid Build Coastguard Worker }
251*288bf522SAndroid Build Coastguard Worker
ProcessSampleRecord(const SampleRecord & record)252*288bf522SAndroid Build Coastguard Worker void TraceSchedCommand::ProcessSampleRecord(const SampleRecord& record) {
253*288bf522SAndroid Build Coastguard Worker std::string thread_name = tracing_field_comm_.ReadFromData(record.raw_data.data);
254*288bf522SAndroid Build Coastguard Worker uint64_t runtime = tracing_field_runtime_.ReadFromData(record.raw_data.data);
255*288bf522SAndroid Build Coastguard Worker ThreadInfo& thread = thread_map_[record.tid_data.tid];
256*288bf522SAndroid Build Coastguard Worker thread.process_id = record.tid_data.pid;
257*288bf522SAndroid Build Coastguard Worker thread.thread_id = record.tid_data.tid;
258*288bf522SAndroid Build Coastguard Worker thread.name = thread_name;
259*288bf522SAndroid Build Coastguard Worker thread.total_runtime_in_ns += runtime;
260*288bf522SAndroid Build Coastguard Worker SpinInfo& spin_info = thread.spin_info;
261*288bf522SAndroid Build Coastguard Worker spin_info.runtime_in_check_period += runtime;
262*288bf522SAndroid Build Coastguard Worker spin_info.samples_in_check_period.push(SampleInfo(record.Timestamp(), runtime));
263*288bf522SAndroid Build Coastguard Worker
264*288bf522SAndroid Build Coastguard Worker // Check spin loop.
265*288bf522SAndroid Build Coastguard Worker if (thread.spin_info.samples_in_check_period.size() == 1u) {
266*288bf522SAndroid Build Coastguard Worker return;
267*288bf522SAndroid Build Coastguard Worker }
268*288bf522SAndroid Build Coastguard Worker uint64_t start_timestamp = spin_info.samples_in_check_period.front().timestamp;
269*288bf522SAndroid Build Coastguard Worker uint64_t time_period_in_ns = record.Timestamp() - start_timestamp;
270*288bf522SAndroid Build Coastguard Worker if (time_period_in_ns < spinloop_check_period_in_sec_ * 1e9) {
271*288bf522SAndroid Build Coastguard Worker return;
272*288bf522SAndroid Build Coastguard Worker }
273*288bf522SAndroid Build Coastguard Worker if (thread.spin_info.runtime_in_check_period > time_period_in_ns * spinloop_check_rate_) {
274*288bf522SAndroid Build Coastguard Worker // Detect a spin loop.
275*288bf522SAndroid Build Coastguard Worker thread.spin_info.spinloop_count++;
276*288bf522SAndroid Build Coastguard Worker double rate = std::min(
277*288bf522SAndroid Build Coastguard Worker 1.0, static_cast<double>(thread.spin_info.runtime_in_check_period) / time_period_in_ns);
278*288bf522SAndroid Build Coastguard Worker if (rate > thread.spin_info.max_rate) {
279*288bf522SAndroid Build Coastguard Worker thread.spin_info.max_rate = rate;
280*288bf522SAndroid Build Coastguard Worker thread.spin_info.max_rate_start_timestamp = start_timestamp;
281*288bf522SAndroid Build Coastguard Worker thread.spin_info.max_rate_end_timestamp = record.Timestamp();
282*288bf522SAndroid Build Coastguard Worker // Clear samples to avoid overlapped spin loop periods.
283*288bf522SAndroid Build Coastguard Worker std::queue<SampleInfo> empty_q;
284*288bf522SAndroid Build Coastguard Worker std::swap(thread.spin_info.samples_in_check_period, empty_q);
285*288bf522SAndroid Build Coastguard Worker thread.spin_info.runtime_in_check_period = 0;
286*288bf522SAndroid Build Coastguard Worker } else {
287*288bf522SAndroid Build Coastguard Worker thread.spin_info.runtime_in_check_period -=
288*288bf522SAndroid Build Coastguard Worker spin_info.samples_in_check_period.front().runtime_in_ns;
289*288bf522SAndroid Build Coastguard Worker thread.spin_info.samples_in_check_period.pop();
290*288bf522SAndroid Build Coastguard Worker }
291*288bf522SAndroid Build Coastguard Worker }
292*288bf522SAndroid Build Coastguard Worker }
293*288bf522SAndroid Build Coastguard Worker
BuildProcessInfo()294*288bf522SAndroid Build Coastguard Worker std::vector<ProcessInfo> TraceSchedCommand::BuildProcessInfo() {
295*288bf522SAndroid Build Coastguard Worker std::unordered_map<pid_t, ProcessInfo> process_map;
296*288bf522SAndroid Build Coastguard Worker for (auto& pair : thread_map_) {
297*288bf522SAndroid Build Coastguard Worker const ThreadInfo& thread = pair.second;
298*288bf522SAndroid Build Coastguard Worker // No need to report simpleperf.
299*288bf522SAndroid Build Coastguard Worker if (thread.name == "simpleperf") {
300*288bf522SAndroid Build Coastguard Worker continue;
301*288bf522SAndroid Build Coastguard Worker }
302*288bf522SAndroid Build Coastguard Worker ProcessInfo& process = process_map[thread.process_id];
303*288bf522SAndroid Build Coastguard Worker process.process_id = thread.process_id;
304*288bf522SAndroid Build Coastguard Worker if (thread.process_id == thread.thread_id) {
305*288bf522SAndroid Build Coastguard Worker process.name = thread.name;
306*288bf522SAndroid Build Coastguard Worker }
307*288bf522SAndroid Build Coastguard Worker process.total_runtime_in_ns += thread.total_runtime_in_ns;
308*288bf522SAndroid Build Coastguard Worker process.threads.push_back(&thread);
309*288bf522SAndroid Build Coastguard Worker }
310*288bf522SAndroid Build Coastguard Worker std::vector<ProcessInfo> processes;
311*288bf522SAndroid Build Coastguard Worker for (auto& pair : process_map) {
312*288bf522SAndroid Build Coastguard Worker processes.push_back(pair.second);
313*288bf522SAndroid Build Coastguard Worker }
314*288bf522SAndroid Build Coastguard Worker auto sort_process = [](const ProcessInfo& p1, const ProcessInfo& p2) {
315*288bf522SAndroid Build Coastguard Worker return p1.total_runtime_in_ns > p2.total_runtime_in_ns;
316*288bf522SAndroid Build Coastguard Worker };
317*288bf522SAndroid Build Coastguard Worker auto sort_thread = [](const ThreadInfo* t1, const ThreadInfo* t2) {
318*288bf522SAndroid Build Coastguard Worker return t1->total_runtime_in_ns > t2->total_runtime_in_ns;
319*288bf522SAndroid Build Coastguard Worker };
320*288bf522SAndroid Build Coastguard Worker std::sort(processes.begin(), processes.end(), sort_process);
321*288bf522SAndroid Build Coastguard Worker for (auto& process : processes) {
322*288bf522SAndroid Build Coastguard Worker std::sort(process.threads.begin(), process.threads.end(), sort_thread);
323*288bf522SAndroid Build Coastguard Worker }
324*288bf522SAndroid Build Coastguard Worker return processes;
325*288bf522SAndroid Build Coastguard Worker }
326*288bf522SAndroid Build Coastguard Worker
ReportProcessInfo(const std::vector<ProcessInfo> & processes)327*288bf522SAndroid Build Coastguard Worker void TraceSchedCommand::ReportProcessInfo(const std::vector<ProcessInfo>& processes) {
328*288bf522SAndroid Build Coastguard Worker uint64_t total_runtime_in_ns = 0u;
329*288bf522SAndroid Build Coastguard Worker for (auto& process : processes) {
330*288bf522SAndroid Build Coastguard Worker total_runtime_in_ns += process.total_runtime_in_ns;
331*288bf522SAndroid Build Coastguard Worker }
332*288bf522SAndroid Build Coastguard Worker printf("Total Runtime: %.3f ms\n", total_runtime_in_ns / 1e6);
333*288bf522SAndroid Build Coastguard Worker struct ReportEntry {
334*288bf522SAndroid Build Coastguard Worker bool is_process = false;
335*288bf522SAndroid Build Coastguard Worker uint64_t runtime_in_ns = 0;
336*288bf522SAndroid Build Coastguard Worker double percentage = 0;
337*288bf522SAndroid Build Coastguard Worker pid_t pid = 0;
338*288bf522SAndroid Build Coastguard Worker std::string name;
339*288bf522SAndroid Build Coastguard Worker };
340*288bf522SAndroid Build Coastguard Worker std::vector<ReportEntry> entries;
341*288bf522SAndroid Build Coastguard Worker for (auto& process : processes) {
342*288bf522SAndroid Build Coastguard Worker ReportEntry entry;
343*288bf522SAndroid Build Coastguard Worker entry.is_process = true;
344*288bf522SAndroid Build Coastguard Worker entry.runtime_in_ns = process.total_runtime_in_ns;
345*288bf522SAndroid Build Coastguard Worker entry.pid = process.process_id;
346*288bf522SAndroid Build Coastguard Worker entry.name = process.name;
347*288bf522SAndroid Build Coastguard Worker entry.percentage = 0.0;
348*288bf522SAndroid Build Coastguard Worker if (total_runtime_in_ns != 0u) {
349*288bf522SAndroid Build Coastguard Worker entry.percentage = 100.0 * process.total_runtime_in_ns / total_runtime_in_ns;
350*288bf522SAndroid Build Coastguard Worker }
351*288bf522SAndroid Build Coastguard Worker // Omit processes taken too small percentage.
352*288bf522SAndroid Build Coastguard Worker if (entry.percentage < 0.01) {
353*288bf522SAndroid Build Coastguard Worker continue;
354*288bf522SAndroid Build Coastguard Worker }
355*288bf522SAndroid Build Coastguard Worker entries.push_back(entry);
356*288bf522SAndroid Build Coastguard Worker if (show_threads_) {
357*288bf522SAndroid Build Coastguard Worker for (auto& thread : process.threads) {
358*288bf522SAndroid Build Coastguard Worker ReportEntry entry;
359*288bf522SAndroid Build Coastguard Worker entry.is_process = false;
360*288bf522SAndroid Build Coastguard Worker entry.runtime_in_ns = thread->total_runtime_in_ns;
361*288bf522SAndroid Build Coastguard Worker entry.pid = thread->thread_id;
362*288bf522SAndroid Build Coastguard Worker entry.name = thread->name;
363*288bf522SAndroid Build Coastguard Worker entry.percentage = 0.0;
364*288bf522SAndroid Build Coastguard Worker if (total_runtime_in_ns != 0u) {
365*288bf522SAndroid Build Coastguard Worker entry.percentage = 100.0 * thread->total_runtime_in_ns / total_runtime_in_ns;
366*288bf522SAndroid Build Coastguard Worker }
367*288bf522SAndroid Build Coastguard Worker // Omit threads taken too small percentage.
368*288bf522SAndroid Build Coastguard Worker if (entry.percentage < 0.01) {
369*288bf522SAndroid Build Coastguard Worker continue;
370*288bf522SAndroid Build Coastguard Worker }
371*288bf522SAndroid Build Coastguard Worker entries.push_back(entry);
372*288bf522SAndroid Build Coastguard Worker }
373*288bf522SAndroid Build Coastguard Worker }
374*288bf522SAndroid Build Coastguard Worker }
375*288bf522SAndroid Build Coastguard Worker
376*288bf522SAndroid Build Coastguard Worker SampleDisplayer<ReportEntry, uint64_t> displayer;
377*288bf522SAndroid Build Coastguard Worker if (show_threads_) {
378*288bf522SAndroid Build Coastguard Worker displayer.AddDisplayFunction("Type", [](const ReportEntry* entry) -> std::string {
379*288bf522SAndroid Build Coastguard Worker return entry->is_process ? "Process" : "Thread";
380*288bf522SAndroid Build Coastguard Worker });
381*288bf522SAndroid Build Coastguard Worker }
382*288bf522SAndroid Build Coastguard Worker displayer.AddDisplayFunction("Runtime", [](const ReportEntry* entry) {
383*288bf522SAndroid Build Coastguard Worker return StringPrintf("%.3f ms", entry->runtime_in_ns / 1e6);
384*288bf522SAndroid Build Coastguard Worker });
385*288bf522SAndroid Build Coastguard Worker displayer.AddDisplayFunction("Percentage", [](const ReportEntry* entry) {
386*288bf522SAndroid Build Coastguard Worker return StringPrintf("%.2f%%", entry->percentage);
387*288bf522SAndroid Build Coastguard Worker });
388*288bf522SAndroid Build Coastguard Worker displayer.AddDisplayFunction(
389*288bf522SAndroid Build Coastguard Worker "Pid", [](const ReportEntry* entry) { return StringPrintf("%d", entry->pid); });
390*288bf522SAndroid Build Coastguard Worker displayer.AddDisplayFunction("Name", [](const ReportEntry* entry) { return entry->name; });
391*288bf522SAndroid Build Coastguard Worker for (auto& entry : entries) {
392*288bf522SAndroid Build Coastguard Worker displayer.AdjustWidth(&entry);
393*288bf522SAndroid Build Coastguard Worker }
394*288bf522SAndroid Build Coastguard Worker displayer.PrintNames(stdout);
395*288bf522SAndroid Build Coastguard Worker for (auto& entry : entries) {
396*288bf522SAndroid Build Coastguard Worker displayer.PrintSample(stdout, &entry);
397*288bf522SAndroid Build Coastguard Worker }
398*288bf522SAndroid Build Coastguard Worker
399*288bf522SAndroid Build Coastguard Worker for (auto& process : processes) {
400*288bf522SAndroid Build Coastguard Worker for (auto& thread : process.threads) {
401*288bf522SAndroid Build Coastguard Worker if (thread->spin_info.spinloop_count != 0u) {
402*288bf522SAndroid Build Coastguard Worker double percentage = 100.0 * thread->spin_info.max_rate;
403*288bf522SAndroid Build Coastguard Worker double duration_in_ns =
404*288bf522SAndroid Build Coastguard Worker thread->spin_info.max_rate_end_timestamp - thread->spin_info.max_rate_start_timestamp;
405*288bf522SAndroid Build Coastguard Worker double running_time_in_ns = duration_in_ns * thread->spin_info.max_rate;
406*288bf522SAndroid Build Coastguard Worker printf("Detect %" PRIu64
407*288bf522SAndroid Build Coastguard Worker " spin loops in process %s (%d) thread %s (%d),\n"
408*288bf522SAndroid Build Coastguard Worker "max rate at [%.6f s - %.6f s], taken %.3f ms / %.3f ms (%.2f%%).\n",
409*288bf522SAndroid Build Coastguard Worker thread->spin_info.spinloop_count, process.name.c_str(), process.process_id,
410*288bf522SAndroid Build Coastguard Worker thread->name.c_str(), thread->thread_id,
411*288bf522SAndroid Build Coastguard Worker thread->spin_info.max_rate_start_timestamp / 1e9,
412*288bf522SAndroid Build Coastguard Worker thread->spin_info.max_rate_end_timestamp / 1e9, running_time_in_ns / 1e6,
413*288bf522SAndroid Build Coastguard Worker duration_in_ns / 1e6, percentage);
414*288bf522SAndroid Build Coastguard Worker }
415*288bf522SAndroid Build Coastguard Worker }
416*288bf522SAndroid Build Coastguard Worker }
417*288bf522SAndroid Build Coastguard Worker }
418*288bf522SAndroid Build Coastguard Worker
419*288bf522SAndroid Build Coastguard Worker } // namespace
420*288bf522SAndroid Build Coastguard Worker
RegisterTraceSchedCommand()421*288bf522SAndroid Build Coastguard Worker void RegisterTraceSchedCommand() {
422*288bf522SAndroid Build Coastguard Worker RegisterCommand("trace-sched", [] { return std::unique_ptr<Command>(new TraceSchedCommand()); });
423*288bf522SAndroid Build Coastguard Worker }
424*288bf522SAndroid Build Coastguard Worker
425*288bf522SAndroid Build Coastguard Worker } // namespace simpleperf
426