zoukankan      html  css  js  c++  java
  • Android之Systrace

    1 简介

     https://www.lizenghai.com/archives/26061.html

    2 用法

    用命令行启动Systrace抓取采样:《Capture a system trace on the command line

    自定义采样的方法:《Define custom events》这个方法确实要仔细看,尤其是采样命令的描述,强调了要使用 -a 参数,之前我忽略了这一点,导致自定义的标签总也显示不出来。

    python systrace.py -a com.autonavi.amapauto -b 16384 -o my_systrace_report.html sched freq idle am wm gfx view binder_driver hal dalvik camera input res

    【坑】采用Android Device Monitor来抓取Systrace时有一个坑,就是经常出现Java heap error(因为我自动插桩所有函数,导致Systrace标签数量庞大),后来发现原因在于Monitor配置的Java堆最大值太小了,收集trace数据时需要大量的堆(怀疑是做数据合并加工)引发堆空间错误。解决方法如下:

    1. 找到Monitor脚本所在目录,里面有libmonitor-x86,例如:C:Usersliuheng.klhAppDataLocalAndroidSdk	oolslibmonitor-x86(或monitor-x86_64)
    2. 打开monitor.ini 配置文件,修改如下三项空间配置(尽可能大点就行):
    -XX:MaxPermSize=1024m
    -Xms2048m
    -Xmx4096m

    (1)实现自定义标签

    在Android 6.0(API 23)中包含trace.h,可以在Native层直接进行引用,获取函数指针进行调用。用 ATRACE_CALL 宏直接在需要插桩的函数入口调用一下即可。关键代码:

     1 #include <android/trace.h>
     2 
     3 /////////////////////////////////////////////////////////////////////
     4 // utils.h
     5 extern void *(*_ATrace_beginSection) (const char* sectionName) __attribute__ ((no_instrument_function));
     6 extern void *(*_ATrace_endSection) (void) __attribute__ ((no_instrument_function));
     7 extern bool *(*_ATrace_isEnabled) (void) __attribute__ ((no_instrument_function));
     8 
     9 // SysTrace
    10 class ScopedTrace {
    11     const char* _name;
    12 public:
    13     static bool _bInit;
    14     static void init() __attribute__ ((no_instrument_function));
    15 
    16     typedef void *(*fp_ATrace_beginSection) (const char* sectionName);
    17     typedef void *(*fp_ATrace_endSection) (void);
    18     typedef bool *(*fp_ATrace_isEnabled) (void);
    19 
    20 public:
    21     inline ScopedTrace(const char* name) __attribute__ ((no_instrument_function)) {
    22         if (!_bInit) {
    23             init();
    24         }
    25         _ATrace_beginSection(name) ;
    26 
    27         _name = name;
    28         //LOGD("_ATrace_beginSection(%s) _ATrace_isEnabled()=%d", name, _ATrace_isEnabled());
    29     }
    30 
    31     inline ~ScopedTrace() __attribute__ ((no_instrument_function)) {
    32         _ATrace_endSection();
    33         //LOGD("_ATrace_endSection(%s) _ATrace_isEnabled()=%d", _name, _ATrace_isEnabled());
    34     }
    35 };
    36 
    37 #define ATRACE_NAME(name) ScopedTrace ___tracer(name)
    38 #define ATRACE_CALL() ATRACE_NAME(__FUNCTION__)
    39 
    40 /////////////////////////////////////////////////////////////////////
    41 // utils.cpp
    42 void *(*_ATrace_beginSection) (const char* sectionName) = NULL;
    43 void *(*_ATrace_endSection) (void) = NULL;
    44 bool *(*_ATrace_isEnabled) (void) = NULL;
    45 
    46 bool ScopedTrace::_bInit = false;
    47 void ScopedTrace::init() {
    48     if (_bInit) {
    49         return;
    50     }
    51 
    52     // Retrieve a handle to libandroid.
    53     void *lib = dlopen("libandroid.so", RTLD_NOW || RTLD_LOCAL);
    54 
    55     // Access the native tracing functions.
    56     if (lib != NULL) {
    57         // Use dlsym() to prevent crashes on devices running Android 5.1
    58         // (API level 22) or lower.
    59         _ATrace_beginSection = reinterpret_cast<fp_ATrace_beginSection>(
    60                 dlsym(lib, "ATrace_beginSection"));
    61         _ATrace_endSection = reinterpret_cast<fp_ATrace_endSection>(
    62                 dlsym(lib, "ATrace_endSection"));
    63         _ATrace_isEnabled = reinterpret_cast<fp_ATrace_isEnabled>(
    64                 dlsym(lib, "ATrace_isEnabled"));
    65     }
    66 
    67     LOGI("lib=%p, _ATrace_beginSection=%p, _ATrace_endSection=%p, _ATrace_isEnabled=%p, ATrace_isEnabled()=%d",
    68          lib, _ATrace_beginSection, _ATrace_endSection, _ATrace_isEnabled, _ATrace_isEnabled());
    69 
    70     _bInit = true;
    71 }
    Systrace(Native)

     (2)一个巧妙的利用——编译器自动插桩

    利用gcc的 “-finstrument-functions” 可以实现自动化插桩。关键代码如下:

     1 // utils.h
     2 extern "C" {
     3 
     4 void __cyg_profile_func_enter( void *, void * ) __attribute__ ((no_instrument_function));
     5 void __cyg_profile_func_exit( void *, void * ) __attribute__ ((no_instrument_function));
     6 
     7 }
     8 
     9 // utils.cpp
    10 #define DUMP(func, call) LOGI("[KLH]%s: func = %p, called by = %p
    ", __FUNCTION__, func, call)
    11 
    12 extern "C" {
    13 
    14 void __cyg_profile_func_enter(void *this_func, void *call_site) {
    15     //DUMP(this_func, call_site);
    16     char addr[32];
    17     snprintf(addr, 32, "%p", this_func);
    18 
    19     ScopedTrace::init();
    20 
    21     if (0 != _ATrace_isEnabled && _ATrace_isEnabled()) {
    22         _ATrace_beginSection(addr);
    23     }
    24 }
    25 
    26 void __cyg_profile_func_exit(void *this_func, void *call_site) {
    27     //DUMP(this_func, call_site);
    28 
    29     if (0 != _ATrace_isEnabled && _ATrace_isEnabled()) {
    30         _ATrace_endSection();
    31     }
    32 }
    33 
    34 }
    自动插桩实现追踪

    这里关键的点是需要extern "C"声明原型。

    (3)编写一套Python(其他语言也行)工具来翻译加工自动插桩的函数(运行时)地址为函数名。翻译的方法为:

    1. 获取进程pid:
    adb shell ps | adb shell grep [进程名]
    分解字符串第一个即是
    
    2. 获取进程maps
    adb shell cat /proc/[pid]/maps
    
    3. 提取trace.html(Systrace报告)中所有运行时地址列表,准备翻译
    
    4. 提取所有so的地址段,以此来确定运行时地址所属so及内部相对地址
    
    5. 提取so内部相对地址与符号映射表
    readelf -sW libXXX.so
    
    6. 根据4、5翻译出运行时地址对应的函数符号,并翻译出函数名
    c++filt -n [symbol]
    
    7. trace.html中线程名缺失的问题可以进一步补充(如果线程中途退出则可能遗漏):
    adb shell cat /proc/pid/task
    [foreach] tid
    adb shell cat /proc/pid/task/tid/status
    得到Name:字段就是线程名称

    3 原理

  • 相关阅读:
    linux安装vsftpd服务器
    安装Twisted
    py文件转换为exe文件
    Python实现批量新建SecureCRT Session
    常见的字符编码
    心得 : 面向对象和面向过程的区别
    Apache配置HTTPS的过程小记
    关于oracle的sequence和trigger。
    oracle在drop表时要注意
    mysql中整数类型后面的数字,是不是指定这个字段的长度?比如int(11),11代表11个字节吗?
  • 原文地址:https://www.cnblogs.com/kuliuheng/p/12030732.html
Copyright © 2011-2022 走看看