11純代碼 oc xcode_iOS代碼染色原理及技術實踐

背景

隨著業務的迅速發展,業務代碼邏輯的復雜度增加。QA測試的質量對于產品上線后的穩定性更加重要。一般QA測試的工作流程分為兩大項:自動化測試和人工測試。這兩種測試后都需要得到代碼覆蓋率。自動化測試的覆蓋率,在雙端都有比較成熟的方案。?

本文著重介紹人工測試過程中,怎么得到對應的代碼覆蓋率。涉及到的技術主要是代碼染色。以下會先介紹整體的工作流程,再對涉及到的技術一一闡述。

染色流程

57a5d60393f7d79c7a4ba209a518d48f.png

流程圖中涉及到了雙端的關鍵節點以及技術點。我們重點介紹編譯階段。

· 編譯階段:生成染色包 (對IR文件插樁)

需要在編譯中增加編譯選項,編譯后會為每個可執行文件生成對應的 .gcno文件。

· 運行階段:生成二進制覆蓋率文件。

在測試代碼中調用覆蓋率分發函數,會生成對應的 .gcda文件。

· 解析階段:將二進制覆蓋率文件可視化。

編譯階段

在上文可以看出,編譯階段最核心的操作是對IR文件進行插樁。

什么是IR文件?插樁邏輯是什么?我們往下看。

語言處理系統

一個完整的語言處理系統中,從源程序到可執行的機器代碼,如下圖所示,歷經幾個重要模塊。而我們上文提到的IR文件,是編譯器模塊中的產物,插樁處理也是在這個模塊中進行。這里重點討論下編譯器。

cc9a276b48bec5d0a116f03d40162330.png

編譯器

說起編譯器,我們了解到的傳統編譯器架構分為前端、優化器和后端。

傳統編譯器的劣勢是:前端和后端沒有完全分離,耦合在了一起,因而如果要支持一門新的語言或硬件平臺,需要做大量的工作。一種更加靈活,適應性更好的編譯器套件應運而生——LLVM.

LLVM

官網:http://www.aosabook.org/en/llvm.html

LLVM是一個開源的,模塊化和可重用的編譯器和工具鏈技術的集合,或者說是一個編譯器套件。

可以使用LLVM來編譯Kotlin,Ruby,Python,Haskell,Java,D,PHP,Pure,Lua和許多其他語言。

LLVM核心庫還提供一個優化器,對流行的CPU做代碼生成支持。

LLVM同時支持AOT預先編譯和JIT即時編譯。

2012年,LLVM獲得美國計算機協會ACM的軟件系統大獎,和UNIX,WWW,TCP/IP,Tex,JAVA等齊名。

LLVM和傳統編譯器最大的不同點在于,前端輸入的任何語言,在經過編譯器前端處理后,生成的中間碼都是IR格式的。接下來看下LLVM架構下的巨大優勢,iOS&MacOS平臺的編譯器。

a8947d8819e414a4b89834c230a2d31e.png

iOS&MacOS平臺編譯器

iOS、MacOS平臺開發用的IDE:Xcode。在 Xcode 5版本前使用的是GCC編譯器,在 Xcode 5中將GCC徹底拋棄,替換為LLVM 。LLVM包含了編譯器前端、優化器和編譯器后端三大模塊。

其中Swift除了在編譯器前端和Objective-C稍有不同,其他模塊都是相同的。

如下圖所示,能看出LLVM的優勢,對于一門新的編程語言,只需要提供對應的編譯前端,生成IR。就可以完成整個新語言的處理。

7c55f1792805a1279a91a51001245626.png

聊過了IR文件在整個語言處理過程中的位置,下面我們看下IR文件生成邏輯以及插樁相關的邏輯。這不得不提到Clang。

Clang

Clang是LLVM的子項目,是C、C++和Objective-C的編譯器。Clang在整個Objective-C編譯過程中扮演了編譯器前端的角色,同時也參與到了Swift編譯過程中的Objective-C API映射階段。

Clang的特點是編譯速度快,模塊化,代碼簡單易懂,診斷信息可讀性強,占用內存小以及容易擴展和重用等。

Clang的主要功能是輸出代碼對應的抽象語法樹(AST),針對用戶發生的編譯錯誤準確地給出建議,并將代碼編譯成LLVM IR。

以Xcode為例,Clang編譯Objective-C代碼的速度是Xcode 5版本前使用的GCC的3倍,其生成的AST所耗用掉的內存僅僅是GCC的五分之一左右。

關于iOS項目可以使用對應的命令獲取,本文不作詳細介紹。

關于編譯器前端的主要工作項,感興趣的讀者閱讀《編譯原理》——龍書。

介紹完了IR的“生成器”。接下來我們詳細介紹IR文件。

LLVM IR

LLVM Intermediate Representation。LLVM的中間代碼,是編譯器前端的輸出,和編譯器后端的輸入。是連接編譯器前端與LLVM后端的一個橋梁。

通常常見的文件格式為ll 和bt 。做過iOS開發的讀者應該了解bitcode。bt就是編譯器開啟bitcode后的一種中間代碼格式。

IR提供了獨立于任何特定機器架構的源語,因此它是LLVM優化和進行代碼生成的關鍵,也是LLVM有別于其他編譯器的最大特點。LLVM的核心功能都是圍繞IR建立的。

通常中間代碼的表示形式分為:語法樹(syntax tree)、三地址指令序列。為了更好的了解IR文件。這里介紹下三地址指令。

三地址指令

也可以稱為三地址代碼。之所以被稱為三地址指令,是源于它的指令形式:x = y op z ,其中op是一個二目運算符,y和z是運算分量的地址,x是運算結果的存放地址。三地址指令最多只執行一個運算,通常是計算,比較或者分支跳轉運算。

三地址代碼拆分了多運算符算術表達式以及控制流語句的嵌套結構,所以適用于目標代碼的生成和優化。

 //像 x+y*z 這樣的源代碼被翻譯成三地址指令序列:
t1=y*z
t2=x+t1//源碼:do i = i + 1; while(a[i] < 10); 被翻譯成如下的三地址指令
i = i + 1
t1 = a[i]
if t1 < 10 goto 6
其中t1,t2是編譯器產生的臨時名字。

但是程序運行過程中,每個模塊并不是完全獨立的。存在著模塊間的跳轉。這些被翻譯出的三地址指令,又被組合成另一種便于理解的形式——BB塊。

基本塊

基本塊(Basic Block)是滿足下列條件的最大的連續三地址指令序列

· 控制流只能從基本塊中的第一個指令進入該塊。

· 除了基本塊的最后一個指令,控制流在離開基本塊之前不會停機或者跳轉。

· 只要基本塊中的第一個指令被執行,那么基本塊中的所有指令都會得到執行

其中中間代碼指令序列生成BB塊的算法如下:

· 確定中間代碼序列中哪些指令是首指令

  • 中間代碼的第一個三地址指令是一個首指令。
  • 任意一個條件或無條件轉移指令之后的目標指令是一個首指令。
  • 緊跟在一個條件或無條件轉移指令之后的指令是一個首指令。

· 每個首指令對應的基本塊包括了從它自己開始,直到下一個首指令(不含)或者中間代碼的結尾指令之間的所有指令。

舉例:

i = 1 //第一個三地址指令,所以作為首指令
j = 1 //第11行,跳轉語句的目標指令。所以作為首指令
t1 = 10*i
t2 = t1+j
t3 = 8*t2
t4 = t3-88
a[t4] = 0.0
j = j+1
if j<=10 goto (3) //本身作為跳轉指令,所以是首指令
i = i+1
if i<=10 goto (2) //本身作為跳轉指令,所以是首指令
i = 1
t5 = i – 1 //第17行,跳轉語句的目標指令。所以是首指令
t6 = 88*t5
a[t6] = 1.0
i = i+1
if i<=10 goto (13)//本身作為跳轉指令,所以是首指令//把一個10x10的矩陣設置成單位矩陣中的中間代碼
for(i=1;i<=10;i++){for(j=1;j<=10;j++){a[i,j] = 0.0;}
}
for(i=1;i<=10;i++){a[i,j] = 1.0;
}

對應被劃分的BB塊:

168d55b2a7c91dc772990dce57a70b76.png

在了解了BB塊之后。我們距離怎么對IR文件進行插樁的真相已經越來越近了,下面我們來看下最后一個最重要的環節。

流圖

當將一個中間代碼程序劃分成為基本塊之后,我們用一個流圖來表示它們之間的控制流。流圖(flow graph)的結點就是這些基本塊。流圖就是通常的圖,它可以用任何適合表示圖的數據結構來表示。

從基本塊B到基本塊C之間有一條邊當且僅當基本塊C的第一個指令緊跟在B的最后一個指令之后執行。存在這樣一條邊的原因有兩種:

· 有一個從B的結尾跳轉到C的開頭的條件或無條件跳轉語句

· 按照原來的三地址語句序列中的順序,C緊跟在B之后,且B的結尾不存在無條件跳轉語句。

我們說B是C的前驅(predecessor), 而C是B的一個后繼(successor)。

通常會增加兩個分部稱為入口(entry)出口(exit)的結點。它們不和任何可執行的中間指令對應。從入口到流圖的第一個可執行結點有一條邊(edges)。從任何包含了可能是程序的最后執行指令的基本塊到出口有一條邊。如果程序的最后指令不是一個無條件轉移指令,那么包含了程序的最后一條指令的基本塊是出口結點的一個前驅。但任何包含了跳轉到程序之外的跳轉指令的基本塊也是出口結點的前驅。

8a02f3135d2d99ad9245727c8f7a74bf.png

其中B0-B7是BB塊。E0-E7是邊(edges)

插樁邏輯

覆蓋率計數指令的插入會進行兩次循環,外層循環遍歷編譯單元中的函數,內層循環遍歷函數的基本塊。函數遍歷用來向gcno文件中寫入函數位置信息。

一個函數中基本塊的插樁方法如下:

· 統計所有BB的后繼數n,創建和后繼數大小相同的數組ctr[n]。

· 以后繼數編號為序號將執行次數依次記錄在 ctr[i] 位置,對于多后繼情況根據條件判斷插入。

根據生成流圖的規則,可以很容易得到樁點位置,[]處就是插入的樁點序號。

eb27ae78f499a661dc35381cf8a6aa45.png

關于工程配置可以參考GCOV的官網:

https://gcc.gnu.org/onlinedocs/gcc/Gcov.html

下面簡單介紹下gcov,gcno,gcda這三個gcc家族的關鍵成員。

GCOV

GCOV是一個GNU的本地覆蓋測試工具, 伴隨GCC發布,配合GCC共同實現對C或者C++文件的語句覆蓋和分支覆蓋測試。是一個命令行方式的控制臺程序。需要工具鏈的支持。

GCNO

利用Clang分別生成源文件的AST和IR文件,對比發現,AST中不存在計數指令,而IR中存在用來記錄執行次數的代碼。

覆蓋率映射關系生成源碼是LLVM的一個Pass,用來向IR中插入計數代碼并生成.gcno文件(關聯計數指令和源文件)。

cc784077cd0606e4eaadf4f35c2ffc33.png

上圖右側。即為gcno的可視化格式。

本質上gcno是二進制內容。需要借助gcov工具(gcov -dump xxx.gcno)將文件轉換為這種可視的格式。

其中每個字段的含義

· 函數所在文件的絕對路徑(如上圖紅框所示)。

· Block :0-7 代表BB文件的編號。

· Counter為插樁后生成的存儲執行次數的字段。

· Source Edges是前繼。

· Destination是后繼。

· Lines是指令在代碼文件中行數。

GCDA

gcda是由加了-fprofile-arcs編譯參數的編譯后的文件運行所產生的,它包含了弧跳變的次數和其他的概要信息。

借助gcov工具可以查看gcda文件的大致內容:

gcda文件已經是一個包括了函數執行情況的文件。剩余的工作就是將執行情況更加可視化,和源碼進行匹配。

44cfd21ccbc51708a1f9990320524829.png

了解了三個gc的重要成員。借助一些前端工具,我們就可以得到一份詳細的覆蓋率報告了。關于前端工具,大家可以自行搜索。

最后附上覆蓋率的一個報告片段

c75d0f13f01ab2e5b4b289c135784814.png

技術擴展

了解上述基礎知識后,我們更加容易理解LLVM中的架構及各個模塊的功能。我們可以在插樁過程中,修改原有的插樁邏輯。我們可以編寫XCode編譯器插件。總之,借助LLVM的源碼及我們了解到的知識。在一個語言的任意處理階段,我們都可以對其進行定制,甚至我們可以創造一個自己的專屬語言。

源碼參考:

https://github.com/llvm-mirror/llvm/blob/release_70/lib/Transforms/Instrumentation/GCOVProfiling.cpp

https://llvm.org/doxygen/group__LLVMCCoreValueBasicBlock.html#ga444a4024b92a990e9ab311c336e74633

https://gcc.gnu.org/onlinedocs/gcc/Gcov.html

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/529571.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/529571.shtml
英文地址,請注明出處:http://en.pswp.cn/news/529571.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

刪除了幾個月的照片能找回么_手機刪除照片怎么恢復正常?自動修復,一看就會...

手機刪除照片怎么恢復正常&#xff1f;都說魚的記憶只有七秒鐘&#xff0c;那你的記憶是多久呢&#xff1f;曾經手機拍照留念的照片&#xff0c;是否由于一些微不足道的事情&#xff0c;沖動之下刪掉了!想要還原刪除掉的手機照片&#xff0c;首先要找對照片恢復器&#xff1a;手…

并發測試mysql_Jmeter性能測試系列——結果分析與報告輸出

場景運行結束后&#xff0c;需針對測試結果進行性能分析。通常而言&#xff0c;Jmeter性能測試結果分析可從性能測試指標達成方面著手&#xff0c;然后再分析測試過程中出現的異常情況&#xff0c;逐一判斷是否存在性能風險。1.用戶登陸并發測試結果分析獲取測試指標提取階段獲…

怎樣取消連續包月自動續費_蘋果手機連續包月會員怎么取消 設置iPhone解除應用自動續費...

[閩南網]相信我們都有在用蘋果手機下載軟件&#xff0c;但是有時候一個不小心就被開通了包月續費&#xff0c;這個的話會長期進行扣費&#xff0c;要是不想開通的話可以進行取消&#xff0c;不知道的話可以查看下面的教程。取消收費項目是可以從電腦端和手機端兩個渠道來進行&a…

oracle 空閑連接數_oracle數據庫空閑連接

RMAN-04006: error from auxiliary database: ORA-01034: ORACLE not available在使用rman duplicate復制standby數據庫的時候&#xff0c;由于standby數據庫實例必須處于非mount狀態&#xff0c;所以主數據庫直接連接是會報錯的&#xff0c;需要在standby數據庫服務上的監聽器…

配置snmp_多種設備基于 SNMP 協議的敏感信息泄露漏洞數據分析報告

作者&#xff1a;知道創宇404實驗室1. 更新情況2. 事件概述SNMP協議[1]&#xff0c;即簡單網絡管理協議&#xff08;SNMP&#xff0c;Simple Network Management Protocol&#xff09;&#xff0c;默認端口為 161/UDP&#xff0c;目前一共有3個版本&#xff1a;V1&#xff0c;V…

python 個人所得稅問題_Python實現的個人所得稅計算器

這篇文章主要介紹了Python實現的個人所得稅計算器,涉及Python條件判斷與數值運算相關操作技巧,需要的朋友可以參考下本文實例講述了Python實現的個人所得稅計算器。分享給大家供大家參考&#xff0c;具體如下&#xff1a;# -*- coding: utf-8 -*-"""Created on …

建立項目接口文檔_分享:一步一個腳印,vue入門之使用mockjs搭建vue項目測試服務器...

在以前的文檔中&#xff0c;我們構建了vue項目的整體架構&#xff0c;詳見vue入門&#xff1a;vue項目架構設計起步&#xff0c;現在我們主要對其中的mock server 進行完善。一、概述前后端分離的項目優點之一就是可以前后端并行開發&#xff0c;互不影響。那么在后端接口沒有完…

有效字符區域 獲取textview_Android編程開發之TextView文字顯示和修改方法(附TextView屬性介紹)...

本文實例講述了Android編程開發之TextView文字顯示和修改方法。分享給大家供大家參考&#xff0c;具體如下&#xff1a;一. 新建一個Activity 和 Layout首先在layout文件夾中新建一個activity_main.xml,在新建工程的時候一般默認會新建此xml文件,修改其代碼如下:activity_main.…

單機最大負載_電流互感器允許接入的實際最大二次負載(注電案例1865)

某國外水電站安裝的水輪發電機組&#xff0c;單機額定容量為 120MW&#xff0c;發電機額定電壓為 13.8kV&#xff0c;cosφ0.85。發電機、主變壓器采用發變組單元接線&#xff0c;未裝設發電機斷路器&#xff0c;主變高壓側三相短路時流過發電機的最大短路電流為 19.6kA。發電機…

vue怎么vw布局好用_vue cli3項目的pc自適應布局_vw

前言vw布局的頁面是等比改變的,比如我們在一個750px的屏幕寬度中使用了vw,當我們把屏幕寬度改為1920的時候,網頁的整個樣式包括字體都會等比放大.vw布局我用于大屏監控數據展示.下圖是750屏幕寬度下的頁面下圖是1920屏幕寬度下的頁面pc端配置1.安裝postcss-px-to-viewport插件,…

python做得怎么變成exe_Python如何生成exe文件?

背景圖來源&#xff1a;click斗魚直播間真實rtmp地址獲取(含工具類下載)?zhuanlan.zhihu.com最經典的Python爬蟲(圖片)案例?zhuanlan.zhihu.com寫在前面&#xff1a;之前用python生成過可執行的exe文件(斗魚推流地址exe &#xff0c;圖片exe&#xff0c;芳兒小團子千人成像ex…

python mkl freebsd_freebsd下之簡單安裝python

先su到root帳戶進入/usr/ports/lang/python26然后直接make整個過程如圖所示&#xff0c;經過漫長的編譯之后...然后make install又是漫長的等待...出現Install them as needed.--------------------------------------------------------This package was built with the exper…

html video顯示進度條_使用 tqdm 在 Python 應用中顯示進度 | Linux 中國

如果你的程序需要一段時間才能顯示結果&#xff0c;可通過顯示它的進度來避免讓用戶感到沮喪。來源&#xff1a;https://linux.cn/article-12990-1.html作者&#xff1a;Moshe Zadka譯者&#xff1a;geekpi&#xff08;本文字數&#xff1a;3093&#xff0c;閱讀時長大約&#…

課題開題報告范文樣本_成都汽車職業技術學校舉行 2020年省、市、區課題開題報告會...

01為深入貫徹落實國務院、省、市、區關于深化教育教學改革的重要精神&#xff0c;充分發揮教育科研的先導作用&#xff0c;明晰課題研究的思路并提高課題研究的針對性&#xff0c;1月12日&#xff0c;成都汽車職業技術學校舉行2020年省、市、區課題開題報告會。本次開題報告會邀…

vsftp日志查看_vsftp日志xferlog格式分析(示例代碼)

1、開始vsftp記錄日志。修改/etc/vsftpd/vsftpd.conf 如下&#xff1a;xferlog_enableYESxferlog_std_formatYESxferlog_file/var/log/xferlogFTP服務器的日志設置&#xff0c;可以通過修改主配置文件/etc/vsftpd.conf實現。主配置文件中與日志設置有關的選項包括xferlog_enabl…

安裝mysql5 1步驟_Linux系統安裝MySQL詳細步驟(mysql-5.1等)

第一步、查找以前是否安裝有mysql使用下面命令&#xff1a;rpm -qa|grep -i mysql如果顯示有包則說明已安裝mysql第二步、如果已安裝&#xff0c;則需要刪除已安裝的數據庫可按以下步驟刪除數據庫&#xff1a;刪除包命令&#xff1a;rpm -e --nodeps 【包名】rpm -e --nodeps …

mysql分頁查詢關鍵_MySQL優化教程之超大分頁查詢

背景基本上只要是做后臺開發&#xff0c;都會接觸到分頁這個需求或者功能吧。基本上大家都是會用MySQL的LIMIT來處理&#xff0c;而且我現在負責的項目也是這樣寫的。但是一旦數據量起來了&#xff0c;其實LIMIT的效率會極其的低&#xff0c;這一篇文章就來講一下LIMIT子句優化…

mysql r_R之RMySQL

linux&#xff0c;mysql和R的版本信息&#xff1a;Linux naci 3.19.0-16-generic #16-Ubuntu SMPServer version: 5.6.24-0ubuntu2 (Ubuntu)R version 3.1.2 (2014-10-31) -- "Pumpkin Helmet"mysql的linux安裝&#xff0c;參照上一篇關于liunx下安裝mysql的文章。1.…

mysql 字符轉換函數是_MySQL日期和字符串轉換函數

Mysql中to_char()和str_to_date()函數轉載路徑&#xff1a;https://blog.csdn.net/ricardo_mli/article/details/802175121.字符串轉換成日期格式str_to_date(date,’%Y-%m-%d’)----->相當于Oracle中的to_char();例子&#xff1a;INSERT INTOt_order(order_time)VALUES(str…

mysql數據加百分號_使用MySQL SELECT語句時,在每個值的末尾添加一個百分號(%)...

要在末尾添加百分號&#xff0c;請使用CONCAT()函數。讓我們首先創建一個表-mysql> create table DemoTable(StudentId int NOT NULL AUTO_INCREMENT PRIMARY KEY,StudentName varchar(100),StudentScore int);使用插入命令在表中插入一些記錄-mysql> insert into DemoTa…