diff options
-rw-r--r-- | partition_tools/dynamic_partitions_device_info.proto | 12 | ||||
-rw-r--r-- | partition_tools/lpdump.cc | 6 | ||||
-rw-r--r-- | profcollectd/libprofcollectd/config.rs | 14 | ||||
-rw-r--r-- | profcollectd/libprofcollectd/report.rs | 3 | ||||
-rw-r--r-- | simpleperf/RecordReadThread.cpp | 12 | ||||
-rw-r--r-- | simpleperf/RecordReadThread.h | 1 | ||||
-rw-r--r-- | simpleperf/cmd_record.cpp | 31 | ||||
-rw-r--r-- | simpleperf/cmd_record_impl.h | 2 | ||||
-rw-r--r-- | simpleperf/cmd_record_test.cpp | 12 | ||||
-rw-r--r-- | simpleperf/doc/collect_etm_data_for_autofdo.md | 80 | ||||
-rw-r--r-- | simpleperf/event_selection_set.cpp | 70 | ||||
-rw-r--r-- | simpleperf/event_selection_set.h | 6 | ||||
-rwxr-xr-x | tools/check_elf_alignment.sh | 90 |
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 "=====================" |