Debugging Rust programs with LLDB on Android and iOS

insert image description here

The log information printed in Rust println!()can be displayed in Xcode, but not in Android Studio. So Android can use android_logger to achieve log output. However, it is not enough to debug by printing logs only in development, we also need debug mode. So with the content of this article.

LLDB(Low Level Debugger) is a new generation of lightweight high-performance debugger, and is the default debugger Xcodein and in. Android StudioCompared with Android development students, iOS students are more familiar with it. Rust provides an LLDB-based debugging toolchain, so we can use it to debug Rust programs.

Preparation

First of all, it is recommended that you read Rust library cross-compilation and use in Android and iOS to learn how to integrate Rust programs into Android or iOS. The examples in this article also use the demo of this article.

Whether it is Android or iOS, our content in this article uses CodeLLDBplug-ins for debugging, so you must have VS Code and this plug-in installed.

insert image description here

Android

Concrete operation

Note that in the Android project, we need to configure the debug so file to not be compressed and optimized , and pay attention to the so file generated by debug.

android {
    
    
	...

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

1.push lldb-server to the phone

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

If your device has debugged JNI code before, then this file will exist /data/local/tmp/in the directory, you can ignore this step. Similarly, if you don't have this file, you can create a new Native C++ project and adjust it.
Please add a picture description
Or go to ndk to find lldb-serverthe file:

find . -name lldb-server

Please add a picture description
For example, my mobile phone is 64-bit, so choose aarch64the next one. Then push lldb-serverto the phone.

2. Start the App and get the pid

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

3. Start lldb-server

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

4. Rust project debug configuration

launch.jsonThe configuration is as follows, the function is to attach to the target process.

{
    
    
    "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",
            ]
        }
    ]
}
  • pidFill in as obtained in the previous steps.
  • target/aarch64-linux-android/debug/librust_demo.soFor the debug binary location, keep the same file as in the Android project.

Other parts can not be modified. Then you can interrupt the debug point to debug when you run Debug.
Please add a picture description

question

The above is actually only suitable for rooted devices, otherwise attach xxxit will prompt no permission when executing. So at first I used the emulator, and then got root permission to debug.

Later, I saw the Android basic development practice: how to analyze the gdb debugging part of Native Crash, and found a solution.

When we use Android Studio's lldb debugger for native debugging, we have the following output:

Please add a picture description

As can be seen from the above, Android Studio uses cat output lldb-serverand run-as to execute cat with the permission of the application to receive, and then lldb-serverwrites to the private data directory of the app, and then chmod 700increases the executable permission. Then use the same method to start_lldb_server.shsend a shell script to the app data directory. Finally, run the script with app permissions to start lldb.

In fact, think about this question carefully, why as can be debugged without root, but we can't. So it is to copy the homework of 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"

If it starts, Operation not permittedan error is reported. You can use unix-abstractthe method, start_lldb_server.shwhich is implemented in this method.

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

The corresponding launch.jsonin platform connect connect://localhost:9876is replaced with
platform connect unix-abstract-connect:///<package-name>/debug.sock.

Of course, <package-name>/debug.sockyou can define the name however you want, here is just a suggested format to avoid conflicts.

optimization

In the fourth step, we need to manually replace the pid every time, which is quite troublesome. We can optimize this and get the pid dynamically.

Create tasks.jsona file with the following content:

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

The function is to get the pid, and then write it into the pid.txt file.

launch.jsonModify as follows:

{
    
    
    "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"
            }
        }
    ]
}

At runtime, read pid.txtthe file content as the pid.

  • Note that the usage here extension.commandvariable.file.contentrequires vs code to install Command Variablethe plug-in.
  • The problem with this method is that the pid in the file will be read first, and it will be get pidexecuted earlier. So you need to run it a second time to get the correct pid. But since the pid will not change as long as the app is not killed, it is not a big problem.

Although Android can debug Rust code through the above method, after all, it does not have the convenience of direct support from as. For now, talk is better than nothing.

iOS

iOS as a whole is much simpler and more convenient than Android. First, you need to use the debug static library package (command) just like Android cargo lipo. Then in Frameworks,Libraries,and Embedded Contentand Library Search Pathsconfigure libxxx.a.
Please add a picture description
I use the emulator, so I choose x86_64-apple-iosthe folder here.

launch.jsonThe configuration is as follows:

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

Here it is connected according to the App name, RustDemowhich is the name of this demo.

Start the App, and then debug just fine.
Please add a picture description
Isn't it very simple.

reference

Guess you like

Origin blog.csdn.net/qq_17766199/article/details/129402080