qemu-devel.nongnu.org archive mirror
 help / color / mirror / Atom feed
* [PATCH V4 0/5] string list functions
@ 2024-01-12 22:49 Steve Sistare
  2024-01-12 22:49 ` [PATCH V4 1/5] util: strList_from_string Steve Sistare
                   ` (4 more replies)
  0 siblings, 5 replies; 19+ messages in thread
From: Steve Sistare @ 2024-01-12 22:49 UTC (permalink / raw)
  To: qemu-devel
  Cc: Markus Armbruster, Michael Roth, Peter Xu, Fabiano Rosas,
	Marc-André Lureau, Steve Sistare

Add some handy string list functions for general use, and use them in
live migration functions.  These will also be needed for cpr exec mode.

Changes in V4:
  * added exec migration patch

Steve Sistare (5):
  util: strList_from_string
  qapi: QAPI_LIST_LENGTH
  util: strv_from_strList
  util: strList unit tests
  migration: simplify exec migration functions

 include/monitor/hmp.h     |  1 -
 include/qapi/util.h       | 13 ++++++++
 include/qemu/strList.h    | 30 ++++++++++++++++++
 migration/exec.c          | 58 +++++-----------------------------
 monitor/hmp-cmds.c        | 19 -----------
 net/net-hmp-cmds.c        |  3 +-
 stats/stats-hmp-cmds.c    |  3 +-
 tests/unit/meson.build    |  1 +
 tests/unit/test-strList.c | 80 +++++++++++++++++++++++++++++++++++++++++++++++
 util/meson.build          |  1 +
 util/strList.c            | 38 ++++++++++++++++++++++
 11 files changed, 175 insertions(+), 72 deletions(-)
 create mode 100644 include/qemu/strList.h
 create mode 100644 tests/unit/test-strList.c
 create mode 100644 util/strList.c

-- 
1.8.3.1



^ permalink raw reply	[flat|nested] 19+ messages in thread

* [PATCH V4 1/5] util: strList_from_string
  2024-01-12 22:49 [PATCH V4 0/5] string list functions Steve Sistare
@ 2024-01-12 22:49 ` Steve Sistare
  2024-02-21 13:29   ` Markus Armbruster
  2024-01-12 22:49 ` [PATCH V4 2/5] qapi: QAPI_LIST_LENGTH Steve Sistare
                   ` (3 subsequent siblings)
  4 siblings, 1 reply; 19+ messages in thread
From: Steve Sistare @ 2024-01-12 22:49 UTC (permalink / raw)
  To: qemu-devel
  Cc: Markus Armbruster, Michael Roth, Peter Xu, Fabiano Rosas,
	Marc-André Lureau, Steve Sistare

Generalize hmp_split_at_comma() to take any delimiter string, rename
as strList_from_string(), and move it to util/strList.c.

No functional change.

Signed-off-by: Steve Sistare <steven.sistare@oracle.com>
---
 include/monitor/hmp.h  |  1 -
 include/qemu/strList.h | 24 ++++++++++++++++++++++++
 monitor/hmp-cmds.c     | 19 -------------------
 net/net-hmp-cmds.c     |  3 ++-
 stats/stats-hmp-cmds.c |  3 ++-
 util/meson.build       |  1 +
 util/strList.c         | 24 ++++++++++++++++++++++++
 7 files changed, 53 insertions(+), 22 deletions(-)
 create mode 100644 include/qemu/strList.h
 create mode 100644 util/strList.c

diff --git a/include/monitor/hmp.h b/include/monitor/hmp.h
index 13f9a2d..2df661e 100644
--- a/include/monitor/hmp.h
+++ b/include/monitor/hmp.h
@@ -19,7 +19,6 @@
 
 bool hmp_handle_error(Monitor *mon, Error *err);
 void hmp_help_cmd(Monitor *mon, const char *name);
-strList *hmp_split_at_comma(const char *str);
 
 void hmp_info_name(Monitor *mon, const QDict *qdict);
 void hmp_info_version(Monitor *mon, const QDict *qdict);
diff --git a/include/qemu/strList.h b/include/qemu/strList.h
new file mode 100644
index 0000000..010237f
--- /dev/null
+++ b/include/qemu/strList.h
@@ -0,0 +1,24 @@
+/*
+ * Copyright (c) 2022 - 2024 Oracle and/or its affiliates.
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or later.
+ * See the COPYING file in the top-level directory.
+ */
+
+#ifndef QEMU_STR_LIST_H
+#define QEMU_STR_LIST_H
+
+#include "qapi/qapi-builtin-types.h"
+
+/*
+ * Break @in into a strList using the delimiter string @delim.
+ * The delimiter is not included in the result.
+ * Return NULL if @in is NULL or an empty string.
+ * A leading, trailing, or consecutive delimiter produces an
+ * empty string at that position in the output.
+ * All strings are g_strdup'd, and the result can be freed
+ * using qapi_free_strList.
+ */
+strList *strList_from_string(const char *in, const char *delim);
+
+#endif
diff --git a/monitor/hmp-cmds.c b/monitor/hmp-cmds.c
index 871898a..66b68a0 100644
--- a/monitor/hmp-cmds.c
+++ b/monitor/hmp-cmds.c
@@ -38,25 +38,6 @@ bool hmp_handle_error(Monitor *mon, Error *err)
     return false;
 }
 
-/*
- * Split @str at comma.
- * A null @str defaults to "".
- */
-strList *hmp_split_at_comma(const char *str)
-{
-    char **split = g_strsplit(str ?: "", ",", -1);
-    strList *res = NULL;
-    strList **tail = &res;
-    int i;
-
-    for (i = 0; split[i]; i++) {
-        QAPI_LIST_APPEND(tail, split[i]);
-    }
-
-    g_free(split);
-    return res;
-}
-
 void hmp_info_name(Monitor *mon, const QDict *qdict)
 {
     NameInfo *info;
diff --git a/net/net-hmp-cmds.c b/net/net-hmp-cmds.c
index 41d326b..e893801 100644
--- a/net/net-hmp-cmds.c
+++ b/net/net-hmp-cmds.c
@@ -26,6 +26,7 @@
 #include "qemu/config-file.h"
 #include "qemu/help_option.h"
 #include "qemu/option.h"
+#include "qemu/strList.h"
 
 void hmp_info_network(Monitor *mon, const QDict *qdict)
 {
@@ -72,7 +73,7 @@ void hmp_announce_self(Monitor *mon, const QDict *qdict)
                                             migrate_announce_params());
 
     qapi_free_strList(params->interfaces);
-    params->interfaces = hmp_split_at_comma(interfaces_str);
+    params->interfaces = strList_from_string(interfaces_str, ",");
     params->has_interfaces = params->interfaces != NULL;
     params->id = g_strdup(id);
     qmp_announce_self(params, NULL);
diff --git a/stats/stats-hmp-cmds.c b/stats/stats-hmp-cmds.c
index 1f91bf8..428c0e6 100644
--- a/stats/stats-hmp-cmds.c
+++ b/stats/stats-hmp-cmds.c
@@ -10,6 +10,7 @@
 #include "monitor/hmp.h"
 #include "monitor/monitor.h"
 #include "qemu/cutils.h"
+#include "qemu/strList.h"
 #include "hw/core/cpu.h"
 #include "qapi/qmp/qdict.h"
 #include "qapi/error.h"
@@ -176,7 +177,7 @@ static StatsFilter *stats_filter(StatsTarget target, const char *names,
             request->provider = provider_idx;
             if (names && !g_str_equal(names, "*")) {
                 request->has_names = true;
-                request->names = hmp_split_at_comma(names);
+                request->names = strList_from_string(names, ",");
             }
             QAPI_LIST_PREPEND(request_list, request);
         }
diff --git a/util/meson.build b/util/meson.build
index af3bf56..e1d1e1f 100644
--- a/util/meson.build
+++ b/util/meson.build
@@ -1,4 +1,5 @@
 util_ss.add(files('osdep.c', 'cutils.c', 'unicode.c', 'qemu-timer-common.c'))
+util_ss.add(files('strList.c'))
 util_ss.add(files('thread-context.c'), numa)
 if not config_host_data.get('CONFIG_ATOMIC64')
   util_ss.add(files('atomic64.c'))
diff --git a/util/strList.c b/util/strList.c
new file mode 100644
index 0000000..7991de3
--- /dev/null
+++ b/util/strList.c
@@ -0,0 +1,24 @@
+/*
+ * Copyright (c) 2023 Red Hat, Inc.
+ * Copyright (c) 2022 - 2024 Oracle and/or its affiliates.
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or later.
+ * See the COPYING file in the top-level directory.
+ */
+
+#include "qemu/osdep.h"
+#include "qemu/strList.h"
+
+strList *strList_from_string(const char *str, const char *delim)
+{
+    g_autofree char **split = g_strsplit(str ?: "", delim, -1);
+    strList *res = NULL;
+    strList **tail = &res;
+    int i;
+
+    for (i = 0; split[i]; i++) {
+        QAPI_LIST_APPEND(tail, split[i]);
+    }
+
+    return res;
+}
-- 
1.8.3.1



^ permalink raw reply related	[flat|nested] 19+ messages in thread

* [PATCH V4 2/5] qapi: QAPI_LIST_LENGTH
  2024-01-12 22:49 [PATCH V4 0/5] string list functions Steve Sistare
  2024-01-12 22:49 ` [PATCH V4 1/5] util: strList_from_string Steve Sistare
@ 2024-01-12 22:49 ` Steve Sistare
  2024-02-21 13:29   ` Markus Armbruster
  2024-01-12 22:49 ` [PATCH V4 3/5] util: strv_from_strList Steve Sistare
                   ` (2 subsequent siblings)
  4 siblings, 1 reply; 19+ messages in thread
From: Steve Sistare @ 2024-01-12 22:49 UTC (permalink / raw)
  To: qemu-devel
  Cc: Markus Armbruster, Michael Roth, Peter Xu, Fabiano Rosas,
	Marc-André Lureau, Steve Sistare

Signed-off-by: Steve Sistare <steven.sistare@oracle.com>
Reviewed-by: Marc-André Lureau <marcandre.lureau@redhat.com>
---
 include/qapi/util.h | 13 +++++++++++++
 1 file changed, 13 insertions(+)

diff --git a/include/qapi/util.h b/include/qapi/util.h
index 81a2b13..e1b8b1d 100644
--- a/include/qapi/util.h
+++ b/include/qapi/util.h
@@ -56,4 +56,17 @@ int parse_qapi_name(const char *name, bool complete);
     (tail) = &(*(tail))->next; \
 } while (0)
 
+/*
+ * For any GenericList @list, return its length.
+ */
+#define QAPI_LIST_LENGTH(list) \
+    ({ \
+        int len = 0; \
+        typeof(list) elem; \
+        for (elem = list; elem != NULL; elem = elem->next) { \
+            len++; \
+        } \
+        len; \
+    })
+
 #endif
-- 
1.8.3.1



^ permalink raw reply related	[flat|nested] 19+ messages in thread

* [PATCH V4 3/5] util: strv_from_strList
  2024-01-12 22:49 [PATCH V4 0/5] string list functions Steve Sistare
  2024-01-12 22:49 ` [PATCH V4 1/5] util: strList_from_string Steve Sistare
  2024-01-12 22:49 ` [PATCH V4 2/5] qapi: QAPI_LIST_LENGTH Steve Sistare
@ 2024-01-12 22:49 ` Steve Sistare
  2024-02-21 13:14   ` Markus Armbruster
  2024-01-12 22:49 ` [PATCH V4 4/5] util: strList unit tests Steve Sistare
  2024-01-12 22:49 ` [PATCH V4 5/5] migration: simplify exec migration functions Steve Sistare
  4 siblings, 1 reply; 19+ messages in thread
From: Steve Sistare @ 2024-01-12 22:49 UTC (permalink / raw)
  To: qemu-devel
  Cc: Markus Armbruster, Michael Roth, Peter Xu, Fabiano Rosas,
	Marc-André Lureau, Steve Sistare

Signed-off-by: Steve Sistare <steven.sistare@oracle.com>
Reviewed-by: Marc-André Lureau <marcandre.lureau@redhat.com>
---
 include/qemu/strList.h |  6 ++++++
 util/strList.c         | 14 ++++++++++++++
 2 files changed, 20 insertions(+)

diff --git a/include/qemu/strList.h b/include/qemu/strList.h
index 010237f..4b86aa6 100644
--- a/include/qemu/strList.h
+++ b/include/qemu/strList.h
@@ -21,4 +21,10 @@
  */
 strList *strList_from_string(const char *in, const char *delim);
 
+/*
+ * Produce and return a NULL-terminated array of strings from @args.
+ * The result is g_malloc'd and all strings are g_strdup'd.
+ */
+GStrv strv_from_strList(const strList *args);
+
 #endif
diff --git a/util/strList.c b/util/strList.c
index 7991de3..bad4187 100644
--- a/util/strList.c
+++ b/util/strList.c
@@ -22,3 +22,17 @@ strList *strList_from_string(const char *str, const char *delim)
 
     return res;
 }
+
+GStrv strv_from_strList(const strList *args)
+{
+    const strList *arg;
+    int i = 0;
+    GStrv argv = g_new(char *, QAPI_LIST_LENGTH(args) + 1);
+
+    for (arg = args; arg != NULL; arg = arg->next) {
+        argv[i++] = g_strdup(arg->value);
+    }
+    argv[i] = NULL;
+
+    return argv;
+}
-- 
1.8.3.1



^ permalink raw reply related	[flat|nested] 19+ messages in thread

* [PATCH V4 4/5] util: strList unit tests
  2024-01-12 22:49 [PATCH V4 0/5] string list functions Steve Sistare
                   ` (2 preceding siblings ...)
  2024-01-12 22:49 ` [PATCH V4 3/5] util: strv_from_strList Steve Sistare
@ 2024-01-12 22:49 ` Steve Sistare
  2024-02-21 13:19   ` Markus Armbruster
  2024-01-12 22:49 ` [PATCH V4 5/5] migration: simplify exec migration functions Steve Sistare
  4 siblings, 1 reply; 19+ messages in thread
From: Steve Sistare @ 2024-01-12 22:49 UTC (permalink / raw)
  To: qemu-devel
  Cc: Markus Armbruster, Michael Roth, Peter Xu, Fabiano Rosas,
	Marc-André Lureau, Steve Sistare

Signed-off-by: Steve Sistare <steven.sistare@oracle.com>
Reviewed-by: Marc-André Lureau <marcandre.lureau@redhat.com>
---
 tests/unit/meson.build    |  1 +
 tests/unit/test-strList.c | 80 +++++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 81 insertions(+)
 create mode 100644 tests/unit/test-strList.c

diff --git a/tests/unit/meson.build b/tests/unit/meson.build
index 69f9c05..113d12e 100644
--- a/tests/unit/meson.build
+++ b/tests/unit/meson.build
@@ -35,6 +35,7 @@ tests = {
   'test-rcu-simpleq': [],
   'test-rcu-tailq': [],
   'test-rcu-slist': [],
+  'test-strList': [],
   'test-qdist': [],
   'test-qht': [],
   'test-qtree': [],
diff --git a/tests/unit/test-strList.c b/tests/unit/test-strList.c
new file mode 100644
index 0000000..49a1cfd
--- /dev/null
+++ b/tests/unit/test-strList.c
@@ -0,0 +1,80 @@
+/*
+ * Copyright (c) 2022 - 2024 Oracle and/or its affiliates.
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or later.
+ * See the COPYING file in the top-level directory.
+ */
+
+#include "qemu/osdep.h"
+#include "qemu/strList.h"
+
+static strList *make_list(int length)
+{
+    strList *head = 0, *list, **prev = &head;
+
+    while (length--) {
+        list = *prev = g_new0(strList, 1);
+        list->value = g_strdup("aaa");
+        prev = &list->next;
+    }
+    return head;
+}
+
+static void test_length(void)
+{
+    strList *list;
+    int i;
+
+    for (i = 0; i < 5; i++) {
+        list = make_list(i);
+        g_assert_cmpint(i, ==, QAPI_LIST_LENGTH(list));
+        qapi_free_strList(list);
+    }
+}
+
+struct {
+    const char *string;
+    const char *delim;
+    const char *args[5];
+} list_data[] = {
+    { 0, ",", { 0 } },
+    { "", ",", { 0 } },
+    { "a", ",", { "a", 0 } },
+    { "a,b", ",", { "a", "b", 0 } },
+    { "a,b,c", ",", { "a", "b", "c", 0 } },
+    { "first last", " ", { "first", "last", 0 } },
+    { "a:", ":", { "a", "", 0 } },
+    { "a::b", ":", { "a", "", "b", 0 } },
+    { ":", ":", { "", "", 0 } },
+    { ":a", ":", { "", "a", 0 } },
+    { "::a", ":", { "", "", "a", 0 } },
+};
+
+static void test_strv(void)
+{
+    int i, j;
+    const char **expect;
+    strList *list;
+    GStrv args;
+
+    for (i = 0; i < ARRAY_SIZE(list_data); i++) {
+        expect = list_data[i].args;
+        list = strList_from_string(list_data[i].string, list_data[i].delim);
+        args = strv_from_strList(list);
+        qapi_free_strList(list);
+        for (j = 0; expect[j] && args[j]; j++) {
+            g_assert_cmpstr(expect[j], ==, args[j]);
+        }
+        g_assert_null(expect[j]);
+        g_assert_null(args[j]);
+        g_strfreev(args);
+    }
+}
+
+int main(int argc, char **argv)
+{
+    g_test_init(&argc, &argv, NULL);
+    g_test_add_func("/test-string/length", test_length);
+    g_test_add_func("/test-string/strv", test_strv);
+    return g_test_run();
+}
-- 
1.8.3.1



^ permalink raw reply related	[flat|nested] 19+ messages in thread

* [PATCH V4 5/5] migration: simplify exec migration functions
  2024-01-12 22:49 [PATCH V4 0/5] string list functions Steve Sistare
                   ` (3 preceding siblings ...)
  2024-01-12 22:49 ` [PATCH V4 4/5] util: strList unit tests Steve Sistare
@ 2024-01-12 22:49 ` Steve Sistare
  2024-02-21 13:10   ` Markus Armbruster
  2024-02-21 13:55   ` Fabiano Rosas
  4 siblings, 2 replies; 19+ messages in thread
From: Steve Sistare @ 2024-01-12 22:49 UTC (permalink / raw)
  To: qemu-devel
  Cc: Markus Armbruster, Michael Roth, Peter Xu, Fabiano Rosas,
	Marc-André Lureau, Steve Sistare

Simplify the exec migration code by using list utility functions.

As a side effect, this also fixes a minor memory leak.  On function return,
"g_auto(GStrv) argv" frees argv and each element, which is wrong, because
the function does not own the individual elements.  To compensate, the code
uses g_steal_pointer which NULLs argv and prevents the destructor from
running, but argv is leaked.

Fixes: cbab4face57b ("migration: convert exec backend ...")
Signed-off-by: Steve Sistare <steven.sistare@oracle.com>
---
 migration/exec.c | 58 ++++++++------------------------------------------------
 1 file changed, 8 insertions(+), 50 deletions(-)

diff --git a/migration/exec.c b/migration/exec.c
index 47d2f3b..1312ca7 100644
--- a/migration/exec.c
+++ b/migration/exec.c
@@ -19,12 +19,12 @@
 
 #include "qemu/osdep.h"
 #include "qemu/error-report.h"
+#include "qemu/strList.h"
 #include "channel.h"
 #include "exec.h"
 #include "migration.h"
 #include "io/channel-command.h"
 #include "trace.h"
-#include "qemu/cutils.h"
 
 #ifdef WIN32
 const char *exec_get_cmd_path(void)
@@ -39,51 +39,16 @@ const char *exec_get_cmd_path(void)
 }
 #endif
 
-/* provides the length of strList */
-static int
-str_list_length(strList *list)
-{
-    int len = 0;
-    strList *elem;
-
-    for (elem = list; elem != NULL; elem = elem->next) {
-        len++;
-    }
-
-    return len;
-}
-
-static void
-init_exec_array(strList *command, char **argv, Error **errp)
-{
-    int i = 0;
-    strList *lst;
-
-    for (lst = command; lst; lst = lst->next) {
-        argv[i++] = lst->value;
-    }
-
-    argv[i] = NULL;
-    return;
-}
-
 void exec_start_outgoing_migration(MigrationState *s, strList *command,
                                    Error **errp)
 {
-    QIOChannel *ioc;
-
-    int length = str_list_length(command);
-    g_auto(GStrv) argv = (char **) g_new0(const char *, length + 1);
-
-    init_exec_array(command, argv, errp);
+    QIOChannel *ioc = NULL;
+    g_auto(GStrv) argv = strv_from_strList(command);
+    const char * const *args = (const char * const *) argv;
     g_autofree char *new_command = g_strjoinv(" ", (char **)argv);
 
     trace_migration_exec_outgoing(new_command);
-    ioc = QIO_CHANNEL(
-        qio_channel_command_new_spawn(
-                            (const char * const *) g_steal_pointer(&argv),
-                            O_RDWR,
-                            errp));
+    ioc = QIO_CHANNEL(qio_channel_command_new_spawn(args, O_RDWR, errp));
     if (!ioc) {
         return;
     }
@@ -105,19 +70,12 @@ static gboolean exec_accept_incoming_migration(QIOChannel *ioc,
 void exec_start_incoming_migration(strList *command, Error **errp)
 {
     QIOChannel *ioc;
-
-    int length = str_list_length(command);
-    g_auto(GStrv) argv = (char **) g_new0(const char *, length + 1);
-
-    init_exec_array(command, argv, errp);
+    g_auto(GStrv) argv = strv_from_strList(command);
+    const char * const *args = (const char * const *) argv;
     g_autofree char *new_command = g_strjoinv(" ", (char **)argv);
 
     trace_migration_exec_incoming(new_command);
-    ioc = QIO_CHANNEL(
-        qio_channel_command_new_spawn(
-                            (const char * const *) g_steal_pointer(&argv),
-                            O_RDWR,
-                            errp));
+    ioc = QIO_CHANNEL(qio_channel_command_new_spawn(args, O_RDWR, errp));
     if (!ioc) {
         return;
     }
-- 
1.8.3.1



^ permalink raw reply related	[flat|nested] 19+ messages in thread

* Re: [PATCH V4 5/5] migration: simplify exec migration functions
  2024-01-12 22:49 ` [PATCH V4 5/5] migration: simplify exec migration functions Steve Sistare
@ 2024-02-21 13:10   ` Markus Armbruster
  2024-02-21 13:55   ` Fabiano Rosas
  1 sibling, 0 replies; 19+ messages in thread
From: Markus Armbruster @ 2024-02-21 13:10 UTC (permalink / raw)
  To: Steve Sistare
  Cc: qemu-devel, Michael Roth, Peter Xu, Fabiano Rosas,
	Marc-André Lureau

Het Gala, Peter, or Fabiano, please review.

Steve Sistare <steven.sistare@oracle.com> writes:

> Simplify the exec migration code by using list utility functions.
>
> As a side effect, this also fixes a minor memory leak.  On function return,
> "g_auto(GStrv) argv" frees argv and each element, which is wrong, because
> the function does not own the individual elements.  To compensate, the code
> uses g_steal_pointer which NULLs argv and prevents the destructor from
> running, but argv is leaked.
>
> Fixes: cbab4face57b ("migration: convert exec backend ...")
> Signed-off-by: Steve Sistare <steven.sistare@oracle.com>
> ---
>  migration/exec.c | 58 ++++++++------------------------------------------------
>  1 file changed, 8 insertions(+), 50 deletions(-)
>
> diff --git a/migration/exec.c b/migration/exec.c
> index 47d2f3b..1312ca7 100644
> --- a/migration/exec.c
> +++ b/migration/exec.c
> @@ -19,12 +19,12 @@
>  
>  #include "qemu/osdep.h"
>  #include "qemu/error-report.h"
> +#include "qemu/strList.h"
>  #include "channel.h"
>  #include "exec.h"
>  #include "migration.h"
>  #include "io/channel-command.h"
>  #include "trace.h"
> -#include "qemu/cutils.h"
>  
>  #ifdef WIN32
>  const char *exec_get_cmd_path(void)
> @@ -39,51 +39,16 @@ const char *exec_get_cmd_path(void)
>  }
>  #endif
>  
> -/* provides the length of strList */
> -static int
> -str_list_length(strList *list)
> -{
> -    int len = 0;
> -    strList *elem;
> -
> -    for (elem = list; elem != NULL; elem = elem->next) {
> -        len++;
> -    }
> -
> -    return len;
> -}
> -
> -static void
> -init_exec_array(strList *command, char **argv, Error **errp)
> -{
> -    int i = 0;
> -    strList *lst;
> -
> -    for (lst = command; lst; lst = lst->next) {
> -        argv[i++] = lst->value;
> -    }
> -
> -    argv[i] = NULL;
> -    return;
> -}
> -
>  void exec_start_outgoing_migration(MigrationState *s, strList *command,
>                                     Error **errp)
>  {
> -    QIOChannel *ioc;
> -
> -    int length = str_list_length(command);
> -    g_auto(GStrv) argv = (char **) g_new0(const char *, length + 1);
> -
> -    init_exec_array(command, argv, errp);
> +    QIOChannel *ioc = NULL;
> +    g_auto(GStrv) argv = strv_from_strList(command);
> +    const char * const *args = (const char * const *) argv;
>      g_autofree char *new_command = g_strjoinv(" ", (char **)argv);
>  
>      trace_migration_exec_outgoing(new_command);
> -    ioc = QIO_CHANNEL(
> -        qio_channel_command_new_spawn(
> -                            (const char * const *) g_steal_pointer(&argv),
> -                            O_RDWR,
> -                            errp));
> +    ioc = QIO_CHANNEL(qio_channel_command_new_spawn(args, O_RDWR, errp));
>      if (!ioc) {
>          return;
>      }
> @@ -105,19 +70,12 @@ static gboolean exec_accept_incoming_migration(QIOChannel *ioc,
>  void exec_start_incoming_migration(strList *command, Error **errp)
>  {
>      QIOChannel *ioc;
> -
> -    int length = str_list_length(command);
> -    g_auto(GStrv) argv = (char **) g_new0(const char *, length + 1);
> -
> -    init_exec_array(command, argv, errp);
> +    g_auto(GStrv) argv = strv_from_strList(command);
> +    const char * const *args = (const char * const *) argv;
>      g_autofree char *new_command = g_strjoinv(" ", (char **)argv);
>  
>      trace_migration_exec_incoming(new_command);
> -    ioc = QIO_CHANNEL(
> -        qio_channel_command_new_spawn(
> -                            (const char * const *) g_steal_pointer(&argv),
> -                            O_RDWR,
> -                            errp));
> +    ioc = QIO_CHANNEL(qio_channel_command_new_spawn(args, O_RDWR, errp));
>      if (!ioc) {
>          return;
>      }



^ permalink raw reply	[flat|nested] 19+ messages in thread

* Re: [PATCH V4 3/5] util: strv_from_strList
  2024-01-12 22:49 ` [PATCH V4 3/5] util: strv_from_strList Steve Sistare
@ 2024-02-21 13:14   ` Markus Armbruster
  2024-02-21 17:01     ` Steven Sistare
  0 siblings, 1 reply; 19+ messages in thread
From: Markus Armbruster @ 2024-02-21 13:14 UTC (permalink / raw)
  To: Steve Sistare
  Cc: qemu-devel, Michael Roth, Peter Xu, Fabiano Rosas,
	Marc-André Lureau

Steve Sistare <steven.sistare@oracle.com> writes:

> Signed-off-by: Steve Sistare <steven.sistare@oracle.com>
> Reviewed-by: Marc-André Lureau <marcandre.lureau@redhat.com>
> ---
>  include/qemu/strList.h |  6 ++++++
>  util/strList.c         | 14 ++++++++++++++
>  2 files changed, 20 insertions(+)
>
> diff --git a/include/qemu/strList.h b/include/qemu/strList.h
> index 010237f..4b86aa6 100644
> --- a/include/qemu/strList.h
> +++ b/include/qemu/strList.h
> @@ -21,4 +21,10 @@
>   */
>  strList *strList_from_string(const char *in, const char *delim);
>  
> +/*
> + * Produce and return a NULL-terminated array of strings from @args.
> + * The result is g_malloc'd and all strings are g_strdup'd.
> + */
> +GStrv strv_from_strList(const strList *args);
> +
>  #endif
> diff --git a/util/strList.c b/util/strList.c
> index 7991de3..bad4187 100644
> --- a/util/strList.c
> +++ b/util/strList.c
> @@ -22,3 +22,17 @@ strList *strList_from_string(const char *str, const char *delim)
>  
>      return res;
>  }
> +
> +GStrv strv_from_strList(const strList *args)

Suggest to name the argument @list.

> +{
> +    const strList *arg;

Suggest to name this @tail.

> +    int i = 0;
> +    GStrv argv = g_new(char *, QAPI_LIST_LENGTH(args) + 1);
> +
> +    for (arg = args; arg != NULL; arg = arg->next) {
> +        argv[i++] = g_strdup(arg->value);
> +    }
> +    argv[i] = NULL;
> +
> +    return argv;
> +}

Can we use char ** instread of GStrv?  I'd find that clearer.  For what
it's worth, GLib documentation of functions like g_strsplit() doesn't
use the GStrv typedef, either.



^ permalink raw reply	[flat|nested] 19+ messages in thread

* Re: [PATCH V4 4/5] util: strList unit tests
  2024-01-12 22:49 ` [PATCH V4 4/5] util: strList unit tests Steve Sistare
@ 2024-02-21 13:19   ` Markus Armbruster
  2024-02-21 17:01     ` Steven Sistare
  0 siblings, 1 reply; 19+ messages in thread
From: Markus Armbruster @ 2024-02-21 13:19 UTC (permalink / raw)
  To: Steve Sistare
  Cc: qemu-devel, Michael Roth, Peter Xu, Fabiano Rosas,
	Marc-André Lureau

Steve Sistare <steven.sistare@oracle.com> writes:

> Signed-off-by: Steve Sistare <steven.sistare@oracle.com>
> Reviewed-by: Marc-André Lureau <marcandre.lureau@redhat.com>
> ---
>  tests/unit/meson.build    |  1 +
>  tests/unit/test-strList.c | 80 +++++++++++++++++++++++++++++++++++++++++++++++
>  2 files changed, 81 insertions(+)
>  create mode 100644 tests/unit/test-strList.c
>
> diff --git a/tests/unit/meson.build b/tests/unit/meson.build
> index 69f9c05..113d12e 100644
> --- a/tests/unit/meson.build
> +++ b/tests/unit/meson.build
> @@ -35,6 +35,7 @@ tests = {
>    'test-rcu-simpleq': [],
>    'test-rcu-tailq': [],
>    'test-rcu-slist': [],
> +  'test-strList': [],
>    'test-qdist': [],
>    'test-qht': [],
>    'test-qtree': [],
> diff --git a/tests/unit/test-strList.c b/tests/unit/test-strList.c
> new file mode 100644
> index 0000000..49a1cfd
> --- /dev/null
> +++ b/tests/unit/test-strList.c
> @@ -0,0 +1,80 @@
> +/*
> + * Copyright (c) 2022 - 2024 Oracle and/or its affiliates.
> + *
> + * This work is licensed under the terms of the GNU GPL, version 2 or later.
> + * See the COPYING file in the top-level directory.
> + */
> +
> +#include "qemu/osdep.h"
> +#include "qemu/strList.h"
> +
> +static strList *make_list(int length)
> +{
> +    strList *head = 0, *list, **prev = &head;
> +
> +    while (length--) {
> +        list = *prev = g_new0(strList, 1);
> +        list->value = g_strdup("aaa");
> +        prev = &list->next;
> +    }
> +    return head;
> +}
> +
> +static void test_length(void)
> +{
> +    strList *list;
> +    int i;
> +
> +    for (i = 0; i < 5; i++) {
> +        list = make_list(i);
> +        g_assert_cmpint(i, ==, QAPI_LIST_LENGTH(list));
> +        qapi_free_strList(list);
> +    }
> +}
> +
> +struct {
> +    const char *string;
> +    const char *delim;
> +    const char *args[5];
> +} list_data[] = {
> +    { 0, ",", { 0 } },
> +    { "", ",", { 0 } },
> +    { "a", ",", { "a", 0 } },
> +    { "a,b", ",", { "a", "b", 0 } },
> +    { "a,b,c", ",", { "a", "b", "c", 0 } },
> +    { "first last", " ", { "first", "last", 0 } },
> +    { "a:", ":", { "a", "", 0 } },
> +    { "a::b", ":", { "a", "", "b", 0 } },
> +    { ":", ":", { "", "", 0 } },
> +    { ":a", ":", { "", "a", 0 } },
> +    { "::a", ":", { "", "", "a", 0 } },
> +};

NULL instead of 0, please.

> +
> +static void test_strv(void)
> +{
> +    int i, j;
> +    const char **expect;
> +    strList *list;
> +    GStrv args;

I'd prefer char **argv;

> +
> +    for (i = 0; i < ARRAY_SIZE(list_data); i++) {
> +        expect = list_data[i].args;
> +        list = strList_from_string(list_data[i].string, list_data[i].delim);
> +        args = strv_from_strList(list);
> +        qapi_free_strList(list);
> +        for (j = 0; expect[j] && args[j]; j++) {

Loop stops when either array element is null.  That's wrong, we need to
exhaust both arrays: || instead of &&.

> +            g_assert_cmpstr(expect[j], ==, args[j]);
> +        }
> +        g_assert_null(expect[j]);
> +        g_assert_null(args[j]);
> +        g_strfreev(args);
> +    }
> +}
> +
> +int main(int argc, char **argv)
> +{
> +    g_test_init(&argc, &argv, NULL);
> +    g_test_add_func("/test-string/length", test_length);
> +    g_test_add_func("/test-string/strv", test_strv);
> +    return g_test_run();
> +}



^ permalink raw reply	[flat|nested] 19+ messages in thread

* Re: [PATCH V4 2/5] qapi: QAPI_LIST_LENGTH
  2024-01-12 22:49 ` [PATCH V4 2/5] qapi: QAPI_LIST_LENGTH Steve Sistare
@ 2024-02-21 13:29   ` Markus Armbruster
  2024-02-21 17:01     ` Steven Sistare
  0 siblings, 1 reply; 19+ messages in thread
From: Markus Armbruster @ 2024-02-21 13:29 UTC (permalink / raw)
  To: Steve Sistare
  Cc: qemu-devel, Markus Armbruster, Michael Roth, Peter Xu,
	Fabiano Rosas, Marc-André Lureau

Steve Sistare <steven.sistare@oracle.com> writes:

> Signed-off-by: Steve Sistare <steven.sistare@oracle.com>
> Reviewed-by: Marc-André Lureau <marcandre.lureau@redhat.com>
> ---
>  include/qapi/util.h | 13 +++++++++++++
>  1 file changed, 13 insertions(+)
>
> diff --git a/include/qapi/util.h b/include/qapi/util.h
> index 81a2b13..e1b8b1d 100644
> --- a/include/qapi/util.h
> +++ b/include/qapi/util.h
> @@ -56,4 +56,17 @@ int parse_qapi_name(const char *name, bool complete);
>      (tail) = &(*(tail))->next; \
>  } while (0)
>  
> +/*
> + * For any GenericList @list, return its length.
> + */
> +#define QAPI_LIST_LENGTH(list) \
> +    ({ \
> +        int len = 0; \

size_t

> +        typeof(list) elem; \

Name this @tail, please.

> +        for (elem = list; elem != NULL; elem = elem->next) { \
> +            len++; \
> +        } \
> +        len; \
> +    })
> +
>  #endif

This is a macro instead of a function so users don't have to cast their
FooList * to GenericList *.

The only user outside tests is strv_from_strList().  I'd be tempted to
open-code it there and call it a day.  Or do you have more users in
mind?

If we keep the macro, please align the backslashes like this:

   #define QAPI_LIST_LENGTH(list)                                  \
       ({                                                          \
           int len = 0;                                            \
           typeof(list) elem;                                      \
           for (elem = list; elem != NULL; elem = elem->next) {    \
               len++;                                              \
           }                                                       \
           len;                                                    \
       })



^ permalink raw reply	[flat|nested] 19+ messages in thread

* Re: [PATCH V4 1/5] util: strList_from_string
  2024-01-12 22:49 ` [PATCH V4 1/5] util: strList_from_string Steve Sistare
@ 2024-02-21 13:29   ` Markus Armbruster
  2024-02-21 17:01     ` Steven Sistare
  0 siblings, 1 reply; 19+ messages in thread
From: Markus Armbruster @ 2024-02-21 13:29 UTC (permalink / raw)
  To: Steve Sistare
  Cc: qemu-devel, Michael Roth, Peter Xu, Fabiano Rosas,
	Marc-André Lureau

I apologize for the lateness of my review.

Steve Sistare <steven.sistare@oracle.com> writes:

> Generalize hmp_split_at_comma() to take any delimiter string, rename
> as strList_from_string(), and move it to util/strList.c.
>
> No functional change.
>
> Signed-off-by: Steve Sistare <steven.sistare@oracle.com>

I can't see an actual use of generalized delimiters outside tests in
this series.  Do you have uses?

> ---
>  include/monitor/hmp.h  |  1 -
>  include/qemu/strList.h | 24 ++++++++++++++++++++++++
>  monitor/hmp-cmds.c     | 19 -------------------
>  net/net-hmp-cmds.c     |  3 ++-
>  stats/stats-hmp-cmds.c |  3 ++-
>  util/meson.build       |  1 +
>  util/strList.c         | 24 ++++++++++++++++++++++++
>  7 files changed, 53 insertions(+), 22 deletions(-)
>  create mode 100644 include/qemu/strList.h
>  create mode 100644 util/strList.c
>
> diff --git a/include/monitor/hmp.h b/include/monitor/hmp.h
> index 13f9a2d..2df661e 100644
> --- a/include/monitor/hmp.h
> +++ b/include/monitor/hmp.h
> @@ -19,7 +19,6 @@
>  
>  bool hmp_handle_error(Monitor *mon, Error *err);
>  void hmp_help_cmd(Monitor *mon, const char *name);
> -strList *hmp_split_at_comma(const char *str);
>  
>  void hmp_info_name(Monitor *mon, const QDict *qdict);
>  void hmp_info_version(Monitor *mon, const QDict *qdict);
> diff --git a/include/qemu/strList.h b/include/qemu/strList.h
> new file mode 100644
> index 0000000..010237f
> --- /dev/null
> +++ b/include/qemu/strList.h
> @@ -0,0 +1,24 @@
> +/*
> + * Copyright (c) 2022 - 2024 Oracle and/or its affiliates.
> + *
> + * This work is licensed under the terms of the GNU GPL, version 2 or later.
> + * See the COPYING file in the top-level directory.
> + */
> +
> +#ifndef QEMU_STR_LIST_H
> +#define QEMU_STR_LIST_H
> +
> +#include "qapi/qapi-builtin-types.h"
> +
> +/*
> + * Break @in into a strList using the delimiter string @delim.
> + * The delimiter is not included in the result.
> + * Return NULL if @in is NULL or an empty string.
> + * A leading, trailing, or consecutive delimiter produces an
> + * empty string at that position in the output.
> + * All strings are g_strdup'd, and the result can be freed
> + * using qapi_free_strList.
> + */
> +strList *strList_from_string(const char *in, const char *delim);

The function name no longer tells us explicitly what the function does:
splitting the string.

`> +
> +#endif
> diff --git a/monitor/hmp-cmds.c b/monitor/hmp-cmds.c
> index 871898a..66b68a0 100644
> --- a/monitor/hmp-cmds.c
> +++ b/monitor/hmp-cmds.c
> @@ -38,25 +38,6 @@ bool hmp_handle_error(Monitor *mon, Error *err)
>      return false;
>  }
>  
> -/*
> - * Split @str at comma.
> - * A null @str defaults to "".
> - */
> -strList *hmp_split_at_comma(const char *str)
> -{
> -    char **split = g_strsplit(str ?: "", ",", -1);
> -    strList *res = NULL;
> -    strList **tail = &res;
> -    int i;
> -
> -    for (i = 0; split[i]; i++) {
> -        QAPI_LIST_APPEND(tail, split[i]);
> -    }
> -
> -    g_free(split);
> -    return res;
> -}
> -
>  void hmp_info_name(Monitor *mon, const QDict *qdict)
>  {
>      NameInfo *info;
> diff --git a/net/net-hmp-cmds.c b/net/net-hmp-cmds.c
> index 41d326b..e893801 100644
> --- a/net/net-hmp-cmds.c
> +++ b/net/net-hmp-cmds.c
> @@ -26,6 +26,7 @@
>  #include "qemu/config-file.h"
>  #include "qemu/help_option.h"
>  #include "qemu/option.h"
> +#include "qemu/strList.h"
>  
>  void hmp_info_network(Monitor *mon, const QDict *qdict)
>  {
> @@ -72,7 +73,7 @@ void hmp_announce_self(Monitor *mon, const QDict *qdict)
>                                              migrate_announce_params());
>  
>      qapi_free_strList(params->interfaces);
> -    params->interfaces = hmp_split_at_comma(interfaces_str);
> +    params->interfaces = strList_from_string(interfaces_str, ",");
>      params->has_interfaces = params->interfaces != NULL;
>      params->id = g_strdup(id);
>      qmp_announce_self(params, NULL);
> diff --git a/stats/stats-hmp-cmds.c b/stats/stats-hmp-cmds.c
> index 1f91bf8..428c0e6 100644
> --- a/stats/stats-hmp-cmds.c
> +++ b/stats/stats-hmp-cmds.c
> @@ -10,6 +10,7 @@
>  #include "monitor/hmp.h"
>  #include "monitor/monitor.h"
>  #include "qemu/cutils.h"
> +#include "qemu/strList.h"
>  #include "hw/core/cpu.h"
>  #include "qapi/qmp/qdict.h"
>  #include "qapi/error.h"
> @@ -176,7 +177,7 @@ static StatsFilter *stats_filter(StatsTarget target, const char *names,
>              request->provider = provider_idx;
>              if (names && !g_str_equal(names, "*")) {
>                  request->has_names = true;
> -                request->names = hmp_split_at_comma(names);
> +                request->names = strList_from_string(names, ",");
>              }
>              QAPI_LIST_PREPEND(request_list, request);
>          }
> diff --git a/util/meson.build b/util/meson.build
> index af3bf56..e1d1e1f 100644
> --- a/util/meson.build
> +++ b/util/meson.build
> @@ -1,4 +1,5 @@
>  util_ss.add(files('osdep.c', 'cutils.c', 'unicode.c', 'qemu-timer-common.c'))
> +util_ss.add(files('strList.c'))
>  util_ss.add(files('thread-context.c'), numa)
>  if not config_host_data.get('CONFIG_ATOMIC64')
>    util_ss.add(files('atomic64.c'))
> diff --git a/util/strList.c b/util/strList.c
> new file mode 100644
> index 0000000..7991de3
> --- /dev/null
> +++ b/util/strList.c
> @@ -0,0 +1,24 @@
> +/*
> + * Copyright (c) 2023 Red Hat, Inc.
> + * Copyright (c) 2022 - 2024 Oracle and/or its affiliates.
> + *
> + * This work is licensed under the terms of the GNU GPL, version 2 or later.
> + * See the COPYING file in the top-level directory.
> + */
> +
> +#include "qemu/osdep.h"
> +#include "qemu/strList.h"
> +
> +strList *strList_from_string(const char *str, const char *delim)
> +{
> +    g_autofree char **split = g_strsplit(str ?: "", delim, -1);
> +    strList *res = NULL;
> +    strList **tail = &res;
> +    int i;
> +
> +    for (i = 0; split[i]; i++) {
> +        QAPI_LIST_APPEND(tail, split[i]);
> +    }
> +
> +    return res;
> +}



^ permalink raw reply	[flat|nested] 19+ messages in thread

* Re: [PATCH V4 5/5] migration: simplify exec migration functions
  2024-01-12 22:49 ` [PATCH V4 5/5] migration: simplify exec migration functions Steve Sistare
  2024-02-21 13:10   ` Markus Armbruster
@ 2024-02-21 13:55   ` Fabiano Rosas
  2024-02-21 15:54     ` Fabiano Rosas
  1 sibling, 1 reply; 19+ messages in thread
From: Fabiano Rosas @ 2024-02-21 13:55 UTC (permalink / raw)
  To: Steve Sistare, qemu-devel
  Cc: Markus Armbruster, Michael Roth, Peter Xu, Marc-André Lureau,
	Steve Sistare

Steve Sistare <steven.sistare@oracle.com> writes:

> Simplify the exec migration code by using list utility functions.
>
> As a side effect, this also fixes a minor memory leak.  On function return,
> "g_auto(GStrv) argv" frees argv and each element, which is wrong, because
> the function does not own the individual elements.  To compensate, the code
> uses g_steal_pointer which NULLs argv and prevents the destructor from
> running, but argv is leaked.
>
> Fixes: cbab4face57b ("migration: convert exec backend ...")
> Signed-off-by: Steve Sistare <steven.sistare@oracle.com>

Reviewed-by: Fabiano Rosas <farosas@suse.de>


^ permalink raw reply	[flat|nested] 19+ messages in thread

* Re: [PATCH V4 5/5] migration: simplify exec migration functions
  2024-02-21 13:55   ` Fabiano Rosas
@ 2024-02-21 15:54     ` Fabiano Rosas
  2024-02-21 17:01       ` Steven Sistare
  0 siblings, 1 reply; 19+ messages in thread
From: Fabiano Rosas @ 2024-02-21 15:54 UTC (permalink / raw)
  To: Steve Sistare, qemu-devel
  Cc: Markus Armbruster, Michael Roth, Peter Xu, Marc-André Lureau,
	Steve Sistare

Fabiano Rosas <farosas@suse.de> writes:

> Steve Sistare <steven.sistare@oracle.com> writes:
>
>> Simplify the exec migration code by using list utility functions.
>>
>> As a side effect, this also fixes a minor memory leak.  On function return,
>> "g_auto(GStrv) argv" frees argv and each element, which is wrong, because
>> the function does not own the individual elements.  To compensate, the code
>> uses g_steal_pointer which NULLs argv and prevents the destructor from
>> running, but argv is leaked.
>>
>> Fixes: cbab4face57b ("migration: convert exec backend ...")
>> Signed-off-by: Steve Sistare <steven.sistare@oracle.com>
>
> Reviewed-by: Fabiano Rosas <farosas@suse.de>

You'll have to reintroduce the qemu/cutils.h include:

../migration/exec.c: In function 'exec_get_cmd_path':
../migration/exec.c:37:5: error: implicit declaration of function 'pstrcat'; did you mean 'strcat'? [-Werror=implicit-function-declaration]
   37 |     pstrcat(detected_path, MAX_PATH, "\\cmd.exe");
      |     ^~~~~~~
      |     strcat
../migration/exec.c:37:5: error: nested extern declaration of 'pstrcat' [-Werror=nested-externs]


^ permalink raw reply	[flat|nested] 19+ messages in thread

* Re: [PATCH V4 1/5] util: strList_from_string
  2024-02-21 13:29   ` Markus Armbruster
@ 2024-02-21 17:01     ` Steven Sistare
  2024-02-22 19:46       ` Steven Sistare
  0 siblings, 1 reply; 19+ messages in thread
From: Steven Sistare @ 2024-02-21 17:01 UTC (permalink / raw)
  To: Markus Armbruster
  Cc: qemu-devel, Michael Roth, Peter Xu, Fabiano Rosas,
	Marc-André Lureau

On 2/21/2024 8:29 AM, Markus Armbruster wrote:
> I apologize for the lateness of my review.

No problem.  Thanks for the review.

> Steve Sistare <steven.sistare@oracle.com> writes:
> 
>> Generalize hmp_split_at_comma() to take any delimiter string, rename
>> as strList_from_string(), and move it to util/strList.c.
>>
>> No functional change.
>>
>> Signed-off-by: Steve Sistare <steven.sistare@oracle.com>
> 
> I can't see an actual use of generalized delimiters outside tests in
> this series.  Do you have uses?

In this series, it is called from hmp_announce_self and stats_filter; 
those were formerly calls to hmp_split_at_comma.

In my live update cpr-exec series, there is an additional call site, with a
space delimiter instead of comma.  Live update V9 is posted but is old and obsolete.  
I will post V10 soon, but I am hoping you can pull this series first, so I can 
whittle down my pending patches and omit these from V10.

>> ---
>>  include/monitor/hmp.h  |  1 -
>>  include/qemu/strList.h | 24 ++++++++++++++++++++++++
>>  monitor/hmp-cmds.c     | 19 -------------------
>>  net/net-hmp-cmds.c     |  3 ++-
>>  stats/stats-hmp-cmds.c |  3 ++-
>>  util/meson.build       |  1 +
>>  util/strList.c         | 24 ++++++++++++++++++++++++
>>  7 files changed, 53 insertions(+), 22 deletions(-)
>>  create mode 100644 include/qemu/strList.h
>>  create mode 100644 util/strList.c
>>
>> diff --git a/include/monitor/hmp.h b/include/monitor/hmp.h
>> index 13f9a2d..2df661e 100644
>> --- a/include/monitor/hmp.h
>> +++ b/include/monitor/hmp.h
>> @@ -19,7 +19,6 @@
>>  
>>  bool hmp_handle_error(Monitor *mon, Error *err);
>>  void hmp_help_cmd(Monitor *mon, const char *name);
>> -strList *hmp_split_at_comma(const char *str);
>>  
>>  void hmp_info_name(Monitor *mon, const QDict *qdict);
>>  void hmp_info_version(Monitor *mon, const QDict *qdict);
>> diff --git a/include/qemu/strList.h b/include/qemu/strList.h
>> new file mode 100644
>> index 0000000..010237f
>> --- /dev/null
>> +++ b/include/qemu/strList.h
>> @@ -0,0 +1,24 @@
>> +/*
>> + * Copyright (c) 2022 - 2024 Oracle and/or its affiliates.
>> + *
>> + * This work is licensed under the terms of the GNU GPL, version 2 or later.
>> + * See the COPYING file in the top-level directory.
>> + */
>> +
>> +#ifndef QEMU_STR_LIST_H
>> +#define QEMU_STR_LIST_H
>> +
>> +#include "qapi/qapi-builtin-types.h"
>> +
>> +/*
>> + * Break @in into a strList using the delimiter string @delim.
>> + * The delimiter is not included in the result.
>> + * Return NULL if @in is NULL or an empty string.
>> + * A leading, trailing, or consecutive delimiter produces an
>> + * empty string at that position in the output.
>> + * All strings are g_strdup'd, and the result can be freed
>> + * using qapi_free_strList.
>> + */
>> +strList *strList_from_string(const char *in, const char *delim);
> 
> The function name no longer tells us explicitly what the function does:
> splitting the string.

The first sentence does not say it?
  "Break @in into a strList using the delimiter string @delim"

Would you prefer this?
  "Split string @in into a strList using the delimiter string @delim"

- Steve

>> +#endif
>> diff --git a/monitor/hmp-cmds.c b/monitor/hmp-cmds.c
>> index 871898a..66b68a0 100644
>> --- a/monitor/hmp-cmds.c
>> +++ b/monitor/hmp-cmds.c
>> @@ -38,25 +38,6 @@ bool hmp_handle_error(Monitor *mon, Error *err)
>>      return false;
>>  }
>>  
>> -/*
>> - * Split @str at comma.
>> - * A null @str defaults to "".
>> - */
>> -strList *hmp_split_at_comma(const char *str)
>> -{
>> -    char **split = g_strsplit(str ?: "", ",", -1);
>> -    strList *res = NULL;
>> -    strList **tail = &res;
>> -    int i;
>> -
>> -    for (i = 0; split[i]; i++) {
>> -        QAPI_LIST_APPEND(tail, split[i]);
>> -    }
>> -
>> -    g_free(split);
>> -    return res;
>> -}
>> -
>>  void hmp_info_name(Monitor *mon, const QDict *qdict)
>>  {
>>      NameInfo *info;
>> diff --git a/net/net-hmp-cmds.c b/net/net-hmp-cmds.c
>> index 41d326b..e893801 100644
>> --- a/net/net-hmp-cmds.c
>> +++ b/net/net-hmp-cmds.c
>> @@ -26,6 +26,7 @@
>>  #include "qemu/config-file.h"
>>  #include "qemu/help_option.h"
>>  #include "qemu/option.h"
>> +#include "qemu/strList.h"
>>  
>>  void hmp_info_network(Monitor *mon, const QDict *qdict)
>>  {
>> @@ -72,7 +73,7 @@ void hmp_announce_self(Monitor *mon, const QDict *qdict)
>>                                              migrate_announce_params());
>>  
>>      qapi_free_strList(params->interfaces);
>> -    params->interfaces = hmp_split_at_comma(interfaces_str);
>> +    params->interfaces = strList_from_string(interfaces_str, ",");
>>      params->has_interfaces = params->interfaces != NULL;
>>      params->id = g_strdup(id);
>>      qmp_announce_self(params, NULL);
>> diff --git a/stats/stats-hmp-cmds.c b/stats/stats-hmp-cmds.c
>> index 1f91bf8..428c0e6 100644
>> --- a/stats/stats-hmp-cmds.c
>> +++ b/stats/stats-hmp-cmds.c
>> @@ -10,6 +10,7 @@
>>  #include "monitor/hmp.h"
>>  #include "monitor/monitor.h"
>>  #include "qemu/cutils.h"
>> +#include "qemu/strList.h"
>>  #include "hw/core/cpu.h"
>>  #include "qapi/qmp/qdict.h"
>>  #include "qapi/error.h"
>> @@ -176,7 +177,7 @@ static StatsFilter *stats_filter(StatsTarget target, const char *names,
>>              request->provider = provider_idx;
>>              if (names && !g_str_equal(names, "*")) {
>>                  request->has_names = true;
>> -                request->names = hmp_split_at_comma(names);
>> +                request->names = strList_from_string(names, ",");
>>              }
>>              QAPI_LIST_PREPEND(request_list, request);
>>          }
>> diff --git a/util/meson.build b/util/meson.build
>> index af3bf56..e1d1e1f 100644
>> --- a/util/meson.build
>> +++ b/util/meson.build
>> @@ -1,4 +1,5 @@
>>  util_ss.add(files('osdep.c', 'cutils.c', 'unicode.c', 'qemu-timer-common.c'))
>> +util_ss.add(files('strList.c'))
>>  util_ss.add(files('thread-context.c'), numa)
>>  if not config_host_data.get('CONFIG_ATOMIC64')
>>    util_ss.add(files('atomic64.c'))
>> diff --git a/util/strList.c b/util/strList.c
>> new file mode 100644
>> index 0000000..7991de3
>> --- /dev/null
>> +++ b/util/strList.c
>> @@ -0,0 +1,24 @@
>> +/*
>> + * Copyright (c) 2023 Red Hat, Inc.
>> + * Copyright (c) 2022 - 2024 Oracle and/or its affiliates.
>> + *
>> + * This work is licensed under the terms of the GNU GPL, version 2 or later.
>> + * See the COPYING file in the top-level directory.
>> + */
>> +
>> +#include "qemu/osdep.h"
>> +#include "qemu/strList.h"
>> +
>> +strList *strList_from_string(const char *str, const char *delim)
>> +{
>> +    g_autofree char **split = g_strsplit(str ?: "", delim, -1);
>> +    strList *res = NULL;
>> +    strList **tail = &res;
>> +    int i;
>> +
>> +    for (i = 0; split[i]; i++) {
>> +        QAPI_LIST_APPEND(tail, split[i]);
>> +    }
>> +
>> +    return res;
>> +}
> 


^ permalink raw reply	[flat|nested] 19+ messages in thread

* Re: [PATCH V4 2/5] qapi: QAPI_LIST_LENGTH
  2024-02-21 13:29   ` Markus Armbruster
@ 2024-02-21 17:01     ` Steven Sistare
  0 siblings, 0 replies; 19+ messages in thread
From: Steven Sistare @ 2024-02-21 17:01 UTC (permalink / raw)
  To: Markus Armbruster
  Cc: qemu-devel, Michael Roth, Peter Xu, Fabiano Rosas,
	Marc-André Lureau

On 2/21/2024 8:29 AM, Markus Armbruster wrote:
> Steve Sistare <steven.sistare@oracle.com> writes:
> 
>> Signed-off-by: Steve Sistare <steven.sistare@oracle.com>
>> Reviewed-by: Marc-André Lureau <marcandre.lureau@redhat.com>
>> ---
>>  include/qapi/util.h | 13 +++++++++++++
>>  1 file changed, 13 insertions(+)
>>
>> diff --git a/include/qapi/util.h b/include/qapi/util.h
>> index 81a2b13..e1b8b1d 100644
>> --- a/include/qapi/util.h
>> +++ b/include/qapi/util.h
>> @@ -56,4 +56,17 @@ int parse_qapi_name(const char *name, bool complete);
>>      (tail) = &(*(tail))->next; \
>>  } while (0)
>>  
>> +/*
>> + * For any GenericList @list, return its length.
>> + */
>> +#define QAPI_LIST_LENGTH(list) \
>> +    ({ \
>> +        int len = 0; \
> 
> size_t

ok.

>> +        typeof(list) elem; \
> 
> Name this @tail, please.

ok.

>> +        for (elem = list; elem != NULL; elem = elem->next) { \
>> +            len++; \
>> +        } \
>> +        len; \
>> +    })
>> +
>>  #endif
> 
> This is a macro instead of a function so users don't have to cast their
> FooList * to GenericList *.
> 
> The only user outside tests is strv_from_strList().  I'd be tempted to
> open-code it there and call it a day.  Or do you have more users in
> mind?

That's the only use.  If I make it private, I would still define it as
a static subroutine in util/strList.c, because it simplifies strv_from_strList.
IMO providing a public list length function or macro is pretty basic, but
I am not wedded to it.  Your call.

- Steve

> If we keep the macro, please align the backslashes like this:
> 
>    #define QAPI_LIST_LENGTH(list)                                  \
>        ({                                                          \
>            int len = 0;                                            \
>            typeof(list) elem;                                      \
>            for (elem = list; elem != NULL; elem = elem->next) {    \
>                len++;                                              \
>            }                                                       \
>            len;                                                    \
>        })
> 


^ permalink raw reply	[flat|nested] 19+ messages in thread

* Re: [PATCH V4 3/5] util: strv_from_strList
  2024-02-21 13:14   ` Markus Armbruster
@ 2024-02-21 17:01     ` Steven Sistare
  0 siblings, 0 replies; 19+ messages in thread
From: Steven Sistare @ 2024-02-21 17:01 UTC (permalink / raw)
  To: Markus Armbruster
  Cc: qemu-devel, Michael Roth, Peter Xu, Fabiano Rosas,
	Marc-André Lureau

On 2/21/2024 8:14 AM, Markus Armbruster wrote:
> Steve Sistare <steven.sistare@oracle.com> writes:
> 
>> Signed-off-by: Steve Sistare <steven.sistare@oracle.com>
>> Reviewed-by: Marc-André Lureau <marcandre.lureau@redhat.com>
>> ---
>>  include/qemu/strList.h |  6 ++++++
>>  util/strList.c         | 14 ++++++++++++++
>>  2 files changed, 20 insertions(+)
>>
>> diff --git a/include/qemu/strList.h b/include/qemu/strList.h
>> index 010237f..4b86aa6 100644
>> --- a/include/qemu/strList.h
>> +++ b/include/qemu/strList.h
>> @@ -21,4 +21,10 @@
>>   */
>>  strList *strList_from_string(const char *in, const char *delim);
>>  
>> +/*
>> + * Produce and return a NULL-terminated array of strings from @args.
>> + * The result is g_malloc'd and all strings are g_strdup'd.
>> + */
>> +GStrv strv_from_strList(const strList *args);
>> +
>>  #endif
>> diff --git a/util/strList.c b/util/strList.c
>> index 7991de3..bad4187 100644
>> --- a/util/strList.c
>> +++ b/util/strList.c
>> @@ -22,3 +22,17 @@ strList *strList_from_string(const char *str, const char *delim)
>>  
>>      return res;
>>  }
>> +
>> +GStrv strv_from_strList(const strList *args)
> 
> Suggest to name the argument @list.
> 
>> +{
>> +    const strList *arg;
> 
> Suggest to name this @tail.

ok.

>> +    int i = 0;
>> +    GStrv argv = g_new(char *, QAPI_LIST_LENGTH(args) + 1);
>> +
>> +    for (arg = args; arg != NULL; arg = arg->next) {
>> +        argv[i++] = g_strdup(arg->value);
>> +    }
>> +    argv[i] = NULL;
>> +
>> +    return argv;
>> +}
> 
> Can we use char ** instread of GStrv?  I'd find that clearer.  For what
> it's worth, GLib documentation of functions like g_strsplit() doesn't
> use the GStrv typedef, either.

ok.

- Steve


^ permalink raw reply	[flat|nested] 19+ messages in thread

* Re: [PATCH V4 4/5] util: strList unit tests
  2024-02-21 13:19   ` Markus Armbruster
@ 2024-02-21 17:01     ` Steven Sistare
  0 siblings, 0 replies; 19+ messages in thread
From: Steven Sistare @ 2024-02-21 17:01 UTC (permalink / raw)
  To: Markus Armbruster
  Cc: qemu-devel, Michael Roth, Peter Xu, Fabiano Rosas,
	Marc-André Lureau

On 2/21/2024 8:19 AM, Markus Armbruster wrote:
> Steve Sistare <steven.sistare@oracle.com> writes:
> 
>> Signed-off-by: Steve Sistare <steven.sistare@oracle.com>
>> Reviewed-by: Marc-André Lureau <marcandre.lureau@redhat.com>
>> ---
>>  tests/unit/meson.build    |  1 +
>>  tests/unit/test-strList.c | 80 +++++++++++++++++++++++++++++++++++++++++++++++
>>  2 files changed, 81 insertions(+)
>>  create mode 100644 tests/unit/test-strList.c
>>
>> diff --git a/tests/unit/meson.build b/tests/unit/meson.build
>> index 69f9c05..113d12e 100644
>> --- a/tests/unit/meson.build
>> +++ b/tests/unit/meson.build
>> @@ -35,6 +35,7 @@ tests = {
>>    'test-rcu-simpleq': [],
>>    'test-rcu-tailq': [],
>>    'test-rcu-slist': [],
>> +  'test-strList': [],
>>    'test-qdist': [],
>>    'test-qht': [],
>>    'test-qtree': [],
>> diff --git a/tests/unit/test-strList.c b/tests/unit/test-strList.c
>> new file mode 100644
>> index 0000000..49a1cfd
>> --- /dev/null
>> +++ b/tests/unit/test-strList.c
>> @@ -0,0 +1,80 @@
>> +/*
>> + * Copyright (c) 2022 - 2024 Oracle and/or its affiliates.
>> + *
>> + * This work is licensed under the terms of the GNU GPL, version 2 or later.
>> + * See the COPYING file in the top-level directory.
>> + */
>> +
>> +#include "qemu/osdep.h"
>> +#include "qemu/strList.h"
>> +
>> +static strList *make_list(int length)
>> +{
>> +    strList *head = 0, *list, **prev = &head;
>> +
>> +    while (length--) {
>> +        list = *prev = g_new0(strList, 1);
>> +        list->value = g_strdup("aaa");
>> +        prev = &list->next;
>> +    }
>> +    return head;
>> +}
>> +
>> +static void test_length(void)
>> +{
>> +    strList *list;
>> +    int i;
>> +
>> +    for (i = 0; i < 5; i++) {
>> +        list = make_list(i);
>> +        g_assert_cmpint(i, ==, QAPI_LIST_LENGTH(list));
>> +        qapi_free_strList(list);
>> +    }
>> +}
>> +
>> +struct {
>> +    const char *string;
>> +    const char *delim;
>> +    const char *args[5];
>> +} list_data[] = {
>> +    { 0, ",", { 0 } },
>> +    { "", ",", { 0 } },
>> +    { "a", ",", { "a", 0 } },
>> +    { "a,b", ",", { "a", "b", 0 } },
>> +    { "a,b,c", ",", { "a", "b", "c", 0 } },
>> +    { "first last", " ", { "first", "last", 0 } },
>> +    { "a:", ":", { "a", "", 0 } },
>> +    { "a::b", ":", { "a", "", "b", 0 } },
>> +    { ":", ":", { "", "", 0 } },
>> +    { ":a", ":", { "", "a", 0 } },
>> +    { "::a", ":", { "", "", "a", 0 } },
>> +};
> 
> NULL instead of 0, please.

ok.

>> +
>> +static void test_strv(void)
>> +{
>> +    int i, j;
>> +    const char **expect;
>> +    strList *list;
>> +    GStrv args;
> 
> I'd prefer char **argv;

ok.

>> +
>> +    for (i = 0; i < ARRAY_SIZE(list_data); i++) {
>> +        expect = list_data[i].args;
>> +        list = strList_from_string(list_data[i].string, list_data[i].delim);
>> +        args = strv_from_strList(list);
>> +        qapi_free_strList(list);
>> +        for (j = 0; expect[j] && args[j]; j++) {
> 
> Loop stops when either array element is null.  That's wrong, we need to
> exhaust both arrays: || instead of &&.

|| is not safe.  After one array is exhausted, [j] will be out of bounds for
the other.  The g_assert_null calls guarantee the arrays are the same length
and all elements have been compared.

- Steve

>> +            g_assert_cmpstr(expect[j], ==, args[j]);
>> +        }
>> +        g_assert_null(expect[j]);
>> +        g_assert_null(args[j]);
>> +        g_strfreev(args);
>> +    }
>> +}
>> +
>> +int main(int argc, char **argv)
>> +{
>> +    g_test_init(&argc, &argv, NULL);
>> +    g_test_add_func("/test-string/length", test_length);
>> +    g_test_add_func("/test-string/strv", test_strv);
>> +    return g_test_run();
>> +}
> 


^ permalink raw reply	[flat|nested] 19+ messages in thread

* Re: [PATCH V4 5/5] migration: simplify exec migration functions
  2024-02-21 15:54     ` Fabiano Rosas
@ 2024-02-21 17:01       ` Steven Sistare
  0 siblings, 0 replies; 19+ messages in thread
From: Steven Sistare @ 2024-02-21 17:01 UTC (permalink / raw)
  To: Fabiano Rosas, qemu-devel
  Cc: Markus Armbruster, Michael Roth, Peter Xu, Marc-André Lureau

On 2/21/2024 10:54 AM, Fabiano Rosas wrote:
> Fabiano Rosas <farosas@suse.de> writes:
> 
>> Steve Sistare <steven.sistare@oracle.com> writes:
>>
>>> Simplify the exec migration code by using list utility functions.
>>>
>>> As a side effect, this also fixes a minor memory leak.  On function return,
>>> "g_auto(GStrv) argv" frees argv and each element, which is wrong, because
>>> the function does not own the individual elements.  To compensate, the code
>>> uses g_steal_pointer which NULLs argv and prevents the destructor from
>>> running, but argv is leaked.
>>>
>>> Fixes: cbab4face57b ("migration: convert exec backend ...")
>>> Signed-off-by: Steve Sistare <steven.sistare@oracle.com>
>>
>> Reviewed-by: Fabiano Rosas <farosas@suse.de>
> 
> You'll have to reintroduce the qemu/cutils.h include:
> 
> ../migration/exec.c: In function 'exec_get_cmd_path':
> ../migration/exec.c:37:5: error: implicit declaration of function 'pstrcat'; did you mean 'strcat'? [-Werror=implicit-function-declaration]
>    37 |     pstrcat(detected_path, MAX_PATH, "\\cmd.exe");
>       |     ^~~~~~~
>       |     strcat
> ../migration/exec.c:37:5: error: nested extern declaration of 'pstrcat' [-Werror=nested-externs]

Thanks, I will rebase to the tip and verify all is well before I post V5.

- Steve


^ permalink raw reply	[flat|nested] 19+ messages in thread

* Re: [PATCH V4 1/5] util: strList_from_string
  2024-02-21 17:01     ` Steven Sistare
@ 2024-02-22 19:46       ` Steven Sistare
  0 siblings, 0 replies; 19+ messages in thread
From: Steven Sistare @ 2024-02-22 19:46 UTC (permalink / raw)
  To: Markus Armbruster
  Cc: qemu-devel, Michael Roth, Peter Xu, Fabiano Rosas,
	Marc-André Lureau

On 2/21/2024 12:01 PM, Steven Sistare wrote:
> On 2/21/2024 8:29 AM, Markus Armbruster wrote:
>> I apologize for the lateness of my review.
> 
> No problem.  Thanks for the review.
> 
>> Steve Sistare <steven.sistare@oracle.com> writes:
>>
>>> Generalize hmp_split_at_comma() to take any delimiter string, rename
>>> as strList_from_string(), and move it to util/strList.c.
>>>
>>> No functional change.
>>>
>>> Signed-off-by: Steve Sistare <steven.sistare@oracle.com>
>>
>> I can't see an actual use of generalized delimiters outside tests in
>> this series.  Do you have uses?
> 
> In this series, it is called from hmp_announce_self and stats_filter; 
> those were formerly calls to hmp_split_at_comma.
> 
> In my live update cpr-exec series, there is an additional call site, with a
> space delimiter instead of comma.  Live update V9 is posted but is old and obsolete.  
> I will post V10 soon, but I am hoping you can pull this series first, so I can 
> whittle down my pending patches and omit these from V10.
> 
>>> ---
>>>  include/monitor/hmp.h  |  1 -
>>>  include/qemu/strList.h | 24 ++++++++++++++++++++++++
>>>  monitor/hmp-cmds.c     | 19 -------------------
>>>  net/net-hmp-cmds.c     |  3 ++-
>>>  stats/stats-hmp-cmds.c |  3 ++-
>>>  util/meson.build       |  1 +
>>>  util/strList.c         | 24 ++++++++++++++++++++++++
>>>  7 files changed, 53 insertions(+), 22 deletions(-)
>>>  create mode 100644 include/qemu/strList.h
>>>  create mode 100644 util/strList.c
>>>
>>> diff --git a/include/monitor/hmp.h b/include/monitor/hmp.h
>>> index 13f9a2d..2df661e 100644
>>> --- a/include/monitor/hmp.h
>>> +++ b/include/monitor/hmp.h
>>> @@ -19,7 +19,6 @@
>>>  
>>>  bool hmp_handle_error(Monitor *mon, Error *err);
>>>  void hmp_help_cmd(Monitor *mon, const char *name);
>>> -strList *hmp_split_at_comma(const char *str);
>>>  
>>>  void hmp_info_name(Monitor *mon, const QDict *qdict);
>>>  void hmp_info_version(Monitor *mon, const QDict *qdict);
>>> diff --git a/include/qemu/strList.h b/include/qemu/strList.h
>>> new file mode 100644
>>> index 0000000..010237f
>>> --- /dev/null
>>> +++ b/include/qemu/strList.h
>>> @@ -0,0 +1,24 @@
>>> +/*
>>> + * Copyright (c) 2022 - 2024 Oracle and/or its affiliates.
>>> + *
>>> + * This work is licensed under the terms of the GNU GPL, version 2 or later.
>>> + * See the COPYING file in the top-level directory.
>>> + */
>>> +
>>> +#ifndef QEMU_STR_LIST_H
>>> +#define QEMU_STR_LIST_H
>>> +
>>> +#include "qapi/qapi-builtin-types.h"
>>> +
>>> +/*
>>> + * Break @in into a strList using the delimiter string @delim.
>>> + * The delimiter is not included in the result.
>>> + * Return NULL if @in is NULL or an empty string.
>>> + * A leading, trailing, or consecutive delimiter produces an
>>> + * empty string at that position in the output.
>>> + * All strings are g_strdup'd, and the result can be freed
>>> + * using qapi_free_strList.
>>> + */
>>> +strList *strList_from_string(const char *in, const char *delim);
>>
>> The function name no longer tells us explicitly what the function does:
>> splitting the string.
> 
> The first sentence does not say it?
>   "Break @in into a strList using the delimiter string @delim"
> 
> Would you prefer this?
>   "Split string @in into a strList using the delimiter string @delim"

Sorry, I read your comment too quickly.  You want a different function name.
I propose:
  strList *str_split(const char *str, const char *delim);

- Steve


^ permalink raw reply	[flat|nested] 19+ messages in thread

end of thread, other threads:[~2024-02-22 19:47 UTC | newest]

Thread overview: 19+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2024-01-12 22:49 [PATCH V4 0/5] string list functions Steve Sistare
2024-01-12 22:49 ` [PATCH V4 1/5] util: strList_from_string Steve Sistare
2024-02-21 13:29   ` Markus Armbruster
2024-02-21 17:01     ` Steven Sistare
2024-02-22 19:46       ` Steven Sistare
2024-01-12 22:49 ` [PATCH V4 2/5] qapi: QAPI_LIST_LENGTH Steve Sistare
2024-02-21 13:29   ` Markus Armbruster
2024-02-21 17:01     ` Steven Sistare
2024-01-12 22:49 ` [PATCH V4 3/5] util: strv_from_strList Steve Sistare
2024-02-21 13:14   ` Markus Armbruster
2024-02-21 17:01     ` Steven Sistare
2024-01-12 22:49 ` [PATCH V4 4/5] util: strList unit tests Steve Sistare
2024-02-21 13:19   ` Markus Armbruster
2024-02-21 17:01     ` Steven Sistare
2024-01-12 22:49 ` [PATCH V4 5/5] migration: simplify exec migration functions Steve Sistare
2024-02-21 13:10   ` Markus Armbruster
2024-02-21 13:55   ` Fabiano Rosas
2024-02-21 15:54     ` Fabiano Rosas
2024-02-21 17:01       ` Steven Sistare

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).