diff mbox

[1/4] qemu-char: Add new char backend CirMemCharDriver

Message ID 1358759628-18278-2-git-send-email-lilei@linux.vnet.ibm.com
State New
Headers show

Commit Message

Lei Li Jan. 21, 2013, 9:13 a.m. UTC
Signed-off-by: Lei Li <lilei@linux.vnet.ibm.com>
---
 qemu-char.c     |  134 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
 qemu-options.hx |   10 ++++
 2 files changed, 144 insertions(+), 0 deletions(-)

Comments

Anthony Liguori Jan. 21, 2013, 7:48 p.m. UTC | #1
Lei Li <lilei@linux.vnet.ibm.com> writes:

> Signed-off-by: Lei Li <lilei@linux.vnet.ibm.com>
> ---
>  qemu-char.c     |  134 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
>  qemu-options.hx |   10 ++++
>  2 files changed, 144 insertions(+), 0 deletions(-)
>
> diff --git a/qemu-char.c b/qemu-char.c
> index 9ba0573..950c543 100644
> --- a/qemu-char.c
> +++ b/qemu-char.c
> @@ -98,6 +98,7 @@
>  #include "ui/qemu-spice.h"
>  
>  #define READ_BUF_LEN 4096
> +#define CBUFF_SIZE 65536
>  
>  /***********************************************************/
>  /* character device */
> @@ -2643,6 +2644,130 @@ size_t qemu_chr_mem_osize(const CharDriverState *chr)
>      return d->outbuf_size;
>  }
>  
> +/*********************************************************/
> +/*CircularMemory chardev*/
> +
> +typedef struct {
> +    size_t size;
> +    size_t head;
> +    size_t count;
> +    uint8_t *cbuf;
> +} CirMemCharDriver;

I think this is correct but it's an unusual way to implement it IMHO.  I
would have done:

typedef struct CirMemCharDriver
{
    size_t size;
    size_t prod;
    size_t cons;
    uint8_t *cbuf;
} CirMemCharDriver;

> +
> +static bool cirmem_chr_is_empty(const CharDriverState *chr)
> +{
> +    const CirMemCharDriver *d = chr->opaque;
> +
> +    return d->count == 0;

return d->cons == d->prod;

> +}
> +
> +static bool cirmem_chr_is_full(const CharDriverState *chr)
> +{
> +    const CirMemCharDriver *d = chr->opaque;
> +
> +    return d->count == d->size;

return (d->prod - d->cons) == d->size;

> +}
> +
> +static size_t qemu_chr_cirmem_count(const CharDriverState *chr)
> +{
> +    const CirMemCharDriver *d = chr->opaque;
> +
> +    return d->count;

return (d->prod - d->cons);

> +}
> +
> +static int cirmem_chr_write(CharDriverState *chr, const uint8_t *buf, int len)
> +{
> +    CirMemCharDriver *d = chr->opaque;
> +    int i;
> +    int tail;
> +
> +    if (!buf || (len < 0)) {
> +        return -1;
> +    }
> +
> +    for (i = 0; i < len; i++ ) {
> +        /* Avoid writing the IAC information to the queue. */
> +        if ((unsigned char)buf[i] == IAC) {
> +            continue;
> +        }

I'd prefer that you dropped this.  Not sure why you encountered this but
it shouldn't come up normally.  IAC is only used by the telnet protocol AFAIK.

> +
> +        tail = (d->head + d->count) % d->size;
> +        d->cbuf[tail] = buf[i];

d->cbuf[d->prod++ % d->size] = buf[i];
if ((d->prod - d->cons) > d->size {
   d->cons = d->prod - d->size;
}

> +    return 0;
> +}
> +
> +static int cirmem_chr_read(CharDriverState *chr, uint8_t *buf, int len)
> +{
> +    CirMemCharDriver *d = chr->opaque;
> +    int i;
> +

for (i = 0; i < len && !circmem_chr_is_empty(chr); i++) {
    buf[i] = d->cbuf[d->cons++];
}

return i;

You need to return the number of bytes read, no?

Regards,

Anthony Liguori

> +    return 0;
> +}
> +
> +static void cirmem_chr_close(struct CharDriverState *chr)
> +{
> +    CirMemCharDriver *d = chr->opaque;
> +
> +    g_free(d->cbuf);
> +    g_free(d);
> +    chr->opaque = NULL;
> +}
> +
> +static CharDriverState *qemu_chr_open_cirmemchr(QemuOpts *opts)
> +{
> +    CharDriverState *chr;
> +    CirMemCharDriver *d;
> +
> +    chr = g_malloc0(sizeof(CharDriverState));
> +    d = g_malloc(sizeof(*d));
> +
> +    d->size = qemu_opt_get_number(opts, "maxcapacity", 0);
> +    if (d->size == 0) {
> +        d->size = CBUFF_SIZE;
> +    }
> +
> +    /* The size must be power of 2 */
> +    if (d->size & (d->size - 1)) {
> +        goto fail;
> +    }
> +
> +    d->head = 0;
> +    d->count = 0;
> +    d->cbuf = g_malloc0(d->size);
> +
> +    chr->opaque = d;
> +    chr->chr_write = cirmem_chr_write;
> +    chr->chr_close = cirmem_chr_close;
> +
> +    return chr;
> +
> +fail:
> +    g_free(d);
> +    g_free(chr);
> +    return NULL;
> +}
> +
>  QemuOpts *qemu_chr_parse_compat(const char *label, const char *filename)
>  {
>      char host[65], port[33], width[8], height[8];
> @@ -2707,6 +2832,11 @@ QemuOpts *qemu_chr_parse_compat(const char *label, const char *filename)
>          qemu_opt_set(opts, "path", p);
>          return opts;
>      }
> +    if (strstart(filename, "memory", &p)) {
> +        qemu_opt_set(opts, "backend", "memory");
> +        qemu_opt_set(opts, "maxcapacity", p);
> +        return opts;
> +    }
>      if (strstart(filename, "tcp:", &p) ||
>          strstart(filename, "telnet:", &p)) {
>          if (sscanf(p, "%64[^:]:%32[^,]%n", host, port, &pos) < 2) {
> @@ -2796,6 +2926,7 @@ static const struct {
>      { .name = "udp",       .open = qemu_chr_open_udp },
>      { .name = "msmouse",   .open = qemu_chr_open_msmouse },
>      { .name = "vc",        .open = text_console_init },
> +    { .name = "memory",    .open = qemu_chr_open_cirmemchr },
>  #ifdef _WIN32
>      { .name = "file",      .open = qemu_chr_open_win_file_out },
>      { .name = "pipe",      .open = qemu_chr_open_win_pipe },
> @@ -3055,6 +3186,9 @@ QemuOptsList qemu_chardev_opts = {
>          },{
>              .name = "debug",
>              .type = QEMU_OPT_NUMBER,
> +        },{
> +            .name = "maxcapacity",
> +            .type = QEMU_OPT_NUMBER,
>          },
>          { /* end of list */ }
>      },
> diff --git a/qemu-options.hx b/qemu-options.hx
> index 40cd683..435550f 100644
> --- a/qemu-options.hx
> +++ b/qemu-options.hx
> @@ -1728,6 +1728,7 @@ DEF("chardev", HAS_ARG, QEMU_OPTION_chardev,
>      "-chardev msmouse,id=id[,mux=on|off]\n"
>      "-chardev vc,id=id[[,width=width][,height=height]][[,cols=cols][,rows=rows]]\n"
>      "         [,mux=on|off]\n"
> +    "-chardev memory,id=id,maxcapacity=maxcapacity\n"
>      "-chardev file,id=id,path=path[,mux=on|off]\n"
>      "-chardev pipe,id=id,path=path[,mux=on|off]\n"
>  #ifdef _WIN32
> @@ -1769,6 +1770,7 @@ Backend is one of:
>  @option{udp},
>  @option{msmouse},
>  @option{vc},
> +@option{memory},
>  @option{file},
>  @option{pipe},
>  @option{console},
> @@ -1877,6 +1879,14 @@ the console, in pixels.
>  @option{cols} and @option{rows} specify that the console be sized to fit a text
>  console with the given dimensions.
>  
> +@item -chardev memory ,id=@var{id} ,maxcapacity=@var{maxcapacity}
> +
> +Create a circular buffer with fixed size indicated by optionally @option{maxcapacity}
> +which will be default 64K if it is not given.
> +
> +@option{maxcapacity} specifies the max capacity of the size of circular buffer
> +to create. Should be power of 2.
> +
>  @item -chardev file ,id=@var{id} ,path=@var{path}
>  
>  Log all traffic received from the guest to a file.
> -- 
> 1.7.7.6
Lei Li Jan. 22, 2013, 7:33 a.m. UTC | #2
On 01/22/2013 03:48 AM, Anthony Liguori wrote:
> Lei Li <lilei@linux.vnet.ibm.com> writes:
>
>> Signed-off-by: Lei Li <lilei@linux.vnet.ibm.com>
>> ---
>>   qemu-char.c     |  134 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
>>   qemu-options.hx |   10 ++++
>>   2 files changed, 144 insertions(+), 0 deletions(-)
>>
>> diff --git a/qemu-char.c b/qemu-char.c
>> index 9ba0573..950c543 100644
>> --- a/qemu-char.c
>> +++ b/qemu-char.c
>> @@ -98,6 +98,7 @@
>>   #include "ui/qemu-spice.h"
>>   
>>   #define READ_BUF_LEN 4096
>> +#define CBUFF_SIZE 65536
>>   
>>   /***********************************************************/
>>   /* character device */
>> @@ -2643,6 +2644,130 @@ size_t qemu_chr_mem_osize(const CharDriverState *chr)
>>       return d->outbuf_size;
>>   }
>>   
>> +/*********************************************************/
>> +/*CircularMemory chardev*/
>> +
>> +typedef struct {
>> +    size_t size;
>> +    size_t head;
>> +    size_t count;
>> +    uint8_t *cbuf;
>> +} CirMemCharDriver;
> I think this is correct but it's an unusual way to implement it IMHO.  I
> would have done:
>
> typedef struct CirMemCharDriver
> {
>      size_t size;
>      size_t prod;
>      size_t cons;
>      uint8_t *cbuf;
> } CirMemCharDriver;
>> +
>> +static bool cirmem_chr_is_empty(const CharDriverState *chr)
>> +{
>> +    const CirMemCharDriver *d = chr->opaque;
>> +
>> +    return d->count == 0;
> return d->cons == d->prod;
>
>> +}
>> +
>> +static bool cirmem_chr_is_full(const CharDriverState *chr)
>> +{
>> +    const CirMemCharDriver *d = chr->opaque;
>> +
>> +    return d->count == d->size;
> return (d->prod - d->cons) == d->size;
>
>> +}
>> +
>> +static size_t qemu_chr_cirmem_count(const CharDriverState *chr)
>> +{
>> +    const CirMemCharDriver *d = chr->opaque;
>> +
>> +    return d->count;
> return (d->prod - d->cons);
>
>> +}
>> +
>> +static int cirmem_chr_write(CharDriverState *chr, const uint8_t *buf, int len)
>> +{
>> +    CirMemCharDriver *d = chr->opaque;
>> +    int i;
>> +    int tail;
>> +
>> +    if (!buf || (len < 0)) {
>> +        return -1;
>> +    }
>> +
>> +    for (i = 0; i < len; i++ ) {
>> +        /* Avoid writing the IAC information to the queue. */
>> +        if ((unsigned char)buf[i] == IAC) {
>> +            continue;
>> +        }
> I'd prefer that you dropped this.  Not sure why you encountered this but
> it shouldn't come up normally.  IAC is only used by the telnet protocol AFAIK.
>
There are some strange characters reading from the buffer while
doing test. like:

{"execute": "memchar-read", "arguments": {"chardev": "foo", "size": 16}}
{"return": "\uFFFF\uFFFF"}

Even I did not start QMP on a TCP socket and use telnet:

[root@localhost qemu]# ./x86_64-softmmu/qemu-system-x86_64 -m 512 -hda /home/lei/img/kvm.img -chardev memory,id=foo,maxcapacity=16 -serial chardev:foo -monitor stdio
QEMU 1.3.50 monitor - type 'help' for more information
(qemu) memchar_read foo 10
������
(qemu)

When I debug, I found that these strange characters are auto written into
the buffer by the monitor, even I did not write anything into it. (I tried
to add qmp interface for the Memory chardev backend to test this problem, it
also happens.)

But after avoiding writing the character with ASCII value 255, it can work
normally...

I thought it was caused by IAC, but sorry I am not quite sure about how it
happens. :(


>> +
>> +        tail = (d->head + d->count) % d->size;
>> +        d->cbuf[tail] = buf[i];
> d->cbuf[d->prod++ % d->size] = buf[i];
> if ((d->prod - d->cons) > d->size {
>     d->cons = d->prod - d->size;
> }
>
>> +    return 0;
>> +}
>> +
>> +static int cirmem_chr_read(CharDriverState *chr, uint8_t *buf, int len)
>> +{
>> +    CirMemCharDriver *d = chr->opaque;
>> +    int i;
>> +
> for (i = 0; i < len && !circmem_chr_is_empty(chr); i++) {
>      buf[i] = d->cbuf[d->cons++];
> }
>
> return i;
>
> You need to return the number of bytes read, no?
>
> Regards,
>
> Anthony Liguori
>
>> +    return 0;
>> +}
>> +
>> +static void cirmem_chr_close(struct CharDriverState *chr)
>> +{
>> +    CirMemCharDriver *d = chr->opaque;
>> +
>> +    g_free(d->cbuf);
>> +    g_free(d);
>> +    chr->opaque = NULL;
>> +}
>> +
>> +static CharDriverState *qemu_chr_open_cirmemchr(QemuOpts *opts)
>> +{
>> +    CharDriverState *chr;
>> +    CirMemCharDriver *d;
>> +
>> +    chr = g_malloc0(sizeof(CharDriverState));
>> +    d = g_malloc(sizeof(*d));
>> +
>> +    d->size = qemu_opt_get_number(opts, "maxcapacity", 0);
>> +    if (d->size == 0) {
>> +        d->size = CBUFF_SIZE;
>> +    }
>> +
>> +    /* The size must be power of 2 */
>> +    if (d->size & (d->size - 1)) {
>> +        goto fail;
>> +    }
>> +
>> +    d->head = 0;
>> +    d->count = 0;
>> +    d->cbuf = g_malloc0(d->size);
>> +
>> +    chr->opaque = d;
>> +    chr->chr_write = cirmem_chr_write;
>> +    chr->chr_close = cirmem_chr_close;
>> +
>> +    return chr;
>> +
>> +fail:
>> +    g_free(d);
>> +    g_free(chr);
>> +    return NULL;
>> +}
>> +
>>   QemuOpts *qemu_chr_parse_compat(const char *label, const char *filename)
>>   {
>>       char host[65], port[33], width[8], height[8];
>> @@ -2707,6 +2832,11 @@ QemuOpts *qemu_chr_parse_compat(const char *label, const char *filename)
>>           qemu_opt_set(opts, "path", p);
>>           return opts;
>>       }
>> +    if (strstart(filename, "memory", &p)) {
>> +        qemu_opt_set(opts, "backend", "memory");
>> +        qemu_opt_set(opts, "maxcapacity", p);
>> +        return opts;
>> +    }
>>       if (strstart(filename, "tcp:", &p) ||
>>           strstart(filename, "telnet:", &p)) {
>>           if (sscanf(p, "%64[^:]:%32[^,]%n", host, port, &pos) < 2) {
>> @@ -2796,6 +2926,7 @@ static const struct {
>>       { .name = "udp",       .open = qemu_chr_open_udp },
>>       { .name = "msmouse",   .open = qemu_chr_open_msmouse },
>>       { .name = "vc",        .open = text_console_init },
>> +    { .name = "memory",    .open = qemu_chr_open_cirmemchr },
>>   #ifdef _WIN32
>>       { .name = "file",      .open = qemu_chr_open_win_file_out },
>>       { .name = "pipe",      .open = qemu_chr_open_win_pipe },
>> @@ -3055,6 +3186,9 @@ QemuOptsList qemu_chardev_opts = {
>>           },{
>>               .name = "debug",
>>               .type = QEMU_OPT_NUMBER,
>> +        },{
>> +            .name = "maxcapacity",
>> +            .type = QEMU_OPT_NUMBER,
>>           },
>>           { /* end of list */ }
>>       },
>> diff --git a/qemu-options.hx b/qemu-options.hx
>> index 40cd683..435550f 100644
>> --- a/qemu-options.hx
>> +++ b/qemu-options.hx
>> @@ -1728,6 +1728,7 @@ DEF("chardev", HAS_ARG, QEMU_OPTION_chardev,
>>       "-chardev msmouse,id=id[,mux=on|off]\n"
>>       "-chardev vc,id=id[[,width=width][,height=height]][[,cols=cols][,rows=rows]]\n"
>>       "         [,mux=on|off]\n"
>> +    "-chardev memory,id=id,maxcapacity=maxcapacity\n"
>>       "-chardev file,id=id,path=path[,mux=on|off]\n"
>>       "-chardev pipe,id=id,path=path[,mux=on|off]\n"
>>   #ifdef _WIN32
>> @@ -1769,6 +1770,7 @@ Backend is one of:
>>   @option{udp},
>>   @option{msmouse},
>>   @option{vc},
>> +@option{memory},
>>   @option{file},
>>   @option{pipe},
>>   @option{console},
>> @@ -1877,6 +1879,14 @@ the console, in pixels.
>>   @option{cols} and @option{rows} specify that the console be sized to fit a text
>>   console with the given dimensions.
>>   
>> +@item -chardev memory ,id=@var{id} ,maxcapacity=@var{maxcapacity}
>> +
>> +Create a circular buffer with fixed size indicated by optionally @option{maxcapacity}
>> +which will be default 64K if it is not given.
>> +
>> +@option{maxcapacity} specifies the max capacity of the size of circular buffer
>> +to create. Should be power of 2.
>> +
>>   @item -chardev file ,id=@var{id} ,path=@var{path}
>>   
>>   Log all traffic received from the guest to a file.
>> -- 
>> 1.7.7.6
diff mbox

Patch

diff --git a/qemu-char.c b/qemu-char.c
index 9ba0573..950c543 100644
--- a/qemu-char.c
+++ b/qemu-char.c
@@ -98,6 +98,7 @@ 
 #include "ui/qemu-spice.h"
 
 #define READ_BUF_LEN 4096
+#define CBUFF_SIZE 65536
 
 /***********************************************************/
 /* character device */
@@ -2643,6 +2644,130 @@  size_t qemu_chr_mem_osize(const CharDriverState *chr)
     return d->outbuf_size;
 }
 
+/*********************************************************/
+/*CircularMemory chardev*/
+
+typedef struct {
+    size_t size;
+    size_t head;
+    size_t count;
+    uint8_t *cbuf;
+} CirMemCharDriver;
+
+static bool cirmem_chr_is_empty(const CharDriverState *chr)
+{
+    const CirMemCharDriver *d = chr->opaque;
+
+    return d->count == 0;
+}
+
+static bool cirmem_chr_is_full(const CharDriverState *chr)
+{
+    const CirMemCharDriver *d = chr->opaque;
+
+    return d->count == d->size;
+}
+
+static size_t qemu_chr_cirmem_count(const CharDriverState *chr)
+{
+    const CirMemCharDriver *d = chr->opaque;
+
+    return d->count;
+}
+
+static int cirmem_chr_write(CharDriverState *chr, const uint8_t *buf, int len)
+{
+    CirMemCharDriver *d = chr->opaque;
+    int i;
+    int tail;
+
+    if (!buf || (len < 0)) {
+        return -1;
+    }
+
+    for (i = 0; i < len; i++ ) {
+        /* Avoid writing the IAC information to the queue. */
+        if ((unsigned char)buf[i] == IAC) {
+            continue;
+        }
+
+        tail = (d->head + d->count) % d->size;
+        d->cbuf[tail] = buf[i];
+        if (d->count == d->size) {
+            d->head = (d->head + 1) % d->size;
+        } else {
+            ++d->count;
+        }
+    }
+
+    return 0;
+}
+
+static int cirmem_chr_read(CharDriverState *chr, uint8_t *buf, int len)
+{
+    CirMemCharDriver *d = chr->opaque;
+    int i;
+
+    if (cirmem_chr_is_empty(chr) || len < 0) {
+        return -1;
+    }
+
+    for (i = 0; i < len; i++) {
+        buf[i] = d->cbuf[d->head];
+        d->head = (d->head + 1) % d->size;
+        d->count--;
+
+        if (cirmem_chr_is_empty(chr)) {
+            break;
+        }
+    }
+
+    return 0;
+}
+
+static void cirmem_chr_close(struct CharDriverState *chr)
+{
+    CirMemCharDriver *d = chr->opaque;
+
+    g_free(d->cbuf);
+    g_free(d);
+    chr->opaque = NULL;
+}
+
+static CharDriverState *qemu_chr_open_cirmemchr(QemuOpts *opts)
+{
+    CharDriverState *chr;
+    CirMemCharDriver *d;
+
+    chr = g_malloc0(sizeof(CharDriverState));
+    d = g_malloc(sizeof(*d));
+
+    d->size = qemu_opt_get_number(opts, "maxcapacity", 0);
+    if (d->size == 0) {
+        d->size = CBUFF_SIZE;
+    }
+
+    /* The size must be power of 2 */
+    if (d->size & (d->size - 1)) {
+        goto fail;
+    }
+
+    d->head = 0;
+    d->count = 0;
+    d->cbuf = g_malloc0(d->size);
+
+    chr->opaque = d;
+    chr->chr_write = cirmem_chr_write;
+    chr->chr_close = cirmem_chr_close;
+
+    return chr;
+
+fail:
+    g_free(d);
+    g_free(chr);
+    return NULL;
+}
+
 QemuOpts *qemu_chr_parse_compat(const char *label, const char *filename)
 {
     char host[65], port[33], width[8], height[8];
@@ -2707,6 +2832,11 @@  QemuOpts *qemu_chr_parse_compat(const char *label, const char *filename)
         qemu_opt_set(opts, "path", p);
         return opts;
     }
+    if (strstart(filename, "memory", &p)) {
+        qemu_opt_set(opts, "backend", "memory");
+        qemu_opt_set(opts, "maxcapacity", p);
+        return opts;
+    }
     if (strstart(filename, "tcp:", &p) ||
         strstart(filename, "telnet:", &p)) {
         if (sscanf(p, "%64[^:]:%32[^,]%n", host, port, &pos) < 2) {
@@ -2796,6 +2926,7 @@  static const struct {
     { .name = "udp",       .open = qemu_chr_open_udp },
     { .name = "msmouse",   .open = qemu_chr_open_msmouse },
     { .name = "vc",        .open = text_console_init },
+    { .name = "memory",    .open = qemu_chr_open_cirmemchr },
 #ifdef _WIN32
     { .name = "file",      .open = qemu_chr_open_win_file_out },
     { .name = "pipe",      .open = qemu_chr_open_win_pipe },
@@ -3055,6 +3186,9 @@  QemuOptsList qemu_chardev_opts = {
         },{
             .name = "debug",
             .type = QEMU_OPT_NUMBER,
+        },{
+            .name = "maxcapacity",
+            .type = QEMU_OPT_NUMBER,
         },
         { /* end of list */ }
     },
diff --git a/qemu-options.hx b/qemu-options.hx
index 40cd683..435550f 100644
--- a/qemu-options.hx
+++ b/qemu-options.hx
@@ -1728,6 +1728,7 @@  DEF("chardev", HAS_ARG, QEMU_OPTION_chardev,
     "-chardev msmouse,id=id[,mux=on|off]\n"
     "-chardev vc,id=id[[,width=width][,height=height]][[,cols=cols][,rows=rows]]\n"
     "         [,mux=on|off]\n"
+    "-chardev memory,id=id,maxcapacity=maxcapacity\n"
     "-chardev file,id=id,path=path[,mux=on|off]\n"
     "-chardev pipe,id=id,path=path[,mux=on|off]\n"
 #ifdef _WIN32
@@ -1769,6 +1770,7 @@  Backend is one of:
 @option{udp},
 @option{msmouse},
 @option{vc},
+@option{memory},
 @option{file},
 @option{pipe},
 @option{console},
@@ -1877,6 +1879,14 @@  the console, in pixels.
 @option{cols} and @option{rows} specify that the console be sized to fit a text
 console with the given dimensions.
 
+@item -chardev memory ,id=@var{id} ,maxcapacity=@var{maxcapacity}
+
+Create a circular buffer with fixed size indicated by optionally @option{maxcapacity}
+which will be default 64K if it is not given.
+
+@option{maxcapacity} specifies the max capacity of the size of circular buffer
+to create. Should be power of 2.
+
 @item -chardev file ,id=@var{id} ,path=@var{path}
 
 Log all traffic received from the guest to a file.