Skip to content

Instantly share code, notes, and snippets.

@johanwiden
Last active August 28, 2023 15:26
Show Gist options
  • Select an option

  • Save johanwiden/ca829362d5802e4dca3ebe1b5712fd5d to your computer and use it in GitHub Desktop.

Select an option

Save johanwiden/ca829362d5802e4dca3ebe1b5712fd5d to your computer and use it in GitHub Desktop.
How to debug Android emacs using lldb

I describe:

  • How to build Android emacs with debug symbols.
  • How to install it on the Android device.
  • How to start lldb-server on the Android device, how to configure lldb, and connect it to lldb-server.
  • Some simple lldb commands, to get you up to speed.
  • A potential pitfall, when looking at global variables in emacs.
  • Some things I have not needed to enable in lldb.

Some useful references for lldb are listed.

Although the article is kind of long, I consider the described debugging method to be quick and easy. It is just important to get the details right.

What I describe here requires that Termux is installed on Android, and that Emacs and Termux have been configured to have permission to access each other.

lldb is the offically supported debugger for Android, and it has been so for a long time. I consider it to be a very good and capable debugger. lldb has two parts:

  • lldb, which runs on the host computer, and is the user interface.
  • lldb-server, which runs on Android, and controls the process to be debugged.

lldb-server can run in several modes:

  • “lldb-server platform” for cooperating with lldb
    • In this mode lldb-server can either serve just one lldb connection, or serve several connections (–server), potentially in parallel.
  • “lldb-server gdbserver” for cooperating with gdb

How to build Android emacs with debug symbols

Clone the emacs repo, if you have not done this already:

Change directory to the emacs repo.

  • make clean
  • ANDROID_CFLAGS=”-g3 -O0” ./configure <options>…

Make any desired changes to source files:

  • make all

If you later on make more changes to source files, I recommend that you rebuild using the following commands:

  • make clean
  • make all

Use make clean unless you are confident that it is not needed. To verify that it is not needed:

  • Insert some statements in a source file, say alloc.c.
  • Rebuild without “make clean”, and install the rebuilt apk file.
  • In the debugger, connected to the rebuilt Android emacs:
    • List a function in alloc.c, that comes after the inserted statements:
      • “l Fcons”
      • The displayed source lines should have the Fcons definition line in the middle of the listing.
    • Compare with listing a function in a module that you have not updated: “l some_other_function”.

If both cases list the specified function in the middle of the listing, there is probably no need to use “make clean”.

How to install the Android emacs APK on your device.

You need to have developer options enabled on the Android device, and permission to connect via USB. Connect the host to the Android device with an USB cable. To check that the connection works do:

  • adb devices
  • adb shell

The first time you try to access the Android device via USB, you will have to grant the host access permission, on the Android device.

Now install the Android emacs APK on the Android device:

  • adb install -r java/*.apk

Start emacs on the Android device using the Android launcher. It is possible to start it from the host too, using adb:

  • adb shell am start -D -n “org.gnu.emacs/org.gnu.emacs.EmacsActivity”

Installing and starting lldb-server in Termux

lldb-server is part of Termux package lldb:

  • pkg install lldb

Now start lldb-server:

  • lldb-server platform –listen “0.0.0.0:7564” –server

This starts lldb-server in a mode compatible with lldb, and the server can handle several lldb connections, potentially in parallel. To stop the server just hit C-c.

Preliminaries on the host computer

On the host computer one must now set up port forwarding:

  • adb forward “tcp:7564” “tcp:7564”

This only has to be done once in a host login session.

lldb configuration

lldb has a configuration file, ~/lldbinit. Here is how my ~/.lldbinit looks:

  • settings set target.load-cwd-lldbinit true
  • settings set target.inline-breakpoint-strategy always
  • process handle -p true -n false -s false SIGUSR1
  • process handle -p true -n false -s false SIGIO

The settings for SIGUSR1 and SIGIO are necessary, otherwise lldb will stop emacs as soon as you do something to the app window, and also when I/O happens.

There is an .lldbinit in the emacs repo src subdirectory, and I consider it to be useful. Thats why I have set:

  • settings set target.load-cwd-lldbinit true

Without this setting lldb will refuse to read that .lldbinit file.

The line “settings set target.inline-breakpoint-strategy always” is for handling the case that a .c, or .cpp, file includes another .c or .cpp file. I do not know if it is needed for the case of emacs, but it does not seem to cause any problems to have it set.

Where is lldb

There is probably an lldb available in your host linux package manager, that is what I am using. There is also an lldb available in NDK: toolchains/llvm/prebuilt/linux-x86_64/bin/lldb. However, that one must be started via toolchains/llvm/prebuilt/linux-x86_64/bin/lldb.sh, otherwise it will complain about several missing dependencies. That lldb seems to have been built with support for reading object file sections compressed with LZMA, while at least my host lldb seems to lack such support. However such support appears to not be essential.

Connect lldb to the lldb-server

Run “adb shell ps | grep emacs” to show the process id for Android emacs. I will call it $emacspid below.

Change dir to the emacs repo src subdirectory:

  • lldb
    • platform select remote-android
    • platform connect connect://R52TC05Y0BF:7564
      • The R52… is what “adb devices” shows.
    • process attach -p $emacspid
    • image list libemacs.so
      • This is just a sanity check. Expected output is something as follows:
        • [ 0] 3BEBBEFC 0x0000006d76e1d000 home/jw.lldb/module_cache/remote-android/.cache/3BEBBEFC/libemacs.so

Emacs is paused. One can now issue various lldb commands and for example set a break point. To resume emacs give command:

  • continue

Some simple lldb commands

Documentation commands:

  • help
  • apropos

There is lots of online help in lldb. For example try “help platform”. Show the value of variable “pure_size” (works even for static variables):

  • target variable pure_size
    • Can be shortened to “ta v pure_size”

Show global variables in current file:

  • target variable

Show variables in current stack frame:

  • frame variable

Show the value of variable “bar” in current stack frame:

  • frame variable bar
    • Can be shortened to “fr v bar”

One can search for symbol, function and file names, using regular expressions:

  • ta v -r .*gc_threshold.*

List beginning of function Fcons:

  • l Fcons

Set a breakpoint on line 12 in file test.c:

  • b test.c:12

A potential pitfall

In alloc.c I declared some debug variables as follows:

  • DEFVAR_INT (“jw-debug-so-and-so”, jw_debug_so_and_so,

    doc: * Documentation of what its for. *);

This enabled me to show the variable in emacs, with command “C-h v”. Very nice. But when I tried “ta v jw_debug_so_and_so”, lldb said that it could not find the variable. It turned out that it was part of the array “globals”. I could list the contents of this big array with the command:

  • ta v -A globals

Some things I have not needed to enable in lldb

lldb has been around for a long time. If one googles for various use cases, one can encounter statements about necessary workarounds. However several of these workarounds appear to no longer be needed, at least in the case described above. Here are some examples:

  • I have not needed to extract debug symbols from shared libraries into separate files.
  • I have not needed to deal with sysroot.
  • I have not needed to copy the Android system libraries to the host, and specify the top of that directory tree as sysroot.
  • I have not needed to specify target.exec-search-paths, such as:
    • settings append target.exec-search-paths “/home/jw/Downloads/emacs-30/emacs/cross/src”

Some useful references for lldb

References:

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment