Merge "Simpleperf: add --max-stack and --percent-limit options for report cmd."
diff --git a/simpleperf/SampleDisplayer.h b/simpleperf/SampleDisplayer.h
index 606f639..bc74e3d 100644
--- a/simpleperf/SampleDisplayer.h
+++ b/simpleperf/SampleDisplayer.h
@@ -96,6 +96,10 @@
 template <typename SampleT, typename CallChainNodeT>
 class CallgraphDisplayer {
  public:
+  CallgraphDisplayer(uint32_t max_stack = UINT32_MAX,
+                     double percent_limit = 0.0)
+      : max_stack_(max_stack), percent_limit_(percent_limit) {}
+
   virtual ~CallgraphDisplayer() {}
 
   void operator()(FILE* fp, const SampleT* sample) {
@@ -113,21 +117,23 @@
   void DisplayCallGraphEntry(FILE* fp, size_t depth, std::string prefix,
                              const std::unique_ptr<CallChainNodeT>& node,
                              uint64_t parent_period, bool last) {
-    if (depth > 20) {
-      LOG(WARNING) << "truncated callgraph at depth " << depth;
+    if (depth > max_stack_) {
       return;
     }
-    prefix += "|";
-    fprintf(fp, "%s\n", prefix.c_str());
-    if (last) {
-      prefix.back() = ' ';
-    }
     std::string percentage_s = "-- ";
     if (node->period + node->children_period != parent_period) {
       double percentage =
           100.0 * (node->period + node->children_period) / parent_period;
+      if (percentage < percent_limit_) {
+        return;
+      }
       percentage_s = android::base::StringPrintf("--%.2f%%-- ", percentage);
     }
+    prefix += "|";
+    fprintf(fp, "%s\n", prefix.c_str());
+    if (last) {
+      prefix.back() = ' ';
+    }
     fprintf(fp, "%s%s%s\n", prefix.c_str(), percentage_s.c_str(),
             PrintSampleName(node->chain[0]).c_str());
     prefix.append(percentage_s.size(), ' ');
@@ -146,6 +152,10 @@
   virtual std::string PrintSampleName(const SampleT* sample) {
     return sample->symbol->DemangledName();
   }
+
+ private:
+  uint32_t max_stack_;
+  double percent_limit_;
 };
 
 // SampleDisplayer is a class using a collections of display functions to show a
diff --git a/simpleperf/cmd_record.cpp b/simpleperf/cmd_record.cpp
index a076778..1cbbd8f 100644
--- a/simpleperf/cmd_record.cpp
+++ b/simpleperf/cmd_record.cpp
@@ -26,6 +26,7 @@
 
 #include <android-base/logging.h>
 #include <android-base/file.h>
+#include <android-base/parsedouble.h>
 #include <android-base/parseint.h>
 #include <android-base/strings.h>
 
@@ -418,10 +419,8 @@
       if (!NextArgumentOrError(args, &i)) {
         return false;
       }
-      errno = 0;
-      char* endptr;
-      duration_in_sec_ = strtod(args[i].c_str(), &endptr);
-      if (duration_in_sec_ <= 0 || *endptr != '\0' || errno == ERANGE) {
+      if (!android::base::ParseDouble(args[i].c_str(), &duration_in_sec_,
+                                      1e-9)) {
         LOG(ERROR) << "Invalid duration: " << args[i].c_str();
         return false;
       }
diff --git a/simpleperf/cmd_report.cpp b/simpleperf/cmd_report.cpp
index 830ec13..7fd0f61 100644
--- a/simpleperf/cmd_report.cpp
+++ b/simpleperf/cmd_report.cpp
@@ -25,6 +25,7 @@
 #include <vector>
 
 #include <android-base/logging.h>
+#include <android-base/parsedouble.h>
 #include <android-base/parseint.h>
 #include <android-base/stringprintf.h>
 #include <android-base/strings.h>
@@ -276,10 +277,12 @@
 "                      the graph shows how functions call others.\n"
 "                      Default is caller mode.\n"
 "-i <file>  Specify path of record file, default is perf.data.\n"
+"--max-stack <frames>  Set max stack frames shown when printing call graph.\n"
 "-n         Print the sample count for each item.\n"
 "--no-demangle         Don't demangle symbol names.\n"
 "--no-show-ip          Don't show vaddr in file for unknown symbols.\n"
 "-o report_file_name   Set report file name, default is stdout.\n"
+"--percent-limit <percent>  Set min percentage shown when printing call graph.\n"
 "--pids pid1,pid2,...  Report only for selected pids.\n"
 "--sort key1,key2,...  Select keys used to sort and print the report. The\n"
 "                      appearance order of keys decides the order of keys used\n"
@@ -312,7 +315,9 @@
         system_wide_collection_(false),
         accumulate_callchain_(false),
         print_callgraph_(false),
-        callgraph_show_callee_(false) {}
+        callgraph_show_callee_(false),
+        callgraph_max_stack_(UINT32_MAX),
+        callgraph_percent_limit_(0) {}
 
   bool Run(const std::vector<std::string>& args);
 
@@ -341,6 +346,8 @@
   bool accumulate_callchain_;
   bool print_callgraph_;
   bool callgraph_show_callee_;
+  uint32_t callgraph_max_stack_;
+  double callgraph_percent_limit_;
 
   std::string report_filename_;
 };
@@ -423,6 +430,14 @@
       }
       record_filename_ = args[i];
 
+    } else if (args[i] == "--max-stack") {
+      if (!NextArgumentOrError(args, &i)) {
+        return false;
+      }
+      if (!android::base::ParseUint(args[i].c_str(), &callgraph_max_stack_)) {
+        LOG(ERROR) << "invalid arg for --max-stack: " << args[i];
+        return false;
+      }
     } else if (args[i] == "-n") {
       print_sample_count = true;
 
@@ -435,7 +450,14 @@
         return false;
       }
       report_filename_ = args[i];
-
+    } else if (args[i] == "--percent-limit") {
+      if (!NextArgumentOrError(args, &i)) {
+        return false;
+      }
+      if (!android::base::ParseDouble(args[i].c_str(),
+                                      &callgraph_percent_limit_, 0.0)) {
+        LOG(ERROR) << "invalid arg for --percent-limit: " << args[i];
+      }
     } else if (args[i] == "--pids" || args[i] == "--tids") {
       const std::string& option = args[i];
       std::unordered_set<int>& filter =
@@ -562,7 +584,8 @@
         displayer.AddExclusiveDisplayFunction(
             ReportCmdCallgraphDisplayerWithVaddrInFile());
       } else {
-        displayer.AddExclusiveDisplayFunction(ReportCmdCallgraphDisplayer());
+        displayer.AddExclusiveDisplayFunction(ReportCmdCallgraphDisplayer(
+            callgraph_max_stack_, callgraph_percent_limit_));
       }
     }
   }
diff --git a/simpleperf/cmd_report_test.cpp b/simpleperf/cmd_report_test.cpp
index 704076a..e00b5ee 100644
--- a/simpleperf/cmd_report_test.cpp
+++ b/simpleperf/cmd_report_test.cpp
@@ -415,6 +415,28 @@
   ASSERT_TRUE(success);
 }
 
+TEST_F(ReportCommandTest, max_stack_and_percent_limit_option) {
+  Report(PERF_DATA_MAX_STACK_AND_PERCENT_LIMIT, {"-g"});
+  ASSERT_TRUE(success);
+  ASSERT_NE(content.find("89.03"), std::string::npos);
+
+  Report(PERF_DATA_MAX_STACK_AND_PERCENT_LIMIT, {"-g", "--max-stack", "0"});
+  ASSERT_TRUE(success);
+  ASSERT_EQ(content.find("89.03"), std::string::npos);
+  Report(PERF_DATA_MAX_STACK_AND_PERCENT_LIMIT, {"-g", "--max-stack", "1"});
+  ASSERT_TRUE(success);
+  ASSERT_NE(content.find("89.03"), std::string::npos);
+
+  Report(PERF_DATA_MAX_STACK_AND_PERCENT_LIMIT,
+         {"-g", "--percent-limit", "90"});
+  ASSERT_TRUE(success);
+  ASSERT_EQ(content.find("89.03"), std::string::npos);
+  Report(PERF_DATA_MAX_STACK_AND_PERCENT_LIMIT,
+         {"-g", "--percent-limit", "70"});
+  ASSERT_TRUE(success);
+  ASSERT_NE(content.find("89.03"), std::string::npos);
+}
+
 #if defined(__linux__)
 #include "event_selection_set.h"
 
diff --git a/simpleperf/cmd_stat.cpp b/simpleperf/cmd_stat.cpp
index 9c5cf8f..06258f3 100644
--- a/simpleperf/cmd_stat.cpp
+++ b/simpleperf/cmd_stat.cpp
@@ -27,6 +27,7 @@
 #include <vector>
 
 #include <android-base/logging.h>
+#include <android-base/parsedouble.h>
 #include <android-base/strings.h>
 
 #include "command.h"
@@ -452,10 +453,8 @@
       if (!NextArgumentOrError(args, &i)) {
         return false;
       }
-      errno = 0;
-      char* endptr;
-      duration_in_sec_ = strtod(args[i].c_str(), &endptr);
-      if (duration_in_sec_ <= 0 || *endptr != '\0' || errno == ERANGE) {
+      if (!android::base::ParseDouble(args[i].c_str(), &duration_in_sec_,
+                                      1e-9)) {
         LOG(ERROR) << "Invalid duration: " << args[i].c_str();
         return false;
       }
@@ -463,10 +462,8 @@
       if (!NextArgumentOrError(args, &i)) {
         return false;
       }
-      errno = 0;
-      char* endptr;
-      interval_in_ms_ = strtod(args[i].c_str(), &endptr);
-      if (interval_in_ms_ <= 0 || *endptr != '\0' || errno == ERANGE) {
+      if (!android::base::ParseDouble(args[i].c_str(), &interval_in_ms_,
+                                      1e-9)) {
         LOG(ERROR) << "Invalid interval: " << args[i].c_str();
         return false;
       }
diff --git a/simpleperf/get_test_data.h b/simpleperf/get_test_data.h
index 339871e..4550843 100644
--- a/simpleperf/get_test_data.h
+++ b/simpleperf/get_test_data.h
@@ -99,4 +99,7 @@
 // generated_by_linux_perf.data is generated by `perf record -F 1 -a -g -- sleep 0.1`.
 static const std::string PERF_DATA_GENERATED_BY_LINUX_PERF = "generated_by_linux_perf.data";
 
+// generated by `simpleperf record -g ls`.
+static const std::string PERF_DATA_MAX_STACK_AND_PERCENT_LIMIT = "perf_test_max_stack_and_percent_limit.data";
+
 #endif  // SIMPLE_PERF_GET_TEST_DATA_H_
diff --git a/simpleperf/testdata/perf_test_max_stack_and_percent_limit.data b/simpleperf/testdata/perf_test_max_stack_and_percent_limit.data
new file mode 100644
index 0000000..b3fc225
--- /dev/null
+++ b/simpleperf/testdata/perf_test_max_stack_and_percent_limit.data
Binary files differ