Merge "simpleperf: exclude kernel callchains when needed."
diff --git a/simpleperf/cmd_record.cpp b/simpleperf/cmd_record.cpp
index 4e7276c..bb1fc0f 100644
--- a/simpleperf/cmd_record.cpp
+++ b/simpleperf/cmd_record.cpp
@@ -182,7 +182,8 @@
         lost_record_count_(0),
         start_profiling_fd_(-1),
         in_app_context_(false),
-        trace_offcpu_(false) {
+        trace_offcpu_(false),
+        exclude_kernel_callchain_(false) {
     // Stop profiling if parent exits.
     prctl(PR_SET_PDEATHSIG, SIGHUP, 0, 0, 0);
     app_package_name_ = GetDefaultAppPackageName();
@@ -245,6 +246,7 @@
   std::string app_package_name_;
   bool in_app_context_;
   bool trace_offcpu_;
+  bool exclude_kernel_callchain_;
 };
 
 bool RecordCommand::Run(const std::vector<std::string>& args) {
@@ -271,6 +273,7 @@
       return false;
     }
   }
+  exclude_kernel_callchain_ = event_selection_set_.ExcludeKernel();
   if (trace_offcpu_ && !TraceOffCpu()) {
     return false;
   }
@@ -890,6 +893,12 @@
     }
   }
   if (record->type() == PERF_RECORD_SAMPLE) {
+    if (record->InKernel() && exclude_kernel_callchain_) {
+      if (static_cast<SampleRecord*>(record)->ExcludeKernelCallChain() == 0u) {
+        // If current record contains no user callchain, skip it.
+        return true;
+      }
+    }
     sample_record_count_++;
   } else if (record->type() == PERF_RECORD_LOST) {
     lost_record_count_ += static_cast<LostRecord*>(record)->lost;
diff --git a/simpleperf/cmd_report.cpp b/simpleperf/cmd_report.cpp
index 5965e63..cc62285 100644
--- a/simpleperf/cmd_report.cpp
+++ b/simpleperf/cmd_report.cpp
@@ -771,7 +771,6 @@
 
 bool ReportCommand::ReadEventAttrFromRecordFile() {
   std::vector<EventAttrWithId> attrs = record_file_reader_->AttrSection();
-  LOG(ERROR) << "attrs.size() = " << attrs.size();
   for (const auto& attr_with_id : attrs) {
     EventAttrWithName attr;
     attr.attr = *attr_with_id.attr;
@@ -795,7 +794,6 @@
   if (trace_offcpu_) {
     size_t i;
     for (i = 0; i < event_attrs_.size(); ++i) {
-      LOG(ERROR) << "event_attrs[" << i << "].name = " << event_attrs_[i].name;
       if (event_attrs_[i].name == "sched:sched_switch") {
         break;
       }
diff --git a/simpleperf/cmd_report_test.cpp b/simpleperf/cmd_report_test.cpp
index f1a9eb6..703ca8f 100644
--- a/simpleperf/cmd_report_test.cpp
+++ b/simpleperf/cmd_report_test.cpp
@@ -521,4 +521,17 @@
   }
 }
 
+TEST_F(ReportCommandTest, exclude_kernel_callchain) {
+  TEST_REQUIRE_HOST_ROOT();
+  std::vector<std::unique_ptr<Workload>> workloads;
+  CreateProcesses(1, &workloads);
+  std::string pid = std::to_string(workloads[0]->GetPid());
+  TemporaryFile tmpfile;
+  ASSERT_TRUE(RecordCmd()->Run({"--trace-offcpu", "-e", "cpu-cycles:u", "-p", pid,
+                                "--duration", "2", "-o", tmpfile.path, "-g"}));
+  ReportRaw(tmpfile.path, {"-g"});
+  ASSERT_TRUE(success);
+  ASSERT_EQ(content.find("[kernel.kallsyms]"), std::string::npos);
+}
+
 #endif
diff --git a/simpleperf/event_selection_set.cpp b/simpleperf/event_selection_set.cpp
index 5f8cf5c..de04d3d 100644
--- a/simpleperf/event_selection_set.cpp
+++ b/simpleperf/event_selection_set.cpp
@@ -189,6 +189,17 @@
   return result;
 }
 
+bool EventSelectionSet::ExcludeKernel() const {
+  for (const auto& group : groups_) {
+    for (const auto& selection : group) {
+      if (!selection.event_type_modifier.exclude_kernel) {
+        return false;
+      }
+    }
+  }
+  return true;
+}
+
 bool EventSelectionSet::HasInplaceSampler() const {
   for (const auto& group : groups_) {
     for (const auto& sel : group) {
diff --git a/simpleperf/event_selection_set.h b/simpleperf/event_selection_set.h
index f5cb53a..3a3bd46 100644
--- a/simpleperf/event_selection_set.h
+++ b/simpleperf/event_selection_set.h
@@ -75,6 +75,7 @@
   bool AddEventGroup(const std::vector<std::string>& event_names);
   std::vector<const EventType*> GetEvents() const;
   std::vector<const EventType*> GetTracepointEvents() const;
+  bool ExcludeKernel() const;
   bool HasInplaceSampler() const;
   std::vector<EventAttrWithId> GetEventAttrWithId() const;
 
diff --git a/simpleperf/record.cpp b/simpleperf/record.cpp
index 65e98ea..f354815 100644
--- a/simpleperf/record.cpp
+++ b/simpleperf/record.cpp
@@ -594,6 +594,31 @@
   *reinterpret_cast<uint64_t*>(p) = callchain_data.ip_nr;
 }
 
+size_t SampleRecord::ExcludeKernelCallChain() {
+  size_t user_callchain_length = 0u;
+  if (sample_type & PERF_SAMPLE_CALLCHAIN) {
+    size_t i;
+    for (i = 0; i < callchain_data.ip_nr; ++i) {
+      if (callchain_data.ips[i] == PERF_CONTEXT_USER) {
+        i++;
+        if (i < callchain_data.ip_nr) {
+          ip_data.ip = callchain_data.ips[i];
+          if (sample_type & PERF_SAMPLE_IP) {
+            *reinterpret_cast<uint64_t*>(const_cast<char*>(binary_ + header_size())) = ip_data.ip;
+          }
+          header.misc = (header.misc & ~PERF_RECORD_MISC_KERNEL) | PERF_RECORD_MISC_USER;
+          reinterpret_cast<perf_event_header*>(const_cast<char*>(binary_))->misc = header.misc;
+        }
+        break;
+      } else {
+        const_cast<uint64_t*>(callchain_data.ips)[i] = PERF_CONTEXT_USER;
+      }
+    }
+    user_callchain_length = callchain_data.ip_nr - i;
+  }
+  return user_callchain_length;
+}
+
 void SampleRecord::DumpData(size_t indent) const {
   PrintIndented(indent, "sample_type: 0x%" PRIx64 "\n", sample_type);
   if (sample_type & PERF_SAMPLE_IP) {
diff --git a/simpleperf/record.h b/simpleperf/record.h
index d6ee2ce..1f8e5b7 100644
--- a/simpleperf/record.h
+++ b/simpleperf/record.h
@@ -387,6 +387,7 @@
                uint64_t period, const std::vector<uint64_t>& ips);
 
   void ReplaceRegAndStackWithCallChain(const std::vector<uint64_t>& ips);
+  size_t ExcludeKernelCallChain();
   uint64_t Timestamp() const override;
   uint32_t Cpu() const override;
   uint64_t Id() const override;
diff --git a/simpleperf/record_test.cpp b/simpleperf/record_test.cpp
index c6f8e9c..5004ae9 100644
--- a/simpleperf/record_test.cpp
+++ b/simpleperf/record_test.cpp
@@ -142,3 +142,33 @@
   ASSERT_EQ(1u, last_records.size());
   ASSERT_EQ(r2, last_records[0].get());
 }
+
+TEST_F(RecordTest, SampleRecord_exclude_kernel_callchain) {
+  SampleRecord r(event_attr, 0, 1, 0, 0, 0, 0, 0, {});
+  ASSERT_EQ(0u, r.ExcludeKernelCallChain());
+
+  event_attr.sample_type |= PERF_SAMPLE_CALLCHAIN;
+  SampleRecord r1(event_attr, 0, 1, 0, 0, 0, 0, 0, {PERF_CONTEXT_USER, 2});
+  ASSERT_EQ(1u, r1.ExcludeKernelCallChain());
+  ASSERT_EQ(2u, r1.ip_data.ip);
+  SampleRecord r2(event_attr, r1.Binary());
+  ASSERT_EQ(1u, r.ip_data.ip);
+  ASSERT_EQ(2u, r2.callchain_data.ip_nr);
+  ASSERT_EQ(PERF_CONTEXT_USER, r2.callchain_data.ips[0]);
+  ASSERT_EQ(2u, r2.callchain_data.ips[1]);
+
+  SampleRecord r3(event_attr, 0, 1, 0, 0, 0, 0, 0, {1, PERF_CONTEXT_USER, 2});
+  ASSERT_EQ(1u, r3.ExcludeKernelCallChain());
+  ASSERT_EQ(2u, r3.ip_data.ip);
+  SampleRecord r4(event_attr, r3.Binary());
+  ASSERT_EQ(2u, r4.ip_data.ip);
+  ASSERT_EQ(3u, r4.callchain_data.ip_nr);
+  ASSERT_EQ(PERF_CONTEXT_USER, r4.callchain_data.ips[0]);
+  ASSERT_EQ(PERF_CONTEXT_USER, r4.callchain_data.ips[1]);
+  ASSERT_EQ(2u, r4.callchain_data.ips[2]);
+
+  SampleRecord r5(event_attr, 0, 1, 0, 0, 0, 0, 0, {1, 2});
+  ASSERT_EQ(0u, r5.ExcludeKernelCallChain());
+  SampleRecord r6(event_attr, 0, 1, 0, 0, 0, 0, 0, {1, 2, PERF_CONTEXT_USER});
+  ASSERT_EQ(0u, r6.ExcludeKernelCallChain());
+}