mozilla / rust-android-gradle

Apache License 2.0
1.03k stars 67 forks source link

Is there any way to debug rust lib on android application? #22

Open SharpKnifer opened 4 years ago

ncalexan commented 4 years ago

Hi @SharpKnifer -- sorry for the very delayed reply. Yes, you can debug a Rust library using all of the regular NDK debugging tools available on Android, i.e. using lldb from within Android Studio, or using gdb/lldb from the command line. There's quite a bit of magic to getting symbols, etc, to work; and the resulting experience just isn't that great, 'cuz Rust doesn't always map smoothly to the gdb paradigm, but it's not significantly different than debugging a regular Rust library or application on, say, Desktop.

That said, I haven't put much effort into this and I'm not actively working on this project so I can't verify that everything works, etc. I would happily take a patch to the documentation detailing anything special required to make this work with Android Studio. I expect that if you build the debug target and use the Android Studio dual-mode debugger, thinks will basically just work. You might need to use the native debugger window in order to set a breakpoint, and determining the Rust name mangling for functions you care about can be tricky. (Use an extern "C" function entry point for simplicity.)

Good luck!

ErikBjare commented 3 years ago

@ncalexan I searched the repo and couldn't find any references to the RUSTFLAGS debuginfo.

In our current build setup we set RUSTFLAGS="-C debuginfo=2" for both debug and release builds (since gradle strips them anyway for production builds) and the release lib with symbols is needed for stack resymbolization with ndk-stack. Are the symbols kept even on release builds?

Looking forward to switching over to using this repo over our current messy build system: https://github.com/ActivityWatch/aw-android/pull/49

ncalexan commented 3 years ago

@ncalexan I searched the repo and couldn't find any references to the RUSTFLAGS debuginfo.

The plugin doesn't do anything special here. I thought that building the debug Cargo profile included debug info out of the box.

In our current build setup we set RUSTFLAGS="-C debuginfo=2" for both debug and release builds (since gradle strips them anyway for production builds) and the release lib with symbols is needed for stack resymbolization with ndk-stack. Are the symbols kept even on release builds?

No, the Android-Gradle plugin strips by default. See packagingOptions { doNotStrip { ... } }.

Looking forward to switching over to using this repo over our current messy build system: ActivityWatch/aw-android#49

Pleased to see it being useful. And thanks for the link to https://github.com/willir/cargo-ndk-android-gradle -- I rather wish we and @willir could have found a good path forward for a unified approach, but multiple options in a young area aren't a bad thing. Can you explain why this plugin is better for your needs than the cargo ndk option?

ErikBjare commented 3 years ago

I thought that building the debug Cargo profile included debug info out of the box.

It does, but when you want to build with the release profile it doesn't, which is what I need it for (to resymbolize stacktraces from crash reports).

No, the Android-Gradle plugin strips by default

I know, but to resymbolize the stacktrace I need the symbols, and the ones from a debug build won't do (due to optimizations, I assume). That's why I'm suggesting passing debuginfo=2 to rustc and leaving all stripping to gradle (where it can be controlled by doNotStrip). Although I realize now that the two might not be equivalent.

I'm asking because when I ran env RUSTFLAGS="-C debuginfo=2" gradlew the env var didn't seem to be respected.

Can you explain why this plugin is better for your needs than the cargo ndk option?

I didn't really know about that repo nor cargo ndk until now. I'd just prefer using a plugin supposedly used & maintained by Mozilla rather than something maintained by a single person :slightly_smiling_face:

Would you mind leaving a comment on https://github.com/mozilla/rust-android-gradle/issues/38 as well? That's the real blocker for us.

willir commented 3 years ago

Hi, @ncalexan

I rather wish we and @willir could have found a good path forward for a unified approach

I would love to merge our projects! Do you have any ideas on how we can approach it? We can try to list all features we would like to have and see from which project to take those.

enfipy commented 3 years ago

Any updates on this?

willir commented 3 years ago

Hi, @ncalexan. Would you be able to find time to merge our project, or at least talk about the best way to proceed forward? What would be the best way to contact you?

ncalexan commented 3 years ago

Hi, @ncalexan. Would you be able to find time to merge our project, or at least talk about the best way to proceed forward? What would be the best way to contact you?

Hi @willir! I think right here on Github will be best: let's migrate to https://github.com/mozilla/rust-android-gradle/issues/40.

ncalexan commented 3 years ago

Any updates on this?

Updates specifically around debugging? Or around merging projects?

tmcguire commented 3 years ago

I've also tried to get Rust debugging with Android Studio to work, so far unsuccessfully.

Here's what I tried:

1: In build.gradle, I've enabled various debugging options:

android {
    buildTypes {
        debug {
            // The doNotStrip option is not properly scoped to the "debug" build type
            // See https://issuetracker.google.com/issues/155215248.
            packagingOptions {
                doNotStrip '**/*.so'
            }
            debuggable true
            jniDebuggable true
        }
    }

    ...
}

cargo {
    profile = 'debug'
    ...
}
  1. I've set Android Studio up for "Dual" debugging via Run->Edit Configurations->Debugger->Debug Type

  2. I made sure the active build variant in Android Studio is "debug"

  3. During a debug session, I can interrupt the application and type LLDB commands in the LLDB console. lldb

  4. The lldb command image list confirms that the native Rust library is loaded correctly

    (lldb) image list
    ...
    [276] D004CD1E-0000-0000-0000-000000000000 0x0000007072045000 /data/app/~~8mbm8UeW73QCDizPPeq8Ag==/com.<redacted>.testapp-YiatM044qSpavP_vA7kOIQ==/base.apk!/lib/arm64-v8a/lib<redacted>.so (0x0000007072045000)
  5. Pulling the APK to the host and examining it confirms that the native library includes debugging information (it is 3MB instead of the stripped 190KB) and that it contains a function called Java_com_<redacted>_rustDummy

    # adb pull /data/app/~~8mbm8UeW73QCDizPPeq8Ag==/com.<redacted>.testapp-YiatM044qSpavP_vA7kOIQ==/base.apk
    # unzip base.apk
    # strings lib/arm64-v8a/lib<redacted>_sdk.so | grep Java_com_<redacted>_rustDummy
    Java_com_<redacted>_rustDummy
    Java_com_<redacted>_rustDummy
    Java_com_<redacted>_rustDummy
    Java_com_<redacted>_rustDummy
  6. Yet setting a breakpoint in that function does not work, neither does looking up that function

    (lldb) image lookup -vn Java_com_<redacted>_rustDummy
    (lldb) b Java_com_<redacted>_rustDummy
    Breakpoint 2: no locations (pending).
    WARNING:  Unable to resolve breakpoint to any actual locations.

Hope this can be helpful to someone.

tmcguire commented 3 years ago

I got a bit further, but Android Studio still does not stop at the breakpoint.

I've added the library I pulled from device (which I could have probably also found somewhere in the build directory) to LLDB, and now LLDB tells me there is a breakpoint:

(lldb) image add /Users/thomas/temp/lib/arm64-v8a/lib<redacted>.so
(lldb) image lookup -vn Java_com_<redacted>_rustDummy
1 match found in /Users/thomas/temp/lib/arm64-v8a/lib<redacted>.so:
        Address: lib<redacted>.so[0x0000000000002ea8] (lib<redacted>.so..text + 72)
        Summary: lib<redacted>.so`Java_com_<redacted>_rustDummy at jni_api.rs:7
         Module: file = "/Users/thomas/temp/lib/arm64-v8a/lib<redacted>.so", arch = "aarch64"
    CompileUnit: id = {0xffffffff00000000}, file = "/Users/thomas/Code/<redacted>/lib.rs", language = "rust"
(lldb) b Java_com_<redacted>_rustDummy
Breakpoint 2: where = lib<redacted>.so`Java_com_<redacted>_rustDummy + 48 at jni_api.rs:11, address = 0x0000000000002ed8

Yet the debugger doesn't actually stop at the breakpoint.

ncalexan commented 3 years ago

You shouldn't need to push the unstripped library to the device and/or to pull it back, because AS can be configured to get debug symbols from unstripped libraries on your development machine. When I last tried this (for a very Mozilla-specific project, GeckoView) I had to do things like I documented here.