mirror of
https://github.com/varun-r-mallya/sysprof.git
synced 2026-02-11 15:40:53 +00:00
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:
committed by
Christian Hergert
parent
88af69c3ec
commit
a3bda35b28
@ -88,17 +88,30 @@ typedef struct
|
|||||||
guint64 time;
|
guint64 time;
|
||||||
guint64 n_ips;
|
guint64 n_ips;
|
||||||
guint64 ips[0];
|
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
|
typedef union
|
||||||
{
|
{
|
||||||
struct perf_event_header header;
|
struct perf_event_header header;
|
||||||
guint8 raw[0];
|
guint8 raw[0];
|
||||||
SpPerfCounterEventFork fork;
|
SpPerfCounterEventFork fork;
|
||||||
SpPerfCounterEventComm comm;
|
SpPerfCounterEventComm comm;
|
||||||
SpPerfCounterEventExit exit;
|
SpPerfCounterEventExit exit;
|
||||||
SpPerfCounterEventMmap mmap;
|
SpPerfCounterEventMmap mmap;
|
||||||
SpPerfCounterEventSample sample;
|
SpPerfCounterEventCallchain callchain;
|
||||||
|
SpPerfCounterEventTracepoint tracepoint;
|
||||||
} SpPerfCounterEvent;
|
} SpPerfCounterEvent;
|
||||||
|
|
||||||
#pragma pack(pop)
|
#pragma pack(pop)
|
||||||
|
|||||||
@ -61,7 +61,8 @@ enum SpTracepoint {
|
|||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
enum SpTracepoint tp;
|
enum SpTracepoint tp;
|
||||||
const char *path;
|
const char *path;
|
||||||
|
const char **fields;
|
||||||
} SpOptionalTracepoint;
|
} SpOptionalTracepoint;
|
||||||
|
|
||||||
/* Global list of the optional tracepoints we might want to watch. */
|
/* 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
|
* won't get the event since it comes in on an IRQ handler, not for
|
||||||
* our pid.
|
* our pid.
|
||||||
*/
|
*/
|
||||||
{ DRM_VBLANK, "drm/drm_vblank_event" },
|
{ DRM_VBLANK, "drm/drm_vblank_event",
|
||||||
|
(const char *[]){ "crtc", "seq", NULL } },
|
||||||
};
|
};
|
||||||
|
|
||||||
/* Struct describing tracepoint events.
|
/* Struct describing tracepoint events.
|
||||||
@ -89,6 +91,7 @@ static const SpOptionalTracepoint optional_tracepoints[] = {
|
|||||||
*/
|
*/
|
||||||
typedef struct {
|
typedef struct {
|
||||||
enum SpTracepoint tp;
|
enum SpTracepoint tp;
|
||||||
|
gsize field_offsets[0];
|
||||||
} SpTracepointDesc;
|
} SpTracepointDesc;
|
||||||
|
|
||||||
struct _SpPerfSource
|
struct _SpPerfSource
|
||||||
@ -172,53 +175,54 @@ do_emit_exited (gpointer data)
|
|||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
sp_perf_source_handle_tracepoint (SpPerfSource *self,
|
sp_perf_source_handle_tracepoint (SpPerfSource *self,
|
||||||
gint cpu,
|
gint cpu,
|
||||||
const SpPerfCounterEventSample *sample,
|
const SpPerfCounterEventTracepoint *sample,
|
||||||
SpTracepointDesc *tp_desc)
|
SpTracepointDesc *tp_desc)
|
||||||
{
|
{
|
||||||
switch (tp_desc->tp)
|
gchar *message = NULL;
|
||||||
{
|
|
||||||
case DRM_VBLANK:
|
|
||||||
sp_capture_writer_add_mark (self->writer,
|
|
||||||
sample->time,
|
|
||||||
cpu,
|
|
||||||
sample->pid,
|
|
||||||
0,
|
|
||||||
"drm",
|
|
||||||
"vblank",
|
|
||||||
NULL);
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
/* Note that field_offsets[] correspond to the
|
||||||
break;
|
* 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
|
static void
|
||||||
sp_perf_source_handle_sample (SpPerfSource *self,
|
sp_perf_source_handle_callchain (SpPerfSource *self,
|
||||||
gint cpu,
|
gint cpu,
|
||||||
const SpPerfCounterEventSample *sample)
|
const SpPerfCounterEventCallchain *sample)
|
||||||
{
|
{
|
||||||
const guint64 *ips;
|
const guint64 *ips;
|
||||||
gint n_ips;
|
gint n_ips;
|
||||||
guint64 trace[3];
|
guint64 trace[3];
|
||||||
SpTracepointDesc *tp_desc;
|
|
||||||
|
|
||||||
g_assert (SP_IS_PERF_SOURCE (self));
|
g_assert (SP_IS_PERF_SOURCE (self));
|
||||||
g_assert (sample != NULL);
|
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;
|
ips = sample->ips;
|
||||||
n_ips = sample->n_ips;
|
n_ips = sample->n_ips;
|
||||||
|
|
||||||
@ -264,6 +268,7 @@ sp_perf_source_handle_event (SpPerfCounterEvent *event,
|
|||||||
gpointer user_data)
|
gpointer user_data)
|
||||||
{
|
{
|
||||||
SpPerfSource *self = user_data;
|
SpPerfSource *self = user_data;
|
||||||
|
SpTracepointDesc *tp_desc;
|
||||||
gsize offset;
|
gsize offset;
|
||||||
gint64 time;
|
gint64 time;
|
||||||
|
|
||||||
@ -345,7 +350,21 @@ sp_perf_source_handle_event (SpPerfCounterEvent *event,
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case PERF_RECORD_SAMPLE:
|
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;
|
break;
|
||||||
|
|
||||||
case PERF_RECORD_THROTTLE:
|
case PERF_RECORD_THROTTLE:
|
||||||
@ -381,6 +400,74 @@ sp_perf_get_tracepoint_config (const char *path, gint64 *config)
|
|||||||
return TRUE;
|
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.
|
/* Adds a perf tracepoint event, if it's available.
|
||||||
*
|
*
|
||||||
* These are kernel tracepoints that we want to include in our capture
|
* 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 config;
|
||||||
gint64 id;
|
gint64 id;
|
||||||
int ret;
|
int ret;
|
||||||
|
gint num_fields;
|
||||||
|
|
||||||
if (!sp_perf_get_tracepoint_config(optional_tracepoint->path, &config))
|
if (!sp_perf_get_tracepoint_config(optional_tracepoint->path, &config))
|
||||||
return;
|
return;
|
||||||
@ -409,7 +497,7 @@ sp_perf_source_add_optional_tracepoint (SpPerfSource *self,
|
|||||||
| PERF_SAMPLE_IP
|
| PERF_SAMPLE_IP
|
||||||
| PERF_SAMPLE_TID
|
| PERF_SAMPLE_TID
|
||||||
| PERF_SAMPLE_IDENTIFIER
|
| PERF_SAMPLE_IDENTIFIER
|
||||||
| PERF_SAMPLE_CALLCHAIN
|
| PERF_SAMPLE_RAW
|
||||||
| PERF_SAMPLE_TIME;
|
| PERF_SAMPLE_TIME;
|
||||||
attr.config = config;
|
attr.config = config;
|
||||||
attr.sample_period = 1;
|
attr.sample_period = 1;
|
||||||
@ -434,7 +522,12 @@ sp_perf_source_add_optional_tracepoint (SpPerfSource *self,
|
|||||||
return;
|
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)
|
if (!tp_desc)
|
||||||
{
|
{
|
||||||
close(fd);
|
close(fd);
|
||||||
@ -443,6 +536,13 @@ sp_perf_source_add_optional_tracepoint (SpPerfSource *self,
|
|||||||
|
|
||||||
tp_desc->tp = optional_tracepoint->tp;
|
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
|
/* Here's where we should inspect the /format file to determine how
|
||||||
* to pick fields out of the _RAW data.
|
* to pick fields out of the _RAW data.
|
||||||
*/
|
*/
|
||||||
|
|||||||
Reference in New Issue
Block a user