如何使用jdb调试android的java程序
习惯了gdb,总觉得eclipse太过臃肿,各种不爽。看到李先静写了一篇“用jdb/jdbshell调试android程序“(http://www.limodev.cn/blog/archives/1281),用了下,感觉比eclipse舒服多了。jdb的命令有那么点别扭,先静写了个jdbshell,加入了命令历史和命令别名(几个常用的gdb命令)。
下面是一个简单的how to,以调试browser应用为例
1)下载jdbshell并编译
www.limodev.cn/blog/?dl_name=jdbshell.tar.gz
因为我基本上每次一开始都要执行threads变量,所以我在开始wile循环前加了一句话
else
{
close(parent_to_child[0]);
write(parent_to_child[1], "threads\n",strlen("threads\n"));
while(1)
{
int i = 0;
line = readline ("");
...
}
}
编译后将jdbshell放到~/bin,或者将目录加到PATH里
出现无法打开readline.h的话,安装libreadline6和libreadline6-dev
2)仿照gdbclient,在build/envsetup.sh中加入jdbclient函数
function jdbclient()
{
local MY_SRC_PATH="$1"
if [ "$MY_SRC_PATH" ] ; then
MY_SRC_PATH=$1
echo $MY_SRC_PATH
else
echo "ROOT" $OUR_ROOT
MY_SRC_PATH="app_process"
fi
local PORT="$2"
if [ "$PORT" ] ; then
PORT=$2
else
PORT=":9000"
fi
local PID
local PROG="$3"
if [ "$PROG" ] ; then
PID=`pid $3`
echo tcp$PORT jdwp$PID
adb forward "tcp$PORT" "jdwp:$PID"
echo jdbshell -sourcepath $MY_SRC_PATH -attach localhost$PORT
jdbshell -sourcepath $MY_SRC_PATH -attach localhost$PORT
sleep 2
else
echo ""
echo "please specify app you want to debug:"
echo ""
fi
}
3)启动android模拟器,运行浏览器
4)jdbclient packets/app/Browser/src/ :9000 browser
第一个参数是你要load进来的代码的地址,如果你要load多个代码(调试的时候可以看),可以
export DEBUG_SRC_PATH=
然后
jdbclient $DEBUG_SRC_PATH :9000 browser
第二个参数是attach端口,如果9000有在用,可以用其他端口
第三个参数是要调试的应用,脚本里面根据
pid browser来取得进程id,所以你要保证
adb shell ps |grep browser
能够看到对应的进程
比如我在~/.bashrc里面,设定了DEBUG_SRC_PATH
export ANDROID_SRC=/home/tom/work/gingerbread
export DEBUG_SRC_PATH=$ANDROID_SRC/frameworks/base/opengl/java:$ANDROID_SRC/frameworks/base/awt/java:$ANDROID_SRC/frameworks/base/core/java:$ANDROID_SRC/frameworks/base/location/java:$ANDROID_SRC/frameworks/base/sax/java:$ANDROID_SRC/frameworks/base/graphics/java:$ANDROID_SRC/frameworks/base/telephony/java:$ANDROID_SRC/frameworks/base/services/java:$ANDROID_SRC/frameworks/base/media/java:$ANDROID_SRC/frameworks/base/wifi/java:$ANDROID_SRC/frameworks/base/im/java:$ANDROID_SRC/dalvik/libcore/suncompat/src/main/java:$ANDROID_SRC/dalvik/libcore/nio_char/src/main/java:$ANDROID_SRC/dalvik/libcore/nio_char/src/main/java/java:$ANDROID_SRC/dalvik/libcore/security-kernel/src/main/java:$ANDROID_SRC/dalvik/libcore/security-kernel/src/main/java/java:$ANDROID_SRC/dalvik/libcore/security/src/main/java:$ANDROID_SRC/dalvik/libcore/security/src/main/java/java:$ANDROID_SRC/dalvik/libcore/archive/src/main/java:$ANDROID_SRC/dalvik/libcore/archive/src/main/java/java:$ANDROID_SRC/dalvik/libcore/awt-kernel/src/main/java:$ANDROID_SRC/dalvik/libcore/awt-kernel/src/main/java/java:$ANDROID_SRC/dalvik/libcore/luni/src/main/java:$ANDROID_SRC/dalvik/libcore/luni/src/main/java/java:$ANDROID_SRC/dalvik/libcore/math/src/main/java:$ANDROID_SRC/dalvik/libcore/math/src/main/java/java:$ANDROID_SRC/dalvik/libcore/x-net/src/main/java:$ANDROID_SRC/dalvik/libcore/openssl/src/main/java:$ANDROID_SRC/dalvik/libcore/dalvik/src/main/java:$ANDROID_SRC/dalvik/libcore/auth/src/main/java:$ANDROID_SRC/dalvik/libcore/concurrent/src/main/java:$ANDROID_SRC/dalvik/libcore/concurrent/src/main/java/java:$ANDROID_SRC/dalvik/libcore/sql/src/main/java:$ANDROID_SRC/dalvik/libcore/sql/src/main/java/java:$ANDROID_SRC/dalvik/libcore/prefs/src/main/java:$ANDROID_SRC/dalvik/libcore/prefs/src/main/java/java:$ANDROID_SRC/dalvik/libcore/xml/src/main/java:$ANDROID_SRC/dalvik/libcore/text/src/main/java:$ANDROID_SRC/dalvik/libcore/text/src/main/java/java:$ANDROID_SRC/dalvik/libcore/luni-kernel/src/main/java:$ANDROID_SRC/dalvik/libcore/luni-kernel/src/main/java/java:$ANDROID_SRC/dalvik/libcore/regex/src/main/java:$ANDROID_SRC/dalvik/libcore/regex/src/main/java/java:$ANDROID_SRC/dalvik/libcore/nio/src/main/java:$ANDROID_SRC/dalvik/libcore/nio/src/main/java/java:$ANDROID_SRC/dalvik/libcore/json/src/main/java:$ANDROID_SRC/dalvik/libcore/crypto/src/main/java:$ANDROID_SRC/dalvik/libcore/icu/src/main/java:$ANDROID_SRC/dalvik/libcore/annotation/src/main/java:$ANDROID_SRC/dalvik/libcore/annotation/src/main/java/java:$ANDROID_SRC/dalvik/libcore/junit/src/main/java:$ANDROID_SRC/dalvik/libcore/logging/src/main/java:$ANDROID_SRC/dalvik/libcore/logging/src/main/java/java:$ANDROID_SRC/dalvik/libcore-disabled/instrument/src/main/java:$ANDROID_SRC/dalvik/libcore-disabled/instrument/src/main/java/java:$ANDROID_SRC/dalvik/libcore-disabled/sound/src/main/java:packages/apps/Browser/src/
5)显示如下信息,说明调试器启动
Set uncaught java.lang.Throwable
Set deferred uncaught java.lang.Throwable
Initializing jdb ...
Group system:
(java.lang.Thread)0xc14050d0c0 <6> Compiler cond. waiting
(java.lang.Thread)0xc14050cf50 <4> Signal Catcher cond. waiting
(java.lang.Thread)0xc14050cea8 <3> GC cond. waiting
(java.lang.Thread)0xc14050cdf0 <2> HeapWorker cond. waiting
Group main:
(java.lang.Thread)0xc14001f1a8 <1> main running
(java.lang.Thread)0xc140562ad8 <21> AsyncTask #5 cond. waiting
(android.os.HandlerThread)0xc1405528f8 <20> WebViewWorkerThread running
(android.net.http.ConnectionThread)0xc14053a780 <19> http3 cond. waiting
(android.net.http.ConnectionThread)0xc14053a5a8 <18> http2 cond. waiting
(android.net.http.ConnectionThread)0xc140530f90 <17> http1 cond. waiting
(android.net.http.ConnectionThread)0xc140530da0 <16> http0 cond. waiting
(java.lang.Thread)0xc14058c008 <15> Thread-18 running
(java.lang.Thread)0xc140593370 <14> AsyncTask #4 cond. waiting
(java.lang.Thread)0xc140595dd0 <13> AsyncTask #3 cond. waiting
(java.lang.Thread)0xc14058f9f0 <12> AsyncTask #2 cond. waiting
(java.lang.Thread)0xc14055a820 <11> WebViewCoreThread running
(java.lang.Thread)0xc140591bc8 <10> AsyncTask #1 cond. waiting
(java.lang.Thread)0xc140589710 <9> CookieSyncManager running
(java.lang.Thread)0xc14050f288 <8> Binder Thread #2 running
(java.lang.Thread)0xc14050e900 <7> Binder Thread #1 running
>
6)选择感兴趣的线程
大部分应用跑在main线程里面,browser也不例外
命令 thread 线程ID
> thread 0xc14001f1a8
<1> main[1]
7)挂起线程
命令 suspend 线程ID
<1> main[1] suspend 0xc14001f1a8
8)设置断点
命令:
stop at <类>:<行号> 或
stop in <类>.<方法名>[(参数类型,...)]
<1> main[1] stop at com.android.browser.BrowserActivity:2689
Set breakpoint com.android.browser.BrowserActivity:2689
这个地方要写类名稍微麻烦一点,哪位大虾可以转化成像gdb那样,直接文件名?
9)继续执行
命令 c(ont)
<1> main[1] c
>
10)点击网页中的某一个链接
就会看到程序在BrowserActivty:2689(shouldOverideUrlLoading)处断住了
此时可以看堆栈
命令:bt/wherei
<1> main[1]bt
[1] com.android.browser.BrowserActivity.shouldOverrideUrlLoading (BrowserActivity.java:2,689), pc = 8
[2] com.android.browser.Tab$2.shouldOverrideUrlLoading (Tab.java:552), pc = 44
[3] android.webkit.CallbackProxy.uiOverrideUrlLoading (CallbackProxy.java:216), pc = 19
[4] android.webkit.CallbackProxy.handleMessage (CallbackProxy.java:323), pc = 347
[5] android.os.Handler.dispatchMessage (Handler.java:99), pc = 20
[6] android.os.Looper.loop (Looper.java:123), pc = 75
[7] android.app.ActivityThread.main (ActivityThread.java:3,683), pc = 31
[8] java.lang.reflect.Method.invokeNative (native method)
[9] java.lang.reflect.Method.invoke (Method.java:507), pc = 18
[10] com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run (ZygoteInit.java:839), pc = 11
[11] com.android.internal.os.ZygoteInit.main (ZygoteInit.java:597), pc = 84
[12] dalvik.system.NativeStart.main (native method)
看代码
命令:l(ist)
<1> main[1] l
2,685 }
2,686 }
2,687
2,688 boolean shouldOverrideUrlLoading(WebView view, String url) {
2,689 => if (url.startsWith(SCHEME_WTAI)) {
2,690 // wtai://wp/mc;number
2,691 // number=string(phone-number)
2,692 if (url.startsWith(SCHEME_WTAI_MC)) {
2,693 Intent intent = new Intent(Intent.ACTION_VIEW,
2,694 Uri.parse(WebView.SCHEME_TEL +
看变量
命令:print
<1> main[1] print url
url = "http://m.baidu.com/img?tn=bdidxiphone&ssid=0&from=844b&bd_page_type=1&uid=wiaui_1320452293_7438&pu=sz%401320_480&itj=41"
继续执行
s(tep) – 执行当前行
step up – 执行到当前方法返回到其调用程序
s(tep)i – 执行当前指令
n(ext) – 跳过一行(跨过调用)
c(ont) – 从断点处继续执行
不说了,help是一种美德
<1> main[1] help
** command list **
connectors -- list available connectors and transports in this VM
run [class [args]] -- start execution of application's main class
threads [threadgroup] -- list threads
thread <thread id> -- set default thread
suspend [thread id(s)] -- suspend threads (default: all)
resume [thread id(s)] -- resume threads (default: all)
where [<thread id> | all] -- dump a thread's stack
wherei [<thread id> | all]-- dump a thread's stack, with pc info
up [n frames] -- move up a thread's stack
down [n frames] -- move down a thread's stack
kill <thread id> <expr> -- kill a thread with the given exception object
interrupt <thread id> -- interrupt a thread
print <expr> -- print value of expression
dump <expr> -- print all object information
eval <expr> -- evaluate expression (same as print)
set <lvalue> = <expr> -- assign new value to field/variable/array element
locals -- print all local variables in current stack frame
classes -- list currently known classes
class <class id> -- show details of named class
methods <class id> -- list a class's methods
fields <class id> -- list a class's fields
threadgroups -- list threadgroups
threadgroup <name> -- set current threadgroup
stop in <class id>.<method>[(argument_type,...)]
-- set a breakpoint in a method
stop at <class id>:<line> -- set a breakpoint at a line
clear <class id>.<method>[(argument_type,...)]
-- clear a breakpoint in a method
clear <class id>:<line> -- clear a breakpoint at a line
clear -- list breakpoints
catch [uncaught|caught|all] <class id>|<class pattern>
-- break when specified exception occurs
ignore [uncaught|caught|all] <class id>|<class pattern>
-- cancel 'catch' for the specified exception
watch [access|all] <class id>.<field name>
-- watch access/modifications to a field
unwatch [access|all] <class id>.<field name>
-- discontinue watching access/modifications to a field
trace [go] methods [thread]
-- trace method entries and exits.
-- All threads are suspended unless 'go' is specified
trace [go] method exit | exits [thread]
-- trace the current method's exit, or all methods' exits
-- All threads are suspended unless 'go' is specified
untrace [methods] -- stop tracing method entrys and/or exits
step -- execute current line
step up -- execute until the current method returns to its caller
stepi -- execute current instruction
next -- step one line (step OVER calls)
cont -- continue execution from breakpoint
list [line number|method] -- print source code
use (or sourcepath) [source file path]
-- display or change the source path
exclude [<class pattern>, ... | "none"]
-- do not report step or method events for specified classes
classpath -- print classpath info from target VM
monitor <command> -- execute command each time the program stops
monitor -- list monitors
unmonitor <monitor#> -- delete a monitor
read <filename> -- read and execute a command file
lock <expr> -- print lock info for an object
threadlocks [thread id] -- print lock info for a thread
pop -- pop the stack through and including the current frame
reenter -- same as pop, but current frame is reentered
redefine <class id> <class file name>
-- redefine the code for a class
disablegc <expr> -- prevent garbage collection of an object
enablegc <expr> -- permit garbage collection of an object
!! -- repeat last command
<n> <command> -- repeat command n times
# <command> -- discard (no-op)
help (or ?) -- list commands
version -- print version information
exit (or quit) -- exit debugger
<class id>: a full class name with package qualifiers
<class pattern>: a class name with a leading or trailing wildcard ('*')
<thread id>: thread number as reported in the 'threads' command
<expr>: a Java(tm) Programming Language expression.
Most common syntax is supported.
Startup commands can be placed in either "jdb.ini" or ".jdbrc"
in user.home or user.dir
如果要看别名映射,在终端执行
tom@tom-laptop:~/work/gingerbread$ jdbshell
usage: jdbshell [jdb args]
example: ./jdbshell -sourcepath $ANDROID_SRC_PATH -attach 6107
command alias:
c -- cont
l -- list
n -- next
r -- run
s -- step
si -- stepi
f -- step up
bt -- wherei