xref: /aosp_15_r20/system/extras/simpleperf/cmd_monitor.cpp (revision 288bf5226967eb3dac5cce6c939ccc2a7f2b4fe5)
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