aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorOt ten Thije <ottenthije@google.com>2010-10-06 17:48:15 +0100
committerOt ten Thije <ottenthije@google.com>2010-10-28 13:47:54 +0100
commit2ff39a367738422c0ca1313cac8ff380e1fdd498 (patch)
treea85cd059101de7f5eac9cd1f205d8971b6a3910d
parentf5f9aed3f35a80d8112849d0d354a465acfbbcc5 (diff)
downloadexternal_qemu-2ff39a367738422c0ca1313cac8ff380e1fdd498.zip
external_qemu-2ff39a367738422c0ca1313cac8ff380e1fdd498.tar.gz
external_qemu-2ff39a367738422c0ca1313cac8ff380e1fdd498.tar.bz2
Control state snapshots from Android console.
This patch exposes Qemu's save, load, delete and list commands for state snapshots on the Android console. A level of indirection is added by means of the OutputChannel construct. This allows us to show the output of the Qemu commands on the console rather than on the monitor, while minimizing the differences with the upstream codebase. The new commands are exposed only when the configuration constant CONFIG_ANDROID_SNAPSHOTS is not 0. Change-Id: I558d5cd505d321fe2da5835713d341d151f60534
-rw-r--r--Makefile.android1
-rw-r--r--android/console.c135
-rw-r--r--monitor.c6
-rw-r--r--monitor.h2
-rw-r--r--outputchannel.c61
-rw-r--r--outputchannel.h38
-rw-r--r--savevm.c99
-rw-r--r--sysemu.h5
8 files changed, 303 insertions, 44 deletions
diff --git a/Makefile.android b/Makefile.android
index 69538b7..1cc904a 100644
--- a/Makefile.android
+++ b/Makefile.android
@@ -650,6 +650,7 @@ CORE_MISC_SOURCES = vl-android.c \
monitor.c \
readline.c \
qemu-char-android.c \
+ outputchannel.c \
qemu-error.c \
qerror.c \
disas.c \
diff --git a/android/console.c b/android/console.c
index af22fc6..4f1a976 100644
--- a/android/console.c
+++ b/android/console.c
@@ -36,6 +36,7 @@
#include "android/utils/bufprint.h"
#include "android/utils/debug.h"
#include "android/utils/stralloc.h"
+#include "android/config/config.h"
#include "tcpdump.h"
#include "net.h"
#include "monitor.h"
@@ -251,18 +252,25 @@ static void control_control_write( ControlClient client, const char* buff, in
}
}
-static void control_write( ControlClient client, const char* format, ... )
+static int control_vwrite( ControlClient client, const char* format, va_list args )
{
static char temp[1024];
- va_list args;
+ int ret = vsnprintf( temp, sizeof(temp), format, args );
+ temp[ sizeof(temp)-1 ] = 0;
+ control_control_write( client, temp, -1 );
+
+ return ret;
+}
+static int control_write( ControlClient client, const char* format, ... )
+{
+ int ret;
+ va_list args;
va_start(args, format);
- vsnprintf( temp, sizeof(temp), format, args );
+ ret = control_vwrite(client, format, args);
va_end(args);
- temp[ sizeof(temp)-1 ] = 0;
-
- control_control_write( client, temp, -1 );
+ return ret;
}
@@ -1929,6 +1937,109 @@ static const CommandDefRec event_commands[] =
{ NULL, NULL, NULL, NULL, NULL, NULL }
};
+#if CONFIG_ANDROID_SNAPSHOTS
+
+
+/********************************************************************************************/
+/********************************************************************************************/
+/***** ******/
+/***** S N A P S H O T C O M M A N D S ******/
+/***** ******/
+/********************************************************************************************/
+/********************************************************************************************/
+
+static int
+control_write_out_cb(void* opaque, const char* fmt, va_list ap)
+{
+ ControlClient client = opaque;
+ int ret = control_vwrite(client, fmt, ap);
+ return ret;
+}
+
+static int
+control_write_err_cb(void* opaque, const char* fmt, va_list ap)
+{
+ int ret = 0;
+ ControlClient client = opaque;
+ ret += control_write(client, "KO: ");
+ ret += control_vwrite(client, fmt, ap);
+ return ret;
+}
+
+static int
+do_snapshot_list( ControlClient client, char* args )
+{
+ int ret;
+ OutputChannel *out = output_channel_alloc(client, control_write_out_cb);
+ OutputChannel *err = output_channel_alloc(client, control_write_err_cb);
+ do_info_snapshots_oc(out, err);
+ ret = output_channel_written(err);
+ output_channel_free(out);
+ output_channel_free(err);
+
+ return ret > 0;
+}
+
+static int
+do_snapshot_save( ControlClient client, char* args )
+{
+ int ret;
+ OutputChannel *err = output_channel_alloc(client, control_write_err_cb);
+ do_savevm_oc(err, args);
+ ret = output_channel_written(err);
+ output_channel_free(err);
+
+ return ret > 0; // no output on error channel indicates success
+}
+
+static int
+do_snapshot_load( ControlClient client, char* args )
+{
+ int ret;
+ OutputChannel *err = output_channel_alloc(client, control_write_err_cb);
+ do_loadvm_oc(err, args);
+ ret = output_channel_written(err);
+ output_channel_free(err);
+
+ return ret > 0;
+}
+
+static int
+do_snapshot_del( ControlClient client, char* args )
+{
+ int ret;
+ OutputChannel *err = output_channel_alloc(client, control_write_err_cb);
+ do_delvm_oc(err, args);
+ ret = output_channel_written(err);
+ output_channel_free(err);
+
+ return ret > 0;
+}
+
+static const CommandDefRec snapshot_commands[] =
+{
+ { "list", "list available state snapshots",
+ "'avd snapshot list' will show a list of all state snapshots that can be loaded\r\n",
+ NULL, do_snapshot_list, NULL },
+
+ { "save", "save state snapshot",
+ "'avd snapshot save <name>' will save the current (run-time) state to a snapshot with the given name\r\n",
+ NULL, do_snapshot_save, NULL },
+
+ { "load", "load state snapshot",
+ "'avd snapshot load <name>' will load the state snapshot of the given name\r\n",
+ NULL, do_snapshot_load, NULL },
+
+ { "del", "delete state snapshot",
+ "'avd snapshot del <name>' will delete the state snapshot with the given name\r\n",
+ NULL, do_snapshot_del, NULL },
+
+ { NULL, NULL, NULL, NULL, NULL, NULL }
+};
+
+
+#endif
+
/********************************************************************************************/
/********************************************************************************************/
@@ -1985,13 +2096,19 @@ static const CommandDefRec vm_commands[] =
NULL, do_avd_start, NULL },
{ "status", "query virtual device status",
- "'avd status' will indicate wether the virtual device is running or not\r\n",
+ "'avd status' will indicate whether the virtual device is running or not\r\n",
NULL, do_avd_status, NULL },
{ "name", "query virtual device name",
"'avd name' will return the name of this virtual device\r\n",
NULL, do_avd_name, NULL },
+#if CONFIG_ANDROID_SNAPSHOTS
+ { "snapshot", "state snapshot commands",
+ "allows you to save and restore the virtual device state in snapshots\r\n",
+ NULL, NULL, snapshot_commands },
+#endif
+
{ NULL, NULL, NULL, NULL, NULL, NULL }
};
@@ -2313,8 +2430,8 @@ static const CommandDefRec main_commands[] =
"allows you to simulate an inbound SMS\r\n", NULL,
NULL, sms_commands },
- { "avd", "manager virtual device state",
- "allows to change (e.g. start/stop) the virtual device state\r\n", NULL,
+ { "avd", "control virtual device execution",
+ "allows you to control (e.g. start/stop) the execution of the virtual device\r\n", NULL,
NULL, vm_commands },
{ "window", "manage emulator window",
diff --git a/monitor.c b/monitor.c
index 9de414a..b0b6a7e 100644
--- a/monitor.c
+++ b/monitor.c
@@ -165,11 +165,13 @@ static void monitor_puts(Monitor *mon, const char *str)
}
}
-void monitor_vprintf(Monitor *mon, const char *fmt, va_list ap)
+int monitor_vprintf(Monitor *mon, const char *fmt, va_list ap)
{
char buf[4096];
- vsnprintf(buf, sizeof(buf), fmt, ap);
+ int ret = vsnprintf(buf, sizeof(buf), fmt, ap);
monitor_puts(mon, buf);
+
+ return ret;
}
void monitor_printf(Monitor *mon, const char *fmt, ...)
diff --git a/monitor.h b/monitor.h
index 2d478fc..fc80bcb 100644
--- a/monitor.h
+++ b/monitor.h
@@ -53,7 +53,7 @@ int monitor_read_bdrv_key_start(Monitor *mon, BlockDriverState *bs,
int monitor_get_fd(Monitor *mon, const char *fdname);
-void monitor_vprintf(Monitor *mon, const char *fmt, va_list ap);
+int monitor_vprintf(Monitor *mon, const char *fmt, va_list ap);
void monitor_printf(Monitor *mon, const char *fmt, ...)
__attribute__ ((__format__ (__printf__, 2, 3)));
void monitor_print_filename(Monitor *mon, const char *filename);
diff --git a/outputchannel.c b/outputchannel.c
new file mode 100644
index 0000000..a58c767
--- /dev/null
+++ b/outputchannel.c
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+#include "outputchannel.h"
+#include "qemu-common.h"
+
+struct OutputChannel {
+ void* opaque; /* caller-specific information */
+ OutputChannelPrintf printf; /* callback function to do the printing */
+ unsigned int written; /* number of bytes written to the channel */
+};
+
+OutputChannel* output_channel_alloc(void* opaque, OutputChannelPrintf cb)
+{
+ OutputChannel* oc = qemu_mallocz(sizeof(oc));
+ oc->printf = cb;
+ oc->opaque = opaque;
+ oc->written = 0;
+
+ return oc;
+}
+
+int output_channel_printf(OutputChannel* oc, const char* fmt, ...)
+{
+ int ret;
+ va_list ap;
+ va_start(ap, fmt);
+ ret = oc->printf(oc->opaque, fmt, ap);
+ va_end(ap);
+
+ /* Don't count errors and no-ops towards number of bytes written */
+ if (ret > 0) {
+ oc->written += ret;
+ }
+
+ return ret;
+}
+
+void output_channel_free(OutputChannel* oc)
+{
+ free(oc);
+}
+
+unsigned int output_channel_written(OutputChannel* oc)
+{
+ return oc->written;
+}
diff --git a/outputchannel.h b/outputchannel.h
new file mode 100644
index 0000000..4dece8d
--- /dev/null
+++ b/outputchannel.h
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#ifndef _OUTPUTCHANNEL_H
+#define _OUTPUTCHANNEL_H
+
+#include <stdarg.h>
+
+/* Callback function to print a printf-formatted string to a channel */
+typedef int (*OutputChannelPrintf) (void* opaque, const char* fmt, va_list ap);
+
+typedef struct OutputChannel OutputChannel;
+
+/* Allocates a new output channel */
+OutputChannel* output_channel_alloc(void* opaque, OutputChannelPrintf cb);
+
+/* Prints a printf-formatted string to the output channel */
+int output_channel_printf(OutputChannel* oc, const char* fmt, ...);
+
+/* Frees an output channel */
+void output_channel_free(OutputChannel* oc);
+
+/* Returns the number of bytes written to the channel */
+unsigned int output_channel_written(OutputChannel* oc);
+
+#endif /* _OUTPUTCHANNEL_H */
diff --git a/savevm.c b/savevm.c
index ee90805..0e908f3 100644
--- a/savevm.c
+++ b/savevm.c
@@ -86,6 +86,7 @@
#include "sysemu.h"
#include "qemu-timer.h"
#include "qemu-char.h"
+#include "outputchannel.h"
#include "block.h"
#include "audio/audio.h"
#include "migration.h"
@@ -1162,8 +1163,22 @@ static int bdrv_snapshot_find(BlockDriverState *bs, QEMUSnapshotInfo *sn_info,
return ret;
}
+static int
+monitor_output_channel_cb(void* opaque, const char* fmt, va_list ap)
+{
+ return monitor_vprintf((Monitor*) opaque, fmt, ap);
+}
+
+
void do_savevm(Monitor *mon, const char *name)
{
+ OutputChannel *oc = output_channel_alloc(mon, monitor_output_channel_cb);
+ do_savevm_oc(oc, name);
+ output_channel_free(oc);
+}
+
+void do_savevm_oc(OutputChannel *err, const char *name)
+{
BlockDriverState *bs, *bs1;
QEMUSnapshotInfo sn1, *sn = &sn1, old_sn1, *old_sn = &old_sn1;
int must_delete, ret, i;
@@ -1179,7 +1194,7 @@ void do_savevm(Monitor *mon, const char *name)
bs = get_bs_snapshots();
if (!bs) {
- monitor_printf(mon, "No block device can accept snapshots\n");
+ output_channel_printf(err, "No block device can accept snapshots\n");
return;
}
@@ -1218,22 +1233,22 @@ void do_savevm(Monitor *mon, const char *name)
sn->vm_clock_nsec = qemu_get_clock(vm_clock);
if (bdrv_get_info(bs, bdi) < 0 || bdi->vm_state_offset <= 0) {
- monitor_printf(mon, "Device %s does not support VM state snapshots\n",
- bdrv_get_device_name(bs));
+ output_channel_printf(err, "Device %s does not support VM state snapshots\n",
+ bdrv_get_device_name(bs));
goto the_end;
}
/* save the VM state */
f = qemu_fopen_bdrv(bs, bdi->vm_state_offset, 1);
if (!f) {
- monitor_printf(mon, "Could not open VM state file\n");
+ output_channel_printf(err, "Could not open VM state file\n");
goto the_end;
}
ret = qemu_savevm_state(f);
vm_state_size = qemu_ftell(f);
qemu_fclose(f);
if (ret < 0) {
- monitor_printf(mon, "Error %d while writing VM\n", ret);
+ output_channel_printf(err, "Error %d while writing VM\n", ret);
goto the_end;
}
@@ -1245,17 +1260,17 @@ void do_savevm(Monitor *mon, const char *name)
if (must_delete) {
ret = bdrv_snapshot_delete(bs1, old_sn->id_str);
if (ret < 0) {
- monitor_printf(mon,
- "Error while deleting snapshot on '%s'\n",
- bdrv_get_device_name(bs1));
+ output_channel_printf(err,
+ "Error while deleting snapshot on '%s'\n",
+ bdrv_get_device_name(bs1));
}
}
/* Write VM state size only to the image that contains the state */
sn->vm_state_size = (bs == bs1 ? vm_state_size : 0);
ret = bdrv_snapshot_create(bs1, sn);
if (ret < 0) {
- monitor_printf(mon, "Error while creating snapshot on '%s'\n",
- bdrv_get_device_name(bs1));
+ output_channel_printf(err, "Error while creating snapshot on '%s'\n",
+ bdrv_get_device_name(bs1));
}
}
}
@@ -1267,6 +1282,13 @@ void do_savevm(Monitor *mon, const char *name)
void do_loadvm(Monitor *mon, const char *name)
{
+ OutputChannel *oc = output_channel_alloc(mon, monitor_output_channel_cb);
+ do_loadvm_oc(oc, name);
+ output_channel_free(oc);
+}
+
+void do_loadvm_oc(OutputChannel *err, const char *name)
+{
BlockDriverState *bs, *bs1;
BlockDriverInfo bdi1, *bdi = &bdi1;
QEMUSnapshotInfo sn;
@@ -1276,7 +1298,7 @@ void do_loadvm(Monitor *mon, const char *name)
bs = get_bs_snapshots();
if (!bs) {
- monitor_printf(mon, "No block device supports snapshots\n");
+ output_channel_printf(err, "No block device supports snapshots\n");
return;
}
@@ -1292,20 +1314,20 @@ void do_loadvm(Monitor *mon, const char *name)
ret = bdrv_snapshot_goto(bs1, name);
if (ret < 0) {
if (bs != bs1)
- monitor_printf(mon, "Warning: ");
+ output_channel_printf(err, "Warning: ");
switch(ret) {
case -ENOTSUP:
- monitor_printf(mon,
+ output_channel_printf(err,
"Snapshots not supported on device '%s'\n",
bdrv_get_device_name(bs1));
break;
case -ENOENT:
- monitor_printf(mon, "Could not find snapshot '%s' on "
+ output_channel_printf(err, "Could not find snapshot '%s' on "
"device '%s'\n",
name, bdrv_get_device_name(bs1));
break;
default:
- monitor_printf(mon, "Error %d while activating snapshot on"
+ output_channel_printf(err, "Error %d while activating snapshot on"
" '%s'\n", ret, bdrv_get_device_name(bs1));
break;
}
@@ -1317,7 +1339,7 @@ void do_loadvm(Monitor *mon, const char *name)
}
if (bdrv_get_info(bs, bdi) < 0 || bdi->vm_state_offset <= 0) {
- monitor_printf(mon, "Device %s does not support VM state snapshots\n",
+ output_channel_printf(err, "Device %s does not support VM state snapshots\n",
bdrv_get_device_name(bs));
return;
}
@@ -1330,13 +1352,13 @@ void do_loadvm(Monitor *mon, const char *name)
/* restore the VM state */
f = qemu_fopen_bdrv(bs, bdi->vm_state_offset, 0);
if (!f) {
- monitor_printf(mon, "Could not open VM state file\n");
+ output_channel_printf(err, "Could not open VM state file\n");
goto the_end;
}
ret = qemu_loadvm_state(f);
qemu_fclose(f);
if (ret < 0) {
- monitor_printf(mon, "Error %d while loading VM state\n", ret);
+ output_channel_printf(err, "Error %d while loading VM state\n", ret);
}
the_end:
if (saved_vm_running)
@@ -1345,12 +1367,18 @@ void do_loadvm(Monitor *mon, const char *name)
void do_delvm(Monitor *mon, const char *name)
{
+ OutputChannel *oc = output_channel_alloc(mon, monitor_output_channel_cb);
+ do_delvm_oc(oc, name);
+ output_channel_free(oc);
+}
+void do_delvm_oc(OutputChannel *err, const char *name)
+{
BlockDriverState *bs, *bs1;
int i, ret;
bs = get_bs_snapshots();
if (!bs) {
- monitor_printf(mon, "No block device supports snapshots\n");
+ output_channel_printf(err, "No block device supports snapshots\n");
return;
}
@@ -1360,12 +1388,12 @@ void do_delvm(Monitor *mon, const char *name)
ret = bdrv_snapshot_delete(bs1, name);
if (ret < 0) {
if (ret == -ENOTSUP)
- monitor_printf(mon,
- "Snapshots not supported on device '%s'\n",
- bdrv_get_device_name(bs1));
+ output_channel_printf(err,
+ "Snapshots not supported on device '%s'\n",
+ bdrv_get_device_name(bs1));
else
- monitor_printf(mon, "Error %d while deleting snapshot on "
- "'%s'\n", ret, bdrv_get_device_name(bs1));
+ output_channel_printf(err, "Error %d while deleting snapshot on "
+ "'%s'\n", ret, bdrv_get_device_name(bs1));
}
}
}
@@ -1373,6 +1401,13 @@ void do_delvm(Monitor *mon, const char *name)
void do_info_snapshots(Monitor *mon)
{
+ OutputChannel *oc = output_channel_alloc(mon, monitor_output_channel_cb);
+ do_info_snapshots_oc(oc, oc);
+ output_channel_free(oc);
+}
+
+void do_info_snapshots_oc(OutputChannel *out, OutputChannel *err)
+{
BlockDriverState *bs, *bs1;
QEMUSnapshotInfo *sn_tab, *sn;
int nb_sns, i;
@@ -1380,30 +1415,30 @@ void do_info_snapshots(Monitor *mon)
bs = get_bs_snapshots();
if (!bs) {
- monitor_printf(mon, "No available block device supports snapshots\n");
+ output_channel_printf(err, "No available block device supports snapshots\n");
return;
}
- monitor_printf(mon, "Snapshot devices:");
+ output_channel_printf(out, "Snapshot devices:");
for(i = 0; i <= nb_drives; i++) {
bs1 = drives_table[i].bdrv;
if (bdrv_can_snapshot(bs1)) {
if (bs == bs1)
- monitor_printf(mon, " %s", bdrv_get_device_name(bs1));
+ output_channel_printf(out, " %s", bdrv_get_device_name(bs1));
}
}
- monitor_printf(mon, "\n");
+ output_channel_printf(out, "\n");
nb_sns = bdrv_snapshot_list(bs, &sn_tab);
if (nb_sns < 0) {
- monitor_printf(mon, "bdrv_snapshot_list: error %d\n", nb_sns);
+ output_channel_printf(err, "bdrv_snapshot_list: error %d\n", nb_sns);
return;
}
- monitor_printf(mon, "Snapshot list (from %s):\n",
+ output_channel_printf(out, "Snapshot list (from %s):\n",
bdrv_get_device_name(bs));
- monitor_printf(mon, "%s\n", bdrv_snapshot_dump(buf, sizeof(buf), NULL));
+ output_channel_printf(out, "%s\n", bdrv_snapshot_dump(buf, sizeof(buf), NULL));
for(i = 0; i < nb_sns; i++) {
sn = &sn_tab[i];
- monitor_printf(mon, "%s\n", bdrv_snapshot_dump(buf, sizeof(buf), sn));
+ output_channel_printf(out, "%s\n", bdrv_snapshot_dump(buf, sizeof(buf), sn));
}
qemu_free(sn_tab);
}
diff --git a/sysemu.h b/sysemu.h
index 0c57d5d..fadf908 100644
--- a/sysemu.h
+++ b/sysemu.h
@@ -8,6 +8,7 @@
#include "qemu-timer.h"
#include "qdict.h"
#include "qerror.h"
+#include "outputchannel.h"
#ifdef _WIN32
#include <windows.h>
@@ -61,9 +62,13 @@ void qemu_system_powerdown(void);
void qemu_system_reset(void);
void do_savevm(Monitor *mon, const char *name);
+void do_savevm_oc(OutputChannel *err, const char *name);
void do_loadvm(Monitor *mon, const char *name);
+void do_loadvm_oc(OutputChannel *err, const char *name);
void do_delvm(Monitor *mon, const char *name);
+void do_delvm_oc(OutputChannel *err, const char *name);
void do_info_snapshots(Monitor *mon);
+void do_info_snapshots_oc(OutputChannel *out, OutputChannel *err);
void qemu_announce_self(void);