调试 SDV

本指南概述了用于调试在 SDV 平台上运行的服务和应用的基本工具和技术。

连接到设备

用于连接设备并与之互动的主要工具是 Android 调试桥 (adb)。验证 adb 是否在系统的路径中。

使用网络连接

如果设备位于同一网络中,并且您知道其 IP 地址,则可以通过以太网进行连接。我们的系统专门使用网络连接进行 adb:

# Connect to the device using its IP address and default port
adb connect <device_ip_address>:5555

连接到多部设备

如需通过单个工作站管理多部设备,您可以通过不同的 IP 地址连接到这些设备。如果您在单个主机上运行多个虚拟机,则需要为每部设备分配不同的端口。为此,您需要配置每个 SDV 设备上的 adb 守护程序,使其监听唯一端口,例如,第一个设备监听 5555 端口,第二个设备监听 5556 端口。您还可以配置主机,以将这些端口转发到不同的 guest:

# Connect to the first device
adb connect <device_1_ip_address>:5555

# Connect to a second device
adb connect <device_2_ip_address>:5555

# Connect to a second VM on first device on different port
adb connect <device_1_ip_address>:5556

# List all connected devices to verify
adb devices

在运行后续 adb 命令时,您可以使用 -s 标志及其完整地址 (ip:port) 来定位特定设备:

# Run a shell command on the second VM on first device in the above example
adb -s <device_1_ip_address>:5556 shell

查看日志

当服务出现故障或行为异常时,请先检查日志。

使用 logcat(Android 日志)

logcat 显示来自系统服务、代理和服务捆绑包的日志:

# View all logs in real-time
adb logcat

# Filter logs by a specific tag (e.g., the name of your service)
adb logcat -s MyServiceTag

# Filter logs by priority (V: Verbose, D: Debug, I: Info, W: Warn, E: Error, F: Fatal)
# This shows only Error and Fatal messages for a specific tag
adb logcat MyServiceTag:E *:S

# Clear old logs before viewing new ones
adb logcat -c && adb logcat

# Show all logs and return
adb logcat -d

# Color logs by level
adb logcat -v color

# Show timestamps as time since boot
adb logcat -v monotonic

使用 dmesg(内核日志)

dmesg 显示来自 Linux 内核的日志。这对于调试以下内容至关重要:

  • 硬件和驱动程序问题
  • 内核崩溃
  • SELinux 拒绝 (avc: denied) - 也会显示在 logcat 中
# View all kernel messages
adb shell dmesg

# Follow kernel messages in real-time
adb shell dmesg -w

访问 Cuttlefish 上的日志

在 Cuttlefish 虚拟设备上运行时,您可以直接从宿主机的文件系统中访问日志文件。如果 adb 不可用,这会特别有用。启动 Cuttlefish 时,它会输出所有调试路径,您还可以使用 cvd fleet 查找所有正在运行的实例。

  • Logcat(Android 日志)cuttlefish/instances/cvd-1/logs/logcat
  • 内核日志cuttlefish/instances/cvd-1/logs/kernel.log
  • Cuttlefish 启动器日志cuttlefish/instances/cvd-1/logs/launcher.log(有助于调试 Cuttlefish 环境本身)

您可以使用 catlesstail -f 等标准命令行工具实时查看这些文件。

与 shell 和文件系统互动

获取 shell 访问权限,以便直接在设备上运行命令、检查进程状态和浏览文件系统:

# Open an interactive shell on the device
adb shell

# From within the shell, you can check running processes
ps -A

# Or check the status of a specific service managed by init
getprop init.svc.<service_name>

执行文件系统操作

使用 adb pushadb pull 在宿主机和设备之间移动文件。您可以使用此功能来部署测试脚本、更新配置或检索崩溃转储:

# Push a file from your computer to the device
adb push my_local_file.txt /data/local/tmp/

# Pull a file from the device to your computer
adb pull /data/tombstones/tombstone_00 .

调试应用 (C++/Rust)

您可以联机或离线调试应用。

使用 lldb 进行实时调试

lldb 是 Android Studio 中的默认调试器,通常是现代 C++ 和 Rust 开发的首选。

请按照以下设置步骤操作

  1. 重启 adb 以允许 root 权限(转发所需)。使用此权限设置端口转发。在主机上,运行:

    adb root
    
  2. 如果二进制文件具有调试符号,请推送未剥离的版本。您可以在 out/target/product/[SDV_FLAVOR]/symbols/[PARTITION]/bin/[EXECUTABLE] 中找到未剥离的版本。例如,如需从 system_extsdv_core_cf 上传 rpcagent,请运行:

    adb push out/target/product/sdv_core_cf/symbols/system_ext/bin/rpcagent /data/local/tmp
    

    现在,未剥离的版本位于 /data/local/tmp 中。

  3. 使用 lldbclient.py 便利脚本连接到 LLDB 客户端:

    # Run and connect a binary on device, run on host:
    lldbclient.py -r /data/local/tmp/rpcagent
    
    # Connect to an existing process with PID 2759
    lldbclient.py -p 2759
    

    此脚本可远程连接 lldb。如需查看所有可用标志,请运行 lldbclient.py。请参阅 IDE 文档,了解如何将其连接到 lldb。

分析崩溃转储 (Tombstone)

当服务崩溃时,系统会在 /data/tombstones/ 中生成一个 tombstone 文件。该文件包含堆栈轨迹、内存映射和其他关键信息。

提取 tombstone 文件,然后使用可识别符号的工具(例如 lldb 或专用脚本)通过未剥离的二进制版本对其进行分析。

分析性能

SDV 支持多种用于衡量性能的工具。

检查 CPU 和内存用量

  • top:提供按 CPU 使用情况排序的正在运行的进程的实时视图。

    adb shell top -m 10  # Show top 10 processes
    
  • procrank:详细列出特定进程或整个系统的内存用量。

    adb shell procrank
    

使用 dumpsys 获取服务状态

dumpsys 是一种强大的工具,可用于查询任何 Android 系统服务的内部状态。

# List all available services
adb shell dumpsys -l

# Dump the state of a specific service or agent
adb shell dumpsys <service_name>

使用 torq 进行性能分析

torq 是一款命令行工具,可简化 Android 上的分析和跟踪任务。它允许在系统性能分析期间运行 Simpleperf、Perfetto 和其他典型任务。

使用 Perfetto 进行跟踪

Perfetto 是 Android 上用于进行系统级跟踪的标准工具。借助 Perfetto,您可以记录系统事件、应用级跟踪点和性能计数器,并在时间轴上直观呈现这些数据。

主要步骤

  1. 在 C++ 或 Rust 代码中添加轨迹点:
    • C++:将 Perfetto SDK 与 TRACE_EVENT 宏搭配使用。
    • Rust:使用 tracing crate,该 crate 会发出与 Perfetto 兼容的 ATrace 事件。
  2. 创建一个 textproto 配置文件,以指定数据源(例如 ftracetrack_event)、类别和标记。
  3. 使用 record_android_trace 脚本 (external/perfetto/tools/record_android_trace) 和配置文件来捕获设备的轨迹。
  4. Perfetto Web 界面中打开生成的跟踪文件,以分析系统行为、识别瓶颈并了解服务互动。

如需了解有关插桩、配置和收集的详细说明,请参阅使用跟踪深入了解系统性能

使用 Simpleperf 进行性能剖析

Simpleperf 是一款通用的命令行工具,用于在 Android 上进行性能分析,类似于 Linux perf。

常见使用场景:

  • CPU 性能剖析:识别消耗最多 CPU 时间的函数。
  • Off-CPU 分析:分析线程为何处于休眠或阻塞状态。
  • 硬件计数器:访问硬件性能计数器。

示例命令:

# Go to simpleperf scripts
cd system/extras/simpleperf/scripts

# Profile system for 20 seconds and record call graph
./app_profiler.py --system_wide -r "--call-graph fp --duration 20"

# Record a profile for a specific process (PID 1)
./app_profiler.py --pid 1 -r "--call-graph fp --duration 5"

# Pull the profile data
adb pull /data/local/tmp/perf.data .

# Generate a report with symbol resolution
./report_html.py --add_source_code --source_dirs $ANDROID_BUILD_TOP --add_disassembly -o report.html

Simpleperf 还提供了更多事件和选项。如需查看全面的文档,请参阅 Simpleperf 指南

SELinux 拒绝

症状:您的服务无法启动、访问文件或使用功能,并且您在 dmesglogcat 中看到 avc: denied 消息。

[ 123.456] avc: denied { read } for pid=789 comm="my_service" name="some_file" dev="sda1" ino=123 scontext=u:r:my_service_t:s0 tcontext=u:object_r:some_file_t:s0 tclass=file permissive=0

快速调试(仅限开发)

您可以暂时停用 SELinux 强制执行,看看问题是否得到解决。这证实了问题是由于缺少 SELinux 政策规则造成的。

# Disabling SELinux requires root permission
adb root

# Set SELinux to permissive mode
adb shell setenforce 0

# Check the current mode (should return "Permissive")
adb shell getenforce

修复:在拒绝消息中,使用 Android 源代码树中的 audit2allow 工具。这会生成正确的 .te 政策规则,以添加到设备的 SELinux 配置中。