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.
This commit is contained in:
Eric Anholt
2018-05-16 14:22:37 +01:00
committed by Christian Hergert
parent 88af69c3ec
commit a3bda35b28
2 changed files with 160 additions and 47 deletions

View File

@ -88,17 +88,30 @@ typedef struct
guint64 time;
guint64 n_ips;
guint64 ips[0];
} SpPerfCounterEventSample;
} SpPerfCounterEventCallchain;
typedef struct
{
struct perf_event_header header;
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;
SpPerfCounterEventSample sample;
struct perf_event_header header;
guint8 raw[0];
SpPerfCounterEventFork fork;
SpPerfCounterEventComm comm;
SpPerfCounterEventExit exit;
SpPerfCounterEventMmap mmap;
SpPerfCounterEventCallchain callchain;
SpPerfCounterEventTracepoint tracepoint;
} SpPerfCounterEvent;
#pragma pack(pop)

View File

@ -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;
gchar *message = NULL;
default:
break;
}
/* 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.
*/