vscode lldb远程调试

背景

在本机远程调试运行在Android环境下的bionic linker64动态库加载过程代码时,发现gdb无法显示STL容器元素内容,只能显示容器结构体数据,另外指针数据只显示指针地址,而非指针所指对象内容。初步分析为bionic linker64使用clang++编译,链接libc++.so库,这些为llvm提供的机制。而gdb为gnu提供的调试工具,其对应gcc编译工具链,链接libstdc++.so。可能是两个编译器对C++实现差异导致。这两种编译器对C++符号修饰一致,因此可兼容执行,但gdb无法解析clang编译的TLS容器元素。采用lldb进行调试,则可显示STL容器元素内容。

vscode调试

针对C/C++调试配置

环境准备

  • 本机:安装vscode

  • 插件:CodeLLDB,C/C++ Extension Pack,ADB Interface for VSCode,Remote-SSH等

  • 调试环境:安装lldb,adb程序(可以是本机,也可以通过remote-SSH插件链接至远程主机)

  • Android环境:安装或从NDK拷贝lldb-server程序,gdbserver64

本地调试配置

用vscode打开被调试程序的源码目录,然后做以下配置:

  1. 创建配置文件:在“Run and Debug”边栏中,点击“create a launch.json file",在标题栏弹出对话框中选择”C++“,即可生成一个launch.json文件

  2. 增加配置模板:在右下方点击”Add Configuration...“,在弹出对话框中选择"{} CodeLLDB: Launch"(没看到就往下拉对话框滚动条),生成lldb调试配置模板。

精简配置

{   "type": "lldb", // 重中之重,此类型为"lldb",而非vscode官网上说的"cppgdb"配上"MIMode": "lldb",如果这样做,那就歇菜了   "request": "launch",   "name": "Launch", // 可以任性点,改一个自己觉得很酷的名字,做为调试标题,调试时好识别,方便选择调试目标   "program": "${workspaceFolder}/<program>",  // 被调试的程序路径   "args": [], // 被调试的程序参数,这个可以没有   "cwd": "${workspaceFolder}" // 被调试时的工作目录,所有相对路径文件都从该目录开始查找 }

这样本地调试配置就这样简单完成了,在源码中打断点,然后点击”Start Debugging“,正式进入调试阶段。

当然,这些涉及程序与源代码关联的问题。要是在本地工程内调试,不存在该问题。若是拷贝过来的程序,可能存在与源代码文件关联问题。

远程调试配置

通过adb连接至Android环境,需要对Android进行root,不能root,那就麻烦了。然后运行lldb-server,步骤如下:

启动lldb-server

adb connect 66.66.66.66:5555 adb root adb forward tcp:5188 tcp:5188  # lldb-server监听端口 adb forward tcp:5189 tcp:5189   # gdbserver监听端口,lldb-server是皮包,真正干活的是gdbserver adb shell lldb-server platform --listen "*:5188" --server --server --gdbserver-port 5189   # 运行lldb-server

调试配置文件配置,具体选项需参照CodeLLDB手册疑难解答或者LLDB手册

远程调试本地配置

{   "name": "hello",   "type": "lldb",   "request": "launch",   "program": "yourprogram",   "args": [],   "initCommands": [     // 选择并连接远程调试平台,在Android环境下调试     "platform select remote-android", // For example: 'remote-linux', 'remote-macosx', 'remote-android', etc.     "platform connect connect://localhost:5188",     "platform settings -w /tmp", // 上传文件至远程的工作目录,文件默认都上传至该目录。   ],   "env": {     "LD_LIBRARY_PATH": "/tmp", // 传给lldb-server的环境变量,动态库搜索路径,可以不配   },   "cwd": "/tmp", // 远程调试环境的工作目录,如果默认路径就行,此配置多此一举,就删掉它   "breakpointMode": "path", // 以文件全路径的方式打断点,而非只是文件名   "relativePathBase": "${workspaceFolder}/",  // 本地源文件所在的相对目录,千万别配错了,实在不清楚就删掉 }

配置完成好,马马虎虎就能远程调试了。对于没有root的Android环境,上传程序文件至/tmp或者其它可执行权限的目录,上传失败;上传至数据目录,无执行权限,执行失败。

疑难解答

断点无效

一般来说,断点无效说明程序符号与源代码没有关联上。怎样关联呢,那得看程序中符号指定的源码路径与调试环境下的源码路径是否匹配。

您可在vscode下方的“DEBUG CONSOLE”中输入以下内容查看程序符号中源码路径。

既然指导文件路径了,就有两种情况:相对路径和绝对路径。如果是相对路径,还不在一个路径下,鱼和熊掌不可兼得,只能按偏好选一个;如果是绝对路径,多多益善。

一般都程序符号中默认为绝对路径,怎么会有相对路径呢,那是因为编译linker64时,编译工具链配置了编译选项”-fdebug-prefix-map=/proc/self/cwd=“

绝对路径找不到源文件,说明你程序的编译环境与你的调试环境不在一个主机上,所以麻烦一些。

缺少依赖动态库

程序依赖的动态库在远程调试环境中没有,需要自己上传,当然做个配置就好,不需要每次手共上传

友情提示:上传的动态库均无可执行权限

远程调试原理

待续。。。

夹点私货-融合调试环境

调试需要debug版本,bionic的编译环境使用-O2优化,需要禁用该优化。不知道命令参数如何配置,因此使用了一个土办法,直接该linker的编译文件。

linker编译配置修改

调试文件准备 

以下是原生Android程序的调试及准备流程,linker64编译等。将bionic库替换成gun库的说明见下下。

环境准备说明

准备gnu库

包括gnu的基础库,如:libc,libstdc++等

准备gnu库环境

调试linker

linker调试与其它的有些差异,一是断点不生效,而是上传的linker无可执行权限。

断点无效解决方法

linker在程序运行前运行,不知怎么回事,lldb无法捕获其断点,现在用一个土办法来规避问题。若谁有好主意,请不吝赐教。

基本上是两步,第一步,在运行时暂停;第二步,重新使能断点。

在BREAKPOINTS边栏的右上角点击两下红色标记的图标。注意该图标可能隐藏了,鼠标指向该位置时会显示。

image-20240218-081912.png

linker无执行权限解决方法

使用”target modules add“上传的库无可执行权限,因此需要用其它方式,在调试开始前将linker文件上传。

规避方式有两步,第一步,在tasks.json文件中上传和修改权限操作;第二步,在launch.json文件中增加"preLaunchTask"选项("preLaunchTask": "install linker64",)。

tasks和launch json配置