diff options
Diffstat (limited to 'test/core/test-profile.c')
-rw-r--r-- | test/core/test-profile.c | 1160 |
1 files changed, 1160 insertions, 0 deletions
diff --git a/test/core/test-profile.c b/test/core/test-profile.c new file mode 100644 index 0000000..10baa0b --- /dev/null +++ b/test/core/test-profile.c @@ -0,0 +1,1160 @@ +/* -*- mode: C; c-file-style: "gnu" -*- */ +/* test-profile.c Program that does basic message-response for timing; doesn't really use glib bindings + * + * Copyright (C) 2003, 2004 Red Hat Inc. + * + * Licensed under the Academic Free License version 2.1 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#include <config.h> +#include <glib.h> + +/* This test uses Unix-specific facilities */ +#ifdef G_OS_WIN32 +#define TEST_PROFILE_DISABLED +#endif + +#ifndef TEST_PROFILE_DISABLED + +#include <dbus/dbus-glib-lowlevel.h> +#include <stdlib.h> +#include <unistd.h> + +#include <errno.h> +#include <fcntl.h> +#include <sys/socket.h> +#include <sys/un.h> +#include <netinet/in.h> +#include <string.h> +#include <sys/time.h> +#include <sys/stat.h> +#ifndef HAVE_SOCKLEN_T +#define socklen_t int +#endif + +#define _DBUS_ZERO(object) (memset (&(object), '\0', sizeof ((object)))) +#define _DBUS_MAX_SUN_PATH_LENGTH 99 + +/* Note that if you set threads > 1 you get a bogus profile since the + * clients start blocking on the server, so the client write() will go + * higher in the profile the larger the number of threads. + */ +#define N_CLIENT_THREADS 1 +/* It seems like at least 750000 or so iterations reduces the variability to sane levels */ +#define N_ITERATIONS 2000 +#define N_PROGRESS_UPDATES 20 +/* Don't make PAYLOAD_SIZE too huge because it gets used as a static buffer size */ +#define PAYLOAD_SIZE 0 + +#define ECHO_SERVICE "org.freedesktop.DBus.GLib.EchoTestServer" +#define ECHO_PATH "/org/freedesktop/DBus/GLib/EchoTest" +#define ECHO_INTERFACE "org.freedesktop.DBus.GLib.EchoTest" +#define ECHO_PING_METHOD "Ping" + +static const char *messages_address; +static const char *plain_sockets_address; +static unsigned char *payload; +static int echo_call_size; +static int echo_return_size; + +typedef struct ProfileRunVTable ProfileRunVTable; + +typedef struct +{ + const ProfileRunVTable *vtable; + int iterations; + GMainLoop *loop; +} ClientData; + +typedef struct +{ + const ProfileRunVTable *vtable; + int handled; + GMainLoop *loop; + int n_clients; +} ServerData; + +struct ProfileRunVTable +{ + const char *name; + gboolean fake_malloc_overhead; + void* (* init_server) (ServerData *sd); + void (* stop_server) (ServerData *sd, + void *server); + void* (* client_thread_func) (void *data); /* Data has to be the vtable */ + + /* this is so different runs show up in the profiler with + * different backtrace + */ + void (* main_loop_run_func) (GMainLoop *loop); +}; + +/* Note, this is all crack-a-rific; it isn't using DBusGProxy and thus is + * a major pain + */ +static void +send_echo_method_call (DBusConnection *connection) +{ + DBusMessage *message; + const char *hello = "Hello World!"; + dbus_int32_t i32 = 123456; + + message = dbus_message_new_method_call (ECHO_SERVICE, + ECHO_PATH, + ECHO_INTERFACE, + ECHO_PING_METHOD); + dbus_message_append_args (message, + DBUS_TYPE_STRING, &hello, + DBUS_TYPE_INT32, &i32, +#if PAYLOAD_SIZE > 0 + DBUS_TYPE_ARRAY, DBUS_TYPE_BYTE, + &payload, PAYLOAD_SIZE, +#endif + DBUS_TYPE_INVALID); + + dbus_connection_send (connection, message, NULL); + dbus_message_unref (message); + dbus_connection_flush (connection); +} + +static void +send_echo_method_return (DBusConnection *connection, + DBusMessage *call_message) +{ + DBusMessage *message; + + message = dbus_message_new_method_return (call_message); + + dbus_connection_send (connection, message, NULL); + dbus_message_unref (message); + dbus_connection_flush (connection); +} + +static DBusHandlerResult +with_or_without_bus_client_filter (DBusConnection *connection, + DBusMessage *message, + ClientData *cd) +{ + if (dbus_message_is_signal (message, + DBUS_INTERFACE_LOCAL, + "Disconnected")) + { + g_printerr ("Client thread disconnected\n"); + exit (1); + } + else if (dbus_message_get_type (message) == DBUS_MESSAGE_TYPE_METHOD_RETURN) + { + cd->iterations += 1; + if (cd->iterations >= N_ITERATIONS) + { + g_printerr ("\nCompleted %d iterations\n", N_ITERATIONS); + g_main_loop_quit (cd->loop); + } + else if (cd->iterations % (N_ITERATIONS/N_PROGRESS_UPDATES) == 0) + { + g_printerr ("%d%% ", (int) (cd->iterations/(double)N_ITERATIONS * 100.0)); + } + + send_echo_method_call (connection); + return DBUS_HANDLER_RESULT_HANDLED; + } + + return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; +} + +static DBusHandlerResult +no_bus_client_filter (DBusConnection *connection, + DBusMessage *message, + void *user_data) +{ + ClientData *cd = user_data; + + return with_or_without_bus_client_filter (connection, message, cd); +} + +static void* +no_bus_thread_func (void *data) +{ + DBusError error; + GMainContext *context; + DBusConnection *connection; + ClientData cd; + + g_printerr ("Starting client thread %p\n", g_thread_self()); + + dbus_error_init (&error); + connection = dbus_connection_open_private (messages_address, &error); + if (connection == NULL) + { + g_printerr ("could not open connection: %s\n", error.message); + dbus_error_free (&error); + exit (1); + } + + context = g_main_context_new (); + + cd.iterations = 1; + cd.loop = g_main_loop_new (context, FALSE); + + if (!dbus_connection_add_filter (connection, + no_bus_client_filter, &cd, NULL)) + g_error ("no memory"); + + + dbus_connection_setup_with_g_main (connection, context); + + g_printerr ("Client thread sending message to prime pingpong\n"); + send_echo_method_call (connection); + g_printerr ("Client thread sent message\n"); + + g_printerr ("Client thread entering main loop\n"); + g_main_loop_run (cd.loop); + g_printerr ("Client thread %p exiting main loop\n", + g_thread_self()); + + dbus_connection_close (connection); + + g_main_loop_unref (cd.loop); + g_main_context_unref (context); + + return NULL; +} + +static DBusHandlerResult +no_bus_server_filter (DBusConnection *connection, + DBusMessage *message, + void *user_data) +{ + ServerData *sd = user_data; + + if (dbus_message_is_signal (message, + DBUS_INTERFACE_LOCAL, + "Disconnected")) + { + g_printerr ("Client disconnected from server\n"); + sd->n_clients -= 1; + if (sd->n_clients == 0) + g_main_loop_quit (sd->loop); + } + else if (dbus_message_is_method_call (message, + ECHO_INTERFACE, + ECHO_PING_METHOD)) + { + sd->handled += 1; + send_echo_method_return (connection, message); + return DBUS_HANDLER_RESULT_HANDLED; + } + + return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; +} + +static void +no_bus_new_connection_callback (DBusServer *server, + DBusConnection *new_connection, + void *user_data) +{ + ServerData *sd = user_data; + + dbus_connection_ref (new_connection); + dbus_connection_setup_with_g_main (new_connection, NULL); + + if (!dbus_connection_add_filter (new_connection, + no_bus_server_filter, sd, NULL)) + g_error ("no memory"); + + sd->n_clients += 1; + + /* FIXME we leak the handler */ +} + +static void* +no_bus_init_server (ServerData *sd) +{ + DBusServer *server; + DBusError error; + + dbus_error_init (&error); + server = dbus_server_listen ("unix:tmpdir="DBUS_TEST_SOCKET_DIR, + &error); + if (server == NULL) + { + g_printerr ("Could not start server: %s\n", + error.message); + exit (1); + } + + messages_address = dbus_server_get_address (server); + + dbus_server_set_new_connection_function (server, + no_bus_new_connection_callback, + sd, NULL); + + dbus_server_setup_with_g_main (server, NULL); + + return server; +} + +static void +no_bus_stop_server (ServerData *sd, + void *server) +{ + dbus_server_disconnect (server); + dbus_server_unref (server); +} + +static void +no_bus_main_loop_run (GMainLoop *loop) +{ + g_main_loop_run (loop); +} + +static const ProfileRunVTable no_bus_vtable = { + "dbus direct without bus", + FALSE, + no_bus_init_server, + no_bus_stop_server, + no_bus_thread_func, + no_bus_main_loop_run +}; + +typedef struct +{ + const ProfileRunVTable *vtable; + ServerData *sd; + GHashTable *client_names; + DBusConnection *connection; +} WithBusServer; + +static DBusHandlerResult +with_bus_client_filter (DBusConnection *connection, + DBusMessage *message, + void *user_data) +{ + ClientData *cd = user_data; + + return with_or_without_bus_client_filter (connection, message, cd); +} + +static void* +with_bus_thread_func (void *data) +{ + DBusError error; + DBusConnection *connection; + ClientData cd; + const char *address; + GMainContext *context; + + g_printerr ("Starting client thread %p\n", g_thread_self()); + + address = g_getenv ("DBUS_SESSION_BUS_ADDRESS"); + if (address == NULL) + { + g_printerr ("DBUS_SESSION_BUS_ADDRESS not set\n"); + exit (1); + } + + dbus_error_init (&error); + connection = dbus_connection_open_private (address, &error); + if (connection == NULL) + { + g_printerr ("could not open connection to bus: %s\n", error.message); + dbus_error_free (&error); + exit (1); + } + + if (!dbus_bus_register (connection, &error)) + { + g_printerr ("could not register with bus: %s\n", error.message); + dbus_error_free (&error); + exit (1); + } + + context = g_main_context_new (); + + cd.iterations = 1; + cd.loop = g_main_loop_new (context, FALSE); + + if (!dbus_connection_add_filter (connection, + with_bus_client_filter, &cd, NULL)) + g_error ("no memory"); + + dbus_connection_setup_with_g_main (connection, context); + + g_printerr ("Client thread sending message to prime pingpong\n"); + send_echo_method_call (connection); + g_printerr ("Client thread sent message\n"); + + g_printerr ("Client thread entering main loop\n"); + g_main_loop_run (cd.loop); + g_printerr ("Client thread %p exiting main loop\n", + g_thread_self()); + + dbus_connection_close (connection); + + g_main_loop_unref (cd.loop); + g_main_context_unref (context); + + return NULL; +} + +static DBusHandlerResult +with_bus_server_filter (DBusConnection *connection, + DBusMessage *message, + void *user_data) +{ + WithBusServer *server = user_data; + + if (dbus_message_is_signal (message, + DBUS_INTERFACE_LOCAL, + "Disconnected")) + { + g_printerr ("Server disconnected from message bus\n"); + exit (1); + } + else if (dbus_message_has_sender (message, + DBUS_SERVICE_DBUS) && + dbus_message_is_signal (message, + DBUS_INTERFACE_DBUS, + "NameOwnerChanged")) + { + const char *name, *old_owner, *new_owner; + DBusError error; + + name = NULL; + old_owner = NULL; + new_owner = NULL; + + dbus_error_init (&error); + if (!dbus_message_get_args (message, + &error, + DBUS_TYPE_STRING, &name, + DBUS_TYPE_STRING, &old_owner, + DBUS_TYPE_STRING, &new_owner, + DBUS_TYPE_INVALID)) + { + g_printerr ("dbus_message_get_args(): %s\n", error.message); + exit (1); + } + + if (g_hash_table_lookup (server->client_names, + name) && + *old_owner != '\0' && + *new_owner == '\0') + { + g_hash_table_remove (server->client_names, + name); + server->sd->n_clients -= 1; + if (server->sd->n_clients == 0) + g_main_loop_quit (server->sd->loop); + } + } + else if (dbus_message_is_method_call (message, + ECHO_INTERFACE, + ECHO_PING_METHOD)) + { + const char *sender; + + sender = dbus_message_get_sender (message); + + if (!g_hash_table_lookup (server->client_names, + sender)) + { + g_printerr ("First message from new client %s on bus\n", sender); + + g_hash_table_replace (server->client_names, + g_strdup (sender), + GINT_TO_POINTER (1)); + server->sd->n_clients += 1; + } + + server->sd->handled += 1; + send_echo_method_return (connection, message); + return DBUS_HANDLER_RESULT_HANDLED; + } + + return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; +} + +static void* +with_bus_init_server (ServerData *sd) +{ + DBusGConnection *gconnection; + DBusConnection *connection; + GError *gerror; + const char *s; + WithBusServer *server; + char *rule; + + server = g_new0 (WithBusServer, 1); + + server->vtable = sd->vtable; + server->sd = sd; + + s = g_getenv ("DBUS_TEST_GLIB_RUN_TEST_SCRIPT"); + if (s == NULL || + *s != '1') + { + g_printerr ("You have to run with_bus mode with the run-test.sh script\n"); + exit (1); + } + + /* Note that we use the standard global bus connection for the + * server, and the clients open their own connections so they can + * have their own main loops and because I'm not sure "talking to + * yourself" really works yet + */ + gerror = NULL; + gconnection = dbus_g_bus_get (DBUS_BUS_SESSION, &gerror); + if (gconnection == NULL) + { + g_printerr ("could not open connection to bus: %s\n", gerror->message); + g_error_free (gerror); + exit (1); + } + + server->client_names = g_hash_table_new_full (g_str_hash, g_str_equal, + g_free, NULL); + + connection = dbus_g_connection_get_connection (gconnection); + + dbus_bus_request_name (connection, + ECHO_SERVICE, + 0, NULL); /* ignore errors because we suck */ + + rule = g_strdup_printf ("type='signal',sender='%s',member='%s'", + DBUS_SERVICE_DBUS, + "NameOwnerChanged"); + + /* ignore errors because we suck */ + dbus_bus_add_match (connection, rule, NULL); + + g_free (rule); + + if (!dbus_connection_add_filter (connection, + with_bus_server_filter, server, NULL)) + g_error ("no memory"); + + server->connection = connection; + server->client_names = g_hash_table_new_full (g_str_hash, g_str_equal, + g_free, NULL); + + return server; +} + +static void +with_bus_stop_server (ServerData *sd, + void *serverv) +{ + WithBusServer *server = serverv; + + dbus_connection_remove_filter (server->connection, + with_bus_server_filter, server); + + g_hash_table_destroy (server->client_names); + dbus_connection_unref (server->connection); + + g_free (server); +} + +static void +with_bus_main_loop_run (GMainLoop *loop) +{ + g_main_loop_run (loop); +} + +static const ProfileRunVTable with_bus_vtable = { + "routing via a bus", + FALSE, + with_bus_init_server, + with_bus_stop_server, + with_bus_thread_func, + with_bus_main_loop_run +}; + + +typedef struct +{ + const ProfileRunVTable *vtable; + int listen_fd; + ServerData *sd; + unsigned int source_id; +} PlainSocketServer; + +static void +read_and_drop_on_floor (int fd, + int count, + gboolean fake_malloc_overhead) +{ + int bytes_read; + int val; + char *buf; + char *allocated; + char not_allocated[512+PAYLOAD_SIZE]; + + g_assert (count < (int) sizeof(not_allocated)); + + if (fake_malloc_overhead) + { + allocated = g_malloc (count); + buf = allocated; + } + else + { + allocated = NULL; + buf = not_allocated; + } + + bytes_read = 0; + + while (bytes_read < count) + { + again: + + val = read (fd, buf + bytes_read, count - bytes_read); + + if (val < 0) + { + if (errno == EINTR) + goto again; + else + { + g_printerr ("read() failed thread %p: %s\n", + g_thread_self(), strerror (errno)); + exit (1); + } + } + else + { + bytes_read += val; + } + } + + if (fake_malloc_overhead) + g_free (allocated); + +#if 0 + g_printerr ("%p read %d bytes from fd %d\n", + g_thread_self(), bytes_read, fd); +#endif +} + +static void +write_junk (int fd, + int count, + gboolean fake_malloc_overhead) +{ + int bytes_written; + int val; + char *buf; + char *allocated; + char not_allocated[512+PAYLOAD_SIZE] = { '\0', }; + + g_assert (count < (int) sizeof(not_allocated)); + + if (fake_malloc_overhead) + { + int i; + + allocated = g_malloc (count); + buf = allocated; + + /* Write some stuff into the allocated buffer to simulate + * creating some sort of data + */ + i = 0; + while (i < count) + { + allocated[i] = (char) i; + ++i; + } + } + else + { + allocated = NULL; + buf = not_allocated; + } + + bytes_written = 0; + + while (bytes_written < count) + { + again: + + val = write (fd, buf + bytes_written, count - bytes_written); + + if (val < 0) + { + if (errno == EINTR) + goto again; + else + { + g_printerr ("write() failed thread %p: %s\n", + g_thread_self(), strerror (errno)); + exit (1); + } + } + else + { + bytes_written += val; + } + } + + if (fake_malloc_overhead) + g_free (allocated); + +#if 0 + g_printerr ("%p wrote %d bytes to fd %d\n", + g_thread_self(), bytes_written, fd); +#endif +} + +static gboolean +plain_sockets_talk_to_client_watch (GIOChannel *source, + GIOCondition condition, + gpointer data) +{ + PlainSocketServer *server = data; + int client_fd = g_io_channel_unix_get_fd (source); + + if (condition & G_IO_HUP) + { + g_printerr ("Client disconnected from server\n"); + server->sd->n_clients -= 1; + if (server->sd->n_clients == 0) + g_main_loop_quit (server->sd->loop); + + return FALSE; /* remove watch */ + } + else if (condition & G_IO_IN) + { + server->sd->handled += 1; + + read_and_drop_on_floor (client_fd, echo_call_size, server->vtable->fake_malloc_overhead); + write_junk (client_fd, echo_return_size, server->vtable->fake_malloc_overhead); + } + else + { + g_printerr ("Unexpected IO condition in server thread\n"); + exit (1); + } + + return TRUE; +} + +static gboolean +plain_sockets_new_client_watch (GIOChannel *source, + GIOCondition condition, + gpointer data) +{ + int client_fd; + struct sockaddr addr; + socklen_t addrlen; + GIOChannel *channel; + PlainSocketServer *server = data; + + if (!(condition & G_IO_IN)) + { + g_printerr ("Unexpected IO condition on server socket\n"); + exit (1); + } + + addrlen = sizeof (addr); + + retry: + client_fd = accept (server->listen_fd, &addr, &addrlen); + + if (client_fd < 0) + { + if (errno == EINTR) + goto retry; + else + { + g_printerr ("Failed to accept() connection from client: %s\n", + strerror (errno)); + exit (1); + } + } + + channel = g_io_channel_unix_new (client_fd); + g_io_add_watch (channel, + G_IO_IN | G_IO_ERR | G_IO_HUP | G_IO_NVAL | G_IO_PRI, + plain_sockets_talk_to_client_watch, + server); + g_io_channel_unref (channel); + + server->sd->n_clients += 1; + + return TRUE; +} + +static void* +plain_sockets_init_server (ServerData *sd) +{ + PlainSocketServer *server; + struct sockaddr_un addr; + static char path[] = "/tmp/dbus-test-profile-XXXXXX"; + char *p; + GIOChannel *channel; + + server = g_new0 (PlainSocketServer, 1); + server->sd = sd; + server->vtable = sd->vtable; /* for convenience */ + + p = path; + while (*p) + { + if (*p == 'X') + *p = 'a' + (int) (26.0*rand()/(RAND_MAX+1.0)); + ++p; + } + + g_printerr ("Socket is %s\n", path); + + server->listen_fd = socket (PF_UNIX, SOCK_STREAM, 0); + + if (server->listen_fd < 0) + { + g_printerr ("Failed to create socket: %s", + strerror (errno)); + exit (1); + } + + _DBUS_ZERO (addr); + addr.sun_family = AF_UNIX; + +#ifdef HAVE_ABSTRACT_SOCKETS + /* remember that abstract names aren't nul-terminated so we rely + * on sun_path being filled in with zeroes above. + */ + addr.sun_path[0] = '\0'; /* this is what says "use abstract" */ + strncpy (&addr.sun_path[1], path, _DBUS_MAX_SUN_PATH_LENGTH - 2); + /* _dbus_verbose_bytes (addr.sun_path, sizeof (addr.sun_path)); */ +#else /* HAVE_ABSTRACT_SOCKETS */ + { + struct stat sb; + + if (stat (path, &sb) == 0 && + S_ISSOCK (sb.st_mode)) + unlink (path); + } + + strncpy (addr.sun_path, path, _DBUS_MAX_SUN_PATH_LENGTH - 1); +#endif /* ! HAVE_ABSTRACT_SOCKETS */ + + if (bind (server->listen_fd, (struct sockaddr*) &addr, sizeof (addr)) < 0) + { + g_printerr ("Failed to bind socket \"%s\": %s", + path, strerror (errno)); + exit (1); + } + + if (listen (server->listen_fd, 30 /* backlog */) < 0) + { + g_printerr ("Failed to listen on socket \"%s\": %s", + path, strerror (errno)); + exit (1); + } + + plain_sockets_address = path; + + channel = g_io_channel_unix_new (server->listen_fd); + server->source_id = + g_io_add_watch (channel, + G_IO_IN | G_IO_ERR | G_IO_HUP | G_IO_NVAL | G_IO_PRI, + plain_sockets_new_client_watch, + server); + g_io_channel_unref (channel); + + return server; +} + +static void +plain_sockets_stop_server (ServerData *sd, + void *server_v) +{ + PlainSocketServer *server = server_v; + + g_source_remove (server->source_id); + + close (server->listen_fd); + g_free (server); + + { + struct stat sb; + + if (stat (plain_sockets_address, &sb) == 0 && + S_ISSOCK (sb.st_mode)) + unlink (plain_sockets_address); + } +} + +static gboolean +plain_sockets_client_side_watch (GIOChannel *source, + GIOCondition condition, + gpointer data) +{ + ClientData *cd = data; + int fd = g_io_channel_unix_get_fd (source); + + if (condition & G_IO_IN) + { + read_and_drop_on_floor (fd, echo_return_size, cd->vtable->fake_malloc_overhead); + } + else if (condition & G_IO_OUT) + { + cd->iterations += 1; + if (cd->iterations >= N_ITERATIONS) + { + g_printerr ("\nCompleted %d iterations\n", N_ITERATIONS); + g_main_loop_quit (cd->loop); + } + else if (cd->iterations % (N_ITERATIONS/N_PROGRESS_UPDATES) == 0) + { + g_printerr ("%d%% ", (int) (cd->iterations/(double)N_ITERATIONS * 100.0)); + } + + write_junk (fd, echo_call_size, cd->vtable->fake_malloc_overhead); + } + else + { + g_printerr ("Unexpected IO condition in client thread\n"); + exit (1); + } + + return TRUE; +} + +static void* +plain_sockets_thread_func (void *data) +{ + GMainContext *context; + ClientData cd; + int fd; + struct sockaddr_un addr; + GIOChannel *channel; + GSource *gsource; + + g_printerr ("Starting client thread %p\n", + g_thread_self()); + + fd = socket (PF_UNIX, SOCK_STREAM, 0); + + if (fd < 0) + { + g_printerr ("Failed to create socket: %s", + strerror (errno)); + exit (1); + } + + _DBUS_ZERO (addr); + addr.sun_family = AF_UNIX; + +#ifdef HAVE_ABSTRACT_SOCKETS + /* remember that abstract names aren't nul-terminated so we rely + * on sun_path being filled in with zeroes above. + */ + addr.sun_path[0] = '\0'; /* this is what says "use abstract" */ + strncpy (&addr.sun_path[1], plain_sockets_address, _DBUS_MAX_SUN_PATH_LENGTH - 2); + /* _dbus_verbose_bytes (addr.sun_path, sizeof (addr.sun_path)); */ +#else /* HAVE_ABSTRACT_SOCKETS */ + strncpy (addr.sun_path, plain_sockets_address, _DBUS_MAX_SUN_PATH_LENGTH - 1); +#endif /* ! HAVE_ABSTRACT_SOCKETS */ + + if (connect (fd, (struct sockaddr*) &addr, sizeof (addr)) < 0) + { + g_printerr ("Failed to connect to socket %s: %s", + plain_sockets_address, strerror (errno)); + exit (1); + } + + context = g_main_context_new (); + + cd.iterations = 1; + cd.loop = g_main_loop_new (context, FALSE); + cd.vtable = data; + + channel = g_io_channel_unix_new (fd); + + gsource = g_io_create_watch (channel, + G_IO_IN | G_IO_OUT | + G_IO_ERR | G_IO_HUP | G_IO_NVAL | G_IO_PRI); + + g_source_set_callback (gsource, + (GSourceFunc)plain_sockets_client_side_watch, + &cd, NULL); + + g_source_attach (gsource, context); + + g_io_channel_unref (channel); + + g_printerr ("Client thread writing to prime pingpong\n"); + write_junk (fd, echo_call_size, cd.vtable->fake_malloc_overhead); + g_printerr ("Client thread done writing primer\n"); + + g_printerr ("Client thread entering main loop\n"); + g_main_loop_run (cd.loop); + g_printerr ("Client thread %p exiting main loop\n", + g_thread_self()); + + g_source_destroy (gsource); + + close (fd); + + g_main_loop_unref (cd.loop); + g_main_context_unref (context); + + return NULL; +} + +static void +plain_sockets_main_loop_run (GMainLoop *loop) +{ + g_main_loop_run (loop); +} + +static const ProfileRunVTable plain_sockets_vtable = { + "plain sockets", + FALSE, + plain_sockets_init_server, + plain_sockets_stop_server, + plain_sockets_thread_func, + plain_sockets_main_loop_run +}; + +static const ProfileRunVTable plain_sockets_with_malloc_vtable = { + "plain sockets with malloc overhead", + TRUE, + plain_sockets_init_server, + plain_sockets_stop_server, + plain_sockets_thread_func, + plain_sockets_main_loop_run +}; + +static double +do_profile_run (const ProfileRunVTable *vtable) +{ + GTimer *timer; + int i; + double secs; + ServerData sd; + void *server; + + g_printerr ("Profiling %s\n", vtable->name); + + sd.handled = 0; + sd.n_clients = 0; + sd.loop = g_main_loop_new (NULL, FALSE); + sd.vtable = vtable; + + server = (* vtable->init_server) (&sd); + + for (i = 0; i < N_CLIENT_THREADS; i++) + { + g_thread_create (vtable->client_thread_func, (void*) vtable, FALSE, NULL); + } + + timer = g_timer_new (); + + g_printerr ("Server thread %p entering main loop\n", + g_thread_self()); + (* vtable->main_loop_run_func) (sd.loop); + g_printerr ("Server thread %p exiting main loop\n", + g_thread_self()); + + secs = g_timer_elapsed (timer, NULL); + g_timer_destroy (timer); + + g_printerr ("%s: %g seconds, %d round trips, %f seconds per pingpong\n", + vtable->name, secs, sd.handled, secs/sd.handled); + + (* vtable->stop_server) (&sd, server); + + g_main_loop_unref (sd.loop); + + return secs; +} + +static void +print_result (const ProfileRunVTable *vtable, + double seconds, + double baseline) +{ + g_printerr (" %g times slower for %s (%g seconds, %f per iteration)\n", + seconds/baseline, vtable->name, + seconds, seconds / N_ITERATIONS); +} +#endif + +int +main (int argc, char *argv[]) +{ +#ifndef TEST_PROFILE_DISABLED + g_thread_init (NULL); + dbus_g_thread_init (); + +#ifndef DBUS_DISABLE_ASSERT + g_printerr ("You should probably --disable-asserts before you profile as they have noticeable overhead\n"); +#endif + +#if DBUS_ENABLE_VERBOSE_MODE + g_printerr ("You should probably --disable-verbose-mode before you profile as verbose has noticeable overhead\n"); +#endif + + payload = g_malloc (PAYLOAD_SIZE); + + /* The actual size of the DBusMessage on the wire, as of Nov 23 2004, + * without the payload + */ + echo_call_size = 140 + PAYLOAD_SIZE; + echo_return_size = 32; + + if (argc > 1 && strcmp (argv[1], "plain_sockets") == 0) + do_profile_run (&plain_sockets_vtable); + else if (argc > 1 && strcmp (argv[1], "plain_sockets_with_malloc") == 0) + do_profile_run (&plain_sockets_with_malloc_vtable); + else if (argc > 1 && strcmp (argv[1], "no_bus") == 0) + do_profile_run (&no_bus_vtable); + else if (argc > 1 && strcmp (argv[1], "with_bus") == 0) + do_profile_run (&with_bus_vtable); + else if (argc > 1 && strcmp (argv[1], "all") == 0) + { + double e1, e2, e3, e4; + + e1 = do_profile_run (&plain_sockets_vtable); + e2 = do_profile_run (&plain_sockets_with_malloc_vtable); + e3 = do_profile_run (&no_bus_vtable); + e4 = do_profile_run (&with_bus_vtable); + + g_printerr ("Baseline plain sockets time %g seconds for %d iterations\n", + e1, N_ITERATIONS); + print_result (&plain_sockets_vtable, e1, e1); + print_result (&plain_sockets_with_malloc_vtable, e2, e1); + print_result (&no_bus_vtable, e3, e1); + print_result (&with_bus_vtable, e4, e1); + } + else + { + g_printerr ("Specify profile type plain_sockets, plain_sockets_with_malloc, no_bus, with_bus, all\n"); + exit (1); + } + + /* Make valgrind happy */ + dbus_shutdown (); +#endif /* TEST_PROFILE_DISABLED */ + return 0; +} |