[PATCH] libtraceevent: parse task state from tracepoint print form=

From: Ze Gao
Date: Wed Aug 02 2023 - 07:20:46 EST


As of this writing, we use prev_state to report task state, which
relies on both the task state macros and TASK_STATE_TO_CHAR_STR
copied from the kernel to interpret its actual meaning. In this way,
libtraceevent gets broken literally each time TASK_STATE_TO_CHAR_STR
changes as the kernel evolves. Counting on hardcoded
TASK_STATE_TO_CHAR_STR gurantees no backward compatibilty.

To fix this, we build the state char map from the print format
parsed from date recorded on the fly and removes dependencies on
these internal kernel definitions.

Signed-off-by: Ze Gao <zegao@xxxxxxxxxxx>
---
plugins/plugin_sched_switch.c | 103 ++++++++++++++++++++++++++++------
1 file changed, 87 insertions(+), 16 deletions(-)

diff --git a/plugins/plugin_sched_switch.c b/plugins/plugin_sched_switch.c
index 8752cae..a0df138 100644
--- a/plugins/plugin_sched_switch.c
+++ b/plugins/plugin_sched_switch.c
@@ -6,28 +6,25 @@
#include <stdlib.h>
#include <string.h>

+#include "event-utils.h"
#include "event-parse.h"
#include "trace-seq.h"

-static void write_state(struct trace_seq *s, int val)
-{
- const char states[] =3D "SDTtZXxW";
- int found =3D 0;
- int i;
+#define TASK_STATE_MAX 16

- for (i =3D 0; i < (sizeof(states) - 1); i++) {
- if (!(val & (1 << i)))
- continue;
+static char state_to_char[TASK_STATE_MAX];
+static unsigned int num_sleeping_states =3D 0;

- if (found)
- trace_seq_putc(s, '|');

- found =3D 1;
- trace_seq_putc(s, states[i]);
- }
+static void write_state(struct trace_seq *s, int val)
+{
+ unsigned int bit =3D val ? ffs(val) : 0;
+ char state;

- if (!found)
- trace_seq_putc(s, 'R');
+ state =3D (!bit || bit > num_sleeping_states) ? 'R' : state_to_char=
[bit];
+ trace_seq_putc(s, state);
+ if(bit > num_sleeping_states)
+ trace_seq_putc(s, '+');
}

static void write_and_save_comm(struct tep_format_field *field,
@@ -79,6 +76,76 @@ static int sched_wakeup_handler(struct trace_seq *s,
return 0;
}

+static const struct tep_print_arg* task_state_print_flag(const struct
tep_event *event) {
+
+ struct tep_print_arg* args;
+
+ if (!event)
+ goto out;
+
+ args =3D event->print_fmt.args;
+ while(args)
+ {
+ if (args->type =3D=3D TEP_PRINT_FLAGS)
+ return args;
+ if (args->type =3D=3D TEP_PRINT_OP) {
+ args =3D args->op.right;
+ args =3D args->op.left;
+ continue;
+ }
+ args =3D args->next;
+ }
+out:
+ return NULL;
+}
+
+static int parse_task_state_arr(const char *value, const char *str)
+{
+ long val;
+ unsigned int bit;
+
+ if (!value || !str)
+ return -1;
+
+ val =3D strtol(value, NULL, 0);
+ bit =3D val ? ffs(val) : 0;
+ state_to_char[bit] =3D str[0];
+ num_sleeping_states++;
+ if (num_sleeping_states > TASK_STATE_MAX - 1) {
+ tep_warning("too many states parsed, possibly bad format\n"=
);
+ return -1;
+ }
+ return 0;
+}
+
+static void parse_print_flag(const struct tep_print_flag_sym* field,
+ int (*parser)(const char *value, const char *s=
tr))
+{
+ int err;
+
+ if (!field || !parser)
+ return;
+ err =3D parser(field->value, field->str);
+ if (err){
+ tep_warning("parsing print flag failed, possibly bad format=
\n");
+ return;
+ }
+ if (field->next)
+ parse_print_flag(field->next, parser);
+
+}
+
+static void build_task_state_arr(const struct tep_event *event)
+{
+ const struct tep_print_arg* args;
+
+ args =3D task_state_print_flag(event);
+ if (!args)
+ tep_warning("print flag not found, possibly bad format\n");
+ else
+ parse_print_flag(args->flags.flags, parse_task_state_arr);
+}
+
static int sched_switch_handler(struct trace_seq *s,
struct tep_record *record,
struct tep_event *event, void *context)
@@ -99,8 +166,12 @@ static int sched_switch_handler(struct trace_seq *s,
if (tep_get_field_val(s, event, "prev_prio", record, &val, 1) =3D=
=3D 0)
trace_seq_printf(s, "[%d] ", (int) val);

- if (tep_get_field_val(s, event, "prev_state", record, &val, 1) =3D=
=3D 0)
+
+ if (tep_get_field_val(s, event, "prev_state", record, &val, 1) =3D=
=3D 0) {
+ if (!num_sleeping_states)
+ build_task_state_arr(event);
write_state(s, val);
+ }

trace_seq_puts(s, " =3D=3D> ");

--
2.41.0



> > Anyway, it works now and I've tested on some perf.data in old formats
> > but not cover all the kernel releases.
> >
> > Thoughts?
>
> I don't maintain the perf code. You'll have to talk with the perf
> maintainers.

Roger that! Thank you very much!

Regards,
Ze

> -- Steve