Merge "Simpleperf: don't expose EventSelection."
diff --git a/simpleperf/cmd_dumprecord.cpp b/simpleperf/cmd_dumprecord.cpp
index 3e89b82..047bfa2 100644
--- a/simpleperf/cmd_dumprecord.cpp
+++ b/simpleperf/cmd_dumprecord.cpp
@@ -157,7 +157,7 @@
 }
 
 void DumpRecordCommand::DumpAttrSection() {
-  std::vector<AttrWithId> attrs = record_file_reader_->AttrSection();
+  std::vector<EventAttrWithId> attrs = record_file_reader_->AttrSection();
   for (size_t i = 0; i < attrs.size(); ++i) {
     const auto& attr = attrs[i];
     printf("attr %zu:\n", i + 1);
diff --git a/simpleperf/cmd_kmem.cpp b/simpleperf/cmd_kmem.cpp
index 16559a2..3cfb93e 100644
--- a/simpleperf/cmd_kmem.cpp
+++ b/simpleperf/cmd_kmem.cpp
@@ -552,7 +552,7 @@
 }
 
 void KmemCommand::ReadEventAttrsFromRecordFile() {
-  std::vector<AttrWithId> attrs = record_file_reader_->AttrSection();
+  std::vector<EventAttrWithId> attrs = record_file_reader_->AttrSection();
   for (const auto& attr_with_id : attrs) {
     EventAttrWithName attr;
     attr.attr = *attr_with_id.attr;
diff --git a/simpleperf/cmd_record.cpp b/simpleperf/cmd_record.cpp
index 07e1468..a076778 100644
--- a/simpleperf/cmd_record.cpp
+++ b/simpleperf/cmd_record.cpp
@@ -56,9 +56,6 @@
     {"ind_call", PERF_SAMPLE_BRANCH_IND_CALL},
 };
 
-constexpr uint64_t DEFAULT_SAMPLE_FREQ_FOR_NONTRACEPOINT_EVENT = 4000;
-constexpr uint64_t DEFAULT_SAMPLE_PERIOD_FOR_TRACEPOINT_EVENT = 1;
-
 // 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;
@@ -306,9 +303,7 @@
 
   // 6. Write records in mapped buffers of perf_event_files to output file while
   //    workload is running.
-  if (!GetPerfClock(&start_sampling_time_in_ns_)) {
-    return false;
-  }
+  start_sampling_time_in_ns_ = GetPerfClock();
   LOG(VERBOSE) << "start_sampling_time is " << start_sampling_time_in_ns_
                << " ns";
   if (workload != nullptr && !workload->Start()) {
@@ -581,23 +576,12 @@
 }
 
 bool RecordCommand::SetEventSelectionFlags() {
-  for (const auto& group : event_selection_set_.groups()) {
-    for (const auto& selection : group) {
-      if (use_sample_freq_) {
-        event_selection_set_.SetSampleFreq(selection, sample_freq_);
-      } else if (use_sample_period_) {
-        event_selection_set_.SetSamplePeriod(selection, sample_period_);
-      } else {
-        if (selection.event_type_modifier.event_type.type ==
-            PERF_TYPE_TRACEPOINT) {
-          event_selection_set_.SetSamplePeriod(
-              selection, DEFAULT_SAMPLE_PERIOD_FOR_TRACEPOINT_EVENT);
-        } else {
-          event_selection_set_.SetSampleFreq(
-              selection, DEFAULT_SAMPLE_FREQ_FOR_NONTRACEPOINT_EVENT);
-        }
-      }
-    }
+  if (use_sample_freq_) {
+    event_selection_set_.SetSampleFreq(sample_freq_);
+  } else if (use_sample_period_) {
+    event_selection_set_.SetSamplePeriod(sample_period_);
+  } else {
+    event_selection_set_.UseDefaultSampleFreq();
   }
   event_selection_set_.SampleIdAll();
   if (!event_selection_set_.SetBranchSampling(branch_sampling_)) {
@@ -626,20 +610,17 @@
     return false;
   }
   // Use first perf_event_attr and first event id to dump mmap and comm records.
-  const EventSelection& selection = event_selection_set_.groups()[0][0];
-  const perf_event_attr& attr = selection.event_attr;
-  const std::vector<std::unique_ptr<EventFd>>& fds = selection.event_fds;
-  uint64_t event_id = fds[0]->Id();
+  EventAttrWithId attr_id = event_selection_set_.GetEventAttrWithId()[0];
   if (!DumpKernelSymbol()) {
     return false;
   }
   if (!DumpTracingData()) {
     return false;
   }
-  if (!DumpKernelAndModuleMmaps(attr, event_id)) {
+  if (!DumpKernelAndModuleMmaps(*attr_id.attr, attr_id.ids[0])) {
     return false;
   }
-  if (!DumpThreadCommAndMmaps(attr, event_id)) {
+  if (!DumpThreadCommAndMmaps(*attr_id.attr, attr_id.ids[0])) {
     return false;
   }
   return true;
@@ -653,20 +634,7 @@
     return nullptr;
   }
 
-  std::vector<AttrWithId> attr_ids;
-  for (const auto& group : event_selection_set_.groups()) {
-    for (const auto& selection : group) {
-      AttrWithId attr_id;
-      attr_id.attr = &selection.event_attr;
-      CHECK(attr_id.attr != nullptr);
-      const std::vector<std::unique_ptr<EventFd>>& fds = selection.event_fds;
-      for (const auto& fd : fds) {
-        attr_id.ids.push_back(fd->Id());
-      }
-      attr_ids.push_back(attr_id);
-    }
-  }
-  if (!writer->WriteAttrSection(attr_ids)) {
+  if (!writer->WriteAttrSection(event_selection_set_.GetEventAttrWithId())) {
     return nullptr;
   }
   return writer;
@@ -675,39 +643,24 @@
 bool RecordCommand::DumpKernelSymbol() {
   if (can_dump_kernel_symbols_) {
     std::string kallsyms;
-    bool need_kernel_symbol = false;
-    for (const auto& group : event_selection_set_.groups()) {
-      for (const auto& selection : group) {
-        if (!selection.event_type_modifier.exclude_kernel) {
-          need_kernel_symbol = true;
-        }
-      }
-    }
-    if (need_kernel_symbol && CheckKernelSymbolAddresses()) {
+    if (event_selection_set_.NeedKernelSymbol() &&
+        CheckKernelSymbolAddresses()) {
       if (!android::base::ReadFileToString("/proc/kallsyms", &kallsyms)) {
         PLOG(ERROR) << "failed to read /proc/kallsyms";
         return false;
       }
-    }
-    KernelSymbolRecord r(kallsyms);
-    if (!ProcessRecord(&r)) {
-      return false;
+      KernelSymbolRecord r(kallsyms);
+      if (!ProcessRecord(&r)) {
+        return false;
+      }
     }
   }
   return true;
 }
 
 bool RecordCommand::DumpTracingData() {
-  std::vector<const EventType*> tracepoint_event_types;
-  for (const auto& group : event_selection_set_.groups()) {
-    for (const auto& selection : group) {
-      if (selection.event_type_modifier.event_type.type ==
-          PERF_TYPE_TRACEPOINT) {
-        tracepoint_event_types.push_back(
-            &selection.event_type_modifier.event_type);
-      }
-    }
-  }
+  std::vector<const EventType*> tracepoint_event_types =
+      event_selection_set_.GetTracepointEvents();
   if (tracepoint_event_types.empty()) {
     return true;  // No need to dump tracing data.
   }
@@ -816,7 +769,7 @@
 }
 
 bool RecordCommand::ProcessRecord(Record* record) {
-  if (record->type() == PERF_RECORD_SAMPLE) {
+  if (system_wide_collection_ && record->type() == PERF_RECORD_SAMPLE) {
     auto& r = *static_cast<SampleRecord*>(record);
     // Omit samples get before start sampling time.
     if (r.time_data.time < start_sampling_time_in_ns_) {
diff --git a/simpleperf/cmd_record_test.cpp b/simpleperf/cmd_record_test.cpp
index 9729904..07cf930 100644
--- a/simpleperf/cmd_record_test.cpp
+++ b/simpleperf/cmd_record_test.cpp
@@ -29,6 +29,7 @@
 #include "record.h"
 #include "record_file.h"
 #include "test_util.h"
+#include "thread_tree.h"
 
 using namespace PerfFileFormat;
 
@@ -223,7 +224,8 @@
       has_kernel_symbol_records = true;
     }
   }
-  ASSERT_EQ(need_kallsyms, has_kernel_symbol_records);
+  bool require_kallsyms = need_kallsyms && CheckKernelSymbolAddresses();
+  ASSERT_EQ(require_kallsyms, has_kernel_symbol_records);
   *success = true;
 }
 
diff --git a/simpleperf/cmd_report.cpp b/simpleperf/cmd_report.cpp
index fe93dbc..830ec13 100644
--- a/simpleperf/cmd_report.cpp
+++ b/simpleperf/cmd_report.cpp
@@ -31,7 +31,6 @@
 
 #include "command.h"
 #include "dwarf_unwind.h"
-#include "environment.h"
 #include "event_attr.h"
 #include "event_type.h"
 #include "perf_regs.h"
@@ -582,7 +581,7 @@
 }
 
 bool ReportCommand::ReadEventAttrFromRecordFile() {
-  std::vector<AttrWithId> attrs = record_file_reader_->AttrSection();
+  std::vector<EventAttrWithId> attrs = record_file_reader_->AttrSection();
   for (const auto& attr_with_id : attrs) {
     EventAttrWithName attr;
     attr.attr = *attr_with_id.attr;
diff --git a/simpleperf/cmd_report_test.cpp b/simpleperf/cmd_report_test.cpp
index a889775..704076a 100644
--- a/simpleperf/cmd_report_test.cpp
+++ b/simpleperf/cmd_report_test.cpp
@@ -24,7 +24,6 @@
 #include <android-base/test_utils.h>
 
 #include "command.h"
-#include "event_selection_set.h"
 #include "get_test_data.h"
 #include "perf_regs.h"
 #include "read_apk.h"
@@ -417,6 +416,7 @@
 }
 
 #if defined(__linux__)
+#include "event_selection_set.h"
 
 static std::unique_ptr<Command> RecordCmd() {
   return CreateCommandInstance("record");
diff --git a/simpleperf/cmd_stat.cpp b/simpleperf/cmd_stat.cpp
index b8153c7..9c5cf8f 100644
--- a/simpleperf/cmd_stat.cpp
+++ b/simpleperf/cmd_stat.cpp
@@ -571,21 +571,21 @@
 
   if (verbose_mode_) {
     for (auto& counters_info : counters) {
-      const EventTypeAndModifier& event_type =
-          counters_info.selection->event_type_modifier;
       for (auto& counter_info : counters_info.counters) {
         if (csv_) {
           fprintf(fp, "%s,tid,%d,cpu,%d,count,%" PRIu64 ",time_enabled,%" PRIu64
                       ",time running,%" PRIu64 ",id,%" PRIu64 ",\n",
-                  event_type.name.c_str(), counter_info.tid, counter_info.cpu,
-                  counter_info.counter.value, counter_info.counter.time_enabled,
+                  counters_info.event_name.c_str(), counter_info.tid,
+                  counter_info.cpu, counter_info.counter.value,
+                  counter_info.counter.time_enabled,
                   counter_info.counter.time_running, counter_info.counter.id);
         } else {
           fprintf(fp,
                   "%s(tid %d, cpu %d): count %" PRIu64 ", time_enabled %" PRIu64
                   ", time running %" PRIu64 ", id %" PRIu64 "\n",
-                  event_type.name.c_str(), counter_info.tid, counter_info.cpu,
-                  counter_info.counter.value, counter_info.counter.time_enabled,
+                  counters_info.event_name.c_str(), counter_info.tid,
+                  counter_info.cpu, counter_info.counter.value,
+                  counter_info.counter.time_enabled,
                   counter_info.counter.time_running, counter_info.counter.id);
         }
       }
@@ -606,10 +606,9 @@
     if (time_running_sum < time_enabled_sum && time_running_sum != 0) {
       scale = static_cast<double>(time_enabled_sum) / time_running_sum;
     }
-    summaries.AddSummary(CounterSummary(
-        counters_info.selection->event_type_modifier.event_type.name,
-        counters_info.selection->event_type_modifier.modifier,
-        counters_info.selection->group_id, value_sum, scale, false, csv_));
+    summaries.AddSummary(
+        CounterSummary(counters_info.event_name, counters_info.event_modifier,
+                       counters_info.group_id, value_sum, scale, false, csv_));
   }
   summaries.AutoGenerateSummaries();
   summaries.GenerateComments(duration_in_sec);
diff --git a/simpleperf/environment.cpp b/simpleperf/environment.cpp
index b7b0355..d65b0df 100644
--- a/simpleperf/environment.cpp
+++ b/simpleperf/environment.cpp
@@ -36,6 +36,7 @@
 #endif
 
 #include "read_elf.h"
+#include "thread_tree.h"
 #include "utils.h"
 
 class LineReader {
diff --git a/simpleperf/environment.h b/simpleperf/environment.h
index 9746720..a6082ff 100644
--- a/simpleperf/environment.h
+++ b/simpleperf/environment.h
@@ -18,6 +18,7 @@
 #define SIMPLE_PERF_ENVIRONMENT_H_
 
 #include <sys/types.h>
+#include <time.h>
 
 #include <functional>
 #include <set>
@@ -29,8 +30,6 @@
 std::vector<int> GetOnlineCpus();
 std::vector<int> GetCpusFromString(const std::string& s);
 
-constexpr char DEFAULT_KERNEL_MMAP_NAME[] = "[kernel.kallsyms]";
-
 struct KernelMmap {
   std::string name;
   uint64_t start_addr;
@@ -46,8 +45,7 @@
 };
 
 bool GetThreadComms(std::vector<ThreadComm>* thread_comms);
-
-constexpr char DEFAULT_EXECNAME_FOR_THREAD_MMAP[] = "//anon";
+bool GetProcessIdForThread(pid_t tid, pid_t* pid);
 
 struct ThreadMmap {
   uint64_t start_addr;
@@ -71,4 +69,13 @@
 bool CheckSampleFrequency(uint64_t sample_freq);
 bool CheckKernelSymbolAddresses();
 
+#if defined(__linux__)
+static inline uint64_t GetSystemClock() {
+  timespec ts;
+  // Assume clock_gettime() doesn't fail.
+  clock_gettime(CLOCK_MONOTONIC, &ts);
+  return ts.tv_sec * 1000000000ULL + ts.tv_nsec;
+}
+#endif
+
 #endif  // SIMPLE_PERF_ENVIRONMENT_H_
diff --git a/simpleperf/event_attr.h b/simpleperf/event_attr.h
index 9182bb9..2d14024 100644
--- a/simpleperf/event_attr.h
+++ b/simpleperf/event_attr.h
@@ -26,6 +26,11 @@
 
 struct EventType;
 
+struct EventAttrWithId {
+  const perf_event_attr* attr;
+  std::vector<uint64_t> ids;
+};
+
 perf_event_attr CreateDefaultPerfEventAttr(const EventType& event_type);
 void DumpPerfEventAttr(const perf_event_attr& attr, size_t indent = 0);
 bool GetCommonEventIdPositionsForAttrs(std::vector<perf_event_attr>& attrs,
diff --git a/simpleperf/event_selection_set.cpp b/simpleperf/event_selection_set.cpp
index d6b1fe6..9202bfa 100644
--- a/simpleperf/event_selection_set.cpp
+++ b/simpleperf/event_selection_set.cpp
@@ -25,6 +25,9 @@
 #include "perf_regs.h"
 #include "utils.h"
 
+constexpr uint64_t DEFAULT_SAMPLE_FREQ_FOR_NONTRACEPOINT_EVENT = 4000;
+constexpr uint64_t DEFAULT_SAMPLE_PERIOD_FOR_TRACEPOINT_EVENT = 1;
+
 bool IsBranchSamplingSupported() {
   const EventType* type = FindEventTypeByName("cpu-cycles");
   if (type == nullptr) {
@@ -106,8 +109,6 @@
     if (!BuildAndCheckEventSelection(event_name, &selection)) {
       return false;
     }
-    selection.selection_id = group.size();
-    selection.group_id = groups_.size();
     group.push_back(std::move(selection));
   }
   groups_.push_back(std::move(group));
@@ -115,6 +116,34 @@
   return true;
 }
 
+std::vector<const EventType*> EventSelectionSet::GetTracepointEvents() const {
+  std::vector<const EventType*> result;
+  for (const auto& group : groups_) {
+    for (const auto& selection : group) {
+      if (selection.event_type_modifier.event_type.type ==
+          PERF_TYPE_TRACEPOINT) {
+        result.push_back(&selection.event_type_modifier.event_type);
+      }
+    }
+  }
+  return result;
+}
+
+std::vector<EventAttrWithId> EventSelectionSet::GetEventAttrWithId() const {
+  std::vector<EventAttrWithId> result;
+  for (const auto& group : groups_) {
+    for (const auto& selection : group) {
+      EventAttrWithId attr_id;
+      attr_id.attr = &selection.event_attr;
+      for (const auto& fd : selection.event_fds) {
+        attr_id.ids.push_back(fd->Id());
+      }
+      result.push_back(attr_id);
+    }
+  }
+  return result;
+}
+
 // Union the sample type of different event attrs can make reading sample
 // records in perf.data easier.
 void EventSelectionSet::UnionSampleType() {
@@ -169,18 +198,39 @@
   }
 }
 
-void EventSelectionSet::SetSampleFreq(const EventSelection& selection,
-                                      uint64_t sample_freq) {
-  EventSelection& sel = groups_[selection.group_id][selection.selection_id];
-  sel.event_attr.freq = 1;
-  sel.event_attr.sample_freq = sample_freq;
+void EventSelectionSet::SetSampleFreq(uint64_t sample_freq) {
+  for (auto& group : groups_) {
+    for (auto& selection : group) {
+      selection.event_attr.freq = 1;
+      selection.event_attr.sample_freq = sample_freq;
+    }
+  }
 }
 
-void EventSelectionSet::SetSamplePeriod(const EventSelection& selection,
-                                        uint64_t sample_period) {
-  EventSelection& sel = groups_[selection.group_id][selection.selection_id];
-  sel.event_attr.freq = 0;
-  sel.event_attr.sample_period = sample_period;
+void EventSelectionSet::SetSamplePeriod(uint64_t sample_period) {
+  for (auto& group : groups_) {
+    for (auto& selection : group) {
+      selection.event_attr.freq = 0;
+      selection.event_attr.sample_period = sample_period;
+    }
+  }
+}
+
+void EventSelectionSet::UseDefaultSampleFreq() {
+  for (auto& group : groups_) {
+    for (auto& selection : group) {
+      if (selection.event_type_modifier.event_type.type ==
+          PERF_TYPE_TRACEPOINT) {
+        selection.event_attr.freq = 0;
+        selection.event_attr.sample_period =
+            DEFAULT_SAMPLE_PERIOD_FOR_TRACEPOINT_EVENT;
+      } else {
+        selection.event_attr.freq = 1;
+        selection.event_attr.sample_freq =
+            DEFAULT_SAMPLE_FREQ_FOR_NONTRACEPOINT_EVENT;
+      }
+    }
+  }
 }
 
 bool EventSelectionSet::SetBranchSampling(uint64_t branch_sample_type) {
@@ -253,6 +303,17 @@
   }
 }
 
+bool EventSelectionSet::NeedKernelSymbol() const {
+  for (const auto& group : groups_) {
+    for (const auto& selection : group) {
+      if (!selection.event_type_modifier.exclude_kernel) {
+        return true;
+      }
+    }
+  }
+  return false;
+}
+
 static bool CheckIfCpusOnline(const std::vector<int>& cpus) {
   std::vector<int> online_cpus = GetOnlineCpus();
   for (const auto& cpu : cpus) {
@@ -265,16 +326,14 @@
   return true;
 }
 
-static bool OpenEventFile(EventSelectionGroup& group, pid_t tid, int cpu,
-                          std::string* failed_event_type) {
+bool EventSelectionSet::OpenEventFilesOnGroup(EventSelectionGroup& group,
+                                              pid_t tid, int cpu,
+                                              std::string* failed_event_type) {
   std::vector<std::unique_ptr<EventFd>> event_fds;
   // Given a tid and cpu, events on the same group should be all opened
   // successfully or all failed to open.
+  EventFd* group_fd = nullptr;
   for (auto& selection : group) {
-    EventFd* group_fd = nullptr;
-    if (selection.selection_id != 0) {
-      group_fd = event_fds[0].get();
-    }
     std::unique_ptr<EventFd> event_fd =
         EventFd::OpenEventFile(selection.event_attr, tid, cpu, group_fd);
     if (event_fd != nullptr) {
@@ -286,6 +345,9 @@
         return false;
       }
     }
+    if (group_fd == nullptr) {
+      group_fd = event_fd.get();
+    }
   }
   for (size_t i = 0; i < group.size(); ++i) {
     group[i].event_fds.push_back(std::move(event_fds[i]));
@@ -319,13 +381,13 @@
       size_t success_cpu_count = 0;
       std::string failed_event_type;
       for (const auto& cpu : cpus) {
-        if (OpenEventFile(group, tid, cpu, &failed_event_type)) {
+        if (OpenEventFilesOnGroup(group, tid, cpu, &failed_event_type)) {
           success_cpu_count++;
         }
       }
       // As the online cpus can be enabled or disabled at runtime, we may not
-      // open event file for all cpus successfully. But we should open at least
-      // one cpu successfully.
+      // open event file for all cpus successfully. But we should open at
+      // least one cpu successfully.
       if (success_cpu_count == 0) {
         PLOG(ERROR) << "failed to open perf event file for event_type "
                     << failed_event_type << " for "
@@ -350,10 +412,12 @@
 
 bool EventSelectionSet::ReadCounters(std::vector<CountersInfo>* counters) {
   counters->clear();
-  for (auto& group : groups_) {
-    for (auto& selection : group) {
+  for (size_t i = 0; i < groups_.size(); ++i) {
+    for (auto& selection : groups_[i]) {
       CountersInfo counters_info;
-      counters_info.selection = &selection;
+      counters_info.group_id = i;
+      counters_info.event_name = selection.event_type_modifier.event_type.name;
+      counters_info.event_modifier = selection.event_type_modifier.modifier;
       counters_info.counters = selection.hotplugged_counters;
       for (auto& event_fd : selection.event_fds) {
         CounterInfo counter;
@@ -550,7 +614,7 @@
   for (auto& group : groups_) {
     for (const auto& tid : threads) {
       std::string failed_event_type;
-      if (!OpenEventFile(group, tid, cpu, &failed_event_type)) {
+      if (!OpenEventFilesOnGroup(group, tid, cpu, &failed_event_type)) {
         // If failed to open event files, maybe the cpu has been offlined.
         PLOG(WARNING) << "failed to open perf event file for event_type "
                       << failed_event_type << " for "
diff --git a/simpleperf/event_selection_set.h b/simpleperf/event_selection_set.h
index 53c8edd..32904c1 100644
--- a/simpleperf/event_selection_set.h
+++ b/simpleperf/event_selection_set.h
@@ -25,6 +25,7 @@
 
 #include <android-base/macros.h>
 
+#include "event_attr.h"
 #include "event_fd.h"
 #include "event_type.h"
 #include "perf_event.h"
@@ -38,25 +39,13 @@
   PerfCounter counter;
 };
 
-struct EventSelection;
-
 struct CountersInfo {
-  const EventSelection* selection;
+  uint32_t group_id;
+  std::string event_name;
+  std::string event_modifier;
   std::vector<CounterInfo> counters;
 };
 
-struct EventSelection {
-  uint32_t group_id;
-  uint32_t selection_id;
-  EventTypeAndModifier event_type_modifier;
-  perf_event_attr event_attr;
-  std::vector<std::unique_ptr<EventFd>> event_fds;
-  // counters for event files closed for cpu hotplug events
-  std::vector<CounterInfo> hotplugged_counters;
-};
-
-typedef std::vector<EventSelection> EventSelectionGroup;
-
 class IOEventLoop;
 
 // EventSelectionSet helps to monitor events. It is used in following steps:
@@ -81,21 +70,23 @@
 
   bool empty() const { return groups_.empty(); }
 
-  const std::vector<EventSelectionGroup>& groups() { return groups_; }
-
   bool AddEventType(const std::string& event_name);
   bool AddEventGroup(const std::vector<std::string>& event_names);
+  std::vector<const EventType*> GetTracepointEvents() const;
+  std::vector<EventAttrWithId> GetEventAttrWithId() const;
 
   void SetEnableOnExec(bool enable);
   bool GetEnableOnExec();
   void SampleIdAll();
-  void SetSampleFreq(const EventSelection& selection, uint64_t sample_freq);
-  void SetSamplePeriod(const EventSelection& selection, uint64_t sample_period);
+  void SetSampleFreq(uint64_t sample_freq);
+  void SetSamplePeriod(uint64_t sample_period);
+  void UseDefaultSampleFreq();
   bool SetBranchSampling(uint64_t branch_sample_type);
   void EnableFpCallChainSampling();
   bool EnableDwarfCallChainSampling(uint32_t dump_stack_size);
   void SetInherit(bool enable);
   void SetLowWatermark();
+  bool NeedKernelSymbol() const;
 
   void AddMonitoredProcesses(const std::set<pid_t>& processes) {
     processes_.insert(processes.begin(), processes.end());
@@ -105,13 +96,9 @@
     threads_.insert(threads.begin(), threads.end());
   }
 
-  const std::set<pid_t>& GetMonitoredProcesses() const {
-    return processes_;
-  }
+  const std::set<pid_t>& GetMonitoredProcesses() const { return processes_; }
 
-  const std::set<pid_t>& GetMonitoredThreads() const {
-    return threads_;
-  }
+  const std::set<pid_t>& GetMonitoredThreads() const { return threads_; }
 
   bool HasMonitoredTarget() const {
     return !processes_.empty() || !threads_.empty();
@@ -131,9 +118,21 @@
           DEFAULT_PERIOD_TO_DETECT_CPU_HOTPLUG_EVENTS_IN_SEC);
 
  private:
+  struct EventSelection {
+    EventTypeAndModifier event_type_modifier;
+    perf_event_attr event_attr;
+    std::vector<std::unique_ptr<EventFd>> event_fds;
+    // counters for event files closed for cpu hotplug events
+    std::vector<CounterInfo> hotplugged_counters;
+  };
+  typedef std::vector<EventSelection> EventSelectionGroup;
+
   bool BuildAndCheckEventSelection(const std::string& event_name,
                                    EventSelection* selection);
   void UnionSampleType();
+  bool OpenEventFilesOnGroup(EventSelectionGroup& group, pid_t tid, int cpu,
+                             std::string* failed_event_type);
+
   bool MmapEventFiles(size_t mmap_pages, bool report_error);
   bool ReadMmapEventDataForFd(EventFd* event_fd);
 
diff --git a/simpleperf/perf_clock.cpp b/simpleperf/perf_clock.cpp
index 9ce4134..f6f6511 100644
--- a/simpleperf/perf_clock.cpp
+++ b/simpleperf/perf_clock.cpp
@@ -25,6 +25,7 @@
 
 #include <android-base/logging.h>
 
+#include "environment.h"
 #include "event_attr.h"
 #include "event_fd.h"
 #include "event_type.h"
@@ -41,16 +42,6 @@
   std::atomic<bool> has_error;
 };
 
-static bool GetSystemClock(uint64_t* time_in_ns) {
-  timespec ts;
-  if (clock_gettime(CLOCK_MONOTONIC, &ts) != 0) {
-    PLOG(ERROR) << "clock_gettime() failed";
-    return false;
-  }
-  *time_in_ns = ts.tv_sec * 1000000000ULL + ts.tv_nsec;
-  return true;
-}
-
 static void ThreadA(ThreadArg* thread_arg) {
   thread_arg->thread_a_tid = syscall(SYS_gettid);
   while (!thread_arg->start_mmap) {
@@ -69,10 +60,7 @@
   // In case current thread is preempted by other threads, we run mmap()
   // multiple times and use the one with the smallest time interval.
   for (size_t i = 0; i < TRY_MMAP_COUNT; ++i) {
-    if (!GetSystemClock(&array[i].start_system_time_in_ns)) {
-      thread_arg->has_error = true;
-      return;
-    }
+    array[i].start_system_time_in_ns = GetSystemClock();
     array[i].mmap_start_addr =
         mmap(NULL, 4096, PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
     if (array[i].mmap_start_addr == MAP_FAILED) {
@@ -81,10 +69,7 @@
       return;
     }
 
-    if (!GetSystemClock(&array[i].end_system_time_in_ns)) {
-      thread_arg->has_error = true;
-      return;
-    }
+    array[i].end_system_time_in_ns = GetSystemClock();
   }
   size_t best_index = 0;
   uint64_t min_duration_in_ns = UINT64_MAX;
@@ -177,12 +162,7 @@
   return true;
 }
 
-bool GetPerfClock(uint64_t* time_in_ns) {
+uint64_t GetPerfClock() {
   CHECK(perf_clock_initialized);
-  uint64_t system_time_in_ns;
-  if (!GetSystemClock(&system_time_in_ns)) {
-    return false;
-  }
-  *time_in_ns = system_time_in_ns + perf_clock_and_system_clock_diff_in_ns;
-  return true;
+  return GetSystemClock() + perf_clock_and_system_clock_diff_in_ns;
 }
diff --git a/simpleperf/perf_clock.h b/simpleperf/perf_clock.h
index 9bd5db9..b4168e2 100644
--- a/simpleperf/perf_clock.h
+++ b/simpleperf/perf_clock.h
@@ -26,6 +26,6 @@
 // InitPerfClock() must be called before GetPerfClock().
 bool InitPerfClock();
 
-bool GetPerfClock(uint64_t* time_in_ns);
+uint64_t GetPerfClock();
 
 #endif  // SIMPLE_PERF_PERF_CLOCK_H_
diff --git a/simpleperf/record_file.h b/simpleperf/record_file.h
index 9277570..2d62343 100644
--- a/simpleperf/record_file.h
+++ b/simpleperf/record_file.h
@@ -28,15 +28,11 @@
 
 #include <android-base/macros.h>
 
+#include "event_attr.h"
 #include "perf_event.h"
 #include "record.h"
 #include "record_file_format.h"
 
-struct AttrWithId {
-  const perf_event_attr* attr;
-  std::vector<uint64_t> ids;
-};
-
 // RecordFileWriter writes to a perf record file, like perf.data.
 class RecordFileWriter {
  public:
@@ -44,7 +40,7 @@
 
   ~RecordFileWriter();
 
-  bool WriteAttrSection(const std::vector<AttrWithId>& attr_ids);
+  bool WriteAttrSection(const std::vector<EventAttrWithId>& attr_ids);
   bool WriteRecord(const Record& record);
   bool SortDataSection();
 
@@ -100,8 +96,8 @@
     return header_;
   }
 
-  std::vector<AttrWithId> AttrSection() const {
-    std::vector<AttrWithId> result(file_attrs_.size());
+  std::vector<EventAttrWithId> AttrSection() const {
+    std::vector<EventAttrWithId> result(file_attrs_.size());
     for (size_t i = 0; i < file_attrs_.size(); ++i) {
       result[i].attr = &file_attrs_[i].attr;
       result[i].ids = event_ids_for_file_attrs_[i];
diff --git a/simpleperf/record_file_test.cpp b/simpleperf/record_file_test.cpp
index f67894d..2d619a2 100644
--- a/simpleperf/record_file_test.cpp
+++ b/simpleperf/record_file_test.cpp
@@ -40,7 +40,7 @@
     perf_event_attr attr = CreateDefaultPerfEventAttr(event_type_modifier->event_type);
     attr.sample_id_all = 1;
     attrs_.push_back(std::unique_ptr<perf_event_attr>(new perf_event_attr(attr)));
-    AttrWithId attr_id;
+    EventAttrWithId attr_id;
     attr_id.attr = attrs_.back().get();
     attr_id.ids.push_back(attrs_.size());  // Fake id.
     attr_ids_.push_back(attr_id);
@@ -48,7 +48,7 @@
 
   TemporaryFile tmpfile_;
   std::vector<std::unique_ptr<perf_event_attr>> attrs_;
-  std::vector<AttrWithId> attr_ids_;
+  std::vector<EventAttrWithId> attr_ids_;
 };
 
 TEST_F(RecordFileTest, smoke) {
@@ -80,7 +80,7 @@
   // Read from a record file.
   std::unique_ptr<RecordFileReader> reader = RecordFileReader::CreateInstance(tmpfile_.path);
   ASSERT_TRUE(reader != nullptr);
-  std::vector<AttrWithId> attrs = reader->AttrSection();
+  std::vector<EventAttrWithId> attrs = reader->AttrSection();
   ASSERT_EQ(1u, attrs.size());
   ASSERT_EQ(0, memcmp(attrs[0].attr, attr_ids_[0].attr, sizeof(perf_event_attr)));
   ASSERT_EQ(attrs[0].ids, attr_ids_[0].ids);
@@ -149,7 +149,7 @@
   // Read from a record file.
   std::unique_ptr<RecordFileReader> reader = RecordFileReader::CreateInstance(tmpfile_.path);
   ASSERT_TRUE(reader != nullptr);
-  std::vector<AttrWithId> attrs = reader->AttrSection();
+  std::vector<EventAttrWithId> attrs = reader->AttrSection();
   ASSERT_EQ(3u, attrs.size());
   for (size_t i = 0; i < attrs.size(); ++i) {
     ASSERT_EQ(0, memcmp(attrs[i].attr, attr_ids_[i].attr, sizeof(perf_event_attr)));
diff --git a/simpleperf/record_file_writer.cpp b/simpleperf/record_file_writer.cpp
index 94de4a7..8b57e83 100644
--- a/simpleperf/record_file_writer.cpp
+++ b/simpleperf/record_file_writer.cpp
@@ -68,7 +68,7 @@
   }
 }
 
-bool RecordFileWriter::WriteAttrSection(const std::vector<AttrWithId>& attr_ids) {
+bool RecordFileWriter::WriteAttrSection(const std::vector<EventAttrWithId>& attr_ids) {
   if (attr_ids.empty()) {
     return false;
   }
diff --git a/simpleperf/report_lib_interface.cpp b/simpleperf/report_lib_interface.cpp
index 096cf49..8522f39 100644
--- a/simpleperf/report_lib_interface.cpp
+++ b/simpleperf/report_lib_interface.cpp
@@ -188,7 +188,7 @@
 Event* ReportLib::GetEventOfCurrentSample() {
   if (!(update_flag_ & UPDATE_FLAG_OF_EVENT)) {
     if (event_attrs_.empty()) {
-      std::vector<AttrWithId> attrs = record_file_reader_->AttrSection();
+      std::vector<EventAttrWithId> attrs = record_file_reader_->AttrSection();
       for (const auto& attr_with_id : attrs) {
         EventAttrWithName attr;
         attr.attr = *attr_with_id.attr;
diff --git a/simpleperf/thread_tree.cpp b/simpleperf/thread_tree.cpp
index 981a5dd..56e5fbf 100644
--- a/simpleperf/thread_tree.cpp
+++ b/simpleperf/thread_tree.cpp
@@ -23,7 +23,6 @@
 #include <android-base/logging.h>
 #include <android-base/stringprintf.h>
 
-#include "environment.h"
 #include "perf_event.h"
 #include "record.h"
 
@@ -59,9 +58,11 @@
     CHECK(pair.second);
     it = pair.first;
   }
-  thread_comm_storage_.push_back(
-      std::unique_ptr<std::string>(new std::string(comm)));
-  it->second->comm = thread_comm_storage_.back()->c_str();
+  if (comm != it->second->comm) {
+    thread_comm_storage_.push_back(
+        std::unique_ptr<std::string>(new std::string(comm)));
+    it->second->comm = thread_comm_storage_.back()->c_str();
+  }
 }
 
 void ThreadTree::ForkThread(int pid, int tid, int ppid, int ptid) {
diff --git a/simpleperf/thread_tree.h b/simpleperf/thread_tree.h
index da32da1..703bc88 100644
--- a/simpleperf/thread_tree.h
+++ b/simpleperf/thread_tree.h
@@ -24,10 +24,14 @@
 #include <set>
 
 #include "dso.h"
-#include "environment.h"
+//#include "environment.h"
 
 struct Record;
 
+constexpr char DEFAULT_KERNEL_MMAP_NAME[] = "[kernel.kallsyms]";
+
+constexpr char DEFAULT_EXECNAME_FOR_THREAD_MMAP[] = "//anon";
+
 namespace simpleperf {
 
 struct MapEntry {