Patchwork Add option to slow down qemu

login
register
mail settings
Submitter Wolfgang Mauerer
Date Jan. 11, 2013, 1:40 p.m.
Message ID <1357911633-12602-1-git-send-email-wolfgang.mauerer@siemens.com>
Download mbox | patch
Permalink /patch/211359/
State New
Headers show

Comments

Wolfgang Mauerer - Jan. 11, 2013, 1:40 p.m.
For slow targets and fast hosts, the emulation may be faster
than the actual hardware, which can be undesirable for various
reasons. Add a run-time option to slow down the emulation
by sleeping in the CPU emulation.

Signed-off-by: Wolfgang Mauerer <wolfgang.mauerer@siemens.com>
---
 cpus.c                |   34 ++++++++++++++++++++++++++++++++++
 include/qemu-common.h |    2 ++
 qemu-options.hx       |   13 +++++++++++++
 vl.c                  |   10 ++++++++++
 4 files changed, 59 insertions(+), 0 deletions(-)
Stefan Weil - Jan. 11, 2013, 7:58 p.m.
Am 11.01.2013 14:40, schrieb Wolfgang Mauerer:
> For slow targets and fast hosts, the emulation may be faster
> than the actual hardware, which can be undesirable for various
> reasons. Add a run-time option to slow down the emulation
> by sleeping in the CPU emulation.
>
> Signed-off-by: Wolfgang Mauerer<wolfgang.mauerer@siemens.com>
> ---
>   cpus.c                |   34 ++++++++++++++++++++++++++++++++++
>   include/qemu-common.h |    2 ++
>   qemu-options.hx       |   13 +++++++++++++
>   vl.c                  |   10 ++++++++++
>   4 files changed, 59 insertions(+), 0 deletions(-)
>
> diff --git a/cpus.c b/cpus.c
> index 4a7782a..41a9e0c 100644
> --- a/cpus.c
> +++ b/cpus.c
> @@ -106,6 +106,11 @@ static QEMUTimer *icount_warp_timer;
>   static int64_t vm_clock_warp_start;
>   static int64_t qemu_icount;
>
> +static double slowdown_factor = 0.0;
> +#ifndef _WIN32
> +static struct timespec slowdown_delay;
> +#endif
> +
>    

Hi,

slowdown_delay is used in configure_slowdown unconditionally,
so I don't expect that the _WIN32 case will compile.

What about using g_usleep? It avoids the conditional compilation.

Is the comparison of double value "slowdown_factor" with 0.0
time critical, or do all QEMU platforms have a fast FPU?

Setting a boolean value once and testing that value would
be much faster of course.

Cheers,
Stefan W.


>   typedef struct TimersState {
>       int64_t cpu_ticks_prev;
>       int64_t cpu_ticks_offset;
> @@ -385,6 +390,21 @@ void configure_icount(const char *option)
>                      qemu_get_clock_ns(vm_clock) + get_ticks_per_sec() / 10);
>   }
>
> +void configure_slowdown(const char *option)
> +{
> +    if (!option) {
> +        return;
> +    }
> +
> +    slowdown_factor = strtod(option, NULL)/100.0;
> +    /* We cannot provide speedups, obviously */
> +    if (slowdown_factor<  0) {
> +        slowdown_factor *= -1.0;
> +    }
> +
> +    slowdown_delay.tv_sec = 0;
> +}
> +
>   /***********************************************************/
>   void hw_error(const char *fmt, ...)
>   {
> @@ -1092,6 +1112,7 @@ void vm_stop_force_state(RunState state)
>   static int tcg_cpu_exec(CPUArchState *env)
>   {
>       int ret;
> +    int64_t ss, delta;
>   #ifdef CONFIG_PROFILER
>       int64_t ti;
>   #endif
> @@ -1112,7 +1133,20 @@ static int tcg_cpu_exec(CPUArchState *env)
>           env->icount_decr.u16.low = decr;
>           env->icount_extra = count;
>       }
> +    ss = qemu_get_clock_ns(rt_clock);
> +
>       ret = cpu_exec(env);
> +
> +    if (slowdown_factor>  0) {
> +        delta = qemu_get_clock_ns(rt_clock) - ss;
> +        delta *= slowdown_factor;
> +#ifdef _WIN32
> +        Sleep(delta/1000000l);
> +#else
> +        slowdown_delay.tv_nsec = delta;
> +        nanosleep(&slowdown_delay, NULL);
> +#endif
> +    }
>   #ifdef CONFIG_PROFILER
>       qemu_time += profile_getclock() - ti;
>   #endif
> diff --git a/include/qemu-common.h b/include/qemu-common.h
> index ca464bb..652abb9 100644
> --- a/include/qemu-common.h
> +++ b/include/qemu-common.h
> @@ -119,6 +119,8 @@ static inline char *realpath(const char *path, char *resolved_path)
>   void configure_icount(const char *option);
>   extern int use_icount;
>
> +void configure_slowdown(const char *option);
> +
>   /* FIXME: Remove NEED_CPU_H.  */
>   #ifndef NEED_CPU_H
>
> diff --git a/qemu-options.hx b/qemu-options.hx
> index 9df0cde..2f6580f 100644
> --- a/qemu-options.hx
> +++ b/qemu-options.hx
> @@ -2665,6 +2665,19 @@ order cores with complex cache hierarchies.  The number of instructions
>   executed often has little or no correlation with actual performance.
>   ETEXI
>
> +DEF("slowdown", HAS_ARG, QEMU_OPTION_slowdown, \
> +    "-slowdown s \n" \
> +    "                Slow down the CPU emulation by (approximately) s"
> +    "		     percent\n", QEMU_ARCH_ALL)
> +STEXI
> +@item -slowdown [@var{s}]
> +@findex -slowdown
> +Slow down the virtual CPU by approximately s percent. This makes it
> +possible to align the emulated machine's performance roughly with
> +the performance of physical entities, but does not provide identical
> +performance profiles since the emulation is not cycle accurate.
> +ETEXI
> +
>   DEF("watchdog", HAS_ARG, QEMU_OPTION_watchdog, \
>       "-watchdog i6300esb|ib700\n" \
>       "                enable virtual hardware watchdog [default=none]\n",
> diff --git a/vl.c b/vl.c
> index 79e5122..624551f 100644
> --- a/vl.c
> +++ b/vl.c
> @@ -2524,6 +2524,7 @@ int main(int argc, char **argv, char **envp)
>       int i;
>       int snapshot, linux_boot;
>       const char *icount_option = NULL;
> +    const char *slowdown_option = NULL;
>       const char *initrd_filename;
>       const char *kernel_filename, *kernel_cmdline;
>       char boot_devices[33] = "cad"; /* default to HD->floppy->CD-ROM */
> @@ -3402,6 +3403,9 @@ int main(int argc, char **argv, char **envp)
>               case QEMU_OPTION_icount:
>                   icount_option = optarg;
>                   break;
> +            case QEMU_OPTION_slowdown:
> +                slowdown_option = optarg;
> +                break;
>               case QEMU_OPTION_incoming:
>                   incoming = optarg;
>                   runstate_set(RUN_STATE_INMIGRATE);
> @@ -3765,6 +3769,12 @@ int main(int argc, char **argv, char **envp)
>       /* clean up network at qemu process termination */
>       atexit(&net_cleanup);
>
> +    if (slowdown_option&&  (kvm_enabled() || xen_enabled())) {
> +        fprintf(stderr, "-slowdown is not allowed with kvm or xen\n");
> +        exit(1);
> +    }
> +    configure_slowdown(slowdown_option);
> +
>       if (net_init_clients()<  0) {
>           exit(1);
>       }
>
Wolfgang Mauerer - Jan. 17, 2013, 5:14 p.m.
On 11/01/13 20:58, Stefan Weil wrote:
> Am 11.01.2013 14:40, schrieb Wolfgang Mauerer:
>> > For slow targets and fast hosts, the emulation may be faster
>> > than the actual hardware, which can be undesirable for various
>> > reasons. Add a run-time option to slow down the emulation
>> > by sleeping in the CPU emulation.
>> >
>> > Signed-off-by: Wolfgang Mauerer<wolfgang.mauerer@siemens.com>
>> > ---
>> >   cpus.c                |   34 ++++++++++++++++++++++++++++++++++
>> >   include/qemu-common.h |    2 ++
>> >   qemu-options.hx       |   13 +++++++++++++
>> >   vl.c                  |   10 ++++++++++
>> >   4 files changed, 59 insertions(+), 0 deletions(-)
>> >
>> > diff --git a/cpus.c b/cpus.c
>> > index 4a7782a..41a9e0c 100644
>> > --- a/cpus.c
>> > +++ b/cpus.c
>> > @@ -106,6 +106,11 @@ static QEMUTimer *icount_warp_timer;
>> >   static int64_t vm_clock_warp_start;
>> >   static int64_t qemu_icount;
>> >
>> > +static double slowdown_factor = 0.0;
>> > +#ifndef _WIN32
>> > +static struct timespec slowdown_delay;
>> > +#endif
>> > +
>> >    
> Hi,
> 
> slowdown_delay is used in configure_slowdown unconditionally,
> so I don't expect that the _WIN32 case will compile.
> 
> What about using g_usleep? It avoids the conditional compilation.
> 
> Is the comparison of double value "slowdown_factor" with 0.0
> time critical, or do all QEMU platforms have a fast FPU?
> 
> Setting a boolean value once and testing that value would
> be much faster of course.

I'm not sure if the floating point comparison has a significant
impact on any important platform, but using a boolean variable
makes the code more similar to the icount mechanism, so I've
changed it accordingly. Using g_usleep is a good idea to let the
conditional _WIN32 parts go away. Revised patch follows.

Cheers, Wolfgang

Patch

diff --git a/cpus.c b/cpus.c
index 4a7782a..41a9e0c 100644
--- a/cpus.c
+++ b/cpus.c
@@ -106,6 +106,11 @@  static QEMUTimer *icount_warp_timer;
 static int64_t vm_clock_warp_start;
 static int64_t qemu_icount;
 
+static double slowdown_factor = 0.0;
+#ifndef _WIN32
+static struct timespec slowdown_delay;
+#endif
+
 typedef struct TimersState {
     int64_t cpu_ticks_prev;
     int64_t cpu_ticks_offset;
@@ -385,6 +390,21 @@  void configure_icount(const char *option)
                    qemu_get_clock_ns(vm_clock) + get_ticks_per_sec() / 10);
 }
 
+void configure_slowdown(const char *option)
+{
+    if (!option) {
+        return;
+    }
+
+    slowdown_factor = strtod(option, NULL)/100.0;
+    /* We cannot provide speedups, obviously */
+    if (slowdown_factor < 0) {
+        slowdown_factor *= -1.0;
+    }
+
+    slowdown_delay.tv_sec = 0;
+}
+
 /***********************************************************/
 void hw_error(const char *fmt, ...)
 {
@@ -1092,6 +1112,7 @@  void vm_stop_force_state(RunState state)
 static int tcg_cpu_exec(CPUArchState *env)
 {
     int ret;
+    int64_t ss, delta;
 #ifdef CONFIG_PROFILER
     int64_t ti;
 #endif
@@ -1112,7 +1133,20 @@  static int tcg_cpu_exec(CPUArchState *env)
         env->icount_decr.u16.low = decr;
         env->icount_extra = count;
     }
+    ss = qemu_get_clock_ns(rt_clock);
+
     ret = cpu_exec(env);
+
+    if (slowdown_factor > 0) {
+        delta = qemu_get_clock_ns(rt_clock) - ss;
+        delta *= slowdown_factor;
+#ifdef _WIN32
+        Sleep(delta/1000000l);
+#else
+        slowdown_delay.tv_nsec = delta;
+        nanosleep(&slowdown_delay, NULL);
+#endif
+    }
 #ifdef CONFIG_PROFILER
     qemu_time += profile_getclock() - ti;
 #endif
diff --git a/include/qemu-common.h b/include/qemu-common.h
index ca464bb..652abb9 100644
--- a/include/qemu-common.h
+++ b/include/qemu-common.h
@@ -119,6 +119,8 @@  static inline char *realpath(const char *path, char *resolved_path)
 void configure_icount(const char *option);
 extern int use_icount;
 
+void configure_slowdown(const char *option);
+
 /* FIXME: Remove NEED_CPU_H.  */
 #ifndef NEED_CPU_H
 
diff --git a/qemu-options.hx b/qemu-options.hx
index 9df0cde..2f6580f 100644
--- a/qemu-options.hx
+++ b/qemu-options.hx
@@ -2665,6 +2665,19 @@  order cores with complex cache hierarchies.  The number of instructions
 executed often has little or no correlation with actual performance.
 ETEXI
 
+DEF("slowdown", HAS_ARG, QEMU_OPTION_slowdown, \
+    "-slowdown s \n" \
+    "                Slow down the CPU emulation by (approximately) s"
+    "		     percent\n", QEMU_ARCH_ALL)
+STEXI
+@item -slowdown [@var{s}]
+@findex -slowdown
+Slow down the virtual CPU by approximately s percent. This makes it
+possible to align the emulated machine's performance roughly with
+the performance of physical entities, but does not provide identical
+performance profiles since the emulation is not cycle accurate.
+ETEXI
+
 DEF("watchdog", HAS_ARG, QEMU_OPTION_watchdog, \
     "-watchdog i6300esb|ib700\n" \
     "                enable virtual hardware watchdog [default=none]\n",
diff --git a/vl.c b/vl.c
index 79e5122..624551f 100644
--- a/vl.c
+++ b/vl.c
@@ -2524,6 +2524,7 @@  int main(int argc, char **argv, char **envp)
     int i;
     int snapshot, linux_boot;
     const char *icount_option = NULL;
+    const char *slowdown_option = NULL;
     const char *initrd_filename;
     const char *kernel_filename, *kernel_cmdline;
     char boot_devices[33] = "cad"; /* default to HD->floppy->CD-ROM */
@@ -3402,6 +3403,9 @@  int main(int argc, char **argv, char **envp)
             case QEMU_OPTION_icount:
                 icount_option = optarg;
                 break;
+            case QEMU_OPTION_slowdown:
+                slowdown_option = optarg;
+                break;
             case QEMU_OPTION_incoming:
                 incoming = optarg;
                 runstate_set(RUN_STATE_INMIGRATE);
@@ -3765,6 +3769,12 @@  int main(int argc, char **argv, char **envp)
     /* clean up network at qemu process termination */
     atexit(&net_cleanup);
 
+    if (slowdown_option && (kvm_enabled() || xen_enabled())) {
+        fprintf(stderr, "-slowdown is not allowed with kvm or xen\n");
+        exit(1);
+    }
+    configure_slowdown(slowdown_option);
+
     if (net_init_clients() < 0) {
         exit(1);
     }