diff --git a/lib/sp-capture-reader.c b/lib/sp-capture-reader.c index f5d4544f..69582b79 100644 --- a/lib/sp-capture-reader.c +++ b/lib/sp-capture-reader.c @@ -539,7 +539,7 @@ sp_capture_reader_read_sample (SpCaptureReader *self) sample = (SpCaptureSample *)(gpointer)&self->buf[self->pos]; - if (self->endian != G_BYTE_ORDER) + if (G_UNLIKELY (self->endian != G_BYTE_ORDER)) { guint i; @@ -552,6 +552,105 @@ sp_capture_reader_read_sample (SpCaptureReader *self) return sample; } +const SpCaptureFrameCounterDefine * +sp_capture_reader_read_counter_define (SpCaptureReader *self) +{ + SpCaptureFrameCounterDefine *def; + + g_assert (self != NULL); + g_assert ((self->pos % SP_CAPTURE_ALIGN) == 0); + g_assert (self->pos <= self->bufsz); + + if (!sp_capture_reader_ensure_space_for (self, sizeof *def)) + return NULL; + + def = (SpCaptureFrameCounterDefine *)(gpointer)&self->buf[self->pos]; + + if (def->frame.type != SP_CAPTURE_FRAME_CTRDEF) + return NULL; + + if (def->frame.len < sizeof *def) + return NULL; + + if (G_UNLIKELY (self->endian != G_BYTE_ORDER)) + def->n_counters = GUINT16_SWAP_LE_BE (def->n_counters); + + if (def->frame.len < (sizeof *def + (sizeof (SpCaptureFrameCounterDefine) * def->n_counters))) + return NULL; + + if (!sp_capture_reader_ensure_space_for (self, def->frame.len)) + return NULL; + + def = (SpCaptureFrameCounterDefine *)(gpointer)&self->buf[self->pos]; + + if (G_UNLIKELY (self->endian != G_BYTE_ORDER)) + { + guint i; + + for (i = 0; i < def->n_counters; i++) + { + def->counters[i].id = GUINT32_SWAP_LE_BE (def->counters[i].id); + def->counters[i].value = GUINT64_SWAP_LE_BE (def->counters[i].value); + } + } + + self->pos += def->frame.len; + + return def; +} + +const SpCaptureFrameCounterSet * +sp_capture_reader_read_counter_set (SpCaptureReader *self) +{ + SpCaptureFrameCounterSet *set; + + g_assert (self != NULL); + g_assert ((self->pos % SP_CAPTURE_ALIGN) == 0); + g_assert (self->pos <= self->bufsz); + + if (!sp_capture_reader_ensure_space_for (self, sizeof *set)) + return NULL; + + set = (SpCaptureFrameCounterSet *)(gpointer)&self->buf[self->pos]; + + if (set->frame.type != SP_CAPTURE_FRAME_CTRSET) + return NULL; + + if (set->frame.len < sizeof *set) + return NULL; + + if (self->endian != G_BYTE_ORDER) + set->n_values = GUINT16_SWAP_LE_BE (set->n_values); + + if (set->frame.len < (sizeof *set + (sizeof (SpCaptureCounterValues) * set->n_values))) + return NULL; + + if (!sp_capture_reader_ensure_space_for (self, set->frame.len)) + return NULL; + + set = (SpCaptureFrameCounterSet *)(gpointer)&self->buf[self->pos]; + + if (G_UNLIKELY (self->endian != G_BYTE_ORDER)) + { + guint i; + + for (i = 0; i < set->n_values; i++) + { + guint j; + + for (j = 0; j < G_N_ELEMENTS (set->values[0].values); i++) + { + set->values[i].ids[j] = GUINT32_SWAP_LE_BE (set->values[i].ids[j]); + set->values[i].values[j] = GUINT64_SWAP_LE_BE (set->values[i].values[j]); + } + } + } + + self->pos += set->frame.len; + + return set; +} + gboolean sp_capture_reader_reset (SpCaptureReader *self) { diff --git a/lib/sp-capture-reader.h b/lib/sp-capture-reader.h index 14d93c85..f703b3df 100644 --- a/lib/sp-capture-reader.h +++ b/lib/sp-capture-reader.h @@ -25,31 +25,33 @@ G_BEGIN_DECLS typedef struct _SpCaptureReader SpCaptureReader; -SpCaptureReader *sp_capture_reader_new (const gchar *filename, - GError **error); -SpCaptureReader *sp_capture_reader_new_from_fd (int fd, - GError **error); -SpCaptureReader *sp_capture_reader_ref (SpCaptureReader *self); -void sp_capture_reader_unref (SpCaptureReader *self); -const gchar *sp_capture_reader_get_filename (SpCaptureReader *self); -const gchar *sp_capture_reader_get_time (SpCaptureReader *self); -gboolean sp_capture_reader_skip (SpCaptureReader *self); -gboolean sp_capture_reader_peek_type (SpCaptureReader *self, - SpCaptureFrameType *type); -const SpCaptureMap *sp_capture_reader_read_map (SpCaptureReader *self); -const SpCaptureExit *sp_capture_reader_read_exit (SpCaptureReader *self); -const SpCaptureFork *sp_capture_reader_read_fork (SpCaptureReader *self); -const SpCaptureTimestamp *sp_capture_reader_read_timestamp (SpCaptureReader *self); -const SpCaptureProcess *sp_capture_reader_read_process (SpCaptureReader *self); -const SpCaptureSample *sp_capture_reader_read_sample (SpCaptureReader *self); -GHashTable *sp_capture_reader_read_jitmap (SpCaptureReader *self); -gboolean sp_capture_reader_reset (SpCaptureReader *self); -gboolean sp_capture_reader_splice (SpCaptureReader *self, - SpCaptureWriter *dest, - GError **error); -gboolean sp_capture_reader_save_as (SpCaptureReader *self, - const gchar *filename, - GError **error); +SpCaptureReader *sp_capture_reader_new (const gchar *filename, + GError **error); +SpCaptureReader *sp_capture_reader_new_from_fd (int fd, + GError **error); +SpCaptureReader *sp_capture_reader_ref (SpCaptureReader *self); +void sp_capture_reader_unref (SpCaptureReader *self); +const gchar *sp_capture_reader_get_filename (SpCaptureReader *self); +const gchar *sp_capture_reader_get_time (SpCaptureReader *self); +gboolean sp_capture_reader_skip (SpCaptureReader *self); +gboolean sp_capture_reader_peek_type (SpCaptureReader *self, + SpCaptureFrameType *type); +const SpCaptureMap *sp_capture_reader_read_map (SpCaptureReader *self); +const SpCaptureExit *sp_capture_reader_read_exit (SpCaptureReader *self); +const SpCaptureFork *sp_capture_reader_read_fork (SpCaptureReader *self); +const SpCaptureTimestamp *sp_capture_reader_read_timestamp (SpCaptureReader *self); +const SpCaptureProcess *sp_capture_reader_read_process (SpCaptureReader *self); +const SpCaptureSample *sp_capture_reader_read_sample (SpCaptureReader *self); +GHashTable *sp_capture_reader_read_jitmap (SpCaptureReader *self); +const SpCaptureFrameCounterDefine *sp_capture_reader_read_counter_define (SpCaptureReader *self); +const SpCaptureFrameCounterSet *sp_capture_reader_read_counter_set (SpCaptureReader *self); +gboolean sp_capture_reader_reset (SpCaptureReader *self); +gboolean sp_capture_reader_splice (SpCaptureReader *self, + SpCaptureWriter *dest, + GError **error); +gboolean sp_capture_reader_save_as (SpCaptureReader *self, + const gchar *filename, + GError **error); #ifndef SP_DISABLE_GOBJECT # define SP_TYPE_CAPTURE_READER (sp_capture_reader_get_type()) diff --git a/lib/sp-capture-types.h b/lib/sp-capture-types.h index 7bb3ab76..ce661287 100644 --- a/lib/sp-capture-types.h +++ b/lib/sp-capture-types.h @@ -54,6 +54,9 @@ typedef enum SP_CAPTURE_FRAME_FORK = 5, SP_CAPTURE_FRAME_EXIT = 6, SP_CAPTURE_FRAME_JITMAP = 7, + SP_CAPTURE_FRAME_CTRDEF = 8, + SP_CAPTURE_FRAME_CTRADD = 9, + SP_CAPTURE_FRAME_CTRSET = 10, } SpCaptureFrameType; #pragma pack(push, 1) @@ -126,6 +129,43 @@ typedef struct SpCaptureFrame frame; } SpCaptureTimestamp; +typedef struct +{ + gchar category[32]; + gchar name[32]; + gchar description[52]; + guint32 id : 24; + guint8 type; + gint64 value; +} SpCaptureCounter; + +typedef struct +{ + SpCaptureFrame frame; + guint16 n_counters; + guint64 padding : 48; + SpCaptureCounter counters[0]; +} SpCaptureFrameCounterDefine; + +typedef struct +{ + /* + * 96 bytes might seem a bit odd, but the counter frame header is 32 + * bytes. So this makes a nice 2-cacheline aligned size which is + * useful when the number of counters is rather small. + */ + guint32 ids[8]; + gint64 values[8]; +} SpCaptureCounterValues; + +typedef struct +{ + SpCaptureFrame frame; + guint16 n_values; + guint64 padding : 48; + SpCaptureCounterValues values[0]; +} SpCaptureFrameCounterSet; + #pragma pack(pop) G_STATIC_ASSERT (sizeof (SpCaptureFileHeader) == 256); @@ -137,6 +177,10 @@ G_STATIC_ASSERT (sizeof (SpCaptureSample) == 32); G_STATIC_ASSERT (sizeof (SpCaptureFork) == 28); G_STATIC_ASSERT (sizeof (SpCaptureExit) == 24); G_STATIC_ASSERT (sizeof (SpCaptureTimestamp) == 24); +G_STATIC_ASSERT (sizeof (SpCaptureCounter) == 128); +G_STATIC_ASSERT (sizeof (SpCaptureCounterValues) == 96); +G_STATIC_ASSERT (sizeof (SpCaptureFrameCounterDefine) == 32); +G_STATIC_ASSERT (sizeof (SpCaptureFrameCounterSet) == 32); static inline gint sp_capture_address_compare (SpCaptureAddress a, diff --git a/lib/sp-capture-writer.c b/lib/sp-capture-writer.c index f0f725f2..794fa383 100644 --- a/lib/sp-capture-writer.c +++ b/lib/sp-capture-writer.c @@ -176,6 +176,10 @@ static inline gboolean sp_capture_writer_ensure_space_for (SpCaptureWriter *self, gsize len) { + /* Check for max frame size */ + if (len > G_MAXUSHORT) + return FALSE; + if ((self->len - self->pos) < len) { if (!sp_capture_writer_flush_data (self)) @@ -952,3 +956,124 @@ sp_capture_writer_stat (SpCaptureWriter *self, *stat = self->stat; } + +gboolean +sp_capture_writer_define_counters (SpCaptureWriter *self, + gint64 time, + gint cpu, + GPid pid, + const SpCaptureCounter *counters, + guint n_counters) +{ + SpCaptureFrameCounterDefine *def; + gsize len; + guint i; + + g_assert (self != NULL); + g_assert (counters != NULL); + g_assert ((self->pos % SP_CAPTURE_ALIGN) == 0); + + if (n_counters == 0) + return TRUE; + + len = sizeof *def + (sizeof *counters * n_counters); + + sp_capture_writer_realign (&len); + + if (!sp_capture_writer_ensure_space_for (self, len)) + return FALSE; + + g_assert ((self->pos % SP_CAPTURE_ALIGN) == 0); + + def = (SpCaptureFrameCounterDefine *)&self->buf[self->pos]; + def->frame.len = len; + def->frame.cpu = cpu; + def->frame.pid = pid; + def->frame.time = time; + def->frame.type = SP_CAPTURE_FRAME_CTRDEF; + def->frame.padding = 0; + def->padding = 0; + def->n_counters = n_counters; + + for (i = 0; i < n_counters; i++) + def->counters[i] = counters[i]; + + self->pos += def->frame.len; + + g_assert ((self->pos % SP_CAPTURE_ALIGN) == 0); + + self->stat.frame_count[SP_CAPTURE_FRAME_CTRDEF]++; + + return TRUE; +} + +gboolean +sp_capture_writer_set_counters (SpCaptureWriter *self, + gint64 time, + gint cpu, + GPid pid, + const guint *counters_ids, + const gint64 *values, + guint n_counters) +{ + SpCaptureFrameCounterSet *set; + gsize len; + guint n_groups; + guint group; + guint field; + guint i; + + g_assert (self != NULL); + g_assert (counters_ids != NULL); + g_assert (values != NULL || !n_counters); + g_assert ((self->pos % SP_CAPTURE_ALIGN) == 0); + + if (n_counters == 0) + return TRUE; + + /* Determine how many value groups we need */ + n_groups = n_counters / G_N_ELEMENTS (set->values[0].values); + if ((n_groups * G_N_ELEMENTS (set->values[0].values)) != n_counters) + n_groups++; + + len = sizeof *set + (n_groups * sizeof (SpCaptureCounterValues)); + + sp_capture_writer_realign (&len); + + if (!sp_capture_writer_ensure_space_for (self, len)) + return FALSE; + + g_assert ((self->pos % SP_CAPTURE_ALIGN) == 0); + + set = (SpCaptureFrameCounterSet *)&self->buf[self->pos]; + set->frame.len = len; + set->frame.cpu = cpu; + set->frame.pid = pid; + set->frame.time = time; + set->frame.type = SP_CAPTURE_FRAME_CTRSET; + set->frame.padding = 0; + set->padding = 0; + set->n_values = n_groups; + + for (i = 0, group = 0, field = 0; i < n_counters; i++) + { + set->values[group].ids[field] = counters_ids[i]; + set->values[group].values[field] = values[i]; + + field++; + + if (field == G_N_ELEMENTS (set->values[0].values)) + { + field = 0; + group++; + } + } + + self->pos += set->frame.len; + + g_assert ((self->pos % SP_CAPTURE_ALIGN) == 0); + + self->stat.frame_count[SP_CAPTURE_FRAME_CTRSET]++; + + return TRUE; +} diff --git a/lib/sp-capture-writer.h b/lib/sp-capture-writer.h index 1846fd1f..81de7dee 100644 --- a/lib/sp-capture-writer.h +++ b/lib/sp-capture-writer.h @@ -81,6 +81,19 @@ gboolean sp_capture_writer_add_timestamp (SpCaptureWriter * gint64 time, gint cpu, GPid pid); +gboolean sp_capture_writer_define_counters (SpCaptureWriter *self, + gint64 time, + gint cpu, + GPid pid, + const SpCaptureCounter *counters, + guint n_counters); +gboolean sp_capture_writer_set_counters (SpCaptureWriter *self, + gint64 time, + gint cpu, + GPid pid, + const guint *counters_ids, + const gint64 *values, + guint n_counters); gboolean sp_capture_writer_flush (SpCaptureWriter *self); gboolean sp_capture_writer_save_as (SpCaptureWriter *self, const gchar *filename, diff --git a/tests/test-capture.c b/tests/test-capture.c index 7ddb956c..f353a93c 100644 --- a/tests/test-capture.c +++ b/tests/test-capture.c @@ -183,6 +183,89 @@ test_reader_basic (void) g_assert_cmpint (ex->child_pid, ==, i); } + { + SpCaptureCounter counters[10]; + + t = SP_CAPTURE_CURRENT_TIME; + + for (i = 0; i < G_N_ELEMENTS (counters); i++) + { + g_snprintf (counters[i].category, sizeof counters[i].category, "cat%d", i); + g_snprintf (counters[i].name, sizeof counters[i].name, "name%d", i); + g_snprintf (counters[i].description, sizeof counters[i].description, "desc%d", i); + counters[i].id = i + 1; + counters[i].type = 0; + counters[i].value = i * G_GINT64_CONSTANT (100000000000); + } + + r = sp_capture_writer_define_counters (writer, t, -1, -1, counters, G_N_ELEMENTS (counters)); + g_assert_cmpint (r, ==, TRUE); + } + + sp_capture_writer_flush (writer); + + { + const SpCaptureFrameCounterDefine *def; + + def = sp_capture_reader_read_counter_define (reader); + g_assert (def != NULL); + g_assert_cmpint (def->n_counters, ==, 10); + + for (i = 0; i < def->n_counters; i++) + { + g_autofree gchar *cat = g_strdup_printf ("cat%d", i); + g_autofree gchar *name = g_strdup_printf ("name%d", i); + g_autofree gchar *desc = g_strdup_printf ("desc%d", i); + + g_assert_cmpstr (def->counters[i].category, ==, cat); + g_assert_cmpstr (def->counters[i].name, ==, name); + g_assert_cmpstr (def->counters[i].description, ==, desc); + } + } + + for (i = 0; i < 1000; i++) + { + gint ids[10] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }; + gint64 values[10] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }; + + r = sp_capture_writer_set_counters (writer, t, -1, -1, ids, values, G_N_ELEMENTS (values)); + g_assert_cmpint (r, ==, TRUE); + } + + sp_capture_writer_flush (writer); + + for (i = 0; i < 1000; i++) + { + const SpCaptureFrameCounterSet *set; + + set = sp_capture_reader_read_counter_set (reader); + g_assert (set != NULL); + + /* 8 per chunk */ + g_assert_cmpint (set->n_values, ==, 2); + + g_assert_cmpint (1, ==, set->values[0].ids[0]); + g_assert_cmpint (2, ==, set->values[0].ids[1]); + g_assert_cmpint (3, ==, set->values[0].ids[2]); + g_assert_cmpint (4, ==, set->values[0].ids[3]); + g_assert_cmpint (5, ==, set->values[0].ids[4]); + g_assert_cmpint (6, ==, set->values[0].ids[5]); + g_assert_cmpint (7, ==, set->values[0].ids[6]); + g_assert_cmpint (8, ==, set->values[0].ids[7]); + g_assert_cmpint (9, ==, set->values[1].ids[0]); + g_assert_cmpint (10, ==, set->values[1].ids[1]); + g_assert_cmpint (1, ==, set->values[0].values[0]); + g_assert_cmpint (2, ==, set->values[0].values[1]); + g_assert_cmpint (3, ==, set->values[0].values[2]); + g_assert_cmpint (4, ==, set->values[0].values[3]); + g_assert_cmpint (5, ==, set->values[0].values[4]); + g_assert_cmpint (6, ==, set->values[0].values[5]); + g_assert_cmpint (7, ==, set->values[0].values[6]); + g_assert_cmpint (8, ==, set->values[0].values[7]); + g_assert_cmpint (9, ==, set->values[1].values[0]); + g_assert_cmpint (10, ==, set->values[1].values[1]); + } + for (i = 0; i < 1000; i++) { SpCaptureAddress addr;