ByteScope

Android System Monitor

每核心的負載與時脈、每一條熱區、即時吞吐與程序表——一秒一次,直接從線上讀。

每一個數值都是你的瀏覽器透過 USB 線讀的,只留在這個分頁。不會在手機上安裝任何東西,也不會上傳任何量測——這頁背後沒有伺服器。

正在檢查瀏覽器支援…

關於這個工具、FAQ 與相關工具

關於這個工具

把 Android 手機用 USB 接上 Chromium 瀏覽器,這頁就變成它的儀表板:帶 60 秒 sparkline 的整體 CPU、每顆核心一條負載長條與即時時脈、裝置願意公開的每一條熱區、含 PSS 前幾名的記憶體、正在發生的網路與磁碟吞吐、最忙的十個程序,還有一段可以停下來匯出成 CSV 的錄製。不會在手機上安裝任何東西,也不會上傳任何東西——這頁是一個沒有伺服器的靜態檔案,關掉分頁,所有數字就不存在了。

你說的「還剩多少記憶體」,其實是 MemAvailable

Android 最有名的畫面之一,就是一台裝了 12 GB 的手機顯示只剩幾百 MB;史上每一個「記憶體清理」app 都建立在這個誤會上。MemFree 是完全沒有被任何東西使用的記憶體,在一顆健康的 Linux 核心上這個數字本來就該很小:其餘的由頁面快取與可回收的 slab 持有,因為沒用到的記憶體就是浪費掉的記憶體。MemAvailable 才是核心自己對「現在新的配置實際拿得到多少」的估計——空閒記憶體,加上它願意在不動用 swap 的前提下回收的部分——這才是人講「剩多少記憶體」時指的東西。這頁到處都用它,而且會寫明。核心太舊(Linux 3.14 之前)報不出來時,會退回傳統的 free+cached 估計,長條上就標 estimated,而不是偷偷換掉數字的意思。下面那份 app 排行用的是 PSS:把 app 獨占的分頁全算,再加上它跟別人共用那些分頁的分攤額,所以這一欄加起來不等於總量,這是刻意的。

每核心時脈,以及為什麼我們不替叢集取名字

每顆核心都有自己一列:來自 /proc/stat 的負載長條、即時的 scaling_cur_freq,以及對照那顆核心*自己*的 cpuinfo_min_freqcpuinfo_max_freq 畫出來的刻度。最後這件事,是這條帶子在大小核手機上讀得懂的關鍵——小核跑到 1.8 GHz 是全開,同樣 1.8 GHz 在大核上卻是在混,共用一根軸只會告訴你完全相反的事。核心依頻率上限分群,因為那是實體上把叢集分開的性質,而且是矽的常數。標示寫「叢集 0 · 上限 1.80 GHz」,不寫 little、big 或 prime:那些是行銷用詞,各家 SoC 與各世代的對法都不同,在儀表板上貼一個很有自信的錯標,比誠實寫出上限更糟。每列旁邊的 governor(schedutilwalt、OEM 自訂名)照原文顯示,因為那是你要拿去跟自己 shell 輸出對照的東西。

降頻等級,以及溫度實際上拿走了什麼

熱區帶是核心原始的 /sys/class/thermal/thermal_zone*/temp,旁邊那個徽章則是 Android 自己的 thermal service 回報的 NONELIGHTMODERATESEVERECRITICALEMERGENCYSHUTDOWN。那是框架的用語,不是這頁自創的,而且有具體意義:從 MODERATE 開始,系統會要求 app 收斂;到了 SEVERE,不管跑分想怎樣,CPU 與 GPU 的時脈都會被壓下來。沒有一個通用的溫度會觸發這件事——trip point 是各裝置、各熱區由 OEM 的 thermal HAL 自己定的,所以表面 42 °C 就開始壓的手機,和跑到 48 °C 的手機,兩台都算正常。你能做的是看哪一條熱區先爬上去、時脈跟著掉得多快。一支手機也可能公開九十條幾乎一樣的熱區,所以這頁每個家族只監看一條,再固定加上全機最熱的那一條,並在磚上寫清楚它現在看的是幾條中的幾條。

這頁本身也會被算進那些數字裡

拿電腦量電腦不是免費的,而一個藏起自己成本的監測頁面,正好是在它最該說實話的地方說謊。這裡每個週期送的是**一條**合併過的 shell 指令,不是每個檔案一條;熱區普查只跑一次,不是每秒跑;top 十秒問一次,因為 toybox 為了算出 %CPU 差分會在裡面睡 250 毫秒;而會對每個執行中的程序發 binder、逼它們各自走一遍 smapsdumpsys meminfo,三十秒問一次,而且可以關掉。任何時刻只會有一條指令在路上,因為並行兩條 shell 不會把時間減半,只會讓同一瞬間的 fork 風暴加倍。分頁退到背景時,全部降到十秒一條。即使如此,在真的跑跑分的手機上,你看到的數字裡有一兩個百分點屬於量測本身。

值得讓分頁一直開著的理由是錄製:按開始,跑你的壓力測試、遊戲或上傳,按停止,就能把所有序列當成一張表帶走——要用試算表就 CSV,想寫腳本就 JSON,兩者都在這個分頁裡產生,不會上傳。欄位在你按下開始的當下就固定,所以跑到一半有核心離線只會留下空欄,不會生出寬度不一致的檔案;而量測失敗就是空欄,永遠不是 0。從這裡出發,Web ADB 主頁有其餘的整套工具,電池頁用 1 Hz 讀同一顆電池並附上循環次數與容量,儲存分析則會告訴你這頁只顯示 I/O 的那些空間到底被什麼吃掉了。

常見問題

怎麼看 Android 上某個 app 吃了多少記憶體?

看記憶體磚裡的 PSS 排行:它列出 proportional set size 最高的五個程序,那正是 Android 自己在決定要殺掉誰時看的數字。PSS 把 app 獨占的分頁全額計入,跟其他程序共用的分頁則只算分攤額——三個 app 共用一個 30 MB 的函式庫,每個算 10 MB。這就是為什麼這一欄加起來不等於手機總量,也正是為什麼它是「這個 app 到底花了我多少」最公平的單一數字。下面程序表裡的 RSS 是另一種量測:它把每個共用分頁對每個對映它的程序都算全額,所以整欄加起來會遠超過手機實際的容量。兩者都是透過 USB 讀的,手機上不必安裝任何東西。

手機 CPU 100% 是壞事嗎?

光看這件事不是。手機衝到 100 % 幾秒鐘,代表它正在做你要它做的事——解碼影片、編譯著色器、安裝 app——而且用全速把事情做完,通常比慢慢磨*更省*電。真正要看的是接下來。螢幕上什麼都沒有卻持續 100 %,代表背景有不該跑的東西在跑,而程序表會把它指出來。溫度一路往上、降頻徽章離開 `NONE` 的情況下持續 100 %,代表手機就要變得比它在 60 % 時更慢,因為時脈正被壓下去。至於把全部東西關掉之後仍然回不到閒置的 100 %,是握著 wakelock 的 app 最典型的特徵,靠程序表加上頁首列的前景 app,通常一分鐘之內就找得到。

Android 手機要多熱才會降頻?

沒有單一數字,會給你一個數字的人其實是在講他自己那台。trip point 是各裝置、各熱區由製造商的 thermal HAL 定的:表面溫度大致在 39 °C 到 45 °C 之間開始有影響,SoC 內部的熱區比機殼高很多,而充電還有一套比 CPU 更嚴格的獨立上限。真正能通用的是*形狀*:看哪一條熱區先爬、看徽章從 `NONE` 走到 `LIGHT` 再到 `MODERATE`、看負載持平的同時每核心時脈往下掉。最後那個組合——負載卡在高點、時脈往下——就是熱降頻,不管絕對溫度顯示多少,這件事沒有模糊空間。

為什麼這裡的網路數字跟 Android 的流量用量頁面不一樣?

兩邊數的東西不同。這頁讀的是 `/proc/net/dev`,也就是核心從開機以來的每介面位元組計數,而且它在加總實體介面時刻意排除 loopback 與 tunnel 類的裝置——VPN 會把同一份內容搬兩次(一次在 `wlan0`、一次在 `tun0`),USB 網路共享則會把這條除錯連線本身也算進去。磚上會寫清楚它加總了哪些介面,你可以自己核對。Android 的流量用量頁面則是完全另一套帳:它按 app 與網路類型歸屬流量、套用自己的計費週期邊界,也不會把核心看到的每一個位元組都算進去。兩邊都沒錯;要回答「現在到底有多少東西正在通過無線電」的,是核心那個數字。

匯出的 CSV 可以拿來做什麼?

那是一張很寬的表,一秒一列,最前面依序是毫秒時間戳、UTC 的 ISO 時間戳,以及經過秒數,所以 Excel、Numbers、`pandas`、R 都能直接打開,不用重整。常見用法:跑十分鐘遊戲或跑分,把時脈疊在溫度上,找出降頻開始的那一秒;讓兩個韌體版本做同一件事,然後比對 CPU 秒數;懷疑耗電異常時讓它一直錄,把前景 app 那一欄跟功率那一欄對起來看。量測失敗的儲存格是空的、不是 0,所以拿它們算平均不會被偷偷拉低。錄製到 1800 列(三十分鐘)就停止,而且保留開頭而不是結尾,因為對壓力測試來說,最值錢的是基準線那一段。