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 * 1024 * 1024;
84 static constexpr size_t kSystemWideRecordBufferSize = 256 * 1024 * 1024;
85
86 class MonitorCommand : public Command {
87 public:
MonitorCommand()88 MonitorCommand()
89 : Command("monitor", "monitor events and print their textual representations to stdout",
90 // clang-format off
91 "Usage: simpleperf monitor [options]\n"
92 " Gather sampling information and print the events on stdout.\n"
93 " For precise recording, prefer the record command.\n"
94 " Currently, only supports system-wide collection.\n"
95 "\n"
96 "Select monitored threads:\n"
97 "-a System-wide collection. Use with --exclude-perf to exclude\n"
98 " samples for simpleperf process.\n"
99 "\n"
100 "Select monitored event types:\n"
101 "-e event1[:modifier1],event2[:modifier2],...\n"
102 " Select a list of events to record. An event can be:\n"
103 " 1) an event name listed in `simpleperf list`;\n"
104 " 2) a raw PMU event in rN format. N is a hex number.\n"
105 " For example, r1b selects event number 0x1b.\n"
106 " Modifiers can be added to define how the event should be\n"
107 " monitored. Possible modifiers are:\n"
108 " u - monitor user space events only\n"
109 " k - monitor kernel space events only\n"
110 "\n"
111 "Select monitoring options:\n"
112 "-f freq Set event sample frequency. It means recording at most [freq]\n"
113 " samples every second. For non-tracepoint events, the default\n"
114 " option is -f 4000. A -f/-c option affects all event types\n"
115 " following it until meeting another -f/-c option. For example,\n"
116 " for \"-f 1000 cpu-cycles -c 1 -e sched:sched_switch\", cpu-cycles\n"
117 " has sample freq 1000, sched:sched_switch event has sample period 1.\n"
118 "-c count Set event sample period. It means recording one sample when\n"
119 " [count] events happen. For tracepoint events, the default option\n"
120 " is -c 1.\n"
121 "--call-graph fp | dwarf[,<dump_stack_size>]\n"
122 " Enable call graph recording. Use frame pointer or dwarf debug\n"
123 " frame as the method to parse call graph in stack.\n"
124 " Default is dwarf,65528.\n"
125 "-g Same as '--call-graph dwarf'.\n"
126 "--duration time_in_sec Monitor for time_in_sec seconds. Here time_in_sec"
127 " may be any positive floating point number.\n"
128 "--cpu-percent <percent> Set the max percent of cpu time used for recording.\n"
129 " percent is in range [1-100], default is 25.\n"
130 "\n"
131 "Sample filter options:\n"
132 "--exclude-perf Exclude samples for simpleperf process.\n"
133 RECORD_FILTER_OPTION_HELP_MSG
134 "\n"
135 // clang-format on
136 ),
137 system_wide_collection_(false),
138 fp_callchain_sampling_(false),
139 dwarf_callchain_sampling_(false),
140 dump_stack_size_in_dwarf_sampling_(MAX_DUMP_STACK_SIZE),
141 unwind_dwarf_callchain_(true),
142 duration_in_sec_(0),
143 event_selection_set_(false),
144 mmap_page_range_(std::make_pair(1, DESIRED_PAGES_IN_MAPPED_BUFFER)),
145 sample_record_count_(0),
146 last_record_timestamp_(0u),
147 record_filter_(thread_tree_) {
148 // If we run `adb shell simpleperf record xxx` and stop profiling by ctrl-c,
149 // adb closes sockets connecting simpleperf. After that, simpleperf will
150 // receive SIGPIPE when writing to stdout/stderr, which is a problem when we
151 // use '--app' option. So ignore SIGPIPE to finish properly.
152 signal(SIGPIPE, SIG_IGN);
153 }
154
155 bool Run(const std::vector<std::string>& args);
156
157 private:
158 bool ParseOptions(const std::vector<std::string>& args);
159 bool AdjustPerfEventLimit();
160 bool PrepareMonitoring();
161 bool DoMonitoring();
162 bool SetEventSelectionFlags();
163 bool DumpProcessMaps(pid_t pid, const std::unordered_set<pid_t>& tids);
164 void DumpSampleRecord(const SampleRecord& sr);
165 void DumpSampleCallchain(const SampleRecord& sr);
166 bool ProcessRecord(Record* record);
167 SymbolInfo GetSymbolInfo(uint32_t pid, uint32_t tid, uint64_t ip, bool in_kernel);
168 bool DumpMapsForRecord(Record* record);
169 void UpdateRecord(Record* record);
170 bool UnwindRecord(SampleRecord& r);
171
172 uint64_t max_sample_freq_ = DEFAULT_SAMPLE_FREQ_FOR_NONTRACEPOINT_EVENT;
173 size_t cpu_time_max_percent_ = 25;
174
175 std::unique_ptr<SampleSpeed> sample_speed_;
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_cutting_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)) {
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)) {
277 return false;
278 }
279 }
280
281 if (duration_in_sec_ != 0) {
282 if (!loop->AddPeriodicEvent(SecondToTimeval(duration_in_sec_),
283 [loop]() { return loop->ExitLoop(); })) {
284 return false;
285 }
286 }
287 return true;
288 }
289
DoMonitoring()290 bool MonitorCommand::DoMonitoring() {
291 if (!event_selection_set_.GetIOEventLoop()->RunLoop()) {
292 return false;
293 }
294 if (!event_selection_set_.FinishReadMmapEventData()) {
295 return false;
296 }
297 LOG(ERROR) << "Processed samples: " << sample_record_count_;
298 return true;
299 }
300
GetMonitorCmdOptionFormats()301 inline const OptionFormatMap& GetMonitorCmdOptionFormats() {
302 static OptionFormatMap option_formats;
303 if (option_formats.empty()) {
304 option_formats = {
305 {"-a", {OptionValueType::NONE, OptionType::SINGLE, AppRunnerType::NOT_ALLOWED}},
306 {"-c", {OptionValueType::UINT, OptionType::ORDERED, AppRunnerType::ALLOWED}},
307 {"--call-graph", {OptionValueType::STRING, OptionType::ORDERED, AppRunnerType::ALLOWED}},
308 {"--cpu-percent", {OptionValueType::UINT, OptionType::SINGLE, AppRunnerType::ALLOWED}},
309 {"--duration", {OptionValueType::DOUBLE, OptionType::SINGLE, AppRunnerType::ALLOWED}},
310 {"-e", {OptionValueType::STRING, OptionType::ORDERED, AppRunnerType::ALLOWED}},
311 {"--exclude-perf", {OptionValueType::NONE, OptionType::SINGLE, AppRunnerType::ALLOWED}},
312 {"-f", {OptionValueType::UINT, OptionType::ORDERED, AppRunnerType::ALLOWED}},
313 {"-g", {OptionValueType::NONE, OptionType::ORDERED, AppRunnerType::ALLOWED}},
314 {"-t", {OptionValueType::STRING, OptionType::MULTIPLE, AppRunnerType::ALLOWED}},
315 };
316 const OptionFormatMap& record_filter_options = GetRecordFilterOptionFormats();
317 option_formats.insert(record_filter_options.begin(), record_filter_options.end());
318 }
319 return option_formats;
320 }
321
ParseOptions(const std::vector<std::string> & args)322 bool MonitorCommand::ParseOptions(const std::vector<std::string>& args) {
323 OptionValueMap options;
324 std::vector<std::pair<OptionName, OptionValue>> ordered_options;
325
326 if (!PreprocessOptions(args, GetMonitorCmdOptionFormats(), &options, &ordered_options, nullptr)) {
327 return false;
328 }
329
330 // Process options.
331 system_wide_collection_ = options.PullBoolValue("-a");
332
333 if (!options.PullUintValue("--cpu-percent", &cpu_time_max_percent_, 1, 100)) {
334 return false;
335 }
336
337 if (!options.PullDoubleValue("--duration", &duration_in_sec_, 1e-9)) {
338 return false;
339 }
340
341 exclude_perf_ = options.PullBoolValue("--exclude-perf");
342 if (!record_filter_.ParseOptions(options)) {
343 return false;
344 }
345
346 CHECK(options.values.empty());
347
348 // Process ordered options.
349 std::vector<size_t> wait_setting_speed_event_groups;
350
351 for (const auto& pair : ordered_options) {
352 const OptionName& name = pair.first;
353 const OptionValue& value = pair.second;
354
355 if (name == "-c" || name == "-f") {
356 if (value.uint_value < 1) {
357 LOG(ERROR) << "invalid " << name << ": " << value.uint_value;
358 return false;
359 }
360 if (name == "-c") {
361 sample_speed_.reset(new SampleSpeed(0, value.uint_value));
362 } else {
363 if (value.uint_value >= INT_MAX) {
364 LOG(ERROR) << "sample freq can't be bigger than INT_MAX: " << value.uint_value;
365 return false;
366 }
367 sample_speed_.reset(new SampleSpeed(value.uint_value, 0));
368 }
369
370 for (auto groud_id : wait_setting_speed_event_groups) {
371 event_selection_set_.SetSampleSpeed(groud_id, *sample_speed_);
372 }
373 wait_setting_speed_event_groups.clear();
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 size_t group_id;
406 if (!event_selection_set_.AddEventType(event_type, &group_id)) {
407 return false;
408 }
409 if (sample_speed_) {
410 event_selection_set_.SetSampleSpeed(group_id, *sample_speed_);
411 } else {
412 wait_setting_speed_event_groups.push_back(group_id);
413 }
414 }
415
416 } else if (name == "-g") {
417 fp_callchain_sampling_ = false;
418 dwarf_callchain_sampling_ = true;
419 } else {
420 CHECK(false) << "unprocessed option: " << name;
421 }
422 }
423
424 if (event_selection_set_.empty()) {
425 LOG(ERROR) << "No event to record. Use `-e` to specify which event should be monitored.";
426 return false;
427 }
428
429 if (fp_callchain_sampling_) {
430 if (GetBuildArch() == ARCH_ARM) {
431 LOG(WARNING) << "`--callgraph fp` option doesn't work well on arm architecture, "
432 << "consider using `-g` option or profiling on aarch64 architecture.";
433 }
434 }
435
436 if (system_wide_collection_ && event_selection_set_.HasMonitoredTarget()) {
437 LOG(ERROR) << "Record system wide and existing processes/threads can't be "
438 "used at the same time.";
439 return false;
440 }
441
442 if (system_wide_collection_ && !IsRoot()) {
443 LOG(ERROR) << "System wide profiling needs root privilege.";
444 return false;
445 }
446 return true;
447 }
448
AdjustPerfEventLimit()449 bool MonitorCommand::AdjustPerfEventLimit() {
450 bool set_prop = false;
451 // 1. Adjust max_sample_rate.
452 uint64_t cur_max_freq;
453 if (GetMaxSampleFrequency(&cur_max_freq) && cur_max_freq < max_sample_freq_ &&
454 !SetMaxSampleFrequency(max_sample_freq_)) {
455 set_prop = true;
456 }
457 // 2. Adjust perf_cpu_time_max_percent.
458 size_t cur_percent;
459 if (GetCpuTimeMaxPercent(&cur_percent) && cur_percent != cpu_time_max_percent_ &&
460 !SetCpuTimeMaxPercent(cpu_time_max_percent_)) {
461 set_prop = true;
462 }
463 // 3. Adjust perf_event_mlock_kb.
464 long cpus = sysconf(_SC_NPROCESSORS_CONF);
465 uint64_t mlock_kb = cpus * (mmap_page_range_.second + 1) * 4;
466
467 uint64_t cur_mlock_kb;
468 if (GetPerfEventMlockKb(&cur_mlock_kb) && cur_mlock_kb < mlock_kb &&
469 !SetPerfEventMlockKb(mlock_kb)) {
470 set_prop = true;
471 }
472
473 if (GetAndroidVersion() >= kAndroidVersionQ && set_prop) {
474 return SetPerfEventLimits(std::max(max_sample_freq_, cur_max_freq), cpu_time_max_percent_,
475 std::max(mlock_kb, cur_mlock_kb));
476 }
477 return true;
478 }
479
SetEventSelectionFlags()480 bool MonitorCommand::SetEventSelectionFlags() {
481 event_selection_set_.SampleIdAll();
482 event_selection_set_.WakeupPerSample();
483 if (fp_callchain_sampling_) {
484 event_selection_set_.EnableFpCallChainSampling();
485 } else if (dwarf_callchain_sampling_) {
486 if (!event_selection_set_.EnableDwarfCallChainSampling(dump_stack_size_in_dwarf_sampling_)) {
487 return false;
488 }
489 }
490 return true;
491 }
492
ProcessRecord(Record * record)493 bool MonitorCommand::ProcessRecord(Record* record) {
494 UpdateRecord(record);
495 last_record_timestamp_ = std::max(last_record_timestamp_, record->Timestamp());
496 // In system wide recording, maps are dumped when they are needed by records.
497 if (system_wide_collection_ && !DumpMapsForRecord(record)) {
498 return false;
499 }
500 if (record->type() == PERF_RECORD_SAMPLE) {
501 auto& r = *static_cast<SampleRecord*>(record);
502
503 // Record filter check should go after DumpMapsForRecord(). Otherwise, process/thread name
504 // filters don't work in system wide collection.
505 if (!record_filter_.Check(&r)) {
506 return true;
507 }
508
509 // AdjustCallChainGeneratedByKernel() should go before UnwindRecord().
510 // Because we don't want to adjust callchains generated by dwarf unwinder.
511 if (fp_callchain_sampling_ || dwarf_callchain_sampling_) {
512 r.AdjustCallChainGeneratedByKernel();
513 if (!UnwindRecord(r)) {
514 return false;
515 }
516 }
517 DumpSampleRecord(r);
518 if (fp_callchain_sampling_ || dwarf_callchain_sampling_) {
519 DumpSampleCallchain(r);
520 }
521 sample_record_count_++;
522 } else {
523 // Other types of record are forwarded to the thread tree to build the
524 // representation of each processes (mmap, comm, etc).
525 thread_tree_.Update(*record);
526 }
527 return true;
528 }
529
DumpSampleRecord(const SampleRecord & sr)530 void MonitorCommand::DumpSampleRecord(const SampleRecord& sr) {
531 std::string output("sample");
532 StringAppendF(&output, " name=%s", event_names_[sr.id_data.id].c_str());
533 StringAppendF(&output, " ip=%p", reinterpret_cast<void*>(sr.ip_data.ip));
534 SymbolInfo s = GetSymbolInfo(sr.tid_data.pid, sr.tid_data.tid, sr.ip_data.ip, sr.InKernel());
535 StringAppendF(&output, " symbol=%s (%s[+%" PRIx64 "])", s.symbol->DemangledName(),
536 s.dso->Path().c_str(), s.vaddr_in_file);
537 StringAppendF(&output, " pid=%u tid=%u", sr.tid_data.pid, sr.tid_data.tid);
538 StringAppendF(&output, " cpu=%u", sr.cpu_data.cpu);
539 printf("%s\n", output.c_str());
540 fflush(stdout);
541 }
542
DumpSampleCallchain(const SampleRecord & sr)543 void MonitorCommand::DumpSampleCallchain(const SampleRecord& sr) {
544 bool in_kernel = sr.InKernel();
545 if (sr.sample_type & PERF_SAMPLE_CALLCHAIN) {
546 for (size_t i = 0; i < sr.callchain_data.ip_nr; ++i) {
547 if (sr.callchain_data.ips[i] >= PERF_CONTEXT_MAX) {
548 if (sr.callchain_data.ips[i] == PERF_CONTEXT_USER) {
549 in_kernel = false;
550 }
551 continue;
552 }
553 SymbolInfo s =
554 GetSymbolInfo(sr.tid_data.pid, sr.tid_data.tid, sr.callchain_data.ips[i], in_kernel);
555 std::string output("sample callchain");
556 StringAppendF(&output, " %s (%s[+%" PRIx64 "])", s.symbol->DemangledName(),
557 s.dso->Path().c_str(), s.vaddr_in_file);
558 printf("%s\n", output.c_str());
559 }
560 fflush(stdout);
561 }
562 }
563
GetSymbolInfo(uint32_t pid,uint32_t tid,uint64_t ip,bool in_kernel)564 SymbolInfo MonitorCommand::GetSymbolInfo(uint32_t pid, uint32_t tid, uint64_t ip, bool in_kernel) {
565 ThreadEntry* thread = thread_tree_.FindThreadOrNew(pid, tid);
566 const MapEntry* map = thread_tree_.FindMap(thread, ip, in_kernel);
567 SymbolInfo info;
568 info.symbol = thread_tree_.FindSymbol(map, ip, &info.vaddr_in_file, &info.dso);
569 return info;
570 }
571
DumpMapsForRecord(Record * record)572 bool MonitorCommand::DumpMapsForRecord(Record* record) {
573 if (record->type() == PERF_RECORD_SAMPLE) {
574 pid_t pid = static_cast<SampleRecord*>(record)->tid_data.pid;
575 if (dumped_processes_.find(pid) == dumped_processes_.end()) {
576 // Dump map info and all thread names for that process.
577 if (!map_record_reader_->ReadProcessMaps(pid, last_record_timestamp_)) {
578 return false;
579 }
580 dumped_processes_.insert(pid);
581 }
582 }
583 return true;
584 }
585
UpdateRecord(Record * record)586 void MonitorCommand::UpdateRecord(Record* record) {
587 if (record->type() == PERF_RECORD_COMM) {
588 auto r = static_cast<CommRecord*>(record);
589 if (r->data->pid == r->data->tid) {
590 std::string s = GetCompleteProcessName(r->data->pid);
591 if (!s.empty()) {
592 r->SetCommandName(s);
593 }
594 }
595 }
596 }
597
UnwindRecord(SampleRecord & r)598 bool MonitorCommand::UnwindRecord(SampleRecord& r) {
599 if ((r.sample_type & PERF_SAMPLE_CALLCHAIN) && (r.sample_type & PERF_SAMPLE_REGS_USER) &&
600 (r.regs_user_data.reg_mask != 0) && (r.sample_type & PERF_SAMPLE_STACK_USER) &&
601 (r.GetValidStackSize() > 0)) {
602 ThreadEntry* thread = thread_tree_.FindThreadOrNew(r.tid_data.pid, r.tid_data.tid);
603 RegSet regs(r.regs_user_data.abi, r.regs_user_data.reg_mask, r.regs_user_data.regs);
604 std::vector<uint64_t> ips;
605 std::vector<uint64_t> sps;
606 if (!offline_unwinder_->UnwindCallChain(*thread, regs, r.stack_user_data.data,
607 r.GetValidStackSize(), &ips, &sps)) {
608 return false;
609 }
610 r.ReplaceRegAndStackWithCallChain(ips);
611 }
612 return true;
613 }
614 } // namespace
615
RegisterMonitorCommand()616 void RegisterMonitorCommand() {
617 RegisterCommand("monitor", [] { return std::unique_ptr<Command>(new MonitorCommand()); });
618 }
619
620 } // namespace simpleperf
621