summaryrefslogtreecommitdiff
path: root/simpleperf
diff options
context:
space:
mode:
authorYabin Cui <yabinc@google.com>2015-06-17 21:15:09 -0700
committerYabin Cui <yabinc@google.com>2015-06-18 16:47:02 -0700
commitb032de7a9e1975a838cd6df2e7c8df3c7f70a3ce (patch)
tree08c1d077015624b5488edc7483050f492364c451 /simpleperf
parent1ded5c77b86bf64d121251e70d84fbe7cf19b06c (diff)
downloadextras-b032de7a9e1975a838cd6df2e7c8df3c7f70a3ce.tar.gz
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
Diffstat (limited to 'simpleperf')
-rw-r--r--simpleperf/cmd_record.cpp110
-rw-r--r--simpleperf/cmd_record_test.cpp24
-rw-r--r--simpleperf/cmd_stat.cpp59
-rw-r--r--simpleperf/cmd_stat_test.cpp24
-rw-r--r--simpleperf/environment.cpp51
-rw-r--r--simpleperf/environment.h4
-rw-r--r--simpleperf/event_fd.cpp24
-rw-r--r--simpleperf/event_fd.h14
-rw-r--r--simpleperf/event_selection_set.cpp29
-rw-r--r--simpleperf/event_selection_set.h5
-rw-r--r--simpleperf/event_type.cpp2
-rw-r--r--simpleperf/record_file_test.cpp2
-rw-r--r--simpleperf/sample_tree_test.cpp4
-rw-r--r--simpleperf/test_util.h27
-rw-r--r--simpleperf/utils.cpp10
-rw-r--r--simpleperf/utils.h1
-rw-r--r--simpleperf/workload_test.cpp7
17 files changed, 297 insertions, 100 deletions
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 <libgen.h>
#include <poll.h>
#include <signal.h>
+#include <set>
#include <string>
#include <unordered_map>
#include <vector>
@@ -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<pid_t> monitored_threads_;
uint64_t branch_sampling_;
bool callchain_sampling_;
const EventType* measured_event_type_;
@@ -140,13 +146,21 @@ bool RecordCommand::Run(const std::vector<std::string>& 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<std::string>({"sleep", "1"});
+ std::unique_ptr<Workload> workload;
+ if (!workload_args.empty()) {
+ workload = Workload::CreateWorkload(workload_args);
+ if (workload == nullptr) {
+ return false;
+ }
}
- std::unique_ptr<Workload> 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<std::string>& 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<std::string>& 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<std::string>& args) {
bool RecordCommand::ParseOptions(const std::vector<std::string>& args,
std::vector<std::string>* non_option_args) {
+ std::set<pid_t> 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<std::string>& 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 <gtest/gtest.h>
+#include <base/stringprintf.h>
+
#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<std::unique_ptr<Workload>> 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<std::unique_ptr<Workload>> 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 <signal.h>
#include <stdio.h>
#include <chrono>
+#include <set>
#include <string>
#include <vector>
@@ -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<pid_t> monitored_threads_;
std::unique_ptr<SignalHandlerRegister> signal_handler_register_;
};
@@ -92,13 +97,21 @@ bool StatCommand::Run(const std::vector<std::string>& 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<std::string>({"sleep", "1"});
+ std::unique_ptr<Workload> workload;
+ if (!workload_args.empty()) {
+ workload = Workload::CreateWorkload(workload_args);
+ if (workload == nullptr) {
+ return false;
+ }
}
- std::unique_ptr<Workload> 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<std::string>& 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<std::string>& args) {
bool StatCommand::ParseOptions(const std::vector<std::string>& args,
std::vector<std::string>* non_option_args) {
+ std::set<pid_t> 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<std::string>& 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<std::string>& 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 <gtest/gtest.h>
+#include <base/stringprintf.h>
+
#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<std::unique_ptr<Workload>> 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<std::unique_ptr<Workload>> 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<ThreadComm>* thread_comms) {
+static std::vector<pid_t> GetThreadsInProcess(pid_t pid) {
+ std::vector<pid_t> result;
std::string task_dirname = android::base::StringPrintf("/proc/%d/task", pid);
std::vector<std::string> subdirs;
GetEntriesInDir(task_dirname, nullptr, &subdirs);
@@ -282,11 +283,20 @@ static bool GetThreadComm(pid_t pid, std::vector<ThreadComm>* 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<ThreadComm>* thread_comms) {
+ std::vector<pid_t> 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<pid_t>* tid_set) {
+ std::vector<std::string> 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<pid_t> 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<pid_t>* tid_set) {
+ std::vector<std::string> 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 <functional>
+#include <set>
#include <string>
#include <vector>
@@ -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<pid_t>* tid_set);
+bool GetValidThreadsFromThreadString(const std::string& tid_str, std::set<pid_t>* tid_set);
+
// Expose the following functions for unit tests.
std::vector<int> 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> EventFd::OpenEventFileForProcess(const perf_event_attr& attr, pid_t pid,
- bool report_error) {
- return OpenEventFile(attr, pid, -1, report_error);
-}
-
-std::unique_ptr<EventFd> EventFd::OpenEventFileForCpu(const perf_event_attr& attr, int cpu,
- bool report_error) {
- return OpenEventFile(attr, -1, cpu, report_error);
-}
-
-std::unique_ptr<EventFd> EventFd::OpenEventFile(const perf_event_attr& attr, pid_t pid, int cpu,
+std::unique_ptr<EventFd> 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> 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<EventFd>(new EventFd(perf_event_fd, event_name, pid, cpu));
+ return std::unique_ptr<EventFd>(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<EventFd> OpenEventFileForProcess(const perf_event_attr& attr, pid_t pid,
- bool report_error = true);
- static std::unique_ptr<EventFd> OpenEventFileForCpu(const perf_event_attr& attr, int cpu,
- bool report_error = true);
- static std::unique_ptr<EventFd> OpenEventFile(const perf_event_attr& attr, pid_t pid, int cpu,
+ static std::unique_ptr<EventFd> 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<pid_t>& 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<pid_t>& threads);
bool EnableEvents();
bool ReadCounters(std::map<const EventType*, std::vector<PerfCounter>>* counters_map);
void PreparePollForEventFiles(std::vector<pollfd>* 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<EventType> 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<EventFd> event_fd = EventFd::OpenEventFileForProcess(event_attr, getpid());
+ std::unique_ptr<EventFd> 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 <gtest/gtest.h>
+#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<std::unique_ptr<Workload>>* 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<std::string>* 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<std::string>* files,
std::vector<std::string>* 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 <gtest/gtest.h>
#include <signal.h>
-#include <utils.h>
-#include <workload.h>
+
+#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) {
}
}