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 #include <inttypes.h>
17 #include <libgen.h>
18 #include <signal.h>
19 #include <sys/mman.h>
20 #include <sys/prctl.h>
21 #include <sys/utsname.h>
22 #include <time.h>
23 #include <unistd.h>
24 #include <optional>
25 #include <set>
26 #include <string>
27 #include <unordered_map>
28 #include <unordered_set>
29 #include <vector>
30
31 #include <android-base/file.h>
32 #include <android-base/logging.h>
33 #include <android-base/parseint.h>
34 #include <android-base/stringprintf.h>
35 #include <android-base/strings.h>
36 #include <android-base/unique_fd.h>
37 #if defined(__ANDROID__)
38 #include <android-base/properties.h>
39 #endif
40
41 #include "IOEventLoop.h"
42 #include "MapRecordReader.h"
43 #include "OfflineUnwinder.h"
44 #include "RecordFilter.h"
45 #include "command.h"
46 #include "dso.h"
47 #include "environment.h"
48 #include "event_selection_set.h"
49 #include "event_type.h"
50 #include "read_elf.h"
51 #include "read_symbol_map.h"
52 #include "record.h"
53 #include "thread_tree.h"
54 #include "tracing.h"
55 #include "utils.h"
56
57 namespace simpleperf {
58 namespace {
59
60 using android::base::ParseUint;
61 using android::base::Realpath;
62 using android::base::StringAppendF;
63
64 struct SymbolInfo {
65 Dso* dso;
66 const Symbol* symbol;
67 uint64_t vaddr_in_file;
68 };
69
70 // The max size of records dumped by kernel is 65535, and dump stack size
71 // should be a multiply of 8, so MAX_DUMP_STACK_SIZE is 65528.
72 constexpr uint32_t MAX_DUMP_STACK_SIZE = 65528;
73
74 // The max allowed pages in mapped buffer is decided by rlimit(RLIMIT_MEMLOCK).
75 // Here 1024 is a desired value for pages in mapped buffer. If mapped
76 // successfully, the buffer size = 1024 * 4K (page size) = 4M.
77 constexpr size_t DESIRED_PAGES_IN_MAPPED_BUFFER = 1024;
78
79 // Currently, the record buffer size in user-space is set to match the kernel
80 // buffer size on a 8 core system. For system-wide recording, it is 8K pages *
81 // 4K page_size * 8 cores = 256MB. For non system-wide recording, it is 1K pages
82 // * 4K page_size * 8 cores = 64MB.
83 static constexpr size_t kRecordBufferSize = 64 * kMegabyte;
84 static constexpr size_t kSystemWideRecordBufferSize = 256 * kMegabyte;
85
86 class MonitorCommand : public Command {
87 public:
MonitorCommand()88 MonitorCommand()
89 : Command(
90 "monitor", "monitor events and print their textual representations to stdout",
91 // clang-format off
92 "Usage: simpleperf monitor [options]\n"
93 " Gather sampling information and print the events on stdout.\n"
94 " For precise recording, prefer the record command.\n"
95 " Currently, only supports system-wide collection.\n"
96 "\n"
97 "Select monitored threads:\n"
98 "-a System-wide collection. Use with --exclude-perf to exclude\n"
99 " samples for simpleperf process.\n"
100 "\n"
101 "Select monitored event types:\n"
102 "-e event1[:modifier1],event2[:modifier2],...\n"
103 " Select a list of events to record. An event can be:\n"
104 " 1) an event name listed in `simpleperf list`;\n"
105 " 2) a raw PMU event in rN format. N is a hex number.\n"
106 " For example, r1b selects event number 0x1b.\n"
107 " Modifiers can be added to define how the event should be\n"
108 " monitored. Possible modifiers are:\n"
109 " u - monitor user space events only\n"
110 " k - monitor kernel space events only\n"
111 "\n"
112 "Select monitoring options:\n"
113 "-f freq Set event sample frequency. It means recording at most [freq]\n"
114 " samples every second. For non-tracepoint events, the default\n"
115 " option is -f 4000. A -f/-c option affects all event types\n"
116 " following it until meeting another -f/-c option. For example,\n"
117 " for \"-f 1000 -e cpu-cycles -c 1 -e sched:sched_switch\", cpu-cycles\n"
118 " has sample freq 1000, sched:sched_switch event has sample period 1.\n"
119 "-c count Set event sample period. It means recording one sample when\n"
120 " [count] events happen. For tracepoint events, the default option\n"
121 " is -c 1.\n"
122 "--call-graph fp | dwarf[,<dump_stack_size>]\n"
123 " Enable call graph recording. Use frame pointer or dwarf debug\n"
124 " frame as the method to parse call graph in stack.\n"
125 " Default is dwarf,65528.\n"
126 "-g Same as '--call-graph dwarf'.\n"
127 "--duration time_in_sec Monitor for time_in_sec seconds. Here time_in_sec"
128 " may be any positive floating point number.\n"
129 "--cpu-percent <percent> Set the max percent of cpu time used for recording.\n"
130 " percent is in range [1-100], default is 25.\n"
131 "\n"
132 "Sample filter options:\n"
133 "--exclude-perf Exclude samples for simpleperf process.\n"
134 RECORD_FILTER_OPTION_HELP_MSG_FOR_RECORDING
135 "\n"
136 // clang-format on
137 ),
138 system_wide_collection_(false),
139 fp_callchain_sampling_(false),
140 dwarf_callchain_sampling_(false),
141 dump_stack_size_in_dwarf_sampling_(MAX_DUMP_STACK_SIZE),
142 unwind_dwarf_callchain_(true),
143 duration_in_sec_(0),
144 event_selection_set_(false),
145 mmap_page_range_(std::make_pair(1, DESIRED_PAGES_IN_MAPPED_BUFFER)),
146 sample_record_count_(0),
147 last_record_timestamp_(0u),
148 record_filter_(thread_tree_) {
149 // If we run `adb shell simpleperf record xxx` and stop profiling by ctrl-c,
150 // adb closes sockets connecting simpleperf. After that, simpleperf will
151 // receive SIGPIPE when writing to stdout/stderr, which is a problem when we
152 // use '--app' option. So ignore SIGPIPE to finish properly.
153 signal(SIGPIPE, SIG_IGN);
154 }
155
156 bool Run(const std::vector<std::string>& args);
157
158 private:
159 bool ParseOptions(const std::vector<std::string>& args);
160 bool AdjustPerfEventLimit();
161 bool PrepareMonitoring();
162 bool DoMonitoring();
163 bool SetEventSelectionFlags();
164 bool DumpProcessMaps(pid_t pid, const std::unordered_set<pid_t>& tids);
165 void DumpSampleRecord(const SampleRecord& sr);
166 void DumpSampleCallchain(const SampleRecord& sr);
167 bool ProcessRecord(Record* record);
168 SymbolInfo GetSymbolInfo(uint32_t pid, uint32_t tid, uint64_t ip, bool in_kernel);
169 bool DumpMapsForRecord(Record* record);
170 void UpdateRecord(Record* record);
171 bool UnwindRecord(SampleRecord& r);
172
173 uint64_t max_sample_freq_ = DEFAULT_SAMPLE_FREQ_FOR_NONTRACEPOINT_EVENT;
174 size_t cpu_time_max_percent_ = 25;
175
176 bool system_wide_collection_;
177 bool fp_callchain_sampling_;
178 bool dwarf_callchain_sampling_;
179 uint32_t dump_stack_size_in_dwarf_sampling_;
180 bool unwind_dwarf_callchain_;
181 std::unique_ptr<OfflineUnwinder> offline_unwinder_;
182 double duration_in_sec_;
183 EventSelectionSet event_selection_set_;
184 std::pair<size_t, size_t> mmap_page_range_;
185 ThreadTree thread_tree_;
186 uint64_t sample_record_count_;
187 uint64_t last_record_timestamp_; // used to insert Mmap2Records for JIT debug info
188 // In system wide recording, record if we have dumped map info for a process.
189 std::unordered_set<pid_t> dumped_processes_;
190 bool exclude_perf_ = false;
191 RecordFilter record_filter_;
192 std::unordered_map<uint64_t, std::string> event_names_;
193
194 std::optional<MapRecordReader> map_record_reader_;
195 };
196
Run(const std::vector<std::string> & args)197 bool MonitorCommand::Run(const std::vector<std::string>& args) {
198 ScopedCurrentArch scoped_arch(GetMachineArch());
199 if (!CheckPerfEventLimit()) {
200 return false;
201 }
202 AllowMoreOpenedFiles();
203
204 if (!ParseOptions(args)) {
205 return false;
206 }
207 if (!AdjustPerfEventLimit()) {
208 return false;
209 }
210
211 if (!PrepareMonitoring()) {
212 return false;
213 }
214 return DoMonitoring();
215 }
216
PrepareMonitoring()217 bool MonitorCommand::PrepareMonitoring() {
218 // 1. Process options before opening perf event files.
219 if (!SetEventSelectionFlags()) {
220 return false;
221 }
222 if (unwind_dwarf_callchain_) {
223 offline_unwinder_ = OfflineUnwinder::Create(false);
224 }
225
226 // 2. Add monitored targets.
227 if (system_wide_collection_) {
228 event_selection_set_.AddMonitoredThreads({-1});
229 } else {
230 LOG(ERROR) << "No threads to monitor. Try `simpleperf help monitor` for help";
231 return false;
232 }
233
234 // 3. Open perf event files and create mapped buffers.
235 if (!event_selection_set_.OpenEventFiles()) {
236 return false;
237 }
238 size_t record_buffer_size =
239 system_wide_collection_ ? kSystemWideRecordBufferSize : kRecordBufferSize;
240 if (!event_selection_set_.MmapEventFiles(mmap_page_range_.first, mmap_page_range_.second,
241 0 /* aux_buffer_size */, record_buffer_size,
242 false /* allow_truncating_samples */, exclude_perf_)) {
243 return false;
244 }
245 auto callback = std::bind(&MonitorCommand::ProcessRecord, this, std::placeholders::_1);
246 if (!event_selection_set_.PrepareToReadMmapEventData(callback)) {
247 return false;
248 }
249
250 // Keep track of the event names per id.
251 event_names_ = event_selection_set_.GetEventNamesById();
252
253 // Use first perf_event_attr and first event id to dump mmap and comm records.
254 EventAttrWithId dumping_attr_id = event_selection_set_.GetEventAttrWithId()[0];
255 map_record_reader_.emplace(dumping_attr_id.attr, dumping_attr_id.ids[0],
256 event_selection_set_.RecordNotExecutableMaps());
257 map_record_reader_->SetCallback([this](Record* r) { return ProcessRecord(r); });
258
259 // 4. Load kallsyms, if possible.
260 std::string kallsyms;
261 if (LoadKernelSymbols(&kallsyms)) {
262 Dso::SetKallsyms(std::move(kallsyms));
263 }
264 map_record_reader_->ReadKernelMaps();
265
266 // 5. Add read/signal/periodic Events.
267 IOEventLoop* loop = event_selection_set_.GetIOEventLoop();
268 auto exit_loop_callback = [loop]() { return loop->ExitLoop(); };
269 if (!loop->AddSignalEvents({SIGCHLD, SIGINT, SIGTERM}, exit_loop_callback, IOEventHighPriority)) {
270 return false;
271 }
272
273 // Only add an event for SIGHUP if we didn't inherit SIG_IGN (e.g. from
274 // nohup).
275 if (!SignalIsIgnored(SIGHUP)) {
276 if (!loop->AddSignalEvent(SIGHUP, exit_loop_callback, IOEventHighPriority)) {
277 return false;
278 }
279 }
280
281 if (duration_in_sec_ != 0) {
282 if (!loop->AddPeriodicEvent(
283 SecondToTimeval(duration_in_sec_), [loop]() { return loop->ExitLoop(); },
284 IOEventHighPriority)) {
285 return false;
286 }
287 }
288 return true;
289 }
290
DoMonitoring()291 bool MonitorCommand::DoMonitoring() {
292 if (!event_selection_set_.GetIOEventLoop()->RunLoop()) {
293 return false;
294 }
295 if (!event_selection_set_.SyncKernelBuffer()) {
296 return false;
297 }
298 event_selection_set_.CloseEventFiles();
299 if (!event_selection_set_.FinishReadMmapEventData()) {
300 return false;
301 }
302 LOG(ERROR) << "Processed samples: " << sample_record_count_;
303 return true;
304 }
305
GetMonitorCmdOptionFormats()306 inline const OptionFormatMap& GetMonitorCmdOptionFormats() {
307 static OptionFormatMap option_formats;
308 if (option_formats.empty()) {
309 option_formats = {
310 {"-a", {OptionValueType::NONE, OptionType::SINGLE, AppRunnerType::NOT_ALLOWED}},
311 {"-c", {OptionValueType::UINT, OptionType::ORDERED, AppRunnerType::ALLOWED}},
312 {"--call-graph", {OptionValueType::STRING, OptionType::ORDERED, AppRunnerType::ALLOWED}},
313 {"--cpu-percent", {OptionValueType::UINT, OptionType::SINGLE, AppRunnerType::ALLOWED}},
314 {"--duration", {OptionValueType::DOUBLE, OptionType::SINGLE, AppRunnerType::ALLOWED}},
315 {"-e", {OptionValueType::STRING, OptionType::ORDERED, AppRunnerType::ALLOWED}},
316 {"--exclude-perf", {OptionValueType::NONE, OptionType::SINGLE, AppRunnerType::ALLOWED}},
317 {"-f", {OptionValueType::UINT, OptionType::ORDERED, AppRunnerType::ALLOWED}},
318 {"-g", {OptionValueType::NONE, OptionType::ORDERED, AppRunnerType::ALLOWED}},
319 {"-t", {OptionValueType::STRING, OptionType::MULTIPLE, AppRunnerType::ALLOWED}},
320 };
321 OptionFormatMap record_filter_options = GetRecordFilterOptionFormats(true);
322 option_formats.insert(record_filter_options.begin(), record_filter_options.end());
323 }
324 return option_formats;
325 }
326
ParseOptions(const std::vector<std::string> & args)327 bool MonitorCommand::ParseOptions(const std::vector<std::string>& args) {
328 OptionValueMap options;
329 std::vector<std::pair<OptionName, OptionValue>> ordered_options;
330
331 if (!PreprocessOptions(args, GetMonitorCmdOptionFormats(), &options, &ordered_options, nullptr)) {
332 return false;
333 }
334
335 // Process options.
336 system_wide_collection_ = options.PullBoolValue("-a");
337
338 if (!options.PullUintValue("--cpu-percent", &cpu_time_max_percent_, 1, 100)) {
339 return false;
340 }
341
342 if (!options.PullDoubleValue("--duration", &duration_in_sec_, 1e-9)) {
343 return false;
344 }
345
346 exclude_perf_ = options.PullBoolValue("--exclude-perf");
347 if (!record_filter_.ParseOptions(options)) {
348 return false;
349 }
350
351 CHECK(options.values.empty());
352
353 // Process ordered options.
354 for (const auto& pair : ordered_options) {
355 const OptionName& name = pair.first;
356 const OptionValue& value = pair.second;
357
358 if (name == "-c" || name == "-f") {
359 if (value.uint_value < 1) {
360 LOG(ERROR) << "invalid " << name << ": " << value.uint_value;
361 return false;
362 }
363 SampleRate rate;
364 if (name == "-c") {
365 rate.sample_period = value.uint_value;
366 } else {
367 if (value.uint_value >= INT_MAX) {
368 LOG(ERROR) << "sample freq can't be bigger than INT_MAX: " << value.uint_value;
369 return false;
370 }
371 rate.sample_freq = value.uint_value;
372 }
373 event_selection_set_.SetSampleRateForNewEvents(rate);
374
375 } else if (name == "--call-graph") {
376 std::vector<std::string> strs = android::base::Split(value.str_value, ",");
377 if (strs[0] == "fp") {
378 fp_callchain_sampling_ = true;
379 dwarf_callchain_sampling_ = false;
380 } else if (strs[0] == "dwarf") {
381 fp_callchain_sampling_ = false;
382 dwarf_callchain_sampling_ = true;
383 if (strs.size() > 1) {
384 uint64_t size;
385 if (!ParseUint(strs[1], &size)) {
386 LOG(ERROR) << "invalid dump stack size in --call-graph option: " << strs[1];
387 return false;
388 }
389 if ((size & 7) != 0) {
390 LOG(ERROR) << "dump stack size " << size << " is not 8-byte aligned.";
391 return false;
392 }
393 if (size >= MAX_DUMP_STACK_SIZE) {
394 LOG(ERROR) << "dump stack size " << size << " is bigger than max allowed size "
395 << MAX_DUMP_STACK_SIZE << ".";
396 return false;
397 }
398 dump_stack_size_in_dwarf_sampling_ = static_cast<uint32_t>(size);
399 }
400 }
401
402 } else if (name == "-e") {
403 std::vector<std::string> event_types = android::base::Split(value.str_value, ",");
404 for (auto& event_type : event_types) {
405 if (!event_selection_set_.AddEventType(event_type)) {
406 return false;
407 }
408 }
409
410 } else if (name == "-g") {
411 fp_callchain_sampling_ = false;
412 dwarf_callchain_sampling_ = true;
413 } else {
414 CHECK(false) << "unprocessed option: " << name;
415 }
416 }
417
418 if (event_selection_set_.empty()) {
419 LOG(ERROR) << "No event to record. Use `-e` to specify which event should be monitored.";
420 return false;
421 }
422
423 if (fp_callchain_sampling_) {
424 if (GetTargetArch() == ARCH_ARM) {
425 LOG(WARNING) << "`--callgraph fp` option doesn't work well on arm architecture, "
426 << "consider using `-g` option or profiling on aarch64 architecture.";
427 }
428 }
429
430 if (system_wide_collection_ && event_selection_set_.HasMonitoredTarget()) {
431 LOG(ERROR) << "Record system wide and existing processes/threads can't be "
432 "used at the same time.";
433 return false;
434 }
435
436 if (system_wide_collection_ && !IsRoot()) {
437 LOG(ERROR) << "System wide profiling needs root privilege.";
438 return false;
439 }
440 return true;
441 }
442
AdjustPerfEventLimit()443 bool MonitorCommand::AdjustPerfEventLimit() {
444 bool set_prop = false;
445 // 1. Adjust max_sample_rate.
446 uint64_t cur_max_freq;
447 if (GetMaxSampleFrequency(&cur_max_freq) && cur_max_freq < max_sample_freq_ &&
448 !SetMaxSampleFrequency(max_sample_freq_)) {
449 set_prop = true;
450 }
451 // 2. Adjust perf_cpu_time_max_percent.
452 size_t cur_percent;
453 if (GetCpuTimeMaxPercent(&cur_percent) && cur_percent != cpu_time_max_percent_ &&
454 !SetCpuTimeMaxPercent(cpu_time_max_percent_)) {
455 set_prop = true;
456 }
457 // 3. Adjust perf_event_mlock_kb.
458 long cpus = sysconf(_SC_NPROCESSORS_CONF);
459 uint64_t mlock_kb = cpus * (mmap_page_range_.second + 1) * 4;
460
461 uint64_t cur_mlock_kb;
462 if (GetPerfEventMlockKb(&cur_mlock_kb) && cur_mlock_kb < mlock_kb &&
463 !SetPerfEventMlockKb(mlock_kb)) {
464 set_prop = true;
465 }
466
467 if (GetAndroidVersion() >= kAndroidVersionQ && set_prop) {
468 return SetPerfEventLimits(std::max(max_sample_freq_, cur_max_freq), cpu_time_max_percent_,
469 std::max(mlock_kb, cur_mlock_kb));
470 }
471 return true;
472 }
473
SetEventSelectionFlags()474 bool MonitorCommand::SetEventSelectionFlags() {
475 event_selection_set_.SampleIdAll();
476 event_selection_set_.WakeupPerSample();
477 if (fp_callchain_sampling_) {
478 event_selection_set_.EnableFpCallChainSampling();
479 } else if (dwarf_callchain_sampling_) {
480 if (!event_selection_set_.EnableDwarfCallChainSampling(dump_stack_size_in_dwarf_sampling_)) {
481 return false;
482 }
483 }
484 return true;
485 }
486
ProcessRecord(Record * record)487 bool MonitorCommand::ProcessRecord(Record* record) {
488 UpdateRecord(record);
489 last_record_timestamp_ = std::max(last_record_timestamp_, record->Timestamp());
490 // In system wide recording, maps are dumped when they are needed by records.
491 if (system_wide_collection_ && !DumpMapsForRecord(record)) {
492 return false;
493 }
494 if (record->type() == PERF_RECORD_SAMPLE) {
495 auto& r = *static_cast<SampleRecord*>(record);
496
497 // Record filter check should go after DumpMapsForRecord(). Otherwise, process/thread name
498 // filters don't work in system wide collection.
499 if (!record_filter_.Check(r)) {
500 return true;
501 }
502
503 // AdjustCallChainGeneratedByKernel() should go before UnwindRecord().
504 // Because we don't want to adjust callchains generated by dwarf unwinder.
505 if (fp_callchain_sampling_ || dwarf_callchain_sampling_) {
506 r.AdjustCallChainGeneratedByKernel();
507 if (!UnwindRecord(r)) {
508 return false;
509 }
510 }
511 DumpSampleRecord(r);
512 if (fp_callchain_sampling_ || dwarf_callchain_sampling_) {
513 DumpSampleCallchain(r);
514 }
515 sample_record_count_++;
516 } else {
517 // Other types of record are forwarded to the thread tree to build the
518 // representation of each processes (mmap, comm, etc).
519 thread_tree_.Update(*record);
520 }
521 return true;
522 }
523
DumpSampleRecord(const SampleRecord & sr)524 void MonitorCommand::DumpSampleRecord(const SampleRecord& sr) {
525 std::string output("sample");
526 StringAppendF(&output, " name=%s", event_names_[sr.id_data.id].c_str());
527 StringAppendF(&output, " ip=%p", reinterpret_cast<void*>(sr.ip_data.ip));
528 SymbolInfo s = GetSymbolInfo(sr.tid_data.pid, sr.tid_data.tid, sr.ip_data.ip, sr.InKernel());
529 StringAppendF(&output, " symbol=%s (%s[+%" PRIx64 "])", s.symbol->DemangledName(),
530 s.dso->Path().c_str(), s.vaddr_in_file);
531 StringAppendF(&output, " pid=%u tid=%u", sr.tid_data.pid, sr.tid_data.tid);
532 StringAppendF(&output, " cpu=%u", sr.cpu_data.cpu);
533 printf("%s\n", output.c_str());
534 fflush(stdout);
535 }
536
DumpSampleCallchain(const SampleRecord & sr)537 void MonitorCommand::DumpSampleCallchain(const SampleRecord& sr) {
538 bool in_kernel = sr.InKernel();
539 if (sr.sample_type & PERF_SAMPLE_CALLCHAIN) {
540 for (size_t i = 0; i < sr.callchain_data.ip_nr; ++i) {
541 if (sr.callchain_data.ips[i] >= PERF_CONTEXT_MAX) {
542 if (sr.callchain_data.ips[i] == PERF_CONTEXT_USER) {
543 in_kernel = false;
544 }
545 continue;
546 }
547 SymbolInfo s =
548 GetSymbolInfo(sr.tid_data.pid, sr.tid_data.tid, sr.callchain_data.ips[i], in_kernel);
549 std::string output("sample callchain");
550 StringAppendF(&output, " %s (%s[+%" PRIx64 "])", s.symbol->DemangledName(),
551 s.dso->Path().c_str(), s.vaddr_in_file);
552 printf("%s\n", output.c_str());
553 }
554 fflush(stdout);
555 }
556 }
557
GetSymbolInfo(uint32_t pid,uint32_t tid,uint64_t ip,bool in_kernel)558 SymbolInfo MonitorCommand::GetSymbolInfo(uint32_t pid, uint32_t tid, uint64_t ip, bool in_kernel) {
559 ThreadEntry* thread = thread_tree_.FindThreadOrNew(pid, tid);
560 const MapEntry* map = thread_tree_.FindMap(thread, ip, in_kernel);
561 SymbolInfo info;
562 info.symbol = thread_tree_.FindSymbol(map, ip, &info.vaddr_in_file, &info.dso);
563 return info;
564 }
565
DumpMapsForRecord(Record * record)566 bool MonitorCommand::DumpMapsForRecord(Record* record) {
567 if (record->type() == PERF_RECORD_SAMPLE) {
568 pid_t pid = static_cast<SampleRecord*>(record)->tid_data.pid;
569 if (dumped_processes_.find(pid) == dumped_processes_.end()) {
570 // Dump map info and all thread names for that process.
571 if (!map_record_reader_->ReadProcessMaps(pid, last_record_timestamp_)) {
572 return false;
573 }
574 dumped_processes_.insert(pid);
575 }
576 }
577 return true;
578 }
579
UpdateRecord(Record * record)580 void MonitorCommand::UpdateRecord(Record* record) {
581 if (record->type() == PERF_RECORD_COMM) {
582 auto r = static_cast<CommRecord*>(record);
583 if (r->data->pid == r->data->tid) {
584 std::string s = GetCompleteProcessName(r->data->pid);
585 if (!s.empty()) {
586 r->SetCommandName(s);
587 }
588 }
589 }
590 }
591
UnwindRecord(SampleRecord & r)592 bool MonitorCommand::UnwindRecord(SampleRecord& r) {
593 if ((r.sample_type & PERF_SAMPLE_CALLCHAIN) && (r.sample_type & PERF_SAMPLE_REGS_USER) &&
594 (r.regs_user_data.reg_mask != 0) && (r.sample_type & PERF_SAMPLE_STACK_USER) &&
595 (r.GetValidStackSize() > 0)) {
596 ThreadEntry* thread = thread_tree_.FindThreadOrNew(r.tid_data.pid, r.tid_data.tid);
597 RegSet regs(r.regs_user_data.abi, r.regs_user_data.reg_mask, r.regs_user_data.regs);
598 std::vector<uint64_t> ips;
599 std::vector<uint64_t> sps;
600 if (!offline_unwinder_->UnwindCallChain(*thread, regs, r.stack_user_data.data,
601 r.GetValidStackSize(), &ips, &sps)) {
602 return false;
603 }
604 r.ReplaceRegAndStackWithCallChain(ips);
605 }
606 return true;
607 }
608 } // namespace
609
RegisterMonitorCommand()610 void RegisterMonitorCommand() {
611 RegisterCommand("monitor", [] { return std::unique_ptr<Command>(new MonitorCommand()); });
612 }
613
614 } // namespace simpleperf
615