You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
621 lines
22 KiB
621 lines
22 KiB
/*
|
|
* Copyright (C) 2020 The Android Open Source Project
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
#include <inttypes.h>
|
|
#include <libgen.h>
|
|
#include <signal.h>
|
|
#include <sys/mman.h>
|
|
#include <sys/prctl.h>
|
|
#include <sys/utsname.h>
|
|
#include <time.h>
|
|
#include <unistd.h>
|
|
#include <optional>
|
|
#include <set>
|
|
#include <string>
|
|
#include <unordered_map>
|
|
#include <unordered_set>
|
|
#include <vector>
|
|
|
|
#include <android-base/file.h>
|
|
#include <android-base/logging.h>
|
|
#include <android-base/parseint.h>
|
|
#include <android-base/stringprintf.h>
|
|
#include <android-base/strings.h>
|
|
#include <android-base/unique_fd.h>
|
|
#if defined(__ANDROID__)
|
|
#include <android-base/properties.h>
|
|
#endif
|
|
|
|
#include "IOEventLoop.h"
|
|
#include "MapRecordReader.h"
|
|
#include "OfflineUnwinder.h"
|
|
#include "RecordFilter.h"
|
|
#include "command.h"
|
|
#include "dso.h"
|
|
#include "environment.h"
|
|
#include "event_selection_set.h"
|
|
#include "event_type.h"
|
|
#include "read_elf.h"
|
|
#include "read_symbol_map.h"
|
|
#include "record.h"
|
|
#include "thread_tree.h"
|
|
#include "tracing.h"
|
|
#include "utils.h"
|
|
|
|
namespace simpleperf {
|
|
namespace {
|
|
|
|
using android::base::ParseUint;
|
|
using android::base::Realpath;
|
|
using android::base::StringAppendF;
|
|
|
|
struct SymbolInfo {
|
|
Dso* dso;
|
|
const Symbol* symbol;
|
|
uint64_t vaddr_in_file;
|
|
};
|
|
|
|
// The max size of records dumped by kernel is 65535, and dump stack size
|
|
// should be a multiply of 8, so MAX_DUMP_STACK_SIZE is 65528.
|
|
constexpr uint32_t MAX_DUMP_STACK_SIZE = 65528;
|
|
|
|
// The max allowed pages in mapped buffer is decided by rlimit(RLIMIT_MEMLOCK).
|
|
// Here 1024 is a desired value for pages in mapped buffer. If mapped
|
|
// successfully, the buffer size = 1024 * 4K (page size) = 4M.
|
|
constexpr size_t DESIRED_PAGES_IN_MAPPED_BUFFER = 1024;
|
|
|
|
// Currently, the record buffer size in user-space is set to match the kernel
|
|
// buffer size on a 8 core system. For system-wide recording, it is 8K pages *
|
|
// 4K page_size * 8 cores = 256MB. For non system-wide recording, it is 1K pages
|
|
// * 4K page_size * 8 cores = 64MB.
|
|
static constexpr size_t kRecordBufferSize = 64 * 1024 * 1024;
|
|
static constexpr size_t kSystemWideRecordBufferSize = 256 * 1024 * 1024;
|
|
|
|
class MonitorCommand : public Command {
|
|
public:
|
|
MonitorCommand()
|
|
: Command("monitor", "monitor events and print their textual representations to stdout",
|
|
// clang-format off
|
|
"Usage: simpleperf monitor [options]\n"
|
|
" Gather sampling information and print the events on stdout.\n"
|
|
" For precise recording, prefer the record command.\n"
|
|
" Currently, only supports system-wide collection.\n"
|
|
"\n"
|
|
"Select monitored threads:\n"
|
|
"-a System-wide collection. Use with --exclude-perf to exclude\n"
|
|
" samples for simpleperf process.\n"
|
|
"\n"
|
|
"Select monitored event types:\n"
|
|
"-e event1[:modifier1],event2[:modifier2],...\n"
|
|
" Select a list of events to record. An event can be:\n"
|
|
" 1) an event name listed in `simpleperf list`;\n"
|
|
" 2) a raw PMU event in rN format. N is a hex number.\n"
|
|
" For example, r1b selects event number 0x1b.\n"
|
|
" Modifiers can be added to define how the event should be\n"
|
|
" monitored. Possible modifiers are:\n"
|
|
" u - monitor user space events only\n"
|
|
" k - monitor kernel space events only\n"
|
|
"\n"
|
|
"Select monitoring options:\n"
|
|
"-f freq Set event sample frequency. It means recording at most [freq]\n"
|
|
" samples every second. For non-tracepoint events, the default\n"
|
|
" option is -f 4000. A -f/-c option affects all event types\n"
|
|
" following it until meeting another -f/-c option. For example,\n"
|
|
" for \"-f 1000 cpu-cycles -c 1 -e sched:sched_switch\", cpu-cycles\n"
|
|
" has sample freq 1000, sched:sched_switch event has sample period 1.\n"
|
|
"-c count Set event sample period. It means recording one sample when\n"
|
|
" [count] events happen. For tracepoint events, the default option\n"
|
|
" is -c 1.\n"
|
|
"--call-graph fp | dwarf[,<dump_stack_size>]\n"
|
|
" Enable call graph recording. Use frame pointer or dwarf debug\n"
|
|
" frame as the method to parse call graph in stack.\n"
|
|
" Default is dwarf,65528.\n"
|
|
"-g Same as '--call-graph dwarf'.\n"
|
|
"--duration time_in_sec Monitor for time_in_sec seconds. Here time_in_sec"
|
|
" may be any positive floating point number.\n"
|
|
"--cpu-percent <percent> Set the max percent of cpu time used for recording.\n"
|
|
" percent is in range [1-100], default is 25.\n"
|
|
"\n"
|
|
"Sample filter options:\n"
|
|
"--exclude-perf Exclude samples for simpleperf process.\n"
|
|
RECORD_FILTER_OPTION_HELP_MSG
|
|
"\n"
|
|
// clang-format on
|
|
),
|
|
system_wide_collection_(false),
|
|
fp_callchain_sampling_(false),
|
|
dwarf_callchain_sampling_(false),
|
|
dump_stack_size_in_dwarf_sampling_(MAX_DUMP_STACK_SIZE),
|
|
unwind_dwarf_callchain_(true),
|
|
duration_in_sec_(0),
|
|
event_selection_set_(false),
|
|
mmap_page_range_(std::make_pair(1, DESIRED_PAGES_IN_MAPPED_BUFFER)),
|
|
sample_record_count_(0),
|
|
last_record_timestamp_(0u),
|
|
record_filter_(thread_tree_) {
|
|
// If we run `adb shell simpleperf record xxx` and stop profiling by ctrl-c,
|
|
// adb closes sockets connecting simpleperf. After that, simpleperf will
|
|
// receive SIGPIPE when writing to stdout/stderr, which is a problem when we
|
|
// use '--app' option. So ignore SIGPIPE to finish properly.
|
|
signal(SIGPIPE, SIG_IGN);
|
|
}
|
|
|
|
bool Run(const std::vector<std::string>& args);
|
|
|
|
private:
|
|
bool ParseOptions(const std::vector<std::string>& args);
|
|
bool AdjustPerfEventLimit();
|
|
bool PrepareMonitoring();
|
|
bool DoMonitoring();
|
|
bool SetEventSelectionFlags();
|
|
bool DumpProcessMaps(pid_t pid, const std::unordered_set<pid_t>& tids);
|
|
void DumpSampleRecord(const SampleRecord& sr);
|
|
void DumpSampleCallchain(const SampleRecord& sr);
|
|
bool ProcessRecord(Record* record);
|
|
SymbolInfo GetSymbolInfo(uint32_t pid, uint32_t tid, uint64_t ip, bool in_kernel);
|
|
bool DumpMapsForRecord(Record* record);
|
|
void UpdateRecord(Record* record);
|
|
bool UnwindRecord(SampleRecord& r);
|
|
|
|
uint64_t max_sample_freq_ = DEFAULT_SAMPLE_FREQ_FOR_NONTRACEPOINT_EVENT;
|
|
size_t cpu_time_max_percent_ = 25;
|
|
|
|
std::unique_ptr<SampleSpeed> sample_speed_;
|
|
bool system_wide_collection_;
|
|
bool fp_callchain_sampling_;
|
|
bool dwarf_callchain_sampling_;
|
|
uint32_t dump_stack_size_in_dwarf_sampling_;
|
|
bool unwind_dwarf_callchain_;
|
|
std::unique_ptr<OfflineUnwinder> offline_unwinder_;
|
|
double duration_in_sec_;
|
|
EventSelectionSet event_selection_set_;
|
|
std::pair<size_t, size_t> mmap_page_range_;
|
|
ThreadTree thread_tree_;
|
|
uint64_t sample_record_count_;
|
|
uint64_t last_record_timestamp_; // used to insert Mmap2Records for JIT debug info
|
|
// In system wide recording, record if we have dumped map info for a process.
|
|
std::unordered_set<pid_t> dumped_processes_;
|
|
bool exclude_perf_ = false;
|
|
RecordFilter record_filter_;
|
|
std::unordered_map<uint64_t, std::string> event_names_;
|
|
|
|
std::optional<MapRecordReader> map_record_reader_;
|
|
};
|
|
|
|
bool MonitorCommand::Run(const std::vector<std::string>& args) {
|
|
ScopedCurrentArch scoped_arch(GetMachineArch());
|
|
if (!CheckPerfEventLimit()) {
|
|
return false;
|
|
}
|
|
AllowMoreOpenedFiles();
|
|
|
|
if (!ParseOptions(args)) {
|
|
return false;
|
|
}
|
|
if (!AdjustPerfEventLimit()) {
|
|
return false;
|
|
}
|
|
|
|
if (!PrepareMonitoring()) {
|
|
return false;
|
|
}
|
|
return DoMonitoring();
|
|
}
|
|
|
|
bool MonitorCommand::PrepareMonitoring() {
|
|
// 1. Process options before opening perf event files.
|
|
if (!SetEventSelectionFlags()) {
|
|
return false;
|
|
}
|
|
if (unwind_dwarf_callchain_) {
|
|
offline_unwinder_ = OfflineUnwinder::Create(false);
|
|
}
|
|
|
|
// 2. Add monitored targets.
|
|
if (system_wide_collection_) {
|
|
event_selection_set_.AddMonitoredThreads({-1});
|
|
} else {
|
|
LOG(ERROR) << "No threads to monitor. Try `simpleperf help monitor` for help";
|
|
return false;
|
|
}
|
|
|
|
// 3. Open perf event files and create mapped buffers.
|
|
if (!event_selection_set_.OpenEventFiles({})) {
|
|
return false;
|
|
}
|
|
size_t record_buffer_size =
|
|
system_wide_collection_ ? kSystemWideRecordBufferSize : kRecordBufferSize;
|
|
if (!event_selection_set_.MmapEventFiles(mmap_page_range_.first, mmap_page_range_.second,
|
|
0 /* aux_buffer_size */, record_buffer_size,
|
|
false /* allow_cutting_samples */, exclude_perf_)) {
|
|
return false;
|
|
}
|
|
auto callback = std::bind(&MonitorCommand::ProcessRecord, this, std::placeholders::_1);
|
|
if (!event_selection_set_.PrepareToReadMmapEventData(callback)) {
|
|
return false;
|
|
}
|
|
|
|
// Keep track of the event names per id.
|
|
event_names_ = event_selection_set_.GetEventNamesById();
|
|
|
|
// Use first perf_event_attr and first event id to dump mmap and comm records.
|
|
EventAttrWithId dumping_attr_id = event_selection_set_.GetEventAttrWithId()[0];
|
|
map_record_reader_.emplace(*dumping_attr_id.attr, dumping_attr_id.ids[0],
|
|
event_selection_set_.RecordNotExecutableMaps());
|
|
map_record_reader_->SetCallback([this](Record* r) { return ProcessRecord(r); });
|
|
|
|
// 4. Load kallsyms, if possible.
|
|
std::string kallsyms;
|
|
if (LoadKernelSymbols(&kallsyms)) {
|
|
Dso::SetKallsyms(std::move(kallsyms));
|
|
}
|
|
map_record_reader_->ReadKernelMaps();
|
|
|
|
// 5. Add read/signal/periodic Events.
|
|
IOEventLoop* loop = event_selection_set_.GetIOEventLoop();
|
|
auto exit_loop_callback = [loop]() { return loop->ExitLoop(); };
|
|
if (!loop->AddSignalEvents({SIGCHLD, SIGINT, SIGTERM}, exit_loop_callback)) {
|
|
return false;
|
|
}
|
|
|
|
// Only add an event for SIGHUP if we didn't inherit SIG_IGN (e.g. from
|
|
// nohup).
|
|
if (!SignalIsIgnored(SIGHUP)) {
|
|
if (!loop->AddSignalEvent(SIGHUP, exit_loop_callback)) {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
if (duration_in_sec_ != 0) {
|
|
if (!loop->AddPeriodicEvent(SecondToTimeval(duration_in_sec_),
|
|
[loop]() { return loop->ExitLoop(); })) {
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool MonitorCommand::DoMonitoring() {
|
|
if (!event_selection_set_.GetIOEventLoop()->RunLoop()) {
|
|
return false;
|
|
}
|
|
if (!event_selection_set_.FinishReadMmapEventData()) {
|
|
return false;
|
|
}
|
|
LOG(ERROR) << "Processed samples: " << sample_record_count_;
|
|
return true;
|
|
}
|
|
|
|
inline const OptionFormatMap& GetMonitorCmdOptionFormats() {
|
|
static OptionFormatMap option_formats;
|
|
if (option_formats.empty()) {
|
|
option_formats = {
|
|
{"-a", {OptionValueType::NONE, OptionType::SINGLE, AppRunnerType::NOT_ALLOWED}},
|
|
{"-c", {OptionValueType::UINT, OptionType::ORDERED, AppRunnerType::ALLOWED}},
|
|
{"--call-graph", {OptionValueType::STRING, OptionType::ORDERED, AppRunnerType::ALLOWED}},
|
|
{"--cpu-percent", {OptionValueType::UINT, OptionType::SINGLE, AppRunnerType::ALLOWED}},
|
|
{"--duration", {OptionValueType::DOUBLE, OptionType::SINGLE, AppRunnerType::ALLOWED}},
|
|
{"-e", {OptionValueType::STRING, OptionType::ORDERED, AppRunnerType::ALLOWED}},
|
|
{"--exclude-perf", {OptionValueType::NONE, OptionType::SINGLE, AppRunnerType::ALLOWED}},
|
|
{"-f", {OptionValueType::UINT, OptionType::ORDERED, AppRunnerType::ALLOWED}},
|
|
{"-g", {OptionValueType::NONE, OptionType::ORDERED, AppRunnerType::ALLOWED}},
|
|
{"-t", {OptionValueType::STRING, OptionType::MULTIPLE, AppRunnerType::ALLOWED}},
|
|
};
|
|
const OptionFormatMap& record_filter_options = GetRecordFilterOptionFormats();
|
|
option_formats.insert(record_filter_options.begin(), record_filter_options.end());
|
|
}
|
|
return option_formats;
|
|
}
|
|
|
|
bool MonitorCommand::ParseOptions(const std::vector<std::string>& args) {
|
|
OptionValueMap options;
|
|
std::vector<std::pair<OptionName, OptionValue>> ordered_options;
|
|
|
|
if (!PreprocessOptions(args, GetMonitorCmdOptionFormats(), &options, &ordered_options, nullptr)) {
|
|
return false;
|
|
}
|
|
|
|
// Process options.
|
|
system_wide_collection_ = options.PullBoolValue("-a");
|
|
|
|
if (!options.PullUintValue("--cpu-percent", &cpu_time_max_percent_, 1, 100)) {
|
|
return false;
|
|
}
|
|
|
|
if (!options.PullDoubleValue("--duration", &duration_in_sec_, 1e-9)) {
|
|
return false;
|
|
}
|
|
|
|
exclude_perf_ = options.PullBoolValue("--exclude-perf");
|
|
if (!record_filter_.ParseOptions(options)) {
|
|
return false;
|
|
}
|
|
|
|
CHECK(options.values.empty());
|
|
|
|
// Process ordered options.
|
|
std::vector<size_t> wait_setting_speed_event_groups;
|
|
|
|
for (const auto& pair : ordered_options) {
|
|
const OptionName& name = pair.first;
|
|
const OptionValue& value = pair.second;
|
|
|
|
if (name == "-c" || name == "-f") {
|
|
if (value.uint_value < 1) {
|
|
LOG(ERROR) << "invalid " << name << ": " << value.uint_value;
|
|
return false;
|
|
}
|
|
if (name == "-c") {
|
|
sample_speed_.reset(new SampleSpeed(0, value.uint_value));
|
|
} else {
|
|
if (value.uint_value >= INT_MAX) {
|
|
LOG(ERROR) << "sample freq can't be bigger than INT_MAX: " << value.uint_value;
|
|
return false;
|
|
}
|
|
sample_speed_.reset(new SampleSpeed(value.uint_value, 0));
|
|
}
|
|
|
|
for (auto groud_id : wait_setting_speed_event_groups) {
|
|
event_selection_set_.SetSampleSpeed(groud_id, *sample_speed_);
|
|
}
|
|
wait_setting_speed_event_groups.clear();
|
|
|
|
} else if (name == "--call-graph") {
|
|
std::vector<std::string> strs = android::base::Split(*value.str_value, ",");
|
|
if (strs[0] == "fp") {
|
|
fp_callchain_sampling_ = true;
|
|
dwarf_callchain_sampling_ = false;
|
|
} else if (strs[0] == "dwarf") {
|
|
fp_callchain_sampling_ = false;
|
|
dwarf_callchain_sampling_ = true;
|
|
if (strs.size() > 1) {
|
|
uint64_t size;
|
|
if (!ParseUint(strs[1], &size)) {
|
|
LOG(ERROR) << "invalid dump stack size in --call-graph option: " << strs[1];
|
|
return false;
|
|
}
|
|
if ((size & 7) != 0) {
|
|
LOG(ERROR) << "dump stack size " << size << " is not 8-byte aligned.";
|
|
return false;
|
|
}
|
|
if (size >= MAX_DUMP_STACK_SIZE) {
|
|
LOG(ERROR) << "dump stack size " << size << " is bigger than max allowed size "
|
|
<< MAX_DUMP_STACK_SIZE << ".";
|
|
return false;
|
|
}
|
|
dump_stack_size_in_dwarf_sampling_ = static_cast<uint32_t>(size);
|
|
}
|
|
}
|
|
|
|
} else if (name == "-e") {
|
|
std::vector<std::string> event_types = android::base::Split(*value.str_value, ",");
|
|
for (auto& event_type : event_types) {
|
|
size_t group_id;
|
|
if (!event_selection_set_.AddEventType(event_type, &group_id)) {
|
|
return false;
|
|
}
|
|
if (sample_speed_) {
|
|
event_selection_set_.SetSampleSpeed(group_id, *sample_speed_);
|
|
} else {
|
|
wait_setting_speed_event_groups.push_back(group_id);
|
|
}
|
|
}
|
|
|
|
} else if (name == "-g") {
|
|
fp_callchain_sampling_ = false;
|
|
dwarf_callchain_sampling_ = true;
|
|
} else {
|
|
CHECK(false) << "unprocessed option: " << name;
|
|
}
|
|
}
|
|
|
|
if (event_selection_set_.empty()) {
|
|
LOG(ERROR) << "No event to record. Use `-e` to specify which event should be monitored.";
|
|
return false;
|
|
}
|
|
|
|
if (fp_callchain_sampling_) {
|
|
if (GetBuildArch() == ARCH_ARM) {
|
|
LOG(WARNING) << "`--callgraph fp` option doesn't work well on arm architecture, "
|
|
<< "consider using `-g` option or profiling on aarch64 architecture.";
|
|
}
|
|
}
|
|
|
|
if (system_wide_collection_ && event_selection_set_.HasMonitoredTarget()) {
|
|
LOG(ERROR) << "Record system wide and existing processes/threads can't be "
|
|
"used at the same time.";
|
|
return false;
|
|
}
|
|
|
|
if (system_wide_collection_ && !IsRoot()) {
|
|
LOG(ERROR) << "System wide profiling needs root privilege.";
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool MonitorCommand::AdjustPerfEventLimit() {
|
|
bool set_prop = false;
|
|
// 1. Adjust max_sample_rate.
|
|
uint64_t cur_max_freq;
|
|
if (GetMaxSampleFrequency(&cur_max_freq) && cur_max_freq < max_sample_freq_ &&
|
|
!SetMaxSampleFrequency(max_sample_freq_)) {
|
|
set_prop = true;
|
|
}
|
|
// 2. Adjust perf_cpu_time_max_percent.
|
|
size_t cur_percent;
|
|
if (GetCpuTimeMaxPercent(&cur_percent) && cur_percent != cpu_time_max_percent_ &&
|
|
!SetCpuTimeMaxPercent(cpu_time_max_percent_)) {
|
|
set_prop = true;
|
|
}
|
|
// 3. Adjust perf_event_mlock_kb.
|
|
long cpus = sysconf(_SC_NPROCESSORS_CONF);
|
|
uint64_t mlock_kb = cpus * (mmap_page_range_.second + 1) * 4;
|
|
|
|
uint64_t cur_mlock_kb;
|
|
if (GetPerfEventMlockKb(&cur_mlock_kb) && cur_mlock_kb < mlock_kb &&
|
|
!SetPerfEventMlockKb(mlock_kb)) {
|
|
set_prop = true;
|
|
}
|
|
|
|
if (GetAndroidVersion() >= kAndroidVersionQ && set_prop) {
|
|
return SetPerfEventLimits(std::max(max_sample_freq_, cur_max_freq), cpu_time_max_percent_,
|
|
std::max(mlock_kb, cur_mlock_kb));
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool MonitorCommand::SetEventSelectionFlags() {
|
|
event_selection_set_.SampleIdAll();
|
|
event_selection_set_.WakeupPerSample();
|
|
if (fp_callchain_sampling_) {
|
|
event_selection_set_.EnableFpCallChainSampling();
|
|
} else if (dwarf_callchain_sampling_) {
|
|
if (!event_selection_set_.EnableDwarfCallChainSampling(dump_stack_size_in_dwarf_sampling_)) {
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool MonitorCommand::ProcessRecord(Record* record) {
|
|
UpdateRecord(record);
|
|
last_record_timestamp_ = std::max(last_record_timestamp_, record->Timestamp());
|
|
// In system wide recording, maps are dumped when they are needed by records.
|
|
if (system_wide_collection_ && !DumpMapsForRecord(record)) {
|
|
return false;
|
|
}
|
|
if (record->type() == PERF_RECORD_SAMPLE) {
|
|
auto& r = *static_cast<SampleRecord*>(record);
|
|
|
|
// Record filter check should go after DumpMapsForRecord(). Otherwise, process/thread name
|
|
// filters don't work in system wide collection.
|
|
if (!record_filter_.Check(&r)) {
|
|
return true;
|
|
}
|
|
|
|
// AdjustCallChainGeneratedByKernel() should go before UnwindRecord().
|
|
// Because we don't want to adjust callchains generated by dwarf unwinder.
|
|
if (fp_callchain_sampling_ || dwarf_callchain_sampling_) {
|
|
r.AdjustCallChainGeneratedByKernel();
|
|
if (!UnwindRecord(r)) {
|
|
return false;
|
|
}
|
|
}
|
|
DumpSampleRecord(r);
|
|
if (fp_callchain_sampling_ || dwarf_callchain_sampling_) {
|
|
DumpSampleCallchain(r);
|
|
}
|
|
sample_record_count_++;
|
|
} else {
|
|
// Other types of record are forwarded to the thread tree to build the
|
|
// representation of each processes (mmap, comm, etc).
|
|
thread_tree_.Update(*record);
|
|
}
|
|
return true;
|
|
}
|
|
|
|
void MonitorCommand::DumpSampleRecord(const SampleRecord& sr) {
|
|
std::string output("sample");
|
|
StringAppendF(&output, " name=%s", event_names_[sr.id_data.id].c_str());
|
|
StringAppendF(&output, " ip=%p", reinterpret_cast<void*>(sr.ip_data.ip));
|
|
SymbolInfo s = GetSymbolInfo(sr.tid_data.pid, sr.tid_data.tid, sr.ip_data.ip, sr.InKernel());
|
|
StringAppendF(&output, " symbol=%s (%s[+%" PRIx64 "])", s.symbol->DemangledName(),
|
|
s.dso->Path().c_str(), s.vaddr_in_file);
|
|
StringAppendF(&output, " pid=%u tid=%u", sr.tid_data.pid, sr.tid_data.tid);
|
|
StringAppendF(&output, " cpu=%u", sr.cpu_data.cpu);
|
|
printf("%s\n", output.c_str());
|
|
fflush(stdout);
|
|
}
|
|
|
|
void MonitorCommand::DumpSampleCallchain(const SampleRecord& sr) {
|
|
bool in_kernel = sr.InKernel();
|
|
if (sr.sample_type & PERF_SAMPLE_CALLCHAIN) {
|
|
for (size_t i = 0; i < sr.callchain_data.ip_nr; ++i) {
|
|
if (sr.callchain_data.ips[i] >= PERF_CONTEXT_MAX) {
|
|
if (sr.callchain_data.ips[i] == PERF_CONTEXT_USER) {
|
|
in_kernel = false;
|
|
}
|
|
continue;
|
|
}
|
|
SymbolInfo s =
|
|
GetSymbolInfo(sr.tid_data.pid, sr.tid_data.tid, sr.callchain_data.ips[i], in_kernel);
|
|
std::string output("sample callchain");
|
|
StringAppendF(&output, " %s (%s[+%" PRIx64 "])", s.symbol->DemangledName(),
|
|
s.dso->Path().c_str(), s.vaddr_in_file);
|
|
printf("%s\n", output.c_str());
|
|
}
|
|
fflush(stdout);
|
|
}
|
|
}
|
|
|
|
SymbolInfo MonitorCommand::GetSymbolInfo(uint32_t pid, uint32_t tid, uint64_t ip, bool in_kernel) {
|
|
ThreadEntry* thread = thread_tree_.FindThreadOrNew(pid, tid);
|
|
const MapEntry* map = thread_tree_.FindMap(thread, ip, in_kernel);
|
|
SymbolInfo info;
|
|
info.symbol = thread_tree_.FindSymbol(map, ip, &info.vaddr_in_file, &info.dso);
|
|
return info;
|
|
}
|
|
|
|
bool MonitorCommand::DumpMapsForRecord(Record* record) {
|
|
if (record->type() == PERF_RECORD_SAMPLE) {
|
|
pid_t pid = static_cast<SampleRecord*>(record)->tid_data.pid;
|
|
if (dumped_processes_.find(pid) == dumped_processes_.end()) {
|
|
// Dump map info and all thread names for that process.
|
|
if (!map_record_reader_->ReadProcessMaps(pid, last_record_timestamp_)) {
|
|
return false;
|
|
}
|
|
dumped_processes_.insert(pid);
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
void MonitorCommand::UpdateRecord(Record* record) {
|
|
if (record->type() == PERF_RECORD_COMM) {
|
|
auto r = static_cast<CommRecord*>(record);
|
|
if (r->data->pid == r->data->tid) {
|
|
std::string s = GetCompleteProcessName(r->data->pid);
|
|
if (!s.empty()) {
|
|
r->SetCommandName(s);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
bool MonitorCommand::UnwindRecord(SampleRecord& r) {
|
|
if ((r.sample_type & PERF_SAMPLE_CALLCHAIN) && (r.sample_type & PERF_SAMPLE_REGS_USER) &&
|
|
(r.regs_user_data.reg_mask != 0) && (r.sample_type & PERF_SAMPLE_STACK_USER) &&
|
|
(r.GetValidStackSize() > 0)) {
|
|
ThreadEntry* thread = thread_tree_.FindThreadOrNew(r.tid_data.pid, r.tid_data.tid);
|
|
RegSet regs(r.regs_user_data.abi, r.regs_user_data.reg_mask, r.regs_user_data.regs);
|
|
std::vector<uint64_t> ips;
|
|
std::vector<uint64_t> sps;
|
|
if (!offline_unwinder_->UnwindCallChain(*thread, regs, r.stack_user_data.data,
|
|
r.GetValidStackSize(), &ips, &sps)) {
|
|
return false;
|
|
}
|
|
r.ReplaceRegAndStackWithCallChain(ips);
|
|
}
|
|
return true;
|
|
}
|
|
} // namespace
|
|
|
|
void RegisterMonitorCommand() {
|
|
RegisterCommand("monitor", [] { return std::unique_ptr<Command>(new MonitorCommand()); });
|
|
}
|
|
|
|
} // namespace simpleperf
|