dumpsys 是一種在 Android 裝置上執行的工具,可提供有關係統服務的資訊。可以使用 Android 偵錯橋 (adb) 從命令列呼叫 dumpsys,獲取在連線的裝置上執行的所有系統服務的診斷輸出。
Binder是Android提供的一套程序間相互通訊框架。用來實現多程序間傳送訊息,同步和共用記憶體。
管道是一種IPC通訊方式,分為有名管道和無名管道,無論是有名管道還是無名管道其原理都是在核心開闢一塊快取空間,這段快取空間的操作是通過檔案讀寫方式進行的。
有名管道與無名管道:
有名管道: 有名管道的通訊可以通過管道名進行通訊,程序間不需要有關係。
無名管道: 無名管道就是匿名管道,匿名管道通訊的程序必須是父子程序。
管道為分半雙工和全雙工:
半雙工: 半雙工管道是單向通訊,程序1只能向管道寫資料,程序2只能從管道讀取資料。只有一個代表讀或者寫的FD(檔案描述符)。
全雙工: 全雙工管道是雙向通訊,有兩個檔案描述符,代表讀和寫。
如下為執行"adb shell dumpsys"指令,控制檯列印的內容,其使用如下:
(1)使用 dumpsys 的一般語法如下:
adb shell dumpsys [-t timeout] [--help | -l | --skip services | service [arguments] | -c | -h]
(2)如需獲取所連線裝置的所有系統服務的診斷輸出,請執行 adb shell dumpsys。不過,這樣輸出的資訊比您通常想要的資訊多得多。若要使輸出更加可控,您可以通過在命令中新增相應服務來指定要檢查的服務。例如,下面的命令會提供輸入元件(如觸控式螢幕或內建鍵盤)的系統資料:
adb shell dumpsys input
(3)如需檢視可與 dumpsys 配合使用的系統服務的完整列表,請使用以下命令:
adb shell dumpsys -l
(4)命令列選項如下:
選項 | 說明 |
---|---|
-t timeout | 指定超時期限(秒)。如果未指定,預設值為 10 秒。 |
--help | 輸出 dumpsys 工具的幫助文字。 |
-l | 輸出可與 dumpsys 配合使用的系統服務的完整列表。 |
--skip services | 指定您不希望包含在輸出中的 services。 |
service [arguments] | 指定您希望輸出的 service。某些服務可能允許您傳遞可選 arguments。如需瞭解這些可選引數,請將 -h 選項與服務一起傳遞:adb shell dumpsys procstats -h |
-c | 指定某些服務時,附加此選項能以計算機可讀的格式輸出資料。 |
-h | 對於某些服務,附加此選項可檢視該服務的幫助文字和其他選項。 |
@frameworks\base\services\core\java\com\android\server\BatteryService.java
private final class BinderService extends Binder {
@Override protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
if (!DumpUtils.checkDumpPermission(mContext, TAG, pw)) return;
if (args.length > 0 && "--proto".equals(args[0])) {
dumpProto(fd);
} else {
dumpInternal(fd, pw, args);
}
}
...
}
private void dumpInternal(FileDescriptor fd, PrintWriter pw, String[] args) {
synchronized (mLock) {
if (args == null || args.length == 0 || "-a".equals(args[0])) {
pw.println("Current Battery Service state:");
if (mUpdatesStopped) {
pw.println(" (UPDATES STOPPED -- use 'reset' to restart)");
}
pw.println(" AC powered: " + mHealthInfo.chargerAcOnline);
pw.println(" USB powered: " + mHealthInfo.chargerUsbOnline);
pw.println(" Wireless powered: " + mHealthInfo.chargerWirelessOnline);
pw.println(" Max charging current: " + mHealthInfo.maxChargingCurrent);
pw.println(" Max charging voltage: " + mHealthInfo.maxChargingVoltage);
pw.println(" Charge counter: " + mHealthInfo.batteryChargeCounter);
pw.println(" status: " + mHealthInfo.batteryStatus);
pw.println(" health: " + mHealthInfo.batteryHealth);
pw.println(" present: " + mHealthInfo.batteryPresent);
pw.println(" level: " + mHealthInfo.batteryLevel);
pw.println(" scale: " + BATTERY_SCALE);
pw.println(" voltage: " + mHealthInfo.batteryVoltage);
pw.println(" temperature: " + mHealthInfo.batteryTemperature);
pw.println(" technology: " + mHealthInfo.batteryTechnology);
} else {
Shell shell = new Shell();
shell.exec(mBinderService, null, fd, null, args, null, new ResultReceiver(null));
}
}
}
dumpsys是個二進位制可執行程式,其通過bp進行編譯,並最終打包到system分割區(system/bin/dumpsys)。
@frameworks\native\cmds\dumpsys\android.bp
cc_binary {
name: "dumpsys",
defaults: ["dumpsys_defaults"],
srcs: [
"main.cpp",
],
}
我們通過執行adb指令 "adb shell dumpsys",可以啟動dumpsys服務,其對應的入口函數如下:
@frameworks\native\cmds\dumpsys\main.cpp
int main(int argc, char* const argv[]) {
signal(SIGPIPE, SIG_IGN);
sp<IServiceManager> sm = defaultServiceManager();//獲取SM物件
fflush(stdout);
if (sm == nullptr) {
ALOGE("Unable to get default service manager!");
std::cerr << "dumpsys: Unable to get default service manager!" << std::endl;
return 20;
}
Dumpsys dumpsys(sm.get());
return dumpsys.main(argc, argv);//進入dumpsys服務
}
這邊比較關鍵的點是獲取ServiceManager物件。
大家通過列印可以發現,dumpsys指令列印的資料是java程序的dump函數,而dumpsys也是獨立的一個程序,那麼dumpsys程序又是怎麼和多個java程序通訊的呢?沒錯,就是通過ServiceManager物件。
那麼,ServiceManager物件是什麼呢?ServiceManager是Binder IPC通訊的管家,本身也是一個Binder服務,他相當於 「DNS伺服器」,內部儲存了serviceName與其Binder Service的對應關係,管理Java層和native層的service,支援addService()、getService()、checkService、listServices()等功能。(Binder機制此處就不展開細說)
當我們使用dumpsys指令,列印的資料太過冗長,一般會配合相關引數進行使用,例如:"dumpsys -l"、"dumpsys -t 100 battery"、"dumpsys --help",第一步我們會先解析目標引數。
@frameworks\native\cmds\dumpsys\dumpsys.cpp
int Dumpsys::main(int argc, char* const argv[]) {
...
while (1) {
...
c = getopt_long(argc, argv, "+t:T:l", longOptions, &optionIndex);//獲取指令引數
...
switch (c) {
case 0://長引數
if (!strcmp(longOptions[optionIndex].name, "skip")) {//跳過某些服務列印
skipServices = true;
} else if (!strcmp(longOptions[optionIndex].name, "proto")) {
asProto = true;
} else if (!strcmp(longOptions[optionIndex].name, "help")) {//指令幫助
usage();
return 0;
} else if (!strcmp(longOptions[optionIndex].name, "priority")) {
...
} else if (!strcmp(longOptions[optionIndex].name, "pid")) {//只顯示服務的pid
type = Type::PID;
} else if (!strcmp(longOptions[optionIndex].name, "thread")) {//僅顯示程序使用情況
type = Type::THREAD;
}
break;
case 't'://超時時間設定,預設10秒
...
break;
case 'T'://超時時間設定,預設10秒
...
break;
case 'l'://顯示支援的服務列表
showListOnly = true;
break;
default://其他引數
fprintf(stderr, "\n");
usage();
return -1;
}
}
...
}
dumpsys內部構造了skippedServices集合,用於記錄需要忽略的服務。
@frameworks\native\cmds\dumpsys\dumpsys.cpp
int Dumpsys::main(int argc, char* const argv[]) {
...
for (int i = optind; i < argc; i++) {
if (skipServices) {
skippedServices.add(String16(argv[i]));//設定待忽略的服務
} else {
...
}
...
}
dumpsys通過ServiceManager獲取支援的服務集合,並排序。
@frameworks\native\cmds\dumpsys\dumpsys.cpp
int Dumpsys::main(int argc, char* const argv[]) {
...
if (services.empty() || showListOnly) {
services = listServices(priorityFlags, asProto);
setServiceArgs(args, asProto, priorityFlags);
}
...
}
Vector<String16> Dumpsys::listServices(int priorityFilterFlags, bool filterByProto) const {
Vector<String16> services = sm_->listServices(priorityFilterFlags);//通過sm獲取服務集合
services.sort(sort_func);//集合排序
...
return services;
}
在獲取了服務集合後,會先檢查服務是否存在,接著列印服務的名稱,且如果當前指令設定了"-l"引數,僅列印服務集合,即流程結束。
@frameworks\native\cmds\dumpsys\dumpsys.cpp
int Dumpsys::main(int argc, char* const argv[]) {
...
const size_t N = services.size();//獲取支援的服務個數
if (N > 1 || showListOnly) {
// first print a list of the current services
std::cout << "Currently running services:" << std::endl;
for (size_t i=0; i<N; i++) {
sp<IBinder> service = sm_->checkService(services[i]);//檢查服務狀態
if (service != nullptr) {
bool skipped = IsSkipped(skippedServices, services[i]);
std::cout << " " << services[i] << (skipped ? " (skipped)" : "") << std::endl;//列印服務名稱
}
}
}
if (showListOnly) {//如果指令僅需要列印服務集合,則結束。
return 0;
}
...
}
先遍歷所有需要列印的服務,如果引數有指定服務名,即N為對應服務的數量,否則N為所有支援的服務數量。接著,開啟執行緒,通過servicemanager呼叫遠端的dump函數,利用管道和poll機制監聽遠端資料。最後如果超時或者dump結束,則關閉執行緒,釋放相關資源。
@frameworks\native\cmds\dumpsys\dumpsys.cpp
int Dumpsys::main(int argc, char* const argv[]) {
...
for (size_t i = 0; i < N; i++) {
const String16& serviceName = services[i];
if (IsSkipped(skippedServices, serviceName)) continue;//跳過部分服務
if (startDumpThread(type, serviceName, args) == OK) {//step 1.建立dump列印的執行緒
...
std::chrono::duration<double> elapsedDuration;
size_t bytesWritten = 0;
status_t status =
writeDump(STDOUT_FILENO, serviceName, std::chrono::milliseconds(timeoutArgMs),
asProto, elapsedDuration, bytesWritten);//step 2.dump執行列印操作
if (status == TIMED_OUT) {//列印超時
std::cout << std::endl
<< "*** SERVICE '" << serviceName << "' DUMP TIMEOUT (" << timeoutArgMs
<< "ms) EXPIRED ***" << std::endl
<< std::endl;
}
...
bool dumpComplete = (status == OK);
stopDumpThread(dumpComplete);//step 3.結束dump列印執行緒
}
}
...
}
step 1. 建立dumpsys列印執行緒
建立了一條管道,接著開啟了一個執行緒,通過ServiceManager物件讀取目標服務的dump函數,即dump列印資料。
@frameworks\native\cmds\dumpsys\dumpsys.cpp
status_t Dumpsys::startDumpThread(Type type, const String16& serviceName,
const Vector<String16>& args) {
sp<IBinder> service = sm_->checkService(serviceName);//通過SM獲取service物件
int sfd[2];
if (pipe(sfd) != 0) {//建立管道,用於讀取service端資料
...
}
...
redirectFd_ = unique_fd(sfd[0]);
unique_fd remote_end(sfd[1]);
sfd[0] = sfd[1] = -1;
// dump blocks until completion, so spawn a thread..
activeThread_ = std::thread([=, remote_end{std::move(remote_end)}]() mutable {//建立執行緒
status_t err = 0;
switch (type) {
case Type::DUMP:
err = service->dump(remote_end.get(), args);//呼叫dump函數
break;
...
}
...
});
return OK;
}
step 2. dumpsys列印到終端
通過poll機制用來監聽管道的資料,並將讀取到的dump資料,列印至控制檯。同時,通過計算剩餘的時間,來判斷當前是否讀取超時。
@frameworks\native\cmds\dumpsys\dumpsys.cpp
status_t Dumpsys::writeDump(int fd, const String16& serviceName, std::chrono::milliseconds timeout,
bool asProto, std::chrono::duration<double>& elapsedDuration,
size_t& bytesWritten) const {
...
int serviceDumpFd = redirectFd_.get();
struct pollfd pfd = {.fd = serviceDumpFd, .events = POLLIN};
while (true) {
...
int rc = TEMP_FAILURE_RETRY(poll(&pfd, 1, time_left_ms()));//poll機制檢測管道資料
if (rc < 0) {
...
} else if (rc == 0 || time_left_ms() == 0) {
status = TIMED_OUT;//計算剩餘時間,來決定是否超時
break;
}
char buf[4096];
rc = TEMP_FAILURE_RETRY(read(redirectFd_.get(), buf, sizeof(buf)));//讀取遠端的資料
...
if (!WriteFully(fd, buf, rc)) {//列印至控制檯
...
break;
}
totalBytes += rc;
}
...
return status;
}
step 3. 關閉dumpsys列印執行緒
將dumpsys列印的執行緒detach掉,相關的fd控制程式碼reset掉,釋放資源。
如後續有什麼應用dumpsys,或者有助於日常開發偵錯的場景,再補充,未完待續。
服務名 | 類名 | 指令 | 功能 |
---|---|---|---|
activity | ActivityManagerService | 獲取某個應用的Activity資訊:adb shell dumpsys activity a packagename 獲取某個應用的Service資訊: adb shell dumpsys activity s packagename 獲取某個應用的Broadcast資訊: adb shell dumpsys activity b packagename 獲取某個應用的Provider資訊: adb shell dumpsys activity prov packagename 獲取某個應用的程序狀態: adb shell dumpsys activity p packagename 獲取當前介面的Activity資訊: adb shell dumpsys activity top | grep ACTIVITY |
AMS相關資訊 |
package | PackageManagerService | adb shell dumpsys package |
PMS相關資訊 |
window | WindowManagerService | adb shell dumpsys window |
WMS相關資訊 |
input | InputManagerService | adb shell dumpsys input |
IMS相關資訊 |
power | PowerManagerService | adb shell dumpsys power |
PMS相關資訊 |
batterystats | BatterystatsService | adb shell dumpsys batterystats |
電池統計資訊 |
battery | BatteryService | adb shell dumpsys battery |
電池資訊 |
alarm | AlarmManagerService | adb shell dumpsys alarm |
鬧鐘資訊 |
dropbox | DropboxManagerService | adb shell dumpsys dropbox |
偵錯相關 |
procstats | ProcessStatsService | adb shell dumpsys procstats |
程序統計 |
cpuinfo | CpuBinder | adb shell dumpsys cpuinfo |
CPU |
meminfo | MemBinder | adb shell dumpsys meminfo |
記憶體 |
gfxinfo | GraphicsBinder | adb shell dumpsys gfxinfo |
影象 |
dbinfo | DbBinder | adb shell dumpsys dbinfo |
資料庫 |
dumpsys指令介紹:
https://developer.android.google.cn/studio/command-line/dumpsys?hl=zh-cn
管道:
https://www.cnblogs.com/naray/p/15365954.html
Binder:
https://blog.csdn.net/shenxiaolinil/article/details/128972302