From nobody Sat Sep 27 11:43:23 2025 Delivered-To: importer@patchew.org Received-SPF: pass (zoho.com: domain of gnu.org designates 209.51.188.17 as permitted sender) client-ip=209.51.188.17; envelope-from=qemu-devel-bounces+importer=patchew.org@nongnu.org; helo=lists.gnu.org; Authentication-Results: mx.zohomail.com; spf=pass (zoho.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org Return-Path: Received: from lists.gnu.org (lists.gnu.org [209.51.188.17]) by mx.zohomail.com with SMTPS id 15470365870631006.4937075275814; Wed, 9 Jan 2019 04:23:07 -0800 (PST) Received: from localhost ([127.0.0.1]:48772 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1ghCsz-0003IT-Kj for importer@patchew.org; Wed, 09 Jan 2019 07:23:05 -0500 Received: from eggs.gnu.org ([209.51.188.92]:33366) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1ghCiq-0003rG-CI for qemu-devel@nongnu.org; Wed, 09 Jan 2019 07:12:38 -0500 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1ghCio-0001lh-Vr for qemu-devel@nongnu.org; Wed, 09 Jan 2019 07:12:36 -0500 Received: from mail.ispras.ru ([83.149.199.45]:37258) by eggs.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1ghCik-0001jo-LX for qemu-devel@nongnu.org; Wed, 09 Jan 2019 07:12:32 -0500 Received: from [127.0.1.1] (unknown [85.142.117.226]) by mail.ispras.ru (Postfix) with ESMTPSA id 6DCB454006A; Wed, 9 Jan 2019 15:12:29 +0300 (MSK) From: Pavel Dovgalyuk To: qemu-devel@nongnu.org Date: Wed, 09 Jan 2019 15:12:29 +0300 Message-ID: <154703594930.13472.14801510978993753998.stgit@pasha-VirtualBox> In-Reply-To: <154703587757.13472.3898702635363120794.stgit@pasha-VirtualBox> References: <154703587757.13472.3898702635363120794.stgit@pasha-VirtualBox> User-Agent: StGit/0.17.1-dirty MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable X-detected-operating-system: by eggs.gnu.org: GNU/Linux 3.x X-Received-From: 83.149.199.45 Subject: [Qemu-devel] [PATCH v9 12/21] replay: introduce breakpoint at the specified step X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.21 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: kwolf@redhat.com, peter.maydell@linaro.org, war2jordan@live.com, pavel.dovgaluk@ispras.ru, pbonzini@redhat.com, crosthwaite.peter@gmail.com, ciro.santilli@gmail.com, jasowang@redhat.com, quintela@redhat.com, armbru@redhat.com, mreitz@redhat.com, alex.bennee@linaro.org, maria.klimushenkova@ispras.ru, mst@redhat.com, kraxel@redhat.com, boost.lists@gmail.com, thomas.dullien@googlemail.com, dovgaluk@ispras.ru, artem.k.pisarenko@gmail.com, dgilbert@redhat.com, rth@twiddle.net Errors-To: qemu-devel-bounces+importer=patchew.org@nongnu.org Sender: "Qemu-devel" This patch introduces replay_break, replay_delete_break qmp and hmp commands. These commands allow stopping at the specified instruction. It may be useful for debugging when there are some known events that should be investigated. replay_break command has one argument - number of instructions executed since the start of the replay. replay_delete_break removes previously set breakpoint. Signed-off-by: Pavel Dovgalyuk -- v2: - renamed replay_break qmp command into replay-break (suggested by Eric Blake) v7: - introduces replay_delete_break command v9: - changed 'step' parameter name to 'icount' - moved json stuff to replay.json and updated the description (suggested by Markus Armbruster) --- hmp-commands.hx | 34 ++++++++++++++++++ hmp.h | 2 + qapi/replay.json | 36 +++++++++++++++++++ replay/replay-debugging.c | 85 +++++++++++++++++++++++++++++++++++++++++= ++++ replay/replay-internal.h | 4 ++ replay/replay.c | 17 +++++++++ 6 files changed, 178 insertions(+) diff --git a/hmp-commands.hx b/hmp-commands.hx index ba71558..6d04c02 100644 --- a/hmp-commands.hx +++ b/hmp-commands.hx @@ -1890,6 +1890,40 @@ Set QOM property @var{property} of object at locatio= n @var{path} to value @var{v ETEXI =20 { + .name =3D "replay_break", + .args_type =3D "icount:i", + .params =3D "icount", + .help =3D "sets breakpoint on the step specified by the icou= nt of the replay", + .cmd =3D hmp_replay_break, + }, + +STEXI +@item replay_break @var{icount} +@findex replay_break +Set breakpoint on the step of the replay specified by @var{icount}. +Execution stops when the specified @var{icount} is reached. +icount for the reference may be observed with 'info replay' command. +There could be at most one breakpoint. When breakpoint is set, the prior +one is removed. The breakpoints may be set only in replay mode and only +at the step in the future. +ETEXI + + { + .name =3D "replay_delete_break", + .args_type =3D "", + .params =3D "", + .help =3D "removes replay breakpoint", + .cmd =3D hmp_replay_delete_break, + }, + +STEXI +@item replay_delete_break +@findex replay_delete_break +Removes replay breakpoint which was previously set with replay_break. +The command is ignored when there are no replay breakpoints. +ETEXI + + { .name =3D "info", .args_type =3D "item:s?", .params =3D "[subcommand]", diff --git a/hmp.h b/hmp.h index d792149..c9b9b4f 100644 --- a/hmp.h +++ b/hmp.h @@ -149,5 +149,7 @@ void hmp_info_vm_generation_id(Monitor *mon, const QDic= t *qdict); void hmp_info_memory_size_summary(Monitor *mon, const QDict *qdict); void hmp_info_sev(Monitor *mon, const QDict *qdict); void hmp_info_replay(Monitor *mon, const QDict *qdict); +void hmp_replay_break(Monitor *mon, const QDict *qdict); +void hmp_replay_delete_break(Monitor *mon, const QDict *qdict); =20 #endif diff --git a/qapi/replay.json b/qapi/replay.json index d7e76cf..a63219c 100644 --- a/qapi/replay.json +++ b/qapi/replay.json @@ -63,3 +63,39 @@ ## { 'command': 'query-replay', 'returns': 'ReplayInfo' } + +## +# @replay-break: +# +# Set breakpoint on the step of the replay specified by @icount. +# Execution stops when the specified @icount is reached. +# icount for the reference may be obtained with @query-replay command. +# There could be at most one breakpoint. When breakpoint is set, the prior +# one is removed. The breakpoints may be set only in replay mode and only +# at the step in the future. +# +# @icount: execution step to stop at +# +# Since: 4.0 +# +# Example: +# +# -> { "execute": "replay-break", "data": { "icount": 220414 } } +# +## +{ 'command': 'replay-break', 'data': { 'icount': 'int' } } + +## +# @replay-delete-break: +# +# Removes replay breakpoint which was set with @replay-break. +# The command is ignored when there are no replay breakpoints. +# +# Since: 4.0 +# +# Example: +# +# -> { "execute": "replay-delete-break" } +# +## +{ 'command': 'replay-delete-break' } diff --git a/replay/replay-debugging.c b/replay/replay-debugging.c index 9956405..8ee64b2 100644 --- a/replay/replay-debugging.c +++ b/replay/replay-debugging.c @@ -16,6 +16,8 @@ #include "hmp.h" #include "monitor/monitor.h" #include "qapi/qapi-commands-replay.h" +#include "qapi/qmp/qdict.h" +#include "qemu/timer.h" =20 void hmp_info_replay(Monitor *mon, const QDict *qdict) { @@ -40,3 +42,86 @@ ReplayInfo *qmp_query_replay(Error **errp) retval->icount =3D replay_get_current_step(); return retval; } + +static void replay_break(uint64_t step, QEMUTimerCB callback, void *opaque) +{ + assert(replay_mode =3D=3D REPLAY_MODE_PLAY); + assert(replay_mutex_locked()); + assert(replay_break_step >=3D replay_get_current_step()); + assert(callback); + + replay_break_step =3D step; + + if (replay_break_timer) { + timer_del(replay_break_timer); + } else { + replay_break_timer =3D timer_new_ns(QEMU_CLOCK_REALTIME, + callback, opaque); + } +} + +static void replay_delete_break(void) +{ + assert(replay_mode =3D=3D REPLAY_MODE_PLAY); + assert(replay_mutex_locked()); + + if (replay_break_timer) { + timer_del(replay_break_timer); + timer_free(replay_break_timer); + replay_break_timer =3D NULL; + } + replay_break_step =3D -1ULL; +} + +static void replay_stop_vm(void *opaque) +{ + vm_stop(RUN_STATE_PAUSED); + replay_delete_break(); +} + +void qmp_replay_break(int64_t icount, Error **errp) +{ + if (replay_mode =3D=3D REPLAY_MODE_PLAY) { + if (icount >=3D replay_get_current_step()) { + replay_break(icount, replay_stop_vm, NULL); + } else { + error_setg(errp, "cannot set breakpoint at the step in the pas= t"); + } + } else { + error_setg(errp, "setting the breakpoint is allowed only in play m= ode"); + } +} + +void hmp_replay_break(Monitor *mon, const QDict *qdict) +{ + int64_t step =3D qdict_get_try_int(qdict, "icount", -1LL); + Error *err =3D NULL; + + qmp_replay_break(step, &err); + if (err) { + error_report_err(err); + error_free(err); + return; + } +} + +void qmp_replay_delete_break(Error **errp) +{ + if (replay_mode =3D=3D REPLAY_MODE_PLAY) { + replay_delete_break(); + } else { + error_setg(errp, "replay breakpoints are allowed only in play mode= "); + } +} + +void hmp_replay_delete_break(Monitor *mon, const QDict *qdict) +{ + Error *err =3D NULL; + + qmp_replay_delete_break(&err); + if (err) { + error_report_err(err); + error_free(err); + return; + } +} diff --git a/replay/replay-internal.h b/replay/replay-internal.h index af6f4d5..94b7e9b 100644 --- a/replay/replay-internal.h +++ b/replay/replay-internal.h @@ -91,6 +91,10 @@ extern ReplayState replay_state; =20 /* File for replay writing */ extern FILE *replay_file; +/* Step of the replay breakpoint */ +extern uint64_t replay_break_step; +/* Timer for the replay breakpoint callback */ +extern QEMUTimer *replay_break_timer; =20 void replay_put_byte(uint8_t byte); void replay_put_event(uint8_t event); diff --git a/replay/replay.c b/replay/replay.c index aa53411..3996499 100644 --- a/replay/replay.c +++ b/replay/replay.c @@ -34,6 +34,10 @@ static char *replay_filename; ReplayState replay_state; static GSList *replay_blockers; =20 +/* Replay breakpoints */ +uint64_t replay_break_step =3D -1ULL; +QEMUTimer *replay_break_timer; + bool replay_next_event_is(int event) { bool res =3D false; @@ -73,6 +77,13 @@ int replay_get_instructions(void) replay_mutex_lock(); if (replay_next_event_is(EVENT_INSTRUCTION)) { res =3D replay_state.instructions_count; + if (replay_break_step !=3D -1LL) { + uint64_t current =3D replay_get_current_step(); + assert(replay_break_step >=3D current); + if (current + res > replay_break_step) { + res =3D replay_break_step - current; + } + } } replay_mutex_unlock(); return res; @@ -99,6 +110,12 @@ void replay_account_executed_instructions(void) will be read from the log. */ qemu_notify_event(); } + /* Execution reached the break step */ + if (replay_break_step =3D=3D replay_state.current_step) { + /* Cannot make callback directly from the vCPU thread */ + timer_mod_ns(replay_break_timer, + qemu_clock_get_ns(QEMU_CLOCK_REALTIME)); + } } } }