• Android里调用callstack(转)


    1.        为什么要打印函数调用堆栈?

    打印调用堆栈可以直接把问题发生时的函数调用关系打出来,非常有利于理解函数调用关系。比如函数A可能被B/C/D调用,如果只看代码,B/C/D谁调用A都有可能,如果打印出调用堆栈,直接就把谁调的打出来了。

    不仅如此,打印函数调用堆栈还有另一个好处。在Android代码里,函数命名很多雷同的,虚函数调用,几个类里的函数名相同等,即使用source insight工具看也未必容易看清函数调用关系。如果用了堆栈打印,很容易看到函数调用逻辑。

    那么一个问题来了,Android/kernel本身在发生问题(kernel panic, tombstone, …)时,都可以打出详细的堆栈信息,这里干嘛还要费劲研究打堆栈?答案是发生问题时的堆栈的确很详细,但这里研究的是不影响(准确说是基本不影响)系统运行的境况下,打印出某个情形下的堆栈信息,这个对源代码逻辑研究很有帮助。或者是说,在未发生kernel panic, tombstone的时候,我们需要打印出堆栈信息。

    2.        Linux Kernel

    Kernel里最简单,直接有几现成的函数可以使用:

    dump_stack() 这个函数打出当前堆栈和函数调用backtrace后接着运行

    WARN_ON(x) 这个函数跟dump_stack很像,它有个条件,如果条件满足了就把stack打出来。

    打印出来的结果都在kernel log里,一般dmesg命令就可以看到了

    3.        Native C++

    Android在新版(至少5.0, 6.0)里加入了CallStack类,这个类可以打出当前的backtrace。用法很简单:

    前面确保包含头文件#include <utils/CallStack.h> 

    Android.mk的库依赖列表(LOCAL_SHARED_LIBRARIES)里包含libutilscallstack,一般都已经包含了。

    然后在要打印堆栈处加入android::CallStack cs(“My CallStack Debug”);

    “My CallStack Debug”是在logcat输出的TAG,这里可以自己定义。如果上下文已经在android namespace里,”android::”前缀就不必加了。

    Native C++的输出log可以在logcat里看到。

    注意,在网上的一些文档里说要这么用:

    CallStack stack; 

    stack.update(); 

    stack.dump();

    这样做已经不行了,在新版Android里编译不过。

    4.        Native C

    Android对C的堆栈打印支持不太好。过去网上的文章一般是推荐libcorkscrew.so,并加入大段代码来unwind_backtrace。新版Android上libcorkscrew已经被拿掉了,网上的加载libcorkscrew库的方法自然就不能用了。

    一个简单方法是用C语言调C++的函数,对,就是extern “C”。

    先在项目里加入一个c++文件,比如callstack.cpp,里面是:

    #include <utils/CallStack.h>
    extern "C" void dumping_callstack(void);
    void dumping_callstack(void)
    {
        android::CallStack cs("My CallStack Debug");
    }

    在项目里再加入一个c++的头文件,比如callstack.h,里面是:

    void dumping_callstack(void);

    在Android.mk里源文件列表LOCAL_SRC_FILES里加入callstack.cpp,并确保libutilscallstack在依赖列表里。

    在native C里include callstack.h后直接调用dumping_callstack()就可以了。

    这个log也可以在logcat里看到。

    For Celadon:

    callstack.cpp:

    复制代码
    #include "callstack.h"
    #include <utils/CallStack.h>
    extern "C" {
        void dump_stack02(void)
       {
            //android::CallStack stack;
            //stack.update();
            //stack.dump("INTEL-MESA");
           android::CallStack cs("INTEL-MESA");
           cs.update();
           cs.log("INTEL-MESA", ANDROID_LOG_ERROR, "");
       }
    }
    复制代码

    callstack.h:

    复制代码
    #ifndef _CALLBACK_H
    #define _CALLBACK_H
    #ifdef __cplusplus
    extern "C" {
    #endif
        void dump_stack02(void);
    #ifdef __cplusplus
    }
    #endif
    #endif
    复制代码

    in Android.mk, add libutilscallstack in LOCAL_SHARED_LIBRARIES

     LOCAL_SHARED_LIBRARIES := 
    +   libutilscallstack 

    in Makefile.sources, add the callstack.cpp and callstack.h like below

    复制代码
    @@ -2,4 +2,6 @@ C_SOURCES := 
        virgl_drm_public.h 
        virgl_drm_winsys.c 
        virgl_drm_winsys.h 
    -   virtgpu_drm.h
    +   virtgpu_drm.h 
    +   callstack.cpp 
    +   callstack.h
    复制代码

    If the project uses Android.bp, need to add libutilscallstack in export_header_lib_headers or cc_library_shared as below:

         export_header_lib_headers: [
    @@ -67,11 +69,14 @@ cc_defaults {
             "libnativewindow",
             "libsync",
             "liblog",
    +        "libutilscallstack",
         ],
    
    
    @@ -70,6 +71,7 @@ cc_library_shared {
             "liblog",
             "libsync",
             "libutils",
    +        "libutilscallstack",
         ],

    and "callstack.cpp" into src files list like below:

    @@ -25,6 +25,7 @@ cc_defaults {
             "vgem.c",
             "virtio_gpu.c",
             "i915_private.c",
    +        "callstack.cpp",
         ],

    if still get error: "error: undefined symbol: android::CallStack::~CallStack()", 需要去check folder下面是不是还有其他Android.bp,子Android.bp没有添加libutilscallstack库的话会导致编译的时候这个错,意思是找不到实现。

    in the file you want to add callstack:

    调用 dump_stack02();

    还需要

    $ adb root

    $ adb remount

    $ adb push out/target/product/caas/vendor/lib64/egl/libGLES_mesa.so /vendor/lib64/egl/
    $ adb push out/target/product/caas/vendor/lib/egl/libGLES_mesa.so /vendor/lib/egl/
    
    $ adb push out/target/product/caas/vendor/lib64/dri/i965_dri.so /vendor/lib64/dri/
    $ adb push out/target/product/caas/vendor/lib/dri/i965_dri.so /vendor/lib/dri/

    然后

    $ adb shell sync

    最后把QEMU停掉,再启动。

    5.        Java

    Java最简单,它的backtrace最详细,连文件名和行号都打出来了:

    Exception e = new Exception("haha");

    e.printStackTrace();

    log在logcat里看以看到。

    大部分内容转自: http://blog.sina.com.cn/s/blog_7213e0310102wtge.html

  • 相关阅读:
    Spinner使用
    5.5 easypoi模板导出excel测试Demo > 我的程序猿之路:第四十五章
    5.4 SpringCloud配置中心搭建以及问题解决 > 我的程序猿之路:第四十四章
    5.3 Spring事物管理详解 > 我的程序猿之路:第四十二章
    5.2 SpringBoot实现断点续传功能 > 我的程序猿之路:第四十二章
    5.1 java实现doc文档转pdf文件 > 我的程序猿之路:第四一章
    5.0 SpringBoot普通上传功能 > 我的程序猿之路:第四十章
    4.8 数字金额大写转换 插件 > 我的程序猿之路:第三十八章
    4.7 基于Spring注解的定时任务(@Schedule) > 我的程序猿之路:第三十七章
    4.6 基于Spring-Boot的Mysql+jpa的增删改查学习记录 > 我的程序猿之路:第三十六章
  • 原文地址:https://www.cnblogs.com/ArsenalfanInECNU/p/14510304.html
Copyright © 2020-2023  润新知