12章内容提交

This commit is contained in:
dqzg12300 2023-04-20 08:08:45 +08:00
parent ec693e322b
commit feb3163e2a

View File

@ -1315,39 +1315,8 @@ I/xunwind_tag: #06 pc 0000000000222248 /apex/com.android.art/lib64/libart.so (a
I/xunwind_tag: #07 pc 0000000000218bec /apex/com.android.art/lib64/libart.so (art_quick_invoke_static_stub+572)
I/xunwind_tag: #08 pc 0000000000290300 /apex/com.android.art/lib64/libart.so (_ZN3art9ArtMethod6InvokeEPNS_6ThreadEPjjPNS_6JValueEPKc+536)
I/xunwind_tag: #09 pc 00000000003f09e4 /apex/com.android.art/lib64/libart.so (_ZN3art11interpreter34ArtInterpreterToCompiledCodeBridgeEPNS_6ThreadEPNS_9ArtMethodEPNS_11ShadowFrameEtPNS_6JValueE+404)
I/xunwind_tag: #10 pc 00000000003eb858 /apex/com.android.art/lib64/libart.so (_ZN3art11interpreter6DoCallILb0ELb0EEEbPNS_9ArtMethodEPNS_6ThreadERNS_11ShadowFrameEPKNS_11InstructionEtPNS_6JValueE+808)
I/xunwind_tag: #11 pc 00000000007658d0 /apex/com.android.art/lib64/libart.so (MterpInvokeStatic+984)
I/xunwind_tag: #12 pc 0000000000203998 /apex/com.android.art/lib64/libart.so (mterp_op_invoke_static+24)
I/xunwind_tag: #13 pc 00000000003e3688 /apex/com.android.art/lib64/libart.so (_ZN3art11interpreterL7ExecuteEPNS_6ThreadERKNS_20CodeItemDataAccessorERNS_11ShadowFrameENS_6JValueEbb+348)
I/xunwind_tag: #14 pc 000000000074920c /apex/com.android.art/lib64/libart.so (artQuickToInterpreterBridge+780)
I/xunwind_tag: #15 pc 000000000022237c /apex/com.android.art/lib64/libart.so (art_quick_to_interpreter_bridge+92)
I/xunwind_tag: #16 pc 000000000021160c /apex/com.android.art/lib64/libart.so (nterp_helper+156)
I/xunwind_tag: #17 pc 0000000000218968 /apex/com.android.art/lib64/libart.so (art_quick_invoke_stub+552)
I/xunwind_tag: #18 pc 00000000002902d4 /apex/com.android.art/lib64/libart.so (_ZN3art9ArtMethod6InvokeEPNS_6ThreadEPjjPNS_6JValueEPKc+492)
I/xunwind_tag: #19 pc 000000000062ba5c /apex/com.android.art/lib64/libart.so (_ZN3art12InvokeMethodILNS_11PointerSizeE8EEEP8_jobjectRKNS_33ScopedObjectAccessAlreadyRunnableES3_S3_S3_m+1388)
I/xunwind_tag: #20 pc 000000000059d2f8 /apex/com.android.art/lib64/libart.so (_ZN3artL13Method_invokeEP7_JNIEnvP8_jobjectS3_P13_jobjectArray+56)
I/xunwind_tag: #21 pc 0000000000222248 /apex/com.android.art/lib64/libart.so (art_quick_generic_jni_trampoline+152)
I/xunwind_tag: #22 pc 0000000000212524 /apex/com.android.art/lib64/libart.so (nterp_helper+4020)
I/xunwind_tag: #23 pc 00000000002132e8 /apex/com.android.art/lib64/libart.so (nterp_helper+7544)
I/xunwind_tag: #24 pc 00000000002124c8 /apex/com.android.art/lib64/libart.so (nterp_helper+3928)
I/xunwind_tag: #25 pc 00000000002124c8 /apex/com.android.art/lib64/libart.so (nterp_helper+3928)
I/xunwind_tag: #26 pc 00000000002115a8 /apex/com.android.art/lib64/libart.so (nterp_helper+56)
I/xunwind_tag: #27 pc 00000000002132e8 /apex/com.android.art/lib64/libart.so (nterp_helper+7544)
I/xunwind_tag: #28 pc 00000000002115a8 /apex/com.android.art/lib64/libart.so (nterp_helper+56)
I/xunwind_tag: #29 pc 00000000002124c8 /apex/com.android.art/lib64/libart.so (nterp_helper+3928)
I/xunwind_tag: #30 pc 00000000002115a8 /apex/com.android.art/lib64/libart.so (nterp_helper+56)
I/xunwind_tag: #31 pc 00000000002115a8 /apex/com.android.art/lib64/libart.so (nterp_helper+56)
I/xunwind_tag: #32 pc 0000000000218bec /apex/com.android.art/lib64/libart.so (art_quick_invoke_static_stub+572)
I/xunwind_tag: #33 pc 0000000000290300 /apex/com.android.art/lib64/libart.so (_ZN3art9ArtMethod6InvokeEPNS_6ThreadEPjjPNS_6JValueEPKc+536)
I/xunwind_tag: #34 pc 000000000062ba5c /apex/com.android.art/lib64/libart.so (_ZN3art12InvokeMethodILNS_11PointerSizeE8EEEP8_jobjectRKNS_33ScopedObjectAccessAlreadyRunnableES3_S3_S3_m+1388)
I/xunwind_tag: #35 pc 000000000059d2f8 /apex/com.android.art/lib64/libart.so (_ZN3artL13Method_invokeEP7_JNIEnvP8_jobjectS3_P13_jobjectArray+56)
I/xunwind_tag: #36 pc 0000000000222248 /apex/com.android.art/lib64/libart.so (art_quick_generic_jni_trampoline+152)
I/xunwind_tag: #37 pc 0000000000212524 /apex/com.android.art/lib64/libart.so (nterp_helper+4020)
I/xunwind_tag: #38 pc 00000000002132e8 /apex/com.android.art/lib64/libart.so (nterp_helper+7544)
I/xunwind_tag: #39 pc 0000000000218bec /apex/com.android.art/lib64/libart.so (art_quick_invoke_static_stub+572)
I/xunwind_tag: #40 pc 0000000000290300 /apex/com.android.art/lib64/libart.so (_ZN3art9ArtMethod6InvokeEPNS_6ThreadEPjjPNS_6JValueEPKc+536)
I/xunwind_tag: #41 pc 000000000062c1b4 /apex/com.android.art/lib64/libart.so (_ZN3art17InvokeWithVarArgsIPNS_9ArtMethodEEENS_6JValueERKNS_33ScopedObjectAccessAlreadyRunnableEP8_jobjectT_St9__va_list+452)
I/xunwind_tag: #42 pc 000000000062c678 /apex/com.android.art/lib64/libart.so (_ZN3art17InvokeWithVarArgsIP10_jmethodIDEENS_6JValueERKNS_33ScopedObjectAccessAlreadyRunnableEP8_jobjectT_St9__va_list+96)
...
...
I/xunwind_tag: #43 pc 0000000000507c68 /apex/com.android.art/lib64/libart.so (_ZN3art3JNIILb1EE21CallStaticVoidMethodVEP7_JNIEnvP7_jclassP10_jmethodIDSt9__va_list+620)
I/xunwind_tag: #44 pc 00000000000aeac8 /system/lib64/libandroid_runtime.so (_ZN7_JNIEnv20CallStaticVoidMethodEP7_jclassP10_jmethodIDz+124)
I/xunwind_tag: #45 pc 00000000000ba060 /system/lib64/libandroid_runtime.so (_ZN7android14AndroidRuntime5startEPKcRKNS_6VectorINS_7String8EEEb+840)
@ -1364,8 +1333,321 @@ I/xunwind_tag: >>> finished <<<
* 优化获取调用栈信息的代码,让其结果仅输出想要关注的目标动态库的调用栈。
* 直接输出日志调整为返回调用栈字符串。
新建`native`的项目,将`xUnwind`的样例`apk`解压后,取出其`lib`目录中的依赖动态库,将其复制到新项目的`libs`目录中,如下图
首先将`xUnwind`中的样例`apk`解压,找到`lib`目录查看其依赖的动态库,分别是`libxdl.so、libxunwind.so`。在源码中能看到`libxunwind.so`中使用`libxdl.so`来查询指定地址对应的动态库信息。所以在加载动态库时,需要优先加载`libxdl.so`
新建`native`的项目,将`libxdl.so、libxunwind.so`复制到新项目的`libs`目录中,然后修改`build.gradle`文件如下。
```
android {
...
defaultConfig {
...
ndk {
abiFilters 'arm64-v8a'
}
...
}
...
sourceSets {
main {
jniLibs.srcDirs = ['libs']
}
}
...
}
```
然后添加测试代码如下。
```java
public class MainActivity extends AppCompatActivity {
// 注入三个动态库
static {
System.loadLibrary("xdl");
System.loadLibrary("xunwind");
System.loadLibrary("nativecppdemo");
}
private ActivityMainBinding binding;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
binding = ActivityMainBinding.inflate(getLayoutInflater());
setContentView(binding.getRoot());
TextView tv = binding.sampleText;
tv.setText(stringFromJNI());
}
public native String stringFromJNI();
}
```
然后在`stringFromJNI`函数调时,获取调用栈,参考样例中的代码,并删除非`EH`方案的代码。创建一个存放头文件`kbacktrace.h`,内容如下。
```c++
#ifndef __KBACKTRACE_H__
#define __KBACKTRACE_H__
#include <sys/wait.h>
void sigabrt_handler(int signum, siginfo_t *siginfo, void *context);
void signal_register(void);
const char* kbacktrace(bool with_context,const char* moduleName);
#endif
```
接着创建一个对应的源码文件`kbacktrace.cpp`,优化后的内容如下。
```c++
#include "kbacktrace.h"
#include <string>
#include <unistd.h>
#include "xunwind.h"
#include <setjmp.h>
static bool g_with_context = false;
static uintptr_t g_frames[128];
static size_t g_frames_sz = 0;
void sigabrt_handler(int signum, siginfo_t *siginfo, void *context) {
(void)signum, (void)siginfo;
// EH local unwind
size_t frames_sz = xunwind_eh_unwind(g_frames, sizeof(g_frames) / sizeof(g_frames[0]),
g_with_context ? context : NULL);
__atomic_store_n(&g_frames_sz, frames_sz, __ATOMIC_SEQ_CST);
}
void signal_register(void) {
struct sigaction act;
memset(&act, 0, sizeof(act));
sigfillset(&act.sa_mask);
sigdelset(&act.sa_mask, SIGSEGV);
act.sa_sigaction = sigabrt_handler;
act.sa_flags = SA_RESTART | SA_SIGINFO | SA_ONSTACK;
sigaction(SIGABRT, &act, NULL);
}
const char* kbacktrace(bool with_context,const char* moduleName) {
g_with_context = with_context;
__atomic_store_n(&g_frames_sz, 0, __ATOMIC_SEQ_CST);
tgkill(getpid(), gettid(), SIGABRT);
if ( __atomic_load_n(&g_frames_sz, __ATOMIC_SEQ_CST) > 0){
const char* res= xunwind_frames_get(g_frames, g_frames_sz,nullptr,moduleName);
return res;
}
return "";
}
```
在原来的样例中有两种信号方式获取调用栈,内置在`AOSP`中固定使用一种方式即可,所以仅保留`SIGABRT`信号获取堆栈即可。接着就可以使用该函数获取调用栈了。示例如下。
```c++
#include <jni.h>
#include <android/log.h>
#include "kbacktrace.h"
#define TAG "mikrom"
#define LOG_PRIORITY ANDROID_LOG_INFO
#define ALOGI(fmt, ...) __android_log_print(LOG_PRIORITY, TAG, fmt, ##__VA_ARGS__)
extern "C" JNIEXPORT jstring JNICALL
Java_com_mik_nativecppdemo_MainActivity_stringFromJNI(
JNIEnv* env,
jobject obj /* this */) {
jstring resObj=env->NewStringUTF("test");
const char* res= kbacktrace(true,"libnativecppdemo.so");
ALOGI("-------------------------Backtrace-------------------------\n%s",res);
return resObj;
}
// 在动态库加载时调用,完成本地方法的注册
JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *, void *) {
signal_register();
return JNI_VERSION_1_6;
}
```
修改完样例后,动态库中的`xunwind_frames_get`函数也需要调整,为其新增参数,将动态库名称传递进去,让其仅返回包含该动态库名称的调用栈。修改如下。
```c++
char *xunwind_frames_get(uintptr_t *frames, size_t frames_sz, const char *prefix,const char* moduleName) {
xu_printer_t printer;
xu_printer_init_get(&printer);
xu_formatter_print(frames, frames_sz, prefix, &printer,moduleName);
return xu_printer_get(&printer);
}
```
继续修改`xu_formatter_print`函数,添加对动态库的过滤。
```c++
void xu_formatter_print(uintptr_t *frames, size_t frames_sz, const char *prefix, xu_printer_t *printer,const char* moduleName) {
if (NULL == frames || 0 == frames_sz) return;
if (NULL == prefix) prefix = "";
void *cache = NULL;
xdl_info_t info;
for (size_t i = 0; i < frames_sz; i++) {
memset(&info, 0, sizeof(xdl_info_t));
int r = 0;
if (0 != frames[i]) {
// find info from linker
r = xdl_addr((void *)(frames[i]), &info, &cache);
// 非目标动态库,则直接跳过
if(!strstr(info.dli_fname,moduleName)){
continue;
}
// find info from maps
char buf[512];
if (0 == r || (uintptr_t)info.dli_fbase > frames[i])
r = xu_formatter_maps_addr(frames[i], &info, buf, sizeof(buf));
}
...
}
xdl_addr_clean(&cache);
}
```
完成修改后,编译时发现错误,是因为没有指定依赖的动态库,修改`CMakeLists`文件如下。
```cmake
if(CMAKE_SYSTEM_PROCESSOR MATCHES "aarch64*")
# 当前 CPU 架构为 arm64
target_link_libraries( # Specifies the target library.
kbacktrace
${PROJECT_SOURCE_DIR}/../../../../app/libs/arm64-v8a/libxunwind.so
# Links the target library to the log library
# included in the NDK.
${log-lib})
endif()
if(CMAKE_SYSTEM_PROCESSOR MATCHES "arm*")
if(CMAKE_SYSTEM_PROCESSOR MATCHES "v7*")
# 当前 CPU 架构为 armeabi-v7a
target_link_libraries( # Specifies the target library.
kbacktrace
${PROJECT_SOURCE_DIR}/../../../../app/libs/armeabi-v7a/libxunwind.so
# Links the target library to the log library
# included in the NDK.
${log-lib})
endif()
endif()
```
最后运行这个新的测试样例,输出日志如下。
```
com.mik.nativecppdemo I -------------------------Backtrace-------------------------
#01 pc 000000000001e02c /data/app/~~-XZEZm9rvOBJhceZK8LuKg==/com.mik.nativecppdemo-SoSNN-J1eghWN4JW7rNYqw==/base.apk!/lib/arm64-v8a/libnativecppdemo.so
#02 pc 000000000001dd74 /data/app/~~-XZEZm9rvOBJhceZK8LuKg==/com.mik.nativecppdemo-SoSNN-J1eghWN4JW7rNYqw==/base.apk!/lib/arm64-v8a/libnativecppdemo.so (Java_com_mik_nativecppdemo_MainActivity_stringFromJNI+180)
```
### 12.6.3 内置获取调用栈
在测试样例中将主要功能逻辑完成后,最后需要将该功能内置到`AOSP`中,提供`JNI`中打桩函数调用。首先是将`libxdl.so、libxunwind.so、libkbacktrace.so`动态库内置到系统目录中。
`AOSP`源码目录`frameworks/base/packages/apps/`下新建目录,用于存放内置的动态库,然后将样例程序`armeabi-v7a``arm64-v8`中的动态库放在新目录下。在新目录下添加规则文件`Android.mk`。内容如下。
```
#--------------------------------
include $(CLEAR_VARS)
LOCAL_MODULE := libxdl
LOCAL_SRC_FILES_arm := libxdl.so
LOCAL_SRC_FILES_arm64 := libxdl_arm64.so
LOCAL_MODULE_TARGET_ARCHS:= arm arm64
LOCAL_MULTILIB := both
LOCAL_MODULE_SUFFIX := .so
LOCAL_MODULE_CLASS := SHARED_LIBRARIES
LOCAL_MODULE_TAGS := optional
LOCAL_SHARED_LIBRARIES := liblog
include $(BUILD_PREBUILT)
#--------------------------------
include $(CLEAR_VARS)
LOCAL_MODULE := libxunwind
LOCAL_SRC_FILES_arm := libxunwind.so
LOCAL_SRC_FILES_arm64 := libxunwind_arm64.so
LOCAL_MODULE_TARGET_ARCHS:= arm arm64
LOCAL_MULTILIB := both
LOCAL_MODULE_SUFFIX := .so
LOCAL_MODULE_CLASS := SHARED_LIBRARIES
LOCAL_MODULE_TAGS := optional
LOCAL_SHARED_LIBRARIES := liblog libxdl
include $(BUILD_PREBUILT)
#--------------------------------
include $(CLEAR_VARS)
LOCAL_MODULE := libkbacktrace
LOCAL_SRC_FILES_arm := libkbacktrace.so
LOCAL_SRC_FILES_arm64 := libkbacktrace_arm64.so
LOCAL_MODULE_TARGET_ARCHS:= arm arm64
LOCAL_MULTILIB := both
LOCAL_MODULE_SUFFIX := .so
LOCAL_MODULE_CLASS := SHARED_LIBRARIES
LOCAL_MODULE_TAGS := optional
LOCAL_SHARED_LIBRARIES := liblog libxunwind
include $(BUILD_PREBUILT)
```
然后在文件`build/make/target/product/generic_system.mk`中添加这模块。
```
PRODUCT_PACKAGES += \
libxdl \
libxunwind \
libkbacktrace \
```
编译刷入手机后,即可在系统动态库目录中找到内置进去的动态库。接下来在应用的启动阶段,判断其是否为目标应用,是目标应用则按顺序加载动态库,所以下面回到在前面准备好的读取配置的测试代码。
```java
public static void loadBlackTraceSo(){
System.loadLibrary("xdl");
System.loadLibrary("xunwind");
System.loadLibrary("kbacktrace");
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
...
// 判断当前app是否为目标应用
PackageItem currentItem=null;
for(PackageItem item : packageItems){
if(item.packageName.contains(this.getPackageName())){
currentItem=item;
break;
}
}
if(currentItem==null){
return;
}
if(currentItem.isJNIMethodPrint){
loadBlackTraceSo();
}
...
}
```
最后在`JNI`打桩函数的结尾获取调用栈信息,