per Intel policy to include file commit date using git cmd below. The policy does not apply to non-Intel (C) notices. git log --follow -C90% --format=%ad --date default <file> | tail -1 and then pull just the 4 digit year from the result. Intel copyrights were not added to files where Intel either had no contribution ot the contribution lacked substance (ie license header updates, formatting changes, etc). Contribution date used "--follow -C95%" to get the most accurate date. Note that several files in this patch didn't end the license/(c) block with a blank comment line so these were added as the vast majority of files do have this last blank line. Simply there for consistency. Signed-off-by: paul luse <paul.e.luse@intel.com> Change-Id: Id5b7ce4f658fe87132f14139ead58d6e285c04d4 Reviewed-on: https://review.spdk.io/gerrit/c/spdk/spdk/+/15192 Tested-by: SPDK CI Jenkins <sys_sgci@intel.com> Reviewed-by: Jim Harris <james.r.harris@intel.com> Reviewed-by: Ben Walker <benjamin.walker@intel.com> Community-CI: Mellanox Build Bot
463 lines
12 KiB
C++
463 lines
12 KiB
C++
/* SPDX-License-Identifier: BSD-3-Clause
|
|
* Copyright (C) 2016 Intel Corporation.
|
|
* All rights reserved.
|
|
*/
|
|
|
|
#include "spdk/stdinc.h"
|
|
#include "spdk/env.h"
|
|
#include "spdk/json.h"
|
|
#include "spdk/likely.h"
|
|
#include "spdk/string.h"
|
|
#include "spdk/util.h"
|
|
|
|
#include <map>
|
|
|
|
extern "C" {
|
|
#include "spdk/trace_parser.h"
|
|
#include "spdk/util.h"
|
|
}
|
|
|
|
static struct spdk_trace_parser *g_parser;
|
|
static const struct spdk_trace_flags *g_flags;
|
|
static struct spdk_json_write_ctx *g_json;
|
|
static bool g_print_tsc = false;
|
|
|
|
/* This is a bit ugly, but we don't want to include env_dpdk in the app, while spdk_util, which we
|
|
* do need, uses some of the functions implemented there. We're not actually using the functions
|
|
* that depend on those, so just define them as no-ops to allow the app to link.
|
|
*/
|
|
extern "C" {
|
|
void *
|
|
spdk_realloc(void *buf, size_t size, size_t align)
|
|
{
|
|
assert(false);
|
|
|
|
return NULL;
|
|
}
|
|
|
|
void
|
|
spdk_free(void *buf)
|
|
{
|
|
assert(false);
|
|
}
|
|
|
|
uint64_t
|
|
spdk_get_ticks(void)
|
|
{
|
|
return 0;
|
|
}
|
|
} /* extern "C" */
|
|
|
|
static void usage(void);
|
|
|
|
static char *g_exe_name;
|
|
|
|
static float
|
|
get_us_from_tsc(uint64_t tsc, uint64_t tsc_rate)
|
|
{
|
|
return ((float)tsc) * 1000 * 1000 / tsc_rate;
|
|
}
|
|
|
|
static const char *
|
|
format_argname(const char *name)
|
|
{
|
|
static char namebuf[16];
|
|
|
|
snprintf(namebuf, sizeof(namebuf), "%s: ", name);
|
|
return namebuf;
|
|
}
|
|
|
|
static void
|
|
print_ptr(const char *arg_string, uint64_t arg)
|
|
{
|
|
printf("%-7.7s0x%-14jx ", format_argname(arg_string), arg);
|
|
}
|
|
|
|
static void
|
|
print_uint64(const char *arg_string, uint64_t arg)
|
|
{
|
|
/*
|
|
* Print arg as signed, since -1 is a common value especially
|
|
* for FLUSH WRITEBUF when writev() returns -1 due to full
|
|
* socket buffer.
|
|
*/
|
|
printf("%-7.7s%-16jd ", format_argname(arg_string), arg);
|
|
}
|
|
|
|
static void
|
|
print_string(const char *arg_string, const char *arg)
|
|
{
|
|
printf("%-7.7s%-16.16s ", format_argname(arg_string), arg);
|
|
}
|
|
|
|
static void
|
|
print_size(uint32_t size)
|
|
{
|
|
if (size > 0) {
|
|
printf("size: %6u ", size);
|
|
} else {
|
|
printf("%13s", " ");
|
|
}
|
|
}
|
|
|
|
static void
|
|
print_object_id(const struct spdk_trace_tpoint *d, struct spdk_trace_parser_entry *entry)
|
|
{
|
|
/* Set size to 128 and 256 bytes to make sure we can fit all the characters we need */
|
|
char related_id[128] = {'\0'};
|
|
char ids[256] = {'\0'};
|
|
|
|
if (entry->related_type != OBJECT_NONE) {
|
|
snprintf(related_id, sizeof(related_id), " (%c%jd)",
|
|
g_flags->object[entry->related_type].id_prefix,
|
|
entry->related_index);
|
|
}
|
|
|
|
snprintf(ids, sizeof(ids), "%c%jd%s", g_flags->object[d->object_type].id_prefix,
|
|
entry->object_index, related_id);
|
|
printf("id: %-17s", ids);
|
|
}
|
|
|
|
static void
|
|
print_float(const char *arg_string, float arg)
|
|
{
|
|
printf("%-7s%-16.3f ", format_argname(arg_string), arg);
|
|
}
|
|
|
|
static void
|
|
print_event(struct spdk_trace_parser_entry *entry, uint64_t tsc_rate, uint64_t tsc_offset)
|
|
{
|
|
struct spdk_trace_entry *e = entry->entry;
|
|
const struct spdk_trace_tpoint *d;
|
|
float us;
|
|
size_t i;
|
|
|
|
d = &g_flags->tpoint[e->tpoint_id];
|
|
us = get_us_from_tsc(e->tsc - tsc_offset, tsc_rate);
|
|
|
|
printf("%2d: %10.3f ", entry->lcore, us);
|
|
if (g_print_tsc) {
|
|
printf("(%9ju) ", e->tsc - tsc_offset);
|
|
}
|
|
if (g_flags->owner[d->owner_type].id_prefix) {
|
|
printf("%c%02d ", g_flags->owner[d->owner_type].id_prefix, e->poller_id);
|
|
} else {
|
|
printf("%4s", " ");
|
|
}
|
|
|
|
printf("%-*s ", (int)sizeof(d->name), d->name);
|
|
print_size(e->size);
|
|
|
|
if (d->new_object) {
|
|
print_object_id(d, entry);
|
|
} else if (d->object_type != OBJECT_NONE) {
|
|
if (entry->object_index != UINT64_MAX) {
|
|
us = get_us_from_tsc(e->tsc - entry->object_start, tsc_rate);
|
|
print_object_id(d, entry);
|
|
print_float("time", us);
|
|
} else {
|
|
printf("id: N/A");
|
|
}
|
|
} else if (e->object_id != 0) {
|
|
print_ptr("object", e->object_id);
|
|
}
|
|
|
|
for (i = 0; i < d->num_args; ++i) {
|
|
switch (d->args[i].type) {
|
|
case SPDK_TRACE_ARG_TYPE_PTR:
|
|
print_ptr(d->args[i].name, (uint64_t)entry->args[i].pointer);
|
|
break;
|
|
case SPDK_TRACE_ARG_TYPE_INT:
|
|
print_uint64(d->args[i].name, entry->args[i].integer);
|
|
break;
|
|
case SPDK_TRACE_ARG_TYPE_STR:
|
|
print_string(d->args[i].name, entry->args[i].string);
|
|
break;
|
|
}
|
|
}
|
|
printf("\n");
|
|
}
|
|
|
|
static void
|
|
print_event_json(struct spdk_trace_parser_entry *entry, uint64_t tsc_rate, uint64_t tsc_offset)
|
|
{
|
|
struct spdk_trace_entry *e = entry->entry;
|
|
const struct spdk_trace_tpoint *d;
|
|
size_t i;
|
|
|
|
d = &g_flags->tpoint[e->tpoint_id];
|
|
|
|
spdk_json_write_object_begin(g_json);
|
|
spdk_json_write_named_uint64(g_json, "lcore", entry->lcore);
|
|
spdk_json_write_named_uint64(g_json, "tpoint", e->tpoint_id);
|
|
spdk_json_write_named_uint64(g_json, "tsc", e->tsc);
|
|
|
|
if (g_flags->owner[d->owner_type].id_prefix) {
|
|
spdk_json_write_named_string_fmt(g_json, "poller", "%c%02d",
|
|
g_flags->owner[d->owner_type].id_prefix,
|
|
e->poller_id);
|
|
}
|
|
if (e->size != 0) {
|
|
spdk_json_write_named_uint32(g_json, "size", e->size);
|
|
}
|
|
if (d->new_object || d->object_type != OBJECT_NONE || e->object_id != 0) {
|
|
char object_type;
|
|
|
|
spdk_json_write_named_object_begin(g_json, "object");
|
|
if (d->new_object) {
|
|
object_type = g_flags->object[d->object_type].id_prefix;
|
|
spdk_json_write_named_string_fmt(g_json, "id", "%c%" PRIu64, object_type,
|
|
entry->object_index);
|
|
} else if (d->object_type != OBJECT_NONE) {
|
|
object_type = g_flags->object[d->object_type].id_prefix;
|
|
if (entry->object_index != UINT64_MAX) {
|
|
spdk_json_write_named_string_fmt(g_json, "id", "%c%" PRIu64,
|
|
object_type,
|
|
entry->object_index);
|
|
spdk_json_write_named_uint64(g_json, "time",
|
|
e->tsc - entry->object_start);
|
|
}
|
|
}
|
|
spdk_json_write_named_uint64(g_json, "value", e->object_id);
|
|
spdk_json_write_object_end(g_json);
|
|
}
|
|
|
|
/* Print related objects array */
|
|
if (entry->related_index != UINT64_MAX) {
|
|
spdk_json_write_named_string_fmt(g_json, "related", "%c%" PRIu64,
|
|
g_flags->object[entry->related_type].id_prefix,
|
|
entry->related_index);
|
|
}
|
|
|
|
if (d->num_args > 0) {
|
|
spdk_json_write_named_array_begin(g_json, "args");
|
|
for (i = 0; i < d->num_args; ++i) {
|
|
switch (d->args[i].type) {
|
|
case SPDK_TRACE_ARG_TYPE_PTR:
|
|
spdk_json_write_uint64(g_json, (uint64_t)entry->args[i].pointer);
|
|
break;
|
|
case SPDK_TRACE_ARG_TYPE_INT:
|
|
spdk_json_write_uint64(g_json, entry->args[i].integer);
|
|
break;
|
|
case SPDK_TRACE_ARG_TYPE_STR:
|
|
spdk_json_write_string(g_json, entry->args[i].string);
|
|
break;
|
|
}
|
|
}
|
|
spdk_json_write_array_end(g_json);
|
|
}
|
|
|
|
spdk_json_write_object_end(g_json);
|
|
}
|
|
|
|
static void
|
|
process_event(struct spdk_trace_parser_entry *e, uint64_t tsc_rate, uint64_t tsc_offset)
|
|
{
|
|
if (g_json == NULL) {
|
|
print_event(e, tsc_rate, tsc_offset);
|
|
} else {
|
|
print_event_json(e, tsc_rate, tsc_offset);
|
|
}
|
|
}
|
|
|
|
static void
|
|
print_tpoint_definitions(void)
|
|
{
|
|
const struct spdk_trace_tpoint *tpoint;
|
|
size_t i, j;
|
|
|
|
/* We only care about these when printing JSON */
|
|
if (!g_json) {
|
|
return;
|
|
}
|
|
|
|
spdk_json_write_named_uint64(g_json, "tsc_rate", g_flags->tsc_rate);
|
|
spdk_json_write_named_array_begin(g_json, "tpoints");
|
|
|
|
for (i = 0; i < SPDK_COUNTOF(g_flags->tpoint); ++i) {
|
|
tpoint = &g_flags->tpoint[i];
|
|
if (tpoint->tpoint_id == 0) {
|
|
continue;
|
|
}
|
|
|
|
spdk_json_write_object_begin(g_json);
|
|
spdk_json_write_named_string(g_json, "name", tpoint->name);
|
|
spdk_json_write_named_uint32(g_json, "id", tpoint->tpoint_id);
|
|
spdk_json_write_named_bool(g_json, "new_object", tpoint->new_object);
|
|
|
|
spdk_json_write_named_array_begin(g_json, "args");
|
|
for (j = 0; j < tpoint->num_args; ++j) {
|
|
spdk_json_write_object_begin(g_json);
|
|
spdk_json_write_named_string(g_json, "name", tpoint->args[j].name);
|
|
spdk_json_write_named_uint32(g_json, "type", tpoint->args[j].type);
|
|
spdk_json_write_named_uint32(g_json, "size", tpoint->args[j].size);
|
|
spdk_json_write_object_end(g_json);
|
|
}
|
|
spdk_json_write_array_end(g_json);
|
|
spdk_json_write_object_end(g_json);
|
|
}
|
|
|
|
spdk_json_write_array_end(g_json);
|
|
}
|
|
|
|
static int
|
|
print_json(void *cb_ctx, const void *data, size_t size)
|
|
{
|
|
ssize_t rc;
|
|
|
|
while (size > 0) {
|
|
rc = write(STDOUT_FILENO, data, size);
|
|
if (rc < 0) {
|
|
fprintf(stderr, "%s: %s\n", g_exe_name, spdk_strerror(errno));
|
|
abort();
|
|
}
|
|
|
|
size -= rc;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void
|
|
usage(void)
|
|
{
|
|
fprintf(stderr, "usage:\n");
|
|
fprintf(stderr, " %s <option> <lcore#>\n", g_exe_name);
|
|
fprintf(stderr, " '-c' to display single lcore history\n");
|
|
fprintf(stderr, " '-t' to display TSC offset for each event\n");
|
|
fprintf(stderr, " '-s' to specify spdk_trace shm name for a\n");
|
|
fprintf(stderr, " currently running process\n");
|
|
fprintf(stderr, " '-i' to specify the shared memory ID\n");
|
|
fprintf(stderr, " '-p' to specify the trace PID\n");
|
|
fprintf(stderr, " (If -s is specified, then one of\n");
|
|
fprintf(stderr, " -i or -p must be specified)\n");
|
|
fprintf(stderr, " '-f' to specify a tracepoint file name\n");
|
|
fprintf(stderr, " (-s and -f are mutually exclusive)\n");
|
|
fprintf(stderr, " '-j' to use JSON to format the output\n");
|
|
}
|
|
|
|
int
|
|
main(int argc, char **argv)
|
|
{
|
|
struct spdk_trace_parser_opts opts;
|
|
struct spdk_trace_parser_entry entry;
|
|
int lcore = SPDK_TRACE_MAX_LCORE;
|
|
uint64_t tsc_offset, entry_count;
|
|
const char *app_name = NULL;
|
|
const char *file_name = NULL;
|
|
int op, i;
|
|
char shm_name[64];
|
|
int shm_id = -1, shm_pid = -1;
|
|
bool json = false;
|
|
|
|
g_exe_name = argv[0];
|
|
while ((op = getopt(argc, argv, "c:f:i:jp:s:t")) != -1) {
|
|
switch (op) {
|
|
case 'c':
|
|
lcore = atoi(optarg);
|
|
if (lcore > SPDK_TRACE_MAX_LCORE) {
|
|
fprintf(stderr, "Selected lcore: %d "
|
|
"exceeds maximum %d\n", lcore,
|
|
SPDK_TRACE_MAX_LCORE);
|
|
exit(1);
|
|
}
|
|
break;
|
|
case 'i':
|
|
shm_id = atoi(optarg);
|
|
break;
|
|
case 'p':
|
|
shm_pid = atoi(optarg);
|
|
break;
|
|
case 's':
|
|
app_name = optarg;
|
|
break;
|
|
case 'f':
|
|
file_name = optarg;
|
|
break;
|
|
case 't':
|
|
g_print_tsc = true;
|
|
break;
|
|
case 'j':
|
|
json = true;
|
|
break;
|
|
default:
|
|
usage();
|
|
exit(1);
|
|
}
|
|
}
|
|
|
|
if (file_name != NULL && app_name != NULL) {
|
|
fprintf(stderr, "-f and -s are mutually exclusive\n");
|
|
usage();
|
|
exit(1);
|
|
}
|
|
|
|
if (file_name == NULL && app_name == NULL) {
|
|
fprintf(stderr, "One of -f and -s must be specified\n");
|
|
usage();
|
|
exit(1);
|
|
}
|
|
|
|
if (json) {
|
|
g_json = spdk_json_write_begin(print_json, NULL, 0);
|
|
if (g_json == NULL) {
|
|
fprintf(stderr, "Failed to allocate JSON write context\n");
|
|
exit(1);
|
|
}
|
|
}
|
|
|
|
if (!file_name) {
|
|
if (shm_id >= 0) {
|
|
snprintf(shm_name, sizeof(shm_name), "/%s_trace.%d", app_name, shm_id);
|
|
} else {
|
|
snprintf(shm_name, sizeof(shm_name), "/%s_trace.pid%d", app_name, shm_pid);
|
|
}
|
|
file_name = shm_name;
|
|
}
|
|
|
|
opts.filename = file_name;
|
|
opts.lcore = lcore;
|
|
opts.mode = app_name == NULL ? SPDK_TRACE_PARSER_MODE_FILE : SPDK_TRACE_PARSER_MODE_SHM;
|
|
g_parser = spdk_trace_parser_init(&opts);
|
|
if (g_parser == NULL) {
|
|
fprintf(stderr, "Failed to initialize trace parser\n");
|
|
exit(1);
|
|
}
|
|
|
|
g_flags = spdk_trace_parser_get_flags(g_parser);
|
|
if (!g_json) {
|
|
printf("TSC Rate: %ju\n", g_flags->tsc_rate);
|
|
} else {
|
|
spdk_json_write_object_begin(g_json);
|
|
print_tpoint_definitions();
|
|
spdk_json_write_named_array_begin(g_json, "entries");
|
|
}
|
|
|
|
for (i = 0; i < SPDK_TRACE_MAX_LCORE; ++i) {
|
|
if (lcore == SPDK_TRACE_MAX_LCORE || i == lcore) {
|
|
entry_count = spdk_trace_parser_get_entry_count(g_parser, i);
|
|
if (entry_count > 0) {
|
|
printf("Trace Size of lcore (%d): %ju\n", i, entry_count);
|
|
}
|
|
}
|
|
}
|
|
|
|
tsc_offset = spdk_trace_parser_get_tsc_offset(g_parser);
|
|
while (spdk_trace_parser_next_entry(g_parser, &entry)) {
|
|
if (entry.entry->tsc < tsc_offset) {
|
|
continue;
|
|
}
|
|
process_event(&entry, g_flags->tsc_rate, tsc_offset);
|
|
}
|
|
|
|
if (g_json != NULL) {
|
|
spdk_json_write_array_end(g_json);
|
|
spdk_json_write_object_end(g_json);
|
|
spdk_json_write_end(g_json);
|
|
}
|
|
|
|
spdk_trace_parser_cleanup(g_parser);
|
|
|
|
return (0);
|
|
}
|