Skip to content

Commit

Permalink
[AAB] Feature: Support AAB And extractNativeLibs=false
Browse files Browse the repository at this point in the history
项目未配置 `useLegacyPackaging=true` 时,
使用AAB安装, 或最低版本为Android M以上的APK
Native库不会从APK中解压, 参考:
https://developer.android.com/guide/topics/manifest/application-element#extractNativeLibs
google/bundletool#39

此时xCrash通过 `ctx.getApplicationInfo().nativeLibraryDir` 获取的目录内容是空的
因此会出现加载xcrash_dumper失败

而通过 PathClassloader.findLibrary(libName) 获取的路径是虚拟路径, 不是真实存在的
因此直接执行也会失败

此时我们参考 Crashpad 的逻辑, 使用linker加载
参考:
https://github.com/chromium/crashpad/blob/main/client/crashpad_client_linux.cc#L114

为了避免大的改动, 默认仅在加载虚拟路径时使用linker, 实际上在所有场景都可以直接通过linker加载

支持外部自定义配置
```
.setNativeLibPath()
.setLoadNativeWithLinker()
```

如果外部没有配置,内部会自动查找
参考: AbiPathProvider.java 逻辑
  • Loading branch information
shiqos committed Sep 5, 2024
1 parent 457066c commit fb45974
Show file tree
Hide file tree
Showing 12 changed files with 209 additions and 7 deletions.
2 changes: 1 addition & 1 deletion build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ ext {

POM_GROUP_ID = "com.iqiyi.xcrash"
POM_ARTIFACT_ID = "xcrash-android-lib"
POM_VERSION_NAME = "3.1.0"
POM_VERSION_NAME = "3.1.1"

POM_NAME = "xCrash Android Lib"
POM_DESCRIPTION = "xCrash provides the Android app with the ability to capture java crash, native crash and ANR."
Expand Down
8 changes: 8 additions & 0 deletions xcrash_lib/src/main/cpp/common/xcc_util.h
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,14 @@ extern "C" {
#define XCC_UTIL_SYSCALL_GETDENTS SYS_getdents64
#endif

#if defined(__LP64__)
#define LINKER_PATH "/system/bin/linker64"
#define LINKER_NAME "linker64"
#else
#define LINKER_PATH "/system/bin/linker"
#define LINKER_NAME "linker"
#endif

#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wpadded"
typedef struct
Expand Down
2 changes: 1 addition & 1 deletion xcrash_lib/src/main/cpp/common/xcc_version.h
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,6 @@
#ifndef XCC_VERSION_H
#define XCC_VERSION_H 1

#define XCC_VERSION_STR "xCrash 3.1.0"
#define XCC_VERSION_STR "xCrash 3.1.1"

#endif
4 changes: 4 additions & 0 deletions xcrash_lib/src/main/cpp/xcrash/xc_common.c
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ char *xc_common_app_id = NULL;
char *xc_common_app_version = NULL;
char *xc_common_app_lib_dir = NULL;
char *xc_common_log_dir = NULL;
int xc_use_linker = 0;

//process info
pid_t xc_common_process_id = 0;
Expand Down Expand Up @@ -130,6 +131,7 @@ int xc_common_init(int api_level,
const char *app_id,
const char *app_version,
const char *app_lib_dir,
int use_linker,
const char *log_dir)
{
int r = 0;
Expand Down Expand Up @@ -179,6 +181,8 @@ int xc_common_init(int api_level,
XC_COMMON_DUP_STR(app_version);
XC_COMMON_DUP_STR(app_lib_dir);
XC_COMMON_DUP_STR(log_dir);

xc_use_linker = use_linker;

//save kernel version
xc_util_get_kernel_version(buf, sizeof(buf));
Expand Down
2 changes: 2 additions & 0 deletions xcrash_lib/src/main/cpp/xcrash/xc_common.h
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ extern char *xc_common_app_id;
extern char *xc_common_app_version;
extern char *xc_common_app_lib_dir;
extern char *xc_common_log_dir;
extern int xc_use_linker;

//process info
extern pid_t xc_common_process_id;
Expand All @@ -86,6 +87,7 @@ int xc_common_init(int api_level,
const char *app_id,
const char *app_version,
const char *app_lib_dir,
int use_linker,
const char *log_dir);

int xc_common_open_crash_log(char *pathname, size_t pathname_len, int *from_placeholder);
Expand Down
6 changes: 5 additions & 1 deletion xcrash_lib/src/main/cpp/xcrash/xc_crash.c
Original file line number Diff line number Diff line change
Expand Up @@ -224,7 +224,11 @@ static int xc_crash_exec_dumper(void *arg)

//escape to the dumper process
errno = 0;
execl(xc_crash_dumper_pathname, XCC_UTIL_XCRASH_DUMPER_FILENAME, NULL);
if (xc_use_linker) {
execl(LINKER_PATH, LINKER_NAME, xc_crash_dumper_pathname, NULL);
} else {
execl(xc_crash_dumper_pathname, XCC_UTIL_XCRASH_DUMPER_FILENAME, NULL);
}
return 100 + errno;
}

Expand Down
3 changes: 3 additions & 0 deletions xcrash_lib/src/main/cpp/xcrash/xc_jni.c
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ static jint xc_jni_init(JNIEnv *env,
jstring app_id,
jstring app_version,
jstring app_lib_dir,
jboolean use_linker,
jstring log_dir,
jboolean crash_enable,
jboolean crash_rethrow,
Expand Down Expand Up @@ -133,6 +134,7 @@ static jint xc_jni_init(JNIEnv *env,
c_app_id,
c_app_version,
c_app_lib_dir,
use_linker ? 1 : 0,
c_log_dir)) goto clean;

r_crash = 0;
Expand Down Expand Up @@ -243,6 +245,7 @@ static JNINativeMethod xc_jni_methods[] = {
"Ljava/lang/String;"
"Ljava/lang/String;"
"Ljava/lang/String;"
"Z"
"Ljava/lang/String;"
"Z"
"Z"
Expand Down
134 changes: 134 additions & 0 deletions xcrash_lib/src/main/java/xcrash/AbiPathProvider.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
package xcrash;

import android.content.Context;
import android.text.TextUtils;
import android.util.Log;
import android.util.Pair;

import java.io.File;

import dalvik.system.PathClassLoader;

public class AbiPathProvider {

private static final String TAG = "AbiPathProvider";

public static final String XCRASH_DUMPER_LIB_NAME = "xcrash_dumper";

public static void test(Context context, String libName) {
Log.d(TAG, "getAbiPathFromDefault: " + getAbiPathFromDefault(context, libName));
Log.d(TAG, "getAbiPathFromClassloader: " + getAbiPathFromClassloader(context, libName));
Log.d(TAG, "getAbiPathFromSplit: " + getAbiPathFromSplit(context));
}

public static Pair<String, Boolean> getAbiPath(Context context, String libName) {
String abiPathFromDefault = getAbiPathFromDefault(context, libName);
if (!TextUtils.isEmpty(abiPathFromDefault) && isSoExist(abiPathFromDefault, libName)) {
return new Pair<>(abiPathFromDefault, false);
}

String abiPathFromClassloader = getAbiPathFromClassloader(context, libName);
if (!TextUtils.isEmpty(abiPathFromClassloader)) {
return new Pair<>(abiPathFromClassloader, true);
}

String abiPathFromSplit = getAbiPathFromSplit(context);
if (!TextUtils.isEmpty(abiPathFromSplit)) {
return new Pair<>(abiPathFromSplit, true);
}

return new Pair<>(context.getApplicationInfo().nativeLibraryDir, false);
}

private static String getAbiPathFromDefault(Context context, String libName) {
return context.getApplicationInfo().nativeLibraryDir;
}

private static boolean isSoExist(String nativeLibDir, String libName) {
File file = new File(nativeLibDir);
if (file.exists() && file.isDirectory()) {
File libFile = new File(file, System.mapLibraryName(libName));
return libFile.exists();
}
return false;
}

private static String getAbiPathFromClassloader(Context context, String libName) {
ClassLoader classLoader = context.getClassLoader();

Log.d(TAG, "classLoader: " + classLoader);

if (classLoader instanceof PathClassLoader) {
String libPath = ((PathClassLoader) classLoader).findLibrary(libName);
if (!TextUtils.isEmpty(libPath)) {
File parentFile = new File(libPath).getParentFile();
if (parentFile != null) {
return parentFile.getAbsolutePath();
}
}
}

return "";
}

private static String getAbiPathFromSplit(Context context) {
if (android.os.Build.VERSION.SDK_INT < android.os.Build.VERSION_CODES.LOLLIPOP) {
return "";
}

String[] splitSourceDirs = context.getApplicationInfo().splitSourceDirs;
AbiName currentAbi = AbiName.ARM64_V8A;
String path = null;
for (String splitSourceDir : splitSourceDirs) {
for (AbiName abiName : AbiName.values()) {
if (splitSourceDir.contains(abiName.getBundleAbi())) {
path = splitSourceDir;
break;
}
}
if (path != null) {
break;
}
}

if (!TextUtils.isEmpty(path)) {
return path + "!/lib/" + currentAbi.platformName;
}

return "";
}

enum AbiName {
ARM64_V8A("arm64-v8a", 64),
ARMEABI_V7A("armeabi-v7a", 32),
X86_64("x86_64", 64),
X86("x86", 32);

private final String platformName;
private final int bitSize;
private final String bundleAbi;

// Constructor for enum fields
AbiName(String platformName, int bitSize) {
this.platformName = platformName;
this.bitSize = bitSize;
this.bundleAbi = platformName.replace("-", "_");
}

// Getter for platformName
public String getPlatformName() {
return platformName;
}

// Getter for bitSize
public int getBitSize() {
return bitSize;
}

// Getter for bundleAbi
public String getBundleAbi() {
return bundleAbi;
}
}

}
20 changes: 18 additions & 2 deletions xcrash_lib/src/main/java/xcrash/NativeHandler.java
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
import android.os.Build;
import android.text.TextUtils;
import android.util.Log;
import android.util.Pair;

import java.io.File;
import java.util.Map;
Expand Down Expand Up @@ -82,7 +83,9 @@ int initialize(Context ctx,
boolean anrDumpFds,
boolean anrDumpNetworkInfo,
ICrashCallback anrCallback,
ICrashCallback anrFastCallback) {
ICrashCallback anrFastCallback,
String nativeLibPath,
boolean loadNativeWithLinker) {
//load lib
if (libLoader == null) {
try {
Expand All @@ -109,6 +112,17 @@ int initialize(Context ctx,
this.anrFastCallback = anrFastCallback;
this.anrTimeoutMs = anrRethrow ? 25 * 1000 : 45 * 1000; //setting rethrow to "false" is NOT recommended

String nativeLibraryDir;
boolean useLinker;
if (!TextUtils.isEmpty(nativeLibPath)) {
nativeLibraryDir = nativeLibPath;
useLinker = loadNativeWithLinker;
} else {
Pair<String, Boolean> abiPath = AbiPathProvider.getAbiPath(ctx, AbiPathProvider.XCRASH_DUMPER_LIB_NAME);
nativeLibraryDir = abiPath.first;
useLinker = abiPath.second;
}

//init native lib
try {
int r = nativeInit(
Expand All @@ -121,7 +135,8 @@ int initialize(Context ctx,
Build.FINGERPRINT,
appId,
appVersion,
ctx.getApplicationInfo().nativeLibraryDir,
nativeLibraryDir,
useLinker,
logDir,
crashEnable,
crashRethrow,
Expand Down Expand Up @@ -291,6 +306,7 @@ private static native int nativeInit(
String appId,
String appVersion,
String appLibDir,
boolean useLinker,
String logDir,
boolean crashEnable,
boolean crashRethrow,
Expand Down
2 changes: 1 addition & 1 deletion xcrash_lib/src/main/java/xcrash/Version.java
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,6 @@ class Version {
private Version() {
}

static final String version = "3.1.0";
static final String version = "3.1.1";
static final String fullVersion = "xCrash " + version;
}
25 changes: 24 additions & 1 deletion xcrash_lib/src/main/java/xcrash/XCrash.java
Original file line number Diff line number Diff line change
Expand Up @@ -214,7 +214,9 @@ public static synchronized int init(Context ctx, InitParameters params) {
params.anrDumpFds,
params.anrDumpNetworkInfo,
params.anrCallback,
params.anrFastCallback);
params.anrFastCallback,
params.nativeLibPath,
params.loadNativeWithLinker);
}

//maintain tombstone and placeholder files in a background thread with some delay
Expand Down Expand Up @@ -517,6 +519,9 @@ public InitParameters setJavaCallback(ICrashCallback callback) {
String[] nativeDumpAllThreadsWhiteList = null;
ICrashCallback nativeCallback = null;

String nativeLibPath = null;
boolean loadNativeWithLinker = false;

/**
* Enable the native crash capture feature. (Default: enable)
*
Expand Down Expand Up @@ -707,6 +712,24 @@ public InitParameters setNativeCallback(ICrashCallback callback) {
return this;
}

/**
* Set native library path
* @param nativeLibPath native library path
*/
public InitParameters setNativeLibPath(String nativeLibPath) {
this.nativeLibPath = nativeLibPath;
return this;
}

/**
* Set whether load native library with linker
* @param loadNativeWithLinker load native library with linker
*/
public InitParameters setLoadNativeWithLinker(boolean loadNativeWithLinker) {
this.loadNativeWithLinker = loadNativeWithLinker;
return this;
}

//anr
boolean enableAnrHandler = true;
boolean anrRethrow = true;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,12 +25,14 @@
import android.app.Application;
import android.content.Context;
import android.util.Log;
import android.util.Pair;

import org.json.JSONObject;

import java.io.File;
import java.io.FileWriter;

import xcrash.AbiPathProvider;
import xcrash.TombstoneManager;
import xcrash.TombstoneParser;
import xcrash.XCrash;
Expand Down Expand Up @@ -78,6 +80,10 @@ public void onCrash(String logPath, String emergency) throws Exception {

Log.d(TAG, "xCrash SDK init: start");

Pair<String, Boolean> abiPath = AbiPathProvider.getAbiPath(this, AbiPathProvider.XCRASH_DUMPER_LIB_NAME);
String nativeLibraryDir = abiPath.first;
boolean loadNativeWithLinker = abiPath.second;

// Initialize xCrash.
XCrash.init(this, new XCrash.InitParameters()
.setAppVersion("1.2.3-beta456-patch789")
Expand All @@ -91,6 +97,8 @@ public void onCrash(String logPath, String emergency) throws Exception {
.setNativeDumpAllThreadsWhiteList(new String[]{"^xcrash\\.sample$", "^Signal Catcher$", "^Jit thread pool$", ".*(R|r)ender.*", ".*Chrome.*"})
.setNativeDumpAllThreadsCountMax(10)
.setNativeCallback(callback)
// .setNativeLibPath(nativeLibraryDir)
// .setLoadNativeWithLinker(loadNativeWithLinker)
// .setAnrCheckProcessState(false)
.setAnrRethrow(true)
.setAnrLogCountMax(10)
Expand Down

0 comments on commit fb45974

Please sign in to comment.