From b032de7a9e1975a838cd6df2e7c8df3c7f70a3ce Mon Sep 17 00:00:00 2001 From: Yabin Cui Date: Wed, 17 Jun 2015 21:15:09 -0700 Subject: [PATCH] Simpleperf: support monitoring existing processes/threads. Also change the default record freq from 1000 to 4000. Because 1000 seems to be too low. Bug: 19483574 Change-Id: I340fcb9d28a156862705e483ee340a1c824eea21 --- simpleperf/cmd_record.cpp | 110 ++++++++++++++++++++++++------------- simpleperf/cmd_record_test.cpp | 24 ++++++++ simpleperf/cmd_stat.cpp | 59 +++++++++++++++----- simpleperf/cmd_stat_test.cpp | 24 ++++++++ simpleperf/environment.cpp | 51 ++++++++++++++++- simpleperf/environment.h | 4 ++ simpleperf/event_fd.cpp | 24 +++----- simpleperf/event_fd.h | 14 ++--- simpleperf/event_selection_set.cpp | 29 +++++++--- simpleperf/event_selection_set.h | 5 +- simpleperf/event_type.cpp | 2 +- simpleperf/record_file_test.cpp | 2 +- simpleperf/sample_tree_test.cpp | 4 +- simpleperf/test_util.h | 27 +++++++++ simpleperf/utils.cpp | 10 ++++ simpleperf/utils.h | 1 + simpleperf/workload_test.cpp | 7 ++- 17 files changed, 297 insertions(+), 100 deletions(-) create mode 100644 simpleperf/test_util.h diff --git a/simpleperf/cmd_record.cpp b/simpleperf/cmd_record.cpp index 2ceeed02..e87a7ec1 100644 --- a/simpleperf/cmd_record.cpp +++ b/simpleperf/cmd_record.cpp @@ -17,6 +17,7 @@ #include #include #include +#include #include #include #include @@ -53,33 +54,37 @@ static void signal_handler(int) { class RecordCommand : public Command { public: RecordCommand() - : Command("record", "record sampling info in perf.data", - "Usage: simpleperf record [options] [command [command-args]]\n" - " Gather sampling information when running [command]. If [command]\n" - " is not specified, sleep 1 is used instead.\n" - " -a System-wide collection.\n" - " -b Enable take branch stack sampling. Same as '-j any'\n" - " -c count Set event sample period.\n" - " -e event Select the event to sample (Use `simpleperf list`)\n" - " to find all possible event names.\n" - " -f freq Set event sample frequency.\n" - " -F freq Same as '-f freq'.\n" - " -g Enables call-graph recording.\n" - " -j branch_filter1,branch_filter2,...\n" - " Enable taken branch stack sampling. Each sample\n" - " captures a series of consecutive taken branches.\n" - " The following filters are defined:\n" - " any: any type of branch\n" - " any_call: any function call or system call\n" - " any_ret: any function return or system call return\n" - " ind_call: any indirect branch\n" - " u: only when the branch target is at the user level\n" - " k: only when the branch target is in the kernel\n" - " This option requires at least one branch type among any,\n" - " any_call, any_ret, ind_call.\n" - " -o record_file_name Set record file name, default is perf.data.\n"), + : Command( + "record", "record sampling info in perf.data", + "Usage: simpleperf record [options] [command [command-args]]\n" + " Gather sampling information when running [command].\n" + " -a System-wide collection.\n" + " -b Enable take branch stack sampling. Same as '-j any'\n" + " -c count Set event sample period.\n" + " -e event Select the event to sample (Use `simpleperf list`)\n" + " to find all possible event names.\n" + " -f freq Set event sample frequency.\n" + " -F freq Same as '-f freq'.\n" + " -g Enables call-graph recording.\n" + " -j branch_filter1,branch_filter2,...\n" + " Enable taken branch stack sampling. Each sample\n" + " captures a series of consecutive taken branches.\n" + " The following filters are defined:\n" + " any: any type of branch\n" + " any_call: any function call or system call\n" + " any_ret: any function return or system call return\n" + " ind_call: any indirect branch\n" + " u: only when the branch target is at the user level\n" + " k: only when the branch target is in the kernel\n" + " This option requires at least one branch type among any,\n" + " any_call, any_ret, ind_call.\n" + " -o record_file_name Set record file name, default is perf.data.\n" + " -p pid1,pid2,...\n" + " Record events on existing processes. Mutually exclusive with -a.\n" + " -t tid1,tid2,...\n" + " Record events on existing threads. Mutually exclusive with -a.\n"), use_sample_freq_(true), - sample_freq_(1000), + sample_freq_(4000), system_wide_collection_(false), branch_sampling_(0), callchain_sampling_(false), @@ -110,6 +115,7 @@ class RecordCommand : public Command { uint64_t sample_period_; // Sample once when 'sample_period_' events occur. bool system_wide_collection_; + std::vector monitored_threads_; uint64_t branch_sampling_; bool callchain_sampling_; const EventType* measured_event_type_; @@ -140,13 +146,21 @@ bool RecordCommand::Run(const std::vector& args) { } // 2. Create workload. - if (workload_args.empty()) { - // TODO: change default workload to sleep 99999, and run record until Ctrl-C. - workload_args = std::vector({"sleep", "1"}); + std::unique_ptr workload; + if (!workload_args.empty()) { + workload = Workload::CreateWorkload(workload_args); + if (workload == nullptr) { + return false; + } } - std::unique_ptr workload = Workload::CreateWorkload(workload_args); - if (workload == nullptr) { - return false; + if (!system_wide_collection_ && monitored_threads_.empty()) { + if (workload != nullptr) { + monitored_threads_.push_back(workload->GetPid()); + event_selection_set_.SetEnableOnExec(true); + } else { + LOG(ERROR) << "No threads to monitor. Try `simpleperf help record` for help\n"; + return false; + } } // 3. Open perf_event_files, create memory mapped buffers for perf_event_files, add prepare poll @@ -156,8 +170,7 @@ bool RecordCommand::Run(const std::vector& args) { return false; } } else { - event_selection_set_.EnableOnExec(); - if (!event_selection_set_.OpenEventFilesForProcess(workload->GetPid())) { + if (!event_selection_set_.OpenEventFilesForThreads(monitored_threads_)) { return false; } } @@ -182,15 +195,12 @@ bool RecordCommand::Run(const std::vector& args) { } // 5. Write records in mmap buffers of perf_event_files to output file while workload is running. - - // If monitoring only one process, we use the enable_on_exec flag, and don't need to start - // recording manually. - if (system_wide_collection_) { + if (!event_selection_set_.GetEnableOnExec()) { if (!event_selection_set_.EnableEvents()) { return false; } } - if (!workload->Start()) { + if (workload != nullptr && !workload->Start()) { return false; } auto callback = @@ -217,6 +227,7 @@ bool RecordCommand::Run(const std::vector& args) { bool RecordCommand::ParseOptions(const std::vector& args, std::vector* non_option_args) { + std::set tid_set; size_t i; for (i = 0; i < args.size() && args[i].size() > 0 && args[i][0] == '-'; ++i) { if (args[i] == "-a") { @@ -272,12 +283,33 @@ bool RecordCommand::ParseOptions(const std::vector& args, return false; } record_filename_ = args[i]; + } else if (args[i] == "-p") { + if (!NextArgumentOrError(args, &i)) { + return false; + } + if (!GetValidThreadsFromProcessString(args[i], &tid_set)) { + return false; + } + } else if (args[i] == "-t") { + if (!NextArgumentOrError(args, &i)) { + return false; + } + if (!GetValidThreadsFromThreadString(args[i], &tid_set)) { + return false; + } } else { ReportUnknownOption(args, i); return false; } } + monitored_threads_.insert(monitored_threads_.end(), tid_set.begin(), tid_set.end()); + if (system_wide_collection_ && !monitored_threads_.empty()) { + LOG(ERROR) + << "Record system wide and existing processes/threads can't be used at the same time."; + return false; + } + if (non_option_args != nullptr) { non_option_args->clear(); for (; i < args.size(); ++i) { diff --git a/simpleperf/cmd_record_test.cpp b/simpleperf/cmd_record_test.cpp index 4a2f3f79..dcc0ad12 100644 --- a/simpleperf/cmd_record_test.cpp +++ b/simpleperf/cmd_record_test.cpp @@ -16,10 +16,13 @@ #include +#include + #include "command.h" #include "environment.h" #include "record.h" #include "record_file.h" +#include "test_util.h" using namespace PerfFileFormat; @@ -103,3 +106,24 @@ TEST(record_cmd, branch_sampling) { TEST(record_cmd, callchain_sampling) { ASSERT_TRUE(RecordCmd()->Run({"-g", "sleep", "1"})); } + +TEST(record_cmd, existing_processes) { + std::vector> workloads; + CreateProcesses(2, &workloads); + std::string pid_list = + android::base::StringPrintf("%d,%d", workloads[0]->GetPid(), workloads[1]->GetPid()); + ASSERT_TRUE(RecordCmd()->Run({"-p", pid_list})); +} + +TEST(record_cmd, existing_threads) { + std::vector> workloads; + CreateProcesses(2, &workloads); + // Process id can also be used as thread id in linux. + std::string tid_list = + android::base::StringPrintf("%d,%d", workloads[0]->GetPid(), workloads[1]->GetPid()); + ASSERT_TRUE(RecordCmd()->Run({"-t", tid_list})); +} + +TEST(record_cmd, no_monitored_threads) { + ASSERT_FALSE(RecordCmd()->Run({""})); +} diff --git a/simpleperf/cmd_stat.cpp b/simpleperf/cmd_stat.cpp index b319eb1e..8feb1a65 100644 --- a/simpleperf/cmd_stat.cpp +++ b/simpleperf/cmd_stat.cpp @@ -18,6 +18,7 @@ #include #include #include +#include #include #include @@ -48,11 +49,14 @@ class StatCommand : public Command { StatCommand() : Command("stat", "gather performance counter information", "Usage: simpleperf stat [options] [command [command-args]]\n" - " Gather performance counter information of running [command]. If [command]\n" - " is not specified, sleep 1 is used instead.\n\n" + " Gather performance counter information of running [command].\n" " -a Collect system-wide information.\n" " -e event1,event2,... Select the event list to count. Use `simpleperf list`\n" " to find all possible event names.\n" + " -p pid1,pid2,...\n" + " Stat events on existing processes. Mutually exclusive with -a.\n" + " -t tid1,tid2,...\n" + " Stat events on existing threads. Mutually exclusive with -a.\n" " --verbose Show result in verbose mode.\n"), verbose_mode_(false), system_wide_collection_(false) { @@ -73,6 +77,7 @@ class StatCommand : public Command { EventSelectionSet event_selection_set_; bool verbose_mode_; bool system_wide_collection_; + std::vector monitored_threads_; std::unique_ptr signal_handler_register_; }; @@ -92,13 +97,21 @@ bool StatCommand::Run(const std::vector& args) { } // 3. Create workload. - if (workload_args.empty()) { - // TODO: change default workload to sleep 99999, and run stat until Ctrl-C. - workload_args = std::vector({"sleep", "1"}); + std::unique_ptr workload; + if (!workload_args.empty()) { + workload = Workload::CreateWorkload(workload_args); + if (workload == nullptr) { + return false; + } } - std::unique_ptr workload = Workload::CreateWorkload(workload_args); - if (workload == nullptr) { - return false; + if (!system_wide_collection_ && monitored_threads_.empty()) { + if (workload != nullptr) { + monitored_threads_.push_back(workload->GetPid()); + event_selection_set_.SetEnableOnExec(true); + } else { + LOG(ERROR) << "No threads to monitor. Try `simpleperf help stat` for help\n"; + return false; + } } // 4. Open perf_event_files. @@ -107,22 +120,19 @@ bool StatCommand::Run(const std::vector& args) { return false; } } else { - event_selection_set_.EnableOnExec(); - if (!event_selection_set_.OpenEventFilesForProcess(workload->GetPid())) { + if (!event_selection_set_.OpenEventFilesForThreads(monitored_threads_)) { return false; } } // 5. Count events while workload running. auto start_time = std::chrono::steady_clock::now(); - // If monitoring only one process, we use the enable_on_exec flag, and don't need to start - // counting manually. - if (system_wide_collection_) { + if (!event_selection_set_.GetEnableOnExec()) { if (!event_selection_set_.EnableEvents()) { return false; } } - if (!workload->Start()) { + if (workload != nullptr && !workload->Start()) { return false; } while (!signaled) { @@ -143,6 +153,7 @@ bool StatCommand::Run(const std::vector& args) { bool StatCommand::ParseOptions(const std::vector& args, std::vector* non_option_args) { + std::set tid_set; size_t i; for (i = 0; i < args.size() && args[i].size() > 0 && args[i][0] == '-'; ++i) { if (args[i] == "-a") { @@ -157,6 +168,20 @@ bool StatCommand::ParseOptions(const std::vector& args, return false; } } + } else if (args[i] == "-p") { + if (!NextArgumentOrError(args, &i)) { + return false; + } + if (!GetValidThreadsFromProcessString(args[i], &tid_set)) { + return false; + } + } else if (args[i] == "-t") { + if (!NextArgumentOrError(args, &i)) { + return false; + } + if (!GetValidThreadsFromThreadString(args[i], &tid_set)) { + return false; + } } else if (args[i] == "--verbose") { verbose_mode_ = true; } else { @@ -165,6 +190,12 @@ bool StatCommand::ParseOptions(const std::vector& args, } } + monitored_threads_.insert(monitored_threads_.end(), tid_set.begin(), tid_set.end()); + if (system_wide_collection_ && !monitored_threads_.empty()) { + LOG(ERROR) << "Stat system wide and existing processes/threads can't be used at the same time."; + return false; + } + if (non_option_args != nullptr) { non_option_args->clear(); for (; i < args.size(); ++i) { diff --git a/simpleperf/cmd_stat_test.cpp b/simpleperf/cmd_stat_test.cpp index f2639797..36d79da0 100644 --- a/simpleperf/cmd_stat_test.cpp +++ b/simpleperf/cmd_stat_test.cpp @@ -16,7 +16,10 @@ #include +#include + #include "command.h" +#include "test_util.h" class StatCommandTest : public ::testing::Test { protected: @@ -48,3 +51,24 @@ TEST_F(StatCommandTest, verbose_option) { TEST_F(StatCommandTest, tracepoint_event) { ASSERT_TRUE(stat_cmd->Run({"-a", "-e", "sched:sched_switch", "sleep", "1"})); } + +TEST_F(StatCommandTest, existing_processes) { + std::vector> workloads; + CreateProcesses(2, &workloads); + std::string pid_list = + android::base::StringPrintf("%d,%d", workloads[0]->GetPid(), workloads[1]->GetPid()); + ASSERT_TRUE(stat_cmd->Run({"-p", pid_list})); +} + +TEST_F(StatCommandTest, existing_threads) { + std::vector> workloads; + CreateProcesses(2, &workloads); + // Process id can be used as thread id in linux. + std::string tid_list = + android::base::StringPrintf("%d,%d", workloads[0]->GetPid(), workloads[1]->GetPid()); + ASSERT_TRUE(stat_cmd->Run({"-t", tid_list})); +} + +TEST_F(StatCommandTest, no_monitored_threads) { + ASSERT_FALSE(stat_cmd->Run({""})); +} diff --git a/simpleperf/environment.cpp b/simpleperf/environment.cpp index f4027fb6..5830cddd 100644 --- a/simpleperf/environment.cpp +++ b/simpleperf/environment.cpp @@ -273,7 +273,8 @@ static bool ReadThreadNameAndTgid(const std::string& status_file, std::string* c return false; } -static bool GetThreadComm(pid_t pid, std::vector* thread_comms) { +static std::vector GetThreadsInProcess(pid_t pid) { + std::vector result; std::string task_dirname = android::base::StringPrintf("/proc/%d/task", pid); std::vector subdirs; GetEntriesInDir(task_dirname, nullptr, &subdirs); @@ -282,11 +283,20 @@ static bool GetThreadComm(pid_t pid, std::vector* thread_comms) { if (!StringToPid(name, &tid)) { continue; } - std::string status_file = task_dirname + "/" + name + "/status"; + result.push_back(tid); + } + return result; +} + +static bool GetThreadComm(pid_t pid, std::vector* thread_comms) { + std::vector tids = GetThreadsInProcess(pid); + for (auto& tid : tids) { + std::string status_file = android::base::StringPrintf("/proc/%d/task/%d/status", pid, tid); std::string comm; pid_t tgid; + // It is possible that the process or thread exited before we can read its status. if (!ReadThreadNameAndTgid(status_file, &comm, &tgid)) { - return false; + continue; } ThreadComm thread; thread.tid = tid; @@ -356,3 +366,38 @@ bool GetModuleBuildId(const std::string& module_name, BuildId* build_id) { std::string notefile = "/sys/module/" + module_name + "/notes/.note.gnu.build-id"; return GetBuildIdFromNoteFile(notefile, build_id); } + +bool GetValidThreadsFromProcessString(const std::string& pid_str, std::set* tid_set) { + std::vector strs = android::base::Split(pid_str, ","); + for (auto& s : strs) { + pid_t pid; + if (!StringToPid(s, &pid)) { + LOG(ERROR) << "Invalid pid '" << s << "'"; + return false; + } + std::vector tids = GetThreadsInProcess(pid); + if (tids.empty()) { + LOG(ERROR) << "Non existing process '" << pid << "'"; + return false; + } + tid_set->insert(tids.begin(), tids.end()); + } + return true; +} + +bool GetValidThreadsFromThreadString(const std::string& tid_str, std::set* tid_set) { + std::vector strs = android::base::Split(tid_str, ","); + for (auto& s : strs) { + pid_t tid; + if (!StringToPid(s, &tid)) { + LOG(ERROR) << "Invalid tid '" << s << "'"; + return false; + } + if (!IsDir(android::base::StringPrintf("/proc/%d", tid))) { + LOG(ERROR) << "Non existing thread '" << tid << "'"; + return false; + } + tid_set->insert(tid); + } + return true; +} diff --git a/simpleperf/environment.h b/simpleperf/environment.h index 6d81e981..6547862f 100644 --- a/simpleperf/environment.h +++ b/simpleperf/environment.h @@ -18,6 +18,7 @@ #define SIMPLE_PERF_ENVIRONMENT_H_ #include +#include #include #include @@ -68,6 +69,9 @@ static const char* DEFAULT_KERNEL_FILENAME_FOR_BUILD_ID = "[kernel.kallsyms]"; bool GetKernelBuildId(BuildId* build_id); bool GetModuleBuildId(const std::string& module_name, BuildId* build_id); +bool GetValidThreadsFromProcessString(const std::string& pid_str, std::set* tid_set); +bool GetValidThreadsFromThreadString(const std::string& tid_str, std::set* tid_set); + // Expose the following functions for unit tests. std::vector GetOnlineCpusFromString(const std::string& s); diff --git a/simpleperf/event_fd.cpp b/simpleperf/event_fd.cpp index b342ab5a..9b06e50b 100644 --- a/simpleperf/event_fd.cpp +++ b/simpleperf/event_fd.cpp @@ -38,17 +38,7 @@ static int perf_event_open(perf_event_attr* attr, pid_t pid, int cpu, int group_ return syscall(__NR_perf_event_open, attr, pid, cpu, group_fd, flags); } -std::unique_ptr EventFd::OpenEventFileForProcess(const perf_event_attr& attr, pid_t pid, - bool report_error) { - return OpenEventFile(attr, pid, -1, report_error); -} - -std::unique_ptr EventFd::OpenEventFileForCpu(const perf_event_attr& attr, int cpu, - bool report_error) { - return OpenEventFile(attr, -1, cpu, report_error); -} - -std::unique_ptr EventFd::OpenEventFile(const perf_event_attr& attr, pid_t pid, int cpu, +std::unique_ptr EventFd::OpenEventFile(const perf_event_attr& attr, pid_t tid, int cpu, bool report_error) { perf_event_attr perf_attr = attr; std::string event_name = "unknown event"; @@ -57,19 +47,19 @@ std::unique_ptr EventFd::OpenEventFile(const perf_event_attr& attr, pid if (event_type != nullptr) { event_name = event_type->name; } - int perf_event_fd = perf_event_open(&perf_attr, pid, cpu, -1, 0); + int perf_event_fd = perf_event_open(&perf_attr, tid, cpu, -1, 0); if (perf_event_fd == -1) { (report_error ? PLOG(ERROR) : PLOG(DEBUG)) << "open perf_event_file (event " << event_name - << ", pid " << pid << ", cpu " << cpu << ") failed"; + << ", tid " << tid << ", cpu " << cpu << ") failed"; return nullptr; } if (fcntl(perf_event_fd, F_SETFD, FD_CLOEXEC) == -1) { (report_error ? PLOG(ERROR) : PLOG(DEBUG)) << "fcntl(FD_CLOEXEC) for perf_event_file (event " - << event_name << ", pid " << pid << ", cpu " << cpu + << event_name << ", tid " << tid << ", cpu " << cpu << ") failed"; return nullptr; } - return std::unique_ptr(new EventFd(perf_event_fd, event_name, pid, cpu)); + return std::unique_ptr(new EventFd(perf_event_fd, event_name, tid, cpu)); } EventFd::~EventFd() { @@ -80,8 +70,8 @@ EventFd::~EventFd() { } std::string EventFd::Name() const { - return android::base::StringPrintf("perf_event_file(event %s, pid %d, cpu %d)", - event_name_.c_str(), pid_, cpu_); + return android::base::StringPrintf("perf_event_file(event %s, tid %d, cpu %d)", + event_name_.c_str(), tid_, cpu_); } uint64_t EventFd::Id() const { diff --git a/simpleperf/event_fd.h b/simpleperf/event_fd.h index e05761e2..bcacc283 100644 --- a/simpleperf/event_fd.h +++ b/simpleperf/event_fd.h @@ -37,16 +37,12 @@ struct PerfCounter { // EventFd represents an opened perf_event_file. class EventFd { public: - static std::unique_ptr OpenEventFileForProcess(const perf_event_attr& attr, pid_t pid, - bool report_error = true); - static std::unique_ptr OpenEventFileForCpu(const perf_event_attr& attr, int cpu, - bool report_error = true); - static std::unique_ptr OpenEventFile(const perf_event_attr& attr, pid_t pid, int cpu, + static std::unique_ptr OpenEventFile(const perf_event_attr& attr, pid_t tid, int cpu, bool report_error = true); ~EventFd(); - // Give information about this perf_event_file, like (event_name, pid, cpu). + // Give information about this perf_event_file, like (event_name, tid, cpu). std::string Name() const; uint64_t Id() const; @@ -75,11 +71,11 @@ class EventFd { void PreparePollForMmapData(pollfd* poll_fd); private: - EventFd(int perf_event_fd, const std::string& event_name, pid_t pid, int cpu) + EventFd(int perf_event_fd, const std::string& event_name, pid_t tid, int cpu) : perf_event_fd_(perf_event_fd), id_(0), event_name_(event_name), - pid_(pid), + tid_(tid), cpu_(cpu), mmap_addr_(nullptr), mmap_len_(0) { @@ -88,7 +84,7 @@ class EventFd { int perf_event_fd_; mutable uint64_t id_; const std::string event_name_; - pid_t pid_; + pid_t tid_; int cpu_; void* mmap_addr_; diff --git a/simpleperf/event_selection_set.cpp b/simpleperf/event_selection_set.cpp index b1618bd8..f03286bc 100644 --- a/simpleperf/event_selection_set.cpp +++ b/simpleperf/event_selection_set.cpp @@ -30,7 +30,7 @@ bool IsBranchSamplingSupported() { perf_event_attr attr = CreateDefaultPerfEventAttr(*event_type); attr.sample_type |= PERF_SAMPLE_BRANCH_STACK; attr.branch_sample_type = PERF_SAMPLE_BRANCH_ANY; - auto event_fd = EventFd::OpenEventFileForProcess(attr, getpid(), false); + auto event_fd = EventFd::OpenEventFile(attr, getpid(), -1, false); return event_fd != nullptr; } @@ -41,12 +41,21 @@ void EventSelectionSet::AddEventType(const EventType& event_type) { selections_.push_back(std::move(selection)); } -void EventSelectionSet::EnableOnExec() { +void EventSelectionSet::SetEnableOnExec(bool enable) { for (auto& selection : selections_) { - selection.event_attr.enable_on_exec = 1; + selection.event_attr.enable_on_exec = (enable ? 1 : 0); } } +bool EventSelectionSet::GetEnableOnExec() { + for (auto& selection : selections_) { + if (selection.event_attr.enable_on_exec == 0) { + return false; + } + } + return true; +} + void EventSelectionSet::SampleIdAll() { for (auto& selection : selections_) { selection.event_attr.sample_id_all = 1; @@ -105,7 +114,7 @@ bool EventSelectionSet::OpenEventFilesForAllCpus() { } for (auto& selection : selections_) { for (auto& cpu : cpus) { - auto event_fd = EventFd::OpenEventFileForCpu(selection.event_attr, cpu); + auto event_fd = EventFd::OpenEventFile(selection.event_attr, -1, cpu); if (event_fd != nullptr) { selection.event_fds.push_back(std::move(event_fd)); } @@ -121,13 +130,15 @@ bool EventSelectionSet::OpenEventFilesForAllCpus() { return true; } -bool EventSelectionSet::OpenEventFilesForProcess(pid_t pid) { +bool EventSelectionSet::OpenEventFilesForThreads(const std::vector& threads) { for (auto& selection : selections_) { - auto event_fd = EventFd::OpenEventFileForProcess(selection.event_attr, pid); - if (event_fd == nullptr) { - return false; + for (auto& tid : threads) { + auto event_fd = EventFd::OpenEventFile(selection.event_attr, tid, -1); + if (event_fd == nullptr) { + return false; + } + selection.event_fds.push_back(std::move(event_fd)); } - selection.event_fds.push_back(std::move(event_fd)); } return true; } diff --git a/simpleperf/event_selection_set.h b/simpleperf/event_selection_set.h index dabef14b..2e5b50cc 100644 --- a/simpleperf/event_selection_set.h +++ b/simpleperf/event_selection_set.h @@ -49,7 +49,8 @@ class EventSelectionSet { void AddEventType(const EventType& event_type); - void EnableOnExec(); + void SetEnableOnExec(bool enable); + bool GetEnableOnExec(); void SampleIdAll(); void SetSampleFreq(uint64_t sample_freq); void SetSamplePeriod(uint64_t sample_period); @@ -57,7 +58,7 @@ class EventSelectionSet { void EnableCallChainSampling(); bool OpenEventFilesForAllCpus(); - bool OpenEventFilesForProcess(pid_t pid); + bool OpenEventFilesForThreads(const std::vector& threads); bool EnableEvents(); bool ReadCounters(std::map>* counters_map); void PreparePollForEventFiles(std::vector* pollfds); diff --git a/simpleperf/event_type.cpp b/simpleperf/event_type.cpp index e4c50e1b..2b2e5b49 100644 --- a/simpleperf/event_type.cpp +++ b/simpleperf/event_type.cpp @@ -38,7 +38,7 @@ static const std::vector static_event_type_array = { static bool IsEventTypeSupportedByKernel(const EventType& event_type) { auto event_fd = - EventFd::OpenEventFileForProcess(CreateDefaultPerfEventAttr(event_type), getpid(), false); + EventFd::OpenEventFile(CreateDefaultPerfEventAttr(event_type), getpid(), -1, false); return event_fd != nullptr; } diff --git a/simpleperf/record_file_test.cpp b/simpleperf/record_file_test.cpp index 8d141339..2d595110 100644 --- a/simpleperf/record_file_test.cpp +++ b/simpleperf/record_file_test.cpp @@ -37,7 +37,7 @@ class RecordFileTest : public ::testing::Test { event_attr = CreateDefaultPerfEventAttr(*event_type); event_attr.sample_id_all = 1; event_attr.sample_type |= PERF_SAMPLE_TIME; - std::unique_ptr event_fd = EventFd::OpenEventFileForProcess(event_attr, getpid()); + std::unique_ptr event_fd = EventFd::OpenEventFile(event_attr, getpid(), -1); ASSERT_TRUE(event_fd != nullptr); event_fds.push_back(std::move(event_fd)); } diff --git a/simpleperf/sample_tree_test.cpp b/simpleperf/sample_tree_test.cpp index 9a2ca0b3..033d3691 100644 --- a/simpleperf/sample_tree_test.cpp +++ b/simpleperf/sample_tree_test.cpp @@ -14,10 +14,10 @@ * limitations under the License. */ -#include "sample_tree.h" - #include +#include "sample_tree.h" + struct ExpectedSampleInMap { int pid; int tid; diff --git a/simpleperf/test_util.h b/simpleperf/test_util.h new file mode 100644 index 00000000..34155a3a --- /dev/null +++ b/simpleperf/test_util.h @@ -0,0 +1,27 @@ +/* + * Copyright (C) 2015 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 "workload.h" + +static void CreateProcesses(size_t count, std::vector>* workloads) { + workloads->clear(); + for (size_t i = 0; i < count; ++i) { + auto workload = Workload::CreateWorkload({"sleep", "1"}); + ASSERT_TRUE(workload != nullptr); + ASSERT_TRUE(workload->Start()); + workloads->push_back(std::move(workload)); + } +} diff --git a/simpleperf/utils.cpp b/simpleperf/utils.cpp index 783bc8ff..b2122636 100644 --- a/simpleperf/utils.cpp +++ b/simpleperf/utils.cpp @@ -68,6 +68,16 @@ void GetEntriesInDir(const std::string& dirpath, std::vector* files closedir(dir); } +bool IsDir(const std::string& dirpath) { + struct stat st; + if (stat(dirpath.c_str(), &st) == 0) { + if (S_ISDIR(st.st_mode)) { + return true; + } + } + return false; +} + bool RemovePossibleFile(const std::string& filename) { struct stat st; if (stat(filename.c_str(), &st) == 0) { diff --git a/simpleperf/utils.h b/simpleperf/utils.h index 43c09be6..b0171978 100644 --- a/simpleperf/utils.h +++ b/simpleperf/utils.h @@ -78,6 +78,7 @@ bool IsPowerOfTwo(uint64_t value); void GetEntriesInDir(const std::string& dirpath, std::vector* files, std::vector* subdirs); +bool IsDir(const std::string& dirpath); bool RemovePossibleFile(const std::string& filename); #endif // SIMPLE_PERF_UTILS_H_ diff --git a/simpleperf/workload_test.cpp b/simpleperf/workload_test.cpp index ada3969f..f250328b 100644 --- a/simpleperf/workload_test.cpp +++ b/simpleperf/workload_test.cpp @@ -17,8 +17,9 @@ #include #include -#include -#include + +#include "utils.h" +#include "workload.h" static volatile bool signaled; static void signal_handler(int) { @@ -49,7 +50,7 @@ static void run_signaled_workload() { auto workload = Workload::CreateWorkload({"sleep", "10"}); ASSERT_TRUE(workload != nullptr); ASSERT_TRUE(workload->Start()); - ASSERT_EQ(0, kill(workload->GetPid(), SIGSEGV)); + ASSERT_EQ(0, kill(workload->GetPid(), SIGABRT)); while (!signaled) { } } -- 2.11.0