Android系统定制/测试(Crash/ANR等Bug/性能分析必备技巧)
Android中打印堆栈的几种方法
public void ThrowException() {
//调试打印堆栈而不退出
Log.d(TAG, Log.getStackTraceString(new Throwable()));
//创建异常打印堆栈
Exception e = new Exception("this is a log");
e.printStackTrace();
//获取当前线程的堆栈
for (StackTraceElement i : Thread.currentThread().getStackTrace()) {
Log.i(TAG, i.toString());
}
RuntimeException re = new RuntimeException();
re.fillInStackTrace();
Log.i(TAG, "stackTrace", re);
//主动抛出异常调试
try {
Log.i(TAG, "---NullPointerException---start);
throw new NullPointerException();
} catch (NullPointerException e1) {
//TODO: handle exception
Log.i(TAG, "---NullPointerException---");
Log.e(TAG, Log.getStackTraceString(e1));
//e1.printStackTrace();
}
Log.i(TAG, "---NullPointerException---end");
}
C++代码中打印堆栈
C++也是支持异常处理的,异常处理库中,已经包含了获取backtrace的接口,Android也是利用这个接口来打印堆栈信息的。在Android的C++中,已经集成了一个工具类CallStack,在libutils.so中
CallStack stack;
stack.update();
stack.dump();
void dumping_callstack(){
android::CallStack stack;
//getpid()和gettid()效果一样
//stack.update(2,getpid());
//stack.update(2,gettid());
stack.update();
//输出到printf
stack.dump(1);
//输出到logcat
stack.log("dump_test");
//可以设置第2、3个参数
//stack.log("Dumping Stack", ANDROID_LOG_ERROR, "test");
}
在Android4.1.1以上,如Android4.4.4,使用Android系统自带的libcorkscrew.so
在Android5.0及以上,Android系统中没有了libcorkscrew.so,使用本身编译的libunwind
http://androidxref.com/4.4.4_r1/xref/system/core/libcorkscrew/
Android5.0至Android12,使用Android系统自带的libbacktrace.so,如下所示
http://androidxref.com/5.0.0_r2/xref/system/core/libbacktrace/
http://aospxref.com/android-7.1.2_r39/xref/system/core/libbacktrace/
http://aospxref.com/android-12.0.0_r3/xref/system/unwinding/libbacktrace/
C代码中打印堆栈
C代码,尤其是底层C库,想要看到调用的堆栈信息,还是比较麻烦的。
CallStack肯定是不能用
一是因为其是C++写的,需要重新封装才能在C中使用
二是底层库反调上层库的函数,会造成链接器循环依赖而无法链接。不过也不是没有办法,可以通过android工具类CallStack实现中使用的unwind调用及符号解析函数来处理。
需要注意的是,为解决链接问题,最好使用dlopen方式,查找需要用到的接口再直接调用,这样会比较简单。如下为相关的实现代码,只需要在要打印的文件中插入此部分代码,然后调用getCallStack()即可,无需包含太多的头文件和修改Android.mk文件
typedef ssize_t (*unwindFn)(backtrace_frame_t*, size_t, size_t);
typedef void (*unwindSymbFn)(const backtrace_frame_t*, size_t, backtrace_symbol_t*);
typedef void (*unwindSymbFreeFn)(backtrace_symbol_t*, size_t);
static void *gHandle = NULL;
static int getCallStack(void){
ssize_t i = 0;
ssize_t result = 0;
ssize_t count;
backtrace_frame_t mStack[MAX_DEPTH];
backtrace_symbol_t symbols[MAX_DEPTH];
unwindFn unwind_backtrace = NULL;
unwindSymbFn get_backtrace_symbols = NULL;
unwindSymbFreeFn free_backtrace_symbols = NULL;
// open the so.
if(gHandle == NULL) gHandle = dlopen(PATH, RTLD_NOW);
// get the interface for unwind and symbol analyse
if(gHandle != NULL) unwind_backtrace = (unwindFn)dlsym(gHandle, "unwind_backtrace");
if(gHandle != NULL) get_backtrace_symbols = (unwindSymbFn)dlsym(gHandle, "get_backtrace_symbols");
if(gHandle != NULL) free_backtrace_symbols = (unwindSymbFreeFn)dlsym(gHandle, "free_backtrace_symbols");
if(!gHandle ||!unwind_backtrace ||!get_backtrace_symbols || !free_backtrace_symbols ){
ALOGE("Error! cannot get unwind info: handle:%p %p %p %p",
gHandle, unwind_backtrace, get_backtrace_symbols, free_backtrace_symbols );
return result;
}
count= unwind_backtrace(mStack, 1, MAX_DEPTH);
get_backtrace_symbols(mStack, count, symbols);
for (i = 0; i < count; i++) {
char line[MAX_BACKTRACE_LINE_LENGTH];
const char* mapName = symbols[i].map_name ? symbols[i].map_name : "<unknown>";
const char* symbolName =symbols[i].demangled_name ? symbols[i].demangled_name : symbols[i].symbol_name;
size_t fieldWidth = (MAX_BACKTRACE_LINE_LENGTH - 80) / 2;
if (symbolName) {
uint32_t pc_offset = symbols[i].relative_pc - symbols[i].relative_symbol_addr;
if (pc_offset) {
snprintf(line, MAX_BACKTRACE_LINE_LENGTH, "#%02d pc %08x %.*s (%.*s+%u)",
i, symbols[i].relative_pc, fieldWidth, mapName,
fieldWidth, symbolName, pc_offset);
} else {
snprintf(line, MAX_BACKTRACE_LINE_LENGTH, "#%02d pc %08x %.*s (%.*s)",
i, symbols[i].relative_pc, fieldWidth, mapName,
fieldWidth, symbolName);
}
} else {
snprintf(line, MAX_BACKTRACE_LINE_LENGTH, "#%02d pc %08x %.*s",
i, symbols[i].relative_pc, fieldWidth, mapName);
}
ALOGD("%s", line);
}
free_backtrace_symbols(symbols, count);
return result;
}
extern "C" void dumping_callstack();
void dumping_callstack(){
android::CallStack stack;
stack.update();
stack.log(“dump_test“);
}
Kernel代码中打印堆栈
#include <asm/ptrace.h>
printk(KERN_ERR "dump_stack start: %s() %d n",__FUNCTION__,__LINE__);
dump_stack();
printk(KERN_ERR "dump_stack stop: %s() %d n",__FUNCTION__,__LINE__);
native crash所对应的java层堆栈
crash线程就是捕获到信号的线程,虽然这在SIGABRT下不必定可靠,在信号处理函数中得到当前线程的名字,而后把crash线程的名字传给java层,在java里dump出这个线程的堆栈,就是crash所对应的java层堆栈
在C中得到线程名字
char* getThreadName(pid_t tid) {
if (tid <= 1) {
return NULL;
}
char* path = (char *) calloc(1, 80);
char* line = (char *) calloc(1, THREAD_NAME_LENGTH);
snprintf(path, PATH_MAX, "proc/%d/comm", tid);
FILE* commFile = NULL;
if (commFile = fopen(path, "r")) {
fgets(line, THREAD_NAME_LENGTH, commFile);
fclose(commFile);
}
free(path);
if (line) {
int length = strlen(line);
if (line[length - 1] == 'n') {
line[length - 1] = '';
}
}
return line;
}
然后传给Java层
/**
* 根据线程名得到线程对象,native层会调用该方法,不能混淆
* @param threadName
* @return
*/
public static Thread getThreadByName(String threadName) {
if (TextUtils.isEmpty(threadName)) {
return null;
}
Set<Thread> threadSet = Thread.getAllStackTraces().keySet();
Thread[] threadArray = threadSet.toArray(new Thread[threadSet.size()]);
Thread theThread = null;
for(Thread thread : threadArray) {
if (thread.getName().equals(threadName)) {
theThread = thread;
}
}
Log.d(TAG, "threadName: " + threadName + ", thread: " + theThread);
return theThread;
}
参考链接
https://github.com/chenxumeng/NativeCrash
https://github.com/xroche/coffeecatch
https://github.com/iqiyi/xCrash
https://github.com/hexhacking/xCrash
http://www.javashuo.com/article/p-kkmrvyow-dq.html
http://www.javashuo.com/article/p-agyiphwq-ws.html
http://aospxref.com/
http://androidxref.com
原文始发于微信公众号(哆啦安全):Android/C/C++/Native打印堆栈的方法
- 左青龙
- 微信扫一扫
- 右白虎
- 微信扫一扫
评论