This task describes how to interface with, and instruct, barman to store (or stream) captured data to storage of your choice.
To configure barman to use an in-memory buffer, follow the steps in Configuring Barman .
This step generates the barman.c and barman.h files. The following steps override the backend selected in the configuration tool.
When initializing the barman library, use the barman_initialize_with_user_supplied function instead of barman_initialize.
The first argument passed to barman_initialize_with_user_supplied gets passed directly to the barman_ext_streaming_backend_init function shown in the following example.
Prepare your code by implementing the functions described in the barman-ext-streaming-backend.h header file that is available on GitHub:
For example, you can implement the functions as:
/**
* Initialize the backend.
*
* @param config Pointer to some configuration data.
* @return True if successful.
*/
bm_bool barman_ext_streaming_backend_init(void * config);
/**
* Write data as a frame.
*
* @param data Data to write in the frame.
* @param length Length of the frame.
* @param channel Channel to write the frame on.
* @param flush_header Set to BM_TRUE when the frame contains an update.
* to the header. Indicates to flush the channel after writing the frame.
*/
void barman_ext_streaming_backend_write_frame(const bm_uint8 * data, bm_uintptr length, bm_uint16 channel, bm_bool flush_header);
/**
* Shutdown the backend.
*/
void barman_ext_streaming_backend_close(void);
/**
* Get the channel bank.
*
* If banked by a core this is just the core number.
* If not banked by a core, this should always be 0.
*
* @return The bank
*/
bm_uint32 barman_ext_streaming_backend_get_bank(void);
The function barman_ext_streaming_backend_init must perform whatever necessary setup (for example, opening the data file), and returns BM_TRUE on success, or BM_FALSE on failure.
The function barman_ext_streaming_backend_close must be called when the capture is disabled, and used to close any relevant storage (for example, by closing the data file).
The function barman_ext_streaming_backend_get_bank must return barman_get_core_no() in a multicore system, or return 0:
bm_uint32 barman_ext_streaming_backend_get_bank(void)
{
return barman_get_core_no();
}
The function barman_ext_streaming_backend_write_frame must be called for each data record, and store the records to the data store according to the following pseudocode:
{
/* Note: When the host has multiple cores or threads, so that it is
* possible for multiple cores to call this function in parallel,
* then you must ensure to synchronize here.
*/
if (flush_header) {
assert(data_store_is_empty || (previous_header_length == length));
/** Write (or overwrite) the existing header at the start of the
* data store.
*/
write_blob_to_data_store_at_offset_zero(data, length);
} else {
assert(received_header);
/** You must prefix the data record with a length field. Note:
* This following example code produces a little-endian length,
* but for big-endian targets this must be changed.
*/
bm_uint8 length_header[8] = {
length >> (0 * 8),
length >> (1 * 8),
length >> (2 * 8),
length >> (3 * 8),
length >> (4 * 8),
length >> (5 * 8),
length >> (6 * 8),
length >> (7 * 8),
};
/** Append the size, which depends on if the target is 32-bit
* (4-bytes) or 64-bit (8-bytes), to the end of the data store.
*/
write_blob_to_end_of_data_store(length_header, sizeof(bm_uintptr));
/** Now append the data to the end of the data store.
*/
write_blob_to_end_of_data_store(data, length);
}
}
For example, on a POSIX-like API, you could implement write_blob_to_data_store_at_offset_zero and write_blob_to_end_of_data_store as:
static int file_handle;
static bm_uintptr header_length = 0;
static void write_blob(const bm_uint8 * data, bm_uintptr length)
{
while (length > 0)
{
int result = write(file_handle, data, length);
assert(result > 0);
length -= result;
data += result;
}
}
static void write_blob_to_end_of_data_store(const bm_uint8 * data, bm_uintptr length)
{
off_t new_offset = lseek(file_handle, 0, SEEK_END);
assert((new_offset > 0) && (new_offset >= header_length));
write_blob(data, length);
}
static void write_blob_to_data_store_at_offset_zero(const bm_uint8 * data, bm_uintptr length)
{
assert((header_length == 0) || (header_length == length));
header_length = length;
off_t new_offset = lseek(file_handle, 0, SEEK_SET);
assert(new_offset == 0);
write_blob(data, length);
new_offset = lseek(file_handle, 0, SEEK_END);
assert(new_offset >= length);
}
Compile your application with the following options:
-DBM_CONFIG_USE_DATASTORE=BM_CONFIG_USE_DATASTORE_STREAMING_USER_SUPPLIED - Sets a user-supplied datastore.
-DBM_CONFIG_STREAMING_DATASTORE_USER_SUPPLIED_NUMBER_OF_BANKS=<n> - Passes the number of banks in the user-supplied datastore. <n> is the maximum number of CPUs on the system. In other words, one greater than the largest value returned by barman_get_core_no().
-DBM_CONFIG_STREAMING_DATASTORE_USER_SUPPLIED_NUMBER_OF_CHANNELS=<n> - Passes the number of channels in the user-supplied datastore. <n> is a number greater than, or equal to, 1, and is the number of buffers per CPU to use for temporary storage.
./<compiler-command> <options> -DBM_CONFIG_USE_DATASTORE=BM_CONFIG_USE_DATASTORE_STREAMING_USER_SUPPLIED -DBM_CONFIG_STREAMING_DATASTORE_USER_SUPPLIED_NUMBER_OF_BANKS=<n> -DBM_CONFIG_STREAMING_DATASTORE_USER_SUPPLIED_NUMBER_OF_CHANNELS=<n> <source>