]> git.proxmox.com Git - mirror_qemu.git/blobdiff - qemu-io-cmds.c
Add migration stream analyzation script
[mirror_qemu.git] / qemu-io-cmds.c
index 8565d49336ac713f26bc959f1fd6718a704bbe2f..e70855254af003e594f3c628bd12ad8193fac695 100644 (file)
 
 #include "qemu-io.h"
 #include "block/block_int.h"
+#include "block/qapi.h"
 #include "qemu/main-loop.h"
+#include "qemu/timer.h"
 
 #define CMD_NOFILE_OK   0x01
 
-int qemuio_misalign;
+bool qemuio_misalign;
 
 static cmdinfo_t *cmdtab;
 static int ncmds;
@@ -27,7 +29,7 @@ static int compare_cmdname(const void *a, const void *b)
 
 void qemuio_add_command(const cmdinfo_t *ci)
 {
-    cmdtab = g_realloc(cmdtab, ++ncmds * sizeof(*cmdtab));
+    cmdtab = g_renew(cmdinfo_t, cmdtab, ++ncmds);
     cmdtab[ncmds - 1] = *ci;
     qsort(cmdtab, ncmds, sizeof(*cmdtab), compare_cmdname);
 }
@@ -93,27 +95,33 @@ static const cmdinfo_t *find_command(const char *cmd)
     return NULL;
 }
 
+/* Invoke fn() for commands with a matching prefix */
+void qemuio_complete_command(const char *input,
+                             void (*fn)(const char *cmd, void *opaque),
+                             void *opaque)
+{
+    cmdinfo_t *ct;
+    size_t input_len = strlen(input);
+
+    for (ct = cmdtab; ct < &cmdtab[ncmds]; ct++) {
+        if (strncmp(input, ct->name, input_len) == 0) {
+            fn(ct->name, opaque);
+        }
+    }
+}
+
 static char **breakline(char *input, int *count)
 {
     int c = 0;
     char *p;
-    char **rval = g_malloc0(sizeof(char *));
-    char **tmp;
+    char **rval = g_new0(char *, 1);
 
     while (rval && (p = qemu_strsep(&input, " ")) != NULL) {
         if (!*p) {
             continue;
         }
         c++;
-        tmp = g_realloc(rval, sizeof(*rval) * (c + 1));
-        if (!tmp) {
-            g_free(rval);
-            rval = NULL;
-            c = 0;
-            break;
-        } else {
-            rval = tmp;
-        }
+        rval = g_renew(char *, rval, (c + 1));
         rval[c - 1] = p;
         rval[c] = NULL;
     }
@@ -441,7 +449,7 @@ static void coroutine_fn co_write_zeroes_entry(void *opaque)
     CoWriteZeroes *data = opaque;
 
     data->ret = bdrv_co_write_zeroes(data->bs, data->offset / BDRV_SECTOR_SIZE,
-                                     data->count / BDRV_SECTOR_SIZE);
+                                     data->count / BDRV_SECTOR_SIZE, 0);
     data->done = true;
     if (data->ret < 0) {
         *data->total = data->ret;
@@ -466,7 +474,7 @@ static int do_co_write_zeroes(BlockDriverState *bs, int64_t offset, int count,
     co = qemu_coroutine_create(co_write_zeroes_entry);
     qemu_coroutine_enter(co, &data);
     while (!data.done) {
-        qemu_aio_wait();
+        aio_poll(bdrv_get_aio_context(bs), true);
     }
     if (data.ret < 0) {
         return data.ret;
@@ -1070,7 +1078,7 @@ writev_help(void)
 " writes a range of bytes from the given offset source from multiple buffers\n"
 "\n"
 " Example:\n"
-" 'write 512 1k 1k' - writes 2 kilobytes at 512 bytes into the open file\n"
+" 'writev 512 1k 1k' - writes 2 kilobytes at 512 bytes into the open file\n"
 "\n"
 " Writes into a segment of the currently open file, using a buffer\n"
 " filled with a set pattern (0xcdcdcdcd).\n"
@@ -1247,9 +1255,9 @@ static int multiwrite_f(BlockDriverState *bs, int argc, char **argv)
         }
     }
 
-    reqs = g_malloc0(nr_reqs * sizeof(*reqs));
-    buf = g_malloc0(nr_reqs * sizeof(*buf));
-    qiovs = g_malloc(nr_reqs * sizeof(*qiovs));
+    reqs = g_new0(BlockRequest, nr_reqs);
+    buf = g_new0(char *, nr_reqs);
+    qiovs = g_new(QEMUIOVector, nr_reqs);
 
     for (i = 0; i < nr_reqs && optind < argc; i++) {
         int j;
@@ -1678,6 +1686,7 @@ static const cmdinfo_t length_cmd = {
 static int info_f(BlockDriverState *bs, int argc, char **argv)
 {
     BlockDriverInfo bdi;
+    ImageInfoSpecific *spec_info;
     char s1[64], s2[64];
     int ret;
 
@@ -1699,6 +1708,13 @@ static int info_f(BlockDriverState *bs, int argc, char **argv)
     printf("cluster size: %s\n", s1);
     printf("vm state offset: %s\n", s2);
 
+    spec_info = bdrv_get_specific_info(bs);
+    if (spec_info) {
+        printf("Format specific information:\n");
+        bdrv_image_info_specific_dump(fprintf, stdout, spec_info);
+        qapi_free_ImageInfoSpecific(spec_info);
+    }
+
     return 0;
 }
 
@@ -1884,7 +1900,7 @@ static int map_is_allocated(BlockDriverState *bs, int64_t sector_num,
 
         num_checked = MIN(nb_sectors, INT_MAX);
         ret = bdrv_is_allocated(bs, sector_num, num_checked, &num);
-        if (ret == firstret) {
+        if (ret == firstret && num) {
             *pnum += num;
         } else {
             break;
@@ -1911,6 +1927,9 @@ static int map_f(BlockDriverState *bs, int argc, char **argv)
         if (ret < 0) {
             error_report("Failed to get allocation status: %s", strerror(-ret));
             return 0;
+        } else if (!num) {
+            error_report("Unexpected end of image");
+            return 0;
         }
 
         retstr = ret ? "    allocated" : "not allocated";
@@ -1947,6 +1966,18 @@ static int break_f(BlockDriverState *bs, int argc, char **argv)
     return 0;
 }
 
+static int remove_break_f(BlockDriverState *bs, int argc, char **argv)
+{
+    int ret;
+
+    ret = bdrv_debug_remove_breakpoint(bs, argv[1]);
+    if (ret < 0) {
+        printf("Could not remove breakpoint %s: %s\n", argv[1], strerror(-ret));
+    }
+
+    return 0;
+}
+
 static const cmdinfo_t break_cmd = {
        .name           = "break",
        .argmin         = 2,
@@ -1957,6 +1988,15 @@ static const cmdinfo_t break_cmd = {
                          "request as tag",
 };
 
+static const cmdinfo_t remove_break_cmd = {
+       .name           = "remove_break",
+       .argmin         = 1,
+       .argmax         = 1,
+       .cfunc          = remove_break_f,
+       .args           = "tag",
+       .oneline        = "remove a breakpoint by tag",
+};
+
 static int resume_f(BlockDriverState *bs, int argc, char **argv)
 {
     int ret;
@@ -1981,7 +2021,7 @@ static const cmdinfo_t resume_cmd = {
 static int wait_break_f(BlockDriverState *bs, int argc, char **argv)
 {
     while (!bdrv_debug_is_suspended(bs, argv[1])) {
-        qemu_aio_wait();
+        aio_poll(bdrv_get_aio_context(bs), true);
     }
 
     return 0;
@@ -2008,6 +2048,91 @@ static const cmdinfo_t abort_cmd = {
        .oneline        = "simulate a program crash using abort(3)",
 };
 
+static void sigraise_help(void)
+{
+    printf(
+"\n"
+" raises the given signal\n"
+"\n"
+" Example:\n"
+" 'sigraise %i' - raises SIGTERM\n"
+"\n"
+" Invokes raise(signal), where \"signal\" is the mandatory integer argument\n"
+" given to sigraise.\n"
+"\n", SIGTERM);
+}
+
+static int sigraise_f(BlockDriverState *bs, int argc, char **argv);
+
+static const cmdinfo_t sigraise_cmd = {
+    .name       = "sigraise",
+    .cfunc      = sigraise_f,
+    .argmin     = 1,
+    .argmax     = 1,
+    .flags      = CMD_NOFILE_OK,
+    .args       = "signal",
+    .oneline    = "raises a signal",
+    .help       = sigraise_help,
+};
+
+static int sigraise_f(BlockDriverState *bs, int argc, char **argv)
+{
+    int sig = cvtnum(argv[1]);
+    if (sig < 0) {
+        printf("non-numeric signal number argument -- %s\n", argv[1]);
+        return 0;
+    }
+
+    /* Using raise() to kill this process does not necessarily flush all open
+     * streams. At least stdout and stderr (although the latter should be
+     * non-buffered anyway) should be flushed, though. */
+    fflush(stdout);
+    fflush(stderr);
+
+    raise(sig);
+    return 0;
+}
+
+static void sleep_cb(void *opaque)
+{
+    bool *expired = opaque;
+    *expired = true;
+}
+
+static int sleep_f(BlockDriverState *bs, int argc, char **argv)
+{
+    char *endptr;
+    long ms;
+    struct QEMUTimer *timer;
+    bool expired = false;
+
+    ms = strtol(argv[1], &endptr, 0);
+    if (ms < 0 || *endptr != '\0') {
+        printf("%s is not a valid number\n", argv[1]);
+        return 0;
+    }
+
+    timer = timer_new_ns(QEMU_CLOCK_HOST, sleep_cb, &expired);
+    timer_mod(timer, qemu_clock_get_ns(QEMU_CLOCK_HOST) + SCALE_MS * ms);
+
+    while (!expired) {
+        main_loop_wait(false);
+    }
+
+    timer_free(timer);
+
+    return 0;
+}
+
+static const cmdinfo_t sleep_cmd = {
+       .name           = "sleep",
+       .argmin         = 1,
+       .argmax         = 1,
+       .cfunc          = sleep_f,
+       .flags          = CMD_NOFILE_OK,
+       .oneline        = "waits for the given value in milliseconds",
+};
+
 static void help_oneline(const char *cmd, const cmdinfo_t *ct)
 {
     if (cmd) {
@@ -2117,7 +2242,10 @@ static void __attribute((constructor)) init_qemuio_commands(void)
     qemuio_add_command(&alloc_cmd);
     qemuio_add_command(&map_cmd);
     qemuio_add_command(&break_cmd);
+    qemuio_add_command(&remove_break_cmd);
     qemuio_add_command(&resume_cmd);
     qemuio_add_command(&wait_break_cmd);
     qemuio_add_command(&abort_cmd);
+    qemuio_add_command(&sleep_cmd);
+    qemuio_add_command(&sigraise_cmd);
 }