1. AppleTraceiOS 應(yīng)用性能分析工具

        聯(lián)合創(chuàng)作 · 2023-10-01 20:00

        AppleTrace 是一個(gè)用來(lái)分析 iOS App 性能的工具。

        背景

        一般情況下使用Instruments(主要是Time Profiler)進(jìn)行iOS App的性能分析就足夠了,但是Time Profiler 把調(diào)用方法都合并了起來(lái),失去了時(shí)序的表現(xiàn)。直到有一天看到Android開(kāi)發(fā)的同事使用 systrace 分析性能,systrace生成一個(gè)html文件,把函數(shù)(方法)的調(diào)用耗時(shí)按照先后順序表現(xiàn)出來(lái)。心里想:要是iOS也有這樣的工具就好了。了解到這個(gè)html文件是 catapult 生成的。

        一天看到iosre論壇一篇hook objc_msgSend的帖子。突然想到,可以結(jié)合catapult來(lái)生成Objective C方法的性能分析圖(暫且這么叫吧)。(雖然一直也有hook objc_msgSend的方法,但這次煮好的佳肴終于忍不住下手了)。

        說(shuō)搞就開(kāi)始搞,暫停幾天開(kāi)發(fā)MachOExplorer。近期一直利用少之又少的業(yè)余時(shí)間蝸牛般開(kāi)發(fā)MachOExplorer,但現(xiàn)在看來(lái)生成性能分析圖更是重要,回想過(guò)去的一些苦力加班,如果能生成這個(gè)性能分析圖,當(dāng)時(shí)豈不是很快就解決問(wèn)題了。

        目標(biāo)

        hook 所有的objc_msgSend,也就是把每個(gè)Objective C方法的耗時(shí)計(jì)算出來(lái),并按照先后順序生成性能分析圖

        sample

        要解決的問(wèn)題

        如何生成最終的html

        這里可以了解到catapult是如何生成html的。其中一種方式可以是:Chrome’s trace_event format。簡(jiǎn)單來(lái)說(shuō),trace_event format 就是個(gè)json格式,按照這個(gè)約定的json格式填充數(shù)據(jù)后,就可以使用trace2html命令(python腳本)轉(zhuǎn)換為最終的html文件了。

        $CATAPULT/tracing/bin/trace2html my_trace.json --output=my_trace.html && open my_trace.html
        

        如何Hook objc_msgSend

        見(jiàn)文章使用HookZz快速逆向(Hack objc_msgSend) 理清邏輯

        HookZz是jmpews開(kāi)發(fā)的微型hook框架,使用起來(lái)十分靈活。詳見(jiàn) https://jmpews.github.io/zzpp/

        如何生成trace_event format的json文件

        參考文檔 Chrome’s trace_event format 可以了解到,最簡(jiǎn)單的json文件,可以是這樣:

        [ {"name": "Asub", "cat": "PERF", "ph": "B", "pid": 22630, "tid": 22630, "ts": 829},
          {"name": "Asub", "cat": "PERF", "ph": "E", "pid": 22630, "tid": 22630, "ts": 833} ]
        

        每一行表示一個(gè)Event,

        {
          "name": "myName",
          "cat": "category,list",
          "ph": "B",
          "ts": 12345,
          "pid": 123,
          "tid": 456,
          "args": {
            "someArg": 1,
            "anotherArg": {
              "value": "my value"
            }
          }
        }
        

        每個(gè)字段的含義如下:

        - name: The name of the event, as displayed in Trace Viewer
        - cat: The event categories. This is a comma separated list of categories for the event. The categories can be used to hide events in the Trace Viewer UI.
        - ph: The event type. This is a single character which changes depending on the type of event being output. The valid values are listed in the table below. We will discuss each phase type below.
        - ts: The tracing clock timestamp of the event. The timestamps are provided at microsecond granularity.
        - tts: Optional. The thread clock timestamp of the event. The timestamps are provided at microsecond granularity.
        - pid: The process ID for the process that output this event.
        - tid: The thread ID for the thread that output this event.
        - args: Any arguments provided for the event. Some of the event types have required argument fields, otherwise, you can put any information you wish in here. The arguments are displayed in Trace Viewer when you view an event in the analysis section.
        
        

        其中ph(event type)是需要關(guān)心的:

        Event type Event phases
        Duration Events B(begin), E(end)

        也就是說(shuō)一個(gè)方法的調(diào)用,至少有兩行,ph=B和ph=E。

        格式弄清楚后,就需要生成json文件了。生成這個(gè)json文件本質(zhì)上就是個(gè)日志功能,為了盡最大可能不影響App的性能,使用內(nèi)存映射mmap方法來(lái)寫(xiě)文件。同時(shí)為了簡(jiǎn)單的處理多線程問(wèn)題,使用了串行queue。代碼見(jiàn)這里

        最終trace文件會(huì)生成在App沙盒中的Library/appletracedata目錄。由于日志量可能很大,又結(jié)合mmap的特性,日志文件會(huì)以下面的邏輯生成:

        trace.appletrace
        trace_1.appletrace
        trace_2.appletrace
        trace_3.appletrace
        ...
        trace_N.appletrace
        

        每個(gè)appletrace文件16MB,由于mmap的特性(只能映射固定大小文件),文件末尾一般會(huì)有\0來(lái)填充。

        生成這些appletrace文件后,需要從App的沙盒中復(fù)制出來(lái)。使用merge.py把a(bǔ)ppletrace文件轉(zhuǎn)換為trace_event format的json文件。

        python merge.py -d <appletracedata directory>
        

        最終執(zhí)行catapult的trace2html腳本,生成最終的html文件。

        python catapult/tracing/bin/trace2html appletracedata/trace.json --output=appletracedata/trace.html
        

         

        使用方法

        采集數(shù)據(jù)

        目前有兩種采集數(shù)據(jù)的方式。

        手動(dòng) APTBeginSection 和 APTEndSection

        這種場(chǎng)景是:我不想hook所有的Objective C方法,我只想在分析性能時(shí),一點(diǎn)一點(diǎn)手動(dòng)添加開(kāi)始點(diǎn)和結(jié)束點(diǎn)。(這點(diǎn)Android的systrace也是支持)雖然麻煩,但在定位到大體方向后,這樣更加精細(xì)和準(zhǔn)確,避免了hook對(duì)App本身性能的影響。

        (1)只需要把 appletrace.happletrace.mm文件拖入自己的功能即可。(當(dāng)然這里可以做成CocoaPods,有時(shí)間可以做下)。

        (2)然后在函數(shù)(方法)的開(kāi)頭和結(jié)尾(或者自己感興趣的區(qū)間),調(diào)用APTBeginSection 和 APTEndSection即可。對(duì)于ObjectiveC方法可以使用宏APTBeginAPTEnd。

        // Objective C class method
        #define APTBegin APTBeginSection([NSString stringWithFormat:@"[%@]%@",self,NSStringFromSelector(_cmd)].UTF8String)
        #define APTEnd APTEndSection([NSString stringWithFormat:@"[%@]%@",self,NSStringFromSelector(_cmd)].UTF8String)
        

        參考例子 sample/ManualSectionDemo。

        Hook objc_msgSend

        這種場(chǎng)景是:我想初步定為哪里有耗時(shí)的操作,可以整體上Hook objc_msgSend一次,對(duì)整個(gè)App的流程有個(gè)大致了解。

        (1)把動(dòng)態(tài)庫(kù)的工程appletrace.xcodeproj拖拽到目標(biāo)工程。 (2)并配置動(dòng)態(tài)庫(kù)的依賴 Target Dependencies 和 Copy Files

        參考 sample/TraceAllMsgDemo。

        注意:

        1. 需要關(guān)閉BitCode。
        2. 僅支持arm64。

        處理數(shù)據(jù),生成html

        從App的沙盒中復(fù)制出 Library/appletracedata 目錄。(例如:Xcode可以直接Dump出整個(gè)沙盒)

        appletracedata

        然后,

        // 處理mmap的日志文件
        python merge.py -d <appletracedata directory>
        
        // 生成html
        python catapult/tracing/bin/trace2html appletracedata/trace.json --output=appletracedata/trace.html
        
        // 打開(kāi)
        open trace.html
        

        就可以看到sample

        性能影響

        目前對(duì)App性能的影響主要是:

        1. Hook objc_msgSend :這個(gè)是主要的影響,因此生成的最終結(jié)果僅用于分析、對(duì)比,而不能認(rèn)為就是耗費(fèi)了這些數(shù)值。
        2. 日志文件:為了寫(xiě)日志,mmap了文件,還創(chuàng)建了隊(duì)列。對(duì)App本身的性能也有影響。

        局限

        由于HookZz對(duì)objc_msgSend的hook僅實(shí)現(xiàn)了arm64架構(gòu),因此只能在真機(jī)上分析。(當(dāng)然這也足夠了,主流設(shè)備就是arm64)

        計(jì)劃

        計(jì)劃1:dtrace

        對(duì)于數(shù)據(jù)的產(chǎn)生來(lái)源,目前有兩種:

        1. 手動(dòng) APTBeginSection 和 APTEndSection
        2. Hook objc_msgSend

        最近一段時(shí)間對(duì) dtrace也學(xué)習(xí)了一段時(shí)間了,完全可以針對(duì)模擬器使用dtrace來(lái)生成數(shù)據(jù)。dtrace由于是內(nèi)核層,對(duì)App本身的性能影響很小,而且dtrace不僅僅可以hook(trace)ObjectiveC方法,還可以trace C方法、swift方法。這是下一步的計(jì)劃。

        計(jì)劃2:白名單類/黑名單類

        Hook objc_msgSend的方法,有的類可能并不關(guān)心??梢圆捎冒酌麊位蛘吆诿麊蔚姆绞?,縮小分析范圍。

        計(jì)劃3:Hook +load and C++ static initializers

        見(jiàn)A method of hook static initializers 和A method of hook objective c +load

        總結(jié)

        這個(gè)工具本身的代碼不多(寫(xiě)日志),主要是組合了catapult和HookZz,再次感謝catapult和HookZz。

        瀏覽 36
        點(diǎn)贊
        評(píng)論
        收藏
        分享

        手機(jī)掃一掃分享

        編輯 分享
        舉報(bào)
        評(píng)論
        圖片
        表情
        推薦
        點(diǎn)贊
        評(píng)論
        收藏
        分享

        手機(jī)掃一掃分享

        編輯 分享
        舉報(bào)
          
          

            1. 欧美成人性爱免费 | 息与子五十路の高齢熟女在线视频 | 一区二区三区福利 | 国产理伦在线 | 插鸡巴网站 |