OSDN Git Service

Simpleperf: support monitoring existing processes/threads.
authorYabin Cui <yabinc@google.com>
Thu, 18 Jun 2015 04:15:09 +0000 (21:15 -0700)
committerYabin Cui <yabinc@google.com>
Thu, 18 Jun 2015 23:47:02 +0000 (16:47 -0700)
Also change the default record freq from 1000 to 4000. Because 1000 seems to be too low.

Bug: 19483574
Change-Id: I340fcb9d28a156862705e483ee340a1c824eea21

17 files changed:
simpleperf/cmd_record.cpp
simpleperf/cmd_record_test.cpp
simpleperf/cmd_stat.cpp
simpleperf/cmd_stat_test.cpp
simpleperf/environment.cpp
simpleperf/environment.h
simpleperf/event_fd.cpp
simpleperf/event_fd.h
simpleperf/event_selection_set.cpp
simpleperf/event_selection_set.h
simpleperf/event_type.cpp
simpleperf/record_file_test.cpp
simpleperf/sample_tree_test.cpp
simpleperf/test_util.h [new file with mode: 0644]
simpleperf/utils.cpp
simpleperf/utils.h
simpleperf/workload_test.cpp

index 2ceeed0..e87a7ec 100644 (file)
@@ -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) {
index 4a2f3f7..dcc0ad1 100644 (file)
 
 #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({""}));
+}
index b319eb1..8feb1a6 100644 (file)
@@ -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) {
index f263979..36d79da 100644 (file)
 
 #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({""}));
+}
index f4027fb..5830cdd 100644 (file)
@@ -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;
+}
index 6d81e98..6547862 100644 (file)
@@ -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);
 
index b342ab5..9b06e50 100644 (file)
@@ -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 {
index e05761e..bcacc28 100644 (file)
@@ -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_;
index b1618bd..f03286b 100644 (file)
@@ -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;
 }
index dabef14..2e5b50c 100644 (file)
@@ -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);
index e4c50e1..2b2e5b4 100644 (file)
@@ -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;
 }
 
index 8d14133..2d59511 100644 (file)
@@ -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));
   }
index 9a2ca0b..033d369 100644 (file)
  * 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 (file)
index 0000000..34155a3
--- /dev/null
@@ -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));
+  }
+}
index 783bc8f..b212263 100644 (file)
@@ -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) {
index 43c09be..b017197 100644 (file)
@@ -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_
index ada3969..f250328 100644 (file)
@@ -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) {
     }
   }