Native
1、C/CPP
#include
ALOGD("%s %d\n", __FUNCTION__, __LINE__)
方法级别为:
VERBOSE ALOGV();
DEBUG ALOGD();
INFO ALOGI();
WARN ALOGW();
ERROR ALOGE();
2、CPP Trace 打印方法
#include
android::CallStack stack(("Debug"));
3、C语言Trace打印方法
3.1 使用C封装调用callstack so
callstacktrace.cpp
#include
extern "C" void dumpstacktrace(void);
void dumpstacktrace(void)
{
android::CallStack cs("Debug");
}
callstacktrace.h
void dumpstacktrace(void);
将callstacktrace.cpp callstacktrace.h编译成 libcallstacktrace.so 后,本地Native C程序include callstacktrace.h后调用动态库的方法来调用dumpstacktrace()
3.2 自定义函数调用 unwind
通过android工具类CallStack实现中使用的unwind调用及符号解析函数来处理,
这里需要注意的是,为解决链接问题,最好使用dlopen方式,查找需要用到的接口再直接调用
这个方法没有确认过 参考自 https://blog.csdn.net/freshui/article/details/9456889
#include "backtrace.h" /* 引入头文件 libcorkscrew-ndk/master/corkscrew/backtrace.h */
#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 : "";
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;
}
Framework
1、Slog
import android.util.Slog;
Slog.d(TAG, "Debug" );
方法级别为:
VERBOSE Slog.v();
DEBUG Slog.d();
INFO Slog.i();
WARN Slog.w();
ERROR Slog.e();
2、Log
import android.util.Log;
Log.d(TAG, "Debug.." );
方法级别为:
VERBOSE Log.v();
DEBUG Log.d();
INFO Log.i();
WARN Log.w();
ERROR Log.e();
3、Trace 打印方法
Thread.currentThread().dumpStack(); //方法1
Log.d(TAG,"Debug", new RuntimeException("Debug")); //方法2
new RuntimeException("Debug").printStackTrace(); //方法3
Kernel
常规打印方法
printk("Debug...");
pr_info("Debug...");
1、Trace 打印方法
WARN_ON(1);
2、动态打印方法
Kernel def_config中需要打开以下两个宏
CONFIG_DEBUG_FS=y
CONFIG_DYNAMIC_DEBUG=y
Demo:
:/ # echo "file hub.c +p" > /sys/kernel/debug/dynamic_debug/control
:/ # echo "8 8 8 8" > /proc/sys/kernel/printk
接入U盘的操作的时候可以看到如下打印
[ 694.261724@0] hub 1-3:1.0: state 7 ports 4 chg 0000 evt 0010
[ 694.262408@0] hub 1-3:1.0: port 4, status 0101, change 0001, 12 Mb/s
动态调试的主要功能是允许动态的打开或关闭内核代码的各种提示信息,即pr_debug()/dev_debug() 之类的函数可以动态的在代码里所使用
动态调试有很多有用的特性:
* 简洁的查询语言允许打开或关闭调试的状态通过匹配以下的任意组合:
- 资源文件名
- 函数名
- 行号 (包括一定范围的行号)
- 模块名
- 格式化字符串
* 提供一个debugfs 控制文件: /dynamic_debug/control 这个文件被读取用来显示已完成的调试信息列表, 通过 cat /dynamic_debug/control 命令可以看对应的配置
跨进程打印堆栈的方式
类别
函数式
命令式
Java
Process.sendSignal(pid, Process.SIGNAL_QUIT)
kill -3 [pid]
Native
Debug.dumpNativeBacktraceToFile(pid, tracesPath)
debuggerd -b [pid]
Kernel
WD.dumpKernelStackTraces()
cat /proc/[tid]/stack
堆栈分析方法
A、addr2line
32bit
arm-linux-androideabi-addr2line -Cfe libsurfaceflinger.so 000000000003fc74
64bit
aarch64-linux-android-addr2line -Cfe libsurfaceflinger.so 000000000003fc74
B、GDB
使用GDB attach 对应Symbols 后使用list 命令
list *000000000003fc74
BugReport
可以使用adb bugreport命令获取系统运行的所有log信息。命令如下:
adb bugreport > /data/bugreport_out.txt
所有log信息输出到bugreport_out.txt文件中。
bugreport 有工具可以做UI可视化阅读
1、google的bettery historian开源了,开源项目的地址:
https://github.com/google/battery-historian
DumpSys
dumpsys 是一种在 Android 设备上运行的工具,可提供有关系统服务的信息。可以使用 Android 调试桥 (ADB) 从命令行调用 dumpsys,获取在连接的设备上运行的所有系统服务的诊断输出。另外前面的bugreport 命令在抓取信息的时候会把dumpsys信息也获取,这个很贴心
关于dumpsys 要写起来也可以写一篇文章了,建议参考谷歌的文章 dumpsys
调试实例