diff options
-rw-r--r-- | pagecache/pagecache.py | 50 | ||||
-rw-r--r-- | profcollectd/README.md | 11 | ||||
-rw-r--r-- | profcollectd/libprofcollectd/config.rs | 19 | ||||
-rw-r--r-- | profcollectd/libprofcollectd/lib.rs | 2 | ||||
-rw-r--r-- | profcollectd/profcollectd.rc | 4 | ||||
-rw-r--r-- | simpleperf/Android.bp | 24 | ||||
-rw-r--r-- | simpleperf/cmd_inject.cpp | 76 | ||||
-rw-r--r-- | simpleperf/cmd_record.cpp | 2 | ||||
-rw-r--r-- | simpleperf/libsimpleperf_report_fuzzer.cpp | 97 | ||||
-rw-r--r-- | simpleperf/simpleperf_dict.dict | 1 | ||||
-rw-r--r-- | simpleperf/simpleperf_writer_fuzzer.cpp | 175 | ||||
-rwxr-xr-x | tools/check_elf_alignment.sh | 18 |
12 files changed, 373 insertions, 106 deletions
diff --git a/pagecache/pagecache.py b/pagecache/pagecache.py index 3f96a5d1..808812c5 100644 --- a/pagecache/pagecache.py +++ b/pagecache/pagecache.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python3 import curses import operator @@ -8,7 +8,7 @@ import re import subprocess import sys import threading -import Queue +import queue STATS_UPDATE_INTERVAL = 0.2 PAGE_SIZE = 4096 @@ -68,21 +68,21 @@ class PagecacheStats(): def print_stats(self): # Create new merged dict - sorted_added = sorted(self._file_pages.items(), key=operator.itemgetter(1), reverse=True) + sorted_added = sorted(list(self._file_pages.items()), key=operator.itemgetter(1), reverse=True) row_format = "{:<70}{:<12}{:<14}{:<9}" - print row_format.format('NAME', 'ADDED (MB)', 'REMOVED (MB)', 'SIZE (MB)') + print(row_format.format('NAME', 'ADDED (MB)', 'REMOVED (MB)', 'SIZE (MB)')) for filename, added in sorted_added: filesize = self._file_size[filename] added = self._file_pages[filename][0] removed = self._file_pages[filename][1] - if (filename > 64): + if (len(filename) > 64): filename = filename[-64:] - print row_format.format(filename, self.pages_to_mb(added), self.pages_to_mb(removed), self.bytes_to_mb(filesize)) + print(row_format.format(filename, self.pages_to_mb(added), self.pages_to_mb(removed), self.bytes_to_mb(filesize))) - print row_format.format('TOTAL', self.pages_to_mb(self._total_pages_added), self.pages_to_mb(self._total_pages_removed), '') + print(row_format.format('TOTAL', self.pages_to_mb(self._total_pages_added), self.pages_to_mb(self._total_pages_removed), '')) def print_stats_curses(self, pad): - sorted_added = sorted(self._file_pages.items(), key=operator.itemgetter(1), reverse=True) + sorted_added = sorted(list(self._file_pages.items()), key=operator.itemgetter(1), reverse=True) height, width = pad.getmaxyx() pad.clear() pad.addstr(0, 2, 'NAME'.ljust(68), curses.A_REVERSE) @@ -94,7 +94,7 @@ class PagecacheStats(): filesize = self._file_size[filename] added = self._file_pages[filename][0] removed = self._file_pages[filename][1] - if (filename > 64): + if (len(filename) > 64): filename = filename[-64:] pad.addstr(y, 2, filename) pad.addstr(y, 70, self.pages_to_mb(added).rjust(10)) @@ -122,7 +122,7 @@ class FileReaderThread(threading.Thread): Args: file_object: The file or pipe to read from. - output_queue: A Queue.Queue object that will receive the data + output_queue: A queue.Queue object that will receive the data text_file: If True, the file will be read one line at a time, and chunk_size will be ignored. If False, line breaks are ignored and chunk_size must be set to a positive integer. @@ -204,10 +204,10 @@ class AdbUtils(): shell=False, universal_newlines=True) except OSError as error: # This usually means that the adb executable was not found in the path. - print >> sys.stderr, ('\nThe command "%s" failed with the following error:' - % ' '.join(adb_command)) - print >> sys.stderr, ' %s' % str(error) - print >> sys.stderr, 'Is adb in your path?' + print('\nThe command "%s" failed with the following error:' + % ' '.join(adb_command), file=sys.stderr) + print(' %s' % str(error), file=sys.stderr) + print('Is adb in your path?', file=sys.stderr) adb_return_code = error.errno adb_output = error except subprocess.CalledProcessError as error: @@ -265,11 +265,11 @@ def get_inode_data(datafile, dumpfile, adb_serial): 'find /apex /system /system_ext /product /data /vendor ' + '-exec stat -c "%d %i %s %n" {} \;', adb_serial) if stat_dump is None: - print 'Could not retrieve inode data from device.' + print('Could not retrieve inode data from device.') sys.exit(1) if dumpfile is not None: - print 'Storing inode data in ' + dumpfile + print('Storing inode data in ' + dumpfile) f = open(dumpfile, 'w') f.write(stat_dump) f.close() @@ -285,8 +285,8 @@ def read_and_parse_trace_file(trace_file, pagecache_stats, app_name): def read_and_parse_trace_data_live(stdout, stderr, pagecache_stats, app_name): # Start reading trace data - stdout_queue = Queue.Queue(maxsize=128) - stderr_queue = Queue.Queue() + stdout_queue = queue.Queue(maxsize=128) + stderr_queue = queue.Queue() stdout_thread = FileReaderThread(stdout, stdout_queue, text_file=True, chunk_size=64) @@ -316,13 +316,13 @@ def read_and_parse_trace_data_live(stdout, stderr, pagecache_stats, app_name): not stdout_queue.empty() or not stderr_queue.empty()): while not stderr_queue.empty(): # Pass along errors from adb. - line = stderr_queue.get() + line = stderr_queue.get().decode("utf-8") sys.stderr.write(line) while True: try: - line = stdout_queue.get(True, STATS_UPDATE_INTERVAL) + line = stdout_queue.get(True, STATS_UPDATE_INTERVAL).decode("utf-8") parse_atrace_line(line, pagecache_stats, app_name) - except Queue.Empty: + except (queue.Empty, KeyboardInterrupt): break key = '' @@ -335,9 +335,9 @@ def read_and_parse_trace_data_live(stdout, stderr, pagecache_stats, app_name): pagecache_stats.reset_stats() pagecache_stats.print_stats_curses(pagecache_pad) - except Exception, e: + except Exception as e: curses.endwin() - print e + print(e) finally: curses.endwin() # The threads should already have stopped, so this is just for cleanup. @@ -383,7 +383,7 @@ def main(): if options.trace_file is not None: if not os.path.isfile(options.trace_file): - print >> sys.stderr, ('Couldn\'t load trace file.') + print('Couldn\'t load trace file.', file=sys.stderr) sys.exit(1) trace_file = open(options.trace_file, 'r') read_and_parse_trace_file(trace_file, pagecache_stats, options.app_name) @@ -396,7 +396,7 @@ def main(): atrace = subprocess.Popen(trace_cmd, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE) except OSError as error: - print >> sys.stderr, ('The command failed') + print('The command failed', file=sys.stderr) sys.exit(1) read_and_parse_trace_data_live(atrace.stdout, atrace.stderr, pagecache_stats, options.app_name) diff --git a/profcollectd/README.md b/profcollectd/README.md index e9066000..1017e7e4 100644 --- a/profcollectd/README.md +++ b/profcollectd/README.md @@ -39,21 +39,18 @@ Setting the frequency value to `0` disables collection for the corresponding eve #### Custom configuration -In adb root: +Under adb root: ``` # Record every 60s (By default, record every 10m). The actual interval will be longer than the # set value if the device goes to hibernation. -oriole:/ # setprop persist.device_config.profcollect_native_boot.collection_interval 60 +oriole:/ # device_config put profcollect_native_boot collection_interval 60 # Each time recording, record ETM data for 1s (By default, it's 0.5s). -oriole:/ # setprop persist.device_config.profcollect_native_boot.sampling_period 1000 +oriole:/ # device_config put profcollect_native_boot sampling_period 1000 # Set ETM data storage limit to 50G (By default, it is 512M). -oriole:/ # setprop persist.device_config.profcollect_native_boot.max_trace_limit 53687091200 - -# Enable ETM data collection (By default, it's decided by the server). -oriole:/ # setprop persist.device_config.profcollect_native_boot.enabled true +oriole:/ # device_config put profcollect_native_boot max_trace_limit 53687091200 # After adjusting configuration, need to restart profcollectd oriole:/ # setprop ctl.stop profcollectd diff --git a/profcollectd/libprofcollectd/config.rs b/profcollectd/libprofcollectd/config.rs index 8a6c9e4f..0a1a9ad9 100644 --- a/profcollectd/libprofcollectd/config.rs +++ b/profcollectd/libprofcollectd/config.rs @@ -24,6 +24,7 @@ use serde::{Deserialize, Serialize}; use std::error::Error; use std::fs::{read_dir, remove_file}; use std::path::Path; +use std::process::Command; use std::str::FromStr; use std::time::Duration; @@ -61,6 +62,8 @@ pub struct Config { pub binary_filter: String, /// Maximum size of the trace directory. pub max_trace_limit: u64, + /// The kernel release version + pub kernel_release: String, } impl Config { @@ -78,13 +81,14 @@ impl Config { "max_trace_limit", /* 512MB */ 512 * 1024 * 1024, )?, + kernel_release: get_kernel_release(), }) } } -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.")) } } @@ -151,6 +155,15 @@ fn generate_random_node_id() -> MacAddr6 { MacAddr6::from(node_id) } +fn get_kernel_release() -> String { + match Command::new("uname").args(["-r"]).output() { + Ok(output) if output.status.success() => { + String::from_utf8_lossy(&output.stdout).trim().to_string() + } + _ => String::new(), + } +} + pub fn clear_data() -> Result<()> { fn remove_files(path: &Path) -> Result<()> { read_dir(path)? diff --git a/profcollectd/libprofcollectd/lib.rs b/profcollectd/libprofcollectd/lib.rs index c8e39753..f0d32a8b 100644 --- a/profcollectd/libprofcollectd/lib.rs +++ b/profcollectd/libprofcollectd/lib.rs @@ -85,7 +85,7 @@ pub fn init_service(schedule_now: bool) -> Result<()> { } fn get_profcollectd_service() -> Result<binder::Strong<dyn IProfCollectd::IProfCollectd>> { - binder::get_interface(PROFCOLLECTD_SERVICE_NAME) + binder::wait_for_interface(PROFCOLLECTD_SERVICE_NAME) .context("Failed to get profcollectd binder service, is profcollectd running?") } diff --git a/profcollectd/profcollectd.rc b/profcollectd/profcollectd.rc index 312c7003..faeb4124 100644 --- a/profcollectd/profcollectd.rc +++ b/profcollectd/profcollectd.rc @@ -13,8 +13,8 @@ on post-fs-data mkdir /data/misc/profcollectd/output 0770 shell shell mkdir /data/misc/profcollectd/report 0770 shell shell -on boot && property:persist.device_config.profcollect_native_boot.enabled=true +on boot && property:persist.device_config.aconfig_flags.profcollect_native_boot.enabled=true start profcollectd -on boot && property:persist.device_config.profcollect_native_boot.enabled= +on boot && property:persist.device_config.aconfig_flags.profcollect_native_boot.enabled= exec_background - root shell -- /system/bin/profcollectctl reset diff --git a/simpleperf/Android.bp b/simpleperf/Android.bp index 4ebc0ed2..daefb8b3 100644 --- a/simpleperf/Android.bp +++ b/simpleperf/Android.bp @@ -753,5 +753,29 @@ cc_fuzz { enabled: false, }, }, + dictionary: "simpleperf_dict.dict", corpus: ["testdata/**/*.data"], } + +cc_fuzz { + name: "simpleperf_writer_fuzzer", + defaults: [ + "simpleperf_static_libs", + ], + host_supported: true, + srcs: [ + "simpleperf_writer_fuzzer.cpp", + ], + static_libs: [ + "libsimpleperf", + ], + target: { + linux: { + // Fuzzer may not be able to load libdexfile. So statically link it. + static_libs: ["libdexfile_static"], + }, + windows: { + enabled: false, + }, + }, +} diff --git a/simpleperf/cmd_inject.cpp b/simpleperf/cmd_inject.cpp index 182f4fd2..6798a32a 100644 --- a/simpleperf/cmd_inject.cpp +++ b/simpleperf/cmd_inject.cpp @@ -683,46 +683,52 @@ class AutoFDOWriter { const AutoFDOBinaryInfo& binary = binary_map_[key]; // AutoFDO text format needs file_offsets instead of virtual addrs in a binary. And it uses // below formula: vaddr = file_offset + GetFirstLoadSegmentVaddr(). - uint64_t first_load_segment_addr = binary.first_load_segment_addr; - - auto to_offset = [&](uint64_t vaddr) -> uint64_t { - if (vaddr == 0) { - return 0; + uint64_t base_addr = binary.first_load_segment_addr; + + // Write range_count_map. Sort the output by addrs. + std::vector<std::pair<AddrPair, uint64_t>> range_counts; + for (std::pair<AddrPair, uint64_t> p : binary.range_count_map) { + if (p.first.first >= base_addr && p.first.second >= base_addr) { + p.first.first -= base_addr; + p.first.second -= base_addr; + range_counts.emplace_back(p); } - CHECK_GE(vaddr, first_load_segment_addr); - return vaddr - first_load_segment_addr; - }; - - // Write range_count_map. - std::map<AddrPair, uint64_t> range_count_map(binary.range_count_map.begin(), - binary.range_count_map.end()); - fprintf(output_fp.get(), "%zu\n", range_count_map.size()); - for (const auto& pair2 : range_count_map) { - const AddrPair& addr_range = pair2.first; - uint64_t count = pair2.second; - - fprintf(output_fp.get(), "%" PRIx64 "-%" PRIx64 ":%" PRIu64 "\n", - to_offset(addr_range.first), to_offset(addr_range.second), count); } - - // Write addr_count_map. - std::map<uint64_t, uint64_t> address_count_map(binary.address_count_map.begin(), - binary.address_count_map.end()); - fprintf(output_fp.get(), "%zu\n", address_count_map.size()); - for (const auto& [addr, count] : address_count_map) { - fprintf(output_fp.get(), "%" PRIx64 ":%" PRIu64 "\n", to_offset(addr), count); + std::sort(range_counts.begin(), range_counts.end()); + fprintf(output_fp.get(), "%zu\n", range_counts.size()); + for (const auto& p : range_counts) { + fprintf(output_fp.get(), "%" PRIx64 "-%" PRIx64 ":%" PRIu64 "\n", p.first.first, + p.first.second, p.second); } - // Write branch_count_map. - std::map<AddrPair, uint64_t> branch_count_map(binary.branch_count_map.begin(), - binary.branch_count_map.end()); - fprintf(output_fp.get(), "%zu\n", branch_count_map.size()); - for (const auto& pair2 : branch_count_map) { - const AddrPair& branch = pair2.first; - uint64_t count = pair2.second; + // Write addr_count_map. Sort the output by addrs. + std::vector<std::pair<uint64_t, uint64_t>> address_counts; + for (std::pair<uint64_t, uint64_t> p : binary.address_count_map) { + if (p.first >= base_addr) { + p.first -= base_addr; + address_counts.emplace_back(p); + } + } + std::sort(address_counts.begin(), address_counts.end()); + fprintf(output_fp.get(), "%zu\n", address_counts.size()); + for (const auto& p : address_counts) { + fprintf(output_fp.get(), "%" PRIx64 ":%" PRIu64 "\n", p.first, p.second); + } - fprintf(output_fp.get(), "%" PRIx64 "->%" PRIx64 ":%" PRIu64 "\n", to_offset(branch.first), - to_offset(branch.second), count); + // Write branch_count_map. Sort the output by addrs. + std::vector<std::pair<AddrPair, uint64_t>> branch_counts; + for (std::pair<AddrPair, uint64_t> p : binary.branch_count_map) { + if (p.first.first >= base_addr) { + p.first.first -= base_addr; + p.first.second = (p.first.second >= base_addr) ? (p.first.second - base_addr) : 0; + branch_counts.emplace_back(p); + } + } + std::sort(branch_counts.begin(), branch_counts.end()); + fprintf(output_fp.get(), "%zu\n", branch_counts.size()); + for (const auto& p : branch_counts) { + fprintf(output_fp.get(), "%" PRIx64 "->%" PRIx64 ":%" PRIu64 "\n", p.first.first, + p.first.second, p.second); } // Write the binary path in comment. diff --git a/simpleperf/cmd_record.cpp b/simpleperf/cmd_record.cpp index e08b153b..cb9ad884 100644 --- a/simpleperf/cmd_record.cpp +++ b/simpleperf/cmd_record.cpp @@ -1057,7 +1057,7 @@ bool RecordCommand::ParseOptions(const std::vector<std::string>& args, etm_branch_list_generator_ = ETMBranchListGenerator::Create(system_wide_collection_); } uint32_t interval = 0; - if (options.PullUintValue("--etm-flush-interval", &interval)) { + if (options.PullUintValue("--etm-flush-interval", &interval) && interval != 0) { etm_flush_interval_ = std::chrono::milliseconds(interval); } diff --git a/simpleperf/libsimpleperf_report_fuzzer.cpp b/simpleperf/libsimpleperf_report_fuzzer.cpp index f448af87..3656be60 100644 --- a/simpleperf/libsimpleperf_report_fuzzer.cpp +++ b/simpleperf/libsimpleperf_report_fuzzer.cpp @@ -1,46 +1,87 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ -#include <android-base/file.h> - +#include <record_file.h> #include "command.h" -#include "report_lib_interface.cpp" +#include "fuzzer/FuzzedDataProvider.h" #include "test_util.h" using namespace simpleperf; +using namespace std; +using namespace android; -namespace { - -class CommandRegister { +class SimplePerfReportFuzzer { public: - CommandRegister() { RegisterDumpRecordCommand(); } -}; - -CommandRegister command_register; - -void TestReportLib(const char* record_file) { - ReportLib* report_lib = CreateReportLib(); - SetRecordFile(report_lib, record_file); - while (true) { - Sample* sample = GetNextSample(report_lib); - if (sample == nullptr) { - break; - } + SimplePerfReportFuzzer(const uint8_t* data, size_t size) : mFdp(data, size) { + /** + * Use maximum of 80% of buffer to write in FD and save at least 20% for fuzzing other APIs + */ + const int32_t dataSize = mFdp.ConsumeIntegralInRange<int32_t>(0, (size * 80) / 100); + std::vector<uint8_t> dataPointer = mFdp.ConsumeBytes<uint8_t>(dataSize); + android::base::WriteFully(mTempfile.fd, dataPointer.data(), dataPointer.size()); + RegisterDumpRecordCommand(); } - DestroyReportLib(report_lib); -} + void process(); -void TestDumpCmd(const char* record_file) { + private: + FuzzedDataProvider mFdp; + TemporaryFile mTempfile; + void TestDumpCmd(); +}; + +void SimplePerfReportFuzzer::TestDumpCmd() { std::unique_ptr<Command> dump_cmd = CreateCommandInstance("dump"); CaptureStdout capture; capture.Start(); - dump_cmd->Run({"-i", record_file, "--dump-etm", "raw,packet,element"}); + dump_cmd->Run({"-i", mTempfile.path, "--dump-etm", "raw,packet,element"}); } -} // namespace +void SimplePerfReportFuzzer::process() { + std::unique_ptr<RecordFileReader> reader = RecordFileReader::CreateInstance(mTempfile.path); + if (!reader.get()) { + return; + } + while (mFdp.remaining_bytes()) { + auto InvokeReader = mFdp.PickValueInArray<const std::function<void()>>({ + [&]() { reader->ReadCmdlineFeature(); }, + [&]() { reader->ReadBuildIdFeature(); }, + [&]() { reader->ReadFeatureString(mFdp.ConsumeIntegral<int32_t>() /* feature */); }, + [&]() { + vector<uint8_t> buf; + bool error; + reader->ReadAuxData(mFdp.ConsumeIntegral<uint32_t>() /* cpu */, + mFdp.ConsumeIntegral<uint64_t>() /* aux_offset */, + mFdp.ConsumeIntegral<size_t>() /* size */, buf, error); + }, + [&]() { reader->ReadDebugUnwindFeature(); }, + [&]() { reader->DataSection(); }, + [&]() { + ThreadTree thread_tree; + reader->LoadBuildIdAndFileFeatures(thread_tree); + }, + }); + InvokeReader(); + } + TestDumpCmd(); + reader->Close(); +} extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { - TemporaryFile tmpfile; - android::base::WriteFully(tmpfile.fd, data, size); - TestReportLib(tmpfile.path); - TestDumpCmd(tmpfile.path); + SimplePerfReportFuzzer simplePerfReportFuzzer(data, size); + simplePerfReportFuzzer.process(); return 0; } diff --git a/simpleperf/simpleperf_dict.dict b/simpleperf/simpleperf_dict.dict new file mode 100644 index 00000000..672bceac --- /dev/null +++ b/simpleperf/simpleperf_dict.dict @@ -0,0 +1 @@ +kw1="PERFILE2"
\ No newline at end of file diff --git a/simpleperf/simpleperf_writer_fuzzer.cpp b/simpleperf/simpleperf_writer_fuzzer.cpp new file mode 100644 index 00000000..06b8f4ed --- /dev/null +++ b/simpleperf/simpleperf_writer_fuzzer.cpp @@ -0,0 +1,175 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include <record_file.h> +#include "fuzzer/FuzzedDataProvider.h" + +using namespace simpleperf; +using namespace std; +using namespace android; +static uint32_t kMaxLen = 100; +static uint32_t kMinCount = 1; +static uint32_t kMaxCount = 1000; +static uint32_t kMinASCII = 0; +static uint32_t kMaxASCII = 255; + +class SimplePerfWriterFuzzer { + public: + SimplePerfWriterFuzzer(const uint8_t* data, size_t size) : mFdp(data, size) { + /** + * Use maximum of 80% of buffer to write in FD and save at least 20% for fuzzing other APIs + */ + const int32_t dataSize = mFdp.ConsumeIntegralInRange<int32_t>(0, (size * 80) / 100); + std::vector<uint8_t> dataPointer = mFdp.ConsumeBytes<uint8_t>(dataSize); + android::base::WriteFully(mTempfile.fd, dataPointer.data(), dataPointer.size()); + } + void process(); + + private: + FuzzedDataProvider mFdp; + TemporaryFile mTempfile; + EventAttrIds mAttributeIDs; + bool AddEvents(); + void CreateRecord(); +}; + +bool SimplePerfWriterFuzzer::AddEvents() { + size_t eventNo = mFdp.ConsumeIntegralInRange<size_t>(kMinCount, kMaxCount); + for (size_t idx = 0; idx < eventNo; ++idx) { + string event = mFdp.ConsumeRandomLengthString(kMaxLen); + if (event.size() == 0) { + event = "cpu-cycles"; + } + uint64_t fakeID = mAttributeIDs.size(); + mAttributeIDs.resize(mAttributeIDs.size() + 1); + EventAttrWithId& attrID = mAttributeIDs.back(); + std::unique_ptr<EventTypeAndModifier> eventTypeModifier = ParseEventType(event); + if (!eventTypeModifier) { + return false; + } + attrID.attr = CreateDefaultPerfEventAttr(eventTypeModifier->event_type); + attrID.ids.push_back(fakeID); + } + return true; +} + +void SimplePerfWriterFuzzer::process() { + std::unique_ptr<RecordFileWriter> writer = RecordFileWriter::CreateInstance(mTempfile.path); + if (!writer.get()) { + return; + } + writer->WriteAttrSection(mAttributeIDs); + int32_t features = mFdp.ConsumeIntegralInRange<int32_t>(kMinCount, kMaxCount); + + if (!AddEvents()) { + return; + } + + string event = mFdp.ConsumeRandomLengthString(kMaxLen); + size_t index = mFdp.ConsumeIntegralInRange<size_t>(0, mAttributeIDs.size() - 1); + perf_event_attr attr = mAttributeIDs[index].attr; + MmapRecord mmprcd( + attr, mFdp.ConsumeBool() /* in_kernel */, getpid() /* pid */, gettid() /* tid */, + mFdp.ConsumeIntegral<uint64_t>() /* addr */, mFdp.ConsumeIntegral<uint64_t>() /* len */, + mFdp.ConsumeIntegral<uint64_t>() /* pgoff */, event, + mFdp.ConsumeIntegral<uint64_t>() /* event_id */, mFdp.ConsumeIntegral<uint64_t>() /* time */); + + writer->WriteRecord(mmprcd); + writer->BeginWriteFeatures(features); + std::vector<Dso*> dsos; + while (features-- > 0) { + auto invokeWriter = mFdp.PickValueInArray<const std::function<void()>>({ + [&]() { + BuildId build_id(mFdp.ConsumeRandomLengthString(kMaxLen)); + std::vector<BuildIdRecord> buildIdRecords; + buildIdRecords.push_back( + BuildIdRecord(mFdp.ConsumeBool() /* in_kernel */, getpid() /* pid */, build_id, + mFdp.ConsumeRandomLengthString(kMaxLen) /* filename */)); + writer->WriteBuildIdFeature(buildIdRecords); + }, + [&]() { + writer->WriteFeatureString( + mFdp.ConsumeBool() ? PerfFileFormat::FEAT_OSRELEASE : PerfFileFormat::FEAT_ARCH, + mFdp.ConsumeRandomLengthString(kMaxLen) /* string */); + }, + [&]() { + std::vector<std::string> cmdline; + cmdline.push_back("simpleperf"); + int32_t iter = mFdp.ConsumeIntegralInRange<int32_t>(kMinCount, kMaxCount); + for (int32_t idx = 0; idx < iter; ++idx) { + cmdline.push_back(mFdp.ConsumeRandomLengthString(kMaxLen)); + } + writer->WriteCmdlineFeature(cmdline); + }, + [&]() { + int32_t iter = mFdp.ConsumeIntegralInRange<int32_t>(kMinCount, kMaxCount); + for (int32_t idx = 0; idx < iter; ++idx) { + DsoType dso_type = + (DsoType)mFdp.ConsumeIntegralInRange<size_t>(DSO_KERNEL, DSO_UNKNOWN_FILE); + std::unique_ptr<Dso> dso = + Dso::CreateDso(dso_type, mFdp.ConsumeRandomLengthString(kMaxLen) /* path */, + mFdp.ConsumeBool() /* force_64bit */); + dsos.push_back(dso.get()); + } + writer->WriteFileFeatures(dsos); + }, + [&]() { + std::unordered_map<std::string, std::string> info_map; + int32_t iter = mFdp.ConsumeIntegralInRange<int32_t>(kMinCount, kMaxCount); + for (int32_t idx = 0; idx < iter; ++idx) { + std::string key = mFdp.ConsumeRandomLengthString(kMaxLen); + std::string value = mFdp.ConsumeRandomLengthString(kMaxLen); + info_map[key] = value; + } + writer->WriteMetaInfoFeature(info_map); + }, + [&]() { writer->WriteBranchStackFeature(); }, + [&]() { + std::vector<uint64_t> auxtrace; + int32_t iter = mFdp.ConsumeIntegralInRange<int32_t>(kMinCount, kMaxCount); + for (int32_t idx = 0; idx < iter; ++idx) { + auxtrace.push_back(mFdp.ConsumeIntegral<uint64_t>()); + } + writer->WriteAuxTraceFeature(auxtrace); + }, + [&]() { + DebugUnwindFeature debugVector; + int32_t iter = mFdp.ConsumeIntegralInRange<int32_t>(kMinCount, kMaxCount); + for (int32_t idx = 0; idx < iter; ++idx) { + DebugUnwindFile testFile; + testFile.path = mFdp.ConsumeRandomLengthString(kMaxLen); + testFile.size = kMaxLen; + debugVector.push_back(testFile); + } + writer->WriteDebugUnwindFeature(debugVector); + }, + }); + invokeWriter(); + } + writer->EndWriteFeatures(); + writer->Close(); + for (Dso* dso : dsos) { + delete dso; + dso = nullptr; + } +} + +extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { + SimplePerfWriterFuzzer simplePerfWriterFuzzer(data, size); + simplePerfWriterFuzzer.process(); + return 0; +} diff --git a/tools/check_elf_alignment.sh b/tools/check_elf_alignment.sh index b74f34ae..6cf9f92b 100755 --- a/tools/check_elf_alignment.sh +++ b/tools/check_elf_alignment.sh @@ -16,7 +16,7 @@ usage() { 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]" + echo "Usage: ${progname} [input-path|input-APK|input-APEX]" } if [ ${#} -ne 1 ]; then @@ -61,6 +61,15 @@ if [[ "${dir}" == *.apk ]]; then dir="${tmp}" fi +if [[ "${dir}" == *.apex ]]; then + trap 'cleanup_trap' EXIT + + dir_filename=$(basename "${dir}") + tmp=$(mktemp -d -t "${dir_filename%.apex}_out_XXXXX") + deapexer extract "${dir}" "${tmp}" >/dev/null 2>&1 + dir="${tmp}" +fi + RED="\e[31m" GREEN="\e[32m" ENDCOLOR="\e[0m" @@ -70,11 +79,12 @@ unaligned_libs=() echo echo "=== ELF alignment ===" -matches="$(find "${dir}" -name "*.so" -type f)" +matches="$(find "${dir}" -type f \( -name "*.so" -or -executable \))" 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 + [[ $(file "${match}") == *"ELF"* ]] || continue + res="$(objdump -p "${match}" | grep LOAD | awk '{ print $NF }' | head -1)" + if [[ $res =~ 2**(1[4-9]|[2-9][0-9]|[1-9][0-9]{2,}) ]]; then echo -e "${match}: ${GREEN}ALIGNED${ENDCOLOR} ($res)" else echo -e "${match}: ${RED}UNALIGNED${ENDCOLOR} ($res)" |