summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--partition_tools/dynamic_partitions_device_info.proto12
-rw-r--r--partition_tools/lpdump.cc6
-rw-r--r--profcollectd/libprofcollectd/config.rs14
-rw-r--r--profcollectd/libprofcollectd/report.rs3
-rw-r--r--simpleperf/RecordReadThread.cpp12
-rw-r--r--simpleperf/RecordReadThread.h1
-rw-r--r--simpleperf/cmd_record.cpp31
-rw-r--r--simpleperf/cmd_record_impl.h2
-rw-r--r--simpleperf/cmd_record_test.cpp12
-rw-r--r--simpleperf/doc/collect_etm_data_for_autofdo.md80
-rw-r--r--simpleperf/event_selection_set.cpp70
-rw-r--r--simpleperf/event_selection_set.h6
-rwxr-xr-xtools/check_elf_alignment.sh90
13 files changed, 285 insertions, 54 deletions
diff --git a/partition_tools/dynamic_partitions_device_info.proto b/partition_tools/dynamic_partitions_device_info.proto
index 8800dac7..82d20098 100644
--- a/partition_tools/dynamic_partitions_device_info.proto
+++ b/partition_tools/dynamic_partitions_device_info.proto
@@ -20,7 +20,7 @@ package android;
// Keep in sync with proto files on EDI backend. Otherwise, new fields will
// go ignored.
-// Next: 6
+// Next: 7
message DynamicPartitionsDeviceInfoProto {
bool enabled = 1;
bool retrofit = 2;
@@ -57,4 +57,14 @@ message DynamicPartitionsDeviceInfoProto {
uint64 alignment_offset = 5 [json_name = "alignment_offset"];
}
repeated BlockDevice block_devices = 5 [json_name = "block_devices"];
+
+ // Next: 4
+ message SuperDevice {
+ string name = 1;
+ /** Used space in bytes */
+ uint64 used_size = 2 [json_name = "used_size"];
+ /** Total size of the super in bytes */
+ uint64 total_size = 3 [json_name = "total_size"];
+ }
+ SuperDevice super_device = 6 [json_name = "super_device"];
}
diff --git a/partition_tools/lpdump.cc b/partition_tools/lpdump.cc
index 97682940..4c1fe954 100644
--- a/partition_tools/lpdump.cc
+++ b/partition_tools/lpdump.cc
@@ -173,6 +173,12 @@ static bool MergeMetadata(const LpMetadata* metadata,
block_device_proto->set_alignment(info.alignment);
block_device_proto->set_alignment_offset(info.alignment_offset);
}
+
+ auto super_device_proto = proto->mutable_super_device();
+ super_device_proto->set_name(GetSuperPartitionName());
+ super_device_proto->set_used_size(builder->UsedSpace());
+ super_device_proto->set_total_size(GetTotalSuperPartitionSize(*metadata));
+
return true;
}
diff --git a/profcollectd/libprofcollectd/config.rs b/profcollectd/libprofcollectd/config.rs
index af714242..cbc5b7cd 100644
--- a/profcollectd/libprofcollectd/config.rs
+++ b/profcollectd/libprofcollectd/config.rs
@@ -82,9 +82,9 @@ impl Config {
}
}
-impl ToString for Config {
- fn to_string(&self) -> String {
- serde_json::to_string(self).expect("Failed to deserialise configuration.")
+impl std::fmt::Display for Config {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ write!(f, "{}", serde_json::to_string(self).expect("Failed to deserialise configuration."))
}
}
@@ -166,3 +166,11 @@ pub fn clear_data() -> Result<()> {
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 e0f2ec84..60410c1a 100644
--- a/profcollectd/libprofcollectd/report.rs
+++ b/profcollectd/libprofcollectd/report.rs
@@ -29,7 +29,7 @@ use zip::write::FileOptions;
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 @@ pub fn pack_report(
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 2ab61278..2d034bc1 100644
--- a/simpleperf/RecordReadThread.cpp
+++ b/simpleperf/RecordReadThread.cpp
@@ -408,6 +408,7 @@ bool RecordReadThread::HandleAddEventFds(IOEventLoop& loop,
success = false;
break;
}
+ has_etm_events_ = true;
}
cpu_map[fd->Cpu()] = fd;
} else {
@@ -620,6 +621,9 @@ void RecordReadThread::PushRecordToRecordBuffer(KernelRecordReader* kernel_recor
}
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 @@ void RecordReadThread::ReadAuxDataFromKernelBuffer(bool* has_data) {
}
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 c104b083..893f8234 100644
--- a/simpleperf/RecordReadThread.h
+++ b/simpleperf/RecordReadThread.h
@@ -211,6 +211,7 @@ class RecordReadThread {
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 40582045..cb9ad884 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 DEFAULT_CALL_CHAIN_JOINER_CACHE_SIZE = 8 * kMegabyte;
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_FILTER_OPTION_HELP_MSG_FOR_RECORDING
"--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 @@ RECORD_FILTER_OPTION_HELP_MSG_FOR_RECORDING
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 @@ bool RecordCommand::PrepareRecording(Workload* workload) {
} 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 @@ bool RecordCommand::PrepareRecording(Workload* workload) {
}
}
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 @@ bool RecordCommand::PrepareRecording(Workload* workload) {
// 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 @@ bool RecordCommand::DoRecording(Workload* workload) {
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 @@ bool RecordCommand::ParseOptions(const std::vector<std::string>& args,
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 29f44809..e8561636 100644
--- a/simpleperf/cmd_record_impl.h
+++ b/simpleperf/cmd_record_impl.h
@@ -51,6 +51,8 @@ inline const OptionFormatMap& GetRecordCmdOptionFormats() {
{"--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 131f6da4..898d8756 100644
--- a/simpleperf/cmd_record_test.cpp
+++ b/simpleperf/cmd_record_test.cpp
@@ -1196,8 +1196,7 @@ TEST(record_cmd, cycle_threshold) {
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 @@ TEST(record_cmd, binary_option) {
}
// @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/doc/collect_etm_data_for_autofdo.md b/simpleperf/doc/collect_etm_data_for_autofdo.md
index 059ffb4e..e86f1bba 100644
--- a/simpleperf/doc/collect_etm_data_for_autofdo.md
+++ b/simpleperf/doc/collect_etm_data_for_autofdo.md
@@ -81,20 +81,15 @@ branch with count info for binary2
We need to split perf_inject.data, and make sure one file only contains info for one binary.
-Then we can use [AutoFDO](https://github.com/google/autofdo) to create profile. AutoFDO only works
-for binaries having an executable segment as its first loadable segment. But binaries built in
-Android may not follow this rule. Simpleperf inject command knows how to work around this problem.
-But there is a check in AutoFDO forcing binaries to start with an executable segment. We need to
-disable the check in AutoFDO, by commenting out L127-L136 in
-https://github.com/google/autofdo/commit/188db2834ce74762ed17108ca344916994640708#diff-2d132ecbb5e4f13e0da65419f6d1759dd27d6b696786dd7096c0c34d499b1710R127-R136.
-Then we can use `create_llvm_prof` in AutoFDO to create profiles used by clang.
+Then we can use [AutoFDO](https://github.com/google/autofdo) to create profile. Follow README.md
+in AutoFDO to build create_llvm_prof, then use `create_llvm_prof` to create profiles for clang.
```sh
# perf_inject_binary1.data is split from perf_inject.data, and only contains branch info for binary1.
-host $ autofdo/create_llvm_prof -profile perf_inject_binary1.data -profiler text -binary path_of_binary1 -out a.prof -format binary
+host $ create_llvm_prof -profile perf_inject_binary1.data -profiler text -binary path_of_binary1 -out a.prof -format binary
# perf_inject_kernel.data is split from perf_inject.data, and only contains branch info for [kernel.kallsyms].
-host $ autofdo/create_llvm_prof -profile perf_inject_kernel.data -profiler text -binary vmlinux -out a.prof -format binary
+host $ create_llvm_prof -profile perf_inject_kernel.data -profiler text -binary vmlinux -out a.prof -format binary
```
Then we can use a.prof for PGO during compilation, via `-fprofile-sample-use=a.prof`.
@@ -121,14 +116,13 @@ Step 2: Run `etm_test_loop` on device, and collect ETM data for its running.
(host) <AOSP>$ adb push out/target/product/generic_arm64/system/bin/etm_test_loop /data/local/tmp
(host) <AOSP>$ adb root
(host) <AOSP>$ adb shell
-(device) / # cd /data/local/tmp
-(device) /data/local/tmp # chmod a+x etm_test_loop
-(device) /data/local/tmp # simpleperf record -e cs-etm:u ./etm_test_loop
-simpleperf I cmd_record.cpp:729] Recorded for 0.0370068 seconds. Start post processing.
-simpleperf I cmd_record.cpp:799] Aux data traced: 1689136
-(device) /data/local/tmp # simpleperf inject -i perf.data --output branch-list -o branch_list.data
-simpleperf W dso.cpp:557] failed to read min virtual address of [vdso]: File not found
-(device) /data/local/tmp # exit
+(device) / $ cd /data/local/tmp
+(device) /data/local/tmp $ chmod a+x etm_test_loop
+(device) /data/local/tmp $ simpleperf record -e cs-etm:u ./etm_test_loop
+simpleperf I cmd_record.cpp:809] Recorded for 0.033556 seconds. Start post processing.
+simpleperf I cmd_record.cpp:879] Aux data traced: 1,134,720
+(device) /data/local/tmp $ simpleperf inject -i perf.data --output branch-list -o branch_list.data
+(device) /data/local/tmp $ exit
(host) <AOSP>$ adb pull /data/local/tmp/branch_list.data
```
@@ -137,47 +131,43 @@ Step 3: Convert ETM data to AutoFDO data.
```sh
# Build simpleperf tool on host.
(host) <AOSP>$ make simpleperf_ndk
-(host) <AOSP>$ simpleperf_ndk64 inject -i branch_list.data -o perf_inject_etm_test_loop.data --symdir out/target/product/generic_arm64/symbols/system/bin
-simpleperf W cmd_inject.cpp:505] failed to build instr ranges for binary [vdso]: File not found
+(host) <AOSP>$ simpleperf inject -i branch_list.data -o perf_inject_etm_test_loop.data --symdir out/target/product/generic_arm64/symbols/system/bin
(host) <AOSP>$ cat perf_inject_etm_test_loop.data
-13
-1000-1010:1
-1014-1050:1
+14
+4000-4010:1
+4014-4048:1
...
-112c->0:1
+418c->0:1
+// build_id: 0xa6fc5b506adf9884cdb680b4893c505a00000000
// /data/local/tmp/etm_test_loop
(host) <AOSP>$ create_llvm_prof -profile perf_inject_etm_test_loop.data -profiler text -binary out/target/product/generic_arm64/symbols/system/bin/etm_test_loop -out etm_test_loop.afdo -format binary
(host) <AOSP>$ ls -lh etm_test_loop.afdo
-rw-r--r-- 1 user group 241 Aug 29 16:04 etm_test_loop.afdo
+rw-r--r-- 1 user group 241 Apr 30 09:52 etm_test_loop.afdo
```
Step 4: Use AutoFDO data to build optimized binary.
```sh
-(host) <AOSP>$ mkdir toolchain/pgo-profiles/sampling/
(host) <AOSP>$ cp etm_test_loop.afdo toolchain/pgo-profiles/sampling/
(host) <AOSP>$ vi toolchain/pgo-profiles/sampling/Android.bp
-# edit Android.bp to add a fdo_profile module
-# soong_namespace {}
+# Edit Android.bp to add a fdo_profile module:
#
# fdo_profile {
-# name: "etm_test_loop_afdo",
-# profile: ["etm_test_loop.afdo"],
+# name: "etm_test_loop",
+# profile: "etm_test_loop.afdo"
# }
-```
-
-`soong_namespace` is added to support fdo_profile modules with the same name
-
-In a product config mk file, update `PRODUCT_AFDO_PROFILES` with
-
-```make
-PRODUCT_AFDO_PROFILES += etm_test_loop://toolchain/pgo-profiles/sampling:etm_test_loop_afdo
-```
-
-```sh
+(host) <AOSP>$ vi toolchain/pgo-profiles/sampling/afdo_profiles.mk
+# Edit afdo_profiles.mk to add etm_test_loop profile mapping:
+#
+# AFDO_PROFILES += keystore2://toolchain/pgo-profiles/sampling:keystore2 \
+# ...
+# server_configurable_flags://toolchain/pgo-profiles/sampling:server_configurable_flags \
+# etm_test_loop://toolchain/pgo-profiles/sampling:etm_test_loop
+#
(host) <AOSP>$ vi system/extras/simpleperf/runtest/Android.bp
-# edit Android.bp to enable afdo for etm_test_loop.
+# Edit Android.bp to enable afdo for etm_test_loop:
+#
# cc_binary {
# name: "etm_test_loop",
# srcs: ["etm_test_loop.cpp"],
@@ -186,6 +176,14 @@ PRODUCT_AFDO_PROFILES += etm_test_loop://toolchain/pgo-profiles/sampling:etm_tes
(host) <AOSP>$ make etm_test_loop
```
+We can check if `etm_test_loop.afdo` is used when building etm_test_loop.
+
+```sh
+(host) <AOSP>$ gzip -d out/verbose.log.gz
+(host) <AOSP>$ cat out/verbose.log | grep etm_test_loop.afdo
+ ... -fprofile-sample-use=toolchain/pgo-profiles/sampling/etm_test_loop.afdo ...
+```
+
If comparing the disassembly of `out/target/product/generic_arm64/symbols/system/bin/etm_test_loop`
before and after optimizing with AutoFDO data, we can see different preferences when branching.
diff --git a/simpleperf/event_selection_set.cpp b/simpleperf/event_selection_set.cpp
index c75f8049..1a7cdef8 100644
--- a/simpleperf/event_selection_set.cpp
+++ b/simpleperf/event_selection_set.cpp
@@ -233,6 +233,8 @@ bool EventSelectionSet::BuildAndCheckEventSelection(const std::string& event_nam
// 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 @@ void EventSelectionSet::SetEnableCondition(bool enable_on_open, bool enable_on_e
}
}
+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 @@ bool EventSelectionSet::SetEnableEvents(bool enable) {
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 e046035b..a892d51e 100644
--- a/simpleperf/event_selection_set.h
+++ b/simpleperf/event_selection_set.h
@@ -122,6 +122,7 @@ class EventSelectionSet {
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 @@ class EventSelectionSet {
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 @@ class EventSelectionSet {
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
new file mode 100755
index 00000000..b74f34ae
--- /dev/null
+++ b/tools/check_elf_alignment.sh
@@ -0,0 +1,90 @@
+#!/bin/bash
+progname="${0##*/}"
+progname="${progname%.sh}"
+
+# usage: check_elf_alignment.sh [path to *.so files|path to *.apk]
+
+cleanup_trap() {
+ if [ -n "${tmp}" -a -d "${tmp}" ]; then
+ rm -rf ${tmp}
+ fi
+ exit $1
+}
+
+usage() {
+ echo "Host side script to check the ELF alignment of shared libraries."
+ echo "Shared libraries are reported ALIGNED when their ELF regions are"
+ echo "16 KB or 64 KB aligned. Otherwise they are reported as UNALIGNED."
+ echo
+ echo "Usage: ${progname} [input-path|input-APK]"
+}
+
+if [ ${#} -ne 1 ]; then
+ usage
+ exit
+fi
+
+case ${1} in
+ --help | -h | -\?)
+ usage
+ exit
+ ;;
+
+ *)
+ dir="${1}"
+ ;;
+esac
+
+if ! [ -f "${dir}" -o -d "${dir}" ]; then
+ echo "Invalid file: ${dir}" >&2
+ exit 1
+fi
+
+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'
+ echo "========================="
+ else
+ echo "NOTICE: Zip alignment check requires build-tools version 35.0.0-rc3 or higher."
+ echo " You can install the latest build-tools by running the below command"
+ echo " and updating your \$PATH:"
+ echo
+ 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/* -d "${tmp}" >/dev/null 2>&1
+ dir="${tmp}"
+fi
+
+RED="\e[31m"
+GREEN="\e[32m"
+ENDCOLOR="\e[0m"
+
+unaligned_libs=()
+
+echo
+echo "=== ELF alignment ==="
+
+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)"
+ if [[ $res =~ "2**14" ]] || [[ $res =~ "2**16" ]]; then
+ echo -e "${match}: ${GREEN}ALIGNED${ENDCOLOR} ($res)"
+ else
+ echo -e "${match}: ${RED}UNALIGNED${ENDCOLOR} ($res)"
+ unaligned_libs+=("${match}")
+ fi
+done
+
+if [ ${#unaligned_libs[@]} -gt 0 ]; then
+ 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
+echo "====================="