下面都是android应用层空间的调试方法总结
android系统中调试Java非常容易,一般遇到错误都在logcat中打印出错时函数的调用关系,和出错的具体行数。而C库或是可执行的后台服务中出错时只看到一些二进制信息。
static void wpa_bss_copy_res(struct wpa_bss *dst, struct wpa_scan_res *src, struct os_time *fetch_time) { dst->flags = src->flags; os_memcpy(dst->bssid, src->bssid, ETH_ALEN); dst->freq = src->freq; dst->beacon_int = src->beacon_int; dst->caps = src->caps; dst->qual = src->qual; dst->noise = src->noise; dst->level = src->level; dst->tsf = src->tsf; wpa_dbg(dst,MSG_ERROR, "---------------src->tsf:%llu ||"MACSTR "\n", src->tsf,MAC2STR(src->bssid));//是我添加的调试log calculate_update_time(fetch_time, src->age, &dst->last_update); }
process_global_event |-->do_process_drv_event |-->send_scan_event |-->wpa_supplicant_event |-->wpa_supplicant_event_scan_results |-->_wpa_supplicant_event_scan_results |-->wpa_supplicant_get_scan_results |-->wpa_bss_update_scan_res |-->wpa_bss_add wpa_bss_copy_res wpa_dbg || wpa_msg wpa_msg_cb || wpa_supplicant_ctrl_iface_msg_cb
gdb远程调试功能需要在linux-x86的环境下才能运行。譬如在ubuntu或debia环境下或
是linux的虚拟机下,调试步骤如下:
首先获取linux pc端下的adb的路径,或者将adb的路径设置到~/.bashrc下:
export PATH=$ADB_PATH:$PATH默认android fs编译出来时在如下的路径:
out/host/`uname -s | tr "[[:upper:]]" "[[:lower:]]"`-x86/bin/adb
需要获取gdb的client端和python 库的路径(gdb需要依赖python),android4.4以后,
一般都是在如下路径:GDB=prebuilts/gcc/$(uname -s | tr "[[:upper:]]" "[[:lower:]]")-x86
/arm/arm-linux-androideabi-4.7/bin/arm-linux-androideabi-gdb
PYTHON_DIR=prebuilts/python/$(uname -s | tr "[[:upper:]]" "[[:lower:]]")-x86/2.7.5
接下来我们需要将pc 端上的gdb(简称为host)跟目标版上的gdb service(简称为target)
连接起来,让他们可以相互通讯
$ADB forward tcp:$GDB_PORT tcp:$GDB_PORT
在这里需要规定下gdb service的侦听端口和gdb client的连接端口,通过adb forward来实现,
这样client与service就可以通过adb来通讯了。至于adb forward具体干些什么事情,
请参考我的另一篇blog:android usb adb流程
接下来需要在目标版上起动gdb server,在指定的端口上侦听gdb client过来的请求,并且attach
上需要调试的进程。使用如下命令:$ADB shell gdbserver :$GDB_PORT --attach $TARGET_PID &
注意这些库和可执行程序,是包含了调试信息的。在android编译输出的如下路径:
out/target/product/wmid/symbols/system,而目标版上的二进制程序则是不包含调试信息的。
远程调试的好处就在这里,简单说,就是将包含调试信息的程序放在pc端,target端的程序是
不包含调试信息的(通过stripped命令去掉了调试信息),这样节省存储空间,gdb client会将
从gdb service那边得到的二进制信息结合pc端的带调试信息的程序,就可以翻译成human-readable
的信息展示出来并且幸运的是,android的编译环境都为我们想好了这些,所以在android编译的输出路径上,
都保存了两份,一份是带调试信息,一份是不带调试信息的。
起动pc段的gdb,并同时指定要调试的程序名字和路径。
$GDB -x $GDBINIT $PROG
PROG就是要调试的程序,注意他们pc端下的路径,是包含调试信息的程序。
至此gdb调试环境就已经搭建完成。以上过程,可以通过一个脚本来实现,每次调试时,
只需要执行以下这个脚本,即可以开始gdb的调试。运行该脚本的方法:
将run-gdb.sh置于android fs sources的root 目录下。然后执行如下命令:
run-gdb.sh attach prog
上面prog就是你需要调试的程序,譬如如果要通过gdb调试wpa_supplicant,则可以执行如下命令:
run-gdb.sh attach wpa_supplicant
在gdb的提示符下,输入命令:thread apply all bt,即可以显示所调试进程下所有线程各
自的调用堆栈。或者在提示符下输入:info threads,即可以查看当前进程下所有的线程,
并且可以thread + number(简写:b n)命令来切换到指定ID(即number)的线程,然后执行
bt(backstrace)即可以打印当前线程下的调用堆栈。然后仔细查看那些停在获取锁的地方的线程,
然后查看这些线程都各自拥有那些锁,找出导致死锁的线程。
info threads
thread apply all bt
bt
print expr(p)
break
info break
run(r)
continue(c)
next(n)
quit(q)
在android下有个/system/bin/debuggerd后台服务,就是被android用来在需要的使用打印线
程的掉哟你堆栈的。这个debuggerd命令,在我们平时调试andoid的死锁程序也是很有用的。
应该多主动使用。基本流程就是先通过ps命令查看你要调试的进程的pid,然后执行命令:
debuggerd -b $pid,即可以将该$pid下所有的线程的堆栈都打印出来,类似于gdb中的
thread apply all bt命令,但是这个命令更方便。
从LOG可以看出ANR的类型,CPU的使用情况:
如果CPU使用量接近100%,说明当前设备很忙,有可能是CPU饥饿导致了ANR
如果CPU使用量很少,说明主线程被BLOCK了
如果IOwait很高,说明ANR有可能是主线程在进行I/O操作造成的
$chmod 777 /data/anr
rm /data/anr/traces.txt
ps
kill -3 PID
service list命令可以列出目前android系统所有的服务进程(注意都是binder的服务进程)。
而dumpsys $service,则可以打印指定服务里的一些重要信息,这样就可以动态的查看
service里面目前的状态,从而发现是否有问题。
另外通过service call命令在控制台就可以有选择的调用你指定的服务中的指定api函数的功能;从而验证该api是否工作正常
例如:
service call WMTWifiService 3 i32 0 i32 17
命令解析如下:
service call WMTWifiService(服务的名字,binder的知名服务,即注册到serviceManager中的名字) 3(binder服务接口中的方法对应的编号) i32 0(参数1 参数值) i32 17(参数2 参数值)