Android 怎么通过adb 查看当前activityi pc蛋蛋信息

46669人阅读
移动开发工具(27)
Android(43)
常识类手册(53)
& Android测试中,经常需要kill掉应用程序。
& 网上有用方法:adb shell中的kill命令后面跟的参数是pid,因此先ps所有的应用程序。缺点是,PS显示很长,找到你的应用比较麻烦。
& 另一种方法是使用包名,比较简单:
&&C:\Android\adt-bundle-windows-x86-\sdk\platform-tools&adb shell &am force-stop 包名&
& &比如 com.amap。
附录参考:
am命令,am全称activity manager,你能使用am去模拟各种系统的行为,例如去启动一个activity,强制停止进程,发送广播进程,修改设备屏幕属性等等
命令窗口通过adb&&进入&的Linux命令界面,输入am
-help看到如下信息:
ce-stop &PACKAGE& : 强制停止指定的package包应用。
kill [options] &PACKAGE& :杀死指定package包应用进程,该命令在模式下杀死进程,不影响用户体验。参数选项:--user &USER_ID&
| all | current: 指定user进程杀死,如果不指定默认为所有users。(关于USER_ID下面会介绍到)
kill-all :杀死所有的后台进程。
broadcast [options] &INTENT& :发送一个intent。具体intent参数参照start命令参数。参数选项:--user &USER_ID& | all | current: 指定user进程杀死,如果不指定默认为所有users。
instrument [options] &COMPONENT& :测试命令,不多作介绍。
profile start &PROCESS& &FILE& :在&PROCESS&进程中运行profile,分析结果写到&FILE&里。
profile stop &PROCESS& :停止profile。
set-debug-app [options] &PACKAGE& :设置package包应用为debug模式。参数选项:-w|--persistent:等待进入调试模式,保留值。
clear-debug-app :清空之前用set-debug-app命令设置的package包应用。
以下命令查看官网:
monitor [options]
screen-compat [on|off] &PACKAGE&
display-size [reset|&WxH&]
display-density &dpi&
to-uri &INTENT&
to-intent-uri &INTENT&
我们可以通过命令启动android中的Activity,Service,BroadcastReceiver 等组件
1. 拨打一个电话:
&&& am start -a android.intent.action.CALL -d tel:10086
&&& 这里-a表示动作,-d表述传入的数据,还有-t表示传入的类型。
2. 打开一个网页:
&&&&am start -a android.intent.action.VIEW -d&&&(这里-d表示传入的data)
3. 打开音乐播放器:
&&&&am start -a android.intent.action.MUSIC_PLAYER 或者
&&& am start -n com.android.music/om.android.music.MusicBrowserActivity
4. 启动一个服务:
&&&&am startservice &服务名称&
&&&&例如:am startservice -n com.android.music/com.android.music.MediaPlaybackService (这里-n表示组件)
&&& 或者&& am startservice -a com.smz.myservice (这里-a表示动作,就是你在Androidmanifest里定义的)&
5. 发送一个广播:
&&&&am broadcast -a &广播动作&
&&& 例如: am broadcast -a com.smz.mybroadcast
6 启动一个Activity&
# am start -n 包(package)名/包名.活动(activity)名称
启动的方法可以从每个应用的AndroidManifest.xml的文件中得到
Music 和 Video(音乐和视频)的启动方法为:
# am start -n com.android.music/com.android.music.MusicBrowserActivity
# am start -n com.android.music/com.android.music.VideoBrowserActivity
# am start -n com.android.music/com.android.music.MediaPlaybackActivity
Camera(照相机)的启动方法为:
# am start -n com.android.camera/com.android.camera.Camera
Browser(浏览器)的启动方法为:
# am start -n com.android.browser/com.android.browser.BrowserActivity
启动 google map 直接定位到北京 :
am start -a android.intent.action.VIEW geo:0,0?q=beijing
am还有很多的用法,有待研究。
接下来介绍pm命令,pm全称package manager,你能使用pm命令去模拟android行为或者查询设备上的应用等,当你在adb shell命令下执行pm命令:
pm &command&
你也可以在adb shell前执行pm命令:
adb shell pm uninstall com.example.MyApp
关于一些pm命令的介绍:
list packages [options] &FILTER& :打印所有包,选择性的查询包列表。参数选项:-f:查看关联文件,即应用apk的位置跟对应的包名(如:package:/system/app/MusicPlayer.apk=com.sec.android.app.music);-d:查看disabled packages;-e:查看enable package;-s:查看系统package;-3:查看第三方package;-i:查看package的对应安装者(如:1、package:com.tencent.qqmusic
installer=null 2、package:com.tencent.qqpim installer=com.android.vending);-u:查看曾被卸载过的package。(卸载后又重新安装依然会被列入);--user&USER_ID&:The user space to query。
list permission-groups :打印所有已知的权限群组。
list permissions [options] &GROUP& :选择性的打印权限。参数选项:
list features :设备特性。硬件之类的性能。
list libraries :当前设备支持的libs。
list users :系统上所有的users。(上面提到的USER_ID查询方式,如:UserInfo{0:Primary:3}那么USER_ID为0)
path &PACKAGE& :查询package的安装位置。
install [options] &PATH& :安装命令。
uninstall [options] &PACKAGE& :卸载命令。
clear &PACKAGE& :对指定的package删除所有数据。
enable &PACKAGE_OR_COMPONENT& :使package或component可用。(如:pm enable \& package/class\& )
disable &PACKAGE_OR_COMPONENT& :使package或component不可用。(如:pm disable \& package/class\& )
disable-user [options] &PACKAGE_OR_COMPONENT& :参数选项:--user &USER_ID&: The user to disable.
grant &PACKAGE_PERMISSION& :授权给应用。
revoke &PACKAGE_PERMISSION& :撤销权限。
set-install-location &LOCATION& :设置默认的安装位置。其中0:让系统自动选择最佳的安装位置。1:安装到内部的设备存储空间。2:安装到外部的设备存储空间。(这只用于调试应用,使用该命令可能导致应用退出或者其他不适的后果)。
get-install-location :返回当前的安装位置。返回结果同上参数选项。
set-permission-enforced &PERMISSION& [true|false] :使指定权限生效或者失效。
create-user &USER_NAME& :增加一个新的USER。
remove-user &USER_ID& :删除一个USER。
get-max-users :该设备所支持的最大USER数。(某些设备不支持该命令)
/program/577315.html
/AutomateProgram/archive//117595.aspx
shell中的kill命令后面跟的参数是pid,因此先ps所有的应用程序:
adb在shell下:
PID&& PPID& VSIZE RSS&& WCHAN&&& PC&&&&&&&& NAME
720&& 553&&
ffffffff afe0d3e4 S com.android.music
例如要关掉music
# kill 720
&&相关文章推荐
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
访问:1295744次
积分:11587
积分:11587
排名:第1243名
原创:172篇
转载:221篇
译文:14篇
评论:247条
(1)(1)(1)(2)(5)(6)(1)(7)(2)(6)(2)(8)(7)(17)(9)(16)(14)(11)(13)(32)(24)(20)(39)(13)(19)(10)(13)(7)(13)(15)(5)(3)(9)(14)(8)(6)(22)(14)android 5 HOOK 技术研究之 ADBI 项目
时间: 13:08:07
&&&& 阅读:6765
&&&& 评论:
&&&& 收藏:0
标签:简介
adbi 是一个android平台的二进制注入框架,源码开放在github上 : &&,从hook技术的分类来说,其属于so注入+inline hook, 这种方式的套路是:基于linux系统的ptrace机制,attach一个目标进程,注入一个动态链接库进入目标进程的地址空间,然后用so里边的函数地址替换目标进程地址空间里原有的函数地址(老的函数地址一般也需要保存起来)。
hijack: &可执行程序,用于注入一个so到目标进程
libbase: &注入库,提供hook和unhook函数,一般编译为一个静态库
example: 一个hook例子(hook了系统函数 epoll_wait),使用libbase,编译成一个动态库,由 hijack 注入目标进程
默认从github clone下面的编译文件 Android.mk 里目标体系架构是 arm ,所以要运行测试例子需要准备一个 Arm 模拟器,另外, README.md 给出的编译方式是 linux 系统的shell脚本,在Windows下不能直接使用,需要改成相应的批处理脚本。这里我直接手动进入相关目录执行ndk-build.
1. 下载ndk, & 下载 ndk
2. 解压到某一目录,将ndk的路径设置进 PATH 环境变量
3. git clone adbi 项目
4. 进入hijack/jni, &instruments/base/jni, instruments/base/example/jni 分别执行 ndk-build
5. 启动模拟器
我使用android studio avd 管理器创建了一个模拟器,参数如下
使用android studio 创建一个android项目(默认即可),启动模拟器
6. adb push 刚刚编译好的文件到模拟器,adb shell 进入模拟器并执行注入
这个文件实现了一个注入工具,可以向 -p 参数指定的进程注入一个so。
要实现这个效果,首先,需要得到目标进程若干函数如dlopen函数的地址,其次,需要能影响目标进程的正常执行流,让其中间某个时候执行dlopen加载指定的库,最后,还要能用动态加载的so里的函数覆盖原有内存里的函数。
下面开始研究,如何得到目标进程指定函数的地址,首先要得到的是dlopen函数的地址,adbi是这么做的:
void *ldl = dlopen("libdl.so", RTLD_LAZY);
if (ldl) {
dlopenaddr = (unsigned long)dlsym(ldl, "dlopen");//dlopenaddr 存放本进程的dlopen函数地址
dlclose(ldl);
unsigned long int
unsigned long int lkaddr2;
find_linker(getpid(), &lkaddr);
find_linker(pid, &lkaddr2);
dlopenaddr = lkaddr2 + (dlopenaddr - lkaddr); // dlopenaddr 存放目标进程的dlopen函数的地址
上述代码是为了得到目标进程的dlopen函数地址。
首先,dlopen加载libdl.so,由于进程启动后libdl.so肯定会先加载好,所以这里返回已经加载好的libdl.so映射在本进程的起始地址空间,然后调用dlsym返回本进程的dlopen函数地址。
接着,find_linker函数利用&/proc/pid/maps 文件可以得到进程pid的地址空间进而得到libdl.so映射到内存的起始地址,其中,注入进程的libdl.so映射的初始地址是 lkaddr, 目标进程是lkaddr2
最后,再利用dlopen函数在libdl.so动态库的代码的偏移是固定的(注入进程和被注入进程使用的是同一个libdl.so),dlopenaddr - lkaddr 先算出这个偏移值,lkaddr2 再上上述偏移值即得到目标进程的 dlopen 函数的地址
&maps文件在linux和android上的地址块命名有些区别,一般linux上libdl.so映射的地址是这样的
7f6af6a r-xp
08:01 397502
/lib/x86_64-linux-gnu/ld-2.19.so
android 里的命名叫 linker
find_linker 函数调用了 load_memmap函数和&find_linker_mem函数,
static int find_linker(pid_t pid, unsigned long *addr)
struct mm mm[1000];
unsigned long
char libc[256];
if (0 & load_memmap(pid, mm, &nmm)) {
printf("cannot read memory map\n");
return -1;
if (0 & find_linker_mem(libc, sizeof(libc), &libcaddr, mm, nmm)) {
printf("cannot find libc\n");
return -1;
load_memmap 函数基本流程:打开maps文件,按照maps文件的格式解析成一个数组,每一项存放一个动态库的名称以及其映射到内存里的起始和结束地址
static int
load_memmap(pid_t pid, struct mm *mm, int *nmmp)
char raw[80000]; // this depends on the number of libraries an executable uses
char name[MAX_NAME_LEN];
unsigned long start,
struct mm *m;
int nmm = 0;
sprintf(raw, "/proc/%d/maps", pid);
fd = open(raw, O_RDONLY);
if (0 & fd) {
printf("Can‘t open %s for reading\n", raw);
return -1;
/* Zero to ensure data is null terminated */
memset(raw, 0, sizeof(raw));
while (1) {
rv = read(fd, p, sizeof(raw)-(p-raw));
if (0 & rv) {
//perror("read");
return -1;
if (0 == rv)
if (p-raw &= sizeof(raw)) {
printf("Too many memory mapping\n");
return -1;
close(fd);
p = strtok(raw, "\n");
while (p) {
/* parse current map line */
rv = sscanf(p, "%08lx-%08lx %*s %*s %*s %*s %s\n",
&start, &end, name);
p = strtok(NULL, "\n");
if (rv == 2) {
m = &mm[nmm++];
m-&start =
strcpy(m-&name, MEMORY_ONLY);
if (strstr(name, "stack") != 0) {
stack_start =
stack_end =
/* search backward for other mapping with same name */
for (i = nmm-1; i &= 0; i--) {
m = &mm[i];
if (!strcmp(m-&name, name))
if (i &= 0) {
if (start & m-&start)
m-&start =
if (end & m-&end)
/* new entry */
m = &mm[nmm++];
m-&start =
strcpy(m-&name, name);
find_linker_mem函数的流程:遍历上述数组,根据动态库名称匹配,即可获取libdl.so对应的数组元素,从而得到libdl.so在进程内的起始和终止地址,代码这里就不贴了。
以上,是获取目标进程某个动态库内的函数在目标进程的真实地址的方法。那么目标进程,非动态库函数的地址怎么获取呢?
接下去研究第二个问题,如何影响目标进程的执行流,这里必须介绍ptrace函数了。
#include &sys/ptrace.h&
long ptrace(enum __ptrace_request request, pid_t pid,
void *addr, void *data);
DESCRIPTION
by which one
process (the "tracer") may observe and control the
of another process (the "tracee"), and examine and change the
tracee‘s memory and
registers.
implement breakpoint debugging and system call tracing.
&动态库注入技术一般都依赖于ptrace机制,ptrace是linux kernel 为了支持应用层debug功能而实现的系统调用,这个系统调用提供了&让A进程关联到B进程,并动态修改B进程的内存和寄存器&的机制,由于A进程可以动态修改B进程的内存空间和寄存器的值,所有可以影响B进程的执行序列,比如在正常的执行序列里插入某段代码,让B进程加载一个动态库。
下面研究adbi是怎么使用ptrace达到目的的:
首先,attach到目标进程
if (0 & ptrace(PTRACE_ATTACH, pid, 0, 0)) {
printf("cannot attach to %d, error!\n", pid);
waitpid(pid, NULL, 0);
&其次,获取目标进程当前寄存器
ptrace(PTRACE_GETREGS, pid, 0, &regs);
接着,构造新的寄存器值,这一步是关键。
数组sc存放初始化的指令
unsigned int sc[] = {
0xe59f0040, //
r0, [pc, #64] 48 &.text+0x48&
0xe3a01000, //
r1, #0 0x0
0xe1a0e00f, //
0xe59ff038, //
pc, [pc, #56] 4c &.text+0x4c&
0xe59fd02c, //
sp, [pc, #44] 44 &.text+0x44&
0xe59f0010, //
r0, [pc, #20] 30 &.text+0x30&
0xe59f1010, //
r1, [pc, #20] 34 &.text+0x34&
0xe59f2010, //
r2, [pc, #20] 38 &.text+0x38&
0xe59f3010, //
r3, [pc, #20] 3c &.text+0x3c&
0xe59fe010, //
lr, [pc, #20] 40 &.text+0x40&
0xe59ff010, //
pc, [pc, #20] 44 &.text+0x44&
0xe1a00000, //
0xe1a00000, //
0xe1a00000, //
0xe1a00000, //
0xe1a00000, //
0xe1a00000, //
0xe1a00000, //
0xe1a00000, //
addr of libname
0xe1a00000, //
dlopenaddr
下面使用ptrace获取的寄存器值填充到sc数组的11到17,
sc[11] = regs.ARM_r0;
sc[12] = regs.ARM_r1;
sc[13] = regs.ARM_r2;
sc[14] = regs.ARM_r3;
sc[15] = regs.ARM_
sc[16] = regs.ARM_
sc[17] = regs.ARM_
然后用前面获取到的目标进程的dlopen函数的地址填充到第19位置,18位置存放动态库的名字字符串的地址,然后调用wirte_mem函数将动态库的名字字符串写到libaddr地址指定的内存区。
  sc[19] =
// push library name to stack
libaddr = regs.ARM_sp - n*4 - sizeof(sc);
// write library name to stack
if (0 & write_mem(pid, (unsigned long*)arg, n, libaddr)) {
printf("cannot write library name (%s) to stack, error!\n", arg);
其中,n是这么算的,动态库(如 /data/local/tmp/libexample.so)的字节数+1,然后除以4,如果有余数,结果加1. 其实,得到的n就是以&4字节&为单位的数值,这么算主要是 write_mem函数的实现,下面会看到。结合上面的代码,这个字符串会被写入 libaddr 对应的内存,这个内存地址是这么算的:&
regs.ARM_sp - n*4 - sizeof(sc); 即原来的栈顶指针往低地址移动 &sc 数组大小+动态库字符串字节长度&
            case ‘l‘:
n = strlen(optarg)+1;
n = n/4 + (n%4 ? 1 : 0);
arg = malloc(n*sizeof(unsigned long));
memcpy(arg, optarg, n*4);
下面看write_mem是怎么将一块数据写入目标进程的内存地址的:
static int
write_mem(pid_t pid, unsigned long *buf, int nlong, unsigned long pos)
unsigned long *p;
for (p = buf, i = 0; i & p++, i++)
if (0 & ptrace(PTRACE_POKETEXT, pid, (void *)(pos+(i*4)), (void *)*p))
return -1;
pid是目标进程标识,buf是要写入目标进程内存的数据块,nlong是&4字节&为单位的长度,pos是要写入的地址。 由于数据buf是 long 型数组,所以循环一次即写入4字节的数据。最终是调用 ptrace 函数,另第一个参数为 &PTRACE_POKETEXT 实现写入的。
接下去,写入新的指令数据(即sc数组)到目标进程:
// write code to stack
codeaddr = regs.ARM_sp - sizeof(sc);
if (0 & write_mem(pid, (unsigned long*)&sc, sizeof(sc)/sizeof(long), codeaddr)) {
printf("cannot write code, error!\n");
}// calc stack pointer
可以看到,方法跟上述写动态库名字字符串类似,要写入的目标地址 = 栈顶指针 - Sc 数组长度,然后调用write_mem函数将数组Sc写入
接下去,移动栈顶指针为新的栈顶(往低地址移动&sc数组长度+动态库名字字符串长度&),接下去,根据是否有 mprotect 调用,会有两种执行流:如果没有mprotect,则将PC寄存器的值变成sc数组开始的位置,即接下去直接执行Sc数组的指令。否则,pc寄存器的值设置为 mprotect 函数,然后将lr寄存器设置为sc数组。并将r0,r1,r2参数设置为 mprotect调用的参数,这样,首先执行 mprotect 函数将 r0,r1指定的内存范围设置为 r2指定的权限,在这个例子是,是将目标内存设置为 rwx, 执行完mprotect后再执行 sc 数组的指令。
regs.ARM_sp = regs.ARM_sp - n*4 - sizeof(sc);
// call mprotect() to make stack executable
regs.ARM_r0 = stack_ // want to make stack executable
//printf("r0 %x\n", regs.ARM_r0);
regs.ARM_r1 = stack_end - stack_ // stack size
//printf("mprotect(%x, %d, ALL)\n", regs.ARM_r0, regs.ARM_r1);
regs.ARM_r2 = PROT_READ|PROT_WRITE|PROT_EXEC; // protections
// normal mode, first call mprotect
if (nomprotect == 0) {
if (debug)
printf("calling mprotect\n");
regs.ARM_lr = // points to loading and fixing code
regs.ARM_pc = // execute mprotect()
// no need to execute mprotect on old Android versions
regs.ARM_pc = // just execute the ‘shellcode‘
经过上述设置后,新的寄存器值如下图:
结合上面的图,新的指令流执行如下(下面转自&参考1&)
一点需要说明一下,对于ARM处理器来说,pc寄存器的值,指向的不是当前正在执行指令的地址,而是往下第二条指令的地址。
好,我们正式开始分析代码的含义,指令将从codeaddr指示的位置从低到高依次执行。
第一条指令将pc寄存器的值加上64,读出那个地方的内容(4个字节),然后放到寄存器r0中。刚才说过了,pc寄存器值指向的是当前指令位置加8个字节,也就是说这条指令实际读出的是当前指令位置向下72个字节。由于sc数组是int型的,就是数组当前元素位置向下18个元素处。数一数,刚好是libaddr的位置。所以这条指令是为了让r0寄存器指向.so共享库路径名字符串。
第二条指令很简单,是将0赋值给寄存器r1。
第三条指令用来将pc寄存器值保存到lr寄存器中,这样做的目的是为了调用dlopen()函数返回后,跳转到指令&ldr sp, [pc, #56]&处。
第四条指令是将pc加上56处的数值加载到pc中,pc+56处是哪?当前指令位置往下64字节,16个元素,刚好是dlopen()函数的调用地址。所以,这条指令其实就是调用dlopen()函数,传入的参数一个是r0寄存器指向的共享库路径名,另一个是r1寄存器中的0。
调用dlopen()返回后将继续执行下面的所有指令,我就不一一分析了,作用就是恢复目标进程原来寄存器的值。先是sp,然后是r0、r1、r2、r3和lr,最后恢复原来pc的值,继续执行被暂停之前的指令,就像什么都没发生过一样。
最后,使用ptrace设置新的寄存器值进入目标内存,hook开始生效
    ptrace(PTRACE_SETREGS, pid, 0, &regs);
ptrace(PTRACE_DETACH, pid, 0, (void *)SIGCONT);
最后,如果参数有 -s ,还会执行下述流程:
if (appname) {
if (ptrace(PTRACE_SETOPTIONS, pid, (void*)1, (void*)(PTRACE_O_TRACEFORK))) {
printf("FATAL ERROR: ptrace(PTRACE_SETOPTIONS, ...)");
return -1;
ptrace(PTRACE_CONT, pid, (void*)1, 0);
int child_pid = 0;
for (;;) {
t = waitpid(-1, &stat, __WALL|WUNTRACED);
if (t != 0 && t == child_pid) {char fname[256];
sprintf(fname, "/proc/%d/cmdline", child_pid);
int fp = open(fname, O_RDONLY);
if (fp & 0) {
ptrace(PTRACE_SYSCALL, child_pid, 0, 0);
read(fp, fname, sizeof(fname));
close(fp);
if (strcmp(fname, appname) == 0) {
// detach from zygote
ptrace(PTRACE_DETACH, pid, 0, (void *)SIGCONT);
// now perform on new process
pid = child_
ptrace(PTRACE_SYSCALL, child_pid, 0, 0);
if (WIFSTOPPED(stat) && (WSTOPSIG(stat) == SIGTRAP)) {
if ((stat && 16) & PTRACE_EVENT_FORK) {
if (debug & 1)
printf("fork\n");
int b = // save parent pid
ptrace(PTRACE_GETEVENTMSG, t, 0, &child_pid);
t = child_
ptrace(PTRACE_CONT, b, (void*)1, 0);
ptrace(PTRACE_SYSCALL, child_pid, 0, 0);
if (zygote) {
int i = 0;
for (i = 0; i & i++) {
// -- zygote fix ---
// we have to wait until the syscall is completed, IMPORTANT!
ptrace(PTRACE_SYSCALL, pid, 0, 0);
if (debug & 1)
printf("/");
waitpid(pid, NULL, 0);
ptrace(PTRACE_GETREGS, pid, 0, &regs);
if (regs.ARM_ip != 0) {
if (debug & 1)
printf("not a syscall entry, wait for entry\n");
ptrace(PTRACE_SYSCALL, pid, 0, 0);
waitpid(pid, NULL, 0);
ptrace(PTRACE_SYSCALL, pid, 0, 0);
if (debug & 1)
printf("\\");
waitpid(pid, NULL, 0);
&&国之画&&&& &&&&chrome插件
版权所有 京ICP备号-2
迷上了代码!19036人阅读
移动云(40)
每次发生android runtime exception后都手足无措,因为拿到的都是如下信息:adb logcat:F/libc
(21559): Fatal signal 11 (SIGSEGV) at 0x (code=1), thread 21578 (m.yunos.browser)
160): *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
160): Build fingerprint: 'Android/full_mako/mako:4.2.1/JOP40G/eng.yuanzhijun.707:userdebug/test-keys'
160): Revision: '11'
160): pid: 21559, tid: 21578, name: m.yunos.browser
&&& com.yunos.browser &&&
160): signal 11 (SIGSEGV), code 1 (SEGV_MAPERR), fault addr
r1 74514f0c
r2 0000001c
r3 74252cf8
160): backtrace:
/system/lib/libwebcore.tgl.so
pc 001b8109
/system/lib/libwebcore.tgl.so
......只能大概知道是libwebcore.tgl.so里发生了错误,如何能打印出详细的堆栈信息呢?起码也得知道是哪个方法抛的错。开始我尝试自己写代码来捕获signal,然后再调用backtrace和backtrace_symbols,但在android的linux上用不了两个方法,估计是得装gcc的扩展库,先把这个方案搁置一下,后面再尝试。然后我再尝试NDK下的ndk-stack,但看语义应该需要NDK编译的才能用,反正我尝试了ndk-stack -sym ...&&-dump ...后,还是失败,原因是symbols找不到,如果是基于ndk开发的android native应用应该是没问题,但这里我是直接要调试android源码,也搁置一下。最后尝试用NDK下的addr2line,这个能用,方法就是:ndk path\toolchains\arm-linux-androideabi-4.4.3\prebuilt\windows-x86_64\bin\arm-linux-androideabi-addr2line.exe -C -f -e Y:\webos_nexus4\out\target\product\mako\symbols\system\lib\libwebcore.tgl.so 其中Y:\webos_nexus4\out\target\product\mako\symbols\system\lib\libwebcore.tgl.so是编译出的symbols,对应的是开头看到的堆栈中的地址信息:I/DEBUG
/system/lib/libwebcore.tgl.so
运行后得到如下结构:WebCore::InspectorInstrumentation::instrumentingAgentsForContext(WebCore::ScriptExecutionContext*)虽然很笨拙,但起码查到了错误发生在哪个方法里。前面说的两个思路应该可行,我后面再尝试一下
&&相关文章推荐
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
访问:2799235次
积分:20367
积分:20367
排名:第367名
原创:152篇
转载:14篇
评论:1029条
(1)(1)(1)(1)(5)(3)(3)(7)(1)(2)(1)(2)(12)(7)(3)(2)(2)(2)(3)(6)(3)(2)(8)(19)(11)(2)(7)(9)(5)(7)(12)(7)(9)

我要回帖

更多关于 adb 查看当前activity 的文章

 

随机推荐