mirror of
https://gitlab.freedesktop.org/dbus/dbus.git
synced 2025-12-26 00:30:09 +01:00
* Throughout, grand renaming to strip out the use of "service", just say "name" instead (or "bus name" when ambiguous). Did not change the internal code of the message bus itself, only the programmer-facing API and messages. * doc/dbus-specification.xml: further update the message bus section * bus/config-parser.c (all_are_equiv): fix bug using freed string in error case
1150 lines
30 KiB
C
1150 lines
30 KiB
C
/* -*- 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
*
|
|
*/
|
|
|
|
#include <config.h>
|
|
#include <glib.h>
|
|
#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 750000
|
|
#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.EchoTestServer"
|
|
#define ECHO_PATH "/org/freedesktop/EchoTest"
|
|
#define ECHO_INTERFACE "org.freedesktop.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_ORG_FREEDESKTOP_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 (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_disconnect (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_ORG_FREEDESKTOP_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)
|
|
{
|
|
g_printerr ("The following g_warning is because we try to call g_source_remove_poll() after g_source_destroy() in dbus-gmain.c, I think we need to add a source free func that clears out the watch/timeout funcs\n");
|
|
|
|
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 (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_disconnect (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_ORG_FREEDESKTOP_LOCAL,
|
|
"Disconnected"))
|
|
{
|
|
g_printerr ("Server disconnected from message bus\n");
|
|
exit (1);
|
|
}
|
|
else if (dbus_message_has_sender (message,
|
|
DBUS_SERVICE_ORG_FREEDESKTOP_DBUS) &&
|
|
dbus_message_is_signal (message,
|
|
DBUS_INTERFACE_ORG_FREEDESKTOP_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_ORG_FREEDESKTOP_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];
|
|
|
|
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);
|
|
|
|
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);
|
|
}
|
|
|
|
int
|
|
main (int argc, char *argv[])
|
|
{
|
|
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 ();
|
|
|
|
return 0;
|
|
}
|