Self-service configuration

Self-service configuration is a mechanism for configuring the behaviour of the Undo Engine using a JSON configuration file.

The Undo Engine looks for the configuration file in the following locations, and uses the first one that it finds:

  1. the file named by the UNDO_selfservice_config_filename environment variable;

  2. ~/.undo-engine.json (that is, in the user’s home directory);

  3. the file undo-engine.json in the same directory as the live-record_<arch> or udbserver_<arch> executable that is being configured; <arch> being one of x32, x64 or arm64;

  4. /var/local/undo/undo-engine.json;

  5. /usr/local/share/undo-engine.json;

  6. /etc/undo-engine.json.

Configuring ioctls

An “ioctl” is a class of system call that is specific to a type of device, for example, the TCGETS ioctl requests the settings for a terminal device. Each type of device has a driver that handles its own ioctls, and custom hardware devices are often controlled using custom ioctls.

If the Undo Engine knows which regions of memory might be read and written by the ioctl, then the side-effects can be recorded efficiently. But if it does not know, then it has to fall back to comparing the entire memory of the process before and after the ioctl, which can lead to poor performance when recording.

The Undo Engine knows the behaviour of commonly used ioctls that are built into the Linux kernel. But if you are recording a program that uses custom ioctls to access custom hardware, then for performance it may be necessary to describe the behaviour of these ioctls as documented below.

When recording a program that uses ioctls for which the Undo Engine does not know the behaviour, the output includes warnings like this:

WARNING: ioctl 0x80012345 (<unknown>) (/dev/customhw6) is unoptimized.

In most cases, the side effects of an ioctl are limited to reading and writing regions of memory at addresses which are given by the arguments to the ioctl, possibly using nested data structures or arrays. In these cases, the behaviour of the ioctl can be described in the self-service configuration file, allowing the Undo Engine to record its side effects efficiently.

Some ioctls have other side effects, including:

  • Changes to memory outside the described buffers;

  • Changes to process-visible system registers;

  • Changes to mapped memory or its attributes.

These ioctls cannot be described using the self-service configuration file. If you are experiencing poor recording performance due to these kinds of ioctl, please contact Undo Support.

Here is an example of a simple self-service configuration file:

{
  "ioctls":
  [
    {
      "comment": "Some device control.",
      "number": "0x4008fa1b",
    },
  ]
}

This configures the Undo Engine to optimise calls to ioctl number 0x4008fa1b.

Note that the description of the behaviour consists only of the ioctl number. This tells the Undo Engine that it should deduce the behaviour of the ioctl by decoding the top 16 bits of the ioctl number according to the standard for Linux ioctl-based interfaces. In cases where the top 16 bits do not completely or correctly describe the behaviour of the ioctl, the behaviour must be described in more detail, as documented below.

Here is an example of a more complex self-service configuration file:

{
  "ioctls":
  [
    {
      "number": "0xf00f",
      "match_filepath" : "/dev/magic",
      "read": true,
      "write": true,
      "length": "0x0100",
      "blocking": true
    },
    {
      "number": "0xc100f010",
      "blocking": false,
      "pointers":
      [
        {
          "offset_to_ptr": "0x40",
          "const_length": "0x100",
          "read": true,
          "pointers":
          [
            {
              "offset_to_ptr": "0x20",
              "const_length": "0x100",
              "write": true
            }
          ]
        },
        {
          "offset_to_ptr": "0x20",
          "const_length": "0x100",
          "read": true
        }
      ]
    }
  ]
}

At the top level must be a root object, containing a key ioctls whose value is a list of objects, each of which describes the behaviour of one ioctl, and may contain the following keys:

number (number or string, required)

The ioctl number, or a string which is converted to the ioctl number. Note that JSON does not support hexadecimal format, so hexadecimal numbers must be represented as strings starting "0x".

match_filepath (string, optional)

The ioctl definition is only used on devices whose file path exactly matches this value.

blocking (boolean, optional)

Whether the ioctl may block (assumed true if not present). Setting this to false can yield slightly higher performance if it is known that other threads will never be required to run before the ioctl can finish. It must be set true if it is possible for the ioctl to block waiting for other events (for example, if it operates similarly to a read system call).

read and/or write (boolean, optional)

If the argument to the ioctl is a pointer, these describe whether the kernel will read the memory, write the memory, or both. If neither is specified, their values will be inferred from bits 30 and 31 respectively of the ioctl number.

length (number or string, optional)

If the argument to the ioctl is a pointer, this describes the length in bytes of the memory region accessed by the kernel. If not specified, this value is inferred from bits 29 to 16 of the ioctl number. Hexadecimal numbers must be represented as strings starting "0x".

pointers (list of objects, optional)

If the argument to the ioctl is a pointer, and it points to a buffer containing one or more pointers to other memory regions that are also accessed by the kernel, these must be specified here. This is a list of objects, each representing one pointer in the buffer. Each object may contain the following keys:

offset_to_ptr (number or string, required)

Offset in bytes from the start of the parent buffer, to the word-sized location where the pointer appears.

const_length (number or string, required)

Length in bytes of the region which may be accessed through the pointer.

read and/or write (boolean, required)

Describe whether the kernel will read and/or write memory at the pointer. At least one must be true.

pointers (list of objects, optional)

A nested list of pointer objects, in case this buffer also contains one or more pointers.

Additionally, the key comment is allowed to appear in any object. This is for the user’s reference only and the Undo Engine ignores its value.