lib/trace: chain entries to extend their buffer size

This patch adds the ability to chain multiple trace entries together to
extend the size of the argument buffer.  This means that a tracepoint is
no longer limited to the size of a single entry, so it can have any
number of arguments, and their size is also not constrained to a single
entry.

Some limitations are still there: a tracepoint can have up to 5
arguments and strings are limited to 255 bytes.  These constraints stem
from the definitions of tracepoint structures, which could be easily
modified to extend the limits if needed.

To record a tracepoint requiring larger buffer, aside from reserving
`spdk_trace_entry` structure, a series of `spdk_trace_entry_buffer`
structures are allocated too.  Each of them acts as a buffer for the
arguments.  To allow trace tools to treat the buffer structures
similarly to regular entries, they also have the `tpoint_id` and `tsc`
fields.  The id is always assigned to `SPDK_TRACE_MAX_TPOINT_ID` to make
sure that a buffer is never mistaken for an entry, while the value of
`tsc` is always shared with the initial entry.  This also provides a way
for the trace tools to verify if an entry is part of a chained buffer.

Signed-off-by: Konrad Sztyber <konrad.sztyber@intel.com>
Change-Id: I51ceea6b6e57df95d4b8bd797f04edbc4936c180
Reviewed-on: https://review.spdk.io/gerrit/c/spdk/spdk/+/8405
Community-CI: Mellanox Build Bot
Tested-by: SPDK CI Jenkins <sys_sgci@intel.com>
Reviewed-by: Jim Harris <james.r.harris@intel.com>
Reviewed-by: Ziye Yang <ziye.yang@intel.com>
Reviewed-by: Tomasz Zawadzki <tomasz.zawadzki@intel.com>
This commit is contained in:
Konrad Sztyber 2021-06-16 14:14:50 +02:00 committed by Tomasz Zawadzki
parent 0cf270910a
commit 6cc3169677
3 changed files with 63 additions and 14 deletions

View File

@ -40,6 +40,7 @@
#define _SPDK_TRACE_H_
#include "spdk/stdinc.h"
#include "spdk/assert.h"
#ifdef __cplusplus
extern "C" {
@ -56,6 +57,15 @@ struct spdk_trace_entry {
uint8_t args[40];
};
struct spdk_trace_entry_buffer {
uint64_t tsc;
uint16_t tpoint_id;
uint8_t data[54];
};
SPDK_STATIC_ASSERT(sizeof(struct spdk_trace_entry_buffer) == sizeof(struct spdk_trace_entry),
"Invalid size of trace entry buffer");
/* If type changes from a uint8_t, change this value. */
#define SPDK_TRACE_MAX_OWNER (UCHAR_MAX + 1)

View File

@ -57,11 +57,12 @@ _spdk_trace_record(uint64_t tsc, uint16_t tpoint_id, uint16_t poller_id, uint32_
{
struct spdk_trace_history *lcore_history;
struct spdk_trace_entry *next_entry;
struct spdk_trace_entry_buffer *buffer;
struct spdk_trace_tpoint *tpoint;
struct spdk_trace_argument *argument;
const char *strval;
unsigned lcore, i, offset;
unsigned lcore, i, offset, num_entries, arglen, argoff, curlen;
uint64_t intval;
void *argval;
va_list vl;
lcore = spdk_env_get_current_core();
@ -91,31 +92,74 @@ _spdk_trace_record(uint64_t tsc, uint16_t tpoint_id, uint16_t poller_id, uint32_
next_entry->size = size;
next_entry->object_id = object_id;
num_entries = 1;
buffer = (struct spdk_trace_entry_buffer *)next_entry;
/* The initial offset needs to be adjusted by the fields present in the first entry
* (poller_id, size, etc.).
*/
offset = offsetof(struct spdk_trace_entry, args) -
offsetof(struct spdk_trace_entry_buffer, data);
va_start(vl, num_args);
for (i = 0, offset = 0; i < tpoint->num_args; ++i) {
for (i = 0; i < tpoint->num_args; ++i) {
argument = &tpoint->args[i];
switch (argument->type) {
case SPDK_TRACE_ARG_TYPE_STR:
strval = va_arg(vl, const char *);
snprintf(&next_entry->args[offset], argument->size, "%s", strval);
argval = va_arg(vl, void *);
arglen = strnlen((const char *)argval, argument->size - 1) + 1;
break;
case SPDK_TRACE_ARG_TYPE_INT:
case SPDK_TRACE_ARG_TYPE_PTR:
intval = va_arg(vl, uint64_t);
memcpy(&next_entry->args[offset], &intval, sizeof(intval));
argval = &intval;
arglen = sizeof(uint64_t);
break;
default:
assert(0 && "Invalid trace argument type");
break;
}
offset += argument->size;
/* Copy argument's data. For some argument types (strings) user is allowed to pass a
* value that is either larger or smaller than what's defined in the tracepoint's
* description. If the value is larger, we'll truncate it, while if it's smaller,
* we'll only fill portion of the buffer, without touching the rest. For instance,
* if the definition marks an argument as 40B and user passes 12B string, we'll only
* copy 13B (accounting for the NULL terminator).
*/
argoff = 0;
while (argoff < argument->size) {
/* Current buffer is full, we need to acquire another one */
if (offset == sizeof(buffer->data)) {
buffer = (struct spdk_trace_entry_buffer *) get_trace_entry(
lcore_history,
lcore_history->next_entry + num_entries);
buffer->tpoint_id = SPDK_TRACE_MAX_TPOINT_ID;
buffer->tsc = tsc;
num_entries++;
offset = 0;
}
curlen = spdk_min(sizeof(buffer->data) - offset, argument->size - argoff);
if (argoff < arglen) {
memcpy(&buffer->data[offset], (uint8_t *)argval + argoff,
spdk_min(curlen, arglen - argoff));
}
offset += curlen;
argoff += curlen;
}
/* Make sure that truncated strings are NULL-terminated */
if (argument->type == SPDK_TRACE_ARG_TYPE_STR) {
assert(offset > 0);
buffer->data[offset - 1] = '\0';
}
}
va_end(vl);
/* Ensure all elements of the trace entry are visible to outside trace tools */
spdk_smp_wmb();
lcore_history->next_entry++;
lcore_history->next_entry += num_entries;
}
int

View File

@ -281,7 +281,7 @@ static void
trace_register_description(const struct spdk_trace_tpoint_opts *opts)
{
struct spdk_trace_tpoint *tpoint;
size_t i, remaining_size, max_name_length;
size_t i, max_name_length;
assert(opts->tpoint_id != 0);
assert(opts->tpoint_id < SPDK_TRACE_MAX_TPOINT_ID);
@ -300,8 +300,6 @@ trace_register_description(const struct spdk_trace_tpoint_opts *opts)
tpoint->new_object = opts->new_object;
max_name_length = sizeof(tpoint->args[0].name);
remaining_size = sizeof(((struct spdk_trace_entry *)0)->args);
for (i = 0; i < SPDK_TRACE_MAX_ARGS_COUNT; ++i) {
if (!opts->args[i].name || opts->args[i].name[0] == '\0') {
break;
@ -322,9 +320,6 @@ trace_register_description(const struct spdk_trace_tpoint_opts *opts)
break;
}
assert(remaining_size >= opts->args[i].size && "tpoint exceeds max size");
remaining_size -= opts->args[i].size;
if (strnlen(opts->args[i].name, max_name_length) == max_name_length) {
SPDK_ERRLOG("argument name (%s) is too long\n", opts->args[i].name);
}