summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorandroid-build-team Robot <android-build-team-robot@google.com>2021-05-15 03:09:34 +0000
committerandroid-build-team Robot <android-build-team-robot@google.com>2021-05-15 03:09:34 +0000
commit93a383b7ad336094e5a27096cdc018ff7305de93 (patch)
tree17acc9a18c48a67531f14d96136d5c77dd88f176
parent6b048fe9fae1c275b82c85a7b94c2a0693b89cb1 (diff)
parentd6e22ee6c4d52319f03010986382aade9e765bba (diff)
downloadextras-93a383b7ad336094e5a27096cdc018ff7305de93.tar.gz
Snap for 7364021 from d6e22ee6c4d52319f03010986382aade9e765bba to sc-d1-release
Change-Id: Ie550aa7069d0e748f8736567ec0663767a3010a4
-rw-r--r--simpleperf/Android.bp3
-rw-r--r--simpleperf/cmd_record.cpp5
-rw-r--r--simpleperf/cmd_record_test.cpp18
-rw-r--r--simpleperf/environment.cpp55
-rw-r--r--simpleperf/environment.h1
-rw-r--r--simpleperf/environment_test.cpp7
-rwxr-xr-xsimpleperf/scripts/debug_unwind_reporter.py3
-rw-r--r--simpleperf/scripts/simpleperf_report_lib.py83
-rw-r--r--simpleperf/scripts/simpleperf_utils.py3
-rwxr-xr-xsimpleperf/scripts/test/do_test.py22
-rw-r--r--simpleperf/simpleperf_app_runner/simpleperf_app_runner.cpp28
11 files changed, 156 insertions, 72 deletions
diff --git a/simpleperf/Android.bp b/simpleperf/Android.bp
index 9dfef2e2..5f9b0de2 100644
--- a/simpleperf/Android.bp
+++ b/simpleperf/Android.bp
@@ -158,13 +158,14 @@ cc_defaults {
linux: {
shared_libs: [
"libcutils",
- "libdexfile_support",
"libevent",
+ "liblog",
"libprocinfo",
"libunwindstack",
],
static_libs: [
"libc++fs",
+ "libdexfile_support",
],
},
host: {
diff --git a/simpleperf/cmd_record.cpp b/simpleperf/cmd_record.cpp
index 2e646bb4..9d9abb61 100644
--- a/simpleperf/cmd_record.cpp
+++ b/simpleperf/cmd_record.cpp
@@ -1914,8 +1914,13 @@ bool RecordCommand::DumpMetaInfoFeature(bool kernel_symbols_available) {
android::base::GetProperty("ro.product.model", "").c_str(),
android::base::GetProperty("ro.product.name", "").c_str());
info_map["android_version"] = android::base::GetProperty("ro.build.version.release", "");
+ info_map["android_sdk_version"] = android::base::GetProperty("ro.build.version.sdk", "");
+ info_map["android_build_type"] = android::base::GetProperty("ro.build.type", "");
if (!app_package_name_.empty()) {
info_map["app_package_name"] = app_package_name_;
+ if (IsRoot()) {
+ info_map["app_type"] = GetAppType(app_package_name_);
+ }
}
if (event_selection_set_.HasAuxTrace()) {
// used by --exclude-perf in cmd_inject.cpp
diff --git a/simpleperf/cmd_record_test.cpp b/simpleperf/cmd_record_test.cpp
index 3e0b9e1a..869c747f 100644
--- a/simpleperf/cmd_record_test.cpp
+++ b/simpleperf/cmd_record_test.cpp
@@ -754,8 +754,7 @@ TEST(record_cmd, app_option_for_debuggable_app) {
SetRunInAppToolForTesting(true, false);
TestRecordingApps("com.android.simpleperf.debuggable", "debuggable");
SetRunInAppToolForTesting(false, true);
- // Although the app is actually debuggable, we profile the app using simpleperf_app_runner.
- TestRecordingApps("com.android.simpleperf.debuggable", "profileable");
+ TestRecordingApps("com.android.simpleperf.debuggable", "debuggable");
}
TEST(record_cmd, app_option_for_profileable_app) {
@@ -1177,3 +1176,18 @@ TEST(record_cmd, add_meta_info_option) {
ASSERT_FALSE(RunRecordCmd({"--add-meta-info", "key1="}, tmpfile.path));
ASSERT_FALSE(RunRecordCmd({"--add-meta-info", "=value1"}, tmpfile.path));
}
+
+TEST(record_cmd, device_meta_info) {
+ TemporaryFile tmpfile;
+ ASSERT_TRUE(RunRecordCmd({}, tmpfile.path));
+ auto reader = RecordFileReader::CreateInstance(tmpfile.path);
+ ASSERT_TRUE(reader);
+
+ const std::unordered_map<std::string, std::string>& meta_info = reader->GetMetaInfoFeature();
+ auto it = meta_info.find("android_sdk_version");
+ ASSERT_NE(it, meta_info.end());
+ ASSERT_FALSE(it->second.empty());
+ it = meta_info.find("android_build_type");
+ ASSERT_NE(it, meta_info.end());
+ ASSERT_FALSE(it->second.empty());
+}
diff --git a/simpleperf/environment.cpp b/simpleperf/environment.cpp
index 4fe908fa..4c960350 100644
--- a/simpleperf/environment.cpp
+++ b/simpleperf/environment.cpp
@@ -530,9 +530,10 @@ std::set<pid_t> WaitForAppProcesses(const std::string& package_name) {
namespace {
-bool IsAppDebuggable(const std::string& user_id, const std::string& package_name) {
- return Workload::RunCmd(
- {"run-as", package_name, "--user", user_id, "echo", ">/dev/null", "2>/dev/null"}, false);
+bool IsAppDebuggable(int user_id, const std::string& package_name) {
+ return Workload::RunCmd({"run-as", package_name, "--user", std::to_string(user_id), "echo",
+ ">/dev/null", "2>/dev/null"},
+ false);
}
class InAppRunner {
@@ -696,10 +697,6 @@ class RunAs : public InAppRunner {
};
bool RunAs::Prepare() {
- // Test if run-as can access the package.
- if (!IsAppDebuggable(user_id_, package_name_)) {
- return false;
- }
// run-as can't run /data/local/tmp/simpleperf directly. So copy simpleperf binary if needed.
if (!android::base::Readlink("/proc/self/exe", &simpleperf_path_)) {
PLOG(ERROR) << "ReadLink failed";
@@ -722,8 +719,12 @@ bool RunAs::Prepare() {
class SimpleperfAppRunner : public InAppRunner {
public:
- SimpleperfAppRunner(int user_id, const std::string& package_name)
- : InAppRunner(user_id, package_name) {}
+ SimpleperfAppRunner(int user_id, const std::string& package_name, const std::string app_type)
+ : InAppRunner(user_id, package_name) {
+ // On Android < S, the app type is unknown before running simpleperf_app_runner. Assume it's
+ // profileable.
+ app_type_ = app_type == "unknown" ? "profileable" : app_type;
+ }
bool Prepare() override { return GetAndroidVersion() >= kAndroidVersionQ; }
protected:
@@ -736,10 +737,12 @@ class SimpleperfAppRunner : public InAppRunner {
args.emplace_back(cmd);
if (cmd == "record" && GetAndroidVersion() >= kAndroidVersionS) {
args.emplace_back("--add-meta-info");
- args.emplace_back("app_type=profileable");
+ args.emplace_back("app_type=" + app_type_);
}
return args;
}
+
+ std::string app_type_;
};
} // namespace
@@ -766,21 +769,45 @@ static int GetCurrentUserId() {
return 0;
}
+std::string GetAppType(const std::string& app_package_name) {
+ if (GetAndroidVersion() < kAndroidVersionS) {
+ return "unknown";
+ }
+ std::string cmd = "simpleperf_app_runner " + app_package_name + " --show-app-type";
+ std::unique_ptr<FILE, decltype(&pclose)> fp(popen(cmd.c_str(), "re"), pclose);
+ if (fp) {
+ char buf[128];
+ if (fgets(buf, sizeof(buf), fp.get()) != nullptr) {
+ return android::base::Trim(buf);
+ }
+ }
+ // Can't get app_type. It means the app doesn't exist.
+ return "not_exist";
+}
+
bool RunInAppContext(const std::string& app_package_name, const std::string& cmd,
const std::vector<std::string>& args, size_t workload_args_size,
const std::string& output_filepath, bool need_tracepoint_events) {
int user_id = GetCurrentUserId();
std::unique_ptr<InAppRunner> in_app_runner;
- if (allow_run_as) {
+
+ std::string app_type = GetAppType(app_package_name);
+ if (app_type == "unknown" && IsAppDebuggable(user_id, app_package_name)) {
+ app_type = "debuggable";
+ }
+
+ if (allow_run_as && app_type == "debuggable") {
in_app_runner.reset(new RunAs(user_id, app_package_name));
if (!in_app_runner->Prepare()) {
in_app_runner = nullptr;
}
}
if (!in_app_runner && allow_simpleperf_app_runner) {
- in_app_runner.reset(new SimpleperfAppRunner(user_id, app_package_name));
- if (!in_app_runner->Prepare()) {
- in_app_runner = nullptr;
+ if (app_type == "debuggable" || app_type == "profileable" || app_type == "unknown") {
+ in_app_runner.reset(new SimpleperfAppRunner(user_id, app_package_name, app_type));
+ if (!in_app_runner->Prepare()) {
+ in_app_runner = nullptr;
+ }
}
}
if (!in_app_runner) {
diff --git a/simpleperf/environment.h b/simpleperf/environment.h
index 665a7c53..180b98c3 100644
--- a/simpleperf/environment.h
+++ b/simpleperf/environment.h
@@ -94,6 +94,7 @@ void SetRunInAppToolForTesting(bool run_as, bool simpleperf_app_runner); // for
bool RunInAppContext(const std::string& app_package_name, const std::string& cmd,
const std::vector<std::string>& args, size_t workload_args_size,
const std::string& output_filepath, bool need_tracepoint_events);
+std::string GetAppType(const std::string& app_package_name);
void AllowMoreOpenedFiles();
diff --git a/simpleperf/environment_test.cpp b/simpleperf/environment_test.cpp
index 596b632a..a95caca8 100644
--- a/simpleperf/environment_test.cpp
+++ b/simpleperf/environment_test.cpp
@@ -129,3 +129,10 @@ TEST(environment, GetProcessUid) {
ASSERT_TRUE(uid.has_value());
ASSERT_EQ(uid.value(), getuid());
}
+
+TEST(environment, GetAppType) {
+ TEST_REQUIRE_APPS();
+ ASSERT_EQ(GetAppType("com.android.simpleperf.debuggable"), "debuggable");
+ ASSERT_EQ(GetAppType("com.android.simpleperf.profileable"), "profileable");
+ ASSERT_EQ(GetAppType("com.android.simpleperf.app_not_exist"), "not_exist");
+}
diff --git a/simpleperf/scripts/debug_unwind_reporter.py b/simpleperf/scripts/debug_unwind_reporter.py
index 7a291b27..238c9acd 100755
--- a/simpleperf/scripts/debug_unwind_reporter.py
+++ b/simpleperf/scripts/debug_unwind_reporter.py
@@ -37,10 +37,9 @@ Below is an example using debug_unwind_reporter.py:
import argparse
from collections import Counter, defaultdict
-from collections.abc import Iterator
from simpleperf_utils import ArgParseFormatter
from texttable import Texttable
-from typing import List, Dict
+from typing import Dict, Iterator, List
class CallChainNode:
diff --git a/simpleperf/scripts/simpleperf_report_lib.py b/simpleperf/scripts/simpleperf_report_lib.py
index e202e796..a38f4876 100644
--- a/simpleperf/scripts/simpleperf_report_lib.py
+++ b/simpleperf/scripts/simpleperf_report_lib.py
@@ -24,30 +24,26 @@ import collections
import ctypes as ct
from pathlib import Path
import struct
-from typing import Dict, Union
+from typing import Any, Dict, List, Optional, Union
from simpleperf_utils import bytes_to_str, get_host_binary_path, is_windows, str_to_bytes
-def _get_native_lib():
- return get_host_binary_path('libsimpleperf_report.so')
-
-
-def _is_null(p):
+def _is_null(p: Optional[ct._Pointer]) -> bool:
if p:
return False
return ct.cast(p, ct.c_void_p).value is None
-def _char_pt(s):
+def _char_pt(s: str) -> bytes:
return str_to_bytes(s)
-def _char_pt_to_str(char_pt):
+def _char_pt_to_str(char_pt: ct.c_char_p) -> str:
return bytes_to_str(char_pt)
-def _check(cond, failmsg):
+def _check(cond: bool, failmsg: str):
if not cond:
raise RuntimeError(failmsg)
@@ -76,7 +72,7 @@ class SampleStruct(ct.Structure):
('period', ct.c_uint64)]
@property
- def thread_comm(self):
+ def thread_comm(self) -> str:
return _char_pt_to_str(self._thread_comm)
@@ -99,10 +95,10 @@ class TracingFieldFormatStruct(ct.Structure):
_unpack_key_dict = {1: 'b', 2: 'h', 4: 'i', 8: 'q'}
@property
- def name(self):
+ def name(self) -> str:
return _char_pt_to_str(self._name)
- def parse_value(self, data):
+ def parse_value(self, data: ct.c_char_p) -> Union[str, bytes, List[bytes]]:
""" Parse value of a field in a tracepoint event.
The return value depends on the type of the field, and can be an int value, a string,
an array of int values, etc. If the type can't be parsed, return a byte array or an
@@ -161,7 +157,7 @@ class EventStruct(ct.Structure):
('tracing_data_format', TracingDataFormatStruct)]
@property
- def name(self):
+ def name(self) -> str:
return _char_pt_to_str(self._name)
@@ -193,11 +189,11 @@ class SymbolStruct(ct.Structure):
('mapping', ct.POINTER(MappingStruct))]
@property
- def dso_name(self):
+ def dso_name(self) -> str:
return _char_pt_to_str(self._dso_name)
@property
- def symbol_name(self):
+ def symbol_name(self) -> str:
return _char_pt_to_str(self._symbol_name)
@@ -239,9 +235,9 @@ class ReportLibStructure(ct.Structure):
# pylint: disable=invalid-name
class ReportLib(object):
- def __init__(self, native_lib_path=None):
+ def __init__(self, native_lib_path: Optional[str] = None):
if native_lib_path is None:
- native_lib_path = _get_native_lib()
+ native_lib_path = self._get_native_lib()
self._load_dependent_lib()
self._lib = ct.CDLL(native_lib_path)
@@ -274,9 +270,12 @@ class ReportLib(object):
self._instance = self._CreateReportLibFunc()
assert not _is_null(self._instance)
- self.meta_info = None
- self.current_sample = None
- self.record_cmd = None
+ self.meta_info: Optional[Dict[str, str]] = None
+ self.current_sample: Optional[SampleStruct] = None
+ self.record_cmd: Optional[str] = None
+
+ def _get_native_lib(self) -> str:
+ return get_host_binary_path('libsimpleperf_report.so')
def _load_dependent_lib(self):
# As the windows dll is built with mingw we need to load 'libwinpthread-1.dll'.
@@ -284,34 +283,33 @@ class ReportLib(object):
self._libwinpthread = ct.CDLL(get_host_binary_path('libwinpthread-1.dll'))
def Close(self):
- if self._instance is None:
- return
- self._DestroyReportLibFunc(self._instance)
- self._instance = None
+ if self._instance:
+ self._DestroyReportLibFunc(self._instance)
+ self._instance = None
- def SetLogSeverity(self, log_level='info'):
+ def SetLogSeverity(self, log_level: str = 'info'):
""" Set log severity of native lib, can be verbose,debug,info,error,fatal."""
- cond = self._SetLogSeverityFunc(self.getInstance(), _char_pt(log_level))
+ cond: bool = self._SetLogSeverityFunc(self.getInstance(), _char_pt(log_level))
_check(cond, 'Failed to set log level')
- def SetSymfs(self, symfs_dir):
+ def SetSymfs(self, symfs_dir: str):
""" Set directory used to find symbols."""
- cond = self._SetSymfsFunc(self.getInstance(), _char_pt(symfs_dir))
+ cond: bool = self._SetSymfsFunc(self.getInstance(), _char_pt(symfs_dir))
_check(cond, 'Failed to set symbols directory')
- def SetRecordFile(self, record_file):
+ def SetRecordFile(self, record_file: str):
""" Set the path of record file, like perf.data."""
- cond = self._SetRecordFileFunc(self.getInstance(), _char_pt(record_file))
+ cond: bool = self._SetRecordFileFunc(self.getInstance(), _char_pt(record_file))
_check(cond, 'Failed to set record file')
def ShowIpForUnknownSymbol(self):
self._ShowIpForUnknownSymbolFunc(self.getInstance())
- def ShowArtFrames(self, show=True):
+ def ShowArtFrames(self, show: bool = True):
""" Show frames of internal methods of the Java interpreter. """
self._ShowArtFramesFunc(self.getInstance(), show)
- def MergeJavaMethods(self, merge=True):
+ def MergeJavaMethods(self, merge: bool = True):
""" This option merges jitted java methods with the same name but in different jit
symfiles. If possible, it also merges jitted methods with interpreted methods,
by mapping jitted methods to their corresponding dex files.
@@ -327,12 +325,13 @@ class ReportLib(object):
if not self._AddProguardMappingFileFunc(self.getInstance(), _char_pt(str(mapping_file))):
raise ValueError(f'failed to add proguard mapping file: {mapping_file}')
- def SetKallsymsFile(self, kallsym_file):
+ def SetKallsymsFile(self, kallsym_file: str):
""" Set the file path to a copy of the /proc/kallsyms file (for off device decoding) """
- cond = self._SetKallsymsFileFunc(self.getInstance(), _char_pt(kallsym_file))
+ cond: bool = self._SetKallsymsFileFunc(self.getInstance(), _char_pt(kallsym_file))
_check(cond, 'Failed to set kallsyms file')
- def GetNextSample(self):
+ def GetNextSample(self) -> Optional[SampleStruct]:
+ """ Return the next sample. If no more samples, return None. """
psample = self._GetNextSampleFunc(self.getInstance())
if _is_null(psample):
self.current_sample = None
@@ -340,25 +339,25 @@ class ReportLib(object):
self.current_sample = psample[0]
return self.current_sample
- def GetCurrentSample(self):
+ def GetCurrentSample(self) -> Optional[SampleStruct]:
return self.current_sample
- def GetEventOfCurrentSample(self):
+ def GetEventOfCurrentSample(self) -> EventStruct:
event = self._GetEventOfCurrentSampleFunc(self.getInstance())
assert not _is_null(event)
return event[0]
- def GetSymbolOfCurrentSample(self):
+ def GetSymbolOfCurrentSample(self) -> SymbolStruct:
symbol = self._GetSymbolOfCurrentSampleFunc(self.getInstance())
assert not _is_null(symbol)
return symbol[0]
- def GetCallChainOfCurrentSample(self):
+ def GetCallChainOfCurrentSample(self) -> CallChainStructure:
callchain = self._GetCallChainOfCurrentSampleFunc(self.getInstance())
assert not _is_null(callchain)
return callchain[0]
- def GetTracingDataOfCurrentSample(self):
+ def GetTracingDataOfCurrentSample(self) -> Optional[Dict[str, Any]]:
data = self._GetTracingDataOfCurrentSampleFunc(self.getInstance())
if _is_null(data):
return None
@@ -369,7 +368,7 @@ class ReportLib(object):
result[field.name] = field.parse_value(data)
return result
- def GetBuildIdForPath(self, path):
+ def GetBuildIdForPath(self, path: str) -> str:
build_id = self._GetBuildIdForPathFunc(self.getInstance(), _char_pt(path))
assert not _is_null(build_id)
return _char_pt_to_str(build_id)
@@ -441,7 +440,7 @@ class ReportLib(object):
self.meta_info[str_list[i]] = str_list[i + 1]
return self.meta_info
- def getInstance(self):
+ def getInstance(self) -> ct._Pointer:
if self._instance is None:
raise Exception('Instance is Closed')
return self._instance
diff --git a/simpleperf/scripts/simpleperf_utils.py b/simpleperf/scripts/simpleperf_utils.py
index 65652210..cf50fcb2 100644
--- a/simpleperf/scripts/simpleperf_utils.py
+++ b/simpleperf/scripts/simpleperf_utils.py
@@ -20,7 +20,6 @@
from __future__ import annotations
import argparse
-from collections.abc import Iterator
import logging
import os
import os.path
@@ -30,7 +29,7 @@ import shutil
import subprocess
import sys
import time
-from typing import Dict, List, Optional, Set, Union
+from typing import Dict, Iterator, List, Optional, Set, Union
def get_script_dir() -> str:
diff --git a/simpleperf/scripts/test/do_test.py b/simpleperf/scripts/test/do_test.py
index b2886b65..946390df 100755
--- a/simpleperf/scripts/test/do_test.py
+++ b/simpleperf/scripts/test/do_test.py
@@ -65,7 +65,8 @@ def get_args() -> argparse.Namespace:
parser.add_argument(
'-d', '--device', nargs='+',
help='set devices used to run tests. Each device in format name:serial-number')
- parser.add_argument('--list-tests', action='store_true', help='List all tests.')
+ parser.add_argument('--only-host-test', action='store_true', help='Only run host tests')
+ parser.add_argument('--list-tests', action='store_true', help='List tests')
parser.add_argument('--ndk-path', type=extant_dir, help='Set the path of a ndk release')
parser.add_argument('-p', '--pattern', nargs='+',
help='Run tests matching the selected pattern.')
@@ -86,8 +87,16 @@ def get_all_tests() -> List[str]:
return sorted(tests)
-def get_filtered_tests(test_from: Optional[str], test_pattern: Optional[List[str]]) -> List[str]:
- tests = get_all_tests()
+def get_host_tests() -> List[str]:
+ def filter_fn(test: str) -> bool:
+ return get_test_type(test) == 'host_test'
+ return list(filter(filter_fn, get_all_tests()))
+
+
+def get_filtered_tests(
+ tests: List[str],
+ test_from: Optional[str],
+ test_pattern: Optional[List[str]]) -> List[str]:
if test_from:
try:
tests = tests[tests.index(test_from):]
@@ -466,12 +475,13 @@ def run_tests_in_child_process(tests: List[str], args: argparse.Namespace) -> bo
def main() -> bool:
args = get_args()
+ tests = get_host_tests() if args.only_host_test else get_all_tests()
+ tests = get_filtered_tests(tests, args.test_from, args.pattern)
+
if args.list_tests:
- print('\n'.join(get_all_tests()))
+ print('\n'.join(tests))
return True
- tests = get_filtered_tests(args.test_from, args.pattern)
-
test_dir = Path(args.test_dir).resolve()
remove(test_dir)
test_dir.mkdir(parents=True)
diff --git a/simpleperf/simpleperf_app_runner/simpleperf_app_runner.cpp b/simpleperf/simpleperf_app_runner/simpleperf_app_runner.cpp
index 20329d0d..f3e6b4b5 100644
--- a/simpleperf/simpleperf_app_runner/simpleperf_app_runner.cpp
+++ b/simpleperf/simpleperf_app_runner/simpleperf_app_runner.cpp
@@ -164,9 +164,16 @@ static void CheckSimpleperfArguments(std::string_view cmd_name, char** args) {
int main(int argc, char* argv[]) {
if (argc < 3) {
- error(1, 0,
- "usage: simpleperf_app_runner package_name [--user uid] simpleperf_cmd "
- "simpleperf_cmd_args...");
+ fprintf(
+ stderr,
+ // clang-format off
+"Usage: simpleperf_app_runner package_name [options] [simpleperf cmd simpleperf_cmd_args]\n"
+"Options:\n"
+"--user uid profile app process run by uid\n"
+"--show-app-type show if the app is debuggable or profileable\n"
+ // clang-format on
+ );
+ return 1;
}
int i = 1;
char* pkgname = argv[i++];
@@ -177,6 +184,21 @@ int main(int argc, char* argv[]) {
}
i += 2;
}
+ if (i < argc && strcmp(argv[i], "--show-app-type") == 0) {
+ pkg_info* info = ReadPackageInfo(pkgname);
+ if (info == nullptr) {
+ error(1, 0, "failed to find package %s", pkgname);
+ }
+ if (info->debuggable) {
+ printf("debuggable\n");
+ } else if (info->profileable_from_shell) {
+ printf("profileable\n");
+ } else {
+ printf("non_profileable\n");
+ }
+ return 0;
+ }
+
if (i == argc) {
error(1, 0, "no simpleperf command name");
}