qemu-devel.nongnu.org archive mirror
 help / color / mirror / Atom feed
From: Pierrick Bouvier <pierrick.bouvier@linaro.org>
To: Rowan Hart <rowanbhart@gmail.com>
Cc: "Alexandre Iooss" <erdnaxe@crans.org>,
	"Mahmoud Mandour" <ma.mandourr@gmail.com>,
	qemu-devel@nongnu.org, "Alex Bennée" <alex.bennee@linaro.org>
Subject: Re: [PATCH] plugins: add plugin API to read guest memory
Date: Mon, 26 Aug 2024 12:11:08 -0700	[thread overview]
Message-ID: <38ca2658-efef-4cf2-b88b-86574920dc01@linaro.org> (raw)
In-Reply-To: <bffcb1ae-151f-4cc5-be32-0221edd7539d@gmail.com>

On 8/26/24 11:47, Rowan Hart wrote:
> Alex & Pierrick,
> 
> Thank you for the feedback! This is my first contribution to QEMU, so I'm glad
> it at least passes the initial smell test :)
> 

Sure, no worries, you did well!

>> I'll make my comments in this patch, but for v2, please split those individual
>> commits, and a cover letter, describing your changes (https://github.com/
>> stefanha/git-publish is a great tool if you want to easily push series).
> 
> Will do! Mailing list dev is new to me but I will do a practice run to try and
> not mess it up.
> 

If you already configured git send-email, git-publish just does all the 
rest for you (collecting reviewers, prepare patches, and send using git 
send-email). In more, it tracks you different versions, so it's pretty 
easy to iterate on a series.

> 
> 
>>> +QEMU_PLUGIN_API
>>> +GByteArray *qemu_plugin_read_memory_vaddr(uint64_t addr,
>>> +                                          size_t len);
>>> +
>>
>> IMHO, it would be better to pass the buffer as a parameter, instead of allocating a new one everytime.
>>
>> bool qemu_plugin_read_memory_vaddr(uint64_t addr, GByteArray *buf, size_t len).
> 
> Sure, this makes perfect sense. I considered both and picked wrong so not hard
> to change.
> 
>>>    /**
>>>     * qemu_plugin_read_register() - read register for current vCPU
>>>     *
>>> diff --git a/plugins/api.c b/plugins/api.c
>>> index 2ff13d09de..f210ca166a 100644
>>> --- a/plugins/api.c
>>> +++ b/plugins/api.c
>>> @@ -527,6 +527,27 @@ GArray *qemu_plugin_get_registers(void)
>>>        return create_register_handles(regs);
>>>    }
>>>    +GByteArray *qemu_plugin_read_memory_vaddr(vaddr addr, size_t len)
>>> +{
>>> +    g_assert(current_cpu);
>>> +
>>> +    if (len == 0) {
>>> +        return NULL;
>>> +    }
>>> +
>>> +    GByteArray *buf = g_byte_array_sized_new(len);
>>> +    g_byte_array_set_size(buf, len);
>>> +
>>> +    int result = cpu_memory_rw_debug(current_cpu, addr, buf->data, buf->len, 0);
>>> +
>>> +    if (result < 0) {
>>> +        g_byte_array_unref(buf);
>>> +        return NULL;
>>> +    }
>>> +
>>> +    return buf;
>>> +}
>>> +
>>>    int qemu_plugin_read_register(struct qemu_plugin_register *reg, GByteArray *buf)
>>>    {
>>>        g_assert(current_cpu);
>>> diff --git a/plugins/qemu-plugins.symbols b/plugins/qemu-plugins.symbols
>>> index ca773d8d9f..3ad479a924 100644
>>> --- a/plugins/qemu-plugins.symbols
>>> +++ b/plugins/qemu-plugins.symbols
>>> @@ -20,6 +20,7 @@
>>>      qemu_plugin_num_vcpus;
>>>      qemu_plugin_outs;
>>>      qemu_plugin_path_to_binary;
>>> +  qemu_plugin_read_memory_vaddr;
>>>      qemu_plugin_read_register;
>>>      qemu_plugin_register_atexit_cb;
>>>      qemu_plugin_register_flush_cb;
>>> diff --git a/tests/tcg/plugins/mem.c b/tests/tcg/plugins/mem.c
>>> index b650dddcce..c65d48680b 100644
>>> --- a/tests/tcg/plugins/mem.c
>>> +++ b/tests/tcg/plugins/mem.c
>>> @@ -24,7 +24,7 @@ typedef struct {
>>>    static struct qemu_plugin_scoreboard *counts;
>>>    static qemu_plugin_u64 mem_count;
>>>    static qemu_plugin_u64 io_count;
>>> -static bool do_inline, do_callback;
>>> +static bool do_inline, do_callback, do_read;
>>>    static bool do_haddr;
>>>    static enum qemu_plugin_mem_rw rw = QEMU_PLUGIN_MEM_RW;
>>>    @@ -58,6 +58,30 @@ static void vcpu_mem(unsigned int cpu_index, qemu_plugin_meminfo_t meminfo,
>>>        } else {
>>>            qemu_plugin_u64_add(mem_count, cpu_index, 1);
>>>        }
>>> +
>>> +    if (do_read) {
>>> +        size_t size = qemu_plugin_mem_size_shift(meminfo);
>>> +        GByteArray *data = qemu_plugin_read_memory_vaddr(vaddr, size);
>>> +
>>> +        if (data) {
>>> +            g_autoptr(GString) out = g_string_new("");
>>> +
>>> +            if (qemu_plugin_mem_is_store(meminfo)) {
>>> +                g_string_append(out, "store: ");
>>> +            } else {
>>> +                g_string_append(out, "load: ");
>>> +            }
>>> +
>>> +            g_string_append_printf(out, "vaddr: 0x%" PRIx64 " data: 0x",
>>> +                                   vaddr);
>>> +            for (size_t i = 0; i < data->len; i++) {
>>> +                g_string_append_printf(out, "%02x", data->data[i]);
>>> +            }
>>> +            g_string_append(out, "\n");
>>> +            qemu_plugin_outs(out->str);
>>> +            g_byte_array_free(data, TRUE);
>>> +        }
>>> +    }
>>
>> See other series, merging an API for getting values read on a memory access. It's a better fit to implement this. So I think it's better to drop this plugin modification.
> 
> Ok, will drop this one and keep the modification to the syscalls plugin only as
> an example of how to use the API.
> 

Good, thanks!

>>>    }
>>>      static void vcpu_tb_trans(qemu_plugin_id_t id, struct qemu_plugin_tb *tb)
>>> @@ -86,7 +110,6 @@ QEMU_PLUGIN_EXPORT int qemu_plugin_install(qemu_plugin_id_t id,
>>>                                               const qemu_info_t *info,
>>>                                               int argc, char **argv)
>>>    {
>>> -
>>>        for (int i = 0; i < argc; i++) {
>>>            char *opt = argv[i];
>>>            g_auto(GStrv) tokens = g_strsplit(opt, "=", 2);
>>> @@ -117,6 +140,11 @@ QEMU_PLUGIN_EXPORT int qemu_plugin_install(qemu_plugin_id_t id,
>>>                    fprintf(stderr, "boolean argument parsing failed: %s\n", opt);
>>>                    return -1;
>>>                }
>>> +        } else if (g_strcmp0(tokens[0], "read") == 0) {
>>> +            if (!qemu_plugin_bool_parse(tokens[0], tokens[1], &do_read)) {
>>> +                fprintf(stderr, "boolean argument parsing failed: %s\n", opt);
>>> +                return -1;
>>> +            }
>>>            } else {
>>>                fprintf(stderr, "option parsing failed: %s\n", opt);
>>>                return -1;
>>> @@ -129,6 +157,11 @@ QEMU_PLUGIN_EXPORT int qemu_plugin_install(qemu_plugin_id_t id,
>>>            return -1;
>>>        }
>>>    +    if (do_read && !do_callback) {
>>> +        fprintf(stderr, "can't enable memory reading without callback\n");
>>> +        return -1;
>>> +    }
>>> +
>>>        counts = qemu_plugin_scoreboard_new(sizeof(CPUCount));
>>>        mem_count = qemu_plugin_scoreboard_u64_in_struct(
>>>            counts, CPUCount, mem_count);
>>> diff --git a/tests/tcg/plugins/syscall.c b/tests/tcg/plugins/syscall.c
>>> index 72e1a5bf90..67c7c404c4 100644
>>> --- a/tests/tcg/plugins/syscall.c
>>> +++ b/tests/tcg/plugins/syscall.c
>>> @@ -22,8 +22,56 @@ typedef struct {
>>>        int64_t errors;
>>>    } SyscallStats;
>>>    +struct SyscallInfo {
>>> +    const char *name;
>>> +    int64_t write_sysno;
>>> +};
>>> +
>>> +const struct SyscallInfo arch_syscall_info[] = {
>>> +    { "aarch64", 64 },
>>> +    { "aarch64_be", 64 },
>>> +    { "alpha", 4 },
>>> +    { "arm", 4 },
>>> +    { "armeb", 4 },
>>> +    { "avr", -1 },
>>> +    { "cris", -1 },
>>> +    { "hexagon", 64 },
>>> +    { "hppa", -1 },
>>> +    { "i386", 4 },
>>> +    { "loongarch64", -1 },
>>> +    { "m68k", 4 },
>>> +    { "microblaze", 4 },
>>> +    { "microblazeel", 4 },
>>> +    { "mips", 1 },
>>> +    { "mips64", 1 },
>>> +    { "mips64el", 1 },
>>> +    { "mipsel", 1 },
>>> +    { "mipsn32", 1 },
>>> +    { "mipsn32el", 1 },
>>> +    { "or1k", -1 },
>>> +    { "ppc", 4 },
>>> +    { "ppc64", 4 },
>>> +    { "ppc64le", 4 },
>>> +    { "riscv32", 64 },
>>> +    { "riscv64", 64 },
>>> +    { "rx", -1 },
>>> +    { "s390x", -1 },
>>> +    { "sh4", -1 },
>>> +    { "sh4eb", -1 },
>>> +    { "sparc", 4 },
>>> +    { "sparc32plus", 4 },
>>> +    { "sparc64", 4 },
>>> +    { "tricore", -1 },
>>> +    { "x86_64", 1 },
>>> +    { "xtensa", 13 },
>>> +    { "xtensaeb", 13 },
>>> +    { NULL, -1 },
>>> +};
>>> +
>>>    static GMutex lock;
>>>    static GHashTable *statistics;
>>> +static bool do_log_writes;
>>> +static int64_t write_sysno = -1;
>>>      static SyscallStats *get_or_create_entry(int64_t num)
>>>    {
>>> @@ -54,6 +102,51 @@ static void vcpu_syscall(qemu_plugin_id_t id, unsigned int vcpu_index,
>>>            g_autofree gchar *out = g_strdup_printf("syscall #%" PRIi64 "\n", num);
>>>            qemu_plugin_outs(out);
>>>        }
>>> +
>>> +    if (do_log_writes && num == write_sysno) {
>>> +        GByteArray *data = qemu_plugin_read_memory_vaddr(a2, a3);
>>> +
>>> +        g_autoptr(GString) out = g_string_new("");
>>> +
>>> +        size_t rows = (data->len % 16 == 0)
>>> +            ? (data->len / 16)
>>> +            : ((data->len / 16) + 1);
>>> +        for (size_t row = 0; row < rows; row++) {
>>> +            size_t len = (rows != data->len / 16 && row == rows - 1)
>>> +                ? (data->len % 16)
>>> +                : 16;
>>> +            for (size_t i = 0; i < len; i++) {
>>> +                g_string_append_printf(out, "%02x ",
>>> +                    data->data[(row * 16) + i]);
>>> +                if (i != 0 && i % 4 == 0) {
>>> +                    g_string_append(out, " ");
>>> +                }
>>> +            }
>>> +            for (size_t i = len; i < 16; i++) {
>>> +                g_string_append(out, "   ");
>>> +                if (i != 0 && i % 4 == 0) {
>>> +                    g_string_append(out, " ");
>>> +                }
>>> +            }
>>> +            g_string_append(out, " | ");
>>> +            for (size_t i = 0; i < len; i++) {
>>> +                g_string_append_printf(out,
>>> +                    (data->data[(row * 16) + i] >= 0x21
>>> +                        && data->data[(row * 16) + i] <= 0x7e)
>>> +                    ? "%c"
>>> +                    : ".", data->data[(row * 16) + i]);
>>> +                if (i != 0 && i % 4 == 0) {
>>> +                    g_string_append(out, " ");
>>> +                }
>>> +            }
>>> +            g_string_append(out, "\n");
>>> +        }
>>
>> Could you add a comment to show what is expected format? From the code, with all these loops and ternary expressions, it's not easy to follow.
> 
> Indeed. I will just generally simplify this
> 
> Thanks!
> 

You're welcome.

> -Rowan

Pierrick


      reply	other threads:[~2024-08-26 19:12 UTC|newest]

Thread overview: 7+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2024-08-21 23:56 [PATCH] plugins: add plugin API to read guest memory Rowan Hart
2024-08-22 20:33 ` Pierrick Bouvier
2024-08-22 20:37   ` Pierrick Bouvier
2024-08-23 10:29   ` Alex Bennée
2024-08-26 18:54     ` Rowan Hart
2024-08-26 18:47   ` Rowan Hart
2024-08-26 19:11     ` Pierrick Bouvier [this message]

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=38ca2658-efef-4cf2-b88b-86574920dc01@linaro.org \
    --to=pierrick.bouvier@linaro.org \
    --cc=alex.bennee@linaro.org \
    --cc=erdnaxe@crans.org \
    --cc=ma.mandourr@gmail.com \
    --cc=qemu-devel@nongnu.org \
    --cc=rowanbhart@gmail.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).