Android Camera簡單整理(四)-Android Camera性能Debug經驗

針對手機廠商相機性能優化部分進行重點總結記錄

一.相機性能場景

類別 場 景 耗時重點 建議
啟動類 熱啟動 App代碼優化精簡顯示,內存預分配,cpu perflock boost
冷啟動 本質上做成熱啟動,一般都讓相機在后臺被殺后過幾分鐘自動重啟,由于是系統平臺應用,做到這點不是很難
切換類 模式切換 要經過開關Camera過程 cpu perflock boost,減少無用初始化,耗時久的可考慮并行化初始化
前后置切換 要經過開關Camera過程 同一型號手機可能攝像頭馬達不一樣,滾珠式或彈簧式,在彈簧式馬達下電關閉相機時如果一下子收回會有攝像頭撞擊的聲音,因此一般讓其分步下電 ,這一步也是較為耗時,在滾珠式馬達下該處理就沒那么必要了,因此可以優化掉一部分馬達下電的耗時
預覽類 拍照預覽 引用的算法
錄像預覽
拍照類 普通拍照 算法上移處理,采用ZSL HAL,或者AppZSL可提升App普通拍照體驗
高像素拍照 高像素照片尺寸大CPU處理就會耗時,如果還要經過一些處理算法,性能必然會受影響 bypass檢查,曝光時間是否合理(這個tuning說了算,==) ,多幀處理的話幀數是否合理等檢查
算法拍照 算法耗時是主要還是算法處理 1.算法上移處理,則應用可以構造出一個Queue出來,拍照不停,圖片排隊算法處理,減少shot2shot時間,三分相機應用同樣可以適用. 2.bypass,在手機開發的時候發現qcom camx架構下,pipeline會復用,某些場景下共用其他場景的pipeline,但pipeline中有該場景不需要的node,如果未使用bypasss,則會導致該經過該node同樣會造成耗時,當這樣的node較多且buffer較大時,耗時還是很明顯的 3.Cpu Boost,算法處理的時候可以將CPU調度調優,以加速處理
視頻類 高幀率錄制 錄像幀率一般有30fps,60fps,120fps,960fps,一般高于60fps的實現都是通過插幀實現,30fps的request一個request帶回4個buffer即實現120fps,同理960fps可以一個request帶回8個buffer即可實現 高幀率對CPU處理依賴較大,因此CPU Boost調優很有必要,在滿足功耗要求的情況下盡量將CPU大小核跑滿而不是集中在某個核上
防抖錄制 EIS還是Vidhance,在高像素錄制下防抖開啟cpu數據處理量必然上升,當緩存用作防抖裁剪的Buffer數量很大的時候內存也會跟著緊張 設置合理的防抖緩存buffer數量,CPU perflock可以調優,盡量大小核分布均勻嚴密
高像素防抖錄制 如上,內存問題 防抖算法緩存幀數要合理,不能一味的只注重防抖效果而忽略內存的能力

本質上,相機性能主要從以下三點入手:
1.代碼架構合理
比如嘗試并行化處理耗時較久的操作,用戶體驗合理(比如拍照完可立即進入圖庫查看,雖然顯示一張模糊的圖正在處理,過一會才處理完顯示變清楚,但用戶體驗要比等待算法處理完才可進圖庫要好很多),動態創建資源等,有些算法庫耗時,不正常耗時即算法庫的處理邏輯有問題也需要庫代碼側去改善.

2.CPU調度調優
若cpu高負載運行必然使得CPU溫頓升高,另外還有Sensor溫度隨處理頻繁而升高,這就會觸發Thermal降頻機制或者其他防止手機繼續升溫的控制,便會給CPU降頻,而CPU大核一降頻,相機拍照時間變久,預覽丟幀,錄制最終的視頻幀率不足等性能問題就來了;而一個好的CPU調度策略,會讓CPU保持大小核均勻負載,沒有其他進程搶占,綁核現象,使CPU負載均勻嚴密,一定程度上不僅可以提高處理效率還可以維持一定的溫度穩定.及CPU不被降頻,調度合理最優.

3.內存管理高效
相機占用的內存較大,如果機器內存較小,且當前內存緊張,由內存不足引起的相機視頻丟幀,預覽卡頓等性能問題便出現了.比如連拍功能,或者高幀率防抖錄制視頻,對內存依賴都很大,因此便需要在內存優化上下功夫,如提前分配更大的buffer以解決一開始啟動時內存突然消耗,或者降低一些算法的Buffer數量,做到合理水平,或者可以在啟動相機或者進入某個高消耗內存的模式時調用系統接口去清理下后臺內存.
內存如果足夠緊張,最終不僅是性能有問題,相機穩定性也會收到影響,比如lmk殺了前臺進程導致相機閃退(醉了),或者其他影響用戶體驗的問題,比如從三方調用相機,結果導致返回應用時應用重新啟動了,對,就是內存不租被干掉了,看樣子,殺不殺后臺進程還得看系統的智慧的判斷才行,比如這里用戶要返回的場景就會導致用戶體驗極差.
不僅CPU用到的內存,GPU的內存使用也很重要,比如GPU如果內存不足導致分配GPU內存變慢同樣會導致預覽卡頓等性能問題.因此GPU內存把控也十分重要.

二.相機性能debug工具及方案建議

1.dumpsys media.camera

adb shell dumpsys media.camera > camera_dumpsys.txt
有些很有用的信息:

1.1 qcom機器


== Service global info: ==
------>// Camera ID及數量情況,這里涉及到邏輯id,物理id等,還有可能手機廠商自己高的虛擬camera也在內,具體要看framework中打印的地方是怎么取的,這個地方沒怎么關注過.
Number of camera devices: 8
Number of normal camera devices: 6
    Device 0 maps to "0"
    Device 1 maps to "1"
    Device 2 maps to "21"
    Device 3 maps to "22"
    Device 4 maps to "100"
    Device 5 maps to "101"
Active Camera Clients:
[
(Camera ID: 0, Cost: 99, PID: 20418, Score: -900, State: 0User Id: 0, Client Package Name: com.android.camera, Conflicting Client Devices: {1, })
]
Allowed user IDs: 0

------->//最近操作史
== Camera service events log (most recent at top): ==
  04-22 06:36:07 : CONNECT device 0 client for package com.android.camera (PID 20418)
  04-22 06:25:41 : DISCONNECT device 0 client for package com.android.camera (PID 20418)
  04-22 06:23:38 : CONNECT device 0 client for package com.android.camera (PID 20418)
  04-22 06:23:38 : DISCONNECT device 1 client for package com.android.camera (PID 20418)
  04-22 06:03:53 : CONNECT device 1 client for package com.android.camera (PID 20418)

------->//后置默認拍照打開此時device0處于活動狀態.
== Camera device 0 dynamic info: ==
  Device 0 is open. Client instance dump:
    Client priority score: -900 state: 0
    Client PID: 20418
    Client package: com.android.camera
  CameraDeviceClient[0] (0xe879d280) dump:
    Current client UID 10072
    State:
      Request ID counter: 3
      No input stream configured.
---------->//outputstream信息,看出來有三個stream
      Current output stream/surface IDs:
        Stream 0 Surface 0
        Stream 1 Surface 0
        Stream 2 Surface 0

        
-------->// 這里就看出來三個stream是什么了,一個surfaceTexture,用于顯示預覽
Device dump:
    Device status: ACTIVE
    Stream configuration:
    Operation mode: CUSTOM (36869) 
      No input stream.
    Stream[0]: Output
      Consumer name: SurfaceTexture-10-20418-3
      State: 4
      Dims: 1440 x 1080, format 0x22, dataspace 0x0
      Max size: 0
      Combined usage: 131328, max HAL buffers: 8
      Frames produced: 201, last timestamp: 984089393217873 ns
      Total buffers: 10, currently dequeued: 6
      DequeueBuffer latency histogram: (207) samples
        5     10     15     20     25     30     35     40     45    inf (max ms)
     98.07   1.45   0.00   0.48   0.00   0.00   0.00   0.00   0.00   0.00 (%)
     
------>//ImageReader用于保存圖片
    Stream[1]: Output
      Consumer name: ImageReader-4000x3000f23m10-20418-15
      State: 4
      Dims: 4000 x 3000, format 0x23, dataspace 0x8c20000
      Max size: 0
      Combined usage: 131075, max HAL buffers: 8
      Frames produced: 0, last timestamp: 0 ns
      Total buffers: 18, currently dequeued: 0
      
------->//竟然還有個imagereader,從其尺寸來看,要么是想搞縮率圖要么就是想干其他事,不曉得,二維碼識別或場景識別可能會用得到,我猜的,==
    Stream[2]: Output
      Consumer name: ImageReader-1440x1080f23m1-20418-16
      State: 4
      Dims: 1440 x 1080, format 0x23, dataspace 0x8c20000
      Max size: 0
      
--------> // 信息很詳細啊,這里還有max HAL buffers數量,這里要是不夠的話就會導致卡頓或者hang住哦,
      Combined usage: 131075, max HAL buffers: 8
      Frames produced: 202, last timestamp: 984089426352300 ns
      Total buffers: 9, currently dequeued: 5
信息太多,找點有用的信息,
// dump hal的信息
== Camera HAL device device@3.5/legacy/0 (v3.5) dumpState: ==
###############  CameraId: 0 Dump Start  ###############
  +------------------------------------------------------------------+
  +         HAL Dump                                                 +
  +------------------------------------------------------------------+

+------------------------------------------------------------------+
  +         Chi statistics                                           +
  +------------------------------------------------------------------+
 ---->//camx chi session信息:
  + Number of open sessions: 5
  + Number of open pipeline descriptors: 7
  +------------------------------------------------------------------+
  +           Chi Session                                            +
  +------------------------------------------------------------------+
  + Session: 0x6f8b344700
    +-----------------------------------------------------------------------------------------+
    Session 0x6f8b344700 state:
      reason                  = "ChiContextDump"
      numPipelines            = 1
      numRealtimePipelines    = 0
      partialMetadataEnabled  = false
   ....

------>//該session里的pipeline信息,因為后置默認拍照用到的是mfnr因此有多個pipeline
拿一個舉例:
    Pipelines:
      Pipeline 0:
      name                          = "MfnrPrefilter_0"
      address                       = 0x6f80930800
   ...
      lastInOrderCompletedRequestId = 0
      currentRequest                = 0
      +------------------------------------------------------------------+
      Pending Nodes:
      Requests Range - lastInOrderCompletedRequestId: 0 currentRequest: 0
        .....
      Graph:
      Nodes + ports:
        Node: MfnrPrefilter_IPE0 
          InputPort 0, format: 0, portDisabled: false, bufferDelta: 0
          InputPort 1, format: 0, portDisabled: false, bufferDelta: 0
          InputPort 2, format: 0, portDisabled: false, bufferDelta: 0
          InputPort 3, format: 0, portDisabled: false, bufferDelta: 0
          OutputPort 10, format: 12  maxNumBuffers: 40, memFlags: 0x00000001, heap: 1,
            flags: 0x00000010, streamEnableMask: 0x00000001, numBatchedFrames: 1
            deviceIndex[0]: 17
          OutputPort 11, format: 18  maxNumBuffers: 40, memFlags: 0x00000001, heap: 1,
            flags: 0x00000010, streamEnableMask: 0x00000002, numBatchedFrames: 1
            deviceIndex[0]: 17
        Node: MfnrPrefilter_BPS0 
          InputPort 0, format: 0, portDisabled: false, bufferDelta: 0
          OutputPort 7, format: 3  maxNumBuffers: 40, memFlags: 0x00000001, heap: 1,
            flags: 0x00000010, streamEnableMask: 0x00000004, numBatchedFrames: 1
            deviceIndex[0]: 17
  ....
            
----->// pipeline里的 node link信息,從日志中就可以知道鏈接順序了.
      Links:
        Node::MfnrPrefilter_BPS0::OutputPort_1 -> Node::MfnrPrefilter_IPE0::InputPort_0 -- bufferDelta = 0, maxImageBuffer = 0
        Node::MfnrPrefilter_BPS0::OutputPort_2 -> Node::MfnrPrefilter_IPE0::InputPort_1 -- bufferDelta = 0, maxImageBuffer = 0
        Node::MfnrPrefilter_BPS0::OutputPort_3 -> Node::MfnrPrefilter_IPE0::InputPort_2 -- bufferDelta = 0, maxImageBuffer = 0
        Node::MfnrPrefilter_BPS0::OutputPort_4 -> Node::MfnrPrefilter_IPE0::InputPort_3 -- bufferDelta = 0, maxImageBuffer = 0
        Node::MfnrPrefilter_IPE0::OutputPort_10 -> SinkBuffer_0 -- BufferQueueDepth = 0, maxImageBuffer = 0
        Node::MfnrPrefilter_IPE0::OutputPort_11 -> SinkBuffer_1 -- BufferQueueDepth = 0, maxImageBuffer = 0
        SourceBuffer_0 -> Node::MfnrPrefilter_BPS0::InputPort_0 -- bufferDelta = 0, maxImageBuffer = 40
        Node::MfnrPrefilter_BPS0::OutputPort_7 -> SinkBuffer_2 -- BufferQueueDepth = 0, maxImageBuffer = 0
        
 -------->其他mfnr的pipeline
     +------------------------------------------------------------------+
      Pipeline 1:
      name                          = "MfnrBlend_0"
      ....
       +------------------------------------------------------------------+
      Pipeline 2:
      name                          = "MfnrPostFilter_0"

--------> 當然還有其他的chx session
 
 一堆tuning的參數看不懂
------>//一些vendortag
== Vendor tags: ==

------>//這里是之前發生過camera故障時抓的進程trace信息,如果未出過錯就沒有有trace信息
== Camera error traces (2): ==
  ----- pid 1035 at 2020-04-10 23:29:00 -----
  Cmd line: /system/bin/cameraserver
  
  "cameraserver" sysTid=1035
    #00 pc 0003b29f  /system/lib/libbinde

1.2 mtk機器

前面一樣,后面dump hal層信息dump了mtk自己的一些東西:
== Camera HAL device device@3.5/internal/0 (v3.5) dumpState: ==

== error state (most recent at bottom): App Stream Manager ==
  [no events yet]
== CommandHandler (tid:3099) isRunning:1 exitPending:0 ==
   No pending command
mtk的有點少,接觸的也少,后面看到有用的再補上!!!

2.systrace

參考:
手把手教你使用Systrace(一)
Android Systrace 基礎知識
相機的systrace抓取需要設置一定的屬性才能看得到pipeline細節,比如Qcom需要操作如下:

1.設置trace打開屬性:
adb root && adb shell setprop persist.vendor.camera.traceGroupsEnable 65566 && adb shell getprop persist.vendor.camera.traceGroupsEnable  
adb reboot //provider進程重啟生效
adb root && adb shell getprop persist.vendor.camera.traceGroupsEnable 
adb shell  setenforce 0 && adb shell  getenforce  //不關selinux可能provider node的systrace抓的不全

traceGroupsEnable的值與Camx日志group一樣,一般按照上面65566基本就夠了.
UMD group:
static const CamxLogGroup CamxLogGroupNone = (1 << 0); ///< Generic group
static const CamxLogGroup CamxLogGroupSensor = (1 << 1); ///< Sensor
static const CamxLogGroup CamxLogGroupIFace = (1 << 2); ///< IFace
static const CamxLogGroup CamxLogGroupISP = (1 << 3); ///< ISP
static const CamxLogGroup CamxLogGroupPProc = (1 << 4); ///< Post Processor
static const CamxLogGroup CamxLogGroupImgLib = (1 << 5); ///< Image Lib
static const CamxLogGroup CamxLogGroupCPP = (1 << 6); ///< CPP
static const CamxLogGroup CamxLogGroupHAL = (1 << 7); ///< HAL
static const CamxLogGroup CamxLogGroupJPEG = (1 << 8); ///< JPEG
static const CamxLogGroup CamxLogGroupStats = (1 << 9); ///< Stats
static const CamxLogGroup CamxLogGroupCSL = (1 << 10); ///< CSL
static const CamxLogGroup CamxLogGroupApp = (1 << 11); ///< Application
static const CamxLogGroup CamxLogGroupUtils = (1 << 12); ///< Utilities
static const CamxLogGroup CamxLogGroupSync = (1 << 13); ///< Sync
static const CamxLogGroup CamxLogGroupMemSpy = (1 << 14); ///< MemSpy
static const CamxLogGroup CamxLogGroupAssert = (1 << 15); ///< Asserts
static const CamxLogGroup CamxLogGroupCore = (1 << 16); ///< Core
static const CamxLogGroup CamxLogGroupHWL = (1 << 17); ///< HWL
static const CamxLogGroup CamxLogGroupChi = (1 << 18); ///< CHI
static const CamxLogGroup CamxLogGroupDRQ = (1 << 19); ///< DRQ
static const CamxLogGroup CamxLogGroupFD = (1 << 20); ///< FD
static const CamxLogGroup CamxLogGroupIQMod = (1 << 21); ///< IQ module
static const CamxLogGroup CamxLogGroupLRME = (1 << 22); ///< LRME
static const CamxLogGroup CamxLogGroupNCS = (1 << 23); ///< NCS
static const CamxLogGroup CamxLogGroupMeta = (1 << 24); ///< Metadata
static const CamxLogGroup CamxLogGroupAEC = (1 << 25); ///< AEC
static const CamxLogGroup CamxLogGroupAWB = (1 << 26); ///< AWB
static const CamxLogGroup CamxLogGroupAF = (1 << 27); ///< AF



2.抓取
python ~/Android/Sdk/platform-tools/systrace/systrace.py -o camera_qidong.html sched freq camera hal  
如果抓取到的trace時間較短,可以 -b 設大緩存,
python ~/Android/Sdk/platform-tools/systrace/systrace.py -b 30726 -o camera_qidong7.html sched freq camera hal
或者 -t ,如下可以抓到4s左右
python ~/Android/Sdk/platform-tools/systrace/systrace.py -t 4 -o camera_qidong7.html sched freq camera hal gfx input view wm am app dalvik sched freq idle load -a com.android.camera

當然debug的時候可能需要自己添加systrace的tag,按照已有的trace tag添加即可,問題不大.
參考:https://blog.csdn.net/oujunli/article/details/16888897

3.perfetto

systrace抓的太久哪怕10秒就會很大,以致打不開,而且像視頻丟幀這種現象基本要過很一段時間后偶現一次,想借用systrace來抓現場基本也是不可能抓得到.這就需要用perfetto去抓systrace了抓取時??梢允?小時,且用在線工具打開毫無壓力,很棒!
參考:
Perfetto工具使用簡介
perfetto -Android Develop介紹
Perfetto 官網

perfetto 工具是Android下一代全新的統一的 trace 收集和分析框架,可以抓取平臺和app的 trace 信息,是用來取代 systrace 的,但 systrace 由于歷史原因也還會一直存在,并且 Perfetto 抓取的 trace 文件也可以同樣轉換成 systrace 視圖,如果習慣用 systrace 的,可以用 Perfetto UI 的 Open with legacy UI 轉換成 systrace 視圖來看。

1、首先查看手機是否已經打開類perfetto的后臺進程

adb logcat -s perfetto
perfetto: service.cc:45 Started traced, listening on /dev/socket/traced_producer /dev/socket/traced_consumer
perfetto: probes.cc:25 Starting /system/bin/traced_probes service
perfetto: probes_producer.cc:32 Connected to the service

2、去Perfetto的UI網址(https://ui.perfetto.dev/#!/record)上定制相關的使用命令
如圖為Perfetto UI的頁面,在該頁面下可以設置抓取信息的時長方式等

在這里插入圖片描述

在Probes的選項中可以設置想要抓取的CPU,GPU等信息
在這里插入圖片描述

最后來到InStructions選項可以復制剛剛設置完成后的命令,將其直接拷貝到Linux終端即可運行。
在這里插入圖片描述

3、等到抓取的長度或者時間達到你第2步設置的值后,會自動退出抓取程序,此時將手機/data/misc/perfetto-traces/文件夾下面的trace文件adb pull到電腦端

4、同樣在https://ui.perfetto.dev/#!/record網址上通過Open trace File選項打開本地存儲的trace文件即可查看相關信息,可以看到時長1min半打開無壓力,用于復現視頻偶先丟幀很棒啊!

在這里插入圖片描述

4.SimplePerf,火焰圖

參考:https://zhuanlan.zhihu.com/p/25277481
https://android.googlesource.com/platform/prebuilts/simpleperf/
https://developer.android.com/ndk/guides/simpleperf?hl=zh-cn
http://www.brendangregg.com/flamegraphs.html
https://github.com/brendangregg/FlameGraph

代碼路徑:/system/extras/simpleperf/
腳本路徑:/system/extras/simpleperf/scripts
手機設備的默認路徑 :/system/bin/simpleperf

Simpleperf是谷歌將perf工具port到Android上的性能分析工具,它的命令行界面支持與linux-tools perf大致相同的選項,但是它還支持許多Android特有的改進。具有三個主要的功能:stat,record 和 report。

使用方法:

抓取方法
在pc端:
快速方式:
python /home/sun/work/code/j6a1-curtana/system/extras/simpleperf/scripts/run_simpleperf_on_device.py kmem record --call-graph fp -f 4000 --duration 10 -o /data/local/tmp/perf.data

完整方式:
python /home/sun/work/code/j6a1-curtana/system/extras/simpleperf/scripts/run_simpleperf_on_device.py kmem record --call-graph fp -f 4000 --duration 10 --symfs ~/work/image/curtana_in_global_symbols_20.3.13.root_10.0_96fc05f913_gdcdaf60/out/target/product/curtana/symbols/ -o /data/local/tmp/perf.data
python /home/sun/work/code/j6a1-curtana/system/extras/simpleperf/scripts/run_simpleperf_on_device.py kmem record -p pid -e kmem:kmalloc,kmem:kmem_cache_alloc --call-graph fp -f 4000 --duration 10 -o /data/local/tmp/perf.data
adb pull /data/local/tmp/perf.data ./

或者在手機端:
快速方式:
simpleperf kmem record --call-graph fp -f 4000 --duration 10 -o /data/local/tmp/perf_2.data
完整方式:
simpleperf kmem record -p pid -e kmem:kmalloc,kmem:kmem_cache_alloc --call-graph fp -f 4000 --duration 10 -o /data/local/tmp/perf_2.data

解析生成火焰圖:

python /home/sun/work/code/j6a1-curtana/system/extras/simpleperf/scripts/report_html.py -i ./perf.data -o ./perf.html
或者
python /home/sun/work/code/j6a1-curtana/system/extras/simpleperf/scripts/report_sample.py --symfs ~/work/image/curtana_in_global_symbols_20.3.13.root_10.0_96fc05f913_gdcdaf60/out/target/product/curtana/symbols/  -i perf.data > out.perf

git clone https://github.com/brendangregg/FlameGraph.git
FlameGraph/stackcollapse-perf.pl out.perf > out.folded
FlameGraph/flamegraph.pl out.folded > graph.svg
解析完成后的perf.html或者graph.svg直接用谷歌瀏覽器打開,如下:
在這里插入圖片描述

在這里插入圖片描述

建議用report_html.py生成的perf.html,會有更直觀的統計信息

y 軸表示調用棧,每一層都是一個函數。調用棧越深,火焰就越高,頂部就是正在執行的函數,下方都是它的父函數。
x 軸表示抽樣數,如果一個函數在 x 軸占據的寬度越寬,就表示它被抽到的次數多,即執行的時間長。注意,x 軸不代表時間,而是所有的調用棧合并后,按字母順序排列的。

火焰圖就是看頂層的哪個函數占據的寬度最大。只要有"平頂"(plateaus),就表示該函數可能存在性能問題。
顏色沒有特殊含義,因為火焰圖表示的是 CPU 的繁忙程度,所以一般選擇暖色調。

5.Android sudio profile

Android studio中的profile功能可以跟進app進程內存變化情況及細節,以及CPU占用情況,但看相機應用內存變化趨勢是可以的了,然后在高位時dump需要的信息繼續debug吧,==
參考:
Android studio中android profile(性能分析器)的使用
Android Studio使用profile簡單優雅的查看內存變化

6.Qcom Perfdump工具

高通可以在手機上安裝,查看顯示幀率,可以用來查看相機預覽界面幀率是否達標,還可以順便抓個systrace,真棒!
具體使用方式在高通網站上有介紹ppt,搜索Perfdump Tool Overview即可.


在這里插入圖片描述
在這里插入圖片描述

在這里插入圖片描述

7.Cpu dump工具

cpu的負載和頻率對相機性能影響很大,有時候需要實時查看cpu變化,提供個shell腳本,可以實時dump cpu信息:
這里是mtk cpu的腳本,mtkcpu與qcom的在讀去溫度上有點區別,qcom可以參考著寫一個,問題不大
鏈接: https://pan.baidu.com/s/1C9FYIQeZHFRHdDhh-tc_1g 提取碼: fw4v

在這里插入圖片描述

主要cpu信息從如下文件節點讀取:

atom:/sys/devices/system/cpu/cpu0/cpufreq # ls -l
total 0
-r--r--r-- 1 root   root   4096 2020-04-07 14:41 affected_cpus
-r-------- 1 root   root   4096 2020-04-07 14:43 cpuinfo_cur_freq
-r--r--r-- 1 root   root   4096 2020-04-07 14:57 cpuinfo_max_freq
-r--r--r-- 1 root   root   4096 2020-04-07 14:41 cpuinfo_min_freq
-r--r--r-- 1 root   root   4096 2020-04-07 14:57 cpuinfo_transition_latency
-r--r--r-- 1 root   root   4096 2020-04-07 14:57 related_cpus
-r--r--r-- 1 root   root   4096 2020-04-07 14:41 scaling_available_frequencies
-r--r--r-- 1 root   root   4096 2020-04-07 14:57 scaling_available_governors
-r--r--r-- 1 root   root   4096 2020-04-07 14:57 scaling_cur_freq
-r--r--r-- 1 root   root   4096 2020-04-07 14:57 scaling_driver
-rw-rw---- 1 system system 4096 2020-03-31 16:58 scaling_governor
-rw-rw-r-- 1 system system 4096 2020-03-31 16:57 scaling_max_freq
-rw-rw-r-- 1 system system 4096 2020-03-31 16:57 scaling_min_freq
-rw-r--r-- 1 root   root   4096 2020-04-07 14:57 scaling_setspeed
drwxr-xr-x 2 root   root      0 2020-04-07 14:42 stats
atom:/sys/devices/system/cpu/cpu0/cpufreq #

含義:

cpuinfo_cur_freq: 當前cpu正在運行的工作頻率
cpuinfo_max_freq:該文件指定了處理器能夠運行的最高工作頻率 (單位: 千赫茲)
cpuinfo_min_freq :該文件指定了處理器能夠運行的最低工作頻率 (單位: 千赫茲)
cpuinfo_transition_latency:該文件定義了處理器在兩個不同頻率之間切換時所需要的時間  (單位: 納秒)
scaling_available_frequencies:所有支持的主頻率列表  (單位: 千赫茲)
scaling_available_governors:該文件顯示當前內核中支持的所有cpufreq governor類型
scaling_cur_freq:被governor和cpufreq核決定的當前CPU工作頻率。該頻率是內核認為該CPU當前運行的主頻率
scaling_driver:該文件顯示該CPU正在使用何種cpufreq driver
scaling_governor:通過echo命令,能夠改變當前處理器的governor類型
scaling_max_freq:顯示當前policy的上下限  (單位: 千赫茲)需要注意的是,當改變cpu policy時,需要首先設置scaling_max_freq, 然后才是scaling_min_freq
scaling_setspeed:如果用戶選擇了“userspace” governor, 那么可以設置cpu工作主頻率到某一個指定值。
需要這個值在scaling_min_freq 和 scaling_max_freq之間即可。

8. CpuFloat & PerfMon+ Apk

cpufloat和PerfMon+都是非常好用的通知狀態欄app,可以對手機的cpu和gpu頻率和溫度進行監控.
百度搜索下載或者從這下載:
鏈接: https://pan.baidu.com/s/1WxScYtAPOY9Gkjv2JkyrtA 提取碼: 9zgn
cpufloat:

在這里插入圖片描述

可以看到給予應用足夠的權限可以讀取的信息將更多:
在這里插入圖片描述

9.查看視頻文件丟幀情況工具

寫個腳本利用ffmpeg實現對視頻文件的解析,從pts時間戳來統計丟幀的情況:
需要ffmpeg環境.源碼如下,保存成sh文件運行:

#!/bin/bash
#######################################################################################
# 2019.10.24
#
# 5種使用方法
# 1.解析手機上相冊下最新產生的視頻文件
# ./as_frameloss.sh
# 2.解析pc上指定路徑下視頻文件
# ./as_frameloss.sh pc_video_file_path
# example:
# ./as_frameloss.sh /home/chengang/Documents/g7b/zhenlv/VID_20191022_194835_HSR_240.mp4
#
# 3.指定pull目錄且使用新生成的視頻文件名進行解析
# as_frameloss.sh pc_tmp_dir video_file_name
# example:
# ./as_frameloss.sh /home/chengang/Documents/g7b/zhenlv/ VID_20191022_194835_HSR_240.mp4
#
# 4.使用默認pull目錄且使用新生成的文件名進行解析
# ./as_frameloss.sh VID_20191022_194835_HSR_240.mp4
#
# 5.使用默認pull目錄且分析完刪除pc上的video文件
# ./as_frameloss.sh -d video_file_name
# ./as_frameloss.sh -d VID_20191022_194835_HSR_240.mp4
 
########################################################################################
 
TMP_PTS_FILE="pkt_pts_time_tmp.txt"
VIDEO_REAL_FPS=0 #視頻文件實際幀率
FPS_DEMANDED=0 #視頻文件要求達到的幀率
VIDEO_FILE_TEMP_DIR="/home/${USER}/as_frameloss_video_files"
analysis_out_fps_from_single_file(){
    path_2_file_name=$1
    avg_frame_rate=$(ffprobe -select_streams v -v quiet -print_format json -show_format -show_streams $path_2_file_name |awk -F "[avg_frame_rate]" '/avg_frame_rate/{print$0}')
    str=$(echo $avg_frame_rate | awk -F "[:]" '/avg_frame_rate/{print$2}')
    #echo $str,上一步得到字符串'"39060000/1362709",'
    #去除行頭的"號
    str1=$(echo ${str#*\"})
    #接著去除行末"號及之后的,號
    final_str=$(echo ${str1%\"*})
    #分割取出地一個數值
    first_value=`echo $final_str|awk -F "[/]" '{print$1}' `
    #取出第二個數值
    second_value=`echo $final_str|awk -F "[/]" '{print$2}' `
    #echo $first_value
    #echo $second_value
    #保留兩位小數輸出幀率,四舍五入與windows上保持一致
    VIDEO_REAL_FPS=`awk 'BEGIN{printf "%.2f\n",'$first_value'/'$second_value'}'`
}
 
analysis_frameloss(){
    pc_tmp_video_file_full_path=$1
    #取出參數中的文件的目錄,這里不帶最后一個斜杠后面需要在補上
    path_of_video_dir=`echo ${pc_tmp_video_file_full_path%/*}`
    path_of_tmp_file=$path_of_video_dir/$TMP_PTS_FILE
 
    str_result=""
    time1=$(date)
    str_result=`ffprobe  -show_frames -select_streams v $pc_tmp_video_file_full_path  |  grep pkt_pts_time`
    echo "$str_result" >  $path_of_tmp_file
    time2=$(date)
 
    frame_count=0
    string_final=""
    last_frame=0
 
    frame_count_demanded_per_0_1s=`expr $FPS_DEMANDED / 10`
    echo "******************************************************************"
    echo " "
    echo "Frameloss Analysis "
    echo " "
    echo "文件路徑 :  $pc_tmp_video_file_full_path"
    echo "要求幀率 :  $FPS_DEMANDED"
    echo "實際幀率 :  $VIDEO_REAL_FPS"
    echo "丟幀情況 : "
    while read line
    do
        time_str=${line:13}
        tmp_str=`echo ${time_str::-5}` #從字符串尾部刪掉5個字符
        let time_str_final=`echo 10#$tmp_str | sed 's/\.\+//g'` #保證10進制打印
        if [ $time_str_final == $last_frame ];then
            let "frame_count++"
        else
            loss_frame_no=`expr $frame_count_demanded_per_0_1s - $frame_count`
            denominator=10
            #res=`echo "scale=1; $last_frame/$denominator" | bc`
            res=$(printf "%.1f" `echo "scale=1;$last_frame/$denominator"|bc`)
            if [ $loss_frame_no != 0 ];then
               echo "第$res秒---> $loss_frame_no"
        #else
             #  echo "第$res秒---> "
            fi
            #開始新的0.1s計數
            last_frame=$time_str_final
            frame_count=1
        fi
 
        if [ "$string_final" == "" ];then
            string_final="$time_str_final"
        else
            string_final="$string_final;$time_str_final"
        fi
        #echo $string_final
    done <  $path_of_tmp_file
    time3=$(date)
}
 
figout_fps_demanded(){
    local_fps=$1
    real_int_fps=`echo $local_fps | awk -F '\.' '{print $1}'`
    echo "figout_fps_demanded-local_fps:real_int_fps[$local_fps:$real_int_fps]"
    if [[ $real_int_fps -gt 150 ]];then
        FPS_DEMANDED=240
    elif [[ $real_int_fps -gt 80 ]];then
        FPS_DEMANDED=120
    elif [[ $real_int_fps -gt 40 ]];then
        FPS_DEMANDED=60
    else  
        FPS_DEMANDED=30
    fi
}
 
file_value="$1"
pc_video_file_path=""
pc_tmp_video_dir=""
device_video_file_path=""
device_video_file_dir="/storage/emulated/0/DCIM/Camera"
 
param_count=$#
need_to_delete=0
need_to_adb_pull=0
if [ $param_count == 2 ]; then
    if  [ "$1" == "-d" ]; then
        need_to_delete=1
        need_to_adb_pull=1
        device_video_file_path=$device_video_file_dir/$2
        pc_tmp_video_dir=$VIDEO_FILE_TEMP_DIR
        pc_video_file_path=$pc_tmp_video_dir/$2
    else
        need_to_adb_pull=1
        pc_tmp_video_dir=$1
        device_video_file_path=$device_video_file_dir/$2
        pc_video_file_path=$pc_tmp_video_dir/$2
    fi
elif [ $param_count == 1 ]; then
    if [ -f $1 ]; then
        pc_video_file_path=$1
    else
        need_to_adb_pull=1
        device_video_file_path=$device_video_file_dir/$1
        pc_tmp_video_dir=$VIDEO_FILE_TEMP_DIR
        pc_video_file_path=$pc_tmp_video_dir/$1
    fi
elif [ $param_count == 0 ]; then
    need_to_adb_pull=1
    #獲取相冊目錄下最新的視頻文件
    device_video_newest_file_name=`adb shell ls -lt /sdcard/DCIM/Camera | grep mp4 | head -n 1 |awk '{print $8}'`
    device_video_file_path=$device_video_file_dir/$device_video_newest_file_name
    pc_tmp_video_dir=$VIDEO_FILE_TEMP_DIR
    pc_video_file_path=$pc_tmp_video_dir/$device_video_newest_file_name
fi
 
 
#需要同步手機文件至pc
if [ $need_to_adb_pull == 1 ]; then
    echo "device_video_file_path:$device_video_file_path"
    echo "pc_tmp_video_dir:$pc_tmp_video_dir"
    if [ ! -d "$pc_tmp_video_dir" ];then
        mkdir -p $pc_tmp_video_dir
    fi
    adb pull $device_video_file_path $pc_tmp_video_dir
fi
 
analysis_out_fps_from_single_file $pc_video_file_path
figout_fps_demanded $VIDEO_REAL_FPS
analysis_frameloss $pc_video_file_path
 
#是否需要刪掉電腦上的視頻文件
if [ $need_to_delete == 1 ]; then
     rm -fr $pc_video_file_path
fi

結果:


在這里插入圖片描述

事實上,可以根據這個結果判斷連續丟幀是否嚴重,作為一個指標進行考量.

10.perflock 及 thermal Debug

有的時候為了排除是perflock 或thermal限制導致,需要排除這個因素,可以嘗試將perflock或thermal機制給關掉驗證,關閉方式跟平臺可能有關系.
高通可通過stop掉perflock相關的服務來實現:

adb shell stop perfservice
adb shell stop vendor.perfservice
//可能沒有,如果不直到該有哪些可以ps -ef | grep perf來看下哪些和perflock相關的服務進程給他關掉即可.
adb shell stop perf-hal-1-0 
adb shell stop perf-hal-2-0

thermal機制關閉同樣可以先ps看下有哪些與thermal相關的進程然后給它stop掉則thermal機制就不起作用了.

    adb shell stop thermalservice
    adb shell stop thermal-engine
    adb shell stop vendor.thermal-engine
    adb shell stop vendor.thermal-hal-1-0

有時候可能需要手動調cpu最大頻率來看效果,各平臺廠商都有提供方法,咨詢下就有了不同平臺可能不一樣,
比如qcom文檔Common Performance Issues Debugging Guide 中提到的

 Issue verification by putting CPU in performance mode
 ? For 8939/8952:
adb wait-for-device root
adb wait-for-device
adb shell setenforce 0
adb shell stop thermal-engine
adb shell "echo 1 > /sys/devices/system/cpu/cpu0/online"
adb shell "echo 1 > /sys/devices/system/cpu/cpu1/online"
adb shell "echo 1 > /sys/devices/system/cpu/cpu2/online"
adb shell "echo 1 > /sys/devices/system/cpu/cpu3/online"
adb shell "echo 1 > /sys/devices/system/cpu/cpu4/online"
adb shell "echo 1 > /sys/devices/system/cpu/cpu5/online"
adb shell "echo 1 > /sys/devices/system/cpu/cpu6/online"
adb shell "echo 1 > /sys/devices/system/cpu/cpu7/online"
adb shell "echo performance >
/sys/devices/system/cpu/cpu0/cpufreq/scaling_governor"
adb shell "echo performance >
/sys/devices/system/cpu/cpu4/cpufreq/scaling_governor"
For 8909:
adb wait-for-device root
adb wait-for-device
adb shell setenforce 0
adb shell stop thermal-engine
adb shell "echo 1 > /sys/devices/system/cpu/cpu0/online"
adb shell "echo 1 > /sys/devices/system/cpu/cpu1/online"
adb shell "echo 1 > /sys/devices/system/cpu/cpu2/online"
adb shell "echo 1 > /sys/devices/system/cpu/cpu3/online"
adb shell "echo performance >
/sys/devices/system/cpu/cpu0/cpufreq/scaling_governor"

同樣的 qcom的gpu也有類似的perfmance mode:

.6 Issue verification by when GPU is in performance
mode
If the below steps, resolve the issue, check from GPU bandwidth voting info from dtsi files.
Check if there any heavy GL calls during the use case using Adreno profiler.
Check if there any Un-resolves during the use case by enabling resolve logs.
Check if GPU is not getting data in time and GPU is going to sleep from systrace.
For 8939/8952:
adb shell "echo 1 > /sys/class/kgsl/kgsl-3d0/force_rail_on"
adb shell "echo 1 > /sys/class/kgsl/kgsl-3d0/force_clk_on"
adb shell "echo 1 > /sys/class/kgsl/kgsl-3d0/force_bus_on"
adb shell "echo 10000000 > /sys/class/kgsl/kgsl-3d0/idle_timer"
adb shell "echo performance > /sys/class/kgsl/kgsl-3d0/devfreq/governor"
adb shell "echo 550000000 > /sys/class/kgsl/kgsl-3d0/gpuclk"
For 8909:
adb shell "echo 1 > /sys/class/kgsl/kgsl-3d0/force_rail_on"
adb shell "echo 1 > /sys/class/kgsl/kgsl-3d0/force_clk_on"
adb shell "echo 1 > /sys/class/kgsl/kgsl-3d0/force_bus_on"
adb shell "echo 10000000 > /sys/class/kgsl/kgsl-3d0/idle_timer"
adb shell "echo performance > /sys/class/kgsl/kgsl-3d0/devfreq/governor"
adb shell "echo 409600000 > /sys/class/kgsl/kgsl-3d0/gpuclk"

3.7 Issue verification by putting GPU and CPU in
performance mode
Please follow steps 3.5 and 3.6 to put the device in Performance mode.
? If this step solves the issue, check the CPU and GPU BW governor, scaling and scheduler
parameters
? Check if there are fence waits in the application rendering thread from systrace.
? Check by increasing the app buffers to 4 in build.prop (hw.sf.app_buff_count=4).
If increasing the app buffers solves the issue, then issue has to be checked from DISPLAY
side for panel settings and other mdp parameters. 

11.內存監控

可以自己寫個內存監控的腳本監控內存變化情況,內存信息來源可以從下獲取:

11.1 procrank

在這里插入圖片描述

在這里插入圖片描述

11.2 top

參考:[Linux-Android][Log] Top命令打印含義

在這里插入圖片描述

11.3 ION內存信息

動態選擇解析

/sys/kernel/debug/ion/heaps/system 
或者
/sys/kernel/debug/dma_buf/ [ bufinfo | dmaprocs ],

11.4 Gpu內存信息

動態選擇解析

/sys/class/kgsl/kgsl/page_alloc   
/sys/class/kgsl/kgsl/pagetables/$pid/mapped    
/sys/kernel/debug/kgsl/proc/$pid/mem

ION 和 GPU這塊還不熟悉,后面整理完再補充,這部分相當重要
GPU 內存泄漏問題,會導致界面顯示卡頓,畢竟GPU和顯示相關,分配GPU內存慢也導致顯示變慢從而用戶側感受到界面卡頓.

GPU內存泄漏可以先看下泄漏時的泄露的內存類型:
高通機型:


在這里插入圖片描述

在通過加callback trace來定位泄露點,或者能看到是哪張大圖導致gpu內存泄露了也可以,怎么看texture就使用下面這個工具了.

12.GAPID

參考看到每個texture長什么樣子,也就可以方便定位異常texture了.
參考:http://www.gcsjj.cn/articles/2019/06/04/1559658578252.html

三.基礎知識補充

Camera性能Debug需要很多基礎知識的支撐,列在這希望日后好好補充自己:

知識點 說明 參考
1.Qcom Cpu Perflock CPU調度合理,在關鍵時刻Boost,避免搶核綁核情況,均勻嚴密使用大小核可以有效提高特定場景下的相機性能 qcom文檔:
80-nr256-2_d_mpctl_feature.pdf
80-nt384-2_c_perflock_in_android_o.pdf
百度網盤:
Perflock框架原理與應用分析-v90-20200424_200315.pdf
2.Linux進程調度 重要 linux書籍
3. Linux內存管理 dma_buff ION相關,gpu內存等如何管理分配理解很重要
4. FFmpeg開發技術 重要
5.Camera App Framework HAL源碼架構 Qcom Camx中有很多不完善的地方,需要對代碼架構熟悉以可以有底氣去修改它改善他
6.GPU Gpu在顯示上舉足輕重,理解Gpu的工作原理或代碼框架對改善相機預覽或者應用幀率有很大的幫助
7.Graphic Debug
.... 后續補充
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容