Skip to content

dumpsys诊断

解释 dumpsys 如何查询和筛选 Binder 服务、转发 dump 请求、处理超时,并按故障类型选择合适的系统服务。

基于android-17.0.0_r1
AndroidADBdumpsysBinder故障诊断

dumpsys诊断 ​

很多资料把 dumpsys 写成“查看系统信息的命令集合”,然后列出几十个服务名。这种记法能让人复制命令,却不能解释三个更重要的问题:服务名从哪里来、一次 dump 请求由谁执行、输出为空或超时时应该检查哪一层。

本文从 frameworks/native/cmds/dumpsys 的真实代码出发,建立一条可追踪的主线:命令行进入 Dumpsys::main,ServiceManager 提供服务列表和 Binder 对象,Dumpsys 为每个服务创建 pipe 和读取线程,再通过 Binder dump() 把服务自己的诊断输出复制到标准输出。本文还把常用服务按诊断问题组织起来,但不把任何服务的输出字段当作稳定 API。前置的传输边界见 ADB架构,跨服务工件与时间窗关联见 bugreport分析。

本文默认读者已经读过 ADB架构 和 ADB shell机制,知道 host、adbd、Binder service 和 shell 命令之间的边界;bugreport分析 可以帮助理解为什么 dumpsys 常作为更大诊断工件的一部分。读完后,你应能从一个现象选择服务,解释一次 dumpsys service 的调用链,并区分“服务不存在”“服务没有输出”“服务 dump 超时”和“服务自身拒绝请求”。

1. dumpsys ​

1.1 main.cpp ​

Android 17 的 native 入口在 frameworks/native/cmds/dumpsys/main.cpp。入口只做两件事:取得默认 IServiceManager,再把 argc/argv 交给 Dumpsys::main。服务的名字、Binder handle 和 dump priority 不由 dumpsys 硬编码维护,而由 ServiceManager 及服务注册者提供。

源码文件:frameworks/native/cmds/dumpsys/main.cpp

相关函数/类型:main

cpp
int main(int argc, char* const argv[]) {
    signal(SIGPIPE, SIG_IGN);
    sp<IServiceManager> sm = defaultServiceManager();
    Dumpsys dumpsys(sm.get());
    return dumpsys.main(argc, argv);
}

Dumpsys 的构造函数只保存一个 IServiceManager*。这说明它不是所有系统状态的 owner;它拥有的只是本次命令的临时编排状态:服务列表、参数、pipe、读取线程和超时计时器。每个系统服务仍然拥有自己的状态和 dump() 实现。

图中最容易混淆的是 Dumpsys 和目标服务。Dumpsys 负责“找谁、以什么模式找、如何读、何时停止”;目标服务负责“输出哪些字段、是否允许调用、如何解释参数”。因此,dumpsys activity 的字段变化应回到 ActivityManagerService 的 dump 实现,而不是继续阅读 cmds/dumpsys。

1.2 IServiceManager ​

Dumpsys::listServices() 调用 sm_->listServices(priorityFlags),再在 native 侧排序。IServiceManager 的接口注释把这个调用定义为“返回所有现有服务的列表”;Android 17 的统一 backend 最终把 AIDL listServices(int dumpPriority) 转发给实际的 servicemanager,并把服务名转换为 Vector<String16>。

这带来两个边界:

  1. dumpsys -l 展示的是当前 ServiceManager 能列出的服务,不是某个固定版本的全量目录。
  2. 服务是否存在、是否已经启动、是否声明为 lazy service,是注册和启动策略的结果;dumpsys 只能按选定的行为查询它。

ServiceManager.addService() 的调用方决定服务名、Binder 对象和 dump priority。Java 侧的 android.os.ServiceManager.addService() 最终进入同一个 Binder service manager;native 服务也可以直接注册。因此服务列表的变化应追查注册者、init 启动项和产品配置,而不是修改 dumpsys 的列表逻辑。

版本边界

服务名和服务输出由 Android release、产品配置、厂商服务和运行状态共同决定。本文给出的常用服务名是诊断入口,不是 Android 17 以外版本的完整保证。

2. 服务列表 ​

2.1 Dumpsys::main ​

Dumpsys::main() 的默认值很有解释力:超时是 10000 ms,优先级过滤是 DUMP_FLAG_PRIORITY_ALL,dump 类型为空时补成 TYPE_DUMP。也就是说,裸运行 dumpsys 会尝试 dump 所有当前可见服务;dumpsys service 则只选择一个服务。

帮助文本在 dumpsys.cpp::usage() 中直接定义,Android 17 支持:

形式作用重要实现点
dumpsysdump 所有服务先列出服务,再逐个 dump
dumpsys -l只列服务调用 listServices() 后立即返回
dumpsys SERVICE [ARGS]指定一个服务并传参数第一个位置参数是服务名,其余进入 Binder dump()
--skip a,bdump 除指定服务外的全部服务位置参数被解释为跳过列表
--priority CRITICAL|HIGH|NORMAL只选择对应 dump priority同时向服务传 --dump-priority
--proto只选支持 proto dump 的服务服务列表取两个 priority 列表的交集,并向服务传 --proto
-t SEC秒级超时解析后乘以 1000
-T MS毫秒级超时直接使用毫秒值
--dump请求服务普通 dump没有其它类型时也是默认值
--pid输出服务宿主 PID不调用服务自己的 dump()
--thread输出 Binder 线程使用量通过 Binder debug 信息读取
--clients输出客户端 PID通过 Binder handle 查询
--stability输出 Binder stability读取 Binder stability 标记
-w等待服务启动只能与指定服务一起使用,可能无限等待

getopt_long() 使用 +t:T:lw,长选项没有短别名。解析完成后,位置参数的第一个被放入 services,后续参数放入 args。如果服务参数中出现旧式 --proto,代码也会把 asProto 置为真,以保持向后兼容。

源码文件:frameworks/native/cmds/dumpsys/dumpsys.cpp

相关函数/类型:Dumpsys::main

cpp
if (dumpTypeFlags == 0) {
    dumpTypeFlags = TYPE_DUMP;
}

for (int i = optind; i < argc; i++) {
    if (skipServices) {
        skippedServices.add(String16(argv[i]));
    } else if (i == optind) {
        services.add(String16(argv[i]));
    } else {
        args.add(String16(argv[i]));
    }
}

2.2 互斥选项 ​

解析后有三类非法组合:--skip 没有服务名、-w 没有指定服务、-l 同时带了服务或 skip 列表。代码调用 usage() 并返回 -1,不会尝试猜测用户意图。

例如,下面两条命令并不等价:

bash
adb shell dumpsys -l
adb shell dumpsys --skip activity window

第一条只是列目录;第二条先列出当前服务,再 dump 除 activity 和 window 外的其它服务。要只检查一个懒加载服务,应使用 -w 和服务名,而不是把它和全量 dump 混合。

2.3 listServices ​

--priority 在调用 ServiceManager 时就参与列表选择。--proto 会调用两次 listServices():一次取指定 priority 的服务,一次取 DUMP_FLAG_PROTO 服务,然后按排序后的名字做交集。它不是把普通文本转换成 proto,而是只请求明确声明支持 proto dump 的服务。

筛选完成后,setServiceArgs() 会把额外参数插入传给服务:

源码文件:frameworks/native/cmds/dumpsys/dumpsys.cpp

cpp
if (asProto) {
    args.insertAt(String16("--proto"), 0);
}
if (priorityFlags == IServiceManager::DUMP_FLAG_PRIORITY_ALL ||
    priorityFlags == IServiceManager::DUMP_FLAG_PRIORITY_NORMAL ||
    priorityFlags == IServiceManager::DUMP_FLAG_PRIORITY_DEFAULT) {
    args.insertAt(String16("-a"), 0);
}
if (priorityFlags == IServiceManager::DUMP_FLAG_PRIORITY_CRITICAL ||
    priorityFlags == IServiceManager::DUMP_FLAG_PRIORITY_HIGH ||
    priorityFlags == IServiceManager::DUMP_FLAG_PRIORITY_NORMAL) {
    args.insertAt(String16("--dump-priority"), 0);
    args.insertAt(ConvertBitmaskToPriorityType(priorityFlags), 1);
}

因此 --priority NORMAL 既改变 ServiceManager 的列表,也让服务收到 --dump-priority NORMAL -a。-a 的含义由服务解释,通常表示包含更完整的内部状态;它不是 dumpsys 自己展开的通用字段开关。

3. 服务dump ​

3.1 checkService ​

全量 dump 和普通指定服务使用 ServiceBehavior::DUMP_IF_STARTED,对应 sm_->checkService(serviceName)。服务没有运行时,checkService 返回空,dumpsys 打印 Can't find service: <name>,继续处理其它服务。

只有显式使用 -w 时才使用 sm_->waitForService(serviceName)。该路径允许 servicemanager 按 lazy service 机制启动服务,但 dumpsys.h 明确提示配置错误时可能无限等待。这个差别决定了诊断时是否允许等待启动:检查现有状态用默认行为,确认一个声明的懒加载服务能否启动才使用 -w。

3.2 pipe/线程 ​

startDumpThread() 创建匿名 pipe。读端保存到 redirectFd_,写端移动进新线程。线程先按 dumpTypeFlags 写 PID、stability、thread、clients 信息,最后在需要时调用 service->dump(remote_end.get(), args)。

服务的 Binder dump() 可能阻塞,不能直接在主线程调用;主线程必须同时负责 poll、超时和把字节写到最终 fd。BpBinder::dump() 把 fd 和参数放入 Binder transaction,远端 BBinder::onTransact() 再调用具体对象的 dump()。

3.3 dump ​

这些选项都由 dumpsys 统一调度,但数据来源不同:

选项数据来源是否调用服务自己的 dump()输出适合回答什么问题
默认/--dump目标服务对象是服务内部状态、历史、配置和统计
--pidIBinder::getDebugPid()否Binder 对象对应的宿主进程
--threadgetBinderPidInfo()否Binder 线程池使用量
--clientsBinder handle 与 getBinderClientPids()否哪些 PID 持有客户端引用
--stabilityinternal::Stability::debugToString()否Binder stability 标记

多个类型可以组合,例如 dumpsys --pid --stability activity。新线程会按位检查标志并依次写入结果;--dump 仍会作为最后一段普通服务输出。--pid 和 --clients 依赖 Binder debug 信息,返回错误时 reportDumpError() 只打印上下文和 statusToString(),不会伪造一个 PID 或客户端列表。

3.4 读取循环 ​

writeDump() 用 steady_clock 计算 deadline,每次 poll() 都重新计算剩余毫秒。读取按最多 4096 字节进行,再用 WriteFully() 写到 stdout。pipe EOF 表示服务线程关闭写端,正常结束;poll 超时返回 TIMED_OUT,普通文本模式会追加:

text
*** SERVICE 'activity' DUMP TIMEOUT (10000ms) EXPIRED ***

多服务 dump 会在每个服务前写 DUMP OF SERVICE <name>:,结束时写入耗时和本地时间。proto 模式不追加文本 timeout,因为额外文本会污染机器可解析输出。

源码文件:frameworks/native/cmds/dumpsys/dumpsys.cpp

cpp
if (rc == 0 || time_left_ms() == 0) {
    status = TIMED_OUT;
    break;
}
...
if ((status == TIMED_OUT) && (!asProto)) {
    WriteStringToFd(timeout_message, fd);
}

超时后 stopDumpThread(false) 选择 detach(),然后关闭主线程持有的 pipe 读端。这样主命令不会继续等待卡住的服务线程;服务线程最终因远端 dump 返回、写端错误或进程结束而结束。超时不是“服务没有任何问题”的证明,而是这次读取没有在 deadline 内完成。

4. 命令用法 ​

4.1 服务目录 ​

bash
adb shell dumpsys -l

-l 不调用服务的 dump(),只输出 ServiceManager 当前能检查到的服务名。服务名排序由 Dumpsys::listServices() 完成,所以输出顺序适合阅读,但不应当被脚本当成稳定编号。

下面的命令分别对应不同问题:

现象首选命令owner/消费者交叉证据
Activity 启动、task、进程状态adb shell dumpsys activityActivityManagerServiceadb logcat 中 ActivityTaskManager/ActivityManager 标签
窗口、焦点、旋转、Insetsadb shell dumpsys windowWindowManagerServicedumpsys input、SurfaceFlinger transaction 日志
包、安装、uid、组件adb shell dumpsys package <package>PackageManagerServicepm path、安装命令输出、logcat
内存压力和进程摘要adb shell dumpsys meminfoActivity/内存统计服务/proc、adb shell procrank(产品可用性取决于构建)
图形帧、渲染统计adb shell dumpsys gfxinfo <package>GraphicsStats/服务端统计SurfaceFlinger、Perfetto
电源、唤醒锁、Dozeadb shell dumpsys powerPowerManagerServicekernel wakeup、logcat
电池历史adb shell dumpsys batterystatsBatteryStatsService电量、电源 HAL、时间窗
网络连接和网络评分adb shell dumpsys connectivityConnectivityServiceip、dumpsys netstats、网络 logcat
合成、显示设备和 Layeradb shell dumpsys SurfaceFlingerSurfaceFlingerdumpsys window、HWC/DRM 日志
输入设备、焦点和事件adb shell dumpsys inputInputManagerServicewindow focus、evdev/触摸日志

服务名必须先以 dumpsys -l 和目标构建实际输出为准;例如某些产品把额外服务注册到 servicemanager,某些服务只在特定 feature 或进程启动后出现。

4.2 服务参数协议 ​

bash
adb shell dumpsys activity top
adb shell dumpsys package com.example.app
adb shell dumpsys gfxinfo com.example.app framestats

dumpsys 只把服务名之后的参数保存到 Vector<String16> args 并传给服务。top、包名和 framestats 的含义分别由 Activity、Package 和 GraphicsStats 的 dump 实现定义。因此要解释参数,应进入目标服务的 dump() 或 ShellCommand 解析,而不能从 dumpsys.cpp 推导字段含义。

-a 是一个例外:当全量、NORMAL 或 DEFAULT priority dump 时,setServiceArgs() 会自动把 -a 放到服务参数最前面。测试 PassAllFlagsToServices 和 PassAllFlagsToNormalServices 通过 mock service 精确断言了这一点。服务是否真正支持 -a,仍由服务实现决定。

4.3 --skip ​

bash
adb shell dumpsys --skip SurfaceFlinger batterystats

该命令会 dump 全部服务,但跳过 SurfaceFlinger 和 batterystats。Dumpsys::main() 在打印当前服务列表时会标记 (skipped),随后在 dump 循环中直接 continue。它不会先执行再丢弃输出,因此能减少慢服务对总时间的影响。

如果只想看少数服务,不要用很长的 skip 列表;直接指定服务名更容易复现,也更容易判断缺失是服务未注册还是被跳过。

4.4 超时单位 ​

bash
adb shell dumpsys -t 30 activity
adb shell dumpsys -T 500 window

-t 30 在解析时变成 30000 ms;-T 500 保持 500 ms。数字必须是正整数,尾随字符或零都会返回错误。对于一个偶发慢服务,先用 -T 做短超时观察,再用 -t 给出合理窗口;不要无限增大超时来掩盖服务 dump 自身的锁等待或死循环。

5. 失败路径定位 ​

5.1 服务不存在 ​

startDumpThread() 在 checkService() 或 waitForService() 得到空 Binder 时输出:

text
Can't find service: example

可能原因包括:名字拼写错误、服务尚未启动、服务按产品 feature 未注册、当前权限看不到服务,或 -w 没有被使用。诊断顺序应是:

  1. adb shell dumpsys -l | grep -w example 确认目录中是否有名字;
  2. 检查服务启动和注册 owner,而不是先检查 dump 字段;
  3. 对声明为 lazy 的服务尝试 dumpsys -w example,并设置外部命令超时;
  4. 若仍失败,查看 init、servicemanager 和目标服务日志。

5.2 空正文 ​

服务可能返回 OK 但没有写任何字节:服务的 dump 实现选择了空输出,或者参数让它没有可打印内容。Dumpsys 只统计 bytesWritten,不会把空输出解释为服务不存在。可用不带服务参数的命令与服务专用参数做对照,并查看服务实现是否需要 -a、包名、用户 ID 或 proto 参数。

5.3 timeout 分层 ​

层证据结论
查询层Can't find service没有拿到可用 Binder,尚未进入 dump
读取层DUMP TIMEOUT服务线程没有在 deadline 内完成写入,可能仍在运行
服务层Error with service ... while dumpingBinder dump 调用返回非 OK,具体错误由服务/Binder 提供

超时后主线程会 detach dump 线程并关闭读端;下一条命令是否成功取决于目标服务是否从阻塞点恢复。若每次都在同一服务超时,应转向该服务的锁、I/O、历史数据规模和权限路径,而不是只修改 dumpsys 参数。

5.4 proto输出边界 ​

--proto 只选择支持 proto 的服务,并抑制 writeDump() 的文本 timeout 消息。服务自身若把日志或错误写到同一 fd,仍可能破坏机器解析;因此应先确认该服务的 proto dump 契约,并把 stderr、返回码和输出文件分开保存。不要因为命令带了 --proto 就声称所有 Android 服务都有统一 proto schema。

5.5 shell权限 ​

adb shell 先由 adbd 建立 shell 命令,再启动设备上的 dumpsys 进程。设备端进程能看到哪些服务,受 shell UID、SELinux 域、Binder service 的访问检查和产品策略共同影响。root shell 可以观察到更多内容,但 root 并不会把服务输出字段变成稳定接口;它只改变了权限边界。

保存输出时应让设备命令和 host 重定向的边界清楚:

bash
adb shell dumpsys activity > activity.host.txt
adb shell 'dumpsys activity > /data/local/tmp/activity.device.txt'
adb pull /data/local/tmp/activity.device.txt .

第一条由 host shell 创建文件,dumpsys 的 stdout 经过 ADB 返回;第二条的重定向发生在设备 shell 内,文件权限、剩余空间和路径都由设备决定。两条命令看到的文本可能相同,但失败位置不同:第一条常见的是 host 目录不可写,第二条常见的是设备路径权限或空间不足。诊断记录应把命令、执行用户和保存位置一起记下。

对于大输出,优先指定目标服务、使用 --skip 排除已知慢服务,或先把输出保存成文件再分析。不要用 head、终端滚屏或复制粘贴判断服务是否完整;截断发生在消费者侧时,dumpsys 本身并不知道你的终端只读了部分字节。

5.6 Binder查询 ​

当问题是“服务进程是否还活着”而不是“服务内部状态是什么”时,可以先运行:

bash
adb shell dumpsys --pid activity
adb shell dumpsys --thread activity
adb shell dumpsys --clients activity
adb shell dumpsys --stability activity

这四条路径绕开服务自定义文本,分别从 Binder 对象取得宿主 PID、线程池使用量、客户端 PID 和 stability 信息。它们适合回答连接关系和进程归属问题,例如窗口服务 dump 卡住时,先确认 Binder 宿主是否存在、线程是否耗尽,再决定是否继续增加普通 dump 的 timeout。

组合选项会在同一个 dump thread 中按固定顺序写入结果;--pid --stability 的输出不是两个独立的 service 查询,也不会自动产生服务状态正文。若目标是查调用方,--clients 只能显示 Binder debug 能识别的 PID,不能替代应用层注册表、死亡通知或权限审计。拿到 PID 后还应结合 ps、/proc/<pid>/status 和 logcat 判断进程身份与生命周期。这四种查询不会替代普通 dump;它们是定位 Binder 拥塞和进程归属的窄入口。保留原始命令和输出时间,便于复核。

6. 参数测试 ​

Android 17 的 frameworks/native/cmds/dumpsys/tests/dumpsys_test.cpp 是理解行为边界的最好入口之一。测试使用 BinderMock 模拟 ServiceManager 返回的 Binder 服务,分别控制服务是否运行、dump 参数、输出内容和是否挂起。

6.1 参数传播验证 ​

PassAllFlagsToServices 注册 Locksmith 和 Valet,期望两者分别收到 {"-a"},然后调用 CallMain({"-T", "500"})。断言证明无服务名时会列出服务,并向普通全量 dump 的每个服务传播 -a。

PassPriorityFlagsToCriticalServices、PassPriorityFlagsToHighServices 和 PassAllFlagsToNormalServices 分别断言服务收到 --dump-priority CRITICAL/HIGH/NORMAL;NORMAL 额外收到 -a。这直接验证了 setServiceArgs() 的条件,而不是凭帮助文本猜测。

DumpWithProto 和 DumpWithPriorityHighAndProto 使用 mock 的 proto priority 列表,断言只有交集中的服务被 dump,并检查服务收到 --proto。它证明 --proto 是列表筛选和参数传播的组合行为,不证明真实服务的 proto 字段稳定。

6.2 Timeout验证 ​

DumpRunningServiceTimeoutInSec 让 mock 服务挂起两秒,调用 CallMain({"-t", "1", "Valet"}),断言输出包含 1000ms。DumpRunningServiceTimeoutInMs 使用 {"-T", "500", "Valet"},断言输出包含 500ms。两者共同证明秒和毫秒在入口处确实走不同转换,但不证明所有服务都能在相同时间内完成。

6.3 Dump状态 ​

DumpWithSkip 设置运行中的 running1、running4 和被跳过的 skipped3、skipped5,另有未运行的 stopped2,调用 --skip 后断言只执行未跳过且运行中的服务。ListAllServicesWithMultipleOptions、ListServiceWithMultipleOptions 则验证 --pid --stability 可以组合,输出同时包含 PID 和 stability。

WriteDumpWithoutThreadStart 直接调用 writeDump() 而未先创建 pipe,断言返回 INVALID_OPERATION。这证明 redirectFd_ 是读取阶段的前置状态,不是一个永远有效的全局 fd。

测试注册在 frameworks/native/cmds/dumpsys/TEST_MAPPING 的 dumpsys_test 中;测试覆盖的是 native 编排器和 mock Binder 行为,不覆盖每个 framework service 的权限、字段、厂商扩展或真实设备上的启动时序。

可以把这些测试整理成一张边界表,避免把“测试通过”泛化成“所有服务都可靠”:

结论arrangeactionassert未证明的范围
服务列表按目录返回mock Locksmith、Valet-l输出服务名且不调用 dump真实设备是否注册这些名字
priority 参数传播mock 服务并指定 priority--priority HIGH收到 --dump-priority HIGH服务如何解释该参数
proto 交集mock 普通和 proto 列表--proto只 dump 交集服务并收到 --protoproto payload schema
秒/毫秒 timeoutmock dump 挂起-t 1 或 -T 500输出对应毫秒数真实服务的阻塞原因
skip运行、停止和跳过服务混合--skip跳过服务不被调用服务注册变化
debug 模式组合mock Binder debug 信息--pid --stability同时有 PID 和 stability产品 Binder debug 权限
pipe 前置状态不启动 dump thread直接 writeDump()返回 INVALID_OPERATION线程退出和设备断连

这张表的作用是划清测试范围:native 测试可以确认参数和编排控制流,但服务字段、权限和产品启动策略仍需对照对应服务的测试与设备输出。

6.4 Dump验证 ​

DumpMultipleServices 用三个 mock 服务覆盖了一个运行中服务、一个未运行服务和另一个运行中服务,调用 CallMain({})。测试断言运行中的服务输出被写入,未运行服务不会导致整个命令失败。这正是实际执行裸 dumpsys 时需要牢记的行为:它是“尽可能收集当前可用服务”,不是事务式操作;单个服务缺失不会回滚已经写出的其它 section。

ListRunningServices 进一步让一个服务的 checkService() 返回 false,然后调用 -l,断言列表只打印真正拿到 Binder 的服务。因而 dumpsys -l 的“当前运行”语义来自查询时的 Binder 可用性,不等同于静态注册表,也不等同于 VINTF 声明列表。

这也解释了为什么采集脚本需要保存 stderr:stdout 可能已经包含多个成功服务,而 stderr 同时记录某个服务的查询或 dump 错误。把两个流混成一份文本,会丢失“哪些 section 成功、哪个服务失败”的阶段信息。

7. 输出检查 ​

7.1 Dump状态 ​

单条 dumpsys 是一次即时查询,服务输出在不同时间点采样;adb bugreport 则由 dumpstate 编排多个 section,并可能保存为 zip。遇到偶发问题时,可以先用目标服务快速确认现状,再生成 bugreport 保存跨服务证据。

7.2 输出稳定性 ​

服务的文本 dump 通常面向工程师和 bugreport 阅读者:字段可能增删、缩进可能变化、厂商可以追加 section,权限也可能隐藏部分内容。脚本若必须自动化,应优先使用明确的 proto、statsd、系统 API 或测试专用接口;若只能解析文本,应固定 Android release 和产品版本,并为缺字段、权限拒绝和空输出编写测试。

7.3 只读诊断流程 ​

bash
adb shell getprop ro.build.version.incremental
adb shell dumpsys -l > services.txt
adb shell dumpsys activity > activity.txt
adb shell dumpsys window > window.txt
adb logcat -d -b main -b system -v threadtime > logcat.txt
sha256sum services.txt activity.txt window.txt logcat.txt > artifacts.sha256

先保存版本和服务目录,再保存目标服务与日志,最后计算 hash。这样后续可以说明输出来自哪个版本、哪个时间窗,而不是只粘贴一段脱离上下文的结果。若某个命令超时,同时保留错误文本和实际 timeout 参数。

8. 源码入口 ​

当你需要深入某个服务时,可以按下面顺序走源码:

  1. 从 frameworks/native/cmds/dumpsys/dumpsys.cpp::Dumpsys::main 确认命令选项最终传了什么参数;
  2. 从 frameworks/native/include/binder/IServiceManager.h 和 frameworks/native/libs/binder/IServiceManager.cpp 确认服务列表、优先级和 Binder 查询的接口边界;
  3. 从 frameworks/native/libs/binder/BpBinder.cpp::dump 进入 Binder transaction;
  4. 在目标服务仓库搜索 dump(int fd, const Vector<String16>& args)、dumpLocked 或对应的 ShellCommand,确认 owner、锁和参数解析;
  5. 回到 dumpsys_test.cpp,检查该行为是否有 mock、端到端或服务测试反向约束;
  6. 用 logcat、bugreport 或 Perfetto 记录服务输出与事件时间的关系。

这条路径能避免一个常见误区:看到 dumpsys package 输出了某个字段,就把字段归因于 ADB。正确的归属是 ADB 负责 transport,dumpsys 负责 Binder dump 编排,PackageManagerService 负责字段和状态,bugreport/dumpstate 负责在更大工件中收集它。

推荐的命令选择

先用 dumpsys -l 确认服务名,再用单服务命令缩小范围;只有需要跨服务快照时才运行裸 dumpsys 或 adb bugreport。遇到 timeout,优先保留现场和时间窗,不要用无限增大的 timeout 掩盖服务内部阻塞。