summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAndroid Build Coastguard Worker <android-build-coastguard-worker@google.com>2022-04-28 21:36:02 +0000
committerAndroid Build Coastguard Worker <android-build-coastguard-worker@google.com>2022-04-28 21:36:02 +0000
commit0ddddbcd4bf1c3cda086cf0e6f29172d634198ef (patch)
treeff52905359cc3119eefdfdfd89ff245124845a77
parentfadc10185dd9273637bba099f9bcb083a4f296c8 (diff)
parentcd6b038e799ea13120e9676e0f1b23cdda4223db (diff)
downloadextras-0ddddbcd4bf1c3cda086cf0e6f29172d634198ef.tar.gz
Snap for 8513843 from cd6b038e799ea13120e9676e0f1b23cdda4223db to mainline-uwb-release
Change-Id: Ic1083a6fa079507b926639019278d6899de8dc47
-rw-r--r--alloc-stress/Android.bp1
-rw-r--r--alloc-stress/alloc-stress.cpp34
-rw-r--r--ext4_utils/mkuserimg_mke2fs.py4
-rw-r--r--ioblame/androidFsParser.py175
-rw-r--r--ioblame/ioblame.py528
-rw-r--r--ioblame/uidProcessMapper.py125
-rw-r--r--ioshark/compile_ioshark.c2
-rw-r--r--ioshark/compile_ioshark_subr.c2
-rw-r--r--ioshark/convert_format.c2
-rw-r--r--ioshark/dump_ioshark_filenames.c2
-rw-r--r--ioshark/ioshark_bench.c2
-rw-r--r--ioshark/ioshark_bench_mmap.c2
-rw-r--r--libfec/Android.bp2
-rw-r--r--simpleperf/Android.bp23
-rw-r--r--simpleperf/Android.mk2
-rw-r--r--simpleperf/IOEventLoop.cpp32
-rw-r--r--simpleperf/IOEventLoop.h25
-rw-r--r--simpleperf/IOEventLoop_test.cpp42
-rw-r--r--simpleperf/JITDebugReader.cpp78
-rw-r--r--simpleperf/JITDebugReader.h27
-rw-r--r--simpleperf/OfflineUnwinder.cpp3
-rw-r--r--simpleperf/OfflineUnwinder.h5
-rw-r--r--simpleperf/cmd_monitor.cpp13
-rw-r--r--simpleperf/cmd_record.cpp50
-rw-r--r--simpleperf/cmd_record_test.cpp8
-rw-r--r--simpleperf/doc/collect_etm_data_for_autofdo.md30
-rw-r--r--simpleperf/dso.cpp109
-rw-r--r--simpleperf/dso.h15
-rw-r--r--simpleperf/dso_test.cpp12
-rw-r--r--simpleperf/environment.h3
-rw-r--r--simpleperf/event_selection_set.cpp9
-rw-r--r--simpleperf/report_lib_interface.cpp15
-rwxr-xr-xsimpleperf/scripts/annotate.py25
-rwxr-xr-xsimpleperf/scripts/bin/android/arm/simpleperfbin2807668 -> 2812664 bytes
-rwxr-xr-xsimpleperf/scripts/bin/android/arm64/simpleperfbin3810832 -> 3819224 bytes
-rwxr-xr-xsimpleperf/scripts/bin/android/x86/simpleperfbin4372260 -> 4386820 bytes
-rwxr-xr-xsimpleperf/scripts/bin/android/x86_64/simpleperfbin4249312 -> 4244008 bytes
-rwxr-xr-xsimpleperf/scripts/bin/darwin/x86_64/libsimpleperf_report.dylibbin13978839 -> 13980215 bytes
-rwxr-xr-xsimpleperf/scripts/bin/darwin/x86_64/simpleperfbin13918013 -> 13902701 bytes
-rwxr-xr-xsimpleperf/scripts/bin/linux/x86_64/libsimpleperf_report.sobin7219712 -> 7208384 bytes
-rwxr-xr-xsimpleperf/scripts/bin/linux/x86_64/simpleperfbin7193024 -> 7181056 bytes
-rwxr-xr-xsimpleperf/scripts/bin/windows/x86_64/libsimpleperf_report.dllbin5598720 -> 5583872 bytes
-rwxr-xr-xsimpleperf/scripts/bin/windows/x86_64/simpleperf.exebin4654080 -> 4638208 bytes
-rwxr-xr-xsimpleperf/scripts/gecko_profile_generator.py21
-rwxr-xr-xsimpleperf/scripts/inferno/inferno.py13
-rwxr-xr-xsimpleperf/scripts/pprof_proto_generator.py26
-rwxr-xr-xsimpleperf/scripts/purgatorio/purgatorio.py6
-rwxr-xr-xsimpleperf/scripts/report_html.py25
-rwxr-xr-xsimpleperf/scripts/report_sample.py31
-rw-r--r--simpleperf/scripts/simpleperf_report_lib.py15
-rw-r--r--simpleperf/scripts/simpleperf_utils.py57
-rwxr-xr-xsimpleperf/scripts/stackcollapse.py23
-rwxr-xr-xsimpleperf/scripts/test/do_test.py13
-rw-r--r--simpleperf/scripts/test/pprof_proto_generator_test.py4
-rw-r--r--simpleperf/scripts/test/report_lib_test.py29
-rw-r--r--simpleperf/scripts/test/report_sample_test.py57
-rw-r--r--tests/kernel.config/AndroidTest.xml2
-rw-r--r--verity/Android.bp7
-rw-r--r--verity/build_verity_metadata.py20
-rw-r--r--verity/fec/Android.bp5
60 files changed, 1301 insertions, 460 deletions
diff --git a/alloc-stress/Android.bp b/alloc-stress/Android.bp
index 4d74841a..e64c822b 100644
--- a/alloc-stress/Android.bp
+++ b/alloc-stress/Android.bp
@@ -24,6 +24,7 @@ cc_binary {
shared_libs: [
"libhardware",
"libcutils",
+ "libprocessgroup",
],
cppflags: [
"-g",
diff --git a/alloc-stress/alloc-stress.cpp b/alloc-stress/alloc-stress.cpp
index 33c12635..ad2353cc 100644
--- a/alloc-stress/alloc-stress.cpp
+++ b/alloc-stress/alloc-stress.cpp
@@ -14,6 +14,8 @@
#include <tuple>
#include <vector>
+#include <processgroup/processgroup.h>
+
//#define TRACE_CHILD_LIFETIME
#ifdef TRACE_CHILD_LIFETIME
@@ -157,36 +159,6 @@ static void write_oomadj_to_lmkd(int oomadj) {
cout << "Wrote " << written << " bytes to lmkd control socket." << endl;
}
-static void create_memcg() {
- char buf[256];
- uid_t uid = getuid();
- pid_t pid = getpid();
-
- snprintf(buf, sizeof(buf), "/dev/memcg/apps/uid_%u", uid);
- int tasks = mkdir(buf, S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH);
- if (tasks < 0 && errno != EEXIST) {
- cerr << "Failed to create memory cgroup under " << buf << endl;
- return;
- }
-
- snprintf(buf, sizeof(buf), "/dev/memcg/apps/uid_%u/pid_%u", uid, pid);
- tasks = mkdir(buf, S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH);
- if (tasks < 0) {
- cerr << "Failed to create memory cgroup under " << buf << endl;
- return;
- }
-
- snprintf(buf, sizeof(buf), "/dev/memcg/apps/uid_%u/pid_%u/tasks", uid, pid);
- tasks = open(buf, O_WRONLY);
- if (tasks < 0) {
- cerr << "Unable to add process to memory cgroup" << endl;
- return;
- }
- snprintf(buf, sizeof(buf), "%u", pid);
- write(tasks, buf, strlen(buf));
- close(tasks);
-}
-
void usage() {
cout << "Application allocates memory until it's killed." << endl
<< "It starts at max oom_score_adj and gradually "
@@ -202,7 +174,7 @@ int main(int argc, char* argv[]) {
if ((argc > 1) && (std::string(argv[1]) == "--worker")) {
if (std::string(argv[5]) == "1") {
- create_memcg();
+ createProcessGroup(getuid(), getpid(), true);
}
write_oomadj_to_lmkd(atoi(argv[2]));
diff --git a/ext4_utils/mkuserimg_mke2fs.py b/ext4_utils/mkuserimg_mke2fs.py
index 599e40ee..41ae8cd7 100644
--- a/ext4_utils/mkuserimg_mke2fs.py
+++ b/ext4_utils/mkuserimg_mke2fs.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env python
+#!/usr/bin/env python3
#
# Copyright (C) 2018 The Android Open Source Project
#
@@ -165,7 +165,7 @@ def ConstructE2fsCommands(args):
if args.flash_erase_block_size:
mke2fs_extended_opts.append("stripe_width={}".format(
- int(args.flash_erase_block_size) / BLOCKSIZE))
+ int(args.flash_erase_block_size) // BLOCKSIZE))
if args.flash_logical_block_size:
# stride should be the max of 8kb and the logical block size
stride = max(int(args.flash_logical_block_size), 8192)
diff --git a/ioblame/androidFsParser.py b/ioblame/androidFsParser.py
new file mode 100644
index 00000000..6ea38dc3
--- /dev/null
+++ b/ioblame/androidFsParser.py
@@ -0,0 +1,175 @@
+#!/usr/bin/env python
+#
+# Copyright (C) 2022 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.
+#
+"""Trace parser for android_fs traces."""
+
+import collections
+import re
+
+# ex) bt_stack_manage-21277 [000] .... 5879.043608: android_fs_datawrite_start: entry_name /misc/bluedroid/bt_config.bak.new, offset 0, bytes 408, cmdline bt_stack_manage, pid 21277, i_size 0, ino 9103
+RE_WRITE_START = r".+-([0-9]+).*\s+([0-9]+\.[0-9]+):\s+android_fs_datawrite_start:\sentry_name\s(\S+)\,\soffset\s([0-9]+)\,\sbytes\s([0-9]+)\,\scmdline\s(\S+)\,\spid\s([0-9]+)\,\si_size\s([0-9]+)\,\sino\s([0-9]+)"
+
+# ex) dumpsys-21321 [001] .... 5877.599324: android_fs_dataread_start: entry_name /system/lib64/libbinder.so, offset 311296, bytes 4096, cmdline dumpsys, pid 21321, i_size 848848, ino 2397
+RE_READ_START = r".+-([0-9]+).*\s+([0-9]+\.[0-9]+):\s+android_fs_dataread_start:\sentry_name\s(\S+)\,\soffset\s([0-9]+)\,\sbytes\s([0-9]+)\,\scmdline\s(\S+)\,\spid\s([0-9]+)\,\si_size\s([0-9]+)\,\sino\s([0-9]+)"
+
+MIN_PID_BYTES = 1024 * 1024 # 1 MiB
+SMALL_FILE_BYTES = 1024 # 1 KiB
+
+
+class ProcessTrace:
+
+ def __init__(self, cmdLine, filename, numBytes):
+ self.cmdLine = cmdLine
+ self.totalBytes = numBytes
+ self.bytesByFiles = {filename: numBytes}
+
+ def add_file_trace(self, filename, numBytes):
+ self.totalBytes += numBytes
+ if filename in self.bytesByFiles:
+ self.bytesByFiles[filename] += numBytes
+ else:
+ self.bytesByFiles[filename] = numBytes
+
+ def dump(self, mode, outputFile):
+ smallFileCnt = 0
+ smallFileBytes = 0
+ for _, numBytes in self.bytesByFiles.items():
+ if numBytes < SMALL_FILE_BYTES:
+ smallFileCnt += 1
+ smallFileBytes += numBytes
+
+ if (smallFileCnt != 0):
+ outputFile.write(
+ "Process: {}, Traced {} KB: {}, Small file count: {}, Small file KB: {}\n"
+ .format(self.cmdLine, mode, to_kib(self.totalBytes), smallFileCnt,
+ to_kib(smallFileBytes)))
+
+ else:
+ outputFile.write("Process: {}, Traced {} KB: {}\n".format(
+ self.cmdLine, mode, to_kib(self.totalBytes)))
+
+ if (smallFileCnt == len(self.bytesByFiles)):
+ return
+
+ sortedEntries = collections.OrderedDict(
+ sorted(
+ self.bytesByFiles.items(), key=lambda item: item[1], reverse=True))
+
+ for i in range(len(sortedEntries)):
+ filename, numBytes = sortedEntries.popitem(last=False)
+ if numBytes < SMALL_FILE_BYTES:
+ # Entries are sorted by bytes. So, break on the first small file entry.
+ break
+
+ outputFile.write("File: {}, {} KB: {}\n".format(filename, mode,
+ to_kib(numBytes)))
+
+
+class UidTrace:
+
+ def __init__(self, uid, cmdLine, filename, numBytes):
+ self.uid = uid
+ self.packageName = ""
+ self.totalBytes = numBytes
+ self.traceByProcess = {cmdLine: ProcessTrace(cmdLine, filename, numBytes)}
+
+ def add_process_trace(self, cmdLine, filename, numBytes):
+ self.totalBytes += numBytes
+ if cmdLine in self.traceByProcess:
+ self.traceByProcess[cmdLine].add_file_trace(filename, numBytes)
+ else:
+ self.traceByProcess[cmdLine] = ProcessTrace(cmdLine, filename, numBytes)
+
+ def dump(self, mode, outputFile):
+ outputFile.write("Traced {} KB: {}\n\n".format(mode,
+ to_kib(self.totalBytes)))
+
+ if self.totalBytes < MIN_PID_BYTES:
+ return
+
+ sortedEntries = collections.OrderedDict(
+ sorted(
+ self.traceByProcess.items(),
+ key=lambda item: item[1].totalBytes,
+ reverse=True))
+ totalEntries = len(sortedEntries)
+ for i in range(totalEntries):
+ _, processTrace = sortedEntries.popitem(last=False)
+ if processTrace.totalBytes < MIN_PID_BYTES:
+ # Entries are sorted by bytes. So, break on the first small PID entry.
+ break
+
+ processTrace.dump(mode, outputFile)
+ if i < totalEntries - 1:
+ outputFile.write("\n")
+
+
+class AndroidFsParser:
+
+ def __init__(self, re_string, uidProcessMapper):
+ self.traceByUid = {} # Key: uid, Value: UidTrace
+ if (re_string == RE_WRITE_START):
+ self.mode = "write"
+ else:
+ self.mode = "read"
+ self.re_matcher = re.compile(re_string)
+ self.uidProcessMapper = uidProcessMapper
+ self.totalBytes = 0
+
+ def parse(self, line):
+ match = self.re_matcher.match(line)
+ if not match:
+ return False
+ try:
+ self.do_parse_start(line, match)
+ except Exception:
+ print("cannot parse: {}".format(line))
+ raise
+ return True
+
+ def do_parse_start(self, line, match):
+ pid = int(match.group(1))
+ # start_time = float(match.group(2)) * 1000 #ms
+ filename = match.group(3)
+ # offset = int(match.group(4))
+ numBytes = int(match.group(5))
+ cmdLine = match.group(6)
+ pid = int(match.group(7))
+ # isize = int(match.group(8))
+ # ino = int(match.group(9))
+ self.totalBytes += numBytes
+ uid = self.uidProcessMapper.get_uid(cmdLine, pid)
+
+ if uid in self.traceByUid:
+ self.traceByUid[uid].add_process_trace(cmdLine, filename, numBytes)
+ else:
+ self.traceByUid[uid] = UidTrace(uid, cmdLine, filename, numBytes)
+
+ def dumpTotal(self, outputFile):
+ if self.totalBytes > 0:
+ outputFile.write("Traced system-wide {} KB: {}\n\n".format(
+ self.mode, to_kib(self.totalBytes)))
+
+ def dump(self, uid, outputFile):
+ if uid not in self.traceByUid:
+ return
+
+ uidTrace = self.traceByUid[uid]
+ uidTrace.dump(self.mode, outputFile)
+
+
+def to_kib(bytes):
+ return bytes / 1024
diff --git a/ioblame/ioblame.py b/ioblame/ioblame.py
new file mode 100644
index 00000000..e00a5ecc
--- /dev/null
+++ b/ioblame/ioblame.py
@@ -0,0 +1,528 @@
+#!/usr/bin/env python
+#
+# Copyright (C) 2022 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.
+#
+"""Reports disk I/O usage by UID/Package, process, and file level breakdowns."""
+
+from datetime import datetime
+from collections import namedtuple
+
+import androidFsParser
+import argparse
+import collections
+import os
+import psutil
+import re
+import signal
+import subprocess
+import sys
+import threading
+import time
+import uidProcessMapper
+
+# ex) lrwxrwxrwx 1 root root 16 1970-01-06 13:22 userdata -> /dev/block/sda14
+RE_LS_BLOCK_DEVICE = r"\S+\s[0-9]+\s\S+\s\S+\s+[0-9]+\s[0-9\-]+\s[0-9]+\:[0-9]+\suserdata\s\-\>\s\/dev\/block\/(\S+)"
+
+# ex) 1002 246373245 418936352 1818624 0 0 0 0 0 0 0
+RE_UID_IO_STATS_LINE = r"([0-9]+)\s([0-9]+)\s([0-9]+)\s([0-9]+)\s([0-9]+)\s([0-9]+)\s([0-9]+)\s([0-9]+)\s([0-9]+)\s([0-9]+)\s([0-9]+)"
+
+# ex) 253 5 dm-5 3117 0 354656 3324 0 0 0 0 0 2696 3324 0 0 0 0
+RE_DISK_STATS_LINE = r"\s+([0-9]+)\s+([0-9]+)\s([a-z\-0-9]+)\s([0-9]+)\s([0-9]+)\s([0-9]+)\s([0-9]+)\s([0-9]+)\s([0-9]+)\s([0-9]+)\s([0-9]+)\s([0-9]+)\s([0-9]+)\s([0-9]+)\s([0-9]+)\s([0-9]+)\s([0-9]+)\s([0-9]+)"
+
+ADB_CMD = "adb"
+
+TEMP_TRACE_FILE = "temp_trace_file.txt"
+CARWATCHDOG_DUMP = "carwatchdog_dump.txt"
+OUTPUT_FILE = "ioblame_out.txt"
+
+WATCHDOG_BUFFER_SECS = 600
+
+DID_RECEIVE_SIGINT = False
+
+
+def signal_handler(sig, frame):
+ global DID_RECEIVE_SIGINT
+ DID_RECEIVE_SIGINT = True
+ print("Received signal interrupt")
+
+
+def init_arguments():
+ parser = argparse.ArgumentParser(
+ description="Collect and process android_fs traces")
+ parser.add_argument(
+ "-s",
+ "--serial",
+ dest="serial",
+ action="store",
+ help="Android device serial number")
+ parser.add_argument(
+ "-r",
+ "--trace_reads",
+ default=False,
+ action="store_true",
+ dest="traceReads",
+ help="Trace android_fs_dataread_start")
+ parser.add_argument(
+ "-w",
+ "--trace_writes",
+ default=False,
+ action="store_true",
+ dest="traceWrites",
+ help="Trace android_fs_datawrite_start")
+ parser.add_argument(
+ "-d",
+ "--trace_duration",
+ type=int,
+ default=3600,
+ dest="traceDuration",
+ help="Total trace duration in seconds")
+ parser.add_argument(
+ "-i",
+ "--sampling_interval",
+ type=int,
+ default=300,
+ dest="samplingInterval",
+ help="Sampling interval in seconds for CarWatchdog collection (applicable only on"
+ " automotive form-factor")
+ parser.add_argument(
+ "-o",
+ "--output_directory",
+ type=dir_path,
+ default=os.getcwd(),
+ dest="outputDir",
+ help="Output directory")
+
+ return parser.parse_args()
+
+
+def verify_arguments(args):
+ if args.serial is not None:
+ global ADB_CMD
+ ADB_CMD = "%s %s" % ("adb -s", args.serial)
+ if not args.traceReads and not args.traceWrites:
+ raise argparse.ArgumentTypeError(
+ "Must provide at least one of the --trace_reads or --trace_writes options"
+ )
+
+
+def dir_path(path):
+ if os.path.isdir(path):
+ return path
+ else:
+ raise argparse.ArgumentTypeError(
+ "{} is not a valid directory path".format(path))
+
+
+def run_adb_cmd(cmd):
+ r = subprocess.check_output(ADB_CMD + " " + cmd, shell=True)
+ return r.decode("utf-8")
+
+
+def run_adb_shell_cmd(cmd):
+ return run_adb_cmd("shell " + cmd)
+
+
+def run_adb_shell_cmd_strip_output(cmd):
+ return run_adb_cmd("shell " + cmd).strip()
+
+
+def run_adb_shell_cmd_ignore_err(cmd):
+ try:
+ r = subprocess.run(
+ ADB_CMD + " shell " + cmd, shell=True, capture_output=True)
+ return r.stdout.decode("utf-8")
+ except Exception:
+ return ""
+
+
+def run_shell_cmd(cmd):
+ return subprocess.check_output(cmd, shell=True)
+
+
+def run_bg_adb_shell_cmd(cmd):
+ return subprocess.Popen(ADB_CMD + " shell " + cmd, shell=True)
+
+
+def run_bg_shell_cmd(cmd):
+ return subprocess.Popen(cmd, shell=True)
+
+
+def get_block_dev():
+ model = run_adb_shell_cmd_strip_output(
+ "'getprop ro.product.name' | sed \'s/[ \\t\\r\\n]*$//\'")
+ print("Found %s Device" % model)
+
+ if "emu" in model:
+ return "vda"
+
+ result = run_adb_shell_cmd_strip_output(
+ "'ls -la /dev/block/bootdevice/by-name | grep userdata'")
+
+ match = re.compile(RE_LS_BLOCK_DEVICE).match(result)
+ if not match:
+ print("Unknown Device {} -- trying Pixel config".format(model))
+ return "sda"
+
+ return match.group(1)
+
+
+def prep_to_do_something():
+ run_adb_shell_cmd("'echo 3 > /proc/sys/vm/drop_caches'")
+ time.sleep(1)
+
+
+def setup_tracepoints(shouldTraceReads, shouldTraceWrites):
+ # This is a good point to check if the Android FS tracepoints are enabled in the
+ # kernel or not
+ isTraceEnabled = run_adb_shell_cmd(
+ "'if [ -d /sys/kernel/tracing/events/android_fs ]; then echo 0; else echo 1; fi'"
+ )
+
+ if isTraceEnabled == 0:
+ raise RuntimeError("Android FS tracing is not enabled")
+
+ run_adb_shell_cmd("'echo 0 > /sys/kernel/tracing/tracing_on;\
+ echo 0 > /sys/kernel/tracing/trace;\
+ echo 0 > /sys/kernel/tracing/events/ext4/enable;\
+ echo 0 > /sys/kernel/tracing/events/block/enable'")
+
+ if shouldTraceReads:
+ run_adb_shell_cmd(
+ "'echo 1 > /sys/kernel/tracing/events/android_fs/android_fs_dataread_start/enable'"
+ )
+
+ if shouldTraceWrites:
+ run_adb_shell_cmd(
+ "'echo 1 > /sys/kernel/tracing/events/android_fs/android_fs_datawrite_start/enable'"
+ )
+
+ run_adb_shell_cmd("'echo 1 > /sys/kernel/tracing/tracing_on'")
+
+
+def clear_tracing(shouldTraceReads, shouldTraceWrites):
+ if shouldTraceReads:
+ run_adb_shell_cmd(
+ "'echo 0 > /sys/kernel/tracing/events/android_fs/android_fs_dataread_start/enable'"
+ )
+
+ if shouldTraceWrites:
+ run_adb_shell_cmd(
+ "'echo 0 > /sys/kernel/tracing/events/android_fs/android_fs_datawrite_start/enable'"
+ )
+
+ run_adb_shell_cmd("'echo 0 > /sys/kernel/tracing/tracing_on'")
+
+
+def start_streaming_trace(traceFile):
+ return run_bg_adb_shell_cmd(
+ "'cat /sys/kernel/tracing/trace_pipe | grep -e android_fs_data -e android_fs_writepages'\
+ > {}".format(traceFile))
+
+
+def stop_streaming_trace(sub_proc):
+ process = psutil.Process(sub_proc.pid)
+ for child_proc in process.children(recursive=True):
+ child_proc.kill()
+ process.kill()
+
+
+class carwatchdog_collection(threading.Thread):
+
+ def __init__(self, traceDuration, samplingInterval):
+ threading.Thread.__init__(self)
+ self.traceDuration = traceDuration
+ self.samplingInterval = samplingInterval
+
+ def run(self):
+ isBootCompleted = 0
+
+ while isBootCompleted == 0:
+ isBootCompleted = run_adb_shell_cmd_strip_output(
+ "'getprop sys.boot_completed'")
+ time.sleep(1)
+
+ # Clean up previous state.
+ run_adb_shell_cmd(
+ "'dumpsys android.automotive.watchdog.ICarWatchdog/default\
+ --stop_perf &>/dev/null'")
+
+ run_adb_shell_cmd(
+ "'dumpsys android.automotive.watchdog.ICarWatchdog/default \
+ --start_perf --max_duration {} --interval {}'".format(
+ self.traceDuration + WATCHDOG_BUFFER_SECS, self.samplingInterval))
+
+
+def stop_carwatchdog_collection(outputDir):
+ run_adb_shell_cmd("'dumpsys android.automotive.watchdog.ICarWatchdog/default"
+ " --stop_perf' > {}/{}".format(outputDir, CARWATCHDOG_DUMP))
+
+
+def do_something(outpuDir, traceDuration, samplingInterval, uidProcessMapperObj):
+ buildChars = run_adb_shell_cmd_strip_output(
+ "'getprop ro.build.characteristics'")
+
+ carwatchdog_collection_thread = None
+ if "automotive" in buildChars:
+ carwatchdog_collection_thread = carwatchdog_collection(
+ traceDuration, samplingInterval)
+ carwatchdog_collection_thread.start()
+
+ for i in range(1, traceDuration):
+ if DID_RECEIVE_SIGINT:
+ break
+ now = time.process_time()
+ read_uid_process_mapping(uidProcessMapperObj)
+ taken = time.process_time() - now
+ if (taken < 1):
+ time.sleep(1 - taken)
+
+ read_uid_package_mapping(uidProcessMapperObj)
+
+ if "automotive" in buildChars:
+ carwatchdog_collection_thread.join()
+ stop_carwatchdog_collection(outpuDir)
+
+
+def read_uid_process_mapping(uidProcessMapperObj):
+ procStatusDump = run_adb_shell_cmd_ignore_err(
+ "'cat /proc/*/status /proc/*/task/*/status 2> /dev/null'")
+
+ uidProcessMapperObj.parse_proc_status_dump(procStatusDump)
+
+
+def read_uid_package_mapping(uidProcessMapperObj):
+ packageMappingDump = run_adb_shell_cmd_ignore_err(
+ "'pm list packages -a -U | sort | uniq'")
+
+ uidProcessMapperObj.parse_uid_package_dump(packageMappingDump)
+
+
+# Parser for "/proc/diskstats".
+class DiskStats:
+
+ def __init__(self, readIos, readSectors, writeIos, writeSectors):
+ self.readIos = readIos
+ self.readSectors = readSectors
+ self.writeIos = writeIos
+ self.writeSectors = writeSectors
+
+ def delta(self, other):
+ return DiskStats(self.readIos - other.readIos,
+ self.readSectors - other.readSectors,
+ self.writeIos - other.writeIos,
+ self.writeSectors - other.writeSectors)
+
+ def dump(self, shouldDumpReads, shouldDumpWrites, outputFile):
+ if self.readIos is None or self.readIos is None or self.readIos is None\
+ or self.readIos is None:
+ outputFile.write("Missing disk stats")
+ return
+
+ if (shouldDumpReads):
+ outputFile.write("Total dev block reads: {} KB, IOs: {}\n".format(
+ self.readSectors / 2, self.readIos))
+
+ if (shouldDumpWrites):
+ outputFile.write("Total dev block writes: {} KB, IOs: {}\n".format(
+ self.writeSectors / 2, self.writeIos))
+
+
+def get_disk_stats(blockDev):
+ line = run_adb_shell_cmd(
+ "'cat /proc/diskstats' | fgrep -w {}".format(blockDev))
+ matcher = re.compile(RE_DISK_STATS_LINE)
+ match = matcher.match(line)
+
+ if not match:
+ return None
+
+ readIos = int(match.group(4))
+ readSectors = int(match.group(6))
+ writeIos = int(match.group(8))
+ writeSectors = int(match.group(10))
+
+ return DiskStats(readIos, readSectors, writeIos, writeSectors)
+
+
+IoBytes = namedtuple("IoBytes", "rdBytes wrBytes")
+
+
+# Parser for "/proc/uid_io/stats".
+class UidIoStats:
+
+ def __init__(self):
+ self.uidIoStatsReMatcher = re.compile(RE_UID_IO_STATS_LINE)
+ self.ioBytesByUid = {} # Key: UID, Value: IoBytes
+ self.totalIoBytes = IoBytes(rdBytes=0, wrBytes=0)
+
+ def parse(self, dump):
+ totalRdBytes = 0
+ totalWrBytes = 0
+ for line in dump.split("\n"):
+ (uid, ioBytes) = self.parse_uid_io_bytes(line)
+ self.ioBytesByUid[uid] = ioBytes
+ totalRdBytes += ioBytes.rdBytes
+ totalWrBytes += ioBytes.wrBytes
+
+ self.totalIoBytes = IoBytes(rdBytes=totalRdBytes, wrBytes=totalWrBytes)
+
+ def parse_uid_io_bytes(self, line):
+ match = self.uidIoStatsReMatcher.match(line)
+ if not match:
+ return None
+ return (int(match.group(1)),
+ IoBytes(
+ rdBytes=(int(match.group(4)) + int(match.group(8))),
+ wrBytes=(int(match.group(5)) + int(match.group(9)))))
+
+ def delta(self, other):
+ deltaStats = UidIoStats()
+ deltaStats.totalIoBytes = IoBytes(
+ rdBytes=self.totalIoBytes.rdBytes - other.totalIoBytes.rdBytes,
+ wrBytes=self.totalIoBytes.wrBytes - other.totalIoBytes.wrBytes)
+
+ for uid, ioBytes in self.ioBytesByUid.items():
+ if uid not in other.ioBytesByUid:
+ deltaStats.ioBytesByUid[uid] = ioBytes
+ continue
+ otherIoBytes = other.ioBytesByUid[uid]
+ rdBytes = ioBytes.rdBytes - otherIoBytes.rdBytes if ioBytes.rdBytes > otherIoBytes.rdBytes\
+ else 0
+ wrBytes = ioBytes.wrBytes - otherIoBytes.wrBytes if ioBytes.wrBytes > otherIoBytes.wrBytes\
+ else 0
+ deltaStats.ioBytesByUid[uid] = IoBytes(rdBytes=rdBytes, wrBytes=wrBytes)
+ return deltaStats
+
+ def dumpTotal(self, mode, outputFile):
+ totalBytes = self.totalIoBytes.wrBytes if mode == "write" else self.totalIoBytes.rdBytes
+ outputFile.write("Total system-wide {} KB: {}\n".format(
+ mode, to_kib(totalBytes)))
+
+ def dump(self, uidProcessMapperObj, mode, func, outputFile):
+ sortedEntries = collections.OrderedDict(
+ sorted(
+ self.ioBytesByUid.items(),
+ key=lambda item: item[1].wrBytes
+ if mode == "write" else item[1].rdBytes,
+ reverse=True))
+ totalEntries = len(sortedEntries)
+ for i in range(totalEntries):
+ uid, ioBytes = sortedEntries.popitem(last=False)
+ totalBytes = ioBytes.wrBytes if mode == "write" else ioBytes.rdBytes
+ if totalBytes < androidFsParser.MIN_PID_BYTES:
+ continue
+ uidInfo = uidProcessMapperObj.get_uid_info(uid)
+ outputFile.write("{}, Total {} KB: {}\n".format(uidInfo.to_string(), mode,
+ to_kib(totalBytes)))
+ func(uid)
+ outputFile.write("\n" + ("=" * 100) + "\n")
+ if i < totalEntries - 1:
+ outputFile.write("\n")
+
+
+def get_uid_io_stats():
+ uidIoStatsDump = run_adb_shell_cmd_strip_output("'cat /proc/uid_io/stats'")
+ uidIoStats = UidIoStats()
+ uidIoStats.parse(uidIoStatsDump)
+ return uidIoStats
+
+
+def to_kib(bytes):
+ return bytes / 1024
+
+
+def main(argv):
+ signal.signal(signal.SIGINT, signal_handler)
+
+ args = init_arguments()
+ verify_arguments(args)
+
+ run_adb_cmd("root")
+ buildDesc = run_adb_shell_cmd_strip_output("'getprop ro.build.description'")
+ blockDev = get_block_dev()
+
+ prep_to_do_something()
+ setup_tracepoints(args.traceReads, args.traceWrites)
+ diskStatsBefore = get_disk_stats(blockDev)
+ uidIoStatsBefore = get_uid_io_stats()
+
+ traceFile = "{}/{}".format(args.outputDir, TEMP_TRACE_FILE)
+
+ startDateTime = datetime.now()
+ proc = start_streaming_trace(traceFile)
+ print("Started trace streaming")
+
+ uidProcessMapperObj = uidProcessMapper.UidProcessMapper()
+ do_something(args.outputDir, args.traceDuration, args.samplingInterval,
+ uidProcessMapperObj)
+
+ stop_streaming_trace(proc)
+ endDateTime = datetime.now()
+ print("Stopped trace streaming")
+
+ clear_tracing(args.traceReads, args.traceWrites)
+
+ diskStatsAfter = get_disk_stats(blockDev)
+ uidIoStatsAfter = get_uid_io_stats()
+ diskStatsDelta = diskStatsAfter.delta(diskStatsBefore)
+ uidIoStatsDelta = uidIoStatsAfter.delta(uidIoStatsBefore)
+
+ print("Completed device side collection")
+
+ writeParser = androidFsParser.AndroidFsParser(androidFsParser.RE_WRITE_START,
+ uidProcessMapperObj)
+ readParser = androidFsParser.AndroidFsParser(androidFsParser.RE_READ_START,
+ uidProcessMapperObj)
+ with open(traceFile) as file:
+ for line in file:
+ if args.traceWrites and writeParser.parse(line):
+ continue
+ if args.traceReads:
+ readParser.parse(line)
+
+ outputFile = open("{}/{}".format(args.outputDir, OUTPUT_FILE), "w")
+ outputFile.write("Collection datetime: {}, Total duration: {}\n".format(
+ endDateTime, endDateTime - startDateTime))
+ outputFile.write("Build description: {}\n".format(buildDesc))
+ outputFile.write(
+ "Minimum KB per process or UID: {}, Small file KB: {}\n\n".format(
+ to_kib(androidFsParser.MIN_PID_BYTES),
+ to_kib(androidFsParser.SMALL_FILE_BYTES)))
+
+ diskStatsDelta.dump(args.traceReads, args.traceWrites, outputFile)
+
+ if args.traceWrites:
+ uidIoStatsDelta.dumpTotal("write", outputFile)
+ writeParser.dumpTotal(outputFile)
+ uidIoStatsDelta.dump(uidProcessMapperObj, "write",
+ lambda uid: writeParser.dump(uid, outputFile),
+ outputFile)
+
+ if args.traceWrites and args.traceReads:
+ outputFile.write("\n\n\n")
+
+ if args.traceReads:
+ uidIoStatsDelta.dumpTotal("read", outputFile)
+ readParser.dumpTotal(outputFile)
+ uidIoStatsDelta.dump(uidProcessMapperObj, "read",
+ lambda uid: readParser.dump(uid, outputFile),
+ outputFile)
+
+ outputFile.close()
+ run_shell_cmd("rm {}/{}".format(args.outputDir, TEMP_TRACE_FILE))
+
+
+if __name__ == "__main__":
+ main(sys.argv)
diff --git a/ioblame/uidProcessMapper.py b/ioblame/uidProcessMapper.py
new file mode 100644
index 00000000..0c0566b9
--- /dev/null
+++ b/ioblame/uidProcessMapper.py
@@ -0,0 +1,125 @@
+#!/usr/bin/env python
+#
+# Copyright (C) 2022 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.
+#
+"""Package <-> UID <-> Process mapper."""
+
+import re
+
+# ex) Name: init
+PROC_STATUS_NAME_LINE = r"Name:\s+(\S+)"
+
+# ex) Pid: 1
+PROC_STATUS_PID_LINE = r"Pid:\s+([0-9]+)"
+
+# ex) Uid: 0 0 0 0
+PROC_STATUS_UID_LINE = r"Uid:\s+([0-9]+)\s+([0-9]+)\s+([0-9]+)\s+([0-9]+)"
+
+# ex) package:com.google.android.car.uxr.sample uid:1000
+PACKAGE_UID_LINE = r"package:(\S+)\suid:([0-9]+)"
+
+USER_ID_OFFSET = 100000
+AID_APP_START = 10000
+UNKNOWN_UID = -1
+
+
+class UidInfo:
+
+ def __init__(self, uid, packageName=None):
+ self.uid = uid
+ self.packageName = packageName
+
+ def to_string(self):
+ appId = int(self.uid % USER_ID_OFFSET)
+ if self.uid == UNKNOWN_UID:
+ return "UID: UNKNOWN"
+ elif self.packageName is None and appId < AID_APP_START:
+ return "User ID: {}, Native service AID: {}".format(
+ int(self.uid / USER_ID_OFFSET), appId)
+ elif self.packageName is None:
+ return "User ID: {}, App ID: {}".format(
+ int(self.uid / USER_ID_OFFSET), appId)
+ else:
+ return "User ID: {}, Package name: {}".format(
+ int(self.uid / USER_ID_OFFSET), self.packageName)
+
+
+class UidProcessMapper:
+
+ def __init__(self):
+ self.nameReMatcher = re.compile(PROC_STATUS_NAME_LINE)
+ self.pidReMatcher = re.compile(PROC_STATUS_PID_LINE)
+ self.uidReMatcher = re.compile(PROC_STATUS_UID_LINE)
+ self.packageUidMatcher = re.compile(PACKAGE_UID_LINE)
+ self.uidByProcessDict = {} # Key: Process Name, Value: {PID: UID}
+ self.packageNameByAppId = {} # Key: App ID, Value: Package name
+
+ def parse_proc_status_dump(self, dump):
+ name, pid, uid = "", "", ""
+
+ for line in dump.split("\n"):
+ if line.startswith("Name:"):
+ name = self.match_re(self.nameReMatcher, line)
+ pid, uid = "", ""
+ elif line.startswith("Pid:"):
+ pid = self.match_re(self.pidReMatcher, line)
+ uid = ""
+ elif line.startswith("Uid:"):
+ uid = self.match_re(self.uidReMatcher, line)
+ if name != "" and pid != "" and uid != "":
+ self.add_mapping(name, int(pid), int(uid))
+ name, pid, uid = "", "", ""
+
+ def parse_uid_package_dump(self, dump):
+ for line in dump.split("\n"):
+ if line == "":
+ continue
+
+ match = self.packageUidMatcher.match(line)
+ if (match):
+ packageName = match.group(1)
+ appId = int(match.group(2))
+ if appId in self.packageNameByAppId:
+ self.packageNameByAppId[appId].add(packageName)
+ else:
+ self.packageNameByAppId[appId] = {packageName}
+ else:
+ print("'{}' line doesn't match '{}' regex".format(
+ line, self.packageUidMatcher))
+
+ def match_re(self, reMatcher, line):
+ match = reMatcher.match(line)
+ if not match:
+ return ""
+ return match.group(1)
+
+ def add_mapping(self, name, pid, uid):
+ if name in self.uidByProcessDict:
+ self.uidByProcessDict[name][pid] = uid
+ else:
+ self.uidByProcessDict[name] = {pid: uid}
+
+ def get_uid(self, name, pid):
+ if name in self.uidByProcessDict:
+ if pid in self.uidByProcessDict[name]:
+ return self.uidByProcessDict[name][pid]
+ return UNKNOWN_UID
+
+ def get_uid_info(self, uid):
+ appId = uid % USER_ID_OFFSET
+ if appId in self.packageNameByAppId:
+ return UidInfo(uid, " | ".join(self.packageNameByAppId[appId]))
+ else:
+ return UidInfo(uid)
diff --git a/ioshark/compile_ioshark.c b/ioshark/compile_ioshark.c
index 13755299..fe9085cf 100644
--- a/ioshark/compile_ioshark.c
+++ b/ioshark/compile_ioshark.c
@@ -22,7 +22,7 @@
#include <signal.h>
#include <string.h>
#include <sys/stat.h>
-#include <sys/errno.h>
+#include <errno.h>
#include <fcntl.h>
#include <ctype.h>
#include "ioshark.h"
diff --git a/ioshark/compile_ioshark_subr.c b/ioshark/compile_ioshark_subr.c
index 43fb2544..6fc301cc 100644
--- a/ioshark/compile_ioshark_subr.c
+++ b/ioshark/compile_ioshark_subr.c
@@ -21,7 +21,7 @@
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
-#include <sys/errno.h>
+#include <errno.h>
#include "ioshark.h"
#include "compile_ioshark.h"
#include <endian.h>
diff --git a/ioshark/convert_format.c b/ioshark/convert_format.c
index 3436ca77..989bfcb8 100644
--- a/ioshark/convert_format.c
+++ b/ioshark/convert_format.c
@@ -22,7 +22,7 @@
#include <signal.h>
#include <string.h>
#include <sys/stat.h>
-#include <sys/errno.h>
+#include <errno.h>
#include <fcntl.h>
#include <ctype.h>
#include <endian.h>
diff --git a/ioshark/dump_ioshark_filenames.c b/ioshark/dump_ioshark_filenames.c
index c082c274..a28ab790 100644
--- a/ioshark/dump_ioshark_filenames.c
+++ b/ioshark/dump_ioshark_filenames.c
@@ -21,7 +21,7 @@
#include <signal.h>
#include <string.h>
#include <sys/stat.h>
-#include <sys/errno.h>
+#include <errno.h>
#include <fcntl.h>
#include <ctype.h>
#include "ioshark.h"
diff --git a/ioshark/ioshark_bench.c b/ioshark/ioshark_bench.c
index e44a2a53..9540b741 100644
--- a/ioshark/ioshark_bench.c
+++ b/ioshark/ioshark_bench.c
@@ -22,7 +22,7 @@
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
-#include <sys/errno.h>
+#include <errno.h>
#include <fcntl.h>
#include <string.h>
#include <assert.h>
diff --git a/ioshark/ioshark_bench_mmap.c b/ioshark/ioshark_bench_mmap.c
index e8b3acce..3b37fbf6 100644
--- a/ioshark/ioshark_bench_mmap.c
+++ b/ioshark/ioshark_bench_mmap.c
@@ -23,7 +23,7 @@
#include <string.h>
#include <pthread.h>
#include <sys/stat.h>
-#include <sys/errno.h>
+#include <errno.h>
#include <fcntl.h>
#include <string.h>
#include <assert.h>
diff --git a/libfec/Android.bp b/libfec/Android.bp
index c19f6a90..302d8e40 100644
--- a/libfec/Android.bp
+++ b/libfec/Android.bp
@@ -58,7 +58,7 @@ cc_defaults {
"-DFEC_NO_KLOG",
],
},
- linux_glibc: {
+ host_linux: {
sanitize: {
misc_undefined: ["integer"],
},
diff --git a/simpleperf/Android.bp b/simpleperf/Android.bp
index b5edda80..58943931 100644
--- a/simpleperf/Android.bp
+++ b/simpleperf/Android.bp
@@ -131,6 +131,9 @@ cc_defaults {
linux_glibc_x86_64: {
stl: "libc++_static",
},
+ linux_musl_x86_64: {
+ stl: "libc++_static",
+ },
windows: {
enabled: true,
stl: "libc++_static",
@@ -434,6 +437,16 @@ cc_binary {
dir: "simpleperf/linux/x86_64",
},
},
+ linux_musl_x86: {
+ dist: {
+ dir: "simpleperf/linux_musl/x86",
+ },
+ },
+ linux_musl_x86_64: {
+ dist: {
+ dir: "simpleperf/linux_musl/x86_64",
+ },
+ },
windows_x86: {
dist: {
dir: "simpleperf/windows/x86",
@@ -518,6 +531,16 @@ cc_library_shared {
dir: "simpleperf/linux/x86_64",
},
},
+ linux_musl_x86: {
+ dist: {
+ dir: "simpleperf/linux_musl/x86",
+ },
+ },
+ linux_musl_x86_64: {
+ dist: {
+ dir: "simpleperf/linux_musl/x86_64",
+ },
+ },
windows_x86: {
dist: {
dir: "simpleperf/windows/x86",
diff --git a/simpleperf/Android.mk b/simpleperf/Android.mk
index df0009f4..954c28c0 100644
--- a/simpleperf/Android.mk
+++ b/simpleperf/Android.mk
@@ -33,4 +33,6 @@ SIMPLEPERF_SCRIPT_PATH := \
$(SIMPLEPERF_SCRIPT_PATH) : $(SOONG_ZIP)
$(hide) $(SOONG_ZIP) -d -o $@ -C system/extras/simpleperf $(SIMPLEPERF_SCRIPT_LIST)
+$(call declare-1p-target,$(SIMPLEPERF_SCRIPT_PATH),system/extras)
+
$(call dist-for-goals,simpleperf,$(SIMPLEPERF_SCRIPT_PATH):simpleperf/simpleperf_script.zip)
diff --git a/simpleperf/IOEventLoop.cpp b/simpleperf/IOEventLoop.cpp
index 06bdd713..239fff97 100644
--- a/simpleperf/IOEventLoop.cpp
+++ b/simpleperf/IOEventLoop.cpp
@@ -84,6 +84,10 @@ bool IOEventLoop::EnsureInit() {
return false;
}
event_config_free(cfg);
+ if (event_base_priority_init(ebase_, 2) != 0) {
+ LOG(ERROR) << "event_base_priority_init failed";
+ return false;
+ }
}
if (ebase_ == nullptr) {
LOG(ERROR) << "failed to create event_base";
@@ -110,39 +114,44 @@ static bool MakeFdNonBlocking(int fd) {
return true;
}
-IOEventRef IOEventLoop::AddReadEvent(int fd, const std::function<bool()>& callback) {
+IOEventRef IOEventLoop::AddReadEvent(int fd, const std::function<bool()>& callback,
+ IOEventPriority priority) {
if (!MakeFdNonBlocking(fd)) {
return nullptr;
}
- return AddEvent(fd, EV_READ | EV_PERSIST, nullptr, callback);
+ return AddEvent(fd, EV_READ | EV_PERSIST, nullptr, callback, priority);
}
-IOEventRef IOEventLoop::AddWriteEvent(int fd, const std::function<bool()>& callback) {
+IOEventRef IOEventLoop::AddWriteEvent(int fd, const std::function<bool()>& callback,
+ IOEventPriority priority) {
if (!MakeFdNonBlocking(fd)) {
return nullptr;
}
- return AddEvent(fd, EV_WRITE | EV_PERSIST, nullptr, callback);
+ return AddEvent(fd, EV_WRITE | EV_PERSIST, nullptr, callback, priority);
}
-bool IOEventLoop::AddSignalEvent(int sig, const std::function<bool()>& callback) {
- return AddEvent(sig, EV_SIGNAL | EV_PERSIST, nullptr, callback) != nullptr;
+bool IOEventLoop::AddSignalEvent(int sig, const std::function<bool()>& callback,
+ IOEventPriority priority) {
+ return AddEvent(sig, EV_SIGNAL | EV_PERSIST, nullptr, callback, priority) != nullptr;
}
-bool IOEventLoop::AddSignalEvents(std::vector<int> sigs, const std::function<bool()>& callback) {
+bool IOEventLoop::AddSignalEvents(std::vector<int> sigs, const std::function<bool()>& callback,
+ IOEventPriority priority) {
for (auto sig : sigs) {
- if (!AddSignalEvent(sig, callback)) {
+ if (!AddSignalEvent(sig, callback, priority)) {
return false;
}
}
return true;
}
-IOEventRef IOEventLoop::AddPeriodicEvent(timeval duration, const std::function<bool()>& callback) {
- return AddEvent(-1, EV_PERSIST, &duration, callback);
+IOEventRef IOEventLoop::AddPeriodicEvent(timeval duration, const std::function<bool()>& callback,
+ IOEventPriority priority) {
+ return AddEvent(-1, EV_PERSIST, &duration, callback, priority);
}
IOEventRef IOEventLoop::AddEvent(int fd_or_sig, int16_t events, timeval* timeout,
- const std::function<bool()>& callback) {
+ const std::function<bool()>& callback, IOEventPriority priority) {
if (!EnsureInit()) {
return nullptr;
}
@@ -152,6 +161,7 @@ IOEventRef IOEventLoop::AddEvent(int fd_or_sig, int16_t events, timeval* timeout
LOG(ERROR) << "event_new() failed";
return nullptr;
}
+ event_priority_set(e->e, priority);
if (event_add(e->e, timeout) != 0) {
LOG(ERROR) << "event_add() failed";
return nullptr;
diff --git a/simpleperf/IOEventLoop.h b/simpleperf/IOEventLoop.h
index f0192969..1578a4d0 100644
--- a/simpleperf/IOEventLoop.h
+++ b/simpleperf/IOEventLoop.h
@@ -18,6 +18,7 @@
#define SIMPLE_PERF_IOEVENT_LOOP_H_
#include <stdint.h>
+#include <sys/time.h>
#include <time.h>
#include <functional>
@@ -31,6 +32,12 @@ namespace simpleperf {
struct IOEvent;
typedef IOEvent* IOEventRef;
+enum IOEventPriority {
+ // Lower value means higher priority.
+ IOEventHighPriority = 0,
+ IOEventLowPriority = 1,
+};
+
// IOEventLoop is a class wrapper of libevent, it monitors events happened,
// and calls the corresponding callbacks. Possible events are: file ready to
// read, file ready to write, signal happens, periodic timer timeout.
@@ -45,22 +52,27 @@ class IOEventLoop {
// Register a read Event, so [callback] is called when [fd] can be read
// without blocking. If registered successfully, return the reference
// to control the Event, otherwise return nullptr.
- IOEventRef AddReadEvent(int fd, const std::function<bool()>& callback);
+ IOEventRef AddReadEvent(int fd, const std::function<bool()>& callback,
+ IOEventPriority priority = IOEventLowPriority);
// Register a write Event, so [callback] is called when [fd] can be written
// without blocking.
- IOEventRef AddWriteEvent(int fd, const std::function<bool()>& callback);
+ IOEventRef AddWriteEvent(int fd, const std::function<bool()>& callback,
+ IOEventPriority priority = IOEventLowPriority);
// Register a signal Event, so [callback] is called each time signal [sig]
// happens.
- bool AddSignalEvent(int sig, const std::function<bool()>& callback);
+ bool AddSignalEvent(int sig, const std::function<bool()>& callback,
+ IOEventPriority priority = IOEventLowPriority);
// Register a vector of signal Events.
- bool AddSignalEvents(std::vector<int> sigs, const std::function<bool()>& callback);
+ bool AddSignalEvents(std::vector<int> sigs, const std::function<bool()>& callback,
+ IOEventPriority priority = IOEventLowPriority);
// Register a periodic Event, so [callback] is called periodically every
// [duration].
- IOEventRef AddPeriodicEvent(timeval duration, const std::function<bool()>& callback);
+ IOEventRef AddPeriodicEvent(timeval duration, const std::function<bool()>& callback,
+ IOEventPriority priority = IOEventLowPriority);
// Run a loop polling for Events. It only exits when ExitLoop() is called
// in a callback function of registered Events.
@@ -80,7 +92,8 @@ class IOEventLoop {
private:
bool EnsureInit();
IOEventRef AddEvent(int fd_or_sig, int16_t events, timeval* timeout,
- const std::function<bool()>& callback);
+ const std::function<bool()>& callback,
+ IOEventPriority priority = IOEventLowPriority);
static void EventCallbackFn(int, int16_t, void*);
event_base* ebase_;
diff --git a/simpleperf/IOEventLoop_test.cpp b/simpleperf/IOEventLoop_test.cpp
index 09f64522..658fe820 100644
--- a/simpleperf/IOEventLoop_test.cpp
+++ b/simpleperf/IOEventLoop_test.cpp
@@ -250,3 +250,45 @@ TEST(IOEventLoop, exit_before_loop) {
IOEventLoop loop;
ASSERT_TRUE(loop.ExitLoop());
}
+
+TEST(IOEventLoop, priority) {
+ int low_priority_fd[2];
+ ASSERT_EQ(0, pipe(low_priority_fd));
+ int high_priority_fd[2];
+ ASSERT_EQ(0, pipe(high_priority_fd));
+
+ IOEventLoop loop;
+ int count = 0;
+
+ ASSERT_NE(nullptr, loop.AddReadEvent(
+ low_priority_fd[0],
+ [&]() {
+ char c;
+ read(low_priority_fd[0], &c, 1);
+ CHECK_EQ(count, 1);
+ count++;
+ return loop.ExitLoop();
+ },
+ IOEventLowPriority));
+
+ ASSERT_NE(nullptr, loop.AddReadEvent(
+ high_priority_fd[0],
+ [&]() {
+ char c;
+ read(high_priority_fd[0], &c, 1);
+ CHECK_EQ(count, 0);
+ count++;
+ return true;
+ },
+ IOEventHighPriority));
+
+ char c;
+ CHECK_EQ(write(low_priority_fd[1], &c, 1), 1);
+ CHECK_EQ(write(high_priority_fd[1], &c, 1), 1);
+ ASSERT_TRUE(loop.RunLoop());
+ ASSERT_EQ(2, count);
+ for (int i = 0; i < 2; i++) {
+ close(low_priority_fd[i]);
+ close(high_priority_fd[i]);
+ }
+}
diff --git a/simpleperf/JITDebugReader.cpp b/simpleperf/JITDebugReader.cpp
index 52db2959..28058940 100644
--- a/simpleperf/JITDebugReader.cpp
+++ b/simpleperf/JITDebugReader.cpp
@@ -411,17 +411,15 @@ bool JITDebugReader::InitializeProcess(Process& process) {
if (art_lib_path.empty()) {
return false;
}
- process.is_64bit = art_lib_path.find("lib64") != std::string::npos;
// 2. Read libart.so to find the addresses of __jit_debug_descriptor and __dex_debug_descriptor.
- const DescriptorsLocation* location = GetDescriptorsLocation(art_lib_path, process.is_64bit);
+ const DescriptorsLocation* location = GetDescriptorsLocation(art_lib_path);
if (location == nullptr) {
return false;
}
- process.descriptors_addr = location->relative_addr + min_vaddr_in_memory;
- process.descriptors_size = location->size;
- process.jit_descriptor_offset = location->jit_descriptor_offset;
- process.dex_descriptor_offset = location->dex_descriptor_offset;
+ process.is_64bit = location->is_64bit;
+ process.jit_descriptor_addr = location->jit_descriptor_addr + min_vaddr_in_memory;
+ process.dex_descriptor_addr = location->dex_descriptor_addr + min_vaddr_in_memory;
for (auto& map : thread_mmaps) {
if (StartsWith(map.name, kJITZygoteCacheMmapPrefix)) {
@@ -434,10 +432,10 @@ bool JITDebugReader::InitializeProcess(Process& process) {
}
const JITDebugReader::DescriptorsLocation* JITDebugReader::GetDescriptorsLocation(
- const std::string& art_lib_path, bool is_64bit) {
+ const std::string& art_lib_path) {
auto it = descriptors_location_cache_.find(art_lib_path);
if (it != descriptors_location_cache_.end()) {
- return it->second.relative_addr == 0u ? nullptr : &it->second;
+ return it->second.jit_descriptor_addr == 0u ? nullptr : &it->second;
}
DescriptorsLocation& location = descriptors_location_cache_[art_lib_path];
@@ -469,18 +467,9 @@ const JITDebugReader::DescriptorsLocation* JITDebugReader::GetDescriptorsLocatio
if (jit_addr == 0u || dex_addr == 0u) {
return nullptr;
}
- location.relative_addr = std::min(jit_addr, dex_addr);
- location.size = std::max(jit_addr, dex_addr) +
- (is_64bit ? sizeof(JITDescriptor64) : sizeof(JITDescriptor32)) -
- location.relative_addr;
- if (location.size >= 4096u) {
- PLOG(WARNING) << "The descriptors_size is unexpected large: " << location.size;
- }
- if (descriptors_buf_.size() < location.size) {
- descriptors_buf_.resize(location.size);
- }
- location.jit_descriptor_offset = jit_addr - location.relative_addr;
- location.dex_descriptor_offset = dex_addr - location.relative_addr;
+ location.is_64bit = elf->Is64Bit();
+ location.jit_descriptor_addr = jit_addr;
+ location.dex_descriptor_addr = dex_addr;
return &location;
}
@@ -505,14 +494,40 @@ bool JITDebugReader::ReadRemoteMem(Process& process, uint64_t remote_addr, uint6
bool JITDebugReader::ReadDescriptors(Process& process, Descriptor* jit_descriptor,
Descriptor* dex_descriptor) {
- if (!ReadRemoteMem(process, process.descriptors_addr, process.descriptors_size,
- descriptors_buf_.data())) {
+ if (process.is_64bit) {
+ return ReadDescriptorsImpl<JITDescriptor64>(process, jit_descriptor, dex_descriptor);
+ }
+ return ReadDescriptorsImpl<JITDescriptor32>(process, jit_descriptor, dex_descriptor);
+}
+
+template <typename DescriptorT>
+bool JITDebugReader::ReadDescriptorsImpl(Process& process, Descriptor* jit_descriptor,
+ Descriptor* dex_descriptor) {
+ DescriptorT raw_jit_descriptor;
+ DescriptorT raw_dex_descriptor;
+ iovec local_iovs[2];
+ local_iovs[0].iov_base = &raw_jit_descriptor;
+ local_iovs[0].iov_len = sizeof(DescriptorT);
+ local_iovs[1].iov_base = &raw_dex_descriptor;
+ local_iovs[1].iov_len = sizeof(DescriptorT);
+ iovec remote_iovs[2];
+ remote_iovs[0].iov_base =
+ reinterpret_cast<void*>(static_cast<uintptr_t>(process.jit_descriptor_addr));
+ remote_iovs[0].iov_len = sizeof(DescriptorT);
+ remote_iovs[1].iov_base =
+ reinterpret_cast<void*>(static_cast<uintptr_t>(process.dex_descriptor_addr));
+ remote_iovs[1].iov_len = sizeof(DescriptorT);
+ ssize_t result = process_vm_readv(process.pid, local_iovs, 2, remote_iovs, 2, 0);
+ if (static_cast<size_t>(result) != sizeof(DescriptorT) * 2) {
+ PLOG(DEBUG) << "ReadDescriptor(pid " << process.pid << ", jit_addr " << std::hex
+ << process.jit_descriptor_addr << ", dex_addr " << process.dex_descriptor_addr
+ << ") failed";
+ process.died = true;
return false;
}
- if (!LoadDescriptor(process.is_64bit, &descriptors_buf_[process.jit_descriptor_offset],
- jit_descriptor) ||
- !LoadDescriptor(process.is_64bit, &descriptors_buf_[process.dex_descriptor_offset],
- dex_descriptor)) {
+
+ if (!ParseDescriptor(raw_jit_descriptor, jit_descriptor) ||
+ !ParseDescriptor(raw_dex_descriptor, dex_descriptor)) {
return false;
}
jit_descriptor->type = DescriptorType::kJIT;
@@ -520,17 +535,8 @@ bool JITDebugReader::ReadDescriptors(Process& process, Descriptor* jit_descripto
return true;
}
-bool JITDebugReader::LoadDescriptor(bool is_64bit, const char* data, Descriptor* descriptor) {
- if (is_64bit) {
- return LoadDescriptorImpl<JITDescriptor64>(data, descriptor);
- }
- return LoadDescriptorImpl<JITDescriptor32>(data, descriptor);
-}
-
template <typename DescriptorT>
-bool JITDebugReader::LoadDescriptorImpl(const char* data, Descriptor* descriptor) {
- DescriptorT raw_descriptor;
- MoveFromBinaryFormat(raw_descriptor, data);
+bool JITDebugReader::ParseDescriptor(const DescriptorT& raw_descriptor, Descriptor* descriptor) {
if (!raw_descriptor.Valid()) {
return false;
}
diff --git a/simpleperf/JITDebugReader.h b/simpleperf/JITDebugReader.h
index 72f3790b..b9e984d5 100644
--- a/simpleperf/JITDebugReader.h
+++ b/simpleperf/JITDebugReader.h
@@ -162,13 +162,10 @@ class JITDebugReader {
bool initialized = false;
bool died = false;
bool is_64bit = false;
- // The jit descriptor and dex descriptor can be read in one process_vm_readv() call.
- uint64_t descriptors_addr = 0;
- uint64_t descriptors_size = 0;
- // offset relative to descriptors_addr
- uint64_t jit_descriptor_offset = 0;
- // offset relative to descriptors_addr
- uint64_t dex_descriptor_offset = 0;
+ // remote addr of jit descriptor
+ uint64_t jit_descriptor_addr = 0;
+ // remote addr of dex descriptor
+ uint64_t dex_descriptor_addr = 0;
// The state we know about the remote jit debug descriptor.
Descriptor last_jit_descriptor;
@@ -181,10 +178,9 @@ class JITDebugReader {
// The location of descriptors in libart.so.
struct DescriptorsLocation {
- uint64_t relative_addr = 0;
- uint64_t size = 0;
- uint64_t jit_descriptor_offset = 0;
- uint64_t dex_descriptor_offset = 0;
+ bool is_64bit = false;
+ uint64_t jit_descriptor_addr = 0;
+ uint64_t dex_descriptor_addr = 0;
};
bool ReadProcess(Process& process, std::vector<JITDebugInfo>* debug_info);
@@ -192,12 +188,14 @@ class JITDebugReader {
std::vector<JITDebugInfo>* debug_info);
bool IsDescriptorChanged(Process& process, Descriptor& old_descriptor);
bool InitializeProcess(Process& process);
- const DescriptorsLocation* GetDescriptorsLocation(const std::string& art_lib_path, bool is_64bit);
+ const DescriptorsLocation* GetDescriptorsLocation(const std::string& art_lib_path);
bool ReadRemoteMem(Process& process, uint64_t remote_addr, uint64_t size, void* data);
bool ReadDescriptors(Process& process, Descriptor* jit_descriptor, Descriptor* dex_descriptor);
- bool LoadDescriptor(bool is_64bit, const char* data, Descriptor* descriptor);
template <typename DescriptorT>
- bool LoadDescriptorImpl(const char* data, Descriptor* descriptor);
+ bool ReadDescriptorsImpl(Process& process, Descriptor* jit_descriptor,
+ Descriptor* dex_descriptor);
+ template <typename DescriptorT>
+ bool ParseDescriptor(const DescriptorT& raw_descriptor, Descriptor* descriptor);
bool ReadNewCodeEntries(Process& process, const Descriptor& descriptor,
uint64_t last_action_timestamp, uint32_t read_entry_limit,
@@ -226,7 +224,6 @@ class JITDebugReader {
// All monitored processes
std::unordered_map<pid_t, Process> processes_;
std::unordered_map<std::string, DescriptorsLocation> descriptors_location_cache_;
- std::vector<char> descriptors_buf_;
std::priority_queue<JITDebugInfo, std::vector<JITDebugInfo>, std::greater<JITDebugInfo>>
debug_info_q_;
diff --git a/simpleperf/OfflineUnwinder.cpp b/simpleperf/OfflineUnwinder.cpp
index f611ef4d..bbc488b4 100644
--- a/simpleperf/OfflineUnwinder.cpp
+++ b/simpleperf/OfflineUnwinder.cpp
@@ -66,6 +66,9 @@ CHECK_ERROR_CODE(ERROR_INVALID_ELF);
CHECK_ERROR_CODE(ERROR_THREAD_DOES_NOT_EXIST);
CHECK_ERROR_CODE(ERROR_THREAD_TIMEOUT);
CHECK_ERROR_CODE(ERROR_SYSTEM_CALL);
+CHECK_ERROR_CODE(ERROR_BAD_ARCH);
+CHECK_ERROR_CODE(ERROR_MAPS_PARSE);
+CHECK_ERROR_CODE(ERROR_INVALID_PARAMETER);
CHECK_ERROR_CODE(ERROR_MAX);
// Max frames seen so far is 463, in http://b/110923759.
diff --git a/simpleperf/OfflineUnwinder.h b/simpleperf/OfflineUnwinder.h
index b6445577..9eb9e8ff 100644
--- a/simpleperf/OfflineUnwinder.h
+++ b/simpleperf/OfflineUnwinder.h
@@ -39,7 +39,10 @@ enum UnwindStackErrorCode : uint8_t {
// not exist.
ERROR_THREAD_TIMEOUT, // Timeout trying to unwind a local thread.
ERROR_SYSTEM_CALL, // System call failed while unwinding.
- ERROR_MAX = ERROR_SYSTEM_CALL,
+ ERROR_BAD_ARCH, // Arch invalid (none, or mismatched).
+ ERROR_MAPS_PARSE, // Failed to parse maps data.
+ ERROR_INVALID_PARAMETER, // Invalid parameter passed to function.
+ ERROR_MAX = ERROR_INVALID_PARAMETER,
};
struct UnwindingResult {
diff --git a/simpleperf/cmd_monitor.cpp b/simpleperf/cmd_monitor.cpp
index ed9edfac..d81ccfea 100644
--- a/simpleperf/cmd_monitor.cpp
+++ b/simpleperf/cmd_monitor.cpp
@@ -266,21 +266,22 @@ bool MonitorCommand::PrepareMonitoring() {
// 5. Add read/signal/periodic Events.
IOEventLoop* loop = event_selection_set_.GetIOEventLoop();
auto exit_loop_callback = [loop]() { return loop->ExitLoop(); };
- if (!loop->AddSignalEvents({SIGCHLD, SIGINT, SIGTERM}, exit_loop_callback)) {
+ if (!loop->AddSignalEvents({SIGCHLD, SIGINT, SIGTERM}, exit_loop_callback, IOEventHighPriority)) {
return false;
}
// Only add an event for SIGHUP if we didn't inherit SIG_IGN (e.g. from
// nohup).
if (!SignalIsIgnored(SIGHUP)) {
- if (!loop->AddSignalEvent(SIGHUP, exit_loop_callback)) {
+ if (!loop->AddSignalEvent(SIGHUP, exit_loop_callback, IOEventHighPriority)) {
return false;
}
}
if (duration_in_sec_ != 0) {
- if (!loop->AddPeriodicEvent(SecondToTimeval(duration_in_sec_),
- [loop]() { return loop->ExitLoop(); })) {
+ if (!loop->AddPeriodicEvent(
+ SecondToTimeval(duration_in_sec_), [loop]() { return loop->ExitLoop(); },
+ IOEventHighPriority)) {
return false;
}
}
@@ -291,6 +292,10 @@ bool MonitorCommand::DoMonitoring() {
if (!event_selection_set_.GetIOEventLoop()->RunLoop()) {
return false;
}
+ if (!event_selection_set_.SyncKernelBuffer()) {
+ return false;
+ }
+ event_selection_set_.CloseEventFiles();
if (!event_selection_set_.FinishReadMmapEventData()) {
return false;
}
diff --git a/simpleperf/cmd_record.cpp b/simpleperf/cmd_record.cpp
index f206b808..4b79eedc 100644
--- a/simpleperf/cmd_record.cpp
+++ b/simpleperf/cmd_record.cpp
@@ -114,9 +114,7 @@ constexpr size_t DEFAULT_CALL_CHAIN_JOINER_CACHE_SIZE = 8 * 1024 * 1024;
static constexpr size_t kRecordBufferSize = 64 * 1024 * 1024;
static constexpr size_t kSystemWideRecordBufferSize = 256 * 1024 * 1024;
-// If the kernel needs to allocate continuous DMA memory for ETR (like when IOMMU for ETR isn't
-// available), requesting 4M ETR buffer may fail and cause warning. So use 1M buffer here.
-static constexpr size_t kDefaultAuxBufferSize = 1 * 1024 * 1024;
+static constexpr size_t kDefaultAuxBufferSize = 4 * 1024 * 1024;
// 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.
@@ -223,7 +221,7 @@ class RecordCommand : public Command {
"--aux-buffer-size <buffer_size> Set aux buffer size, only used in cs-etm event type.\n"
" Need to be power of 2 and page size aligned.\n"
" Used memory size is (buffer_size * (cpu_count + 1).\n"
-" Default is 1M.\n"
+" Default is 4M.\n"
"--no-inherit Don't record created child threads/processes.\n"
"--cpu-percent <percent> Set the max percent of cpu time used for recording.\n"
" percent is in range [1-100], default is 25.\n"
@@ -631,25 +629,26 @@ bool RecordCommand::PrepareRecording(Workload* workload) {
}
IOEventLoop* loop = event_selection_set_.GetIOEventLoop();
auto exit_loop_callback = [loop]() { return loop->ExitLoop(); };
- if (!loop->AddSignalEvents({SIGCHLD, SIGINT, SIGTERM}, exit_loop_callback)) {
+ if (!loop->AddSignalEvents({SIGCHLD, SIGINT, SIGTERM}, exit_loop_callback, IOEventHighPriority)) {
return false;
}
// Only add an event for SIGHUP if we didn't inherit SIG_IGN (e.g. from nohup).
if (!SignalIsIgnored(SIGHUP)) {
- if (!loop->AddSignalEvent(SIGHUP, exit_loop_callback)) {
+ if (!loop->AddSignalEvent(SIGHUP, exit_loop_callback, IOEventHighPriority)) {
return false;
}
}
if (stop_signal_fd_ != -1) {
- if (!loop->AddReadEvent(stop_signal_fd_, exit_loop_callback)) {
+ if (!loop->AddReadEvent(stop_signal_fd_, exit_loop_callback, IOEventHighPriority)) {
return false;
}
}
if (duration_in_sec_ != 0) {
- if (!loop->AddPeriodicEvent(SecondToTimeval(duration_in_sec_),
- [loop]() { return loop->ExitLoop(); })) {
+ if (!loop->AddPeriodicEvent(
+ SecondToTimeval(duration_in_sec_), [loop]() { return loop->ExitLoop(); },
+ IOEventHighPriority)) {
return false;
}
}
@@ -720,9 +719,10 @@ bool RecordCommand::DoRecording(Workload* workload) {
return false;
}
time_stat_.stop_recording_time = GetSystemClock();
- if (!event_selection_set_.FinishReadMmapEventData()) {
+ if (!event_selection_set_.SyncKernelBuffer()) {
return false;
}
+ event_selection_set_.CloseEventFiles();
time_stat_.finish_recording_time = GetSystemClock();
uint64_t recording_time = time_stat_.finish_recording_time - time_stat_.start_recording_time;
LOG(INFO) << "Recorded for " << recording_time / 1e9 << " seconds. Start post processing.";
@@ -756,26 +756,31 @@ static bool WriteRecordDataToOutFd(const std::string& in_filename,
}
bool RecordCommand::PostProcessRecording(const std::vector<std::string>& args) {
- // 1. Merge map records dumped while recording by map record thread.
+ // 1. Read records left in the buffer.
+ if (!event_selection_set_.FinishReadMmapEventData()) {
+ return false;
+ }
+
+ // 2. Merge map records dumped while recording by map record thread.
if (map_record_thread_) {
if (!map_record_thread_->Join() || !MergeMapRecords()) {
return false;
}
}
- // 2. Post unwind dwarf callchain.
+ // 3. Post unwind dwarf callchain.
if (unwind_dwarf_callchain_ && post_unwind_) {
if (!PostUnwindRecords()) {
return false;
}
}
- // 3. Optionally join Callchains.
+ // 4. Optionally join Callchains.
if (callchain_joiner_) {
JoinCallChains();
}
- // 4. Dump additional features, and close record file.
+ // 5. Dump additional features, and close record file.
if (!DumpAdditionalFeatures(args)) {
return false;
}
@@ -787,7 +792,7 @@ bool RecordCommand::PostProcessRecording(const std::vector<std::string>& args) {
}
time_stat_.post_process_time = GetSystemClock();
- // 4. Show brief record result.
+ // 6. Show brief record result.
auto record_stat = event_selection_set_.GetRecordStat();
if (event_selection_set_.HasAuxTrace()) {
LOG(INFO) << "Aux data traced: " << record_stat.aux_data_size;
@@ -1280,8 +1285,9 @@ bool RecordCommand::CreateAndInitRecordFile() {
return false;
}
// Use first perf_event_attr and first event id to dump mmap and comm records.
- EventAttrWithId dumping_attr_id = event_selection_set_.GetEventAttrWithId()[0];
- map_record_reader_.emplace(*dumping_attr_id.attr, dumping_attr_id.ids[0],
+ dumping_attr_id_ = event_selection_set_.GetEventAttrWithId()[0];
+ CHECK(!dumping_attr_id_.ids.empty());
+ map_record_reader_.emplace(*dumping_attr_id_.attr, dumping_attr_id_.ids[0],
event_selection_set_.RecordNotExecutableMaps());
map_record_reader_->SetCallback([this](Record* r) { return ProcessRecord(r); });
@@ -1505,14 +1511,13 @@ bool RecordCommand::SaveRecordWithoutUnwinding(Record* record) {
bool RecordCommand::ProcessJITDebugInfo(const std::vector<JITDebugInfo>& debug_info,
bool sync_kernel_records) {
- EventAttrWithId attr_id = event_selection_set_.GetEventAttrWithId()[0];
for (auto& info : debug_info) {
if (info.type == JITDebugInfo::JIT_DEBUG_JIT_CODE) {
uint64_t timestamp =
jit_debug_reader_->SyncWithRecords() ? info.timestamp : last_record_timestamp_;
- Mmap2Record record(*attr_id.attr, false, info.pid, info.pid, info.jit_code_addr,
+ Mmap2Record record(*dumping_attr_id_.attr, false, info.pid, info.pid, info.jit_code_addr,
info.jit_code_len, info.file_offset, map_flags::PROT_JIT_SYMFILE_MAP,
- info.file_path, attr_id.ids[0], timestamp);
+ info.file_path, dumping_attr_id_.ids[0], timestamp);
if (!ProcessRecord(&record)) {
return false;
}
@@ -1521,8 +1526,9 @@ bool RecordCommand::ProcessJITDebugInfo(const std::vector<JITDebugInfo>& debug_i
ThreadMmap& map = *info.extracted_dex_file_map;
uint64_t timestamp =
jit_debug_reader_->SyncWithRecords() ? info.timestamp : last_record_timestamp_;
- Mmap2Record record(*attr_id.attr, false, info.pid, info.pid, map.start_addr, map.len,
- map.pgoff, map.prot, map.name, attr_id.ids[0], timestamp);
+ Mmap2Record record(*dumping_attr_id_.attr, false, info.pid, info.pid, map.start_addr,
+ map.len, map.pgoff, map.prot, map.name, dumping_attr_id_.ids[0],
+ timestamp);
if (!ProcessRecord(&record)) {
return false;
}
diff --git a/simpleperf/cmd_record_test.cpp b/simpleperf/cmd_record_test.cpp
index ef2c1b68..01861c60 100644
--- a/simpleperf/cmd_record_test.cpp
+++ b/simpleperf/cmd_record_test.cpp
@@ -565,10 +565,12 @@ TEST(record_cmd, exit_with_parent_option) {
TEST(record_cmd, use_cmd_exit_code_option) {
TemporaryFile tmpfile;
int exit_code;
- RecordCmd()->Run({"--use-cmd-exit-code", "-o", tmpfile.path, "ls", "."}, &exit_code);
- ASSERT_EQ(exit_code, 0);
- RecordCmd()->Run({"--use-cmd-exit-code", "-o", tmpfile.path, "ls", "/not_exist_path"},
+ RecordCmd()->Run({"-e", GetDefaultEvent(), "--use-cmd-exit-code", "-o", tmpfile.path, "ls", "."},
&exit_code);
+ ASSERT_EQ(exit_code, 0);
+ RecordCmd()->Run(
+ {"-e", GetDefaultEvent(), "--use-cmd-exit-code", "-o", tmpfile.path, "ls", "/not_exist_path"},
+ &exit_code);
ASSERT_NE(exit_code, 0);
}
diff --git a/simpleperf/doc/collect_etm_data_for_autofdo.md b/simpleperf/doc/collect_etm_data_for_autofdo.md
index f9f5a158..145c0adf 100644
--- a/simpleperf/doc/collect_etm_data_for_autofdo.md
+++ b/simpleperf/doc/collect_etm_data_for_autofdo.md
@@ -94,7 +94,7 @@ Then we can use a.prof for PGO during compilation, via `-fprofile-sample-use=a.p
## Collect ETM data with a daemon
Android also has a daemon collecting ETM data periodically. It only runs on userdebug and eng
-devices. The source code is in `<aosp-top>/system/extras/profcollectd`.
+devices. The source code is in https://android.googlesource.com/platform/system/extras/+/master/profcollectd/.
## Support ETM in the kernel
@@ -107,21 +107,37 @@ The Coresight driver can be enabled by below kernel configs:
CONFIG_CORESIGHT=y
CONFIG_CORESIGHT_LINK_AND_SINK_TMC=y
CONFIG_CORESIGHT_SOURCE_ETM4X=y
- CONFIG_CORESIGHT_DYNAMIC_REPLICATOR=y
```
-On Kernel 5.10+, we can build Coresight driver as kernel modules instead.
+On Kernel 5.10+, we recommend building Coresight driver as kernel modules. Because it works with
+GKI kernel.
-Android common kernel 5.10+ should have all the Coresight patches needed. And we have backported
-necessary Coresight patches to Android common kernel 4.14 and 4.19. Android common kernel 5.4
-misses a few patches. Please create an [ndk issue](https://github.com/android/ndk/issues) if you
-need ETM function on 5.4 kernel.
+```config
+ CONFIG_CORESIGHT=m
+ CONFIG_CORESIGHT_LINK_AND_SINK_TMC=m
+ CONFIG_CORESIGHT_SOURCE_ETM4X=m
+```
+
+Android common kernel 5.10+ should have all the Coresight patches needed to collect ETM data.
+Android common kernel 5.4 misses two patches. But by adding patches in
+https://android-review.googlesource.com/q/topic:test_etm_on_hikey960_5.4, we can collect ETM data
+on hikey960 with 5.4 kernel.
+For Android common kernel 4.14 and 4.19, we have backported all necessary Coresight patches.
Besides Coresight driver, we also need to add Coresight devices in device tree. An example is in
https://github.com/torvalds/linux/blob/master/arch/arm64/boot/dts/arm/juno-base.dtsi. There should
be a path flowing ETM data from ETM device through funnels, ETF and replicators, all the way to
ETR, which writes ETM data to system memory.
+One optional flag in ETM device tree is "arm,coresight-loses-context-with-cpu". It saves ETM
+registers when a CPU enters low power state. It may be needed to avoid
+"coresight_disclaim_device_unlocked" warning when doing system wide collection.
+
+One optional flag in ETR device tree is "arm,scatter-gather". Simpleperf requests 4M system memory
+for ETR to store ETM data. Without IOMMU, the memory needs to be contiguous. If the kernel can't
+fulfill the request, simpleperf will report out of memory error. Fortunately, we can use
+"arm,scatter-gather" flag to let ETR run in scatter gather mode, which uses non-contiguous memory.
+
## Enable ETM in the bootloader
Unless ARMv8.4 Self-hosted Trace extension is implemented, ETM is considered as an external debug
diff --git a/simpleperf/dso.cpp b/simpleperf/dso.cpp
index bd565b1c..6d559770 100644
--- a/simpleperf/dso.cpp
+++ b/simpleperf/dso.cpp
@@ -326,14 +326,13 @@ BuildId Dso::FindExpectedBuildIdForPath(const std::string& path) {
return BuildId();
}
-BuildId Dso::GetExpectedBuildId() {
+BuildId Dso::GetExpectedBuildId() const {
return FindExpectedBuildIdForPath(path_);
}
-Dso::Dso(DsoType type, const std::string& path, const std::string& debug_file_path)
+Dso::Dso(DsoType type, const std::string& path)
: type_(type),
path_(path),
- debug_file_path_(debug_file_path),
is_loaded_(false),
dump_id_(UINT_MAX),
symbol_dump_id_(0),
@@ -465,8 +464,7 @@ static void SortAndFixSymbols(std::vector<Symbol>& symbols) {
class DexFileDso : public Dso {
public:
- DexFileDso(const std::string& path, const std::string& debug_file_path)
- : Dso(DSO_DEX_FILE, path, debug_file_path) {}
+ DexFileDso(const std::string& path) : Dso(DSO_DEX_FILE, path) {}
void AddDexFileOffset(uint64_t dex_file_offset) override {
auto it = std::lower_bound(dex_file_offsets_.begin(), dex_file_offsets_.end(), dex_file_offset);
@@ -484,11 +482,12 @@ class DexFileDso : public Dso {
std::vector<Symbol> LoadSymbolsImpl() override {
std::vector<Symbol> symbols;
- auto tuple = SplitUrlInApk(debug_file_path_);
+ const std::string& debug_file_path = GetDebugFilePath();
+ auto tuple = SplitUrlInApk(debug_file_path);
// Symbols of dex files are collected on device. If the dex file doesn't exist, probably
// we are reporting on host, and there is no need to report warning of missing dex files.
- if (!IsRegularFile(std::get<0>(tuple) ? std::get<1>(tuple) : debug_file_path_)) {
- LOG(DEBUG) << "skip reading symbols from non-exist dex_file " << debug_file_path_;
+ if (!IsRegularFile(std::get<0>(tuple) ? std::get<1>(tuple) : debug_file_path)) {
+ LOG(DEBUG) << "skip reading symbols from non-exist dex_file " << debug_file_path;
return symbols;
}
bool status = false;
@@ -501,19 +500,19 @@ class DexFileDso : public Dso {
std::vector<uint8_t> data;
if (ahelper && ahelper->FindEntry(std::get<2>(tuple), &entry) &&
ahelper->GetEntryData(entry, &data)) {
- status = ReadSymbolsFromDexFileInMemory(data.data(), data.size(), debug_file_path_,
+ status = ReadSymbolsFromDexFileInMemory(data.data(), data.size(), debug_file_path,
dex_file_offsets_, symbol_callback);
}
} else {
- status = ReadSymbolsFromDexFile(debug_file_path_, dex_file_offsets_, symbol_callback);
+ status = ReadSymbolsFromDexFile(debug_file_path, dex_file_offsets_, symbol_callback);
}
if (!status) {
android::base::LogSeverity level =
symbols_.empty() ? android::base::WARNING : android::base::DEBUG;
- LOG(level) << "Failed to read symbols from dex_file " << debug_file_path_;
+ LOG(level) << "Failed to read symbols from dex_file " << debug_file_path;
return symbols;
}
- LOG(VERBOSE) << "Read symbols from dex_file " << debug_file_path_ << " successfully";
+ LOG(VERBOSE) << "Read symbols from dex_file " << debug_file_path << " successfully";
SortAndFixSymbols(symbols);
return symbols;
}
@@ -524,8 +523,8 @@ class DexFileDso : public Dso {
class ElfDso : public Dso {
public:
- ElfDso(const std::string& path, const std::string& debug_file_path)
- : Dso(DSO_ELF_FILE, path, debug_file_path) {}
+ ElfDso(const std::string& path, bool force_64bit)
+ : Dso(DSO_ELF_FILE, path), force_64bit_(force_64bit) {}
std::string_view GetReportPath() const override {
if (JITDebugReader::IsPathInJITSymFile(path_)) {
@@ -551,11 +550,11 @@ class ElfDso : public Dso {
BuildId build_id = GetExpectedBuildId();
ElfStatus status;
- auto elf = ElfFile::Open(debug_file_path_, &build_id, &status);
+ auto elf = ElfFile::Open(GetDebugFilePath(), &build_id, &status);
if (elf) {
min_vaddr_ = elf->ReadMinExecutableVaddr(&file_offset_of_min_vaddr_);
} else {
- LOG(WARNING) << "failed to read min virtual address of " << debug_file_path_ << ": "
+ LOG(WARNING) << "failed to read min virtual address of " << GetDebugFilePath() << ": "
<< status;
}
}
@@ -587,7 +586,7 @@ class ElfDso : public Dso {
// ELF_FILE Dso objects should actually be DEX_FILE, because they have dex file offsets.
// So here converts ELF_FILE Dso into DEX_FILE Dso.
type_ = DSO_DEX_FILE;
- dex_file_dso_.reset(new DexFileDso(path_, path_));
+ dex_file_dso_.reset(new DexFileDso(path_));
}
dex_file_dso_->AddDexFileOffset(dex_file_offset);
}
@@ -597,6 +596,11 @@ class ElfDso : public Dso {
}
protected:
+ std::string FindDebugFilePath() const override {
+ BuildId build_id = GetExpectedBuildId();
+ return debug_elf_file_finder_.FindDebugFile(path_, force_64bit_, build_id);
+ }
+
std::vector<Symbol> LoadSymbolsImpl() override {
if (dex_file_dso_) {
return dex_file_dso_->LoadSymbolsImpl();
@@ -609,11 +613,11 @@ class ElfDso : public Dso {
}
};
ElfStatus status;
- auto elf = ElfFile::Open(debug_file_path_, &build_id, &status);
+ auto elf = ElfFile::Open(GetDebugFilePath(), &build_id, &status);
if (elf) {
status = elf->ParseSymbols(symbol_callback);
}
- ReportReadElfSymbolResult(status, path_, debug_file_path_,
+ ReportReadElfSymbolResult(status, path_, GetDebugFilePath(),
symbols_.empty() ? android::base::WARNING : android::base::DEBUG);
SortAndFixSymbols(symbols);
return symbols;
@@ -622,6 +626,7 @@ class ElfDso : public Dso {
private:
static constexpr uint64_t uninitialized_value = std::numeric_limits<uint64_t>::max();
+ bool force_64bit_;
uint64_t min_vaddr_ = uninitialized_value;
uint64_t file_offset_of_min_vaddr_ = uninitialized_value;
std::unique_ptr<DexFileDso> dex_file_dso_;
@@ -629,8 +634,8 @@ class ElfDso : public Dso {
class KernelDso : public Dso {
public:
- KernelDso(const std::string& path, const std::string& debug_file_path)
- : Dso(DSO_KERNEL, path, debug_file_path) {
+ KernelDso(const std::string& path) : Dso(DSO_KERNEL, path) {
+ debug_file_path_ = FindDebugFilePath();
if (!vmlinux_.empty()) {
// Use vmlinux as the kernel debug file.
BuildId build_id = GetExpectedBuildId();
@@ -639,7 +644,7 @@ class KernelDso : public Dso {
debug_file_path_ = vmlinux_;
has_debug_file_ = true;
}
- } else if (IsRegularFile(debug_file_path_)) {
+ } else if (IsRegularFile(GetDebugFilePath())) {
has_debug_file_ = true;
}
}
@@ -663,6 +668,11 @@ class KernelDso : public Dso {
}
protected:
+ std::string FindDebugFilePath() const override {
+ BuildId build_id = GetExpectedBuildId();
+ return debug_elf_file_finder_.FindDebugFile(path_, false, build_id);
+ }
+
std::vector<Symbol> LoadSymbolsImpl() override {
std::vector<Symbol> symbols;
if (has_debug_file_) {
@@ -689,7 +699,7 @@ class KernelDso : public Dso {
if (!fix_kernel_address_randomization_) {
LOG(WARNING) << "Don't know how to fix addresses changed by kernel address randomization. So "
"symbols in "
- << debug_file_path_ << " are not used";
+ << GetDebugFilePath() << " are not used";
return;
}
// symbols_ are kernel symbols got from /proc/kallsyms while recording. Those symbols are
@@ -703,10 +713,10 @@ class KernelDso : public Dso {
}
};
ElfStatus status;
- if (auto elf = ElfFile::Open(debug_file_path_, &status); elf) {
+ if (auto elf = ElfFile::Open(GetDebugFilePath(), &status); elf) {
status = elf->ParseSymbols(symbol_callback);
}
- ReportReadElfSymbolResult(status, path_, debug_file_path_);
+ ReportReadElfSymbolResult(status, path_, GetDebugFilePath());
}
void ReadSymbolsFromKallsyms(std::string& kallsyms, std::vector<Symbol>* symbols) {
@@ -771,7 +781,7 @@ class KernelDso : public Dso {
kernel_start_file_offset_ = 0;
if (has_debug_file_) {
ElfStatus status;
- if (auto elf = ElfFile::Open(debug_file_path_, &status); elf) {
+ if (auto elf = ElfFile::Open(GetDebugFilePath(), &status); elf) {
for (const auto& section : elf->GetSectionHeader()) {
if (section.name == ".text") {
kernel_start_addr_ = section.vaddr;
@@ -791,9 +801,9 @@ class KernelDso : public Dso {
class KernelModuleDso : public Dso {
public:
- KernelModuleDso(const std::string& path, const std::string& debug_file_path,
- uint64_t memory_start, uint64_t memory_end, Dso* kernel_dso)
- : Dso(DSO_KERNEL_MODULE, path, debug_file_path),
+ KernelModuleDso(const std::string& path, uint64_t memory_start, uint64_t memory_end,
+ Dso* kernel_dso)
+ : Dso(DSO_KERNEL_MODULE, path),
memory_start_(memory_start),
memory_end_(memory_end),
kernel_dso_(kernel_dso) {}
@@ -819,6 +829,11 @@ class KernelModuleDso : public Dso {
}
protected:
+ std::string FindDebugFilePath() const override {
+ BuildId build_id = GetExpectedBuildId();
+ return debug_elf_file_finder_.FindDebugFile(path_, false, build_id);
+ }
+
std::vector<Symbol> LoadSymbolsImpl() override {
std::vector<Symbol> symbols;
BuildId build_id = GetExpectedBuildId();
@@ -829,11 +844,11 @@ class KernelModuleDso : public Dso {
}
};
ElfStatus status;
- auto elf = ElfFile::Open(debug_file_path_, &build_id, &status);
+ auto elf = ElfFile::Open(GetDebugFilePath(), &build_id, &status);
if (elf) {
status = elf->ParseSymbols(symbol_callback);
}
- ReportReadElfSymbolResult(status, path_, debug_file_path_,
+ ReportReadElfSymbolResult(status, path_, GetDebugFilePath(),
symbols_.empty() ? android::base::WARNING : android::base::DEBUG);
SortAndFixSymbols(symbols);
return symbols;
@@ -897,7 +912,7 @@ class KernelModuleDso : public Dso {
class SymbolMapFileDso : public Dso {
public:
- SymbolMapFileDso(const std::string& path) : Dso(DSO_SYMBOL_MAP_FILE, path, path) {}
+ SymbolMapFileDso(const std::string& path) : Dso(DSO_SYMBOL_MAP_FILE, path) {}
uint64_t IpToVaddrInFile(uint64_t ip, uint64_t, uint64_t) override { return ip; }
@@ -907,7 +922,7 @@ class SymbolMapFileDso : public Dso {
class UnknownDso : public Dso {
public:
- UnknownDso(const std::string& path) : Dso(DSO_UNKNOWN_FILE, path, path) {}
+ UnknownDso(const std::string& path) : Dso(DSO_UNKNOWN_FILE, path) {}
uint64_t IpToVaddrInFile(uint64_t ip, uint64_t, uint64_t) override { return ip; }
@@ -917,15 +932,13 @@ class UnknownDso : public Dso {
std::unique_ptr<Dso> Dso::CreateDso(DsoType dso_type, const std::string& dso_path,
bool force_64bit) {
- BuildId build_id = FindExpectedBuildIdForPath(dso_path);
- std::string debug_path = debug_elf_file_finder_.FindDebugFile(dso_path, force_64bit, build_id);
switch (dso_type) {
case DSO_ELF_FILE:
- return std::unique_ptr<Dso>(new ElfDso(dso_path, debug_path));
+ return std::unique_ptr<Dso>(new ElfDso(dso_path, force_64bit));
case DSO_KERNEL:
- return std::unique_ptr<Dso>(new KernelDso(dso_path, debug_path));
+ return std::unique_ptr<Dso>(new KernelDso(dso_path));
case DSO_DEX_FILE:
- return std::unique_ptr<Dso>(new DexFileDso(dso_path, dso_path));
+ return std::unique_ptr<Dso>(new DexFileDso(dso_path));
case DSO_SYMBOL_MAP_FILE:
return std::unique_ptr<Dso>(new SymbolMapFileDso(dso_path));
case DSO_UNKNOWN_FILE:
@@ -938,26 +951,28 @@ std::unique_ptr<Dso> Dso::CreateDso(DsoType dso_type, const std::string& dso_pat
std::unique_ptr<Dso> Dso::CreateDsoWithBuildId(DsoType dso_type, const std::string& dso_path,
BuildId& build_id) {
- std::string debug_path = debug_elf_file_finder_.FindDebugFile(dso_path, false, build_id);
+ std::unique_ptr<Dso> dso;
switch (dso_type) {
case DSO_ELF_FILE:
- return std::unique_ptr<Dso>(new ElfDso(dso_path, debug_path));
+ dso.reset(new ElfDso(dso_path, false));
+ break;
case DSO_KERNEL:
- return std::unique_ptr<Dso>(new KernelDso(dso_path, debug_path));
+ dso.reset(new KernelDso(dso_path));
+ break;
case DSO_KERNEL_MODULE:
- return std::unique_ptr<Dso>(new KernelModuleDso(dso_path, debug_path, 0, 0, nullptr));
+ dso.reset(new KernelModuleDso(dso_path, 0, 0, nullptr));
+ break;
default:
LOG(FATAL) << "Unexpected dso_type " << static_cast<int>(dso_type);
+ return nullptr;
}
- return nullptr;
+ dso->debug_file_path_ = debug_elf_file_finder_.FindDebugFile(dso_path, false, build_id);
+ return dso;
}
std::unique_ptr<Dso> Dso::CreateKernelModuleDso(const std::string& dso_path, uint64_t memory_start,
uint64_t memory_end, Dso* kernel_dso) {
- BuildId build_id = FindExpectedBuildIdForPath(dso_path);
- std::string debug_path = debug_elf_file_finder_.FindDebugFile(dso_path, false, build_id);
- return std::unique_ptr<Dso>(
- new KernelModuleDso(dso_path, debug_path, memory_start, memory_end, kernel_dso));
+ return std::unique_ptr<Dso>(new KernelModuleDso(dso_path, memory_start, memory_end, kernel_dso));
}
const char* DsoTypeToString(DsoType dso_type) {
diff --git a/simpleperf/dso.h b/simpleperf/dso.h
index 866aadfd..30427766 100644
--- a/simpleperf/dso.h
+++ b/simpleperf/dso.h
@@ -145,7 +145,13 @@ class Dso {
// Return the path recorded in perf.data.
const std::string& Path() const { return path_; }
// Return the path containing symbol table and debug information.
- const std::string& GetDebugFilePath() const { return debug_file_path_; }
+ const std::string& GetDebugFilePath() const {
+ if (!debug_file_path_.has_value()) {
+ debug_file_path_ = FindDebugFilePath();
+ }
+ return debug_file_path_.value();
+ }
+
// Return the path beautified for reporting.
virtual std::string_view GetReportPath() const { return Path(); }
// Return the file name without directory info.
@@ -195,9 +201,10 @@ class Dso {
static uint32_t g_dump_id_;
static simpleperf_dso_impl::DebugElfFileFinder debug_elf_file_finder_;
- Dso(DsoType type, const std::string& path, const std::string& debug_file_path);
- BuildId GetExpectedBuildId();
+ Dso(DsoType type, const std::string& path);
+ BuildId GetExpectedBuildId() const;
+ virtual std::string FindDebugFilePath() const { return path_; }
virtual std::vector<Symbol> LoadSymbolsImpl() = 0;
DsoType type_;
@@ -205,7 +212,7 @@ class Dso {
const std::string path_;
// path of the shared library having symbol table and debug information
// It is the same as path_, or has the same build id as path_.
- std::string debug_file_path_;
+ mutable std::optional<std::string> debug_file_path_;
// File name of the shared library, got by removing directories in path_.
std::string file_name_;
std::vector<Symbol> symbols_;
diff --git a/simpleperf/dso_test.cpp b/simpleperf/dso_test.cpp
index 1338c1c0..53f62a57 100644
--- a/simpleperf/dso_test.cpp
+++ b/simpleperf/dso_test.cpp
@@ -310,3 +310,15 @@ TEST(dso, FunctionName) {
symbol = Symbol("ctep.v", 0x0, 0x1);
ASSERT_EQ(symbol.FunctionName(), "ctep.v");
}
+
+TEST(dso, search_debug_file_only_when_needed) {
+ Dso::SetBuildIds({std::make_pair("/elf", BuildId("1b12a384a9f4a3f3659b7171ca615dbec3a81f71"))});
+ Dso::SetSymFsDir(GetTestDataDir());
+ CapturedStderr capture;
+ capture.Start();
+ auto dso = Dso::CreateDso(DSO_ELF_FILE, "/elf");
+ ASSERT_EQ(capture.str().find("build id mismatch"), std::string::npos);
+ ASSERT_EQ(dso->GetDebugFilePath(), "/elf");
+ ASSERT_NE(capture.str().find("build id mismatch"), std::string::npos);
+ capture.Stop();
+}
diff --git a/simpleperf/environment.h b/simpleperf/environment.h
index 1dd6952e..34ce7fa5 100644
--- a/simpleperf/environment.h
+++ b/simpleperf/environment.h
@@ -144,7 +144,8 @@ static inline uint64_t GetSystemClock() {
#if defined(__ANDROID__)
bool IsInAppUid();
-#else
+#endif
+#if !defined(__ANDROID__) && !defined(ANDROID_HOST_MUSL)
static inline int gettid() {
return syscall(__NR_gettid);
}
diff --git a/simpleperf/event_selection_set.cpp b/simpleperf/event_selection_set.cpp
index d5bca318..58b4b956 100644
--- a/simpleperf/event_selection_set.cpp
+++ b/simpleperf/event_selection_set.cpp
@@ -840,14 +840,7 @@ bool EventSelectionSet::ReadMmapEventData(bool with_time_limit) {
}
bool EventSelectionSet::FinishReadMmapEventData() {
- // Stop the read thread, so we don't get more records beyond current time.
- if (!SyncKernelBuffer() || !record_read_thread_->StopReadThread()) {
- return false;
- }
- if (!ReadMmapEventData(false)) {
- return false;
- }
- return true;
+ return ReadMmapEventData(false);
}
void EventSelectionSet::CloseEventFiles() {
diff --git a/simpleperf/report_lib_interface.cpp b/simpleperf/report_lib_interface.cpp
index c55209ab..1025c325 100644
--- a/simpleperf/report_lib_interface.cpp
+++ b/simpleperf/report_lib_interface.cpp
@@ -199,7 +199,7 @@ class ReportLib {
}
const char* GetSupportedTraceOffCpuModes();
bool SetTraceOffCpuMode(const char* mode);
- bool SetSampleFilter(const char* filter);
+ bool SetSampleFilter(const char** filters, int filters_len);
Sample* GetNextSample();
Event* GetEventOfCurrentSample() { return &current_event_; }
@@ -296,8 +296,11 @@ bool ReportLib::SetTraceOffCpuMode(const char* mode) {
return true;
}
-bool ReportLib::SetSampleFilter(const char* filter) {
- std::vector<std::string> args = android::base::Split(filter, " ");
+bool ReportLib::SetSampleFilter(const char** filters, int filters_len) {
+ std::vector<std::string> args;
+ for (int i = 0; i < filters_len; i++) {
+ args.emplace_back(filters[i]);
+ }
OptionFormatMap option_formats = GetRecordFilterOptionFormats(false);
OptionValueMap options;
std::vector<std::pair<OptionName, OptionValue>> ordered_options;
@@ -595,7 +598,7 @@ void MergeJavaMethods(ReportLib* report_lib, bool merge) EXPORT;
bool AddProguardMappingFile(ReportLib* report_lib, const char* mapping_file) EXPORT;
const char* GetSupportedTraceOffCpuModes(ReportLib* report_lib) EXPORT;
bool SetTraceOffCpuMode(ReportLib* report_lib, const char* mode) EXPORT;
-bool SetSampleFilter(ReportLib* report_lib, const char* filter) EXPORT;
+bool SetSampleFilter(ReportLib* report_lib, const char** filters, int filters_len) EXPORT;
Sample* GetNextSample(ReportLib* report_lib) EXPORT;
Event* GetEventOfCurrentSample(ReportLib* report_lib) EXPORT;
@@ -656,8 +659,8 @@ bool SetTraceOffCpuMode(ReportLib* report_lib, const char* mode) {
return report_lib->SetTraceOffCpuMode(mode);
}
-bool SetSampleFilter(ReportLib* report_lib, const char* filter) {
- return report_lib->SetSampleFilter(filter);
+bool SetSampleFilter(ReportLib* report_lib, const char** filters, int filters_len) {
+ return report_lib->SetSampleFilter(filters, filters_len);
}
Sample* GetNextSample(ReportLib* report_lib) {
diff --git a/simpleperf/scripts/annotate.py b/simpleperf/scripts/annotate.py
index ecfb2fdb..7bf8fe92 100755
--- a/simpleperf/scripts/annotate.py
+++ b/simpleperf/scripts/annotate.py
@@ -146,7 +146,7 @@ class SourceFileAnnotator(object):
def __init__(self, config):
# check config variables
- config_names = ['perf_data_list', 'source_dirs', 'comm_filters', 'dso_filters', 'ndk_path']
+ config_names = ['perf_data_list', 'source_dirs', 'dso_filters', 'ndk_path']
for name in config_names:
if name not in config:
log_exit('config [%s] is missing' % name)
@@ -161,7 +161,6 @@ class SourceFileAnnotator(object):
self.config = config
self.symfs_dir = symfs_dir
self.kallsyms = kallsyms
- self.comm_filter = set(config['comm_filters']) if config.get('comm_filters') else None
self.dso_filter = set(config['dso_filters']) if config.get('dso_filters') else None
config['annotate_dest_dir'] = 'annotated_files'
@@ -193,16 +192,12 @@ class SourceFileAnnotator(object):
lib.SetSymfs(self.symfs_dir)
if self.kallsyms:
lib.SetKallsymsFile(self.kallsyms)
- if self.config.get('sample_filter'):
- lib.SetSampleFilter(self.config.get('sample_filter'))
lib.SetReportOptions(self.config['report_lib_options'])
while True:
sample = lib.GetNextSample()
if sample is None:
lib.Close()
break
- if not self._filter_sample(sample):
- continue
symbols = []
symbols.append(lib.GetSymbolOfCurrentSample())
callchain = lib.GetCallChainOfCurrentSample()
@@ -216,13 +211,6 @@ class SourceFileAnnotator(object):
self.addr2line.add_addr(symbol.dso_name, build_id, symbol.symbol_addr,
symbol.symbol_addr)
- def _filter_sample(self, sample):
- """Return true if the sample can be used."""
- if self.comm_filter:
- if sample.thread_comm not in self.comm_filter:
- return False
- return True
-
def _filter_symbol(self, symbol):
if not self.dso_filter or symbol.dso_name in self.dso_filter:
return True
@@ -242,16 +230,12 @@ class SourceFileAnnotator(object):
lib.SetSymfs(self.symfs_dir)
if self.kallsyms:
lib.SetKallsymsFile(self.kallsyms)
- if self.config.get('sample_filter'):
- lib.SetSampleFilter(self.config.get('sample_filter'))
lib.SetReportOptions(self.config['report_lib_options'])
while True:
sample = lib.GetNextSample()
if sample is None:
lib.Close()
break
- if not self._filter_sample(sample):
- continue
self._generate_periods_for_sample(lib, sample)
def _generate_periods_for_sample(self, lib, sample):
@@ -479,12 +463,9 @@ def main():
help='show raw period instead of percentage')
parser.add_argument('--summary-width', type=int, default=80, help='max width of summary file')
sample_filter_group = parser.add_argument_group('Sample filter options')
- parser.add_sample_filter_options(sample_filter_group)
- sample_filter_group.add_argument('--comm', nargs='+', action='append', help="""
- Use samples only in threads with selected names.""")
sample_filter_group.add_argument('--dso', nargs='+', action='append', help="""
Use samples only in selected binaries.""")
- parser.add_report_lib_options()
+ parser.add_report_lib_options(sample_filter_group=sample_filter_group)
args = parser.parse_args()
config = {}
@@ -492,12 +473,10 @@ def main():
if not config['perf_data_list']:
config['perf_data_list'].append('perf.data')
config['source_dirs'] = flatten_arg_list(args.source_dirs)
- config['comm_filters'] = flatten_arg_list(args.comm)
config['dso_filters'] = flatten_arg_list(args.dso)
config['ndk_path'] = args.ndk_path
config['raw_period'] = args.raw_period
config['summary_width'] = args.summary_width
- config['sample_filter'] = args.sample_filter
config['report_lib_options'] = args.report_lib_options
annotator = SourceFileAnnotator(config)
diff --git a/simpleperf/scripts/bin/android/arm/simpleperf b/simpleperf/scripts/bin/android/arm/simpleperf
index 5ccbd4f1..db2fde7f 100755
--- a/simpleperf/scripts/bin/android/arm/simpleperf
+++ b/simpleperf/scripts/bin/android/arm/simpleperf
Binary files differ
diff --git a/simpleperf/scripts/bin/android/arm64/simpleperf b/simpleperf/scripts/bin/android/arm64/simpleperf
index 862e4d93..785b6e33 100755
--- a/simpleperf/scripts/bin/android/arm64/simpleperf
+++ b/simpleperf/scripts/bin/android/arm64/simpleperf
Binary files differ
diff --git a/simpleperf/scripts/bin/android/x86/simpleperf b/simpleperf/scripts/bin/android/x86/simpleperf
index a78e0514..97a168d5 100755
--- a/simpleperf/scripts/bin/android/x86/simpleperf
+++ b/simpleperf/scripts/bin/android/x86/simpleperf
Binary files differ
diff --git a/simpleperf/scripts/bin/android/x86_64/simpleperf b/simpleperf/scripts/bin/android/x86_64/simpleperf
index 8872cf4a..c250c450 100755
--- a/simpleperf/scripts/bin/android/x86_64/simpleperf
+++ b/simpleperf/scripts/bin/android/x86_64/simpleperf
Binary files differ
diff --git a/simpleperf/scripts/bin/darwin/x86_64/libsimpleperf_report.dylib b/simpleperf/scripts/bin/darwin/x86_64/libsimpleperf_report.dylib
index 32631f67..191e51f8 100755
--- a/simpleperf/scripts/bin/darwin/x86_64/libsimpleperf_report.dylib
+++ b/simpleperf/scripts/bin/darwin/x86_64/libsimpleperf_report.dylib
Binary files differ
diff --git a/simpleperf/scripts/bin/darwin/x86_64/simpleperf b/simpleperf/scripts/bin/darwin/x86_64/simpleperf
index c4daaf2b..1a6a8875 100755
--- a/simpleperf/scripts/bin/darwin/x86_64/simpleperf
+++ b/simpleperf/scripts/bin/darwin/x86_64/simpleperf
Binary files differ
diff --git a/simpleperf/scripts/bin/linux/x86_64/libsimpleperf_report.so b/simpleperf/scripts/bin/linux/x86_64/libsimpleperf_report.so
index 6f148a8e..ca444ec9 100755
--- a/simpleperf/scripts/bin/linux/x86_64/libsimpleperf_report.so
+++ b/simpleperf/scripts/bin/linux/x86_64/libsimpleperf_report.so
Binary files differ
diff --git a/simpleperf/scripts/bin/linux/x86_64/simpleperf b/simpleperf/scripts/bin/linux/x86_64/simpleperf
index f2968884..dca7b30d 100755
--- a/simpleperf/scripts/bin/linux/x86_64/simpleperf
+++ b/simpleperf/scripts/bin/linux/x86_64/simpleperf
Binary files differ
diff --git a/simpleperf/scripts/bin/windows/x86_64/libsimpleperf_report.dll b/simpleperf/scripts/bin/windows/x86_64/libsimpleperf_report.dll
index 074bff34..b49e8f8f 100755
--- a/simpleperf/scripts/bin/windows/x86_64/libsimpleperf_report.dll
+++ b/simpleperf/scripts/bin/windows/x86_64/libsimpleperf_report.dll
Binary files differ
diff --git a/simpleperf/scripts/bin/windows/x86_64/simpleperf.exe b/simpleperf/scripts/bin/windows/x86_64/simpleperf.exe
index fc98293a..9838e771 100755
--- a/simpleperf/scripts/bin/windows/x86_64/simpleperf.exe
+++ b/simpleperf/scripts/bin/windows/x86_64/simpleperf.exe
Binary files differ
diff --git a/simpleperf/scripts/gecko_profile_generator.py b/simpleperf/scripts/gecko_profile_generator.py
index f6d6854a..396c9a58 100755
--- a/simpleperf/scripts/gecko_profile_generator.py
+++ b/simpleperf/scripts/gecko_profile_generator.py
@@ -295,23 +295,16 @@ def _gecko_profile(
record_file: str,
symfs_dir: Optional[str],
kallsyms_file: Optional[str],
- proguard_mapping_file: List[str],
- comm_filter: Set[str],
- sample_filter: Optional[str],
report_lib_options: ReportLibOptions) -> GeckoProfile:
"""convert a simpleperf profile to gecko format"""
lib = ReportLib()
lib.ShowIpForUnknownSymbol()
- for file_path in proguard_mapping_file:
- lib.AddProguardMappingFile(file_path)
if symfs_dir is not None:
lib.SetSymfs(symfs_dir)
lib.SetRecordFile(record_file)
if kallsyms_file is not None:
lib.SetKallsymsFile(kallsyms_file)
- if sample_filter:
- lib.SetSampleFilter(sample_filter)
lib.SetReportOptions(report_lib_options)
arch = lib.GetArch()
@@ -326,9 +319,6 @@ def _gecko_profile(
if sample is None:
lib.Close()
break
- if comm_filter:
- if sample.thread_comm not in comm_filter:
- continue
event = lib.GetEventOfCurrentSample()
symbol = lib.GetSymbolOfCurrentSample()
callchain = lib.GetCallChainOfCurrentSample()
@@ -407,23 +397,12 @@ def main() -> None:
parser.add_argument('--kallsyms', help='Set the path to find kernel symbols.')
parser.add_argument('-i', '--record_file', nargs='?', default='perf.data',
help='Default is perf.data.')
- parser.add_argument(
- '--proguard-mapping-file', nargs='+',
- help='Add proguard mapping file to de-obfuscate symbols',
- default=[])
- sample_filter_group = parser.add_argument_group('Sample filter options')
- parser.add_sample_filter_options(sample_filter_group)
- sample_filter_group.add_argument('--comm', nargs='+', action='append', help="""
- Use samples only in threads with selected names.""")
parser.add_report_lib_options()
args = parser.parse_args()
profile = _gecko_profile(
record_file=args.record_file,
symfs_dir=args.symfs,
kallsyms_file=args.kallsyms,
- proguard_mapping_file=args.proguard_mapping_file,
- comm_filter=set(flatten_arg_list(args.comm)),
- sample_filter=args.sample_filter,
report_lib_options=args.report_lib_options)
json.dump(profile, sys.stdout, sort_keys=True)
diff --git a/simpleperf/scripts/inferno/inferno.py b/simpleperf/scripts/inferno/inferno.py
index 6606da27..ccf1b6b6 100755
--- a/simpleperf/scripts/inferno/inferno.py
+++ b/simpleperf/scripts/inferno/inferno.py
@@ -111,12 +111,6 @@ def parse_samples(process, args, sample_filter_fn):
lib.SetRecordFile(record_file)
if kallsyms_file:
lib.SetKallsymsFile(kallsyms_file)
- for file_path in args.proguard_mapping_file or []:
- lib.AddProguardMappingFile(file_path)
- if args.trace_offcpu:
- lib.SetTraceOffCpuMode(args.trace_offcpu)
- if args.sample_filter:
- lib.SetSampleFilter(args.sample_filter)
lib.SetReportOptions(args.report_lib_options)
process.cmd = lib.GetRecordCmd()
product_props = lib.MetaInfo().get("product_props")
@@ -312,11 +306,8 @@ def main():
report_group.add_argument('--symfs', help="""Set the path to find binaries with symbols and
debug info.""")
report_group.add_argument('--title', help='Show a title in the report.')
- report_group.add_argument('--proguard-mapping-file', nargs='+',
- help='Add proguard mapping file to de-obfuscate symbols')
- parser.add_trace_offcpu_option(report_group)
- parser.add_sample_filter_options(report_group, False)
- parser.add_report_lib_options(report_group)
+ parser.add_report_lib_options(
+ report_group, sample_filter_group=report_group, sample_filter_with_pid_shortcut=False)
debug_group = parser.add_argument_group('Debug options')
debug_group.add_argument('--disable_adb_root', action='store_true', help="""Force adb to run
diff --git a/simpleperf/scripts/pprof_proto_generator.py b/simpleperf/scripts/pprof_proto_generator.py
index 4fcb6378..57c988b9 100755
--- a/simpleperf/scripts/pprof_proto_generator.py
+++ b/simpleperf/scripts/pprof_proto_generator.py
@@ -270,7 +270,6 @@ class PprofProfileGenerator(object):
config['binary_cache_dir'] = 'binary_cache'
if not os.path.isdir(config['binary_cache_dir']):
config['binary_cache_dir'] = None
- self.comm_filter = set(config['comm_filters']) if config.get('comm_filters') else None
self.dso_filter = set(config['dso_filters']) if config.get('dso_filters') else None
self.max_chain_length = config['max_chain_length']
self.profile = profile_pb2.Profile()
@@ -303,10 +302,6 @@ class PprofProfileGenerator(object):
if self.config.get('show_art_frames'):
self.lib.ShowArtFrames()
- for file_path in self.config['proguard_mapping_file'] or []:
- self.lib.AddProguardMappingFile(file_path)
- if self.config.get('sample_filter'):
- self.lib.SetSampleFilter(self.config['sample_filter'])
self.lib.SetReportOptions(self.config['report_lib_options'])
comments = [
@@ -330,9 +325,6 @@ class PprofProfileGenerator(object):
symbol = self.lib.GetSymbolOfCurrentSample()
callchain = self.lib.GetCallChainOfCurrentSample()
- if not self._filter_report_sample(report_sample):
- continue
-
sample_type_id = self.get_sample_type_id(event.name)
sample = Sample()
sample.add_value(sample_type_id, 1)
@@ -380,13 +372,6 @@ class PprofProfileGenerator(object):
return self.profile
- def _filter_report_sample(self, sample):
- """Return true if the sample can be used."""
- if self.comm_filter:
- if sample.thread_comm not in self.comm_filter:
- return False
- return True
-
def _filter_symbol(self, symbol):
if not self.dso_filter or symbol.dso_name in self.dso_filter:
return True
@@ -635,18 +620,12 @@ def main():
Maximum depth of samples to be converted.""") # Large value as infinity standin.
parser.add_argument('--ndk_path', type=extant_dir, help='Set the path of a ndk release.')
parser.add_argument(
- '--proguard-mapping-file', nargs='+',
- help='Add proguard mapping file to de-obfuscate symbols')
- parser.add_argument(
'-j', '--jobs', type=int, default=os.cpu_count(),
help='Use multithreading to speed up source code annotation.')
sample_filter_group = parser.add_argument_group('Sample filter options')
- parser.add_sample_filter_options(sample_filter_group)
- sample_filter_group.add_argument('--comm', nargs='+', action='append', help="""
- Use samples only in threads with selected names.""")
sample_filter_group.add_argument('--dso', nargs='+', action='append', help="""
Use samples only in selected binaries.""")
- parser.add_report_lib_options()
+ parser.add_report_lib_options(sample_filter_group=sample_filter_group)
args = parser.parse_args()
if args.show:
@@ -658,12 +637,9 @@ def main():
config = {}
config['output_file'] = args.output_file
- config['comm_filters'] = flatten_arg_list(args.comm)
config['dso_filters'] = flatten_arg_list(args.dso)
config['ndk_path'] = args.ndk_path
config['max_chain_length'] = args.max_chain_length
- config['proguard_mapping_file'] = args.proguard_mapping_file
- config['sample_filter'] = args.sample_filter
config['report_lib_options'] = args.report_lib_options
generator = PprofProfileGenerator(config)
for record_file in args.record_file:
diff --git a/simpleperf/scripts/purgatorio/purgatorio.py b/simpleperf/scripts/purgatorio/purgatorio.py
index 93336cb1..4e2560a2 100755
--- a/simpleperf/scripts/purgatorio/purgatorio.py
+++ b/simpleperf/scripts/purgatorio/purgatorio.py
@@ -172,9 +172,6 @@ def generate_datasource(args):
lib.SetReportOptions(args.report_lib_options)
- for file_path in args.proguard_mapping_file or []:
- lib.AddProguardMappingFile(file_path)
-
product = lib.MetaInfo().get('product_props')
if product:
@@ -279,9 +276,6 @@ def main():
help='Include dso names in backtraces')
parser.add_argument('--include_symbols_addr', '-s', action='store_true',
help='Include addresses of symbols in backtraces')
- parser.add_argument(
- '--proguard-mapping-file', nargs='+',
- help='Add proguard mapping file to de-obfuscate symbols')
parser.add_report_lib_options(default_show_art_frames=True)
args = parser.parse_args()
diff --git a/simpleperf/scripts/report_html.py b/simpleperf/scripts/report_html.py
index af3144ea..56f8dae5 100755
--- a/simpleperf/scripts/report_html.py
+++ b/simpleperf/scripts/report_html.py
@@ -605,13 +605,10 @@ class RecordData(object):
def __init__(
self, binary_cache_path: Optional[str],
ndk_path: Optional[str],
- build_addr_hit_map: bool, proguard_mapping_files: Optional[List[str]],
- trace_offcpu: Optional[str]):
+ build_addr_hit_map: bool):
self.binary_cache_path = binary_cache_path
self.ndk_path = ndk_path
self.build_addr_hit_map = build_addr_hit_map
- self.proguard_mapping_files = proguard_mapping_files
- self.trace_offcpu = trace_offcpu
self.meta_info: Optional[Dict[str, str]] = None
self.cmdline: Optional[str] = None
self.arch: Optional[str] = None
@@ -623,9 +620,7 @@ class RecordData(object):
self.gen_addr_hit_map_in_record_info = False
self.binary_finder = BinaryFinder(binary_cache_path, ReadElf(ndk_path))
- def load_record_file(
- self, record_file: str, report_lib_options: ReportLibOptions,
- sample_filter: Optional[str]):
+ def load_record_file(self, record_file: str, report_lib_options: ReportLibOptions):
lib = ReportLib()
lib.SetRecordFile(record_file)
# If not showing ip for unknown symbols, the percent of the unknown symbol may be
@@ -633,12 +628,6 @@ class RecordData(object):
lib.ShowIpForUnknownSymbol()
if self.binary_cache_path:
lib.SetSymfs(self.binary_cache_path)
- for file_path in self.proguard_mapping_files or []:
- lib.AddProguardMappingFile(file_path)
- if self.trace_offcpu:
- lib.SetTraceOffCpuMode(self.trace_offcpu)
- if sample_filter:
- lib.SetSampleFilter(sample_filter)
lib.SetReportOptions(report_lib_options)
self.meta_info = lib.MetaInfo()
self.cmdline = lib.GetRecordCmd()
@@ -990,11 +979,6 @@ def get_args() -> argparse.Namespace:
parser.add_argument('--aggregate-by-thread-name', action='store_true', help="""aggregate
samples by thread name instead of thread id. This is useful for
showing multiple perf.data generated for the same app.""")
- parser.add_argument(
- '--proguard-mapping-file', nargs='+',
- help='Add proguard mapping file to de-obfuscate symbols')
- parser.add_trace_offcpu_option()
- parser.add_sample_filter_options()
parser.add_report_lib_options()
return parser.parse_args()
@@ -1021,10 +1005,9 @@ def main():
log_exit('Invalid --jobs option.')
# 2. Produce record data.
- record_data = RecordData(binary_cache_path, ndk_path, build_addr_hit_map,
- args.proguard_mapping_file, args.trace_offcpu)
+ record_data = RecordData(binary_cache_path, ndk_path, build_addr_hit_map)
for record_file in args.record_file:
- record_data.load_record_file(record_file, args.report_lib_options, args.sample_filter)
+ record_data.load_record_file(record_file, args.report_lib_options)
if args.aggregate_by_thread_name:
record_data.aggregate_by_thread_name()
record_data.limit_percents(args.min_func_percent, args.min_callchain_percent)
diff --git a/simpleperf/scripts/report_sample.py b/simpleperf/scripts/report_sample.py
index 7388442a..dc5c4e2b 100755
--- a/simpleperf/scripts/report_sample.py
+++ b/simpleperf/scripts/report_sample.py
@@ -19,7 +19,7 @@
"""
from simpleperf_report_lib import ReportLib
-from simpleperf_utils import BaseArgumentParser, flatten_arg_list
+from simpleperf_utils import BaseArgumentParser, flatten_arg_list, ReportLibOptions
from typing import List, Set, Optional
@@ -28,27 +28,19 @@ def report_sample(
symfs_dir: str,
kallsyms_file: str,
show_tracing_data: bool,
- proguard_mapping_file: List[str],
header: bool,
- comm_filter: Set[str],
- trace_offcpu: Optional[str],
- sample_filter: Optional[str]):
+ report_lib_options: ReportLibOptions):
""" read record_file, and print each sample"""
lib = ReportLib()
lib.ShowIpForUnknownSymbol()
- for file_path in proguard_mapping_file:
- lib.AddProguardMappingFile(file_path)
if symfs_dir is not None:
lib.SetSymfs(symfs_dir)
if record_file is not None:
lib.SetRecordFile(record_file)
if kallsyms_file is not None:
lib.SetKallsymsFile(kallsyms_file)
- if trace_offcpu:
- lib.SetTraceOffCpuMode(trace_offcpu)
- if sample_filter:
- lib.SetSampleFilter(sample_filter)
+ lib.SetReportOptions(report_lib_options)
if header:
print("# ========")
@@ -64,9 +56,6 @@ def report_sample(
if sample is None:
lib.Close()
break
- if comm_filter:
- if sample.thread_comm not in comm_filter:
- continue
event = lib.GetEventOfCurrentSample()
symbol = lib.GetSymbolOfCurrentSample()
callchain = lib.GetCallChainOfCurrentSample()
@@ -97,27 +86,17 @@ def main():
parser.add_argument('-i', '--record_file', nargs='?', default='perf.data',
help='Default is perf.data.')
parser.add_argument('--show_tracing_data', action='store_true', help='print tracing data.')
- parser.add_argument(
- '--proguard-mapping-file', nargs='+',
- help='Add proguard mapping file to de-obfuscate symbols',
- default=[])
parser.add_argument('--header', action='store_true',
help='Show metadata header, like perf script --header')
- parser.add_argument('--comm', nargs='+', action='append', help="""
- Use samples only in threads with selected names.""")
- parser.add_trace_offcpu_option()
- parser.add_sample_filter_options()
+ parser.add_report_lib_options()
args = parser.parse_args()
report_sample(
record_file=args.record_file,
symfs_dir=args.symfs,
kallsyms_file=args.kallsyms,
show_tracing_data=args.show_tracing_data,
- proguard_mapping_file=args.proguard_mapping_file,
header=args.header,
- comm_filter=set(flatten_arg_list(args.comm)),
- trace_offcpu=args.trace_offcpu,
- sample_filter=args.sample_filter)
+ report_lib_options=args.report_lib_options)
if __name__ == '__main__':
diff --git a/simpleperf/scripts/simpleperf_report_lib.py b/simpleperf/scripts/simpleperf_report_lib.py
index f8f0841c..48b31199 100644
--- a/simpleperf/scripts/simpleperf_report_lib.py
+++ b/simpleperf/scripts/simpleperf_report_lib.py
@@ -300,8 +300,15 @@ class ReportLib(object):
def SetReportOptions(self, options: ReportLibOptions):
""" Set report options in one call. """
+ if options.proguard_mapping_files:
+ for file_path in options.proguard_mapping_files:
+ self.AddProguardMappingFile(file_path)
if options.show_art_frames:
self.ShowArtFrames(True)
+ if options.trace_offcpu:
+ self.SetTraceOffCpuMode(options.trace_offcpu)
+ if options.sample_filters:
+ self.SetSampleFilter(options.sample_filters)
def SetLogSeverity(self, log_level: str = 'info'):
""" Set log severity of native lib, can be verbose,debug,info,error,fatal."""
@@ -368,7 +375,7 @@ class ReportLib(object):
res: bool = self._SetTraceOffCpuModeFunc(self.getInstance(), _char_pt(mode))
_check(res, f'Failed to call SetTraceOffCpuMode({mode})')
- def SetSampleFilter(self, filter: str):
+ def SetSampleFilter(self, filters: List[str]):
""" Set options used to filter samples. Available options are:
--exclude-pid pid1,pid2,... Exclude samples for selected processes.
--exclude-tid tid1,tid2,... Exclude samples for selected threads.
@@ -387,8 +394,10 @@ class ReportLib(object):
The filter argument should be a concatenation of options.
"""
- res: bool = self._SetSampleFilterFunc(self.getInstance(), _char_pt(filter))
- _check(res, f'Failed to call SetSampleFilter({filter})')
+ filter_array = (ct.c_char_p * len(filters))()
+ filter_array[:] = [_char_pt(f) for f in filters]
+ res: bool = self._SetSampleFilterFunc(self.getInstance(), filter_array, len(filters))
+ _check(res, f'Failed to call SetSampleFilter({filters})')
def GetNextSample(self) -> Optional[SampleStruct]:
""" Return the next sample. If no more samples, return None. """
diff --git a/simpleperf/scripts/simpleperf_utils.py b/simpleperf/scripts/simpleperf_utils.py
index 1e963310..2a7dfd35 100644
--- a/simpleperf/scripts/simpleperf_utils.py
+++ b/simpleperf/scripts/simpleperf_utils.py
@@ -1003,6 +1003,9 @@ class ArgParseFormatter(
@dataclass
class ReportLibOptions:
show_art_frames: bool
+ trace_offcpu: str
+ proguard_mapping_files: List[str]
+ sample_filters: List[str]
class BaseArgumentParser(argparse.ArgumentParser):
@@ -1013,15 +1016,17 @@ class BaseArgumentParser(argparse.ArgumentParser):
self.has_report_lib_options = False
def add_report_lib_options(self, group: Optional[Any] = None,
- default_show_art_frames: bool = False):
+ default_show_art_frames: bool = False,
+ sample_filter_group: Optional[Any] = None,
+ sample_filter_with_pid_shortcut: bool = True):
self.has_report_lib_options = True
parser = group if group else self
+ parser.add_argument(
+ '--proguard-mapping-file', nargs='+',
+ help='Add proguard mapping file to de-obfuscate symbols')
parser.add_argument('--show-art-frames', '--show_art_frames',
action=argparse.BooleanOptionalAction, default=default_show_art_frames,
help='Show frames of internal methods in the ART Java interpreter.')
-
- def add_trace_offcpu_option(self, subparser: Optional[Any] = None):
- parser = subparser if subparser else self
parser.add_argument(
'--trace-offcpu', choices=['on-cpu', 'off-cpu', 'on-off-cpu', 'mixed-on-off-cpu'],
help="""Set report mode for profiles recorded with --trace-offcpu option. All possible
@@ -1030,8 +1035,9 @@ class BaseArgumentParser(argparse.ArgumentParser):
mixed-on-off-cpu (on-cpu and off-cpu samples using the same event name).
If not set, mixed-on-off-cpu mode is used.
""")
+ self._add_sample_filter_options(sample_filter_group, sample_filter_with_pid_shortcut)
- def add_sample_filter_options(
+ def _add_sample_filter_options(
self, group: Optional[Any] = None, with_pid_shortcut: bool = True):
if not group:
group = self.add_argument_group('Sample filter options')
@@ -1059,7 +1065,8 @@ class BaseArgumentParser(argparse.ArgumentParser):
'--include-process-name', metavar='process_name_regex', nargs='+',
help='only include samples for processes with name containing the regular expression')
group.add_argument(
- '--include-thread-name', metavar='thread_name_regex', nargs='+',
+ '--comm', '--include-thread-name', metavar='thread_name_regex',
+ dest='include_thread_name', nargs='+',
help='only include samples for threads with name containing the regular expression')
group.add_argument(
'--filter-file', metavar='file',
@@ -1068,40 +1075,38 @@ class BaseArgumentParser(argparse.ArgumentParser):
self.has_sample_filter_options = True
self.sample_filter_with_pid_shortcut = with_pid_shortcut
- def _build_sample_filter(self, args: argparse.Namespace) -> Optional[str]:
- """ Convert sample filter options into a sample filter string, which can be passed to
- ReportLib.SetSampleFilter().
- """
+ def _build_sample_filter(self, args: argparse.Namespace) -> List[str]:
+ """ Build sample filters, which can be passed to ReportLib.SetSampleFilter(). """
filters = []
if args.exclude_pid:
- filters.append('--exclude-pid ' + ','.join(str(pid) for pid in args.exclude_pid))
+ filters.extend(['--exclude-pid', ','.join(str(pid) for pid in args.exclude_pid)])
if args.exclude_tid:
- filters.append('--exclude-tid ' + ','.join(str(tid) for tid in args.exclude_tid))
+ filters.extend(['--exclude-tid', ','.join(str(tid) for tid in args.exclude_tid)])
if args.exclude_process_name:
for name in args.exclude_process_name:
- filters.append('--exclude-process-name ' + name)
+ filters.extend(['--exclude-process-name', name])
if args.exclude_thread_name:
for name in args.exclude_thread_name:
- filters.append('--exclude-thread-name ' + name)
+ filters.extend(['--exclude-thread-name', name])
if args.include_pid:
- filters.append('--include-pid ' + ','.join(str(pid) for pid in args.include_pid))
+ filters.extend(['--include-pid', ','.join(str(pid) for pid in args.include_pid)])
if args.include_tid:
- filters.append('--include-tid ' + ','.join(str(tid) for tid in args.include_tid))
+ filters.extend(['--include-tid', ','.join(str(tid) for tid in args.include_tid)])
if self.sample_filter_with_pid_shortcut:
if args.pid:
- filters.append('--include-pid ' + ','.join(str(pid) for pid in args.pid))
+ filters.extend(['--include-pid', ','.join(str(pid) for pid in args.pid)])
if args.tid:
- filters.append('--include-tid ' + ','.join(str(pid) for pid in args.tid))
+ filters.extend(['--include-tid', ','.join(str(pid) for pid in args.tid)])
if args.include_process_name:
for name in args.include_process_name:
- filters.append('--include-process-name ' + name)
+ filters.extend(['--include-process-name', name])
if args.include_thread_name:
for name in args.include_thread_name:
- filters.append('--include-thread-name ' + name)
+ filters.extend(['--include-thread-name', name])
if args.filter_file:
- filters.append('--filter-file ' + args.filter_file)
- return ' '.join(filters)
+ filters.extend(['--filter-file', args.filter_file])
+ return filters
def parse_known_args(self, *args, **kwargs):
self.add_argument(
@@ -1109,11 +1114,11 @@ class BaseArgumentParser(argparse.ArgumentParser):
default='info', help='set log level')
namespace, left_args = super().parse_known_args(*args, **kwargs)
- if self.has_sample_filter_options:
- setattr(namespace, 'sample_filter', self._build_sample_filter(namespace))
-
if self.has_report_lib_options:
- report_lib_options = ReportLibOptions(namespace.show_art_frames)
+ sample_filters = self._build_sample_filter(namespace)
+ report_lib_options = ReportLibOptions(
+ namespace.show_art_frames, namespace.trace_offcpu, namespace.proguard_mapping_file,
+ sample_filters)
setattr(namespace, 'report_lib_options', report_lib_options)
if not Log.initialized:
diff --git a/simpleperf/scripts/stackcollapse.py b/simpleperf/scripts/stackcollapse.py
index 4e96c55f..e0e1d86f 100755
--- a/simpleperf/scripts/stackcollapse.py
+++ b/simpleperf/scripts/stackcollapse.py
@@ -37,31 +37,24 @@ def collapse_stacks(
record_file: str,
symfs_dir: str,
kallsyms_file: str,
- proguard_mapping_file: List[str],
event_filter: str,
include_pid: bool,
include_tid: bool,
annotate_kernel: bool,
annotate_jit: bool,
include_addrs: bool,
- comm_filter: Set[str],
- sample_filter: Optional[str],
report_lib_options: ReportLibOptions):
"""read record_file, aggregate per-stack and print totals per-stack"""
lib = ReportLib()
if include_addrs:
lib.ShowIpForUnknownSymbol()
- for file_path in proguard_mapping_file:
- lib.AddProguardMappingFile(file_path)
if symfs_dir is not None:
lib.SetSymfs(symfs_dir)
if record_file is not None:
lib.SetRecordFile(record_file)
if kallsyms_file is not None:
lib.SetKallsymsFile(kallsyms_file)
- if sample_filter:
- lib.SetSampleFilter(sample_filter)
lib.SetReportOptions(report_lib_options)
stacks: DefaultDict[str, int] = defaultdict(int)
@@ -72,9 +65,6 @@ def collapse_stacks(
if sample is None:
lib.Close()
break
- if comm_filter:
- if sample.thread_comm not in comm_filter:
- continue
event = lib.GetEventOfCurrentSample()
symbol = lib.GetSymbolOfCurrentSample()
callchain = lib.GetCallChainOfCurrentSample()
@@ -125,31 +115,22 @@ def main():
parser.add_argument('--jit', action='store_true', help='Annotate JIT functions with a _[j]')
parser.add_argument('--addrs', action='store_true',
help='include raw addresses where symbols can\'t be found')
- parser.add_argument(
- '--proguard-mapping-file', nargs='+',
- help='Add proguard mapping file to de-obfuscate symbols',
- default=[])
sample_filter_group = parser.add_argument_group('Sample filter options')
- parser.add_sample_filter_options(sample_filter_group, False)
sample_filter_group.add_argument('--event-filter', nargs='?', default='',
help='Event type filter e.g. "cpu-cycles" or "instructions"')
- sample_filter_group.add_argument('--comm', nargs='+', action='append', help="""
- Use samples only in threads with selected names.""")
- parser.add_report_lib_options()
+ parser.add_report_lib_options(sample_filter_group=sample_filter_group,
+ sample_filter_with_pid_shortcut=False)
args = parser.parse_args()
collapse_stacks(
record_file=args.record_file,
symfs_dir=args.symfs,
kallsyms_file=args.kallsyms,
- proguard_mapping_file=args.proguard_mapping_file,
event_filter=args.event_filter,
include_pid=args.pid,
include_tid=args.tid,
annotate_kernel=args.kernel,
annotate_jit=args.jit,
include_addrs=args.addrs,
- comm_filter=set(flatten_arg_list(args.comm)),
- sample_filter=args.sample_filter,
report_lib_options=args.report_lib_options)
diff --git a/simpleperf/scripts/test/do_test.py b/simpleperf/scripts/test/do_test.py
index 2c67bb78..b95a1fef 100755
--- a/simpleperf/scripts/test/do_test.py
+++ b/simpleperf/scripts/test/do_test.py
@@ -32,6 +32,7 @@ import multiprocessing as mp
import os
from pathlib import Path
import re
+import subprocess
import sys
import time
from tqdm import tqdm
@@ -39,7 +40,7 @@ import types
from typing import List, Optional
import unittest
-from simpleperf_utils import BaseArgumentParser, extant_dir, log_exit, remove
+from simpleperf_utils import BaseArgumentParser, extant_dir, log_exit, remove, is_darwin
from . api_profiler_test import *
from . annotate_test import *
@@ -517,6 +518,15 @@ def run_tests_in_child_process(tests: List[str], args: argparse.Namespace) -> bo
return False
+def sign_executables_on_darwin():
+ """ Sign executables on M1 Mac, otherwise they can't run. """
+ if not is_darwin():
+ return
+ bin_dir = Path(__file__).resolve().parents[1] / 'bin' / 'darwin' / 'x86_64'
+ for path in bin_dir.iterdir():
+ subprocess.run(f'codesign --force -s - {path}', shell=True, check=True)
+
+
def main() -> bool:
args = get_args()
tests = get_host_tests() if args.only_host_test else get_all_tests()
@@ -532,4 +542,5 @@ def main() -> bool:
# Switch to the test dir.
os.chdir(test_dir)
build_testdata(Path('testdata'))
+ sign_executables_on_darwin()
return run_tests_in_child_process(tests, args)
diff --git a/simpleperf/scripts/test/pprof_proto_generator_test.py b/simpleperf/scripts/test/pprof_proto_generator_test.py
index 7504db14..cbeb8d6a 100644
--- a/simpleperf/scripts/test/pprof_proto_generator_test.py
+++ b/simpleperf/scripts/test/pprof_proto_generator_test.py
@@ -217,8 +217,8 @@ class TestPprofProtoGenerator(TestBase):
binary_cache_builder.build_binary_cache(testdata_file, [TestHelper.testdata_dir])
# Read recording file.
- config = {'ndk_path': None, 'max_chain_length': 1000000, 'proguard_mapping_file': None,
- 'report_lib_options': ReportLibOptions(False)}
+ config = {'ndk_path': None, 'max_chain_length': 1000000,
+ 'report_lib_options': ReportLibOptions(False, '', None, None)}
generator = PprofProfileGenerator(config)
generator.load_record_file(testdata_file)
diff --git a/simpleperf/scripts/test/report_lib_test.py b/simpleperf/scripts/test/report_lib_test.py
index ba7ae466..1447c5ae 100644
--- a/simpleperf/scripts/test/report_lib_test.py
+++ b/simpleperf/scripts/test/report_lib_test.py
@@ -16,7 +16,7 @@
import os
import tempfile
-from typing import Set
+from typing import List, Set
from simpleperf_report_lib import ReportLib
from . test_utils import TestBase, TestHelper
@@ -275,34 +275,39 @@ class TestReportLib(TestBase):
def test_set_sample_filter(self):
""" Test using ReportLib.SetSampleFilter(). """
- def get_threads_for_filter(filter: str) -> Set[int]:
+ def get_threads_for_filter(filters: List[str]) -> Set[int]:
self.report_lib.Close()
self.report_lib = ReportLib()
self.report_lib.SetRecordFile(TestHelper.testdata_path('perf_display_bitmaps.data'))
- self.report_lib.SetSampleFilter(filter)
+ self.report_lib.SetSampleFilter(filters)
threads = set()
while self.report_lib.GetNextSample():
sample = self.report_lib.GetCurrentSample()
threads.add(sample.tid)
return threads
- self.assertNotIn(31850, get_threads_for_filter('--exclude-pid 31850'))
- self.assertIn(31850, get_threads_for_filter('--include-pid 31850'))
- self.assertNotIn(31881, get_threads_for_filter('--exclude-tid 31881'))
- self.assertIn(31881, get_threads_for_filter('--include-tid 31881'))
+ self.assertNotIn(31850, get_threads_for_filter(['--exclude-pid', '31850']))
+ self.assertIn(31850, get_threads_for_filter(['--include-pid', '31850']))
+ self.assertNotIn(31881, get_threads_for_filter(['--exclude-tid', '31881']))
+ self.assertIn(31881, get_threads_for_filter(['--include-tid', '31881']))
self.assertNotIn(31881, get_threads_for_filter(
- '--exclude-process-name com.example.android.displayingbitmaps'))
+ ['--exclude-process-name', 'com.example.android.displayingbitmaps']))
self.assertIn(31881, get_threads_for_filter(
- '--include-process-name com.example.android.displayingbitmaps'))
+ ['--include-process-name', 'com.example.android.displayingbitmaps']))
self.assertNotIn(31850, get_threads_for_filter(
- '--exclude-thread-name com.example.android.displayingbitmaps'))
+ ['--exclude-thread-name', 'com.example.android.displayingbitmaps']))
self.assertIn(31850, get_threads_for_filter(
- '--include-thread-name com.example.android.displayingbitmaps'))
+ ['--include-thread-name', 'com.example.android.displayingbitmaps']))
+
+ # Check that thread name can have space.
+ self.assertNotIn(31856, get_threads_for_filter(
+ ['--exclude-thread-name', 'Jit thread pool']))
+ self.assertIn(31856, get_threads_for_filter(['--include-thread-name', 'Jit thread pool']))
with tempfile.NamedTemporaryFile('w', delete=False) as filter_file:
filter_file.write('GLOBAL_BEGIN 684943449406175\nGLOBAL_END 684943449406176')
filter_file.flush()
- threads = get_threads_for_filter('--filter-file ' + filter_file.name)
+ threads = get_threads_for_filter(['--filter-file', filter_file.name])
self.assertIn(31881, threads)
self.assertNotIn(31850, threads)
os.unlink(filter_file.name)
diff --git a/simpleperf/scripts/test/report_sample_test.py b/simpleperf/scripts/test/report_sample_test.py
index 4b09f82f..5b6681cb 100644
--- a/simpleperf/scripts/test/report_sample_test.py
+++ b/simpleperf/scripts/test/report_sample_test.py
@@ -17,32 +17,27 @@
import os
import re
import tempfile
-from typing import Set
+from typing import List, Optional, Set
from . test_utils import TestBase, TestHelper
class TestReportSample(TestBase):
+ def get_record_data_string(self, record_file: str, options: Optional[List[str]] = None):
+ args = ['report_sample.py', '-i', TestHelper.testdata_path(record_file)]
+ if options:
+ args += options
+ report = self.run_cmd(args, return_output=True)
+ return report.replace('\r', '')
def test_no_flags(self):
- got = self.run_cmd(
- ['report_sample.py',
- '-i',
- TestHelper.testdata_path('perf_display_bitmaps.data')],
- return_output=True)
- got = got.replace('\r', '')
+ got = self.get_record_data_string('perf_display_bitmaps.data')
with open(TestHelper.testdata_path('perf_display_bitmaps.perf-script')) as f:
want = f.read()
self.assertEqual(got, want)
def test_comm_filter_to_renderthread(self):
- got = self.run_cmd(
- ['report_sample.py',
- '-i',
- TestHelper.testdata_path('perf_display_bitmaps.data'),
- '--comm', 'RenderThread'],
- return_output=True)
- got = got.replace('\r', '')
+ got = self.get_record_data_string('perf_display_bitmaps.data', ['--comm', 'RenderThread'])
self.assertIn('RenderThread', got)
self.assertNotIn('com.example.android.displayingbitmaps', got)
@@ -51,13 +46,8 @@ class TestReportSample(TestBase):
self.assertEqual(got, want)
def test_comm_filter_to_ui_thread(self):
- got = self.run_cmd(
- ['report_sample.py',
- '-i',
- TestHelper.testdata_path('perf_display_bitmaps.data'),
- '--comm', 'com.example.android.displayingbitmaps'],
- return_output=True)
- got = got.replace('\r', '')
+ got = self.get_record_data_string('perf_display_bitmaps.data', [
+ '--comm', 'com.example.android.displayingbitmaps'])
self.assertIn('com.example.android.displayingbitmaps', got)
self.assertNotIn('RenderThread', got)
with open(TestHelper.testdata_path('perf_display_bitmaps.UiThread.perf-script')) as f:
@@ -65,29 +55,20 @@ class TestReportSample(TestBase):
self.assertEqual(got, want)
def test_header(self):
- got = self.run_cmd(
- ['report_sample.py',
- '-i',
- TestHelper.testdata_path('perf_display_bitmaps.data'),
- '--header'],
- return_output=True)
- got = got.replace('\r', '')
+ got = self.get_record_data_string('perf_display_bitmaps.data', ['--header'])
with open(TestHelper.testdata_path('perf_display_bitmaps.header.perf-script')) as f:
want = f.read()
self.assertEqual(got, want)
def test_trace_offcpu(self):
- got = self.run_cmd(
- ['report_sample.py', '-i', TestHelper.testdata_path('perf_with_trace_offcpu_v2.data'),
- '--trace-offcpu', 'on-cpu'], return_output=True)
+ got = self.get_record_data_string('perf_with_trace_offcpu_v2.data', [
+ '--trace-offcpu', 'on-cpu'])
self.assertIn('cpu-clock:u', got)
self.assertNotIn('sched:sched_switch', got)
def test_sample_filters(self):
def get_threads_for_filter(filter: str) -> Set[int]:
- report = self.run_cmd(
- ['report_sample.py', '-i', TestHelper.testdata_path('perf_display_bitmaps.data')] +
- filter.split(), return_output=True)
+ report = self.get_record_data_string('perf_display_bitmaps.data', filter.split())
pattern = re.compile(r'\s+31850/(\d+)\s+')
threads = set()
for m in re.finditer(pattern, report):
@@ -116,3 +97,11 @@ class TestReportSample(TestBase):
self.assertIn(31881, threads)
self.assertNotIn(31850, threads)
os.unlink(filter_file.name)
+
+ def test_show_art_frames(self):
+ art_frame_str = 'art::interpreter::DoCall'
+ report = self.get_record_data_string('perf_with_interpreter_frames.data')
+ self.assertNotIn(art_frame_str, report)
+ report = self.get_record_data_string(
+ 'perf_with_interpreter_frames.data', ['--show-art-frames'])
+ self.assertIn(art_frame_str, report)
diff --git a/tests/kernel.config/AndroidTest.xml b/tests/kernel.config/AndroidTest.xml
index 19da90af..78452e1c 100644
--- a/tests/kernel.config/AndroidTest.xml
+++ b/tests/kernel.config/AndroidTest.xml
@@ -26,7 +26,7 @@
</target_preparer>
<!-- Make sure there is some data in the pstore then reboot -->
<target_preparer class="com.android.tradefed.targetprep.RunCommandTargetPreparer">
- <option name="run-command" value="echo HELLOWORLD >/dev/pmsg0" />
+ <option name="run-command" value="if [ -e /dev/pmsg0 ] ; then echo HELLOWORLD > /dev/pmsg0; fi" />
<option name="throw-if-cmd-fail" value="true" />
</target_preparer>
<target_preparer class="com.android.tradefed.targetprep.RebootTargetPreparer" />
diff --git a/verity/Android.bp b/verity/Android.bp
index fc2b827c..ef09528c 100644
--- a/verity/Android.bp
+++ b/verity/Android.bp
@@ -154,13 +154,8 @@ python_binary_host {
name: "build_verity_metadata",
srcs: ["build_verity_metadata.py"],
version: {
- py2: {
- enabled: true,
- embedded_launcher: true,
- },
py3: {
- enabled: false,
- embedded_launcher: false,
+ embedded_launcher: true,
},
},
}
diff --git a/verity/build_verity_metadata.py b/verity/build_verity_metadata.py
index 5a7d7d27..a428f270 100644
--- a/verity/build_verity_metadata.py
+++ b/verity/build_verity_metadata.py
@@ -1,4 +1,4 @@
-#! /usr/bin/env python
+#!/usr/bin/env python3
#
# Copyright (C) 2013 The Android Open Source Project
#
@@ -29,9 +29,9 @@ BLOCK_SIZE = 4096
METADATA_SIZE = BLOCK_SIZE * 8
def run(cmd):
- p = subprocess.Popen(cmd, stdout=subprocess.PIPE)
+ p = subprocess.Popen(cmd, stdout=subprocess.PIPE, text=True)
output, _ = p.communicate()
- print output
+ print(output)
if p.returncode:
exit(-1)
@@ -43,12 +43,12 @@ def build_metadata_block(verity_table, signature, verity_disable=False):
magic = MAGIC_DISABLE if verity_disable else MAGIC_NUMBER
block = struct.pack("II256sI", magic, VERSION, signature, table_len)
block += verity_table
- block = block.ljust(METADATA_SIZE, '\x00')
+ block = block.ljust(METADATA_SIZE, b'\x00')
return block
def sign_verity_table(table, signer_path, key_path, signer_args=None):
- with tempfile.NamedTemporaryFile(suffix='.table') as table_file:
- with tempfile.NamedTemporaryFile(suffix='.sig') as signature_file:
+ with tempfile.NamedTemporaryFile(mode='wb', suffix='.table') as table_file:
+ with tempfile.NamedTemporaryFile(mode='rb', suffix='.sig') as signature_file:
table_file.write(table)
table_file.flush()
if signer_args is None:
@@ -56,7 +56,7 @@ def sign_verity_table(table, signer_path, key_path, signer_args=None):
else:
args_list = shlex.split(signer_args)
cmd = [signer_path] + args_list + [table_file.name, key_path, signature_file.name]
- print cmd
+ print(cmd)
run(cmd)
return signature_file.read()
@@ -70,7 +70,7 @@ def build_verity_table(block_device, data_blocks, root_hash, salt):
data_blocks,
root_hash,
salt)
- return table
+ return table.encode()
def build_verity_metadata(data_blocks, metadata_image, root_hash, salt,
block_device, signer_path, signing_key, signer_args=None,
@@ -109,9 +109,9 @@ if __name__ == "__main__":
args = parser.parse_args()
if args.dest == 'size':
- print get_verity_metadata_size(args.partition_size)
+ print(get_verity_metadata_size(args.partition_size))
else:
- build_verity_metadata(args.blocks / 4096, args.metadata_image,
+ build_verity_metadata(args.blocks // 4096, args.metadata_image,
args.root_hash, args.salt, args.block_device,
args.signer_path, args.signing_key,
args.signer_args, args.verity_disable)
diff --git a/verity/fec/Android.bp b/verity/fec/Android.bp
index 9ca45600..ede5fd19 100644
--- a/verity/fec/Android.bp
+++ b/verity/fec/Android.bp
@@ -25,6 +25,11 @@ cc_binary_host {
misc_undefined: ["integer"],
},
},
+ linux_musl_x86_64: {
+ sanitize: {
+ misc_undefined: ["integer"],
+ },
+ },
linux: {
static_libs: [
"libavb",