[Spice-devel] [RfC 3/4] server/red_worker: record to SPICE_WORKER_RECORD_FILENAME

Alon Levy alevy at redhat.com
Fri Jul 1 19:49:44 PDT 2011


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):

http://annarchy.freedesktop.org/~alon/win7_boot_shutdown.cmd.xz
---
 server/red_worker.c |   63 +++++++++++++++++++++++++++++++++++++++++++++++++++
 1 files changed, 63 insertions(+), 0 deletions(-)

diff --git a/server/red_worker.c b/server/red_worker.c
index 4a3f78a..4964942 100644
--- a/server/red_worker.c
+++ b/server/red_worker.c
@@ -54,6 +54,7 @@
 #include "mjpeg_encoder.h"
 #include "red_memslots.h"
 #include "red_parse_qxl.h"
+#include "red_record_qxl.h"
 #include "jpeg_encoder.h"
 #include "rect.h"
 #include "marshaller.h"
@@ -898,6 +899,8 @@ typedef struct RedWorker {
     uint64_t *wakeup_counter;
     uint64_t *command_counter;
 #endif
+    FILE *record_fd;
+    clockid_t record_clock_id;
 } RedWorker;
 
 typedef enum {
@@ -4223,6 +4226,44 @@ static int red_process_cursor(RedWorker *worker, uint32_t max_pipe_size, int *ri
     return n;
 }
 
+static void red_record_event(RedWorker *worker, int what, uint32_t type)
+{
+    struct timespec ts;
+    static int counter = 0;
+
+    // TODO: record the size of the packet in the header. This would make
+    // navigating it much faster (well, I can add an index while I'm at it..)
+    // and make it trivial to get a histogram from a file.
+    // But to implement that I would need some temporary buffer for each event.
+    // (that can be up to VGA_FRAMEBUFFER large)
+    clock_gettime(worker->record_clock_id, &ts);
+    fprintf(worker->record_fd, "event %d %d %d %ld\n", counter++, what, type,
+        ts.tv_nsec + ts.tv_sec * 1000 * 1000 * 1000);
+}
+
+static void red_record_command(RedWorker *worker, QXLCommandExt ext_cmd)
+{
+    red_record_event(worker, 0, ext_cmd.cmd.type);
+    switch (ext_cmd.cmd.type) {
+    case QXL_CMD_DRAW:
+        red_record_drawable(worker->record_fd, &worker->mem_slots, ext_cmd.group_id,
+                            ext_cmd.cmd.data, ext_cmd.flags);
+        break;
+    case QXL_CMD_UPDATE:
+        red_record_update_cmd(worker->record_fd, &worker->mem_slots, ext_cmd.group_id,
+                           ext_cmd.cmd.data);
+        break;
+    case QXL_CMD_MESSAGE:
+        red_record_message(worker->record_fd, &worker->mem_slots, ext_cmd.group_id,
+                           ext_cmd.cmd.data);
+        break;
+    case QXL_CMD_SURFACE:
+        red_record_surface_cmd(worker->record_fd, &worker->mem_slots, ext_cmd.group_id,
+                               ext_cmd.cmd.data);
+        break;
+    }
+}
+
 static int red_process_commands(RedWorker *worker, uint32_t max_pipe_size, int *ring_is_empty)
 {
     QXLCommandExt ext_cmd;
@@ -4245,6 +4286,9 @@ static int red_process_commands(RedWorker *worker, uint32_t max_pipe_size, int *
             }
             continue;
         }
+        if (worker->record_fd) {
+            red_record_command(worker, ext_cmd);
+        }
         stat_inc_counter(worker->command_counter, 1);
         worker->repoll_cmd_ring = 0;
         switch (ext_cmd.cmd.type) {
@@ -9621,6 +9665,10 @@ static inline void handle_dev_create_primary_surface(RedWorker *worker)
     if (worker->cursor_channel) {
         red_channel_pipe_add_type(&worker->cursor_channel->common.base, PIPE_ITEM_TYPE_CURSOR_INIT);
     }
+    if (worker->record_fd) {
+        red_record_dev_input_primary_surface_create(worker->record_fd,
+                    &surface, line_0);
+    }
 
     message = RED_WORKER_MESSAGE_READY;
     write_message(worker->channel, &message);
@@ -9680,6 +9728,10 @@ static void handle_dev_input(EventListener *listener, uint32_t events)
 
     read_message(worker->channel, &message);
 
+    if (worker->record_fd) {
+        red_record_event(worker, 1, message);
+    }
+
     switch (message) {
     case RED_WORKER_MESSAGE_UPDATE:
         handle_dev_update(worker);
@@ -9988,6 +10040,7 @@ static void red_init(RedWorker *worker, WorkerInitData *init_data)
 void *red_worker_main(void *arg)
 {
     RedWorker worker;
+    const char *record_filename;
 
     red_printf("begin");
     ASSERT(MAX_PIPE_SIZE > WIDE_CLIENT_ACK_WINDOW &&
@@ -10004,6 +10057,16 @@ void *red_worker_main(void *arg)
     red_init_lz(&worker);
     red_init_jpeg(&worker);
     red_init_zlib(&worker);
+    record_filename = getenv("SPICE_WORKER_RECORD_FILENAME");
+    if (record_filename) {
+        worker.record_fd = fopen(record_filename, "w+");
+        if (worker.record_fd == NULL) {
+            red_error("failed to open recording file %s\n", record_filename);
+        }
+        if (pthread_getcpuclockid(pthread_self(), &worker.record_clock_id)) {
+            red_error("pthread_getcpuclockid failed");
+        }
+    }
     worker.epoll_timeout = INF_EPOLL_WAIT;
     for (;;) {
         struct epoll_event events[MAX_EPOLL_SOURCES];
-- 
1.7.5.4



More information about the Spice-devel mailing list