mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2026-08-31 12:52:29 -04:00
perf trace: Correct default cpumask formatting to hexadecimal
Currently, dynamic non-array fields such as 'cpumask_t' are mishandled in
'perf trace', causing the raw length and offset descriptors to be interpreted
and displayed as a literal integer (e.g., "cpumask: 524320" instead of the
actual mask data).
Correct the parsing of dynamic fields that do not have the
TEP_FIELD_IS_ARRAY flag set by introducing helper functions
format_field__get_raw_data() and format_field__get_cpumask().
Using these helpers, resolve the pointer to the raw bits within the
payload and format the cpumask as a zero-padded hexadecimal string by default.
Fixes: c5e006cdbd ("perf trace: Support tracepoint dynamic char arrays")
Signed-off-by: Aaron Tomlin <atomlin@atomlin.com>
Signed-off-by: Namhyung Kim <namhyung@kernel.org>
This commit is contained in:
committed by
Namhyung Kim
parent
eaab2eb09d
commit
89493fe58c
@@ -3207,6 +3207,22 @@ static void bpf_output__fprintf(struct trace *trace,
|
||||
++trace->nr_events_printed;
|
||||
}
|
||||
|
||||
static unsigned char bitmap_byte(const unsigned long *mask, int byte_idx)
|
||||
{
|
||||
unsigned char b_val = 0;
|
||||
int bit_in_byte;
|
||||
|
||||
for (bit_in_byte = 0; bit_in_byte < 8; bit_in_byte++) {
|
||||
int b_idx = byte_idx * 8 + bit_in_byte;
|
||||
int host_w_idx = b_idx / BITS_PER_LONG;
|
||||
int host_bit_in_word = b_idx % BITS_PER_LONG;
|
||||
|
||||
if (mask[host_w_idx] & (1UL << host_bit_in_word))
|
||||
b_val |= (1 << bit_in_byte);
|
||||
}
|
||||
return b_val;
|
||||
}
|
||||
|
||||
static size_t trace__fprintf_tp_fields(struct trace *trace, struct perf_sample *sample,
|
||||
struct thread *thread, void *augmented_args, int augmented_args_size)
|
||||
{
|
||||
@@ -3238,17 +3254,54 @@ static size_t trace__fprintf_tp_fields(struct trace *trace, struct perf_sample *
|
||||
syscall_arg.len = 0;
|
||||
syscall_arg.fmt = arg;
|
||||
if (field->flags & TEP_FIELD_IS_ARRAY) {
|
||||
int offset = field->offset;
|
||||
void *ptr = format_field__get_raw_data(field, sample,
|
||||
evsel->needs_swap,
|
||||
&syscall_arg.len);
|
||||
|
||||
if (field->flags & TEP_FIELD_IS_DYNAMIC) {
|
||||
offset = format_field__intval(field, sample, evsel->needs_swap);
|
||||
syscall_arg.len = offset >> 16;
|
||||
offset &= 0xffff;
|
||||
if (tep_field_is_relative(field->flags))
|
||||
offset += field->offset + field->size;
|
||||
if (!ptr) {
|
||||
pr_err("Problem processing %s field, skipping...\n", field->name);
|
||||
continue;
|
||||
}
|
||||
val = (uintptr_t)ptr;
|
||||
} else if ((field->flags & TEP_FIELD_IS_DYNAMIC) &&
|
||||
strstr(field->type, "cpumask")) {
|
||||
unsigned long *mask = format_field__get_cpumask(field, sample,
|
||||
evsel->needs_swap,
|
||||
&syscall_arg.len);
|
||||
|
||||
if (!mask) {
|
||||
pr_err("Problem processing %s field, skipping...\n", field->name);
|
||||
continue;
|
||||
}
|
||||
|
||||
val = (uintptr_t)(sample->raw_data + offset);
|
||||
printed += scnprintf(bf + printed, size - printed, "%s", printed ? ", " : "");
|
||||
if (trace->show_arg_names)
|
||||
printed += scnprintf(bf + printed, size - printed, "%s: ", field->name);
|
||||
|
||||
if (syscall_arg.len == 0) {
|
||||
printed += scnprintf(bf + printed, size - printed, "0");
|
||||
} else {
|
||||
int i;
|
||||
bool skip_zero = true;
|
||||
|
||||
printed += scnprintf(bf + printed, size - printed, "0x");
|
||||
/* Print bytes from most significant to least significant */
|
||||
for (i = syscall_arg.len - 1; i >= 0; i--) {
|
||||
unsigned char b_val = bitmap_byte(mask, i);
|
||||
|
||||
if (skip_zero && b_val == 0 && i > 0)
|
||||
continue;
|
||||
|
||||
if (skip_zero) {
|
||||
printed += scnprintf(bf + printed, size - printed, "%x", b_val);
|
||||
skip_zero = false;
|
||||
} else {
|
||||
printed += scnprintf(bf + printed, size - printed, "%02x", b_val);
|
||||
}
|
||||
}
|
||||
}
|
||||
free(mask);
|
||||
continue;
|
||||
} else
|
||||
val = format_field__intval(field, sample, evsel->needs_swap);
|
||||
/*
|
||||
|
||||
@@ -16,9 +16,11 @@
|
||||
#include <errno.h>
|
||||
#include <inttypes.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <dirent.h>
|
||||
#include <linux/bitops.h>
|
||||
#include <linux/bitmap.h>
|
||||
#include <linux/compiler.h>
|
||||
#include <linux/ctype.h>
|
||||
#include <linux/err.h>
|
||||
@@ -3933,6 +3935,95 @@ void *perf_sample__rawptr(struct perf_sample *sample, const char *name)
|
||||
return sample->raw_data + offset;
|
||||
}
|
||||
|
||||
void *format_field__get_raw_data(struct tep_format_field *field, struct
|
||||
perf_sample *sample, bool needs_swap,
|
||||
u16 *len_out)
|
||||
{
|
||||
int offset = field->offset;
|
||||
int size = field->size;
|
||||
|
||||
if (field->flags & TEP_FIELD_IS_DYNAMIC) {
|
||||
unsigned int dynamic_data;
|
||||
|
||||
if (out_of_bounds(field, field->offset, field->size, sample->raw_size))
|
||||
return NULL;
|
||||
|
||||
dynamic_data = format_field__intval(field, sample, needs_swap);
|
||||
|
||||
offset = dynamic_data & 0xffff;
|
||||
size = (dynamic_data >> 16) & 0xffff;
|
||||
|
||||
if (tep_field_is_relative(field->flags))
|
||||
offset += field->offset + field->size;
|
||||
}
|
||||
|
||||
if (out_of_bounds(field, offset, size, sample->raw_size))
|
||||
return NULL;
|
||||
|
||||
*len_out = size;
|
||||
return sample->raw_data + offset;
|
||||
}
|
||||
|
||||
unsigned long *format_field__get_cpumask(struct tep_format_field *field,
|
||||
struct perf_sample *sample,
|
||||
bool needs_swap, u16 *len_out)
|
||||
{
|
||||
u16 len;
|
||||
void *ptr = format_field__get_raw_data(field, sample, needs_swap, &len);
|
||||
unsigned long *mask;
|
||||
struct perf_env *env;
|
||||
bool target_is_64;
|
||||
int target_word_size;
|
||||
int nr_words;
|
||||
int bit_idx;
|
||||
int nbits;
|
||||
|
||||
if (!ptr)
|
||||
return NULL;
|
||||
|
||||
nbits = len * 8;
|
||||
mask = bitmap_zalloc(nbits ?: 1);
|
||||
if (!mask)
|
||||
return NULL;
|
||||
|
||||
env = evsel__env(sample->evsel);
|
||||
target_is_64 = env ? perf_env__kernel_is_64_bit(env) : (sizeof(void *) == 8);
|
||||
target_word_size = target_is_64 ? 8 : 4;
|
||||
nr_words = len / target_word_size;
|
||||
|
||||
for (bit_idx = 0; bit_idx < nbits; bit_idx++) {
|
||||
int w_idx = bit_idx / (target_word_size * 8);
|
||||
int bit_in_word = bit_idx % (target_word_size * 8);
|
||||
bool set = false;
|
||||
|
||||
if (w_idx >= nr_words)
|
||||
break;
|
||||
|
||||
if (target_is_64) {
|
||||
u64 word;
|
||||
memcpy(&word, (unsigned char *)ptr + w_idx * 8, 8);
|
||||
if (needs_swap)
|
||||
word = bswap_64(word);
|
||||
set = (word & (1ULL << bit_in_word)) != 0;
|
||||
} else {
|
||||
u32 word32;
|
||||
memcpy(&word32, (unsigned char *)ptr + w_idx * 4, 4);
|
||||
if (needs_swap)
|
||||
word32 = bswap_32(word32);
|
||||
set = (word32 & (1U << bit_in_word)) != 0;
|
||||
}
|
||||
|
||||
if (set) {
|
||||
int host_w_idx = bit_idx / BITS_PER_LONG;
|
||||
int host_bit_in_word = bit_idx % BITS_PER_LONG;
|
||||
mask[host_w_idx] |= (1UL << host_bit_in_word);
|
||||
}
|
||||
}
|
||||
|
||||
*len_out = len;
|
||||
return mask;
|
||||
}
|
||||
|
||||
u64 format_field__intval(struct tep_format_field *field, struct perf_sample *sample,
|
||||
bool needs_swap)
|
||||
{
|
||||
|
||||
@@ -398,6 +398,12 @@ static inline char *perf_sample__strval(struct perf_sample *sample, const char *
|
||||
|
||||
struct tep_format_field;
|
||||
|
||||
void *format_field__get_raw_data(struct tep_format_field *field,
|
||||
struct perf_sample *sample,
|
||||
bool needs_swap, u16 *len_out);
|
||||
unsigned long *format_field__get_cpumask(struct tep_format_field *field,
|
||||
struct perf_sample *sample,
|
||||
bool needs_swap, u16 *len_out);
|
||||
u64 format_field__intval(struct tep_format_field *field, struct perf_sample *sample, bool needs_swap);
|
||||
|
||||
#ifdef HAVE_LIBTRACEEVENT
|
||||
|
||||
Reference in New Issue
Block a user