在Android与iOS中使用LLDB调试Rust程序

在这里插入图片描述

在Rust中通过println!()打印的日志信息在Xcode中可以显示,但是Android Studio里不显示。所以Android可以使用android_logger实现日志输出。但是开发中仅使用打印日志的方式进行调试还是不够的,我们还需要debug调式。所以有了本篇的内容。

LLDB(Low Level Debugger)是新一代轻量级高性能调试器, 是XcodeAndroid Studio中的默认调试器。相较于Android开发同学,iOS同学更熟悉它。Rust提供了基于LLDB的调试工具链,所以我们就可以借此用来调试Rust程序。

准备工作

首先建议你先阅读Rust库交叉编译以及在Android与iOS中使用这篇内容,了解如何将Rust程序集成进Android或iOS。本篇的例子也是使用此篇的demo。

不论是Android还是iOS,我们本篇的内容都使用CodeLLDB插件进行调试,所以你必须有安装 VS Code和此插件。

在这里插入图片描述

Android

具体操作

注意Android项目中我们需要将调试的so文件配置为不压缩优化,同时注意使用debug生成的so文件。

android {
    
    
	...

	packagingOptions{
    
    
    	doNotStrip "**/libxxx.so"
	}
}

1.push lldb-server到手机

adb push lldb-server /data/local/tmp/

如果之前你的设备有调式过JNI代码,那么此文件就会存在/data/local/tmp/目录下,可以忽略这一步。同理,如果你没有这个文件,可以新建一个Native C++项目调式一下。
请添加图片描述
或者去ndk中寻找lldb-server文件:

find . -name lldb-server

请添加图片描述
比如我的手机64位的, 所以选择aarch64下的。然后pushlldb-server到手机。

2.启动App,然后获取pid

# 启动App
adb shell am start -a android.intent.action.MAIN -n <package-name>/.<activity-name>
# 获取pid
adb shell pidof <package-name>

3.启动lldb-server

adb shell /data/local/tmp/lldb-server p --server --listen "*:9876"

4.rust项目debug配置

launch.json配置如下,作用是attch到目标进程。

{
    
    
    "version": "0.2.0",
    "configurations": [
        {
    
    
            "type": "lldb",
            "request": "attach",
            "name": "Android",
            "pid": "xxx",
            "initCommands": [
                "platform select remote-android",
                "platform connect connect://localhost:9876",
                "file target/aarch64-linux-android/debug/libxxx.so",
            ]
        }
    ]
}
  • pid 按照前面步骤获取的填入。
  • target/aarch64-linux-android/debug/librust_demo.so为调试二进制文件位置,保持与Android项目中的文件一致。

其他部分可以不用修改。然后Debug运行就可以打断点进行调式了。
请添加图片描述

问题

以上其实只适合root设备,否则执行attach xxx时会提示无权限。所以一开始我是使用的模拟器,然后获取root权限进行调试的。

后面看到了Android基础开发实践:如何分析Native Crash这篇gdb调试部分,找到了解决思路。

我们用Android Studio的lldb调试器进行native调试时有如下的输出:

请添加图片描述

从上面可以看出,Android Studio通过cat输出lldb-server并run-as以应用的权限执行cat进行接收,然后将lldb-server写入到app的私有数据目录,紧接着chmod 700增加可执行权限。然后使用同样的方式将一个shell脚本start_lldb_server.sh发送到app数据目录。最后以app的权限运行脚本启动lldb。

其实这个问题仔细想想,为啥as可以不用root就能调试,我们不行。所以就是抄as的作业:

adb shell "cat /data/local/tmp/lldb-server 
| run-as <package-name> sh -c 'cat > /data/data/<package-name>/lldb/bin/lldb-server 
&& chmod 700 /data/data/<package-name>/lldb/bin/lldb-server'"

# 启动lldb-server
adb shell run-as <package-name> ./lldb/bin/lldb-server p --server --listen "*:9876"

如果启动时,报Operation not permitted错误。可以使用unix-abstract方式,start_lldb_server.sh中就是此种方法实现。

adb shell run-as <package-name> ./lldb/bin/lldb-server p --server --listen unix-abstract:///<package-name>/debug.sock

相应的launch.json中的platform connect connect://localhost:9876替换为
platform connect unix-abstract-connect:///<package-name>/debug.sock

当然<package-name>/debug.sock名字随你怎么定义,这里只是给了一个建议格式,避免冲突。

优化

在第四步中,我们需要每次手动替换pid,这还是比较麻烦的。我们可以优化一下这里,动态获取pid。

创建tasks.json文件,输入以下内容:

{
    
    
    "version": "2.0.0",
    "tasks": [
        {
    
    
            "label": "get pid",
            "type": "shell",
            "command": "adb shell pidof <package-name> | tr -d '\n' > ${workspaceRoot}/pid.txt"
        }
    ]
}

作用是获取pid,然后写入到pid.txt文件中。

launch.json修改如下:

{
    
    
    "version": "0.2.0",
    "configurations": [
        {
    
    
            "type": "lldb",
            "request": "attach",
            "name": "Attach",
            "pid": "${input:pid}",
            "preLaunchTask": "get pid",
            "initCommands": [
                "platform select remote-android",
                "platform connect connect://localhost:9876",
                "file target/aarch64-linux-android/debug/libxxx.so",
            ]
        }
    ],
    "inputs": [
        {
    
    
            "id": "pid",
            "type": "command",
            "command": "extension.commandvariable.file.content",
            "args": {
    
    
                "fileName": "${workspaceFolder}/pid.txt"
            }
        }
    ]
}

运行时,读取pid.txt文件内容作为pid。

  • 注意这里使用extension.commandvariable.file.content需要vs code安装Command Variable插件。
  • 目前这种方式的问题是会先读取文件中的pid,早于get pid的执行。所以需要运行第二次才可以拿到正确的pid。但由于pid只要app不杀死就不会变动,所以问题不大。

Android虽然可以通过上述方法实现调试Rust代码,但毕竟没有as直接支持来的方便。目前来说,聊胜于无。

iOS

iOS整体比Android简单方便很多,首先和Android一样需要用debug静态库包(cargo lipo命令)。然后在 Frameworks,Libraries,and Embedded ContentLibrary Search Paths 配置libxxx.a
请添加图片描述
我用的模拟器,所以这里选择x86_64-apple-ios文件夹下的。

launch.json配置如下:

{
    
    
    "version": "0.2.0",
    "configurations": [
        {
    
    
            "type": "lldb",
            "request": "attach",
            "name": "iOS",
            "program": "RustDemo",
        },
    ]
}

这里是根据App名称连接的,RustDemo就是这个的demo的名称。

启动App,然后debug就好了。
请添加图片描述
是不是非常简单。

参考

猜你喜欢

转载自blog.csdn.net/qq_17766199/article/details/129402080