diff options
author | Yabin Cui <yabinc@google.com> | 2018-04-12 17:42:59 +0000 |
---|---|---|
committer | Gerrit Code Review <noreply-gerritcodereview@google.com> | 2018-04-12 17:42:59 +0000 |
commit | a6ee510913c53783875a5512ff0d00a26b31eaad (patch) | |
tree | ef5b40452a30b0ced61313d64bae177255835e3e | |
parent | 7f1b4927bfe7b2e816cd95445e47476cf8fffd50 (diff) | |
parent | 40b70ffb58d830edefad2c82a9d33e1c7ce91138 (diff) | |
download | extras-a6ee510913c53783875a5512ff0d00a26b31eaad.tar.gz |
Merge "simpleperf: Improve the way downloading native libs on device."
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 Binary files differnew file mode 100644 index 00000000..3da69c6b --- /dev/null +++ b/simpleperf/demo/SimpleperfExampleOfKotlin/.idea/caches/build_file_checksums.ser 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 Binary files differindex 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 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 Binary files differindex 87541bde..db66eea2 100644 --- a/simpleperf/demo/SimpleperfExamplePureJava/.idea/caches/build_file_checksums.ser +++ b/simpleperf/demo/SimpleperfExamplePureJava/.idea/caches/build_file_checksums.ser 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 Binary files differdeleted file mode 100644 index c933afe9..00000000 --- a/simpleperf/demo/SimpleperfExamplePureJava/app/build/outputs/apk/app-profiling.apk +++ /dev/null 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 Binary files differindex 61451ca8..aa013bdb 100644 --- a/simpleperf/demo/SimpleperfExampleWithNative/.idea/caches/build_file_checksums.ser +++ b/simpleperf/demo/SimpleperfExampleWithNative/.idea/caches/build_file_checksums.ser 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 Binary files differindex 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 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 Binary files differindex 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 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 Binary files differdeleted file mode 100755 index 61842c9e..00000000 --- a/simpleperf/demo/SimpleperfExampleWithNative/app/build/intermediates/cmake/profiling/obj/armeabi/libnative-lib.so +++ /dev/null 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 Binary files differdeleted file mode 100755 index 95cf41aa..00000000 --- a/simpleperf/demo/SimpleperfExampleWithNative/app/build/intermediates/cmake/profiling/obj/mips/libnative-lib.so +++ /dev/null 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 Binary files differdeleted file mode 100755 index 8c210574..00000000 --- a/simpleperf/demo/SimpleperfExampleWithNative/app/build/intermediates/cmake/profiling/obj/mips64/libnative-lib.so +++ /dev/null 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 Binary files differindex 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 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 Binary files differindex 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 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 Binary files differindex 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 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) |