From 6cc3169677c013119e04ca4869f0438f85b4b181 Mon Sep 17 00:00:00 2001 From: Konrad Sztyber Date: Wed, 16 Jun 2021 14:14:50 +0200 Subject: [PATCH] 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 Change-Id: I51ceea6b6e57df95d4b8bd797f04edbc4936c180 Reviewed-on: https://review.spdk.io/gerrit/c/spdk/spdk/+/8405 Community-CI: Mellanox Build Bot Tested-by: SPDK CI Jenkins Reviewed-by: Jim Harris Reviewed-by: Ziye Yang Reviewed-by: Tomasz Zawadzki --- include/spdk/trace.h | 10 +++++++ lib/trace/trace.c | 60 +++++++++++++++++++++++++++++++++++------ lib/trace/trace_flags.c | 7 +---- 3 files changed, 63 insertions(+), 14 deletions(-) diff --git a/include/spdk/trace.h b/include/spdk/trace.h index 06a99efc30..4a29bb8c4f 100644 --- a/include/spdk/trace.h +++ b/include/spdk/trace.h @@ -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) diff --git a/lib/trace/trace.c b/lib/trace/trace.c index 582ad7ba02..5adc1910c5 100644 --- a/lib/trace/trace.c +++ b/lib/trace/trace.c @@ -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 diff --git a/lib/trace/trace_flags.c b/lib/trace/trace_flags.c index ec4845a7d2..3c11739d7f 100644 --- a/lib/trace/trace_flags.c +++ b/lib/trace/trace_flags.c @@ -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); }