adb dumpsys源碼分析
源碼位置:platform\frameworks\native\cmds\dumpsys
通過adb shell執(zhí)行dumpsys工具
- dumpsys模塊使用Android.bp文件進(jìn)行編譯配置,指定cc_binary字段表示將編譯該模塊為一個(gè)二進(jìn)制可執(zhí)行文件,因此我們可以使用adb shell dumpsys命令來執(zhí)行該模塊的代碼
配置文件摘要如下:
//
// Executable
//
cc_binary {
name: "dumpsys",
defaults: ["dumpsys_defaults"],
srcs: [
"main.cpp",
],
}
srcs參數(shù)表示指定的第一個(gè)文件是main.cpp文件
可執(zhí)行文件與系統(tǒng)服務(wù)打交道
- dumpsys工具的流程就是調(diào)用ServiceManager服務(wù)的listServices查詢系統(tǒng)注冊的所有服務(wù),然后嗲用checkservice接口獲取服務(wù)的遠(yuǎn)程binder代理對象,最終調(diào)用服務(wù)的dump函數(shù)并傳遞相應(yīng)的參數(shù)用來打印該服務(wù)的特定信息
- 過程涉及進(jìn)程間的binder通信,pipe管道通信
- 我們來看一下代碼
- main.cpp
//argc參數(shù)數(shù)量,argv參數(shù)數(shù)組索引0表示參數(shù)名
int main(int argc, char* const argv[]) {
//adb通過tcp協(xié)議與手機(jī)adbd進(jìn)程進(jìn)行通信,這里防止手機(jī)端產(chǎn)生僵尸進(jìn)程
signal(SIGPIPE, SIG_IGN);
//獲取IServiceManager對象
sp<IServiceManager> sm = defaultServiceManager();
//清空輸出
fflush(stdout);
if (sm == nullptr) {
ALOGE("Unable to get default service manager!");
aerr << "dumpsys: Unable to get default service manager!" << endl;
return 20;
}
Dumpsys dumpsys(sm.get());
//調(diào)用dumpsys.cpp文件的main函數(shù)
return dumpsys.main(argc, argv);
}
- dumpsys.cpp
int Dumpsys::main(int argc, char* const argv[]) {
//定義局部變量將參與到后續(xù)的顯示工作中
Vector<String16> services;
Vector<String16> args;
String16 priorityType;
Vector<String16> skippedServices;
Vector<String16> protoServices;
//參與解析工作的布爾控制變量,名稱含義比較明確
bool showListOnly = false;
bool skipServices = false;
bool asProto = false;
int timeoutArgMs = 10000;
//最終將參與StringAppendF函數(shù)的打印中
int priorityFlags = IServiceManager::DUMP_FLAG_PRIORITY_ALL;
static struct option longOptions[] = {{"priority", required_argument, 0, 0},
{"proto", no_argument, 0, 0},
{"skip", no_argument, 0, 0},
{"help", no_argument, 0, 0},
{0, 0, 0, 0}};
// Must reset optind, otherwise subsequent calls will fail (wouldn't happen on main.cpp, but
// happens on test cases).
optind = 1;
while (1) {
int c;
int optionIndex = 0;
//通過getopt_long循環(huán)獲取長參數(shù)列表中的參數(shù),并一一解析
c = getopt_long(argc, argv, "+t:T:l", longOptions, &optionIndex);
...
for (int i = optind; i < argc; i++) {
if (skipServices) {、
//在第一篇文件中有提到可以直接使用dumpsys工具并指定export的服務(wù),這里添加要export的服務(wù)
skippedServices.add(String16(argv[i]));
} else {
if (i == optind) {
services.add(String16(argv[i]));
} else {
args.add(String16(argv[i]));
}
}
}
//跳過服務(wù)沒有指定跳過哪些服務(wù)
if ((skipServices && skippedServices.empty()) ||
(showListOnly && (!services.empty() || !skippedServices.empty()))) {
usage();
return -1;
}
//只展示服務(wù)列表
if (services.empty() || showListOnly) {
services = listServices(priorityFlags, asProto);
setServiceArgs(args, asProto, priorityFlags);
}
const size_t N = services.size();
if (N > 1) {
// first print a list of the current services
aout << "Currently running services:" << 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]);
aout << " " << services[i] << (skipped ? " (skipped)" : "") << endl;
}
}
}
if (showListOnly) {
return 0;
}
for (size_t i = 0; i < N; i++) {
const String16& serviceName = services[i];
if (IsSkipped(skippedServices, serviceName)) continue;
//新開一個(gè)線程進(jìn)行服務(wù)的dump操作
if (startDumpThread(serviceName, args) == OK) {
bool addSeparator = (N > 1);
if (addSeparator) {
writeDumpHeader(STDOUT_FILENO, serviceName, priorityFlags);
}
std::chrono::duration<double> elapsedDuration;
size_t bytesWritten = 0;
status_t status =
//讀取結(jié)果
writeDump(STDOUT_FILENO, serviceName, std::chrono::milliseconds(timeoutArgMs),
asProto, elapsedDuration, bytesWritten);
if (status == TIMED_OUT) {
aout << endl
<< "*** SERVICE '" << serviceName << "' DUMP TIMEOUT (" << timeoutArgMs
<< "ms) EXPIRED ***" << endl
<< endl;
}
//
if (addSeparator) {
writeDumpFooter(STDOUT_FILENO, serviceName, elapsedDuration);
}
bool dumpComplete = (status == OK);
//停止dump操作
stopDumpThread(dumpComplete);
}
}
return 0;
}
- 開線程dumpsys
status_t Dumpsys::startDumpThread(const String16& serviceName, const Vector<String16>& args) {
//1. 調(diào)用servicemanager的checkservice方法獲取相應(yīng)service的binder代理對象
sp<IBinder> service = sm_->checkService(serviceName);
if (service == nullptr) {
aerr << "Can't find service: " << serviceName << endl;
return NAME_NOT_FOUND;
}
int sfd[2];
//2. 系統(tǒng)調(diào)用創(chuàng)建一個(gè)簡單的管道,sfd[0]用于讀取管道,sfd[1]用于寫入管道
if (pipe(sfd) != 0) {
aerr << "Failed to create pipe to dump service info for " << serviceName << ": "
<< strerror(errno) << endl;
return -errno;
}
//3. 管道通信的輸入和輸出均使用fd進(jìn)行包裝
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 {
//4. 調(diào)用service的dump方法,上層通過進(jìn)程binder類實(shí)現(xiàn)該功能
int err = service->dump(remote_end.get(), args);
// It'd be nice to be able to close the remote end of the socketpair before the dump call returns, to terminate our reads if the other end closes their copy of the file descriptor, but then hangs for some reason. There doesn't seem to be a good way to do this, though.
remote_end.reset();
if (err != 0) {
aerr << "Error dumping service info: (" << strerror(err) << ") "
<< serviceName << endl;
}
});
return OK;
}
status_t Dumpsys::writeDump(int fd, const String16& serviceName, std::chrono::milliseconds timeout,
bool asProto, std::chrono::duration<double>& elapsedDuration,
size_t& bytesWritten) const {
//...
//5. 系統(tǒng)調(diào)用poll讀取管道數(shù)據(jù)
struct pollfd pfd = {.fd = serviceDumpFd, .events = POLLIN};
while (true) {
// Wrap this in a lambda so that TEMP_FAILURE_RETRY recalculates the timeout.
auto time_left_ms = [end]() {
auto now = std::chrono::steady_clock::now();
auto diff = std::chrono::duration_cast<std::chrono::milliseconds>(end - now);
return std::max(diff.count(), 0ll);
};
char buf[4096];
//6. 讀取管道數(shù)據(jù),TEMP_FAILURE_RETRY宏將在失敗后重試
rc = TEMP_FAILURE_RETRY(read(redirectFd_.get(), buf, sizeof(buf)));
if (rc < 0) {
aerr << "Failed to read while dumping service " << serviceName << ": "
<< strerror(errno) << endl;
status = -errno;
break;
} else if (rc == 0) {
// EOF.
break;
}
//7. 寫數(shù)據(jù)到fd
if (!WriteFully(fd, buf, rc)) {
aerr << "Failed to write while dumping service " << serviceName << ": "
<< strerror(errno) << endl;
status = -errno;
break;
}
totalBytes += rc;
}
return status;
}
- 過程中的binder調(diào)用
這一塊抽出去在后邊的文章專門進(jìn)行討論。
- framework層系統(tǒng)服務(wù)
系統(tǒng)服務(wù)通過繼承binder類并重寫其dump方法,完成最終獲取framework信息的步驟
@Override
public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
PriorityDump.dump(mPriorityDumper, fd, pw, args);
}
//最終調(diào)用
// Wrapper function to print out debug data filtered by specified arguments.
private void doDump(FileDescriptor fd, PrintWriter pw, String[] args, boolean useProto)