Android系统的问题分析笔记(8) - Android 系统的调试方法有哪些 ?

问题

Android 系统的调试方法有哪些?(此篇分析基础为Android 7.1.1系统源码)

概述

  偌大的Android系统中,出bug是难免的,对于开发者而言出bug不可怕,关键是要知道如何debug。此篇总结下Android系统常用的调试方法。

1 Native部分调试

1.1 标准Linux调试方法

  由于Android 系统基于 Linux 实现,在系统中可以使用 Linux 中标准的方法进行调试。在Android系统中有一个类似于 Linux 中 busybox 一样的工具集 toybox 。常用的通用 Linux 的调试方法主要包括以下几个:

  • 系统工具
  • 网络相关工具
  • /dev/设备文件 和 /sys/伪文件系统
  • 系统性能工具
  • gdb 调试

以我安装了Android 7.1.1 系统的 Nexus5手机而言,基本是toybox和toolbox工具集中的命令

hammerhead:/system/bin # ls -al|grep toybox
lrwxr-xr-x  1 root   shell          6 2022-05-17 17:39 acpi -> toybox
lrwxr-xr-x  1 root   shell          6 2022-05-17 17:39 arp -> toybox
lrwxr-xr-x  1 root   shell          6 2022-05-17 17:39 base64 -> toybox
lrwxr-xr-x  1 root   shell          6 2022-05-17 17:39 basename -> toybox
lrwxr-xr-x  1 root   shell          6 2022-05-17 17:39 blockdev -> toybox
lrwxr-xr-x  1 root   shell          6 2022-05-17 17:39 bzcat -> toybox
lrwxr-xr-x  1 root   shell          6 2022-05-17 17:39 cal -> toybox
lrwxr-xr-x  1 root   shell          6 2022-05-17 17:39 cat -> toybox
lrwxr-xr-x  1 root   shell          6 2022-05-17 17:39 chattr -> toybox
lrwxr-xr-x  1 root   shell          6 2022-05-17 17:39 chcon -> toybox
lrwxr-xr-x  1 root   shell          6 2022-05-17 17:39 chgrp -> toybox
lrwxr-xr-x  1 root   shell          6 2022-05-17 17:39 chmod -> toybox
lrwxr-xr-x  1 root   shell          6 2022-05-17 17:39 chown -> toybox
lrwxr-xr-x  1 root   shell          6 2022-05-17 17:39 chroot -> toybox
lrwxr-xr-x  1 root   shell          6 2022-05-17 17:39 chrt -> toybox
lrwxr-xr-x  1 root   shell          6 2022-05-17 17:39 cksum -> toybox
lrwxr-xr-x  1 root   shell          6 2022-05-17 17:39 clear -> toybox
lrwxr-xr-x  1 root   shell          6 2022-05-17 17:39 cmp -> toybox
lrwxr-xr-x  1 root   shell          6 2022-05-17 17:39 comm -> toybox
lrwxr-xr-x  1 root   shell          6 2022-05-17 17:39 cp -> toybox
lrwxr-xr-x  1 root   shell          6 2022-05-17 17:39 cpio -> toybox
lrwxr-xr-x  1 root   shell          6 2022-05-17 17:39 cut -> toybox
lrwxr-xr-x  1 root   shell          6 2022-05-17 17:39 date -> toybox
lrwxr-xr-x  1 root   shell          6 2022-05-17 17:39 df -> toybox
lrwxr-xr-x  1 root   shell          6 2022-05-17 17:39 diff -> toybox
lrwxr-xr-x  1 root   shell          6 2022-05-17 17:39 dirname -> toybox
lrwxr-xr-x  1 root   shell          6 2022-05-17 17:39 dmesg -> toybox
lrwxr-xr-x  1 root   shell          6 2022-05-17 17:39 dos2unix -> toybox
lrwxr-xr-x  1 root   shell          6 2022-05-17 17:39 du -> toybox
lrwxr-xr-x  1 root   shell          6 2022-05-17 17:39 echo -> toybox
lrwxr-xr-x  1 root   shell          6 2022-05-17 17:39 env -> toybox
lrwxr-xr-x  1 root   shell          6 2022-05-17 17:39 expand -> toybox
lrwxr-xr-x  1 root   shell          6 2022-05-17 17:39 expr -> toybox
lrwxr-xr-x  1 root   shell          6 2022-05-17 17:39 fallocate -> toybox
lrwxr-xr-x  1 root   shell          6 2022-05-17 17:39 false -> toybox
lrwxr-xr-x  1 root   shell          6 2022-05-17 17:39 fdisk -> toybox
lrwxr-xr-x  1 root   shell          6 2022-05-17 17:39 file -> toybox
lrwxr-xr-x  1 root   shell          6 2022-05-17 17:39 find -> toybox
lrwxr-xr-x  1 root   shell          6 2022-05-17 17:39 flock -> toybox
lrwxr-xr-x  1 root   shell          6 2022-05-17 17:39 free -> toybox
lrwxr-xr-x  1 root   shell          6 2022-05-17 17:39 freeramdisk -> toybox
lrwxr-xr-x  1 root   shell          6 2022-05-17 17:39 fsfreeze -> toybox
lrwxr-xr-x  1 root   shell          6 2022-05-17 17:39 fstype -> toybox
lrwxr-xr-x  1 root   shell          6 2022-05-17 17:39 ftpget -> toybox
lrwxr-xr-x  1 root   shell          6 2022-05-17 17:39 ftpput -> toybox
lrwxr-xr-x  1 root   shell          6 2022-05-17 17:39 getenforce -> toybox
lrwxr-xr-x  1 root   shell          6 2022-05-17 17:39 getfattr -> toybox
lrwxr-xr-x  1 root   shell          6 2022-05-17 17:39 getprop -> toybox
lrwxr-xr-x  1 root   shell          6 2022-05-17 17:39 groups -> toybox
lrwxr-xr-x  1 root   shell          6 2022-05-17 17:39 head -> toybox
lrwxr-xr-x  1 root   shell          6 2022-05-17 17:39 help -> toybox
lrwxr-xr-x  1 root   shell          6 2022-05-17 17:39 host -> toybox
lrwxr-xr-x  1 root   shell          6 2022-05-17 17:39 hostname -> toybox
lrwxr-xr-x  1 root   shell          6 2022-05-17 17:39 hwclock -> toybox
lrwxr-xr-x  1 root   shell          6 2022-05-17 17:39 id -> toybox
lrwxr-xr-x  1 root   shell          6 2022-05-17 17:39 ifconfig -> toybox
lrwxr-xr-x  1 root   shell          6 2022-05-17 17:39 inotifyd -> toybox
lrwxr-xr-x  1 root   shell          6 2022-05-17 17:39 insmod -> toybox
lrwxr-xr-x  1 root   shell          6 2022-05-17 17:39 install -> toybox
lrwxr-xr-x  1 root   shell          6 2022-05-17 17:39 ionice -> toybox
lrwxr-xr-x  1 root   shell          6 2022-05-17 17:39 iorenice -> toybox
lrwxr-xr-x  1 root   shell          6 2022-05-17 17:39 kill -> toybox
lrwxr-xr-x  1 root   shell          6 2022-05-17 17:39 killall -> toybox
lrwxr-xr-x  1 root   shell          6 2022-05-17 17:39 ln -> toybox
lrwxr-xr-x  1 root   shell          6 2022-05-17 17:39 load_policy -> toybox
lrwxr-xr-x  1 root   shell          6 2022-05-17 17:39 logname -> toybox
lrwxr-xr-x  1 root   shell          6 2022-05-17 17:39 losetup -> toybox
lrwxr-xr-x  1 root   shell          6 2022-05-17 17:39 ls -> toybox
lrwxr-xr-x  1 root   shell          6 2022-05-17 17:39 lsattr -> toybox
lrwxr-xr-x  1 root   shell          6 2022-05-17 17:39 lsmod -> toybox
lrwxr-xr-x  1 root   shell          6 2022-05-17 17:39 lsof -> toybox
lrwxr-xr-x  1 root   shell          6 2022-05-17 17:39 lspci -> toybox
lrwxr-xr-x  1 root   shell          6 2022-05-17 17:39 lsusb -> toybox
lrwxr-xr-x  1 root   shell          6 2022-05-17 17:39 makedevs -> toybox
lrwxr-xr-x  1 root   shell          6 2022-05-17 17:39 md5sum -> toybox
lrwxr-xr-x  1 root   shell          6 2022-05-17 17:39 mkdir -> toybox
lrwxr-xr-x  1 root   shell          6 2022-05-17 17:39 mkfifo -> toybox
lrwxr-xr-x  1 root   shell          6 2022-05-17 17:39 mknod -> toybox
lrwxr-xr-x  1 root   shell          6 2022-05-17 17:39 mkswap -> toybox
lrwxr-xr-x  1 root   shell          6 2022-05-17 17:39 mktemp -> toybox
lrwxr-xr-x  1 root   shell          6 2022-05-17 17:39 modinfo -> toybox
lrwxr-xr-x  1 root   shell          6 2022-05-17 17:39 more -> toybox
lrwxr-xr-x  1 root   shell          6 2022-05-17 17:39 mount -> toybox
lrwxr-xr-x  1 root   shell          6 2022-05-17 17:39 mountpoint -> toybox
lrwxr-xr-x  1 root   shell          6 2022-05-17 17:39 mv -> toybox
lrwxr-xr-x  1 root   shell          6 2022-05-17 17:39 nbd-client -> toybox
lrwxr-xr-x  1 root   shell          6 2022-05-17 17:39 nc -> toybox
lrwxr-xr-x  1 root   shell          6 2022-05-17 17:39 netcat -> toybox
lrwxr-xr-x  1 root   shell          6 2022-05-17 17:39 netstat -> toybox
lrwxr-xr-x  1 root   shell          6 2022-05-17 17:39 nice -> toybox
lrwxr-xr-x  1 root   shell          6 2022-05-17 17:39 nl -> toybox
lrwxr-xr-x  1 root   shell          6 2022-05-17 17:39 nohup -> toybox
lrwxr-xr-x  1 root   shell          6 2022-05-17 17:39 nproc -> toybox
lrwxr-xr-x  1 root   shell          6 2022-05-17 17:39 od -> toybox
lrwxr-xr-x  1 root   shell          6 2022-05-17 17:39 partprobe -> toybox
lrwxr-xr-x  1 root   shell          6 2022-05-17 17:39 paste -> toybox
lrwxr-xr-x  1 root   shell          6 2022-05-17 17:39 patch -> toybox
lrwxr-xr-x  1 root   shell          6 2022-05-17 17:39 pgrep -> toybox
lrwxr-xr-x  1 root   shell          6 2022-05-17 17:39 pidof -> toybox
lrwxr-xr-x  1 root   shell          6 2022-05-17 17:39 pivot_root -> toybox
lrwxr-xr-x  1 root   shell          6 2022-05-17 17:39 pkill -> toybox
lrwxr-xr-x  1 root   shell          6 2022-05-17 17:39 pmap -> toybox
lrwxr-xr-x  1 root   shell          6 2022-05-17 17:39 printenv -> toybox
lrwxr-xr-x  1 root   shell          6 2022-05-17 17:39 printf -> toybox
lrwxr-xr-x  1 root   shell          6 2022-05-17 17:39 pwd -> toybox
lrwxr-xr-x  1 root   shell          6 2022-05-17 17:39 pwdx -> toybox
lrwxr-xr-x  1 root   shell          6 2022-05-17 17:39 readahead -> toybox
lrwxr-xr-x  1 root   shell          6 2022-05-17 17:39 readlink -> toybox
lrwxr-xr-x  1 root   shell          6 2022-05-17 17:39 realpath -> toybox
lrwxr-xr-x  1 root   shell          6 2022-05-17 17:39 renice -> toybox
lrwxr-xr-x  1 root   shell          6 2022-05-17 17:39 reset -> toybox
lrwxr-xr-x  1 root   shell          6 2022-05-17 17:39 resize -> toybox
lrwxr-xr-x  1 root   shell          6 2022-05-17 17:39 restorecon -> toybox
lrwxr-xr-x  1 root   shell          6 2022-05-17 17:39 rev -> toybox
lrwxr-xr-x  1 root   shell          6 2022-05-17 17:39 rfkill -> toybox
lrwxr-xr-x  1 root   shell          6 2022-05-17 17:39 rm -> toybox
lrwxr-xr-x  1 root   shell          6 2022-05-17 17:39 rmdir -> toybox
lrwxr-xr-x  1 root   shell          6 2022-05-17 17:39 rmmod -> toybox
lrwxr-xr-x  1 root   shell          6 2022-05-17 17:39 route -> toybox
lrwxr-xr-x  1 root   shell          6 2022-05-17 17:39 runcon -> toybox
lrwxr-xr-x  1 root   shell          6 2022-05-17 17:39 sed -> toybox
lrwxr-xr-x  1 root   shell          6 2022-05-17 17:39 seq -> toybox
lrwxr-xr-x  1 root   shell          6 2022-05-17 17:39 setenforce -> toybox
lrwxr-xr-x  1 root   shell          6 2022-05-17 17:39 setfattr -> toybox
lrwxr-xr-x  1 root   shell          6 2022-05-17 17:39 setprop -> toybox
lrwxr-xr-x  1 root   shell          6 2022-05-17 17:39 setsid -> toybox
lrwxr-xr-x  1 root   shell          6 2022-05-17 17:39 sha1sum -> toybox
lrwxr-xr-x  1 root   shell          6 2022-05-17 17:39 sha224sum -> toybox
lrwxr-xr-x  1 root   shell          6 2022-05-17 17:39 sha256sum -> toybox
lrwxr-xr-x  1 root   shell          6 2022-05-17 17:39 sha384sum -> toybox
lrwxr-xr-x  1 root   shell          6 2022-05-17 17:39 sha512sum -> toybox
lrwxr-xr-x  1 root   shell          6 2022-05-17 17:39 sleep -> toybox
lrwxr-xr-x  1 root   shell          6 2022-05-17 17:39 sort -> toybox
lrwxr-xr-x  1 root   shell          6 2022-05-17 17:39 split -> toybox
lrwxr-xr-x  1 root   shell          6 2022-05-17 17:39 stat -> toybox
lrwxr-xr-x  1 root   shell          6 2022-05-17 17:39 strings -> toybox
lrwxr-xr-x  1 root   shell          6 2022-05-17 17:39 swapoff -> toybox
lrwxr-xr-x  1 root   shell          6 2022-05-17 17:39 swapon -> toybox
lrwxr-xr-x  1 root   shell          6 2022-05-17 17:39 sync -> toybox
lrwxr-xr-x  1 root   shell          6 2022-05-17 17:39 sysctl -> toybox
lrwxr-xr-x  1 root   shell          6 2022-05-17 17:39 tac -> toybox
lrwxr-xr-x  1 root   shell          6 2022-05-17 17:39 tail -> toybox
lrwxr-xr-x  1 root   shell          6 2022-05-17 17:39 tar -> toybox
lrwxr-xr-x  1 root   shell          6 2022-05-17 17:39 taskset -> toybox
lrwxr-xr-x  1 root   shell          6 2022-05-17 17:39 tee -> toybox
lrwxr-xr-x  1 root   shell          6 2022-05-17 17:39 telnet -> toybox
lrwxr-xr-x  1 root   shell          6 2022-05-17 17:39 test -> toybox
lrwxr-xr-x  1 root   shell          6 2022-05-17 17:39 time -> toybox
lrwxr-xr-x  1 root   shell          6 2022-05-17 17:39 timeout -> toybox
lrwxr-xr-x  1 root   shell          6 2022-05-17 17:39 touch -> toybox
-rwxr-xr-x  1 root   shell     358624 2022-05-17 17:39 toybox
lrwxr-xr-x  1 root   shell          6 2022-05-17 17:39 tr -> toybox
lrwxr-xr-x  1 root   shell          6 2022-05-17 17:39 traceroute -> toybox
lrwxr-xr-x  1 root   shell          6 2022-05-17 17:39 true -> toybox
lrwxr-xr-x  1 root   shell          6 2022-05-17 17:39 truncate -> toybox
lrwxr-xr-x  1 root   shell          6 2022-05-17 17:39 tty -> toybox
lrwxr-xr-x  1 root   shell          6 2022-05-17 17:39 tunctl -> toybox
lrwxr-xr-x  1 root   shell          6 2022-05-17 17:39 ulimit -> toybox
lrwxr-xr-x  1 root   shell          6 2022-05-17 17:39 umount -> toybox
lrwxr-xr-x  1 root   shell          6 2022-05-17 17:39 uname -> toybox
lrwxr-xr-x  1 root   shell          6 2022-05-17 17:39 uniq -> toybox
lrwxr-xr-x  1 root   shell          6 2022-05-17 17:39 unix2dos -> toybox
lrwxr-xr-x  1 root   shell          6 2022-05-17 17:39 uptime -> toybox
lrwxr-xr-x  1 root   shell          6 2022-05-17 17:39 usleep -> toybox
lrwxr-xr-x  1 root   shell          6 2022-05-17 17:39 vconfig -> toybox
lrwxr-xr-x  1 root   shell          6 2022-05-17 17:39 vmstat -> toybox
lrwxr-xr-x  1 root   shell          6 2022-05-17 17:39 watch -> toybox
lrwxr-xr-x  1 root   shell          6 2022-05-17 17:39 wc -> toybox
lrwxr-xr-x  1 root   shell          6 2022-05-17 17:39 which -> toybox
lrwxr-xr-x  1 root   shell          6 2022-05-17 17:39 whoami -> toybox
lrwxr-xr-x  1 root   shell          6 2022-05-17 17:39 xargs -> toybox
lrwxr-xr-x  1 root   shell          6 2022-05-17 17:39 xxd -> toybox
lrwxr-xr-x  1 root   shell          6 2022-05-17 17:39 xzcat -> toybox
lrwxr-xr-x  1 root   shell          6 2022-05-17 17:39 yes -> toybox

hammerhead:/system/bin # ls -al|grep toolbox
lrwxr-xr-x  1 root   shell          7 2022-05-16 19:11 dd -> toolbox
lrwxr-xr-x  1 root   shell          7 2022-05-16 19:11 getevent -> toolbox
lrwxr-xr-x  1 root   shell          7 2022-05-16 19:11 iftop -> toolbox
lrwxr-xr-x  1 root   shell          7 2022-05-16 19:11 ioctl -> toolbox
lrwxr-xr-x  1 root   shell          7 2022-05-16 19:11 log -> toolbox
lrwxr-xr-x  1 root   shell          7 2022-05-16 19:11 nandread -> toolbox
lrwxr-xr-x  1 root   shell          7 2022-05-16 19:11 newfs_msdos -> toolbox
lrwxr-xr-x  1 root   shell          7 2022-05-16 19:11 prlimit -> toolbox
lrwxr-xr-x  1 root   shell          7 2022-05-16 19:11 ps -> toolbox
lrwxr-xr-x  1 root   shell          7 2022-05-16 19:11 restart -> toolbox
lrwxr-xr-x  1 root   shell          7 2022-05-16 19:11 sendevent -> toolbox
lrwxr-xr-x  1 root   shell          7 2022-05-16 19:11 start -> toolbox
lrwxr-xr-x  1 root   shell          7 2022-05-16 19:11 stop -> toolbox
-rwxr-xr-x  1 root   shell      88960 2022-05-16 19:11 toolbox
lrwxr-xr-x  1 root   shell          7 2022-05-16 19:11 top -> toolbox

1.1.1 系统工具

如:ps、mount、umount、lsmod、kill等命令。

1.1.2 网络相关工具

如:ifconfig、netstat、rouute、iftopd等命令。

1.1.3 /dev/设备文件 和 /sys/伪文件系统

  Linux 系统设备文件位于 /dev/ 目录中,设备相关的信息位于 /sys 文件系统中。这些文件内容都是当前设备信息的反映,通过他们可以得知很多系统状态。

1.1.4 系统性能工具

  如:vmstat、top等命令。

  vmstat (Virtual Meomory Statistics,虚拟内存统计),命令报告关于内核线程、虚拟内存磁盘、陷阱和 CPU 活动的统计信息。由 vmstat 命令生成的报告可以用于平衡系统负载活动。vmstat 在运行过程中,会定期打出一行的内容,表示系统当前的运行状态:

  • r: 在运行队列中等待的进程数。
  • b: 在等待 的进程数。
  • w: 可以进运行队列被替换的进程。
  • free: 空闲的内存(单位 k)。
  • mapped: 影射的内存(单位 k)。
  • in: 每秒的中断数,包括时钟中断。
  • cs: 每秒的环境(上下文)切换次数。
  • us: CPU使用时间。
  • sy: CPU系统使用时间。
  • id: 闲置时间。
hammerhead:/system/bin # vmstat
procs -----------memory---------- ---swap-- -----io---- -system-- ----cpu----
 r  b   swpd   free   buff  cache   si   so    bi    bo   in   cs us sy id wa
 2  0      0 591988  34120 526384    0    0     3     2    0   35  0  0 99  0

  top 命令表示列出系统最耗资源的进程,主要可以检测各个进程对 CPU 的消耗情况,信息将一屏一屏的阶段性地打印到终端上。打印出的信息中,VSS (Virtual Set Size) 表示进程使用的虚拟内存,RSS(Resident Set Size) 表示进程使用的物理内存。

hammerhead:/system/bin # top
User 3%, System 10%, IOW 0%, IRQ 0%
User 1 + Nice 0 + Sys 3 + Idle 25 + IOW 0 + IRQ 0 + SIRQ 0 = 29

  PID USER     PR  NI CPU% S  #THR     VSS     RSS PCY Name
19907 root     20   0  27% R     1   4560K   1552K  fg top
 1462 root     13  -7   3% S     7  11380K   1444K  fg /system/bin/mpdecision
    3 root     20   0   0% S     1      0K      0K  fg ksoftirqd/0
    7 root      0 -20   0% D     1      0K      0K  fg kworker/u:0H
    8 root     RT   0   0% S     1      0K      0K  fg migration/0
   13 root      0 -20   0% S     1      0K      0K  fg khelper
   14 root      0 -20   0% S     1      0K      0K  fg netns
   18 root      0 -20   0% S     1      0K      0K  fg modem_notifier
......

1.1.5 gdb 调试

  在Android 中 gdb 调试的方法和标准的 Linux 相同,主机运行 gdb 调试程序,目标机运行 gdbserver。需要找到某种主机到目标机的连接方式。例如,在目标系统使用网络的 adb情况下,adb 默认使用 5555 端口,可以将其他端口给 gdb 使用。

hammerhead:/system/bin # ls gdbserver -al
-rwxr-xr-x 1 root shell 596484 2022-05-16 19:11 gdbserver

hammerhead:/system/bin # ./gdbserver
Usage:  gdbserver [OPTIONS] COMM PROG [ARGS ...]
        gdbserver [OPTIONS] --attach COMM PID
        gdbserver [OPTIONS] --multi COMM

COMM may either be a tty device (for serial debugging),
HOST:PORT to listen for a TCP connection, or '-' or 'stdio' to use
stdin/stdout of gdbserver.
PROG is the executable program.  ARGS are arguments passed to inferior.
PID is the process ID to attach to, when --attach is specified.

Operating modes:

  --attach              Attach to running process PID.
  --multi               Start server without a specific program, and
                        only quit when explicitly commanded.
  --once                Exit after the first connection has closed.
  --help                Print this message and then exit.
  --version             Display version information and exit.

Other options:

  --wrapper WRAPPER --  Run WRAPPER to start new programs.
  --disable-randomization
                        Run PROG with address space randomization disabled.
  --no-disable-randomization
                        Don't disable address space randomization when
                        starting PROG.

Debug options:

  --debug               Enable general debugging output.
  --debug-format=opt1[,opt2,...]
                        Specify extra content in debugging output.
                          Options:
                            all
                            none
                            timestamp
  --remote-debug        Enable remote protocol debugging output.
  --disable-packet=opt1[,opt2,...]
                        Disable support for RSP packets or features.
                          Options:
                            vCont, Tthread, qC, qfThreadInfo and
                            threads (disable all threading packets).

For more information, consult the GDB manual (available as on-line
info or a printed manual).

  gdbserver 的使用方式有两种,一种是命令行启动一个应用,另一种是连接(attach)到个已经运行进程上。COMM 表示连接的类型,可以使用一个TTY 设备或者 HOST:PORT 格式的 TCP 网络连接。与标准的 gdb 使用相同,需要对所调试的代码以 -g 的方式进行编译和连接,表示生成调试版本。

Android 主机系统使用 gdb 可以通过系统的封装进行:

source build/envsetup.sh
gdbclient <port> <pid>

例如,通常按照如下的方式来进行使用,将自动启动目标机的 gdbserver

gdbclient :5039 1

1.2 使用log的方法

1.2.1 写入 log

  Android 中本地代码 log 使用统一的方式。头文件是 system/core/include/cutils/log.h,实际上 libutils 的 Log.h 包含了 log.h。

在 Android 的纯 C 代码中,通常使用如下的方式加入 log:

//#define LOG_NDEBUG 0
#define LOG_TAG "XXX"		//Log的前缀
#include 		//包含C工具库的头文件

在 Android 的 C++ 代码中,通常使用如下的方式加入 log:

//#define LOG_NDEBUG 0
#define LOG TAG "XXX"		//Log 的前缀
#include 		//包含 C++工具库的头文件

  将 #define LOG_NDEBUG 0 的注释取消,即可获得 DEBUG 的调试信息,而 LOGT_AG表示调试信息的前缀。LOGV()、LOGD()、LOGI()、LOGW() 和 LOGE() 表示 5 个不同的 log 级别。

1.2.2 查看 log

  使用logcat命令

hammerhead:/system/bin # logcat --help
Usage: logcat [options] [filterspecs]
options include:
  -s              Set default filter to silent. Equivalent to filterspec '*:S'
  -f <file>, --file=<file>               Log to file. Default is stdout
  -r <kbytes>, --rotate-kbytes=<kbytes>
                  Rotate log every kbytes. Requires -f option
  -n <count>, --rotate-count=<count>
                  Sets max number of rotated logs to <count>, default 4
  -v <format>, --format=<format>
                  Sets the log print format, where <format> is:
                    brief color epoch long monotonic printable process raw
                    tag thread threadtime time uid usec UTC year zone
  -D, --dividers  Print dividers between each log buffer
  -c, --clear     Clear (flush) the entire log and exit
                  if Log to File specified, clear fileset instead
  -d              Dump the log and then exit (don't block)
  -e , --regex=
                  Only print lines where the log message matches 
                  where  is a regular expression
  -m , --max-count=
                  Quit after printing  lines. This is meant to be
                  paired with --regex, but will work on its own.
  --print         Paired with --regex and --max-count to let content bypass
                  regex filter but still stop at number of matches.
  -t       Print only the most recent  lines (implies -d)
  -t '<time>'     Print most recent lines since specified time (implies -d)
  -T       Print only the most recent  lines (does not imply -d)
  -T '<time>'     Print most recent lines since specified time (not imply -d)
                  count is pure numerical, time is 'MM-DD hh:mm:ss.mmm...'
                  'YYYY-MM-DD hh:mm:ss.mmm...' or 'sssss.mmm...' format
  -g, --buffer-size                      Get the size of the ring buffer.
  -G , --buffer-size=
                  Set size of log ring buffer, may suffix with K or M.
  -L, -last       Dump logs from prior to last reboot
  -b , --buffer=         Request alternate ring buffer, 'main',
                  'system', 'radio', 'events', 'crash', 'default' or 'all'.
                  Multiple -b parameters or comma separated list of buffers are
                  allowed. Buffers interleaved. Default -b main,system,crash.
  -B, --binary    Output the log in binary.
  -S, --statistics                       Output statistics.
  -p, --prune     Print prune white and ~black list. Service is specified as
                  UID, UID/PID or /PID. Weighed for quicker pruning if prefix
                  with ~, otherwise weighed for longevity if unadorned. All
                  other pruning activity is oldest first. Special case ~!
                  represents an automatic quicker pruning for the noisiest
                  UID as determined by the current statistics.
  -C              colored output
  -P '<list> ...', --prune='<list> ...'
                  Set prune white and ~black list, using same format as
                  listed above. Must be quoted.
  --pid=     Only prints logs from the given pid.
  --wrap          Sleep for 2 hours or when buffer about to wrap whichever
                  comes first. Improves efficiency of polling by providing
                  an about-to-wrap wakeup.

filterspecs are a series of
  [:priority]

where  is a log component tag (or * for all) and priority is:
  V    Verbose (default for )
  D    Debug (default for '*')
  I    Info
  W    Warn
  E    Error
  F    Fatal
  S    Silent (suppress all output)

'*' by itself means '*:D' and  by itself means :V.
If no '*' filterspec or -s on command line, all filter defaults to '*:V'.
eg: '*:S <tag>' prints only , '<tag>:S' suppresses all <tag> log messages.

If not specified on the command line, filterspec is set from ANDROID_LOG_TAGS.

If not specified with -v on command line, format is set from ANDROID_PRINTF_LOG
or defaults to "threadtime"

1.3 Android特有调试方法

  在Android 有一些用于调试非标准化的辅助手段,不是 Linux 的 shell 标准的内容,而是 Android 系统特有的内容。

1.3.1 几个单独的工具

  1. netcfg
    netcfg 主要可以将某个网络接口,也就是网络设备,配置为上行、下行或者 DHCP(动态得到网络地址)。
  2. vdc
    vold的辅助工具。
  3. ndc
    netd的辅助工具。
  4. sdcard
    sdcard 工具用于让设备模拟 SD 卡。

1.3.2 系统信息获取工具

Android 中还提供了 dumpstate、dumpsys、bugreport 等几个工具,用于查看系统各个方面的信息。

  • dumpstate 工具用于将系统设备的状态导出,通过访问 sys 文件系统和调用其他工具来完成;
  • dumpsys 工具将回导出系统服务各个方面的信息,输出的内容也是比较多;
  • bugreport 工具提供类似功能,实际上调用了 dumpstate 工具;

1.3.3 toolbox中的特殊工具

  1. setprop/getprop
    设置/获取系统属性值。
  2. start/stop
    用于开始或停止init进程中的service。这两个工具的本质是设置 ctl.start.<服务名> 和 ctl.stop.<服务名> 属性,init 进程会对其进行处理。
  3. ioctl
    用户可以直接控制设备节点的 ioctl 命令,这样可以在没有写程序的情况下进行一些测试工作。
    hammerhead:/system/bin # ioctl -h
    ioctl [-l <length>] [-a <argsize>] [-rdh] <device> <ioctlnr>
    -l <length>   Length of io buffer
    -a <argsize>  Size of each argument (1-8)
    -r            Open device in read only mode
    -d            Direct argument (no iobuffer)
    -h            Print help
    
    hammerhead:/system/bin # ioctl -l 16 -r /dev/graphics/fb0 0x4600
    sending ioctl 0x4600 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 to /dev/graphics/fb0
    return buf: 38 04 00 00 80 07 00 00 38 04 00 00 00 0f 00 00
    
    使用 ioctl 程序,查看 framebuffer 驱动的情况。linux/fb.h 头文件中定义了获取 framebufer 信息的 ioctl 命令FBIOGET_VSCREENINFO 为 0x4600,并且 FBIOGET_VSCREENINFO 这个 ioctl 命令使用的结构体为 struct fb_var_screeninf,前面几个字节的内容如下所示:
    struct fb_var_screeninfo {
    	__u32 xres;				/*可见分辨率*/
    	__u32 yres;
    	__u32 xres_virtual;		/*虚拟分辨率 */
    	__u32 yres_virtual;
    	......
    }
    
    以上的命令使用读取了 16 字节的消息,得到的信息表示屏幕可见的宽为 1080(0x438)可见的高为 1920 (0x780),虚拟的宽为 1080 (0x438),虚拟的高为 3840(0xf00),这就是双缓冲的屏幕。

1.3.4 service 工具

参考此篇:Android 系统的分区和文件系统(5)- Android Framework层上的工具和命令

1.3.5 strace 工具

系统调用相关的调试工具。

2 Java部分调试

2.1 Java中使用Log

import android.util.Log;

2.2 Java层的命令行工具

  如:am、pm、dalvikvm等命令,注意 dalvikvm 与前两者的区别。参考此篇:Android 系统的分区和文件系统(5)- Android Framework层上的工具和命令

2.3 代码内容相关的调试

  Android 的 Java 代码中有一些特殊的调试方法。调试的基础实际上是利用 Dalvik 虚拟机提供的特殊机制,让外部的程序与之进行交互。

2.3.1 基本调试

  android.os 包中的 Debug 是一个与调试相关的类。Debug 类中有众多的 get 函数用于获得系统的各种信息。例如,线程相关信息,统计内存等。源码位置:Frameworks/base/core/java/android/os/Debug.java,Debug 类中一些常用的方法如下所示:

// 获得 Java 虚拟机相关的内存信息:空闲数目,空闲大小,垃圾回收调用次数,类数目
public static int getGlobalFreedCount() {...}
public static int getGlobalFreedSize() {...}
public static int getGlobalGcInvocationCount() {...}

public static int getLoadedClassCount() {...}
// 获取本地堆内存信息
public static native long getNativeHeapAllocatedSize();
public static native long getNativeHeapSize();
public static native long getNativeHeapFreeSize();
//获得线程的信息,线程的数目和大小
public static int getThreadAllocCount() {...}
public static int getThreadAllocSize() {...}

  与上述方法相对应的是一系列 resetXXX() 方法,用于重设上述相关的信息。不过上面这些方法都已经是@Deprecated的了,另有一个比较高级的方法如下所示:

public static native void getMemoryInfo(MemoryInfo memoryInfo);

  Debug.MemoryInfo 是一个静态的类,这个类当中包含了一些公共的域。它们表示了当前进程内存映射的信息。
场景分为3 种:虚拟机 (dalvik)、本地 (native) 和其他 (other);
内存类型分为3种:Pss 表示实际使用的物理内存(Proportional Set Size),PrivateDirty 表示私有的脏页, SharedDirty 表示共享脏页。

Debug.InstructionCount 是另外一个子类,用于计算程序中执行的指令数目和方法数目。简单的示例如下:

import android.os.Debug;
import android.os.Debug.InstructionCount;

	Debug.InstructionCount icount = new Debug.InstructionCount();
	icount.resetAndstart();
	//执行实际的工作
	if (icount.collect()) {					// 返回 boolean 类型,表示是否采集到
		icount.globalTotal();				// 指令(Instruction)执行的总数
		icount.globalMethodInvocations();	// 方法(Method)执行的总数
	}

  由于具体方法和指令消耗的资源并不能严格按照“数目”衡量。在一般情况下,InstructionCount 仅仅用于粗略统计一个程序耗费 CPU 的情况。
  Debug 执行的精确度比较高,在代码中加入 Debug 类之后,可以实时获得当前进程运行情况的信息。
  Dalvik 虚拟机具有特殊信号处理器,因此对任何 Android 中的 Java 进程发送的一些信号,具有特殊的含义,可以用来调试应用程序。

  • 发送 SIGQUIT 信号(3) : /data/misc/anr/ 生成 trace 信息;
  • 发送 SIGUSR1 信号 (10) : 回收资源 (GC)。

2.3.2 程序跟踪

  Debug 类的一个主要功能是获取 Android Java 代码运行过程中的跟踪文件。得到跟踪文件之后,可以进一步通过文件分析代码运行的资源占用率等信息。

import android.os.Debug;
android.os.Debug.startMethodTracing("/data/trace/test"); // test,trace
/* ...需要剖析的内容... */
android.os.Debug.stopMethodTracing();

  在 startMethodTracing() 和 stopMethodTracing() 之间运行的代码,就是被跟踪文件提供运行信息的部分。其中 startMethodTracing() 的参数是输出跟踪文件的路径,如果不加路径,默认的路径就是 /sdcard/ 。如果增加了类似 /data/trace/ 根式的目录,则要求这个目录应用程序可以写。运行后将在目录中生成后缀名为 trace 的文件,例如 test.trace 文件。
将跟踪文件 (*.trace) 导入到主机,可以使用 Android 中提供的 traceview 工具分析其内容。

2.3.3 Hprof 分析

  HProf 工具用于对内存的剖析 (Heap Profile),实际上包含了部分内存和 CPU 的运行的信息。Debug 类具有如下的方法用于获得 HProf 文件:

    public static void dumpHprofData(String fileName) throws IOException {
        VMDebug.dumpHprofData(fileName);
    }

在代码中,通常需要加入如下的内容来获得HProf:

import android.os.Debug;
import java.io.IOException;
	try {
		android.os.Debug.dumpHprofData(/data/hprof/test.hprof”}
	catch (IOException ioe) {//异常处理}
	}

  test.hprof 文件就是执行后的输出结果。这个文件不能被直接处理,需要使用 Android 中的 hprod-conv 工具将其转化,方法如下所示:

hprof-conv test.hprof out.hprof

  获得 out.hprof 文件之后,可以使用 Java 中一种名称为 MAT(Memory Analyzer Tool) 的工具来进行分析。MAT 工具可以直接列出值得怀疑的问题(Problem Suspect)。

2.3.4 采样剖析器

  libcore/dalvik/src/main/java/dalvik/system/SamplingProfiler.java,至少Android7 已经没有了

2.3.5 严格模式的检查

  StrictMode 可以检查应用程序主线程(UI线程)中的意外,并引起开发者的注意,以此让开发者修复它们。例如:耗费时间较多的磁盘读/写、网络访问等。
  StrictMode 是 android.os 包中的一个类。打开了 StrictMode 的检测功能之后,如果程序中有违反策略的操作,程序运行时就会提示开发者。StrictMode 负责两个方面的策略,一个是线程方面的策略,一个是虚拟机(vm)方面的策略。类中主要包含以下几个静态的方法:

    public static void enableDefaults()
    public static ThreadPolicy getThreadPolicy()
    public static void setThreadPolicy(final ThreadPolicy policy)
    public static VmPolicy getVmPolicy()
    public static void setVmPolicy(final VmPolicy policy)

  enableDefaults() 表示使用默认的严格模式策略,所有违反策略的情况使用 Log 来进行记录。StrictMode.ThreadPolicy 和 StrictMode.VmPolicy 是 StrictMode 的两个子类,分别用于表示线程和虚拟机两个方面的策略。它们都使用其中的 build 子类来构建。StrictMode.ThreadPolicy 的 build 子类的主要方法如下所示:

public StrictMode.ThreadPolicy.Builder detectAll()			//所有的
public StrictMode.ThreadPolicy.Builder permitAll()
public StrictMode.ThreadPolicy.Builder detectDiskReads()	//磁盘读
public StrictMode.ThreadPolicy.Builder permitDiskReads()
public StrictMode.ThreadPolicy.Builder detectDiskWrites()	//磁盘写
public StrictMode.ThreadPolicy.Builder permitDiskWrites()
public StrictMode.ThreadPolicy.Builder detectNetwork()		//网络访问
public StrictMode.ThreadPolicy.Builder permitNetwork()

  detect 和 permit 二者对应,前者表示进行检测,如果情况发生就会有所反应,后者表示允许所有,对情况不做出反应。操作分成磁盘读、磁盘写和网络访问,all 表示所有的。如果实施了检测,出现违反规则情况,将造成的结果,也称为“处罚 (penalty)”。几个方法用于设置违反规则造成的结果,如下所示:

public StrictMode.ThreadPolicy.Builder penaltyLog()			//在Log中提示
public StrictMode.ThreadPolicy.Builder penaltyDialog()		//在对话框提示
public StrictMode.ThreadPolicy.Builder penaltyDropBox()		//在DropBox提示
public StrictMode.ThreadPolicy.Builder penaltyDeath()		//让程序崩溃

一个程序设置线程策略可以如下所示:

	StrictMode.ThreadPolicy policy = new StrictMode.ThreadPolicy.Builder()
										.detectAl1().penaltyLog().build();	//检测所有,使用Log提示
	StrictMode.setThreadPolicy(policy);

StrictMode.VmPolicy 的 build 子类的主要方法如下所示:

	public StrictMode.VmPolicy.Builder detectAll()
	public StrictMode.VmPolicy.Builder detectLeakedSqlliteObjects()

  主要针对了使用 SQLiteCursor 等数据库的对象,并且使用完成还没有关闭的情况。StrictMode.VmPolicy 的 build 子类的几个处罚和 StrictMode.ThreadPolicy 中类似。一个程序设置虚拟机策略可以如下所示:

	StrictMode.VmPolicy policy = new StrictMode.VmPolicy.Builder()
										.detectAll().penaltyLog().build(); //检测所有,使用Log提示
	StrictMode.setVmPolicy(policy);

  StrictMode 的设置通常在 Application、Activity 或者其他组件的 onCreate() 方法中进行设置。这种设置应该仅限于开发者调试的模式。StrictMode 的提示是在尽量严格的情况下对程序中可能出现的问题的一种提示,并不表示所有违反规则的情况都需要修正。当进行发布的时候,不应该使用任何的严格模式限定。
  StrictMode 的原理主要是在 Java 框架层代码中加入了检查机制。如果使用 JNI 操作磁盘和访问网络,则不在StrictMode 的检查范围中。此外,如果通过 Binder 进行远程调用,也不在 StrictMode 的检查范围中

2.4 DDMS 工具

  用于APP动态调试,此工具在AndroidStudio安装后的SDK/tools中。

2.5 HierarchyViewer 工具

  HierarchyViewer 的含义为层级查看器,查看的对象是 View 树的层次结构。HierarchyViewerh 还可以列出 Layout-View(布局到视图)的属性和层次关系。

你可能感兴趣的:(#,android,调试)