OSDN Git Service

Merge "libfec: correct verity table only if it's invalid"
authorTreehugger Robot <treehugger-gerrit@google.com>
Thu, 20 Oct 2016 18:16:52 +0000 (18:16 +0000)
committerGerrit Code Review <noreply-gerritcodereview@google.com>
Thu, 20 Oct 2016 18:16:52 +0000 (18:16 +0000)
simpleperf/IOEventLoop.cpp
simpleperf/IOEventLoop.h
simpleperf/IOEventLoop_test.cpp
simpleperf/SampleDisplayer.h
simpleperf/cmd_record.cpp
simpleperf/cmd_report.cpp
simpleperf/cmd_report_test.cpp
simpleperf/cmd_stat.cpp
simpleperf/get_test_data.h
simpleperf/testdata/perf_test_max_stack_and_percent_limit.data [new file with mode: 0644]

index de232ea..44de289 100644 (file)
@@ -25,9 +25,10 @@ struct IOEvent {
   IOEventLoop* loop;
   event* e;
   std::function<bool()> callback;
+  bool enabled;
 
   IOEvent(IOEventLoop* loop, const std::function<bool()>& callback)
-      : loop(loop), e(nullptr), callback(callback) {}
+      : loop(loop), e(nullptr), callback(callback), enabled(false) {}
 
   ~IOEvent() {
     if (e != nullptr) {
@@ -80,6 +81,14 @@ IOEventRef IOEventLoop::AddReadEvent(int fd,
   return AddEvent(fd, EV_READ | EV_PERSIST, nullptr, callback);
 }
 
+IOEventRef IOEventLoop::AddWriteEvent(int fd,
+                                      const std::function<bool()>& callback) {
+  if (!MakeFdNonBlocking(fd)) {
+    return nullptr;
+  }
+  return AddEvent(fd, EV_WRITE | EV_PERSIST, nullptr, callback);
+}
+
 bool IOEventLoop::AddSignalEvent(int sig,
                                  const std::function<bool()>& callback) {
   return AddEvent(sig, EV_SIGNAL | EV_PERSIST, nullptr, callback) != nullptr;
@@ -115,6 +124,7 @@ IOEventRef IOEventLoop::AddEvent(int fd_or_sig, short events, timeval* timeout,
     LOG(ERROR) << "event_add() failed";
     return nullptr;
   }
+  e->enabled = true;
   events_.push_back(std::move(e));
   return events_.back().get();
 }
@@ -138,14 +148,33 @@ bool IOEventLoop::ExitLoop() {
   return true;
 }
 
+bool IOEventLoop::DisableEvent(IOEventRef ref) {
+  if (ref->enabled) {
+    if (event_del(ref->e) != 0) {
+      LOG(ERROR) << "event_del() failed";
+      return false;
+    }
+    ref->enabled = false;
+  }
+  return true;
+}
+
+bool IOEventLoop::EnableEvent(IOEventRef ref) {
+  if (!ref->enabled) {
+    if (event_add(ref->e, nullptr) != 0) {
+      LOG(ERROR) << "event_add() failed";
+      return false;
+    }
+    ref->enabled = true;
+  }
+  return true;
+}
+
 bool IOEventLoop::DelEvent(IOEventRef ref) {
+  DisableEvent(ref);
   IOEventLoop* loop = ref->loop;
   for (auto it = loop->events_.begin(); it != loop->events_.end(); ++it) {
     if (it->get() == ref) {
-      if (event_del((*it)->e) != 0) {
-        LOG(ERROR) << "event_del() failed";
-        return false;
-      }
       loop->events_.erase(it);
       break;
     }
index f35e9e0..9b96629 100644 (file)
@@ -40,6 +40,10 @@ class IOEventLoop {
   // to control the Event, otherwise return nullptr.
   IOEventRef AddReadEvent(int fd, const std::function<bool()>& callback);
 
+  // Register a write Event, so [callback] is called when [fd] can be written
+  // without blocking.
+  IOEventRef AddWriteEvent(int fd, const std::function<bool()>& callback);
+
   // Register a signal Event, so [callback] is called each time signal [sig]
   // happens.
   bool AddSignalEvent(int sig, const std::function<bool()>& callback);
@@ -60,6 +64,11 @@ class IOEventLoop {
   // Exit the loop started by RunLoop().
   bool ExitLoop();
 
+  // Disable an Event, which can be enabled later.
+  static bool DisableEvent(IOEventRef ref);
+  // Enable a disabled Event.
+  static bool EnableEvent(IOEventRef ref);
+
   // Unregister an Event.
   static bool DelEvent(IOEventRef ref);
 
index 68475c7..90bb4fa 100644 (file)
@@ -25,10 +25,8 @@ TEST(IOEventLoop, read) {
   int fd[2];
   ASSERT_EQ(0, pipe(fd));
   IOEventLoop loop;
-  static int count;
-  static int retry_count;
-  count = 0;
-  retry_count = 0;
+  int count = 0;
+  int retry_count = 0;
   ASSERT_NE(nullptr, loop.AddReadEvent(fd[0], [&]() {
     while (true) {
       char c;
@@ -62,10 +60,45 @@ TEST(IOEventLoop, read) {
   close(fd[1]);
 }
 
+TEST(IOEventLoop, write) {
+  int fd[2];
+  ASSERT_EQ(0, pipe(fd));
+  IOEventLoop loop;
+  int count = 0;
+  ASSERT_NE(nullptr, loop.AddWriteEvent(fd[1], [&]() {
+    int ret = 0;
+    char buf[4096];
+    while ((ret = write(fd[1], buf, sizeof(buf))) > 0) {
+    }
+    if (ret == -1 && errno == EAGAIN) {
+      if (++count == 100) {
+        loop.ExitLoop();
+      }
+      return true;
+    }
+    return false;
+  }));
+  std::thread thread([&]() {
+    usleep(500000);
+    while (true) {
+      usleep(1000);
+      char buf[4096];
+      if (read(fd[0], buf, sizeof(buf)) <= 0) {
+        break;
+      }
+    }
+  });
+  ASSERT_TRUE(loop.RunLoop());
+  // close fd[1] to make read thread stop.
+  close(fd[1]);
+  thread.join();
+  close(fd[0]);
+  ASSERT_EQ(100, count);
+}
+
 TEST(IOEventLoop, signal) {
   IOEventLoop loop;
-  static int count;
-  count = 0;
+  int count = 0;
   ASSERT_TRUE(loop.AddSignalEvent(SIGINT, [&]() {
     if (++count == 100) {
       loop.ExitLoop();
@@ -87,8 +120,7 @@ TEST(IOEventLoop, periodic) {
   timeval tv;
   tv.tv_sec = 0;
   tv.tv_usec = 1000;
-  static int count;
-  count = 0;
+  int count = 0;
   IOEventLoop loop;
   ASSERT_TRUE(loop.AddPeriodicEvent(tv, [&]() {
     if (++count == 100) {
@@ -113,8 +145,7 @@ TEST(IOEventLoop, read_and_del_event) {
   int fd[2];
   ASSERT_EQ(0, pipe(fd));
   IOEventLoop loop;
-  static int count;
-  count = 0;
+  int count = 0;
   IOEventRef ref = loop.AddReadEvent(fd[0], [&]() {
     count++;
     return IOEventLoop::DelEvent(ref);
@@ -134,3 +165,40 @@ TEST(IOEventLoop, read_and_del_event) {
   close(fd[0]);
   close(fd[1]);
 }
+
+TEST(IOEventLoop, disable_enable_event) {
+  int fd[2];
+  ASSERT_EQ(0, pipe(fd));
+  IOEventLoop loop;
+  int count = 0;
+  IOEventRef ref = loop.AddWriteEvent(fd[1], [&]() {
+    count++;
+    return IOEventLoop::DisableEvent(ref);
+  });
+  ASSERT_NE(nullptr, ref);
+
+  timeval tv;
+  tv.tv_sec = 0;
+  tv.tv_usec = 500000;
+  int periodic_count = 0;
+  ASSERT_TRUE(loop.AddPeriodicEvent(tv, [&]() {
+    periodic_count++;
+    if (periodic_count == 1) {
+      if (count != 1) {
+        return false;
+      }
+      return IOEventLoop::EnableEvent(ref);
+    } else {
+      if (count != 2) {
+        return false;
+      }
+      return loop.ExitLoop();
+    }
+  }));
+
+  ASSERT_TRUE(loop.RunLoop());
+  ASSERT_EQ(2, count);
+  ASSERT_EQ(2, periodic_count);
+  close(fd[0]);
+  close(fd[1]);
+}
index 606f639..bc74e3d 100644 (file)
@@ -96,6 +96,10 @@ std::string DisplaySymbolFrom(const EntryT* sample) {
 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 @@ class CallgraphDisplayer {
   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 @@ class CallgraphDisplayer {
   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
index a076778..1cbbd8f 100644 (file)
@@ -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 @@ bool RecordCommand::ParseOptions(const std::vector<std::string>& args,
       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;
       }
index 830ec13..7fd0f61 100644 (file)
@@ -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 @@ class ReportCommand : public Command {
 "                      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 @@ class ReportCommand : public Command {
         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 @@ class ReportCommand : public Command {
   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 @@ bool ReportCommand::ParseOptions(const std::vector<std::string>& args) {
       }
       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 @@ bool ReportCommand::ParseOptions(const std::vector<std::string>& args) {
         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 @@ bool ReportCommand::ParseOptions(const std::vector<std::string>& args) {
         displayer.AddExclusiveDisplayFunction(
             ReportCmdCallgraphDisplayerWithVaddrInFile());
       } else {
-        displayer.AddExclusiveDisplayFunction(ReportCmdCallgraphDisplayer());
+        displayer.AddExclusiveDisplayFunction(ReportCmdCallgraphDisplayer(
+            callgraph_max_stack_, callgraph_percent_limit_));
       }
     }
   }
index 704076a..e00b5ee 100644 (file)
@@ -415,6 +415,28 @@ TEST_F(ReportCommandTest, report_data_generated_by_linux_perf) {
   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"
 
index 9c5cf8f..06258f3 100644 (file)
@@ -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 @@ bool StatCommand::ParseOptions(const std::vector<std::string>& args,
       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 @@ bool StatCommand::ParseOptions(const std::vector<std::string>& args,
       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;
       }
index 339871e..4550843 100644 (file)
@@ -99,4 +99,7 @@ static const std::string SYMFS_FOR_READ_ELF_FILE_WARNING = "data/symfs_for_read_
 // 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 (file)
index 0000000..b3fc225
Binary files /dev/null and b/simpleperf/testdata/perf_test_max_stack_and_percent_limit.data differ