[Spice-devel] [PATCH spice-common v3 1/3] agent-interface: introduce the core of the Agent Interface

Frediano Ziglio fziglio at redhat.com
Wed Nov 6 09:21:18 UTC 2019


> 
> On Fri, Oct 18, 2019 at 6:06 PM Frediano Ziglio <fziglio at redhat.com> wrote:
> >
> >
> > >
> > > When initialized (recorder_initialization), the Agent Interface launch
> > > a GThread (handle_communications) that opens a TCP server socket and
> > > waits for Smart Local Agent connections. When a Local Agent connects
> > > to the sockets, the communication is initialized
> > > (agent_initialize_communication), the communication socket is stored
> > > and the list of Recorders is sent. In return, the local agent
> > > indicates which recorders to enable.
> > >
> > > On the SPICE side, the Agent Interface handles the record() calls
> > > (recorder_append*). When a record is received from SPICE, and if the
> > > recorder is enabled, the record entry is sent through the TCP
> > > connection. Otherwise, the record is dropped.
> > >
> > > Signed-off-by: Kevin Pouget <kpouget at redhat.com>
> >
> > Acked the series
> 
> Hello Frediano,
> 
> you acked the v2, but I assume you meant the v3, right?
> I'll push it upstream when you confirm
> 
> thanks,
> 
> Kevin
> 

Probably. It has been already merged on 18th October.

Frediano

> > > ---
> > > v2->v3: unchanged
> > > ---
> > >  common/agent_interface.c    | 467 +++++++++++++++++++++++++++++++
> > >  common/agent_interface.h    | 542 ++++++++++++++++++++++++++++++++++++
> > >  tests/test-dummy-recorder.c |   3 +-
> > >  3 files changed, 1011 insertions(+), 1 deletion(-)
> > >  create mode 100644 common/agent_interface.c
> > >  create mode 100644 common/agent_interface.h
> > >
> > > diff --git a/common/agent_interface.c b/common/agent_interface.c
> > > new file mode 100644
> > > index 0000000..768b6a9
> > > --- /dev/null
> > > +++ b/common/agent_interface.c
> > > @@ -0,0 +1,467 @@
> > > +/* -*- Mode: C; c-basic-offset: 4; indent-tabs-mode: nil -*- */
> > > +/*
> > > +   Copyright (C) 2019 Red Hat, Inc.
> > > +
> > > +   This library is free software; you can redistribute it and/or
> > > +   modify it under the terms of the GNU Lesser General Public
> > > +   License as published by the Free Software Foundation; either
> > > +   version 2.1 of the License, or (at your option) any later version.
> > > +
> > > +   This library is distributed in the hope that it will be useful,
> > > +   but WITHOUT ANY WARRANTY; without even the implied warranty of
> > > +   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
> > > +   Lesser General Public License for more details.
> > > +
> > > +   You should have received a copy of the GNU Lesser General Public
> > > +   License along with this library; if not, see
> > > <http://www.gnu.org/licenses/>.
> > > +*/
> > > +
> > > +#include <config.h>
> > > +#include <stdlib.h>
> > > +#include <sys/time.h>
> > > +#include <string.h>
> > > +#include <glib.h>
> > > +#include <stdio.h>
> > > +#include <sys/socket.h>
> > > +#include <netinet/in.h>
> > > +#include <unistd.h>
> > > +#include <arpa/inet.h>
> > > +#include <sys/eventfd.h>
> > > +#include <errno.h>
> > > +#include <poll.h>
> > > +
> > > +#include <common/agent_interface.h>
> > > +
> > > +typedef struct sockaddr SA;
> > > +
> > > +static GThread *recorder_comm_thr;
> > > +static bool agent_terminated = false;
> > > +static int terminate_efd = -1;
> > > +static FILE *communication_f = NULL;
> > > +
> > > +#define NB_MAX_RECORDERS 16
> > > +static recorder_info *recorders[NB_MAX_RECORDERS];
> > > +static uint32_t nb_recorders = 0;
> > > +
> > > +static uintptr_t recorder_tick(void);
> > > +
> > > +#ifndef RECORDER_HZ
> > > +#define RECORDER_HZ     1000000
> > > +#endif // RECORDER_HZ
> > > +
> > > +static GMutex mutex_socket;
> > > +
> > > +static int agent_initialize_communication(int socket)
> > > +{
> > > +    uint32_t i;
> > > +    int ret = -1;
> > > +    FILE *socket_f;
> > > +
> > > +    g_mutex_lock(&mutex_socket);
> > > +
> > > +    if (communication_f != NULL) {
> > > +        g_warning("A client is already connected, rejecting the
> > > connection.");
> > > +
> > > +        goto unlock;
> > > +    }
> > > +
> > > +    socket_f = fdopen(socket, "w+b");
> > > +
> > > +    fprintf(socket_f, "Recorders: ");
> > > +    for (i = 0; i < nb_recorders; i++) {
> > > +        g_debug("Sending %s", recorders[i]->name);
> > > +        fprintf(socket_f, "%s;", recorders[i]->name);
> > > +    }
> > > +    fprintf(socket_f, "\n");
> > > +    fflush(socket_f);
> > > +
> > > +    for (i = 0; i < nb_recorders; i++) {
> > > +        char enable;
> > > +
> > > +        if (read(socket, &enable, sizeof(enable)) != sizeof(enable)) {
> > > +            g_warning("Invalid read on the client socket");
> > > +
> > > +            goto unlock;
> > > +        }
> > > +        if (enable != '0' && enable != '1') {
> > > +            g_critical("Invalid enable-value received for recorder '%s':
> > > %u",
> > > +                       recorders[i]->name, enable);
> > > +
> > > +            goto unlock;
> > > +        }
> > > +
> > > +        if (enable == '0') {
> > > +            continue;
> > > +        }
> > > +
> > > +        recorders[i]->trace = 1;
> > > +        g_info("Enable recorder '%s'", recorders[i]->name);
> > > +    }
> > > +
> > > +    communication_f = socket_f;
> > > +    ret = 0;
> > > +
> > > +unlock:
> > > +    g_mutex_unlock(&mutex_socket);
> > > +
> > > +    return ret;
> > > +}
> > > +
> > > +static void agent_finalize_communication(int socket)
> > > +{
> > > +    uint32_t i;
> > > +    g_info("Communication socket closed.");
> > > +
> > > +    g_mutex_lock(&mutex_socket);
> > > +    g_assert(socket == fileno(communication_f));
> > > +
> > > +    fclose(communication_f);
> > > +    communication_f = NULL;
> > > +
> > > +    for (i = 0; i < nb_recorders; i++) {
> > > +        recorders[i]->trace = 0;
> > > +    }
> > > +    g_mutex_unlock(&mutex_socket);
> > > +}
> > > +
> > > +static int agent_process_communication(int socket)
> > > +{
> > > +    static char msg_in[128];
> > > +
> > > +    static long unsigned int len = 0;
> > > +
> > > +    g_assert(socket == fileno(communication_f));
> > > +
> > > +    int nbytes = read(socket, msg_in + len, 1);
> > > +
> > > +    if (nbytes < 0 && errno == EINTR) {
> > > +       return 0;
> > > +    }
> > > +
> > > +    if (nbytes <= 0) {
> > > +        agent_finalize_communication(socket);
> > > +        return -1; // socket closed
> > > +    }
> > > +
> > > +    if (msg_in[len] == '\0') {
> > > +        // TODO: process quality indicator
> > > +        len = 0;
> > > +        return 0;
> > > +    }
> > > +
> > > +    len += nbytes;
> > > +
> > > +    if (len >= sizeof(msg_in) - 1) {
> > > +        msg_in[sizeof(msg_in) - 1] = '\0';
> > > +        g_warning("Invalid message received (too long?): %s", msg_in);
> > > +        len = 0;
> > > +    }
> > > +
> > > +    return 0;
> > > +}
> > > +
> > > +static int make_socket(guint port)
> > > +{
> > > +    struct sockaddr_in servaddr;
> > > +    int listen_socket = socket(AF_INET, SOCK_STREAM, 0);
> > > +
> > > +    if (listen_socket == -1) {
> > > +        g_critical("socket creation failed");
> > > +        return -1;
> > > +    }
> > > +
> > > +    int enable = 1;
> > > +    if (setsockopt(listen_socket, SOL_SOCKET, SO_REUSEADDR, &enable,
> > > sizeof(int)) < 0) {
> > > +        g_critical("setsockopt(SO_REUSEADDR) failed");
> > > +        close(listen_socket);
> > > +        return -1;
> > > +    }
> > > +
> > > +    memset(&servaddr, 0, sizeof(servaddr));
> > > +
> > > +    servaddr.sin_family = AF_INET;
> > > +    servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
> > > +    servaddr.sin_port = htons(port);
> > > +
> > > +    if (bind(listen_socket, (SA *) &servaddr, sizeof(servaddr)) != 0) {
> > > +        g_critical("socket bind failed");
> > > +        close(listen_socket);
> > > +        return -1;
> > > +    }
> > > +
> > > +    return listen_socket;
> > > +}
> > > +
> > > +static gpointer handle_communications(gpointer user_data)
> > > +{
> > > +    struct pollfd fds[3];
> > > +    int nb_fd = 0;
> > > +    int listen_socket;
> > > +    int i;
> > > +    guint port = GPOINTER_TO_UINT(user_data);
> > > +
> > > +    listen_socket = make_socket(port);
> > > +    if (listen_socket < 0) {
> > > +        return NULL;
> > > +    }
> > > +
> > > +    g_debug("Listening!");
> > > +
> > > +    if ((listen(listen_socket, 1)) != 0) {
> > > +        g_critical("listen failed: %m");
> > > +        return NULL;
> > > +    }
> > > +
> > > +    fds[0].fd = terminate_efd;
> > > +    fds[0].events = POLLIN;
> > > +    fds[1].fd = listen_socket;
> > > +    fds[1].events = POLLIN;
> > > +    nb_fd = 2;
> > > +
> > > +    while (!agent_terminated) {
> > > +
> > > +        /* Block until input arrives on one or more active sockets. */
> > > +        int ret = poll(fds, nb_fd, -1);
> > > +
> > > +        if (ret < 0) {
> > > +            g_critical("poll failed: %m");
> > > +            break;
> > > +        }
> > > +
> > > +        /* Service all the sockets with input pending. */
> > > +        for (i = 0; i < nb_fd; i++) {
> > > +            int fd = fds[i].fd;
> > > +            if (fd == terminate_efd) {
> > > +                if (fds[i].revents & POLLIN) {
> > > +                    g_assert(agent_terminated);
> > > +                    break;
> > > +                }
> > > +            } else if (fd == listen_socket) {
> > > +                if (fds[i].revents & ~POLLIN) {
> > > +                    g_critical("server socket closed");
> > > +                    break;
> > > +                }
> > > +                if (!(fds[i].revents & POLLIN)) {
> > > +                    continue;
> > > +                }
> > > +
> > > +                /* Connection request on original socket. */
> > > +                int new_fd = accept(listen_socket, NULL, NULL);
> > > +
> > > +                if (new_fd < 0) {
> > > +                    g_critical("accept failed: %m");
> > > +                    break;
> > > +                }
> > > +
> > > +                if (nb_fd == 3) {
> > > +                    close(new_fd);
> > > +                    g_warning("Too many clients accepted ...");
> > > +                    continue;
> > > +                }
> > > +
> > > +                g_debug("Agent Interface: client connected!");
> > > +
> > > +                if (agent_initialize_communication(new_fd)) {
> > > +                    close(new_fd);
> > > +                    g_warning("Initialization failed ...");
> > > +                    continue;
> > > +                }
> > > +
> > > +                fds[nb_fd].fd = new_fd;
> > > +                fds[nb_fd].events = POLLIN;
> > > +                nb_fd++;
> > > +
> > > +                /* fds array modified, restart the poll. */
> > > +                break;
> > > +            } else {
> > > +                if (!(fds[i].revents & POLLIN)) {
> > > +                    continue;
> > > +                }
> > > +
> > > +                /* Data arriving on an already-connected socket. */
> > > +                if (agent_process_communication(fd) < 0) {
> > > +                    nb_fd--;
> > > +                }
> > > +            }
> > > +        }
> > > +    }
> > > +
> > > +    close(terminate_efd);
> > > +    close(listen_socket);
> > > +
> > > +    g_info("Agent interface thread: bye!");
> > > +    return NULL;
> > > +}
> > > +
> > > +static void recorder_deregister(void);
> > > +
> > > +static void recorder_initialization(unsigned int port)
> > > +{
> > > +    GError *error = NULL;
> > > +
> > > +    terminate_efd = eventfd(0, 0);
> > > +    if (terminate_efd == -1) {
> > > +        g_critical("eventfd failed: %m");
> > > +        return;
> > > +    }
> > > +
> > > +    recorder_comm_thr = g_thread_try_new("smart_agent_interface",
> > > +                                         handle_communications,
> > > +                                         GUINT_TO_POINTER((guint) port),
> > > &error);
> > > +    if (error) {
> > > +        g_assert(!recorder_comm_thr);
> > > +        g_critical("Error: Could not start the agent interface thread:
> > > %s",
> > > error->message);
> > > +        g_error_free(error);
> > > +        return;
> > > +    }
> > > +
> > > +    atexit(recorder_deregister);
> > > +}
> > > +
> > > +static void recorder_interrupt_communications(void)
> > > +{
> > > +    agent_terminated = true;
> > > +
> > > +    uint64_t msg = 1;
> > > +    ssize_t s = write(terminate_efd, &msg, sizeof(uint64_t));
> > > +
> > > +    if (s != sizeof(uint64_t)) {
> > > +        g_warning("failed to send recorder thread termination event:
> > > %m");
> > > +    }
> > > +}
> > > +
> > > +
> > > +static void recorder_deregister(void)
> > > +{
> > > +    if (recorder_comm_thr) {
> > > +        recorder_interrupt_communications();
> > > +        g_thread_join(recorder_comm_thr);
> > > +        recorder_comm_thr = NULL;
> > > +    }
> > > +}
> > > +
> > > +void recorder_activate(recorder_info *recorder)
> > > +{
> > > +    if (nb_recorders >= NB_MAX_RECORDERS) {
> > > +        g_critical("Too many recorders configured (nb max: %d)",
> > > NB_MAX_RECORDERS);
> > > +        return;
> > > +    }
> > > +
> > > +    recorders[nb_recorders] = recorder;
> > > +    nb_recorders++;
> > > +}
> > > +
> > > +static void do_send_entry(FILE *dest, recorder_info *info,
> > > recorder_entry
> > > *entry, va_list args)
> > > +{
> > > +    fprintf(dest, "Name: %s\nFunction: %s\nTime: %lu\n",
> > > +            info->name, entry->where, entry->timestamp);
> > > +
> > > +    vfprintf(dest, entry->format, args);
> > > +    fprintf(dest, "\n\n");
> > > +
> > > +    fflush(dest);
> > > +}
> > > +
> > > +
> > > +static void recorder_trace_entry(recorder_info *info, recorder_entry
> > > *entry,
> > > ...)
> > > +//
> > > ----------------------------------------------------------------------------
> > > +//   Show a recorder entry when a trace is enabled
> > > +//
> > > ----------------------------------------------------------------------------
> > > +{
> > > +    va_list args;
> > > +
> > > +    if (strchr(entry->format, '\n') != NULL) {
> > > +        g_critical("Agent records cannot contain '\n' char ... (%s)",
> > > entry->where);
> > > +        return;
> > > +    }
> > > +
> > > +    // send info/entry to the socket
> > > +    g_mutex_lock(&mutex_socket);
> > > +
> > > +    if (communication_f == NULL) {
> > > +        g_mutex_unlock(&mutex_socket);
> > > +        return;
> > > +    }
> > > +
> > > +    va_start(args, entry);
> > > +    do_send_entry(communication_f, info, entry, args);
> > > +    va_end(args);
> > > +
> > > +    if (g_strcmp0(g_getenv("SPICE_AGENT_LOG_RECORDS"), "1") == 0) {
> > > +        va_start(args, entry);
> > > +        do_send_entry(stderr, info, entry, args);
> > > +        va_end(args);
> > > +    }
> > > +
> > > +    g_mutex_unlock(&mutex_socket);
> > > +}
> > > +
> > > +void recorder_append(recorder_info *rec,
> > > +                     const char *where,
> > > +                     const char *format,
> > > +                     uintptr_t a0,
> > > +                     uintptr_t a1,
> > > +                     uintptr_t a2,
> > > +                     uintptr_t a3)
> > > +//
> > > ----------------------------------------------------------------------------
> > > +//  Enter a record entry in ring buffer with given set of args
> > > +//
> > > ----------------------------------------------------------------------------
> > > +{
> > > +    recorder_entry entry;
> > > +
> > > +    if (!rec->trace) {
> > > +        return;
> > > +    }
> > > +
> > > +    entry.format = format;
> > > +    entry.timestamp = recorder_tick();
> > > +    entry.where = where;
> > > +
> > > +    recorder_trace_entry(rec, &entry, a0, a1, a2, a3);
> > > +}
> > > +
> > > +void recorder_append2(recorder_info *rec,
> > > +                      const char *where,
> > > +                      const char *format,
> > > +                      uintptr_t a0,
> > > +                      uintptr_t a1,
> > > +                      uintptr_t a2,
> > > +                      uintptr_t a3,
> > > +                      uintptr_t a4,
> > > +                      uintptr_t a5,
> > > +                      uintptr_t a6,
> > > +                      uintptr_t a7)
> > > +//
> > > ----------------------------------------------------------------------------
> > > +//   Enter a double record (up to 8 args)
> > > +//
> > > ----------------------------------------------------------------------------
> > > +{
> > > +    recorder_entry entry;
> > > +
> > > +    if (!rec->trace) {
> > > +        return;
> > > +    }
> > > +
> > > +    entry.format = format;
> > > +    entry.timestamp = recorder_tick();
> > > +    entry.where = where;
> > > +
> > > +    recorder_trace_entry(rec, &entry, a0, a1, a2, a3, a4, a5, a6, a7);
> > > +}
> > > +
> > > +//
> > > ============================================================================
> > > +//
> > > +//    Support functions
> > > +//
> > > +//
> > > ============================================================================
> > > +
> > > +static uintptr_t recorder_tick(void)
> > > +//
> > > ----------------------------------------------------------------------------
> > > +//   Return the "ticks" as stored in the recorder
> > > +//
> > > ----------------------------------------------------------------------------
> > > +{
> > > +    struct timeval t;
> > > +
> > > +    gettimeofday(&t, NULL);
> > > +
> > > +    return t.tv_sec * RECORDER_HZ + t.tv_usec / (1000000 / RECORDER_HZ);
> > > +}
> > > diff --git a/common/agent_interface.h b/common/agent_interface.h
> > > new file mode 100644
> > > index 0000000..042120e
> > > --- /dev/null
> > > +++ b/common/agent_interface.h
> > > @@ -0,0 +1,542 @@
> > > +#pragma once
> > > +
> > > +//
> > > *****************************************************************************
> > > +// This software is licensed under the GNU Lesser General Public License
> > > v2+
> > > +// (C) 2017-2019, Christophe de Dinechin <christophe at dinechin.org>
> > > +//
> > > *****************************************************************************
> > > +// This file was part of Recorder
> > > +//
> > > +// Recorder is free software: you can redistribute it and/or modify
> > > +// it under the terms of the GNU Lesser General Public License as
> > > published
> > > by
> > > +// the Free Software Foundation, either version 2 of the License, or
> > > +// (at your option) any later version.
> > > +//
> > > +// Recorder is distributed in the hope that it will be useful,
> > > +// but WITHOUT ANY WARRANTY; without even the implied warranty of
> > > +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> > > +// GNU Lesser General Public License for more details.
> > > +//
> > > +// You should have received a copy of the GNU Lesser General Public
> > > License
> > > +// along with Recorder, in a file named COPYING.
> > > +// If not, see <https://www.gnu.org/licenses/>.
> > > +//
> > > *****************************************************************************
> > > +/* This file is based on Recorder's recorder.h file, that describes a
> > > general-
> > > + * purpose instrumentation interface. agent_interface.h is a
> > > trimmed-down
> > > + * version of it. */
> > > +
> > > +#include <stdarg.h>
> > > +#include <stdint.h>
> > > +#include <stdbool.h>
> > > +#include <stddef.h>
> > > +
> > > +#ifdef __cplusplus
> > > +extern "C" {
> > > +#endif // __cplusplus
> > > +
> > > +static inline void
> > > +recorder_dump_on_common_signals(unsigned add, unsigned remove)
> > > +{
> > > +}
> > > +
> > > +//
> > > ============================================================================
> > > +//
> > > +//    Recorder data structures
> > > +//
> > > +//
> > > ============================================================================
> > > +
> > > +typedef struct recorder_entry
> > > +///
> > > ---------------------------------------------------------------------------
> > > +///   Entry in the flight recorder.
> > > +///----------------------------------------------------------------------------
> > > +///  Notice that the arguments are stored as "intptr_t" because that
> > > type
> > > +///  is guaranteed to be the same size as a pointer. This allows us to
> > > +///  properly align recorder entries to powers of 2 for efficiency.
> > > +///  Also read explanations of \ref _recorder_double and \ref
> > > _recorder_float
> > > +///  below regarding how to use floating-point with the recorder.
> > > +{
> > > +    const char *format;         ///< Printf-style format for record +
> > > file/line
> > > +    uintptr_t   timestamp;      ///< Time at which record took place
> > > +    const char *where;          ///< Source code function
> > > +    uintptr_t   args[4];        ///< Four arguments, for a total of 8
> > > fields
> > > +} recorder_entry;
> > > +
> > > +
> > > +/// A global counter indicating the order of entries across recorders.
> > > +/// this is incremented atomically for each record() call.
> > > +/// It must be exposed because all XYZ_record() implementations need to
> > > +/// touch the same shared variable in order to provide a global order.
> > > +extern uintptr_t recorder_order;
> > > +
> > > +typedef struct recorder_info
> > > +///----------------------------------------------------------------------------
> > > +///   A linked list of the activated recorders
> > > +///----------------------------------------------------------------------------
> > > +{
> > > +    intptr_t                trace;      ///< Trace this recorder
> > > +    const char *            name;       ///< Name of this parameter /
> > > recorder
> > > +    const char *            description;///< Description of what is
> > > recorded
> > > +    recorder_entry          data[0];    ///< Data for this recorder
> > > +} recorder_info;
> > > +
> > > +//
> > > ============================================================================
> > > +//
> > > +//   Adding data to a recorder
> > > +//
> > > +//
> > > ============================================================================
> > > +
> > > +extern void recorder_append(recorder_info *rec,
> > > +                            const char *where,
> > > +                            const char *format,
> > > +                            uintptr_t a0,
> > > +                            uintptr_t a1,
> > > +                            uintptr_t a2,
> > > +                            uintptr_t a3);
> > > +extern void recorder_append2(recorder_info *rec,
> > > +                             const char *where,
> > > +                             const char *format,
> > > +                             uintptr_t a0,
> > > +                             uintptr_t a1,
> > > +                             uintptr_t a2,
> > > +                             uintptr_t a3,
> > > +                             uintptr_t a4,
> > > +                             uintptr_t a5,
> > > +                             uintptr_t a6,
> > > +                             uintptr_t a7);
> > > +extern void recorder_append3(recorder_info *rec,
> > > +                             const char *where,
> > > +                             const char *format,
> > > +                             uintptr_t a0,
> > > +                             uintptr_t a1,
> > > +                             uintptr_t a2,
> > > +                             uintptr_t a3,
> > > +                             uintptr_t a4,
> > > +                             uintptr_t a5,
> > > +                             uintptr_t a6,
> > > +                             uintptr_t a7,
> > > +                             uintptr_t a8,
> > > +                             uintptr_t a9,
> > > +                             uintptr_t a10,
> > > +                             uintptr_t a11);
> > > +
> > > +/// Activate a recorder (during construction time)
> > > +extern void recorder_activate(recorder_info *recorder);
> > > +
> > > +//
> > > ============================================================================
> > > +//
> > > +//    Declaration of recorders and tweaks
> > > +//
> > > +//
> > > ============================================================================
> > > +
> > > +#define RECORDER_DECLARE(Name)
> > > \
> > > +/* ----------------------------------------------------------------*/
> > > \
> > > +/*  Declare a recorder with the given name (for use in headers)    */
> > > \
> > > +/* ----------------------------------------------------------------*/
> > > \
> > > +    extern recorder_info * const recorder_info_ptr_for_##Name;
> > > \
> > > +    extern struct recorder_info_for_##Name recorder_info_for_##Name
> > > +
> > > +
> > > +//
> > > ============================================================================
> > > +//
> > > +//    Definition of recorders and tweaks
> > > +//
> > > +//
> > > ============================================================================
> > > +
> > > +#define RECORDER(Name, Size, Info)      RECORDER_DEFINE(Name,Size,Info)
> > > +
> > > +#define RECORDER_DEFINE(Name, Size, Info)
> > > \
> > > +/*!----------------------------------------------------------------*/
> > > \
> > > +/*! Define a recorder type with Size elements                      */
> > > \
> > > +/*!----------------------------------------------------------------*/
> > > \
> > > +/*! \param Name is the C name fo the recorder.
> > > \
> > > + *! \param Size is the number of entries in the circular buffer.
> > > \
> > > + *! \param Info is a description of the recorder for help. */
> > > \
> > > +
> > > \
> > > +/* The entry in linked list for this type */
> > > \
> > > +struct recorder_info_for_##Name
> > > \
> > > +{
> > > \
> > > +    recorder_info       info;
> > > \
> > > +    recorder_entry      data[Size];
> > > \
> > > +}
> > > \
> > > +recorder_info_for_##Name =
> > > \
> > > +{
> > > \
> > > +    {
> > > \
> > > +        0, #Name, Info, {}
> > > \
> > > +    },
> > > \
> > > +    {}
> > > \
> > > +};
> > > \
> > > +recorder_info * const recorder_info_ptr_for_##Name =
> > > \
> > > +    &recorder_info_for_##Name.info;
> > > \
> > > +
> > > \
> > > +RECORDER_CONSTRUCTOR
> > > \
> > > +static void recorder_activate_##Name(void)
> > > \
> > > +/* ----------------------------------------------------------------*/
> > > \
> > > +/*  Activate recorder before entering main()                       */
> > > \
> > > +/* ----------------------------------------------------------------*/
> > > \
> > > +{
> > > \
> > > +    recorder_activate(RECORDER_INFO(Name));
> > > \
> > > +}
> > > \
> > > +
> > > \
> > > +/* Purposefully generate compile error if macro not followed by ; */
> > > \
> > > +extern void recorder_activate(recorder_info *recorder)
> > > +
> > > +typedef struct SpiceDummyTweak {
> > > +    intptr_t tweak_value;
> > > +} SpiceDummyTweak;
> > > +
> > > +typedef struct SpiceEmptyStruct {
> > > +    char dummy[0];
> > > +} SpiceEmptyStruct;
> > > +
> > > +#define RECORDER_TWEAK_DECLARE(rec) \
> > > +    extern const SpiceDummyTweak spice_recorder_tweak_ ## rec
> > > +
> > > +#define RECORDER_TWEAK_DEFINE(rec, value, comment) \
> > > +    const SpiceDummyTweak spice_recorder_tweak_ ## rec = { (value) }
> > > +
> > > +#define RECORDER_TWEAK(rec) \
> > > +    ((spice_recorder_tweak_ ## rec).tweak_value)
> > > +
> > > +#define RECORDER_TRACE(rec) \
> > > +    (sizeof(struct recorder_info_for_ ## rec) !=
> > > sizeof(SpiceEmptyStruct))
> > > +
> > > +
> > > +//
> > > ============================================================================
> > > +//
> > > +//    Access to recorder and tweak info
> > > +//
> > > +//
> > > ============================================================================
> > > +
> > > +#define RECORDER_INFO(Name)     (recorder_info_ptr_for_##Name)
> > > +
> > > +//
> > > ============================================================================
> > > +//
> > > +//    Recording stuff
> > > +//
> > > +//
> > > ============================================================================
> > > +
> > > +#define record(Name, ...)               RECORD_MACRO(Name, __VA_ARGS__)
> > > +#define RECORD(Name,...)                RECORD_MACRO(Name, __VA_ARGS__)
> > > +#define RECORD_MACRO(Name, Format,...)
> > > \
> > > +    RECORD_(RECORD,RECORD_COUNT_(__VA_ARGS__),Name,Format,##__VA_ARGS__)
> > > +#define RECORD_(RECORD,RCOUNT,Name,Format,...)
> > > \
> > > +    RECORD__(RECORD,RCOUNT,Name,Format,## __VA_ARGS__)
> > > +#define RECORD__(RECORD,RCOUNT,Name,Format,...)
> > > \
> > > +    RECORD##RCOUNT(Name,Format,##__VA_ARGS__)
> > > +#define RECORD_COUNT_(...)
> > > RECORD_COUNT__(Dummy,##__VA_ARGS__,_X,_X,_12,_11,_10,_9,_8,_7,_6,_5,_4,_3,_2,_1,_0)
> > > +#define
> > > RECORD_COUNT__(Dummy,_0,_1,_2,_3,_4,_5,_6,_7,_8,_9,_10,_11,_12,_13,_N,...)
> > > _N
> > > +
> > > +#define RECORD_0(Name, Format)                          \
> > > +    recorder_append(RECORDER_INFO(Name),                \
> > > +                    RECORDER_SOURCE_FUNCTION,           \
> > > +                    RECORDER_SOURCE_LOCATION            \
> > > +                    Format, 0, 0, 0, 0)
> > > +#define RECORD_1(Name, Format, a)                       \
> > > +    recorder_append(RECORDER_INFO(Name),                \
> > > +                    RECORDER_SOURCE_FUNCTION,           \
> > > +                    RECORDER_SOURCE_LOCATION            \
> > > +                    Format,                             \
> > > +                    RECORDER_ARG(a), 0, 0, 0)
> > > +#define RECORD_2(Name, Format, a,b)                     \
> > > +    recorder_append(RECORDER_INFO(Name),                \
> > > +                    RECORDER_SOURCE_FUNCTION,           \
> > > +                    RECORDER_SOURCE_LOCATION            \
> > > +                    Format,                             \
> > > +                    RECORDER_ARG(a),                    \
> > > +                    RECORDER_ARG(b), 0, 0)
> > > +#define RECORD_3(Name, Format, a,b,c)                   \
> > > +    recorder_append(RECORDER_INFO(Name),                \
> > > +                    RECORDER_SOURCE_FUNCTION,           \
> > > +                    RECORDER_SOURCE_LOCATION            \
> > > +                    Format,                             \
> > > +                    RECORDER_ARG(a),                    \
> > > +                    RECORDER_ARG(b),                    \
> > > +                    RECORDER_ARG(c), 0)
> > > +#define RECORD_4(Name, Format, a,b,c,d)                 \
> > > +    recorder_append(RECORDER_INFO(Name),                \
> > > +                    RECORDER_SOURCE_FUNCTION,           \
> > > +                    RECORDER_SOURCE_LOCATION            \
> > > +                    Format,                             \
> > > +                    RECORDER_ARG(a),                    \
> > > +                    RECORDER_ARG(b),                    \
> > > +                    RECORDER_ARG(c),                    \
> > > +                    RECORDER_ARG(d))
> > > +#define RECORD_5(Name, Format, a,b,c,d,e)               \
> > > +    recorder_append2(RECORDER_INFO(Name),               \
> > > +                     RECORDER_SOURCE_FUNCTION,          \
> > > +                     RECORDER_SOURCE_LOCATION           \
> > > +                     Format,                            \
> > > +                     RECORDER_ARG(a),                   \
> > > +                     RECORDER_ARG(b),                   \
> > > +                     RECORDER_ARG(c),                   \
> > > +                     RECORDER_ARG(d),                   \
> > > +                     RECORDER_ARG(e), 0, 0, 0)
> > > +#define RECORD_6(Name, Format, a,b,c,d,e,f)             \
> > > +    recorder_append2(RECORDER_INFO(Name),               \
> > > +                     RECORDER_SOURCE_FUNCTION,          \
> > > +                     RECORDER_SOURCE_LOCATION           \
> > > +                     Format,                            \
> > > +                     RECORDER_ARG(a),                   \
> > > +                     RECORDER_ARG(b),                   \
> > > +                     RECORDER_ARG(c),                   \
> > > +                     RECORDER_ARG(d),                   \
> > > +                     RECORDER_ARG(e),                   \
> > > +                     RECORDER_ARG(f), 0, 0)
> > > +#define RECORD_7(Name, Format, a,b,c,d,e,f,g)           \
> > > +    recorder_append2(RECORDER_INFO(Name),               \
> > > +                     RECORDER_SOURCE_FUNCTION,          \
> > > +                     RECORDER_SOURCE_LOCATION           \
> > > +                     Format,                            \
> > > +                     RECORDER_ARG(a),                   \
> > > +                     RECORDER_ARG(b),                   \
> > > +                     RECORDER_ARG(c),                   \
> > > +                     RECORDER_ARG(d),                   \
> > > +                     RECORDER_ARG(e),                   \
> > > +                     RECORDER_ARG(f),                   \
> > > +                     RECORDER_ARG(g), 0)
> > > +#define RECORD_8(Name, Format, a,b,c,d,e,f,g,h)         \
> > > +    recorder_append2(RECORDER_INFO(Name),               \
> > > +                     RECORDER_SOURCE_FUNCTION,          \
> > > +                     RECORDER_SOURCE_LOCATION           \
> > > +                     Format,                            \
> > > +                     RECORDER_ARG(a),                   \
> > > +                     RECORDER_ARG(b),                   \
> > > +                     RECORDER_ARG(c),                   \
> > > +                     RECORDER_ARG(d),                   \
> > > +                     RECORDER_ARG(e),                   \
> > > +                     RECORDER_ARG(f),                   \
> > > +                     RECORDER_ARG(g),                   \
> > > +                     RECORDER_ARG(h))
> > > +#define RECORD_9(Name, Format, a,b,c,d,e,f,g,h,i)       \
> > > +    recorder_append3(RECORDER_INFO(Name),               \
> > > +                     RECORDER_SOURCE_FUNCTION,          \
> > > +                     RECORDER_SOURCE_LOCATION           \
> > > +                     Format,                            \
> > > +                     RECORDER_ARG(a),                   \
> > > +                     RECORDER_ARG(b),                   \
> > > +                     RECORDER_ARG(c),                   \
> > > +                     RECORDER_ARG(d),                   \
> > > +                     RECORDER_ARG(e),                   \
> > > +                     RECORDER_ARG(f),                   \
> > > +                     RECORDER_ARG(g),                   \
> > > +                     RECORDER_ARG(h),                   \
> > > +                     RECORDER_ARG(i), 0,0,0)
> > > +#define RECORD_10(Name, Format, a,b,c,d,e,f,g,h,i,j)    \
> > > +    recorder_append3(RECORDER_INFO(Name),               \
> > > +                     RECORDER_SOURCE_FUNCTION,          \
> > > +                     RECORDER_SOURCE_LOCATION           \
> > > +                     Format,                            \
> > > +                     RECORDER_ARG(a),                   \
> > > +                     RECORDER_ARG(b),                   \
> > > +                     RECORDER_ARG(c),                   \
> > > +                     RECORDER_ARG(d),                   \
> > > +                     RECORDER_ARG(e),                   \
> > > +                     RECORDER_ARG(f),                   \
> > > +                     RECORDER_ARG(g),                   \
> > > +                     RECORDER_ARG(h),                   \
> > > +                     RECORDER_ARG(i),                   \
> > > +                     RECORDER_ARG(j), 0,0)
> > > +#define RECORD_11(Name, Format, a,b,c,d,e,f,g,h,i,j,k)  \
> > > +    recorder_append3(RECORDER_INFO(Name),               \
> > > +                     RECORDER_SOURCE_FUNCTION,          \
> > > +                     RECORDER_SOURCE_LOCATION           \
> > > +                     Format,                            \
> > > +                     RECORDER_ARG(a),                   \
> > > +                     RECORDER_ARG(b),                   \
> > > +                     RECORDER_ARG(c),                   \
> > > +                     RECORDER_ARG(d),                   \
> > > +                     RECORDER_ARG(e),                   \
> > > +                     RECORDER_ARG(f),                   \
> > > +                     RECORDER_ARG(g),                   \
> > > +                     RECORDER_ARG(h),                   \
> > > +                     RECORDER_ARG(i),                   \
> > > +                     RECORDER_ARG(j),                   \
> > > +                     RECORDER_ARG(k),0)
> > > +#define RECORD_12(Name,Format,a,b,c,d,e,f,g,h,i,j,k,l)  \
> > > +    recorder_append3(RECORDER_INFO(Name),               \
> > > +                     RECORDER_SOURCE_FUNCTION,          \
> > > +                     RECORDER_SOURCE_LOCATION           \
> > > +                     Format,                            \
> > > +                     RECORDER_ARG(a),                   \
> > > +                     RECORDER_ARG(b),                   \
> > > +                     RECORDER_ARG(c),                   \
> > > +                     RECORDER_ARG(d),                   \
> > > +                     RECORDER_ARG(e),                   \
> > > +                     RECORDER_ARG(f),                   \
> > > +                     RECORDER_ARG(g),                   \
> > > +                     RECORDER_ARG(h),                   \
> > > +                     RECORDER_ARG(i),                   \
> > > +                     RECORDER_ARG(j),                   \
> > > +                     RECORDER_ARG(k),                   \
> > > +                     RECORDER_ARG(l))
> > > +#define RECORD_X(Name, Format, ...)
> > > RECORD_TOO_MANY_ARGS(printf(Format,
> > > __VA_ARGS__))
> > > +
> > > +
> > > +// Some ugly macro drudgery to make things easy to use. Adjust type.
> > > +#ifdef __cplusplus
> > > +#define RECORDER_ARG(arg)       _recorder_arg(arg)
> > > +#else // !__cplusplus
> > > +
> > > +#if defined(__GNUC__) && !defined(__clang__)
> > > +#  if __GNUC__ <= 4 || (__GNUC__ == 4 && __GNUC_MINOR__ < 9)
> > > +#    define RECORDER_WITHOUT_GENERIC
> > > +#  endif
> > > +#endif // __GNUC__
> > > +
> > > +#ifdef RECORDER_WITHOUT_GENERIC
> > > +#define RECORDER_ARG(arg) ((uintptr_t) (arg))
> > > +#else // !RECORDER_WITHOUT_GENERIC
> > > +#define RECORDER_ARG(arg)                               \
> > > +    _Generic(arg,                                       \
> > > +             unsigned char:     _recorder_unsigned,     \
> > > +             unsigned short:    _recorder_unsigned,     \
> > > +             unsigned:          _recorder_unsigned,     \
> > > +             unsigned long:     _recorder_unsigned,     \
> > > +             unsigned long long:_recorder_unsigned,     \
> > > +             char:              _recorder_char,         \
> > > +             signed char:       _recorder_signed,       \
> > > +             signed short:      _recorder_signed,       \
> > > +             signed:            _recorder_signed,       \
> > > +             signed long:       _recorder_signed,       \
> > > +             signed long long:  _recorder_signed,       \
> > > +             float:             _recorder_float,        \
> > > +             double:            _recorder_double,       \
> > > +             default:           _recorder_pointer)(arg)
> > > +#endif // RECORDER_WITHOUT_GENERIC
> > > +#endif // __cplusplus
> > > +
> > > +//
> > > ============================================================================
> > > +//
> > > +//    Timing information
> > > +//
> > > +//
> > > ============================================================================
> > > +
> > > +#define RECORD_TIMING_BEGIN(rec) \
> > > +    do { RECORD(rec, "begin");
> > > +#define RECORD_TIMING_END(rec, op, name, value) \
> > > +        RECORD(rec, "end" op name); \
> > > +    } while (0)
> > > +
> > > +
> > > +//
> > > ============================================================================
> > > +//
> > > +//   Support macros
> > > +//
> > > +//
> > > ============================================================================
> > > +
> > > +#define RECORDER_SOURCE_FUNCTION    __func__ /* Works in C99 and C++11
> > > */
> > > +#define RECORDER_SOURCE_LOCATION    __FILE__ ":"
> > > RECORDER_STRING(__LINE__)
> > > ":"
> > > +#define RECORDER_STRING(LINE)       RECORDER_STRING_(LINE)
> > > +#define RECORDER_STRING_(LINE)      #LINE
> > > +
> > > +#ifdef __GNUC__
> > > +#define RECORDER_CONSTRUCTOR            __attribute__((constructor))
> > > +#else
> > > +#define RECORDER_CONSTRUCTOR
> > > +#endif
> > > +
> > > +#ifdef __cplusplus
> > > +}
> > > +#endif // __cplusplus
> > > +
> > > +//
> > > ============================================================================
> > > +//
> > > +//    Utility: Convert floating point values for vararg format
> > > +//
> > > +//
> > > ============================================================================
> > > +//
> > > +//   The recorder stores only uintptr_t in recorder entries. Integer
> > > types
> > > +//   are promoted, pointer types are converted. Floating point values
> > > +//   are converted a floating point type of the same size as uintptr_t,
> > > +//   i.e. float are converted to double on 64-bit platforms, and
> > > conversely.
> > > +
> > > +#ifdef __cplusplus
> > > +#include <string>
> > > +
> > > +// In C++, we don't use _Generic but actual overloading
> > > +template <class inttype>
> > > +static inline uintptr_t         _recorder_arg(inttype i)
> > > +{
> > > +    return (uintptr_t) i;
> > > +}
> > > +
> > > +
> > > +static inline uintptr_t         _recorder_arg(const std::string &arg)
> > > +{
> > > +    return (uintptr_t) arg.c_str();
> > > +}
> > > +#define _recorder_float         _recorder_arg
> > > +#define _recorder_double        _recorder_arg
> > > +
> > > +#else // !__cplusplus
> > > +
> > > +static inline uintptr_t _recorder_char(char c)
> > > +//
> > > ----------------------------------------------------------------------------
> > > +//   Necessary because of the way generic selections work
> > > +//
> > > ----------------------------------------------------------------------------
> > > +{
> > > +    return c;
> > > +}
> > > +
> > > +
> > > +static inline uintptr_t _recorder_unsigned(uintptr_t i)
> > > +//
> > > ----------------------------------------------------------------------------
> > > +//   Necessary because of the way generic selections work
> > > +//
> > > ----------------------------------------------------------------------------
> > > +{
> > > +    return i;
> > > +}
> > > +
> > > +
> > > +static inline uintptr_t _recorder_signed(intptr_t i)
> > > +//
> > > ----------------------------------------------------------------------------
> > > +//   Necessary because of the way generic selections work
> > > +//
> > > ----------------------------------------------------------------------------
> > > +{
> > > +    return (uintptr_t) i;
> > > +}
> > > +
> > > +
> > > +static inline uintptr_t _recorder_pointer(const void *i)
> > > +//
> > > ----------------------------------------------------------------------------
> > > +//   Necessary because of the way generic selections work
> > > +//
> > > ----------------------------------------------------------------------------
> > > +{
> > > +    return (uintptr_t) i;
> > > +}
> > > +
> > > +#endif // __cplusplus
> > > +
> > > +
> > > +static inline uintptr_t _recorder_float(float f)
> > > +//
> > > ----------------------------------------------------------------------------
> > > +//   Convert floating point number to intptr_t representation for
> > > recorder
> > > +//
> > > ----------------------------------------------------------------------------
> > > +{
> > > +    if (sizeof(float) == sizeof(intptr_t)) {
> > > +        union { float f; uintptr_t i; } u;
> > > +        u.f = f;
> > > +        return u.i;
> > > +    } else {
> > > +        union { double d; uintptr_t i; } u;
> > > +        u.d = (double) f;
> > > +        return u.i;
> > > +    }
> > > +}
> > > +
> > > +
> > > +static inline uintptr_t _recorder_double(double d)
> > > +//
> > > ----------------------------------------------------------------------------
> > > +//   Convert double-precision floating point number to intptr_t
> > > representation
> > > +//
> > > ----------------------------------------------------------------------------
> > > +{
> > > +    if (sizeof(double) == sizeof(intptr_t)) {
> > > +        union { double d; uintptr_t i; } u;
> > > +        u.d = d;
> > > +        return u.i;
> > > +    } else {
> > > +        // Better to lose precision than not store any data
> > > +        union { float f; uintptr_t i; } u;
> > > +        u.f = d;
> > > +        return u.i;
> > > +    }
> > > +}
> > > diff --git a/tests/test-dummy-recorder.c b/tests/test-dummy-recorder.c
> > > index 4e674a9..5be4cbd 100644
> > > --- a/tests/test-dummy-recorder.c
> > > +++ b/tests/test-dummy-recorder.c
> > > @@ -19,7 +19,8 @@
> > >  #include <config.h>
> > >  #include <assert.h>
> > >
> > > -#undef ENABLE_RECORDER
> > > +#undef ENABLE_C3D_RECORDER
> > > +#undef ENABLE_AGENT_INTERFACE
> > >
> > >  #include <common/recorder.h>
> > >
> >
> > Frediano
> 



More information about the Spice-devel mailing list