diff mbox series

[v3,01/10] cxl: add helper function to parse trace event to json object

Message ID 166742400990.2654617.4918438509154032997.stgit@djiang5-desk3.ch.intel.com
State Superseded
Headers show
Series cxl: add monitor support for trace events | expand

Commit Message

Dave Jiang Nov. 2, 2022, 9:20 p.m. UTC
Add the helper function that parses a trace event captured by
libtraceevent in a tep handle. All the parsed fields are added to a json
object. The json object is added to the provided list in the input parameter.

Signed-off-by: Dave Jiang <dave.jiang@intel.com>
---
 cxl/event_trace.c |  166 +++++++++++++++++++++++++++++++++++++++++++++++++++++
 cxl/event_trace.h |   14 ++++
 cxl/meson.build   |    2 +
 meson.build       |    1 
 4 files changed, 183 insertions(+)
 create mode 100644 cxl/event_trace.c
 create mode 100644 cxl/event_trace.h

Comments

Steven Rostedt Nov. 3, 2022, 5:48 a.m. UTC | #1
On Wed, 02 Nov 2022 14:20:09 -0700
Dave Jiang <dave.jiang@intel.com> wrote:

> Add the helper function that parses a trace event captured by
> libtraceevent in a tep handle. All the parsed fields are added to a json
> object. The json object is added to the provided list in the input parameter.
> 
> Signed-off-by: Dave Jiang <dave.jiang@intel.com>
> ---
>  cxl/event_trace.c |  166 +++++++++++++++++++++++++++++++++++++++++++++++++++++
>  cxl/event_trace.h |   14 ++++
>  cxl/meson.build   |    2 +
>  meson.build       |    1 
>  4 files changed, 183 insertions(+)
>  create mode 100644 cxl/event_trace.c
>  create mode 100644 cxl/event_trace.h
> 
> diff --git a/cxl/event_trace.c b/cxl/event_trace.c
> new file mode 100644
> index 000000000000..803df34452f3
> --- /dev/null
> +++ b/cxl/event_trace.c
> @@ -0,0 +1,166 @@
> +// SPDX-License-Identifier: GPL-2.0
> +// Copyright (C) 2022, Intel Corp. All rights reserved.
> +#include <stdio.h>
> +#include <json-c/json.h>
> +#include <util/json.h>
> +#include <util/util.h>
> +#include <util/parse-options.h>
> +#include <util/parse-configs.h>
> +#include <util/strbuf.h>
> +#include <util/sysfs.h>
> +#include <ccan/list/list.h>
> +#include <ndctl/ndctl.h>
> +#include <ndctl/libndctl.h>
> +#include <sys/epoll.h>
> +#include <sys/stat.h>
> +#include <libcxl.h>
> +#include <uuid/uuid.h>
> +#include <traceevent/event-parse.h>
> +#include "json.h"
> +#include "event_trace.h"
> +
> +#define _GNU_SOURCE
> +#include <string.h>
> +
> +static struct json_object *num_to_json(void *num, int size)
> +{
> +	if (size <= 4)
> +		return json_object_new_int(*(int *)num);
> +
> +	return util_json_object_hex(*(unsigned long long *)num, 0);
> +}
> +
> +static int cxl_event_to_json_callback(struct tep_event *event,
> +		struct tep_record *record, struct list_head *jlist_head)
> +{
> +	struct tep_format_field **fields;
> +	struct json_object *jevent, *jobj, *jarray;
> +	struct jlist_node *jnode;
> +	int i, j, rc = 0;
> +
> +	jnode = malloc(sizeof(*jnode));
> +	if (!jnode)
> +		return -ENOMEM;
> +
> +	jevent = json_object_new_object();
> +	if (!jevent) {
> +		rc = -ENOMEM;
> +		goto err_jevent;
> +	}
> +	jnode->jobj = jevent;
> +
> +	fields = tep_event_fields(event);
> +	if (!fields) {
> +		rc = -ENOENT;
> +		goto err;
> +	}
> +
> +	jobj = json_object_new_string(event->system);
> +	if (!jobj) {
> +		rc = -ENOMEM;
> +		goto err;
> +	}
> +	json_object_object_add(jevent, "system", jobj);
> +
> +	jobj = json_object_new_string(event->name);
> +	if (!jobj) {
> +		rc = -ENOMEM;
> +		goto err;
> +	}
> +	json_object_object_add(jevent, "event", jobj);
> +
> +	jobj = json_object_new_uint64(record->ts);
> +	if (!jobj) {
> +		rc = -ENOMEM;
> +		goto err;
> +	}
> +	json_object_object_add(jevent, "timestamp", jobj);
> +
> +	for (i = 0; fields[i]; i++) {
> +		struct tep_format_field *f = fields[i];
> +		int len;
> +		char *tmp;
> +
> +		tmp = strcasestr(f->type, "char[]");

Instead of looking for strings, you could use the field flags.

	f->flags & TEP_FIELD_IS_STRING

> +		if (tmp) { /* event field is a string */
> +			char *str;
> +
> +			str = tep_get_field_raw(NULL, event, f->name, record, &len, 0);
> +			if (!str)
> +				continue;
> +
> +			jobj = json_object_new_string(str);
> +			if (!jobj) {
> +				rc = -ENOMEM;
> +				goto err;
> +			}
> +
> +			json_object_object_add(jevent, f->name, jobj);
> +		} else if (f->arraylen) { /* data array */

		} else if (f->flags & TEP_FIELD_IS_ARRAY) {

 too.

> +			unsigned char *data;
> +			int chunks;
> +
> +			data = tep_get_field_raw(NULL, event, f->name, record, &len, 0);
> +			if (!data)
> +				continue;
> +
> +			jarray = json_object_new_array();
> +			if (!jarray) {
> +				rc = -ENOMEM;
> +				goto err;
> +			}
> +
> +			chunks = f->size / f->elementsize;
> +			for (j = 0; j < chunks; j++) {
> +				jobj = num_to_json(data, f->elementsize);
> +				if (!jobj) {
> +					json_object_put(jarray);
> +					return -ENOMEM;
> +				}
> +				json_object_array_add(jarray, jobj);
> +				data += f->elementsize;
> +			}
> +
> +			json_object_object_add(jevent, f->name, jarray);
> +		} else { /* single number */
> +			unsigned char *data;
> +
> +			data = tep_get_field_raw(NULL, event, f->name, record, &len, 0);
> +			if (!data)
> +				continue;
> +
> +			/* check to see if we have a UUID */
> +			tmp = strcasestr(f->type, "uuid_t");

I didn't even know event fields for uuid existed.

-- Steve

> +			if (tmp) {
> +				char uuid[SYSFS_ATTR_SIZE];
> +
> +				uuid_unparse(data, uuid);
> +				jobj = json_object_new_string(uuid);
> +				if (!jobj) {
> +					rc = -ENOMEM;
> +					goto err;
> +				}
> +
> +				json_object_object_add(jevent, f->name, jobj);
> +				continue;
> +			}
> +
> +			jobj = num_to_json(data, f->elementsize);
> +			if (!jobj) {
> +				rc = -ENOMEM;
> +				goto err;
> +			}
> +
> +			json_object_object_add(jevent, f->name, jobj);
> +		}
> +	}
> +
> +	list_add_tail(jlist_head, &jnode->list);
> +	return 0;
> +
> +err:
> +	json_object_put(jevent);
> +err_jevent:
> +	free(jnode);
> +	return rc;
> +}
Dave Jiang Nov. 3, 2022, 4:15 p.m. UTC | #2
On 11/2/2022 10:48 PM, Steven Rostedt wrote:
> On Wed, 02 Nov 2022 14:20:09 -0700
> Dave Jiang <dave.jiang@intel.com> wrote:
> 
>> Add the helper function that parses a trace event captured by
>> libtraceevent in a tep handle. All the parsed fields are added to a json
>> object. The json object is added to the provided list in the input parameter.
>>
>> Signed-off-by: Dave Jiang <dave.jiang@intel.com>
>> ---
>>   cxl/event_trace.c |  166 +++++++++++++++++++++++++++++++++++++++++++++++++++++
>>   cxl/event_trace.h |   14 ++++
>>   cxl/meson.build   |    2 +
>>   meson.build       |    1
>>   4 files changed, 183 insertions(+)
>>   create mode 100644 cxl/event_trace.c
>>   create mode 100644 cxl/event_trace.h
>>
>> diff --git a/cxl/event_trace.c b/cxl/event_trace.c
>> new file mode 100644
>> index 000000000000..803df34452f3
>> --- /dev/null
>> +++ b/cxl/event_trace.c
>> @@ -0,0 +1,166 @@
>> +// SPDX-License-Identifier: GPL-2.0
>> +// Copyright (C) 2022, Intel Corp. All rights reserved.
>> +#include <stdio.h>
>> +#include <json-c/json.h>
>> +#include <util/json.h>
>> +#include <util/util.h>
>> +#include <util/parse-options.h>
>> +#include <util/parse-configs.h>
>> +#include <util/strbuf.h>
>> +#include <util/sysfs.h>
>> +#include <ccan/list/list.h>
>> +#include <ndctl/ndctl.h>
>> +#include <ndctl/libndctl.h>
>> +#include <sys/epoll.h>
>> +#include <sys/stat.h>
>> +#include <libcxl.h>
>> +#include <uuid/uuid.h>
>> +#include <traceevent/event-parse.h>
>> +#include "json.h"
>> +#include "event_trace.h"
>> +
>> +#define _GNU_SOURCE
>> +#include <string.h>
>> +
>> +static struct json_object *num_to_json(void *num, int size)
>> +{
>> +	if (size <= 4)
>> +		return json_object_new_int(*(int *)num);
>> +
>> +	return util_json_object_hex(*(unsigned long long *)num, 0);
>> +}
>> +
>> +static int cxl_event_to_json_callback(struct tep_event *event,
>> +		struct tep_record *record, struct list_head *jlist_head)
>> +{
>> +	struct tep_format_field **fields;
>> +	struct json_object *jevent, *jobj, *jarray;
>> +	struct jlist_node *jnode;
>> +	int i, j, rc = 0;
>> +
>> +	jnode = malloc(sizeof(*jnode));
>> +	if (!jnode)
>> +		return -ENOMEM;
>> +
>> +	jevent = json_object_new_object();
>> +	if (!jevent) {
>> +		rc = -ENOMEM;
>> +		goto err_jevent;
>> +	}
>> +	jnode->jobj = jevent;
>> +
>> +	fields = tep_event_fields(event);
>> +	if (!fields) {
>> +		rc = -ENOENT;
>> +		goto err;
>> +	}
>> +
>> +	jobj = json_object_new_string(event->system);
>> +	if (!jobj) {
>> +		rc = -ENOMEM;
>> +		goto err;
>> +	}
>> +	json_object_object_add(jevent, "system", jobj);
>> +
>> +	jobj = json_object_new_string(event->name);
>> +	if (!jobj) {
>> +		rc = -ENOMEM;
>> +		goto err;
>> +	}
>> +	json_object_object_add(jevent, "event", jobj);
>> +
>> +	jobj = json_object_new_uint64(record->ts);
>> +	if (!jobj) {
>> +		rc = -ENOMEM;
>> +		goto err;
>> +	}
>> +	json_object_object_add(jevent, "timestamp", jobj);
>> +
>> +	for (i = 0; fields[i]; i++) {
>> +		struct tep_format_field *f = fields[i];
>> +		int len;
>> +		char *tmp;
>> +
>> +		tmp = strcasestr(f->type, "char[]");
> 
> Instead of looking for strings, you could use the field flags.
> 
> 	f->flags & TEP_FIELD_IS_STRING

Thanks for the review Steve. This is good to know! I will update.

> 
>> +		if (tmp) { /* event field is a string */
>> +			char *str;
>> +
>> +			str = tep_get_field_raw(NULL, event, f->name, record, &len, 0);
>> +			if (!str)
>> +				continue;
>> +
>> +			jobj = json_object_new_string(str);
>> +			if (!jobj) {
>> +				rc = -ENOMEM;
>> +				goto err;
>> +			}
>> +
>> +			json_object_object_add(jevent, f->name, jobj);
>> +		} else if (f->arraylen) { /* data array */
> 
> 		} else if (f->flags & TEP_FIELD_IS_ARRAY) {
> 
>   too.

Will update.

> 
>> +			unsigned char *data;
>> +			int chunks;
>> +
>> +			data = tep_get_field_raw(NULL, event, f->name, record, &len, 0);
>> +			if (!data)
>> +				continue;
>> +
>> +			jarray = json_object_new_array();
>> +			if (!jarray) {
>> +				rc = -ENOMEM;
>> +				goto err;
>> +			}
>> +
>> +			chunks = f->size / f->elementsize;
>> +			for (j = 0; j < chunks; j++) {
>> +				jobj = num_to_json(data, f->elementsize);
>> +				if (!jobj) {
>> +					json_object_put(jarray);
>> +					return -ENOMEM;
>> +				}
>> +				json_object_array_add(jarray, jobj);
>> +				data += f->elementsize;
>> +			}
>> +
>> +			json_object_object_add(jevent, f->name, jarray);
>> +		} else { /* single number */
>> +			unsigned char *data;
>> +
>> +			data = tep_get_field_raw(NULL, event, f->name, record, &len, 0);
>> +			if (!data)
>> +				continue;
>> +
>> +			/* check to see if we have a UUID */
>> +			tmp = strcasestr(f->type, "uuid_t");
> 
> I didn't even know event fields for uuid existed.

Ira figured out that you can you can use __field_struct() to accommodate 
uuid_t. Here's what he changed below:

diff --git a/include/trace/events/cxl.h b/include/trace/events/cxl.h
index 6777a1119e68..22fcd677b6d0 100644
--- a/include/trace/events/cxl.h
+++ b/include/trace/events/cxl.h
@@ -68,7 +68,7 @@ TRACE_EVENT(cxl_overflow,
#define CXL_EVT_TP_entry                                       \
         __string(dev_name, dev_name)                            \
         __field(int, log)                                       \
-       __array(u8, hdr_uuid, UUID_SIZE)                        \
+       __field_struct(uuid_t, hdr_uuid)                        \
         __field(u32, hdr_flags)                                 \
         __field(u16, hdr_handle)                                \
         __field(u16, hdr_related_handle)                        \
@@ -79,7 +79,7 @@ TRACE_EVENT(cxl_overflow,
#define CXL_EVT_TP_fast_assign(dname, l, hdr)                   \
         __assign_str(dev_name, (dname));                        \
         __entry->log = (l);                                     \
-       memcpy(__entry->hdr_uuid, &(hdr).id, UUID_SIZE);        \
+       memcpy(&__entry->hdr_uuid, &(hdr).id, sizeof(uuid_t));  \
         __entry->hdr_length = (hdr).length;                     \
         __entry->hdr_flags = get_unaligned_le24((hdr).flags);   \
         __entry->hdr_handle = le16_to_cpu((hdr).handle);        \
@@ -93,7 +93,7 @@ TRACE_EVENT(cxl_overflow,
                 "handle=%x related_handle=%x maint_op_class=%u" \
                 " : " fmt,                                      \
                __get_str(dev_name),cxl_event_log_type_str(__entry->log), \
-               __entry->hdr_timestamp, __entry->hdr_uuid, 
__entry->hdr_length, \
+               __entry->hdr_timestamp, &__entry->hdr_uuid, 
__entry->hdr_length,        \
                 show_hdr_flags(__entry->hdr_flags), 
__entry->hdr_handle,        \
                 __entry->hdr_related_handle, 
__entry->hdr_maint_op_class,       \
                 ##__VA_ARGS__)

> 
> -- Steve
> 
>> +			if (tmp) {
>> +				char uuid[SYSFS_ATTR_SIZE];
>> +
>> +				uuid_unparse(data, uuid);
>> +				jobj = json_object_new_string(uuid);
>> +				if (!jobj) {
>> +					rc = -ENOMEM;
>> +					goto err;
>> +				}
>> +
>> +				json_object_object_add(jevent, f->name, jobj);
>> +				continue;
>> +			}
>> +
>> +			jobj = num_to_json(data, f->elementsize);
>> +			if (!jobj) {
>> +				rc = -ENOMEM;
>> +				goto err;
>> +			}
>> +
>> +			json_object_object_add(jevent, f->name, jobj);
>> +		}
>> +	}
>> +
>> +	list_add_tail(jlist_head, &jnode->list);
>> +	return 0;
>> +
>> +err:
>> +	json_object_put(jevent);
>> +err_jevent:
>> +	free(jnode);
>> +	return rc;
>> +}
Ira Weiny Nov. 4, 2022, 6:56 p.m. UTC | #3
On Thu, Nov 03, 2022 at 09:15:03AM -0700, Jiang, Dave wrote:
> 
> 
> On 11/2/2022 10:48 PM, Steven Rostedt wrote:
> > On Wed, 02 Nov 2022 14:20:09 -0700
> > Dave Jiang <dave.jiang@intel.com> wrote:
> > 
> > > Add the helper function that parses a trace event captured by
> > > libtraceevent in a tep handle. All the parsed fields are added to a json
> > > object. The json object is added to the provided list in the input parameter.
> > > 
> > > Signed-off-by: Dave Jiang <dave.jiang@intel.com>
> > > ---
> > >   cxl/event_trace.c |  166 +++++++++++++++++++++++++++++++++++++++++++++++++++++
> > >   cxl/event_trace.h |   14 ++++

[snip]

> > > +		} else { /* single number */
> > > +			unsigned char *data;
> > > +
> > > +			data = tep_get_field_raw(NULL, event, f->name, record, &len, 0);
> > > +			if (!data)
> > > +				continue;
> > > +
> > > +			/* check to see if we have a UUID */
> > > +			tmp = strcasestr(f->type, "uuid_t");
> > 
> > I didn't even know event fields for uuid existed.
> 
> Ira figured out that you can you can use __field_struct() to accommodate
> uuid_t. Here's what he changed below:

I guess this is a hack.  I looked into creating a new entry specifier and saw
that __field_struct did what I needed.

It all seems to work fine until Alison pointed out this morning that trace-cmd
can't seem to parse this.

:-/

So now I'm not sure what is technically correct.  I've never used trace-cmd
before and this all works fine on the cmd line and with this monitor command.

Is this messing up trace-cmd?

Ira

> 
> diff --git a/include/trace/events/cxl.h b/include/trace/events/cxl.h
> index 6777a1119e68..22fcd677b6d0 100644
> --- a/include/trace/events/cxl.h
> +++ b/include/trace/events/cxl.h
> @@ -68,7 +68,7 @@ TRACE_EVENT(cxl_overflow,
> #define CXL_EVT_TP_entry                                       \
>         __string(dev_name, dev_name)                            \
>         __field(int, log)                                       \
> -       __array(u8, hdr_uuid, UUID_SIZE)                        \
> +       __field_struct(uuid_t, hdr_uuid)                        \
>         __field(u32, hdr_flags)                                 \
>         __field(u16, hdr_handle)                                \
>         __field(u16, hdr_related_handle)                        \
> @@ -79,7 +79,7 @@ TRACE_EVENT(cxl_overflow,
> #define CXL_EVT_TP_fast_assign(dname, l, hdr)                   \
>         __assign_str(dev_name, (dname));                        \
>         __entry->log = (l);                                     \
> -       memcpy(__entry->hdr_uuid, &(hdr).id, UUID_SIZE);        \
> +       memcpy(&__entry->hdr_uuid, &(hdr).id, sizeof(uuid_t));  \
>         __entry->hdr_length = (hdr).length;                     \
>         __entry->hdr_flags = get_unaligned_le24((hdr).flags);   \
>         __entry->hdr_handle = le16_to_cpu((hdr).handle);        \
> @@ -93,7 +93,7 @@ TRACE_EVENT(cxl_overflow,
>                 "handle=%x related_handle=%x maint_op_class=%u" \
>                 " : " fmt,                                      \
>                __get_str(dev_name),cxl_event_log_type_str(__entry->log), \
> -               __entry->hdr_timestamp, __entry->hdr_uuid,
> __entry->hdr_length, \
> +               __entry->hdr_timestamp, &__entry->hdr_uuid,
> __entry->hdr_length,        \
>                 show_hdr_flags(__entry->hdr_flags), __entry->hdr_handle,
> \
>                 __entry->hdr_related_handle, __entry->hdr_maint_op_class,
> \
>                 ##__VA_ARGS__)
>
Steven Rostedt Nov. 14, 2022, 5:32 p.m. UTC | #4
On Fri, 4 Nov 2022 11:56:46 -0700
Ira Weiny <ira.weiny@intel.com> wrote:

> > Ira figured out that you can you can use __field_struct() to accommodate
> > uuid_t. Here's what he changed below:  
> 
> I guess this is a hack.  I looked into creating a new entry specifier and saw
> that __field_struct did what I needed.
> 
> It all seems to work fine until Alison pointed out this morning that trace-cmd
> can't seem to parse this.
> 
> :-/

If it works in a trace file, it should work in trace-cmd. If it does not,
then trace-cmd needs to be fixed (unless it needs more information from the
kernel, which it might).

> 
> So now I'm not sure what is technically correct.  I've never used trace-cmd
> before and this all works fine on the cmd line and with this monitor command.
> 
> Is this messing up trace-cmd?

If you see an issue, would you be able to send me a trace.dat file that
includes the failed parsing?

That is:

 trace-cmd record -e <your-event>

Do something to trigger your event.

Then send me the produced trace.dat file that fails to parse from the
 trace-cmd report.

Thanks!

-- Steve
Alison Schofield Nov. 14, 2022, 8:44 p.m. UTC | #5
On Mon, Nov 14, 2022 at 12:32:38PM -0500, Steven Rostedt wrote:
> On Fri, 4 Nov 2022 11:56:46 -0700
> Ira Weiny <ira.weiny@intel.com> wrote:
> 
> > > Ira figured out that you can you can use __field_struct() to accommodate
> > > uuid_t. Here's what he changed below:  
> > 
> > I guess this is a hack.  I looked into creating a new entry specifier and saw
> > that __field_struct did what I needed.
> > 
> > It all seems to work fine until Alison pointed out this morning that trace-cmd
> > can't seem to parse this.
> > 
> > :-/
> 
> If it works in a trace file, it should work in trace-cmd. If it does not,
> then trace-cmd needs to be fixed (unless it needs more information from the
> kernel, which it might).
> 
> > 
> > So now I'm not sure what is technically correct.  I've never used trace-cmd
> > before and this all works fine on the cmd line and with this monitor command.
> > 
> > Is this messing up trace-cmd?
> 
> If you see an issue, would you be able to send me a trace.dat file that
> includes the failed parsing?
> 
> That is:
> 
>  trace-cmd record -e <your-event>
> 
> Do something to trigger your event.
> 
> Then send me the produced trace.dat file that fails to parse from the
>  trace-cmd report.

Hi Steve,

I tried to use the uuid_t type in another TRACE event (cxl_poison), and
ran into the problem mentioned above.

It seems to be a problem in libtraceevent (or a user error ;))

When we have:
	TP_STRUCT__entry
		__field_struct(uuid_t, hdr_uuid)

	TP__fast_assign
		memcpy(&__entry->hdr.uuid, &(hdr).id, sizeof(uuid_t));

	TP_printk("uuid= %pU",  &__entry->hdr.uuid)

Libtraceevent fails to handle that "&" in "&__entry->hrd.uuid" when
creating the pretty print string. It fails in process_op() and then
later in pretty_print() prefaces the string w '[[FAILED TO PARSE]"
follwed by the raw values.

If I omit that '&', it works. But - I get unacceptable compiler
warnings from the kernel build - trying to assign to %p.

So, it's really not 'trace-cmd' per se that is failing, that is
just where the failure is visible.

The 'other' way that we view these CXL events are through monitor
and ndctl, where we pull the fields directly from the event, and
don't care about that TP_printk string. uuid_t works fine in that
case.

CXL events were not the first to use uuid_t. I see events/afs.h
using it also. I don't see a difference in their syntax, that would
makes it work there, while CXL fails. Maybe you do.

I don't have the environment up at the moment, so I'm just spewing
from notes and memory. If you need actual duplication, ask me again.

Thanks for helping w this Steve!

Alison

-- Steve



> 
> Thanks!
> 
> -- Steve
Steven Rostedt Nov. 14, 2022, 8:53 p.m. UTC | #6
On Mon, 14 Nov 2022 12:44:51 -0800
Alison Schofield <alison.schofield@intel.com> wrote:

> Hi Steve,
> 
> I tried to use the uuid_t type in another TRACE event (cxl_poison), and
> ran into the problem mentioned above.
> 
> It seems to be a problem in libtraceevent (or a user error ;))

Correct. When I said trace-cmd, I really meant libtraceevent, but I tend to
blur the two as libtracevenet was born out of trace-cmd.

> 
> When we have:
> 	TP_STRUCT__entry
> 		__field_struct(uuid_t, hdr_uuid)
> 
> 	TP__fast_assign
> 		memcpy(&__entry->hdr.uuid, &(hdr).id, sizeof(uuid_t));
> 
> 	TP_printk("uuid= %pU",  &__entry->hdr.uuid)
> 
> Libtraceevent fails to handle that "&" in "&__entry->hrd.uuid" when
> creating the pretty print string. It fails in process_op() and then
> later in pretty_print() prefaces the string w '[[FAILED TO PARSE]"
> follwed by the raw values.
> 
> If I omit that '&', it works. But - I get unacceptable compiler
> warnings from the kernel build - trying to assign to %p.
> 
> So, it's really not 'trace-cmd' per se that is failing, that is
> just where the failure is visible.

Right.

> 
> The 'other' way that we view these CXL events are through monitor
> and ndctl, where we pull the fields directly from the event, and
> don't care about that TP_printk string. uuid_t works fine in that
> case.
> 
> CXL events were not the first to use uuid_t. I see events/afs.h
> using it also. I don't see a difference in their syntax, that would
> makes it work there, while CXL fails. Maybe you do.

I'm guessing they don't work ;-)

> 
> I don't have the environment up at the moment, so I'm just spewing
> from notes and memory. If you need actual duplication, ask me again.
> 
> Thanks for helping w this Steve!
> 

No problem.

I'll can add this to a test event and then update libtraceevent to handle
it. I'm currently working on a new release of the libraries anyway.

-- Steve
diff mbox series

Patch

diff --git a/cxl/event_trace.c b/cxl/event_trace.c
new file mode 100644
index 000000000000..803df34452f3
--- /dev/null
+++ b/cxl/event_trace.c
@@ -0,0 +1,166 @@ 
+// SPDX-License-Identifier: GPL-2.0
+// Copyright (C) 2022, Intel Corp. All rights reserved.
+#include <stdio.h>
+#include <json-c/json.h>
+#include <util/json.h>
+#include <util/util.h>
+#include <util/parse-options.h>
+#include <util/parse-configs.h>
+#include <util/strbuf.h>
+#include <util/sysfs.h>
+#include <ccan/list/list.h>
+#include <ndctl/ndctl.h>
+#include <ndctl/libndctl.h>
+#include <sys/epoll.h>
+#include <sys/stat.h>
+#include <libcxl.h>
+#include <uuid/uuid.h>
+#include <traceevent/event-parse.h>
+#include "json.h"
+#include "event_trace.h"
+
+#define _GNU_SOURCE
+#include <string.h>
+
+static struct json_object *num_to_json(void *num, int size)
+{
+	if (size <= 4)
+		return json_object_new_int(*(int *)num);
+
+	return util_json_object_hex(*(unsigned long long *)num, 0);
+}
+
+static int cxl_event_to_json_callback(struct tep_event *event,
+		struct tep_record *record, struct list_head *jlist_head)
+{
+	struct tep_format_field **fields;
+	struct json_object *jevent, *jobj, *jarray;
+	struct jlist_node *jnode;
+	int i, j, rc = 0;
+
+	jnode = malloc(sizeof(*jnode));
+	if (!jnode)
+		return -ENOMEM;
+
+	jevent = json_object_new_object();
+	if (!jevent) {
+		rc = -ENOMEM;
+		goto err_jevent;
+	}
+	jnode->jobj = jevent;
+
+	fields = tep_event_fields(event);
+	if (!fields) {
+		rc = -ENOENT;
+		goto err;
+	}
+
+	jobj = json_object_new_string(event->system);
+	if (!jobj) {
+		rc = -ENOMEM;
+		goto err;
+	}
+	json_object_object_add(jevent, "system", jobj);
+
+	jobj = json_object_new_string(event->name);
+	if (!jobj) {
+		rc = -ENOMEM;
+		goto err;
+	}
+	json_object_object_add(jevent, "event", jobj);
+
+	jobj = json_object_new_uint64(record->ts);
+	if (!jobj) {
+		rc = -ENOMEM;
+		goto err;
+	}
+	json_object_object_add(jevent, "timestamp", jobj);
+
+	for (i = 0; fields[i]; i++) {
+		struct tep_format_field *f = fields[i];
+		int len;
+		char *tmp;
+
+		tmp = strcasestr(f->type, "char[]");
+		if (tmp) { /* event field is a string */
+			char *str;
+
+			str = tep_get_field_raw(NULL, event, f->name, record, &len, 0);
+			if (!str)
+				continue;
+
+			jobj = json_object_new_string(str);
+			if (!jobj) {
+				rc = -ENOMEM;
+				goto err;
+			}
+
+			json_object_object_add(jevent, f->name, jobj);
+		} else if (f->arraylen) { /* data array */
+			unsigned char *data;
+			int chunks;
+
+			data = tep_get_field_raw(NULL, event, f->name, record, &len, 0);
+			if (!data)
+				continue;
+
+			jarray = json_object_new_array();
+			if (!jarray) {
+				rc = -ENOMEM;
+				goto err;
+			}
+
+			chunks = f->size / f->elementsize;
+			for (j = 0; j < chunks; j++) {
+				jobj = num_to_json(data, f->elementsize);
+				if (!jobj) {
+					json_object_put(jarray);
+					return -ENOMEM;
+				}
+				json_object_array_add(jarray, jobj);
+				data += f->elementsize;
+			}
+
+			json_object_object_add(jevent, f->name, jarray);
+		} else { /* single number */
+			unsigned char *data;
+
+			data = tep_get_field_raw(NULL, event, f->name, record, &len, 0);
+			if (!data)
+				continue;
+
+			/* check to see if we have a UUID */
+			tmp = strcasestr(f->type, "uuid_t");
+			if (tmp) {
+				char uuid[SYSFS_ATTR_SIZE];
+
+				uuid_unparse(data, uuid);
+				jobj = json_object_new_string(uuid);
+				if (!jobj) {
+					rc = -ENOMEM;
+					goto err;
+				}
+
+				json_object_object_add(jevent, f->name, jobj);
+				continue;
+			}
+
+			jobj = num_to_json(data, f->elementsize);
+			if (!jobj) {
+				rc = -ENOMEM;
+				goto err;
+			}
+
+			json_object_object_add(jevent, f->name, jobj);
+		}
+	}
+
+	list_add_tail(jlist_head, &jnode->list);
+	return 0;
+
+err:
+	json_object_put(jevent);
+err_jevent:
+	free(jnode);
+	return rc;
+}
diff --git a/cxl/event_trace.h b/cxl/event_trace.h
new file mode 100644
index 000000000000..00975a0b5680
--- /dev/null
+++ b/cxl/event_trace.h
@@ -0,0 +1,14 @@ 
+/* SPDX-License-Identifier: GPL-2.0 */
+/* Copyright (C) 2022 Intel Corporation. All rights reserved. */
+#ifndef __CXL_EVENT_TRACE_H__
+#define __CXL_EVENT_TRACE_H__
+
+#include <json-c/json.h>
+#include <ccan/list/list.h>
+
+struct jlist_node {
+	struct json_object *jobj;
+	struct list_node list;
+};
+
+#endif
diff --git a/cxl/meson.build b/cxl/meson.build
index f2474aaa6e2e..8c7733431613 100644
--- a/cxl/meson.build
+++ b/cxl/meson.build
@@ -7,6 +7,7 @@  cxl_src = [
   'memdev.c',
   'json.c',
   'filter.c',
+  'event_trace.c',
 ]
 
 cxl_tool = executable('cxl',
@@ -19,6 +20,7 @@  cxl_tool = executable('cxl',
     kmod,
     json,
     versiondep,
+    traceevent,
   ],
   install : true,
   install_dir : rootbindir,
diff --git a/meson.build b/meson.build
index 20a646d135c7..f611e0bdd7f3 100644
--- a/meson.build
+++ b/meson.build
@@ -142,6 +142,7 @@  kmod = dependency('libkmod')
 libudev = dependency('libudev')
 uuid = dependency('uuid')
 json = dependency('json-c')
+traceevent = dependency('libtraceevent')
 if get_option('docs').enabled()
   if get_option('asciidoctor').enabled()
     asciidoc = find_program('asciidoctor', required : true)