Android 调试方法

Native

1、C/CPP

#include <utils/Log.h>

ALOGD("%s %d\n", __FUNCTION__, __LINE__)

方法级别为:
VERBOSE ALOGV();
DEBUG ALOGD();
INFO ALOGI();
WARN ALOGW();
ERROR ALOGE();

2、CPP Trace 打印方法

#include <utils/CallStack.h>
android::CallStack stack(("Debug"));

3、C语言Trace打印方法

3.1 使用C封装调用callstack so

callstacktrace.cpp

#include <utils/CallStack.h>
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 : "<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;
}

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 控制文件: <debugfs>/dynamic_debug/control 这个文件被读取用来显示已完成的调试信息列表, 通过 cat <debugfs>/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

2、Sony开源的ChkBugReport工具,ChkBugReport开源地址首页:
https://github.com/sonyxperiadev/ChkBugReport

DumpSys

dumpsys 是一种在 Android 设备上运行的工具,可提供有关系统服务的信息。可以使用 Android 调试桥 (ADB) 从命令行调用 dumpsys,获取在连接的设备上运行的所有系统服务的诊断输出。另外前面的bugreport 命令在抓取信息的时候会把dumpsys信息也获取,这个很贴心
关于dumpsys 要写起来也可以写一篇文章了,建议参考谷歌的文章 dumpsys

调试实例

Android Fuse USB 文件系统问题分析

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。