Debugging Go Mobile on Android

Introduction

I got started with Go Mobile because my team has been experiencing challenges with mobile development including:

  • Difficulty keeping functional parity between iOS and Android
  • Extra time needed to implement the same logic in each of the two platforms
  • A team of four Engineers was functioning as two teams of two. Two Engineers for iOS and two for Android.

Go Mobile is a promising way to solve that problem. There are other options such as Kotlin Multiplatform Mobile (KMM), but we wanted to keep both iOS and Android Engineers on equal footing and not require iOS Engineers to learn Kotlin.

All seemed to be going well in our experiment until the first time we had to debug a crash in Go. It is much easier if you can attach to the process and inspect values at the time of the crash. We needed to allow VSCode to connect to LLDB on Android to debug Go code.

Setting Up The Emulator

The first step is to get an LLDB server on the target device so that we can remote debug. The Android NDK toolchain provides LLDB servers for various platforms. You will want to choose one that matches your target architecture. In my case it was located at ~/Library/Android/sdk/ndk/26.1.10909125/toolchains/llvm/prebuilt/darwin-x86_64/lib/clang/17.0.2/lib/linux/aarch64/lldb-server. We will copy it to /data/local/tmp on our device so we can access it later:

adb push ~/Library/Android/sdk/ndk/26.1.10909125/toolchains/llvm/prebuilt/darwin-x86_64/lib/clang/17.0.2/lib/linux/aarch64/lldb-server /data/local/tmp

We want to be able to run the lldb-server as the Android package of our app, so we need to copy the executable to where that process has access to it. I will use StructEd as an example:

adb shell run-as com.digitaltorque.structed cp /data/local/tmp/lldb-server /data/data/com.digitaltorque.structed/

If we want to debug something that happens during start-up, we need the app to wait until we have the debugger attached. To do that, we can set a property on Android:

adb shell setprop debug.debuggerd.wait_for_debugger true

Next we forward the port we want use for remote debugging to our computer. I used 23456 here, but it could be a different port:

adb forward tcp:23456 tcp:23456

We can now start our app up (this could be done via the device’s UI as well):

adb shell am start -n com.digitaltorque.structed/com.digitaltorque.structed.MainActivity

We will need our app’s process ID to attach to it, so we can get that now:

adb shell ps -A | grep com.digitaltorque.structed | awk '{ print $2 }'

Finally, we can start the LLDB server to listen on our chosen port:

adb shell run-as com.digitaltorque.structed /data/data/com.digitaltorque.structed/lldb-server --server --listen "*:23456"

Connecting VSCode to LLDB

Open the Extensions marketplace and install CodeLLDB. Next, add a run / debug configuration in VSCode. This can be done via the command pallette (Command + Shift + P on OS X), Then select Debug: Add Configuration.... It doesn’t matter the specifics of what you add, you just need a launch.json file to get started.

Edit your launch.json file and add a configuration like this:

        {
            "name": "Go Mobile Debugging",
            "type": "lldb",
            "request": "custom",
            "initCommands": ["platform select remote-android", "file debug/jni/x86_64/libgojni.so"],
            "processCreateCommands": ["platform connect connect://emulator-5554:23456", "attach 22112"]
        }

In this example, debug/jni/x86_64/libgojni.so is the location of the Go shared library generated with gomobile bind. I have extracted it from the .aar file so that I can reference it here. emulator-5554 is the serial for the target device. This could be a physical device or an emulator. 23456 references the port number lldb-server is listening to on the device, and 22112 represents the PID of the running app.

At this point, I can select that configuration and press the run button to connect to the LLDB server. Should the app crash, I can look through the threads and the stack frames to see what crashed, in what file, and on what line.

Making it easier

That is a lot of steps to run every time a debugging session is needed. To streamline that process, I wrote up a little Go CLI to help set everything up.

The CLI tool’s code is on github: https://github.com/marcuswu/jniDebug

It can be installed with:

go install github.com/marcuswu/jnidebug@latest

I am still experimenting with this method of debugging – especially with setting breakpoints and examining memory at runtime.

Please let me know if this helps you or if you have come across a better way to do this.

Related Posts

3D Printed Curta Part VI

Time for change I changed jobs and moved to the home of the Carolina Panthers last year and have been busy getting settled in the new house.

Read More

3D Printed Curta Part V

Tolerances in OnShape I waited through two updates to OnShape and decided that I needed to stop waiting on them for features.

Read More

Curta Build Manual Release

I uploaded an initial build manual for the 3D printed Curta Calculator!

Read More