android 如何分析应用的内存(三)

news2024/11/23 19:07:03

android 如何分析应用的内存(三)

接上文

细节部分包括如下

  1. native部分
    • 寄存器内容是什么。如pc指向何处,sp指向何处
    • 指定地址内容是什么。如变量a对应的内容
    • 线程堆栈内容是什么。如主线程的堆栈,UI线程的堆栈
    • 堆区的对象有哪些。
  2. java部分
    • 线程堆栈有什么内容。
    • 堆中对象分配情况

其中介绍了native部分的前三个。

这些都需要使用工具才能查看,部分内容还需要写代码才能查看,因此先介绍了xdd工具,
接着介绍了gdb工具,接下来介绍lldb。

前一篇文章中,介绍的gdb,是GCC项目的调试器。
从android NDKr13中推荐使用LLVM项目,到NDK r18全面拥抱LLVM项目之后

android更推荐使用的调试器为LLVM项目的LLDB.

工具篇LLDB

本小节主要为两大内容:

  1. lldb的命令行调试
  2. lldb在AS上面的调试

前者更加适用于Framework工程师,MMI工程师

后者更加适用于Android应用工程师,但我也推荐Android应用工程师阅读本小节内容。
理由有三:

  1. 部分应用因为不知名原因,导致AS调试经常断连
  2. 部分应用使用了第三方库,第三方库中一些SIGxx,可能导致AS没办法正确显示。
  3. 不支持AS进行lldb调试的设备。如何查看请看,LLDB的GUI篇

LLDB的命令行

在进行调试之前,依然要根据前一篇文章所提及的步骤,对so库进行处理

1. 在合适的地方,加上-g选项,打开调试。可以是Android.mk也可以是build.gradle,还可以是CMake,依据自己的项目编译脚本而定
2. 去掉apk里面的编译优化,如下:
buildTypes {
        debug {
            minifyEnabled false
            shrinkResources false
        }
    }
packagingOptions{
        doNotStrip "*/arm64-v8a/*.so"
    }

跟gdb一样,LLDB依然有server和client两部分。

server运行在android平台,client运行在PC平台

第一步:将server拷贝到手机中
NDK中的工具链文件夹中,已经将LLDB放入其中,位置如下:
NDK目录/llvm/prebuilt/对应平台/lib64/clang/版本号/lib/linux/androidABI/lldb-server

注意:在不同版本的NDK包中,可能路径不一样,可升级NDK包进行查看

将其push到手机中如下:

## push到/data/local
adb push lldb-server /data/local/
## 赋予可执行权限
adb shell chmod 777 /data/local/lldb-server

第二步:运行server

./lldb-server p --listen "*:5039" --server
## 其中p表示,使用platform命令,即pc端可使用platform进行连接
## 除了p以外,还可以使用g。表示使用gdb远程协议,此处不做介绍

下面介绍lldb-server的p模式下的命令参数

--server:运行在服务器模式,这样可以操作多个连接。如果没有这个选项,仅仅接受一个连接,并在完成
    之后自动退出
--listen <host>:<port>:监听的主机名字和端口,如果端口位置为0,则会使用一个随机端口。上面命
    令的星号,表示所有主机,端口为5039.即,使用一个可用主机的5039号端口。
--socketf-file <path>:将正在监听的端口号写入这个文件中,当--listen指定的端口号为0时有用。
--log-file <path>:将log输出到指定的文件中,如果没有,则输出到stderr

第三部:设置端口转发命令

adb forward tcp:5039 tcp:5039
## 同gdb一样,需要设置端口转发命令,将android端的5039转发到pc端的5039

第四步:使用client命令连接server

lldb的client命令在
NDK目录/toolchains/llvm/prebuilt/pc平台/bin/lldb

注意:在不同版本的NDK包中,可能路径不一样,可升级NDK包进行查看
运行如下命令

.\lldb.cmd
## 进入lldb的命令行界面。
## 注意,同目录下还会有一个lldb.exe命令,该命令为lldb.cmd要使用的命令,直接运行它可能会出现
## 一些python路径找不到的情况 。因此,在lldb.cmd中进行了正确的配置。

## 同样的,如果实在linux环境下,应该运行的是lldb.sh而不是直接运行lldb(包括mac)

第五步:在lldb中选择正确的平台插件

platform list 
## 查看支持的平台有哪些,输出如下:
(lldb) platform list
Available platforms:
host: Local Windows user platform plug-in.
remote-linux: Remote Linux user platform plug-in.
remote-freebsd: Remote FreeBSD user platform plug-in.
remote-netbsd: Remote NetBSD user platform plug-in.
remote-openbsd: Remote OpenBSD user platform plug-in.
remote-ios: Remote iOS platform plug-in.
remote-macosx: Remote Mac OS X user platform plug-in.
host: Local Mac OS X user platform plug-in.
remote-windows: Remote Windows user platform plug-in.
remote-gdb-server: A platform that uses the GDB remote protocol as the communication transport.
remote-android: Remote Android user platform plug-in.

选择正确的平台插件,我们调试Android,就应该选择remote-android.如果不选择,则为本机的平台(此处为windows)

platform select remote-android
## 运行之后,它会提示,如下:
(lldb) platform select remote-android
  Platform: remote-android
 Connected: no
 ##此处显示还未连接,故下一步就是进入连接

第六步:连接远端server

platform connect connect://P1008N19120251:5039
## 其中P1008N19120251表示adb devices列出的sn号
## 5039是对应的端口号,输出如下:
(lldb) platform connect connect://P1008N19120251:5039
  Platform: remote-android
    Triple: aarch64-unknown-linux-android
OS Version: 27 (3.18.71-perf)
  Hostname: localhost
 Connected: yes
WorkingDir: /data/local
    Kernel: #1 SMP PREEMPT Tue Aug 30 19:49:21 CST 2022

第七步:与具体的程序进行连接

process attach --pid 123
## 与pid为123的程序建立连接

process attach --name programName
## 与programName建立连接 (注意Android平台不可用)

process attach --name programName --waitfor
## 一旦programName启动,就和它进行连接(注意Android平台不可用)

一旦连接成功输出大致如下:

(lldb) process attach --pid 20469
Process 20469 stopped
* thread #1, name = 'findpiano.piano', stop reason = signal SIGSTOP
    frame #0: 0x000000704c878be4 libc.so`__epoll_pwait + 8
libc.so`__epoll_pwait:
->  0x704c878be4 <+8>:  cmn    x0, #0x1, lsl #12         ; =0x1000
    0x704c878be8 <+12>: cneg   x0, x0, hi
    0x704c878bec <+16>: b.hi   0x704c829f44              ; __set_errno_internal
    0x704c878bf0 <+20>: ret
  thread #2, name = 'Jit thread pool', stop reason = signal SIGSTOP
//省略若干类似log
  thread #114, name = 'pool-15-thread-', stop reason = signal SIGSTOP
    frame #0: 0x000000704c829b2c libc.so`syscall + 28
libc.so`syscall:
->  0x704c829b2c <+28>: svc    #0
    0x704c829b30 <+32>: cmn    x0, #0x1, lsl #12         ; =0x1000
    0x704c829b34 <+36>: cneg   x0, x0, hi
    0x704c829b38 <+40>: b.hi   0x704c829f44              ; __set_errno_internal

Executable module set to "C:\Users\wanbiao\.lldb\module_cache\remote-android\.cache\27FE2319-A0C9-B3CD-A572-C955E7D6E716\app_process64".

第八步:进入真正的调试阶段

  • 如何设置源码

A,先查看调试表中的源码路径

image lookup -vn functionname
## 查看functionname对应的信息,其中就包括对应的源码路径位置。
参数说明:
-v: 输出详细信息
-n: 后面跟的是待查找的符号名字,如函数名
-a: 后面跟一个地址表达式
-r: 后面跟一个正则表达式,按照正则表达式查找
-t: 后面跟一个类型名,按照类型名查找

注意:在ubunt系统中,-vn 应该分开写成 -v -n

举例如下:(从此处开始,后面的例子,用图片展示)
在这里插入图片描述

从图片可以看到,notifyChannel的源文件是

/Users/biaowan/StudioProjects/FindAndroidPianoApp/rom.sdk/src/main/cpp/findmidiserver/core/MidiPort.cpp

这是我在Mac上面的编译源文件。

B,将上面找到的路径,映射到本地的源文件路径:

settings set target.source-map 源码中的路径  本地路径
## 设置源码中的路径,映射为本地路径
 settings show target.source-map
 ## 查看源码中的路径映射关系
source list -f xxx.cpp
## 查看源文件

注意注意:非常得不幸,lldb在ndk24以下的版本中target.source-map没有作用,而在Ubuntu和MAC上面运行良好
如若发现不能正常工作,可切换到ndk 25.2.9519653版本中。亲测有效,例子如下:
在这里插入图片描述

补充知识:如何查看一个so库中,对应的源文件路径都有哪些呢?
在lldb中并没有找到对应的命令,若有人知道,请告诉我。
而我经常使用的命令是:

gdb
## 运行gdb,进入gdb命令行
file xx.so
## 加载xx.so文件
info sources
## 打印所有的可能的源文件

在这里插入图片描述

  • 如何设置断点
breakpoint set --file myfile.cpp --line 42
## 再文件的第几行,设置断点

breakpoint set --name myFunction
## 再函数处,设置断点

breakpoint set --address 0x12345678
## 在地址处设置,端点
  • 如何查看断点
breakpoint list

举例如下:
在这里插入图片描述

设置断点的命令依然还有很多,可以查看https://lldb.llvm.org/use/tutorial.html

  • 如何设置watchpoint

因为在我正在使用的版本不支持watchpoint,所以此处不做介绍,后面可能出一篇专门的lldb的文章,到时候会详细介绍,为了达到类似的效果,我们使用条件断点,如下

  • 如何设置条件断点

在断点后面,加上–conditoin “表达式”,举例如下:

breakpoint set --file MidiPort.cpp --line 304 --condition "buff[1]=0x9f"
## 当buff[1]等于0x9f时,断点停止在304行

因为mac和ubuntu的lldb可以使用tab进行补全命令,因此后续的举例将直接在mac或者ubuntu中进行
在这里插入图片描述

  • 如何查看线程
thread list
## 列出素有的线程,包括线程id,线程名等等
thread info 线程id
## 查看对应线程的详细信息,包括线程ID,状态,寄存器等
thread backtrace 线程id
## 查看对应线程的调用栈
thread select 线程id
## 切换到对应的线程

在这里插入图片描述
在这里插入图片描述

  • 如何进行单步调试
step ## 单步执行,进入函数的调用
next ## 单步执行,跳过函数的调用
finish ## 在当前函数中,执行到返回语句
thread step-in ## 在当前线程中,执行单步调试,进入函数中
thread step-over ## 在当前线程中,执行单步调试,跳过函数调用
thread step-out ## 在当前线程中,执行单步调试,从当前函数返回
  • 如何读取变量的值
print (简写p) ## 打印变量
print -f format 变量 ## 以format格式打印变量
## format 有binary、decimal、hex、octal、float、char 等
## -r 表示以原始格式打印

在这里插入图片描述

express ## 计算并打印表达式的值
## 它所具有的参数,同print一样,有-f,-r
## 两者之间的区别就是express会计算表达式

在这里插入图片描述

  • 如何切换栈帧
thread backtrace 
## 打印当前的线程栈
frame select number
## 切换到number对应的栈帧中
  • 如何查看已经加载的共享库
image list 
## 查看所有的已经加载的共享库
image list -b xx.so
## 查看某个共享库
  • 如何添加符号表
target symbols add /path/to/symbol/file
## 将指定的路径添加到符号表搜索路径中。
  • 如何查看当前堆栈的所有变量
frame variable
## 查看当前堆栈的所有变量
frame variable --show-types <type>
## 只查看特定类型的变量
frame variable myVariable
## 只查看myVariable变量
  • 如何查看寄存器的值
register read
## 查看所有寄存器的值
register read --format hex
## 以十六进制查看
## --format 可以有binary、decimal、hex、float 等
register read sp
## 读取sp寄存器的值
  • 如何查看某个内存地址
memory read address
## 查看某个地址的内容
memory read -c n address
## 查看address开始的n个字节
memroy read -c n address --format format
## 以format格式查看,format的值有binary、decimal、hex、float 
  • 如何修改某个内存的值
memory write address value
## 写入value到某个内存中
  • 如何查看当前线程的全局变量
target variable

LLDB分析coredump

第一步当然是加载corefile。以上一篇的core.27055为例。格式如下

lldb.sh  <执行文件>  -c  <corefile>

在这里插入图片描述

一旦加载成功,则可以使用上面介绍的命令

LLDB的及使调试技术

LLDB的及时调试,同GDB一样,包括两部分

  1. 在程序启动的时候,就连接上lldb
  2. 在程序崩溃的时候,就连接上lldb

但这两部分的操作同GDB一样,再此不再过多介绍。

到目前为止,所有文章所使用的Android版本最高为8.1,因此对于其后出现的一些调试技术,可能稍微有差异
但这些差异并不是这些技术的落后,而是对这些技术的一层层封装。读者可放心使用

因为LLDB的GUI调试,会有大量的图片产生,因此,将这部分放入下一篇文章中。
在下一篇文章中,将会有两种GUI的使用。分别是AS和VScode

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/610107.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

图像边缘提取

什么是图像边缘: 图象的边缘是指图象局部区域亮度变化显著的部分&#xff0c;该区域的灰度剖面一般可以看作是一个阶跃&#xff0c;既从一个灰度值在很小的缓冲区域内急剧变化到另一个灰度相差较大的灰度值。 什么是灰度值: 指图像中点的颜色深度&#xff0c;范围一般从0到255…

C/C++性能提升之cache分析

在开发过程中&#xff0c;我们有时会碰到程序性能瓶颈&#xff0c;这时候需要我们查找热点代码&#xff0c;借用一些命令、工具去分析自己的程序&#xff0c;下面我就介绍一下如何使用perf工具分析程序的cache命中率。 在编写代码前先介绍一下我们的硬件平台&#xff0c;我电脑…

【LeetCode全题库算法速练】2、两数相加

文章目录 一、题目&#x1f538;题目描述&#x1f538;样例1&#x1f538;样例2&#x1f538;样例3 二、代码参考 作者&#xff1a;KJ.JK &#x1f308; &#x1f308; &#x1f308; &#x1f308; &#x1f308; &#x1f308; &#x1f308; &#x1f308; &#x1f308; &a…

chatgpt赋能python:Python多行代码一行展示:精简代码,高效编程的绝佳选择

Python多行代码一行展示&#xff1a;精简代码&#xff0c;高效编程的绝佳选择 介绍 在Python的开发中&#xff0c;我们经常需要编写较长的代码&#xff0c;在展示和调试代码时&#xff0c;多行代码会使代码显得过长和复杂。同时&#xff0c;多行代码还会增加代码中的空白行数…

chatgpt赋能python:Python处理颜色RGB简介

Python 处理颜色 RGB 简介 在现代 Web 设计中&#xff0c;颜色的使用非常重要。网站和应用程序的设计师通常需要控制经他们的项目中使用的颜色。最常见的颜色表示方法是 RGB&#xff0c;即红、绿、蓝。RGB 是一种添加光线颜色的方法&#xff0c;它基于红、绿和蓝三种颜色原料的…

Linux操作系统——第一章 进程

目录 基本概念 描述进程-PCB task_struct-PCB的一种 task_ struct内容分类 组织进程 查看进程 通过系统调用获取进程标示符 通过系统调用创建进程-fork初识 进程状态 进程状态查看 Z(zombie)-僵尸进程 僵尸进程危害 孤儿进程 进程优先级 基本概念 查看系统进程 …

【RestAPI】优秀Rest API设计规范

一、API 设计原则 将 REST 映射到 DDD 模式 实体、聚合和值对象等模式旨在对领域模型中的对象施加特定的约束。 在 DDD 的许多介绍文章中&#xff0c;模式是使用构造函数或属性 getter 和 setter 等面向对象的 (OO) 语言概念建模的。 设计 API 时&#xff0c;请考虑这些 API…

前后端分离的前端部署渲染方案总结

前后端分离主要是为了区分后端和前端&#xff0c;以前前端代码是直接将HTML和静态文件丢给后端&#xff0c;由后端完成数据动态交互&#xff0c;所以后端既要写后端逻辑&#xff0c;又要写前端的数据交互逻辑。 前后端分离后后端只需要提供接口&#xff0c;前端则必须要完成对…

安装lora+启动lora+训练一个model

一、安装步骤 conda create -n kohya_ss python3.10.8 cd code git clone https://github.com/bmaltais/kohya_ss.git cd kohya_ss 然后修改了setup.sh里面的xformers里面的下载地址&#xff08;因为自带的那个地址&#xff0c;拉取需要1个小时&#xff0c;太慢了&#xff09;…

chatgpt赋能python:Python基础词汇解析

Python基础词汇解析 作为一门流行且易学的编程语言&#xff0c;Python在很多场合得到了广泛的应用。在学习Python编程的过程中&#xff0c;掌握各类基础词汇是非常关键的。本文将介绍Python编程中一些常见且重要的基础词汇&#xff0c;帮助大家更好地了解和掌握Python编程。 …

chatgpt赋能python:Python多级雷达图绘制解析

Python多级雷达图绘制解析 雷达图&#xff08;Radar Chart&#xff09;是一种可视化工具&#xff0c;常用于多个指标的对比展示。与其他图形不同&#xff0c;雷达图中&#xff0c;数据不是放在X、Y轴上&#xff0c;而是以多边形的形式展现。利用Python语言&#xff0c;可以绘制…

chatgpt赋能python:Python声音检测:如何用Python实现声音检测

Python声音检测&#xff1a;如何用Python实现声音检测 声音检测是近年来越来越受到关注的技术&#xff0c;它可以应用在很多场合&#xff0c;如语音识别、安防监控等。Python作为一种强大的编程语言&#xff0c;也可以实现声音检测功能。本文将介绍Python声音检测的原理、实现…

chatgpt赋能python:Python多选:提升代码效率的必备工具

Python 多选&#xff1a;提升代码效率的必备工具 如果你是一个有多年 Python 编程经验的工程师&#xff0c;那么你肯定会知道 Python 多选是一个非常实用的工具。它可以帮助你提高代码的效率&#xff0c;减少编程的时间和工作量。在本文中&#xff0c;我们将介绍 Python 多选的…

模拟实现 Spring IOC(详解)

文章目录 前言Spring IoCSpring IoC 概述Spring IoC 技术难点Spring IoC 框架思考需求分析 Spring IoC 技术难点实现Spring IoC 模拟实现Bean工厂模式实现Bean注解的循环依赖基础建立 前言 Spring是一种 Java 开发框架&#xff0c;其主要功能有两个&#xff1a;IoC(DI)和AOP。…

什么是高并发?

目录 什么是高井发系统 1.1 什么是高井发 1.2 高井发系统有哪些关键指标 1.2.1 响应时间 1.2.2 吞吐量 1.2.3 每秒请求数(QPS) 1.2.4 每秒事务数 (TPS) 1.2.5 访问量 (PV) 1.2.6 独立访客 (UV) 1.2.7 网络流量 1.3 为什么学习高并发系统 1.32在面试中脱颖而出 什么…

Android:Selector + Layer-lists + Shape 实现 “缺右下角Button“

UI需求&#xff1a;实现"缺右下角的渐变Button"效果 实现方式有两种&#xff1a; 一.UI绘制.9背景图&#xff0c;Selector直接实现 二.使用Shape与Selector、Layer-lists实现 UI给的设计稿里没有Button背景图&#xff0c;我用Shape做完了他告诉我他有做背景图&…

字符串搜索算法:暴力搜索,KMP

目录 前言废话暴力搜索KMP算法 前言废话 最近脑子有点昏昏沉沉&#xff0c;喝点那种红枣泡的白酒居然神奇的好了一些&#xff0c;感觉很舒服。看来喝少量的酒可以让人更清醒&#xff0c;长期喝可能有养生的效果&#xff1f; 写道这里去百度了下&#xff0c;发现红枣还真有养生效…

js中this关键字的作用和如何改变其上下文

一、this 关键字的作用 JavaScript 中的 this 关键字引用了所在函数正在被调用时的对象。在不同的上下文中&#xff0c;this 的指向会发生变化。 在全局上下文中&#xff0c;this 指向全局对象&#xff08;在浏览器中是 window 对象&#xff0c;在 Node.js 中是 global 对象&…

CV | Emotionally Enhanced Talking Face Generation论文详解及代码实现

本博客主要讲解了Emotionally Enhanced Talking Face Generation&#xff08;情感增强的谈话人脸生成&#xff09;论文概括与项目实现&#xff0c;以及代码理解。 Emotionally Enhanced Talking Face Generation Paper :https://arxiv.org/pdf/2303.11548.pdf Code: GitHub - s…

ROS:服务数据(srv)的定义与使用

目录 一、服务模型二、创建功能包三、自定义服务数据3.1定义srv文件3.2在package.xml中添加功能包依赖3.3在CMakeLists.txt中添加编译选项3.4编译生成语言相关文件 四、创建代码并编译运行&#xff08;C&#xff09;4.1创建代码4.2编译4.3运行 一、服务模型 Client发布显示某个…