dumpState log在Android开发中是解决问题的重要途径之一,这篇文章记录我关于dumpstate的学习总结。
dumpstate文件一般有几十万行的log,要从这么多log中找出想要的关键信息,如果不掌握一定的技巧,俨然大海捞针。先从总体上了解dumpstate的结构,以至于不会迷失在log海当中。随手抓了一个dumpstate,大概如下
对于三星型号,电话盘输入*#9900#进入以下界面
这个界面的代码在android\vendor\samsung\packages\apps\MSP\FactoryTest\ServiceModeApp\src\com\sec\android\app\servicemodeapp\app\SysDump.java
这里只分析点击第三项RUN DUMPSTATE/LOGCAT为例,实际运行的主要代码是
Runtime.getRuntime().exec("bugreport >/data/log/dumpstate_sysdump_time.log");
这里运行了bugreport程序,并将结果重定位到log文件中,这个log文件就是我们所说的dumpstate log了
bugreport的代码位于android\frameworks\native\cmds\bugreport\Bugreport.c
dumpstate的代码位于android\frameworks\native\cmds\dumpstate\Dumpstate.c
在bugreport中,先启动dumpstate的service
/* start the dumpstate service */
property_set("ctl.start", "dumpstate");
然后通过linux socket的方式,在bugreport和dumpstate两个进程间建立通讯。
Bugreport Client端
s = socket_local_client("dumpstate",
ANDROID_SOCKET_NAMESPACE_RESERVED,
SOCK_STREAM);
redirect_to_socket(stdout, "dumpstate");
void redirect_to_socket(FILE *redirect, const char *service) {
int s = android_get_control_socket(service);
if (s < 0) {
fprintf(stderr, "android_get_control_socket(%s): %s\n", service, strerror(errno));
exit(1);
}
if (listen(s, 4) < 0) {//监听是否有client连接
fprintf(stderr, "listen(control socket): %s\n", strerror(errno));
exit(1);
}
struct sockaddr addr;
socklen_t alen = sizeof(addr);
int fd = accept(s, &addr, &alen);
if (fd < 0) {
fprintf(stderr, "accept(control socket): %s\n", strerror(errno));
exit(1);
}
fflush(redirect);
dup2(fd, fileno(redirect));
close(fd);
}
这样dumpstate的结果就输出到stdout中,然后再重定位到log文件中,所以dumpstate.c这个程序才是重点。
从main函数进来,整个dump过程分五个过程,DumpState、ProcLibRank、ShowmapAll、DumpsysAll、DumpsysOther,分别调用的函数是dumpstate、proc_lib_rank、do_showmap_all、dumpsys_all、dumpsys_other
printf("========================================================\n");
printf("== dumpstate: %s\n", date);
printf("========================================================\n");
------ UPTIME (uptime) ------
打印系统当前运行了多长时间等信息
run_command("UPTIME", 10, "uptime", NULL);
uptime: 开机运行了多长时间
idle time:所有CPU休眠的总时间
------ MEMORY INFO (/proc/meminfo) ------
输出结果和cat /proc/meminfo的结果相同,proc系统反映了linux运行时的信息
dump_file("MEMORY INFO", "/proc/meminfo");
------ CPU CORE INFO ------
实际上输出的和cat下面等信息相同
"/sys/devices/system/cpu/offline",
"/sys/devices/system/cpu/online",
"/sys/devices/system/cpu/possible",
"/sys/devices/system/cpu/cpu0/rq-stats/cpu_normalized_load",
"/sys/devices/system/cpu/cpu0/rq-stats/run_queue_avg",
"/sys/devices/system/cpu/cpu0/rq-stats/run_queue_poll_ms"
------ CPU INFO (top -n 1 -d 1 -m 30 -t) ------
实际上是运行top命令
------ VIRTUAL MEMORY STATS (/proc/vmstat) ------
显示虚拟内存的信息,cat /proc/vmstat
------ VMALLOC INFO (/proc/vmallocinfo) ------
vmalloc内存分配信息,cat /proc/vmallocinfo
------ SLAB INFO (/proc/slabinfo) ------
slab分配器信息,cat /proc/slabinfo
------ ZONEINFO (/proc/zoneinfo) ------
内存域信息,cat /proc/zoneinfo
------ PAGETYPEINFO (/proc/pagetypeinfo) ------
cat /proc/pagetypeinfo
------ BUDDYINFO (/proc/buddyinfo) ------
伙伴系统信息, cat /proc/buddyinfo
------ KERNEL WAKELOCKS (/proc/wakelocks) ------
内核唤醒锁信息, cat /proc/wakelocks
------ KERNEL WAKE SOURCES (/d/wakeup_sources: 1970-01-01 08:00:00) ------
查看所有的wake_lock,cat /sys/kernel/debug/wakeup_sources
------ KERNEL CPUFREQ (/sys/devices/system/cpu/cpu0/cpufreq/stats/time_in_state) ------
记录了CPU从开机以来在每个频率下的运行时间,cat /sys/devices/system/cpu/cpu0/cpufreq/stats/time_in_state
------ KERNEL BLUEDROID (/d/bluedroid) ------
蓝牙协议信息,cat /sys/kernel/debug/bluedroid
------ PROCESSES (ps -P --abi) ------
进程信息,相当于ps命令的输出结果
------ KERNEL LOG (dmesg) ------
显示系统启动时的kernel log,相当于dmesg命令的输出结果
------ UART LOG (/sys/kernel/debug/ipc_logging/uart_log/log) ------
相当于cat /sys/kernel/debug/ipc_logging/uart_log/log
------ USB LOG (/proc/usblog) ------
相当于cat /proc/usblog
**********************************************************************************************************************************************************
------ SYSTEM LOG (logcat -v threadtime -d *:v) ------
相当于命令logcat -v threadtime -d *:v 的输出结果
------ EVENT LOG (logcat -b events -v threadtime -d *:v) ------
相当于logcat -b events -v threadtime -d *:v 的输出结果
------ RADIO LOG (logcat -b radio -v threadtime -d *:v) ------
相当于logcat -b radio -v threadtime -d *:v 的输出结果
logcat -b表示请求不同的缓冲区(main、radio、events、system、crash、kernel),默认为-b main -b system,所以SYSTEM LOG包含了main缓冲区的和system缓冲区的,关于缓冲区,看下面的介绍。-v
Log把日志保存到缓冲区,Logcat从缓冲区读取并查看。
要打印一句log,通过这样的方法,Log.i("csf", "log test");
Log源码在android\frameworks\base\core\java\android\util\Log.java,看Log.i的实现
public static int i(String tag, String msg) {
return println_native(LOG_ID_MAIN, INFO, tag, msg);
}
在native层实现,android\frameworks\base\core\jni\android_util_Log.cpp
static jint android_util_Log_println_native(JNIEnv* env, jobject clazz,
jint bufID, jint priority, jstring tagObj, jstring msgObj)
{
const char* tag = NULL;
const char* msg = NULL;
if (msgObj == NULL) {
jniThrowNullPointerException(env, "println needs a message");
return -1;
}
if (bufID < 0 || bufID >= LOG_ID_MAX) {
jniThrowNullPointerException(env, "bad bufID");
return -1;
}
if (tagObj != NULL)
tag = env->GetStringUTFChars(tagObj, NULL);
msg = env->GetStringUTFChars(msgObj, NULL);
int res = __android_log_buf_write(bufID, (android_LogPriority)priority, tag, msg);
if (tag != NULL)
env->ReleaseStringUTFChars(tagObj, tag);
env->ReleaseStringUTFChars(msgObj, msg);
return res;
}
真面目还藏在__android_log_buf_write()函数中,这个函数在android\system\core\liblog\logd_write.c中
通过分析,总结出下面,具体请查看源码
通过Log.d、Log.i、Log.v等输出的log保存在/dev/log/main这个缓冲区中,一般app用这种方式打log
通过Rlog.d、Rlog.v、Rlog.i等输出的log保存在/dev/log/radio这个缓冲区中,一般射频、SIM、telephony模块用这个比较多
通过Slog.d、Slog.v、Slog.i等输出的log保存在/dev/log/system这个缓冲区中,很多framework的log都用Slog
通过EventLog.writeEvent输出的log保存在/dev/log/events这个缓冲区中,一般用在framework模块中
logcat的源码位于/android/system/core/logcat中
当运行logcat -v threadtime -d *:v 这样的命令时,便会启动logcat进入main函数,这里就会根据传入的参数读取不同缓冲区的内容
threadtime输入的格式如下
case FORMAT_THREADTIME:
len = snprintf(prefixBuf + prefixLen, sizeof(prefixBuf) - prefixLen,
"%s %5d %5d %c %-8s: ", timeBuf,
entry->pid, entry->tid, priChar, entry->tag);
strcpy(suffixBuf + suffixLen, "\n");
++suffixLen;
break;
比如:
Time PID TID priority Tag Message
05-25 15:48:17.318 1603 3061 V WindowOrientationListener: OrientationSensorJudge.getProposedRotation, Rotation: 0
**********************************************************************************************************************************************************
**********************************************************************************************************************************************************
------ LIST OF OPEN FILES (/system/xbin/su root lsof) ------
列出当前系统打开的文件,相当于命令/system/xbin/su root lsof,输出结果如下
COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME
zygote 518 root mem ??? 00:04 0 11702 /dev/ashmem/dalvik-main
COMMAND:进程名称
PID:进程号
USER:进程所有者
FD:文件描述符,应用程序通过文件描述符识别该文件
TYPE:文件类型,如DIR、REG等
DEVICE:指定磁盘的名称
SIZE:文件的大小
NODE:索引节点(文件在磁盘上的标识)
NAME:打开文件的确切名称
**********************************************************************************************************************************************************
------ BLOCKED PROCESS WAIT-CHANNELS ------
列出系统上所有睡眠的线程的名称和函数名,如
tid 睡眠的函数
6051 Binder_3 binder_thread_read
------ VM TRACES JUST NOW (/data/anr/traces.txt.bugreport: 2016-05-25 15:49:20) ------
虚拟机相关信息,收集虚拟机和native的堆栈信息,这里需要root user权限
/proc目录下存在很多以数字命名的目录,这些数字其实就是进程号,每创建一个进程,/proc就会新建一个以进程号命名的目录,以下两个命令大概就是遍历每个进程,并获取backtrace,具体的实现是dump_backtrace_to_file()这个函数,源码位于/android/system/core/libcutils/Debugger.c
TOMBSTONE,当android发生crash时,会产生一个叫做Tombstone的文件,tombstone包含了进程crash时的重要信息,该文件位于/data/tombstone_xx下,每发生一次crash,xx递增一次,如果没有,则打印出
*** NO TOMBSTONES to dump in /data/tombstones
网络相关信息
------ NETWORK DEV INFO (/proc/net/dev) ------
相当于cat /proc/net/dev,输出结果如下
Inter-| Receive | Transmit
face |bytes packets errs drop fifo frame compressed multicast|bytes packets errs drop fifo colls carrier compressed
wlan0: 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
receive表示收包,Transmit表示发包。bytes表示收发的字节数,packets表示收发正确的包数,errs表示收发错误的包数,drop表示收发丢弃的包数
------ NETWORK MCAST INFO (/proc/net/dev_mcast) ------
------ QTAGUID NETWORK INTERFACES INFO (/proc/net/xt_qtaguid/iface_stat_all) ------
------ QTAGUID NETWORK INTERFACES INFO (xt) (/proc/net/xt_qtaguid/iface_stat_fmt) ------
------ QTAGUID CTRL INFO (/proc/net/xt_qtaguid/ctrl) ------
------ QTAGUID STATS INFO (/proc/net/xt_qtaguid/stats) ------
------ LAST KMSG (/proc/last_kmsg) ------
系统重启前的kernel log,相当于cat /proc/last_kmsg或者cat /sys/fs/pstore/console-ramoops,必须系统重启才存在这个log,这里也可以看出系统重启的原因
------ POWER ON INFO (/proc/boot_stat) ------
开机后一些重要事件的时间戳
------ POWER OFF INFO (/data/log/poweroff_info.txt) ------
------ POWER RESET INFO (/data/log/powerreset_info.txt) ------
------ POWER OFF RESET REASON (/data/log/power_off_reset_reason.txt) ------
------ POWER OFF RESET REASON BACKUP (/data/log/power_off_reset_reason_backup.txt) ------
------ NETWORK INTERFACES (ip link) ------
网络设备运行状态
------ IPv4 ADDRESSES (ip -4 addr show) ------
------ IPv6 ADDRESSES (ip -6 addr show) ------
------ IP ADDR (ip addr) ------
------ IP RULES (ip rule show) ------
------ IP RULES v6 (ip -6 rule show) ------
IPV4和IPV6相关信息
------ TZBSP QSEE LOG (cat /d/tzdbg/qsee_log) ------
------ TZBSP LOG (cat /d/tzdbg/log) ------
高通相关的log?
------ INTERRUPTS (1) (/proc/interrupts) ------
------ INTERRUPTS (2) (/proc/interrupts) ------
列出系统当前的中断信息
------ SYSTEM PROPERTIES ------
列出系统所有属性的键值对
------ FILESYSTEMS & FREE SPACE (df) ------
磁盘容量信息
------ PROC FILESYSTEMS (/proc/filesystems) ------
当前内核支持的文件系统
------ PACKAGE LIST (/system/xbin/su root cat /data/system/packages.list) ------
所有包的信息
------ PACKAGE USAGE LIST (/data/system/package-usage.list: 2016-05-25 15:23:02) ------
------ USAGE HISTORY BACKUP (/data/system/usage-history.xml.backup) ------
------ BACKLIGHTS ------
背光信息
------ BINDER FAILED TRANSACTION LOG (/sys/kernel/debug/binder/failed_transaction_log) ------
------ BINDER TRANSACTION LOG (/sys/kernel/debug/binder/transaction_log) ------
------ BINDER TRANSACTIONS (/sys/kernel/debug/binder/transactions) ------
------ BINDER STATS (/sys/kernel/debug/binder/stats) ------
binder通信信息
========================================================
== Board
========================================================
板级信息
------ BACKGROUND BYTES (/proc/sys/vm/dirty_background_bytes) ------
------ BACKGROUND RATIO (/proc/sys/vm/dirty_background_ratio) ------
------ BACKGROUND BYTES (/proc/sys/vm/dirty_bytes) ------
------ DIRTY RATIO (/proc/sys/vm/dirty_ratio) ------
------ SCHEDULER (/sys/block/mmcblk0/queue/scheduler) ------
------ SLICE IDLE (/sys/block/mmcblk0/queue/iosched/slice_idle) ------
------ MAX SECTORS (/sys/block/mmcblk0/queue/max_sectors_kb) ------
------ READ AHEAD (/sys/block/mmcblk0/queue/read_ahead_kb) ------
------ CID (/sys/block/mmcblk0/device/cid) ------
------ CLK SCALING (/sys/devices/msm_sdcc.1/mmc_host/mmc0/clk_scaling/enable) ------
------ CSD (/sys/block/mmcblk0/device/csd) ------
------ CAPS (/sys/block/mmcblk0/device/caps) ------
------ CAPS2 (/sys/block/mmcblk0/device/caps2) ------
------ FILESYSTEMS (/proc/filesystems) ------
这些都是System Memory Info
------ Partitions (/proc/partitions) ------
分区信息
========================================================
== Procrank/Librank: Android Framework Services
========================================================
实际上运行的是两个命令procrank和librank
procrank从高到低顺序列出进程的内存使用情况
------ PROCRANK (procrank) ------
PID Vss Rss Pss Uss Swap cmdline
562 123948K 33748K 26013K 25192K 916K /system/bin/mm-qcamera-daemon
VSS >= RSS >= PSS >= USS
VSS:Virtual Set Size,虚拟耗用内存,包含共享库占用的内存,是单个进程全部可访问的地址空间
RSS:Resident Set Size,实际使用物理内存,包含共享库占用的内存,是单个进程实际占用的内存大小,对于单个共享库,无论多少个进程使用,实际该共享库只会装入内存一次。
PSS:Proportional Set Size,实际使用的物理内存,比例分配共享库占用的内存
USS:Unique Set Size,进程独自占用的物理内存,不包含共享库占用的内存,USS是一个非常有用的数字,因为它揭示了运行一个特定进程的真实的内存增量大小。如果进程被终止,USS就是实际被返还给系统的内存大小。
USS是针对某个进程是否有内存泄露,进行检测的最佳数字。怀疑是否有内存泄露可以查看这个值是否一直有增加。
查看每个进程的内存使用信息,实际是运行showmap pid号的命令
========================================================
== DUMPSYS: Android Framework Services
========================================================
查看FrameWork所有service的信息,实际上运行的是dumpsys serviceName
比如:
dumpsys account列出所有账户
dumpsys activity activity的相关信息
dumpsys的代码在android\frameworks\native\cmds\dumpsys\dumpsys.cpp
从源码实现中看,是遍历所有的service,然后调用service->dump()方法,比如查看EmailService的栈
EmailContent$Account.dumpAccountInfo(Context) line: 7597
EmailSyncServiceLogger.dumpAllAccountInfo(Context, PrintWriter) line: 768
EmailService.dump(FileDescriptor, PrintWriter, String[]) line: 395
ActivityThread.handleDumpService(ActivityThread$DumpComponentInfo) line: 3655
dumpsys activity all -> 所有task和task上activity,activity的view Hierachy
dumpsys activity service all ->所有app service的信息
具体参考源码dumpstate.cpp的dumpsys_other()函数
1、广播相关信息
ACTIVITY MANAGER BROADCAST STATE (dumpsys activity broadcasts)
Registered Receivers 列出系统中所有动态注册的广播接收者ReceiverList,该结构包含一些Intent filter,比如下面的log
* ReceiverList{27d9778 18700 system/1000/u0 local:com.android.server.AlarmManagerService$UninstallReceiver@86fd5ea,7c790db}
app=18700:system/1000 pid=18700 uid=1000 user=0
Filter #0: BroadcastFilter{2703e51}
Action: "android.intent.action.PACKAGE_REMOVED"
Action: "android.intent.action.PACKAGE_RESTARTED"
Action: "android.intent.action.PACKAGE_DATA_CLEARED"
Action: "android.intent.action.QUERY_PACKAGE_RESTART"
Scheme: "package"
AutoVerify=false
Filter #1: BroadcastFilter{6d66b6}
Action: "android.intent.action.EXTERNAL_APPLICATIONS_UNAVAILABLE"
Action: "android.intent.action.USER_STOPPED"
Action: "android.intent.action.UID_REMOVED"
AutoVerify=false
存在Filter #0 和 Filter #1,说明UninstallReceiver这个receiver动态注册了两次,两次注册的filter分别有所不同,过滤的action有所不同,查看AlaramManagerService.java的源码后也确实如此
public UninstallReceiver() {
IntentFilter filter = new IntentFilter();
filter.addAction(Intent.ACTION_PACKAGE_REMOVED);
filter.addAction(Intent.ACTION_PACKAGE_RESTARTED);
filter.addAction(Intent.ACTION_PACKAGE_DATA_CLEARED);
filter.addAction(Intent.ACTION_QUERY_PACKAGE_RESTART);
filter.addDataScheme("package");
getContext().registerReceiver(this, filter);
// Register for events related to sdcard installation.
IntentFilter sdFilter = new IntentFilter();
sdFilter.addAction(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE);
sdFilter.addAction(Intent.ACTION_USER_STOPPED);
sdFilter.addAction(Intent.ACTION_UID_REMOVED);
getContext().registerReceiver(this, sdFilter);
}
Historical Broadcast foreground 列出位于前台的历史广播
Historical Broadcast background 列出位于后台的历史广播
经常可以查看广播从哪里发出。
2、虚拟机信息
Dalvik Thread
3、查看系统登录了哪些账号
DUMP OF SERVICE account:
属于dumpstate的第四个阶段的DumpsysAll中的dumpsys account
4、等待的intent的状态
ACTIVITY MANAGER PENDING INTENTS (dumpsys activity intents)
5、所有所有注册的broadcastReceiver及其filter、action的信息
ACTIVITY MANAGER BROADCAST STATE (dumpsys activity broadcasts)
6、显示所有content providers信息
ACTIVITY MANAGER CONTENT PROVIDERS (dumpsys activity providers)
7、当前active状态的service信息
ACTIVITY MANAGER SERVICES (dumpsys activity services)
上述4、5、6、7都是发生在dumpsys activity的过程中
8、查看CPU最近的使用情况
DUMP OF SERVICE cpuinfo:
9、查看系统上db的相关情况,app的数据库最近的操作情况
DUMP OF SERVICE dbinfo
10、查看当前的notification,每个NotificationRecord记录一个通知
DUMP OF SERVICE notification
11、查看某个包的信息,包名是AndroidManifest.xml中的包名
Package [com.samsung.android.email.provider]
这里也可以查看app的版本
12、查看是否发生了ANR
WINDOW MANAGER LAST ANR (dumpsys window lastanr)
13、查看data是否连接的信息
DUMP OF SERVICE connectivity
14、查看email service的相关信息
SERVICE com.samsung.android.email.provider/com.samsung.android.email.sync.service.EmailService
SERVICE com.samsung.android.email.provider/com.samsung.android.email.sync.exchange.ExchangeService
15、查看Email是否收到新邮件,更新badge
11-16 14:50:54.314: D/BadgeCache(12920): 1. updateBadgeCounts: com.samsung.android.email.provider = 2,2表示badge的数量
16、查看手机芯片
Chip Name
17、手机使用的网络
Network: China Telecom
18、Debug Level : 1
19、语言Current Lang Name : English (US)
20、查看是哪里启动当前activity,可查看event log
如查到当前activity的event log:am_create_activity
可查看前面am_on_stop_called,am_destroy_activity,am_finish_activity,am_pause_activity,便可以推断出是哪里启动当前activity
21、查看每个进程的流量消耗
------ QTAGUID STATS INFO (/proc/net/xt_qtaguid/stats) ------
idx iface acct_tag_hex uid_tag_int cnt_set rx_bytes rx_packets tx_bytes tx_packets rx_tcp_bytes rx_tcp_packets rx_udp_bytes rx_udp_packets rx_other_bytes rx_other_packets tx_tcp_bytes tx_tcp_packets tx_udp_bytes tx_udp_packets tx_other_bytes tx_other_packets