From 16dff882f9967f58e9415808456492fcf8cb20bf Mon Sep 17 00:00:00 2001 From: xuqingqing Date: Mon, 24 Jan 2022 18:52:08 +0800 Subject: [PATCH] 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 @@ - +