[sysprof] sources/perf: Capture the CRTC number and MSC for vblank events.
- From: Christian Hergert <chergert src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [sysprof] sources/perf: Capture the CRTC number and MSC for vblank events.
- Date: Wed, 16 May 2018 13:33:32 +0000 (UTC)
commit a3bda35b287de3708d32d765f094131916f8c129
Author: Eric Anholt <eric anholt net>
Date: Wed May 16 14:22:37 2018 +0100
sources/perf: Capture the CRTC number and MSC for vblank events.
This will help make sense of vblank events in a multi-screen
environment (where there are two streams of vblanks), and hopefully
also useful for correlating to compositor events.
lib/sources/sp-perf-counter.h | 29 +++++--
lib/sources/sp-perf-source.c | 180 ++++++++++++++++++++++++++++++++---------
2 files changed, 161 insertions(+), 48 deletions(-)
---
diff --git a/lib/sources/sp-perf-counter.h b/lib/sources/sp-perf-counter.h
index a473b3d..3451842 100644
--- a/lib/sources/sp-perf-counter.h
+++ b/lib/sources/sp-perf-counter.h
@@ -88,17 +88,30 @@ typedef struct
guint64 time;
guint64 n_ips;
guint64 ips[0];
-} SpPerfCounterEventSample;
+} SpPerfCounterEventCallchain;
-typedef union
+typedef struct
{
struct perf_event_header header;
- guint8 raw[0];
- SpPerfCounterEventFork fork;
- SpPerfCounterEventComm comm;
- SpPerfCounterEventExit exit;
- SpPerfCounterEventMmap mmap;
- SpPerfCounterEventSample sample;
+ guint64 identifier;
+ guint64 ip;
+ guint32 pid;
+ guint32 tid;
+ guint64 time;
+ guint32 raw_size;
+ guchar raw[];
+} SpPerfCounterEventTracepoint;
+
+typedef union
+{
+ struct perf_event_header header;
+ guint8 raw[0];
+ SpPerfCounterEventFork fork;
+ SpPerfCounterEventComm comm;
+ SpPerfCounterEventExit exit;
+ SpPerfCounterEventMmap mmap;
+ SpPerfCounterEventCallchain callchain;
+ SpPerfCounterEventTracepoint tracepoint;
} SpPerfCounterEvent;
#pragma pack(pop)
diff --git a/lib/sources/sp-perf-source.c b/lib/sources/sp-perf-source.c
index 29e5e03..5accd4c 100644
--- a/lib/sources/sp-perf-source.c
+++ b/lib/sources/sp-perf-source.c
@@ -61,7 +61,8 @@ enum SpTracepoint {
typedef struct {
enum SpTracepoint tp;
- const char *path;
+ const char *path;
+ const char **fields;
} SpOptionalTracepoint;
/* Global list of the optional tracepoints we might want to watch. */
@@ -77,7 +78,8 @@ static const SpOptionalTracepoint optional_tracepoints[] = {
* won't get the event since it comes in on an IRQ handler, not for
* our pid.
*/
- { DRM_VBLANK, "drm/drm_vblank_event" },
+ { DRM_VBLANK, "drm/drm_vblank_event",
+ (const char *[]){ "crtc", "seq", NULL } },
};
/* Struct describing tracepoint events.
@@ -89,6 +91,7 @@ static const SpOptionalTracepoint optional_tracepoints[] = {
*/
typedef struct {
enum SpTracepoint tp;
+ gsize field_offsets[0];
} SpTracepointDesc;
struct _SpPerfSource
@@ -172,53 +175,54 @@ do_emit_exited (gpointer data)
}
static void
-sp_perf_source_handle_tracepoint (SpPerfSource *self,
- gint cpu,
- const SpPerfCounterEventSample *sample,
- SpTracepointDesc *tp_desc)
+sp_perf_source_handle_tracepoint (SpPerfSource *self,
+ gint cpu,
+ const SpPerfCounterEventTracepoint *sample,
+ SpTracepointDesc *tp_desc)
{
- switch (tp_desc->tp)
- {
- case DRM_VBLANK:
- sp_capture_writer_add_mark (self->writer,
- sample->time,
- cpu,
- sample->pid,
- 0,
- "drm",
- "vblank",
- NULL);
- break;
-
- default:
- break;
- }
+ gchar *message = NULL;
+
+ /* Note that field_offsets[] correspond to the
+ * SpOptionalTracepoint->fields[] strings. Yes, this is gross.
+ */
+ switch (tp_desc->tp)
+ {
+ case DRM_VBLANK:
+ message = g_strdup_printf ("crtc=%d, seq=%u",
+ *(gint *)(sample->raw +
+ tp_desc->field_offsets[0]),
+ *(guint *)(sample->raw +
+ tp_desc->field_offsets[1]));
+
+ sp_capture_writer_add_mark (self->writer,
+ sample->time,
+ cpu,
+ sample->pid,
+ 0,
+ "drm",
+ "vblank",
+ message);
+ break;
+
+ default:
+ break;
+ }
+
+ g_free (message);
}
static void
-sp_perf_source_handle_sample (SpPerfSource *self,
- gint cpu,
- const SpPerfCounterEventSample *sample)
+sp_perf_source_handle_callchain (SpPerfSource *self,
+ gint cpu,
+ const SpPerfCounterEventCallchain *sample)
{
const guint64 *ips;
gint n_ips;
guint64 trace[3];
- SpTracepointDesc *tp_desc;
g_assert (SP_IS_PERF_SOURCE (self));
g_assert (sample != NULL);
- /* We don't capture IPs with tracepoints, and get _RAW data instead. Handle
- * them separately.
- */
- tp_desc = g_hash_table_lookup (self->tracepoint_event_ids,
- GINT_TO_POINTER (sample->identifier));
- if (tp_desc)
- {
- sp_perf_source_handle_tracepoint (self, cpu, sample, tp_desc);
- return;
- }
-
ips = sample->ips;
n_ips = sample->n_ips;
@@ -264,6 +268,7 @@ sp_perf_source_handle_event (SpPerfCounterEvent *event,
gpointer user_data)
{
SpPerfSource *self = user_data;
+ SpTracepointDesc *tp_desc;
gsize offset;
gint64 time;
@@ -345,7 +350,21 @@ sp_perf_source_handle_event (SpPerfCounterEvent *event,
break;
case PERF_RECORD_SAMPLE:
- sp_perf_source_handle_sample (self, cpu, &event->sample);
+ /* We don't capture IPs with tracepoints, and get _RAW data
+ * instead. Handle them separately.
+ */
+ g_assert (&event->callchain.identifier == &event->tracepoint.identifier);
+ tp_desc = g_hash_table_lookup (self->tracepoint_event_ids,
+ GINT_TO_POINTER (event->callchain.identifier));
+ if (tp_desc)
+ {
+ sp_perf_source_handle_tracepoint (self, cpu, &event->tracepoint,
+ tp_desc);
+ }
+ else
+ {
+ sp_perf_source_handle_callchain (self, cpu, &event->callchain);
+ }
break;
case PERF_RECORD_THROTTLE:
@@ -381,6 +400,74 @@ sp_perf_get_tracepoint_config (const char *path, gint64 *config)
return TRUE;
}
+static gboolean
+sp_perf_get_tracepoint_fields (SpTracepointDesc *tp_desc,
+ const SpOptionalTracepoint *optional_tp,
+ GError **error)
+{
+ gchar *filename = NULL;
+ gchar *contents;
+ size_t len;
+ gint i;
+ filename = g_strdup_printf ("/sys/kernel/debug/tracing/events/%s/format",
+ optional_tp->path);
+ if (!filename)
+ return FALSE;
+
+ if (!g_file_get_contents (filename, &contents, &len, NULL))
+ {
+ g_free (filename);
+ return FALSE;
+ }
+
+ g_free (filename);
+
+ /* Look up our fields. Some example strings:
+ *
+ * field:unsigned short common_type; offset:0; size:2; signed:0;
+ * field:int crtc; offset:8; size:4; signed:1;
+ * field:unsigned int seq; offset:12; size:4; signed:0;
+ */
+ for (i = 0; optional_tp->fields[i] != NULL; i++)
+ {
+ gchar *pattern = g_strdup_printf ("%s;\toffset:", optional_tp->fields[i]);
+ gchar *match;
+ gint64 offset;
+
+ match = strstr (contents, pattern);
+ if (!match)
+ {
+ g_set_error (error,
+ G_IO_ERROR,
+ G_IO_ERROR_FAILED,
+ _("Sysprof failed to find field '%s'."),
+ optional_tp->fields[i]);
+ g_free (contents);
+ return FALSE;
+ }
+
+ offset = g_ascii_strtoll (match + strlen (pattern),
+ NULL, 0);
+ if (offset == G_MININT64 && errno != 0)
+ {
+ g_set_error (error,
+ G_IO_ERROR,
+ G_IO_ERROR_FAILED,
+ _("Sysprof failed to parse offset for '%s'."),
+ optional_tp->fields[i]);
+ g_free (contents);
+ return FALSE;
+ }
+
+ tp_desc->field_offsets[i] = offset;
+ g_free (pattern);
+ }
+
+ g_free (contents);
+
+ return TRUE;
+}
+
/* Adds a perf tracepoint event, if it's available.
*
* These are kernel tracepoints that we want to include in our capture
@@ -400,6 +487,7 @@ sp_perf_source_add_optional_tracepoint (SpPerfSource *self,
gint64 config;
gint64 id;
int ret;
+ gint num_fields;
if (!sp_perf_get_tracepoint_config(optional_tracepoint->path, &config))
return;
@@ -409,7 +497,7 @@ sp_perf_source_add_optional_tracepoint (SpPerfSource *self,
| PERF_SAMPLE_IP
| PERF_SAMPLE_TID
| PERF_SAMPLE_IDENTIFIER
- | PERF_SAMPLE_CALLCHAIN
+ | PERF_SAMPLE_RAW
| PERF_SAMPLE_TIME;
attr.config = config;
attr.sample_period = 1;
@@ -434,7 +522,12 @@ sp_perf_source_add_optional_tracepoint (SpPerfSource *self,
return;
}
- tp_desc = g_malloc (sizeof (*tp_desc));
+ /* The fields list is NULL-terminated, count how many are there. */
+ for (num_fields = 0; optional_tracepoint->fields[num_fields]; num_fields++)
+ ;
+
+ tp_desc = g_malloc (sizeof (*tp_desc) +
+ sizeof(*tp_desc->field_offsets) * num_fields);
if (!tp_desc)
{
close(fd);
@@ -443,6 +536,13 @@ sp_perf_source_add_optional_tracepoint (SpPerfSource *self,
tp_desc->tp = optional_tracepoint->tp;
+ if (!sp_perf_get_tracepoint_fields (tp_desc, optional_tracepoint, error))
+ {
+ free(tp_desc);
+ close(fd);
+ return;
+ }
+
/* Here's where we should inspect the /format file to determine how
* to pick fields out of the _RAW data.
*/
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]