* tools/dbus-send.c (main):
[platform/upstream/dbus.git] / test / glib / test-profile.c
1 /* -*- mode: C; c-file-style: "gnu" -*- */
2 /* test-profile.c Program that does basic message-response for timing; doesn't really use glib bindings
3  *
4  * Copyright (C) 2003, 2004  Red Hat Inc.
5  *
6  * Licensed under the Academic Free License version 2.1
7  * 
8  * This program is free software; you can redistribute it and/or modify
9  * it under the terms of the GNU General Public License as published by
10  * the Free Software Foundation; either version 2 of the License, or
11  * (at your option) any later version.
12  *
13  * This program is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  * GNU General Public License for more details.
17  * 
18  * You should have received a copy of the GNU General Public License
19  * along with this program; if not, write to the Free Software
20  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
21  *
22  */
23
24 #include <config.h>
25 #include <glib.h>
26 #include <dbus/dbus-glib-lowlevel.h>
27 #include <stdlib.h>
28 #include <unistd.h>
29
30 #include <errno.h>
31 #include <fcntl.h>
32 #include <sys/socket.h>
33 #include <sys/un.h>
34 #include <netinet/in.h>
35 #include <string.h>
36 #include <sys/time.h>
37 #include <sys/stat.h>
38 #ifndef HAVE_SOCKLEN_T
39 #define socklen_t int
40 #endif
41
42 #define _DBUS_ZERO(object) (memset (&(object), '\0', sizeof ((object))))
43 #define _DBUS_MAX_SUN_PATH_LENGTH 99
44
45 /* Note that if you set threads > 1 you get a bogus profile since the
46  * clients start blocking on the server, so the client write() will go
47  * higher in the profile the larger the number of threads.
48  */
49 #define N_CLIENT_THREADS 1
50 /* It seems like at least 750000 or so iterations reduces the variability to sane levels */
51 #define N_ITERATIONS 750000
52 #define N_PROGRESS_UPDATES 20
53 /* Don't make PAYLOAD_SIZE too huge because it gets used as a static buffer size */
54 #define PAYLOAD_SIZE 0
55
56 #define ECHO_SERVICE "org.freedesktop.EchoTestServer"
57 #define ECHO_PATH "/org/freedesktop/EchoTest"
58 #define ECHO_INTERFACE "org.freedesktop.EchoTest"
59 #define ECHO_PING_METHOD "Ping"
60
61 static const char *messages_address;
62 static const char *plain_sockets_address;
63 static unsigned char *payload;
64 static int echo_call_size;
65 static int echo_return_size;
66
67 typedef struct ProfileRunVTable ProfileRunVTable;
68
69 typedef struct
70 {
71   const ProfileRunVTable *vtable;
72   int iterations;
73   GMainLoop *loop;
74 } ClientData;
75
76 typedef struct
77 {
78   const ProfileRunVTable *vtable;
79   int handled;
80   GMainLoop *loop;
81   int n_clients;
82 } ServerData;
83
84 struct ProfileRunVTable
85 {
86   const char *name;
87   gboolean fake_malloc_overhead;
88   void* (* init_server)        (ServerData *sd);
89   void  (* stop_server)        (ServerData *sd,
90                                 void       *server);
91   void* (* client_thread_func) (void *data); /* Data has to be the vtable */
92
93   /* this is so different runs show up in the profiler with
94    * different backtrace
95    */
96   void  (* main_loop_run_func) (GMainLoop *loop);
97 };
98
99 /* Note, this is all crack-a-rific; it isn't using DBusGProxy and thus is
100  * a major pain
101  */
102 static void
103 send_echo_method_call (DBusConnection *connection)
104 {
105   DBusMessage *message;
106   const char *hello = "Hello World!";
107   dbus_int32_t i32 = 123456;
108
109   message = dbus_message_new_method_call (ECHO_SERVICE,
110                                           ECHO_PATH,
111                                           ECHO_INTERFACE,
112                                           ECHO_PING_METHOD);
113   dbus_message_append_args (message,
114                             DBUS_TYPE_STRING, &hello,
115                             DBUS_TYPE_INT32, &i32,
116 #if PAYLOAD_SIZE > 0
117                             DBUS_TYPE_ARRAY, DBUS_TYPE_BYTE,
118                             &payload, PAYLOAD_SIZE,
119 #endif
120                             DBUS_TYPE_INVALID);
121   
122   dbus_connection_send (connection, message, NULL);
123   dbus_message_unref (message);
124   dbus_connection_flush (connection);
125 }
126
127 static void
128 send_echo_method_return (DBusConnection *connection,
129                          DBusMessage    *call_message)
130 {
131   DBusMessage *message;
132
133   message = dbus_message_new_method_return (call_message);
134   
135   dbus_connection_send (connection, message, NULL);
136   dbus_message_unref (message);
137   dbus_connection_flush (connection);
138 }
139
140 static DBusHandlerResult
141 with_or_without_bus_client_filter (DBusConnection     *connection,
142                                    DBusMessage        *message,
143                                    ClientData         *cd)
144 {
145   if (dbus_message_is_signal (message,
146                               DBUS_INTERFACE_LOCAL,
147                               "Disconnected"))
148     {
149       g_printerr ("Client thread disconnected\n");
150       exit (1);
151     }
152   else if (dbus_message_get_type (message) == DBUS_MESSAGE_TYPE_METHOD_RETURN)
153     {
154       cd->iterations += 1;
155       if (cd->iterations >= N_ITERATIONS)
156         {
157           g_printerr ("\nCompleted %d iterations\n", N_ITERATIONS);
158           g_main_loop_quit (cd->loop);
159         }
160       else if (cd->iterations % (N_ITERATIONS/N_PROGRESS_UPDATES) == 0)
161         {
162           g_printerr ("%d%% ", (int) (cd->iterations/(double)N_ITERATIONS * 100.0));
163         }
164       
165       send_echo_method_call (connection);
166       return DBUS_HANDLER_RESULT_HANDLED;
167     }
168   
169   return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
170 }
171
172 static DBusHandlerResult
173 no_bus_client_filter (DBusConnection     *connection,
174                       DBusMessage        *message,
175                       void               *user_data)
176 {
177   ClientData *cd = user_data;
178
179   return with_or_without_bus_client_filter (connection, message, cd);
180 }
181
182 static void*
183 no_bus_thread_func (void *data)
184 {
185   DBusError error;
186   GMainContext *context;
187   DBusConnection *connection;
188   ClientData cd;
189   
190   g_printerr ("Starting client thread %p\n", g_thread_self());  
191   
192   dbus_error_init (&error);
193   connection = dbus_connection_open_private (messages_address, &error);
194   if (connection == NULL)
195     {
196       g_printerr ("could not open connection: %s\n", error.message);
197       dbus_error_free (&error);
198       exit (1);
199     }
200
201   context = g_main_context_new ();
202
203   cd.iterations = 1;
204   cd.loop = g_main_loop_new (context, FALSE);
205   
206   if (!dbus_connection_add_filter (connection,
207                                    no_bus_client_filter, &cd, NULL))
208     g_error ("no memory");
209   
210   
211   dbus_connection_setup_with_g_main (connection, context);
212
213   g_printerr ("Client thread sending message to prime pingpong\n");
214   send_echo_method_call (connection);
215   g_printerr ("Client thread sent message\n");
216
217   g_printerr ("Client thread entering main loop\n");
218   g_main_loop_run (cd.loop);
219   g_printerr ("Client thread %p exiting main loop\n",
220               g_thread_self());
221
222   dbus_connection_close (connection);
223   
224   g_main_loop_unref (cd.loop);
225   g_main_context_unref (context);
226   
227   return NULL;
228 }
229
230 static DBusHandlerResult
231 no_bus_server_filter (DBusConnection     *connection,
232                       DBusMessage        *message,
233                       void               *user_data)
234 {
235   ServerData *sd = user_data;
236   
237   if (dbus_message_is_signal (message,
238                               DBUS_INTERFACE_LOCAL,
239                               "Disconnected"))
240     {
241       g_printerr ("Client disconnected from server\n");
242       sd->n_clients -= 1;
243       if (sd->n_clients == 0)
244         g_main_loop_quit (sd->loop);
245     }
246   else if (dbus_message_is_method_call (message,
247                                         ECHO_INTERFACE,
248                                         ECHO_PING_METHOD))
249     {
250       sd->handled += 1;
251       send_echo_method_return (connection, message);
252       return DBUS_HANDLER_RESULT_HANDLED;
253     }
254   
255   return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
256 }
257
258 static void
259 no_bus_new_connection_callback (DBusServer     *server,
260                                 DBusConnection *new_connection,
261                                 void           *user_data)
262 {
263   ServerData *sd = user_data;
264   
265   dbus_connection_ref (new_connection);
266   dbus_connection_setup_with_g_main (new_connection, NULL);  
267   
268   if (!dbus_connection_add_filter (new_connection,
269                                    no_bus_server_filter, sd, NULL))
270     g_error ("no memory");
271
272   sd->n_clients += 1;
273
274   /* FIXME we leak the handler */  
275 }
276
277 static void*
278 no_bus_init_server (ServerData       *sd)
279 {
280   DBusServer *server;
281   DBusError error;
282
283   dbus_error_init (&error);
284   server = dbus_server_listen ("unix:tmpdir="DBUS_TEST_SOCKET_DIR,
285                                &error);
286   if (server == NULL)
287     {
288       g_printerr ("Could not start server: %s\n",
289                   error.message);
290       exit (1);
291     }
292
293   messages_address = dbus_server_get_address (server);
294   
295   dbus_server_set_new_connection_function (server,
296                                            no_bus_new_connection_callback,
297                                            sd, NULL);
298
299   dbus_server_setup_with_g_main (server, NULL);
300   
301   return server;
302 }
303
304 static void
305 no_bus_stop_server (ServerData *sd,
306                     void       *server)
307 {
308   dbus_server_disconnect (server);
309   dbus_server_unref (server);
310 }
311
312 static void
313 no_bus_main_loop_run (GMainLoop *loop)
314 {
315   g_main_loop_run (loop);
316 }
317
318 static const ProfileRunVTable no_bus_vtable = {
319   "dbus direct without bus",
320   FALSE,
321   no_bus_init_server,
322   no_bus_stop_server,
323   no_bus_thread_func,
324   no_bus_main_loop_run
325 };
326
327 typedef struct
328 {
329   const ProfileRunVTable *vtable;
330   ServerData *sd;
331   GHashTable *client_names;
332   DBusConnection *connection;
333 } WithBusServer;
334
335 static DBusHandlerResult
336 with_bus_client_filter (DBusConnection     *connection,
337                         DBusMessage        *message,
338                         void               *user_data)
339 {
340   ClientData *cd = user_data;
341
342   return with_or_without_bus_client_filter (connection, message, cd);
343 }
344
345 static void*
346 with_bus_thread_func (void *data)
347 {
348   DBusError error;
349   DBusConnection *connection;
350   ClientData cd;
351   const char *address;
352   GMainContext *context;
353   
354   g_printerr ("Starting client thread %p\n", g_thread_self());  
355
356   address = g_getenv ("DBUS_SESSION_BUS_ADDRESS");
357   if (address == NULL)
358     {
359       g_printerr ("DBUS_SESSION_BUS_ADDRESS not set\n");
360       exit (1);
361     }
362   
363   dbus_error_init (&error);
364   connection = dbus_connection_open_private (address, &error);
365   if (connection == NULL)
366     {
367       g_printerr ("could not open connection to bus: %s\n", error.message);
368       dbus_error_free (&error);
369       exit (1);
370     }
371
372   if (!dbus_bus_register (connection, &error))
373     {
374       g_printerr ("could not register with bus: %s\n", error.message);
375       dbus_error_free (&error);
376       exit (1);
377     }
378   
379   context = g_main_context_new ();
380
381   cd.iterations = 1;
382   cd.loop = g_main_loop_new (context, FALSE);
383   
384   if (!dbus_connection_add_filter (connection,
385                                    with_bus_client_filter, &cd, NULL))
386     g_error ("no memory");
387   
388   dbus_connection_setup_with_g_main (connection, context);
389
390   g_printerr ("Client thread sending message to prime pingpong\n");
391   send_echo_method_call (connection);
392   g_printerr ("Client thread sent message\n");
393
394   g_printerr ("Client thread entering main loop\n");
395   g_main_loop_run (cd.loop);
396   g_printerr ("Client thread %p exiting main loop\n",
397               g_thread_self());
398
399   dbus_connection_close (connection);
400   
401   g_main_loop_unref (cd.loop);
402   g_main_context_unref (context);
403   
404   return NULL;
405 }
406
407 static DBusHandlerResult
408 with_bus_server_filter (DBusConnection     *connection,
409                         DBusMessage        *message,
410                         void               *user_data)
411 {
412   WithBusServer *server = user_data;
413   
414   if (dbus_message_is_signal (message,
415                               DBUS_INTERFACE_LOCAL,
416                               "Disconnected"))
417     {
418       g_printerr ("Server disconnected from message bus\n");
419       exit (1);
420     }
421   else if (dbus_message_has_sender (message,
422                                     DBUS_SERVICE_DBUS) &&
423            dbus_message_is_signal (message,
424                                    DBUS_INTERFACE_DBUS,
425                                    "NameOwnerChanged"))
426     {
427       const char *name, *old_owner, *new_owner;
428       DBusError error;
429
430       name = NULL;
431       old_owner = NULL;
432       new_owner = NULL;
433
434       dbus_error_init (&error);
435       if (!dbus_message_get_args (message,
436                                   &error,
437                                   DBUS_TYPE_STRING, &name,
438                                   DBUS_TYPE_STRING, &old_owner,
439                                   DBUS_TYPE_STRING, &new_owner,
440                                   DBUS_TYPE_INVALID))
441         {
442           g_printerr ("dbus_message_get_args(): %s\n", error.message);
443           exit (1);
444         }
445
446       if (g_hash_table_lookup (server->client_names,
447                                name) &&
448           *old_owner != '\0' &&
449           *new_owner == '\0')
450         {
451           g_hash_table_remove (server->client_names,
452                                name);
453           server->sd->n_clients -= 1;
454           if (server->sd->n_clients == 0)
455             g_main_loop_quit (server->sd->loop);
456         }
457     }
458   else if (dbus_message_is_method_call (message,
459                                         ECHO_INTERFACE,
460                                         ECHO_PING_METHOD))
461     {
462       const char *sender;
463
464       sender = dbus_message_get_sender (message);
465
466       if (!g_hash_table_lookup (server->client_names,
467                                 sender))
468         {
469           g_printerr ("First message from new client %s on bus\n", sender);
470           
471           g_hash_table_replace (server->client_names,
472                                 g_strdup (sender),
473                                 GINT_TO_POINTER (1));
474           server->sd->n_clients += 1;
475         }
476       
477       server->sd->handled += 1;
478       send_echo_method_return (connection, message);
479       return DBUS_HANDLER_RESULT_HANDLED;
480     }
481   
482   return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
483 }
484
485 static void*
486 with_bus_init_server (ServerData       *sd)
487 {
488   DBusGConnection *gconnection;
489   DBusConnection *connection;
490   GError *gerror;
491   const char *s;
492   WithBusServer *server;
493   char *rule;
494
495   server = g_new0 (WithBusServer, 1);
496
497   server->vtable = sd->vtable;
498   server->sd = sd;
499   
500   s = g_getenv ("DBUS_TEST_GLIB_RUN_TEST_SCRIPT");
501   if (s == NULL ||
502       *s != '1')
503     {
504       g_printerr ("You have to run with_bus mode with the run-test.sh script\n");
505       exit (1);
506     }
507
508   /* Note that we use the standard global bus connection for the
509    * server, and the clients open their own connections so they can
510    * have their own main loops and because I'm not sure "talking to
511    * yourself" really works yet
512    */
513   gerror = NULL;
514   gconnection = dbus_g_bus_get (DBUS_BUS_SESSION, &gerror);
515   if (gconnection == NULL)
516     {
517       g_printerr ("could not open connection to bus: %s\n", gerror->message);
518       g_error_free (gerror);
519       exit (1);
520     }
521
522   server->client_names = g_hash_table_new_full (g_str_hash, g_str_equal,
523                                                 g_free, NULL);
524   
525   connection = dbus_g_connection_get_connection (gconnection);
526
527   dbus_bus_request_name (connection,
528                          ECHO_SERVICE,
529                          0, NULL); /* ignore errors because we suck */
530   
531   rule = g_strdup_printf ("type='signal',sender='%s',member='%s'",
532                           DBUS_SERVICE_DBUS,
533                           "NameOwnerChanged");
534
535   /* ignore errors because we suck */
536   dbus_bus_add_match (connection, rule, NULL);
537
538   g_free (rule);
539   
540   if (!dbus_connection_add_filter (connection,
541                                    with_bus_server_filter, server, NULL))
542     g_error ("no memory");
543
544   server->connection = connection;
545   server->client_names = g_hash_table_new_full (g_str_hash, g_str_equal,
546                                                 g_free, NULL);
547   
548   return server;
549 }
550
551 static void
552 with_bus_stop_server (ServerData *sd,
553                       void       *serverv)
554 {
555   WithBusServer *server = serverv;
556   
557   dbus_connection_remove_filter (server->connection,
558                                  with_bus_server_filter, server);
559
560   g_hash_table_destroy (server->client_names);
561   dbus_connection_unref (server->connection);
562   
563   g_free (server);
564 }
565
566 static void
567 with_bus_main_loop_run (GMainLoop *loop)
568 {
569   g_main_loop_run (loop);
570 }
571
572 static const ProfileRunVTable with_bus_vtable = {
573   "routing via a bus",
574   FALSE,
575   with_bus_init_server,
576   with_bus_stop_server,
577   with_bus_thread_func,
578   with_bus_main_loop_run
579 };
580
581
582 typedef struct
583 {
584   const ProfileRunVTable *vtable;
585   int listen_fd;
586   ServerData *sd;
587   unsigned int source_id;
588 } PlainSocketServer;
589
590 static void
591 read_and_drop_on_floor (int fd,
592                         int count,
593                         gboolean fake_malloc_overhead)
594 {
595   int bytes_read;
596   int val;
597   char *buf;
598   char *allocated;
599   char not_allocated[512+PAYLOAD_SIZE];
600
601   g_assert (count < (int) sizeof(not_allocated));
602   
603   if (fake_malloc_overhead)
604     {
605       allocated = g_malloc (count);
606       buf = allocated;
607     }
608   else
609     {
610       allocated = NULL;
611       buf = not_allocated;
612     }
613   
614   bytes_read = 0;
615
616   while (bytes_read < count)
617     {
618     again:
619       
620       val = read (fd, buf + bytes_read, count - bytes_read);                  
621       
622       if (val < 0)
623         {
624           if (errno == EINTR)
625             goto again;
626           else
627             {
628               g_printerr ("read() failed thread %p: %s\n",
629                           g_thread_self(), strerror (errno));
630               exit (1);
631             }
632         }
633       else
634         {
635           bytes_read += val;
636         }
637     }
638
639   if (fake_malloc_overhead)
640     g_free (allocated);
641
642 #if 0
643   g_printerr ("%p read %d bytes from fd %d\n",
644            g_thread_self(), bytes_read, fd);
645 #endif
646 }
647
648 static void
649 write_junk (int fd,
650             int count,
651             gboolean fake_malloc_overhead)
652 {
653   int bytes_written;
654   int val;
655   char *buf;
656   char *allocated;
657   char not_allocated[512+PAYLOAD_SIZE];
658
659   g_assert (count < (int) sizeof(not_allocated));
660   
661   if (fake_malloc_overhead)
662     {
663       int i;
664       
665       allocated = g_malloc (count);
666       buf = allocated;
667
668       /* Write some stuff into the allocated buffer to simulate
669        * creating some sort of data
670        */
671       i = 0;
672       while (i < count)
673         {
674           allocated[i] = (char) i;
675           ++i;
676         }
677     }
678   else
679     {
680       allocated = NULL;
681       buf = not_allocated;
682     }
683   
684   bytes_written = 0;
685   
686   while (bytes_written < count)
687     {
688     again:
689       
690       val = write (fd, buf + bytes_written, count - bytes_written);
691       
692       if (val < 0)
693         {
694           if (errno == EINTR)
695             goto again;
696           else
697             {
698               g_printerr ("write() failed thread %p: %s\n",
699                           g_thread_self(), strerror (errno));
700               exit (1);
701             }
702         }
703       else
704         {
705           bytes_written += val;
706         }
707     }
708
709   if (fake_malloc_overhead)
710     g_free (allocated);
711   
712 #if 0
713   g_printerr ("%p wrote %d bytes to fd %d\n",
714            g_thread_self(), bytes_written, fd);
715 #endif
716 }
717
718 static gboolean
719 plain_sockets_talk_to_client_watch (GIOChannel   *source,
720                                     GIOCondition  condition,
721                                     gpointer      data)
722 {
723   PlainSocketServer *server = data;
724   int client_fd = g_io_channel_unix_get_fd (source);
725   
726   if (condition & G_IO_HUP)
727     {
728       g_printerr ("Client disconnected from server\n");
729       server->sd->n_clients -= 1;
730       if (server->sd->n_clients == 0)
731         g_main_loop_quit (server->sd->loop);
732
733       return FALSE; /* remove watch */
734     }
735   else if (condition & G_IO_IN)
736     {
737       server->sd->handled += 1;
738
739       read_and_drop_on_floor (client_fd, echo_call_size, server->vtable->fake_malloc_overhead);
740       write_junk (client_fd, echo_return_size, server->vtable->fake_malloc_overhead);
741     }
742   else
743     {
744       g_printerr ("Unexpected IO condition in server thread\n");
745       exit (1);
746     }
747
748   return TRUE;
749 }
750
751 static gboolean
752 plain_sockets_new_client_watch (GIOChannel   *source,
753                                 GIOCondition  condition,
754                                 gpointer      data)
755 {
756   int client_fd;
757   struct sockaddr addr;
758   socklen_t addrlen;
759   GIOChannel *channel;
760   PlainSocketServer *server = data;
761
762   if (!(condition & G_IO_IN))
763     {
764       g_printerr ("Unexpected IO condition on server socket\n");
765       exit (1);
766     }
767   
768   addrlen = sizeof (addr);
769   
770  retry:
771   client_fd = accept (server->listen_fd, &addr, &addrlen);
772   
773   if (client_fd < 0)
774     {
775       if (errno == EINTR)
776         goto retry;
777       else
778         {
779           g_printerr ("Failed to accept() connection from client: %s\n",
780                       strerror (errno));
781           exit (1);
782         }
783     }
784   
785   channel = g_io_channel_unix_new (client_fd);
786   g_io_add_watch (channel,
787                   G_IO_IN | G_IO_ERR | G_IO_HUP | G_IO_NVAL | G_IO_PRI,
788                   plain_sockets_talk_to_client_watch,
789                   server);
790   g_io_channel_unref (channel);
791
792   server->sd->n_clients += 1;
793   
794   return TRUE;
795 }
796
797 static void*
798 plain_sockets_init_server (ServerData *sd)
799 {
800   PlainSocketServer *server;
801   struct sockaddr_un addr;
802   static char path[] = "/tmp/dbus-test-profile-XXXXXX";
803   char *p;
804   GIOChannel *channel;
805
806   server = g_new0 (PlainSocketServer, 1);
807   server->sd = sd;
808   server->vtable = sd->vtable; /* for convenience */
809   
810   p = path;
811   while (*p)
812     {
813       if (*p == 'X')
814         *p = 'a' + (int) (26.0*rand()/(RAND_MAX+1.0));
815       ++p;
816     }
817
818   g_printerr ("Socket is %s\n", path);
819   
820   server->listen_fd = socket (PF_UNIX, SOCK_STREAM, 0);
821   
822   if (server->listen_fd < 0)
823     {
824       g_printerr ("Failed to create socket: %s",
825                   strerror (errno));
826       exit (1);
827     }
828
829   _DBUS_ZERO (addr);
830   addr.sun_family = AF_UNIX;
831   
832 #ifdef HAVE_ABSTRACT_SOCKETS
833   /* remember that abstract names aren't nul-terminated so we rely
834    * on sun_path being filled in with zeroes above.
835    */
836   addr.sun_path[0] = '\0'; /* this is what says "use abstract" */
837   strncpy (&addr.sun_path[1], path, _DBUS_MAX_SUN_PATH_LENGTH - 2);
838   /* _dbus_verbose_bytes (addr.sun_path, sizeof (addr.sun_path)); */
839 #else /* HAVE_ABSTRACT_SOCKETS */
840   {
841     struct stat sb;
842     
843     if (stat (path, &sb) == 0 &&
844         S_ISSOCK (sb.st_mode))
845       unlink (path);
846   }
847
848   strncpy (addr.sun_path, path, _DBUS_MAX_SUN_PATH_LENGTH - 1);
849 #endif /* ! HAVE_ABSTRACT_SOCKETS */
850   
851   if (bind (server->listen_fd, (struct sockaddr*) &addr, sizeof (addr)) < 0)
852     {
853       g_printerr ("Failed to bind socket \"%s\": %s",
854                   path, strerror (errno));
855       exit (1);
856     }
857
858   if (listen (server->listen_fd, 30 /* backlog */) < 0)
859     {
860       g_printerr ("Failed to listen on socket \"%s\": %s",
861                   path, strerror (errno));
862       exit (1);
863     }
864
865   plain_sockets_address = path;
866
867   channel = g_io_channel_unix_new (server->listen_fd);
868   server->source_id =
869     g_io_add_watch (channel,
870                     G_IO_IN | G_IO_ERR | G_IO_HUP | G_IO_NVAL | G_IO_PRI,
871                     plain_sockets_new_client_watch,
872                     server);
873   g_io_channel_unref (channel);
874   
875   return server;
876 }
877
878 static void
879 plain_sockets_stop_server (ServerData *sd,
880                            void       *server_v)
881 {
882   PlainSocketServer *server = server_v;
883
884   g_source_remove (server->source_id);
885   
886   close (server->listen_fd);
887   g_free (server);
888   
889   {
890     struct stat sb;
891     
892     if (stat (plain_sockets_address, &sb) == 0 &&
893         S_ISSOCK (sb.st_mode))
894       unlink (plain_sockets_address);
895   }
896 }
897
898 static gboolean
899 plain_sockets_client_side_watch (GIOChannel   *source,
900                                  GIOCondition  condition,
901                                  gpointer      data)
902 {
903   ClientData *cd = data;
904   int fd = g_io_channel_unix_get_fd (source);
905
906   if (condition & G_IO_IN)
907     {
908       read_and_drop_on_floor (fd, echo_return_size, cd->vtable->fake_malloc_overhead);
909     }
910   else if (condition & G_IO_OUT)
911     {
912       cd->iterations += 1;
913       if (cd->iterations >= N_ITERATIONS)
914         {
915           g_printerr ("\nCompleted %d iterations\n", N_ITERATIONS);
916           g_main_loop_quit (cd->loop);
917         }
918       else if (cd->iterations % (N_ITERATIONS/N_PROGRESS_UPDATES) == 0)
919         {
920           g_printerr ("%d%% ", (int) (cd->iterations/(double)N_ITERATIONS * 100.0));
921         }
922       
923       write_junk (fd, echo_call_size, cd->vtable->fake_malloc_overhead);
924     }
925   else
926     {
927       g_printerr ("Unexpected IO condition in client thread\n");
928       exit (1);
929     }
930
931   return TRUE;
932 }
933
934 static void*
935 plain_sockets_thread_func (void *data)
936 {
937   GMainContext *context;
938   ClientData cd;
939   int fd;
940   struct sockaddr_un addr;
941   GIOChannel *channel;
942   GSource *gsource;
943   
944   g_printerr ("Starting client thread %p\n",
945               g_thread_self());
946   
947   fd = socket (PF_UNIX, SOCK_STREAM, 0);
948   
949   if (fd < 0)
950     {
951       g_printerr ("Failed to create socket: %s",
952                   strerror (errno)); 
953       exit (1);
954     }
955
956   _DBUS_ZERO (addr);
957   addr.sun_family = AF_UNIX;
958
959 #ifdef HAVE_ABSTRACT_SOCKETS
960   /* remember that abstract names aren't nul-terminated so we rely
961    * on sun_path being filled in with zeroes above.
962    */
963   addr.sun_path[0] = '\0'; /* this is what says "use abstract" */
964   strncpy (&addr.sun_path[1], plain_sockets_address, _DBUS_MAX_SUN_PATH_LENGTH - 2);
965   /* _dbus_verbose_bytes (addr.sun_path, sizeof (addr.sun_path)); */
966 #else /* HAVE_ABSTRACT_SOCKETS */
967   strncpy (addr.sun_path, plain_sockets_address, _DBUS_MAX_SUN_PATH_LENGTH - 1);
968 #endif /* ! HAVE_ABSTRACT_SOCKETS */
969   
970   if (connect (fd, (struct sockaddr*) &addr, sizeof (addr)) < 0)
971     {      
972       g_printerr ("Failed to connect to socket %s: %s",
973                   plain_sockets_address, strerror (errno));
974       exit (1);
975     }
976
977   context = g_main_context_new ();
978
979   cd.iterations = 1;
980   cd.loop = g_main_loop_new (context, FALSE);
981
982   channel = g_io_channel_unix_new (fd);
983   
984   gsource = g_io_create_watch (channel,
985                                G_IO_IN | G_IO_OUT |
986                                G_IO_ERR | G_IO_HUP | G_IO_NVAL | G_IO_PRI);
987
988   g_source_set_callback (gsource,
989                          (GSourceFunc)plain_sockets_client_side_watch,
990                          &cd, NULL);
991
992   g_source_attach (gsource, context);
993
994   g_io_channel_unref (channel);
995
996   g_printerr ("Client thread writing to prime pingpong\n");
997   write_junk (fd, echo_call_size, cd.vtable->fake_malloc_overhead);
998   g_printerr ("Client thread done writing primer\n");
999
1000   g_printerr ("Client thread entering main loop\n");
1001   g_main_loop_run (cd.loop);
1002   g_printerr ("Client thread %p exiting main loop\n",
1003               g_thread_self());
1004
1005   g_source_destroy (gsource);
1006   
1007   close (fd);
1008   
1009   g_main_loop_unref (cd.loop);
1010   g_main_context_unref (context);
1011
1012   return NULL;
1013 }
1014
1015 static void
1016 plain_sockets_main_loop_run (GMainLoop *loop)
1017 {
1018   g_main_loop_run (loop);
1019 }
1020
1021 static const ProfileRunVTable plain_sockets_vtable = {
1022   "plain sockets",
1023   FALSE,
1024   plain_sockets_init_server,
1025   plain_sockets_stop_server,
1026   plain_sockets_thread_func,
1027   plain_sockets_main_loop_run
1028 };
1029
1030 static const ProfileRunVTable plain_sockets_with_malloc_vtable = {
1031   "plain sockets with malloc overhead",
1032   TRUE,
1033   plain_sockets_init_server,
1034   plain_sockets_stop_server,
1035   plain_sockets_thread_func,
1036   plain_sockets_main_loop_run
1037 };
1038
1039 static double
1040 do_profile_run (const ProfileRunVTable *vtable)
1041 {
1042   GTimer *timer;
1043   int i;
1044   double secs;
1045   ServerData sd;
1046   void *server;
1047
1048   g_printerr ("Profiling %s\n", vtable->name);
1049   
1050   sd.handled = 0;
1051   sd.n_clients = 0;
1052   sd.loop = g_main_loop_new (NULL, FALSE);
1053   sd.vtable = vtable;
1054
1055   server = (* vtable->init_server) (&sd);
1056   
1057   for (i = 0; i < N_CLIENT_THREADS; i++)
1058     {
1059       g_thread_create (vtable->client_thread_func, (void*) vtable, FALSE, NULL);
1060     }
1061
1062   timer = g_timer_new ();
1063   
1064   g_printerr ("Server thread %p entering main loop\n",
1065               g_thread_self());
1066   (* vtable->main_loop_run_func) (sd.loop);
1067   g_printerr ("Server thread %p exiting main loop\n",
1068               g_thread_self());
1069
1070   secs = g_timer_elapsed (timer, NULL);
1071   g_timer_destroy (timer);
1072
1073   g_printerr ("%s: %g seconds, %d round trips, %f seconds per pingpong\n",
1074               vtable->name, secs, sd.handled, secs/sd.handled);
1075
1076   (* vtable->stop_server) (&sd, server);
1077   
1078   g_main_loop_unref (sd.loop);
1079
1080   return secs;
1081 }
1082
1083 static void
1084 print_result (const ProfileRunVTable *vtable,
1085               double            seconds,
1086               double            baseline)
1087 {
1088   g_printerr (" %g times slower for %s (%g seconds, %f per iteration)\n",
1089               seconds/baseline, vtable->name,
1090               seconds, seconds / N_ITERATIONS);
1091 }
1092
1093 int
1094 main (int argc, char *argv[])
1095 {
1096   g_thread_init (NULL);
1097   dbus_g_thread_init ();
1098   
1099 #ifndef DBUS_DISABLE_ASSERT
1100   g_printerr ("You should probably --disable-asserts before you profile as they have noticeable overhead\n");
1101 #endif
1102   
1103 #if DBUS_ENABLE_VERBOSE_MODE
1104   g_printerr ("You should probably --disable-verbose-mode before you profile as verbose has noticeable overhead\n");
1105 #endif
1106   
1107   payload = g_malloc (PAYLOAD_SIZE);
1108
1109   /* The actual size of the DBusMessage on the wire, as of Nov 23 2004,
1110    * without the payload
1111    */
1112   echo_call_size = 140 + PAYLOAD_SIZE;
1113   echo_return_size = 32;
1114
1115   if (argc > 1 && strcmp (argv[1], "plain_sockets") == 0)
1116     do_profile_run (&plain_sockets_vtable);
1117   else if (argc > 1 && strcmp (argv[1], "plain_sockets_with_malloc") == 0)
1118     do_profile_run (&plain_sockets_with_malloc_vtable);
1119   else if (argc > 1 && strcmp (argv[1], "no_bus") == 0)
1120     do_profile_run (&no_bus_vtable);
1121   else if (argc > 1 && strcmp (argv[1], "with_bus") == 0)
1122     do_profile_run (&with_bus_vtable);
1123   else if (argc > 1 && strcmp (argv[1], "all") == 0)
1124     {
1125       double e1, e2, e3, e4;
1126
1127       e1 = do_profile_run (&plain_sockets_vtable);
1128       e2 = do_profile_run (&plain_sockets_with_malloc_vtable);
1129       e3 = do_profile_run (&no_bus_vtable);
1130       e4 = do_profile_run (&with_bus_vtable);
1131
1132       g_printerr ("Baseline plain sockets time %g seconds for %d iterations\n",
1133                   e1, N_ITERATIONS);
1134       print_result (&plain_sockets_vtable, e1, e1);
1135       print_result (&plain_sockets_with_malloc_vtable, e2, e1);
1136       print_result (&no_bus_vtable, e3, e1);
1137       print_result (&with_bus_vtable, e4, e1);
1138     }
1139   else
1140     {
1141       g_printerr ("Specify profile type plain_sockets, plain_sockets_with_malloc, no_bus, with_bus, all\n");
1142       exit (1);
1143     }
1144
1145   /* Make valgrind happy */
1146   dbus_shutdown ();
1147   
1148   return 0;
1149 }