From 9c94c545d61ee591f8173b555a8509e26cfd8eac Mon Sep 17 00:00:00 2001 From: CAI Kelun Date: Sun, 8 Dec 2019 22:36:34 +0800 Subject: [PATCH 01/46] Create a logo for xCrash. Good luck! --- README.md | 6 +- README.zh-CN.md | 6 +- doc/intro.png | Bin 64045 -> 59032 bytes doc/xcrash_logo.png | Bin 0 -> 17931 bytes doc/xcrash_logo.svg | 165 +++++++++++++++++++++++++++++++++++++++++++ doc/xcrash_logo2.png | Bin 0 -> 62120 bytes 6 files changed, 173 insertions(+), 4 deletions(-) create mode 100644 doc/xcrash_logo.png create mode 100644 doc/xcrash_logo.svg create mode 100644 doc/xcrash_logo2.png diff --git a/README.md b/README.md index 6c46590..0baa36b 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,5 @@ +

xCrash Logo

+ # xCrash ![](https://img.shields.io/badge/license-MIT-brightgreen.svg?style=flat) @@ -8,10 +10,10 @@ xCrash provides the Android app with the ability to capture java crash, native crash and ANR. No root permission or any system permissions are required. -xCrash can generate a tombstone file (similar format as Android system's tombstone file) in the directory you specified when the app process crashes or ANRs. -

intro

+xCrash can generate a tombstone file (similar format as Android system's tombstone file) in the directory you specified when the app process crashes or ANRs. + xCrash has been used in many Android apps (including iQIYI video) on different platforms (mobile, tablet, TV) of [iQIYI](http://www.iqiyi.com/) for many years. [README 中文版](README.zh-CN.md) diff --git a/README.zh-CN.md b/README.zh-CN.md index 39fdd62..8fded5d 100644 --- a/README.zh-CN.md +++ b/README.zh-CN.md @@ -1,3 +1,5 @@ +

xCrash Logo

+ # xCrash ![](https://img.shields.io/badge/license-MIT-brightgreen.svg?style=flat) @@ -8,10 +10,10 @@ xCrash 能为安卓 app 提供捕获 java 崩溃,native 崩溃和 ANR 的能力。不需要 root 权限或任何系统权限。 -xCrash 能在 app 进程崩溃或 ANR 时,在你指定的目录中生成一个 tombstone 文件(格式与安卓系统的 tombstone 文件类似)。 -

intro

+xCrash 能在 app 进程崩溃或 ANR 时,在你指定的目录中生成一个 tombstone 文件(格式与安卓系统的 tombstone 文件类似)。 + xCrash 已经在 [爱奇艺](http://www.iqiyi.com/) 的不同平台(手机,平板,电视)的很多安卓 app(包括爱奇艺视频)中被使用了很多年。 [README English Version](README.md) diff --git a/doc/intro.png b/doc/intro.png index b60bad04ab7fe7bba34c82ee0560030c88433f2f..36f561db2c93096dedc91acc87ae3caf082339e1 100644 GIT binary patch literal 59032 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
  • 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

    qq group

    diff --git a/README.zh-CN.md b/README.zh-CN.md index f7dfbc4..395a795 100644 --- a/README.zh-CN.md +++ b/README.zh-CN.md @@ -4,9 +4,9 @@ ![](https://img.shields.io/badge/license-MIT-brightgreen.svg?style=flat) ![](https://img.shields.io/badge/PRs-welcome-brightgreen.svg?style=flat) -![](https://img.shields.io/badge/release-2.4.9-red.svg?style=flat) -![](https://img.shields.io/badge/Android-4.0%20--%2010-blue.svg?style=flat) -![](https://img.shields.io/badge/arch-armeabi%20%7C%20armeabi--v7a%20%7C%20arm64--v8a%20%7C%20x86%20%7C%20x86__64-blue.svg?style=flat) +![](https://img.shields.io/badge/release-3.0.0-red.svg?style=flat) +![](https://img.shields.io/badge/Android-4.1%20--%2011-blue.svg?style=flat) +![](https://img.shields.io/badge/armeabi--v7a%20%7C%20arm64--v8a%20%7C%20x86%20%7C%20x86__64-blue.svg?style=flat) xCrash 能为安卓 app 提供捕获 java 崩溃,native 崩溃和 ANR 的能力。不需要 root 权限或任何系统权限。 @@ -21,8 +21,8 @@ xCrash 已经在 [爱奇艺](http://www.iqiyi.com/) 的不同平台(手机, ## 特征 -* 支持 Android 4.0 - 10(API level 14 - 29)。 -* 支持 armeabi,armeabi-v7a,arm64-v8a,x86 和 x86_64。 +* 支持 Android 4.1 - 11(API level 16 - 30)。 +* 支持 armeabi-v7a,arm64-v8a,x86 和 x86_64。 * 捕获 java 崩溃,native 崩溃和 ANR。 * 获取详细的进程、线程、内存、FD、网络统计信息。 * 通过正则表达式设置需要获取哪些线程的信息。 @@ -32,12 +32,10 @@ xCrash 已经在 [爱奇艺](http://www.iqiyi.com/) 的不同平台(手机, ## Tombstone 文件预览 * [java 崩溃](doc/tombstone_java.txt) -* [native 崩溃 (armeabi)](doc/tombstone_native_armeabi.txt) * [native 崩溃 (armeabi-v7a)](doc/tombstone_native_armeabi-v7a.txt) * [native 崩溃 (arm64-v8a)](doc/tombstone_native_arm64-v8a.txt) * [native 崩溃 (x86)](doc/tombstone_native_x86.txt) * [native 崩溃 (x86_64)](doc/tombstone_native_x86_64.txt) -* [ANR (armeabi)](doc/tombstone_anr_armeabi.txt) * [ANR (armeabi-v7a)](doc/tombstone_anr_armeabi-v7a.txt) * [ANR (arm64-v8a)](doc/tombstone_anr_arm64-v8a.txt) * [ANR (x86)](doc/tombstone_anr_x86.txt) @@ -64,7 +62,7 @@ xCrash 已经在 [爱奇艺](http://www.iqiyi.com/) 的不同平台(手机, ```Gradle dependencies { - implementation 'com.iqiyi.xcrash:xcrash-android-lib:2.4.9' + implementation 'com.iqiyi.xcrash:xcrash-android-lib:3.0.0' } ``` @@ -74,7 +72,7 @@ dependencies { android { defaultConfig { ndk { - abiFilters 'armeabi', 'armeabi-v7a', 'arm64-v8a', 'x86', 'x86_64' + abiFilters 'armeabi-v7a', 'arm64-v8a', 'x86', 'x86_64' } } } @@ -111,36 +109,23 @@ class MyCustomApplication : Application() { Tombstone 文件默认将被写入到 `Context#getFilesDir() + "/tombstones"` 目录。(通常在: `/data/data/PACKAGE_NAME/files/tombstones`) -在 [src/java/xcrash/xcrash_sample](src/java/xcrash/xcrash_sample) 文件夹中,有一个更实际和复杂的示例 app。 +在 [xcrash_sample](xcrash_sample) 文件夹中,有一个更实际和复杂的示例 app。 ## 构建 -如果你想编译 xCrash 的源码。请按以下步骤进行: - -#### 1. 下载 [Android NDK r20b](https://developer.android.com/ndk/downloads/revision_history.html),设置 PATH 环境变量。 - -#### 2. 编译和复制 native 库。 - -``` -cd ./src/native/ -./build.sh -./install.sh -``` - -#### 3. 编译 AAR 库。 +#### 编译 xCrash AAR 库: ``` -cd ./src/java/xcrash/ ./gradlew :xcrash_lib:build ``` ## 技术支持 -1. 查看 [xcrash-sample](src/java/xcrash/xcrash_sample)。 +1. 查看 [xcrash-sample](xcrash_sample)。 2. 在 [GitHub issues](https://github.com/iqiyi/xCrash/issues) 交流。 -3. 邮件: caikelun@gmail.com +3. 邮件: caikelun@gmail.com   xuqnqn@qq.com 4. QQ 群: 603635869。二维码:

    qq group

    diff --git a/src/java/xcrash/build.gradle b/build.gradle similarity index 85% rename from src/java/xcrash/build.gradle rename to build.gradle index 4a7056d..a03e50f 100644 --- a/src/java/xcrash/build.gradle +++ b/build.gradle @@ -4,7 +4,7 @@ buildscript { jcenter() } dependencies { - classpath 'com.android.tools.build:gradle:3.5.3' + classpath 'com.android.tools.build:gradle:4.1.0' classpath 'digital.wup:android-maven-publish:3.6.2' classpath 'com.jfrog.bintray.gradle:gradle-bintray-plugin:1.8.4' } @@ -27,10 +27,14 @@ ext { targetSdkVersion = 30 buildToolsVersion = '30.0.2' javaVersion = JavaVersion.VERSION_1_6 + ndkVersion = "21.3.6528147" + cmakeVersion = "3.10.2" + abiFilters = "armeabi-v7a,arm64-v8a,x86,x86_64" + useASAN = false POM_GROUP_ID = "com.iqiyi.xcrash" POM_ARTIFACT_ID = "xcrash-android-lib" - POM_VERSION_NAME = "2.5.9" + POM_VERSION_NAME = "3.0.0" POM_NAME = "xCrash Android Lib" POM_DESCRIPTION = "xCrash provides the Android app with the ability to capture java crash, native crash and ANR." diff --git a/src/java/xcrash/checkstyle.xml b/checkstyle.xml similarity index 100% rename from src/java/xcrash/checkstyle.xml rename to checkstyle.xml diff --git a/src/java/xcrash/gradle.properties b/gradle.properties similarity index 67% rename from src/java/xcrash/gradle.properties rename to gradle.properties index 743d692..c73d239 100644 --- a/src/java/xcrash/gradle.properties +++ b/gradle.properties @@ -11,3 +11,9 @@ org.gradle.jvmargs=-Xmx1536m # This option should only be used with decoupled projects. More details, visit # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects # org.gradle.parallel=true +# AndroidX package structure to make it clearer which packages are bundled with the +# Android operating system, and which are packaged with your app's APK +# https://developer.android.com/topic/libraries/support-library/androidx-rn +android.useAndroidX=true +# Automatically convert third-party libraries to use AndroidX +android.enableJetifier=true diff --git a/src/java/xcrash/gradle/check.gradle b/gradle/check.gradle similarity index 100% rename from src/java/xcrash/gradle/check.gradle rename to gradle/check.gradle diff --git a/src/java/xcrash/gradle/publish.gradle b/gradle/publish.gradle similarity index 100% rename from src/java/xcrash/gradle/publish.gradle rename to gradle/publish.gradle diff --git a/gradle/sanitizer.gradle b/gradle/sanitizer.gradle new file mode 100644 index 0000000..3eb4dc4 --- /dev/null +++ b/gradle/sanitizer.gradle @@ -0,0 +1,60 @@ +project.afterEvaluate { + + preBuild.doFirst { + if (rootProject.ext.useASAN) { + def ndkLibDir = android.ndkDirectory.absolutePath + "/toolchains/llvm/prebuilt/" + def ABIs = rootProject.ext.abiFilters.split(",") + + for (String abi : ABIs) { + def arch = abi + if (abi == 'armeabi-v7a') { + arch = "arm" + } else if (abi == "arm64-v8a") { + arch = "aarch64" + } else if (abi == "x86") { + arch = "i686" + } else if (abi == "x86_64") { + arch = "x86_64" + } + + // create ASAN wrap.sh + def resDir = new File("src/main/resources/lib/" + abi) + resDir.mkdirs() + def wrapFile = new File(resDir, "wrap.sh") + wrapFile.withWriter { writer -> + writer.write('#!/system/bin/sh\n') + writer.write('HERE="$(cd "$(dirname "$0")" && pwd)"\n') + writer.write('export ASAN_OPTIONS=log_to_syslog=false,allow_user_segv_handler=1,detect_stack_use_after_return=1,check_initialization_order=true,quarantine_size_mb=64,color=never,new_delete_type_mismatch=0,sleep_before_dying=5,use_sigaltstack=0\n') + writer.write("ASAN_LIB=\$(ls \$HERE/libclang_rt.asan-${arch}-android.so)\n") + writer.write('if [ -f "$HERE/libc++_shared.so" ]; then\n') + writer.write(' export LD_PRELOAD="$ASAN_LIB $HERE/libc++_shared.so"\n') + writer.write('else\n') + writer.write(' export LD_PRELOAD="$ASAN_LIB"\n') + writer.write('fi\n') + writer.write('"\$@"\n') + } + println "sanitizer: [${abi}] create wrap.sh: " + wrapFile.absolutePath + + // copy ASAN libs + def libDir = new File("src/main/jniLibs/" + abi) + libDir.mkdirs() + FileTree tree = fileTree(dir: ndkLibDir).include("**/libclang_rt.asan-${arch}-android.so") + tree.each { File file -> + copy { + from file + into libDir.absolutePath + } + println "sanitizer: [${abi}] copy lib: ${file.absolutePath} -> ${libDir.absolutePath}" + } + } + } else { + delete 'src/main/resources/' + delete 'src/main/jniLibs/' + } + } +} + +clean.doFirst { + delete 'src/main/resources/' + delete 'src/main/jniLibs/' +} diff --git a/src/java/xcrash/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar similarity index 100% rename from src/java/xcrash/gradle/wrapper/gradle-wrapper.jar rename to gradle/wrapper/gradle-wrapper.jar diff --git a/src/java/xcrash/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties similarity index 80% rename from src/java/xcrash/gradle/wrapper/gradle-wrapper.properties rename to gradle/wrapper/gradle-wrapper.properties index 75cc802..fc3b162 100644 --- a/src/java/xcrash/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ -#Thu Jun 20 18:09:11 CST 2019 +#Fri Oct 23 17:17:57 CST 2020 distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-5.4.1-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-6.5-all.zip diff --git a/src/java/xcrash/gradlew b/gradlew similarity index 100% rename from src/java/xcrash/gradlew rename to gradlew diff --git a/src/java/xcrash/gradlew.bat b/gradlew.bat similarity index 100% rename from src/java/xcrash/gradlew.bat rename to gradlew.bat diff --git a/src/java/xcrash/settings.gradle b/settings.gradle similarity index 100% rename from src/java/xcrash/settings.gradle rename to settings.gradle diff --git a/src/java/xcrash/.gitignore b/src/java/xcrash/.gitignore deleted file mode 100644 index fd45b12..0000000 --- a/src/java/xcrash/.gitignore +++ /dev/null @@ -1,11 +0,0 @@ -*.iml -.gradle -/local.properties -/.idea/caches/build_file_checksums.ser -/.idea/libraries -/.idea/modules.xml -/.idea/workspace.xml -.DS_Store -/build -/captures -.externalNativeBuild diff --git a/src/native/build.sh b/src/native/build.sh deleted file mode 100755 index b34a836..0000000 --- a/src/native/build.sh +++ /dev/null @@ -1,4 +0,0 @@ -#!/bin/bash - -ndk-build -C ./libxcrash/jni -ndk-build -C ./libxcrash_dumper/jni diff --git a/src/native/clean.sh b/src/native/clean.sh deleted file mode 100755 index 7e78205..0000000 --- a/src/native/clean.sh +++ /dev/null @@ -1,4 +0,0 @@ -#!/bin/bash - -ndk-build -C ./libxcrash/jni clean -ndk-build -C ./libxcrash_dumper/jni clean diff --git a/src/native/install.sh b/src/native/install.sh deleted file mode 100755 index 7e4bbea..0000000 --- a/src/native/install.sh +++ /dev/null @@ -1,31 +0,0 @@ -#!/bin/bash - -mkdir -p ../java/xcrash/xcrash_lib/src/main/jniLibs/armeabi -mkdir -p ../java/xcrash/xcrash_lib/src/main/jniLibs/armeabi-v7a -mkdir -p ../java/xcrash/xcrash_lib/src/main/jniLibs/arm64-v8a -mkdir -p ../java/xcrash/xcrash_lib/src/main/jniLibs/x86 -mkdir -p ../java/xcrash/xcrash_lib/src/main/jniLibs/x86_64 - -#cp -f ./libxcrash/libs/armeabi/libxcrash.so ../java/xcrash/xcrash_lib/src/main/jniLibs/armeabi/libxcrash.so -cp -f ./libxcrash/libs/armeabi-v7a/libxcrash.so ../java/xcrash/xcrash_lib/src/main/jniLibs/armeabi-v7a/libxcrash.so -cp -f ./libxcrash/libs/arm64-v8a/libxcrash.so ../java/xcrash/xcrash_lib/src/main/jniLibs/arm64-v8a/libxcrash.so -cp -f ./libxcrash/libs/x86/libxcrash.so ../java/xcrash/xcrash_lib/src/main/jniLibs/x86/libxcrash.so -cp -f ./libxcrash/libs/x86_64/libxcrash.so ../java/xcrash/xcrash_lib/src/main/jniLibs/x86_64/libxcrash.so - -#cp -f ./libxcrash_dumper/libs/armeabi/xcrash_dumper ../java/xcrash/xcrash_lib/src/main/jniLibs/armeabi/libxcrash_dumper.so -cp -f ./libxcrash_dumper/libs/armeabi-v7a/xcrash_dumper ../java/xcrash/xcrash_lib/src/main/jniLibs/armeabi-v7a/libxcrash_dumper.so -cp -f ./libxcrash_dumper/libs/arm64-v8a/xcrash_dumper ../java/xcrash/xcrash_lib/src/main/jniLibs/arm64-v8a/libxcrash_dumper.so -cp -f ./libxcrash_dumper/libs/x86/xcrash_dumper ../java/xcrash/xcrash_lib/src/main/jniLibs/x86/libxcrash_dumper.so -cp -f ./libxcrash_dumper/libs/x86_64/xcrash_dumper ../java/xcrash/xcrash_lib/src/main/jniLibs/x86_64/libxcrash_dumper.so - -#version=`strings ./libxcrash_dumper/obj/local/armeabi-v7a/xcrash_dumper | grep 'Tombstone maker' | awk -F "\'" '{print $2}' | awk '{print $2}'` -#folder=$version -#tarfile=v${version}.tar.gz -#rm -rf $folder $tarfile -#mkdir -p ./$folder/armeabi-v7a -#mkdir -p ./$folder/arm64-v8a -#cp -f ./libxcrash/obj/local/armeabi-v7a/libxcrash.so ./$folder/armeabi-v7a/libxcrash.so -#cp -f ./libxcrash/obj/local/arm64-v8a/libxcrash.so ./$folder/arm64-v8a/libxcrash.so -#cp -f ./libxcrash_dumper/obj/local/armeabi-v7a/xcrash_dumper ./$folder/armeabi-v7a/libxcrash_dumper.so -#cp -f ./libxcrash_dumper/obj/local/arm64-v8a/xcrash_dumper ./$folder/arm64-v8a/libxcrash_dumper.so -#tar cvzf $tarfile $folder > /dev/null 2>&1 diff --git a/src/native/libxcrash/jni/Android.mk b/src/native/libxcrash/jni/Android.mk deleted file mode 100644 index 3cef887..0000000 --- a/src/native/libxcrash/jni/Android.mk +++ /dev/null @@ -1,25 +0,0 @@ -LOCAL_PATH := $(call my-dir) - -include $(CLEAR_VARS) -LOCAL_MODULE := test -LOCAL_CFLAGS := -std=c11 -Weverything -Werror -O0 -flto -LOCAL_C_INCLUDES := $(LOCAL_PATH) $(LOCAL_PATH)/../../common -LOCAL_SRC_FILES := xc_test.c -include $(BUILD_STATIC_LIBRARY) - -include $(CLEAR_VARS) -LOCAL_MODULE := xcrash -LOCAL_CFLAGS := -std=c11 -Weverything -Werror -fvisibility=hidden -Oz -flto -LOCAL_LDFLAGS := -flto -LOCAL_LDLIBS := -ldl -llog -LOCAL_STATIC_LIBRARIES := test -LOCAL_C_INCLUDES := $(LOCAL_PATH) $(LOCAL_PATH)/../../common $(LOCAL_PATH)/../../libxcrash_dumper/jni -LOCAL_SRC_FILES := xc_jni.c \ - xc_common.c \ - xc_crash.c \ - xc_trace.c \ - xc_dl.c \ - xc_fallback.c \ - xc_util.c \ - $(wildcard $(LOCAL_PATH)/../../common/*.c) -include $(BUILD_SHARED_LIBRARY) diff --git a/src/native/libxcrash/jni/Application.mk b/src/native/libxcrash/jni/Application.mk deleted file mode 100644 index ea0dee5..0000000 --- a/src/native/libxcrash/jni/Application.mk +++ /dev/null @@ -1,2 +0,0 @@ -APP_ABI := armeabi-v7a arm64-v8a x86 x86_64 -APP_PLATFORM := android-16 diff --git a/src/native/libxcrash_dumper/jni/Android.mk b/src/native/libxcrash_dumper/jni/Android.mk deleted file mode 100644 index 41051fc..0000000 --- a/src/native/libxcrash_dumper/jni/Android.mk +++ /dev/null @@ -1,12 +0,0 @@ -LOCAL_PATH := $(call my-dir) - -include $(CLEAR_VARS) -LOCAL_MODULE := xcrash_dumper -LOCAL_CFLAGS := -std=c11 -Weverything -Werror -fvisibility=hidden -fPIE -Oz -flto -LOCAL_LDFLAGS := -pie -flto -LOCAL_LDLIBS := -ldl -llog -LOCAL_STATIC_LIBRARIES := lzma -LOCAL_C_INCLUDES := $(LOCAL_PATH) $(LOCAL_PATH)/../../common -LOCAL_SRC_FILES := $(wildcard $(LOCAL_PATH)/*.c) $(wildcard $(LOCAL_PATH)/../../common/*.c) -include $(BUILD_EXECUTABLE) -include $(LOCAL_PATH)/lzma/Android.mk diff --git a/src/native/libxcrash_dumper/jni/Application.mk b/src/native/libxcrash_dumper/jni/Application.mk deleted file mode 100644 index ea0dee5..0000000 --- a/src/native/libxcrash_dumper/jni/Application.mk +++ /dev/null @@ -1,2 +0,0 @@ -APP_ABI := armeabi-v7a arm64-v8a x86 x86_64 -APP_PLATFORM := android-16 diff --git a/src/native/libxcrash_dumper/jni/lzma/Android.mk b/src/native/libxcrash_dumper/jni/lzma/Android.mk deleted file mode 100644 index 2d8c5d4..0000000 --- a/src/native/libxcrash_dumper/jni/lzma/Android.mk +++ /dev/null @@ -1,38 +0,0 @@ -LOCAL_PATH := $(call my-dir) - -include $(CLEAR_VARS) -LOCAL_MODULE := lzma -LOCAL_CFLAGS := -std=c11 -Weverything -Werror \ - -Wno-enum-conversion \ - -Wno-reserved-id-macro \ - -Wno-undef \ - -Wno-missing-prototypes \ - -Wno-missing-variable-declarations \ - -Wno-cast-align \ - -Wno-sign-conversion \ - -Wno-assign-enum \ - -Wno-unused-macros \ - -Wno-padded \ - -Wno-cast-qual \ - -Wno-strict-prototypes \ - -fPIE \ - -Oz \ - -flto \ - -D_7ZIP_ST -LOCAL_C_INCLUDES := $(LOCAL_PATH) -LOCAL_EXPORT_C_INCLUDES := $(LOCAL_PATH) -LOCAL_SRC_FILES := 7zCrc.c \ - 7zCrcOpt.c \ - CpuArch.c \ - Bra.c \ - Bra86.c \ - BraIA64.c \ - Delta.c \ - Lzma2Dec.c \ - LzmaDec.c \ - Sha256.c \ - Xz.c \ - XzCrc64.c \ - XzCrc64Opt.c \ - XzDec.c -include $(BUILD_STATIC_LIBRARY) diff --git a/src/java/xcrash/xcrash_lib/.gitignore b/xcrash_lib/.gitignore similarity index 100% rename from src/java/xcrash/xcrash_lib/.gitignore rename to xcrash_lib/.gitignore diff --git a/src/java/xcrash/xcrash_lib/build.gradle b/xcrash_lib/build.gradle similarity index 59% rename from src/java/xcrash/xcrash_lib/build.gradle rename to xcrash_lib/build.gradle index 9e37287..f2405d1 100644 --- a/src/java/xcrash/xcrash_lib/build.gradle +++ b/xcrash_lib/build.gradle @@ -3,10 +3,26 @@ apply plugin: 'com.android.library' android { compileSdkVersion rootProject.ext.compileSdkVersion buildToolsVersion rootProject.ext.buildToolsVersion + ndkVersion rootProject.ext.ndkVersion defaultConfig { minSdkVersion rootProject.ext.minSdkVersion targetSdkVersion rootProject.ext.targetSdkVersion consumerProguardFiles 'proguard-rules.pro' + externalNativeBuild { + cmake { + abiFilters rootProject.ext.abiFilters.split(",") + if(rootProject.ext.useASAN) { + arguments "-DANDROID_ARM_MODE=arm" + arguments "-DUSEASAN=ON" + } + } + } + } + externalNativeBuild { + cmake { + path "src/main/cpp/CMakeLists.txt" + version rootProject.ext.cmakeVersion + } } compileOptions { sourceCompatibility rootProject.ext.javaVersion diff --git a/src/java/xcrash/xcrash_lib/proguard-rules.pro b/xcrash_lib/proguard-rules.pro similarity index 100% rename from src/java/xcrash/xcrash_lib/proguard-rules.pro rename to xcrash_lib/proguard-rules.pro diff --git a/src/java/xcrash/xcrash_lib/src/main/AndroidManifest.xml b/xcrash_lib/src/main/AndroidManifest.xml similarity index 100% rename from src/java/xcrash/xcrash_lib/src/main/AndroidManifest.xml rename to xcrash_lib/src/main/AndroidManifest.xml diff --git a/xcrash_lib/src/main/cpp/CMakeLists.txt b/xcrash_lib/src/main/cpp/CMakeLists.txt new file mode 100644 index 0000000..602a7dc --- /dev/null +++ b/xcrash_lib/src/main/cpp/CMakeLists.txt @@ -0,0 +1,145 @@ +cmake_minimum_required(VERSION 3.4.1) + +####################################### +# global +####################################### + +add_compile_options( + -std=c11 + -Weverything + -Werror) + +####################################### +# libxcrash.so +####################################### + +file(GLOB XCRASH_SRC + xcrash/*.c + common/*.c + dl/*.c) + +add_library(xcrash SHARED + ${XCRASH_SRC}) + +target_include_directories(xcrash PUBLIC + xcrash + xcrash_dumper + common + dl) + +target_link_libraries(xcrash + log + dl) + +if(USEASAN) + +target_compile_options(xcrash PUBLIC + -fsanitize=address + -fno-omit-frame-pointer) + +set_target_properties(xcrash PROPERTIES + LINK_FLAGS " \ + -fsanitize=address") + +else() + +target_compile_options(xcrash PUBLIC + -Oz + -flto + -ffunction-sections + -fdata-sections) + +set_target_properties(xcrash PROPERTIES + LINK_FLAGS " \ + -O3 \ + -flto \ + -Wl,--exclude-libs,ALL \ + -Wl,--gc-sections \ + -Wl,--version-script=${CMAKE_CURRENT_SOURCE_DIR}/xcrash.exports") + +endif() + +####################################### +# libxcrash_dumper.so +####################################### + +file(GLOB XCRASH_DUMPER_SRC + xcrash_dumper/*.c + common/*.c) + +set(LZME_SRC + lzma/7zCrc.c + lzma/7zCrcOpt.c + lzma/CpuArch.c + lzma/Bra.c + lzma/Bra86.c + lzma/BraIA64.c + lzma/Delta.c + lzma/Lzma2Dec.c + lzma/LzmaDec.c + lzma/Sha256.c + lzma/Xz.c + lzma/XzCrc64.c + lzma/XzCrc64Opt.c + lzma/XzDec.c) + +set_source_files_properties(${LZME_SRC} PROPERTIES + COMPILE_FLAGS " \ + -D_7ZIP_ST \ + -Wno-enum-conversion \ + -Wno-reserved-id-macro \ + -Wno-undef \ + -Wno-missing-prototypes \ + -Wno-missing-variable-declarations \ + -Wno-cast-align \ + -Wno-sign-conversion \ + -Wno-assign-enum \ + -Wno-unused-macros \ + -Wno-padded \ + -Wno-cast-qual \ + -Wno-strict-prototypes \ + -Wno-extra-semi-stmt") + +add_executable(xcrash_dumper + ${XCRASH_DUMPER_SRC} + ${LZME_SRC}) + +target_include_directories(xcrash_dumper PUBLIC + xcrash_dumper + common + lzma) + +target_link_libraries(xcrash_dumper + log + dl) + +if(USEASAN) + +target_compile_options(xcrash_dumper PUBLIC + -fsanitize=address + -fno-omit-frame-pointer) + +set_target_properties(xcrash_dumper PROPERTIES + LINK_FLAGS " \ + -fsanitize=address") + +else() + +target_compile_options(xcrash_dumper PUBLIC + -Oz + -flto + -ffunction-sections + -fdata-sections) + +set_target_properties(xcrash_dumper PROPERTIES + LINK_FLAGS " \ + -O3 \ + -flto \ + -Wl,--exclude-libs,ALL \ + -Wl,--gc-sections") + +endif() + +set_target_properties(xcrash_dumper PROPERTIES + PREFIX "lib" + SUFFIX ".so") diff --git a/src/native/common/queue.h b/xcrash_lib/src/main/cpp/common/queue.h similarity index 100% rename from src/native/common/queue.h rename to xcrash_lib/src/main/cpp/common/queue.h diff --git a/src/native/common/tree.h b/xcrash_lib/src/main/cpp/common/tree.h similarity index 100% rename from src/native/common/tree.h rename to xcrash_lib/src/main/cpp/common/tree.h diff --git a/src/native/common/xcc_b64.c b/xcrash_lib/src/main/cpp/common/xcc_b64.c similarity index 100% rename from src/native/common/xcc_b64.c rename to xcrash_lib/src/main/cpp/common/xcc_b64.c diff --git a/src/native/common/xcc_b64.h b/xcrash_lib/src/main/cpp/common/xcc_b64.h similarity index 100% rename from src/native/common/xcc_b64.h rename to xcrash_lib/src/main/cpp/common/xcc_b64.h diff --git a/src/native/common/xcc_errno.h b/xcrash_lib/src/main/cpp/common/xcc_errno.h similarity index 100% rename from src/native/common/xcc_errno.h rename to xcrash_lib/src/main/cpp/common/xcc_errno.h diff --git a/src/native/common/xcc_fmt.c b/xcrash_lib/src/main/cpp/common/xcc_fmt.c similarity index 100% rename from src/native/common/xcc_fmt.c rename to xcrash_lib/src/main/cpp/common/xcc_fmt.c diff --git a/src/native/common/xcc_fmt.h b/xcrash_lib/src/main/cpp/common/xcc_fmt.h similarity index 100% rename from src/native/common/xcc_fmt.h rename to xcrash_lib/src/main/cpp/common/xcc_fmt.h diff --git a/src/native/common/xcc_libc_support.c b/xcrash_lib/src/main/cpp/common/xcc_libc_support.c similarity index 100% rename from src/native/common/xcc_libc_support.c rename to xcrash_lib/src/main/cpp/common/xcc_libc_support.c diff --git a/src/native/common/xcc_libc_support.h b/xcrash_lib/src/main/cpp/common/xcc_libc_support.h similarity index 100% rename from src/native/common/xcc_libc_support.h rename to xcrash_lib/src/main/cpp/common/xcc_libc_support.h diff --git a/src/native/common/xcc_meminfo.c b/xcrash_lib/src/main/cpp/common/xcc_meminfo.c similarity index 100% rename from src/native/common/xcc_meminfo.c rename to xcrash_lib/src/main/cpp/common/xcc_meminfo.c diff --git a/src/native/common/xcc_meminfo.h b/xcrash_lib/src/main/cpp/common/xcc_meminfo.h similarity index 100% rename from src/native/common/xcc_meminfo.h rename to xcrash_lib/src/main/cpp/common/xcc_meminfo.h diff --git a/src/native/common/xcc_signal.c b/xcrash_lib/src/main/cpp/common/xcc_signal.c similarity index 100% rename from src/native/common/xcc_signal.c rename to xcrash_lib/src/main/cpp/common/xcc_signal.c diff --git a/src/native/common/xcc_signal.h b/xcrash_lib/src/main/cpp/common/xcc_signal.h similarity index 100% rename from src/native/common/xcc_signal.h rename to xcrash_lib/src/main/cpp/common/xcc_signal.h diff --git a/src/native/common/xcc_spot.h b/xcrash_lib/src/main/cpp/common/xcc_spot.h similarity index 100% rename from src/native/common/xcc_spot.h rename to xcrash_lib/src/main/cpp/common/xcc_spot.h diff --git a/src/native/common/xcc_unwind.c b/xcrash_lib/src/main/cpp/common/xcc_unwind.c similarity index 100% rename from src/native/common/xcc_unwind.c rename to xcrash_lib/src/main/cpp/common/xcc_unwind.c diff --git a/src/native/common/xcc_unwind.h b/xcrash_lib/src/main/cpp/common/xcc_unwind.h similarity index 100% rename from src/native/common/xcc_unwind.h rename to xcrash_lib/src/main/cpp/common/xcc_unwind.h diff --git a/src/native/common/xcc_unwind_clang.c b/xcrash_lib/src/main/cpp/common/xcc_unwind_clang.c similarity index 100% rename from src/native/common/xcc_unwind_clang.c rename to xcrash_lib/src/main/cpp/common/xcc_unwind_clang.c diff --git a/src/native/common/xcc_unwind_clang.h b/xcrash_lib/src/main/cpp/common/xcc_unwind_clang.h similarity index 100% rename from src/native/common/xcc_unwind_clang.h rename to xcrash_lib/src/main/cpp/common/xcc_unwind_clang.h diff --git a/src/native/common/xcc_unwind_libcorkscrew.c b/xcrash_lib/src/main/cpp/common/xcc_unwind_libcorkscrew.c similarity index 100% rename from src/native/common/xcc_unwind_libcorkscrew.c rename to xcrash_lib/src/main/cpp/common/xcc_unwind_libcorkscrew.c diff --git a/src/native/common/xcc_unwind_libcorkscrew.h b/xcrash_lib/src/main/cpp/common/xcc_unwind_libcorkscrew.h similarity index 100% rename from src/native/common/xcc_unwind_libcorkscrew.h rename to xcrash_lib/src/main/cpp/common/xcc_unwind_libcorkscrew.h diff --git a/src/native/common/xcc_unwind_libunwind.c b/xcrash_lib/src/main/cpp/common/xcc_unwind_libunwind.c similarity index 100% rename from src/native/common/xcc_unwind_libunwind.c rename to xcrash_lib/src/main/cpp/common/xcc_unwind_libunwind.c diff --git a/src/native/common/xcc_unwind_libunwind.h b/xcrash_lib/src/main/cpp/common/xcc_unwind_libunwind.h similarity index 100% rename from src/native/common/xcc_unwind_libunwind.h rename to xcrash_lib/src/main/cpp/common/xcc_unwind_libunwind.h diff --git a/src/native/common/xcc_util.c b/xcrash_lib/src/main/cpp/common/xcc_util.c similarity index 99% rename from src/native/common/xcc_util.c rename to xcrash_lib/src/main/cpp/common/xcc_util.c index 9cd8c00..e758c4c 100644 --- a/src/native/common/xcc_util.c +++ b/xcrash_lib/src/main/cpp/common/xcc_util.c @@ -660,7 +660,7 @@ int xcc_util_record_fds(int fd, pid_t pid) xcc_fmt_snprintf(path, sizeof(path), "/proc/%d/fd/%d", pid, fd_num); len = readlink(path, fd_path, sizeof(fd_path) - 1); if(len <= 0 || len > (ssize_t)(sizeof(fd_path) - 1)) - strncpy(path, "???", sizeof(path)); + strncpy(fd_path, "???", sizeof(fd_path)); else fd_path[len] = '\0'; diff --git a/src/native/common/xcc_util.h b/xcrash_lib/src/main/cpp/common/xcc_util.h similarity index 100% rename from src/native/common/xcc_util.h rename to xcrash_lib/src/main/cpp/common/xcc_util.h diff --git a/src/native/common/xcc_version.h b/xcrash_lib/src/main/cpp/common/xcc_version.h similarity index 96% rename from src/native/common/xcc_version.h rename to xcrash_lib/src/main/cpp/common/xcc_version.h index f5ce3ee..415be1c 100644 --- a/src/native/common/xcc_version.h +++ b/xcrash_lib/src/main/cpp/common/xcc_version.h @@ -24,6 +24,6 @@ #ifndef XCC_VERSION_H #define XCC_VERSION_H 1 -#define XCC_VERSION_STR "xCrash 2.5.9" +#define XCC_VERSION_STR "xCrash 3.0.0" #endif diff --git a/xcrash_lib/src/main/cpp/dl/README.md b/xcrash_lib/src/main/cpp/dl/README.md new file mode 100644 index 0000000..540b184 --- /dev/null +++ b/xcrash_lib/src/main/cpp/dl/README.md @@ -0,0 +1,117 @@ +# xCrash DL + +## Problem + +All the time, Android's DL series functions have some compatibility and ability issues: + +### `dl_iterate_phdr()` + +1. On arm32 arch, only available when Android >= 5.0. +2. It does not hold the linker's global lock in Android 5.x and 4.x, this may cause a crash during iterating. +3. In some Android 4.x and 5.x devices, it returns basename instead of full pathname. +4. In some Android 4.x and 5.x devices, it returns package name for `app_process` instead of `/system/bin/app_process32` or `/system/bin/app_process64`. +5. linker/linker64 is only included since Android 8.1. (AOSP has included linker/linker64 since 8.0, but a large number of devices from other manufacturers have included linker/linker64 since Android 8.1) + +### `dlopen()` & `dlsym()` + +1. Since Android 7.0, `dlopen()` and `dlsym()` cannot operate system libraries. (Although in most cases, we don’t really need to load the dynamic library from the disk, but just need to get the address of a function to call it.) +2. `dlsym()` can only obtain symbols in `.dynsym`, but we sometimes need to obtain internal symbols in `.symtab` and "`.symtab` in `.gnu_debugdata`". +3. `dlsym()` does not distinguish between functions and objects when searching for symbols. In ELF files with a lot of symbols, this will reduce search efficiency, especially for `.symtab`. + +## Solution + +xCrash DL tries to make up the above problems. Enjoy the code ~ + +In addition, the source code of this module (in the current directory) does not depend on any other source code of xCrash, so you can easily use them in your own projects. + +## Usage + +### `xc_dl_iterate()` + +```c +#define XC_DL_DEFAULT 0x00 +#define XC_DL_WITH_LINKER 0x01 + +typedef int (*xc_dl_iterate_cb_t)(struct dl_phdr_info *info, size_t size, void *arg); +int xc_dl_iterate(xc_dl_iterate_cb_t cb, void *cb_arg, int flags); +``` + +Similar to `dl_iterate_phdr()`. + +Difference from the original `dl_iterate_phdr()`, xCrach DL's `xc_dl_iterate()` has an additional "flags" parameter. If you confirm that you need to iterate to the linker/linker64, pass `XC_DL_WITH_LINKER` to the "flags" parameter, otherwise pass `XC_DL_DEFAULT`. + +The reason for this is that in Android < 8.1, we need to find the linker/linker64 from `/proc/self/maps`, which has additional overhead. + +### `xc_dl_open()` and `xc_dl_close()` + +```c +#define XC_DL_DYNSYM 0x01 +#define XC_DL_SYMTAB 0x02 +#define XC_DL_ALL (XC_DL_DYNSYM | XC_DL_SYMTAB) + +typedef struct xc_dl xc_dl_t; + +xc_dl_t *xc_dl_open(const char *pathname, int flags); +void xc_dl_close(xc_dl_t **self); +``` + +`xc_dl_close()` and `dlclose()` have the same usage. + +`xc_dl_open()` and `dlopen()` are similar. But you need to specify whether you need to "open" `.dynsym` (`XC_DL_DYNSYM`), `.symtab` (`XC_DL_SYMTAB`), or both (`XC_DL_ALL`) through the "flags" parameter. + +You can “open” ELF by basename or full pathname. However, Android has used the namespace mechanism since 8.0. If you use basename, you need to make sure that no duplicate ELF is loaded into the current process. `xc_dl_open()` will only return the first matching ELF. + +This is part of the `/proc/self/maps` of a certain process on Android 10: + +``` +...... +756fc2c000-756fc7c000 r--p 00000000 fd:03 2985 /system/lib64/vndk-sp-29/libc++.so +756fc7c000-756fcee000 --xp 00050000 fd:03 2985 /system/lib64/vndk-sp-29/libc++.so +756fcee000-756fcef000 rw-p 000c2000 fd:03 2985 /system/lib64/vndk-sp-29/libc++.so +756fcef000-756fcf7000 r--p 000c3000 fd:03 2985 /system/lib64/vndk-sp-29/libc++.so +...... +7571fdd000-757202d000 r--p 00000000 07:38 20 /apex/com.android.conscrypt/lib64/libc++.so +757202d000-757209f000 --xp 00050000 07:38 20 /apex/com.android.conscrypt/lib64/libc++.so +757209f000-75720a0000 rw-p 000c2000 07:38 20 /apex/com.android.conscrypt/lib64/libc++.so +75720a0000-75720a8000 r--p 000c3000 07:38 20 /apex/com.android.conscrypt/lib64/libc++.so +...... +760b9df000-760ba2f000 r--p 00000000 fd:03 2441 /system/lib64/libc++.so +760ba2f000-760baa1000 --xp 00050000 fd:03 2441 /system/lib64/libc++.so +760baa1000-760baa2000 rw-p 000c2000 fd:03 2441 /system/lib64/libc++.so +760baa2000-760baaa000 r--p 000c3000 fd:03 2441 /system/lib64/libc++.so +...... +756fb2f000-756fb31000 r--p 00000000 fd:03 2983 /system/lib64/vndk-sp-29/libbinderthreadstate.so +756fb31000-756fb33000 --xp 00002000 fd:03 2983 /system/lib64/vndk-sp-29/libbinderthreadstate.so +756fb33000-756fb34000 rw-p 00004000 fd:03 2983 /system/lib64/vndk-sp-29/libbinderthreadstate.so +756fb34000-756fb35000 r--p 00005000 fd:03 2983 /system/lib64/vndk-sp-29/libbinderthreadstate.so +...... +76090f9000-76090fb000 r--p 00000000 fd:03 2424 /system/lib64/libbinderthreadstate.so +76090fb000-76090fd000 --xp 00002000 fd:03 2424 /system/lib64/libbinderthreadstate.so +76090fd000-76090fe000 rw-p 00004000 fd:03 2424 /system/lib64/libbinderthreadstate.so +76090fe000-76090ff000 r--p 00005000 fd:03 2424 /system/lib64/libbinderthreadstate.so +...... +``` + +### `xc_dl_dynsym_func()` and `xc_dl_dynsym_object()` + +```c +void *xc_dl_dynsym_func(xc_dl_t *self, const char *sym_name); +void *xc_dl_dynsym_object(xc_dl_t *self, const char *sym_name); +``` + +Used to find functions and objects in `.dynsym`. + +### `xc_dl_symtab_func()` and `xc_dl_symtab_object()` + +```c +void *xc_dl_symtab_func(xc_dl_t *self, const char *sym_name); +void *xc_dl_symtab_object(xc_dl_t *self, const char *sym_name); +``` + +Used to find functions and objects in `.symtab` and "`.symtab` in `.gnu_debugdata`". + +## History + +xCrash 2.x contains a very rudimentary module "xc_dl" for searching system library symbols, which has many problems in performance and compatibility. xCrash uses it to search a few symbols from libart, libc and libc++. + +Later, some other projects began to use the "xc_dl" module alone, including in some performance-sensitive usage scenarios. At this time, we began to realize that we need to rewrite this module, and we need a better implementation. diff --git a/src/native/libxcrash/jni/xc_dl.c b/xcrash_lib/src/main/cpp/dl/xc_dl.c similarity index 100% rename from src/native/libxcrash/jni/xc_dl.c rename to xcrash_lib/src/main/cpp/dl/xc_dl.c diff --git a/src/native/libxcrash/jni/xc_dl.h b/xcrash_lib/src/main/cpp/dl/xc_dl.h similarity index 100% rename from src/native/libxcrash/jni/xc_dl.h rename to xcrash_lib/src/main/cpp/dl/xc_dl.h diff --git a/src/native/libxcrash_dumper/jni/lzma/7z.h b/xcrash_lib/src/main/cpp/lzma/7z.h similarity index 100% rename from src/native/libxcrash_dumper/jni/lzma/7z.h rename to xcrash_lib/src/main/cpp/lzma/7z.h diff --git a/src/native/libxcrash_dumper/jni/lzma/7zAlloc.c b/xcrash_lib/src/main/cpp/lzma/7zAlloc.c similarity index 100% rename from src/native/libxcrash_dumper/jni/lzma/7zAlloc.c rename to xcrash_lib/src/main/cpp/lzma/7zAlloc.c diff --git a/src/native/libxcrash_dumper/jni/lzma/7zAlloc.h b/xcrash_lib/src/main/cpp/lzma/7zAlloc.h similarity index 100% rename from src/native/libxcrash_dumper/jni/lzma/7zAlloc.h rename to xcrash_lib/src/main/cpp/lzma/7zAlloc.h diff --git a/src/native/libxcrash_dumper/jni/lzma/7zArcIn.c b/xcrash_lib/src/main/cpp/lzma/7zArcIn.c similarity index 100% rename from src/native/libxcrash_dumper/jni/lzma/7zArcIn.c rename to xcrash_lib/src/main/cpp/lzma/7zArcIn.c diff --git a/src/native/libxcrash_dumper/jni/lzma/7zBuf.c b/xcrash_lib/src/main/cpp/lzma/7zBuf.c similarity index 100% rename from src/native/libxcrash_dumper/jni/lzma/7zBuf.c rename to xcrash_lib/src/main/cpp/lzma/7zBuf.c diff --git a/src/native/libxcrash_dumper/jni/lzma/7zBuf.h b/xcrash_lib/src/main/cpp/lzma/7zBuf.h similarity index 100% rename from src/native/libxcrash_dumper/jni/lzma/7zBuf.h rename to xcrash_lib/src/main/cpp/lzma/7zBuf.h diff --git a/src/native/libxcrash_dumper/jni/lzma/7zBuf2.c b/xcrash_lib/src/main/cpp/lzma/7zBuf2.c similarity index 100% rename from src/native/libxcrash_dumper/jni/lzma/7zBuf2.c rename to xcrash_lib/src/main/cpp/lzma/7zBuf2.c diff --git a/src/native/libxcrash_dumper/jni/lzma/7zCrc.c b/xcrash_lib/src/main/cpp/lzma/7zCrc.c similarity index 100% rename from src/native/libxcrash_dumper/jni/lzma/7zCrc.c rename to xcrash_lib/src/main/cpp/lzma/7zCrc.c diff --git a/src/native/libxcrash_dumper/jni/lzma/7zCrc.h b/xcrash_lib/src/main/cpp/lzma/7zCrc.h similarity index 100% rename from src/native/libxcrash_dumper/jni/lzma/7zCrc.h rename to xcrash_lib/src/main/cpp/lzma/7zCrc.h diff --git a/src/native/libxcrash_dumper/jni/lzma/7zCrcOpt.c b/xcrash_lib/src/main/cpp/lzma/7zCrcOpt.c similarity index 100% rename from src/native/libxcrash_dumper/jni/lzma/7zCrcOpt.c rename to xcrash_lib/src/main/cpp/lzma/7zCrcOpt.c diff --git a/src/native/libxcrash_dumper/jni/lzma/7zDec.c b/xcrash_lib/src/main/cpp/lzma/7zDec.c similarity index 100% rename from src/native/libxcrash_dumper/jni/lzma/7zDec.c rename to xcrash_lib/src/main/cpp/lzma/7zDec.c diff --git a/src/native/libxcrash_dumper/jni/lzma/7zFile.c b/xcrash_lib/src/main/cpp/lzma/7zFile.c similarity index 100% rename from src/native/libxcrash_dumper/jni/lzma/7zFile.c rename to xcrash_lib/src/main/cpp/lzma/7zFile.c diff --git a/src/native/libxcrash_dumper/jni/lzma/7zFile.h b/xcrash_lib/src/main/cpp/lzma/7zFile.h similarity index 100% rename from src/native/libxcrash_dumper/jni/lzma/7zFile.h rename to xcrash_lib/src/main/cpp/lzma/7zFile.h diff --git a/src/native/libxcrash_dumper/jni/lzma/7zStream.c b/xcrash_lib/src/main/cpp/lzma/7zStream.c similarity index 100% rename from src/native/libxcrash_dumper/jni/lzma/7zStream.c rename to xcrash_lib/src/main/cpp/lzma/7zStream.c diff --git a/src/native/libxcrash_dumper/jni/lzma/7zTypes.h b/xcrash_lib/src/main/cpp/lzma/7zTypes.h similarity index 100% rename from src/native/libxcrash_dumper/jni/lzma/7zTypes.h rename to xcrash_lib/src/main/cpp/lzma/7zTypes.h diff --git a/src/native/libxcrash_dumper/jni/lzma/7zVersion.h b/xcrash_lib/src/main/cpp/lzma/7zVersion.h similarity index 100% rename from src/native/libxcrash_dumper/jni/lzma/7zVersion.h rename to xcrash_lib/src/main/cpp/lzma/7zVersion.h diff --git a/src/native/libxcrash_dumper/jni/lzma/7zVersion.rc b/xcrash_lib/src/main/cpp/lzma/7zVersion.rc similarity index 100% rename from src/native/libxcrash_dumper/jni/lzma/7zVersion.rc rename to xcrash_lib/src/main/cpp/lzma/7zVersion.rc diff --git a/src/native/libxcrash_dumper/jni/lzma/Aes.c b/xcrash_lib/src/main/cpp/lzma/Aes.c similarity index 100% rename from src/native/libxcrash_dumper/jni/lzma/Aes.c rename to xcrash_lib/src/main/cpp/lzma/Aes.c diff --git a/src/native/libxcrash_dumper/jni/lzma/Aes.h b/xcrash_lib/src/main/cpp/lzma/Aes.h similarity index 100% rename from src/native/libxcrash_dumper/jni/lzma/Aes.h rename to xcrash_lib/src/main/cpp/lzma/Aes.h diff --git a/src/native/libxcrash_dumper/jni/lzma/AesOpt.c b/xcrash_lib/src/main/cpp/lzma/AesOpt.c similarity index 100% rename from src/native/libxcrash_dumper/jni/lzma/AesOpt.c rename to xcrash_lib/src/main/cpp/lzma/AesOpt.c diff --git a/src/native/libxcrash_dumper/jni/lzma/Alloc.c b/xcrash_lib/src/main/cpp/lzma/Alloc.c similarity index 100% rename from src/native/libxcrash_dumper/jni/lzma/Alloc.c rename to xcrash_lib/src/main/cpp/lzma/Alloc.c diff --git a/src/native/libxcrash_dumper/jni/lzma/Alloc.h b/xcrash_lib/src/main/cpp/lzma/Alloc.h similarity index 100% rename from src/native/libxcrash_dumper/jni/lzma/Alloc.h rename to xcrash_lib/src/main/cpp/lzma/Alloc.h diff --git a/src/native/libxcrash_dumper/jni/lzma/Bcj2.c b/xcrash_lib/src/main/cpp/lzma/Bcj2.c similarity index 100% rename from src/native/libxcrash_dumper/jni/lzma/Bcj2.c rename to xcrash_lib/src/main/cpp/lzma/Bcj2.c diff --git a/src/native/libxcrash_dumper/jni/lzma/Bcj2.h b/xcrash_lib/src/main/cpp/lzma/Bcj2.h similarity index 100% rename from src/native/libxcrash_dumper/jni/lzma/Bcj2.h rename to xcrash_lib/src/main/cpp/lzma/Bcj2.h diff --git a/src/native/libxcrash_dumper/jni/lzma/Bcj2Enc.c b/xcrash_lib/src/main/cpp/lzma/Bcj2Enc.c similarity index 100% rename from src/native/libxcrash_dumper/jni/lzma/Bcj2Enc.c rename to xcrash_lib/src/main/cpp/lzma/Bcj2Enc.c diff --git a/src/native/libxcrash_dumper/jni/lzma/Bra.c b/xcrash_lib/src/main/cpp/lzma/Bra.c similarity index 100% rename from src/native/libxcrash_dumper/jni/lzma/Bra.c rename to xcrash_lib/src/main/cpp/lzma/Bra.c diff --git a/src/native/libxcrash_dumper/jni/lzma/Bra.h b/xcrash_lib/src/main/cpp/lzma/Bra.h similarity index 100% rename from src/native/libxcrash_dumper/jni/lzma/Bra.h rename to xcrash_lib/src/main/cpp/lzma/Bra.h diff --git a/src/native/libxcrash_dumper/jni/lzma/Bra86.c b/xcrash_lib/src/main/cpp/lzma/Bra86.c similarity index 100% rename from src/native/libxcrash_dumper/jni/lzma/Bra86.c rename to xcrash_lib/src/main/cpp/lzma/Bra86.c diff --git a/src/native/libxcrash_dumper/jni/lzma/BraIA64.c b/xcrash_lib/src/main/cpp/lzma/BraIA64.c similarity index 100% rename from src/native/libxcrash_dumper/jni/lzma/BraIA64.c rename to xcrash_lib/src/main/cpp/lzma/BraIA64.c diff --git a/src/native/libxcrash_dumper/jni/lzma/Compiler.h b/xcrash_lib/src/main/cpp/lzma/Compiler.h similarity index 100% rename from src/native/libxcrash_dumper/jni/lzma/Compiler.h rename to xcrash_lib/src/main/cpp/lzma/Compiler.h diff --git a/src/native/libxcrash_dumper/jni/lzma/CpuArch.c b/xcrash_lib/src/main/cpp/lzma/CpuArch.c similarity index 100% rename from src/native/libxcrash_dumper/jni/lzma/CpuArch.c rename to xcrash_lib/src/main/cpp/lzma/CpuArch.c diff --git a/src/native/libxcrash_dumper/jni/lzma/CpuArch.h b/xcrash_lib/src/main/cpp/lzma/CpuArch.h similarity index 100% rename from src/native/libxcrash_dumper/jni/lzma/CpuArch.h rename to xcrash_lib/src/main/cpp/lzma/CpuArch.h diff --git a/src/native/libxcrash_dumper/jni/lzma/Delta.c b/xcrash_lib/src/main/cpp/lzma/Delta.c similarity index 100% rename from src/native/libxcrash_dumper/jni/lzma/Delta.c rename to xcrash_lib/src/main/cpp/lzma/Delta.c diff --git a/src/native/libxcrash_dumper/jni/lzma/Delta.h b/xcrash_lib/src/main/cpp/lzma/Delta.h similarity index 100% rename from src/native/libxcrash_dumper/jni/lzma/Delta.h rename to xcrash_lib/src/main/cpp/lzma/Delta.h diff --git a/src/native/libxcrash_dumper/jni/lzma/DllSecur.c b/xcrash_lib/src/main/cpp/lzma/DllSecur.c similarity index 100% rename from src/native/libxcrash_dumper/jni/lzma/DllSecur.c rename to xcrash_lib/src/main/cpp/lzma/DllSecur.c diff --git a/src/native/libxcrash_dumper/jni/lzma/DllSecur.h b/xcrash_lib/src/main/cpp/lzma/DllSecur.h similarity index 100% rename from src/native/libxcrash_dumper/jni/lzma/DllSecur.h rename to xcrash_lib/src/main/cpp/lzma/DllSecur.h diff --git a/src/native/libxcrash_dumper/jni/lzma/LICENSE b/xcrash_lib/src/main/cpp/lzma/LICENSE similarity index 100% rename from src/native/libxcrash_dumper/jni/lzma/LICENSE rename to xcrash_lib/src/main/cpp/lzma/LICENSE diff --git a/src/native/libxcrash_dumper/jni/lzma/LzFind.c b/xcrash_lib/src/main/cpp/lzma/LzFind.c similarity index 100% rename from src/native/libxcrash_dumper/jni/lzma/LzFind.c rename to xcrash_lib/src/main/cpp/lzma/LzFind.c diff --git a/src/native/libxcrash_dumper/jni/lzma/LzFind.h b/xcrash_lib/src/main/cpp/lzma/LzFind.h similarity index 100% rename from src/native/libxcrash_dumper/jni/lzma/LzFind.h rename to xcrash_lib/src/main/cpp/lzma/LzFind.h diff --git a/src/native/libxcrash_dumper/jni/lzma/LzFindMt.c b/xcrash_lib/src/main/cpp/lzma/LzFindMt.c similarity index 100% rename from src/native/libxcrash_dumper/jni/lzma/LzFindMt.c rename to xcrash_lib/src/main/cpp/lzma/LzFindMt.c diff --git a/src/native/libxcrash_dumper/jni/lzma/LzFindMt.h b/xcrash_lib/src/main/cpp/lzma/LzFindMt.h similarity index 100% rename from src/native/libxcrash_dumper/jni/lzma/LzFindMt.h rename to xcrash_lib/src/main/cpp/lzma/LzFindMt.h diff --git a/src/native/libxcrash_dumper/jni/lzma/LzHash.h b/xcrash_lib/src/main/cpp/lzma/LzHash.h similarity index 100% rename from src/native/libxcrash_dumper/jni/lzma/LzHash.h rename to xcrash_lib/src/main/cpp/lzma/LzHash.h diff --git a/src/native/libxcrash_dumper/jni/lzma/Lzma2Dec.c b/xcrash_lib/src/main/cpp/lzma/Lzma2Dec.c similarity index 100% rename from src/native/libxcrash_dumper/jni/lzma/Lzma2Dec.c rename to xcrash_lib/src/main/cpp/lzma/Lzma2Dec.c diff --git a/src/native/libxcrash_dumper/jni/lzma/Lzma2Dec.h b/xcrash_lib/src/main/cpp/lzma/Lzma2Dec.h similarity index 100% rename from src/native/libxcrash_dumper/jni/lzma/Lzma2Dec.h rename to xcrash_lib/src/main/cpp/lzma/Lzma2Dec.h diff --git a/src/native/libxcrash_dumper/jni/lzma/Lzma2DecMt.c b/xcrash_lib/src/main/cpp/lzma/Lzma2DecMt.c similarity index 100% rename from src/native/libxcrash_dumper/jni/lzma/Lzma2DecMt.c rename to xcrash_lib/src/main/cpp/lzma/Lzma2DecMt.c diff --git a/src/native/libxcrash_dumper/jni/lzma/Lzma2DecMt.h b/xcrash_lib/src/main/cpp/lzma/Lzma2DecMt.h similarity index 100% rename from src/native/libxcrash_dumper/jni/lzma/Lzma2DecMt.h rename to xcrash_lib/src/main/cpp/lzma/Lzma2DecMt.h diff --git a/src/native/libxcrash_dumper/jni/lzma/Lzma2Enc.c b/xcrash_lib/src/main/cpp/lzma/Lzma2Enc.c similarity index 100% rename from src/native/libxcrash_dumper/jni/lzma/Lzma2Enc.c rename to xcrash_lib/src/main/cpp/lzma/Lzma2Enc.c diff --git a/src/native/libxcrash_dumper/jni/lzma/Lzma2Enc.h b/xcrash_lib/src/main/cpp/lzma/Lzma2Enc.h similarity index 100% rename from src/native/libxcrash_dumper/jni/lzma/Lzma2Enc.h rename to xcrash_lib/src/main/cpp/lzma/Lzma2Enc.h diff --git a/src/native/libxcrash_dumper/jni/lzma/Lzma86.h b/xcrash_lib/src/main/cpp/lzma/Lzma86.h similarity index 100% rename from src/native/libxcrash_dumper/jni/lzma/Lzma86.h rename to xcrash_lib/src/main/cpp/lzma/Lzma86.h diff --git a/src/native/libxcrash_dumper/jni/lzma/Lzma86Dec.c b/xcrash_lib/src/main/cpp/lzma/Lzma86Dec.c similarity index 100% rename from src/native/libxcrash_dumper/jni/lzma/Lzma86Dec.c rename to xcrash_lib/src/main/cpp/lzma/Lzma86Dec.c diff --git a/src/native/libxcrash_dumper/jni/lzma/Lzma86Enc.c b/xcrash_lib/src/main/cpp/lzma/Lzma86Enc.c similarity index 100% rename from src/native/libxcrash_dumper/jni/lzma/Lzma86Enc.c rename to xcrash_lib/src/main/cpp/lzma/Lzma86Enc.c diff --git a/src/native/libxcrash_dumper/jni/lzma/LzmaDec.c b/xcrash_lib/src/main/cpp/lzma/LzmaDec.c similarity index 100% rename from src/native/libxcrash_dumper/jni/lzma/LzmaDec.c rename to xcrash_lib/src/main/cpp/lzma/LzmaDec.c diff --git a/src/native/libxcrash_dumper/jni/lzma/LzmaDec.h b/xcrash_lib/src/main/cpp/lzma/LzmaDec.h similarity index 100% rename from src/native/libxcrash_dumper/jni/lzma/LzmaDec.h rename to xcrash_lib/src/main/cpp/lzma/LzmaDec.h diff --git a/src/native/libxcrash_dumper/jni/lzma/LzmaEnc.c b/xcrash_lib/src/main/cpp/lzma/LzmaEnc.c similarity index 100% rename from src/native/libxcrash_dumper/jni/lzma/LzmaEnc.c rename to xcrash_lib/src/main/cpp/lzma/LzmaEnc.c diff --git a/src/native/libxcrash_dumper/jni/lzma/LzmaEnc.h b/xcrash_lib/src/main/cpp/lzma/LzmaEnc.h similarity index 100% rename from src/native/libxcrash_dumper/jni/lzma/LzmaEnc.h rename to xcrash_lib/src/main/cpp/lzma/LzmaEnc.h diff --git a/src/native/libxcrash_dumper/jni/lzma/LzmaLib.c b/xcrash_lib/src/main/cpp/lzma/LzmaLib.c similarity index 100% rename from src/native/libxcrash_dumper/jni/lzma/LzmaLib.c rename to xcrash_lib/src/main/cpp/lzma/LzmaLib.c diff --git a/src/native/libxcrash_dumper/jni/lzma/LzmaLib.h b/xcrash_lib/src/main/cpp/lzma/LzmaLib.h similarity index 100% rename from src/native/libxcrash_dumper/jni/lzma/LzmaLib.h rename to xcrash_lib/src/main/cpp/lzma/LzmaLib.h diff --git a/src/native/libxcrash_dumper/jni/lzma/MtCoder.c b/xcrash_lib/src/main/cpp/lzma/MtCoder.c similarity index 100% rename from src/native/libxcrash_dumper/jni/lzma/MtCoder.c rename to xcrash_lib/src/main/cpp/lzma/MtCoder.c diff --git a/src/native/libxcrash_dumper/jni/lzma/MtCoder.h b/xcrash_lib/src/main/cpp/lzma/MtCoder.h similarity index 100% rename from src/native/libxcrash_dumper/jni/lzma/MtCoder.h rename to xcrash_lib/src/main/cpp/lzma/MtCoder.h diff --git a/src/native/libxcrash_dumper/jni/lzma/MtDec.c b/xcrash_lib/src/main/cpp/lzma/MtDec.c similarity index 100% rename from src/native/libxcrash_dumper/jni/lzma/MtDec.c rename to xcrash_lib/src/main/cpp/lzma/MtDec.c diff --git a/src/native/libxcrash_dumper/jni/lzma/MtDec.h b/xcrash_lib/src/main/cpp/lzma/MtDec.h similarity index 100% rename from src/native/libxcrash_dumper/jni/lzma/MtDec.h rename to xcrash_lib/src/main/cpp/lzma/MtDec.h diff --git a/src/native/libxcrash_dumper/jni/lzma/Ppmd.h b/xcrash_lib/src/main/cpp/lzma/Ppmd.h similarity index 100% rename from src/native/libxcrash_dumper/jni/lzma/Ppmd.h rename to xcrash_lib/src/main/cpp/lzma/Ppmd.h diff --git a/src/native/libxcrash_dumper/jni/lzma/Ppmd7.c b/xcrash_lib/src/main/cpp/lzma/Ppmd7.c similarity index 100% rename from src/native/libxcrash_dumper/jni/lzma/Ppmd7.c rename to xcrash_lib/src/main/cpp/lzma/Ppmd7.c diff --git a/src/native/libxcrash_dumper/jni/lzma/Ppmd7.h b/xcrash_lib/src/main/cpp/lzma/Ppmd7.h similarity index 100% rename from src/native/libxcrash_dumper/jni/lzma/Ppmd7.h rename to xcrash_lib/src/main/cpp/lzma/Ppmd7.h diff --git a/src/native/libxcrash_dumper/jni/lzma/Ppmd7Dec.c b/xcrash_lib/src/main/cpp/lzma/Ppmd7Dec.c similarity index 100% rename from src/native/libxcrash_dumper/jni/lzma/Ppmd7Dec.c rename to xcrash_lib/src/main/cpp/lzma/Ppmd7Dec.c diff --git a/src/native/libxcrash_dumper/jni/lzma/Ppmd7Enc.c b/xcrash_lib/src/main/cpp/lzma/Ppmd7Enc.c similarity index 100% rename from src/native/libxcrash_dumper/jni/lzma/Ppmd7Enc.c rename to xcrash_lib/src/main/cpp/lzma/Ppmd7Enc.c diff --git a/src/native/libxcrash_dumper/jni/lzma/Precomp.h b/xcrash_lib/src/main/cpp/lzma/Precomp.h similarity index 100% rename from src/native/libxcrash_dumper/jni/lzma/Precomp.h rename to xcrash_lib/src/main/cpp/lzma/Precomp.h diff --git a/src/native/libxcrash_dumper/jni/lzma/RotateDefs.h b/xcrash_lib/src/main/cpp/lzma/RotateDefs.h similarity index 100% rename from src/native/libxcrash_dumper/jni/lzma/RotateDefs.h rename to xcrash_lib/src/main/cpp/lzma/RotateDefs.h diff --git a/src/native/libxcrash_dumper/jni/lzma/Sha256.c b/xcrash_lib/src/main/cpp/lzma/Sha256.c similarity index 100% rename from src/native/libxcrash_dumper/jni/lzma/Sha256.c rename to xcrash_lib/src/main/cpp/lzma/Sha256.c diff --git a/src/native/libxcrash_dumper/jni/lzma/Sha256.h b/xcrash_lib/src/main/cpp/lzma/Sha256.h similarity index 100% rename from src/native/libxcrash_dumper/jni/lzma/Sha256.h rename to xcrash_lib/src/main/cpp/lzma/Sha256.h diff --git a/src/native/libxcrash_dumper/jni/lzma/Sort.c b/xcrash_lib/src/main/cpp/lzma/Sort.c similarity index 100% rename from src/native/libxcrash_dumper/jni/lzma/Sort.c rename to xcrash_lib/src/main/cpp/lzma/Sort.c diff --git a/src/native/libxcrash_dumper/jni/lzma/Sort.h b/xcrash_lib/src/main/cpp/lzma/Sort.h similarity index 100% rename from src/native/libxcrash_dumper/jni/lzma/Sort.h rename to xcrash_lib/src/main/cpp/lzma/Sort.h diff --git a/src/native/libxcrash_dumper/jni/lzma/Threads.c b/xcrash_lib/src/main/cpp/lzma/Threads.c similarity index 100% rename from src/native/libxcrash_dumper/jni/lzma/Threads.c rename to xcrash_lib/src/main/cpp/lzma/Threads.c diff --git a/src/native/libxcrash_dumper/jni/lzma/Threads.h b/xcrash_lib/src/main/cpp/lzma/Threads.h similarity index 100% rename from src/native/libxcrash_dumper/jni/lzma/Threads.h rename to xcrash_lib/src/main/cpp/lzma/Threads.h diff --git a/src/native/libxcrash_dumper/jni/lzma/Xz.c b/xcrash_lib/src/main/cpp/lzma/Xz.c similarity index 100% rename from src/native/libxcrash_dumper/jni/lzma/Xz.c rename to xcrash_lib/src/main/cpp/lzma/Xz.c diff --git a/src/native/libxcrash_dumper/jni/lzma/Xz.h b/xcrash_lib/src/main/cpp/lzma/Xz.h similarity index 100% rename from src/native/libxcrash_dumper/jni/lzma/Xz.h rename to xcrash_lib/src/main/cpp/lzma/Xz.h diff --git a/src/native/libxcrash_dumper/jni/lzma/XzCrc64.c b/xcrash_lib/src/main/cpp/lzma/XzCrc64.c similarity index 100% rename from src/native/libxcrash_dumper/jni/lzma/XzCrc64.c rename to xcrash_lib/src/main/cpp/lzma/XzCrc64.c diff --git a/src/native/libxcrash_dumper/jni/lzma/XzCrc64.h b/xcrash_lib/src/main/cpp/lzma/XzCrc64.h similarity index 100% rename from src/native/libxcrash_dumper/jni/lzma/XzCrc64.h rename to xcrash_lib/src/main/cpp/lzma/XzCrc64.h diff --git a/src/native/libxcrash_dumper/jni/lzma/XzCrc64Opt.c b/xcrash_lib/src/main/cpp/lzma/XzCrc64Opt.c similarity index 100% rename from src/native/libxcrash_dumper/jni/lzma/XzCrc64Opt.c rename to xcrash_lib/src/main/cpp/lzma/XzCrc64Opt.c diff --git a/src/native/libxcrash_dumper/jni/lzma/XzDec.c b/xcrash_lib/src/main/cpp/lzma/XzDec.c similarity index 100% rename from src/native/libxcrash_dumper/jni/lzma/XzDec.c rename to xcrash_lib/src/main/cpp/lzma/XzDec.c diff --git a/src/native/libxcrash_dumper/jni/lzma/XzEnc.c b/xcrash_lib/src/main/cpp/lzma/XzEnc.c similarity index 100% rename from src/native/libxcrash_dumper/jni/lzma/XzEnc.c rename to xcrash_lib/src/main/cpp/lzma/XzEnc.c diff --git a/src/native/libxcrash_dumper/jni/lzma/XzEnc.h b/xcrash_lib/src/main/cpp/lzma/XzEnc.h similarity index 100% rename from src/native/libxcrash_dumper/jni/lzma/XzEnc.h rename to xcrash_lib/src/main/cpp/lzma/XzEnc.h diff --git a/src/native/libxcrash_dumper/jni/lzma/XzIn.c b/xcrash_lib/src/main/cpp/lzma/XzIn.c similarity index 100% rename from src/native/libxcrash_dumper/jni/lzma/XzIn.c rename to xcrash_lib/src/main/cpp/lzma/XzIn.c diff --git a/xcrash_lib/src/main/cpp/xcrash.exports b/xcrash_lib/src/main/cpp/xcrash.exports new file mode 100644 index 0000000..47d8c51 --- /dev/null +++ b/xcrash_lib/src/main/cpp/xcrash.exports @@ -0,0 +1,12 @@ +{ + global: + JNI_OnLoad; + xc_test_crash; + xc_test_call_1; + xc_test_call_2; + xc_test_call_3; + xc_test_call_4; + + local: + *; +}; diff --git a/src/native/libxcrash/jni/xc_common.c b/xcrash_lib/src/main/cpp/xcrash/xc_common.c similarity index 100% rename from src/native/libxcrash/jni/xc_common.c rename to xcrash_lib/src/main/cpp/xcrash/xc_common.c diff --git a/src/native/libxcrash/jni/xc_common.h b/xcrash_lib/src/main/cpp/xcrash/xc_common.h similarity index 100% rename from src/native/libxcrash/jni/xc_common.h rename to xcrash_lib/src/main/cpp/xcrash/xc_common.h diff --git a/src/native/libxcrash/jni/xc_crash.c b/xcrash_lib/src/main/cpp/xcrash/xc_crash.c similarity index 100% rename from src/native/libxcrash/jni/xc_crash.c rename to xcrash_lib/src/main/cpp/xcrash/xc_crash.c diff --git a/src/native/libxcrash/jni/xc_crash.h b/xcrash_lib/src/main/cpp/xcrash/xc_crash.h similarity index 100% rename from src/native/libxcrash/jni/xc_crash.h rename to xcrash_lib/src/main/cpp/xcrash/xc_crash.h diff --git a/src/native/libxcrash/jni/xc_fallback.c b/xcrash_lib/src/main/cpp/xcrash/xc_fallback.c similarity index 100% rename from src/native/libxcrash/jni/xc_fallback.c rename to xcrash_lib/src/main/cpp/xcrash/xc_fallback.c diff --git a/src/native/libxcrash/jni/xc_fallback.h b/xcrash_lib/src/main/cpp/xcrash/xc_fallback.h similarity index 100% rename from src/native/libxcrash/jni/xc_fallback.h rename to xcrash_lib/src/main/cpp/xcrash/xc_fallback.h diff --git a/src/native/libxcrash/jni/xc_jni.c b/xcrash_lib/src/main/cpp/xcrash/xc_jni.c similarity index 100% rename from src/native/libxcrash/jni/xc_jni.c rename to xcrash_lib/src/main/cpp/xcrash/xc_jni.c diff --git a/src/native/libxcrash/jni/xc_jni.h b/xcrash_lib/src/main/cpp/xcrash/xc_jni.h similarity index 100% rename from src/native/libxcrash/jni/xc_jni.h rename to xcrash_lib/src/main/cpp/xcrash/xc_jni.h diff --git a/src/native/libxcrash/jni/xc_test.c b/xcrash_lib/src/main/cpp/xcrash/xc_test.c similarity index 100% rename from src/native/libxcrash/jni/xc_test.c rename to xcrash_lib/src/main/cpp/xcrash/xc_test.c diff --git a/src/native/libxcrash/jni/xc_test.h b/xcrash_lib/src/main/cpp/xcrash/xc_test.h similarity index 100% rename from src/native/libxcrash/jni/xc_test.h rename to xcrash_lib/src/main/cpp/xcrash/xc_test.h diff --git a/src/native/libxcrash/jni/xc_trace.c b/xcrash_lib/src/main/cpp/xcrash/xc_trace.c similarity index 100% rename from src/native/libxcrash/jni/xc_trace.c rename to xcrash_lib/src/main/cpp/xcrash/xc_trace.c diff --git a/src/native/libxcrash/jni/xc_trace.h b/xcrash_lib/src/main/cpp/xcrash/xc_trace.h similarity index 100% rename from src/native/libxcrash/jni/xc_trace.h rename to xcrash_lib/src/main/cpp/xcrash/xc_trace.h diff --git a/src/native/libxcrash/jni/xc_util.c b/xcrash_lib/src/main/cpp/xcrash/xc_util.c similarity index 100% rename from src/native/libxcrash/jni/xc_util.c rename to xcrash_lib/src/main/cpp/xcrash/xc_util.c diff --git a/src/native/libxcrash/jni/xc_util.h b/xcrash_lib/src/main/cpp/xcrash/xc_util.h similarity index 100% rename from src/native/libxcrash/jni/xc_util.h rename to xcrash_lib/src/main/cpp/xcrash/xc_util.h diff --git a/src/native/libxcrash_dumper/jni/xcd_arm_exidx.c b/xcrash_lib/src/main/cpp/xcrash_dumper/xcd_arm_exidx.c similarity index 100% rename from src/native/libxcrash_dumper/jni/xcd_arm_exidx.c rename to xcrash_lib/src/main/cpp/xcrash_dumper/xcd_arm_exidx.c diff --git a/src/native/libxcrash_dumper/jni/xcd_arm_exidx.h b/xcrash_lib/src/main/cpp/xcrash_dumper/xcd_arm_exidx.h similarity index 100% rename from src/native/libxcrash_dumper/jni/xcd_arm_exidx.h rename to xcrash_lib/src/main/cpp/xcrash_dumper/xcd_arm_exidx.h diff --git a/src/native/libxcrash_dumper/jni/xcd_core.c b/xcrash_lib/src/main/cpp/xcrash_dumper/xcd_core.c similarity index 100% rename from src/native/libxcrash_dumper/jni/xcd_core.c rename to xcrash_lib/src/main/cpp/xcrash_dumper/xcd_core.c diff --git a/src/native/libxcrash_dumper/jni/xcd_dwarf.c b/xcrash_lib/src/main/cpp/xcrash_dumper/xcd_dwarf.c similarity index 99% rename from src/native/libxcrash_dumper/jni/xcd_dwarf.c rename to xcrash_lib/src/main/cpp/xcrash_dumper/xcd_dwarf.c index 24ecdaf..e7ff71b 100644 --- a/src/native/libxcrash_dumper/jni/xcd_dwarf.c +++ b/xcrash_lib/src/main/cpp/xcrash_dumper/xcd_dwarf.c @@ -102,6 +102,7 @@ struct xcd_dwarf xcd_dwarf_type_t type; pid_t pid; uintptr_t load_bias; + uintptr_t hdr_load_bias; //for .eh_frame_hdr xcd_dwarf_cie_tree_t cie_cache; xcd_memory_t *memory; @@ -911,12 +912,14 @@ static int xcd_dwarf_get_fde_offset_from_pc(xcd_dwarf_t *self, uintptr_t pc, siz { int r; uint64_t v64; - int is_rel_encoded = ((self->eh_frame_hdr_table_encoding & 0x70) <= DW_EH_PE_funcrel ? 1 : 0); size_t first = 0; size_t last = self->eh_frame_hdr_fde_count; size_t cur; uintptr_t cur_pc; + int is_rel_encoded = ((self->eh_frame_hdr_table_encoding & 0x70) <= DW_EH_PE_funcrel && + (self->eh_frame_hdr_table_encoding & 0x70) > 0) ? 1 : 0; + while(first < last) { cur = (first + last) / 2; @@ -926,7 +929,7 @@ static int xcd_dwarf_get_fde_offset_from_pc(xcd_dwarf_t *self, uintptr_t pc, siz self->memory_pc_offset = 0; if(0 != (r = xcd_dwarf_read_encoded(self, &v64, self->eh_frame_hdr_table_encoding))) return r; cur_pc = (uintptr_t)v64; - if(is_rel_encoded) cur_pc += self->load_bias; + if(is_rel_encoded) cur_pc += self->hdr_load_bias; if(pc == cur_pc) { @@ -1659,7 +1662,7 @@ static int xcd_dwarf_init_eh_frame_hdr(xcd_dwarf_t *self) return 0; } -int xcd_dwarf_create(xcd_dwarf_t **self, xcd_memory_t *memory, pid_t pid, uintptr_t load_bias, +int xcd_dwarf_create(xcd_dwarf_t **self, xcd_memory_t *memory, pid_t pid, uintptr_t load_bias, uintptr_t hdr_load_bias, size_t offset, size_t size, xcd_dwarf_type_t type) { int r = 0; @@ -1668,6 +1671,7 @@ int xcd_dwarf_create(xcd_dwarf_t **self, xcd_memory_t *memory, pid_t pid, uintpt (*self)->type = type; (*self)->pid = pid; (*self)->load_bias = load_bias; + (*self)->hdr_load_bias = hdr_load_bias; RB_INIT(&((*self)->cie_cache)); (*self)->memory = memory; (*self)->memory_cur_offset = offset; diff --git a/src/native/libxcrash_dumper/jni/xcd_dwarf.h b/xcrash_lib/src/main/cpp/xcrash_dumper/xcd_dwarf.h similarity index 96% rename from src/native/libxcrash_dumper/jni/xcd_dwarf.h rename to xcrash_lib/src/main/cpp/xcrash_dumper/xcd_dwarf.h index 864c314..e27aa0c 100644 --- a/src/native/libxcrash_dumper/jni/xcd_dwarf.h +++ b/xcrash_lib/src/main/cpp/xcrash_dumper/xcd_dwarf.h @@ -42,7 +42,7 @@ typedef enum typedef struct xcd_dwarf xcd_dwarf_t; -int xcd_dwarf_create(xcd_dwarf_t **self, xcd_memory_t *memory, pid_t pid, uintptr_t load_bias, +int xcd_dwarf_create(xcd_dwarf_t **self, xcd_memory_t *memory, pid_t pid, uintptr_t load_bias, uintptr_t hdr_load_bias, size_t offset, size_t size, xcd_dwarf_type_t type); int xcd_dwarf_step(xcd_dwarf_t *self, xcd_regs_t *regs, uintptr_t pc, int *finished); diff --git a/src/native/libxcrash_dumper/jni/xcd_elf.c b/xcrash_lib/src/main/cpp/xcrash_dumper/xcd_elf.c similarity index 94% rename from src/native/libxcrash_dumper/jni/xcd_elf.c rename to xcrash_lib/src/main/cpp/xcrash_dumper/xcd_elf.c index 812519a..be49348 100644 --- a/src/native/libxcrash_dumper/jni/xcd_elf.c +++ b/xcrash_lib/src/main/cpp/xcrash_dumper/xcd_elf.c @@ -111,14 +111,17 @@ int xcd_elf_step(xcd_elf_t *self, uintptr_t rel_pc, uintptr_t step_pc, xcd_regs_ *sigreturn = 0; //try sigreturn - if(0 == xcd_regs_try_step_sigreturn(regs, rel_pc, self->memory, self->pid)) + if(rel_pc >= self->load_bias) { - *finished = 0; - *sigreturn = 1; + if(0 == xcd_regs_try_step_sigreturn(regs, rel_pc - self->load_bias, self->memory, self->pid)) + { + *finished = 0; + *sigreturn = 1; #if XCD_ELF_DEBUG - XCD_LOG_DEBUG("ELF: step by sigreturn OK, rel_pc=%"PRIxPTR", finished=0", rel_pc); + XCD_LOG_DEBUG("ELF: step by sigreturn OK, rel_pc=%"PRIxPTR", finished=0", rel_pc); #endif - return 0; + return 0; + } } //try DWARF (.debug_frame and .eh_frame) diff --git a/src/native/libxcrash_dumper/jni/xcd_elf.h b/xcrash_lib/src/main/cpp/xcrash_dumper/xcd_elf.h similarity index 100% rename from src/native/libxcrash_dumper/jni/xcd_elf.h rename to xcrash_lib/src/main/cpp/xcrash_dumper/xcd_elf.h diff --git a/src/native/libxcrash_dumper/jni/xcd_elf_interface.c b/xcrash_lib/src/main/cpp/xcrash_dumper/xcd_elf_interface.c similarity index 94% rename from src/native/libxcrash_dumper/jni/xcd_elf_interface.c rename to xcrash_lib/src/main/cpp/xcrash_dumper/xcd_elf_interface.c index 6ef4fd5..4885d95 100644 --- a/src/native/libxcrash_dumper/jni/xcd_elf_interface.c +++ b/xcrash_lib/src/main/cpp/xcrash_dumper/xcd_elf_interface.c @@ -85,9 +85,6 @@ struct xcd_elf_interface uintptr_t load_bias; int is_gnu; - //PT_LOAD(s) in program headers - xcd_elf_load_queue_t loads; - //symbols (.dynsym with .dynstr, .symtab with .strtab) xcd_elf_symbols_queue_t symbolsq; @@ -101,8 +98,10 @@ struct xcd_elf_interface //.eh_frame with option .eh_frame_hdr size_t eh_frame_offset; size_t eh_frame_size; + uintptr_t eh_frame_load_bias; size_t eh_frame_hdr_offset; size_t eh_frame_hdr_size; + uintptr_t eh_frame_hdr_load_bias; xcd_dwarf_t *dwarf_eh_frame; xcd_dwarf_type_t dwarf_eh_frame_type; @@ -167,10 +166,10 @@ static int xcd_elf_interface_check_valid(ElfW(Ehdr) *ehdr) static int xcd_elf_interface_read_program_headers(xcd_elf_interface_t *self, ElfW(Ehdr) *ehdr, uintptr_t *load_bias) { - size_t i; - ElfW(Phdr) phdr; - xcd_elf_load_t *load, *load_tmp; - int r; + size_t i; + ElfW(Phdr) phdr; + int try_save_load_bias = 0; + int r; for(i = 0; i < ehdr->e_phnum * ehdr->e_phentsize; i += ehdr->e_phentsize) { @@ -186,36 +185,26 @@ static int xcd_elf_interface_read_program_headers(xcd_elf_interface_t *self, Elf { if(0 == (phdr.p_flags & PF_X)) continue; - //save load bias - if(0 == phdr.p_offset) + if(!try_save_load_bias && phdr.p_vaddr > phdr.p_offset) { - self->load_bias = phdr.p_vaddr; - + self->load_bias = phdr.p_vaddr - phdr.p_offset; if(NULL != load_bias) *load_bias = self->load_bias; } - - if(NULL == (load = malloc(sizeof(xcd_elf_load_t)))) - { - r = XCC_ERRNO_NOMEM; - goto err; - } - load->vaddr = phdr.p_vaddr; - load->offset = phdr.p_offset; - load->size = phdr.p_memsz; - TAILQ_INSERT_TAIL(&(self->loads), load, link); + try_save_load_bias = 1; break; } case PT_GNU_EH_FRAME: self->eh_frame_hdr_offset = phdr.p_offset; - self->eh_frame_hdr_size = phdr.p_memsz; + self->eh_frame_hdr_size = phdr.p_memsz; + self->eh_frame_hdr_load_bias = phdr.p_vaddr - phdr.p_offset; break; case PT_ARM_EXIDX: self->arm_exidx_offset = phdr.p_offset; - self->arm_exidx_size = phdr.p_memsz; + self->arm_exidx_size = phdr.p_memsz; break; case PT_DYNAMIC: self->dynamic_offset = phdr.p_offset; - self->dynamic_size = phdr.p_memsz; + self->dynamic_size = phdr.p_memsz; break; default: break; @@ -224,11 +213,6 @@ static int xcd_elf_interface_read_program_headers(xcd_elf_interface_t *self, Elf return 0; err: - TAILQ_FOREACH_SAFE(load, &(self->loads), link, load_tmp) - { - TAILQ_REMOVE(&(self->loads), load, link); - free(load); - } return r; } @@ -331,11 +315,13 @@ static int xcd_elf_interface_read_section_headers(xcd_elf_interface_t *self, Elf { self->eh_frame_offset = shdr.sh_offset; self->eh_frame_size = shdr.sh_size; + self->eh_frame_load_bias = shdr.sh_addr - shdr.sh_offset; } - else if(0 == strcmp(name, ".eh_frame_hdr")) + else if(0 == strcmp(name, ".eh_frame_hdr") && 0 == self->eh_frame_hdr_offset) { self->eh_frame_hdr_offset = shdr.sh_offset; self->eh_frame_hdr_size = shdr.sh_size; + self->eh_frame_hdr_load_bias = shdr.sh_addr - shdr.sh_offset; } else if(0 == strcmp(name, ".gnu_debugdata")) { @@ -380,7 +366,6 @@ int xcd_elf_interface_create(xcd_elf_interface_t **self, pid_t pid, xcd_memory_t if(NULL == (*self = calloc(1, sizeof(xcd_elf_interface_t)))) return XCC_ERRNO_NOMEM; (*self)->pid = pid; (*self)->memory = memory; - TAILQ_INIT(&((*self)->loads)); TAILQ_INIT(&((*self)->symbolsq)); TAILQ_INIT(&((*self)->strtabq)); @@ -398,7 +383,8 @@ int xcd_elf_interface_create(xcd_elf_interface_t **self, pid_t pid, xcd_memory_t //dwarf from .eh_frame with .eh_frame_hdr if(0 != (*self)->eh_frame_hdr_offset && 0 != (*self)->eh_frame_hdr_size) { - xcd_dwarf_create(&((*self)->dwarf_eh_frame), memory, pid, (*self)->load_bias, + xcd_dwarf_create(&((*self)->dwarf_eh_frame), memory, pid, + (*self)->eh_frame_load_bias, (*self)->eh_frame_hdr_load_bias, (*self)->eh_frame_hdr_offset, (*self)->eh_frame_hdr_size, XCD_DWARF_TYPE_EH_FRAME_HDR); (*self)->dwarf_eh_frame_type = XCD_DWARF_TYPE_EH_FRAME_HDR; @@ -407,7 +393,8 @@ int xcd_elf_interface_create(xcd_elf_interface_t **self, pid_t pid, xcd_memory_t //dwarf from .eh_frame without .eh_frame_hdr if(NULL == (*self)->dwarf_eh_frame && 0 != (*self)->eh_frame_offset && 0 != (*self)->eh_frame_size) { - xcd_dwarf_create(&((*self)->dwarf_eh_frame), memory, pid, (*self)->load_bias, + xcd_dwarf_create(&((*self)->dwarf_eh_frame), memory, pid, + (*self)->eh_frame_load_bias, 0, (*self)->eh_frame_offset, (*self)->eh_frame_size, XCD_DWARF_TYPE_EH_FRAME); (*self)->dwarf_eh_frame_type = XCD_DWARF_TYPE_EH_FRAME; @@ -415,7 +402,8 @@ int xcd_elf_interface_create(xcd_elf_interface_t **self, pid_t pid, xcd_memory_t //dwarf from .debug_frame if(0 != (*self)->debug_frame_offset && 0 != (*self)->debug_frame_size) - xcd_dwarf_create(&((*self)->dwarf_debug_frame), memory, pid, (*self)->load_bias, + xcd_dwarf_create(&((*self)->dwarf_debug_frame), memory, pid, + (*self)->load_bias, 0, (*self)->debug_frame_offset, (*self)->debug_frame_size, XCD_DWARF_TYPE_DEBUG_FRAME); diff --git a/src/native/libxcrash_dumper/jni/xcd_elf_interface.h b/xcrash_lib/src/main/cpp/xcrash_dumper/xcd_elf_interface.h similarity index 100% rename from src/native/libxcrash_dumper/jni/xcd_elf_interface.h rename to xcrash_lib/src/main/cpp/xcrash_dumper/xcd_elf_interface.h diff --git a/src/native/libxcrash_dumper/jni/xcd_frames.c b/xcrash_lib/src/main/cpp/xcrash_dumper/xcd_frames.c similarity index 100% rename from src/native/libxcrash_dumper/jni/xcd_frames.c rename to xcrash_lib/src/main/cpp/xcrash_dumper/xcd_frames.c diff --git a/src/native/libxcrash_dumper/jni/xcd_frames.h b/xcrash_lib/src/main/cpp/xcrash_dumper/xcd_frames.h similarity index 100% rename from src/native/libxcrash_dumper/jni/xcd_frames.h rename to xcrash_lib/src/main/cpp/xcrash_dumper/xcd_frames.h diff --git a/src/native/libxcrash_dumper/jni/xcd_log.h b/xcrash_lib/src/main/cpp/xcrash_dumper/xcd_log.h similarity index 100% rename from src/native/libxcrash_dumper/jni/xcd_log.h rename to xcrash_lib/src/main/cpp/xcrash_dumper/xcd_log.h diff --git a/src/native/libxcrash_dumper/jni/xcd_map.c b/xcrash_lib/src/main/cpp/xcrash_dumper/xcd_map.c similarity index 90% rename from src/native/libxcrash_dumper/jni/xcd_map.c rename to xcrash_lib/src/main/cpp/xcrash_dumper/xcd_map.c index 3e0f9e1..c546650 100644 --- a/src/native/libxcrash_dumper/jni/xcd_map.c +++ b/xcrash_lib/src/main/cpp/xcrash_dumper/xcd_map.c @@ -88,18 +88,18 @@ xcd_elf_t *xcd_map_get_elf(xcd_map_t *self, pid_t pid, void *maps_obj) return self->elf; } -uintptr_t xcd_map_get_rel_pc(xcd_map_t *self, uintptr_t pc, pid_t pid, void *maps_obj) +uintptr_t xcd_map_get_rel_pc(xcd_map_t *self, uintptr_t abs_pc, pid_t pid, void *maps_obj) { xcd_elf_t *elf = xcd_map_get_elf(self, pid, maps_obj); uintptr_t load_bias = (NULL == elf ? 0 : xcd_elf_get_load_bias(elf)); - return pc - self->start + load_bias + self->elf_offset; + return abs_pc - (self->start - self->elf_offset - load_bias); } -uintptr_t xcd_map_get_abs_pc(xcd_map_t *self, uintptr_t pc, pid_t pid, void *maps_obj) +uintptr_t xcd_map_get_abs_pc(xcd_map_t *self, uintptr_t rel_pc, pid_t pid, void *maps_obj) { xcd_elf_t *elf = xcd_map_get_elf(self, pid, maps_obj); uintptr_t load_bias = (NULL == elf ? 0 : xcd_elf_get_load_bias(elf)); - return self->start + pc - load_bias - self->elf_offset; + return (self->start - self->elf_offset - load_bias) + rel_pc; } diff --git a/src/native/libxcrash_dumper/jni/xcd_map.h b/xcrash_lib/src/main/cpp/xcrash_dumper/xcd_map.h similarity index 91% rename from src/native/libxcrash_dumper/jni/xcd_map.h rename to xcrash_lib/src/main/cpp/xcrash_dumper/xcd_map.h index 5be02c7..a8c2e9d 100644 --- a/src/native/libxcrash_dumper/jni/xcd_map.h +++ b/xcrash_lib/src/main/cpp/xcrash_dumper/xcd_map.h @@ -58,8 +58,8 @@ int xcd_map_init(xcd_map_t *self, uintptr_t start, uintptr_t end, size_t offset, void xcd_map_uninit(xcd_map_t *self); xcd_elf_t *xcd_map_get_elf(xcd_map_t *self, pid_t pid, void *maps_obj); -uintptr_t xcd_map_get_rel_pc(xcd_map_t *self, uintptr_t pc, pid_t pid, void *maps_obj); -uintptr_t xcd_map_get_abs_pc(xcd_map_t *self, uintptr_t pc, pid_t pid, void *maps_obj); +uintptr_t xcd_map_get_rel_pc(xcd_map_t *self, uintptr_t abs_pc, pid_t pid, void *maps_obj); +uintptr_t xcd_map_get_abs_pc(xcd_map_t *self, uintptr_t rel_pc, pid_t pid, void *maps_obj); #ifdef __cplusplus } diff --git a/src/native/libxcrash_dumper/jni/xcd_maps.c b/xcrash_lib/src/main/cpp/xcrash_dumper/xcd_maps.c similarity index 95% rename from src/native/libxcrash_dumper/jni/xcd_maps.c rename to xcrash_lib/src/main/cpp/xcrash_dumper/xcd_maps.c index ac68f51..20c01a3 100644 --- a/src/native/libxcrash_dumper/jni/xcd_maps.c +++ b/xcrash_lib/src/main/cpp/xcrash_dumper/xcd_maps.c @@ -200,7 +200,7 @@ int xcd_maps_record(xcd_maps_t *self, int log_fd) size_t width_offset = 0; uintptr_t load_bias = 0; char load_bias_buf[64] = "\0"; - char *name = ""; + char *name; char *prev_name = NULL; //get width of size and offset columns @@ -228,22 +228,17 @@ int xcd_maps_record(xcd_maps_t *self, int log_fd) TAILQ_FOREACH(mi, &(self->maps), link) { //get load_bias - if(NULL != mi->map.elf) - { - if(0 != (load_bias = xcd_elf_get_load_bias(mi->map.elf))) - snprintf(load_bias_buf, sizeof(load_bias_buf), " (load base 0x%"PRIxPTR")", load_bias); - } + if(NULL != mi->map.elf && 0 != (load_bias = xcd_elf_get_load_bias(mi->map.elf))) + snprintf(load_bias_buf, sizeof(load_bias_buf), " (load bias 0x%"PRIxPTR")", load_bias); else - { load_bias_buf[0] = '\0'; - } - //get name + //fix name and load_bias if(NULL != mi->map.name) { if(NULL == prev_name) name = mi->map.name; - else if(0 == strcmp(prev_name, mi->map.name)) + else if(0 == strcmp(prev_name, mi->map.name) && '\0' == load_bias_buf[0]) name = ">"; //same as prev line else name = mi->map.name; diff --git a/src/native/libxcrash_dumper/jni/xcd_maps.h b/xcrash_lib/src/main/cpp/xcrash_dumper/xcd_maps.h similarity index 100% rename from src/native/libxcrash_dumper/jni/xcd_maps.h rename to xcrash_lib/src/main/cpp/xcrash_dumper/xcd_maps.h diff --git a/src/native/libxcrash_dumper/jni/xcd_md5.c b/xcrash_lib/src/main/cpp/xcrash_dumper/xcd_md5.c similarity index 100% rename from src/native/libxcrash_dumper/jni/xcd_md5.c rename to xcrash_lib/src/main/cpp/xcrash_dumper/xcd_md5.c diff --git a/src/native/libxcrash_dumper/jni/xcd_md5.h b/xcrash_lib/src/main/cpp/xcrash_dumper/xcd_md5.h similarity index 100% rename from src/native/libxcrash_dumper/jni/xcd_md5.h rename to xcrash_lib/src/main/cpp/xcrash_dumper/xcd_md5.h diff --git a/src/native/libxcrash_dumper/jni/xcd_memory.c b/xcrash_lib/src/main/cpp/xcrash_dumper/xcd_memory.c similarity index 100% rename from src/native/libxcrash_dumper/jni/xcd_memory.c rename to xcrash_lib/src/main/cpp/xcrash_dumper/xcd_memory.c diff --git a/src/native/libxcrash_dumper/jni/xcd_memory.h b/xcrash_lib/src/main/cpp/xcrash_dumper/xcd_memory.h similarity index 100% rename from src/native/libxcrash_dumper/jni/xcd_memory.h rename to xcrash_lib/src/main/cpp/xcrash_dumper/xcd_memory.h diff --git a/src/native/libxcrash_dumper/jni/xcd_memory_buf.c b/xcrash_lib/src/main/cpp/xcrash_dumper/xcd_memory_buf.c similarity index 100% rename from src/native/libxcrash_dumper/jni/xcd_memory_buf.c rename to xcrash_lib/src/main/cpp/xcrash_dumper/xcd_memory_buf.c diff --git a/src/native/libxcrash_dumper/jni/xcd_memory_buf.h b/xcrash_lib/src/main/cpp/xcrash_dumper/xcd_memory_buf.h similarity index 100% rename from src/native/libxcrash_dumper/jni/xcd_memory_buf.h rename to xcrash_lib/src/main/cpp/xcrash_dumper/xcd_memory_buf.h diff --git a/src/native/libxcrash_dumper/jni/xcd_memory_file.c b/xcrash_lib/src/main/cpp/xcrash_dumper/xcd_memory_file.c similarity index 100% rename from src/native/libxcrash_dumper/jni/xcd_memory_file.c rename to xcrash_lib/src/main/cpp/xcrash_dumper/xcd_memory_file.c diff --git a/src/native/libxcrash_dumper/jni/xcd_memory_file.h b/xcrash_lib/src/main/cpp/xcrash_dumper/xcd_memory_file.h similarity index 100% rename from src/native/libxcrash_dumper/jni/xcd_memory_file.h rename to xcrash_lib/src/main/cpp/xcrash_dumper/xcd_memory_file.h diff --git a/src/native/libxcrash_dumper/jni/xcd_memory_remote.c b/xcrash_lib/src/main/cpp/xcrash_dumper/xcd_memory_remote.c similarity index 100% rename from src/native/libxcrash_dumper/jni/xcd_memory_remote.c rename to xcrash_lib/src/main/cpp/xcrash_dumper/xcd_memory_remote.c diff --git a/src/native/libxcrash_dumper/jni/xcd_memory_remote.h b/xcrash_lib/src/main/cpp/xcrash_dumper/xcd_memory_remote.h similarity index 100% rename from src/native/libxcrash_dumper/jni/xcd_memory_remote.h rename to xcrash_lib/src/main/cpp/xcrash_dumper/xcd_memory_remote.h diff --git a/src/native/libxcrash_dumper/jni/xcd_process.c b/xcrash_lib/src/main/cpp/xcrash_dumper/xcd_process.c similarity index 100% rename from src/native/libxcrash_dumper/jni/xcd_process.c rename to xcrash_lib/src/main/cpp/xcrash_dumper/xcd_process.c diff --git a/src/native/libxcrash_dumper/jni/xcd_process.h b/xcrash_lib/src/main/cpp/xcrash_dumper/xcd_process.h similarity index 100% rename from src/native/libxcrash_dumper/jni/xcd_process.h rename to xcrash_lib/src/main/cpp/xcrash_dumper/xcd_process.h diff --git a/src/native/libxcrash_dumper/jni/xcd_regs.h b/xcrash_lib/src/main/cpp/xcrash_dumper/xcd_regs.h similarity index 100% rename from src/native/libxcrash_dumper/jni/xcd_regs.h rename to xcrash_lib/src/main/cpp/xcrash_dumper/xcd_regs.h diff --git a/src/native/libxcrash_dumper/jni/xcd_regs_arm.c b/xcrash_lib/src/main/cpp/xcrash_dumper/xcd_regs_arm.c similarity index 100% rename from src/native/libxcrash_dumper/jni/xcd_regs_arm.c rename to xcrash_lib/src/main/cpp/xcrash_dumper/xcd_regs_arm.c diff --git a/src/native/libxcrash_dumper/jni/xcd_regs_arm64.c b/xcrash_lib/src/main/cpp/xcrash_dumper/xcd_regs_arm64.c similarity index 100% rename from src/native/libxcrash_dumper/jni/xcd_regs_arm64.c rename to xcrash_lib/src/main/cpp/xcrash_dumper/xcd_regs_arm64.c diff --git a/src/native/libxcrash_dumper/jni/xcd_regs_x86.c b/xcrash_lib/src/main/cpp/xcrash_dumper/xcd_regs_x86.c similarity index 100% rename from src/native/libxcrash_dumper/jni/xcd_regs_x86.c rename to xcrash_lib/src/main/cpp/xcrash_dumper/xcd_regs_x86.c diff --git a/src/native/libxcrash_dumper/jni/xcd_regs_x86_64.c b/xcrash_lib/src/main/cpp/xcrash_dumper/xcd_regs_x86_64.c similarity index 100% rename from src/native/libxcrash_dumper/jni/xcd_regs_x86_64.c rename to xcrash_lib/src/main/cpp/xcrash_dumper/xcd_regs_x86_64.c diff --git a/src/native/libxcrash_dumper/jni/xcd_sys.c b/xcrash_lib/src/main/cpp/xcrash_dumper/xcd_sys.c similarity index 100% rename from src/native/libxcrash_dumper/jni/xcd_sys.c rename to xcrash_lib/src/main/cpp/xcrash_dumper/xcd_sys.c diff --git a/src/native/libxcrash_dumper/jni/xcd_sys.h b/xcrash_lib/src/main/cpp/xcrash_dumper/xcd_sys.h similarity index 100% rename from src/native/libxcrash_dumper/jni/xcd_sys.h rename to xcrash_lib/src/main/cpp/xcrash_dumper/xcd_sys.h diff --git a/src/native/libxcrash_dumper/jni/xcd_thread.c b/xcrash_lib/src/main/cpp/xcrash_dumper/xcd_thread.c similarity index 100% rename from src/native/libxcrash_dumper/jni/xcd_thread.c rename to xcrash_lib/src/main/cpp/xcrash_dumper/xcd_thread.c diff --git a/src/native/libxcrash_dumper/jni/xcd_thread.h b/xcrash_lib/src/main/cpp/xcrash_dumper/xcd_thread.h similarity index 100% rename from src/native/libxcrash_dumper/jni/xcd_thread.h rename to xcrash_lib/src/main/cpp/xcrash_dumper/xcd_thread.h diff --git a/src/native/libxcrash_dumper/jni/xcd_util.c b/xcrash_lib/src/main/cpp/xcrash_dumper/xcd_util.c similarity index 60% rename from src/native/libxcrash_dumper/jni/xcd_util.c rename to xcrash_lib/src/main/cpp/xcrash_dumper/xcd_util.c index 68df636..8eb2909 100644 --- a/src/native/libxcrash_dumper/jni/xcd_util.c +++ b/xcrash_lib/src/main/cpp/xcrash_dumper/xcd_util.c @@ -45,61 +45,92 @@ #include "XzCrc64.h" #pragma clang diagnostic pop -int xcd_util_ptrace_read_long(pid_t pid, uintptr_t addr, long *value) +extern __attribute((weak)) ssize_t process_vm_readv(pid_t, const struct iovec *, unsigned long, const struct iovec *, unsigned long, unsigned long); + +static size_t xcd_util_process_vm_readv(pid_t pid, uintptr_t remote_addr, void* dst, size_t dst_len) { - // ptrace() returns -1 and sets errno when the operation fails. - // To disambiguate -1 from a valid result, we clear errno beforehand. - errno = 0; - *value = ptrace(PTRACE_PEEKTEXT, pid, (void *)addr, NULL); - if(-1 == *value && 0 != errno) + size_t page_size = (size_t)sysconf(_SC_PAGE_SIZE); + struct iovec src_iovs[64]; + uintptr_t cur_remote_addr = remote_addr; + size_t total_read = 0; + + while (dst_len > 0) { - XCD_LOG_ERROR("UTIL: ptrace error, addr:%"PRIxPTR", errno:%d\n", addr, errno); - return errno; + // the destination + struct iovec dst_iov = {.iov_base = &((uint8_t *)dst)[total_read], .iov_len = dst_len}; + + // the source + size_t iovecs_used = 0; + while (dst_len > 0) + { + // fill iov_base + src_iovs[iovecs_used].iov_base = (void *)cur_remote_addr; + + // fill iov_len (one page at a time, page boundaries aligned) + uintptr_t misalignment = cur_remote_addr & (page_size - 1); + size_t iov_len = page_size - misalignment; + src_iovs[iovecs_used].iov_len = (iov_len > dst_len ? dst_len : iov_len); + + if (__builtin_add_overflow(cur_remote_addr, iov_len, &cur_remote_addr)) return total_read; + dst_len -= iov_len; + + if(64 == ++iovecs_used) break; + } + + // read from source to destination + ssize_t rc; + if(NULL != process_vm_readv) + rc = process_vm_readv(pid, &dst_iov, 1, src_iovs, iovecs_used, 0); + else + rc = syscall(__NR_process_vm_readv, pid, &dst_iov, 1, src_iovs, iovecs_used, 0); + if(-1 == rc) return total_read; + + total_read += (size_t)rc; } - - return 0; + + return total_read; } -size_t xcd_util_ptrace_read(pid_t pid, uintptr_t addr, void *dst, size_t bytes) +static size_t xcd_util_original_ptrace(pid_t pid, uintptr_t remote_addr, void *dst, size_t dst_len) { // Make sure that there is no overflow. uintptr_t max_size; - if(__builtin_add_overflow(addr, bytes, &max_size)) return 0; + if(__builtin_add_overflow(remote_addr, dst_len, &max_size)) return 0; size_t bytes_read = 0; long data; - size_t align_bytes = addr & (sizeof(long) - 1); + size_t align_bytes = remote_addr & (sizeof(long) - 1); if(align_bytes != 0) { - if(0 != xcd_util_ptrace_read_long(pid, addr & ~(sizeof(long) - 1), &data)) return 0; + if(0 != xcd_util_ptrace_read_long(pid, remote_addr & ~(sizeof(long) - 1), &data)) return 0; #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wgnu-statement-expression" - size_t copy_bytes = XCC_UTIL_MIN(sizeof(long) - align_bytes, bytes); + size_t copy_bytes = XCC_UTIL_MIN(sizeof(long) - align_bytes, dst_len); #pragma clang diagnostic pop memcpy(dst, (uint8_t *)(&data) + align_bytes, copy_bytes); - addr += copy_bytes; + remote_addr += copy_bytes; dst = (void *)((uintptr_t)dst + copy_bytes); - bytes -= copy_bytes; + dst_len -= copy_bytes; bytes_read += copy_bytes; } size_t i; - for(i = 0; i < bytes / sizeof(long); i++) + for(i = 0; i < dst_len / sizeof(long); i++) { - if(0 != xcd_util_ptrace_read_long(pid, addr, &data)) return bytes_read; + if(0 != xcd_util_ptrace_read_long(pid, remote_addr, &data)) return bytes_read; memcpy(dst, &data, sizeof(long)); dst = (void *)((uintptr_t)dst + sizeof(long)); - addr += sizeof(long); + remote_addr += sizeof(long); bytes_read += sizeof(long); } - size_t left_over = bytes & (sizeof(long) - 1); + size_t left_over = dst_len & (sizeof(long) - 1); if(left_over) { - if(0 != xcd_util_ptrace_read_long(pid, addr, &data)) return bytes_read; + if(0 != xcd_util_ptrace_read_long(pid, remote_addr, &data)) return bytes_read; memcpy(dst, &data, left_over); bytes_read += left_over; @@ -108,12 +139,54 @@ size_t xcd_util_ptrace_read(pid_t pid, uintptr_t addr, void *dst, size_t bytes) return bytes_read; } +size_t xcd_util_ptrace_read(pid_t pid, uintptr_t remote_addr, void *dst, size_t dst_len) +{ + static size_t (*ptrace_read)(pid_t, uintptr_t, void *, size_t) = NULL; + + if(NULL != ptrace_read) + { + return ptrace_read(pid, remote_addr, dst, dst_len); + } + else + { + size_t bytes = xcd_util_process_vm_readv(pid, remote_addr, dst, dst_len); + if(bytes > 0) + { + __atomic_store_n(&ptrace_read, xcd_util_process_vm_readv, __ATOMIC_SEQ_CST); + return bytes; + } + bytes = xcd_util_original_ptrace(pid, remote_addr, dst, dst_len); + if(bytes > 0) + { + __atomic_store_n(&ptrace_read, xcd_util_original_ptrace, __ATOMIC_SEQ_CST); + return bytes; + } + return 0; + } +} + int xcd_util_ptrace_read_fully(pid_t pid, uintptr_t addr, void *dst, size_t bytes) { size_t rc = xcd_util_ptrace_read(pid, addr, dst, bytes); return rc == bytes ? 0 : XCC_ERRNO_MISSING; } + +int xcd_util_ptrace_read_long(pid_t pid, uintptr_t addr, long *value) +{ + // ptrace() returns -1 and sets errno when the operation fails. + // To disambiguate -1 from a valid result, we clear errno beforehand. + errno = 0; + *value = ptrace(PTRACE_PEEKTEXT, pid, (void *)addr, NULL); + if(-1 == *value && 0 != errno) + { + //XCD_LOG_INFO("UTIL: ptrace error, addr:%"PRIxPTR", errno:%d\n", addr, errno); + return errno; + } + + return 0; +} + static void* xcd_util_xz_alloc(ISzAllocPtr p, size_t size) { (void)p; diff --git a/src/native/libxcrash_dumper/jni/xcd_util.h b/xcrash_lib/src/main/cpp/xcrash_dumper/xcd_util.h similarity index 81% rename from src/native/libxcrash_dumper/jni/xcd_util.h rename to xcrash_lib/src/main/cpp/xcrash_dumper/xcd_util.h index 0cd3f9e..a12a10c 100644 --- a/src/native/libxcrash_dumper/jni/xcd_util.h +++ b/xcrash_lib/src/main/cpp/xcrash_dumper/xcd_util.h @@ -27,32 +27,14 @@ #include #include #include -#include #ifdef __cplusplus extern "C" { #endif -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wpadded" -typedef struct -{ - int api_level; - char *os_version; - char *abi_list; - char *manufacturer; - char *brand; - char *model; - char *build_fingerprint; - char *revision; -} xcd_util_build_prop_t; -#pragma clang diagnostic pop - -void xcd_util_load_build_prop(xcd_util_build_prop_t *prop); - -int xcd_util_ptrace_read_long(pid_t pid, uintptr_t addr, long *value); size_t xcd_util_ptrace_read(pid_t pid, uintptr_t addr, void *dst, size_t bytes); int xcd_util_ptrace_read_fully(pid_t pid, uintptr_t addr, void *dst, size_t bytes); +int xcd_util_ptrace_read_long(pid_t pid, uintptr_t addr, long *value); int xcd_util_xz_decompress(uint8_t* src, size_t src_size, uint8_t** dst, size_t* dst_size); diff --git a/src/java/xcrash/xcrash_lib/src/main/java/xcrash/ActivityMonitor.java b/xcrash_lib/src/main/java/xcrash/ActivityMonitor.java similarity index 100% rename from src/java/xcrash/xcrash_lib/src/main/java/xcrash/ActivityMonitor.java rename to xcrash_lib/src/main/java/xcrash/ActivityMonitor.java diff --git a/src/java/xcrash/xcrash_lib/src/main/java/xcrash/AnrHandler.java b/xcrash_lib/src/main/java/xcrash/AnrHandler.java similarity index 100% rename from src/java/xcrash/xcrash_lib/src/main/java/xcrash/AnrHandler.java rename to xcrash_lib/src/main/java/xcrash/AnrHandler.java diff --git a/src/java/xcrash/xcrash_lib/src/main/java/xcrash/DefaultLogger.java b/xcrash_lib/src/main/java/xcrash/DefaultLogger.java similarity index 100% rename from src/java/xcrash/xcrash_lib/src/main/java/xcrash/DefaultLogger.java rename to xcrash_lib/src/main/java/xcrash/DefaultLogger.java diff --git a/src/java/xcrash/xcrash_lib/src/main/java/xcrash/Errno.java b/xcrash_lib/src/main/java/xcrash/Errno.java similarity index 100% rename from src/java/xcrash/xcrash_lib/src/main/java/xcrash/Errno.java rename to xcrash_lib/src/main/java/xcrash/Errno.java diff --git a/src/java/xcrash/xcrash_lib/src/main/java/xcrash/FileManager.java b/xcrash_lib/src/main/java/xcrash/FileManager.java similarity index 100% rename from src/java/xcrash/xcrash_lib/src/main/java/xcrash/FileManager.java rename to xcrash_lib/src/main/java/xcrash/FileManager.java diff --git a/src/java/xcrash/xcrash_lib/src/main/java/xcrash/ICrashCallback.java b/xcrash_lib/src/main/java/xcrash/ICrashCallback.java similarity index 100% rename from src/java/xcrash/xcrash_lib/src/main/java/xcrash/ICrashCallback.java rename to xcrash_lib/src/main/java/xcrash/ICrashCallback.java diff --git a/src/java/xcrash/xcrash_lib/src/main/java/xcrash/ILibLoader.java b/xcrash_lib/src/main/java/xcrash/ILibLoader.java similarity index 100% rename from src/java/xcrash/xcrash_lib/src/main/java/xcrash/ILibLoader.java rename to xcrash_lib/src/main/java/xcrash/ILibLoader.java diff --git a/src/java/xcrash/xcrash_lib/src/main/java/xcrash/ILogger.java b/xcrash_lib/src/main/java/xcrash/ILogger.java similarity index 100% rename from src/java/xcrash/xcrash_lib/src/main/java/xcrash/ILogger.java rename to xcrash_lib/src/main/java/xcrash/ILogger.java diff --git a/src/java/xcrash/xcrash_lib/src/main/java/xcrash/JavaCrashHandler.java b/xcrash_lib/src/main/java/xcrash/JavaCrashHandler.java similarity index 100% rename from src/java/xcrash/xcrash_lib/src/main/java/xcrash/JavaCrashHandler.java rename to xcrash_lib/src/main/java/xcrash/JavaCrashHandler.java diff --git a/src/java/xcrash/xcrash_lib/src/main/java/xcrash/NativeHandler.java b/xcrash_lib/src/main/java/xcrash/NativeHandler.java similarity index 100% rename from src/java/xcrash/xcrash_lib/src/main/java/xcrash/NativeHandler.java rename to xcrash_lib/src/main/java/xcrash/NativeHandler.java diff --git a/src/java/xcrash/xcrash_lib/src/main/java/xcrash/TombstoneManager.java b/xcrash_lib/src/main/java/xcrash/TombstoneManager.java similarity index 100% rename from src/java/xcrash/xcrash_lib/src/main/java/xcrash/TombstoneManager.java rename to xcrash_lib/src/main/java/xcrash/TombstoneManager.java diff --git a/src/java/xcrash/xcrash_lib/src/main/java/xcrash/TombstoneParser.java b/xcrash_lib/src/main/java/xcrash/TombstoneParser.java similarity index 100% rename from src/java/xcrash/xcrash_lib/src/main/java/xcrash/TombstoneParser.java rename to xcrash_lib/src/main/java/xcrash/TombstoneParser.java diff --git a/src/java/xcrash/xcrash_lib/src/main/java/xcrash/Util.java b/xcrash_lib/src/main/java/xcrash/Util.java similarity index 100% rename from src/java/xcrash/xcrash_lib/src/main/java/xcrash/Util.java rename to xcrash_lib/src/main/java/xcrash/Util.java diff --git a/src/java/xcrash/xcrash_lib/src/main/java/xcrash/Version.java b/xcrash_lib/src/main/java/xcrash/Version.java similarity index 96% rename from src/java/xcrash/xcrash_lib/src/main/java/xcrash/Version.java rename to xcrash_lib/src/main/java/xcrash/Version.java index 067c79d..041b30b 100644 --- a/src/java/xcrash/xcrash_lib/src/main/java/xcrash/Version.java +++ b/xcrash_lib/src/main/java/xcrash/Version.java @@ -27,6 +27,6 @@ class Version { private Version() { } - static final String version = "2.5.9"; + static final String version = "3.0.0"; static final String fullVersion = "xCrash " + version; } diff --git a/src/java/xcrash/xcrash_lib/src/main/java/xcrash/XCrash.java b/xcrash_lib/src/main/java/xcrash/XCrash.java similarity index 100% rename from src/java/xcrash/xcrash_lib/src/main/java/xcrash/XCrash.java rename to xcrash_lib/src/main/java/xcrash/XCrash.java diff --git a/src/java/xcrash/xcrash_sample/.gitignore b/xcrash_sample/.gitignore similarity index 100% rename from src/java/xcrash/xcrash_sample/.gitignore rename to xcrash_sample/.gitignore diff --git a/src/java/xcrash/xcrash_sample/build.gradle b/xcrash_sample/build.gradle similarity index 73% rename from src/java/xcrash/xcrash_sample/build.gradle rename to xcrash_sample/build.gradle index a77e8e9..16b843e 100644 --- a/src/java/xcrash/xcrash_sample/build.gradle +++ b/xcrash_sample/build.gradle @@ -3,6 +3,7 @@ apply plugin: 'com.android.application' android { compileSdkVersion rootProject.ext.compileSdkVersion buildToolsVersion rootProject.ext.buildToolsVersion + ndkVersion rootProject.ext.ndkVersion defaultConfig { minSdkVersion rootProject.ext.minSdkVersion targetSdkVersion rootProject.ext.targetSdkVersion @@ -10,7 +11,7 @@ android { versionCode 1 versionName "1.0" ndk { - abiFilters 'armeabi-v7a', 'arm64-v8a', 'x86', 'x86_64' + abiFilters rootProject.ext.abiFilters.split(",") } } compileOptions { @@ -31,7 +32,10 @@ android { dependencies { implementation fileTree(dir: 'libs', include: ['*.jar']) - implementation 'androidx.appcompat:appcompat:1.1.0' - implementation 'androidx.constraintlayout:constraintlayout:1.1.3' + implementation 'androidx.appcompat:appcompat:1.2.0' + implementation 'androidx.constraintlayout:constraintlayout:2.0.1' + //implementation 'io.hexhacking.xcrash:xcrash-android-lib:3.0.0' implementation project(':xcrash_lib') } + +apply from: rootProject.file('gradle/sanitizer.gradle') diff --git a/src/java/xcrash/xcrash_sample/proguard-rules.pro b/xcrash_sample/proguard-rules.pro similarity index 100% rename from src/java/xcrash/xcrash_sample/proguard-rules.pro rename to xcrash_sample/proguard-rules.pro diff --git a/src/java/xcrash/xcrash_sample/src/main/AndroidManifest.xml b/xcrash_sample/src/main/AndroidManifest.xml similarity index 100% rename from src/java/xcrash/xcrash_sample/src/main/AndroidManifest.xml rename to xcrash_sample/src/main/AndroidManifest.xml diff --git a/src/java/xcrash/xcrash_sample/src/main/java/xcrash/sample/MainActivity.java b/xcrash_sample/src/main/java/xcrash/sample/MainActivity.java similarity index 100% rename from src/java/xcrash/xcrash_sample/src/main/java/xcrash/sample/MainActivity.java rename to xcrash_sample/src/main/java/xcrash/sample/MainActivity.java diff --git a/src/java/xcrash/xcrash_sample/src/main/java/xcrash/sample/MyCustomApplication.java b/xcrash_sample/src/main/java/xcrash/sample/MyCustomApplication.java similarity index 100% rename from src/java/xcrash/xcrash_sample/src/main/java/xcrash/sample/MyCustomApplication.java rename to xcrash_sample/src/main/java/xcrash/sample/MyCustomApplication.java diff --git a/src/java/xcrash/xcrash_sample/src/main/java/xcrash/sample/MyService.java b/xcrash_sample/src/main/java/xcrash/sample/MyService.java similarity index 100% rename from src/java/xcrash/xcrash_sample/src/main/java/xcrash/sample/MyService.java rename to xcrash_sample/src/main/java/xcrash/sample/MyService.java diff --git a/src/java/xcrash/xcrash_sample/src/main/java/xcrash/sample/SecondActivity.java b/xcrash_sample/src/main/java/xcrash/sample/SecondActivity.java similarity index 100% rename from src/java/xcrash/xcrash_sample/src/main/java/xcrash/sample/SecondActivity.java rename to xcrash_sample/src/main/java/xcrash/sample/SecondActivity.java diff --git a/src/java/xcrash/xcrash_sample/src/main/res/drawable-v24/ic_launcher_foreground.xml b/xcrash_sample/src/main/res/drawable-v24/ic_launcher_foreground.xml similarity index 100% rename from src/java/xcrash/xcrash_sample/src/main/res/drawable-v24/ic_launcher_foreground.xml rename to xcrash_sample/src/main/res/drawable-v24/ic_launcher_foreground.xml diff --git a/src/java/xcrash/xcrash_sample/src/main/res/drawable/ic_launcher_background.xml b/xcrash_sample/src/main/res/drawable/ic_launcher_background.xml similarity index 100% rename from src/java/xcrash/xcrash_sample/src/main/res/drawable/ic_launcher_background.xml rename to xcrash_sample/src/main/res/drawable/ic_launcher_background.xml diff --git a/src/java/xcrash/xcrash_sample/src/main/res/layout/activity_main.xml b/xcrash_sample/src/main/res/layout/activity_main.xml similarity index 100% rename from src/java/xcrash/xcrash_sample/src/main/res/layout/activity_main.xml rename to xcrash_sample/src/main/res/layout/activity_main.xml diff --git a/src/java/xcrash/xcrash_sample/src/main/res/layout/activity_second.xml b/xcrash_sample/src/main/res/layout/activity_second.xml similarity index 100% rename from src/java/xcrash/xcrash_sample/src/main/res/layout/activity_second.xml rename to xcrash_sample/src/main/res/layout/activity_second.xml diff --git a/src/java/xcrash/xcrash_sample/src/main/res/mipmap-anydpi-v26/ic_launcher.xml b/xcrash_sample/src/main/res/mipmap-anydpi-v26/ic_launcher.xml similarity index 100% rename from src/java/xcrash/xcrash_sample/src/main/res/mipmap-anydpi-v26/ic_launcher.xml rename to xcrash_sample/src/main/res/mipmap-anydpi-v26/ic_launcher.xml diff --git a/src/java/xcrash/xcrash_sample/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml b/xcrash_sample/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml similarity index 100% rename from src/java/xcrash/xcrash_sample/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml rename to xcrash_sample/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml diff --git a/src/java/xcrash/xcrash_sample/src/main/res/mipmap-hdpi/ic_launcher.png b/xcrash_sample/src/main/res/mipmap-hdpi/ic_launcher.png similarity index 100% rename from src/java/xcrash/xcrash_sample/src/main/res/mipmap-hdpi/ic_launcher.png rename to xcrash_sample/src/main/res/mipmap-hdpi/ic_launcher.png diff --git a/src/java/xcrash/xcrash_sample/src/main/res/mipmap-hdpi/ic_launcher_round.png b/xcrash_sample/src/main/res/mipmap-hdpi/ic_launcher_round.png similarity index 100% rename from src/java/xcrash/xcrash_sample/src/main/res/mipmap-hdpi/ic_launcher_round.png rename to xcrash_sample/src/main/res/mipmap-hdpi/ic_launcher_round.png diff --git a/src/java/xcrash/xcrash_sample/src/main/res/mipmap-mdpi/ic_launcher.png b/xcrash_sample/src/main/res/mipmap-mdpi/ic_launcher.png similarity index 100% rename from src/java/xcrash/xcrash_sample/src/main/res/mipmap-mdpi/ic_launcher.png rename to xcrash_sample/src/main/res/mipmap-mdpi/ic_launcher.png diff --git a/src/java/xcrash/xcrash_sample/src/main/res/mipmap-mdpi/ic_launcher_round.png b/xcrash_sample/src/main/res/mipmap-mdpi/ic_launcher_round.png similarity index 100% rename from src/java/xcrash/xcrash_sample/src/main/res/mipmap-mdpi/ic_launcher_round.png rename to xcrash_sample/src/main/res/mipmap-mdpi/ic_launcher_round.png diff --git a/src/java/xcrash/xcrash_sample/src/main/res/mipmap-xhdpi/ic_launcher.png b/xcrash_sample/src/main/res/mipmap-xhdpi/ic_launcher.png similarity index 100% rename from src/java/xcrash/xcrash_sample/src/main/res/mipmap-xhdpi/ic_launcher.png rename to xcrash_sample/src/main/res/mipmap-xhdpi/ic_launcher.png diff --git a/src/java/xcrash/xcrash_sample/src/main/res/mipmap-xhdpi/ic_launcher_round.png b/xcrash_sample/src/main/res/mipmap-xhdpi/ic_launcher_round.png similarity index 100% rename from src/java/xcrash/xcrash_sample/src/main/res/mipmap-xhdpi/ic_launcher_round.png rename to xcrash_sample/src/main/res/mipmap-xhdpi/ic_launcher_round.png diff --git a/src/java/xcrash/xcrash_sample/src/main/res/mipmap-xxhdpi/ic_launcher.png b/xcrash_sample/src/main/res/mipmap-xxhdpi/ic_launcher.png similarity index 100% rename from src/java/xcrash/xcrash_sample/src/main/res/mipmap-xxhdpi/ic_launcher.png rename to xcrash_sample/src/main/res/mipmap-xxhdpi/ic_launcher.png diff --git a/src/java/xcrash/xcrash_sample/src/main/res/mipmap-xxhdpi/ic_launcher_round.png b/xcrash_sample/src/main/res/mipmap-xxhdpi/ic_launcher_round.png similarity index 100% rename from src/java/xcrash/xcrash_sample/src/main/res/mipmap-xxhdpi/ic_launcher_round.png rename to xcrash_sample/src/main/res/mipmap-xxhdpi/ic_launcher_round.png diff --git a/src/java/xcrash/xcrash_sample/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/xcrash_sample/src/main/res/mipmap-xxxhdpi/ic_launcher.png similarity index 100% rename from src/java/xcrash/xcrash_sample/src/main/res/mipmap-xxxhdpi/ic_launcher.png rename to xcrash_sample/src/main/res/mipmap-xxxhdpi/ic_launcher.png diff --git a/src/java/xcrash/xcrash_sample/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png b/xcrash_sample/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png similarity index 100% rename from src/java/xcrash/xcrash_sample/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png rename to xcrash_sample/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png diff --git a/src/java/xcrash/xcrash_sample/src/main/res/values/colors.xml b/xcrash_sample/src/main/res/values/colors.xml similarity index 100% rename from src/java/xcrash/xcrash_sample/src/main/res/values/colors.xml rename to xcrash_sample/src/main/res/values/colors.xml diff --git a/src/java/xcrash/xcrash_sample/src/main/res/values/strings.xml b/xcrash_sample/src/main/res/values/strings.xml similarity index 100% rename from src/java/xcrash/xcrash_sample/src/main/res/values/strings.xml rename to xcrash_sample/src/main/res/values/strings.xml diff --git a/src/java/xcrash/xcrash_sample/src/main/res/values/styles.xml b/xcrash_sample/src/main/res/values/styles.xml similarity index 100% rename from src/java/xcrash/xcrash_sample/src/main/res/values/styles.xml rename to xcrash_sample/src/main/res/values/styles.xml From df83fca59c6a4a79eff882a950969677a09ce97a Mon Sep 17 00:00:00 2001 From: Xu Qingqing Date: Tue, 27 Oct 2020 18:37:19 +0800 Subject: [PATCH 39/46] add iqiyi xcrash dependency in sample project --- xcrash_sample/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/xcrash_sample/build.gradle b/xcrash_sample/build.gradle index 16b843e..3e9c27a 100644 --- a/xcrash_sample/build.gradle +++ b/xcrash_sample/build.gradle @@ -34,7 +34,7 @@ dependencies { implementation fileTree(dir: 'libs', include: ['*.jar']) implementation 'androidx.appcompat:appcompat:1.2.0' implementation 'androidx.constraintlayout:constraintlayout:2.0.1' - //implementation 'io.hexhacking.xcrash:xcrash-android-lib:3.0.0' + //implementation 'com.iqiyi.xcrash:xcrash-android-lib:3.0.0' implementation project(':xcrash_lib') } From 78bb54a3de16a95630e2e67cf60a2d8936a89c01 Mon Sep 17 00:00:00 2001 From: Tim Gates Date: Fri, 27 Nov 2020 20:45:15 +1100 Subject: [PATCH 40/46] docs: fix simple typo, lenghts -> lengths There is a small typo in xcrash_lib/src/main/cpp/common/xcc_spot.h. Should read `lengths` rather than `lenghts`. --- xcrash_lib/src/main/cpp/common/xcc_spot.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/xcrash_lib/src/main/cpp/common/xcc_spot.h b/xcrash_lib/src/main/cpp/common/xcc_spot.h index 1954c0d..d2ec61b 100644 --- a/xcrash_lib/src/main/cpp/common/xcc_spot.h +++ b/xcrash_lib/src/main/cpp/common/xcc_spot.h @@ -59,10 +59,10 @@ typedef struct int dump_all_threads; unsigned int dump_all_threads_count_max; - //set when crashed (content lenghts after this struct) + //set when crashed (content lengths after this struct) size_t log_pathname_len; - //set when inited (content lenghts after this struct) + //set when inited (content lengths after this struct) size_t os_version_len; size_t kernel_version_len; size_t abi_list_len; From 58c113a056b3b8e467a7b4d31e2b10df86e4f3c7 Mon Sep 17 00:00:00 2001 From: vhow Date: Sun, 29 Aug 2021 10:38:47 +0800 Subject: [PATCH 41/46] remove unnecessary `String.valueOf()` call --- xcrash_lib/src/main/java/xcrash/Util.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/xcrash_lib/src/main/java/xcrash/Util.java b/xcrash_lib/src/main/java/xcrash/Util.java index f180191..7e6b834 100644 --- a/xcrash_lib/src/main/java/xcrash/Util.java +++ b/xcrash_lib/src/main/java/xcrash/Util.java @@ -221,14 +221,14 @@ static String getProcessMemoryInfo() { sb.append(String.format(Locale.US, memInfoFmt2, "TOTAL:", mi.getMemoryStat("summary.total-pss"), "TOTAL SWAP:", mi.getMemoryStat("summary.total-swap"))); } else { sb.append(String.format(Locale.US, memInfoFmt, "Java Heap:", "~ " + mi.dalvikPrivateDirty)); - sb.append(String.format(Locale.US, memInfoFmt, "Native Heap:", String.valueOf(mi.nativePrivateDirty))); + sb.append(String.format(Locale.US, memInfoFmt, "Native Heap:", mi.nativePrivateDirty)); sb.append(String.format(Locale.US, memInfoFmt, "Private Other:", "~ " + mi.otherPrivateDirty)); if (Build.VERSION.SDK_INT >= 19) { - sb.append(String.format(Locale.US, memInfoFmt, "System:", String.valueOf(mi.getTotalPss() - mi.getTotalPrivateDirty() - mi.getTotalPrivateClean()))); + sb.append(String.format(Locale.US, memInfoFmt, "System:", (mi.getTotalPss() - mi.getTotalPrivateDirty() - mi.getTotalPrivateClean()))); } else { sb.append(String.format(Locale.US, memInfoFmt, "System:", "~ " + (mi.getTotalPss() - mi.getTotalPrivateDirty()))); } - sb.append(String.format(Locale.US, memInfoFmt, "TOTAL:", String.valueOf(mi.getTotalPss()))); + sb.append(String.format(Locale.US, memInfoFmt, "TOTAL:", mi.getTotalPss())); } } catch (Exception e) { XCrash.getLogger().i(Util.TAG, "Util getProcessMemoryInfo failed", e); From b27ef0aa4e389a03d7dc46ac23195d63ee1eaa79 Mon Sep 17 00:00:00 2001 From: xuqingqing Date: Mon, 24 Jan 2022 18:08:14 +0800 Subject: [PATCH 42/46] migrate from jcenter to mavenCentral --- build.gradle | 14 ++-- gradle/publish.gradle | 181 +++++++++++++++++----------------------- gradle/sanitizer.gradle | 15 ++-- 3 files changed, 92 insertions(+), 118 deletions(-) diff --git a/build.gradle b/build.gradle index a03e50f..b92e5e7 100644 --- a/build.gradle +++ b/build.gradle @@ -1,19 +1,21 @@ buildscript { repositories { google() - jcenter() + mavenCentral() } + dependencies { classpath 'com.android.tools.build:gradle:4.1.0' - classpath 'digital.wup:android-maven-publish:3.6.2' - classpath 'com.jfrog.bintray.gradle:gradle-bintray-plugin:1.8.4' } } allprojects { repositories { google() - jcenter() +// mavenLocal() + mavenCentral() + maven {url "https://s01.oss.sonatype.org/content/groups/public"} + maven {url "https://s01.oss.sonatype.org/content/repositories/releases"} } } @@ -53,8 +55,4 @@ ext { POM_DEVELOPER_ID = "iQIYI" POM_DEVELOPER_NAME = "iQIYI, Inc." - - BINTRAY_REPO = "maven" - BINTRAY_ORGANIZATION = "xcrash" - BINTRAY_LICENCE = ['MIT'] } diff --git a/gradle/publish.gradle b/gradle/publish.gradle index d837cc3..aad8282 100644 --- a/gradle/publish.gradle +++ b/gradle/publish.gradle @@ -1,38 +1,29 @@ -apply plugin: 'digital.wup.android-maven-publish' -apply plugin: 'com.jfrog.bintray' - -def readPropertyFromLocalProperties(String key) { - Properties properties = new Properties() - try { - properties.load(project.rootProject.file('local.properties').newDataInputStream()) - } catch (Exception e) { - println("load local.properties failed. msg: ${e.message}") +apply plugin: 'maven-publish' +apply plugin: 'signing' + +ext["signing.keyId"] = '' +ext["signing.password"] = '' +ext["signing.secretKeyRingFile"] = '' +ext["ossrhUsername"] = '' +ext["ossrhPassword"] = '' + +File secretPropsFile = project.rootProject.file('local.properties') +if (secretPropsFile.exists()) { + Properties p = new Properties() + p.load(new FileInputStream(secretPropsFile)) + p.each { name, value -> + ext[name] = value } - return properties.getProperty(key) -} - -def getCustomMavenRepo() { - return readPropertyFromLocalProperties('CUSTOM_MAVEN_REPO') -} - -def getCustomMavenUsername() { - return readPropertyFromLocalProperties('CUSTOM_MAVEN_USERNAME') -} - -def getCustomMavenPassword() { - return readPropertyFromLocalProperties('CUSTOM_MAVEN_PASSWORD') -} - -def getBintrayUser() { - return readPropertyFromLocalProperties('BINTRAY_USER') -} - -def getBintrayApikey() { - return readPropertyFromLocalProperties('BINTRAY_APIKEY') +} else { + ext["signing.keyId"] = System.getenv('SIGNING_KEY_ID') + ext["signing.password"] = System.getenv('SIGNING_PASSWORD') + ext["signing.secretKeyRingFile"] = System.getenv('SIGNING_SECRET_KEY_RING_FILE') + ext["ossrhUsername"] = System.getenv('OSSRH_USERNAME') + ext["ossrhPassword"] = System.getenv('OSSRH_PASSWORD') } task sourcesJar(type: Jar) { - classifier = 'sources' + archiveClassifier.set("sources") from android.sourceSets.main.java.srcDirs } @@ -42,88 +33,70 @@ task javadoc(type: Javadoc) { } task javadocJar(type: Jar, dependsOn: javadoc) { - classifier = 'javadoc' + archiveClassifier.set("javadoc") from javadoc.destinationDir } -publishing.publications { - mavenAar(MavenPublication) { - from components.android - - artifact sourcesJar - artifact javadocJar - - artifactId POM_ARTIFACT_ID - groupId POM_GROUP_ID - version POM_VERSION_NAME - - pom { - name = POM_NAME - description = POM_DESCRIPTION - url = POM_URL - inceptionYear = POM_INCEPTION_YEAR - packaging = POM_PACKAGING - scm { - connection = POM_SCM_CONNECTION - url = POM_URL - } - issueManagement { - system = POM_ISSUE_SYSTEM - url = POM_ISSUE_URL - } - licenses { - license { - name = POM_LICENCE_NAME - url = POM_LICENCE_URL - distribution = POM_LICENCE_DIST - } - } - developers { - developer { - id = POM_DEVELOPER_ID - name = POM_DEVELOPER_NAME +project.afterEvaluate { + publishing { + publications { + release(MavenPublication) { + from components.release + + artifact sourcesJar + artifact javadocJar + + artifactId POM_ARTIFACT_ID + groupId POM_GROUP_ID + version POM_VERSION_NAME + + pom { + name = POM_NAME + description = POM_DESCRIPTION + url = POM_URL + inceptionYear = POM_INCEPTION_YEAR + packaging = POM_PACKAGING + scm { + connection = POM_SCM_CONNECTION + url = POM_URL + } + issueManagement { + system = POM_ISSUE_SYSTEM + url = POM_ISSUE_URL + } + licenses { + license { + name = POM_LICENCE_NAME + url = POM_LICENCE_URL + distribution = POM_LICENCE_DIST + } + } + developers { + developer { + id = POM_DEVELOPER_ID + name = POM_DEVELOPER_NAME + } + } } } } - } -} + repositories { + maven { + name = "sonatype" -// custom maven repo -publishing.repositories { - maven { - url getCustomMavenRepo() - credentials { - username getCustomMavenUsername() - password getCustomMavenPassword() - } - } -} - -// bintray maven repo -bintray { - user = getBintrayUser() - key = getBintrayApikey() - publications = ['mavenAar'] - pkg { - repo = BINTRAY_REPO - name = "${POM_GROUP_ID}:${POM_ARTIFACT_ID}" - userOrg = BINTRAY_ORGANIZATION - desc = POM_DESCRIPTION - websiteUrl = POM_URL - issueTrackerUrl = POM_ISSUE_URL - vcsUrl = POM_SCM_CONNECTION - licenses = BINTRAY_LICENCE - publicDownloadNumbers = true - publish = true - dryRun = false + def releasesRepoUrl = "https://s01.oss.sonatype.org/service/local/staging/deploy/maven2/" + def snapshotsRepoUrl = "https://s01.oss.sonatype.org/content/repositories/snapshots/" + url = version.endsWith('SNAPSHOT') ? snapshotsRepoUrl : releasesRepoUrl - version { - name = POM_VERSION_NAME - released = new Date() - vcsTag = POM_VERSION_NAME - gpg { - sign = true + credentials { + username ossrhUsername + password ossrhPassword + } } } } } + +signing { + sign publishing.publications +} diff --git a/gradle/sanitizer.gradle b/gradle/sanitizer.gradle index 3eb4dc4..2867a56 100644 --- a/gradle/sanitizer.gradle +++ b/gradle/sanitizer.gradle @@ -1,6 +1,8 @@ project.afterEvaluate { preBuild.doFirst { + def projectDir = project.projectDir.toString() + if (rootProject.ext.useASAN) { def ndkLibDir = android.ndkDirectory.absolutePath + "/toolchains/llvm/prebuilt/" def ABIs = rootProject.ext.abiFilters.split(",") @@ -18,7 +20,7 @@ project.afterEvaluate { } // create ASAN wrap.sh - def resDir = new File("src/main/resources/lib/" + abi) + def resDir = new File(projectDir + "/src/main/resources/lib/" + abi) resDir.mkdirs() def wrapFile = new File(resDir, "wrap.sh") wrapFile.withWriter { writer -> @@ -36,7 +38,7 @@ project.afterEvaluate { println "sanitizer: [${abi}] create wrap.sh: " + wrapFile.absolutePath // copy ASAN libs - def libDir = new File("src/main/jniLibs/" + abi) + def libDir = new File(projectDir + "/src/main/jniLibs/" + abi) libDir.mkdirs() FileTree tree = fileTree(dir: ndkLibDir).include("**/libclang_rt.asan-${arch}-android.so") tree.each { File file -> @@ -48,13 +50,14 @@ project.afterEvaluate { } } } else { - delete 'src/main/resources/' - delete 'src/main/jniLibs/' + delete projectDir + '/src/main/resources/' + delete projectDir + '/src/main/jniLibs/' } } } clean.doFirst { - delete 'src/main/resources/' - delete 'src/main/jniLibs/' + def projectDir = project.projectDir.toString() + delete projectDir + '/src/main/resources/' + delete projectDir + '/src/main/jniLibs/' } From 16dff882f9967f58e9415808456492fcf8cb20bf Mon Sep 17 00:00:00 2001 From: xuqingqing Date: Mon, 24 Jan 2022 18:52:08 +0800 Subject: [PATCH 43/46] add anr trace fast callback, avoid to call getRunningAppProcesses, etc --- build.gradle | 8 +- xcrash_lib/proguard-rules.pro | 1 + xcrash_lib/src/main/cpp/common/xcc_version.h | 2 +- xcrash_lib/src/main/cpp/xcrash/xc_trace.c | 15 +- .../src/main/java/xcrash/AnrHandler.java | 11 +- .../src/main/java/xcrash/NativeHandler.java | 27 ++- xcrash_lib/src/main/java/xcrash/Util.java | 154 +++++++++++++++--- xcrash_lib/src/main/java/xcrash/Version.java | 2 +- xcrash_lib/src/main/java/xcrash/XCrash.java | 23 ++- xcrash_sample/build.gradle | 2 +- xcrash_sample/src/main/AndroidManifest.xml | 6 +- .../xcrash/sample/MyCustomApplication.java | 11 +- 12 files changed, 224 insertions(+), 38 deletions(-) diff --git a/build.gradle b/build.gradle index b92e5e7..d7bfef5 100644 --- a/build.gradle +++ b/build.gradle @@ -25,10 +25,10 @@ task clean(type: Delete) { ext { minSdkVersion = 16 - compileSdkVersion = 30 - targetSdkVersion = 30 + compileSdkVersion = 31 + targetSdkVersion = 31 buildToolsVersion = '30.0.2' - javaVersion = JavaVersion.VERSION_1_6 + javaVersion = JavaVersion.VERSION_1_8 ndkVersion = "21.3.6528147" cmakeVersion = "3.10.2" abiFilters = "armeabi-v7a,arm64-v8a,x86,x86_64" @@ -36,7 +36,7 @@ ext { POM_GROUP_ID = "com.iqiyi.xcrash" POM_ARTIFACT_ID = "xcrash-android-lib" - POM_VERSION_NAME = "3.0.0" + POM_VERSION_NAME = "3.1.0" POM_NAME = "xCrash Android Lib" POM_DESCRIPTION = "xCrash provides the Android app with the ability to capture java crash, native crash and ANR." diff --git a/xcrash_lib/proguard-rules.pro b/xcrash_lib/proguard-rules.pro index f001c9f..aed903f 100644 --- a/xcrash_lib/proguard-rules.pro +++ b/xcrash_lib/proguard-rules.pro @@ -2,4 +2,5 @@ native ; void crashCallback(...); void traceCallback(...); + void traceCallbackBeforeDump(...); } diff --git a/xcrash_lib/src/main/cpp/common/xcc_version.h b/xcrash_lib/src/main/cpp/common/xcc_version.h index 415be1c..7791bf5 100644 --- a/xcrash_lib/src/main/cpp/common/xcc_version.h +++ b/xcrash_lib/src/main/cpp/common/xcc_version.h @@ -24,6 +24,6 @@ #ifndef XCC_VERSION_H #define XCC_VERSION_H 1 -#define XCC_VERSION_STR "xCrash 3.0.0" +#define XCC_VERSION_STR "xCrash 3.1.0" #endif diff --git a/xcrash_lib/src/main/cpp/xcrash/xc_trace.c b/xcrash_lib/src/main/cpp/xcrash/xc_trace.c index dc9cbe3..6a88003 100644 --- a/xcrash_lib/src/main/cpp/xcrash/xc_trace.c +++ b/xcrash_lib/src/main/cpp/xcrash/xc_trace.c @@ -49,6 +49,10 @@ #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wgnu-statement-expression" + +#define XC_TRACE_FAST_CALLBACK_METHOD_NAME "traceCallbackBeforeDump" +#define XC_TRACE_FAST_CALLBACK_METHOD_SIGNATURE "()V" + #define XC_TRACE_CALLBACK_METHOD_NAME "traceCallback" #define XC_TRACE_CALLBACK_METHOD_SIGNATURE "(Ljava/lang/String;Ljava/lang/String;)V" @@ -78,6 +82,7 @@ static int xc_trace_dump_fds; static int xc_trace_dump_network_info; //callback +static jmethodID xc_trace_fast_cb_method = NULL; static jmethodID xc_trace_cb_method = NULL; static int xc_trace_notifier = -1; @@ -314,6 +319,10 @@ static void *xc_trace_dumper(void *arg) //check if process already crashed if(xc_common_native_crashed || xc_common_java_crashed) break; + if(NULL == xc_trace_fast_cb_method) continue; + (*env)->CallStaticVoidMethod(env, xc_common_cb_class, xc_trace_fast_cb_method); + XC_JNI_IGNORE_PENDING_EXCEPTION(); + //trace time if(0 != gettimeofday(&tv, NULL)) break; trace_time = (uint64_t)(tv.tv_sec) * 1000 * 1000 + (uint64_t)tv.tv_usec; @@ -417,12 +426,16 @@ static void xc_trace_handler(int sig, siginfo_t *si, void *uc) static void xc_trace_init_callback(JNIEnv *env) { if(NULL == xc_common_cb_class) return; - + + xc_trace_fast_cb_method = (*env)->GetStaticMethodID(env, xc_common_cb_class, XC_TRACE_FAST_CALLBACK_METHOD_NAME, XC_TRACE_FAST_CALLBACK_METHOD_SIGNATURE); + XC_JNI_CHECK_NULL_AND_PENDING_EXCEPTION(xc_trace_fast_cb_method, err); + xc_trace_cb_method = (*env)->GetStaticMethodID(env, xc_common_cb_class, XC_TRACE_CALLBACK_METHOD_NAME, XC_TRACE_CALLBACK_METHOD_SIGNATURE); XC_JNI_CHECK_NULL_AND_PENDING_EXCEPTION(xc_trace_cb_method, err); return; err: + xc_trace_fast_cb_method = NULL; xc_trace_cb_method = NULL; } diff --git a/xcrash_lib/src/main/java/xcrash/AnrHandler.java b/xcrash_lib/src/main/java/xcrash/AnrHandler.java index 4e03c5c..fbb3850 100644 --- a/xcrash_lib/src/main/java/xcrash/AnrHandler.java +++ b/xcrash_lib/src/main/java/xcrash/AnrHandler.java @@ -62,6 +62,7 @@ class AnrHandler { private boolean dumpFds; private boolean dumpNetworkInfo; private ICrashCallback callback; + private ICrashCallback anrFastCallback; private long lastTime = 0; private FileObserver fileObserver = null; @@ -75,7 +76,7 @@ static AnrHandler getInstance() { @SuppressWarnings("deprecation") void initialize(Context ctx, int pid, String processName, String appId, String appVersion, String logDir, boolean checkProcessState, int logcatSystemLines, int logcatEventsLines, int logcatMainLines, - boolean dumpFds, boolean dumpNetworkInfo, ICrashCallback callback) { + boolean dumpFds, boolean dumpNetworkInfo, ICrashCallback callback, ICrashCallback anrFastCallback) { //check API level if (Build.VERSION.SDK_INT >= 21) { @@ -95,6 +96,7 @@ void initialize(Context ctx, int pid, String processName, String appId, String a this.dumpFds = dumpFds; this.dumpNetworkInfo = dumpNetworkInfo; this.callback = callback; + this.anrFastCallback = anrFastCallback; fileObserver = new FileObserver("/data/anr/", CLOSE_WRITE) { public void onEvent(int event, String path) { @@ -139,6 +141,13 @@ private void handleAnr(String filepath) { return; } + if(anrFastCallback != null) { + try { + anrFastCallback.onCrash(null, null); + } catch (Exception ignored) { + } + } + //check process error state if (this.checkProcessState) { if (!Util.checkProcessAnrState(this.ctx, anrTimeoutMs)) { diff --git a/xcrash_lib/src/main/java/xcrash/NativeHandler.java b/xcrash_lib/src/main/java/xcrash/NativeHandler.java index c2396bd..f41c68a 100644 --- a/xcrash_lib/src/main/java/xcrash/NativeHandler.java +++ b/xcrash_lib/src/main/java/xcrash/NativeHandler.java @@ -26,6 +26,7 @@ import android.content.Context; import android.os.Build; import android.text.TextUtils; +import android.util.Log; import java.io.File; import java.util.Map; @@ -33,8 +34,9 @@ @SuppressLint("StaticFieldLeak") class NativeHandler { + private static final String TAG = "xcrash"; private static final NativeHandler instance = new NativeHandler(); - private long anrTimeoutMs = 15 * 1000; + private long anrTimeoutMs = 25 * 1000; private Context ctx; private boolean crashRethrow; @@ -42,6 +44,7 @@ class NativeHandler { private boolean anrEnable; private boolean anrCheckProcessState; private ICrashCallback anrCallback; + private ICrashCallback anrFastCallback; private boolean initNativeLibOk = false; @@ -78,7 +81,8 @@ int initialize(Context ctx, int anrLogcatMainLines, boolean anrDumpFds, boolean anrDumpNetworkInfo, - ICrashCallback anrCallback) { + ICrashCallback anrCallback, + ICrashCallback anrFastCallback) { //load lib if (libLoader == null) { try { @@ -102,7 +106,8 @@ int initialize(Context ctx, this.anrEnable = anrEnable; this.anrCheckProcessState = anrCheckProcessState; this.anrCallback = anrCallback; - this.anrTimeoutMs = anrRethrow ? 15 * 1000 : 30 * 1000; //setting rethrow to "false" is NOT recommended + this.anrFastCallback = anrFastCallback; + this.anrTimeoutMs = anrRethrow ? 25 * 1000 : 45 * 1000; //setting rethrow to "false" is NOT recommended //init native lib try { @@ -214,9 +219,25 @@ private static void crashCallback(String logPath, String emergency, boolean dump } } + // do NOT obfuscate this method + @SuppressWarnings("unused") + private static void traceCallbackBeforeDump() { + Log.i(TAG, "trace fast callback time: " + System.currentTimeMillis()); + ICrashCallback anrFastCallback = NativeHandler.getInstance().anrFastCallback; + if (anrFastCallback != null) { + try { + anrFastCallback.onCrash(null, null); + } catch (Exception e) { + XCrash.getLogger().w(Util.TAG, "NativeHandler ANR callback.onCrash failed", e); + } + } + } + // do NOT obfuscate this method @SuppressWarnings("unused") private static void traceCallback(String logPath, String emergency) { + Log.i(TAG, "trace slow callback time: " + System.currentTimeMillis()); + if (TextUtils.isEmpty(logPath)) { return; } diff --git a/xcrash_lib/src/main/java/xcrash/Util.java b/xcrash_lib/src/main/java/xcrash/Util.java index f180191..65d99bd 100644 --- a/xcrash_lib/src/main/java/xcrash/Util.java +++ b/xcrash_lib/src/main/java/xcrash/Util.java @@ -28,6 +28,7 @@ import android.os.Debug; import android.system.Os; import android.text.TextUtils; +import android.util.Log; import java.io.BufferedReader; import java.io.File; @@ -76,20 +77,20 @@ private Util() { static String getProcessName(Context ctx, int pid) { //get from ActivityManager - try { - ActivityManager manager = (ActivityManager) ctx.getSystemService(Context.ACTIVITY_SERVICE); - if (manager != null) { - List processInfoList = manager.getRunningAppProcesses(); - if (processInfoList != null) { - for (ActivityManager.RunningAppProcessInfo processInfo : processInfoList) { - if (processInfo.pid == pid && !TextUtils.isEmpty(processInfo.processName)) { - return processInfo.processName; //OK - } - } - } - } - } catch (Exception ignored) { - } +// try { +// ActivityManager manager = (ActivityManager) ctx.getSystemService(Context.ACTIVITY_SERVICE); +// if (manager != null) { +// List processInfoList = manager.getRunningAppProcesses(); +// if (processInfoList != null) { +// for (ActivityManager.RunningAppProcessInfo processInfo : processInfoList) { +// if (processInfo.pid == pid && !TextUtils.isEmpty(processInfo.processName)) { +// return processInfo.processName; //OK +// } +// } +// } +// } +// } catch (Exception ignored) { +// } //get from /proc/PID/cmdline BufferedReader br = null; @@ -298,11 +299,15 @@ static boolean checkProcessAnrState(Context ctx, long timeoutMs) { for (int i = 0; i < poll; i++) { List processErrorList = am.getProcessesInErrorState(); if (processErrorList != null) { + XCrash.getLogger().e(Util.TAG, "processErrorList is NOT null !!!!" + ", i = " + i); for (ActivityManager.ProcessErrorStateInfo errorStateInfo : processErrorList) { + XCrash.getLogger().e(Util.TAG, "errorStateInfo.pid = " + errorStateInfo.pid + ", my pid = " + pid + ", errorStateInfo.condition = " + errorStateInfo.condition); if (errorStateInfo.pid == pid && errorStateInfo.condition == ActivityManager.ProcessErrorStateInfo.NOT_RESPONDING) { return true; } } + } else { + XCrash.getLogger().e(Util.TAG, "processErrorList is null !!!!" + " poll = " + poll + ", i = " + i); } try { @@ -515,14 +520,9 @@ public static String getSystemProperty(String key, String defaultValue) { return defaultValue; } - public static boolean isMIUI() { - String property = getSystemProperty("ro.miui.ui.version.name", ""); - return !TextUtils.isEmpty(property); - } - public static String getMobileModel() { String mobileModel = null; - if (isMIUI()) { + if (Rom.isMiui()) { String deviceName = ""; try { @@ -553,4 +553,118 @@ public static String getMobileModel() { return mobileModel; } + + + public static class Rom { + private static final String TAG = "Rom"; + + public static final String ROM_MIUI = "MIUI"; + public static final String ROM_EMUI = "EMUI"; + public static final String ROM_FLYME = "FLYME"; + public static final String ROM_OPPO = "OPPO"; + public static final String ROM_SMARTISAN = "SMARTISAN"; + public static final String ROM_VIVO = "VIVO"; + public static final String ROM_QIKU = "QIKU"; + + private static final String KEY_VERSION_MIUI = "ro.miui.ui.version.name"; + private static final String KEY_VERSION_EMUI = "ro.build.version.emui"; + private static final String KEY_VERSION_OPPO = "ro.build.version.opporom"; + private static final String KEY_VERSION_SMARTISAN = "ro.smartisan.version"; + private static final String KEY_VERSION_VIVO = "ro.vivo.os.version"; + + private static String sName; + private static String sVersion; + + public static boolean isEmui() { + return check(ROM_EMUI); + } + + public static boolean isMiui() { + return check(ROM_MIUI); + } + + public static boolean isVivo() { + return check(ROM_VIVO); + } + + public static boolean isOppo() { + return check(ROM_OPPO); + } + + public static boolean isFlyme() { + return check(ROM_FLYME); + } + + public static boolean is360() { + return check(ROM_QIKU) || check("360"); + } + + public static boolean isSmartisan() { + return check(ROM_SMARTISAN); + } + + public static String getName() { + if (sName == null) { + check(""); + } + return sName; + } + + public static String getVersion() { + if (sVersion == null) { + check(""); + } + return sVersion; + } + + public static boolean check(String rom) { + if (sName != null) { + return sName.equals(rom); + } + + if (!TextUtils.isEmpty(sVersion = getProp(KEY_VERSION_MIUI))) { + sName = ROM_MIUI; + } else if (!TextUtils.isEmpty(sVersion = getProp(KEY_VERSION_EMUI))) { + sName = ROM_EMUI; + } else if (!TextUtils.isEmpty(sVersion = getProp(KEY_VERSION_OPPO))) { + sName = ROM_OPPO; + } else if (!TextUtils.isEmpty(sVersion = getProp(KEY_VERSION_VIVO))) { + sName = ROM_VIVO; + } else if (!TextUtils.isEmpty(sVersion = getProp(KEY_VERSION_SMARTISAN))) { + sName = ROM_SMARTISAN; + } else { + sVersion = Build.DISPLAY; + if (sVersion.toUpperCase().contains(ROM_FLYME)) { + sName = ROM_FLYME; + } else { + sVersion = Build.UNKNOWN; + sName = Build.MANUFACTURER.toUpperCase(); + } + } + return sName.equals(rom); + } + + public static String getProp(String name) { + String line = null; + BufferedReader input = null; + try { + Process p = Runtime.getRuntime().exec("getprop " + name); + input = new BufferedReader(new InputStreamReader(p.getInputStream()), 1024); + line = input.readLine(); + input.close(); + } catch (IOException ex) { + Log.e(TAG, "Unable to read prop " + name, ex); + return null; + } finally { + if (input != null) { + try { + input.close(); + } catch (IOException e) { + e.printStackTrace(); + } + } + } + return line; + } + } } diff --git a/xcrash_lib/src/main/java/xcrash/Version.java b/xcrash_lib/src/main/java/xcrash/Version.java index 041b30b..afcb82e 100644 --- a/xcrash_lib/src/main/java/xcrash/Version.java +++ b/xcrash_lib/src/main/java/xcrash/Version.java @@ -27,6 +27,6 @@ class Version { private Version() { } - static final String version = "3.0.0"; + static final String version = "3.1.0"; static final String fullVersion = "xCrash " + version; } diff --git a/xcrash_lib/src/main/java/xcrash/XCrash.java b/xcrash_lib/src/main/java/xcrash/XCrash.java index bf3f622..73821b0 100644 --- a/xcrash_lib/src/main/java/xcrash/XCrash.java +++ b/xcrash_lib/src/main/java/xcrash/XCrash.java @@ -179,7 +179,8 @@ public static synchronized int init(Context ctx, InitParameters params) { params.anrLogcatMainLines, params.anrDumpFds, params.anrDumpNetworkInfo, - params.anrCallback); + params.anrCallback, + params.anrFastCallback); } //init native crash handler / ANR handler (API level >= 21) @@ -212,7 +213,8 @@ public static synchronized int init(Context ctx, InitParameters params) { params.anrLogcatMainLines, params.anrDumpFds, params.anrDumpNetworkInfo, - params.anrCallback); + params.anrCallback, + params.anrFastCallback); } //maintain tombstone and placeholder files in a background thread with some delay @@ -716,6 +718,7 @@ public InitParameters setNativeCallback(ICrashCallback callback) { boolean anrDumpFds = true; boolean anrDumpNetworkInfo = true; ICrashCallback anrCallback = null; + ICrashCallback anrFastCallback = null; /** * Enable the ANR capture feature. (Default: enable) @@ -759,7 +762,7 @@ public InitParameters setAnrRethrow(boolean rethrow) { /** * Set whether the process error state (from "ActivityManager#getProcessesInErrorState()") is a necessary condition for ANR. (Default: true) * - *

    Note: On some Android TV box devices, the ANR is not reflected by process error state. In this case, set this option to false. + *

    Note: On some Android TV box devices and on most Oppo phones, the ANR is not reflected by process error state. In this case, set this option to false. * * @param checkProcessState If true, process state error will be a necessary condition for ANR. * @return The InitParameters object. @@ -853,6 +856,20 @@ public InitParameters setAnrCallback(ICrashCallback callback) { this.anrCallback = callback; return this; } + + /** + * Set a fast callback to be executed when an ANR occurred. + * This callback is called before ANR trace dump + * (If not set, nothing will be happened.) + * + * @param fastCallback An instance of {@link xcrash.ICrashCallback}. + * @return The InitParameters object. + */ + @SuppressWarnings("unused") + public InitParameters setAnrFastCallback(ICrashCallback fastCallback) { + this.anrFastCallback = fastCallback; + return this; + } } static String getAppId() { diff --git a/xcrash_sample/build.gradle b/xcrash_sample/build.gradle index 3e9c27a..e578f86 100644 --- a/xcrash_sample/build.gradle +++ b/xcrash_sample/build.gradle @@ -34,7 +34,7 @@ dependencies { implementation fileTree(dir: 'libs', include: ['*.jar']) implementation 'androidx.appcompat:appcompat:1.2.0' implementation 'androidx.constraintlayout:constraintlayout:2.0.1' - //implementation 'com.iqiyi.xcrash:xcrash-android-lib:3.0.0' +// implementation 'com.iqiyi.xcrash:xcrash-android-lib:3.1.0' implementation project(':xcrash_lib') } diff --git a/xcrash_sample/src/main/AndroidManifest.xml b/xcrash_sample/src/main/AndroidManifest.xml index e25f77e..7e70e04 100644 --- a/xcrash_sample/src/main/AndroidManifest.xml +++ b/xcrash_sample/src/main/AndroidManifest.xml @@ -13,7 +13,8 @@ android:theme="@style/AppTheme" tools:ignore="AllowBackup,GoogleAppIndexingWarning"> - + @@ -21,7 +22,8 @@ - + Date: Tue, 26 Apr 2022 01:57:29 +0800 Subject: [PATCH 44/46] Upgrade readme. --- README.md | 10 ++++------ README.zh-CN.md | 10 ++++------ doc/qq_group.jpg | Bin 54650 -> 0 bytes 3 files changed, 8 insertions(+), 12 deletions(-) delete mode 100644 doc/qq_group.jpg diff --git a/README.md b/README.md index 79da5be..8c14694 100644 --- a/README.md +++ b/README.md @@ -126,12 +126,10 @@ If you want to build xCrash from source code. Follow this guide: ## Support -1. Check the [xcrash-sample](xcrash_sample). -2. Communicate on [GitHub issues](https://github.com/iqiyi/xCrash/issues). -3. Email: caikelun@gmail.com   xuqnqn@qq.com -4. QQ group: 603635869. QR code: - -

    qq group

    +* [GitHub Issues](https://github.com/iqiyi/xCrash/issues) +* [GitHub Discussions](https://github.com/iqiyi/xCrash/discussions) +* [Telegram Public Group](https://t.me/android_native_geeks) +* Email: caikelun@gmail.com, xuqnqn@qq.com ## Contributing diff --git a/README.zh-CN.md b/README.zh-CN.md index 395a795..054bc6e 100644 --- a/README.zh-CN.md +++ b/README.zh-CN.md @@ -123,12 +123,10 @@ Tombstone 文件默认将被写入到 `Context#getFilesDir() + "/tombstones"` ## 技术支持 -1. 查看 [xcrash-sample](xcrash_sample)。 -2. 在 [GitHub issues](https://github.com/iqiyi/xCrash/issues) 交流。 -3. 邮件: caikelun@gmail.com   xuqnqn@qq.com -4. QQ 群: 603635869。二维码: - -

    qq group

    +* [GitHub Issues](https://github.com/iqiyi/xCrash/issues) +* [GitHub Discussions](https://github.com/iqiyi/xCrash/discussions) +* [Telegram Public Group](https://t.me/android_native_geeks) +* Email: caikelun@gmail.com, xuqnqn@qq.com ## 贡献 diff --git a/doc/qq_group.jpg b/doc/qq_group.jpg deleted file mode 100644 index 0b9ccbb1af48bedbd20718f58e6648e7b4e3850c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 54650 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 Y{!c>=4 Date: Fri, 27 Jun 2025 13:36:15 +0800 Subject: [PATCH 45/46] Update README.md --- README.md | 1 - 1 file changed, 1 deletion(-) diff --git a/README.md b/README.md index 8c14694..f54bc95 100644 --- a/README.md +++ b/README.md @@ -128,7 +128,6 @@ If you want to build xCrash from source code. Follow this guide: * [GitHub Issues](https://github.com/iqiyi/xCrash/issues) * [GitHub Discussions](https://github.com/iqiyi/xCrash/discussions) -* [Telegram Public Group](https://t.me/android_native_geeks) * Email: caikelun@gmail.com, xuqnqn@qq.com From 86dea4fa0cdbae68b63e9b34436fa20b51244d11 Mon Sep 17 00:00:00 2001 From: Kelun Cai Date: Fri, 27 Jun 2025 13:36:39 +0800 Subject: [PATCH 46/46] Update README.zh-CN.md --- README.zh-CN.md | 1 - 1 file changed, 1 deletion(-) diff --git a/README.zh-CN.md b/README.zh-CN.md index 054bc6e..69d8521 100644 --- a/README.zh-CN.md +++ b/README.zh-CN.md @@ -125,7 +125,6 @@ Tombstone 文件默认将被写入到 `Context#getFilesDir() + "/tombstones"` * [GitHub Issues](https://github.com/iqiyi/xCrash/issues) * [GitHub Discussions](https://github.com/iqiyi/xCrash/discussions) -* [Telegram Public Group](https://t.me/android_native_geeks) * Email: caikelun@gmail.com, xuqnqn@qq.com

    RUzA>ti@;5G2#=r?vgJWw>&7$4Z}{P^f0=&H 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 diff --git a/doc/xcrash_logo.png b/doc/xcrash_logo.png new file mode 100644 index 0000000000000000000000000000000000000000..96e53b91fd1806a23ed4187fe6d342d8334f66d3 GIT binary patch literal 17931 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 FV1hC)5ySuh literal 0 HcmV?d00001 diff --git a/doc/xcrash_logo.svg b/doc/xcrash_logo.svg new file mode 100644 index 0000000..b41f605 --- /dev/null +++ b/doc/xcrash_logo.svg @@ -0,0 +1,165 @@ + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + xCrash + + + + + + xCrash + + diff --git a/doc/xcrash_logo2.png b/doc/xcrash_logo2.png new file mode 100644 index 0000000000000000000000000000000000000000..edc10cc1bf2a83d29ef0f085314d34a7a82c3846 GIT binary patch literal 62120 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 Date: Thu, 19 Dec 2019 12:25:34 +0800 Subject: [PATCH 02/46] [bug] Fix FD info error in java crash tombstone. When API level is greater than or equal to 21, use android.system.Os.readlink() instead of java.io.File#getCanonicalPath(). --- .../xcrash/xcrash_lib/src/main/java/xcrash/Util.java | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/java/xcrash/xcrash_lib/src/main/java/xcrash/Util.java b/src/java/xcrash/xcrash_lib/src/main/java/xcrash/Util.java index 2303097..4f2b197 100644 --- a/src/java/xcrash/xcrash_lib/src/main/java/xcrash/Util.java +++ b/src/java/xcrash/xcrash_lib/src/main/java/xcrash/Util.java @@ -26,6 +26,7 @@ import android.content.Context; import android.os.Build; import android.os.Debug; +import android.system.Os; import android.text.TextUtils; import java.io.BufferedReader; @@ -363,11 +364,15 @@ public boolean accept(File dir, String name) { for (File fd : fds) { String path = null; try { - path = fd.getCanonicalPath(); + if (Build.VERSION.SDK_INT >= 21) { + path = Os.readlink(fd.getAbsolutePath()); + } else { + path = fd.getCanonicalPath(); + } } catch (Exception ignored) { } sb.append(" fd ").append(fd.getName()).append(": ") - .append(TextUtils.isEmpty(path) ? "???" : path).append('\n'); + .append(TextUtils.isEmpty(path) ? "???" : path.trim()).append('\n'); count++; if (count > 1024) { From 4748d183c1395c54bfb760ec6c454966d52ab73f Mon Sep 17 00:00:00 2001 From: CAI Kelun Date: Thu, 19 Dec 2019 14:18:40 +0800 Subject: [PATCH 03/46] [improve] Upgrade Android Gradle plugin from 3.5.2 to 3.5.3. [version] 2.4.9. --- README.md | 4 ++-- README.zh-CN.md | 4 ++-- src/java/xcrash/build.gradle | 4 ++-- src/java/xcrash/xcrash_lib/src/main/java/xcrash/Version.java | 2 +- src/java/xcrash/xcrash_sample/build.gradle | 2 +- src/native/common/xcc_version.h | 2 +- 6 files changed, 9 insertions(+), 9 deletions(-) diff --git a/README.md b/README.md index 0baa36b..299ddb2 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ ![](https://img.shields.io/badge/license-MIT-brightgreen.svg?style=flat) ![](https://img.shields.io/badge/PRs-welcome-brightgreen.svg?style=flat) -![](https://img.shields.io/badge/release-2.4.8-red.svg?style=flat) +![](https://img.shields.io/badge/release-2.4.9-red.svg?style=flat) ![](https://img.shields.io/badge/Android-4.0%20--%2010-blue.svg?style=flat) ![](https://img.shields.io/badge/arch-armeabi%20%7C%20armeabi--v7a%20%7C%20arm64--v8a%20%7C%20x86%20%7C%20x86__64-blue.svg?style=flat) @@ -64,7 +64,7 @@ xCrash has been used in many Android apps (including iQIYI video) on different p ```Gradle dependencies { - implementation 'com.iqiyi.xcrash:xcrash-android-lib:2.4.8' + implementation 'com.iqiyi.xcrash:xcrash-android-lib:2.4.9' } ``` diff --git a/README.zh-CN.md b/README.zh-CN.md index 8fded5d..516e701 100644 --- a/README.zh-CN.md +++ b/README.zh-CN.md @@ -4,7 +4,7 @@ ![](https://img.shields.io/badge/license-MIT-brightgreen.svg?style=flat) ![](https://img.shields.io/badge/PRs-welcome-brightgreen.svg?style=flat) -![](https://img.shields.io/badge/release-2.4.8-red.svg?style=flat) +![](https://img.shields.io/badge/release-2.4.9-red.svg?style=flat) ![](https://img.shields.io/badge/Android-4.0%20--%2010-blue.svg?style=flat) ![](https://img.shields.io/badge/arch-armeabi%20%7C%20armeabi--v7a%20%7C%20arm64--v8a%20%7C%20x86%20%7C%20x86__64-blue.svg?style=flat) @@ -64,7 +64,7 @@ xCrash 已经在 [爱奇艺](http://www.iqiyi.com/) 的不同平台(手机, ```Gradle dependencies { - implementation 'com.iqiyi.xcrash:xcrash-android-lib:2.4.8' + implementation 'com.iqiyi.xcrash:xcrash-android-lib:2.4.9' } ``` diff --git a/src/java/xcrash/build.gradle b/src/java/xcrash/build.gradle index f68141c..ea9cccd 100644 --- a/src/java/xcrash/build.gradle +++ b/src/java/xcrash/build.gradle @@ -4,7 +4,7 @@ buildscript { jcenter() } dependencies { - classpath 'com.android.tools.build:gradle:3.5.2' + classpath 'com.android.tools.build:gradle:3.5.3' classpath 'digital.wup:android-maven-publish:3.6.2' classpath 'com.jfrog.bintray.gradle:gradle-bintray-plugin:1.8.4' } @@ -30,7 +30,7 @@ ext { POM_GROUP_ID = "com.iqiyi.xcrash" POM_ARTIFACT_ID = "xcrash-android-lib" - POM_VERSION_NAME = "2.4.8" + POM_VERSION_NAME = "2.4.9" POM_NAME = "xCrash Android Lib" POM_DESCRIPTION = "xCrash provides the Android app with the ability to capture java crash, native crash and ANR." diff --git a/src/java/xcrash/xcrash_lib/src/main/java/xcrash/Version.java b/src/java/xcrash/xcrash_lib/src/main/java/xcrash/Version.java index eecdb0e..126dc91 100644 --- a/src/java/xcrash/xcrash_lib/src/main/java/xcrash/Version.java +++ b/src/java/xcrash/xcrash_lib/src/main/java/xcrash/Version.java @@ -27,6 +27,6 @@ class Version { private Version() { } - static final String version = "2.4.8"; + static final String version = "2.4.9"; static final String fullVersion = "xCrash " + version; } diff --git a/src/java/xcrash/xcrash_sample/build.gradle b/src/java/xcrash/xcrash_sample/build.gradle index 35ba618..5e51758 100644 --- a/src/java/xcrash/xcrash_sample/build.gradle +++ b/src/java/xcrash/xcrash_sample/build.gradle @@ -33,6 +33,6 @@ dependencies { implementation fileTree(dir: 'libs', include: ['*.jar']) implementation 'androidx.appcompat:appcompat:1.1.0' implementation 'androidx.constraintlayout:constraintlayout:1.1.3' - //implementation 'com.iqiyi.xcrash:xcrash-android-lib:2.4.8' + //implementation 'com.iqiyi.xcrash:xcrash-android-lib:2.4.9' implementation project(':xcrash_lib') } diff --git a/src/native/common/xcc_version.h b/src/native/common/xcc_version.h index 9127531..8918b59 100644 --- a/src/native/common/xcc_version.h +++ b/src/native/common/xcc_version.h @@ -24,6 +24,6 @@ #ifndef XCC_VERSION_H #define XCC_VERSION_H 1 -#define XCC_VERSION_STR "xCrash 2.4.8" +#define XCC_VERSION_STR "xCrash 2.4.9" #endif From f33f65d4cdbeebddd7885be7b5b94537cdb1f897 Mon Sep 17 00:00:00 2001 From: yueming Date: Wed, 8 Jan 2020 16:35:56 +0800 Subject: [PATCH 04/46] [version] 2.5.0. Upgrading to NDK r20b. Change minSdkVersion to 16. Remove armeabi support. --- README.md | 2 +- README.zh-CN.md | 2 +- src/java/xcrash/build.gradle | 4 ++-- src/java/xcrash/xcrash_sample/build.gradle | 2 +- src/native/install.sh | 4 ++-- src/native/libxcrash/jni/Application.mk | 4 ++-- src/native/libxcrash_dumper/jni/Application.mk | 4 ++-- 7 files changed, 11 insertions(+), 11 deletions(-) diff --git a/README.md b/README.md index 299ddb2..2233d59 100644 --- a/README.md +++ b/README.md @@ -118,7 +118,7 @@ There is a more practical and complex sample app in the [src/java/xcrash/xcrash_ If you want to build xCrash from source code. Follow this guide: -#### 1. Download [Android NDK r16b](https://developer.android.com/ndk/downloads/revision_history.html), set PATH environment. +#### 1. Download [Android NDK r20b](https://developer.android.com/ndk/downloads/revision_history.html), set PATH environment. #### 2. Build and copy the native libraries. diff --git a/README.zh-CN.md b/README.zh-CN.md index 516e701..f7dfbc4 100644 --- a/README.zh-CN.md +++ b/README.zh-CN.md @@ -118,7 +118,7 @@ Tombstone 文件默认将被写入到 `Context#getFilesDir() + "/tombstones"` 如果你想编译 xCrash 的源码。请按以下步骤进行: -#### 1. 下载 [Android NDK r16b](https://developer.android.com/ndk/downloads/revision_history.html),设置 PATH 环境变量。 +#### 1. 下载 [Android NDK r20b](https://developer.android.com/ndk/downloads/revision_history.html),设置 PATH 环境变量。 #### 2. 编译和复制 native 库。 diff --git a/src/java/xcrash/build.gradle b/src/java/xcrash/build.gradle index ea9cccd..92b9259 100644 --- a/src/java/xcrash/build.gradle +++ b/src/java/xcrash/build.gradle @@ -22,7 +22,7 @@ task clean(type: Delete) { } ext { - minSdkVersion = 14 + minSdkVersion = 16 compileSdkVersion = 29 targetSdkVersion = 29 buildToolsVersion = '29.0.2' @@ -30,7 +30,7 @@ ext { POM_GROUP_ID = "com.iqiyi.xcrash" POM_ARTIFACT_ID = "xcrash-android-lib" - POM_VERSION_NAME = "2.4.9" + POM_VERSION_NAME = "2.5.0" POM_NAME = "xCrash Android Lib" POM_DESCRIPTION = "xCrash provides the Android app with the ability to capture java crash, native crash and ANR." diff --git a/src/java/xcrash/xcrash_sample/build.gradle b/src/java/xcrash/xcrash_sample/build.gradle index 5e51758..6860012 100644 --- a/src/java/xcrash/xcrash_sample/build.gradle +++ b/src/java/xcrash/xcrash_sample/build.gradle @@ -10,7 +10,7 @@ android { versionCode 1 versionName "1.0" ndk { - abiFilters 'armeabi', 'armeabi-v7a', 'arm64-v8a', 'x86', 'x86_64' + abiFilters 'armeabi-v7a', 'arm64-v8a', 'x86', 'x86_64' } } compileOptions { diff --git a/src/native/install.sh b/src/native/install.sh index dedd136..cefc210 100755 --- a/src/native/install.sh +++ b/src/native/install.sh @@ -6,13 +6,13 @@ mkdir -p ../java/xcrash/xcrash_lib/src/main/jniLibs/arm64-v8a mkdir -p ../java/xcrash/xcrash_lib/src/main/jniLibs/x86 mkdir -p ../java/xcrash/xcrash_lib/src/main/jniLibs/x86_64 -cp -f ./libxcrash/libs/armeabi/libxcrash.so ../java/xcrash/xcrash_lib/src/main/jniLibs/armeabi/libxcrash.so +#cp -f ./libxcrash/libs/armeabi/libxcrash.so ../java/xcrash/xcrash_lib/src/main/jniLibs/armeabi/libxcrash.so cp -f ./libxcrash/libs/armeabi-v7a/libxcrash.so ../java/xcrash/xcrash_lib/src/main/jniLibs/armeabi-v7a/libxcrash.so cp -f ./libxcrash/libs/arm64-v8a/libxcrash.so ../java/xcrash/xcrash_lib/src/main/jniLibs/arm64-v8a/libxcrash.so cp -f ./libxcrash/libs/x86/libxcrash.so ../java/xcrash/xcrash_lib/src/main/jniLibs/x86/libxcrash.so cp -f ./libxcrash/libs/x86_64/libxcrash.so ../java/xcrash/xcrash_lib/src/main/jniLibs/x86_64/libxcrash.so -cp -f ./libxcrash_dumper/libs/armeabi/xcrash_dumper ../java/xcrash/xcrash_lib/src/main/jniLibs/armeabi/libxcrash_dumper.so +#cp -f ./libxcrash_dumper/libs/armeabi/xcrash_dumper ../java/xcrash/xcrash_lib/src/main/jniLibs/armeabi/libxcrash_dumper.so cp -f ./libxcrash_dumper/libs/armeabi-v7a/xcrash_dumper ../java/xcrash/xcrash_lib/src/main/jniLibs/armeabi-v7a/libxcrash_dumper.so cp -f ./libxcrash_dumper/libs/arm64-v8a/xcrash_dumper ../java/xcrash/xcrash_lib/src/main/jniLibs/arm64-v8a/libxcrash_dumper.so cp -f ./libxcrash_dumper/libs/x86/xcrash_dumper ../java/xcrash/xcrash_lib/src/main/jniLibs/x86/libxcrash_dumper.so diff --git a/src/native/libxcrash/jni/Application.mk b/src/native/libxcrash/jni/Application.mk index f93f05a..ea0dee5 100644 --- a/src/native/libxcrash/jni/Application.mk +++ b/src/native/libxcrash/jni/Application.mk @@ -1,2 +1,2 @@ -APP_ABI := armeabi armeabi-v7a arm64-v8a x86 x86_64 -APP_PLATFORM := android-14 +APP_ABI := armeabi-v7a arm64-v8a x86 x86_64 +APP_PLATFORM := android-16 diff --git a/src/native/libxcrash_dumper/jni/Application.mk b/src/native/libxcrash_dumper/jni/Application.mk index f93f05a..ea0dee5 100644 --- a/src/native/libxcrash_dumper/jni/Application.mk +++ b/src/native/libxcrash_dumper/jni/Application.mk @@ -1,2 +1,2 @@ -APP_ABI := armeabi armeabi-v7a arm64-v8a x86 x86_64 -APP_PLATFORM := android-14 +APP_ABI := armeabi-v7a arm64-v8a x86 x86_64 +APP_PLATFORM := android-16 From 8ae97fec1a943614d1eced6a2b8c4ca398e4d3e1 Mon Sep 17 00:00:00 2001 From: yueming Date: Wed, 4 Mar 2020 20:18:33 +0800 Subject: [PATCH 05/46] Opt with Oz and LTO. --- src/native/libxcrash/jni/Android.mk | 5 +++-- src/native/libxcrash_dumper/jni/Android.mk | 4 ++-- src/native/libxcrash_dumper/jni/lzma/Android.mk | 2 ++ 3 files changed, 7 insertions(+), 4 deletions(-) diff --git a/src/native/libxcrash/jni/Android.mk b/src/native/libxcrash/jni/Android.mk index 7666bf0..1a4651e 100644 --- a/src/native/libxcrash/jni/Android.mk +++ b/src/native/libxcrash/jni/Android.mk @@ -2,14 +2,15 @@ LOCAL_PATH := $(call my-dir) include $(CLEAR_VARS) LOCAL_MODULE := test -LOCAL_CFLAGS := -std=c11 -Weverything -Werror -O0 +LOCAL_CFLAGS := -std=c11 -Weverything -Werror -O0 -flto LOCAL_C_INCLUDES := $(LOCAL_PATH) $(LOCAL_PATH)/../../common LOCAL_SRC_FILES := xc_test.c include $(BUILD_STATIC_LIBRARY) include $(CLEAR_VARS) LOCAL_MODULE := xcrash -LOCAL_CFLAGS := -std=c11 -Weverything -Werror -fvisibility=hidden +LOCAL_CFLAGS := -std=c11 -Weverything -Werror -fvisibility=hidden -Oz -flto +LOCAL_LDFLAGS := -flto LOCAL_LDLIBS := -ldl -llog LOCAL_STATIC_LIBRARIES := test LOCAL_C_INCLUDES := $(LOCAL_PATH) $(LOCAL_PATH)/../../common diff --git a/src/native/libxcrash_dumper/jni/Android.mk b/src/native/libxcrash_dumper/jni/Android.mk index ebc44c2..41051fc 100644 --- a/src/native/libxcrash_dumper/jni/Android.mk +++ b/src/native/libxcrash_dumper/jni/Android.mk @@ -2,8 +2,8 @@ LOCAL_PATH := $(call my-dir) include $(CLEAR_VARS) LOCAL_MODULE := xcrash_dumper -LOCAL_CFLAGS := -std=c11 -Weverything -Werror -fvisibility=hidden -fPIE -LOCAL_LDFLAGS := -pie +LOCAL_CFLAGS := -std=c11 -Weverything -Werror -fvisibility=hidden -fPIE -Oz -flto +LOCAL_LDFLAGS := -pie -flto LOCAL_LDLIBS := -ldl -llog LOCAL_STATIC_LIBRARIES := lzma LOCAL_C_INCLUDES := $(LOCAL_PATH) $(LOCAL_PATH)/../../common diff --git a/src/native/libxcrash_dumper/jni/lzma/Android.mk b/src/native/libxcrash_dumper/jni/lzma/Android.mk index 3f07649..2d8c5d4 100644 --- a/src/native/libxcrash_dumper/jni/lzma/Android.mk +++ b/src/native/libxcrash_dumper/jni/lzma/Android.mk @@ -16,6 +16,8 @@ LOCAL_CFLAGS := -std=c11 -Weverything -Werror \ -Wno-cast-qual \ -Wno-strict-prototypes \ -fPIE \ + -Oz \ + -flto \ -D_7ZIP_ST LOCAL_C_INCLUDES := $(LOCAL_PATH) LOCAL_EXPORT_C_INCLUDES := $(LOCAL_PATH) From 2b5ea28464e68818ba5069ef008befadf2761af5 Mon Sep 17 00:00:00 2001 From: unknown Date: Tue, 12 May 2020 19:17:49 +0800 Subject: [PATCH 06/46] collect MD5, filesize, lastModifyDate for the so files that meet UnsatisfiedLinkError in java --- src/java/xcrash/build.gradle | 4 +- .../main/java/xcrash/JavaCrashHandler.java | 40 ++++++++++++++++++- .../xcrash_lib/src/main/java/xcrash/Util.java | 31 ++++++++++++++ .../src/main/java/xcrash/XCrash.java | 3 ++ 4 files changed, 75 insertions(+), 3 deletions(-) diff --git a/src/java/xcrash/build.gradle b/src/java/xcrash/build.gradle index 92b9259..f132172 100644 --- a/src/java/xcrash/build.gradle +++ b/src/java/xcrash/build.gradle @@ -30,12 +30,12 @@ ext { POM_GROUP_ID = "com.iqiyi.xcrash" POM_ARTIFACT_ID = "xcrash-android-lib" - POM_VERSION_NAME = "2.5.0" + POM_VERSION_NAME = "2.5.1" POM_NAME = "xCrash Android Lib" POM_DESCRIPTION = "xCrash provides the Android app with the ability to capture java crash, native crash and ANR." POM_URL = "https://github.com/iqiyi/xCrash" - POM_INCEPTION_YEAR = "2019" + POM_INCEPTION_YEAR = "2020" POM_PACKAGING = "aar" POM_SCM_CONNECTION = "https://github.com/iqiyi/xCrash.git" diff --git a/src/java/xcrash/xcrash_lib/src/main/java/xcrash/JavaCrashHandler.java b/src/java/xcrash/xcrash_lib/src/main/java/xcrash/JavaCrashHandler.java index d74b373..25ca9c0 100644 --- a/src/java/xcrash/xcrash_lib/src/main/java/xcrash/JavaCrashHandler.java +++ b/src/java/xcrash/xcrash_lib/src/main/java/xcrash/JavaCrashHandler.java @@ -27,6 +27,7 @@ import java.io.RandomAccessFile; import java.io.StringWriter; import java.lang.Thread.UncaughtExceptionHandler; +import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Date; import java.util.Locale; @@ -203,6 +204,42 @@ private void handleException(Thread thread, Throwable throwable) { } } + private String getBuildId(String stktrace) { + String buildId = ""; + if (stktrace.contains("UnsatisfiedLinkError")) { + String libInfo = " No lib info\n"; + String[] tempLibPathStr; + tempLibPathStr = stktrace.split("\""); // " is the delimiter + for (String libPathStr : tempLibPathStr) { + if (libPathStr.isEmpty() || !libPathStr.endsWith(".so")) continue; + String[] libNameStr; + libNameStr = libPathStr.split("/"); + for(String libName : libNameStr) { + if(libName.isEmpty() || !libName.endsWith(".so")) continue; + String libPath = XCrash.nativeLibDir + "/" + libName; + File libFile = new File(libPath); + if (libFile.exists() && libFile.isFile()) { + String md5 = Util.getFileMD5(libFile); + + long lastTime = libFile.lastModified(); + SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd: HH.mm.ss"); + Date date = new Date(lastTime); + simpleDateFormat.format(date); + + libInfo = " " + libPathStr + "(FileSize: " + libFile.length() + ". LastModified: " + simpleDateFormat.format(date) + ". MD5: " + md5 + ")\n"; + } + } + } + + buildId = "build id:" + + "\n" + + libInfo + + "\n"; + } + + return buildId; + } + private String getEmergency(Date crashTime, Thread thread, Throwable throwable) { //stack stace @@ -216,7 +253,8 @@ private String getEmergency(Date crashTime, Thread thread, Throwable throwable) + "\n" + "java stacktrace:\n" + stacktrace - + "\n"; + + "\n" + + getBuildId(stacktrace); } private String getOtherThreadsInfo(Thread crashedThread) { diff --git a/src/java/xcrash/xcrash_lib/src/main/java/xcrash/Util.java b/src/java/xcrash/xcrash_lib/src/main/java/xcrash/Util.java index 4f2b197..2390b16 100644 --- a/src/java/xcrash/xcrash_lib/src/main/java/xcrash/Util.java +++ b/src/java/xcrash/xcrash_lib/src/main/java/xcrash/Util.java @@ -31,10 +31,13 @@ import java.io.BufferedReader; import java.io.File; +import java.io.FileInputStream; import java.io.FileReader; import java.io.FilenameFilter; import java.io.IOException; import java.io.InputStreamReader; +import java.math.BigInteger; +import java.security.MessageDigest; import java.text.DateFormat; import java.text.SimpleDateFormat; import java.util.ArrayList; @@ -152,6 +155,34 @@ static String getAbiList() { } } + static String getFileMD5(File file) { + String s = null; + + if (!file.exists()) { + return null; + } + + FileInputStream in = null; + byte[] buffer = new byte[1024]; + int len; + + try { + MessageDigest md = MessageDigest.getInstance("MD5"); + in = new FileInputStream(file); + while ((len = in.read(buffer, 0, 1024)) != -1) { + md.update(buffer, 0, len); + } + in.close(); + BigInteger bigInt = new BigInteger(1, md.digest()); + s = String.format("%032x", bigInt); + } catch (Exception e) { + e.printStackTrace(); + return null; + } + + return s; + } + static String getAppVersion(Context ctx) { String appVersion = null; diff --git a/src/java/xcrash/xcrash_lib/src/main/java/xcrash/XCrash.java b/src/java/xcrash/xcrash_lib/src/main/java/xcrash/XCrash.java index b5943d1..0e5f782 100644 --- a/src/java/xcrash/xcrash_lib/src/main/java/xcrash/XCrash.java +++ b/src/java/xcrash/xcrash_lib/src/main/java/xcrash/XCrash.java @@ -38,6 +38,7 @@ public final class XCrash { private static String appVersion = null; private static String logDir = null; private static ILogger logger = new DefaultLogger(); + public static String nativeLibDir = null; private XCrash() { } @@ -69,8 +70,10 @@ public static synchronized int init(Context ctx, InitParameters params) { if (XCrash.initialized) { return Errno.OK; } + XCrash.initialized = true; + nativeLibDir = ctx.getApplicationInfo().nativeLibraryDir; if (ctx == null) { return Errno.CONTEXT_IS_NULL; } From 5326846d2494c6b5ccb0a40d78c69f2ef946ce5a Mon Sep 17 00:00:00 2001 From: unknown Date: Fri, 15 May 2020 11:20:27 +0800 Subject: [PATCH 07/46] adjust date format and a code defect --- src/java/xcrash/build.gradle | 2 +- .../src/main/java/xcrash/JavaCrashHandler.java | 10 +++++----- .../xcrash/xcrash_lib/src/main/java/xcrash/XCrash.java | 3 ++- 3 files changed, 8 insertions(+), 7 deletions(-) diff --git a/src/java/xcrash/build.gradle b/src/java/xcrash/build.gradle index f132172..8b1d697 100644 --- a/src/java/xcrash/build.gradle +++ b/src/java/xcrash/build.gradle @@ -30,7 +30,7 @@ ext { POM_GROUP_ID = "com.iqiyi.xcrash" POM_ARTIFACT_ID = "xcrash-android-lib" - POM_VERSION_NAME = "2.5.1" + POM_VERSION_NAME = "2.5.2" POM_NAME = "xCrash Android Lib" POM_DESCRIPTION = "xCrash provides the Android app with the ability to capture java crash, native crash and ANR." diff --git a/src/java/xcrash/xcrash_lib/src/main/java/xcrash/JavaCrashHandler.java b/src/java/xcrash/xcrash_lib/src/main/java/xcrash/JavaCrashHandler.java index 25ca9c0..5d13fb3 100644 --- a/src/java/xcrash/xcrash_lib/src/main/java/xcrash/JavaCrashHandler.java +++ b/src/java/xcrash/xcrash_lib/src/main/java/xcrash/JavaCrashHandler.java @@ -27,6 +27,7 @@ import java.io.RandomAccessFile; import java.io.StringWriter; import java.lang.Thread.UncaughtExceptionHandler; +import java.text.DateFormat; import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Date; @@ -221,12 +222,11 @@ private String getBuildId(String stktrace) { if (libFile.exists() && libFile.isFile()) { String md5 = Util.getFileMD5(libFile); - long lastTime = libFile.lastModified(); - SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd: HH.mm.ss"); - Date date = new Date(lastTime); - simpleDateFormat.format(date); + DateFormat timeFormatter = new SimpleDateFormat(Util.timeFormatterStr, Locale.US); + Date lastTime = new Date(libFile.lastModified()); - libInfo = " " + libPathStr + "(FileSize: " + libFile.length() + ". LastModified: " + simpleDateFormat.format(date) + ". MD5: " + md5 + ")\n"; + libInfo = " " + libPathStr + "(BuildId: unknown. FileSize: " + libFile.length() + ". LastModified: " + + timeFormatter.format(lastTime) + ". MD5: " + md5 + ")\n"; } } } diff --git a/src/java/xcrash/xcrash_lib/src/main/java/xcrash/XCrash.java b/src/java/xcrash/xcrash_lib/src/main/java/xcrash/XCrash.java index 0e5f782..39e3715 100644 --- a/src/java/xcrash/xcrash_lib/src/main/java/xcrash/XCrash.java +++ b/src/java/xcrash/xcrash_lib/src/main/java/xcrash/XCrash.java @@ -73,7 +73,6 @@ public static synchronized int init(Context ctx, InitParameters params) { XCrash.initialized = true; - nativeLibDir = ctx.getApplicationInfo().nativeLibraryDir; if (ctx == null) { return Errno.CONTEXT_IS_NULL; } @@ -107,6 +106,8 @@ public static synchronized int init(Context ctx, InitParameters params) { } XCrash.appVersion = params.appVersion; + XCrash.nativeLibDir = ctx.getApplicationInfo().nativeLibraryDir; + //save log dir if (TextUtils.isEmpty(params.logDir)) { params.logDir = ctx.getFilesDir() + "/tombstones"; From 4105f63dec8e2e157c51c4c57d6e7cbad46a91bd Mon Sep 17 00:00:00 2001 From: xuqingqing Date: Wed, 27 May 2020 15:13:20 +0800 Subject: [PATCH 08/46] correct xCrash version number --- src/java/xcrash/build.gradle | 2 +- src/java/xcrash/xcrash_lib/src/main/java/xcrash/Version.java | 2 +- src/java/xcrash/xcrash_sample/build.gradle | 1 - src/native/common/xcc_version.h | 2 +- 4 files changed, 3 insertions(+), 4 deletions(-) diff --git a/src/java/xcrash/build.gradle b/src/java/xcrash/build.gradle index 8b1d697..f132172 100644 --- a/src/java/xcrash/build.gradle +++ b/src/java/xcrash/build.gradle @@ -30,7 +30,7 @@ ext { POM_GROUP_ID = "com.iqiyi.xcrash" POM_ARTIFACT_ID = "xcrash-android-lib" - POM_VERSION_NAME = "2.5.2" + POM_VERSION_NAME = "2.5.1" POM_NAME = "xCrash Android Lib" POM_DESCRIPTION = "xCrash provides the Android app with the ability to capture java crash, native crash and ANR." diff --git a/src/java/xcrash/xcrash_lib/src/main/java/xcrash/Version.java b/src/java/xcrash/xcrash_lib/src/main/java/xcrash/Version.java index 126dc91..1d1c554 100644 --- a/src/java/xcrash/xcrash_lib/src/main/java/xcrash/Version.java +++ b/src/java/xcrash/xcrash_lib/src/main/java/xcrash/Version.java @@ -27,6 +27,6 @@ class Version { private Version() { } - static final String version = "2.4.9"; + static final String version = "2.5.1"; static final String fullVersion = "xCrash " + version; } diff --git a/src/java/xcrash/xcrash_sample/build.gradle b/src/java/xcrash/xcrash_sample/build.gradle index 6860012..a77e8e9 100644 --- a/src/java/xcrash/xcrash_sample/build.gradle +++ b/src/java/xcrash/xcrash_sample/build.gradle @@ -33,6 +33,5 @@ dependencies { implementation fileTree(dir: 'libs', include: ['*.jar']) implementation 'androidx.appcompat:appcompat:1.1.0' implementation 'androidx.constraintlayout:constraintlayout:1.1.3' - //implementation 'com.iqiyi.xcrash:xcrash-android-lib:2.4.9' implementation project(':xcrash_lib') } diff --git a/src/native/common/xcc_version.h b/src/native/common/xcc_version.h index 8918b59..0fa2e93 100644 --- a/src/native/common/xcc_version.h +++ b/src/native/common/xcc_version.h @@ -24,6 +24,6 @@ #ifndef XCC_VERSION_H #define XCC_VERSION_H 1 -#define XCC_VERSION_STR "xCrash 2.4.9" +#define XCC_VERSION_STR "xCrash 2.5.1" #endif From dddbd21f61e761a3774e06a05115bc53223fe496 Mon Sep 17 00:00:00 2001 From: unknown Date: Wed, 27 May 2020 17:13:38 +0800 Subject: [PATCH 09/46] change version num --- src/java/xcrash/build.gradle | 2 +- src/java/xcrash/xcrash_lib/src/main/java/xcrash/Version.java | 2 +- src/native/common/xcc_version.h | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/java/xcrash/build.gradle b/src/java/xcrash/build.gradle index f132172..8b1d697 100644 --- a/src/java/xcrash/build.gradle +++ b/src/java/xcrash/build.gradle @@ -30,7 +30,7 @@ ext { POM_GROUP_ID = "com.iqiyi.xcrash" POM_ARTIFACT_ID = "xcrash-android-lib" - POM_VERSION_NAME = "2.5.1" + POM_VERSION_NAME = "2.5.2" POM_NAME = "xCrash Android Lib" POM_DESCRIPTION = "xCrash provides the Android app with the ability to capture java crash, native crash and ANR." diff --git a/src/java/xcrash/xcrash_lib/src/main/java/xcrash/Version.java b/src/java/xcrash/xcrash_lib/src/main/java/xcrash/Version.java index 1d1c554..48f36e5 100644 --- a/src/java/xcrash/xcrash_lib/src/main/java/xcrash/Version.java +++ b/src/java/xcrash/xcrash_lib/src/main/java/xcrash/Version.java @@ -27,6 +27,6 @@ class Version { private Version() { } - static final String version = "2.5.1"; + static final String version = "2.5.2"; static final String fullVersion = "xCrash " + version; } diff --git a/src/native/common/xcc_version.h b/src/native/common/xcc_version.h index 0fa2e93..3ad893c 100644 --- a/src/native/common/xcc_version.h +++ b/src/native/common/xcc_version.h @@ -24,6 +24,6 @@ #ifndef XCC_VERSION_H #define XCC_VERSION_H 1 -#define XCC_VERSION_STR "xCrash 2.5.1" +#define XCC_VERSION_STR "xCrash 2.5.2" #endif From 65fb0a3ee516203f195acb1d72d4cdd2c0d91ef2 Mon Sep 17 00:00:00 2001 From: xuqingqing Date: Tue, 9 Jun 2020 17:08:55 +0800 Subject: [PATCH 10/46] get SIGABRT while dump ANR trace, make it continue to dump other info, trying best to get full log info --- src/native/common/xcc_util.c | 59 ++++++++++++++++++++++ src/native/common/xcc_util.h | 1 + src/native/libxcrash/jni/Android.mk | 2 +- src/native/libxcrash/jni/xc_crash.c | 12 +++++ src/native/libxcrash/jni/xc_trace.c | 24 +++++++++ src/native/libxcrash/jni/xc_trace.h | 3 ++ src/native/libxcrash_dumper/jni/Android.mk | 2 +- 7 files changed, 101 insertions(+), 2 deletions(-) diff --git a/src/native/common/xcc_util.c b/src/native/common/xcc_util.c index 9cd8c00..fbc4efe 100644 --- a/src/native/common/xcc_util.c +++ b/src/native/common/xcc_util.c @@ -39,6 +39,7 @@ #include "xcc_util.h" #include "xcc_errno.h" #include "xcc_fmt.h" +#include "xc_common.h" #include "xcc_version.h" #include "xcc_libc_support.h" @@ -683,6 +684,64 @@ int xcc_util_record_fds(int fd, pid_t pid) return r; } +int xcc_util_check_if_trace_xcrash_file_opened(pid_t pid) +{ + int fd2 = -1; + char path[128]; + char fd_path[512]; + char buf[512]; + long n, i; + int fd_num; + size_t total = 0; + xcc_util_dirent_t *ent; + ssize_t len; + + xcc_fmt_snprintf(path, sizeof(path), "/proc/%d/fd", pid); + if((fd2 = XCC_UTIL_TEMP_FAILURE_RETRY(open(path, O_RDONLY | O_DIRECTORY | O_CLOEXEC))) < 0) goto end; + + while((n = syscall(XCC_UTIL_SYSCALL_GETDENTS, fd2, buf, sizeof(buf))) > 0) + { + for(i = 0; i < n;) + { + ent = (xcc_util_dirent_t *)(buf + i); + + //get the fd + if('\0' == ent->d_name[0]) goto next; + if(0 == memcmp(ent->d_name, ".", 1)) goto next; + if(0 == memcmp(ent->d_name, "..", 2)) goto next; + if(0 != xcc_util_atoi(ent->d_name, &fd_num)) goto next; + if(fd_num < 0) goto next; + + //count + total++; + if(total > 1024) goto end; + + //read link of the path + xcc_fmt_snprintf(path, sizeof(path), "/proc/%d/fd/%d", pid, fd_num); + len = readlink(path, fd_path, sizeof(fd_path) - 1); + if(len <= 0 || len > (ssize_t)(sizeof(fd_path) - 1)) + { + goto next; + } + else + { + fd_path[len] = '\0'; + unsigned int suffix_len = strlen(XC_COMMON_LOG_SUFFIX_TRACE); + if((len > (ssize_t)suffix_len) && (0 == memcmp(fd_path + len - suffix_len, XC_COMMON_LOG_SUFFIX_TRACE, suffix_len))) + return 1; + } + + next: + i += ent->d_reclen; + } + } + + end: + if(fd2 >= 0) close(fd2); + return 0; +} + + int xcc_util_record_network_info(int fd, pid_t pid, int api_level) { int r; diff --git a/src/native/common/xcc_util.h b/src/native/common/xcc_util.h index 38975d4..78a8a58 100644 --- a/src/native/common/xcc_util.h +++ b/src/native/common/xcc_util.h @@ -186,6 +186,7 @@ int xcc_util_record_logcat(int fd, unsigned int logcat_main_lines); int xcc_util_record_fds(int fd, pid_t pid); +int xcc_util_check_if_trace_xcrash_file_opened(pid_t pid); int xcc_util_record_network_info(int fd, pid_t pid, int api_level); diff --git a/src/native/libxcrash/jni/Android.mk b/src/native/libxcrash/jni/Android.mk index 1a4651e..d3026dc 100644 --- a/src/native/libxcrash/jni/Android.mk +++ b/src/native/libxcrash/jni/Android.mk @@ -13,7 +13,7 @@ LOCAL_CFLAGS := -std=c11 -Weverything -Werror -fvisibility=hidden -Oz LOCAL_LDFLAGS := -flto LOCAL_LDLIBS := -ldl -llog LOCAL_STATIC_LIBRARIES := test -LOCAL_C_INCLUDES := $(LOCAL_PATH) $(LOCAL_PATH)/../../common +LOCAL_C_INCLUDES := $(LOCAL_PATH) $(LOCAL_PATH)/../../common $(LOCAL_PATH)/../../libxcrash_dumper/jni LOCAL_SRC_FILES := xc_jni.c \ xc_common.c \ xc_crash.c \ diff --git a/src/native/libxcrash/jni/xc_crash.c b/src/native/libxcrash/jni/xc_crash.c index 9888a74..1dcdfbe 100644 --- a/src/native/libxcrash/jni/xc_crash.c +++ b/src/native/libxcrash/jni/xc_crash.c @@ -52,11 +52,13 @@ #include "xcc_b64.h" #include "xcc_util.h" #include "xc_crash.h" +#include "xc_trace.h" #include "xc_common.h" #include "xc_dl.h" #include "xc_util.h" #include "xc_jni.h" #include "xc_fallback.h" +#include "xcd_log.h" #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wgnu-statement-expression" @@ -411,6 +413,16 @@ static void xc_crash_signal_handler(int sig, siginfo_t *si, void *uc) if(0 != xcc_signal_crash_ignore()) goto exit; } + if(sig == 6) //SIGABRT + { + if(xcc_util_check_if_trace_xcrash_file_opened(xc_common_process_id)) + { + XCD_LOG_WARN("get SIGABRT while dumping trace to trace.xcrash file\n"); + xc_trace_dumper_bottom_half(xc_trace_file_fd); + goto exit; + } + } + //save crash time clock_gettime(CLOCK_REALTIME, &crash_tp); xc_crash_time = (uint64_t)(crash_tp.tv_sec) * 1000 * 1000 + (uint64_t)crash_tp.tv_nsec / 1000; diff --git a/src/native/libxcrash/jni/xc_trace.c b/src/native/libxcrash/jni/xc_trace.c index e15cd94..1629824 100644 --- a/src/native/libxcrash/jni/xc_trace.c +++ b/src/native/libxcrash/jni/xc_trace.c @@ -79,6 +79,8 @@ static int xc_trace_dump_network_info; static jmethodID xc_trace_cb_method = NULL; static int xc_trace_notifier = -1; +int xc_trace_file_fd = -1; + static void xc_trace_load_signal_catcher_tid() { char buf[256]; @@ -316,6 +318,7 @@ static void *xc_trace_dumper(void *arg) //create and open log file if((fd = xc_common_open_trace_log(pathname, sizeof(pathname), trace_time)) < 0) continue; + xc_trace_file_fd = fd; //write header info if(0 != xc_trace_write_header(fd, trace_time)) goto end; @@ -380,6 +383,27 @@ static void *xc_trace_dumper(void *arg) return NULL; } +void xc_trace_dumper_bottom_half(int fd) +{ + fflush(NULL); + dup2(xc_common_fd_null, STDERR_FILENO); + + if(0 != xcc_util_write_str(fd, "\n"XCC_UTIL_THREAD_END"\n")) goto end; + + //write other info + if(0 != xcc_util_record_logcat(fd, xc_common_process_id, xc_common_api_level, xc_trace_logcat_system_lines, + xc_trace_logcat_events_lines, xc_trace_logcat_main_lines)) goto end; + if(xc_trace_dump_fds) + if(0 != xcc_util_record_fds(fd, xc_common_process_id)) goto end; + if(xc_trace_dump_network_info) + if(0 != xcc_util_record_network_info(fd, xc_common_process_id, xc_common_api_level)) goto end; + if(0 != xcc_meminfo_record(fd, xc_common_process_id)) goto end; + + end: + //close log file + xc_common_close_trace_log(fd); +} + static void xc_trace_handler(int sig, siginfo_t *si, void *uc) { uint64_t data; diff --git a/src/native/libxcrash/jni/xc_trace.h b/src/native/libxcrash/jni/xc_trace.h index 607dfd0..a5ecf37 100644 --- a/src/native/libxcrash/jni/xc_trace.h +++ b/src/native/libxcrash/jni/xc_trace.h @@ -32,6 +32,8 @@ extern "C" { #endif +extern int xc_trace_file_fd; + int xc_trace_init(JNIEnv *env, int rethrow, unsigned int logcat_system_lines, @@ -40,6 +42,7 @@ int xc_trace_init(JNIEnv *env, int dump_fds, int dump_network_info); +void xc_trace_dumper_bottom_half(int fd); #ifdef __cplusplus } #endif diff --git a/src/native/libxcrash_dumper/jni/Android.mk b/src/native/libxcrash_dumper/jni/Android.mk index 41051fc..2f44bf0 100644 --- a/src/native/libxcrash_dumper/jni/Android.mk +++ b/src/native/libxcrash_dumper/jni/Android.mk @@ -6,7 +6,7 @@ LOCAL_CFLAGS := -std=c11 -Weverything -Werror -fvisibility=hidden -fPI LOCAL_LDFLAGS := -pie -flto LOCAL_LDLIBS := -ldl -llog LOCAL_STATIC_LIBRARIES := lzma -LOCAL_C_INCLUDES := $(LOCAL_PATH) $(LOCAL_PATH)/../../common +LOCAL_C_INCLUDES := $(LOCAL_PATH) $(LOCAL_PATH)/../../common $(LOCAL_PATH)/../../libxcrash/jni LOCAL_SRC_FILES := $(wildcard $(LOCAL_PATH)/*.c) $(wildcard $(LOCAL_PATH)/../../common/*.c) include $(BUILD_EXECUTABLE) include $(LOCAL_PATH)/lzma/Android.mk From adf12853f0547ad5ef14679ab9ad64694852c534 Mon Sep 17 00:00:00 2001 From: xuqingqing Date: Tue, 9 Jun 2020 19:26:50 +0800 Subject: [PATCH 11/46] got SIGABRT after dump ANR trace, create a new trace callback thread for other info dumping, trying best to collect ANR log --- src/native/libxcrash/jni/xc_crash.c | 2 +- src/native/libxcrash/jni/xc_trace.c | 65 +++++++++++++++++++++++++---- src/native/libxcrash/jni/xc_trace.h | 3 +- 3 files changed, 59 insertions(+), 11 deletions(-) diff --git a/src/native/libxcrash/jni/xc_crash.c b/src/native/libxcrash/jni/xc_crash.c index 1dcdfbe..9a36503 100644 --- a/src/native/libxcrash/jni/xc_crash.c +++ b/src/native/libxcrash/jni/xc_crash.c @@ -418,7 +418,7 @@ static void xc_crash_signal_handler(int sig, siginfo_t *si, void *uc) if(xcc_util_check_if_trace_xcrash_file_opened(xc_common_process_id)) { XCD_LOG_WARN("get SIGABRT while dumping trace to trace.xcrash file\n"); - xc_trace_dumper_bottom_half(xc_trace_file_fd); + xc_trace_callback(); goto exit; } } diff --git a/src/native/libxcrash/jni/xc_trace.c b/src/native/libxcrash/jni/xc_trace.c index 1629824..e455cbc 100644 --- a/src/native/libxcrash/jni/xc_trace.c +++ b/src/native/libxcrash/jni/xc_trace.c @@ -80,6 +80,7 @@ static jmethodID xc_trace_cb_method = NULL; static int xc_trace_notifier = -1; int xc_trace_file_fd = -1; +char xc_trace_file_pathname[1024]; static void xc_trace_load_signal_catcher_tid() { @@ -319,6 +320,8 @@ static void *xc_trace_dumper(void *arg) //create and open log file if((fd = xc_common_open_trace_log(pathname, sizeof(pathname), trace_time)) < 0) continue; xc_trace_file_fd = fd; + memcpy(xc_trace_file_pathname, pathname, strlen(pathname) + 1); + //write header info if(0 != xc_trace_write_header(fd, trace_time)) goto end; @@ -383,25 +386,69 @@ static void *xc_trace_dumper(void *arg) return NULL; } -void xc_trace_dumper_bottom_half(int fd) +static void *xc_trace_callback_th(void *arg) { + JNIEnv *env = NULL; + jstring j_pathname; + + (void)arg; + + pthread_detach(pthread_self()); + + JavaVMAttachArgs attach_args = { + .version = XC_JNI_VERSION, + .name = "xcrash_trace_cb", + .group = NULL + }; + if(JNI_OK != (*xc_common_vm)->AttachCurrentThread(xc_common_vm, &env, &attach_args)) goto exit; + + //check if process already crashed + if(xc_common_java_crashed) goto exit; + fflush(NULL); dup2(xc_common_fd_null, STDERR_FILENO); - if(0 != xcc_util_write_str(fd, "\n"XCC_UTIL_THREAD_END"\n")) goto end; + if(0 != xcc_util_write_str(xc_trace_file_fd, "\n"XCC_UTIL_THREAD_END"\n")) goto end; //write other info - if(0 != xcc_util_record_logcat(fd, xc_common_process_id, xc_common_api_level, xc_trace_logcat_system_lines, - xc_trace_logcat_events_lines, xc_trace_logcat_main_lines)) goto end; + if(0 != xcc_util_record_logcat(xc_trace_file_fd, xc_common_process_id, xc_common_api_level, xc_trace_logcat_system_lines, xc_trace_logcat_events_lines, xc_trace_logcat_main_lines)) goto end; if(xc_trace_dump_fds) - if(0 != xcc_util_record_fds(fd, xc_common_process_id)) goto end; + if(0 != xcc_util_record_fds(xc_trace_file_fd, xc_common_process_id)) goto end; if(xc_trace_dump_network_info) - if(0 != xcc_util_record_network_info(fd, xc_common_process_id, xc_common_api_level)) goto end; - if(0 != xcc_meminfo_record(fd, xc_common_process_id)) goto end; + if(0 != xcc_util_record_network_info(xc_trace_file_fd, xc_common_process_id, xc_common_api_level)) goto end; + if(0 != xcc_meminfo_record(xc_trace_file_fd, xc_common_process_id)) goto end; - end: +end: //close log file - xc_common_close_trace_log(fd); + xc_common_close_trace_log(xc_trace_file_fd); + + //rethrow SIGQUIT to ART Signal Catcher + //if(xc_trace_rethrow) xc_trace_send_sigquit(); + + //JNI callback + //Do we need to implement an emergency buffer for disk exhausted? + if(NULL == xc_trace_cb_method) goto exit; + if(NULL == (j_pathname = (*env)->NewStringUTF(env, xc_trace_file_pathname))) goto exit; + (*env)->CallStaticVoidMethod(env, xc_common_cb_class, xc_trace_cb_method, j_pathname, NULL); + XC_JNI_IGNORE_PENDING_EXCEPTION(); + (*env)->DeleteLocalRef(env, j_pathname); + + (*xc_common_vm)->DetachCurrentThread(xc_common_vm); + + exit: + return NULL; +} + +void xc_trace_callback() +{ + pthread_t thd; + + if(NULL == xc_common_cb_class || NULL == xc_trace_cb_method) return; + + //create thread for trace callback + if(0 != pthread_create(&thd, NULL, xc_trace_callback_th, NULL)) return; + + pthread_join(thd, NULL); } static void xc_trace_handler(int sig, siginfo_t *si, void *uc) diff --git a/src/native/libxcrash/jni/xc_trace.h b/src/native/libxcrash/jni/xc_trace.h index a5ecf37..101ee8b 100644 --- a/src/native/libxcrash/jni/xc_trace.h +++ b/src/native/libxcrash/jni/xc_trace.h @@ -33,6 +33,7 @@ extern "C" { #endif extern int xc_trace_file_fd; +extern char xc_trace_file_pathname[]; int xc_trace_init(JNIEnv *env, int rethrow, @@ -42,7 +43,7 @@ int xc_trace_init(JNIEnv *env, int dump_fds, int dump_network_info); -void xc_trace_dumper_bottom_half(int fd); +void xc_trace_callback(void); #ifdef __cplusplus } #endif From dfd2529a9db83aa115c3219d9bd31fd3d574672c Mon Sep 17 00:00:00 2001 From: xuqingqing Date: Tue, 9 Jun 2020 20:36:18 +0800 Subject: [PATCH 12/46] new version num --- src/native/common/xcc_version.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/native/common/xcc_version.h b/src/native/common/xcc_version.h index 3ad893c..3f81ced 100644 --- a/src/native/common/xcc_version.h +++ b/src/native/common/xcc_version.h @@ -24,6 +24,6 @@ #ifndef XCC_VERSION_H #define XCC_VERSION_H 1 -#define XCC_VERSION_STR "xCrash 2.5.2" +#define XCC_VERSION_STR "xCrash 2.5.3" #endif From 03271ab38b82e2d53b19c25d672cdba33f1bc051 Mon Sep 17 00:00:00 2001 From: xuqingqing Date: Thu, 11 Jun 2020 15:56:59 +0800 Subject: [PATCH 13/46] dump trace backup thread should be joinable --- src/native/libxcrash/jni/xc_trace.c | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/src/native/libxcrash/jni/xc_trace.c b/src/native/libxcrash/jni/xc_trace.c index e455cbc..7ecf35d 100644 --- a/src/native/libxcrash/jni/xc_trace.c +++ b/src/native/libxcrash/jni/xc_trace.c @@ -393,7 +393,11 @@ static void *xc_trace_callback_th(void *arg) (void)arg; - pthread_detach(pthread_self()); + fflush(NULL); + dup2(xc_common_fd_null, STDERR_FILENO); + + //check if process already crashed + if(xc_common_java_crashed) goto exit; JavaVMAttachArgs attach_args = { .version = XC_JNI_VERSION, @@ -402,12 +406,6 @@ static void *xc_trace_callback_th(void *arg) }; if(JNI_OK != (*xc_common_vm)->AttachCurrentThread(xc_common_vm, &env, &attach_args)) goto exit; - //check if process already crashed - if(xc_common_java_crashed) goto exit; - - fflush(NULL); - dup2(xc_common_fd_null, STDERR_FILENO); - if(0 != xcc_util_write_str(xc_trace_file_fd, "\n"XCC_UTIL_THREAD_END"\n")) goto end; //write other info From df6d9e98f80e94632c6f3a79c379a5e9225af064 Mon Sep 17 00:00:00 2001 From: xuqingqing Date: Thu, 11 Jun 2020 16:08:34 +0800 Subject: [PATCH 14/46] add a missed close(fd) --- src/native/common/xcc_util.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/native/common/xcc_util.c b/src/native/common/xcc_util.c index fbc4efe..297dc53 100644 --- a/src/native/common/xcc_util.c +++ b/src/native/common/xcc_util.c @@ -728,7 +728,10 @@ int xcc_util_check_if_trace_xcrash_file_opened(pid_t pid) fd_path[len] = '\0'; unsigned int suffix_len = strlen(XC_COMMON_LOG_SUFFIX_TRACE); if((len > (ssize_t)suffix_len) && (0 == memcmp(fd_path + len - suffix_len, XC_COMMON_LOG_SUFFIX_TRACE, suffix_len))) + { + if(fd2 >= 0) close(fd2); return 1; + } } next: From 0d53a2a9a1bbefcb08fe7e92d77a9cd942f8ec1d Mon Sep 17 00:00:00 2001 From: xuqingqing Date: Thu, 11 Jun 2020 16:44:18 +0800 Subject: [PATCH 15/46] publish v2.5.3 xCrash lib to maven --- src/java/xcrash/build.gradle | 2 +- src/java/xcrash/xcrash_lib/src/main/java/xcrash/Version.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/java/xcrash/build.gradle b/src/java/xcrash/build.gradle index 8b1d697..702c9e2 100644 --- a/src/java/xcrash/build.gradle +++ b/src/java/xcrash/build.gradle @@ -30,7 +30,7 @@ ext { POM_GROUP_ID = "com.iqiyi.xcrash" POM_ARTIFACT_ID = "xcrash-android-lib" - POM_VERSION_NAME = "2.5.2" + POM_VERSION_NAME = "2.5.3" POM_NAME = "xCrash Android Lib" POM_DESCRIPTION = "xCrash provides the Android app with the ability to capture java crash, native crash and ANR." diff --git a/src/java/xcrash/xcrash_lib/src/main/java/xcrash/Version.java b/src/java/xcrash/xcrash_lib/src/main/java/xcrash/Version.java index 48f36e5..293c1e5 100644 --- a/src/java/xcrash/xcrash_lib/src/main/java/xcrash/Version.java +++ b/src/java/xcrash/xcrash_lib/src/main/java/xcrash/Version.java @@ -27,6 +27,6 @@ class Version { private Version() { } - static final String version = "2.5.2"; + static final String version = "2.5.3"; static final String fullVersion = "xCrash " + version; } From 4a2e6093ccf18f62da98f61785931fdd63e4bde5 Mon Sep 17 00:00:00 2001 From: xuqingqing Date: Wed, 1 Jul 2020 18:21:56 +0800 Subject: [PATCH 16/46] use sigsetjmp & siglongjmp for handling ANR dumping's abort, removed the old implementation --- src/java/xcrash/build.gradle | 2 +- .../src/main/java/xcrash/Version.java | 2 +- src/native/common/xcc_util.c | 62 ------------ src/native/common/xcc_util.h | 1 - src/native/common/xcc_version.h | 2 +- src/native/libxcrash/jni/Android.mk | 2 +- src/native/libxcrash/jni/xc_crash.c | 8 +- src/native/libxcrash/jni/xc_trace.c | 95 +++++-------------- src/native/libxcrash/jni/xc_trace.h | 6 +- src/native/libxcrash_dumper/jni/Android.mk | 2 +- 10 files changed, 34 insertions(+), 148 deletions(-) diff --git a/src/java/xcrash/build.gradle b/src/java/xcrash/build.gradle index 702c9e2..a38f780 100644 --- a/src/java/xcrash/build.gradle +++ b/src/java/xcrash/build.gradle @@ -30,7 +30,7 @@ ext { POM_GROUP_ID = "com.iqiyi.xcrash" POM_ARTIFACT_ID = "xcrash-android-lib" - POM_VERSION_NAME = "2.5.3" + POM_VERSION_NAME = "2.5.4" POM_NAME = "xCrash Android Lib" POM_DESCRIPTION = "xCrash provides the Android app with the ability to capture java crash, native crash and ANR." diff --git a/src/java/xcrash/xcrash_lib/src/main/java/xcrash/Version.java b/src/java/xcrash/xcrash_lib/src/main/java/xcrash/Version.java index 293c1e5..3d887a8 100644 --- a/src/java/xcrash/xcrash_lib/src/main/java/xcrash/Version.java +++ b/src/java/xcrash/xcrash_lib/src/main/java/xcrash/Version.java @@ -27,6 +27,6 @@ class Version { private Version() { } - static final String version = "2.5.3"; + static final String version = "2.5.4"; static final String fullVersion = "xCrash " + version; } diff --git a/src/native/common/xcc_util.c b/src/native/common/xcc_util.c index 297dc53..9cd8c00 100644 --- a/src/native/common/xcc_util.c +++ b/src/native/common/xcc_util.c @@ -39,7 +39,6 @@ #include "xcc_util.h" #include "xcc_errno.h" #include "xcc_fmt.h" -#include "xc_common.h" #include "xcc_version.h" #include "xcc_libc_support.h" @@ -684,67 +683,6 @@ int xcc_util_record_fds(int fd, pid_t pid) return r; } -int xcc_util_check_if_trace_xcrash_file_opened(pid_t pid) -{ - int fd2 = -1; - char path[128]; - char fd_path[512]; - char buf[512]; - long n, i; - int fd_num; - size_t total = 0; - xcc_util_dirent_t *ent; - ssize_t len; - - xcc_fmt_snprintf(path, sizeof(path), "/proc/%d/fd", pid); - if((fd2 = XCC_UTIL_TEMP_FAILURE_RETRY(open(path, O_RDONLY | O_DIRECTORY | O_CLOEXEC))) < 0) goto end; - - while((n = syscall(XCC_UTIL_SYSCALL_GETDENTS, fd2, buf, sizeof(buf))) > 0) - { - for(i = 0; i < n;) - { - ent = (xcc_util_dirent_t *)(buf + i); - - //get the fd - if('\0' == ent->d_name[0]) goto next; - if(0 == memcmp(ent->d_name, ".", 1)) goto next; - if(0 == memcmp(ent->d_name, "..", 2)) goto next; - if(0 != xcc_util_atoi(ent->d_name, &fd_num)) goto next; - if(fd_num < 0) goto next; - - //count - total++; - if(total > 1024) goto end; - - //read link of the path - xcc_fmt_snprintf(path, sizeof(path), "/proc/%d/fd/%d", pid, fd_num); - len = readlink(path, fd_path, sizeof(fd_path) - 1); - if(len <= 0 || len > (ssize_t)(sizeof(fd_path) - 1)) - { - goto next; - } - else - { - fd_path[len] = '\0'; - unsigned int suffix_len = strlen(XC_COMMON_LOG_SUFFIX_TRACE); - if((len > (ssize_t)suffix_len) && (0 == memcmp(fd_path + len - suffix_len, XC_COMMON_LOG_SUFFIX_TRACE, suffix_len))) - { - if(fd2 >= 0) close(fd2); - return 1; - } - } - - next: - i += ent->d_reclen; - } - } - - end: - if(fd2 >= 0) close(fd2); - return 0; -} - - int xcc_util_record_network_info(int fd, pid_t pid, int api_level) { int r; diff --git a/src/native/common/xcc_util.h b/src/native/common/xcc_util.h index 78a8a58..38975d4 100644 --- a/src/native/common/xcc_util.h +++ b/src/native/common/xcc_util.h @@ -186,7 +186,6 @@ int xcc_util_record_logcat(int fd, unsigned int logcat_main_lines); int xcc_util_record_fds(int fd, pid_t pid); -int xcc_util_check_if_trace_xcrash_file_opened(pid_t pid); int xcc_util_record_network_info(int fd, pid_t pid, int api_level); diff --git a/src/native/common/xcc_version.h b/src/native/common/xcc_version.h index 3f81ced..8e5b7c2 100644 --- a/src/native/common/xcc_version.h +++ b/src/native/common/xcc_version.h @@ -24,6 +24,6 @@ #ifndef XCC_VERSION_H #define XCC_VERSION_H 1 -#define XCC_VERSION_STR "xCrash 2.5.3" +#define XCC_VERSION_STR "xCrash 2.5.4" #endif diff --git a/src/native/libxcrash/jni/Android.mk b/src/native/libxcrash/jni/Android.mk index d3026dc..3cef887 100644 --- a/src/native/libxcrash/jni/Android.mk +++ b/src/native/libxcrash/jni/Android.mk @@ -13,7 +13,7 @@ LOCAL_CFLAGS := -std=c11 -Weverything -Werror -fvisibility=hidden -Oz LOCAL_LDFLAGS := -flto LOCAL_LDLIBS := -ldl -llog LOCAL_STATIC_LIBRARIES := test -LOCAL_C_INCLUDES := $(LOCAL_PATH) $(LOCAL_PATH)/../../common $(LOCAL_PATH)/../../libxcrash_dumper/jni +LOCAL_C_INCLUDES := $(LOCAL_PATH) $(LOCAL_PATH)/../../common $(LOCAL_PATH)/../../libxcrash_dumper/jni LOCAL_SRC_FILES := xc_jni.c \ xc_common.c \ xc_crash.c \ diff --git a/src/native/libxcrash/jni/xc_crash.c b/src/native/libxcrash/jni/xc_crash.c index 9a36503..b23f619 100644 --- a/src/native/libxcrash/jni/xc_crash.c +++ b/src/native/libxcrash/jni/xc_crash.c @@ -37,6 +37,7 @@ #include #include #include +#include #include #include #include @@ -415,11 +416,10 @@ static void xc_crash_signal_handler(int sig, siginfo_t *si, void *uc) if(sig == 6) //SIGABRT { - if(xcc_util_check_if_trace_xcrash_file_opened(xc_common_process_id)) + if(1 == xc_trace_dumping) { - XCD_LOG_WARN("get SIGABRT while dumping trace to trace.xcrash file\n"); - xc_trace_callback(); - goto exit; + XCD_LOG_WARN("get SIGABRT while ART dumping trace\n"); + siglongjmp(jmpenv, 1); } } diff --git a/src/native/libxcrash/jni/xc_trace.c b/src/native/libxcrash/jni/xc_trace.c index 7ecf35d..8ae3f25 100644 --- a/src/native/libxcrash/jni/xc_trace.c +++ b/src/native/libxcrash/jni/xc_trace.c @@ -29,6 +29,7 @@ #include #include #include +#include #include #include #include @@ -43,6 +44,7 @@ #include "xc_dl.h" #include "xc_jni.h" #include "xc_util.h" +#include "xcd_log.h" #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wgnu-statement-expression" @@ -79,8 +81,8 @@ static int xc_trace_dump_network_info; static jmethodID xc_trace_cb_method = NULL; static int xc_trace_notifier = -1; -int xc_trace_file_fd = -1; -char xc_trace_file_pathname[1024]; +unsigned char xc_trace_dumping = 0; +sigjmp_buf jmpenv; static void xc_trace_load_signal_catcher_tid() { @@ -319,9 +321,6 @@ static void *xc_trace_dumper(void *arg) //create and open log file if((fd = xc_common_open_trace_log(pathname, sizeof(pathname), trace_time)) < 0) continue; - xc_trace_file_fd = fd; - memcpy(xc_trace_file_pathname, pathname, strlen(pathname) + 1); - //write header info if(0 != xc_trace_write_header(fd, trace_time)) goto end; @@ -344,11 +343,23 @@ static void *xc_trace_dumper(void *arg) if(0 != xcc_util_write_str(fd, "Failed to duplicate FD.\n")) goto end; goto skip; } - if(xc_trace_is_lollipop) - xc_trace_libart_dbg_suspend(); - xc_trace_libart_runtime_dump(*xc_trace_libart_runtime_instance, xc_trace_libcpp_cerr); - if(xc_trace_is_lollipop) - xc_trace_libart_dbg_resume(); + + xc_trace_dumping = 1; + if(sigsetjmp(jmpenv, 1) == 0) + { + if(xc_trace_is_lollipop) + xc_trace_libart_dbg_suspend(); + xc_trace_libart_runtime_dump(*xc_trace_libart_runtime_instance, xc_trace_libcpp_cerr); + if(xc_trace_is_lollipop) + xc_trace_libart_dbg_resume(); + } + else + { + fflush(NULL); + XCD_LOG_WARN("longjmp to skip dumping trace\n"); + } + xc_trace_dumping = 0; + dup2(xc_common_fd_null, STDERR_FILENO); skip: @@ -386,69 +397,6 @@ static void *xc_trace_dumper(void *arg) return NULL; } -static void *xc_trace_callback_th(void *arg) -{ - JNIEnv *env = NULL; - jstring j_pathname; - - (void)arg; - - fflush(NULL); - dup2(xc_common_fd_null, STDERR_FILENO); - - //check if process already crashed - if(xc_common_java_crashed) goto exit; - - JavaVMAttachArgs attach_args = { - .version = XC_JNI_VERSION, - .name = "xcrash_trace_cb", - .group = NULL - }; - if(JNI_OK != (*xc_common_vm)->AttachCurrentThread(xc_common_vm, &env, &attach_args)) goto exit; - - if(0 != xcc_util_write_str(xc_trace_file_fd, "\n"XCC_UTIL_THREAD_END"\n")) goto end; - - //write other info - if(0 != xcc_util_record_logcat(xc_trace_file_fd, xc_common_process_id, xc_common_api_level, xc_trace_logcat_system_lines, xc_trace_logcat_events_lines, xc_trace_logcat_main_lines)) goto end; - if(xc_trace_dump_fds) - if(0 != xcc_util_record_fds(xc_trace_file_fd, xc_common_process_id)) goto end; - if(xc_trace_dump_network_info) - if(0 != xcc_util_record_network_info(xc_trace_file_fd, xc_common_process_id, xc_common_api_level)) goto end; - if(0 != xcc_meminfo_record(xc_trace_file_fd, xc_common_process_id)) goto end; - -end: - //close log file - xc_common_close_trace_log(xc_trace_file_fd); - - //rethrow SIGQUIT to ART Signal Catcher - //if(xc_trace_rethrow) xc_trace_send_sigquit(); - - //JNI callback - //Do we need to implement an emergency buffer for disk exhausted? - if(NULL == xc_trace_cb_method) goto exit; - if(NULL == (j_pathname = (*env)->NewStringUTF(env, xc_trace_file_pathname))) goto exit; - (*env)->CallStaticVoidMethod(env, xc_common_cb_class, xc_trace_cb_method, j_pathname, NULL); - XC_JNI_IGNORE_PENDING_EXCEPTION(); - (*env)->DeleteLocalRef(env, j_pathname); - - (*xc_common_vm)->DetachCurrentThread(xc_common_vm); - - exit: - return NULL; -} - -void xc_trace_callback() -{ - pthread_t thd; - - if(NULL == xc_common_cb_class || NULL == xc_trace_cb_method) return; - - //create thread for trace callback - if(0 != pthread_create(&thd, NULL, xc_trace_callback_th, NULL)) return; - - pthread_join(thd, NULL); -} - static void xc_trace_handler(int sig, siginfo_t *si, void *uc) { uint64_t data; @@ -493,6 +441,7 @@ int xc_trace_init(JNIEnv *env, //is Android Lollipop (5.x)? xc_trace_is_lollipop = ((21 == xc_common_api_level || 22 == xc_common_api_level) ? 1 : 0); + xc_trace_dumping = 0; xc_trace_rethrow = rethrow; xc_trace_logcat_system_lines = logcat_system_lines; xc_trace_logcat_events_lines = logcat_events_lines; diff --git a/src/native/libxcrash/jni/xc_trace.h b/src/native/libxcrash/jni/xc_trace.h index 101ee8b..518c6e6 100644 --- a/src/native/libxcrash/jni/xc_trace.h +++ b/src/native/libxcrash/jni/xc_trace.h @@ -25,6 +25,7 @@ #define XC_TRACE_H 1 #include +#include #include #include @@ -32,8 +33,8 @@ extern "C" { #endif -extern int xc_trace_file_fd; -extern char xc_trace_file_pathname[]; +extern unsigned char xc_trace_dumping; +extern sigjmp_buf jmpenv; int xc_trace_init(JNIEnv *env, int rethrow, @@ -43,7 +44,6 @@ int xc_trace_init(JNIEnv *env, int dump_fds, int dump_network_info); -void xc_trace_callback(void); #ifdef __cplusplus } #endif diff --git a/src/native/libxcrash_dumper/jni/Android.mk b/src/native/libxcrash_dumper/jni/Android.mk index 2f44bf0..41051fc 100644 --- a/src/native/libxcrash_dumper/jni/Android.mk +++ b/src/native/libxcrash_dumper/jni/Android.mk @@ -6,7 +6,7 @@ LOCAL_CFLAGS := -std=c11 -Weverything -Werror -fvisibility=hidden -fPI LOCAL_LDFLAGS := -pie -flto LOCAL_LDLIBS := -ldl -llog LOCAL_STATIC_LIBRARIES := lzma -LOCAL_C_INCLUDES := $(LOCAL_PATH) $(LOCAL_PATH)/../../common $(LOCAL_PATH)/../../libxcrash/jni +LOCAL_C_INCLUDES := $(LOCAL_PATH) $(LOCAL_PATH)/../../common LOCAL_SRC_FILES := $(wildcard $(LOCAL_PATH)/*.c) $(wildcard $(LOCAL_PATH)/../../common/*.c) include $(BUILD_EXECUTABLE) include $(LOCAL_PATH)/lzma/Android.mk From d7899b755da924f60982aac638611f6f1ecb37e0 Mon Sep 17 00:00:00 2001 From: xuqingqing Date: Wed, 8 Jul 2020 10:35:47 +0800 Subject: [PATCH 17/46] skip segv error in some devices's libunwind.so while dumping trace for anr --- src/native/libxcrash/jni/xc_crash.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/native/libxcrash/jni/xc_crash.c b/src/native/libxcrash/jni/xc_crash.c index b23f619..a128551 100644 --- a/src/native/libxcrash/jni/xc_crash.c +++ b/src/native/libxcrash/jni/xc_crash.c @@ -414,11 +414,12 @@ static void xc_crash_signal_handler(int sig, siginfo_t *si, void *uc) if(0 != xcc_signal_crash_ignore()) goto exit; } - if(sig == 6) //SIGABRT + if(6 == sig || 11 == sig) //SIGABRT(6), SIGSEGV(11) { if(1 == xc_trace_dumping) { - XCD_LOG_WARN("get SIGABRT while ART dumping trace\n"); + //xc_trace_dumping = 0; + XCD_LOG_WARN("meet error sig(%d) while calling ART dump trace\n", sig); siglongjmp(jmpenv, 1); } } From 0551be7c272d2dffa6cfc7051010db30ac41f6e2 Mon Sep 17 00:00:00 2001 From: xuqingqing Date: Thu, 9 Jul 2020 17:55:10 +0800 Subject: [PATCH 18/46] update to version 2.5.5 --- src/java/xcrash/build.gradle | 2 +- src/java/xcrash/xcrash_lib/src/main/java/xcrash/Version.java | 2 +- src/native/common/xcc_version.h | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/java/xcrash/build.gradle b/src/java/xcrash/build.gradle index a38f780..f591ace 100644 --- a/src/java/xcrash/build.gradle +++ b/src/java/xcrash/build.gradle @@ -30,7 +30,7 @@ ext { POM_GROUP_ID = "com.iqiyi.xcrash" POM_ARTIFACT_ID = "xcrash-android-lib" - POM_VERSION_NAME = "2.5.4" + POM_VERSION_NAME = "2.5.5" POM_NAME = "xCrash Android Lib" POM_DESCRIPTION = "xCrash provides the Android app with the ability to capture java crash, native crash and ANR." diff --git a/src/java/xcrash/xcrash_lib/src/main/java/xcrash/Version.java b/src/java/xcrash/xcrash_lib/src/main/java/xcrash/Version.java index 3d887a8..6d49074 100644 --- a/src/java/xcrash/xcrash_lib/src/main/java/xcrash/Version.java +++ b/src/java/xcrash/xcrash_lib/src/main/java/xcrash/Version.java @@ -27,6 +27,6 @@ class Version { private Version() { } - static final String version = "2.5.4"; + static final String version = "2.5.5"; static final String fullVersion = "xCrash " + version; } diff --git a/src/native/common/xcc_version.h b/src/native/common/xcc_version.h index 8e5b7c2..3d00bc3 100644 --- a/src/native/common/xcc_version.h +++ b/src/native/common/xcc_version.h @@ -24,6 +24,6 @@ #ifndef XCC_VERSION_H #define XCC_VERSION_H 1 -#define XCC_VERSION_STR "xCrash 2.5.4" +#define XCC_VERSION_STR "xCrash 2.5.5" #endif From dfa22bc8efb0d31de745cce49551198269188705 Mon Sep 17 00:00:00 2001 From: xuqingqing Date: Fri, 17 Jul 2020 14:32:31 +0800 Subject: [PATCH 19/46] don't rethow sigquit to art, if art dump trace crash and siglongjmp to re-dump --- src/java/xcrash/build.gradle | 2 +- .../xcrash_lib/src/main/java/xcrash/Version.java | 2 +- src/native/common/xcc_version.h | 2 +- src/native/libxcrash/jni/xc_crash.c | 4 ++-- src/native/libxcrash/jni/xc_trace.c | 10 +++++----- src/native/libxcrash/jni/xc_trace.h | 9 ++++++++- 6 files changed, 18 insertions(+), 11 deletions(-) diff --git a/src/java/xcrash/build.gradle b/src/java/xcrash/build.gradle index f591ace..3365c96 100644 --- a/src/java/xcrash/build.gradle +++ b/src/java/xcrash/build.gradle @@ -30,7 +30,7 @@ ext { POM_GROUP_ID = "com.iqiyi.xcrash" POM_ARTIFACT_ID = "xcrash-android-lib" - POM_VERSION_NAME = "2.5.5" + POM_VERSION_NAME = "2.5.6" POM_NAME = "xCrash Android Lib" POM_DESCRIPTION = "xCrash provides the Android app with the ability to capture java crash, native crash and ANR." diff --git a/src/java/xcrash/xcrash_lib/src/main/java/xcrash/Version.java b/src/java/xcrash/xcrash_lib/src/main/java/xcrash/Version.java index 6d49074..116105b 100644 --- a/src/java/xcrash/xcrash_lib/src/main/java/xcrash/Version.java +++ b/src/java/xcrash/xcrash_lib/src/main/java/xcrash/Version.java @@ -27,6 +27,6 @@ class Version { private Version() { } - static final String version = "2.5.5"; + static final String version = "2.5.6"; static final String fullVersion = "xCrash " + version; } diff --git a/src/native/common/xcc_version.h b/src/native/common/xcc_version.h index 3d00bc3..475031f 100644 --- a/src/native/common/xcc_version.h +++ b/src/native/common/xcc_version.h @@ -24,6 +24,6 @@ #ifndef XCC_VERSION_H #define XCC_VERSION_H 1 -#define XCC_VERSION_STR "xCrash 2.5.5" +#define XCC_VERSION_STR "xCrash 2.5.6" #endif diff --git a/src/native/libxcrash/jni/xc_crash.c b/src/native/libxcrash/jni/xc_crash.c index a128551..099351c 100644 --- a/src/native/libxcrash/jni/xc_crash.c +++ b/src/native/libxcrash/jni/xc_crash.c @@ -416,9 +416,9 @@ static void xc_crash_signal_handler(int sig, siginfo_t *si, void *uc) if(6 == sig || 11 == sig) //SIGABRT(6), SIGSEGV(11) { - if(1 == xc_trace_dumping) + if(XC_TRACE_DUMP_NOT_START == xc_trace_dump_status) { - //xc_trace_dumping = 0; + xc_trace_dump_status = XC_TRACE_DUMP_ART_CRASH; XCD_LOG_WARN("meet error sig(%d) while calling ART dump trace\n", sig); siglongjmp(jmpenv, 1); } diff --git a/src/native/libxcrash/jni/xc_trace.c b/src/native/libxcrash/jni/xc_trace.c index 8ae3f25..15b0d19 100644 --- a/src/native/libxcrash/jni/xc_trace.c +++ b/src/native/libxcrash/jni/xc_trace.c @@ -81,7 +81,7 @@ static int xc_trace_dump_network_info; static jmethodID xc_trace_cb_method = NULL; static int xc_trace_notifier = -1; -unsigned char xc_trace_dumping = 0; +xc_trace_dump_status_t xc_trace_dump_status = XC_TRACE_DUMP_NOT_START; sigjmp_buf jmpenv; static void xc_trace_load_signal_catcher_tid() @@ -344,7 +344,7 @@ static void *xc_trace_dumper(void *arg) goto skip; } - xc_trace_dumping = 1; + xc_trace_dump_status = XC_TRACE_DUMP_ON_GOING; if(sigsetjmp(jmpenv, 1) == 0) { if(xc_trace_is_lollipop) @@ -358,7 +358,6 @@ static void *xc_trace_dumper(void *arg) fflush(NULL); XCD_LOG_WARN("longjmp to skip dumping trace\n"); } - xc_trace_dumping = 0; dup2(xc_common_fd_null, STDERR_FILENO); @@ -378,7 +377,8 @@ static void *xc_trace_dumper(void *arg) xc_common_close_trace_log(fd); //rethrow SIGQUIT to ART Signal Catcher - if(xc_trace_rethrow) xc_trace_send_sigquit(); + if(xc_trace_rethrow && (XC_TRACE_DUMP_ART_CRASH != xc_trace_dump_status)) xc_trace_send_sigquit(); + xc_trace_dump_status = XC_TRACE_DUMP_END; //JNI callback //Do we need to implement an emergency buffer for disk exhausted? @@ -441,7 +441,7 @@ int xc_trace_init(JNIEnv *env, //is Android Lollipop (5.x)? xc_trace_is_lollipop = ((21 == xc_common_api_level || 22 == xc_common_api_level) ? 1 : 0); - xc_trace_dumping = 0; + xc_trace_dump_status = XC_TRACE_DUMP_NOT_START; xc_trace_rethrow = rethrow; xc_trace_logcat_system_lines = logcat_system_lines; xc_trace_logcat_events_lines = logcat_events_lines; diff --git a/src/native/libxcrash/jni/xc_trace.h b/src/native/libxcrash/jni/xc_trace.h index 518c6e6..a0ec2fd 100644 --- a/src/native/libxcrash/jni/xc_trace.h +++ b/src/native/libxcrash/jni/xc_trace.h @@ -33,7 +33,14 @@ extern "C" { #endif -extern unsigned char xc_trace_dumping; +typedef enum { + XC_TRACE_DUMP_NOT_START = 0, + XC_TRACE_DUMP_ON_GOING, + XC_TRACE_DUMP_ART_CRASH, + XC_TRACE_DUMP_END +} xc_trace_dump_status_t; + +extern xc_trace_dump_status_t xc_trace_dump_status; extern sigjmp_buf jmpenv; int xc_trace_init(JNIEnv *env, From a8046d39e28f83b25e3a0c38f7aac8e9ef668014 Mon Sep 17 00:00:00 2001 From: xuqingqing Date: Fri, 17 Jul 2020 15:04:52 +0800 Subject: [PATCH 20/46] add debug so back cmd --- src/native/install.sh | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/src/native/install.sh b/src/native/install.sh index cefc210..1bdfc6d 100755 --- a/src/native/install.sh +++ b/src/native/install.sh @@ -17,3 +17,19 @@ cp -f ./libxcrash_dumper/libs/armeabi-v7a/xcrash_dumper ../java/xcrash/xcrash_li cp -f ./libxcrash_dumper/libs/arm64-v8a/xcrash_dumper ../java/xcrash/xcrash_lib/src/main/jniLibs/arm64-v8a/libxcrash_dumper.so cp -f ./libxcrash_dumper/libs/x86/xcrash_dumper ../java/xcrash/xcrash_lib/src/main/jniLibs/x86/libxcrash_dumper.so cp -f ./libxcrash_dumper/libs/x86_64/xcrash_dumper ../java/xcrash/xcrash_lib/src/main/jniLibs/x86_64/libxcrash_dumper.so + +version=$1 +mkdir -p ~/xcrash_libs_backup/$version/jniLibs_Debug/armeabi-v7a +mkdir -p ~/xcrash_libs_backup/$version/jniLibs_Debug/arm64-v8a +mkdir -p ~/xcrash_libs_backup/$version/jniLibs_Debug/x86 +mkdir -p ~/xcrash_libs_backup/$version/jniLibs_Debug/x86_64 + +cp -f ./libxcrash/obj/local/armeabi-v7a/libxcrash.so ~/xcrash_libs_backup/$version/jniLibs_Debug/armeabi-v7a/libxcrash.so +cp -f ./libxcrash/obj/local/arm64-v8a/libxcrash.so ~/xcrash_libs_backup/$version/jniLibs_Debug/arm64-v8a/libxcrash.so +cp -f ./libxcrash/obj/local/x86/libxcrash.so ~/xcrash_libs_backup/$version/jniLibs_Debug/x86/libxcrash.so +cp -f ./libxcrash/obj/local/x86_64/libxcrash.so ~/xcrash_libs_backup/$version/jniLibs_Debug/x86_64/libxcrash.so + +cp -f ./libxcrash_dumper/obj/local/armeabi-v7a/xcrash_dumper ~/xcrash_libs_backup/$version/jniLibs_Debug/armeabi-v7a/libxcrash_dumper.so +cp -f ./libxcrash_dumper/obj/local/arm64-v8a/xcrash_dumper ~/xcrash_libs_backup/$version/jniLibs_Debug/arm64-v8a/libxcrash_dumper.so +cp -f ./libxcrash_dumper/obj/local/x86/xcrash_dumper ~/xcrash_libs_backup/$version/jniLibs_Debug/x86/libxcrash_dumper.so +cp -f ./libxcrash_dumper/obj/local/x86_64/xcrash_dumper ~/xcrash_libs_backup/$version/jniLibs_Debug/x86_64/libxcrash_dumper.so From b2a497533b15dfc3960aa8c29dbe7b5ca649ebd9 Mon Sep 17 00:00:00 2001 From: xuqingqing Date: Fri, 17 Jul 2020 15:10:38 +0800 Subject: [PATCH 21/46] optimize install.sh --- src/native/install.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/native/install.sh b/src/native/install.sh index 1bdfc6d..31a8735 100755 --- a/src/native/install.sh +++ b/src/native/install.sh @@ -18,7 +18,7 @@ cp -f ./libxcrash_dumper/libs/arm64-v8a/xcrash_dumper ../java/xcrash/xcrash_li cp -f ./libxcrash_dumper/libs/x86/xcrash_dumper ../java/xcrash/xcrash_lib/src/main/jniLibs/x86/libxcrash_dumper.so cp -f ./libxcrash_dumper/libs/x86_64/xcrash_dumper ../java/xcrash/xcrash_lib/src/main/jniLibs/x86_64/libxcrash_dumper.so -version=$1 +version=`strings ./libxcrash_dumper/obj/local/armeabi-v7a/xcrash_dumper | grep 'Tombstone maker' | awk -F "\'" '{print $2}' | awk '{print $2}'` mkdir -p ~/xcrash_libs_backup/$version/jniLibs_Debug/armeabi-v7a mkdir -p ~/xcrash_libs_backup/$version/jniLibs_Debug/arm64-v8a mkdir -p ~/xcrash_libs_backup/$version/jniLibs_Debug/x86 From e8a4bb993d9755d17a0176d60a4b5205fc6f265b Mon Sep 17 00:00:00 2001 From: xuqingqing Date: Wed, 22 Jul 2020 10:53:15 +0800 Subject: [PATCH 22/46] fix xCrash 2.5.6's bug for causing normal SIGSEGV and SIGABORT not correctly handled --- src/java/xcrash/build.gradle | 2 +- src/java/xcrash/xcrash_lib/src/main/java/xcrash/Version.java | 2 +- src/native/common/xcc_version.h | 2 +- src/native/libxcrash/jni/xc_crash.c | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/java/xcrash/build.gradle b/src/java/xcrash/build.gradle index 3365c96..ec69aac 100644 --- a/src/java/xcrash/build.gradle +++ b/src/java/xcrash/build.gradle @@ -30,7 +30,7 @@ ext { POM_GROUP_ID = "com.iqiyi.xcrash" POM_ARTIFACT_ID = "xcrash-android-lib" - POM_VERSION_NAME = "2.5.6" + POM_VERSION_NAME = "2.5.7" POM_NAME = "xCrash Android Lib" POM_DESCRIPTION = "xCrash provides the Android app with the ability to capture java crash, native crash and ANR." diff --git a/src/java/xcrash/xcrash_lib/src/main/java/xcrash/Version.java b/src/java/xcrash/xcrash_lib/src/main/java/xcrash/Version.java index 116105b..6a8916f 100644 --- a/src/java/xcrash/xcrash_lib/src/main/java/xcrash/Version.java +++ b/src/java/xcrash/xcrash_lib/src/main/java/xcrash/Version.java @@ -27,6 +27,6 @@ class Version { private Version() { } - static final String version = "2.5.6"; + static final String version = "2.5.7"; static final String fullVersion = "xCrash " + version; } diff --git a/src/native/common/xcc_version.h b/src/native/common/xcc_version.h index 475031f..8789ab6 100644 --- a/src/native/common/xcc_version.h +++ b/src/native/common/xcc_version.h @@ -24,6 +24,6 @@ #ifndef XCC_VERSION_H #define XCC_VERSION_H 1 -#define XCC_VERSION_STR "xCrash 2.5.6" +#define XCC_VERSION_STR "xCrash 2.5.7" #endif diff --git a/src/native/libxcrash/jni/xc_crash.c b/src/native/libxcrash/jni/xc_crash.c index 099351c..997522f 100644 --- a/src/native/libxcrash/jni/xc_crash.c +++ b/src/native/libxcrash/jni/xc_crash.c @@ -416,7 +416,7 @@ static void xc_crash_signal_handler(int sig, siginfo_t *si, void *uc) if(6 == sig || 11 == sig) //SIGABRT(6), SIGSEGV(11) { - if(XC_TRACE_DUMP_NOT_START == xc_trace_dump_status) + if(XC_TRACE_DUMP_ON_GOING == xc_trace_dump_status) { xc_trace_dump_status = XC_TRACE_DUMP_ART_CRASH; XCD_LOG_WARN("meet error sig(%d) while calling ART dump trace\n", sig); From 48655f79a6fdce407d370733da5e0d3738456d56 Mon Sep 17 00:00:00 2001 From: xuqingqing Date: Wed, 22 Jul 2020 16:25:33 +0800 Subject: [PATCH 23/46] =?UTF-8?q?=E5=B0=8F=E7=B1=B3=E6=9C=BA=E5=9E=8B?= =?UTF-8?q?=E8=8E=B7=E5=8F=96=E6=96=B9=E5=BC=8F=E8=B0=83=E6=95=B4=E7=9A=84?= =?UTF-8?q?=E9=80=82=E9=85=8D=EF=BC=88Build.MODEL=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/main/java/xcrash/NativeHandler.java | 2 +- .../src/main/java/xcrash/TombstoneParser.java | 2 +- .../xcrash_lib/src/main/java/xcrash/Util.java | 62 ++++++++++++++++++- 3 files changed, 63 insertions(+), 3 deletions(-) diff --git a/src/java/xcrash/xcrash_lib/src/main/java/xcrash/NativeHandler.java b/src/java/xcrash/xcrash_lib/src/main/java/xcrash/NativeHandler.java index 463e1dc..c2396bd 100644 --- a/src/java/xcrash/xcrash_lib/src/main/java/xcrash/NativeHandler.java +++ b/src/java/xcrash/xcrash_lib/src/main/java/xcrash/NativeHandler.java @@ -112,7 +112,7 @@ int initialize(Context ctx, Util.getAbiList(), Build.MANUFACTURER, Build.BRAND, - Build.MODEL, + Util.getMobileModel(), Build.FINGERPRINT, appId, appVersion, diff --git a/src/java/xcrash/xcrash_lib/src/main/java/xcrash/TombstoneParser.java b/src/java/xcrash/xcrash_lib/src/main/java/xcrash/TombstoneParser.java index bfb3375..1b122dc 100644 --- a/src/java/xcrash/xcrash_lib/src/main/java/xcrash/TombstoneParser.java +++ b/src/java/xcrash/xcrash_lib/src/main/java/xcrash/TombstoneParser.java @@ -510,7 +510,7 @@ private static void addSystemInfo(Map map) { } if (TextUtils.isEmpty(map.get(keyModel))) { - map.put(keyModel, Build.MODEL); + map.put(keyModel, Util.getMobileModel()); } if (TextUtils.isEmpty(map.get(keyAbiList))) { diff --git a/src/java/xcrash/xcrash_lib/src/main/java/xcrash/Util.java b/src/java/xcrash/xcrash_lib/src/main/java/xcrash/Util.java index 2390b16..5c916f3 100644 --- a/src/java/xcrash/xcrash_lib/src/main/java/xcrash/Util.java +++ b/src/java/xcrash/xcrash_lib/src/main/java/xcrash/Util.java @@ -36,6 +36,8 @@ import java.io.FilenameFilter; import java.io.IOException; import java.io.InputStreamReader; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; import java.math.BigInteger; import java.security.MessageDigest; import java.text.DateFormat; @@ -328,7 +330,7 @@ static String getLogHeader(Date startTime, Date crashTime, String crashType, Str + "ABI list: '" + Util.getAbiList() + "'\n" + "Manufacturer: '" + Build.MANUFACTURER + "'\n" + "Brand: '" + Build.BRAND + "'\n" - + "Model: '" + Build.MODEL + "'\n" + + "Model: '" + Util.getMobileModel() + "'\n" + "Build fingerprint: '" + Build.FINGERPRINT + "'\n"; } @@ -493,4 +495,62 @@ private static void getLogcatByBufferName(int pid, StringBuilder sb, String buff } } } + + + public static String getSystemProperty(String key, String defaultValue) { + try { + Class clz = Class.forName("android.os.SystemProperties"); + Method get = clz.getMethod("get", String.class, String.class); + return (String)get.invoke(clz, key, defaultValue); + } catch (NoSuchMethodException var4) { + var4.printStackTrace(); + } catch (IllegalAccessException var5) { + var5.printStackTrace(); + } catch (InvocationTargetException var6) { + var6.printStackTrace(); + } catch (ClassNotFoundException var7) { + var7.printStackTrace(); + } + + return defaultValue; + } + + public static boolean isMIUI() { + String property = getSystemProperty("ro.miui.ui.version.name", ""); + return !TextUtils.isEmpty(property); + } + + public static String getMobileModel() { + String mobileModel = null; + if (isMIUI()) { + String deviceName = ""; + + try { + Class SystemProperties = Class.forName("android.os.SystemProperties"); + Method get = SystemProperties.getDeclaredMethod("get", String.class, String.class); + deviceName = (String) get.invoke(SystemProperties, "ro.product.marketname", ""); + if (TextUtils.isEmpty(deviceName)) { + deviceName = (String) get.invoke(SystemProperties, "ro.product.model", ""); + } + } catch (InvocationTargetException var3) { + var3.printStackTrace(); + } catch (NoSuchMethodException var4) { + var4.printStackTrace(); + } catch (IllegalAccessException var5) { + var5.printStackTrace(); + } catch (ClassNotFoundException var6) { + var6.printStackTrace(); + } + + mobileModel = deviceName; + } else { + mobileModel = Build.MODEL; + } + + if (mobileModel == null) { + mobileModel = ""; + } + + return mobileModel; + } } From 67b1b1bf299f0d301b1ba60310329b4de67b0cb8 Mon Sep 17 00:00:00 2001 From: xuqingqing Date: Wed, 29 Jul 2020 12:09:53 +0800 Subject: [PATCH 24/46] add anr crash signal SIGBUS according to on-line crash record --- src/native/libxcrash/jni/xc_crash.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/native/libxcrash/jni/xc_crash.c b/src/native/libxcrash/jni/xc_crash.c index 997522f..9d1faac 100644 --- a/src/native/libxcrash/jni/xc_crash.c +++ b/src/native/libxcrash/jni/xc_crash.c @@ -414,7 +414,7 @@ static void xc_crash_signal_handler(int sig, siginfo_t *si, void *uc) if(0 != xcc_signal_crash_ignore()) goto exit; } - if(6 == sig || 11 == sig) //SIGABRT(6), SIGSEGV(11) + if(6 == sig || 11 == sig || 7 == sig) //SIGABRT(6), SIGSEGV(11), SIGBUS(7) { if(XC_TRACE_DUMP_ON_GOING == xc_trace_dump_status) { From 431c83690d6845b12f2450d1a4e9eca447cf66db Mon Sep 17 00:00:00 2001 From: xuqingqing Date: Fri, 31 Jul 2020 21:36:00 +0800 Subject: [PATCH 25/46] add backup debug so for download/upload to jfrog --- src/native/install.sh | 35 +++++++++++++++++++++-------------- 1 file changed, 21 insertions(+), 14 deletions(-) diff --git a/src/native/install.sh b/src/native/install.sh index 31a8735..877d30b 100755 --- a/src/native/install.sh +++ b/src/native/install.sh @@ -19,17 +19,24 @@ cp -f ./libxcrash_dumper/libs/x86/xcrash_dumper ../java/xcrash/xcrash_li cp -f ./libxcrash_dumper/libs/x86_64/xcrash_dumper ../java/xcrash/xcrash_lib/src/main/jniLibs/x86_64/libxcrash_dumper.so version=`strings ./libxcrash_dumper/obj/local/armeabi-v7a/xcrash_dumper | grep 'Tombstone maker' | awk -F "\'" '{print $2}' | awk '{print $2}'` -mkdir -p ~/xcrash_libs_backup/$version/jniLibs_Debug/armeabi-v7a -mkdir -p ~/xcrash_libs_backup/$version/jniLibs_Debug/arm64-v8a -mkdir -p ~/xcrash_libs_backup/$version/jniLibs_Debug/x86 -mkdir -p ~/xcrash_libs_backup/$version/jniLibs_Debug/x86_64 - -cp -f ./libxcrash/obj/local/armeabi-v7a/libxcrash.so ~/xcrash_libs_backup/$version/jniLibs_Debug/armeabi-v7a/libxcrash.so -cp -f ./libxcrash/obj/local/arm64-v8a/libxcrash.so ~/xcrash_libs_backup/$version/jniLibs_Debug/arm64-v8a/libxcrash.so -cp -f ./libxcrash/obj/local/x86/libxcrash.so ~/xcrash_libs_backup/$version/jniLibs_Debug/x86/libxcrash.so -cp -f ./libxcrash/obj/local/x86_64/libxcrash.so ~/xcrash_libs_backup/$version/jniLibs_Debug/x86_64/libxcrash.so - -cp -f ./libxcrash_dumper/obj/local/armeabi-v7a/xcrash_dumper ~/xcrash_libs_backup/$version/jniLibs_Debug/armeabi-v7a/libxcrash_dumper.so -cp -f ./libxcrash_dumper/obj/local/arm64-v8a/xcrash_dumper ~/xcrash_libs_backup/$version/jniLibs_Debug/arm64-v8a/libxcrash_dumper.so -cp -f ./libxcrash_dumper/obj/local/x86/xcrash_dumper ~/xcrash_libs_backup/$version/jniLibs_Debug/x86/libxcrash_dumper.so -cp -f ./libxcrash_dumper/obj/local/x86_64/xcrash_dumper ~/xcrash_libs_backup/$version/jniLibs_Debug/x86_64/libxcrash_dumper.so +folder=version +tarfile=v${version}.tar.gz +rm -rf $folder $tarfile +mkdir -p ./$folder/armeabi-v7a +mkdir -p ./$folder/arm64-v8a + + +cp -f ./libxcrash/obj/local/armeabi-v7a/libxcrash.so ./$folder/armeabi-v7a/libxcrash.so +cp -f ./libxcrash/obj/local/arm64-v8a/libxcrash.so ./$folder/arm64-v8a/libxcrash.so +cp -f ./libxcrash_dumper/obj/local/armeabi-v7a/xcrash_dumper ./$folder/armeabi-v7a/libxcrash_dumper.so +cp -f ./libxcrash_dumper/obj/local/arm64-v8a/xcrash_dumper ./$folder/arm64-v8a/libxcrash_dumper.so + +tar cvzf $tarfile $folder +md5=`md5sum $tarfile | awk '{print $1}'` +sha1=`sha1sum $tarfile | awk '{print $1}'` + +#download +#curl -u iqiyi-generic-android-nativelib-debug:hr3QySAENz7u -o $tarfile "http://jfrog.cloud.qiyi.domain/iqiyi-generic-android-nativelib-debug/xcrash/$tarfile" + +#upload +curl -X PUT -u iqiyi-generic-android-nativelib-debug:hr3QySAENz7u "http://jfrog.cloud.qiyi.domain/iqiyi-generic-android-nativelib-debug/xcrash/$tarfile" -H "X-Checksum-Sha1:$sha1" -H "X-Checksum-Md5:$md5" -T $tarfile From 3a8372c7d0eda6c159ada18e0e36e8c627c41893 Mon Sep 17 00:00:00 2001 From: xuqingqing Date: Mon, 3 Aug 2020 11:25:39 +0800 Subject: [PATCH 26/46] fix script error --- src/native/install.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/native/install.sh b/src/native/install.sh index 877d30b..0a87ed9 100755 --- a/src/native/install.sh +++ b/src/native/install.sh @@ -19,7 +19,7 @@ cp -f ./libxcrash_dumper/libs/x86/xcrash_dumper ../java/xcrash/xcrash_li cp -f ./libxcrash_dumper/libs/x86_64/xcrash_dumper ../java/xcrash/xcrash_lib/src/main/jniLibs/x86_64/libxcrash_dumper.so version=`strings ./libxcrash_dumper/obj/local/armeabi-v7a/xcrash_dumper | grep 'Tombstone maker' | awk -F "\'" '{print $2}' | awk '{print $2}'` -folder=version +folder=$version tarfile=v${version}.tar.gz rm -rf $folder $tarfile mkdir -p ./$folder/armeabi-v7a From 30c08b8ac6f059e7cec07681eb6cfecfe70af2e6 Mon Sep 17 00:00:00 2001 From: xuqingqing Date: Thu, 10 Sep 2020 17:46:51 +0800 Subject: [PATCH 27/46] add more lib path to check when meet UnsatisfiedLinkError --- .../main/java/xcrash/JavaCrashHandler.java | 52 +++++++++++++------ 1 file changed, 35 insertions(+), 17 deletions(-) diff --git a/src/java/xcrash/xcrash_lib/src/main/java/xcrash/JavaCrashHandler.java b/src/java/xcrash/xcrash_lib/src/main/java/xcrash/JavaCrashHandler.java index 5d13fb3..8d21cc9 100644 --- a/src/java/xcrash/xcrash_lib/src/main/java/xcrash/JavaCrashHandler.java +++ b/src/java/xcrash/xcrash_lib/src/main/java/xcrash/JavaCrashHandler.java @@ -205,32 +205,50 @@ private void handleException(Thread thread, Throwable throwable) { } } + private String getLibInfo(ArrayList libPathList) { + String libInfo = null; + for(String libPath : libPathList) { + File libFile = new File(libPath); + if (libFile.exists() && libFile.isFile()) { + String md5 = Util.getFileMD5(libFile); + + DateFormat timeFormatter = new SimpleDateFormat(Util.timeFormatterStr, Locale.US); + Date lastTime = new Date(libFile.lastModified()); + + libInfo += " " + libPath + "(BuildId: unknown. FileSize: " + libFile.length() + ". LastModified: " + + timeFormatter.format(lastTime) + ". MD5: " + md5 + ")\n"; + } + } + + return libInfo; + } + private String getBuildId(String stktrace) { String buildId = ""; + ArrayList libPathList = new ArrayList(); if (stktrace.contains("UnsatisfiedLinkError")) { - String libInfo = " No lib info\n"; + String libInfo = null; String[] tempLibPathStr; tempLibPathStr = stktrace.split("\""); // " is the delimiter for (String libPathStr : tempLibPathStr) { if (libPathStr.isEmpty() || !libPathStr.endsWith(".so")) continue; - String[] libNameStr; - libNameStr = libPathStr.split("/"); - for(String libName : libNameStr) { - if(libName.isEmpty() || !libName.endsWith(".so")) continue; - String libPath = XCrash.nativeLibDir + "/" + libName; - File libFile = new File(libPath); - if (libFile.exists() && libFile.isFile()) { - String md5 = Util.getFileMD5(libFile); - - DateFormat timeFormatter = new SimpleDateFormat(Util.timeFormatterStr, Locale.US); - Date lastTime = new Date(libFile.lastModified()); - - libInfo = " " + libPathStr + "(BuildId: unknown. FileSize: " + libFile.length() + ". LastModified: " - + timeFormatter.format(lastTime) + ". MD5: " + md5 + ")\n"; - } - } + + libPathList.add(libPathStr); + + String libName = libPathStr.substring(libPathStr.lastIndexOf('/')); + + libPathList.add(XCrash.nativeLibDir + "/" + libName); + libPathList.add("/vendor/lib/" + libName); + libPathList.add("/vendor/lib64/" + libName); + libPathList.add("/system/lib/" + libName); + libPathList.add("/system/lib64/" + libName); + + libInfo = getLibInfo(libPathList); } + if(libInfo == null) + libInfo = " No lib info\n"; + buildId = "build id:" + "\n" + libInfo From ff01a8f7f21b2397f396d61dc496386628194532 Mon Sep 17 00:00:00 2001 From: xuqingqing Date: Mon, 17 Aug 2020 15:23:42 +0800 Subject: [PATCH 28/46] rm signal limitation for anr handling crash --- src/native/libxcrash/jni/xc_crash.c | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/src/native/libxcrash/jni/xc_crash.c b/src/native/libxcrash/jni/xc_crash.c index 9d1faac..fbcbb09 100644 --- a/src/native/libxcrash/jni/xc_crash.c +++ b/src/native/libxcrash/jni/xc_crash.c @@ -414,14 +414,11 @@ static void xc_crash_signal_handler(int sig, siginfo_t *si, void *uc) if(0 != xcc_signal_crash_ignore()) goto exit; } - if(6 == sig || 11 == sig || 7 == sig) //SIGABRT(6), SIGSEGV(11), SIGBUS(7) + if(XC_TRACE_DUMP_ON_GOING == xc_trace_dump_status) { - if(XC_TRACE_DUMP_ON_GOING == xc_trace_dump_status) - { - xc_trace_dump_status = XC_TRACE_DUMP_ART_CRASH; - XCD_LOG_WARN("meet error sig(%d) while calling ART dump trace\n", sig); - siglongjmp(jmpenv, 1); - } + xc_trace_dump_status = XC_TRACE_DUMP_ART_CRASH; + XCD_LOG_WARN("meet error sig(%d) while calling ART dump trace\n", sig); + siglongjmp(jmpenv, 1); } //save crash time From 774a0508b2dbe7e5dcff7f2a2b759adf36c9523a Mon Sep 17 00:00:00 2001 From: xuqingqing Date: Thu, 10 Sep 2020 17:56:18 +0800 Subject: [PATCH 29/46] rm extra info in install.sh --- src/native/install.sh | 7 ------- 1 file changed, 7 deletions(-) diff --git a/src/native/install.sh b/src/native/install.sh index 0a87ed9..ea367bc 100755 --- a/src/native/install.sh +++ b/src/native/install.sh @@ -32,11 +32,4 @@ cp -f ./libxcrash_dumper/obj/local/armeabi-v7a/xcrash_dumper ./$folder/armeabi-v cp -f ./libxcrash_dumper/obj/local/arm64-v8a/xcrash_dumper ./$folder/arm64-v8a/libxcrash_dumper.so tar cvzf $tarfile $folder -md5=`md5sum $tarfile | awk '{print $1}'` -sha1=`sha1sum $tarfile | awk '{print $1}'` -#download -#curl -u iqiyi-generic-android-nativelib-debug:hr3QySAENz7u -o $tarfile "http://jfrog.cloud.qiyi.domain/iqiyi-generic-android-nativelib-debug/xcrash/$tarfile" - -#upload -curl -X PUT -u iqiyi-generic-android-nativelib-debug:hr3QySAENz7u "http://jfrog.cloud.qiyi.domain/iqiyi-generic-android-nativelib-debug/xcrash/$tarfile" -H "X-Checksum-Sha1:$sha1" -H "X-Checksum-Md5:$md5" -T $tarfile From d01b4a268e9ba0e53745fde14af7a0cfda8bddcd Mon Sep 17 00:00:00 2001 From: Xu Qingqing Date: Fri, 11 Sep 2020 11:16:37 +0800 Subject: [PATCH 30/46] refine code for "UnsatisfiedLinkError" exception handle --- .../main/java/xcrash/JavaCrashHandler.java | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/src/java/xcrash/xcrash_lib/src/main/java/xcrash/JavaCrashHandler.java b/src/java/xcrash/xcrash_lib/src/main/java/xcrash/JavaCrashHandler.java index 8d21cc9..55bebb1 100644 --- a/src/java/xcrash/xcrash_lib/src/main/java/xcrash/JavaCrashHandler.java +++ b/src/java/xcrash/xcrash_lib/src/main/java/xcrash/JavaCrashHandler.java @@ -31,6 +31,7 @@ import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Date; +import java.util.List; import java.util.Locale; import java.util.Map; import java.util.regex.Pattern; @@ -205,8 +206,8 @@ private void handleException(Thread thread, Throwable throwable) { } } - private String getLibInfo(ArrayList libPathList) { - String libInfo = null; + private String getLibInfo(List libPathList) { + StringBuilder sb = new StringBuilder(); for(String libPath : libPathList) { File libFile = new File(libPath); if (libFile.exists() && libFile.isFile()) { @@ -215,17 +216,20 @@ private String getLibInfo(ArrayList libPathList) { DateFormat timeFormatter = new SimpleDateFormat(Util.timeFormatterStr, Locale.US); Date lastTime = new Date(libFile.lastModified()); - libInfo += " " + libPath + "(BuildId: unknown. FileSize: " + libFile.length() + ". LastModified: " - + timeFormatter.format(lastTime) + ". MD5: " + md5 + ")\n"; + sb.append(" ").append(libPath).append("(BuildId: unknown. FileSize: ").append(libFile.length()).append(". LastModified: ") + .append(timeFormatter.format(lastTime)).append(". MD5: ").append(md5).append(")\n"); + } else { + sb.append(" ").append(libPath).append(" (Not found)\n"); } } + String libInfo = sb.toString(); return libInfo; } private String getBuildId(String stktrace) { String buildId = ""; - ArrayList libPathList = new ArrayList(); + List libPathList = new ArrayList(); if (stktrace.contains("UnsatisfiedLinkError")) { String libInfo = null; String[] tempLibPathStr; @@ -235,7 +239,7 @@ private String getBuildId(String stktrace) { libPathList.add(libPathStr); - String libName = libPathStr.substring(libPathStr.lastIndexOf('/')); + String libName = libPathStr.substring(libPathStr.lastIndexOf('/') + 1); libPathList.add(XCrash.nativeLibDir + "/" + libName); libPathList.add("/vendor/lib/" + libName); @@ -246,9 +250,6 @@ private String getBuildId(String stktrace) { libInfo = getLibInfo(libPathList); } - if(libInfo == null) - libInfo = " No lib info\n"; - buildId = "build id:" + "\n" + libInfo From a015badcde6ed80a8a512ae76cf49b2a16c0f0ba Mon Sep 17 00:00:00 2001 From: Xu Qingqing Date: Mon, 21 Sep 2020 16:20:38 +0800 Subject: [PATCH 31/46] fix compiling error when using android-ndk-r21b toolchain --- src/native/libxcrash/jni/xc_crash.c | 2 +- .../libxcrash_dumper/jni/lzma/7zTypes.h | 2 +- src/native/libxcrash_dumper/jni/lzma/Bra.h | 2 +- .../libxcrash_dumper/jni/lzma/Compiler.h | 2 +- .../libxcrash_dumper/jni/lzma/CpuArch.h | 6 +-- .../libxcrash_dumper/jni/lzma/LzmaDec.c | 42 +++++++++---------- .../libxcrash_dumper/jni/lzma/LzmaDec.h | 2 +- src/native/libxcrash_dumper/jni/lzma/XzDec.c | 6 +-- 8 files changed, 32 insertions(+), 32 deletions(-) diff --git a/src/native/libxcrash/jni/xc_crash.c b/src/native/libxcrash/jni/xc_crash.c index fbcbb09..0d49c57 100644 --- a/src/native/libxcrash/jni/xc_crash.c +++ b/src/native/libxcrash/jni/xc_crash.c @@ -458,7 +458,7 @@ static void xc_crash_signal_handler(int sig, siginfo_t *si, void *uc) if(EINVAL == errno) { //this kernel does not support PR_SET_PTRACER_ANY, or Yama is not enabled - ; + errno = errno; } else { diff --git a/src/native/libxcrash_dumper/jni/lzma/7zTypes.h b/src/native/libxcrash_dumper/jni/lzma/7zTypes.h index 4977cda..0a3cd1f 100644 --- a/src/native/libxcrash_dumper/jni/lzma/7zTypes.h +++ b/src/native/libxcrash_dumper/jni/lzma/7zTypes.h @@ -60,7 +60,7 @@ typedef int WRes; #ifndef RINOK -#define RINOK(x) { int __result__ = (x); if (__result__ != 0) return __result__; } +#define RINOK(x) do { int __result__ = (x); if (__result__ != 0) return __result__; } while(0) #endif typedef unsigned char Byte; diff --git a/src/native/libxcrash_dumper/jni/lzma/Bra.h b/src/native/libxcrash_dumper/jni/lzma/Bra.h index aba8dce..1e7d3ea 100644 --- a/src/native/libxcrash_dumper/jni/lzma/Bra.h +++ b/src/native/libxcrash_dumper/jni/lzma/Bra.h @@ -51,7 +51,7 @@ in CALL instructions to increase the compression ratio. } */ -#define x86_Convert_Init(state) { state = 0; } +#define x86_Convert_Init(state) do { state = 0; } while(0) SizeT x86_Convert(Byte *data, SizeT size, UInt32 ip, UInt32 *state, int encoding); SizeT ARM_Convert(Byte *data, SizeT size, UInt32 ip, int encoding); SizeT ARMT_Convert(Byte *data, SizeT size, UInt32 ip, int encoding); diff --git a/src/native/libxcrash_dumper/jni/lzma/Compiler.h b/src/native/libxcrash_dumper/jni/lzma/Compiler.h index c788648..71606ad 100644 --- a/src/native/libxcrash_dumper/jni/lzma/Compiler.h +++ b/src/native/libxcrash_dumper/jni/lzma/Compiler.h @@ -27,7 +27,7 @@ #endif -#define UNUSED_VAR(x) (void)x; +#define UNUSED_VAR(x) (void)x /* #define UNUSED_VAR(x) x=x; */ #endif diff --git a/src/native/libxcrash_dumper/jni/lzma/CpuArch.h b/src/native/libxcrash_dumper/jni/lzma/CpuArch.h index 7fb2728..743a210 100644 --- a/src/native/libxcrash_dumper/jni/lzma/CpuArch.h +++ b/src/native/libxcrash_dumper/jni/lzma/CpuArch.h @@ -203,7 +203,7 @@ MY_CPU_LE_UNALIGN means that CPU is LITTLE ENDIAN and CPU supports unaligned mem #define GetUi64(p) (*(const UInt64 *)(const void *)(p)) #define SetUi16(p, v) { *(UInt16 *)(p) = (v); } -#define SetUi32(p, v) { *(UInt32 *)(p) = (v); } +#define SetUi32(p, v) do { *(UInt32 *)(p) = (v); } while(0) #define SetUi64(p, v) { *(UInt64 *)(p) = (v); } #else @@ -224,11 +224,11 @@ MY_CPU_LE_UNALIGN means that CPU is LITTLE ENDIAN and CPU supports unaligned mem _ppp_[0] = (Byte)_vvv_; \ _ppp_[1] = (Byte)(_vvv_ >> 8); } -#define SetUi32(p, v) { Byte *_ppp_ = (Byte *)(p); UInt32 _vvv_ = (v); \ +#define SetUi32(p, v) do { Byte *_ppp_ = (Byte *)(p); UInt32 _vvv_ = (v); \ _ppp_[0] = (Byte)_vvv_; \ _ppp_[1] = (Byte)(_vvv_ >> 8); \ _ppp_[2] = (Byte)(_vvv_ >> 16); \ - _ppp_[3] = (Byte)(_vvv_ >> 24); } + _ppp_[3] = (Byte)(_vvv_ >> 24); } while(0); #define SetUi64(p, v) { Byte *_ppp2_ = (Byte *)(p); UInt64 _vvv2_ = (v); \ SetUi32(_ppp2_ , (UInt32)_vvv2_); \ diff --git a/src/native/libxcrash_dumper/jni/lzma/LzmaDec.c b/src/native/libxcrash_dumper/jni/lzma/LzmaDec.c index 962b94b..05f0f48 100644 --- a/src/native/libxcrash_dumper/jni/lzma/LzmaDec.c +++ b/src/native/libxcrash_dumper/jni/lzma/LzmaDec.c @@ -17,26 +17,26 @@ #define RC_INIT_SIZE 5 -#define NORMALIZE if (range < kTopValue) { range <<= 8; code = (code << 8) | (*buf++); } +#define NORMALIZE do { if (range < kTopValue) { range <<= 8; code = (code << 8) | (*buf++); } } while(0) #define IF_BIT_0(p) ttt = *(p); NORMALIZE; bound = (range >> kNumBitModelTotalBits) * ttt; if (code < bound) -#define UPDATE_0(p) range = bound; *(p) = (CLzmaProb)(ttt + ((kBitModelTotal - ttt) >> kNumMoveBits)); -#define UPDATE_1(p) range -= bound; code -= bound; *(p) = (CLzmaProb)(ttt - (ttt >> kNumMoveBits)); +#define UPDATE_0(p) do { range = bound; *(p) = (CLzmaProb)(ttt + ((kBitModelTotal - ttt) >> kNumMoveBits)); } while(0) +#define UPDATE_1(p) do { range -= bound; code -= bound; *(p) = (CLzmaProb)(ttt - (ttt >> kNumMoveBits)); } while(0) #define GET_BIT2(p, i, A0, A1) IF_BIT_0(p) \ { UPDATE_0(p); i = (i + i); A0; } else \ { UPDATE_1(p); i = (i + i) + 1; A1; } -#define TREE_GET_BIT(probs, i) { GET_BIT2(probs + i, i, ;, ;); } +#define TREE_GET_BIT(probs, i) do { GET_BIT2(probs + i, i, ;, ;); } while(0) -#define REV_BIT(p, i, A0, A1) IF_BIT_0(p + i) \ +#define REV_BIT(p, i, A0, A1) do { IF_BIT_0(p + i) \ { UPDATE_0(p + i); A0; } else \ - { UPDATE_1(p + i); A1; } + { UPDATE_1(p + i); A1; } } while(0) #define REV_BIT_VAR( p, i, m) REV_BIT(p, i, i += m; m += m, m += m; i += m; ) #define REV_BIT_CONST(p, i, m) REV_BIT(p, i, i += m; , i += m * 2; ) #define REV_BIT_LAST( p, i, m) REV_BIT(p, i, i -= m , ; ) #define TREE_DECODE(probs, limit, i) \ - { i = 1; do { TREE_GET_BIT(probs, i); } while (i < limit); i -= limit; } + do { i = 1; do { TREE_GET_BIT(probs, i); } while (i < limit); i -= limit; } while(0) /* #define _LZMA_SIZE_OPT */ @@ -44,17 +44,17 @@ #define TREE_6_DECODE(probs, i) TREE_DECODE(probs, (1 << 6), i) #else #define TREE_6_DECODE(probs, i) \ - { i = 1; \ + do { i = 1; \ TREE_GET_BIT(probs, i); \ TREE_GET_BIT(probs, i); \ TREE_GET_BIT(probs, i); \ TREE_GET_BIT(probs, i); \ TREE_GET_BIT(probs, i); \ TREE_GET_BIT(probs, i); \ - i -= 0x40; } + i -= 0x40; } while(0) #endif -#define NORMAL_LITER_DEC TREE_GET_BIT(prob, symbol) +#define NORMAL_LITER_DEC TREE_GET_BIT(prob, symbol); #define MATCHED_LITER_DEC \ matchByte += matchByte; \ bit = offs; \ @@ -64,22 +64,22 @@ -#define NORMALIZE_CHECK if (range < kTopValue) { if (buf >= bufLimit) return DUMMY_ERROR; range <<= 8; code = (code << 8) | (*buf++); } +#define NORMALIZE_CHECK do { if (range < kTopValue) { if (buf >= bufLimit) return DUMMY_ERROR; range <<= 8; code = (code << 8) | (*buf++); } } while(0) #define IF_BIT_0_CHECK(p) ttt = *(p); NORMALIZE_CHECK; bound = (range >> kNumBitModelTotalBits) * ttt; if (code < bound) -#define UPDATE_0_CHECK range = bound; -#define UPDATE_1_CHECK range -= bound; code -= bound; +#define UPDATE_0_CHECK do { range = bound; } while(0) +#define UPDATE_1_CHECK do { range -= bound; code -= bound; } while(0) #define GET_BIT2_CHECK(p, i, A0, A1) IF_BIT_0_CHECK(p) \ { UPDATE_0_CHECK; i = (i + i); A0; } else \ { UPDATE_1_CHECK; i = (i + i) + 1; A1; } #define GET_BIT_CHECK(p, i) GET_BIT2_CHECK(p, i, ; , ;) #define TREE_DECODE_CHECK(probs, limit, i) \ - { i = 1; do { GET_BIT_CHECK(probs + i, i) } while (i < limit); i -= limit; } + do { i = 1; do { GET_BIT_CHECK(probs + i, i) } while (i < limit); i -= limit; } while(0) -#define REV_BIT_CHECK(p, i, m) IF_BIT_0_CHECK(p + i) \ +#define REV_BIT_CHECK(p, i, m) do { IF_BIT_0_CHECK(p + i) \ { UPDATE_0_CHECK; i += m; m += m; } else \ - { UPDATE_1_CHECK; m += m; i += m; } + { UPDATE_1_CHECK; m += m; i += m; } } while(0) #define kNumPosBitsMax 4 @@ -472,7 +472,7 @@ int MY_FAST_CALL LZMA_DECODE_REAL(CLzmaDec *p, SizeT limit, const Byte *bufLimit numDirectBits -= kNumAlignBits; do { - NORMALIZE + NORMALIZE; range >>= 1; { @@ -675,7 +675,7 @@ static ELzmaDummy LzmaDec_TryDummy(const CLzmaDec *p, const Byte *buf, SizeT inS prob = probs + IsMatch + COMBINED_PS_STATE; IF_BIT_0_CHECK(prob) { - UPDATE_0_CHECK + UPDATE_0_CHECK; /* if (bufLimit - buf >= 7) return DUMMY_LIT; */ @@ -823,7 +823,7 @@ static ELzmaDummy LzmaDec_TryDummy(const CLzmaDec *p, const Byte *buf, SizeT inS numDirectBits -= kNumAlignBits; do { - NORMALIZE_CHECK + NORMALIZE_CHECK; range >>= 1; code -= range & (((code - range) >> 31) - 1); /* if (code >= range) code -= range; */ @@ -1137,8 +1137,8 @@ SRes LzmaDec_Allocate(CLzmaDec *p, const Byte *props, unsigned propsSize, ISzAll { UInt32 dictSize = propNew.dicSize; SizeT mask = ((UInt32)1 << 12) - 1; - if (dictSize >= ((UInt32)1 << 30)) mask = ((UInt32)1 << 22) - 1; - else if (dictSize >= ((UInt32)1 << 22)) mask = ((UInt32)1 << 20) - 1;; + if (dictSize >= ((UInt32)1 << 30)) mask = ((UInt32)1 << 22) - 1; + else if (dictSize >= ((UInt32)1 << 22)) mask = ((UInt32)1 << 20) - 1; dicBufSize = ((SizeT)dictSize + mask) & ~mask; if (dicBufSize < dictSize) dicBufSize = dictSize; diff --git a/src/native/libxcrash_dumper/jni/lzma/LzmaDec.h b/src/native/libxcrash_dumper/jni/lzma/LzmaDec.h index 28ce60c..46a83b6 100644 --- a/src/native/libxcrash_dumper/jni/lzma/LzmaDec.h +++ b/src/native/libxcrash_dumper/jni/lzma/LzmaDec.h @@ -73,7 +73,7 @@ typedef struct Byte tempBuf[LZMA_REQUIRED_INPUT_MAX]; } CLzmaDec; -#define LzmaDec_Construct(p) { (p)->dic = NULL; (p)->probs = NULL; } +#define LzmaDec_Construct(p) do { (p)->dic = NULL; (p)->probs = NULL; } while(0) void LzmaDec_Init(CLzmaDec *p); diff --git a/src/native/libxcrash_dumper/jni/lzma/XzDec.c b/src/native/libxcrash_dumper/jni/lzma/XzDec.c index ebf1983..597321d 100644 --- a/src/native/libxcrash_dumper/jni/lzma/XzDec.c +++ b/src/native/libxcrash_dumper/jni/lzma/XzDec.c @@ -22,7 +22,7 @@ #ifdef SHOW_DEBUG_INFO #define PRF(x) x #else -#define PRF(x) +#define PRF(x) do {} while(0) #endif #define PRF_STR(s) PRF(printf("\n" s "\n")) @@ -771,8 +771,8 @@ static Bool Xz_CheckFooter(CXzStreamFlags flags, UInt64 indexSize, const Byte *b } #define READ_VARINT_AND_CHECK(buf, pos, size, res) \ - { unsigned s = Xz_ReadVarInt(buf + pos, size - pos, res); \ - if (s == 0) return SZ_ERROR_ARCHIVE; pos += s; } + do { unsigned s = Xz_ReadVarInt(buf + pos, size - pos, res); \ + if (s == 0) return SZ_ERROR_ARCHIVE; pos += s; } while(0) static Bool XzBlock_AreSupportedFilters(const CXzBlock *p) From 3f7cb5668273f9d231720530cd89f813bd05f19d Mon Sep 17 00:00:00 2001 From: Xu Qingqing Date: Mon, 21 Sep 2020 16:28:19 +0800 Subject: [PATCH 32/46] fix for compiling error with ndk r21b --- src/native/libxcrash/jni/xc_crash.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/native/libxcrash/jni/xc_crash.c b/src/native/libxcrash/jni/xc_crash.c index 0d49c57..d5f2661 100644 --- a/src/native/libxcrash/jni/xc_crash.c +++ b/src/native/libxcrash/jni/xc_crash.c @@ -458,7 +458,8 @@ static void xc_crash_signal_handler(int sig, siginfo_t *si, void *uc) if(EINVAL == errno) { //this kernel does not support PR_SET_PTRACER_ANY, or Yama is not enabled - errno = errno; + //errno = errno; + (void)errno; } else { From 4e4b80486bc8c2c5fcd5298296d64d36773644a7 Mon Sep 17 00:00:00 2001 From: Xu Qingqing Date: Mon, 21 Sep 2020 17:13:19 +0800 Subject: [PATCH 33/46] fix code style error --- .../src/main/java/xcrash/JavaCrashHandler.java | 3 +-- .../xcrash/xcrash_lib/src/main/java/xcrash/Util.java | 10 +++++----- 2 files changed, 6 insertions(+), 7 deletions(-) diff --git a/src/java/xcrash/xcrash_lib/src/main/java/xcrash/JavaCrashHandler.java b/src/java/xcrash/xcrash_lib/src/main/java/xcrash/JavaCrashHandler.java index 55bebb1..a2db3f0 100644 --- a/src/java/xcrash/xcrash_lib/src/main/java/xcrash/JavaCrashHandler.java +++ b/src/java/xcrash/xcrash_lib/src/main/java/xcrash/JavaCrashHandler.java @@ -208,7 +208,7 @@ private void handleException(Thread thread, Throwable throwable) { private String getLibInfo(List libPathList) { StringBuilder sb = new StringBuilder(); - for(String libPath : libPathList) { + for (String libPath : libPathList) { File libFile = new File(libPath); if (libFile.exists() && libFile.isFile()) { String md5 = Util.getFileMD5(libFile); @@ -236,7 +236,6 @@ private String getBuildId(String stktrace) { tempLibPathStr = stktrace.split("\""); // " is the delimiter for (String libPathStr : tempLibPathStr) { if (libPathStr.isEmpty() || !libPathStr.endsWith(".so")) continue; - libPathList.add(libPathStr); String libName = libPathStr.substring(libPathStr.lastIndexOf('/') + 1); diff --git a/src/java/xcrash/xcrash_lib/src/main/java/xcrash/Util.java b/src/java/xcrash/xcrash_lib/src/main/java/xcrash/Util.java index 5c916f3..f180191 100644 --- a/src/java/xcrash/xcrash_lib/src/main/java/xcrash/Util.java +++ b/src/java/xcrash/xcrash_lib/src/main/java/xcrash/Util.java @@ -501,7 +501,7 @@ public static String getSystemProperty(String key, String defaultValue) { try { Class clz = Class.forName("android.os.SystemProperties"); Method get = clz.getMethod("get", String.class, String.class); - return (String)get.invoke(clz, key, defaultValue); + return (String) get.invoke(clz, key, defaultValue); } catch (NoSuchMethodException var4) { var4.printStackTrace(); } catch (IllegalAccessException var5) { @@ -526,11 +526,11 @@ public static String getMobileModel() { String deviceName = ""; try { - Class SystemProperties = Class.forName("android.os.SystemProperties"); - Method get = SystemProperties.getDeclaredMethod("get", String.class, String.class); - deviceName = (String) get.invoke(SystemProperties, "ro.product.marketname", ""); + Class systemProperties = Class.forName("android.os.SystemProperties"); + Method get = systemProperties.getDeclaredMethod("get", String.class, String.class); + deviceName = (String) get.invoke(systemProperties, "ro.product.marketname", ""); if (TextUtils.isEmpty(deviceName)) { - deviceName = (String) get.invoke(SystemProperties, "ro.product.model", ""); + deviceName = (String) get.invoke(systemProperties, "ro.product.model", ""); } } catch (InvocationTargetException var3) { var3.printStackTrace(); From 708a24fadee692154055f0bd54d7601a5045020b Mon Sep 17 00:00:00 2001 From: Xu Qingqing Date: Wed, 30 Sep 2020 16:15:33 +0800 Subject: [PATCH 34/46] upgrade xcrash to support android 11 --- src/java/xcrash/build.gradle | 8 ++--- .../src/main/java/xcrash/XCrash.java | 2 +- .../xcrash/sample/MyCustomApplication.java | 3 +- src/native/common/xcc_util.h | 6 ++-- src/native/install.sh | 33 +++++++------------ src/native/libxcrash/jni/xc_crash.c | 5 ++- src/native/libxcrash/jni/xc_trace.c | 8 +++-- 7 files changed, 31 insertions(+), 34 deletions(-) diff --git a/src/java/xcrash/build.gradle b/src/java/xcrash/build.gradle index ec69aac..3af3751 100644 --- a/src/java/xcrash/build.gradle +++ b/src/java/xcrash/build.gradle @@ -23,14 +23,14 @@ task clean(type: Delete) { ext { minSdkVersion = 16 - compileSdkVersion = 29 - targetSdkVersion = 29 - buildToolsVersion = '29.0.2' + compileSdkVersion = 30 + targetSdkVersion = 30 + buildToolsVersion = '30.0.2' javaVersion = JavaVersion.VERSION_1_6 POM_GROUP_ID = "com.iqiyi.xcrash" POM_ARTIFACT_ID = "xcrash-android-lib" - POM_VERSION_NAME = "2.5.7" + POM_VERSION_NAME = "2.5.8" POM_NAME = "xCrash Android Lib" POM_DESCRIPTION = "xCrash provides the Android app with the ability to capture java crash, native crash and ANR." diff --git a/src/java/xcrash/xcrash_lib/src/main/java/xcrash/XCrash.java b/src/java/xcrash/xcrash_lib/src/main/java/xcrash/XCrash.java index 39e3715..bf3f622 100644 --- a/src/java/xcrash/xcrash_lib/src/main/java/xcrash/XCrash.java +++ b/src/java/xcrash/xcrash_lib/src/main/java/xcrash/XCrash.java @@ -863,7 +863,7 @@ static String getAppVersion() { return appVersion; } - static String getLogDir() { + public static String getLogDir() { return logDir; } diff --git a/src/java/xcrash/xcrash_sample/src/main/java/xcrash/sample/MyCustomApplication.java b/src/java/xcrash/xcrash_sample/src/main/java/xcrash/sample/MyCustomApplication.java index 0f59e3e..bee9c2d 100644 --- a/src/java/xcrash/xcrash_sample/src/main/java/xcrash/sample/MyCustomApplication.java +++ b/src/java/xcrash/xcrash_sample/src/main/java/xcrash/sample/MyCustomApplication.java @@ -89,6 +89,7 @@ public void onCrash(String logPath, String emergency) { .setAnrCallback(callback) .setPlaceholderCountMax(3) .setPlaceholderSizeKb(512) + .setLogDir(getExternalFilesDir("xcrash").toString()) .setLogFileMaintainDelayMs(1000)); Log.d(TAG, "xCrash SDK init: end"); @@ -124,7 +125,7 @@ private void debug(String logPath, String emergency) { // Parse and save the crash info to a JSON file for debugging. FileWriter writer = null; try { - File debug = new File(getApplicationContext().getFilesDir() + "/tombstones/debug.json"); + File debug = new File(XCrash.getLogDir() + "/debug.json"); debug.createNewFile(); writer = new FileWriter(debug, false); writer.write(new JSONObject(TombstoneParser.parse(logPath, emergency)).toString()); diff --git a/src/native/common/xcc_util.h b/src/native/common/xcc_util.h index 38975d4..792ba82 100644 --- a/src/native/common/xcc_util.h +++ b/src/native/common/xcc_util.h @@ -109,14 +109,16 @@ typedef struct #define XCC_UTIL_LIBART "/system/lib/libart.so" #define XCC_UTIL_LIBC_APEX "/apex/com.android.runtime/lib/bionic/libc.so" #define XCC_UTIL_LIBCPP_APEX "/apex/com.android.runtime/lib/libc++.so" -#define XCC_UTIL_LIBART_APEX "/apex/com.android.runtime/lib/libart.so" +#define XCC_UTIL_LIBART_APEX_29 "/apex/com.android.runtime/lib/libart.so" +#define XCC_UTIL_LIBART_APEX_30 "/apex/com.android.art/lib/libart.so" #else #define XCC_UTIL_LIBC "/system/lib64/libc.so" #define XCC_UTIL_LIBCPP "/system/lib64/libc++.so" #define XCC_UTIL_LIBART "/system/lib64/libart.so" #define XCC_UTIL_LIBC_APEX "/apex/com.android.runtime/lib64/bionic/libc.so" #define XCC_UTIL_LIBCPP_APEX "/apex/com.android.runtime/lib64/libc++.so" -#define XCC_UTIL_LIBART_APEX "/apex/com.android.runtime/lib64/libart.so" +#define XCC_UTIL_LIBART_APEX_29 "/apex/com.android.runtime/lib64/libart.so" +#define XCC_UTIL_LIBART_APEX_30 "/apex/com.android.art/lib64/libart.so" #endif #define XCC_UTIL_LIBC_ABORT_MSG_PTR "__abort_message_ptr" diff --git a/src/native/install.sh b/src/native/install.sh index 0a87ed9..7e4bbea 100755 --- a/src/native/install.sh +++ b/src/native/install.sh @@ -18,25 +18,14 @@ cp -f ./libxcrash_dumper/libs/arm64-v8a/xcrash_dumper ../java/xcrash/xcrash_li cp -f ./libxcrash_dumper/libs/x86/xcrash_dumper ../java/xcrash/xcrash_lib/src/main/jniLibs/x86/libxcrash_dumper.so cp -f ./libxcrash_dumper/libs/x86_64/xcrash_dumper ../java/xcrash/xcrash_lib/src/main/jniLibs/x86_64/libxcrash_dumper.so -version=`strings ./libxcrash_dumper/obj/local/armeabi-v7a/xcrash_dumper | grep 'Tombstone maker' | awk -F "\'" '{print $2}' | awk '{print $2}'` -folder=$version -tarfile=v${version}.tar.gz -rm -rf $folder $tarfile -mkdir -p ./$folder/armeabi-v7a -mkdir -p ./$folder/arm64-v8a - - -cp -f ./libxcrash/obj/local/armeabi-v7a/libxcrash.so ./$folder/armeabi-v7a/libxcrash.so -cp -f ./libxcrash/obj/local/arm64-v8a/libxcrash.so ./$folder/arm64-v8a/libxcrash.so -cp -f ./libxcrash_dumper/obj/local/armeabi-v7a/xcrash_dumper ./$folder/armeabi-v7a/libxcrash_dumper.so -cp -f ./libxcrash_dumper/obj/local/arm64-v8a/xcrash_dumper ./$folder/arm64-v8a/libxcrash_dumper.so - -tar cvzf $tarfile $folder -md5=`md5sum $tarfile | awk '{print $1}'` -sha1=`sha1sum $tarfile | awk '{print $1}'` - -#download -#curl -u iqiyi-generic-android-nativelib-debug:hr3QySAENz7u -o $tarfile "http://jfrog.cloud.qiyi.domain/iqiyi-generic-android-nativelib-debug/xcrash/$tarfile" - -#upload -curl -X PUT -u iqiyi-generic-android-nativelib-debug:hr3QySAENz7u "http://jfrog.cloud.qiyi.domain/iqiyi-generic-android-nativelib-debug/xcrash/$tarfile" -H "X-Checksum-Sha1:$sha1" -H "X-Checksum-Md5:$md5" -T $tarfile +#version=`strings ./libxcrash_dumper/obj/local/armeabi-v7a/xcrash_dumper | grep 'Tombstone maker' | awk -F "\'" '{print $2}' | awk '{print $2}'` +#folder=$version +#tarfile=v${version}.tar.gz +#rm -rf $folder $tarfile +#mkdir -p ./$folder/armeabi-v7a +#mkdir -p ./$folder/arm64-v8a +#cp -f ./libxcrash/obj/local/armeabi-v7a/libxcrash.so ./$folder/armeabi-v7a/libxcrash.so +#cp -f ./libxcrash/obj/local/arm64-v8a/libxcrash.so ./$folder/arm64-v8a/libxcrash.so +#cp -f ./libxcrash_dumper/obj/local/armeabi-v7a/xcrash_dumper ./$folder/armeabi-v7a/libxcrash_dumper.so +#cp -f ./libxcrash_dumper/obj/local/arm64-v8a/xcrash_dumper ./$folder/arm64-v8a/libxcrash_dumper.so +#tar cvzf $tarfile $folder > /dev/null 2>&1 diff --git a/src/native/libxcrash/jni/xc_crash.c b/src/native/libxcrash/jni/xc_crash.c index 9d1faac..b6fe248 100644 --- a/src/native/libxcrash/jni/xc_crash.c +++ b/src/native/libxcrash/jni/xc_crash.c @@ -257,7 +257,10 @@ static void xc_xcrash_record_java_stacktrace() if(NULL == (cerr = xc_dl_sym(libcpp, XCC_UTIL_LIBCPP_CERR))) goto end; //peek libart.so - if(xc_common_api_level >= 29) libart = xc_dl_create(XCC_UTIL_LIBART_APEX); + if(xc_common_api_level >= 30) + libart = xc_dl_create(XCC_UTIL_LIBART_APEX_30); + else if(xc_common_api_level == 29) + libart = xc_dl_create(XCC_UTIL_LIBART_APEX_29); if(NULL == libart && NULL == (libart = xc_dl_create(XCC_UTIL_LIBART))) goto end; if(NULL == (current = (xcc_util_libart_thread_current_t)xc_dl_sym(libart, XCC_UTIL_LIBART_THREAD_CURRENT))) goto end; if(NULL == (dump = (xcc_util_libart_thread_dump_t)xc_dl_sym(libart, XCC_UTIL_LIBART_THREAD_DUMP))) diff --git a/src/native/libxcrash/jni/xc_trace.c b/src/native/libxcrash/jni/xc_trace.c index 15b0d19..dc9cbe3 100644 --- a/src/native/libxcrash/jni/xc_trace.c +++ b/src/native/libxcrash/jni/xc_trace.c @@ -147,7 +147,10 @@ static int xc_trace_load_symbols() if(NULL == libcpp && NULL == (libcpp = xc_dl_create(XCC_UTIL_LIBCPP))) goto end; if(NULL == (xc_trace_libcpp_cerr = xc_dl_sym(libcpp, XCC_UTIL_LIBCPP_CERR))) goto end; - if(xc_common_api_level >= 29) libart = xc_dl_create(XCC_UTIL_LIBART_APEX); + if(xc_common_api_level >= 30) + libart = xc_dl_create(XCC_UTIL_LIBART_APEX_30); + else if(xc_common_api_level == 29) + libart = xc_dl_create(XCC_UTIL_LIBART_APEX_29); if(NULL == libart && NULL == (libart = xc_dl_create(XCC_UTIL_LIBART))) goto end; if(NULL == (xc_trace_libart_runtime_instance = (void **)xc_dl_sym(libart, XCC_UTIL_LIBART_RUNTIME_INSTANCE))) goto end; if(NULL == (xc_trace_libart_runtime_dump = (xcc_util_libart_runtime_dump_t)xc_dl_sym(libart, XCC_UTIL_LIBART_RUNTIME_DUMP))) goto end; @@ -180,7 +183,6 @@ static int xc_trace_check_address_valid() int r = XCC_ERRNO_INVAL; if(NULL == (f = fopen("/proc/self/maps", "r"))) return XCC_ERRNO_SYS; - while(fgets(line, sizeof(line), f)) { if(2 != sscanf(line, "%"SCNxPTR"-%"SCNxPTR" r", &start, &end)) continue; @@ -207,6 +209,7 @@ static int xc_trace_check_address_valid() } } if(0 != r) goto end; + if(xc_common_api_level >= 30) goto end; r = XCC_ERRNO_INVAL; rewind(f); @@ -221,7 +224,6 @@ static int xc_trace_check_address_valid() break; } } - end: fclose(f); return r; From 484654eff7fd247136d3f12f543ea5c5fdaee475 Mon Sep 17 00:00:00 2001 From: Xu Qingqing Date: Wed, 30 Sep 2020 16:37:02 +0800 Subject: [PATCH 35/46] new xcrash version 2.5.8 --- src/native/common/xcc_version.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/native/common/xcc_version.h b/src/native/common/xcc_version.h index 8789ab6..e1d3213 100644 --- a/src/native/common/xcc_version.h +++ b/src/native/common/xcc_version.h @@ -24,6 +24,6 @@ #ifndef XCC_VERSION_H #define XCC_VERSION_H 1 -#define XCC_VERSION_STR "xCrash 2.5.7" +#define XCC_VERSION_STR "xCrash 2.5.8" #endif From f9d02619b81b2a4c9f6003c16eb6dcf453fa99d7 Mon Sep 17 00:00:00 2001 From: Xu Qingqing Date: Wed, 30 Sep 2020 18:04:21 +0800 Subject: [PATCH 36/46] Change version to 2.5.9-beta --- src/java/xcrash/build.gradle | 2 +- src/java/xcrash/xcrash_lib/src/main/java/xcrash/Version.java | 2 +- src/native/common/xcc_version.h | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/java/xcrash/build.gradle b/src/java/xcrash/build.gradle index 3af3751..f3ac271 100644 --- a/src/java/xcrash/build.gradle +++ b/src/java/xcrash/build.gradle @@ -30,7 +30,7 @@ ext { POM_GROUP_ID = "com.iqiyi.xcrash" POM_ARTIFACT_ID = "xcrash-android-lib" - POM_VERSION_NAME = "2.5.8" + POM_VERSION_NAME = "2.5.9-beta" POM_NAME = "xCrash Android Lib" POM_DESCRIPTION = "xCrash provides the Android app with the ability to capture java crash, native crash and ANR." diff --git a/src/java/xcrash/xcrash_lib/src/main/java/xcrash/Version.java b/src/java/xcrash/xcrash_lib/src/main/java/xcrash/Version.java index 6a8916f..067c79d 100644 --- a/src/java/xcrash/xcrash_lib/src/main/java/xcrash/Version.java +++ b/src/java/xcrash/xcrash_lib/src/main/java/xcrash/Version.java @@ -27,6 +27,6 @@ class Version { private Version() { } - static final String version = "2.5.7"; + static final String version = "2.5.9"; static final String fullVersion = "xCrash " + version; } diff --git a/src/native/common/xcc_version.h b/src/native/common/xcc_version.h index e1d3213..f5ce3ee 100644 --- a/src/native/common/xcc_version.h +++ b/src/native/common/xcc_version.h @@ -24,6 +24,6 @@ #ifndef XCC_VERSION_H #define XCC_VERSION_H 1 -#define XCC_VERSION_STR "xCrash 2.5.8" +#define XCC_VERSION_STR "xCrash 2.5.9" #endif From f9febb15da345ccbec83ed5cfbdedfe161ee7f01 Mon Sep 17 00:00:00 2001 From: Xu Qingqing Date: Wed, 14 Oct 2020 20:08:29 +0800 Subject: [PATCH 37/46] version 2.5.9 --- src/java/xcrash/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/java/xcrash/build.gradle b/src/java/xcrash/build.gradle index f3ac271..4a7056d 100644 --- a/src/java/xcrash/build.gradle +++ b/src/java/xcrash/build.gradle @@ -30,7 +30,7 @@ ext { POM_GROUP_ID = "com.iqiyi.xcrash" POM_ARTIFACT_ID = "xcrash-android-lib" - POM_VERSION_NAME = "2.5.9-beta" + POM_VERSION_NAME = "2.5.9" POM_NAME = "xCrash Android Lib" POM_DESCRIPTION = "xCrash provides the Android app with the ability to capture java crash, native crash and ANR." From f3cd5a81487b070d29414b2aff6d326f980a4956 Mon Sep 17 00:00:00 2001 From: Xu Qingqing Date: Tue, 27 Oct 2020 17:57:34 +0800 Subject: [PATCH 38/46] Use CMake instead of ndk-build, Support AddressSanitizer(ASan), refactor project struture --- .gitignore | 5 +- README.md | 34 ++-- README.zh-CN.md | 37 ++--- src/java/xcrash/build.gradle => build.gradle | 8 +- .../xcrash/checkstyle.xml => checkstyle.xml | 0 .../gradle.properties => gradle.properties | 6 + .../xcrash/gradle => gradle}/check.gradle | 0 .../xcrash/gradle => gradle}/publish.gradle | 0 gradle/sanitizer.gradle | 60 ++++++++ .../wrapper/gradle-wrapper.jar | Bin .../wrapper/gradle-wrapper.properties | 4 +- src/java/xcrash/gradlew => gradlew | 0 src/java/xcrash/gradlew.bat => gradlew.bat | 0 .../xcrash/settings.gradle => settings.gradle | 0 src/java/xcrash/.gitignore | 11 -- src/native/build.sh | 4 - src/native/clean.sh | 4 - src/native/install.sh | 31 ---- src/native/libxcrash/jni/Android.mk | 25 --- src/native/libxcrash/jni/Application.mk | 2 - src/native/libxcrash_dumper/jni/Android.mk | 12 -- .../libxcrash_dumper/jni/Application.mk | 2 - .../libxcrash_dumper/jni/lzma/Android.mk | 38 ----- .../xcrash_lib => xcrash_lib}/.gitignore | 0 .../xcrash_lib => xcrash_lib}/build.gradle | 16 ++ .../proguard-rules.pro | 0 .../src/main/AndroidManifest.xml | 0 xcrash_lib/src/main/cpp/CMakeLists.txt | 145 ++++++++++++++++++ .../src/main/cpp}/common/queue.h | 0 .../src/main/cpp}/common/tree.h | 0 .../src/main/cpp}/common/xcc_b64.c | 0 .../src/main/cpp}/common/xcc_b64.h | 0 .../src/main/cpp}/common/xcc_errno.h | 0 .../src/main/cpp}/common/xcc_fmt.c | 0 .../src/main/cpp}/common/xcc_fmt.h | 0 .../src/main/cpp}/common/xcc_libc_support.c | 0 .../src/main/cpp}/common/xcc_libc_support.h | 0 .../src/main/cpp}/common/xcc_meminfo.c | 0 .../src/main/cpp}/common/xcc_meminfo.h | 0 .../src/main/cpp}/common/xcc_signal.c | 0 .../src/main/cpp}/common/xcc_signal.h | 0 .../src/main/cpp}/common/xcc_spot.h | 0 .../src/main/cpp}/common/xcc_unwind.c | 0 .../src/main/cpp}/common/xcc_unwind.h | 0 .../src/main/cpp}/common/xcc_unwind_clang.c | 0 .../src/main/cpp}/common/xcc_unwind_clang.h | 0 .../cpp}/common/xcc_unwind_libcorkscrew.c | 0 .../cpp}/common/xcc_unwind_libcorkscrew.h | 0 .../main/cpp}/common/xcc_unwind_libunwind.c | 0 .../main/cpp}/common/xcc_unwind_libunwind.h | 0 .../src/main/cpp}/common/xcc_util.c | 2 +- .../src/main/cpp}/common/xcc_util.h | 0 .../src/main/cpp}/common/xcc_version.h | 2 +- xcrash_lib/src/main/cpp/dl/README.md | 117 ++++++++++++++ .../src/main/cpp/dl}/xc_dl.c | 0 .../src/main/cpp/dl}/xc_dl.h | 0 .../jni => xcrash_lib/src/main/cpp}/lzma/7z.h | 0 .../src/main/cpp}/lzma/7zAlloc.c | 0 .../src/main/cpp}/lzma/7zAlloc.h | 0 .../src/main/cpp}/lzma/7zArcIn.c | 0 .../src/main/cpp}/lzma/7zBuf.c | 0 .../src/main/cpp}/lzma/7zBuf.h | 0 .../src/main/cpp}/lzma/7zBuf2.c | 0 .../src/main/cpp}/lzma/7zCrc.c | 0 .../src/main/cpp}/lzma/7zCrc.h | 0 .../src/main/cpp}/lzma/7zCrcOpt.c | 0 .../src/main/cpp}/lzma/7zDec.c | 0 .../src/main/cpp}/lzma/7zFile.c | 0 .../src/main/cpp}/lzma/7zFile.h | 0 .../src/main/cpp}/lzma/7zStream.c | 0 .../src/main/cpp}/lzma/7zTypes.h | 0 .../src/main/cpp}/lzma/7zVersion.h | 0 .../src/main/cpp}/lzma/7zVersion.rc | 0 .../src/main/cpp}/lzma/Aes.c | 0 .../src/main/cpp}/lzma/Aes.h | 0 .../src/main/cpp}/lzma/AesOpt.c | 0 .../src/main/cpp}/lzma/Alloc.c | 0 .../src/main/cpp}/lzma/Alloc.h | 0 .../src/main/cpp}/lzma/Bcj2.c | 0 .../src/main/cpp}/lzma/Bcj2.h | 0 .../src/main/cpp}/lzma/Bcj2Enc.c | 0 .../src/main/cpp}/lzma/Bra.c | 0 .../src/main/cpp}/lzma/Bra.h | 0 .../src/main/cpp}/lzma/Bra86.c | 0 .../src/main/cpp}/lzma/BraIA64.c | 0 .../src/main/cpp}/lzma/Compiler.h | 0 .../src/main/cpp}/lzma/CpuArch.c | 0 .../src/main/cpp}/lzma/CpuArch.h | 0 .../src/main/cpp}/lzma/Delta.c | 0 .../src/main/cpp}/lzma/Delta.h | 0 .../src/main/cpp}/lzma/DllSecur.c | 0 .../src/main/cpp}/lzma/DllSecur.h | 0 .../src/main/cpp}/lzma/LICENSE | 0 .../src/main/cpp}/lzma/LzFind.c | 0 .../src/main/cpp}/lzma/LzFind.h | 0 .../src/main/cpp}/lzma/LzFindMt.c | 0 .../src/main/cpp}/lzma/LzFindMt.h | 0 .../src/main/cpp}/lzma/LzHash.h | 0 .../src/main/cpp}/lzma/Lzma2Dec.c | 0 .../src/main/cpp}/lzma/Lzma2Dec.h | 0 .../src/main/cpp}/lzma/Lzma2DecMt.c | 0 .../src/main/cpp}/lzma/Lzma2DecMt.h | 0 .../src/main/cpp}/lzma/Lzma2Enc.c | 0 .../src/main/cpp}/lzma/Lzma2Enc.h | 0 .../src/main/cpp}/lzma/Lzma86.h | 0 .../src/main/cpp}/lzma/Lzma86Dec.c | 0 .../src/main/cpp}/lzma/Lzma86Enc.c | 0 .../src/main/cpp}/lzma/LzmaDec.c | 0 .../src/main/cpp}/lzma/LzmaDec.h | 0 .../src/main/cpp}/lzma/LzmaEnc.c | 0 .../src/main/cpp}/lzma/LzmaEnc.h | 0 .../src/main/cpp}/lzma/LzmaLib.c | 0 .../src/main/cpp}/lzma/LzmaLib.h | 0 .../src/main/cpp}/lzma/MtCoder.c | 0 .../src/main/cpp}/lzma/MtCoder.h | 0 .../src/main/cpp}/lzma/MtDec.c | 0 .../src/main/cpp}/lzma/MtDec.h | 0 .../src/main/cpp}/lzma/Ppmd.h | 0 .../src/main/cpp}/lzma/Ppmd7.c | 0 .../src/main/cpp}/lzma/Ppmd7.h | 0 .../src/main/cpp}/lzma/Ppmd7Dec.c | 0 .../src/main/cpp}/lzma/Ppmd7Enc.c | 0 .../src/main/cpp}/lzma/Precomp.h | 0 .../src/main/cpp}/lzma/RotateDefs.h | 0 .../src/main/cpp}/lzma/Sha256.c | 0 .../src/main/cpp}/lzma/Sha256.h | 0 .../src/main/cpp}/lzma/Sort.c | 0 .../src/main/cpp}/lzma/Sort.h | 0 .../src/main/cpp}/lzma/Threads.c | 0 .../src/main/cpp}/lzma/Threads.h | 0 .../jni => xcrash_lib/src/main/cpp}/lzma/Xz.c | 0 .../jni => xcrash_lib/src/main/cpp}/lzma/Xz.h | 0 .../src/main/cpp}/lzma/XzCrc64.c | 0 .../src/main/cpp}/lzma/XzCrc64.h | 0 .../src/main/cpp}/lzma/XzCrc64Opt.c | 0 .../src/main/cpp}/lzma/XzDec.c | 0 .../src/main/cpp}/lzma/XzEnc.c | 0 .../src/main/cpp}/lzma/XzEnc.h | 0 .../src/main/cpp}/lzma/XzIn.c | 0 xcrash_lib/src/main/cpp/xcrash.exports | 12 ++ .../src/main/cpp/xcrash}/xc_common.c | 0 .../src/main/cpp/xcrash}/xc_common.h | 0 .../src/main/cpp/xcrash}/xc_crash.c | 0 .../src/main/cpp/xcrash}/xc_crash.h | 0 .../src/main/cpp/xcrash}/xc_fallback.c | 0 .../src/main/cpp/xcrash}/xc_fallback.h | 0 .../src/main/cpp/xcrash}/xc_jni.c | 0 .../src/main/cpp/xcrash}/xc_jni.h | 0 .../src/main/cpp/xcrash}/xc_test.c | 0 .../src/main/cpp/xcrash}/xc_test.h | 0 .../src/main/cpp/xcrash}/xc_trace.c | 0 .../src/main/cpp/xcrash}/xc_trace.h | 0 .../src/main/cpp/xcrash}/xc_util.c | 0 .../src/main/cpp/xcrash}/xc_util.h | 0 .../main/cpp/xcrash_dumper}/xcd_arm_exidx.c | 0 .../main/cpp/xcrash_dumper}/xcd_arm_exidx.h | 0 .../src/main/cpp/xcrash_dumper}/xcd_core.c | 0 .../src/main/cpp/xcrash_dumper}/xcd_dwarf.c | 10 +- .../src/main/cpp/xcrash_dumper}/xcd_dwarf.h | 2 +- .../src/main/cpp/xcrash_dumper}/xcd_elf.c | 13 +- .../src/main/cpp/xcrash_dumper}/xcd_elf.h | 0 .../cpp/xcrash_dumper}/xcd_elf_interface.c | 56 +++---- .../cpp/xcrash_dumper}/xcd_elf_interface.h | 0 .../src/main/cpp/xcrash_dumper}/xcd_frames.c | 0 .../src/main/cpp/xcrash_dumper}/xcd_frames.h | 0 .../src/main/cpp/xcrash_dumper}/xcd_log.h | 0 .../src/main/cpp/xcrash_dumper}/xcd_map.c | 8 +- .../src/main/cpp/xcrash_dumper}/xcd_map.h | 4 +- .../src/main/cpp/xcrash_dumper}/xcd_maps.c | 15 +- .../src/main/cpp/xcrash_dumper}/xcd_maps.h | 0 .../src/main/cpp/xcrash_dumper}/xcd_md5.c | 0 .../src/main/cpp/xcrash_dumper}/xcd_md5.h | 0 .../src/main/cpp/xcrash_dumper}/xcd_memory.c | 0 .../src/main/cpp/xcrash_dumper}/xcd_memory.h | 0 .../main/cpp/xcrash_dumper}/xcd_memory_buf.c | 0 .../main/cpp/xcrash_dumper}/xcd_memory_buf.h | 0 .../main/cpp/xcrash_dumper}/xcd_memory_file.c | 0 .../main/cpp/xcrash_dumper}/xcd_memory_file.h | 0 .../cpp/xcrash_dumper}/xcd_memory_remote.c | 0 .../cpp/xcrash_dumper}/xcd_memory_remote.h | 0 .../src/main/cpp/xcrash_dumper}/xcd_process.c | 0 .../src/main/cpp/xcrash_dumper}/xcd_process.h | 0 .../src/main/cpp/xcrash_dumper}/xcd_regs.h | 0 .../main/cpp/xcrash_dumper}/xcd_regs_arm.c | 0 .../main/cpp/xcrash_dumper}/xcd_regs_arm64.c | 0 .../main/cpp/xcrash_dumper}/xcd_regs_x86.c | 0 .../main/cpp/xcrash_dumper}/xcd_regs_x86_64.c | 0 .../src/main/cpp/xcrash_dumper}/xcd_sys.c | 0 .../src/main/cpp/xcrash_dumper}/xcd_sys.h | 0 .../src/main/cpp/xcrash_dumper}/xcd_thread.c | 0 .../src/main/cpp/xcrash_dumper}/xcd_thread.h | 0 .../src/main/cpp/xcrash_dumper}/xcd_util.c | 117 +++++++++++--- .../src/main/cpp/xcrash_dumper}/xcd_util.h | 20 +-- .../src/main/java/xcrash/ActivityMonitor.java | 0 .../src/main/java/xcrash/AnrHandler.java | 0 .../src/main/java/xcrash/DefaultLogger.java | 0 .../src/main/java/xcrash/Errno.java | 0 .../src/main/java/xcrash/FileManager.java | 0 .../src/main/java/xcrash/ICrashCallback.java | 0 .../src/main/java/xcrash/ILibLoader.java | 0 .../src/main/java/xcrash/ILogger.java | 0 .../main/java/xcrash/JavaCrashHandler.java | 0 .../src/main/java/xcrash/NativeHandler.java | 0 .../main/java/xcrash/TombstoneManager.java | 0 .../src/main/java/xcrash/TombstoneParser.java | 0 .../src/main/java/xcrash/Util.java | 0 .../src/main/java/xcrash/Version.java | 2 +- .../src/main/java/xcrash/XCrash.java | 0 .../.gitignore | 0 .../build.gradle | 10 +- .../proguard-rules.pro | 0 .../src/main/AndroidManifest.xml | 0 .../main/java/xcrash/sample/MainActivity.java | 0 .../xcrash/sample/MyCustomApplication.java | 0 .../main/java/xcrash/sample/MyService.java | 0 .../java/xcrash/sample/SecondActivity.java | 0 .../drawable-v24/ic_launcher_foreground.xml | 0 .../res/drawable/ic_launcher_background.xml | 0 .../src/main/res/layout/activity_main.xml | 0 .../src/main/res/layout/activity_second.xml | 0 .../res/mipmap-anydpi-v26/ic_launcher.xml | 0 .../mipmap-anydpi-v26/ic_launcher_round.xml | 0 .../src/main/res/mipmap-hdpi/ic_launcher.png | Bin .../res/mipmap-hdpi/ic_launcher_round.png | Bin .../src/main/res/mipmap-mdpi/ic_launcher.png | Bin .../res/mipmap-mdpi/ic_launcher_round.png | Bin .../src/main/res/mipmap-xhdpi/ic_launcher.png | Bin .../res/mipmap-xhdpi/ic_launcher_round.png | Bin .../main/res/mipmap-xxhdpi/ic_launcher.png | Bin .../res/mipmap-xxhdpi/ic_launcher_round.png | Bin .../main/res/mipmap-xxxhdpi/ic_launcher.png | Bin .../res/mipmap-xxxhdpi/ic_launcher_round.png | Bin .../src/main/res/values/colors.xml | 0 .../src/main/res/values/strings.xml | 0 .../src/main/res/values/styles.xml | 0 235 files changed, 543 insertions(+), 291 deletions(-) rename src/java/xcrash/build.gradle => build.gradle (85%) rename src/java/xcrash/checkstyle.xml => checkstyle.xml (100%) rename src/java/xcrash/gradle.properties => gradle.properties (67%) rename {src/java/xcrash/gradle => gradle}/check.gradle (100%) rename {src/java/xcrash/gradle => gradle}/publish.gradle (100%) create mode 100644 gradle/sanitizer.gradle rename {src/java/xcrash/gradle => gradle}/wrapper/gradle-wrapper.jar (100%) rename {src/java/xcrash/gradle => gradle}/wrapper/gradle-wrapper.properties (80%) rename src/java/xcrash/gradlew => gradlew (100%) rename src/java/xcrash/gradlew.bat => gradlew.bat (100%) rename src/java/xcrash/settings.gradle => settings.gradle (100%) delete mode 100644 src/java/xcrash/.gitignore delete mode 100755 src/native/build.sh delete mode 100755 src/native/clean.sh delete mode 100755 src/native/install.sh delete mode 100644 src/native/libxcrash/jni/Android.mk delete mode 100644 src/native/libxcrash/jni/Application.mk delete mode 100644 src/native/libxcrash_dumper/jni/Android.mk delete mode 100644 src/native/libxcrash_dumper/jni/Application.mk delete mode 100644 src/native/libxcrash_dumper/jni/lzma/Android.mk rename {src/java/xcrash/xcrash_lib => xcrash_lib}/.gitignore (100%) rename {src/java/xcrash/xcrash_lib => xcrash_lib}/build.gradle (59%) rename {src/java/xcrash/xcrash_lib => xcrash_lib}/proguard-rules.pro (100%) rename {src/java/xcrash/xcrash_lib => xcrash_lib}/src/main/AndroidManifest.xml (100%) create mode 100644 xcrash_lib/src/main/cpp/CMakeLists.txt rename {src/native => xcrash_lib/src/main/cpp}/common/queue.h (100%) rename {src/native => xcrash_lib/src/main/cpp}/common/tree.h (100%) rename {src/native => xcrash_lib/src/main/cpp}/common/xcc_b64.c (100%) rename {src/native => xcrash_lib/src/main/cpp}/common/xcc_b64.h (100%) rename {src/native => xcrash_lib/src/main/cpp}/common/xcc_errno.h (100%) rename {src/native => xcrash_lib/src/main/cpp}/common/xcc_fmt.c (100%) rename {src/native => xcrash_lib/src/main/cpp}/common/xcc_fmt.h (100%) rename {src/native => xcrash_lib/src/main/cpp}/common/xcc_libc_support.c (100%) rename {src/native => xcrash_lib/src/main/cpp}/common/xcc_libc_support.h (100%) rename {src/native => xcrash_lib/src/main/cpp}/common/xcc_meminfo.c (100%) rename {src/native => xcrash_lib/src/main/cpp}/common/xcc_meminfo.h (100%) rename {src/native => xcrash_lib/src/main/cpp}/common/xcc_signal.c (100%) rename {src/native => xcrash_lib/src/main/cpp}/common/xcc_signal.h (100%) rename {src/native => xcrash_lib/src/main/cpp}/common/xcc_spot.h (100%) rename {src/native => xcrash_lib/src/main/cpp}/common/xcc_unwind.c (100%) rename {src/native => xcrash_lib/src/main/cpp}/common/xcc_unwind.h (100%) rename {src/native => xcrash_lib/src/main/cpp}/common/xcc_unwind_clang.c (100%) rename {src/native => xcrash_lib/src/main/cpp}/common/xcc_unwind_clang.h (100%) rename {src/native => xcrash_lib/src/main/cpp}/common/xcc_unwind_libcorkscrew.c (100%) rename {src/native => xcrash_lib/src/main/cpp}/common/xcc_unwind_libcorkscrew.h (100%) rename {src/native => xcrash_lib/src/main/cpp}/common/xcc_unwind_libunwind.c (100%) rename {src/native => xcrash_lib/src/main/cpp}/common/xcc_unwind_libunwind.h (100%) rename {src/native => xcrash_lib/src/main/cpp}/common/xcc_util.c (99%) rename {src/native => xcrash_lib/src/main/cpp}/common/xcc_util.h (100%) rename {src/native => xcrash_lib/src/main/cpp}/common/xcc_version.h (96%) create mode 100644 xcrash_lib/src/main/cpp/dl/README.md rename {src/native/libxcrash/jni => xcrash_lib/src/main/cpp/dl}/xc_dl.c (100%) rename {src/native/libxcrash/jni => xcrash_lib/src/main/cpp/dl}/xc_dl.h (100%) rename {src/native/libxcrash_dumper/jni => xcrash_lib/src/main/cpp}/lzma/7z.h (100%) rename {src/native/libxcrash_dumper/jni => xcrash_lib/src/main/cpp}/lzma/7zAlloc.c (100%) rename {src/native/libxcrash_dumper/jni => xcrash_lib/src/main/cpp}/lzma/7zAlloc.h (100%) rename {src/native/libxcrash_dumper/jni => xcrash_lib/src/main/cpp}/lzma/7zArcIn.c (100%) rename {src/native/libxcrash_dumper/jni => xcrash_lib/src/main/cpp}/lzma/7zBuf.c (100%) rename {src/native/libxcrash_dumper/jni => xcrash_lib/src/main/cpp}/lzma/7zBuf.h (100%) rename {src/native/libxcrash_dumper/jni => xcrash_lib/src/main/cpp}/lzma/7zBuf2.c (100%) rename {src/native/libxcrash_dumper/jni => xcrash_lib/src/main/cpp}/lzma/7zCrc.c (100%) rename {src/native/libxcrash_dumper/jni => xcrash_lib/src/main/cpp}/lzma/7zCrc.h (100%) rename {src/native/libxcrash_dumper/jni => xcrash_lib/src/main/cpp}/lzma/7zCrcOpt.c (100%) rename {src/native/libxcrash_dumper/jni => xcrash_lib/src/main/cpp}/lzma/7zDec.c (100%) rename {src/native/libxcrash_dumper/jni => xcrash_lib/src/main/cpp}/lzma/7zFile.c (100%) rename {src/native/libxcrash_dumper/jni => xcrash_lib/src/main/cpp}/lzma/7zFile.h (100%) rename {src/native/libxcrash_dumper/jni => xcrash_lib/src/main/cpp}/lzma/7zStream.c (100%) rename {src/native/libxcrash_dumper/jni => xcrash_lib/src/main/cpp}/lzma/7zTypes.h (100%) rename {src/native/libxcrash_dumper/jni => xcrash_lib/src/main/cpp}/lzma/7zVersion.h (100%) rename {src/native/libxcrash_dumper/jni => xcrash_lib/src/main/cpp}/lzma/7zVersion.rc (100%) rename {src/native/libxcrash_dumper/jni => xcrash_lib/src/main/cpp}/lzma/Aes.c (100%) rename {src/native/libxcrash_dumper/jni => xcrash_lib/src/main/cpp}/lzma/Aes.h (100%) rename {src/native/libxcrash_dumper/jni => xcrash_lib/src/main/cpp}/lzma/AesOpt.c (100%) rename {src/native/libxcrash_dumper/jni => xcrash_lib/src/main/cpp}/lzma/Alloc.c (100%) rename {src/native/libxcrash_dumper/jni => xcrash_lib/src/main/cpp}/lzma/Alloc.h (100%) rename {src/native/libxcrash_dumper/jni => xcrash_lib/src/main/cpp}/lzma/Bcj2.c (100%) rename {src/native/libxcrash_dumper/jni => xcrash_lib/src/main/cpp}/lzma/Bcj2.h (100%) rename {src/native/libxcrash_dumper/jni => xcrash_lib/src/main/cpp}/lzma/Bcj2Enc.c (100%) rename {src/native/libxcrash_dumper/jni => xcrash_lib/src/main/cpp}/lzma/Bra.c (100%) rename {src/native/libxcrash_dumper/jni => xcrash_lib/src/main/cpp}/lzma/Bra.h (100%) rename {src/native/libxcrash_dumper/jni => xcrash_lib/src/main/cpp}/lzma/Bra86.c (100%) rename {src/native/libxcrash_dumper/jni => xcrash_lib/src/main/cpp}/lzma/BraIA64.c (100%) rename {src/native/libxcrash_dumper/jni => xcrash_lib/src/main/cpp}/lzma/Compiler.h (100%) rename {src/native/libxcrash_dumper/jni => xcrash_lib/src/main/cpp}/lzma/CpuArch.c (100%) rename {src/native/libxcrash_dumper/jni => xcrash_lib/src/main/cpp}/lzma/CpuArch.h (100%) rename {src/native/libxcrash_dumper/jni => xcrash_lib/src/main/cpp}/lzma/Delta.c (100%) rename {src/native/libxcrash_dumper/jni => xcrash_lib/src/main/cpp}/lzma/Delta.h (100%) rename {src/native/libxcrash_dumper/jni => xcrash_lib/src/main/cpp}/lzma/DllSecur.c (100%) rename {src/native/libxcrash_dumper/jni => xcrash_lib/src/main/cpp}/lzma/DllSecur.h (100%) rename {src/native/libxcrash_dumper/jni => xcrash_lib/src/main/cpp}/lzma/LICENSE (100%) rename {src/native/libxcrash_dumper/jni => xcrash_lib/src/main/cpp}/lzma/LzFind.c (100%) rename {src/native/libxcrash_dumper/jni => xcrash_lib/src/main/cpp}/lzma/LzFind.h (100%) rename {src/native/libxcrash_dumper/jni => xcrash_lib/src/main/cpp}/lzma/LzFindMt.c (100%) rename {src/native/libxcrash_dumper/jni => xcrash_lib/src/main/cpp}/lzma/LzFindMt.h (100%) rename {src/native/libxcrash_dumper/jni => xcrash_lib/src/main/cpp}/lzma/LzHash.h (100%) rename {src/native/libxcrash_dumper/jni => xcrash_lib/src/main/cpp}/lzma/Lzma2Dec.c (100%) rename {src/native/libxcrash_dumper/jni => xcrash_lib/src/main/cpp}/lzma/Lzma2Dec.h (100%) rename {src/native/libxcrash_dumper/jni => xcrash_lib/src/main/cpp}/lzma/Lzma2DecMt.c (100%) rename {src/native/libxcrash_dumper/jni => xcrash_lib/src/main/cpp}/lzma/Lzma2DecMt.h (100%) rename {src/native/libxcrash_dumper/jni => xcrash_lib/src/main/cpp}/lzma/Lzma2Enc.c (100%) rename {src/native/libxcrash_dumper/jni => xcrash_lib/src/main/cpp}/lzma/Lzma2Enc.h (100%) rename {src/native/libxcrash_dumper/jni => xcrash_lib/src/main/cpp}/lzma/Lzma86.h (100%) rename {src/native/libxcrash_dumper/jni => xcrash_lib/src/main/cpp}/lzma/Lzma86Dec.c (100%) rename {src/native/libxcrash_dumper/jni => xcrash_lib/src/main/cpp}/lzma/Lzma86Enc.c (100%) rename {src/native/libxcrash_dumper/jni => xcrash_lib/src/main/cpp}/lzma/LzmaDec.c (100%) rename {src/native/libxcrash_dumper/jni => xcrash_lib/src/main/cpp}/lzma/LzmaDec.h (100%) rename {src/native/libxcrash_dumper/jni => xcrash_lib/src/main/cpp}/lzma/LzmaEnc.c (100%) rename {src/native/libxcrash_dumper/jni => xcrash_lib/src/main/cpp}/lzma/LzmaEnc.h (100%) rename {src/native/libxcrash_dumper/jni => xcrash_lib/src/main/cpp}/lzma/LzmaLib.c (100%) rename {src/native/libxcrash_dumper/jni => xcrash_lib/src/main/cpp}/lzma/LzmaLib.h (100%) rename {src/native/libxcrash_dumper/jni => xcrash_lib/src/main/cpp}/lzma/MtCoder.c (100%) rename {src/native/libxcrash_dumper/jni => xcrash_lib/src/main/cpp}/lzma/MtCoder.h (100%) rename {src/native/libxcrash_dumper/jni => xcrash_lib/src/main/cpp}/lzma/MtDec.c (100%) rename {src/native/libxcrash_dumper/jni => xcrash_lib/src/main/cpp}/lzma/MtDec.h (100%) rename {src/native/libxcrash_dumper/jni => xcrash_lib/src/main/cpp}/lzma/Ppmd.h (100%) rename {src/native/libxcrash_dumper/jni => xcrash_lib/src/main/cpp}/lzma/Ppmd7.c (100%) rename {src/native/libxcrash_dumper/jni => xcrash_lib/src/main/cpp}/lzma/Ppmd7.h (100%) rename {src/native/libxcrash_dumper/jni => xcrash_lib/src/main/cpp}/lzma/Ppmd7Dec.c (100%) rename {src/native/libxcrash_dumper/jni => xcrash_lib/src/main/cpp}/lzma/Ppmd7Enc.c (100%) rename {src/native/libxcrash_dumper/jni => xcrash_lib/src/main/cpp}/lzma/Precomp.h (100%) rename {src/native/libxcrash_dumper/jni => xcrash_lib/src/main/cpp}/lzma/RotateDefs.h (100%) rename {src/native/libxcrash_dumper/jni => xcrash_lib/src/main/cpp}/lzma/Sha256.c (100%) rename {src/native/libxcrash_dumper/jni => xcrash_lib/src/main/cpp}/lzma/Sha256.h (100%) rename {src/native/libxcrash_dumper/jni => xcrash_lib/src/main/cpp}/lzma/Sort.c (100%) rename {src/native/libxcrash_dumper/jni => xcrash_lib/src/main/cpp}/lzma/Sort.h (100%) rename {src/native/libxcrash_dumper/jni => xcrash_lib/src/main/cpp}/lzma/Threads.c (100%) rename {src/native/libxcrash_dumper/jni => xcrash_lib/src/main/cpp}/lzma/Threads.h (100%) rename {src/native/libxcrash_dumper/jni => xcrash_lib/src/main/cpp}/lzma/Xz.c (100%) rename {src/native/libxcrash_dumper/jni => xcrash_lib/src/main/cpp}/lzma/Xz.h (100%) rename {src/native/libxcrash_dumper/jni => xcrash_lib/src/main/cpp}/lzma/XzCrc64.c (100%) rename {src/native/libxcrash_dumper/jni => xcrash_lib/src/main/cpp}/lzma/XzCrc64.h (100%) rename {src/native/libxcrash_dumper/jni => xcrash_lib/src/main/cpp}/lzma/XzCrc64Opt.c (100%) rename {src/native/libxcrash_dumper/jni => xcrash_lib/src/main/cpp}/lzma/XzDec.c (100%) rename {src/native/libxcrash_dumper/jni => xcrash_lib/src/main/cpp}/lzma/XzEnc.c (100%) rename {src/native/libxcrash_dumper/jni => xcrash_lib/src/main/cpp}/lzma/XzEnc.h (100%) rename {src/native/libxcrash_dumper/jni => xcrash_lib/src/main/cpp}/lzma/XzIn.c (100%) create mode 100644 xcrash_lib/src/main/cpp/xcrash.exports rename {src/native/libxcrash/jni => xcrash_lib/src/main/cpp/xcrash}/xc_common.c (100%) rename {src/native/libxcrash/jni => xcrash_lib/src/main/cpp/xcrash}/xc_common.h (100%) rename {src/native/libxcrash/jni => xcrash_lib/src/main/cpp/xcrash}/xc_crash.c (100%) rename {src/native/libxcrash/jni => xcrash_lib/src/main/cpp/xcrash}/xc_crash.h (100%) rename {src/native/libxcrash/jni => xcrash_lib/src/main/cpp/xcrash}/xc_fallback.c (100%) rename {src/native/libxcrash/jni => xcrash_lib/src/main/cpp/xcrash}/xc_fallback.h (100%) rename {src/native/libxcrash/jni => xcrash_lib/src/main/cpp/xcrash}/xc_jni.c (100%) rename {src/native/libxcrash/jni => xcrash_lib/src/main/cpp/xcrash}/xc_jni.h (100%) rename {src/native/libxcrash/jni => xcrash_lib/src/main/cpp/xcrash}/xc_test.c (100%) rename {src/native/libxcrash/jni => xcrash_lib/src/main/cpp/xcrash}/xc_test.h (100%) rename {src/native/libxcrash/jni => xcrash_lib/src/main/cpp/xcrash}/xc_trace.c (100%) rename {src/native/libxcrash/jni => xcrash_lib/src/main/cpp/xcrash}/xc_trace.h (100%) rename {src/native/libxcrash/jni => xcrash_lib/src/main/cpp/xcrash}/xc_util.c (100%) rename {src/native/libxcrash/jni => xcrash_lib/src/main/cpp/xcrash}/xc_util.h (100%) rename {src/native/libxcrash_dumper/jni => xcrash_lib/src/main/cpp/xcrash_dumper}/xcd_arm_exidx.c (100%) rename {src/native/libxcrash_dumper/jni => xcrash_lib/src/main/cpp/xcrash_dumper}/xcd_arm_exidx.h (100%) rename {src/native/libxcrash_dumper/jni => xcrash_lib/src/main/cpp/xcrash_dumper}/xcd_core.c (100%) rename {src/native/libxcrash_dumper/jni => xcrash_lib/src/main/cpp/xcrash_dumper}/xcd_dwarf.c (99%) rename {src/native/libxcrash_dumper/jni => xcrash_lib/src/main/cpp/xcrash_dumper}/xcd_dwarf.h (96%) rename {src/native/libxcrash_dumper/jni => xcrash_lib/src/main/cpp/xcrash_dumper}/xcd_elf.c (94%) rename {src/native/libxcrash_dumper/jni => xcrash_lib/src/main/cpp/xcrash_dumper}/xcd_elf.h (100%) rename {src/native/libxcrash_dumper/jni => xcrash_lib/src/main/cpp/xcrash_dumper}/xcd_elf_interface.c (94%) rename {src/native/libxcrash_dumper/jni => xcrash_lib/src/main/cpp/xcrash_dumper}/xcd_elf_interface.h (100%) rename {src/native/libxcrash_dumper/jni => xcrash_lib/src/main/cpp/xcrash_dumper}/xcd_frames.c (100%) rename {src/native/libxcrash_dumper/jni => xcrash_lib/src/main/cpp/xcrash_dumper}/xcd_frames.h (100%) rename {src/native/libxcrash_dumper/jni => xcrash_lib/src/main/cpp/xcrash_dumper}/xcd_log.h (100%) rename {src/native/libxcrash_dumper/jni => xcrash_lib/src/main/cpp/xcrash_dumper}/xcd_map.c (90%) rename {src/native/libxcrash_dumper/jni => xcrash_lib/src/main/cpp/xcrash_dumper}/xcd_map.h (91%) rename {src/native/libxcrash_dumper/jni => xcrash_lib/src/main/cpp/xcrash_dumper}/xcd_maps.c (95%) rename {src/native/libxcrash_dumper/jni => xcrash_lib/src/main/cpp/xcrash_dumper}/xcd_maps.h (100%) rename {src/native/libxcrash_dumper/jni => xcrash_lib/src/main/cpp/xcrash_dumper}/xcd_md5.c (100%) rename {src/native/libxcrash_dumper/jni => xcrash_lib/src/main/cpp/xcrash_dumper}/xcd_md5.h (100%) rename {src/native/libxcrash_dumper/jni => xcrash_lib/src/main/cpp/xcrash_dumper}/xcd_memory.c (100%) rename {src/native/libxcrash_dumper/jni => xcrash_lib/src/main/cpp/xcrash_dumper}/xcd_memory.h (100%) rename {src/native/libxcrash_dumper/jni => xcrash_lib/src/main/cpp/xcrash_dumper}/xcd_memory_buf.c (100%) rename {src/native/libxcrash_dumper/jni => xcrash_lib/src/main/cpp/xcrash_dumper}/xcd_memory_buf.h (100%) rename {src/native/libxcrash_dumper/jni => xcrash_lib/src/main/cpp/xcrash_dumper}/xcd_memory_file.c (100%) rename {src/native/libxcrash_dumper/jni => xcrash_lib/src/main/cpp/xcrash_dumper}/xcd_memory_file.h (100%) rename {src/native/libxcrash_dumper/jni => xcrash_lib/src/main/cpp/xcrash_dumper}/xcd_memory_remote.c (100%) rename {src/native/libxcrash_dumper/jni => xcrash_lib/src/main/cpp/xcrash_dumper}/xcd_memory_remote.h (100%) rename {src/native/libxcrash_dumper/jni => xcrash_lib/src/main/cpp/xcrash_dumper}/xcd_process.c (100%) rename {src/native/libxcrash_dumper/jni => xcrash_lib/src/main/cpp/xcrash_dumper}/xcd_process.h (100%) rename {src/native/libxcrash_dumper/jni => xcrash_lib/src/main/cpp/xcrash_dumper}/xcd_regs.h (100%) rename {src/native/libxcrash_dumper/jni => xcrash_lib/src/main/cpp/xcrash_dumper}/xcd_regs_arm.c (100%) rename {src/native/libxcrash_dumper/jni => xcrash_lib/src/main/cpp/xcrash_dumper}/xcd_regs_arm64.c (100%) rename {src/native/libxcrash_dumper/jni => xcrash_lib/src/main/cpp/xcrash_dumper}/xcd_regs_x86.c (100%) rename {src/native/libxcrash_dumper/jni => xcrash_lib/src/main/cpp/xcrash_dumper}/xcd_regs_x86_64.c (100%) rename {src/native/libxcrash_dumper/jni => xcrash_lib/src/main/cpp/xcrash_dumper}/xcd_sys.c (100%) rename {src/native/libxcrash_dumper/jni => xcrash_lib/src/main/cpp/xcrash_dumper}/xcd_sys.h (100%) rename {src/native/libxcrash_dumper/jni => xcrash_lib/src/main/cpp/xcrash_dumper}/xcd_thread.c (100%) rename {src/native/libxcrash_dumper/jni => xcrash_lib/src/main/cpp/xcrash_dumper}/xcd_thread.h (100%) rename {src/native/libxcrash_dumper/jni => xcrash_lib/src/main/cpp/xcrash_dumper}/xcd_util.c (60%) rename {src/native/libxcrash_dumper/jni => xcrash_lib/src/main/cpp/xcrash_dumper}/xcd_util.h (81%) rename {src/java/xcrash/xcrash_lib => xcrash_lib}/src/main/java/xcrash/ActivityMonitor.java (100%) rename {src/java/xcrash/xcrash_lib => xcrash_lib}/src/main/java/xcrash/AnrHandler.java (100%) rename {src/java/xcrash/xcrash_lib => xcrash_lib}/src/main/java/xcrash/DefaultLogger.java (100%) rename {src/java/xcrash/xcrash_lib => xcrash_lib}/src/main/java/xcrash/Errno.java (100%) rename {src/java/xcrash/xcrash_lib => xcrash_lib}/src/main/java/xcrash/FileManager.java (100%) rename {src/java/xcrash/xcrash_lib => xcrash_lib}/src/main/java/xcrash/ICrashCallback.java (100%) rename {src/java/xcrash/xcrash_lib => xcrash_lib}/src/main/java/xcrash/ILibLoader.java (100%) rename {src/java/xcrash/xcrash_lib => xcrash_lib}/src/main/java/xcrash/ILogger.java (100%) rename {src/java/xcrash/xcrash_lib => xcrash_lib}/src/main/java/xcrash/JavaCrashHandler.java (100%) rename {src/java/xcrash/xcrash_lib => xcrash_lib}/src/main/java/xcrash/NativeHandler.java (100%) rename {src/java/xcrash/xcrash_lib => xcrash_lib}/src/main/java/xcrash/TombstoneManager.java (100%) rename {src/java/xcrash/xcrash_lib => xcrash_lib}/src/main/java/xcrash/TombstoneParser.java (100%) rename {src/java/xcrash/xcrash_lib => xcrash_lib}/src/main/java/xcrash/Util.java (100%) rename {src/java/xcrash/xcrash_lib => xcrash_lib}/src/main/java/xcrash/Version.java (96%) rename {src/java/xcrash/xcrash_lib => xcrash_lib}/src/main/java/xcrash/XCrash.java (100%) rename {src/java/xcrash/xcrash_sample => xcrash_sample}/.gitignore (100%) rename {src/java/xcrash/xcrash_sample => xcrash_sample}/build.gradle (73%) rename {src/java/xcrash/xcrash_sample => xcrash_sample}/proguard-rules.pro (100%) rename {src/java/xcrash/xcrash_sample => xcrash_sample}/src/main/AndroidManifest.xml (100%) rename {src/java/xcrash/xcrash_sample => xcrash_sample}/src/main/java/xcrash/sample/MainActivity.java (100%) rename {src/java/xcrash/xcrash_sample => xcrash_sample}/src/main/java/xcrash/sample/MyCustomApplication.java (100%) rename {src/java/xcrash/xcrash_sample => xcrash_sample}/src/main/java/xcrash/sample/MyService.java (100%) rename {src/java/xcrash/xcrash_sample => xcrash_sample}/src/main/java/xcrash/sample/SecondActivity.java (100%) rename {src/java/xcrash/xcrash_sample => xcrash_sample}/src/main/res/drawable-v24/ic_launcher_foreground.xml (100%) rename {src/java/xcrash/xcrash_sample => xcrash_sample}/src/main/res/drawable/ic_launcher_background.xml (100%) rename {src/java/xcrash/xcrash_sample => xcrash_sample}/src/main/res/layout/activity_main.xml (100%) rename {src/java/xcrash/xcrash_sample => xcrash_sample}/src/main/res/layout/activity_second.xml (100%) rename {src/java/xcrash/xcrash_sample => xcrash_sample}/src/main/res/mipmap-anydpi-v26/ic_launcher.xml (100%) rename {src/java/xcrash/xcrash_sample => xcrash_sample}/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml (100%) rename {src/java/xcrash/xcrash_sample => xcrash_sample}/src/main/res/mipmap-hdpi/ic_launcher.png (100%) rename {src/java/xcrash/xcrash_sample => xcrash_sample}/src/main/res/mipmap-hdpi/ic_launcher_round.png (100%) rename {src/java/xcrash/xcrash_sample => xcrash_sample}/src/main/res/mipmap-mdpi/ic_launcher.png (100%) rename {src/java/xcrash/xcrash_sample => xcrash_sample}/src/main/res/mipmap-mdpi/ic_launcher_round.png (100%) rename {src/java/xcrash/xcrash_sample => xcrash_sample}/src/main/res/mipmap-xhdpi/ic_launcher.png (100%) rename {src/java/xcrash/xcrash_sample => xcrash_sample}/src/main/res/mipmap-xhdpi/ic_launcher_round.png (100%) rename {src/java/xcrash/xcrash_sample => xcrash_sample}/src/main/res/mipmap-xxhdpi/ic_launcher.png (100%) rename {src/java/xcrash/xcrash_sample => xcrash_sample}/src/main/res/mipmap-xxhdpi/ic_launcher_round.png (100%) rename {src/java/xcrash/xcrash_sample => xcrash_sample}/src/main/res/mipmap-xxxhdpi/ic_launcher.png (100%) rename {src/java/xcrash/xcrash_sample => xcrash_sample}/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png (100%) rename {src/java/xcrash/xcrash_sample => xcrash_sample}/src/main/res/values/colors.xml (100%) rename {src/java/xcrash/xcrash_sample => xcrash_sample}/src/main/res/values/strings.xml (100%) rename {src/java/xcrash/xcrash_sample => xcrash_sample}/src/main/res/values/styles.xml (100%) diff --git a/.gitignore b/.gitignore index ec815e8..82b1a68 100644 --- a/.gitignore +++ b/.gitignore @@ -1,12 +1,11 @@ .DS_Store -libs/ -obj/ - build/ +.cxx/ .gradle/ .idea/ *.iml *.log *.so +wrap.sh local.properties diff --git a/README.md b/README.md index 2233d59..79da5be 100644 --- a/README.md +++ b/README.md @@ -4,9 +4,9 @@ ![](https://img.shields.io/badge/license-MIT-brightgreen.svg?style=flat) ![](https://img.shields.io/badge/PRs-welcome-brightgreen.svg?style=flat) -![](https://img.shields.io/badge/release-2.4.9-red.svg?style=flat) -![](https://img.shields.io/badge/Android-4.0%20--%2010-blue.svg?style=flat) -![](https://img.shields.io/badge/arch-armeabi%20%7C%20armeabi--v7a%20%7C%20arm64--v8a%20%7C%20x86%20%7C%20x86__64-blue.svg?style=flat) +![](https://img.shields.io/badge/release-3.0.0-red.svg?style=flat) +![](https://img.shields.io/badge/Android-4.1%20--%2011-blue.svg?style=flat) +![](https://img.shields.io/badge/armeabi--v7a%20%7C%20arm64--v8a%20%7C%20x86%20%7C%20x86__64-blue.svg?style=flat) xCrash provides the Android app with the ability to capture java crash, native crash and ANR. No root permission or any system permissions are required. @@ -21,8 +21,8 @@ xCrash has been used in many Android apps (including iQIYI video) on different p ## Features -* Support Android 4.0 - 10 (API level 14 - 29). -* Support armeabi, armeabi-v7a, arm64-v8a, x86 and x86_64. +* Support Android 4.1 - 11 (API level 16 - 30). +* Support armeabi-v7a, arm64-v8a, x86 and x86_64. * Capturing java crash, native crash and ANR. * Dumping detailed statistics about process, threads, memory, FD and network. * Setting which thread's info should be dumped via regular expressions. @@ -32,12 +32,10 @@ xCrash has been used in many Android apps (including iQIYI video) on different p ## Tombstone File Previews * [java crash](doc/tombstone_java.txt) -* [native crash (armeabi)](doc/tombstone_native_armeabi.txt) * [native crash (armeabi-v7a)](doc/tombstone_native_armeabi-v7a.txt) * [native crash (arm64-v8a)](doc/tombstone_native_arm64-v8a.txt) * [native crash (x86)](doc/tombstone_native_x86.txt) * [native crash (x86_64)](doc/tombstone_native_x86_64.txt) -* [ANR (armeabi)](doc/tombstone_anr_armeabi.txt) * [ANR (armeabi-v7a)](doc/tombstone_anr_armeabi-v7a.txt) * [ANR (arm64-v8a)](doc/tombstone_anr_arm64-v8a.txt) * [ANR (x86)](doc/tombstone_anr_x86.txt) @@ -64,7 +62,7 @@ xCrash has been used in many Android apps (including iQIYI video) on different p ```Gradle dependencies { - implementation 'com.iqiyi.xcrash:xcrash-android-lib:2.4.9' + implementation 'com.iqiyi.xcrash:xcrash-android-lib:3.0.0' } ``` @@ -74,7 +72,7 @@ dependencies { android { defaultConfig { ndk { - abiFilters 'armeabi', 'armeabi-v7a', 'arm64-v8a', 'x86', 'x86_64' + abiFilters 'armeabi-v7a', 'arm64-v8a', 'x86', 'x86_64' } } } @@ -111,36 +109,26 @@ class MyCustomApplication : Application() { Tombstone files will be written to `Context#getFilesDir() + "/tombstones"` directory by default. (usually in: `/data/data/PACKAGE_NAME/files/tombstones`) -There is a more practical and complex sample app in the [src/java/xcrash/xcrash_sample](src/java/xcrash/xcrash_sample) folder. +There is a more practical and complex sample app in the [xcrash_sample](xcrash_sample) folder. ## Build If you want to build xCrash from source code. Follow this guide: -#### 1. Download [Android NDK r20b](https://developer.android.com/ndk/downloads/revision_history.html), set PATH environment. -#### 2. Build and copy the native libraries. +#### Build AAR library. ``` -cd ./src/native/ -./build.sh -./install.sh -``` - -#### 3. Build AAR library. - -``` -cd ./src/java/xcrash/ ./gradlew :xcrash_lib:build ``` ## Support -1. Check the [xcrash-sample](src/java/xcrash/xcrash_sample). +1. Check the [xcrash-sample](xcrash_sample). 2. Communicate on [GitHub issues](https://github.com/iqiyi/xCrash/issues). -3. Email: caikelun@gmail.com +3. Email: caikelun@gmail.com   xuqnqn@qq.com 4. QQ group: 603635869. QR code: