[Spice-devel] [PATCH v3 3/6] server/red_worker: record to SPICE_WORKER_RECORD_FILENAME

Frediano Ziglio fziglio at redhat.com
Wed Aug 19 00:43:40 PDT 2015


From: Alon Levy <alon at pobox.com>

if the environment variable in the title is set and can be
opened for writing a log of all display commands (no cursor
commands yet) and any QXLWorker calls (particularily primary
create and destroy) will be logged to that file, and possible
to replay using the replay utility introduced later.

For an example file (4 MB download, 300 MB after unpack with xz,
these 300 MB are themselves reduced from 1.2GB using zlib compression
for any chunk):

(old file without a header)
http://annarchy.freedesktop.org/~alon/win7_boot_shutdown.cmd.xz

Signed-off-by: Alon Levy <alon at pobox.com>
Signed-off-by: Marc-André Lureau <marcandre.lureau at gmail.com>
---
 server/red_worker.c | 40 +++++++++++++++++++++++++++++++++++++---
 1 file changed, 37 insertions(+), 3 deletions(-)

diff --git a/server/red_worker.c b/server/red_worker.c
index 1655bbe..7d7858e 100644
--- a/server/red_worker.c
+++ b/server/red_worker.c
@@ -73,6 +73,7 @@
 #include "mjpeg_encoder.h"
 #include "red_memslots.h"
 #include "red_parse_qxl.h"
+#include "red_record_qxl.h"
 #include "jpeg_encoder.h"
 #ifdef USE_LZ4
 #include "lz4_encoder.h"
@@ -166,7 +167,6 @@ static inline red_time_t timespec_to_red_time(struct timespec *time)
     return time->tv_sec * (1000 * 1000 * 1000) + time->tv_nsec;
 }
 
-#if defined(RED_WORKER_STAT) || defined(COMPRESS_STAT)
 static clockid_t clock_id;
 
 typedef unsigned long stat_time_t;
@@ -179,6 +179,7 @@ static stat_time_t stat_now(void)
     return ts.tv_nsec + ts.tv_sec * 1000 * 1000 * 1000;
 }
 
+#if defined(RED_WORKER_STAT) || defined(COMPRESS_STAT)
 double stat_cpu_time_to_sec(stat_time_t time)
 {
     return (double)time / (1000 * 1000 * 1000);
@@ -1034,6 +1035,8 @@ typedef struct RedWorker {
 
     int driver_cap_monitors_config;
     int set_client_capabilities_pending;
+
+    FILE *record_fd;
 } RedWorker;
 
 typedef enum {
@@ -5045,6 +5048,11 @@ static int red_process_commands(RedWorker *worker, uint32_t max_pipe_size, int *
             }
             continue;
         }
+
+        if (worker->record_fd)
+            red_record_qxl_command(worker->record_fd, &worker->mem_slots, ext_cmd,
+                                   stat_now());
+
         stat_inc_counter(worker->command_counter, 1);
         worker->repoll_cmd_ring = 0;
         switch (ext_cmd.cmd.type) {
@@ -11337,6 +11345,11 @@ static void dev_create_primary_surface(RedWorker *worker, uint32_t surface_id,
     if (error) {
         return;
     }
+    if (worker->record_fd) {
+        red_record_dev_input_primary_surface_create(worker->record_fd,
+                    &surface, line_0);
+    }
+
     if (surface.stride < 0) {
         line_0 -= (int32_t)(surface.stride * (surface.height -1));
     }
@@ -11882,6 +11895,13 @@ static void worker_handle_dispatcher_async_done(void *opaque,
     red_dispatcher_async_complete(worker->red_dispatcher, msg_async->cmd);
 }
 
+static void worker_dispatcher_record(void *opaque, uint32_t message_type, void *payload)
+{
+    RedWorker *worker = opaque;
+
+    red_record_event(worker->record_fd, 1, message_type, stat_now());
+}
+
 static void register_callbacks(Dispatcher *dispatcher)
 {
     dispatcher_register_async_done_callback(
@@ -12078,10 +12098,23 @@ static void red_init(RedWorker *worker, WorkerInitData *init_data)
     RedWorkerMessage message;
     Dispatcher *dispatcher;
     int i;
+    const char *record_filename;
 
     spice_assert(sizeof(CursorItem) <= QXL_CURSUR_DEVICE_DATA_SIZE);
 
     memset(worker, 0, sizeof(RedWorker));
+    record_filename = getenv("SPICE_WORKER_RECORD_FILENAME");
+    if (record_filename) {
+        static const char header[] = "SPICE_REPLAY 1\n";
+
+        worker->record_fd = fopen(record_filename, "w+");
+        if (worker->record_fd == NULL) {
+            spice_error("failed to open recording file %s\n", record_filename);
+        }
+	if (fwrite(header, sizeof(header)-1, 1, worker->record_fd) != 1) {
+            spice_error("failed to write replay header");
+        }
+    }
     dispatcher = red_dispatcher_get_dispatcher(init_data->red_dispatcher);
     dispatcher_set_opaque(dispatcher, worker);
     worker->red_dispatcher = init_data->red_dispatcher;
@@ -12089,6 +12122,9 @@ static void red_init(RedWorker *worker, WorkerInitData *init_data)
     worker->id = init_data->id;
     worker->channel = dispatcher_get_recv_fd(dispatcher);
     register_callbacks(dispatcher);
+    if (worker->record_fd) {
+        dispatcher_register_universal_handler(dispatcher, worker_dispatcher_record);
+    }
     worker->pending = init_data->pending;
     worker->cursor_visible = TRUE;
     spice_assert(init_data->num_renderers > 0);
@@ -12170,11 +12206,9 @@ SPICE_GNUC_NORETURN void *red_worker_main(void *arg)
     spice_assert(MAX_PIPE_SIZE > WIDE_CLIENT_ACK_WINDOW &&
            MAX_PIPE_SIZE > NARROW_CLIENT_ACK_WINDOW); //ensure wakeup by ack message
 
-#if  defined(RED_WORKER_STAT) || defined(COMPRESS_STAT)
     if (pthread_getcpuclockid(pthread_self(), &clock_id)) {
         spice_error("pthread_getcpuclockid failed");
     }
-#endif
 
     red_init(worker, (WorkerInitData *)arg);
 
-- 
2.4.3



More information about the Spice-devel mailing list