summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorYabin Cui <yabinc@google.com>2018-04-12 17:42:59 +0000
committerGerrit Code Review <noreply-gerritcodereview@google.com>2018-04-12 17:42:59 +0000
commita6ee510913c53783875a5512ff0d00a26b31eaad (patch)
treeef5b40452a30b0ced61313d64bae177255835e3e
parent7f1b4927bfe7b2e816cd95445e47476cf8fffd50 (diff)
parent40b70ffb58d830edefad2c82a9d33e1c7ce91138 (diff)
downloadextras-a6ee510913c53783875a5512ff0d00a26b31eaad.tar.gz
Merge "simpleperf: Improve the way downloading native libs on device."
-rw-r--r--simpleperf/Android.mk1
-rw-r--r--simpleperf/cmd_record.cpp21
-rw-r--r--simpleperf/cmd_report_test.cpp4
-rw-r--r--simpleperf/demo/README.md2
-rw-r--r--simpleperf/demo/SimpleperfExampleOfKotlin/.idea/caches/build_file_checksums.serbin0 -> 552 bytes
-rw-r--r--simpleperf/demo/SimpleperfExampleOfKotlin/.idea/codeStyles/Project.xml29
-rw-r--r--simpleperf/demo/SimpleperfExampleOfKotlin/.idea/kotlinc.xml8
-rw-r--r--simpleperf/demo/SimpleperfExampleOfKotlin/.idea/misc.xml7
-rw-r--r--simpleperf/demo/SimpleperfExampleOfKotlin/app/build/outputs/apk/profiling/app-profiling.apkbin1975972 -> 1963814 bytes
-rw-r--r--simpleperf/demo/SimpleperfExampleOfKotlin/app/profiling.gradle2
-rw-r--r--simpleperf/demo/SimpleperfExampleOfKotlin/build.gradle2
-rw-r--r--simpleperf/demo/SimpleperfExampleOfKotlin/gradle/wrapper/gradle-wrapper.properties4
-rw-r--r--simpleperf/demo/SimpleperfExamplePureJava/.idea/caches/build_file_checksums.serbin552 -> 552 bytes
-rw-r--r--simpleperf/demo/SimpleperfExamplePureJava/.idea/codeStyles/Project.xml29
-rw-r--r--simpleperf/demo/SimpleperfExamplePureJava/app/build.gradle2
-rw-r--r--simpleperf/demo/SimpleperfExamplePureJava/app/build/outputs/apk/app-profiling.apkbin1577612 -> 0 bytes
-rw-r--r--simpleperf/demo/SimpleperfExamplePureJava/app/profiling.gradle2
-rw-r--r--simpleperf/demo/SimpleperfExamplePureJava/build.gradle4
-rw-r--r--simpleperf/demo/SimpleperfExamplePureJava/gradle/wrapper/gradle-wrapper.properties4
-rw-r--r--simpleperf/demo/SimpleperfExampleWithNative/.idea/caches/build_file_checksums.serbin599 -> 599 bytes
-rw-r--r--simpleperf/demo/SimpleperfExampleWithNative/.idea/codeStyles/Project.xml29
-rwxr-xr-xsimpleperf/demo/SimpleperfExampleWithNative/app/build/intermediates/cmake/profiling/obj/arm64-v8a/libnative-lib.sobin1334536 -> 2535360 bytes
-rwxr-xr-xsimpleperf/demo/SimpleperfExampleWithNative/app/build/intermediates/cmake/profiling/obj/armeabi-v7a/libnative-lib.sobin976252 -> 1661432 bytes
-rwxr-xr-xsimpleperf/demo/SimpleperfExampleWithNative/app/build/intermediates/cmake/profiling/obj/armeabi/libnative-lib.sobin1021064 -> 0 bytes
-rwxr-xr-xsimpleperf/demo/SimpleperfExampleWithNative/app/build/intermediates/cmake/profiling/obj/mips/libnative-lib.sobin1178452 -> 0 bytes
-rwxr-xr-xsimpleperf/demo/SimpleperfExampleWithNative/app/build/intermediates/cmake/profiling/obj/mips64/libnative-lib.sobin1519256 -> 0 bytes
-rwxr-xr-xsimpleperf/demo/SimpleperfExampleWithNative/app/build/intermediates/cmake/profiling/obj/x86/libnative-lib.sobin1057000 -> 2093120 bytes
-rwxr-xr-xsimpleperf/demo/SimpleperfExampleWithNative/app/build/intermediates/cmake/profiling/obj/x86_64/libnative-lib.sobin1264944 -> 2519560 bytes
-rw-r--r--simpleperf/demo/SimpleperfExampleWithNative/app/build/outputs/apk/profiling/app-profiling.apkbin4567473 -> 1950766 bytes
-rw-r--r--simpleperf/demo/SimpleperfExampleWithNative/app/profiling.gradle3
-rw-r--r--simpleperf/demo/SimpleperfExampleWithNative/build.gradle1
-rw-r--r--simpleperf/dso.cpp159
-rw-r--r--simpleperf/dso.h26
-rw-r--r--simpleperf/dso_test.cpp67
-rw-r--r--simpleperf/get_test_data.h1
-rw-r--r--simpleperf/scripts/app_profiler.py190
-rw-r--r--simpleperf/scripts/binary_cache_builder.py27
-rw-r--r--simpleperf/scripts/test.py116
-rw-r--r--simpleperf/scripts/utils.py93
39 files changed, 618 insertions, 215 deletions
diff --git a/simpleperf/Android.mk b/simpleperf/Android.mk
index 1f89cdb2..911d8981 100644
--- a/simpleperf/Android.mk
+++ b/simpleperf/Android.mk
@@ -362,6 +362,7 @@ simpleperf_unit_test_src_files := \
cmd_report_test.cpp \
cmd_report_sample_test.cpp \
command_test.cpp \
+ dso_test.cpp \
gtest_main.cpp \
read_apk_test.cpp \
read_elf_test.cpp \
diff --git a/simpleperf/cmd_record.cpp b/simpleperf/cmd_record.cpp
index 75d17ff2..203943b4 100644
--- a/simpleperf/cmd_record.cpp
+++ b/simpleperf/cmd_record.cpp
@@ -1396,26 +1396,13 @@ bool RecordCommand::DumpBuildIdFeature() {
continue;
}
build_id_records.push_back(BuildIdRecord(true, UINT_MAX, build_id, path));
- } else {
+ } else if (dso->type() == DSO_ELF_FILE) {
if (dso->Path() == DEFAULT_EXECNAME_FOR_THREAD_MMAP) {
continue;
}
- auto tuple = SplitUrlInApk(dso->Path());
- if (std::get<0>(tuple)) {
- ElfStatus result = GetBuildIdFromApkFile(std::get<1>(tuple),
- std::get<2>(tuple), &build_id);
- if (result != ElfStatus::NO_ERROR) {
- LOG(DEBUG) << "can't read build_id from file " << dso->Path() << ": "
- << result;
- continue;
- }
- } else {
- ElfStatus result = GetBuildIdFromElfFile(dso->Path(), &build_id);
- if (result != ElfStatus::NO_ERROR) {
- LOG(DEBUG) << "can't read build_id from file " << dso->Path() << ": "
- << result;
- continue;
- }
+ if (!GetBuildIdFromDsoPath(dso->Path(), &build_id)) {
+ LOG(DEBUG) << "Can't read build_id from file " << dso->Path();
+ continue;
}
build_id_records.push_back(
BuildIdRecord(false, UINT_MAX, build_id, dso->Path()));
diff --git a/simpleperf/cmd_report_test.cpp b/simpleperf/cmd_report_test.cpp
index 6ebbf31e..3eb3c918 100644
--- a/simpleperf/cmd_report_test.cpp
+++ b/simpleperf/cmd_report_test.cpp
@@ -374,7 +374,7 @@ TEST_F(ReportCommandTest, check_build_id) {
}
exit(0);
},
- testing::ExitedWithCode(0), "Build id mismatch");
+ testing::ExitedWithCode(0), "failed to read symbols from /elf_for_build_id_check");
}
TEST_F(ReportCommandTest, no_show_ip_option) {
@@ -415,7 +415,7 @@ TEST_F(ReportCommandTest, read_elf_file_warning) {
}
exit(0);
},
- testing::ExitedWithCode(0), "elf: Read failed");
+ testing::ExitedWithCode(0), "failed to read symbols from /elf: File not found");
}
TEST_F(ReportCommandTest, report_data_generated_by_linux_perf) {
diff --git a/simpleperf/demo/README.md b/simpleperf/demo/README.md
index 2a293c58..61e74224 100644
--- a/simpleperf/demo/README.md
+++ b/simpleperf/demo/README.md
@@ -30,7 +30,7 @@ $ cd extras/simpleperf/demo
The testing environment:
```
-Android Studio 3.0
+Android Studio 3.2
test device: Android O (Google Pixel 2)
test device: Android N (Google Nexus 6P)
Please make sure your device having Android version >= N.
diff --git a/simpleperf/demo/SimpleperfExampleOfKotlin/.idea/caches/build_file_checksums.ser b/simpleperf/demo/SimpleperfExampleOfKotlin/.idea/caches/build_file_checksums.ser
new file mode 100644
index 00000000..3da69c6b
--- /dev/null
+++ b/simpleperf/demo/SimpleperfExampleOfKotlin/.idea/caches/build_file_checksums.ser
Binary files differ
diff --git a/simpleperf/demo/SimpleperfExampleOfKotlin/.idea/codeStyles/Project.xml b/simpleperf/demo/SimpleperfExampleOfKotlin/.idea/codeStyles/Project.xml
new file mode 100644
index 00000000..30aa626c
--- /dev/null
+++ b/simpleperf/demo/SimpleperfExampleOfKotlin/.idea/codeStyles/Project.xml
@@ -0,0 +1,29 @@
+<component name="ProjectCodeStyleConfiguration">
+ <code_scheme name="Project" version="173">
+ <Objective-C-extensions>
+ <file>
+ <option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Import" />
+ <option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Macro" />
+ <option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Typedef" />
+ <option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Enum" />
+ <option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Constant" />
+ <option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Global" />
+ <option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Struct" />
+ <option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="FunctionPredecl" />
+ <option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Function" />
+ </file>
+ <class>
+ <option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Property" />
+ <option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Synthesize" />
+ <option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="InitMethod" />
+ <option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="StaticMethod" />
+ <option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="InstanceMethod" />
+ <option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="DeallocMethod" />
+ </class>
+ <extensions>
+ <pair source="cpp" header="h" fileNamingConvention="NONE" />
+ <pair source="c" header="h" fileNamingConvention="NONE" />
+ </extensions>
+ </Objective-C-extensions>
+ </code_scheme>
+</component> \ No newline at end of file
diff --git a/simpleperf/demo/SimpleperfExampleOfKotlin/.idea/kotlinc.xml b/simpleperf/demo/SimpleperfExampleOfKotlin/.idea/kotlinc.xml
new file mode 100644
index 00000000..1d2cf4e1
--- /dev/null
+++ b/simpleperf/demo/SimpleperfExampleOfKotlin/.idea/kotlinc.xml
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project version="4">
+ <component name="KotlinCommonCompilerArguments">
+ <option name="errors">
+ <ArgumentParseErrors />
+ </option>
+ </component>
+</project> \ No newline at end of file
diff --git a/simpleperf/demo/SimpleperfExampleOfKotlin/.idea/misc.xml b/simpleperf/demo/SimpleperfExampleOfKotlin/.idea/misc.xml
index 39638799..99202cc2 100644
--- a/simpleperf/demo/SimpleperfExampleOfKotlin/.idea/misc.xml
+++ b/simpleperf/demo/SimpleperfExampleOfKotlin/.idea/misc.xml
@@ -5,11 +5,12 @@
<option name="myDefaultNotNull" value="android.support.annotation.NonNull" />
<option name="myNullables">
<value>
- <list size="4">
+ <list size="5">
<item index="0" class="java.lang.String" itemvalue="org.jetbrains.annotations.Nullable" />
<item index="1" class="java.lang.String" itemvalue="javax.annotation.Nullable" />
- <item index="2" class="java.lang.String" itemvalue="edu.umd.cs.findbugs.annotations.Nullable" />
- <item index="3" class="java.lang.String" itemvalue="android.support.annotation.Nullable" />
+ <item index="2" class="java.lang.String" itemvalue="javax.annotation.CheckForNull" />
+ <item index="3" class="java.lang.String" itemvalue="edu.umd.cs.findbugs.annotations.Nullable" />
+ <item index="4" class="java.lang.String" itemvalue="android.support.annotation.Nullable" />
</list>
</value>
</option>
diff --git a/simpleperf/demo/SimpleperfExampleOfKotlin/app/build/outputs/apk/profiling/app-profiling.apk b/simpleperf/demo/SimpleperfExampleOfKotlin/app/build/outputs/apk/profiling/app-profiling.apk
index 50b88caf..414fd6e6 100644
--- a/simpleperf/demo/SimpleperfExampleOfKotlin/app/build/outputs/apk/profiling/app-profiling.apk
+++ b/simpleperf/demo/SimpleperfExampleOfKotlin/app/build/outputs/apk/profiling/app-profiling.apk
Binary files differ
diff --git a/simpleperf/demo/SimpleperfExampleOfKotlin/app/profiling.gradle b/simpleperf/demo/SimpleperfExampleOfKotlin/app/profiling.gradle
index 257adbaf..aa23c8d2 100644
--- a/simpleperf/demo/SimpleperfExampleOfKotlin/app/profiling.gradle
+++ b/simpleperf/demo/SimpleperfExampleOfKotlin/app/profiling.gradle
@@ -13,8 +13,6 @@ android {
}
}
packagingOptions {
- // Contain debug info in the libraries.
- doNotStrip "**.so"
// Exclude wrap.sh for architectures not built.
if (abiFiltersForWrapScript) {
diff --git a/simpleperf/demo/SimpleperfExampleOfKotlin/build.gradle b/simpleperf/demo/SimpleperfExampleOfKotlin/build.gradle
index 65872e75..ba06bb9f 100644
--- a/simpleperf/demo/SimpleperfExampleOfKotlin/build.gradle
+++ b/simpleperf/demo/SimpleperfExampleOfKotlin/build.gradle
@@ -7,7 +7,7 @@ buildscript {
jcenter()
}
dependencies {
- classpath 'com.android.tools.build:gradle:3.0.0'
+ classpath 'com.android.tools.build:gradle:3.2.0-alpha09'
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
// NOTE: Do not place your application dependencies here; they belong
diff --git a/simpleperf/demo/SimpleperfExampleOfKotlin/gradle/wrapper/gradle-wrapper.properties b/simpleperf/demo/SimpleperfExampleOfKotlin/gradle/wrapper/gradle-wrapper.properties
index 8727af9b..be54b8d6 100644
--- a/simpleperf/demo/SimpleperfExampleOfKotlin/gradle/wrapper/gradle-wrapper.properties
+++ b/simpleperf/demo/SimpleperfExampleOfKotlin/gradle/wrapper/gradle-wrapper.properties
@@ -1,6 +1,6 @@
-#Fri Oct 27 18:12:06 PDT 2017
+#Tue Apr 10 15:23:21 PDT 2018
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
-distributionUrl=https\://services.gradle.org/distributions/gradle-4.1-all.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-4.6-all.zip
diff --git a/simpleperf/demo/SimpleperfExamplePureJava/.idea/caches/build_file_checksums.ser b/simpleperf/demo/SimpleperfExamplePureJava/.idea/caches/build_file_checksums.ser
index 87541bde..db66eea2 100644
--- a/simpleperf/demo/SimpleperfExamplePureJava/.idea/caches/build_file_checksums.ser
+++ b/simpleperf/demo/SimpleperfExamplePureJava/.idea/caches/build_file_checksums.ser
Binary files differ
diff --git a/simpleperf/demo/SimpleperfExamplePureJava/.idea/codeStyles/Project.xml b/simpleperf/demo/SimpleperfExamplePureJava/.idea/codeStyles/Project.xml
new file mode 100644
index 00000000..30aa626c
--- /dev/null
+++ b/simpleperf/demo/SimpleperfExamplePureJava/.idea/codeStyles/Project.xml
@@ -0,0 +1,29 @@
+<component name="ProjectCodeStyleConfiguration">
+ <code_scheme name="Project" version="173">
+ <Objective-C-extensions>
+ <file>
+ <option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Import" />
+ <option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Macro" />
+ <option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Typedef" />
+ <option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Enum" />
+ <option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Constant" />
+ <option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Global" />
+ <option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Struct" />
+ <option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="FunctionPredecl" />
+ <option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Function" />
+ </file>
+ <class>
+ <option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Property" />
+ <option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Synthesize" />
+ <option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="InitMethod" />
+ <option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="StaticMethod" />
+ <option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="InstanceMethod" />
+ <option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="DeallocMethod" />
+ </class>
+ <extensions>
+ <pair source="cpp" header="h" fileNamingConvention="NONE" />
+ <pair source="c" header="h" fileNamingConvention="NONE" />
+ </extensions>
+ </Objective-C-extensions>
+ </code_scheme>
+</component> \ No newline at end of file
diff --git a/simpleperf/demo/SimpleperfExamplePureJava/app/build.gradle b/simpleperf/demo/SimpleperfExamplePureJava/app/build.gradle
index 02a9b3cf..17e3f143 100644
--- a/simpleperf/demo/SimpleperfExamplePureJava/app/build.gradle
+++ b/simpleperf/demo/SimpleperfExamplePureJava/app/build.gradle
@@ -4,7 +4,7 @@ apply from: 'profiling.gradle'
android {
compileSdkVersion 25
- buildToolsVersion "25.0.2"
+ buildToolsVersion '27.0.3'
defaultConfig {
applicationId "com.example.simpleperf.simpleperfexamplepurejava"
minSdkVersion 15
diff --git a/simpleperf/demo/SimpleperfExamplePureJava/app/build/outputs/apk/app-profiling.apk b/simpleperf/demo/SimpleperfExamplePureJava/app/build/outputs/apk/app-profiling.apk
deleted file mode 100644
index c933afe9..00000000
--- a/simpleperf/demo/SimpleperfExamplePureJava/app/build/outputs/apk/app-profiling.apk
+++ /dev/null
Binary files differ
diff --git a/simpleperf/demo/SimpleperfExamplePureJava/app/profiling.gradle b/simpleperf/demo/SimpleperfExamplePureJava/app/profiling.gradle
index 257adbaf..aa23c8d2 100644
--- a/simpleperf/demo/SimpleperfExamplePureJava/app/profiling.gradle
+++ b/simpleperf/demo/SimpleperfExamplePureJava/app/profiling.gradle
@@ -13,8 +13,6 @@ android {
}
}
packagingOptions {
- // Contain debug info in the libraries.
- doNotStrip "**.so"
// Exclude wrap.sh for architectures not built.
if (abiFiltersForWrapScript) {
diff --git a/simpleperf/demo/SimpleperfExamplePureJava/build.gradle b/simpleperf/demo/SimpleperfExamplePureJava/build.gradle
index b78a0b86..5af83b42 100644
--- a/simpleperf/demo/SimpleperfExamplePureJava/build.gradle
+++ b/simpleperf/demo/SimpleperfExamplePureJava/build.gradle
@@ -3,9 +3,10 @@
buildscript {
repositories {
jcenter()
+ google()
}
dependencies {
- classpath 'com.android.tools.build:gradle:2.3.1'
+ classpath 'com.android.tools.build:gradle:3.2.0-alpha09'
// NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files
@@ -14,6 +15,7 @@ buildscript {
allprojects {
repositories {
+ google()
jcenter()
}
}
diff --git a/simpleperf/demo/SimpleperfExamplePureJava/gradle/wrapper/gradle-wrapper.properties b/simpleperf/demo/SimpleperfExamplePureJava/gradle/wrapper/gradle-wrapper.properties
index 287d38f7..275de5d6 100644
--- a/simpleperf/demo/SimpleperfExamplePureJava/gradle/wrapper/gradle-wrapper.properties
+++ b/simpleperf/demo/SimpleperfExamplePureJava/gradle/wrapper/gradle-wrapper.properties
@@ -1,6 +1,6 @@
-#Wed Apr 26 19:57:50 PDT 2017
+#Tue Apr 10 14:48:08 PDT 2018
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
-distributionUrl=https\://services.gradle.org/distributions/gradle-3.3-all.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-4.6-all.zip
diff --git a/simpleperf/demo/SimpleperfExampleWithNative/.idea/caches/build_file_checksums.ser b/simpleperf/demo/SimpleperfExampleWithNative/.idea/caches/build_file_checksums.ser
index 61451ca8..aa013bdb 100644
--- a/simpleperf/demo/SimpleperfExampleWithNative/.idea/caches/build_file_checksums.ser
+++ b/simpleperf/demo/SimpleperfExampleWithNative/.idea/caches/build_file_checksums.ser
Binary files differ
diff --git a/simpleperf/demo/SimpleperfExampleWithNative/.idea/codeStyles/Project.xml b/simpleperf/demo/SimpleperfExampleWithNative/.idea/codeStyles/Project.xml
new file mode 100644
index 00000000..30aa626c
--- /dev/null
+++ b/simpleperf/demo/SimpleperfExampleWithNative/.idea/codeStyles/Project.xml
@@ -0,0 +1,29 @@
+<component name="ProjectCodeStyleConfiguration">
+ <code_scheme name="Project" version="173">
+ <Objective-C-extensions>
+ <file>
+ <option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Import" />
+ <option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Macro" />
+ <option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Typedef" />
+ <option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Enum" />
+ <option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Constant" />
+ <option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Global" />
+ <option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Struct" />
+ <option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="FunctionPredecl" />
+ <option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Function" />
+ </file>
+ <class>
+ <option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Property" />
+ <option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Synthesize" />
+ <option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="InitMethod" />
+ <option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="StaticMethod" />
+ <option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="InstanceMethod" />
+ <option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="DeallocMethod" />
+ </class>
+ <extensions>
+ <pair source="cpp" header="h" fileNamingConvention="NONE" />
+ <pair source="c" header="h" fileNamingConvention="NONE" />
+ </extensions>
+ </Objective-C-extensions>
+ </code_scheme>
+</component> \ No newline at end of file
diff --git a/simpleperf/demo/SimpleperfExampleWithNative/app/build/intermediates/cmake/profiling/obj/arm64-v8a/libnative-lib.so b/simpleperf/demo/SimpleperfExampleWithNative/app/build/intermediates/cmake/profiling/obj/arm64-v8a/libnative-lib.so
index 3cbbc2ac..f711fc8d 100755
--- a/simpleperf/demo/SimpleperfExampleWithNative/app/build/intermediates/cmake/profiling/obj/arm64-v8a/libnative-lib.so
+++ b/simpleperf/demo/SimpleperfExampleWithNative/app/build/intermediates/cmake/profiling/obj/arm64-v8a/libnative-lib.so
Binary files differ
diff --git a/simpleperf/demo/SimpleperfExampleWithNative/app/build/intermediates/cmake/profiling/obj/armeabi-v7a/libnative-lib.so b/simpleperf/demo/SimpleperfExampleWithNative/app/build/intermediates/cmake/profiling/obj/armeabi-v7a/libnative-lib.so
index 65592cfd..3af7b797 100755
--- a/simpleperf/demo/SimpleperfExampleWithNative/app/build/intermediates/cmake/profiling/obj/armeabi-v7a/libnative-lib.so
+++ b/simpleperf/demo/SimpleperfExampleWithNative/app/build/intermediates/cmake/profiling/obj/armeabi-v7a/libnative-lib.so
Binary files differ
diff --git a/simpleperf/demo/SimpleperfExampleWithNative/app/build/intermediates/cmake/profiling/obj/armeabi/libnative-lib.so b/simpleperf/demo/SimpleperfExampleWithNative/app/build/intermediates/cmake/profiling/obj/armeabi/libnative-lib.so
deleted file mode 100755
index 61842c9e..00000000
--- a/simpleperf/demo/SimpleperfExampleWithNative/app/build/intermediates/cmake/profiling/obj/armeabi/libnative-lib.so
+++ /dev/null
Binary files differ
diff --git a/simpleperf/demo/SimpleperfExampleWithNative/app/build/intermediates/cmake/profiling/obj/mips/libnative-lib.so b/simpleperf/demo/SimpleperfExampleWithNative/app/build/intermediates/cmake/profiling/obj/mips/libnative-lib.so
deleted file mode 100755
index 95cf41aa..00000000
--- a/simpleperf/demo/SimpleperfExampleWithNative/app/build/intermediates/cmake/profiling/obj/mips/libnative-lib.so
+++ /dev/null
Binary files differ
diff --git a/simpleperf/demo/SimpleperfExampleWithNative/app/build/intermediates/cmake/profiling/obj/mips64/libnative-lib.so b/simpleperf/demo/SimpleperfExampleWithNative/app/build/intermediates/cmake/profiling/obj/mips64/libnative-lib.so
deleted file mode 100755
index 8c210574..00000000
--- a/simpleperf/demo/SimpleperfExampleWithNative/app/build/intermediates/cmake/profiling/obj/mips64/libnative-lib.so
+++ /dev/null
Binary files differ
diff --git a/simpleperf/demo/SimpleperfExampleWithNative/app/build/intermediates/cmake/profiling/obj/x86/libnative-lib.so b/simpleperf/demo/SimpleperfExampleWithNative/app/build/intermediates/cmake/profiling/obj/x86/libnative-lib.so
index 58953c0b..eaa9a0b1 100755
--- a/simpleperf/demo/SimpleperfExampleWithNative/app/build/intermediates/cmake/profiling/obj/x86/libnative-lib.so
+++ b/simpleperf/demo/SimpleperfExampleWithNative/app/build/intermediates/cmake/profiling/obj/x86/libnative-lib.so
Binary files differ
diff --git a/simpleperf/demo/SimpleperfExampleWithNative/app/build/intermediates/cmake/profiling/obj/x86_64/libnative-lib.so b/simpleperf/demo/SimpleperfExampleWithNative/app/build/intermediates/cmake/profiling/obj/x86_64/libnative-lib.so
index 37b2b69e..5dcd0a0f 100755
--- a/simpleperf/demo/SimpleperfExampleWithNative/app/build/intermediates/cmake/profiling/obj/x86_64/libnative-lib.so
+++ b/simpleperf/demo/SimpleperfExampleWithNative/app/build/intermediates/cmake/profiling/obj/x86_64/libnative-lib.so
Binary files differ
diff --git a/simpleperf/demo/SimpleperfExampleWithNative/app/build/outputs/apk/profiling/app-profiling.apk b/simpleperf/demo/SimpleperfExampleWithNative/app/build/outputs/apk/profiling/app-profiling.apk
index 6d853690..21ffc64d 100644
--- a/simpleperf/demo/SimpleperfExampleWithNative/app/build/outputs/apk/profiling/app-profiling.apk
+++ b/simpleperf/demo/SimpleperfExampleWithNative/app/build/outputs/apk/profiling/app-profiling.apk
Binary files differ
diff --git a/simpleperf/demo/SimpleperfExampleWithNative/app/profiling.gradle b/simpleperf/demo/SimpleperfExampleWithNative/app/profiling.gradle
index 257adbaf..f4c05d19 100644
--- a/simpleperf/demo/SimpleperfExampleWithNative/app/profiling.gradle
+++ b/simpleperf/demo/SimpleperfExampleWithNative/app/profiling.gradle
@@ -13,9 +13,6 @@ android {
}
}
packagingOptions {
- // Contain debug info in the libraries.
- doNotStrip "**.so"
-
// Exclude wrap.sh for architectures not built.
if (abiFiltersForWrapScript) {
def exclude_abis = ["armeabi", "armeabi-v7a", "arm64-v8a",
diff --git a/simpleperf/demo/SimpleperfExampleWithNative/build.gradle b/simpleperf/demo/SimpleperfExampleWithNative/build.gradle
index 5e02be8d..82a28ae1 100644
--- a/simpleperf/demo/SimpleperfExampleWithNative/build.gradle
+++ b/simpleperf/demo/SimpleperfExampleWithNative/build.gradle
@@ -15,6 +15,7 @@ buildscript {
allprojects {
repositories {
+ google()
jcenter()
}
}
diff --git a/simpleperf/dso.cpp b/simpleperf/dso.cpp
index f3e9b2ce..396bb352 100644
--- a/simpleperf/dso.cpp
+++ b/simpleperf/dso.cpp
@@ -25,6 +25,7 @@
#include <android-base/file.h>
#include <android-base/logging.h>
+#include <android-base/strings.h>
#include "environment.h"
#include "read_apk.h"
@@ -32,6 +33,91 @@
#include "read_elf.h"
#include "utils.h"
+namespace simpleperf_dso_impl {
+
+void DebugElfFileFinder::Reset() {
+ vdso_64bit_.clear();
+ vdso_32bit_.clear();
+ symfs_dir_.clear();
+ build_id_to_file_map_.clear();
+}
+
+bool DebugElfFileFinder::SetSymFsDir(const std::string& symfs_dir) {
+ std::string dirname = symfs_dir;
+ if (!dirname.empty()) {
+ if (dirname.back() != '/') {
+ dirname.push_back('/');
+ }
+ if (!IsDir(symfs_dir)) {
+ LOG(ERROR) << "Invalid symfs_dir '" << symfs_dir << "'";
+ return false;
+ }
+ }
+ symfs_dir_ = dirname;
+ build_id_to_file_map_.clear();
+ std::string build_id_list_file = symfs_dir_ + "build_id_list";
+ std::string build_id_list;
+ if (android::base::ReadFileToString(build_id_list_file, &build_id_list)) {
+ for (auto& line : android::base::Split(build_id_list, "\n")) {
+ std::vector<std::string> items = android::base::Split(line, "=");
+ if (items.size() == 2u) {
+ build_id_to_file_map_[items[0]] = items[1];
+ }
+ }
+ }
+ return true;
+}
+
+void DebugElfFileFinder::SetVdsoFile(const std::string& vdso_file, bool is_64bit) {
+ if (is_64bit) {
+ vdso_64bit_ = vdso_file;
+ } else {
+ vdso_32bit_ = vdso_file;
+ }
+}
+
+std::string DebugElfFileFinder::FindDebugFile(const std::string& dso_path, bool force_64bit,
+ BuildId& build_id) {
+ if (dso_path == "[vdso]") {
+ if (force_64bit && !vdso_64bit_.empty()) {
+ return vdso_64bit_;
+ } else if (!force_64bit && !vdso_32bit_.empty()) {
+ return vdso_32bit_;
+ }
+ } else if (!symfs_dir_.empty()) {
+ if (!build_id.IsEmpty() || GetBuildIdFromDsoPath(dso_path, &build_id)) {
+ std::string result;
+ auto check_path = [&](const std::string& path) {
+ BuildId debug_build_id;
+ if (GetBuildIdFromDsoPath(path, &debug_build_id) && debug_build_id == build_id) {
+ result = path;
+ return true;
+ }
+ return false;
+ };
+
+ // 1. Try build_id_to_file_map.
+ auto it = build_id_to_file_map_.find(build_id.ToString());
+ if (it != build_id_to_file_map_.end()) {
+ if (check_path(symfs_dir_ + it->second)) {
+ return result;
+ }
+ }
+ // 2. Try concatenating symfs_dir and dso_path.
+ if (check_path(symfs_dir_ + dso_path)) {
+ return result;
+ }
+ // 3. Try concatenating /usr/lib/debug and dso_path.
+ // Linux host can store debug shared libraries in /usr/lib/debug.
+ if (check_path("/usr/lib/debug" + dso_path)) {
+ return result;
+ }
+ }
+ }
+ return dso_path;
+}
+} // namespace simpleperf_dso_imp
+
static OneTimeFreeAllocator symbol_name_allocator;
Symbol::Symbol(const std::string& name, uint64_t addr, uint64_t len)
@@ -55,15 +141,13 @@ const char* Symbol::DemangledName() const {
}
bool Dso::demangle_ = true;
-std::string Dso::symfs_dir_;
std::string Dso::vmlinux_;
std::string Dso::kallsyms_;
bool Dso::read_kernel_symbols_from_proc_;
std::unordered_map<std::string, BuildId> Dso::build_id_map_;
size_t Dso::dso_count_;
uint32_t Dso::g_dump_id_;
-std::string Dso::vdso_64bit_;
-std::string Dso::vdso_32bit_;
+simpleperf_dso_impl::DebugElfFileFinder Dso::debug_elf_file_finder_;
void Dso::SetDemangle(bool demangle) { demangle_ = demangle; }
@@ -96,18 +180,7 @@ std::string Dso::Demangle(const std::string& name) {
}
bool Dso::SetSymFsDir(const std::string& symfs_dir) {
- std::string dirname = symfs_dir;
- if (!dirname.empty()) {
- if (dirname.back() != '/') {
- dirname.push_back('/');
- }
- if (!IsDir(symfs_dir)) {
- LOG(ERROR) << "Invalid symfs_dir '" << symfs_dir << "'";
- return false;
- }
- }
- symfs_dir_ = dirname;
- return true;
+ return debug_elf_file_finder_.SetSymFsDir(symfs_dir);
}
void Dso::SetVmlinux(const std::string& vmlinux) { vmlinux_ = vmlinux; }
@@ -124,11 +197,7 @@ void Dso::SetBuildIds(
}
void Dso::SetVdsoFile(const std::string& vdso_file, bool is_64bit) {
- if (is_64bit) {
- vdso_64bit_ = vdso_file;
- } else {
- vdso_32bit_ = vdso_file;
- }
+ debug_elf_file_finder_.SetVdsoFile(vdso_file, is_64bit);
}
BuildId Dso::FindExpectedBuildIdForPath(const std::string& path) {
@@ -165,12 +234,12 @@ Dso::~Dso() {
// Clean up global variables when no longer used.
symbol_name_allocator.Clear();
demangle_ = true;
- symfs_dir_.clear();
vmlinux_.clear();
kallsyms_.clear();
read_kernel_symbols_from_proc_ = false;
build_id_map_.clear();
g_dump_id_ = 0;
+ debug_elf_file_finder_.Reset();
}
}
@@ -402,41 +471,18 @@ class KernelModuleDso : public Dso {
std::unique_ptr<Dso> Dso::CreateDso(DsoType dso_type, const std::string& dso_path,
bool force_64bit) {
- auto find_debug_file = [&]() {
- // Check if file matching path_ exists in symfs directory before using it as
- // debug_file_path_.
- if (!symfs_dir_.empty()) {
- std::string path_in_symfs = symfs_dir_ + dso_path;
- std::tuple<bool, std::string, std::string> tuple = SplitUrlInApk(path_in_symfs);
- std::string file_path = std::get<0>(tuple) ? std::get<1>(tuple) : path_in_symfs;
- if (IsRegularFile(file_path)) {
- return path_in_symfs;
- }
- } else if (dso_path == "[vdso]") {
- if (force_64bit && !vdso_64bit_.empty()) {
- return vdso_64bit_;
- } else if (!force_64bit && !vdso_32bit_.empty()) {
- return vdso_32bit_;
- }
- } else if (dso_type == DSO_ELF_FILE) {
- // Linux host can store debug shared libraries in /usr/lib/debug.
- std::string path = "/usr/lib/debug" + dso_path;
- if (IsRegularFile(path)) {
- return path;
- }
- }
- return dso_path;
- };
-
switch (dso_type) {
- case DSO_ELF_FILE:
- return std::unique_ptr<Dso>(new ElfDso(dso_path, find_debug_file()));
+ case DSO_ELF_FILE: {
+ BuildId build_id = FindExpectedBuildIdForPath(dso_path);
+ return std::unique_ptr<Dso>(new ElfDso(dso_path,
+ debug_elf_file_finder_.FindDebugFile(dso_path, force_64bit, build_id)));
+ }
case DSO_KERNEL:
return std::unique_ptr<Dso>(new KernelDso(dso_path, dso_path));
case DSO_KERNEL_MODULE:
- return std::unique_ptr<Dso>(new KernelModuleDso(dso_path, find_debug_file()));
+ return std::unique_ptr<Dso>(new KernelModuleDso(dso_path, dso_path));
case DSO_DEX_FILE:
- return std::unique_ptr<Dso>(new DexFileDso(dso_path, find_debug_file()));
+ return std::unique_ptr<Dso>(new DexFileDso(dso_path, dso_path));
default:
LOG(FATAL) << "Unexpected dso_type " << static_cast<int>(dso_type);
}
@@ -474,3 +520,14 @@ const char* DsoTypeToString(DsoType dso_type) {
return "unknown";
}
}
+
+bool GetBuildIdFromDsoPath(const std::string& dso_path, BuildId* build_id) {
+ auto tuple = SplitUrlInApk(dso_path);
+ ElfStatus result;
+ if (std::get<0>(tuple)) {
+ result = GetBuildIdFromApkFile(std::get<1>(tuple), std::get<2>(tuple), build_id);
+ } else {
+ result = GetBuildIdFromElfFile(dso_path, build_id);
+ }
+ return result == ElfStatus::NO_ERROR;
+}
diff --git a/simpleperf/dso.h b/simpleperf/dso.h
index 877e27ff..3b79faa3 100644
--- a/simpleperf/dso.h
+++ b/simpleperf/dso.h
@@ -28,6 +28,27 @@
#include "build_id.h"
#include "read_elf.h"
+
+namespace simpleperf_dso_impl {
+
+// Find elf files with symbol table and debug information.
+class DebugElfFileFinder {
+ public:
+ void Reset();
+ bool SetSymFsDir(const std::string& symfs_dir);
+ void SetVdsoFile(const std::string& vdso_file, bool is_64bit);
+ std::string FindDebugFile(const std::string& dso_path, bool force_64bit,
+ BuildId& build_id);
+
+ private:
+ std::string vdso_64bit_;
+ std::string vdso_32bit_;
+ std::string symfs_dir_;
+ std::unordered_map<std::string, std::string> build_id_to_file_map_;
+};
+
+} // namespace simpleperf_dso_impl
+
struct Symbol {
uint64_t addr;
// TODO: make len uint32_t.
@@ -147,15 +168,13 @@ class Dso {
protected:
static bool demangle_;
- static std::string symfs_dir_;
static std::string vmlinux_;
static std::string kallsyms_;
static bool read_kernel_symbols_from_proc_;
static std::unordered_map<std::string, BuildId> build_id_map_;
static size_t dso_count_;
static uint32_t g_dump_id_;
- static std::string vdso_64bit_;
- static std::string vdso_32bit_;
+ static simpleperf_dso_impl::DebugElfFileFinder debug_elf_file_finder_;
Dso(DsoType type, const std::string& path, const std::string& debug_file_path);
BuildId GetExpectedBuildId();
@@ -204,5 +223,6 @@ class DexFileDso : public Dso {
};
const char* DsoTypeToString(DsoType dso_type);
+bool GetBuildIdFromDsoPath(const std::string& dso_path, BuildId* build_id);
#endif // SIMPLE_PERF_DSO_H_
diff --git a/simpleperf/dso_test.cpp b/simpleperf/dso_test.cpp
new file mode 100644
index 00000000..f42b276f
--- /dev/null
+++ b/simpleperf/dso_test.cpp
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "dso.h"
+
+#include <gtest/gtest.h>
+
+#include <android-base/file.h>
+#include <android-base/stringprintf.h>
+#include <android-base/test_utils.h>
+
+#include "get_test_data.h"
+
+using namespace simpleperf_dso_impl;
+
+TEST(DebugElfFileFinder, use_build_id_list) {
+ // Create a temp symdir with build_id_list.
+ TemporaryDir tmpdir;
+ TemporaryFile tmpfile(tmpdir.path);
+ std::string data;
+ ASSERT_TRUE(android::base::ReadFileToString(GetTestData(ELF_FILE), &data));
+ ASSERT_TRUE(android::base::WriteStringToFile(data, tmpfile.path));
+ BuildId build_id(ELF_FILE_BUILD_ID);
+ std::string build_id_list = android::base::StringPrintf(
+ "%s=%s\n", build_id.ToString().c_str(), android::base::Basename(tmpfile.path).c_str());
+ std::string build_id_list_file = std::string(tmpdir.path) + "/build_id_list";
+ ASSERT_TRUE(android::base::WriteStringToFile(build_id_list, build_id_list_file));
+
+ DebugElfFileFinder finder;
+ ASSERT_TRUE(finder.SetSymFsDir(tmpdir.path));
+ ASSERT_EQ(finder.FindDebugFile("elf", false, build_id), std::string(tmpfile.path));
+ unlink(build_id_list_file.c_str());
+}
+
+TEST(DebugElfFileFinder, concatenating_symfs_dir) {
+ DebugElfFileFinder finder;
+ ASSERT_TRUE(finder.SetSymFsDir(GetTestDataDir()));
+ BuildId build_id(ELF_FILE_BUILD_ID);
+ ASSERT_EQ(finder.FindDebugFile(ELF_FILE, false, build_id), GetTestDataDir() + ELF_FILE);
+ std::string native_lib_in_apk = APK_FILE + "!/" + NATIVELIB_IN_APK;
+ ASSERT_EQ(finder.FindDebugFile(native_lib_in_apk, false, native_lib_build_id),
+ GetTestDataDir() + native_lib_in_apk);
+}
+
+TEST(DebugElfFileFinder, use_vdso) {
+ DebugElfFileFinder finder;
+ std::string fake_vdso32 = "fake_vdso32";
+ std::string fake_vdso64 = "fake_vdso64";
+ finder.SetVdsoFile(fake_vdso32, false);
+ finder.SetVdsoFile(fake_vdso64, true);
+ BuildId build_id;
+ ASSERT_EQ(finder.FindDebugFile("[vdso]", false, build_id), fake_vdso32);
+ ASSERT_EQ(finder.FindDebugFile("[vdso]", true, build_id), fake_vdso64);
+}
diff --git a/simpleperf/get_test_data.h b/simpleperf/get_test_data.h
index 46600df4..9e70c39a 100644
--- a/simpleperf/get_test_data.h
+++ b/simpleperf/get_test_data.h
@@ -26,6 +26,7 @@ const std::string& GetTestDataDir();
// The source code of elf and elf_with_mini_debug_info is testdata/elf_file_source.cpp.
static const std::string ELF_FILE = "elf";
+static const std::string ELF_FILE_BUILD_ID = "0b12a384a9f4a3f3659b7171ca615dbec3a81f71";
static const std::string ELF_FILE_WITH_MINI_DEBUG_INFO = "elf_with_mini_debug_info";
// perf.data is generated by sampling on three processes running different
// executables: elf, t1, t2 (all generated by elf_file_source.cpp, but with different
diff --git a/simpleperf/scripts/app_profiler.py b/simpleperf/scripts/app_profiler.py
index e2619008..e0ff7380 100644
--- a/simpleperf/scripts/app_profiler.py
+++ b/simpleperf/scripts/app_profiler.py
@@ -35,6 +35,119 @@ from binary_cache_builder import BinaryCacheBuilder
from simpleperf_report_lib import *
from utils import *
+NATIVE_LIBS_DIR_ON_DEVICE = '/data/local/tmp/native_libs/'
+
+class HostElfEntry(object):
+ """ Represent a native lib on host in NativeLibDownloader. """
+ def __init__(self, path, name, score):
+ self.path = path
+ self.name = name
+ self.score = score
+
+ def __repr__(self):
+ return self.__str__()
+
+ def __str__(self):
+ return '[path: %s, name %s, score %s]' % (self.path, self.name, self.score)
+
+
+class NativeLibDownloader(object):
+ """ Download native libs on device.
+
+ 1. Collect info of all native libs in the native_lib_dir on host.
+ 2. Check the available native libs in /data/local/tmp/native_libs on device.
+ 3. Sync native libs on device.
+ """
+ def __init__(self, ndk_path, device_arch, adb):
+ self.adb = adb
+ self.readelf = ReadElf(ndk_path)
+ self.need_archs = self._get_need_archs(device_arch)
+ self.host_build_id_map = {} # Map from build_id to HostElfEntry.
+ self.device_build_id_map = {} # Map from build_id to relative_path on device.
+ self.name_count_map = {} # Used to give a unique name for each library.
+ self.dir_on_device = NATIVE_LIBS_DIR_ON_DEVICE
+ self.build_id_list_file = 'build_id_list'
+
+ def _get_need_archs(self, device_arch):
+ """ Return the archs of binaries needed on device. """
+ if device_arch == 'arm64':
+ return ['arm', 'arm64']
+ if device_arch == 'arm':
+ return ['arm']
+ if device_arch == 'x86_64':
+ return ['x86', 'x86_64']
+ if device_arch == 'x86':
+ return ['x86']
+ return []
+
+ def collect_native_libs_on_host(self, native_lib_dir):
+ self.host_build_id_map.clear()
+ for root, _, files in os.walk(native_lib_dir):
+ for name in files:
+ if not name.endswith('.so'):
+ continue
+ self.add_native_lib_on_host(os.path.join(root, name), name)
+
+ def add_native_lib_on_host(self, path, name):
+ build_id = self.readelf.get_build_id(path)
+ if not build_id:
+ return
+ arch = self.readelf.get_arch(path)
+ if arch not in self.need_archs:
+ return
+ sections = self.readelf.get_sections(path)
+ score = 0
+ if '.debug_info' in sections:
+ score = 3
+ elif '.gnu_debugdata' in sections:
+ score = 2
+ elif '.symtab' in sections:
+ score = 1
+ entry = self.host_build_id_map.get(build_id)
+ if entry:
+ if entry.score < score:
+ entry.path = path
+ entry.score = score
+ else:
+ repeat_count = self.name_count_map.get(name, 0)
+ self.name_count_map[name] = repeat_count + 1
+ unique_name = name if repeat_count == 0 else name + '_' + str(repeat_count)
+ self.host_build_id_map[build_id] = HostElfEntry(path, unique_name, score)
+
+ def collect_native_libs_on_device(self):
+ self.device_build_id_map.clear()
+ self.adb.check_run(['shell', 'mkdir', '-p', self.dir_on_device])
+ if os.path.exists(self.build_id_list_file):
+ os.remove(self.build_id_list_file)
+ self.adb.run(['pull', self.dir_on_device + self.build_id_list_file])
+ if os.path.exists(self.build_id_list_file):
+ with open(self.build_id_list_file) as fh:
+ for line in fh.readlines():
+ line = line.strip()
+ items = line.split('=')
+ if len(items) == 2:
+ self.device_build_id_map[items[0]] = items[1]
+
+ def sync_natives_libs_on_device(self):
+ # Push missing native libs on device.
+ for build_id in self.host_build_id_map:
+ if build_id not in self.device_build_id_map:
+ entry = self.host_build_id_map[build_id]
+ self.adb.check_run(['push', entry.path, self.dir_on_device + entry.name])
+ # Remove native libs not exist on host.
+ for build_id in self.device_build_id_map:
+ if build_id not in self.host_build_id_map:
+ name = self.device_build_id_map[build_id]
+ self.adb.run(['shell', 'rm', self.dir_on_device + name])
+ # Push new build_id_list on device.
+ with open(self.build_id_list_file, 'w') as fh:
+ for build_id in self.host_build_id_map:
+ fh.write('%s=%s\n' % (build_id, self.host_build_id_map[build_id].name))
+ self.adb.check_run(['push', self.build_id_list_file,
+ self.dir_on_device + self.build_id_list_file])
+ os.remove(self.build_id_list_file)
+
+
class AppProfiler(object):
"""Used to manage the process of profiling an android app.
@@ -53,14 +166,14 @@ class AppProfiler(object):
self.app_arch = self.config['app_arch']
self.app_program = self.config['app_package_name'] or self.config['native_program']
self.app_pid = None
- self.has_symfs_on_device = False
self.record_subproc = None
def check_config(self, config):
config_names = ['app_package_name', 'native_program', 'cmd', 'native_lib_dir',
'apk_file_path', 'recompile_app', 'launch_activity', 'launch_inst_test',
- 'record_options', 'perf_data_path', 'profile_from_launch', 'app_arch']
+ 'record_options', 'perf_data_path', 'profile_from_launch', 'app_arch',
+ 'ndk_path']
for name in config_names:
if name not in config:
log_exit('config [%s] is missing' % name)
@@ -78,6 +191,8 @@ class AppProfiler(object):
native_lib_dir = config.get('native_lib_dir')
if native_lib_dir and not os.path.isdir(native_lib_dir):
log_exit('[native_lib_dir] "%s" is not a dir' % native_lib_dir)
+ if config.get('download_libs') and not native_lib_dir:
+ log_exit('-lib option should be set to download libraries on device.')
apk_file_path = config.get('apk_file_path')
if apk_file_path and not os.path.isfile(apk_file_path):
log_exit('[apk_file_path] "%s" is not a file' % apk_file_path)
@@ -106,13 +221,14 @@ class AppProfiler(object):
def prepare_profiling(self):
self._get_device_environment()
+ if self.config.get('download_libs'):
+ self._download_native_libs()
self._enable_profiling()
self._recompile_app()
self._restart_app()
self._get_app_environment()
if not self.config['profile_from_launch']:
self._download_simpleperf()
- self._download_native_libs()
def _get_device_environment(self):
@@ -123,6 +239,12 @@ class AppProfiler(object):
self.device_arch = self.adb.get_device_arch()
+ def _download_native_libs(self):
+ downloader = NativeLibDownloader(self.config['ndk_path'], self.device_arch, self.adb)
+ downloader.collect_native_libs_on_host(self.config['native_lib_dir'])
+ downloader.collect_native_libs_on_device()
+ downloader.sync_natives_libs_on_device()
+
def _enable_profiling(self):
self.adb.set_property('security.perf_harden', '0')
if self.is_root_device:
@@ -265,59 +387,6 @@ class AppProfiler(object):
self.adb.check_run(['shell', 'chmod', 'a+x', '/data/local/tmp/simpleperf'])
- def _download_native_libs(self):
- if not self.config['native_lib_dir'] or not self.config['app_package_name']:
- return
- filename_dict = dict()
- for root, _, files in os.walk(self.config['native_lib_dir']):
- for file in files:
- if not file.endswith('.so'):
- continue
- path = os.path.join(root, file)
- old_path = filename_dict.get(file)
- log_info('app_arch = %s' % self.app_arch)
- if self._is_lib_better(path, old_path):
- log_info('%s is better than %s' % (path, old_path))
- filename_dict[file] = path
- else:
- log_info('%s is worse than %s' % (path, old_path))
- maps = self.run_in_app_dir(['cat', '/proc/%d/maps' % self.app_pid], log_output=False)
- searched_lib = dict()
- for item in maps.split():
- if item.endswith('.so') and searched_lib.get(item) is None:
- searched_lib[item] = True
- # Use '/' as path separator as item comes from android environment.
- filename = item[item.rfind('/') + 1:]
- dirname = '/data/local/tmp/native_libs' + item[:item.rfind('/')]
- path = filename_dict.get(filename)
- if path is None:
- continue
- self.adb.check_run(['shell', 'mkdir', '-p', dirname])
- self.adb.check_run(['push', path, dirname])
- self.has_symfs_on_device = True
-
-
- def _is_lib_better(self, new_path, old_path):
- """ Return true if new_path is more likely to be used on device. """
- if old_path is None:
- return True
- if self.app_arch == 'arm':
- result1 = 'armeabi-v7a/' in new_path
- result2 = 'armeabi-v7a' in old_path
- if result1 != result2:
- return result1
- arch_dir = self.app_arch + '/'
- result1 = arch_dir in new_path
- result2 = arch_dir in old_path
- if result1 != result2:
- return result1
- result1 = 'obj/' in new_path
- result2 = 'obj/' in old_path
- if result1 != result2:
- return result1
- return False
-
-
def start_and_wait_profiling(self):
if self.record_subproc is None:
self.start_profiling()
@@ -348,8 +417,8 @@ class AppProfiler(object):
args += ['-p', str(self.app_pid)]
elif self.config['cmd']:
args.append(self.config['cmd'])
- if self.has_symfs_on_device:
- args += ['--symfs', '/data/local/tmp/native_libs']
+ if self.adb.run(['shell', 'ls', NATIVE_LIBS_DIR_ON_DEVICE]):
+ args += ['--symfs', NATIVE_LIBS_DIR_ON_DEVICE]
adb_args = [self.adb.adb_path, 'shell'] + args
log_debug('run adb cmd: %s' % adb_args)
self.record_subproc = subprocess.Popen(adb_args)
@@ -410,6 +479,8 @@ Like -np surfaceflinger.""")
"""Run a cmd and profile it. Like -cmd "pm -l".""")
parser.add_argument('-lib', '--native_lib_dir', help=
"""Path to find debug version of native shared libraries used in the app.""")
+ parser.add_argument('--download_libs', action='store_true', help= """Download native
+libraries in native_lib_dir on device.""")
parser.add_argument('-nc', '--skip_recompile', action='store_true', help=
"""When profiling an Android app, by default we recompile java bytecode to native instructions
to profile java code. It takes some time. You can skip it if the code has been compiled or you
@@ -443,12 +514,14 @@ But with --profile_from_launch option, we change the order as below: kill the ap
already running, download simpleperf on device, start simpleperf record, and start the app.""")
parser.add_argument('--disable_adb_root', action='store_true', help=
"""Force adb to run in non root mode.""")
+ parser.add_argument('--ndk_path', nargs=1, help='Find tools in the ndk path.')
args = parser.parse_args()
config = {}
config['app_package_name'] = args.app
config['native_program'] = args.native_program
config['cmd'] = args.cmd
config['native_lib_dir'] = args.native_lib_dir
+ config['download_libs'] = args.download_libs
config['recompile_app'] = args.app and not args.skip_recompile
config['apk_file_path'] = args.apk
@@ -461,6 +534,7 @@ already running, download simpleperf on device, start simpleperf record, and sta
config['collect_binaries'] = not args.skip_collect_binaries
config['profile_from_launch'] = args.profile_from_launch
config['disable_adb_root'] = args.disable_adb_root
+ config['ndk_path'] = None if not args.ndk_path else args.ndk_path[0]
profiler = AppProfiler(config)
profiler.profile()
diff --git a/simpleperf/scripts/binary_cache_builder.py b/simpleperf/scripts/binary_cache_builder.py
index e4b18ab3..deb312ad 100644
--- a/simpleperf/scripts/binary_cache_builder.py
+++ b/simpleperf/scripts/binary_cache_builder.py
@@ -36,7 +36,7 @@ from utils import *
class BinaryCacheBuilder(object):
"""Collect all binaries needed by perf.data in binary_cache."""
def __init__(self, config):
- config_names = ['perf_data_path', 'symfs_dirs']
+ config_names = ['perf_data_path', 'symfs_dirs', 'ndk_path']
for name in config_names:
if name not in config:
log_exit('config for "%s" is missing' % name)
@@ -49,9 +49,7 @@ class BinaryCacheBuilder(object):
if not os.path.isdir(symfs_dir):
log_exit("symfs_dir '%s' is not a directory" % symfs_dir)
self.adb = AdbHelper(enable_switch_to_root=not config['disable_adb_root'])
- self.readelf_path = find_tool_path('readelf')
- if not self.readelf_path and self.symfs_dirs:
- log_warning("Debug shared libraries on host are not used because can't find readelf.")
+ self.readelf = ReadElf(config.get('ndk_path'))
self.binary_cache_dir = 'binary_cache'
if not os.path.isdir(self.binary_cache_dir):
os.makedirs(self.binary_cache_dir)
@@ -180,27 +178,12 @@ class BinaryCacheBuilder(object):
def _read_build_id(self, file):
"""read build id of a binary on host."""
- if not self.readelf_path:
- return ""
- output = subprocess.check_output([self.readelf_path, '-n', file])
- output = bytes_to_str(output)
- result = re.search(r'Build ID:\s*(\S+)', output)
- if result:
- build_id = result.group(1)
- if len(build_id) < 40:
- build_id += '0' * (40 - len(build_id))
- build_id = '0x' + build_id
- return build_id
- return ""
+ return self.readelf.get_build_id(file)
def _file_has_symbol_table(self, file):
"""Test if an elf file has symbol table section."""
- if not self.readelf_path:
- return False
- output = subprocess.check_output([self.readelf_path, '-S', file])
- output = bytes_to_str(output)
- return '.symtab' in output
+ return '.symtab' in self.readelf.get_sections(file)
def _pull_file_from_device(self, device_path, host_path):
@@ -236,11 +219,13 @@ def main():
action='append')
parser.add_argument('--disable_adb_root', action='store_true', help=
"""Force adb to run in non root mode.""")
+ parser.add_argument('--ndk_path', nargs=1, help='Find tools in the ndk path.')
args = parser.parse_args()
config = {}
config['perf_data_path'] = args.perf_data_path
config['symfs_dirs'] = flatten_arg_list(args.native_lib_dir)
config['disable_adb_root'] = args.disable_adb_root
+ config['ndk_path'] = None if not args.ndk_path else args.ndk_path[0]
builder = BinaryCacheBuilder(config)
builder.build_binary_cache()
diff --git a/simpleperf/scripts/test.py b/simpleperf/scripts/test.py
index 0452c09b..cad5d703 100644
--- a/simpleperf/scripts/test.py
+++ b/simpleperf/scripts/test.py
@@ -44,6 +44,7 @@ import tempfile
import time
import unittest
+from app_profiler import NativeLibDownloader
from simpleperf_report_lib import ReportLib
from utils import *
@@ -137,10 +138,22 @@ class TestExampleBase(TestBase):
cls.adb.check_run(args)
cls.adb_root = adb_root
cls.compiled = False
+ cls.has_perf_data_for_report = False
def setUp(self):
if self.id().find('TraceOffCpu') != -1 and not is_trace_offcpu_supported():
self.skipTest('trace-offcpu is not supported on device')
+ cls = self.__class__
+ if not cls.has_perf_data_for_report:
+ cls.has_perf_data_for_report = True
+ self.run_app_profiler()
+ shutil.copy('perf.data', 'perf.data_for_report')
+ remove('binary_cache_for_report')
+ shutil.copytree('binary_cache', 'binary_cache_for_report')
+ else:
+ shutil.copy('perf.data_for_report', 'perf.data')
+ remove('binary_cache')
+ shutil.copytree('binary_cache_for_report', 'binary_cache')
@classmethod
def tearDownClass(cls):
@@ -153,6 +166,10 @@ class TestExampleBase(TestBase):
remove("perf.data")
remove("report.txt")
remove("pprof.profile")
+ if cls.has_perf_data_for_report:
+ cls.has_perf_data_for_report = False
+ remove('perf.data_for_report')
+ remove('binary_cache_for_report')
def run(self, result=None):
self.__class__.test_result = result
@@ -160,7 +177,7 @@ class TestExampleBase(TestBase):
def run_app_profiler(self, record_arg = "-g -f 1000 --duration 3 -e cpu-cycles:u",
build_binary_cache=True, skip_compile=False, start_activity=True,
- native_lib_dir=None, profile_from_launch=False, add_arch=False):
+ profile_from_launch=False, add_arch=False):
args = ["app_profiler.py", "--app", self.package_name, "--apk", self.apk_path,
"-r", record_arg, "-o", "perf.data"]
if not build_binary_cache:
@@ -169,8 +186,7 @@ class TestExampleBase(TestBase):
args.append("-nc")
if start_activity:
args += ["-a", self.activity_name]
- if native_lib_dir:
- args += ["-lib", native_lib_dir]
+ args += ["-lib", self.example_path, '--download_libs']
if profile_from_launch:
args.append("--profile_from_launch")
if add_arch:
@@ -264,7 +280,6 @@ class TestExampleBase(TestBase):
def common_test_report(self):
self.run_cmd(["report.py", "-h"])
- self.run_app_profiler(build_binary_cache=False)
self.run_cmd(["report.py"])
self.run_cmd(["report.py", "-i", "perf.data"])
self.run_cmd(["report.py", "-g"])
@@ -272,21 +287,15 @@ class TestExampleBase(TestBase):
def common_test_annotate(self):
self.run_cmd(["annotate.py", "-h"])
- self.run_app_profiler()
remove("annotated_files")
self.run_cmd(["annotate.py", "-s", self.example_path])
self.check_exist(dir="annotated_files")
def common_test_report_sample(self, check_strings):
self.run_cmd(["report_sample.py", "-h"])
- remove("binary_cache")
- self.run_app_profiler(build_binary_cache=False)
self.run_cmd(["report_sample.py"])
output = self.run_cmd(["report_sample.py", "perf.data"], return_output=True)
self.check_strings_in_content(output, check_strings)
- self.run_app_profiler(record_arg="-g -f 1000 --duration 3 -e cpu-cycles:u --no-dump-symbols")
- output = self.run_cmd(["report_sample.py", "--symfs", "binary_cache"], return_output=True)
- self.check_strings_in_content(output, check_strings)
def common_test_pprof_proto_generator(self, check_strings_with_lines,
check_strings_without_lines):
@@ -294,7 +303,6 @@ class TestExampleBase(TestBase):
log_info('Skip test for pprof_proto_generator because google.protobuf is missing')
return
self.run_cmd(["pprof_proto_generator.py", "-h"])
- self.run_app_profiler()
self.run_cmd(["pprof_proto_generator.py"])
remove("pprof.profile")
self.run_cmd(["pprof_proto_generator.py", "-i", "perf.data", "-o", "pprof.profile"])
@@ -325,13 +333,12 @@ class TestExampleBase(TestBase):
def common_test_report_html(self):
self.run_cmd(['report_html.py', '-h'])
- self.run_app_profiler(record_arg='-g -f 1000 --duration 3 -e task-clock:u')
self.run_cmd(['report_html.py'])
self.run_cmd(['report_html.py', '--add_source_code', '--source_dirs', 'testdata'])
self.run_cmd(['report_html.py', '--add_disassembly'])
# Test with multiple perf.data.
shutil.move('perf.data', 'perf2.data')
- self.run_app_profiler()
+ self.run_app_profiler(record_arg='-g -f 1000 --duration 3 -e task-clock:u')
self.run_cmd(['report_html.py', '-i', 'perf.data', 'perf2.data'])
remove('perf2.data')
@@ -505,8 +512,6 @@ class TestExampleWithNative(TestExampleBase):
def test_app_profiler(self):
self.common_test_app_profiler()
- remove("binary_cache")
- self.run_app_profiler(native_lib_dir=self.example_path)
def test_app_profiler_profile_from_launch(self):
self.run_app_profiler(profile_from_launch=True, add_arch=True, build_binary_cache=False)
@@ -564,8 +569,6 @@ class TestExampleWithNativeRoot(TestExampleBase):
def test_app_profiler(self):
self.common_test_app_profiler()
- remove("binary_cache")
- self.run_app_profiler(native_lib_dir=self.example_path)
class TestExampleWithNativeTraceOffCpu(TestExampleBase):
@@ -1019,6 +1022,85 @@ class TestTools(unittest.TestCase):
for item in dso_info['expected_items']:
self.assertTrue(item in disassemble_code)
+ def test_readelf(self):
+ test_map = {
+ '/simpleperf_runtest_two_functions_arm64': {
+ 'arch': 'arm64',
+ 'build_id': '0xe8ecb3916d989dbdc068345c30f0c24300000000',
+ 'sections': ['.interp', '.note.android.ident', '.note.gnu.build-id', '.dynsym',
+ '.dynstr', '.gnu.hash', '.gnu.version', '.gnu.version_r', '.rela.dyn',
+ '.rela.plt', '.plt', '.text', '.rodata', '.eh_frame', '.eh_frame_hdr',
+ '.preinit_array', '.init_array', '.fini_array', '.dynamic', '.got',
+ '.got.plt', '.data', '.bss', '.comment', '.debug_str', '.debug_loc',
+ '.debug_abbrev', '.debug_info', '.debug_ranges', '.debug_macinfo',
+ '.debug_pubnames', '.debug_pubtypes', '.debug_line',
+ '.note.gnu.gold-version', '.symtab', '.strtab', '.shstrtab'],
+ },
+ '/simpleperf_runtest_two_functions_arm': {
+ 'arch': 'arm',
+ 'build_id': '0x718f5b36c4148ee1bd3f51af89ed2be600000000',
+ },
+ '/simpleperf_runtest_two_functions_x86_64': {
+ 'arch': 'x86_64',
+ },
+ '/simpleperf_runtest_two_functions_x86': {
+ 'arch': 'x86',
+ }
+ }
+ readelf = ReadElf(None)
+ for dso_path in test_map:
+ dso_info = test_map[dso_path]
+ path = 'testdata' + dso_path
+ self.assertEqual(dso_info['arch'], readelf.get_arch(path))
+ if 'build_id' in dso_info:
+ self.assertEqual(dso_info['build_id'], readelf.get_build_id(path))
+ if 'sections' in dso_info:
+ self.assertEqual(dso_info['sections'], readelf.get_sections(path))
+ self.assertEqual(readelf.get_arch('not_exist_file'), 'unknown')
+ self.assertEqual(readelf.get_build_id('not_exist_file'), '')
+ self.assertEqual(readelf.get_sections('not_exist_file'), [])
+
+
+class TestNativeLibDownloader(unittest.TestCase):
+ def test_smoke(self):
+ self.adb = AdbHelper()
+ # Sync all native libs on device.
+ self.adb.run(['shell', 'rm', '-rf', '/data/local/tmp/native_libs'])
+ downloader = NativeLibDownloader(None, 'arm64', self.adb)
+ downloader.collect_native_libs_on_host(
+ os.path.join('testdata', 'SimpleperfExampleWithNative'))
+ self.assertEqual(len(downloader.host_build_id_map), 6)
+ for entry in downloader.host_build_id_map.values():
+ self.assertEqual(entry.score, 3)
+ downloader.collect_native_libs_on_device()
+ self.assertEqual(len(downloader.device_build_id_map), 0)
+
+ lib_list = downloader.host_build_id_map.items()
+ for sync_count in [4, 3, 5]:
+ build_id_map = {}
+ for i in range(sync_count):
+ build_id_map[lib_list[i][0]] = lib_list[i][1]
+ print('sync_count %d, build_id_map %s' % (sync_count, build_id_map))
+ downloader.host_build_id_map = build_id_map
+ downloader.sync_natives_libs_on_device()
+ downloader.collect_native_libs_on_device()
+ self.assertEqual(len(downloader.device_build_id_map), sync_count)
+ for i in range(len(lib_list)):
+ build_id = lib_list[i][0]
+ name = lib_list[i][1].name
+ if i < sync_count:
+ self.assertTrue(build_id in downloader.device_build_id_map)
+ self.assertEqual(name, downloader.device_build_id_map[build_id])
+ print('path = %s' % (downloader.dir_on_device + name))
+ self.assertTrue(self._is_lib_on_device(downloader.dir_on_device + name))
+ else:
+ self.assertTrue(build_id not in downloader.device_build_id_map)
+ self.assertFalse(self._is_lib_on_device(downloader.dir_on_device + name))
+ self.adb.run(['shell', 'rm', '-rf', '/data/local/tmp/native_libs'])
+
+ def _is_lib_on_device(self, path):
+ return self.adb.run(['shell', 'ls', path])
+
def main():
os.chdir(get_script_dir())
diff --git a/simpleperf/scripts/utils.py b/simpleperf/scripts/utils.py
index aa097b8f..b104a132 100644
--- a/simpleperf/scripts/utils.py
+++ b/simpleperf/scripts/utils.py
@@ -22,6 +22,7 @@ from __future__ import print_function
import logging
import os
import os.path
+import re
import shutil
import subprocess
import sys
@@ -369,21 +370,6 @@ def find_real_dso_path(dso_path_in_record_file, binary_cache_path):
return dso_path_in_record_file
return None
-def get_arch_of_dso_path(readelf_path, dso_path):
- try:
- output = subprocess.check_output([readelf_path, '-h', dso_path])
- if output.find('AArch64') != -1:
- return 'arm64'
- if output.find('ARM') != -1:
- return 'arm'
- if output.find('X86-64') != -1:
- return 'x86_64'
- if output.find('80386') != -1:
- return 'x86'
- except subprocess.CalledProcessError:
- pass
- return 'unknown'
-
class Addr2Nearestline(object):
""" Use addr2line to convert (dso_path, func_addr, addr) to (source_file, line) pairs.
@@ -433,9 +419,7 @@ class Addr2Nearestline(object):
self.addr2line_path = find_tool_path('addr2line', ndk_path)
if not self.addr2line_path:
log_exit("Can't find addr2line. Please set ndk path by --ndk-path option.")
- self.readelf_path = find_tool_path('readelf', ndk_path)
- if not self.readelf_path:
- log_exit("Can't find readelf. Please set ndk path by --ndk-path option.")
+ self.readelf = ReadElf(ndk_path)
self.dso_map = {} # map from dso_path to Dso.
self.binary_cache_path = binary_cache_path
# Saving file names for each addr takes a lot of memory. So we store file ids in Addr,
@@ -472,14 +456,10 @@ class Addr2Nearestline(object):
range(-addr_step * 5, -addr_step * 128 - 1, -addr_step))
def _check_debug_line_section(self, real_path):
- try:
- output = subprocess.check_output([self.readelf_path, '-S', real_path])
- return output.find('.debug_line') != -1
- except subprocess.CalledProcessError:
- return False
+ return '.debug_line' in self.readelf.get_sections(real_path)
def _get_addr_step(self, real_path):
- arch = get_arch_of_dso_path(self.readelf_path, real_path)
+ arch = self.readelf.get_arch(real_path)
if arch == 'arm64':
return 4
if arch == 'arm':
@@ -579,9 +559,7 @@ class Objdump(object):
def __init__(self, ndk_path, binary_cache_path):
self.ndk_path = ndk_path
self.binary_cache_path = binary_cache_path
- self.readelf_path = find_tool_path('readelf', ndk_path)
- if not self.readelf_path:
- log_exit("Can't find readelf. Please set ndk path by --ndk_path option.")
+ self.readelf = ReadElf(ndk_path)
self.objdump_paths = {}
def disassemble_code(self, dso_path, start_addr, addr_len):
@@ -594,7 +572,7 @@ class Objdump(object):
return None
# 2. Get path of objdump.
- arch = get_arch_of_dso_path(self.readelf_path, real_path)
+ arch = self.readelf.get_arch(real_path)
objdump_path = self.objdump_paths.get(arch)
if not objdump_path:
objdump_path = find_tool_path('objdump', self.ndk_path, arch)
@@ -628,4 +606,63 @@ class Objdump(object):
return result
+class ReadElf(object):
+ """ A wrapper of readelf. """
+ def __init__(self, ndk_path):
+ self.readelf_path = find_tool_path('readelf', ndk_path)
+ if not self.readelf_path:
+ log_exit("Can't find readelf. Please set ndk path by --ndk_path option.")
+
+ def get_arch(self, elf_file_path):
+ """ Get arch of an elf file. """
+ try:
+ output = subprocess.check_output([self.readelf_path, '-h', elf_file_path])
+ if output.find('AArch64') != -1:
+ return 'arm64'
+ if output.find('ARM') != -1:
+ return 'arm'
+ if output.find('X86-64') != -1:
+ return 'x86_64'
+ if output.find('80386') != -1:
+ return 'x86'
+ except subprocess.CalledProcessError:
+ pass
+ return 'unknown'
+
+ def get_build_id(self, elf_file_path):
+ """ Get build id of an elf file. """
+ try:
+ output = subprocess.check_output([self.readelf_path, '-n', elf_file_path])
+ output = bytes_to_str(output)
+ result = re.search(r'Build ID:\s*(\S+)', output)
+ if result:
+ build_id = result.group(1)
+ if len(build_id) < 40:
+ build_id += '0' * (40 - len(build_id))
+ else:
+ build_id = build_id[:40]
+ build_id = '0x' + build_id
+ return build_id
+ except subprocess.CalledProcessError:
+ pass
+ return ""
+
+ def get_sections(self, elf_file_path):
+ """ Get sections of an elf file. """
+ section_names = []
+ try:
+ output = subprocess.check_output([self.readelf_path, '-SW', elf_file_path])
+ output = bytes_to_str(output)
+ for line in output.split('\n'):
+ # Parse line like:" [ 1] .note.android.ident NOTE 0000000000400190 ...".
+ result = re.search(r'^\s+\[\s*\d+\]\s(.+?)\s', line)
+ if result:
+ section_name = result.group(1).strip()
+ if section_name:
+ section_names.append(section_name)
+ except subprocess.CalledProcessError:
+ pass
+ return section_names
+
+
logging.getLogger().setLevel(logging.DEBUG)