Merge "Simpleperf: add signal handler for SIGCHLD, SIGINT, SIGTERM."
diff --git a/simpleperf/cmd_record.cpp b/simpleperf/cmd_record.cpp
index fe4beac..7cd7ec8 100644
--- a/simpleperf/cmd_record.cpp
+++ b/simpleperf/cmd_record.cpp
@@ -64,6 +64,7 @@
                 "                 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"
@@ -81,6 +82,7 @@
         sample_freq_(1000),
         system_wide_collection_(false),
         branch_sampling_(0),
+        callchain_sampling_(false),
         measured_event_type_(nullptr),
         perf_mmap_pages_(256),
         record_filename_("perf.data") {
@@ -109,6 +111,7 @@
 
   bool system_wide_collection_;
   uint64_t branch_sampling_;
+  bool callchain_sampling_;
   const EventType* measured_event_type_;
   EventSelectionSet event_selection_set_;
 
@@ -249,6 +252,8 @@
         return false;
       }
       use_sample_freq_ = true;
+    } else if (args[i] == "-g") {
+      callchain_sampling_ = true;
     } else if (args[i] == "-j") {
       if (!NextArgumentOrError(args, &i)) {
         return false;
@@ -302,6 +307,9 @@
   if (!event_selection_set_.SetBranchSampling(branch_sampling_)) {
     return false;
   }
+  if (callchain_sampling_) {
+    event_selection_set_.EnableCallChainSampling();
+  }
   return true;
 }
 
diff --git a/simpleperf/cmd_record_test.cpp b/simpleperf/cmd_record_test.cpp
index c513e2e..4a2f3f7 100644
--- a/simpleperf/cmd_record_test.cpp
+++ b/simpleperf/cmd_record_test.cpp
@@ -99,3 +99,7 @@
         << "This test does nothing as branch stack sampling is not supported on this device.";
   }
 }
+
+TEST(record_cmd, callchain_sampling) {
+  ASSERT_TRUE(RecordCmd()->Run({"-g", "sleep", "1"}));
+}
diff --git a/simpleperf/event_selection_set.cpp b/simpleperf/event_selection_set.cpp
index 644938c..b1618bd 100644
--- a/simpleperf/event_selection_set.cpp
+++ b/simpleperf/event_selection_set.cpp
@@ -92,6 +92,12 @@
   return true;
 }
 
+void EventSelectionSet::EnableCallChainSampling() {
+  for (auto& selection : selections_) {
+    selection.event_attr.sample_type |= PERF_SAMPLE_CALLCHAIN;
+  }
+}
+
 bool EventSelectionSet::OpenEventFilesForAllCpus() {
   std::vector<int> cpus = GetOnlineCpus();
   if (cpus.empty()) {
diff --git a/simpleperf/event_selection_set.h b/simpleperf/event_selection_set.h
index e764e0b..dabef14 100644
--- a/simpleperf/event_selection_set.h
+++ b/simpleperf/event_selection_set.h
@@ -54,6 +54,7 @@
   void SetSampleFreq(uint64_t sample_freq);
   void SetSamplePeriod(uint64_t sample_period);
   bool SetBranchSampling(uint64_t branch_sample_type);
+  void EnableCallChainSampling();
 
   bool OpenEventFilesForAllCpus();
   bool OpenEventFilesForProcess(pid_t pid);
diff --git a/simpleperf/record.cpp b/simpleperf/record.cpp
index 4a1edb4..f25c98f 100644
--- a/simpleperf/record.cpp
+++ b/simpleperf/record.cpp
@@ -270,6 +270,12 @@
   if (sample_type & PERF_SAMPLE_PERIOD) {
     MoveFromBinaryFormat(period_data, p);
   }
+  if (sample_type & PERF_SAMPLE_CALLCHAIN) {
+    uint64_t nr;
+    MoveFromBinaryFormat(nr, p);
+    callchain_data.ips.resize(nr);
+    MoveFromBinaryFormat(callchain_data.ips.data(), nr, p);
+  }
   if (sample_type & PERF_SAMPLE_BRANCH_STACK) {
     uint64_t nr;
     MoveFromBinaryFormat(nr, p);
@@ -309,6 +315,12 @@
   if (sample_type & PERF_SAMPLE_PERIOD) {
     PrintIndented(indent, "period %" PRId64 "\n", period_data.period);
   }
+  if (sample_type & PERF_SAMPLE_CALLCHAIN) {
+    PrintIndented(indent, "callchain nr=%" PRIu64 "\n", callchain_data.ips.size());
+    for (auto& ip : callchain_data.ips) {
+      PrintIndented(indent + 1, "0x%" PRIx64 "\n", ip);
+    }
+  }
   if (sample_type & PERF_SAMPLE_BRANCH_STACK) {
     PrintIndented(indent, "branch_stack nr=%" PRIu64 "\n", branch_stack_data.stack.size());
     for (auto& item : branch_stack_data.stack) {
diff --git a/simpleperf/record.h b/simpleperf/record.h
index bbcd7d0..25b91ec 100644
--- a/simpleperf/record.h
+++ b/simpleperf/record.h
@@ -68,6 +68,10 @@
   uint64_t period;
 };
 
+struct PerfSampleCallChainType {
+  std::vector<uint64_t> ips;
+};
+
 struct PerfSampleBranchStackType {
   struct BranchStackItemType {
     uint64_t from;
@@ -186,6 +190,7 @@
   PerfSampleCpuType cpu_data;             // Valid if PERF_SAMPLE_CPU.
   PerfSamplePeriodType period_data;       // Valid if PERF_SAMPLE_PERIOD.
 
+  PerfSampleCallChainType callchain_data;       // Valid if PERF_SAMPLE_CALLCHAIN.
   PerfSampleBranchStackType branch_stack_data;  // Valid if PERF_SAMPLE_BRANCH_STACK.
 
   SampleRecord(const perf_event_attr& attr, const perf_event_header* pheader);