If you’re more or less successful in your debugging session, it’s quite likely that you’ll have to modify some source code so you can actually fix a bug. And if you’re more or less careful, you might want to validate your changes actually work. We saw some time ago that you don’t need to restart gdb after a recompile because gdb is already smart enough to know that the binary changed.
Turns out you don’t even need to drop from gdb to a shell: just type make (using the parameters you’d usually call make with) and watch gdb take care of building your binary again.
Rebuilding your project like this is not only useful to save time: you can also keep your breakpoints and they should still make sense, assuming you didn’t refactor your code too much.
Raise your hand if you have run gdb in tui (graphical) mode, only to find you can’t refer to the previous command when pressing “up”. I can’t see you but I know this is true for pretty much everyone reading this blog. All three of you.
In the gdb-TUI mode, the arrow keys are used by the active window for scrolling. This means they are not available for readline, the component that takes care of the magic invocations needed to bring back the previous command from the land of the dead. Luckily there are alternative readline keybindings: just try C-p, C-n, C-b and C-f. Takes a while getting used to it but you can finally use gdb-TUI and forget about copy-pasting every gdb command.
Bonus tip: if pressing “up” (or C-p) in gdb doesn’t bring back the previous command, it probably means you don’t have the readline package installed. Go ahead an install it. It’ll change your life.
I don’t know how good Android Studio support for native apps is nowadays (it changes from week to week!). Up to a few months ago, Gradle, the build system used by AS, had poor support for native development. If you’re having problems, you may find it easier to workaround it completely when it comes to build and debug C/C++ applications.
To debug a native Android application, a binary called gdbserver and its associated gdb.setup must be included in the generated APK file. Including this into the APK can be very painful in Gradle, so here’s a workaround I found:
- Build your stuff the way you normally would (I’m assuming you know already how to build a native app, and if you don’t there are guides online that explain it much better than I could).
- Deploy your application the way you normally would.
- Discover ndk-gdb won’t run. Bang forehead against keyboard a few times.
- After losing some hours looking at logs, figure out there’s no gdbserver included in your apk.
- Lose some more hours trying to figure out how to include it in your apk using Gradle.
- Give up. Bang forehead against keyboard some more.
- find the gdbserver and gdb.setup in your build directory.
- adb push each of these files to the device.
- Using adb shell, move the files you copied to /data/app-lib/com.yourapp/ – you may need to root your device for this.
- Profit! ndk-gdb now works.
Edit: remember you may need to chmod +777 your gdbserver.
Crosscompiling is always fun. No matter how ready-to-use it’s packaged, and Android does a pretty decent job at that, you’re still bound to find problems that leak through the abstraction layers. If something says it’s dummy-proof, I always find the way to perfect myself and be even dumber. For people like me; do yourselves a favour and start launching ndk-gdb this way:
ndk-gdb --start --verbose
Using the –verbose parameter will probably reveal some hidden errors. For example, when I forgot to chmod 777 my gdbserver binary:
## COMMAND: adb_cmd pull /system/bin/app_process ./obj/local/armeabi-v7a/app_process run-as: exec failed for /data/data/com.nico.trippingsdcardphotomanager/lib/gdbserver Error:Permission denied 117 KB/s (9560 bytes in 0.079s) Pulled app_process from device/emulator.