Merge "Replace impl ToString with impl Display" into main
diff --git a/profcollectd/libprofcollectd/config.rs b/profcollectd/libprofcollectd/config.rs
index cdf1487..cbc5b7c 100644
--- a/profcollectd/libprofcollectd/config.rs
+++ b/profcollectd/libprofcollectd/config.rs
@@ -166,3 +166,11 @@
     remove_files(&REPORT_OUTPUT_DIR)?;
     Ok(())
 }
+pub fn clear_processed_files() -> Result<()> {
+    read_dir(&PROFILE_OUTPUT_DIR as &Path)?
+        .filter_map(|e| e.ok())
+        .map(|e| e.path())
+        .filter(|e| e.is_file() && e != (&CONFIG_FILE as &Path))
+        .try_for_each(remove_file)?;
+    Ok(())
+}
diff --git a/profcollectd/libprofcollectd/report.rs b/profcollectd/libprofcollectd/report.rs
index e0f2ec8..60410c1 100644
--- a/profcollectd/libprofcollectd/report.rs
+++ b/profcollectd/libprofcollectd/report.rs
@@ -29,7 +29,7 @@
 use zip::CompressionMethod::Deflated;
 use zip::ZipWriter;
 
-use crate::config::Config;
+use crate::config::{clear_processed_files, Config};
 
 pub const NO_USAGE_SETTING: i32 = -1;
 
@@ -80,6 +80,7 @@
         zip.write_all(usage_setting.to_string().as_bytes())?;
     }
     zip.finish()?;
+    clear_processed_files()?;
 
     Ok(report_filename)
 }
diff --git a/simpleperf/RecordReadThread.cpp b/simpleperf/RecordReadThread.cpp
index 2ab6127..2d034bc 100644
--- a/simpleperf/RecordReadThread.cpp
+++ b/simpleperf/RecordReadThread.cpp
@@ -408,6 +408,7 @@
             success = false;
             break;
           }
+          has_etm_events_ = true;
         }
         cpu_map[fd->Cpu()] = fd;
       } else {
@@ -620,6 +621,9 @@
 }
 
 void RecordReadThread::ReadAuxDataFromKernelBuffer(bool* has_data) {
+  if (!has_etm_events_) {
+    return;
+  }
   for (auto& reader : kernel_record_readers_) {
     EventFd* event_fd = reader.GetEventFd();
     if (event_fd->HasAuxBuffer()) {
@@ -659,6 +663,14 @@
 }
 
 bool RecordReadThread::SendDataNotificationToMainThread() {
+  if (has_etm_events_) {
+    // For ETM recording, the default buffer size is large enough to hold ETM data for several
+    // seconds. To reduce impact of processing ETM data (especially when --decode-etm is used),
+    // delay processing ETM data until the buffer is half full.
+    if (record_buffer_.GetFreeSize() >= record_buffer_.size() / 2) {
+      return true;
+    }
+  }
   if (!has_data_notification_.load(std::memory_order_relaxed)) {
     has_data_notification_ = true;
     char unused = 0;
diff --git a/simpleperf/RecordReadThread.h b/simpleperf/RecordReadThread.h
index c104b08..893f823 100644
--- a/simpleperf/RecordReadThread.h
+++ b/simpleperf/RecordReadThread.h
@@ -211,6 +211,7 @@
   std::unique_ptr<std::thread> read_thread_;
   std::vector<KernelRecordReader> kernel_record_readers_;
   pid_t exclude_pid_ = -1;
+  bool has_etm_events_ = false;
 
   std::unordered_set<EventFd*> event_fds_disabled_by_kernel_;
 
diff --git a/simpleperf/cmd_record.cpp b/simpleperf/cmd_record.cpp
index 4058204..cb9ad88 100644
--- a/simpleperf/cmd_record.cpp
+++ b/simpleperf/cmd_record.cpp
@@ -22,6 +22,7 @@
 #include <sys/utsname.h>
 #include <time.h>
 #include <unistd.h>
+#include <chrono>
 #include <filesystem>
 #include <optional>
 #include <set>
@@ -111,8 +112,8 @@
 static constexpr size_t kDefaultAuxBufferSize = 4 * kMegabyte;
 
 // On Pixel 3, it takes about 1ms to enable ETM, and 16-40ms to disable ETM and copy 4M ETM data.
-// So make default period to 100ms.
-static constexpr double kDefaultEtmDataFlushPeriodInSec = 0.1;
+// So make default interval to 100ms.
+static constexpr uint32_t kDefaultEtmDataFlushIntervalInMs = 100;
 
 struct TimeStat {
   uint64_t prepare_recording_time = 0;
@@ -316,6 +317,8 @@
 "--record-timestamp               Generate timestamp packets in ETM stream.\n"
 "--record-cycles                  Generate cycle count packets in ETM stream.\n"
 "--cycle-threshold <threshold>    Set cycle count counter threshold for ETM cycle count packets.\n"
+"--etm-flush-interval <interval>  Set the interval between ETM data flushes from the ETR buffer\n"
+"                                 to the perf event buffer (in milliseconds). Default is 100 ms.\n"
 "\n"
 "Other options:\n"
 "--exit-with-parent            Stop recording when the thread starting simpleperf dies.\n"
@@ -480,6 +483,7 @@
 
   std::unique_ptr<ETMBranchListGenerator> etm_branch_list_generator_;
   std::unique_ptr<RegEx> binary_name_regex_;
+  std::chrono::milliseconds etm_flush_interval_{kDefaultEtmDataFlushIntervalInMs};
 };
 
 std::string RecordCommand::LongHelpString() const {
@@ -631,7 +635,7 @@
   } else {
     need_to_check_targets = true;
   }
-  if (delay_in_ms_ != 0) {
+  if (delay_in_ms_ != 0 || event_selection_set_.HasAuxTrace()) {
     event_selection_set_.SetEnableCondition(false, false);
   }
 
@@ -755,6 +759,12 @@
     }
   }
   if (event_selection_set_.HasAuxTrace()) {
+    // ETM events can only be enabled successfully after MmapEventFiles().
+    if (delay_in_ms_ == 0 && !event_selection_set_.IsEnabledOnExec()) {
+      if (!event_selection_set_.EnableETMEvents()) {
+        return false;
+      }
+    }
     // ETM data is dumped to kernel buffer only when there is no thread traced by ETM. It happens
     // either when all monitored threads are scheduled off cpu, or when all etm perf events are
     // disabled.
@@ -762,10 +772,9 @@
     // makes less than expected data, especially in system wide recording. So add a periodic event
     // to flush etm data by temporarily disable all perf events.
     auto etm_flush = [this]() {
-      return event_selection_set_.SetEnableEvents(false) &&
-             event_selection_set_.SetEnableEvents(true);
+      return event_selection_set_.DisableETMEvents() && event_selection_set_.EnableETMEvents();
     };
-    if (!loop->AddPeriodicEvent(SecondToTimeval(kDefaultEtmDataFlushPeriodInSec), etm_flush)) {
+    if (!loop->AddPeriodicEvent(SecondToTimeval(etm_flush_interval_.count() / 1000.0), etm_flush)) {
       return false;
     }
 
@@ -800,6 +809,12 @@
     return false;
   }
   time_stat_.stop_recording_time = GetSystemClock();
+  if (event_selection_set_.HasAuxTrace()) {
+    // Disable ETM events to flush the last ETM data.
+    if (!event_selection_set_.DisableETMEvents()) {
+      return false;
+    }
+  }
   if (!event_selection_set_.SyncKernelBuffer()) {
     return false;
   }
@@ -1041,6 +1056,10 @@
   if (options.PullBoolValue("--decode-etm")) {
     etm_branch_list_generator_ = ETMBranchListGenerator::Create(system_wide_collection_);
   }
+  uint32_t interval = 0;
+  if (options.PullUintValue("--etm-flush-interval", &interval) && interval != 0) {
+    etm_flush_interval_ = std::chrono::milliseconds(interval);
+  }
 
   if (options.PullBoolValue("--record-timestamp")) {
     ETMRecorder& recorder = ETMRecorder::GetInstance();
diff --git a/simpleperf/cmd_record_impl.h b/simpleperf/cmd_record_impl.h
index 29f4480..e856163 100644
--- a/simpleperf/cmd_record_impl.h
+++ b/simpleperf/cmd_record_impl.h
@@ -51,6 +51,8 @@
         {"--cycle-threshold", {OptionValueType::UINT, OptionType::SINGLE, AppRunnerType::ALLOWED}},
         {"--decode-etm", {OptionValueType::NONE, OptionType::SINGLE, AppRunnerType::ALLOWED}},
         {"--delay", {OptionValueType::UINT, OptionType::SINGLE, AppRunnerType::ALLOWED}},
+        {"--etm-flush-interval",
+         {OptionValueType::UINT, OptionType::SINGLE, AppRunnerType::ALLOWED}},
         {"--record-timestamp", {OptionValueType::NONE, OptionType::SINGLE, AppRunnerType::ALLOWED}},
         {"--record-cycles", {OptionValueType::NONE, OptionType::SINGLE, AppRunnerType::ALLOWED}},
         {"--duration", {OptionValueType::DOUBLE, OptionType::SINGLE, AppRunnerType::ALLOWED}},
diff --git a/simpleperf/cmd_record_test.cpp b/simpleperf/cmd_record_test.cpp
index 131f6da..898d875 100644
--- a/simpleperf/cmd_record_test.cpp
+++ b/simpleperf/cmd_record_test.cpp
@@ -1196,8 +1196,7 @@
     GTEST_LOG_(INFO) << "Omit this test since etm isn't supported on this device";
     return;
   }
-  ASSERT_TRUE(RunRecordCmd({"-e", "cs-etm", "--record-cycles",
-                            "--cycle-threshold", "8"}));
+  ASSERT_TRUE(RunRecordCmd({"-e", "cs-etm", "--record-cycles", "--cycle-threshold", "8"}));
 }
 
 // @CddTest = 6.1/C-0-2
@@ -1210,6 +1209,15 @@
 }
 
 // @CddTest = 6.1/C-0-2
+TEST(record_cmd, etm_flush_interval_option) {
+  if (!ETMRecorder::GetInstance().CheckEtmSupport().ok()) {
+    GTEST_LOG_(INFO) << "Omit this test since etm isn't supported on this device";
+    return;
+  }
+  ASSERT_TRUE(RunRecordCmd({"-e", "cs-etm", "--etm-flush-interval", "10"}));
+}
+
+// @CddTest = 6.1/C-0-2
 TEST(record_cmd, pmu_event_option) {
   TEST_REQUIRE_PMU_COUNTER();
   TEST_REQUIRE_HW_COUNTER();
diff --git a/simpleperf/event_selection_set.cpp b/simpleperf/event_selection_set.cpp
index c75f804..1a7cdef 100644
--- a/simpleperf/event_selection_set.cpp
+++ b/simpleperf/event_selection_set.cpp
@@ -233,6 +233,8 @@
       // enabling/disabling etm devices. So don't adjust frequency by default.
       selection->event_attr.freq = 0;
       selection->event_attr.sample_period = 1;
+      // An ETM event can't be enabled without mmap aux buffer. So disable it by default.
+      selection->event_attr.disabled = 1;
     } else {
       selection->event_attr.freq = 1;
       // Set default sample freq here may print msg "Adjust sample freq to max allowed sample
@@ -461,6 +463,17 @@
   }
 }
 
+bool EventSelectionSet::IsEnabledOnExec() const {
+  for (const auto& group : groups_) {
+    for (const auto& selection : group.selections) {
+      if (!selection.event_attr.enable_on_exec) {
+        return false;
+      }
+    }
+  }
+  return true;
+}
+
 void EventSelectionSet::SampleIdAll() {
   for (auto& group : groups_) {
     for (auto& selection : group.selections) {
@@ -939,4 +952,61 @@
   return true;
 }
 
+bool EventSelectionSet::EnableETMEvents() {
+  for (auto& group : groups_) {
+    for (auto& sel : group.selections) {
+      if (!sel.event_type_modifier.event_type.IsEtmEvent()) {
+        continue;
+      }
+      for (auto& fd : sel.event_fds) {
+        if (!fd->SetEnableEvent(true)) {
+          return false;
+        }
+      }
+    }
+  }
+  return true;
+}
+
+bool EventSelectionSet::DisableETMEvents() {
+  for (auto& group : groups_) {
+    for (auto& sel : group.selections) {
+      if (!sel.event_type_modifier.event_type.IsEtmEvent()) {
+        continue;
+      }
+      // When using ETR, ETM data is flushed to the aux buffer of the last cpu disabling ETM events.
+      // To avoid overflowing the aux buffer for one cpu, rotate the last cpu disabling ETM events.
+      if (etm_event_cpus_.empty()) {
+        for (const auto& fd : sel.event_fds) {
+          etm_event_cpus_.insert(fd->Cpu());
+        }
+        if (etm_event_cpus_.empty()) {
+          continue;
+        }
+        etm_event_cpus_it_ = etm_event_cpus_.begin();
+      }
+      int last_disabled_cpu = *etm_event_cpus_it_;
+      if (++etm_event_cpus_it_ == etm_event_cpus_.end()) {
+        etm_event_cpus_it_ = etm_event_cpus_.begin();
+      }
+
+      for (auto& fd : sel.event_fds) {
+        if (fd->Cpu() != last_disabled_cpu) {
+          if (!fd->SetEnableEvent(false)) {
+            return false;
+          }
+        }
+      }
+      for (auto& fd : sel.event_fds) {
+        if (fd->Cpu() == last_disabled_cpu) {
+          if (!fd->SetEnableEvent(false)) {
+            return false;
+          }
+        }
+      }
+    }
+  }
+  return true;
+}
+
 }  // namespace simpleperf
diff --git a/simpleperf/event_selection_set.h b/simpleperf/event_selection_set.h
index e046035..a892d51 100644
--- a/simpleperf/event_selection_set.h
+++ b/simpleperf/event_selection_set.h
@@ -122,6 +122,7 @@
   std::map<int, size_t> GetHardwareCountersForCpus() const;
 
   void SetEnableCondition(bool enable_on_open, bool enable_on_exec);
+  bool IsEnabledOnExec() const;
   void SampleIdAll();
   // Only set sample rate for events that haven't set sample rate.
   void SetSampleRateForNewEvents(const SampleRate& rate);
@@ -179,6 +180,8 @@
       double check_interval_in_sec = DEFAULT_PERIOD_TO_CHECK_MONITORED_TARGETS_IN_SEC);
 
   bool SetEnableEvents(bool enable);
+  bool EnableETMEvents();
+  bool DisableETMEvents();
 
  private:
   struct EventSelection {
@@ -232,6 +235,9 @@
   std::optional<SampleRate> sample_rate_;
   std::optional<std::vector<int>> cpus_;
 
+  std::set<int> etm_event_cpus_;
+  std::set<int>::const_iterator etm_event_cpus_it_;
+
   DISALLOW_COPY_AND_ASSIGN(EventSelectionSet);
 };
 
diff --git a/tools/check_elf_alignment.sh b/tools/check_elf_alignment.sh
index ba82eb7..b74f34a 100755
--- a/tools/check_elf_alignment.sh
+++ b/tools/check_elf_alignment.sh
@@ -40,12 +40,12 @@
   exit 1
 fi
 
-if [[ ${dir} == *.apk ]]; then
+if [[ "${dir}" == *.apk ]]; then
   trap 'cleanup_trap' EXIT
 
   if { zipalign --help 2>&1 | grep -q "\-P <pagesize_kb>"; }; then
     echo "=== APK zip-alignment ==="
-    zipalign -v -c -P 16 4 ${dir} | egrep 'lib/arm64-v8a|lib/x86_64|Verification'
+    zipalign -v -c -P 16 4 "${dir}" | egrep 'lib/arm64-v8a|lib/x86_64|Verification'
     echo "========================="
   else
     echo "NOTICE: Zip alignment check requires build-tools version 35.0.0-rc3 or higher."
@@ -55,10 +55,10 @@
     echo "    sdkmanager \"build-tools;35.0.0-rc3\""
   fi
 
-  dir_filename=$(basename ${dir})
-  tmp=$(mktemp -d -t ${dir_filename%.apk}_out_XXXXX)
-  unzip ${dir} lib/arm64-v8a/* lib/x86_64/* -d ${tmp} >/dev/null 2>&1
-  dir=${tmp}
+  dir_filename=$(basename "${dir}")
+  tmp=$(mktemp -d -t "${dir_filename%.apk}_out_XXXXX")
+  unzip "${dir}" lib/* -d "${tmp}" >/dev/null 2>&1
+  dir="${tmp}"
 fi
 
 RED="\e[31m"
@@ -70,7 +70,7 @@
 echo
 echo "=== ELF alignment ==="
 
-matches="$(find ${dir} -name "*.so" -type f)"
+matches="$(find "${dir}" -name "*.so" -type f)"
 IFS=$'\n'
 for match in $matches; do
   res="$(objdump -p ${match} | grep LOAD | awk '{ print $NF }' | head -1)"
@@ -78,12 +78,12 @@
     echo -e "${match}: ${GREEN}ALIGNED${ENDCOLOR} ($res)"
   else
     echo -e "${match}: ${RED}UNALIGNED${ENDCOLOR} ($res)"
-    unaligned_libs+=(${match})
+    unaligned_libs+=("${match}")
   fi
 done
 
 if [ ${#unaligned_libs[@]} -gt 0 ]; then
-  echo -e "${RED}Found ${#unaligned_libs[@]} unaligned libs${ENDCOLOR}"
+  echo -e "${RED}Found ${#unaligned_libs[@]} unaligned libs (only arm64-v8a/x86_64 libs need to be aligned).${ENDCOLOR}"
 elif [ -n "${dir_filename}" ]; then
   echo -e "ELF Verification Successful"
 fi