Modify a pre existent event or block entry - draios/sysdig GitHub Wiki

Background

The event table (event_table.h) contains information on how to parse/display/handle syscall enter/exit events. This table is indexed by unique IDs (from the ppm_event_type enum) and contains ppm_event_info structs, in which are stored metadata like the name of the syscall, the number of params of the entry/exit event as well as their name, type and preferred display method.

Those entries, to provide backward-compatibility while reading captures created with an older version of sysdig, have always been immutable. When there was the need to add/modify an event's parameter, the current entry was flagged as an "old version", a new event type (and related entry in the event table) had to be created and all the code referencing that event type had to be patched to include the new version, alongside the addition of the code to handle the new parameter.

Alongside syscall events, a sysdig capture also contains information about the users, network interfaces, processes, file descriptors, etcetera. These lists are stored as separate pcap blocks in the capture, and identified with a block type. Similarly to an event change, a change on a list entry consisted in a bump of the block type and the patching of its usages.

But what about reading a capture created with a newer version of sysdig? This is something that was not supported, and created incompatibility issues in environments where sysdig cannot be easily upgraded. Since new versions of events had a different event type, the old code was completely unaware on how those "new" events were shaped and therefore couldn't properly parse newer captures.

To resolve these issues, starting from sysdig version 0.23.0 (scap version 1.2), event types and block types are no longer changed when an event/list entry is modified, and the capture format has been changed to increase the flexibility of the reading process:

  • for events, the number of params of an event it's no longer only stated in its event table entry, but also on the capture event block. If new parameters are added, older readers can skip them and the event can be parsed regardless of the information in the current event table entry.
  • for the other block types (proclist, fdlist, userlist, etc.), the length of each list entry has been added to the capture, so that new fields can be skipped and the capture offset can be realigned to the next entry. Appending a new parameter/field can now be performed more easily and without breaking forward-compatibility. Other types of change, like removal, will not be allowed.

New events (with a new event type, e.g. a complete new syscalls not currently supported in any shape or form) will be ignored, since older readers don't have the related event table entry.

Modifying an event

NB: those steps are required only if you need to change an event table entry. For example, adding a filtercheck like proc.apid is not the subject of this document, and can be achieved without these steps.

The only allowed change is appending parameters to the enter/exit events (and please note, no userspace pointers in the entry event because they may generate a page fault in the driver module).

Step1: Modify the event table

Find the related ppm_event_info entry in event_table.c and append the new parameter info to the event's parameter list. Don't forget to bump the nparams field as well.

Step2: Modify the filler

If needed, add the parsing of the new parameter in the related driver (ppm_fillers.c) and bpf (fillers.h) filler.

Step3: Modify the parser

If the new parameter requires special parsing or contains information that influences sysdig state, you might need to parse it in libsinsp, inside libsinsp/parsers.cpp.

Remember that sysdig has to be both retro and forward compatible, so must support both older and newer captures. sinsp_evt::get_num_params() returns the actual number of parameters of an event (if sysdig is reading a capture, this function will return the number of parameters present in the capture event block, not the number available in the event table entry). It can be used to check if the parameter is available in that event.

Step3: Add filter fields

If you want to add new filter fields (e.g. proc.vpid) related to the new parameter, the place to edit is libsinsp/filterchecks.cpp. As for the previous step, use sinsp_evt::get_num_params() to make sure that the parameter is available.

Step4: Done!

Modifying a capture block entry

As for events, the only allowed change is appending fields to the block entry related structure.

Step1: Modify the writer function

Modify the related scap_write_* function in libscap/scap_savefile.c. As you will see in the current code, you'll have to account for the new field when calculating the entry size, then append the new field using scap_dump_write().

Step2: Modify the reader function

Modify the related scap_read_* function in libscap/scap_savefile.c to read the new field.

As with events, this step is a little bit more tricky because the change need to be {retro,forward}-compatible. Checking against the list entry size it's the way to verify if the new field is available: when reading an entry, the number of bytes read is accounted for while the fields are read, so the new field will be available based on how much space is left before reaching the list entry size.

Step3: Check for other usages of the reader/writer functions

For example, if you're modifying the process list entry block, remember to modify dump_threads_to_file() and thread_to_scap() in libsinsp/threadinfo.cpp as well.

Step4: Done!