diff options
| author | Simon McVittie <simon.mcvittie@collabora.co.uk> | 2011-03-16 14:56:22 +0000 |
|---|---|---|
| committer | Simon McVittie <simon.mcvittie@collabora.co.uk> | 2011-04-29 15:40:02 +0100 |
| commit | 8cd52e9bd6fb43ce5a2c5438bedc4170caba3f8c (patch) | |
| tree | 090b97dc782c47bead788eac0aad5055511f138f | |
| parent | 2f67fcaf913a90053dca13d6993d15d094267ac7 (diff) | |
WiP: horde of screaming children testwip-heap-profiling
| -rw-r--r-- | test/.gitignore | 1 | ||||
| -rw-r--r-- | test/Makefile.am | 8 | ||||
| -rw-r--r-- | test/daemon-memory.c | 433 | ||||
| -rw-r--r-- | test/data/valid-config-files/limits.conf | 19 | ||||
| -rw-r--r-- | test/data/valid-config-files/outgoing-limit.conf | 18 | ||||
| -rw-r--r-- | test/data/valid-config-files/realistic-incoming-limit.conf | 18 |
6 files changed, 497 insertions, 0 deletions
diff --git a/test/.gitignore b/test/.gitignore index d63568d8..46304bae 100644 --- a/test/.gitignore +++ b/test/.gitignore @@ -25,4 +25,5 @@ test-shell-service test-names test-loopback test-relay +test-daemon-memory test-dbus-daemon diff --git a/test/Makefile.am b/test/Makefile.am index bb57226d..a8f41284 100644 --- a/test/Makefile.am +++ b/test/Makefile.am @@ -73,6 +73,7 @@ spawn_test_LDFLAGS=@R_DYNAMIC_LDFLAG@ EXTRA_DIST= modular_tests = \ + test-daemon-memory \ test-dbus-daemon \ test-loopback \ test-relay @@ -103,6 +104,13 @@ test_relay_LDADD = $(top_builddir)/dbus/libdbus-1.la \ $(GLIB_LIBS) \ $(DBUS_GLIB_LIBS) +test_daemon_memory_SOURCES = daemon-memory.c +test_daemon_memory_CPPFLAGS = $(GLIB_CFLAGS) $(DBUS_GLIB_CFLAGS) +test_daemon_memory_LDFLAGS = @R_DYNAMIC_LDFLAG@ +test_daemon_memory_LDADD = $(top_builddir)/dbus/libdbus-1.la \ + $(GLIB_LIBS) \ + $(DBUS_GLIB_LIBS) + test_dbus_daemon_SOURCES = dbus-daemon.c test_dbus_daemon_CPPFLAGS = $(GLIB_CFLAGS) $(DBUS_GLIB_CFLAGS) test_dbus_daemon_LDFLAGS = @R_DYNAMIC_LDFLAG@ diff --git a/test/daemon-memory.c b/test/daemon-memory.c new file mode 100644 index 00000000..657a974f --- /dev/null +++ b/test/daemon-memory.c @@ -0,0 +1,433 @@ +/* Memory fragmentation / DoS resilience tests for the bus daemon + * + * Author: Simon McVittie <simon.mcvittie@collabora.co.uk> + * Copyright © 2010 Nokia Corporation + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation files + * (the "Software"), to deal in the Software without restriction, + * including without limitation the rights to use, copy, modify, merge, + * publish, distribute, sublicense, and/or sell copies of the Software, + * and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include <config.h> + +#include <glib.h> + +#include <dbus/dbus.h> +#include <dbus/dbus-glib-lowlevel.h> + +#include <string.h> + +#ifdef DBUS_WIN +# include <windows.h> +#else +# include <signal.h> +# include <unistd.h> +#endif + +#define N_MESSAGES (100 * 1000) +#define MIN_MESSAGE 100 +#define MAX_MESSAGE 1000 +#define N_CONNS 10 + +static unsigned char payload[MAX_MESSAGE + 1] = { 0 }; + +typedef struct { + const char *bug_ref; + const char *config_file; + dbus_bool_t lossy; +} Config; + +typedef struct { + const Config *config; + + DBusError e; + GError *ge; + + gint daemon_pid; + + DBusConnection *senders[N_CONNS]; + DBusConnection *recipients[N_CONNS]; + guint received; + guint errors; +} Fixture; + +#define assert_no_error(e) _assert_no_error (e, __FILE__, __LINE__) +static void +_assert_no_error (const DBusError *e, + const char *file, + int line) +{ + if (G_UNLIKELY (dbus_error_is_set (e))) + g_error ("%s:%d: expected success but got error: %s: %s", + file, line, e->name, e->message); +} + +static void +pmap (GPid pid) +{ +#ifdef DBUS_UNIX + char *pmap_argv[] = { + "pmap", + "-x", + NULL, + NULL + }; + char *pid_str; + + if (! g_test_perf ()) + return; + + pid_str = g_strdup_printf ("%d", pid); + pmap_argv[2] = pid_str; + + /* ignore errors, if any; just run it */ + g_spawn_sync (NULL, pmap_argv, NULL, G_SPAWN_SEARCH_PATH, + NULL, NULL, NULL, NULL, NULL, NULL); + + g_free (pid_str); +#endif +} + +static gchar * +spawn_dbus_daemon (gchar *binary, + gchar *configuration, + gint *daemon_pid) +{ + GError *error = NULL; + GString *address; + gint address_fd; + gchar *argv[] = { +#if 0 + "/usr/bin/valgrind", + "--tool=massif", +#endif + binary, + configuration, + "--nofork", + "--print-address=1", /* stdout */ + NULL + }; + + g_spawn_async_with_pipes (NULL, /* working directory */ + argv, + NULL, /* envp */ + G_SPAWN_DO_NOT_REAP_CHILD, + NULL, /* child_setup */ + NULL, /* user data */ + daemon_pid, + NULL, /* child's stdin = /dev/null */ + &address_fd, + NULL, /* child's stderr = our stderr */ + &error); + g_assert_no_error (error); + + address = g_string_new (NULL); + + /* polling until the dbus-daemon writes out its address is a bit stupid, + * but at least it's simple, unlike dbus-launch... in principle we could + * use select() here, but life's too short */ + while (1) + { + gssize bytes; + gchar buf[4096]; + gchar *newline; + + bytes = read (address_fd, buf, sizeof (buf)); + + if (bytes > 0) + g_string_append_len (address, buf, bytes); + + newline = strchr (address->str, '\n'); + + if (newline != NULL) + { + g_string_truncate (address, newline - address->str); + break; + } + + g_usleep (G_USEC_PER_SEC / 10); + } + + return g_string_free (address, FALSE); +} + +static DBusConnection * +connect_to_bus (const gchar *address) +{ + DBusConnection *conn; + DBusError error = DBUS_ERROR_INIT; + dbus_bool_t ok; + + conn = dbus_connection_open_private (address, &error); + assert_no_error (&error); + g_assert (conn != NULL); + + ok = dbus_bus_register (conn, &error); + assert_no_error (&error); + g_assert (ok); + g_assert (dbus_bus_get_unique_name (conn) != NULL); + + dbus_connection_setup_with_g_main (conn, NULL); + return conn; +} + +static DBusHandlerResult +recipient_filter (DBusConnection *recipient, + DBusMessage *message, + void *user_data) +{ + Fixture *f = user_data; + + if (dbus_message_get_type (message) == DBUS_MESSAGE_TYPE_METHOD_CALL) + { + DBusMessage *reply; + + f->received++; + + reply = dbus_message_new_method_return (message); + + if (reply == NULL || !dbus_connection_send (recipient, reply, NULL)) + g_error ("OOM"); + + return DBUS_HANDLER_RESULT_HANDLED; + } + else + { + return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; + } +} + +static DBusHandlerResult +sender_filter (DBusConnection *sender, + DBusMessage *message, + void *user_data) +{ + Fixture *f = user_data; + + if (dbus_message_get_type (message) == DBUS_MESSAGE_TYPE_ERROR) + { + f->errors++; + return DBUS_HANDLER_RESULT_HANDLED; + } + else + { + return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; + } +} + +static void +setup (Fixture *f, + gconstpointer context) +{ + const Config *config = context; + gchar *dbus_daemon; + gchar *config_arg; + gchar *address; + guint i; + + f->config = config; + f->ge = NULL; + dbus_error_init (&f->e); + + g_assert (g_getenv ("DBUS_TEST_DAEMON") != NULL); + + dbus_daemon = g_strdup (g_getenv ("DBUS_TEST_DAEMON")); + + if (config != NULL && config->config_file != NULL) + { + g_assert (g_getenv ("DBUS_TEST_DATA") != NULL); + config_arg = g_strdup_printf ("--config-file=%s/%s", + g_getenv ("DBUS_TEST_DATA"), config->config_file); + } + else if (g_getenv ("DBUS_TEST_USE_INSTALLED") != NULL) + { + config_arg = g_strdup ("--session"); + } + else + { + g_assert (g_getenv ("DBUS_TEST_DATA") != NULL); + config_arg = g_strdup_printf ( + "--config-file=%s/valid-config-files/session.conf", + g_getenv ("DBUS_TEST_DATA")); + } + + address = spawn_dbus_daemon (dbus_daemon, config_arg, &f->daemon_pid); + + g_free (dbus_daemon); + g_free (config_arg); + + pmap (f->daemon_pid); + + for (i = 0; i < N_CONNS; i++) + { + f->senders[i] = connect_to_bus (address); + f->recipients[i] = connect_to_bus (address); + } + + g_free (address); + + pmap (f->daemon_pid); +} + +static void +test_flood (Fixture *f, + gconstpointer context) +{ + const Config *config = context; + guint count = N_MESSAGES; + double elapsed; + guint i; + + if (! g_test_perf ()) + count = 10000; + + for (i = 0; i < N_CONNS; i++) + { + if (!dbus_connection_add_filter (f->senders[i], sender_filter, f, + NULL)) + g_error ("OOM"); + + if (!dbus_connection_add_filter (f->recipients[i], recipient_filter, f, + NULL)) + g_error ("OOM"); + } + + g_test_timer_start (); + + /* queue up twice as many messages as we want: this will fail + * horribly if we're losing more than 50%, but that'd be bad anyway */ + for (i = 0; i < count * 2; i++) + { + guint src, dest; + dbus_uint32_t len; + DBusMessage *m; + unsigned char *payload_pointer = payload; + + src = g_test_rand_int_range (0, N_CONNS); + dest = g_test_rand_int_range (0, N_CONNS); + + m = dbus_message_new_method_call ( + dbus_bus_get_unique_name (f->recipients[dest]), "/", + "com.example", "Spam"); + + if (m == NULL) + g_error ("OOM"); + + dbus_message_set_no_reply (m, FALSE); + + len = g_test_rand_int_range (MIN_MESSAGE, MAX_MESSAGE + 1); + + if (!dbus_message_append_args (m, + DBUS_TYPE_ARRAY, DBUS_TYPE_BYTE, &payload_pointer, len, + DBUS_TYPE_INVALID)) + g_error ("OOM"); + + if (!dbus_connection_send (f->senders[src], m, NULL)) + g_error ("OOM"); + + dbus_message_unref (m); + } + + while (f->received + f->errors < count) + g_main_context_iteration (NULL, TRUE); + + elapsed = g_test_timer_elapsed (); + + g_test_message ("%u sent, %u received, %u errors in %f seconds", + count * 2, f->received, f->errors, elapsed); + + for (i = 0; i < N_CONNS; i++) + { + dbus_connection_remove_filter (f->senders[i], sender_filter, f); + dbus_connection_remove_filter (f->recipients[i], recipient_filter, f); + } + + g_test_maximized_result (f->received / elapsed, "%u messages / %f seconds", + f->received, elapsed); + + pmap (f->daemon_pid); +} + +static void +teardown (Fixture *f, + gconstpointer context G_GNUC_UNUSED) +{ + guint i; + + dbus_error_free (&f->e); + g_clear_error (&f->ge); + + for (i = 0; i < N_CONNS; i++) + { + if (f->senders[i] != NULL) + { + dbus_connection_close (f->senders[i]); + dbus_connection_unref (f->senders[i]); + f->senders[i] = NULL; + } + + if (f->recipients[i] != NULL) + { + dbus_connection_close (f->recipients[i]); + dbus_connection_unref (f->recipients[i]); + f->recipients[i] = NULL; + } + } + + g_usleep (G_USEC_PER_SEC); + pmap (f->daemon_pid); + +#ifdef DBUS_WIN + TerminateProcess (f->daemon_pid, 1); +#else + kill (f->daemon_pid, SIGTERM); +#endif + + g_spawn_close_pid (f->daemon_pid); +} + +static Config incoming_config = { + NULL, "valid-config-files/realistic-incoming-limit.conf", FALSE +}; + +static Config outgoing_config = { + NULL, "valid-config-files/outgoing-limit.conf", TRUE +}; + +static Config limited_config = { + NULL, "valid-config-files/limits.conf", TRUE +}; + +int +main (int argc, + char **argv) +{ + g_test_init (&argc, &argv, NULL); + g_test_bug_base ("https://bugs.freedesktop.org/show_bug.cgi?id="); + + g_test_add ("/flood/session", Fixture, NULL, setup, test_flood, teardown); + g_test_add ("/flood/in-limit", Fixture, &incoming_config, + setup, test_flood, teardown); + g_test_add ("/flood/out-limit", Fixture, &outgoing_config, + setup, test_flood, teardown); + g_test_add ("/flood/limited", Fixture, &limited_config, + setup, test_flood, teardown); + + return g_test_run (); +} diff --git a/test/data/valid-config-files/limits.conf b/test/data/valid-config-files/limits.conf new file mode 100644 index 00000000..8d6b97f5 --- /dev/null +++ b/test/data/valid-config-files/limits.conf @@ -0,0 +1,19 @@ +<!DOCTYPE busconfig PUBLIC "-//freedesktop//DTD D-Bus Bus Configuration 1.0//EN" + "http://www.freedesktop.org/standards/dbus/1.0/busconfig.dtd"> +<busconfig> + <!-- Our well-known bus type, don't change this --> + <type>session</type> + <listen>unix:tmpdir=/tmp</listen> + + <policy context="default"> + <!-- Allow everything to be sent --> + <allow send_destination="*" eavesdrop="true"/> + <!-- Allow everything to be received --> + <allow eavesdrop="true"/> + <!-- Allow anyone to own anything --> + <allow own="*"/> + </policy> + + <limit name="max_incoming_bytes">100000</limit> + <limit name="max_outgoing_bytes">100000</limit> +</busconfig> diff --git a/test/data/valid-config-files/outgoing-limit.conf b/test/data/valid-config-files/outgoing-limit.conf new file mode 100644 index 00000000..1456ff1c --- /dev/null +++ b/test/data/valid-config-files/outgoing-limit.conf @@ -0,0 +1,18 @@ +<!DOCTYPE busconfig PUBLIC "-//freedesktop//DTD D-Bus Bus Configuration 1.0//EN" + "http://www.freedesktop.org/standards/dbus/1.0/busconfig.dtd"> +<busconfig> + <!-- Our well-known bus type, don't change this --> + <type>session</type> + <listen>unix:tmpdir=/tmp</listen> + + <policy context="default"> + <!-- Allow everything to be sent --> + <allow send_destination="*" eavesdrop="true"/> + <!-- Allow everything to be received --> + <allow eavesdrop="true"/> + <!-- Allow anyone to own anything --> + <allow own="*"/> + </policy> + + <limit name="max_outgoing_bytes">100000</limit> +</busconfig> diff --git a/test/data/valid-config-files/realistic-incoming-limit.conf b/test/data/valid-config-files/realistic-incoming-limit.conf new file mode 100644 index 00000000..12aad595 --- /dev/null +++ b/test/data/valid-config-files/realistic-incoming-limit.conf @@ -0,0 +1,18 @@ +<!DOCTYPE busconfig PUBLIC "-//freedesktop//DTD D-Bus Bus Configuration 1.0//EN" + "http://www.freedesktop.org/standards/dbus/1.0/busconfig.dtd"> +<busconfig> + <!-- Our well-known bus type, don't change this --> + <type>session</type> + <listen>unix:tmpdir=/tmp</listen> + + <policy context="default"> + <!-- Allow everything to be sent --> + <allow send_destination="*" eavesdrop="true"/> + <!-- Allow everything to be received --> + <allow eavesdrop="true"/> + <!-- Allow anyone to own anything --> + <allow own="*"/> + </policy> + + <limit name="max_incoming_bytes">100000</limit> +</busconfig> |
