diff --git a/src/libsysprof-profile/sysprof-recording-private.h b/src/libsysprof-profile/sysprof-recording-private.h index 38f0354d..ecc2eaa7 100644 --- a/src/libsysprof-profile/sysprof-recording-private.h +++ b/src/libsysprof-profile/sysprof-recording-private.h @@ -20,6 +20,8 @@ #pragma once +#include + #include "sysprof-instrument.h" #include "sysprof-recording.h" #include "sysprof-spawnable.h" @@ -32,5 +34,8 @@ SysprofRecording *_sysprof_recording_new (SysprofCaptureWriter *w void _sysprof_recording_start (SysprofRecording *self); SysprofCaptureWriter *_sysprof_recording_writer (SysprofRecording *self); SysprofSpawnable *_sysprof_recording_get_spawnable (SysprofRecording *self); +DexFuture *_sysprof_recording_add_file (SysprofRecording *self, + const char *path, + gboolean compress); G_END_DECLS diff --git a/src/libsysprof-profile/sysprof-recording.c b/src/libsysprof-profile/sysprof-recording.c index 1dc4885d..82545cfe 100644 --- a/src/libsysprof-profile/sysprof-recording.c +++ b/src/libsysprof-profile/sysprof-recording.c @@ -296,3 +296,140 @@ _sysprof_recording_writer (SysprofRecording *self) return self->writer; } + +typedef struct _AddFile +{ + SysprofCaptureWriter *writer; + char *path; + guint compress : 1; +} AddFile; + +static void +add_file_free (AddFile *add_file) +{ + g_clear_pointer (&add_file->writer, sysprof_capture_writer_unref); + g_clear_pointer (&add_file->path, g_free); + g_free (add_file); +} + +static DexFuture * +sysprof_recording_add_file_fiber (gpointer user_data) +{ + AddFile *add_file = user_data; + g_autoptr(GInputStream) input = NULL; + g_autoptr(GOutputStream) memory_stream = NULL; + g_autoptr(GOutputStream) zlib_stream = NULL; + g_autoptr(GError) error = NULL; + g_autoptr(GBytes) bytes = NULL; + g_autoptr(GFile) proc = NULL; + g_autoptr(GFile) file = NULL; + g_autofree char *dest_path = NULL; + const guint8 *data = NULL; + GOutputStream *output; + gsize len; + + g_assert (add_file != NULL); + g_assert (add_file->path != NULL); + g_assert (add_file->writer != NULL); + + /* If we are compressing the file, store it as ".gz" in the capture + * so that the reader knows to decompress it automatically. + */ + dest_path = add_file->compress ? g_strdup_printf ("%s.gz", add_file->path) + : g_strdup (add_file->path); + + file = g_file_new_for_path (add_file->path); + proc = g_file_new_for_path ("/proc"); + + /* If the file has a prefix of `/proc/` then we need to request the + * file from sysprofd as our user is not guaranteed to be able to + * read the file. Even if we can open the file, we may get data that + * has been redacted (such as /proc/kallsyms). + * + * With `/proc/kallsyms` it's even worse in that if we get an FD back + * from sysprofd and read it from our process, it *too* will be redacted. + * That leaves us with the only option of letting sysprofd read the file + * and transfer the contents to our process over D-Bus. + * + * We use g_file_has_prefix() for this as it will canonicalize the paths + * of #GFile rather than us having to be careful here. + */ + //if (g_file_has_prefix (file, proc)) + //{ + //} + //else + { + if (!(input = dex_await_object (dex_file_read (file, 0), &error))) + return dex_future_new_for_error (g_steal_pointer (&error)); + } + + g_assert (input != NULL); + g_assert (G_IS_INPUT_STREAM (input)); + + output = memory_stream = g_memory_output_stream_new_resizable (); + + if (add_file->compress) + { + g_autoptr(GZlibCompressor) compressor = g_zlib_compressor_new (G_ZLIB_COMPRESSOR_FORMAT_GZIP, 6); + output = zlib_stream = g_converter_output_stream_new (memory_stream, G_CONVERTER (compressor)); + } + + g_assert (output != NULL); + g_assert (G_IS_OUTPUT_STREAM (output)); + g_assert (G_IS_MEMORY_OUTPUT_STREAM (output) || G_IS_CONVERTER_OUTPUT_STREAM (output)); + g_assert (G_IS_MEMORY_OUTPUT_STREAM (memory_stream)); + g_assert (!zlib_stream || G_IS_CONVERTER_OUTPUT_STREAM (zlib_stream)); + + if (!dex_await (dex_output_stream_splice (output, + input, + (G_OUTPUT_STREAM_SPLICE_CLOSE_SOURCE | + G_OUTPUT_STREAM_SPLICE_CLOSE_TARGET), + 0), + &error)) + return dex_future_new_for_error (g_steal_pointer (&error)); + + bytes = g_memory_output_stream_steal_as_bytes (G_MEMORY_OUTPUT_STREAM (memory_stream)); + data = g_bytes_get_data (bytes, &len); + + while (len > 0) + { + gsize to_write = MIN (len, 4096*8); + + if (!sysprof_capture_writer_add_file (add_file->writer, + SYSPROF_CAPTURE_CURRENT_TIME, + -1, + -1, + dest_path, + to_write == len, + data, + to_write)) + break; + + len -= to_write; + data += to_write; + } + + return dex_future_new_for_boolean (TRUE); +} + +DexFuture * +_sysprof_recording_add_file (SysprofRecording *self, + const char *path, + gboolean compress) +{ + g_autoptr(GFile) file = NULL; + AddFile *add_file; + + g_return_val_if_fail (SYSPROF_IS_RECORDING (self), NULL); + g_return_val_if_fail (path != NULL, NULL); + + add_file = g_new0 (AddFile, 1); + add_file->writer = sysprof_capture_writer_ref (self->writer); + add_file->path = g_strdup (path); + add_file->compress = !!compress; + + return dex_scheduler_spawn (NULL, 0, + sysprof_recording_add_file_fiber, + g_steal_pointer (&add_file), + (GDestroyNotify)add_file_free); +}