第3.4節 調用鏈路分析服務開發

3.4.1 什么是Code Call Graph(CCG)

Code Call Graph(CCG)即業務代碼中的調用關系圖,是通過靜態分析手段分析并構建出的一種描述代碼間關系的圖。根據精度不同,一般分為類級別、方法級別、控制流級別,本文重點在方法級別上。
我們以一段代碼進行舉例:
class A {public void funA1() {funA2();C c = new C();c.funC1();}public void funA2() {B b = new B();b.funB1();}
}class B {public void funB1() {funB2();}public void funB2() {if (randN(10) < 5) {Logger.log("Hello B2");} else {funB2()}}
}class C {public void funC1() {B b = new B();b.funB2();}
}

如上代碼所構建出的方法級別的CCG是這樣的:

1,CCG的作用主要有兩個

假設當我們出現一個需求改動到
1,funB1該方法時,我們可以從該圖上進行逆向查找,找到所有直接調用或者間接調用該方法的所有方法A2,A1,這個代表對B2的改動,會影響到A2,A1,{B1, A2, A1}即方法B2的代碼影響域。
2,在單元測試場景下,如果某個測試用例
testX是針對funC1的測試,那么我們可以從該方法上進行正向查找,找到所有它直接調用或者間接調用的方法B2,這個代表,我的測試用例testX的執行后可以測試到方法C1, B2,{B2, C1}即用例testX的關聯代碼。

2,CCG的應用場景

除了可以應用在精準測試場景下之外,還能在如下場景應用:
1,app啟動或頁面啟動場景下的性能分析與性能優化:當我們要進行某個場景下的耗時優化時,我們可以從幾個核心入口函數如android的下的application.onCreate(),application.onBaseContextAttached()的方法作為起點,查找后續調用方法,獲知在整個啟動流程里,哪些方法通過什么方式被執行了,幫助判斷這種執行是否是啟動場景下必須執行的任務。
2,組件化解耦:當我們需要判斷兩個組件間的耦合關系時,我們可以以其中一個組件中的方法作為起點,查找調用鏈上是否有另一個組件的方法,來尋找兩個組件間的詳細耦合關系,幫助后續進行解耦。相比傳統靜態分析方案,CCG可以更準確高效的查找出非直接依賴的隱性耦合。

3.4.2. CCG構建業界方案一覽

目前業界有一部分相對完成度比較高的開源callgraph或者AST生成方案:

Android/Java

1,soot/wala等靜態代碼分析框架:GitHub - soot-oss/soot: Soot - A Java optimization framework,soot是比較完善的靜態代碼分析框架,從能力設計上都符合我們的需求,但是soot本身是一個通用性框架,沒有專門為call graph場景去設計,比如匿名內部類,Runnable/Callable/Thread等線程類,lambda表達式,Stream調用,泛型處理等等,這些都需要我們去對soot做定制才能達到我們的需求。此外僅針對callgraph生成場景,soot設計是過復雜的,導致對于百萬級方法節點的處理性能并不足夠好。

2,java-all-call-graph: GitHub - Adrninistrator/java-all-call-graph: Generate all call graph for Java Code.,這個項目是一個比較簡單的基于class字節碼分析生成callgraph的方案,解決了soot的各種缺失能力,同時在處理性能上要優于soot。但是仍然存在一定缺陷,比如無法支持使用Redux框架進行開發的代碼,反射,廣播等場景。

iOS/Objc-c, swift

1,Drafter: GitHub - L-Zephyr/Drafter: 在iOS項目中自動生成類圖和方法調用圖 - Generate call graph in iOS project,Drafter是一個簡單的語法+詞法分析器,由于不帶語義信息,只能支持單個類下的call graph生成,不符合我們的需求。
2,libTooling:官方工具,獨立AST生成工具,libTooling可以生成一個完整的帶語義分析的AST,我們可以基于該AST來生成所需的call graph,但是libTooling的性能非常差(需要為每個文件或者模塊生成編譯參數,并且無法應用各種編譯優化),在全量情況下快手app的call graph生成耗時達到數小時,增量情況下一個500行文件的生成耗時達到幾十秒,對于大型mr無法承受。
3,Clang Plugin:官方工具,集成進編譯流程中的AST生成插件,clang plugin方案通過集成在編譯流程里,目標產物為語義AST,由于可集成在編譯流程中,我們可以復用包括gundum在內的各種編譯優化手段,在增量情況下每個文件的生成耗時可以降到秒級,全量情況下為十幾分鐘。但是clang plugin只能支持oc代碼,并且我們無法直接將打包集群的編譯環境替換掉,因此我們需要在clang plugin基礎上搞定swift/c++/c的支持,以及跨語言構建問題。同樣的,我們還需要支持泛型、代理、redux、廣播、KVO、oc runtime等特殊場景。

3.4.3 現在的CCG整體架構

CCG服務提供了Android,IOS調用釧的生成,序列化保存,查詢等相關功能,以及對git diff獲取diff函數的相關功能和接口。

3.4.4 . 流水線上CCG服務構建與更新流程

隨著代碼的改動,CCG需要同步更新,因此CCG服務需要與流水線深度關聯。CCG服務主要分為3個階段:

1,全量構建階段

CCG全量構建基于定時觸發,每隔固定時間(目前為24小時),CCG服務平臺會觸發一次雙端全源碼包構建請求,完成一次全量CCG構建,流程如下
如前文提到,CCG構建時需要使用特定jenkins腳本構建相關產物,獲得產物后通過相關分析腳本得到完整版CCG:
生成出的完整版本CCG大概長上面這個樣子,每個節點代表一個方法,我們需要存儲該方法本身信息,其所屬函數、參數列表,指向的前序與后序方法節點。對于一個超大型應用而言,我們可能有幾百萬個方法節點,這種存儲方式最后得到的CCG產物十分龐大,內存占用達到幾GB,顯然這對內存、磁盤甚至CPU都是很大的負擔(一個CCG服務器上需要同時維護多份CCG)。
因此我們在構建出全量CCG后,引入了CCG壓縮流程,壓縮后的CCG會被分成兩部分:CCG-Node Map,CCG-Meta DB。壓縮后的每個節點上只存儲了該Node的hash值,并以此hash值作為key,構建meta DB,存儲詳細信息。在后續查詢時,我們從Node Map中拿到對應的hash list后再從數據庫中做一次sql查詢,即可得到完整信息。這種方案也有利于獲取擴展更多的節點信息,比如我們要增加線上用戶熱度圖(見5.5)信息時,只需要在meta DB中插入即可。
全量構建完成的CCG我們稱之為Base CCG,會以commitID作為版本號進行持久化。

2. 更新階段

由于每個mr提交后都會改變局部CCG,因此我們需要引入實時CCG更新方案。我們選擇引入git webhook監聽所有mr merge操作,當一個mr合并入主分支后(dev分支),會觸發CCG更新機制。整個更新機制分成兩種平臺,四種場景:
Android(復用產物分析)
場景1. 如果mr有新增/更新/刪除單元測試case,一定會觸發單元測試節點,此時我們根據mr diff與已經打好的單元測試包做一次增量分析,得到增量CCG
場景2. 如果mr沒有相關單元測試case改動,我們根據mr diff與已經打好的編譯檢查包做一次增量分析,得到增量CCG
場景3. 如果因為各種原因沒有匹配的編譯檢查包,我們需要觸發一次jenkins debug包打包,再結合mr diff進行增量分析,得到增量CCG
iOS(無法使用產物結果,需要重新進行語義AST分析)
場景1. iOS場景下,在mr merge觸發后,會直接觸發jenkins打包服務,構建語義AST,與全量構建場景不同的是,這種場景下只會觸發增量編譯,因此語義AST構建只針對mr diff中的增量文件進行觸發(目前主站增量編譯是pod級緩存,AST構建也是pod級,當文件級緩存上線后,AST構建也將變成文件級)。
增量更新的CCG我們稱之為Diff CCG,以mrId + 最新commitId作為索引值持久化,該CCG唯一綁定某個版本的Base CCG(取決于基于哪個base版本進行的diff),并存儲指向對應版本的Base CCG的文件指針。
Diff CCG尋找綁定Base CCG的算法可以簡化描述為:從提交mr對應的開發分支上向前尋找到最近的與dev分支的共同祖先節點,以這個節點commitId作為基準值,再向前尋找最鄰近的關聯有Base CCG的commitId,該Base CCG即目標CCG。
Question1. 為什么可以使用開發分支上的編譯產物獲取增量CCG并合入dev分支后的CCG?
事實上CCG的merge操作和git代碼的merge操作是類似的,由于開發分支合并入dev分支時,代碼層面一定不存在沖突,因此我們可以保證CCG merge時也不存在沖突。
另一方面對于代碼層面的merge,最終可以歸類為三種情況:add method,change method,delete method。這幾種情況,反應到CCG上對應于添加一個方法節點,修改某個方法節點的出邊,刪除一個方法節點,可以實現一一對應。
因此我們在開發分支上獲得增量CCG可以與當前mr的diff代碼保證一一對應,merge進主干分支的CCG上時也等價于mr merge進主干分支。
Question2. 如果CCG更新太慢,后續mr所基于的dev分支代碼已經領先于最新CCG會出現什么后果?
由于指向dev分支的merge操作是保證原子時序性的(不會出現兩個merge操作并發執行),因此我們對于git merge的webhook也是時序性,在CCG更新操作上我們采用了同步非阻塞設計,即當出現一次merge操作后,我們觸發更新操作,該更新操作會被push到執行隊列中并立刻返回,執行隊列是一個順序的任務隊列,保證前一個更新任務完全完成后,后一個才會執行。
當出現一個查詢任務時,如果該查詢任務所基于dev分支節點的CCG還未更新完成,為了避免阻塞等待,我們會使用最鄰近CCG進行查詢,這會帶來一定程度的誤差(事實上這種誤差可以忽略,大多數情況下不會存在兩個mr在很短的時間內去更新同一個功能模塊)。

3,查詢階段

用戶提交mr后,會通過流水線觸發代碼影響域查詢服務,輸入為mr diff文件,輸出為受影響的方法list,以及相關權重信息。
  1. 查詢可以分為兩個階段:
  2. mr diff分析找到所有改動方法
根據改動方法,在CCG上找到所有受影響的方法
MR Diff分析
MR分析階段,我們會根據mr diff信息使用前置分析器找到變動方法,為了確保分析性能(MR分析階段需要足夠快,否則會影響整個流水線的執行速度),我們引入/自研了高性能的詞法語法分析器作為我們的前置分析器,可以非常快的構建出一棵不帶語義信息的AST。關于前置分析器的具體實現細節可見3.4、3.5節。
CCG查詢階段
拿到變動方法列表后,我們還無法直接進行查詢,因為在CCG平臺上存儲的是一系列Base CCG和Diff CCG,我們需要找到并構建出我們的mr所匹配的CCG。
考慮如下的一個Git分支模型:
我們從Dev分支拉出Task分支后,當我們第一次提交mr到流水線上時(圖中create MR),我們的CCG服務會基于該mr的最新commit(紅色分支第二個節點)向前尋找最近一次checkout/merge/rebase Dev分支的節點(綠色第二個節點),找到該節點后,向CCG持久化服務中搜索對應的CCG圖,此時我們找到了MR1-1 CCG,這是一個Diff CCG,該Diff CCG中存儲了它依賴的Base CCG指針,然后對這兩個CCG進行一次merge操作,即得到了我們想要的CCG(即CCG v1),在該圖上即可進行后續的查詢服務。當我們后續有新的commit提交到MR上時,重復上述操作即可獲得新的CCG版本。
在具體查詢上,我們根據變動類型將查詢分為三類:
  1. 新增方法:新增方法不會影響其它方法,并且在沒有匹配的新增case時,該新增方法也不會存在關聯存量用例,而對于新增用例的場景,這些用例無論怎么樣都會直接推薦,因此在CCG階段直接忽略新增方法
  2. 刪除方法:刪除方法不會影響其它方法,也不會對測試產生影響,直接忽略
  3. 變更方法:變更方法是我們唯一需要進行查詢的場景,我們以變更方法作為起始節點,向前追溯該方法的所有前序節點,即可獲得對應變更方法的代碼影響域,為了提供更多信息,我們會引入更多的權重信息來輔助后續的推薦策略,在第五章中會作詳述。
至此整個CCG服務完成,作為一個單獨的服務,為整個精準測試平臺提供調用鏈路查詢和分析相關功能。

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

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

相關文章

【Liblib】基于LiblibAI自定義模型,總結一下Python開發步驟

一、前言 Liblib AI&#xff08;哩布哩布 AI&#xff09;是一個集成了先進人工智能技術和用戶友好設計的 AI 圖像創作繪畫平臺和模型分享社區。 強大的圖像生成能力 &#xff1a;以 Stable Diffusion 技術為核心&#xff0c;提供文生圖、圖生圖、圖像后期處理等功能&#xff…

編程日志5.5

樹的結構代碼 #include<iostream> using namespace std; //由于樹的每個結點可能有一些孩子結點,這些孩子結點的數量不確定,所以可以用一個鏈表來把所有的孩子結點給串起來 //鏈表結點定義 //這段代碼定義了一個結構體ListNode,用于表示鏈表中的一個結點。這個結構…

【消息隊列】RabbitMQ基本認識

目錄 一、基本概念 1. 生產者&#xff08;Producer&#xff09; 2. 消費者&#xff08;Consumer&#xff09; 3. 隊列&#xff08;Queue&#xff09; 4. 交換器&#xff08;Exchange&#xff09; 5. 綁定&#xff08;Binding&#xff09; 6. 路由鍵&#xff08;Routing …

uniapp -- 驗證碼倒計時按鈕組件

jia-countdown-verify 驗證碼倒計時按鈕組件 一個用于發送短信驗證碼的倒計時按鈕組件&#xff0c;支持自定義樣式、倒計時時間和文本內容。適用于各種需要驗證碼功能的表單場景。 代碼已經 發布到插件市場 可以自行下載 下載地址 特性 支持自定義按鈕樣式&#xff08;顏色、…

知識圖譜重構電商搜索:下一代AI搜索引擎的底層邏輯

1. 搜索引擎的進化論 從雅虎目錄式搜索到Google的PageRank算法&#xff0c;搜索引擎經歷了三次技術躍遷。而AI搜索引擎正在掀起第四次革命&#xff1a;在電商場景中&#xff0c;傳統的「關鍵詞匹配」已無法滿足個性化購物需求&#xff0c;MOE搜索等新一代架構開始融合知識圖譜…

深度學習 自然語言處理(RNN) day_02

1. 感知機與神經網絡 1.1 感知機 生物神經元&#xff1a; 1.1.1 感知機的概念 感知機&#xff08;Perceptron&#xff09;&#xff0c;又稱神經元&#xff08;Neuron&#xff0c;對生物神經元進行了模仿&#xff09;是神 經網絡&#xff08;深度學習&#xff09;的起源算法&am…

PYTHON訓練營DAY25

BUG與報錯 一、try else try:# 可能會引發異常的代碼 except ExceptionType: # 最好指定具體的異常類型&#xff0c;例如 ZeroDivisionError, FileNotFoundError# 當 try 塊中發生 ExceptionType 類型的異常時執行的代碼 except: # 不推薦&#xff1a;捕獲所有類型的異常&…

LU分解求解線性方程組

L U LU LU分解 前言 L U LU LU分解 由以下定理得以保證&#xff1a; 設 A \boldsymbol{A} A為 n n n階方陣&#xff0c;若其各界階順序主子式都不為 0 0 0&#xff0c;那么它可以 被唯一的上下三角矩陣積分解。 步驟 確定各矩陣形式 A L U \mathbf{A}\mathbf{LU} ALU ( a 1…

Linux——數據庫備份與恢復

一&#xff0c;Mysql數據庫備份概述 1&#xff0c;數據庫備份的重要性 數據災難恢復&#xff1a;數據庫可能會因為各種原因出現故障&#xff0c;如硬件故障、軟件錯誤、誤操作、病毒攻擊、自然災害等。這些情況都可能導致數據丟失或損壞。如果有定期的備份&#xff0c;就可以…

SVM在醫療設備故障維修服務決策中的應用:策略、技術與實踐

SVM在醫療設備故障維修服務決策中的應用&#xff1a;策略、技術與實踐 醫療設備的高可靠性、安全性及嚴格合規性要求&#xff0c;使其故障維修決策具有顯著的特殊性。支持向量機&#xff08;SVM&#xff09;憑借小樣本學習、非線性建模及高精度分類能力&#xff0c;可有效解決…

WEB安全--Java安全--CC1利用鏈

一、梳理基本邏輯 WEB后端JVM通過readObject()的反序列化方式接收用戶輸入的數據 用戶編寫惡意代碼并將其序列化為原始數據流 WEB后端JVM接收到序列化后惡意的原始數據并進行反序列化 當調用&#xff1a; ObjectInputStream.readObject() JVM 內部邏輯&#xff1a; → 反…

FlashInfer - 介紹 LLM服務加速庫 地基的一塊石頭

FlashInfer - 介紹 LLM服務加速庫 地基的一塊石頭 flyfish 大型語言模型服務中的注意力機制 大型語言模型服務&#xff08;LLM Serving&#xff09;迅速成為重要的工作負載。Transformer中的算子效率——尤其是矩陣乘法&#xff08;GEMM&#xff09;、自注意力&#xff08;S…

反向操作:如何用AI檢測工具優化自己的論文“人味”?

大家好&#xff0c;這里是論文寫手的一線自救指南&#x1f624; 在AIGC橫行的今天&#xff0c;誰還沒偷偷用過AI寫幾段論文內容&#xff1f;但問題來了&#xff1a;學校越來越會“識AI”了&#xff01; 有的學校甚至不看重復率&#xff0c;只盯AIGC率報告&#xff0c;一句“AI…

關于單片機的基礎知識(一)

成長路上不孤單&#x1f60a;&#x1f60a;&#x1f60a;&#x1f60a;&#x1f60a;&#x1f60a; 【14后&#x1f60a;///計算機愛好者&#x1f60a;///持續分享所學&#x1f60a;///如有需要歡迎收藏轉發///&#x1f60a;】 今日分享關于單片機基礎知識的相關內容&#xf…

AWS技術助力企業滿足GDPR合規要求

GDPR(通用數據保護條例)作為歐盟嚴格的數據保護法規,給許多企業帶來了合規挑戰。本文將探討如何利用AWS(亞馬遜云服務)的相關技術來滿足GDPR的核心要求,幫助企業實現數據保護合規。 一、GDPR核心要求概覽 GDPR的主要目標是保護歐盟公民的個人數據和隱私權。其核心要求包括: 數…

FFplay 音視頻同步機制解析:以音頻為基準的時間校準與動態幀調整策略

1.?視頻同步基礎 1.2 簡介 看視頻時&#xff0c;要是聲音和畫面不同步&#xff0c;體驗會大打折扣。之所以會出現這種情況&#xff0c;和音視頻數據的處理過程密切相關。音頻和視頻的輸出不在同一個線程&#xff0c;就像兩個工人在不同車間工作&#xff0c;而且不一定會同時…

車載網關--- 職責邊界劃分與功能解耦設計

我是穿拖鞋的漢子,魔都中堅持長期主義的汽車電子工程師。 老規矩,分享一段喜歡的文字,避免自己成為高知識低文化的工程師: 鈍感力的“鈍”,不是木訥、遲鈍,而是直面困境的韌勁和耐力,是面對外界噪音的通透淡然。 生活中有兩種人,一種人格外在意別人的眼光;另一種人無論…

最優化方法Python計算:有約束優化應用——近似線性可分問題支持向量機

二分問題的數據集 { ( x i , y i ) } \{(\boldsymbol{x}_i,y_i)\} {(xi?,yi?)}&#xff0c; i 1 , 2 , ? , m i1,2,\cdots,m i1,2,?,m中&#xff0c;特征數據 { x i } \{\boldsymbol{x}_i\} {xi?}未必能被一塊超平面按其標簽值 y i ∈ { ? 1 , 1 } y_i\in\{-1,1\} yi?∈…

aardio - 將文本生成CSS格式顯示

import win.ui; /*DSG{{*/ var winform win.form(text"aardio form";right759;bottom469) winform.add( button{cls"button";text"Button";left340;top130;right430;bottom180;z3}; edit{cls"edit";text"我是一串文本";lef…

數字IC后端設計實現 | 如何自動刪除Innovus 中冗余的hold buffer?

我們都知道在postCTS階段做optDesign時序優化時需要進行hold violation的fixing。所以這個過程勢必要通過插hold buffer來解決hold violation。這類hold buffer的名字帶有"PHC"的關鍵詞。 select_obj [dbGet top.insts.name PHC] llength [dbGet top.insts.name PH…