Android/C/C++/Native打印堆栈的方法

admin 2022年5月11日09:08:53评论2,513 views字数 5705阅读19分1秒阅读模式


超实用的优质公众号推荐

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中

#include <utils/CallStack.h>
CallStack stack;stack.update();stack.dump(); 


#include <utils/Log.h>#include <utils/CallStack.h>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/

Android/C/C++/Native打印堆栈的方法


Android5.0至Android12,使用Android系统自带的libbacktrace.so,如下所示

http://androidxref.com/5.0.0_r2/xref/system/core/libbacktrace/

Android/C/C++/Native打印堆栈的方法


http://aospxref.com/android-7.1.2_r39/xref/system/core/libbacktrace/

Android/C/C++/Native打印堆栈的方法


http://aospxref.com/android-12.0.0_r3/xref/system/unwinding/libbacktrace/

Android/C/C++/Native打印堆栈的方法


C代码中打印堆栈

C代码,尤其是底层C库,想要看到调用的堆栈信息,还是比较麻烦的。 


CallStack肯定是不能用

一是因为其是C++写的,需要重新封装才能在C中使用

二是底层库反调上层库的函数,会造成链接器循环依赖而无法链接。不过也不是没有办法,可以通过android工具类CallStack实现中使用的unwind调用及符号解析函数来处理。


需要注意的是,为解决链接问题,最好使用dlopen方式,查找需要用到的接口再直接调用,这样会比较简单。如下为相关的实现代码,只需要在要打印的文件中插入此部分代码,然后调用getCallStack()即可,无需包含太多的头文件和修改Android.mk文件

#define MAX_DEPTH 31#define MAX_BACKTRACE_LINE_LENGTH 800#define PATH "/system/lib/libcorkscrew.so"
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;}


#include <utils/CallStack.h>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 */@Keeppublic 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;}


Android平台Native代码的崩溃捕获机制及实现


参考链接

https://github.com/chenxumeng/NativeCrashhttps://github.com/xroche/coffeecatchhttps://github.com/iqiyi/xCrashhttps://github.com/hexhacking/xCrashhttp://www.javashuo.com/article/p-kkmrvyow-dq.htmlhttp://www.javashuo.com/article/p-agyiphwq-ws.html


http://aospxref.com/http://androidxref.com



Android/C/C++/Native打印堆栈的方法

原文始发于微信公众号(哆啦安全):Android/C/C++/Native打印堆栈的方法

  • 左青龙
  • 微信扫一扫
  • weinxin
  • 右白虎
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2022年5月11日09:08:53
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                   Android/C/C++/Native打印堆栈的方法https://cn-sec.com/archives/997507.html

发表评论

匿名网友 填写信息