> > 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@xxxxxxxxxx> Acked the series > --- > 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@xxxxxxxxxxxx> > +// > ***************************************************************************** > +// 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 _______________________________________________ Spice-devel mailing list Spice-devel@xxxxxxxxxxxxxxxxxxxxx https://lists.freedesktop.org/mailman/listinfo/spice-devel