Debug a snap application

Once a snap is built and installed one might face unexpected problems like missing a configuration file or a library or simply not the expected behaviour.

Even if snaps are immutable there are still means to introspect and analyse their state as well as running tests inside the snap environment.

Keep in mind that we want to debug the packaging. Debugging your own code etc should be done prior and outside the packaging.

Snap logs

When we call a snap app command, the logs are usually printed in the terminal. On the contrary when we are running a daemon, the logs are not instantly visible. Snap daemon are systemd background services.

To visualize the last 100 lines of a snap daemon:

sudo snap logs SNAP_NAME.DAEMON_NAME -n 100

In case we want to wait for new lines and print them as they come in. We can use the -f option.

Additionally, if our snap contains multiple applications daemons, we can log them all by simply omitting the application name:

sudo snap logs SNAP_NAME -f

Similarly, we can use the pure systemd command:

journalctl -u snap.SNAP_NAME.DAEMON_NAME.service

As we can see, snap daemon are simply systemd services with the snap. prefix and the .service suffix. For the sake of simplicity, the snap logs command is preferred,

If the robot is not behaving as expected this should be the first action.

This way we can quickly check the output of our launchfiles.

Snap environment

When our snap is built and installed but doesn’t work as expected there are solutions. Our snap is running in a confined and containerized environment making the debugging sometimes more difficult.

Snap file structure

If we are curious about the folder/file structure of our system, we can simply check it from our host. As an example, we can list the files at the root of our snap system with:

$ ls /snap/SNAP_NAME/current

etc/ lib/ meta/ opt/ usr/ var/

Everything under this directory /snap/SNAP_NAME/current is only for our snap. When strictly confined, our snap cannot access anything outside this (apart from the different data directories). Checking the files in this directory can sometimes be enough to figure out our issue.

Snap run

When it’s not enough, and we actually need to be in the snap environment to debug, we can run the snap run command along with the --shell option.

snap run --shell SNAP_NAME.APP_NAME

This command is actually going to start a shell of the snap app environment (command-chain included) instead of starting our app. This way, we have the exact same environment to run any kind of command. We can even call the application ourselves if we want to reproduce the issue. We can also potentially launch an entirely different command. Furthermore, we can then check the environment variables, files location, permissions etc.

Note that when calling snap run --shell the started shell will be in the same directory where the command was called. Shelling into the snap environment will keep the original snap permissions.

This method has a very high potential for debugging our snaps.

Debugging a missing library

Robotics applications sometimes rely on hundreds of dynamic libraries. A very common error is that when an application starts, a library is missing. When this is happening a good way to verify that is to use ldd. It will print the shared object libraries and their paths as they are found. So after calling the snap run --shell SNAP_NAME.APP_NAME it’s the perfect moment to call ldd on a library.

$ ldd $SNAP/opt/ros/foxy/lib/librmw.so

linux-vdso.so.1 (0x00007fff841c5000) librcutils.so => /snap/MYSNAP/REVISION/opt/ros/foxy/lib/librcutils.so (0x00007f4c2276a000)

libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f4c2251b000)

libdl.so.2 => /lib/x86_64-linux-gnu/libdl.so.2 (0x00007f4c22515000) /lib64/ld-linux-x86-64.so.2 (0x00007f4c2278d000)

In case one library is marked as not found, the definition of $LD_LIBRARY_PATH is usually at fault.

If a library is missing in the library path but is installed, we can find its location with the help of the find command after entering the snap shell:

find $SNAP -type f -name "librmw.so"

Snap connections

Snaps are strictly confined, but they can access our host by the means of interfaces. These interfaces can sometimes be the source of our problems. Let’s see the different ways to troubleshoot them.

The very first thing that can help is the following command:

$ snap connections lxd

Interface        Plug                Slot             Notes

lxd              multipass:lxd       lxd:lxd          -
Lxd-support      lxd:lxd-support     :lxd-support     -
network          lxd:network         :network         -
network-bind     lxd:network-bind    :network-bind    -
system-observe   lxd:system-observe  :system-observe  -

Here we listed all the connections of our snap lxd. As we can see all the interfaces have a plug and a slot. This means that everything is connected.

Some interfaces are auto-connect while some others are not. This means that we must connect them manually.

To do so we must use the command snap connect. An example of the usage would be:

sudo snap connect SNAP_NAME:camera :camera

The command above presupposes that our snap application had the camera plug declared. Similarly, we can use the snap disconnect command to undo the connect action.

When a snap cannot access a host resource that it was declared to access, checking the connection is usually a good starting point.

One can request “auto-connect“ on the forum of an interface that doesn’t auto-connect for a snap.

Snappy-debug

The snap, being strictly confined, sometimes tries to access resources that were not declared. It generates an App Armor policy violation that might be hard to diagnose.

The easiest way to find and fix policy violations is to use the snappy-debug tool. It’s a tool provided by Canonical and allows us to:

  • watches syslog for policy violations
  • shows them in a human-readable format
  • get recommendations for how to solve them

We can install the snappy-debug tool with the command:

sudo snap install snappy-debug

We can then call the snappy-debug command and in another terminal, call our snap app. The snappy-debug tool could then produce an output similar to the following one:

mars 02 17:27:39 user-computer audit[721546]: AVC apparmor="DENIED" operation="open" profile="snap.SNAP_NAME" name="/dev/video0" pid=721546 comm="APP" requested_mask="c" denied_mask="c" fsuid=1000 ouid=1000

In this log we can see that the access to /dev/video0 was attempted and denied. This gives us the information that either our snap misses the camera plug, or that we simply forgot to connect it.