simpleperf: adjust mapped buffer size in record command.

Instead of fixing the mapped buffer size, we can adjust
it at runtime. So users don't need to adjust -m manually.

Bug: 29574526
Change-Id: Icb580df3d60f8d2cf554c0d4139e6f7f64b19f8f
Test: run simpleperf_unit_test.
diff --git a/simpleperf/cmd_record.cpp b/simpleperf/cmd_record.cpp
index 050ca85..9690e1d 100644
--- a/simpleperf/cmd_record.cpp
+++ b/simpleperf/cmd_record.cpp
@@ -65,6 +65,11 @@
 // should be a multiply of 8, so MAX_DUMP_STACK_SIZE is 65528.
 constexpr uint32_t MAX_DUMP_STACK_SIZE = 65528;
 
+// The max allowed pages in mapped buffer is decided by rlimit(RLIMIT_MEMLOCK).
+// Here 1024 is a desired value for pages in mapped buffer. If mapped
+// successfully, the buffer size = 1024 * 4K (page size) = 4M.
+constexpr size_t DESIRED_PAGES_IN_MAPPED_BUFFER = 1024;
+
 class RecordCommand : public Command {
  public:
   RecordCommand()
@@ -117,9 +122,8 @@
 "             This option requires at least one branch type among any, any_call,\n"
 "             any_ret, ind_call.\n"
 "-m mmap_pages   Set the size of the buffer used to receiving sample data from\n"
-"                the kernel. It should be a power of 2. The default value for\n"
-"                system wide profiling is 256. The default value for non system\n"
-"                wide profiling is 128.\n"
+"                the kernel. It should be a power of 2. If not set, the max\n"
+"                possible value <= 1024 will be used.\n"
 "--no-dump-kernel-symbols  Don't dump kernel symbols in perf.data. By default\n"
 "                          kernel symbols will be dumped when needed.\n"
 "--no-inherit  Don't record created child threads/processes.\n"
@@ -153,7 +157,7 @@
         child_inherit_(true),
         can_dump_kernel_symbols_(true),
         dump_symbols_(false),
-        perf_mmap_pages_(0),
+        mmap_page_range_(std::make_pair(1, DESIRED_PAGES_IN_MAPPED_BUFFER)),
         record_filename_("perf.data"),
         sample_record_count_(0),
         lost_record_count_(0) {
@@ -206,8 +210,7 @@
   std::vector<int> cpus_;
   EventSelectionSet event_selection_set_;
 
-  // mmap pages used by each perf event file, should be a power of 2.
-  size_t perf_mmap_pages_;
+  std::pair<size_t, size_t> mmap_page_range_;
 
   ThreadTree thread_tree_;
   std::string record_filename_;
@@ -254,7 +257,7 @@
       event_selection_set_.SetEnableOnExec(true);
     } else {
       LOG(ERROR)
-          << "No threads to monitor. Try `simpleperf help record` for help\n";
+          << "No threads to monitor. Try `simpleperf help record` for help";
       return false;
     }
   }
@@ -272,7 +275,8 @@
     }
   }
   std::vector<pollfd> pollfds;
-  if (!event_selection_set_.MmapEventFiles(perf_mmap_pages_, &pollfds)) {
+  if (!event_selection_set_.MmapEventFiles(mmap_page_range_.first,
+                                           mmap_page_range_.second, &pollfds)) {
     return false;
   }
 
@@ -335,7 +339,6 @@
 bool RecordCommand::ParseOptions(const std::vector<std::string>& args,
                                  std::vector<std::string>* non_option_args) {
   std::set<pid_t> tid_set;
-  size_t mmap_pages = 0;
   size_t i;
   for (i = 0; i < args.size() && !args[i].empty() && args[i][0] == '-'; ++i) {
     if (args[i] == "-a") {
@@ -454,7 +457,7 @@
         LOG(ERROR) << "Invalid mmap_pages: '" << args[i] << "'";
         return false;
       }
-      mmap_pages = pages;
+      mmap_page_range_.first = mmap_page_range_.second = pages;
     } else if (args[i] == "--no-dump-kernel-symbols") {
       can_dump_kernel_symbols_ = false;
     } else if (args[i] == "--no-inherit") {
@@ -538,12 +541,6 @@
     can_dump_kernel_symbols_ = false;
   }
 
-  if (mmap_pages != 0) {
-    perf_mmap_pages_ = mmap_pages;
-  } else {
-    perf_mmap_pages_ = (system_wide_collection_ ? 256 : 128);
-  }
-
   if (non_option_args != nullptr) {
     non_option_args->clear();
     for (; i < args.size(); ++i) {
diff --git a/simpleperf/event_fd.cpp b/simpleperf/event_fd.cpp
index 2fd4d52..0002d02 100644
--- a/simpleperf/event_fd.cpp
+++ b/simpleperf/event_fd.cpp
@@ -76,9 +76,7 @@
 }
 
 EventFd::~EventFd() {
-  if (mmap_addr_ != nullptr) {
-    munmap(mmap_addr_, mmap_len_);
-  }
+  DestroyMappedBuffer();
   close(perf_event_fd_);
 }
 
@@ -115,19 +113,22 @@
   return true;
 }
 
-bool EventFd::CreateMappedBuffer(size_t mmap_pages, pollfd* poll_fd) {
+bool EventFd::CreateMappedBuffer(size_t mmap_pages, pollfd* poll_fd, bool report_error) {
   CHECK(IsPowerOfTwo(mmap_pages));
   size_t page_size = sysconf(_SC_PAGE_SIZE);
   size_t mmap_len = (mmap_pages + 1) * page_size;
   void* mmap_addr = mmap(nullptr, mmap_len, PROT_READ | PROT_WRITE, MAP_SHARED, perf_event_fd_, 0);
   if (mmap_addr == MAP_FAILED) {
     bool is_perm_error = (errno == EPERM);
-    PLOG(ERROR) << "mmap() failed for " << Name();
-    if (is_perm_error) {
+    if (report_error) {
+      PLOG(ERROR) << "mmap(" << mmap_pages << ") failed for " << Name();
+    } else {
+      PLOG(DEBUG) << "mmap(" << mmap_pages << ") failed for " << Name();
+    }
+    if (report_error && is_perm_error) {
       LOG(ERROR) << "It seems the kernel doesn't allow allocating enough "
-          << "buffer for dumping samples, consider decreasing the number of "
-          << "monitored threads(-t), or decreasing mmap pages(-m), or "
-          << "decreasing the number of events(-e).";
+          << "buffer for dumping samples, consider decreasing mmap pages(-m), "
+          << "or decreasing the number of events(-e).";
     }
     return false;
   }
@@ -145,18 +146,31 @@
   return true;
 }
 
-bool EventFd::ShareMappedBuffer(const EventFd& event_fd) {
+bool EventFd::ShareMappedBuffer(const EventFd& event_fd, bool report_error) {
   CHECK(!HasMappedBuffer());
   CHECK(event_fd.HasMappedBuffer());
   int result = ioctl(perf_event_fd_, PERF_EVENT_IOC_SET_OUTPUT, event_fd.perf_event_fd_);
   if (result != 0) {
-    PLOG(ERROR) << "failed to share mapped buffer of "
-        << event_fd.perf_event_fd_ << " with " << perf_event_fd_;
+    if (report_error) {
+      PLOG(ERROR) << "failed to share mapped buffer of "
+          << event_fd.perf_event_fd_ << " with " << perf_event_fd_;
+    }
     return false;
   }
   return true;
 }
 
+void EventFd::DestroyMappedBuffer() {
+  if (HasMappedBuffer()) {
+    munmap(mmap_addr_, mmap_len_);
+    mmap_addr_ = nullptr;
+    mmap_len_ = 0;
+    mmap_metadata_page_ = nullptr;
+    mmap_data_buffer_ = nullptr;
+    mmap_data_buffer_size_ = 0;
+  }
+}
+
 size_t EventFd::GetAvailableMmapData(char** pdata) {
   if (!HasMappedBuffer()) {
     return 0;
diff --git a/simpleperf/event_fd.h b/simpleperf/event_fd.h
index 651f3fd..d33368c 100644
--- a/simpleperf/event_fd.h
+++ b/simpleperf/event_fd.h
@@ -65,16 +65,18 @@
   // Create mapped buffer used to receive records sent by the kernel.
   // mmap_pages should be power of 2. If created successfully, fill pollfd,
   // which is used to poll() on available mapped data.
-  bool CreateMappedBuffer(size_t mmap_pages, pollfd* poll_fd);
+  bool CreateMappedBuffer(size_t mmap_pages, pollfd* poll_fd, bool report_error);
 
   // Share the mapped buffer used by event_fd. The two EventFds should monitor
   // the same event on the same cpu, but have different thread ids.
-  bool ShareMappedBuffer(const EventFd& event_fd);
+  bool ShareMappedBuffer(const EventFd& event_fd, bool report_error);
 
   bool HasMappedBuffer() const {
     return mmap_data_buffer_size_ != 0;
   }
 
+  void DestroyMappedBuffer();
+
   // When the kernel writes new sampled records to the mapped area, we can get them by returning
   // the start address and size of the data.
   size_t GetAvailableMmapData(char** pdata);
diff --git a/simpleperf/event_selection_set.cpp b/simpleperf/event_selection_set.cpp
index 267f40d..5a98fd2 100644
--- a/simpleperf/event_selection_set.cpp
+++ b/simpleperf/event_selection_set.cpp
@@ -335,7 +335,29 @@
   return true;
 }
 
-bool EventSelectionSet::MmapEventFiles(size_t mmap_pages, std::vector<pollfd>* pollfds) {
+bool EventSelectionSet::MmapEventFiles(size_t min_mmap_pages,
+                                       size_t max_mmap_pages,
+                                       std::vector<pollfd>* pollfds) {
+  for (size_t i = max_mmap_pages; i >= min_mmap_pages; i >>= 1) {
+    if (MmapEventFiles(i, pollfds, i == min_mmap_pages)) {
+      LOG(VERBOSE) << "Mapped buffer size is " << i << " pages.";
+      return true;
+    }
+    for (auto& group : groups_) {
+      for (auto& selection : group) {
+        for (auto& event_fd : selection.event_fds) {
+          event_fd->DestroyMappedBuffer();
+        }
+      }
+    }
+  }
+  return false;
+}
+
+bool EventSelectionSet::MmapEventFiles(size_t mmap_pages,
+                                       std::vector<pollfd>* pollfds,
+                                       bool report_error) {
+  pollfds->clear();
   for (auto& group : groups_) {
     for (auto& selection : group) {
       // For each event, allocate a mapped buffer for each cpu.
@@ -343,12 +365,13 @@
       for (auto& event_fd : selection.event_fds) {
         auto it = cpu_map.find(event_fd->Cpu());
         if (it != cpu_map.end()) {
-          if (!event_fd->ShareMappedBuffer(*(it->second))) {
+          if (!event_fd->ShareMappedBuffer(*(it->second), report_error)) {
             return false;
           }
         } else {
           pollfd poll_fd;
-          if (!event_fd->CreateMappedBuffer(mmap_pages, &poll_fd)) {
+          if (!event_fd->CreateMappedBuffer(mmap_pages, &poll_fd,
+                                            report_error)) {
             return false;
           }
           pollfds->push_back(poll_fd);
diff --git a/simpleperf/event_selection_set.h b/simpleperf/event_selection_set.h
index 87bdeab..5af8c0c 100644
--- a/simpleperf/event_selection_set.h
+++ b/simpleperf/event_selection_set.h
@@ -88,7 +88,7 @@
   bool OpenEventFilesForCpus(const std::vector<int>& cpus);
   bool OpenEventFilesForThreadsOnCpus(const std::vector<pid_t>& threads, std::vector<int> cpus);
   bool ReadCounters(std::vector<CountersInfo>* counters);
-  bool MmapEventFiles(size_t mmap_pages, std::vector<pollfd>* pollfds);
+  bool MmapEventFiles(size_t min_mmap_pages, size_t max_mmap_pages, std::vector<pollfd>* pollfds);
   void PrepareToReadMmapEventData(std::function<bool (Record*)> callback);
   bool ReadMmapEventData();
   bool FinishReadMmapEventData();
@@ -98,6 +98,7 @@
                                    EventSelection* selection);
   void UnionSampleType();
   bool OpenEventFiles(const std::vector<pid_t>& threads, const std::vector<int>& cpus);
+  bool MmapEventFiles(size_t mmap_pages, std::vector<pollfd>* pollfds, bool report_error);
   bool ReadMmapEventDataForFd(std::unique_ptr<EventFd>& event_fd, const perf_event_attr& attr,
                               bool* has_data);