diff --git a/src/libsysprof-capture/sysprof-capture-cursor.c b/src/libsysprof-capture/sysprof-capture-cursor.c index d3ae153b..bdfc44c0 100644 --- a/src/libsysprof-capture/sysprof-capture-cursor.c +++ b/src/libsysprof-capture/sysprof-capture-cursor.c @@ -167,6 +167,10 @@ sysprof_capture_cursor_foreach (SysprofCaptureCursor *self, delegate = READ_DELEGATE (sysprof_capture_reader_read_metadata); break; + case SYSPROF_CAPTURE_FRAME_FILE_CHUNK: + delegate = READ_DELEGATE (sysprof_capture_reader_read_file); + break; + default: if (!sysprof_capture_reader_skip (self->reader)) return; diff --git a/src/libsysprof-capture/sysprof-capture-reader.c b/src/libsysprof-capture/sysprof-capture-reader.c index 19f471df..640bfd41 100644 --- a/src/libsysprof-capture/sysprof-capture-reader.c +++ b/src/libsysprof-capture/sysprof-capture-reader.c @@ -232,6 +232,17 @@ sysprof_capture_reader_bswap_frame (SysprofCaptureReader *self, } } +static inline void +sysprof_capture_reader_bswap_file_chunk (SysprofCaptureReader *self, + SysprofCaptureFileChunk *file_chunk) +{ + g_assert (self != NULL); + g_assert (file_chunk != NULL); + + if (G_UNLIKELY (self->endian != G_BYTE_ORDER)) + file_chunk->len = GUINT16_SWAP_LE_BE (file_chunk->len); +} + static inline void sysprof_capture_reader_bswap_log (SysprofCaptureReader *self, SysprofCaptureLog *log) @@ -1122,3 +1133,43 @@ sysprof_capture_reader_get_stat (SysprofCaptureReader *self, return self->st_buf_set; } + +const SysprofCaptureFileChunk * +sysprof_capture_reader_read_file (SysprofCaptureReader *self) +{ + SysprofCaptureFileChunk *file_chunk; + + g_assert (self != NULL); + g_assert ((self->pos % SYSPROF_CAPTURE_ALIGN) == 0); + g_assert (self->pos <= self->bufsz); + + if (!sysprof_capture_reader_ensure_space_for (self, sizeof *file_chunk)) + return NULL; + + file_chunk = (SysprofCaptureFileChunk *)(gpointer)&self->buf[self->pos]; + + sysprof_capture_reader_bswap_frame (self, &file_chunk->frame); + + if (file_chunk->frame.type != SYSPROF_CAPTURE_FRAME_FILE_CHUNK) + return NULL; + + if (file_chunk->frame.len < sizeof *file_chunk) + return NULL; + + if (!sysprof_capture_reader_ensure_space_for (self, file_chunk->frame.len)) + return NULL; + + file_chunk = (SysprofCaptureFileChunk *)(gpointer)&self->buf[self->pos]; + + sysprof_capture_reader_bswap_file_chunk (self, file_chunk); + + self->pos += file_chunk->frame.len; + + if ((self->pos % SYSPROF_CAPTURE_ALIGN) != 0) + return NULL; + + /* Ensure trailing \0 in .path */ + file_chunk->path[sizeof file_chunk->path - 1] = 0; + + return file_chunk; +} diff --git a/src/libsysprof-capture/sysprof-capture-reader.h b/src/libsysprof-capture/sysprof-capture-reader.h index cbddc565..f14128e3 100644 --- a/src/libsysprof-capture/sysprof-capture-reader.h +++ b/src/libsysprof-capture/sysprof-capture-reader.h @@ -76,9 +76,11 @@ const SysprofCaptureSample *sysprof_capture_reader_read_sample SYSPROF_AVAILABLE_IN_ALL GHashTable *sysprof_capture_reader_read_jitmap (SysprofCaptureReader *self); SYSPROF_AVAILABLE_IN_ALL -const SysprofCaptureCounterDefine *sysprof_capture_reader_read_counter_define (SysprofCaptureReader *self); +const SysprofCaptureCounterDefine *sysprof_capture_reader_read_counter_define (SysprofCaptureReader *self); SYSPROF_AVAILABLE_IN_ALL -const SysprofCaptureCounterSet *sysprof_capture_reader_read_counter_set (SysprofCaptureReader *self); +const SysprofCaptureCounterSet *sysprof_capture_reader_read_counter_set (SysprofCaptureReader *self); +SYSPROF_AVAILABLE_IN_ALL +const SysprofCaptureFileChunk *sysprof_capture_reader_read_file (SysprofCaptureReader *self); SYSPROF_AVAILABLE_IN_ALL gboolean sysprof_capture_reader_reset (SysprofCaptureReader *self); SYSPROF_AVAILABLE_IN_ALL diff --git a/src/libsysprof-capture/sysprof-capture-types.h b/src/libsysprof-capture/sysprof-capture-types.h index 42817860..59d93c10 100644 --- a/src/libsysprof-capture/sysprof-capture-types.h +++ b/src/libsysprof-capture/sysprof-capture-types.h @@ -79,18 +79,19 @@ typedef union typedef enum { - SYSPROF_CAPTURE_FRAME_TIMESTAMP = 1, - SYSPROF_CAPTURE_FRAME_SAMPLE = 2, - SYSPROF_CAPTURE_FRAME_MAP = 3, - SYSPROF_CAPTURE_FRAME_PROCESS = 4, - SYSPROF_CAPTURE_FRAME_FORK = 5, - SYSPROF_CAPTURE_FRAME_EXIT = 6, - SYSPROF_CAPTURE_FRAME_JITMAP = 7, - SYSPROF_CAPTURE_FRAME_CTRDEF = 8, - SYSPROF_CAPTURE_FRAME_CTRSET = 9, - SYSPROF_CAPTURE_FRAME_MARK = 10, - SYSPROF_CAPTURE_FRAME_METADATA = 11, - SYSPROF_CAPTURE_FRAME_LOG = 12, + SYSPROF_CAPTURE_FRAME_TIMESTAMP = 1, + SYSPROF_CAPTURE_FRAME_SAMPLE = 2, + SYSPROF_CAPTURE_FRAME_MAP = 3, + SYSPROF_CAPTURE_FRAME_PROCESS = 4, + SYSPROF_CAPTURE_FRAME_FORK = 5, + SYSPROF_CAPTURE_FRAME_EXIT = 6, + SYSPROF_CAPTURE_FRAME_JITMAP = 7, + SYSPROF_CAPTURE_FRAME_CTRDEF = 8, + SYSPROF_CAPTURE_FRAME_CTRSET = 9, + SYSPROF_CAPTURE_FRAME_MARK = 10, + SYSPROF_CAPTURE_FRAME_METADATA = 11, + SYSPROF_CAPTURE_FRAME_LOG = 12, + SYSPROF_CAPTURE_FRAME_FILE_CHUNK = 13, } SysprofCaptureFrameType; SYSPROF_ALIGNED_BEGIN(1) @@ -262,6 +263,18 @@ typedef struct } SysprofCaptureLog SYSPROF_ALIGNED_END(1); +SYSPROF_ALIGNED_BEGIN(1) +typedef struct +{ + SysprofCaptureFrame frame; + guint32 is_last : 1; + guint32 padding1 : 15; + guint32 len : 16; + gchar path[256]; + guint8 data[0]; +} SysprofCaptureFileChunk +SYSPROF_ALIGNED_END(1); + G_STATIC_ASSERT (sizeof (SysprofCaptureFileHeader) == 256); G_STATIC_ASSERT (sizeof (SysprofCaptureFrame) == 24); G_STATIC_ASSERT (sizeof (SysprofCaptureMap) == 56); @@ -278,6 +291,7 @@ G_STATIC_ASSERT (sizeof (SysprofCaptureCounterSet) == 32); G_STATIC_ASSERT (sizeof (SysprofCaptureMark) == 96); G_STATIC_ASSERT (sizeof (SysprofCaptureMetadata) == 64); G_STATIC_ASSERT (sizeof (SysprofCaptureLog) == 64); +G_STATIC_ASSERT (sizeof (SysprofCaptureFileChunk) == 284); static inline gint sysprof_capture_address_compare (SysprofCaptureAddress a, diff --git a/src/libsysprof-capture/sysprof-capture-writer-cat.c b/src/libsysprof-capture/sysprof-capture-writer-cat.c index 36e066f8..ac1b110f 100644 --- a/src/libsysprof-capture/sysprof-capture-writer-cat.c +++ b/src/libsysprof-capture/sysprof-capture-writer-cat.c @@ -151,6 +151,24 @@ sysprof_capture_writer_cat (SysprofCaptureWriter *self, break; } + case SYSPROF_CAPTURE_FRAME_FILE_CHUNK: + { + const SysprofCaptureFileChunk *frame; + + if (!(frame = sysprof_capture_reader_read_file (reader))) + goto panic; + + sysprof_capture_writer_add_file (self, + frame->frame.time, + frame->frame.cpu, + frame->frame.pid, + frame->path, + frame->is_last, + frame->data, + frame->len); + break; + } + case SYSPROF_CAPTURE_FRAME_LOG: { const SysprofCaptureLog *frame; diff --git a/src/libsysprof-capture/sysprof-capture-writer.c b/src/libsysprof-capture/sysprof-capture-writer.c index 17925915..0440d784 100644 --- a/src/libsysprof-capture/sysprof-capture-writer.c +++ b/src/libsysprof-capture/sysprof-capture-writer.c @@ -1349,3 +1349,75 @@ sysprof_capture_writer_add_log (SysprofCaptureWriter *self, return TRUE; } + +gboolean +sysprof_capture_writer_add_file (SysprofCaptureWriter *self, + gint64 time, + gint cpu, + gint32 pid, + const gchar *path, + gboolean is_last, + const guint8 *data, + gsize data_len) +{ + SysprofCaptureFileChunk *ev; + gsize len; + + g_assert (self != NULL); + + len = sizeof *ev + data_len; + ev = (SysprofCaptureFileChunk *)sysprof_capture_writer_allocate (self, &len); + if (!ev) + return FALSE; + + sysprof_capture_writer_frame_init (&ev->frame, + len, + cpu, + pid, + time, + SYSPROF_CAPTURE_FRAME_FILE_CHUNK); + + ev->padding1 = 0; + ev->is_last = !!is_last; + ev->len = data_len; + g_strlcpy (ev->path, path, sizeof ev->path); + memcpy (ev->data, data, data_len); + + self->stat.frame_count[SYSPROF_CAPTURE_FRAME_FILE_CHUNK]++; + + return TRUE; +} + +gboolean +sysprof_capture_writer_add_file_fd (SysprofCaptureWriter *self, + gint64 time, + gint cpu, + gint32 pid, + const gchar *path, + gint fd) +{ + guint8 data[(4096*4L) - sizeof(SysprofCaptureFileChunk)]; + + g_assert (self != NULL); + + for (;;) + { + gboolean is_last; + gssize n_read; + + again: + n_read = read (fd, data, sizeof data); + if (n_read < 0 && errno == EAGAIN) + goto again; + + is_last = n_read == 0; + + if (!sysprof_capture_writer_add_file (self, time, cpu, pid, path, is_last, data, n_read)) + return FALSE; + + if (is_last) + break; + } + + return TRUE; +} diff --git a/src/libsysprof-capture/sysprof-capture-writer.h b/src/libsysprof-capture/sysprof-capture-writer.h index 4aa94493..1cc31360 100644 --- a/src/libsysprof-capture/sysprof-capture-writer.h +++ b/src/libsysprof-capture/sysprof-capture-writer.h @@ -45,6 +45,22 @@ SYSPROF_AVAILABLE_IN_ALL void sysprof_capture_writer_stat (SysprofCaptureWriter *self, SysprofCaptureStat *stat); SYSPROF_AVAILABLE_IN_ALL +gboolean sysprof_capture_writer_add_file (SysprofCaptureWriter *self, + gint64 time, + gint cpu, + gint32 pid, + const gchar *path, + gboolean is_last, + const guint8 *data, + gsize data_len); +SYSPROF_AVAILABLE_IN_ALL +gboolean sysprof_capture_writer_add_file_fd (SysprofCaptureWriter *self, + gint64 time, + gint cpu, + gint32 pid, + const gchar *path, + gint fd); +SYSPROF_AVAILABLE_IN_ALL gboolean sysprof_capture_writer_add_map (SysprofCaptureWriter *self, gint64 time, gint cpu, diff --git a/src/tests/test-capture.c b/src/tests/test-capture.c index ef65d21e..3f603252 100644 --- a/src/tests/test-capture.c +++ b/src/tests/test-capture.c @@ -20,9 +20,12 @@ #include "config.h" +#include #include #include #include +#include +#include static void test_reader_basic (void) @@ -708,6 +711,73 @@ test_reader_writer_metadata (void) g_unlink ("metadata1.syscap"); } +static void +test_reader_writer_file (void) +{ + g_autofree gchar *data = NULL; + GByteArray *buf = g_byte_array_new (); + SysprofCaptureWriter *writer; + SysprofCaptureReader *reader; + SysprofCaptureFrameType type; + GError *error = NULL; + gssize len; + gsize data_len; + guint count = 0; + gint fd; + gint r; + + writer = sysprof_capture_writer_new ("file1.syscap", 0); + fd = g_open ("/proc/kallsyms", O_RDONLY); + + r = g_file_get_contents ("/proc/kallsyms", &data, &data_len, NULL); + g_assert_true (r); + + len = lseek (fd, SEEK_END, 0); + g_assert_cmpint (len, >, 0); + + lseek (fd, SEEK_SET, 0); + sysprof_capture_writer_add_file_fd (writer, SYSPROF_CAPTURE_CURRENT_TIME, -1, -1, "/proc/kallsyms", fd); + + lseek (fd, SEEK_SET, 0); + sysprof_capture_writer_add_file_fd (writer, SYSPROF_CAPTURE_CURRENT_TIME, -1, -1, "/proc/kallsyms", fd); + + close (fd); + + g_clear_pointer (&writer, sysprof_capture_writer_unref); + + reader = sysprof_capture_reader_new ("file1.syscap", &error); + g_assert_no_error (error); + g_assert (reader != NULL); + + while (count < 2) + { + const SysprofCaptureFileChunk *file; + + r = sysprof_capture_reader_peek_type (reader, &type); + g_assert_true (r); + g_assert_cmpint (type, ==, SYSPROF_CAPTURE_FRAME_FILE_CHUNK); + + file = sysprof_capture_reader_read_file (reader); + g_assert_nonnull (file); + g_assert_cmpstr (file->path, ==, "/proc/kallsyms"); + + if (count == 0) + g_byte_array_append (buf, file->data, file->len); + + count += file->is_last; + } + + g_assert_cmpint (0, ==, memcmp (data, buf->data, data_len)); + + r = sysprof_capture_reader_peek_type (reader, &type); + g_assert_cmpint (r, ==, FALSE); + + g_clear_pointer (&reader, sysprof_capture_reader_unref); + g_clear_pointer (&buf, g_byte_array_unref); + + g_unlink ("file1.syscap"); +} + int main (int argc, char *argv[]) @@ -720,5 +790,6 @@ main (int argc, g_test_add_func ("/SysprofCapture/ReaderWriter/log", test_reader_writer_log); g_test_add_func ("/SysprofCapture/ReaderWriter/mark", test_reader_writer_mark); g_test_add_func ("/SysprofCapture/ReaderWriter/metadata", test_reader_writer_metadata); + g_test_add_func ("/SysprofCapture/ReaderWriter/file", test_reader_writer_file); return g_test_run (); } diff --git a/src/tools/sysprof-dump.c b/src/tools/sysprof-dump.c index 77ec7c37..513f993a 100644 --- a/src/tools/sysprof-dump.c +++ b/src/tools/sysprof-dump.c @@ -139,6 +139,21 @@ main (gint argc, break; } + case SYSPROF_CAPTURE_FRAME_FILE_CHUNK: + { + const SysprofCaptureFileChunk *file_chunk = sysprof_capture_reader_read_file (reader); + gdouble ptime = (file_chunk->frame.time - begin_time) / (gdouble)NSEC_PER_SEC; + + g_print ("FILE_CHUNK: pid=%d time=%"G_GINT64_FORMAT" (%lf)\n" + " path = %s\n" + " is_last = %d\n" + " bytes = %d\n", + file_chunk->frame.pid, file_chunk->frame.time, ptime, + file_chunk->path, file_chunk->is_last, file_chunk->len); + + break; + } + case SYSPROF_CAPTURE_FRAME_MARK: { const SysprofCaptureMark *mark = sysprof_capture_reader_read_mark (reader);