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腳本所在目錄,里面有lib\monitor-x86,例如:C:\Users\liuheng.klh\AppData\Local\Android\Sdk\tools\lib\monitor-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\n", __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 原理

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM