天天看點

一種檢測和測試容器高可用線程卡頓的技術(Android)

作者:閃念基因

線程卡頓的問題已經成為很多app産品的重大關注對象,市場上也出現了很多相關的技術探索,如Raster、Sliver、BlockCanary、Matrix、Dokit、ANR-WatchDog、Profilo、Nanoscope...但都較為局限或不适用我們實際的場景。

每個業務都有自己的特性,市場上大多數的技術都是一種很通用的技術形式,解決是common的問題,但是如果我們想突破瓶頸,就需要結合業務場景結合已有技術繼續深挖研究出新的技術模型。

本文章主要介紹了我們如何站在巨人的肩膀上去更準确的檢測到卡頓的根因慢函數;又如何把自動化測試緊密的結合起來形成一套完整的線程卡頓的卡口體系。

一. 線程卡頓的根因分析

1.1 首先明确卡頓和ANR的差別

卡頓:是指程式運作慢導緻目前使用不流暢

ANR:在沒有特定的自定義技術實作的情況下一般是指因主線程耗時超過了系統設定的閥值而抛出的系統anr導緻目前應用不可用的現狀

兩者聯系:卡頓是發生ANR的前兆,卡頓造成的卡死必然引起ANR,兩者表現形式背後的技術原理都是一樣的。

1.2 ANR的技術根因

應用程式的主線程響應逾時會導緻ANR,由AMS進行通知且發生在四大元件:Service、Activity、ContentProviver,BroadcastReceiver

下面以Service為例,去了解ANR的前世今生,不難猜測任何機制的觸發一定要經過:機制預埋、機制觸發、結果響應。

1.2.1 機制如何預埋?

一種檢測和測試容器高可用線程卡頓的技術(Android)

1.關鍵點一,timeoutneed預設是true代表是需要在此監測的,但是隻有在三方服務還未啟動時不需要檢測,三方服務一般是系統服務,會先于使用者服務啟動。

2.關鍵點二就是真正的anr監測逾時的設定了。

一種檢測和測試容器高可用線程卡頓的技術(Android)

1.其實很簡單,就是進入一個消息隊列發送一個延時的消息。

2.兩個時間點在代碼裡可以看到如果service在前台是20s,如果處于背景是100s。也就是說兩種場景下在有限的時間内沒執行完就會發生ANR。

1.2.2 機制如何觸發?

一種檢測和測試容器高可用線程卡頓的技術(Android)

1.以上标紅的地方是service的ANR預埋的時間到了之後message觸發的地方。

2.這個時候還不知道是否發生了ANR,隻是說時間到了去檢測一下ANR是否發生。

1.2.3 結果如何響應?

1.關鍵1很明顯是代表有沒有ANR的辨別。

2.關鍵2就是核心,給ANR的辨別指派。

3.所有正在執行的service都會進入AMS的 mLruProcesses這個隊列裡,同時每個service的基本資訊會在ServiceRecord裡記錄,executingStart記錄的就是service真正啟動時的時間可以看ActiveServices的realStartServiceLocked的方法内的這一句。

一種檢測和測試容器高可用線程卡頓的技術(Android)

4.用目前的時間減掉我們ANR的預埋時間,代表的就是service最早的啟動時間,如果比這個還早那就代表ANR了,是以有了

一種檢測和測試容器高可用線程卡頓的技術(Android)

這個判斷。

5.最後根據timeout這個辨別和正在運作的process裡是否包含此service來證明确實發生了ANR。

6.在關鍵2會看到如果發生ANR後了,會把堆棧資訊收集手釋放掉堆棧資訊的一些操作也是為了性能考慮代表此次ANR監聽采集完畢。

7.最後根據anrMessage這個錯誤進行系統通知,接下來就是會看到系統彈出的無響應界面。

1.2.4 總結ANR根因流程

一種檢測和測試容器高可用線程卡頓的技術(Android)

1.以Service為例就是在Service真正啟動的時候記錄啟動時間,同時通過一個Handler發送一個滿足ANR延時的消息,在預期的延時資訊處理時,去check正在運作的service的開始執行時間是否遭早于ANR發生的最早時間,如果比ANR預期的時間還早說明執行超過了預期時間,會直接上報系統産生ANR,鎖住程序。

2.另外,service的執行也是隊列的形式,一個執行完會把執行的時間設定成預期的下一個執行時間,根據exectuingStart和nextTime進行執行。

主線程阻塞、挂起、死循環、執行了比較耗時的操作等;

:其他程序對CPU的使用占用時間過高導緻應用程序搶不到CPU的時間片。

三.我們的痛點

3.1 現狀

根據以上的實際案例我們也發現,雖然我們版本釋出前進行了嚴格的測試包括格魯特穩定性測試,以及也分析了微鏡5s和10sANR的原理但是仍舊攔不住我們的卡死的問題。

通過我們的實際的案例分析以及我們對目前卡頓測試手段的分析,我們得出以下原因和痛點:

一種檢測和測試容器高可用線程卡頓的技術(Android)

3.2 痛點明細

  1. 目前已有的線程卡頓檢測技術隻能檢測到目前Message耗時但存在目标堆棧偏移問題更不能回放卡頓前函數狀态,比如ANR-WatchDog和blockCanary。
  2. 微鏡的5s和10s的ANR攔截就是Watch-Dog的原理,但是缺點一:不能線下測試;缺點二:堆棧偏移且不夠準确導緻分析問題困難,解決成本太高。
  3. ANR問題的修複驗證困難且驗證周期長,目前完全依賴線上問題是否暴露來說明是否修複。
  4. 沒有針對性的測試case和線下自動化卡口測試,使其問題流到線上造成風險治理偏右。
  5. 沒有基線,單純的看和上個版本的ANR率不能細分版本的劣化的根因,解決問題處在拆東牆補西牆的漩渦。
  6. 沒有一套完整完整的防劣化系統,導緻我們無法很好的攔截新生問題使其anr以及卡頓的問題反複糾纏我們。

四.技術實作說明

4.1 技術方向和特性

業界所有的穩定性技術無非分兩派:一是技術識别方向;二是測試場景覆寫方向;

一種檢測和測試容器高可用線程卡頓的技術(Android)
  1. 識别分析方向一般是假設場景發生和存在的情況下,有足夠的能力去搜集和分析發生的問題。
  2. 識别分析方向的能力一般會聚焦:如何能做到顆粒度更細的記憶體配置設定,堆棧的精準擷取,關鍵節點的耗時精準捕捉等技術專項,它的基石是技術創新。
  3. 測試自動化方向則是識别分析方向的能力落地,測試自動化方向更專注的是:測試提效能力,全面發現問題的能力,它的基石是技術測試用例的設計和高階白盒的技術創新。

兩個方向都是業界比較關注的方向,但是穩定性的治理單憑一個方向是不夠的,可以說兩者缺一不可,因為再牛的技術需要落地場景,是以我們針對線程的卡口做了一整套;

是以線程卡頓的卡口的特性圍繞四個能力如展開技術識别能力、技術驗證能力(自動化測試能力)、技術分析定位能力以及實驗室能力。

一種檢測和測試容器高可用線程卡頓的技術(Android)

4.2 技術架構

主要包含幾個子系統:函數級别的識别系統,核心技術子產品插樁系統,基線Case執行系統,卡口解析和識别系統,卡口自動化适配和分析系統,卡口基線資料生産系統,卡口資料劣化分析系統。

一種檢測和測試容器高可用線程卡頓的技術(Android)

4.3 技術識别能力

技術方案:方案是通過監控單Message、多Message、單函數、容器對外擴充實作的方式對運作期的代碼進行全面監控。

4.3.1 多Message耗時監控原理

我們都知道android的所有執行都是handler和looper的那套機制,最終都是通過message一個個去執行,如果目前message被阻塞就必然會引起卡頓(如果實在主線程),多Message的耗時原理可以以目前支付寶使用的這套類似ANR-WatchDog來分析,核心代碼:

一種檢測和測試容器高可用線程卡頓的技術(Android)

  1. 安排一個可運作對象盡快在 UI 線程上運作。
  2. 等待 5 秒鐘。(5 秒是預設值,但可以配置)。
  3. 檢視runnable是否已經運作。如果有,請傳回 1。
  4. 如果 runnable 尚未運作,這意味着 UI 線程已被阻塞至少 5 秒,則會引發所有正在運作的線程堆棧跟蹤的錯誤

問題1:ANR監控過程中可能會包含多條Message,因為設定的門檻值是5s。像這類其中一個Message耗時高達4.5s的慢消息,因為機制原因不會被檢測出來。

一種檢測和測試容器高可用線程卡頓的技術(Android)

問題2:ANR監控周期内涉及3個Message,Message耗時高達4.9s,Message3耗時隻有150ms,按照ANR-WatchDog堆棧采集機制,會定位到Message3上,單Message3并不是真正造成此次ANR的原因。

一種檢測和測試容器高可用線程卡頓的技術(Android)

4.3.2 單Message耗時監控原理

目前我們通過在Looper處理Message前後做卡頓時間判斷

一種檢測和測試容器高可用線程卡頓的技術(Android)

問題1:ams關鍵消息(ams會做anr監控)前面有11個消息,每個消息耗時30ms不是很長,但是合在一起就是330ms,超過了AMS Input ANR 5s監控了。是以這類問題如果你不知道Message之前發生了什麼,基本很難定位問題。

一種檢測和測試容器高可用線程卡頓的技術(Android)

問題2:最後堆棧采集也可能會存在輕微堆棧偏移問題,盡管一般都是從卡頓門檻值80%時開始采集堆棧,最後做個TopOne篩選再記錄。

問題3:單Message卡頓監控過程中會包含多個方法調用,因為設定的門檻值是200ms。像這類其中一個方法耗時高達195ms的慢方法,因為機制原因不會被檢測出來。

一種檢測和測試容器高可用線程卡頓的技術(Android)

問題4:因為該耗時監聽原理,基本都是代理現有print,然後自定義自己邏輯後再設定給Looper。因為自定義邏輯通常都是根據字元串來判斷一個Message執行開始和結束,是以存在字元串拼接和銷毀問題。

4.3.3 我們的技術解決方案

我們分析了市場上的技術方案存在的不完整性也是導緻我們上面痛點講到的問題一旦發生解決成本高且不好定位的一個根本的技術原因。

了解到以上各個方案的技術原理後,解決這種技術風險帶來的痛點是我們必須要做的,是以我們的技術方案是結合我們業務特性柔和了以上技術,通過堆棧追溯、單函數插樁、關卡設定來解決以上技術風險進而解決技術風險帶來的堆棧不準确分析問題的難的痛點。

一種檢測和測試容器高可用線程卡頓的技術(Android)

通過以上方法我們的檢測結果如:

一種檢測和測試容器高可用線程卡頓的技術(Android)

4.4 技術驗證能力

目前本地檢測能力有整體

  • 卡頓
    • 核心是挖掘Handler特性,通過對單/多Message及相關屬性的多元度分析,推斷出卡頓風險點。
    • 檢測能力理論上大于端上檢測能力,主要原因是線下使用,無需過多擔心相容性問題。
  • 慢函數
    • 目前對慢函數檢測并非是對線上所有函數都攔截計時,這樣的成本太大。
    • 一是統計成本太大(侵入代碼,計算耗時),二是聚合成本太大(調用鍊串聯與過濾)。
    • 目前采用的是堆棧定時采集再聚合堆棧的方式,聚合完堆棧再單獨計算各函數耗時。
  • 擴充
    • 線上:當發生ANR時,沒有一條消息是無辜的。各種的擴充實作也是同樣如此,分析一類ANR問題時,我們通常需要具體化這類ANR所涉及的各擴充的詳細耗時情況,才能給出優化政策。是以線上積攢着很多無解的ANR,這些ANR如果不采取特殊手段,但從現有資訊确實無解。
    • 線下:線下我們有自動化卡口能力,其中包含各擴充的檢測。但畢竟自動化測試的場景和機型有限,無法和線上真實大資料相比,是以同樣我們着手建立起了線上擴充監控能力。
  • IPC
    • 目前是對耗時與傳輸大小進行統計進行實作。
    • 通過ARTHook技術,對系統/業務代碼進行Hook,實作IPC資訊采集與盡可能的無侵入內建。

4.4.1 驗證什麼?

這裡主要是驗證其核心能力,更多的邊緣能力不在此文章中做詳細闡述;需要驗證的核心能力主要有以下幾個:

  1. 多message的情況下是否能檢測到慢函數且堆棧資訊正确
  2. 單message的情況下是否能檢測到慢函數且堆棧資訊正确
  3. 正常ANR的情況下是否能檢測到且堆棧資訊正确
  4. 真實的線上版本是否能攔截到卡口規則的問題

4.4.2 如何驗證?

一種檢測和測試容器高可用線程卡頓的技術(Android)

4.5 技術分析定位能力

4.5.1 卡口規則如何設計

設定三層關卡,層層深入,每一層都定義出自己的能力,如下:

一種檢測和測試容器高可用線程卡頓的技術(Android)

4.5.2 卡口技術的實作

1、 KEY值:每個卡頓點KEY值定義規則。

  • 首先我們需要給每個卡頓問題提煉出一個KEY值作為該問題的ID值;
  • 既利用這個KEY值來聚合該卡頓問題出現了多少次,平均耗時是多少等資料;
  • 卡頓問題的KEY值規則為:大于根節點1/2耗時的最後一個節點鍊路為此卡頓的KEY;
  • 如下圖的卡頓KEY值為:A->B->E。
一種檢測和測試容器高可用線程卡頓的技術(Android)

2、慢函數類型:

  • 卡頓标準:單方法耗時超出門檻值且下方無子堆棧。
  • 自動化分析會把此次卡頓中典型慢函數單獨分析出來,如下方的CRVUtils.loadSo問題。
  • 此類問題一般是鎖等待耗時、native函數調用耗時等,既如果多次超出門檻值,一定是非正常耗時。
一種檢測和測試容器高可用線程卡頓的技術(Android)

4.5.3 用例如何設計

:業務了解、技術原理、實際問題分析、用例的分類設計、設計的方法、用例的可執行性和有效性。

一種檢測和測試容器高可用線程卡頓的技術(Android)

  • 目标:假設想挖掘ANR的潛在風險
  • 業務對象:容器
  • 測試環境:機型是Redmi Note4x-3G記憶體 && 獨立實驗室
  • 詳細設計:
    • STEP1: 首先來了解業務,針對容器的業務特性直接操縱的宏觀場景是小程式運作或者是h5的運作。
    • STEP2:了解ANR的技術原理,主因是主線程阻塞、挂起、死循環、執行了比較耗時的操作等;他因是其他程序對CPU的使用占用時間過高導緻應用程序搶不到CPU的時間片。
    • STEP3:找個線上的例子實際分析,比如螞蟻森林卡頓是因為線程阻塞導緻(整體分析下來業務存在分技術風險點:1.線程池管理不統一,存在多個線程池管理 2.單線程操作耗時)。
    • STEP4:這種用例若設計該如何分類?我暫時定為典型性業務的技術類case。
    • STEP5:進行用例設計的方法選擇,根據以上分析業務特性很明顯适合黑盒的壓測或者是特定的業務路徑設計;而技術風險case适合白盒的case進行技術邊界的壓測。
    • STEP6:綜合以上用例基本可以定型了,可執行和有效性我定義為-用最少的case覆寫盡可能最多的路徑,否則就會造成備援case隻有數量沒有品質。
  • 用例明細:
    • :可以吸收多種場景的黑盒case

1. 驗證程序之間是否有影響

2. 驗證交叉case對程序和線程排程的影響,去盡量命中技術邊界,挖掘技術風險類問題

3.驗證壓測的熱啟動是否存在記憶體洩漏

4.快速點選事件去交叉驗證線程的響應和應用的響應速度,去盡量命中邊界卡頓的場景

  • :壓測20000067預熱小程式10次,每一次啟動後靜置10s後依次啟動top10的業務小程式執行monkey,其中前五次是支付寶冷啟動且執行次數為偶數的時候monkey模拟正常點選且執行10min,後五次是支付寶熱啟動且執行次數為偶數的時候模拟使用者快速點選且執行30min;:(可一次執行n次,n個預熱小程式以及n個top小程式,n:使用者自定義)
  • Demo用例執行效果:
一種檢測和測試容器高可用線程卡頓的技術(Android)

4.5.4 如何實作自動化測試

整體實作是通過java工程,實作基線Case執行系統,卡口解析和識别系統,卡口自動化适配和分析系統,卡口基線資料生産系統,卡口資料劣化分析系統五個子系統,具體代碼實作就不粘貼了;

一種檢測和測試容器高可用線程卡頓的技術(Android)
  • Shell腳本:負責用例的執行
  • Java:負責整個資料的流通和分析
  • 動态Html:負責資料的展示
  • 通信實作:Java調用Shell,腳本執行完成通知Java;Java資料處理完成後通知Html;Html若需要發生資料行為需通知Java;各負其責互相解決耦合;
  • 技術架構原型:MVP

4.5.5 如何自動化分析問題

4.5.5.1 通用blockfile的卡口聚合

一種檢測和測試容器高可用線程卡頓的技術(Android)

4.5.5.2 其他技術子產品的卡口實作

一種檢測和測試容器高可用線程卡頓的技術(Android)

4.6 實驗室能力

實驗室亮點

  • 【可測】獨立實驗室,支援24小時無間隔測試,任意壓測;
  • 【可擴充】支援case的交叉執行,部分case可定制化路徑;
  • 【可攔截】靈活的規則應用和擴充,能使一些穩定性問題可測可攔截;存量問題也能線下曆史複現;
  • 【可提效】支援一鍵全自動化,極大了提高了測試效率,支援實驗室靈活部署;

測試流程體系

  • 從測試-- >發現問題-->分析問題-->問題跟進解決-->問題修複驗證的一條龍測試
  • 目前已經上線的測試環境是低端機+2h的測試
一種檢測和測試容器高可用線程卡頓的技術(Android)

五.展望

問:什麼是可伸縮架構?

答:根據應用環境以及容器狀态(比如webview 執行的效率,V8執行的效率等等這些特質化的判斷來得出我們的運作環境),根據這些綜合屬性,我們動态的給目前容器運作環境定級。根據級别來動态的對容器現有能力做調整

問:可伸縮架構将來會有什麼能力?

答:比如低端機上關閉轉場動畫、關閉部分非必要預起操作、關閉部分非必要JSAPI調用、根據目前cpu或者記憶體情況動态調整容器内部對這兩處高消費的實作

一種檢測和測試容器高可用線程卡頓的技術(Android)

特别說明:我們的卡口技術體系可以完全吸收可伸縮架構将來的擴充,我們做架構設計的時候也是保留了這份擴充能力

作者:巧善

來源:微信公衆号:螞蟻品質AnTest

出處:https://mp.weixin.qq.com/s/PhKGhY0zBy7GjuAaRciw3w

繼續閱讀