Barman V2 Protocol

Index

Introduction

The barman data format is a simple struct based data format for in-memory and on disk storage of the Streamline Barman bare-metal agent capture data.

In most cases the structs that form the basis of the format are tightly packed, with 1-byte alignment. There are only a few cases where there are specific alignment requirements on certain values, stemming from the in-memory usage of those structures.

The format consists of a fixed header followed by a sequence of zero or more data records. The data records are ordered either linearly (that is to say in order from first to last), or as a circular ring buffer (where logical records are arranged physically as 0...n or m...n, 0...(m-1) depending on whether or not the data in the buffer was wrapped).

The header is defined as a fixed length structure (where the length is a function of certain compile time parameters which are also stored in the structure). This means there is only a limited amount of space available for storing information about processes or ELF image memory layouts. Writers of this format will need to know ahead-of-time the maximum number of these structures they want to store in the header, or accept that not all information will be able to be encoded at runtime.

Compile Time Constants

The generated barman agent uses a number of compile time constants to configure various aspects of its behaviour. Some of these constants affect the layout of data within the data file.

The table below lists the various compile time constants that a relevant to the data format:

Constant Description
BM_CONFIG_MAX_CORES The maximum number of unique processors on the target device. Unique processor identifiers are in the range [0, BM_CONFIG_MAX_CORES)
BM_CONFIG_MAX_TASK_INFOS The maximum number of task information structures that can be stored in the header to describe different processes/tasks running on the target.
BM_CONFIG_MAX_MMAP_LAYOUTS The maximum number of memory map layout structures that can be stored in the header. These structures are used to provide the mapping from executable sections of an ELF image to the address of the executable code in memory.
BM_CUSTOM_CHARTS_COUNT The number of custom charts defined
BM_NUM_CUSTOM_COUNTERS The total number of series across all custom charts.
Must be zero if BM_CUSTOM_CHARTS_COUNT is zero, otherwise must be greater than or equal to BM_CUSTOM_CHARTS_COUNT.
BM_PROTOCOL_STRING_TABLE_LENGTH The size (in bytes) of the string table data area.
BM_MAX_PMU_COUNTERS The maximum number of counters that may be supported by a processor PMU. In most cases this value is 32.

Data Types

Throughout this document the following custom integer types are used to represent various fixed size integers.

Type Description
bm_bool Signed 1-byte integer, used for boolean values
bm_int8 Signed 1-byte integer
bm_int16 Signed 2-byte integer
bm_int32 Signed 4-byte integer
bm_int64 Signed 8-byte integer
bm_intptr Signed integer of same size as pointer.
On 32-bit targets this is the same as bm_int32 and on 64-bit targets this is the same as bm_int64.
bm_uint8 Unsigned 1-byte integer
bm_uint16 Unsigned 2-byte integer
bm_uint32 Unsigned 4-byte integer
bm_uint64 Unsigned 8-byte integer
bm_intptr Unsigned integer of same size as pointer.
On 32-bit targets this is the same as bm_uint32 and on 64-bit targets this is the same as bm_uint64.
bm_size_t Same as bm_uintptr
bm_task_id_t Same as bm_uint32
bm_datastore_header_length Same as bm_uint64

The header record is a fixed structure at the start of the capture file. This structure defines contains the magic bytes used to identify the file, as well as storing the configuration values used to generate the barman agent code, and static, one off information needed to interpret the remaining records in the file. It defines information about what counters were captured, details of any processes, clock and custom chart descriptions.

Magic Bytes

The data format begins with an 8 byte sequence of bytes which encodes both the bitness and endianness of the data file. The magic bytes form the string "BARMAN32" or "BARMAN64" depending on whether or not the target is a 32-bit or 64-bit target. The bytes are written as a 64-bit value in native endianness meaning that on a little-endian target the order of characters in the magic string is reversed:

Table of possible magic byte combinations

Target Magic Bytes
Little Endian, 32-bit 0x32 0x33 0x4E 0x41 0x4D 0x52 0x41 0x42
Little Endian, 64-bit 0x34 0x36 0x4E 0x41 0x4D 0x52 0x41 0x42
Big Endian, 32-bit 0x42 0x41 0x52 0x4D 0x41 0x4E 0x33 0x32
Big Endian, 64-bit 0x42 0x41 0x52 0x4D 0x41 0x4E 0x36 0x34

Example code showing how magic bytes may be written

#define BM_PROTOCOL_MAGIC_BYTES_64 0x4241524D414E3634ull #define BM_PROTOCOL_MAGIC_BYTES_32 0x4241524D414E3332ull #define BM_PROTOCOL_MAGIC_BYTES (sizeof(void*) == 8 ? BM_PROTOCOL_MAGIC_BYTES_64 : BM_PROTOCOL_MAGIC_BYTES_32) void write_magic_bytes(char * buffer) { *((u64 *) buffer) = BM_PROTOCOL_MAGIC_BYTES; }

Header Record

The header contains a single bm_protocol_header struct.

Definition of struct bm_protocol_header

struct bm_protocol_header { bm_uint64 magic_bytes; bm_uint32 protocol_version; bm_uint32 header_length; bm_uint32 data_store_type; bm_uint32 target_name_ptr; bm_uint64 last_timestamp; bm_uint32 timer_sample_rate; struct bm_protocol_config_values config_constants; struct bm_protocol_clock_info clock_info; struct bm_protocol_header_string_table string_table; struct bm_protocol_header_pmu_settings per_core_pmu_settings[BM_CONFIG_MAX_CORES]; #if BM_CONFIG_MAX_TASK_INFOS > 0 bm_uint32 num_task_entries; struct bm_protocol_header_task_info task_info[BM_CONFIG_MAX_TASK_INFOS]; #endif #if BM_CONFIG_MAX_MMAP_LAYOUTS > 0 bm_uint32 num_mmap_layout_entries; struct bm_protocol_header_mmap_layout mmap_layout[BM_CONFIG_MAX_MMAP_LAYOUTS]; #endif #if BM_NUM_CUSTOM_COUNTERS > 0 bm_uint32 num_custom_charts; struct bm_protocol_header_custom_chart custom_charts[BM_CUSTOM_CHARTS_COUNT]; struct bm_protocol_header_custom_chart_series custom_charts_series[BM_NUM_CUSTOM_COUNTERS]; #endif struct bm_datastore_header_data data_store_parameters; };
Field Type Offset (bytes) Description
magic_bytes bm_uint64 0 Magic bytes value used to identify the file type, bitness and endianness.
protocol_version bm_uint32 8 Protocol version value. This value should be 2
header_length bm_uint32 12 The length of the header struct; i.e. sizeof(struct bm_protocol_header).
Includes any magic bytes and is used to calculate the offset to the first record. This value may be rounded up to a multiple of 8.
data_store_type bm_uint32 16 Data store type. Valid values for this field are:
Value Description
1 Record data is stored as a linear buffer
2 Record data is stored as a circular buffer
target_name_ptr bm_uint32 20 The offset into the string table that contains the target description string
last_timestamp bm_uint64 24 Timestamp of last attempt to write a sample (even if write failed). This value is in arbitrary ticks as per struct bm_protocol_clock_info.
timer_sample_rate bm_uint32 32 Timer based sampling rate; in Hz. Zero indicates no timer based sampling. This information is supplied by the user and is used to store the sample rate of timer that performs period sampling. It is for information purposes only.
config_constants struct bm_protocol_config_values 36 Config constant values. This structure contains the values of various compile time constants that affect the size of various structures in the data including the header.
clock_info struct bm_protocol_clock_info 60 Clock parameters used to convert from arbitrary tick values to nanoseconds.
string_table struct bm_protocol_header_string_table 92 The string table
per_core_pmu_settings struct bm_protocol_header_pmu_settings[BM_CONFIG_MAX_CORES] Per-core PMU configuration settings. Each index maps to a core as numbered by the function barman_ext_map_multiprocessor_affinity_to_core_no.
Each entry in the table describes the type and multiprocessor ID of that particular core, as well as the event types programmed into the processors PMU.
Entries for processors that are not used/valid should be zero initialized.
num_task_entries bm_uint32 Number of task records that contain data. Must be in the range [0, BM_CONFIG_MAX_TASK_INFOS) .

This field is only present if BM_CONFIG_MAX_TASK_INFOS is greater than 0.

task_info struct bm_protocol_header_task_info[BM_CONFIG_MAX_TASK_INFOS] Task information. This array contains of all the processes/tasks mentioned in the trace.

This field is only present if BM_CONFIG_MAX_TASK_INFOS is greater than 0.

num_mmap_layout_entries bm_uint32 Number of mmap records that contain data. Must be in the range [0, BM_CONFIG_MAX_MMAP_LAYOUTS) .

This field is only present if BM_CONFIG_MAX_MMAP_LAYOUTS is greater than 0.

mmap_layout struct bm_protocol_header_mmap_layout[BM_CONFIG_MAX_MMAP_LAYOUTS] MMAP information. This array contains all of ELF image mappings used to lookup process image names and to map sample addresses to functions in ELF files.

This field is only present if BM_CONFIG_MAX_MMAP_LAYOUTS is greater than 0.

num_custom_charts bm_uint32 Number of custom charts (must be equal to BM_CUSTOM_CHARTS_COUNT).

This field is only present if BM_NUM_CUSTOM_COUNTERS is greater than 0.

custom_charts struct bm_protocol_header_custom_chart[BM_CUSTOM_CHARTS_COUNT] Custom chart descriptions.

This field is only present if BM_NUM_CUSTOM_COUNTERS is greater than 0.

custom_charts_series struct bm_protocol_header_custom_chart_series[BM_NUM_CUSTOM_COUNTERS] Custom chart series'.

This field is only present if BM_NUM_CUSTOM_COUNTERS is greater than 0.

data_store_parameters struct bm_datastore_header_data Aligned to 8-byte boundary Data store parameters. Contains information about layout and number of records in linear/circular buffer.

Header Member Structs

Definition of struct bm_protocol_config_values

This struct stores various compile time constant values that are necessary to decode the data.

struct bm_protocol_config_values { bm_uint32 max_cores; bm_uint32 max_task_infos; bm_uint32 max_mmap_layout; bm_uint32 max_pmu_counters; bm_uint32 max_string_table_length; bm_uint32 num_custom_counters; };
Field Type Offset (bytes) Description
max_cores bm_uint32 0 The value of BM_CONFIG_MAX_CORES.
max_task_infos bm_uint32 4 The value of BM_CONFIG_MAX_TASK_INFOS.
max_mmap_layout bm_uint32 8 The value of BM_CONFIG_MAX_MMAP_LAYOUTS.
max_pmu_counters bm_uint32 12 The value of BM_MAX_PMU_COUNTERS.
max_string_table_length bm_uint32 16 The value of BM_PROTOCOL_STRING_TABLE_LENGTH.
num_custom_counters bm_uint32 20 The value of BM_NUM_CUSTOM_COUNTERS.

This structure has a size of 24 bytes.

Definition of struct bm_protocol_clock_info

This struct stores information about the monotonic clock used in the trace.

struct bm_protocol_clock_info { bm_uint64 timestamp_base; bm_uint64 timestamp_multiplier; bm_uint64 timestamp_divisor; bm_uint64 unix_base_ns; };
Details

Timestamp information is stored in arbitrary units within samples. This reduces the overhead of making the trace by removing the need to transform the timestamp into nanoseconds at the point the sample is recorded. The host expects timestamps to be in nanoseconds.

The arbitrary timestamp information is transformed to nanoseconds according to the following formula:
bm_uint64 nanoseconds = (((timestamp - timestamp_base) * timestamp_multiplier) / timestamp_divisor; Therefore for a clock that already returns time in nanoseconds, timestamp_multiplier and timestamp_divisor should be configured as 1 and 1.
If the clock counts in microseconds then timestamp_multiplier and timestamp_divisor should be set 1000 and 1 respectively.
If the clock counts at a rate of n Hz, then timestamp_multiplier should be set 1000000000 and timestamp_divisor as n.

Field Type Offset (bytes) Description
timestamp_base bm_uint64 0 The base offset for the timestamp value. A timestamp with this value will be equivalent to the starting point of the capture.
timestamp_multiplier bm_uint64 8 The timestamp multiplier used to convert from arbitrary ticks to nanoseconds.
timestamp_divisor bm_uint64 16 The timestamp divisor used to convert from arbitrary ticks to nanoseconds.
unix_base_ns bm_uint64 24 The unix timestamp in nanoseconds. The arbitrary timestamp_base tick maps to this time in wall-clock time. For information purposes only.

This structure has a size of 32 bytes.

Definition of struct bm_protocol_header_string_table

This struct stores the string table. A block of memory reserved for storing string data related to the header.

struct bm_protocol_header_string_table { bm_uint32 string_table_length; char string_table[BM_PROTOCOL_STRING_TABLE_LENGTH]; };
Details

All Strings are null terminated. It is acceptable for strings to overlap within the table if one is the suffix of another. Strings are given as integer values which are pointers into the string table data. The find an actual string within the table from its pointer (for example, to get the string given by bm_protocol_header.target_name_ptr) the following 'C' code could be used:

const char * get_string_from_string_table(const struct bm_protocol_header_string_table * string_table, bm_uint32 string_pointer) { assert(string_pointer < string_table->string_table_length); return string_table->string_table + string_pointer; }
Field Type Offset (bytes) Description
string_table_length bm_uint32 0 The number of bytes in string_table that are used.
string_table char[BM_PROTOCOL_STRING_TABLE_LENGTH] 4 The block of char data used to store strings.

This structure has a size of BM_PROTOCOL_STRING_TABLE_LENGTH + 4 bytes.

Definition of struct bm_protocol_header_pmu_settings

Describes the per-core pmu settings. This record is used to describe the processor type, topology and PMU counter configuration.

struct bm_protocol_header_pmu_settings { bm_uint64 configuration_timestamp; bm_uint32 midr; bm_uintptr mpidr; bm_uint32 cluster_id; bm_uint32 num_counters; bm_uint32 counter_types[BM_MAX_PMU_COUNTERS]; };
Field Type Offset (bytes) Description
configuration_timestamp bm_uint64 0 The timestamp the configuration was written. This value is in arbitrary ticks as per struct bm_protocol_clock_info.
midr bm_uint32 8 The contents of the MIDR register.
mpidr bm_uintptr 12 The multiprocessor affinity register value (MPIDR)
cluster_id bm_uint32 16/20 (32-bit/64-bit) The cluster number of the processor. (As given by the barman_ext_map_multiprocessor_affinity_to_cluster_no function).
num_counters bm_uint32 20/24 (32-bit/64-bit) The number of valid entries in counter_types.
counter_types bm_uint32[BM_MAX_PMU_COUNTERS] 24/28 (32-bit/64-bit) The event types programmed into the PMU for that particular processor.

This structure has a size of (24 + 4 * BM_MAX_PMU_COUNTERS) bytes on a 32-bit target, and (28 + 4 * BM_MAX_PMU_COUNTERS) bytes on a 64-bit target.

Definition of struct bm_protocol_header_task_info

A task information record. Describes information about a unique task or process within the system.

struct bm_protocol_header_task_info { bm_uint64 timestamp; bm_task_id_t task_id; bm_uint32 task_name_ptr; };
Field Type Offset (bytes) Description
timestamp bm_uint64 0 The timestamp the record was inserted. This value is in arbitrary ticks as per struct bm_protocol_clock_info.
task_id bm_task_id_t 8 The task id. A unique integer value used in the various other records/structures to identify this entry.
task_name_ptr bm_uint32 12 The offset of name of the task in the string table.

This structure has a size of 16 bytes.

Definition of struct bm_protocol_header_mmap_layout

A MMAP record; describes the load address of a section of an executable ELF image in memory. This structure should be used whenever more than one ELF image is referenced by a sample, or where the load address of an image does not match the load address specified in the ELF image (it has been relocated).

struct bm_protocol_header_mmap_layout { #if BM_CONFIG_MAX_TASK_INFOS > 0 bm_uint64 timestamp; bm_task_id_t task_id; #endif bm_uintptr base_address; bm_uintptr length; bm_uintptr image_offset; bm_uint32 image_ptr; };
Field Type Offset (bytes) Description
timestamp bm_uint64 0 The timestamp the record was inserted.

This field is only present if BM_CONFIG_MAX_TASK_INFOS is greater than 0.

task_id bm_task_id_t 8 The task ID to associate with the map.

This field is only present if BM_CONFIG_MAX_TASK_INFOS is greater than 0.

base_address bm_uintptr 0/12 (without/with BM_CONFIG_MAX_TASK_INFOS) The base address of the image or image section as it was loaded/relocated in memory.
length bm_uintptr offsetof(base_address) + sizeof(bm_uintptr) The length of the image or image section.
image_offset bm_uintptr offsetof(base_address) + 2 * sizeof(bm_uintptr) The image section offset. The offset relative to the start of the ELF file.
image_ptr bm_uint32 offsetof(base_address) + 3 * sizeof(bm_uintptr) The offset of name of the image in the string table.

This structure has a size of ((BM_CONFIG_MAX_TASK_INFOS > 0 ? 12 : 0) + (3 * sizeof(bm_uintptr)) + 4) bytes.

Definition of struct bm_protocol_header_custom_chart

Describes the details of a custom counter chart. Each custom chart consists of a number of series.

struct bm_protocol_header_custom_chart { bm_uint32 name_ptr; bm_uint8 series_composition; bm_uint8 rendering_type; bm_uint8 boolean_flags; };
Field Type Offset (bytes) Description
name_ptr bm_uint32 0 The offset of name of the chart in the string table.
series_composition bm_uint8 4 The series composition (as per the chart configuration shown in the Streamline UI). Valid values for this field are:
Value Description
1 Stacked
2 Overlayed
3 Log10
rendering_type bm_uint8 5 The rendering type (as per the chart configuration shown in the Streamline UI). Valid values for this field are:
Value Description
1 Filled
2 Line
3 Bar
boolean_flags bm_uint8 6 Bitmap of boolean flags as follows:
Bit (Mask value) Description
0 (0x01) Sets whether or not average values are displayed by the Cross Section Marker in Streamline.
1 (0x02) Sets whether or not Streamline averages the values of multiple cores when viewing the aggregate data of a per-processor chart.
2 (0x04) Sets whether or not the Timeline view displays data as a percentage of the maximum value in the chart.
3 (0x08) Sets whether or not Streamline collects data on a per processor basis.

This structure has a size of 7 bytes.

Definition of struct bm_protocol_header_custom_chart_series

Describes a series within a custom chart.

struct bm_protocol_header_custom_chart_series { bm_uint32 chart_index; bm_uint32 name_ptr; bm_uint32 units_ptr; bm_uint32 description_ptr; bm_uint32 colour; double multiplier; bm_uint8 class; bm_uint8 display; bm_uint8 boolean_flags; };
Field Type Offset (bytes) Description
chart_index bm_uint32 0 The index (into the array custom_charts in the header) of the chart the series belongs to.
name_ptr bm_uint32 4 The offset of name of the series in the string table.
units_ptr bm_uint32 8 The offset of the series units in the string table.
description_ptr bm_uint32 12 The offset of the series description in the string table.
colour bm_uint32 16 The colour value (as 0x00RRGGBB RGB encoded value) for the colour of the chart.
multiplier double 20 Multiplier value. All counter values are stored as bm_uint64 in the capture. This allows fixed conversion to some floating point value.
class bm_uint8 28 Describes the class of data received. Valid values are:
Value Description
1 Delta. Used for values that increment or are accumulated over time such as hardware performance counters but the exact time when the data occurs is unknown and therefore the data is interpolated between timestamps.
2 Incident. Used for values that increment or are accumulated over time such as software counters where the timestamp of the change is known and so no interpolation occurs.
3 Absolute. Used for singular or impulse values such as system memory used.
display bm_uint8 29 How data within the series should be displayed when data is aggregated at lower zoom levels. Valid values are:
Value Description
1 Accumulate. Delta/Incident values are accumulated.

Only valid where class is delta/incident.

2 Average. Absolute values are averaged over the duration of the zoom period.

Only valid where class is absolute.

3 Hertz. Accumulate and average over the last second.

Only valid where class is delta/incident.

4 Maximum. Show the maximum value over all samples within the zoom period.

Only valid where class is absolute.

5 Minimum. Show the minimum value over all samples within the zoom period.

Only valid where class is absolute.

boolean_flags bm_uint8 30 Bitmap of boolean flags as follows:
Bit (Mask value) Description
0 (0x01) When set indicates the chart was sampled as part of the processor PMU sampling process. Its value will be included in the regular sample record.
When not set indicates the chart value is written into the capture by some external process and the counter will stored in a custom counter value record.

This structure has a size of 31 bytes.

Definition of struct bm_datastore_header_data

Describes how records are layout out in the part of the file following the header.

struct bm_datastore_header_data { bm_datastore_header_length buffer_length; bm_datastore_header_length write_offset; bm_datastore_header_length read_offset; bm_datastore_header_length total_written; bm_uint8 * base_pointer; };
Field Type Offset (bytes) Description
buffer_length bm_datastore_header_length 0 The length of the region used for storing records
write_offset bm_datastore_header_length 8 The last write offset; points to the first unwritten byte within the buffer. For circular buffers this is the first byte past the end of the ring. For linear buffers this is the end of the record data.
read_offset bm_datastore_header_length 16 The initial read offset. For circular buffers this is the start of the ring. For linear buffers this should always be zero zero.
total_written bm_datastore_header_length 24 Total number of bytes consumed; always increments. For circular buffers this can be used to determine whether or not the buffer wrapped or not. May be many multiples of buffer_length.
base_pointer bm_uint8 * 32 The base address of the buffer. This field is not used outside of the running agent and can safely be set to zero.

This structure has a size of 36 bytes on a 32-bit target, and 40 bytes on a 64-bit target bytes. This structure has an alignment requirement that it is aligned to 8 bytes. This means that when used within the header there may be between 0-7 bytes of padding before it.

Record Data

Capture records are stored in the record buffer which follows the header data. The record buffer starts at offset ((header.header_length + 7u) & -7u), (the header length, aligned up to an 8-byte boundary), and continues for a maximum of header.data_store_parameters.buffer_length bytes. How the data in the record buffer is to be interpreted depends on the value of header.data_store_type.

Data Store Types

The following sections describe the two different formats for the record buffer; linear and circular. They are written primarily from the point of view of decoding the data. It is strongly recommended that third-parties looking to implement a writer for the this data format, should write data using the linear format.

Linear

With the linear data storage format, records are written sequentially into the record buffer from beginning to end, stopping once the buffer is full. The contents of the record buffer are considered valid only between within the range of offset [header.data_store_parameters.read_offset, header.data_store_parameters.write_offset). In most cases header.data_store_parameters.read_offset will be set

To decode the contents of the record buffer when data is stored in linear format, one would seek to header.data_store_parameters.read_offset bytes past the beginning of the record buffer and from there continue to read records, stopping at offset header.data_store_parameters.write_offset from the beginning of the record buffer.

To decode a single record, first read a bm_uintptr value starting at the current read offset. This value encodes the record length and valid flag as per Record Length Encoding. Immediately following the length value is length bytes of data which will either be treated as record data and can be decoded as described in Record Types if the encoded length value indicates a valid block, or which must be discarded/skipped over if the encoded length value indicates the block is invalid/padding.

Records within the record buffer are tightly packed so the next record's length value will follow immediately after the current records data bytes.

Circular

With the circular data storage format, the record buffer is treated as a circular ring buffer. Initially records are written into the buffer as per the linear format in that records are written sequentially from beginning to end, however once the buffer is filled, rather than stopping, the circular buffer will wrap around and start overwriting blocks from the beginning of the buffer. It is important to note that when over writing a previous block, it will read the length of that block and adjust the read head accordingly so as to free the whole block. It will do this repeatedly until sufficient space is freed for the new block to be inserted. In this way it ensures that valid records remain tightly packed.

To decode the contents of the record buffer when data is stored in circular format the following values are defined:

const bm_uintptr data_length = calculate_circular_data_length(&header.data_store_parameters); const bm_uintptr initial_read_offset = header.data_store_parameters.read_offset % header.data_store_parameters.buffer_length; const bm_uintptr wrap_offset = header.data_store_parameters.buffer_length - initial_read_offset; bm_uintptr linear_read_offset = 0;

The decoder proceeds as if it were parsing the linear buffer, but it uses linear_read_offset as if it were the linear read head within the buffer. Whenever bytes are read from the buffer, the linear read offset is translated into a physical offset within the ring buffer using the following function:

static bm_uintptr calculate_physical_read_offset(bm_uintptr initial_read_offset, bm_uintptr wrap_offset, bm_uintptr linear_read_offset) { if (linear_read_offset < wrap_offset) { return initial_read_offset + linear_read_offset; } else { return linear_read_offset - wrap_offset; } }

Decoding proceeds exactly as per the linear buffer, using the additional layer of translation, except for one minor difference regarding the length of the buffer and wrapping. If the length of the record buffer is not a multiple of sizeof(bm_uintptr) then the trailing bytes at the end of the buffer are treated automatically as if they were padding and are discarded, updating linear_read_offset accordingly.

No block will wrapped across the ring buffer end. When writing the data to the circular buffer, the barman agent will insert a padding block at the wrapping point if required to ensure that the new block can be written fully. No block will ever be split from its length either, which means if it is possible to write the length but not the block, padding will also be inserted to keep the length and data together.

Example

Below is shown an example record buffer containing 5 records and one padding block.

Buffer Contents
 0  1  2  3  4  5  6  7  8  9 10 11 12 13 14 15
0x00 10
0x10 8 10
0x20 6
0x30 8
Light green cells are the block length bytes, Light yellow cells are the record data bytes, Darker shades indicate padding.

In this example header.data_store_parameters contains the following values:

Additionally:

The records are decoded as follows:

  1. 8-byte long record starting at physical offset 0x10 (linear_read_offset = 0x00)
  2. 10-byte long record starting at physical offset 0x1C (linear_read_offset = 0x0C)
  3. 6-byte long record starting at physical offset 0x2A (linear_read_offset = 0x1A)
  4. 8-byte long padding block starting at physical offset 0x34 (linear_read_offset = 0x24)
  5. 10-byte long record starting at physical offset 0x00 (linear_read_offset = 0x30)

Record Length Encoding

The record length field encodes both the length of the record data, and a boolean flag indicating whether or not the block is a valid record or a padding block. If the flag indicates the block is a padding block it should be skipped over rather than being decoded.

The record length value encodes the valid flag in the most significant bit, where a set bit indicates a padding block, and a clear bit indicates a valid record block.

The length and valid flag may be decoded as follows:

#define PADDING_BIT_MASK (1ul << ((sizeof(bm_uintptr) * 8) - 1)) bm_bool is_padding_block(bm_uintptr encoded_length) { return (encoded_length & PADDING_BIT_MASK) == PADDING_BIT_MASK; } bm_uintptr get_block_length(bm_uintptr encoded_length) { return encoded_length & ~PADDING_BIT_MASK; }

Record Types

Each valid record block decoded from the record buffer contains a single record object. The record object begins with a header containing an identifier value which determines the record type.

It is valid for the record block to be longer than the number of bytes required to store the record object, and in most cases it will be as the barman agent will pad a block to a multiple of 8-bytes.

Record header

All records start with the same header structure as follows:

struct bm_protocol_record_header { bm_uint32 record_type; bm_uint32 core; bm_uint64 timestamp; };
Field Type Offset (bytes) Description
record_type bm_uint32 0 Identifies the record type. Valid values for this field are:
Value Record Type
1 Counter Sample
2 Counter Sample (with PC address sample)
3 Task switch Event
4 Custom Counter Value
5 Annotation
6 Halting (WFE/WFI) Event
core bm_uint32 4 The processor number.
timestamp bm_uint64 8 The timestamp of the event. This value is in arbitrary ticks as per struct bm_protocol_clock_info.

This structure has a size of 16 bytes.

Counter Sample / Counter Sample (with PC address sample)

These two record types are used to store PMU counter samples (as well as any sampled custom counter series values). Each instance of one of these record types corresponds to a reading of the counter values for a particular processor at a particular time. Both record types have almost identical layouts, the only difference being whether or not the PC address sample value is present.

struct bm_protocol_sample { struct bm_protocol_record_header header; #if BM_CONFIG_MAX_TASK_INFOS > 0 bm_task_id_t task_id; #endif #if BM_NUM_CUSTOM_COUNTERS > 0 bm_uint32 num_custom_counters; #endif bm_uintptr pc_sample; /* Only for Counter Sample with PC address sample */ bm_uint64 pmu_counter_delta_value[num_pmu_counters]; #if BM_NUM_CUSTOM_COUNTERS > 0 struct bm_custom_counter_sample custom_counter_values[num_custom_counters]; #endif };
Field Type Offset (bytes) Description
header struct bm_protocol_record_header 0 The record header
task_id bm_task_id_t 16 Task id field. The id of the currently running task.

This field is only present if BM_CONFIG_MAX_TASK_INFOS is greater than 0.

num_custom_counters bm_uint32 16/20 The number of custom counter values sent

This field is only present if BM_NUM_CUSTOM_COUNTERS is greater than 0.

pc_sample bm_uintptr 16/20 + 0/4 The sampled program counter address

This field is only present if the record is a Counter Sample (with PC address sample)

pmu_counter_delta_value bm_uint64[num_pmu_counters] 16/20 + 0/4 + 0/sizeof(bm_uintptr) The PMU counter sample delta values (i.e. the different between the counter and the last read of the counter).

The length of this field is determined by reading file_header.per_core_pmu_settings[record.header.core].num_counters for the corresponding processor number.

custom_counter_values struct bm_custom_counter_sample[num_custom_counters] 16/20 + 0/4 + 0/sizeof(bm_uintptr) + (num_pmu_counters * 8) The custom counter value samples. The type of this field is a packed struct defined as follows: struct bm_custom_counter_sample { bm_uint32 id; bm_uint64 value; }; Where id is the index of the custom counter series in file_header.custom_charts_series and value is the sampled value for that counter series.

The length of this field is determined by reading the num_custom_counters field from the record.

This record has a size of (16 + (BM_CONFIG_MAX_TASK_INFOS > 0 ? 4 : 0) + (BM_NUM_CUSTOM_COUNTERS > 0 ? 4 : 0) + (has_pc_sample ? sizeof(bm_uintptr) : 0) + (num_pmu_counters * 8) + (num_custom_counters * 12)) bytes.

Task switch Event

This event is used to record a scheduler task switch event, such as when the scheduler switches to a new process.

struct bm_protocol_task_switch { struct bm_protocol_record_header header; bm_task_id_t task_id; bm_uint8 reason; };
Field Type Offset (bytes) Description
header struct bm_protocol_record_header 0 The record header
task_id bm_task_id_t 16 Task id field. This identifies the task that is being switched to.
reason bm_uint8 20 The reason for the task switch. Valid values for this field are:
Value Description
0 The task switch occurred because the previous task was preempted by the current task
1 The task switch occurred because the previous task had blocked on some event (e.g. waiting on IO)

This record has a size of 21 bytes.

This record is only valid in a capture if BM_CONFIG_MAX_TASK_INFOS is greater than 0.

Custom Counter Value

This event contains the value of a custom counter.

struct bm_protocol_custom_counter_record { struct bm_protocol_record_header header; #if BM_CONFIG_MAX_TASK_INFOS > 0 bm_task_id_t task_id; #endif bm_uint32 counter; bm_uint64 value; };
Field Type Offset (bytes) Description
header struct bm_protocol_record_header 0 The record header
task_id bm_task_id_t 16 Task id field. The id of the currently running task.

This field is only present if BM_CONFIG_MAX_TASK_INFOS is greater than 0.

counter bm_uint32 16/20 The custom counter id
value bm_uint64 20/24 The custom counter value

This record has a size of (BM_CONFIG_MAX_TASK_INFOS > 0 ? 32 : 28) bytes.

This record is only valid in a capture if BM_NUM_CUSTOM_COUNTERS is greater than 0.

Annotation

This event encodes a custom annotation value

struct bm_protocol_annotation_record { struct bm_protocol_record_header header; #if BM_CONFIG_MAX_TASK_INFOS > 0 bm_task_id_t task_id; #endif bm_uintptr data_length; bm_uint32 channel; bm_uint32 group; bm_uint32 colour; bm_uint8 type; bm_uint8 data[data_length] };
Field Type Offset (bytes) Description
header struct bm_protocol_record_header 0 The record header
task_id bm_task_id_t 16 Task id field. The id of the currently running task.

This field is only present if BM_CONFIG_MAX_TASK_INFOS is greater than 0.

data_length bm_uintptr 16/20 Length of the byte data that follows the record
channel bm_uint32 16/20 + sizeof(bm_uintptr) Annotation channel
group bm_uint32 16/20 + sizeof(bm_uintptr) + 4 Annotation group
colour bm_uint32 16/20 + sizeof(bm_uintptr) + 8 Annotation colour (as 0x00RRGGBB RGB encoded value)
type bm_uint8 16/20 + sizeof(bm_uintptr) + 12 Annotation type. Valid values for this field are:
Value Description
0 A string annotation as would be generated by barman_annotate_channel.
1 A bookmark annotation as would be generated by barman_annotate_marker.
2 A channel creation annotation as would be generated by barman_annotate_name_channel.
3 A group creation annotation as would be generated by barman_annotate_name_group.
data bm_uint8[data_length] 16/20 + sizeof(bm_uintptr) + 13 String data associated with the annotation.

The length of this field is taken from the data_length field.

This record has a size of ((BM_CONFIG_MAX_TASK_INFOS > 0 ? 33 : 29) + sizeof(bm_uintptr) + data_length) bytes.

Halting (WFE/WFI) Event

This event records entry to or exit from some sort of halting event such as WFE or WFI instruction.

Records of this type are expected to come in entered-exited pairs for a given processor, with no other intervening record types as would be the case if the records would emitted just before and then just after a WFI instruction.

struct bm_protocol_halting_event_record { struct bm_protocol_record_header header; bm_uint8 entered_halt; };
Field Type Offset (bytes) Description
header struct bm_protocol_record_header 0 The record header
entered_halt bm_uint8 16 Non-zero if entered halting state, Zero if exited.

This record has a size of 17 bytes.