On 9/22/24 23:05, Manos Pitsidianakis wrote:
Add -build-info and -build-info-json CLI arguments for human and machine
readable output of build/compile-time configuration data. The source for
this data is the meson generated config-host.h.

This information is mainly of interest to developers and other folk who
deal with many different builds of QEMU and need a way to properly
differentiate them.

Because there's always the chance someone will want to consume an
interface programmatically, also add a -json option that does the same
but outputs a machine-readable JSON object.

Signed-off-by: Manos Pitsidianakis <manos.pitsidiana...@linaro.org>
---
Changes in v2:
- Fixed alignment of command and documentation in -h output.
- Link to v1: 
https://lore.kernel.org/r/20240923-feature-build-info-cli-v1-1-e8c42d845...@linaro.org
---
Notes:
Sample output:

$ ./qemu-system-aarch64 -build-info
./qemu-system-aarch64 version 9.1.50 (v9.0.0-3444-g8d988656d8) build information

   configuration key          key value
   -----------------          ---------
   accept4
   af_vsock
   asan_iface_fiber
   atomic64
   attr
   audio_alsa
   audio_drivers              pa,sndio,oss
   audio_oss
   audio_pa
   audio_pipewire
   audio_sdl
   audio_sndio
   avx2_opt
   avx512bw_opt
   bdrv_ro_whitelist
   bdrv_rw_whitelist
   bindir                     /usr/local/bin
   blkzoned
   brlapi
   capstone
   clock_adjtime
   close_range
   coroutine_pool
   cpuid_h
   curl
   curses
   dbus_display
   debug_graph_lock
   debug_mutex
   debug_tcg
   dup3
   ebpf
   epoll
   epoll_create1
   eventfd
   fallocate
   fallocate_punch_hole
   fallocate_zero_range
   fdatasync
   fdt
   fiemap
   fsfreeze
   fstrim
   fuse
   fuse_lseek
   gbm
   getauxval
   getcpu
   getrandom
   gettid
   gio
   gnutls
   gnutls_crypto
   gtk
   hexagon_idef_parser
   host_dsosuf                .so
   iasl                       /bin/iasl
   inotify
   inotify1
   int128
   int128_type
   iovec
   keyutils
   kvm_targets                i386-softmmu,x86_64-softmmu
   l2tpv3
   libdw
   libudev
   linux
   linux_io_uring
   linux_magic_h
   madvise
   malloc_trim
   memalign
   memfd
   opengl
   open_by_handle
   pixman
   plugin
   png
   posix
   posix_fallocate
   posix_madvise
   posix_memalign
   ppoll
   prctl_pr_set_timerslack
   preadv
   prefix                     /usr/local
   pthread_affinity_np
   pthread_setname_np_w_tid
   qemu_confdir               /usr/local/etc/qemu
   qemu_datadir               /usr/local/share/qemu
   qemu_desktopdir            /usr/local/share/applications
   qemu_firmwarepath          /usr/local/share/qemu-firmware
   qemu_helperdir             /usr/local/libexec
   qemu_icondir               /usr/local/share/icons
   qemu_localedir             /usr/local/share/locale
   qemu_localstatedir         /var/local
   qemu_moddir                /usr/local/lib/x86_64-linux-gnu/qemu
   qom_cast_debug
   relocatable
   replication
   rtnetlink
   sdl
   seccomp
   seccomp_sysrawrc
   secret_keyring
   selinux
   sendfile
   setns
   signalfd
   slirp
   smbd_command               /usr/sbin/smbd
   splice
   statx
   statx_mnt_id
   syncfs
   sync_file_range
   sysconfdir                 /usr/local/etc
   sysmacros
   tasn1
   tcg                        1
   timerfd
   tls_priority               NORMAL
   tpm
   trace_file                 trace
   trace_log
   usbfs
   usb_libusb
   valgrind_h
   valloc
   vduse_blk_export
   vhost
   vhost_crypto
   vhost_kernel
   vhost_net
   vhost_net_user
   vhost_net_vdpa
   vhost_user
   vhost_user_blk_server
   vhost_vdpa
   virtfs
   vnc
   vnc_jpeg
   vte
   x11
   xen_backend
   xen_ctrl_interface_version 41700
   xkbcommon
   zstd
   blk_zone_rep_capacity
   btrfs_h
   copy_file_range
   drm_h
   fsxattr
   getifaddrs
   host_block_device
   ipproto_mptcp
   mlockall
   openpty
   pty_h
   strchrnul
   struct_stat_st_atim
   system_function
   utmpx
   virgl_d3d_info_ext
   host_x86_64                1
   qemu_version               9.1.50
   qemu_version_major         9
   qemu_version_micro         50
   qemu_version_minor         1


This is a nice addition, and an easy way to get access to this information, including for a qemu you compile yourself.

If there are some concerns about it becoming a "public API" for consumers, maybe the option could simply be hidden from help, so only qemu devs know it's there.

An alternative could be to distribute a "config file" with qemu, like what is available for Linux kernel (cat /boot/config-*). But I really prefer something embedded in binary and accessible via option to be honest.

To: qemu-devel@nongnu.org
Cc: "Cleber Rosa" <cr...@redhat.com>
Cc: "Daniel P. Berrangé" <berra...@redhat.com>
Cc: "John Snow" <js...@redhat.com>
Cc: "Marc-André Lureau" <marcandre.lur...@redhat.com>
Cc: "Paolo Bonzini" <pbonz...@redhat.com>
Cc: "Alex Bennée" <alex.ben...@linaro.org>
Cc: "Gustavo Romero" <gustavo.rom...@linaro.org>
Cc: "Peter Maydell" <peter.mayd...@linaro.org>
Cc: "Philippe Mathieu-Daudé" <phi...@linaro.org>
Cc: "Pierrick Bouvier" <pierrick.bouv...@linaro.org>
Cc: "Richard Henderson" <richard.hender...@linaro.org>
Signed-off-by: Manos Pitsidianakis <manos.pitsidiana...@linaro.org>
---
  meson.build               | 15 +++++++-
  qemu-options.hx           | 15 ++++++++
  scripts/build_info_gen.py | 91 +++++++++++++++++++++++++++++++++++++++++++++++
  system/vl.c               | 41 +++++++++++++++++++++
  4 files changed, 161 insertions(+), 1 deletion(-)

diff --git a/meson.build b/meson.build
index 10464466ff..eff2ee323a 100644
--- a/meson.build
+++ b/meson.build
@@ -3292,7 +3292,20 @@ endif
  # Generated sources #
  #####################
-genh += configure_file(output: 'config-host.h', configuration: config_host_data)
+config_host_h = configure_file(output: 'config-host.h', configuration: 
config_host_data)
+genh += config_host_h
+
+build_info_h = custom_target('build-info.h',
+                             output: 'build-info.h',
+                             command: [
+                               find_program('scripts/build_info_gen.py'),
+                               '--config-headers',
+                               config_host_h
+                             ],
+                             capture: true,
+                             build_by_default: true,
+                             build_always_stale: true)
+genh += build_info_h
hxtool = find_program('scripts/hxtool')
  shaderinclude = find_program('scripts/shaderinclude.py')
diff --git a/qemu-options.hx b/qemu-options.hx
index d94e2cbbae..6a32e0624f 100644
--- a/qemu-options.hx
+++ b/qemu-options.hx
@@ -24,6 +24,21 @@ SRST
      Display version information and exit
  ERST
+DEF("build-info", 0, QEMU_OPTION_build_info,
+    "-build-info     display build information of executable and exit\n", 
QEMU_ARCH_ALL)
+SRST
+``-build-info``
+    Display build information of executable and exit
+ERST
+
+DEF("build-info-json", 0, QEMU_OPTION_build_info_json,
+    "-build-info-json\n"
+    "                dump build information of executable in JSON format and 
exit\n", QEMU_ARCH_ALL)
+SRST
+``-build-info-json``
+    Dump build information of executable in JSON format and exit
+ERST
+
  DEF("machine", HAS_ARG, QEMU_OPTION_machine, \
      "-machine [type=]name[,prop[=value][,...]]\n"
      "                selects emulated machine ('-machine help' for list)\n"
diff --git a/scripts/build_info_gen.py b/scripts/build_info_gen.py
new file mode 100755
index 0000000000..37a9421651
--- /dev/null
+++ b/scripts/build_info_gen.py
@@ -0,0 +1,91 @@
+#!/usr/bin/env python3
+
+"""
+Generate build information header, build-info.h,
+for output of -build-info* command line arguments.
+
+SPDX-FileContributor: Manos Pitsidianakis <manos.pitsidiana...@linaro.org>
+SPDX-FileCopyrightText: 2024 Linaro Ltd.
+SPDX-License-Identifier: GPL-2.0-or-later
+"""
+
+# Formatted with black --line-length 80 scripts/build-info-gen.py
+
+import argparse
+import logging
+
+
+def generate_key_val(header: str) -> str:
+    # pylint: disable=missing-function-docstring
+
+    with open(header, encoding="utf-8") as cfg:
+        config = [l.split()[1:] for l in cfg if l.startswith("#define")]
+
+    for cfg in config:
+        if cfg[0].startswith("HAVE_"):
+            yield (cfg[0].removeprefix("HAVE_").lower(), None)
+            continue
+        yield (
+            cfg[0].removeprefix("CONFIG_").lower(),
+            (
+                cfg[1]
+                if len(cfg) == 2
+                else "".join(cfg[1:]).replace('"', "") if len(cfg) > 2 else 
None
+            ),
+        )
+
+
+def main() -> None:
+    # pylint: disable=missing-function-docstring
+    parser = argparse.ArgumentParser()
+    parser.add_argument("-v", "--verbose", action="store_true")
+    parser.add_argument(
+        "--config-headers",
+        metavar="CONFIG_HEADER",
+        action="append",
+        dest="config_headers",
+        help="paths to configuration host C headers (*.h files)",
+        required=False,
+        default=[],
+    )
+    args = parser.parse_args()
+    if args.verbose:
+        logging.basicConfig(level=logging.ERROR)
+    logging.debug("args: %s", args)
+    print(
+        """// @generated by scripts/build-info-gen.py
+
+#include <stddef.h>"""
+    )
+    print(
+        """static struct build_info_t {
+    const char *key;
+    const char *value;
+} BUILD_INFO[] = {"""
+    )
+    total = 0
+    header_width = 0
+    for header in args.config_headers:
+        for key, val in generate_key_val(header):
+            total += 1
+            header_width = max(header_width, len(key))
+            print(
+                '{"',
+                key,
+                '", "',
+                val.strip('"').strip(",").strip('"') if val else "",
+                '"},',
+                sep="",
+            )
+    print("};")
+    print("\nstatic size_t BUILD_INFO_SIZE = ", total, ";", sep="")
+    print(
+        "static unsigned int BUILD_INFO_HEADER_WIDTH = ",
+        header_width,
+        ";",
+        sep="",
+    )
+
+
+if __name__ == "__main__":
+    main()
diff --git a/system/vl.c b/system/vl.c
index fe547ca47c..5266b85d22 100644
--- a/system/vl.c
+++ b/system/vl.c
@@ -908,6 +908,41 @@ static void help(int exitcode)
      exit(exitcode);
  }
+static void build_info(const char *execname, bool as_json)
+{
+#include "build-info.h"
+    if (as_json) {
+        printf("{\n");
+        for (size_t i = 0; i + 1 < BUILD_INFO_SIZE ; i++) {
+            printf("\"%s\":\"%s\",\n",
+                   BUILD_INFO[i].key,
+                   BUILD_INFO[i].value);
+        }
+        if (BUILD_INFO_SIZE > 0) {
+            printf("\"%s\":\"%s\"\n",
+                   BUILD_INFO[BUILD_INFO_SIZE - 1].key,
+                   BUILD_INFO[BUILD_INFO_SIZE - 1].value);
+        }
+        printf("}\n");
+    } else {
+        printf("%s version "
+               QEMU_FULL_VERSION
+               " build information\n\n", execname ?:"QEMU");
+        printf("  %-*s key value\n",
+               BUILD_INFO_HEADER_WIDTH,
+               "configuration key");
+        printf("  %-*s ---------\n",
+               BUILD_INFO_HEADER_WIDTH,
+               "-----------------");
+        for (size_t i = 0; i < BUILD_INFO_SIZE ; i++) {
+            printf("  %-*s %s\n",
+                   BUILD_INFO_HEADER_WIDTH,
+                   BUILD_INFO[i].key,
+                   BUILD_INFO[i].value);
+        }
+    }
+}
+
  enum {
#define DEF(option, opt_arg, opt_enum, opt_help, arch_mask) \
@@ -3019,6 +3054,12 @@ void qemu_init(int argc, char **argv)
                  version();
                  exit(0);
                  break;
+            case QEMU_OPTION_build_info:
+                /* fallthrough */
+            case QEMU_OPTION_build_info_json:
+                build_info(argv[0], popt->index != QEMU_OPTION_build_info);
+                exit(0);
+                break;
              case QEMU_OPTION_m:
                  opts = qemu_opts_parse_noisily(qemu_find_opts("memory"), 
optarg, true);
                  if (opts == NULL) {

---
base-commit: 01dc65a3bc262ab1bec8fe89775e9bbfa627becb
change-id: 20240922-feature-build-info-cli-c9e08e34c34b

--
γαῖα πυρί μιχθήτω

Reply via email to