summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--pagecache/pagecache.py50
-rw-r--r--profcollectd/README.md11
-rw-r--r--profcollectd/libprofcollectd/config.rs19
-rw-r--r--profcollectd/libprofcollectd/lib.rs2
-rw-r--r--profcollectd/profcollectd.rc4
-rw-r--r--simpleperf/Android.bp24
-rw-r--r--simpleperf/cmd_inject.cpp76
-rw-r--r--simpleperf/cmd_record.cpp2
-rw-r--r--simpleperf/libsimpleperf_report_fuzzer.cpp97
-rw-r--r--simpleperf/simpleperf_dict.dict1
-rw-r--r--simpleperf/simpleperf_writer_fuzzer.cpp175
-rwxr-xr-xtools/check_elf_alignment.sh18
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)"