77ce4369e1d968600df93951d62fe003d55ce1e6
[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_ORG_FREEDESKTOP_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 (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_disconnect (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_ORG_FREEDESKTOP_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   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");
309   
310   dbus_server_unref (server);
311 }
312
313 static void
314 no_bus_main_loop_run (GMainLoop *loop)
315 {
316   g_main_loop_run (loop);
317 }
318
319 static const ProfileRunVTable no_bus_vtable = {
320   "dbus direct without bus",
321   FALSE,
322   no_bus_init_server,
323   no_bus_stop_server,
324   no_bus_thread_func,
325   no_bus_main_loop_run
326 };
327
328 typedef struct
329 {
330   const ProfileRunVTable *vtable;
331   ServerData *sd;
332   GHashTable *client_names;
333   DBusConnection *connection;
334 } WithBusServer;
335
336 static DBusHandlerResult
337 with_bus_client_filter (DBusConnection     *connection,
338                         DBusMessage        *message,
339                         void               *user_data)
340 {
341   ClientData *cd = user_data;
342
343   return with_or_without_bus_client_filter (connection, message, cd);
344 }
345
346 static void*
347 with_bus_thread_func (void *data)
348 {
349   DBusError error;
350   DBusConnection *connection;
351   ClientData cd;
352   const char *address;
353   GMainContext *context;
354   
355   g_printerr ("Starting client thread %p\n", g_thread_self());  
356
357   address = g_getenv ("DBUS_SESSION_BUS_ADDRESS");
358   if (address == NULL)
359     {
360       g_printerr ("DBUS_SESSION_BUS_ADDRESS not set\n");
361       exit (1);
362     }
363   
364   dbus_error_init (&error);
365   connection = dbus_connection_open (address, &error);
366   if (connection == NULL)
367     {
368       g_printerr ("could not open connection to bus: %s\n", error.message);
369       dbus_error_free (&error);
370       exit (1);
371     }
372
373   if (!dbus_bus_register (connection, &error))
374     {
375       g_printerr ("could not register with bus: %s\n", error.message);
376       dbus_error_free (&error);
377       exit (1);
378     }
379   
380   context = g_main_context_new ();
381
382   cd.iterations = 1;
383   cd.loop = g_main_loop_new (context, FALSE);
384   
385   if (!dbus_connection_add_filter (connection,
386                                    with_bus_client_filter, &cd, NULL))
387     g_error ("no memory");
388   
389   dbus_connection_setup_with_g_main (connection, context);
390
391   g_printerr ("Client thread sending message to prime pingpong\n");
392   send_echo_method_call (connection);
393   g_printerr ("Client thread sent message\n");
394
395   g_printerr ("Client thread entering main loop\n");
396   g_main_loop_run (cd.loop);
397   g_printerr ("Client thread %p exiting main loop\n",
398               g_thread_self());
399
400   dbus_connection_disconnect (connection);
401   
402   g_main_loop_unref (cd.loop);
403   g_main_context_unref (context);
404   
405   return NULL;
406 }
407
408 static DBusHandlerResult
409 with_bus_server_filter (DBusConnection     *connection,
410                         DBusMessage        *message,
411                         void               *user_data)
412 {
413   WithBusServer *server = user_data;
414   
415   if (dbus_message_is_signal (message,
416                               DBUS_INTERFACE_ORG_FREEDESKTOP_LOCAL,
417                               "Disconnected"))
418     {
419       g_printerr ("Server disconnected from message bus\n");
420       exit (1);
421     }
422   else if (dbus_message_has_sender (message,
423                                     DBUS_SERVICE_ORG_FREEDESKTOP_DBUS) &&
424            dbus_message_is_signal (message,
425                                    DBUS_INTERFACE_ORG_FREEDESKTOP_DBUS,
426                                    "ServiceOwnerChanged"))
427     {
428       const char *service_name, *old_owner, *new_owner;
429       DBusError error;
430
431       service_name = NULL;
432       old_owner = NULL;
433       new_owner = NULL;
434
435       dbus_error_init (&error);
436       if (!dbus_message_get_args (message,
437                                   &error,
438                                   DBUS_TYPE_STRING, &service_name,
439                                   DBUS_TYPE_STRING, &old_owner,
440                                   DBUS_TYPE_STRING, &new_owner,
441                                   DBUS_TYPE_INVALID))
442         {
443           g_printerr ("dbus_message_get_args(): %s\n", error.message);
444           exit (1);
445         }
446
447       if (g_hash_table_lookup (server->client_names,
448                                service_name) &&
449           *old_owner != '\0' &&
450           *new_owner == '\0')
451         {
452           g_hash_table_remove (server->client_names,
453                                service_name);
454           server->sd->n_clients -= 1;
455           if (server->sd->n_clients == 0)
456             g_main_loop_quit (server->sd->loop);
457         }
458     }
459   else if (dbus_message_is_method_call (message,
460                                         ECHO_INTERFACE,
461                                         ECHO_PING_METHOD))
462     {
463       const char *sender;
464
465       sender = dbus_message_get_sender (message);
466
467       if (!g_hash_table_lookup (server->client_names,
468                                 sender))
469         {
470           g_printerr ("First message from new client %s on bus\n", sender);
471           
472           g_hash_table_replace (server->client_names,
473                                 g_strdup (sender),
474                                 GINT_TO_POINTER (1));
475           server->sd->n_clients += 1;
476         }
477       
478       server->sd->handled += 1;
479       send_echo_method_return (connection, message);
480       return DBUS_HANDLER_RESULT_HANDLED;
481     }
482   
483   return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
484 }
485
486 static void*
487 with_bus_init_server (ServerData       *sd)
488 {
489   DBusGConnection *gconnection;
490   DBusConnection *connection;
491   GError *gerror;
492   const char *s;
493   WithBusServer *server;
494   char *rule;
495
496   server = g_new0 (WithBusServer, 1);
497
498   server->vtable = sd->vtable;
499   server->sd = sd;
500   
501   s = g_getenv ("DBUS_TEST_GLIB_RUN_TEST_SCRIPT");
502   if (s == NULL ||
503       *s != '1')
504     {
505       g_printerr ("You have to run with_bus mode with the run-test.sh script\n");
506       exit (1);
507     }
508
509   /* Note that we use the standard global bus connection for the
510    * server, and the clients open their own connections so they can
511    * have their own main loops and because I'm not sure "talking to
512    * yourself" really works yet
513    */
514   gerror = NULL;
515   gconnection = dbus_g_bus_get (DBUS_BUS_SESSION, &gerror);
516   if (gconnection == NULL)
517     {
518       g_printerr ("could not open connection to bus: %s\n", gerror->message);
519       g_error_free (gerror);
520       exit (1);
521     }
522
523   server->client_names = g_hash_table_new_full (g_str_hash, g_str_equal,
524                                                 g_free, NULL);
525   
526   connection = dbus_g_connection_get_connection (gconnection);
527
528   dbus_bus_acquire_service (connection,
529                             ECHO_SERVICE,
530                             0, NULL); /* ignore errors because we suck */
531   
532   rule = g_strdup_printf ("type='signal',sender='%s',member='%s'",
533                           DBUS_SERVICE_ORG_FREEDESKTOP_DBUS,
534                           "ServiceOwnerChanged");
535
536   /* ignore errors because we suck */
537   dbus_bus_add_match (connection, rule, NULL);
538
539   g_free (rule);
540   
541   if (!dbus_connection_add_filter (connection,
542                                    with_bus_server_filter, server, NULL))
543     g_error ("no memory");
544
545   server->connection = connection;
546   server->client_names = g_hash_table_new_full (g_str_hash, g_str_equal,
547                                                 g_free, NULL);
548   
549   return server;
550 }
551
552 static void
553 with_bus_stop_server (ServerData *sd,
554                       void       *serverv)
555 {
556   WithBusServer *server = serverv;
557   
558   dbus_connection_remove_filter (server->connection,
559                                  with_bus_server_filter, server);
560
561   g_hash_table_destroy (server->client_names);
562   dbus_connection_unref (server->connection);
563   
564   g_free (server);
565 }
566
567 static void
568 with_bus_main_loop_run (GMainLoop *loop)
569 {
570   g_main_loop_run (loop);
571 }
572
573 static const ProfileRunVTable with_bus_vtable = {
574   "routing via a bus",
575   FALSE,
576   with_bus_init_server,
577   with_bus_stop_server,
578   with_bus_thread_func,
579   with_bus_main_loop_run
580 };
581
582
583 typedef struct
584 {
585   const ProfileRunVTable *vtable;
586   int listen_fd;
587   ServerData *sd;
588   unsigned int source_id;
589 } PlainSocketServer;
590
591 static void
592 read_and_drop_on_floor (int fd,
593                         int count,
594                         gboolean fake_malloc_overhead)
595 {
596   int bytes_read;
597   int val;
598   char *buf;
599   char *allocated;
600   char not_allocated[512+PAYLOAD_SIZE];
601
602   g_assert (count < (int) sizeof(not_allocated));
603   
604   if (fake_malloc_overhead)
605     {
606       allocated = g_malloc (count);
607       buf = allocated;
608     }
609   else
610     {
611       allocated = NULL;
612       buf = not_allocated;
613     }
614   
615   bytes_read = 0;
616
617   while (bytes_read < count)
618     {
619     again:
620       
621       val = read (fd, buf + bytes_read, count - bytes_read);                  
622       
623       if (val < 0)
624         {
625           if (errno == EINTR)
626             goto again;
627           else
628             {
629               g_printerr ("read() failed thread %p: %s\n",
630                           g_thread_self(), strerror (errno));
631               exit (1);
632             }
633         }
634       else
635         {
636           bytes_read += val;
637         }
638     }
639
640   if (fake_malloc_overhead)
641     g_free (allocated);
642
643 #if 0
644   g_printerr ("%p read %d bytes from fd %d\n",
645            g_thread_self(), bytes_read, fd);
646 #endif
647 }
648
649 static void
650 write_junk (int fd,
651             int count,
652             gboolean fake_malloc_overhead)
653 {
654   int bytes_written;
655   int val;
656   char *buf;
657   char *allocated;
658   char not_allocated[512+PAYLOAD_SIZE];
659
660   g_assert (count < (int) sizeof(not_allocated));
661   
662   if (fake_malloc_overhead)
663     {
664       int i;
665       
666       allocated = g_malloc (count);
667       buf = allocated;
668
669       /* Write some stuff into the allocated buffer to simulate
670        * creating some sort of data
671        */
672       i = 0;
673       while (i < count)
674         {
675           allocated[i] = (char) i;
676           ++i;
677         }
678     }
679   else
680     {
681       allocated = NULL;
682       buf = not_allocated;
683     }
684   
685   bytes_written = 0;
686   
687   while (bytes_written < count)
688     {
689     again:
690       
691       val = write (fd, buf + bytes_written, count - bytes_written);
692       
693       if (val < 0)
694         {
695           if (errno == EINTR)
696             goto again;
697           else
698             {
699               g_printerr ("write() failed thread %p: %s\n",
700                           g_thread_self(), strerror (errno));
701               exit (1);
702             }
703         }
704       else
705         {
706           bytes_written += val;
707         }
708     }
709
710   if (fake_malloc_overhead)
711     g_free (allocated);
712   
713 #if 0
714   g_printerr ("%p wrote %d bytes to fd %d\n",
715            g_thread_self(), bytes_written, fd);
716 #endif
717 }
718
719 static gboolean
720 plain_sockets_talk_to_client_watch (GIOChannel   *source,
721                                     GIOCondition  condition,
722                                     gpointer      data)
723 {
724   PlainSocketServer *server = data;
725   int client_fd = g_io_channel_unix_get_fd (source);
726   
727   if (condition & G_IO_HUP)
728     {
729       g_printerr ("Client disconnected from server\n");
730       server->sd->n_clients -= 1;
731       if (server->sd->n_clients == 0)
732         g_main_loop_quit (server->sd->loop);
733
734       return FALSE; /* remove watch */
735     }
736   else if (condition & G_IO_IN)
737     {
738       server->sd->handled += 1;
739
740       read_and_drop_on_floor (client_fd, echo_call_size, server->vtable->fake_malloc_overhead);
741       write_junk (client_fd, echo_return_size, server->vtable->fake_malloc_overhead);
742     }
743   else
744     {
745       g_printerr ("Unexpected IO condition in server thread\n");
746       exit (1);
747     }
748
749   return TRUE;
750 }
751
752 static gboolean
753 plain_sockets_new_client_watch (GIOChannel   *source,
754                                 GIOCondition  condition,
755                                 gpointer      data)
756 {
757   int client_fd;
758   struct sockaddr addr;
759   socklen_t addrlen;
760   GIOChannel *channel;
761   PlainSocketServer *server = data;
762
763   if (!(condition & G_IO_IN))
764     {
765       g_printerr ("Unexpected IO condition on server socket\n");
766       exit (1);
767     }
768   
769   addrlen = sizeof (addr);
770   
771  retry:
772   client_fd = accept (server->listen_fd, &addr, &addrlen);
773   
774   if (client_fd < 0)
775     {
776       if (errno == EINTR)
777         goto retry;
778       else
779         {
780           g_printerr ("Failed to accept() connection from client: %s\n",
781                       strerror (errno));
782           exit (1);
783         }
784     }
785   
786   channel = g_io_channel_unix_new (client_fd);
787   g_io_add_watch (channel,
788                   G_IO_IN | G_IO_ERR | G_IO_HUP | G_IO_NVAL | G_IO_PRI,
789                   plain_sockets_talk_to_client_watch,
790                   server);
791   g_io_channel_unref (channel);
792
793   server->sd->n_clients += 1;
794   
795   return TRUE;
796 }
797
798 static void*
799 plain_sockets_init_server (ServerData *sd)
800 {
801   PlainSocketServer *server;
802   struct sockaddr_un addr;
803   static char path[] = "/tmp/dbus-test-profile-XXXXXX";
804   char *p;
805   GIOChannel *channel;
806
807   server = g_new0 (PlainSocketServer, 1);
808   server->sd = sd;
809   server->vtable = sd->vtable; /* for convenience */
810   
811   p = path;
812   while (*p)
813     {
814       if (*p == 'X')
815         *p = 'a' + (int) (26.0*rand()/(RAND_MAX+1.0));
816       ++p;
817     }
818
819   g_printerr ("Socket is %s\n", path);
820   
821   server->listen_fd = socket (PF_UNIX, SOCK_STREAM, 0);
822   
823   if (server->listen_fd < 0)
824     {
825       g_printerr ("Failed to create socket: %s",
826                   strerror (errno));
827       exit (1);
828     }
829
830   _DBUS_ZERO (addr);
831   addr.sun_family = AF_UNIX;
832   
833 #ifdef HAVE_ABSTRACT_SOCKETS
834   /* remember that abstract names aren't nul-terminated so we rely
835    * on sun_path being filled in with zeroes above.
836    */
837   addr.sun_path[0] = '\0'; /* this is what says "use abstract" */
838   strncpy (&addr.sun_path[1], path, _DBUS_MAX_SUN_PATH_LENGTH - 2);
839   /* _dbus_verbose_bytes (addr.sun_path, sizeof (addr.sun_path)); */
840 #else /* HAVE_ABSTRACT_SOCKETS */
841   {
842     struct stat sb;
843     
844     if (stat (path, &sb) == 0 &&
845         S_ISSOCK (sb.st_mode))
846       unlink (path);
847   }
848
849   strncpy (addr.sun_path, path, _DBUS_MAX_SUN_PATH_LENGTH - 1);
850 #endif /* ! HAVE_ABSTRACT_SOCKETS */
851   
852   if (bind (server->listen_fd, (struct sockaddr*) &addr, sizeof (addr)) < 0)
853     {
854       g_printerr ("Failed to bind socket \"%s\": %s",
855                   path, strerror (errno));
856       exit (1);
857     }
858
859   if (listen (server->listen_fd, 30 /* backlog */) < 0)
860     {
861       g_printerr ("Failed to listen on socket \"%s\": %s",
862                   path, strerror (errno));
863       exit (1);
864     }
865
866   plain_sockets_address = path;
867
868   channel = g_io_channel_unix_new (server->listen_fd);
869   server->source_id =
870     g_io_add_watch (channel,
871                     G_IO_IN | G_IO_ERR | G_IO_HUP | G_IO_NVAL | G_IO_PRI,
872                     plain_sockets_new_client_watch,
873                     server);
874   g_io_channel_unref (channel);
875   
876   return server;
877 }
878
879 static void
880 plain_sockets_stop_server (ServerData *sd,
881                            void       *server_v)
882 {
883   PlainSocketServer *server = server_v;
884
885   g_source_remove (server->source_id);
886   
887   close (server->listen_fd);
888   g_free (server);
889   
890   {
891     struct stat sb;
892     
893     if (stat (plain_sockets_address, &sb) == 0 &&
894         S_ISSOCK (sb.st_mode))
895       unlink (plain_sockets_address);
896   }
897 }
898
899 static gboolean
900 plain_sockets_client_side_watch (GIOChannel   *source,
901                                  GIOCondition  condition,
902                                  gpointer      data)
903 {
904   ClientData *cd = data;
905   int fd = g_io_channel_unix_get_fd (source);
906
907   if (condition & G_IO_IN)
908     {
909       read_and_drop_on_floor (fd, echo_return_size, cd->vtable->fake_malloc_overhead);
910     }
911   else if (condition & G_IO_OUT)
912     {
913       cd->iterations += 1;
914       if (cd->iterations >= N_ITERATIONS)
915         {
916           g_printerr ("\nCompleted %d iterations\n", N_ITERATIONS);
917           g_main_loop_quit (cd->loop);
918         }
919       else if (cd->iterations % (N_ITERATIONS/N_PROGRESS_UPDATES) == 0)
920         {
921           g_printerr ("%d%% ", (int) (cd->iterations/(double)N_ITERATIONS * 100.0));
922         }
923       
924       write_junk (fd, echo_call_size, cd->vtable->fake_malloc_overhead);
925     }
926   else
927     {
928       g_printerr ("Unexpected IO condition in client thread\n");
929       exit (1);
930     }
931
932   return TRUE;
933 }
934
935 static void*
936 plain_sockets_thread_func (void *data)
937 {
938   GMainContext *context;
939   ClientData cd;
940   int fd;
941   struct sockaddr_un addr;
942   GIOChannel *channel;
943   GSource *gsource;
944   
945   g_printerr ("Starting client thread %p\n",
946               g_thread_self());
947   
948   fd = socket (PF_UNIX, SOCK_STREAM, 0);
949   
950   if (fd < 0)
951     {
952       g_printerr ("Failed to create socket: %s",
953                   strerror (errno)); 
954       exit (1);
955     }
956
957   _DBUS_ZERO (addr);
958   addr.sun_family = AF_UNIX;
959
960 #ifdef HAVE_ABSTRACT_SOCKETS
961   /* remember that abstract names aren't nul-terminated so we rely
962    * on sun_path being filled in with zeroes above.
963    */
964   addr.sun_path[0] = '\0'; /* this is what says "use abstract" */
965   strncpy (&addr.sun_path[1], plain_sockets_address, _DBUS_MAX_SUN_PATH_LENGTH - 2);
966   /* _dbus_verbose_bytes (addr.sun_path, sizeof (addr.sun_path)); */
967 #else /* HAVE_ABSTRACT_SOCKETS */
968   strncpy (addr.sun_path, plain_sockets_address, _DBUS_MAX_SUN_PATH_LENGTH - 1);
969 #endif /* ! HAVE_ABSTRACT_SOCKETS */
970   
971   if (connect (fd, (struct sockaddr*) &addr, sizeof (addr)) < 0)
972     {      
973       g_printerr ("Failed to connect to socket %s: %s",
974                   plain_sockets_address, strerror (errno));
975       exit (1);
976     }
977
978   context = g_main_context_new ();
979
980   cd.iterations = 1;
981   cd.loop = g_main_loop_new (context, FALSE);
982
983   channel = g_io_channel_unix_new (fd);
984   
985   gsource = g_io_create_watch (channel,
986                                G_IO_IN | G_IO_OUT |
987                                G_IO_ERR | G_IO_HUP | G_IO_NVAL | G_IO_PRI);
988
989   g_source_set_callback (gsource,
990                          (GSourceFunc)plain_sockets_client_side_watch,
991                          &cd, NULL);
992
993   g_source_attach (gsource, context);
994
995   g_io_channel_unref (channel);
996
997   g_printerr ("Client thread writing to prime pingpong\n");
998   write_junk (fd, echo_call_size, cd.vtable->fake_malloc_overhead);
999   g_printerr ("Client thread done writing primer\n");
1000
1001   g_printerr ("Client thread entering main loop\n");
1002   g_main_loop_run (cd.loop);
1003   g_printerr ("Client thread %p exiting main loop\n",
1004               g_thread_self());
1005
1006   g_source_destroy (gsource);
1007   
1008   close (fd);
1009   
1010   g_main_loop_unref (cd.loop);
1011   g_main_context_unref (context);
1012
1013   return NULL;
1014 }
1015
1016 static void
1017 plain_sockets_main_loop_run (GMainLoop *loop)
1018 {
1019   g_main_loop_run (loop);
1020 }
1021
1022 static const ProfileRunVTable plain_sockets_vtable = {
1023   "plain sockets",
1024   FALSE,
1025   plain_sockets_init_server,
1026   plain_sockets_stop_server,
1027   plain_sockets_thread_func,
1028   plain_sockets_main_loop_run
1029 };
1030
1031 static const ProfileRunVTable plain_sockets_with_malloc_vtable = {
1032   "plain sockets with malloc overhead",
1033   TRUE,
1034   plain_sockets_init_server,
1035   plain_sockets_stop_server,
1036   plain_sockets_thread_func,
1037   plain_sockets_main_loop_run
1038 };
1039
1040 static double
1041 do_profile_run (const ProfileRunVTable *vtable)
1042 {
1043   GTimer *timer;
1044   int i;
1045   double secs;
1046   ServerData sd;
1047   void *server;
1048
1049   g_printerr ("Profiling %s\n", vtable->name);
1050   
1051   sd.handled = 0;
1052   sd.n_clients = 0;
1053   sd.loop = g_main_loop_new (NULL, FALSE);
1054   sd.vtable = vtable;
1055
1056   server = (* vtable->init_server) (&sd);
1057   
1058   for (i = 0; i < N_CLIENT_THREADS; i++)
1059     {
1060       g_thread_create (vtable->client_thread_func, (void*) vtable, FALSE, NULL);
1061     }
1062
1063   timer = g_timer_new ();
1064   
1065   g_printerr ("Server thread %p entering main loop\n",
1066               g_thread_self());
1067   (* vtable->main_loop_run_func) (sd.loop);
1068   g_printerr ("Server thread %p exiting main loop\n",
1069               g_thread_self());
1070
1071   secs = g_timer_elapsed (timer, NULL);
1072   g_timer_destroy (timer);
1073
1074   g_printerr ("%s: %g seconds, %d round trips, %f seconds per pingpong\n",
1075               vtable->name, secs, sd.handled, secs/sd.handled);
1076
1077   (* vtable->stop_server) (&sd, server);
1078   
1079   g_main_loop_unref (sd.loop);
1080
1081   return secs;
1082 }
1083
1084 static void
1085 print_result (const ProfileRunVTable *vtable,
1086               double            seconds,
1087               double            baseline)
1088 {
1089   g_printerr (" %g times slower for %s (%g seconds, %f per iteration)\n",
1090               seconds/baseline, vtable->name,
1091               seconds, seconds / N_ITERATIONS);
1092 }
1093
1094 int
1095 main (int argc, char *argv[])
1096 {
1097   g_thread_init (NULL);
1098   dbus_g_thread_init ();
1099   
1100 #ifndef DBUS_DISABLE_ASSERT
1101   g_printerr ("You should probably --disable-asserts before you profile as they have noticeable overhead\n");
1102 #endif
1103   
1104 #if DBUS_ENABLE_VERBOSE_MODE
1105   g_printerr ("You should probably --disable-verbose-mode before you profile as verbose has noticeable overhead\n");
1106 #endif
1107   
1108   payload = g_malloc (PAYLOAD_SIZE);
1109
1110   /* The actual size of the DBusMessage on the wire, as of Nov 23 2004,
1111    * without the payload
1112    */
1113   echo_call_size = 140 + PAYLOAD_SIZE;
1114   echo_return_size = 32;
1115
1116   if (argc > 1 && strcmp (argv[1], "plain_sockets") == 0)
1117     do_profile_run (&plain_sockets_vtable);
1118   else if (argc > 1 && strcmp (argv[1], "plain_sockets_with_malloc") == 0)
1119     do_profile_run (&plain_sockets_with_malloc_vtable);
1120   else if (argc > 1 && strcmp (argv[1], "no_bus") == 0)
1121     do_profile_run (&no_bus_vtable);
1122   else if (argc > 1 && strcmp (argv[1], "with_bus") == 0)
1123     do_profile_run (&with_bus_vtable);
1124   else if (argc > 1 && strcmp (argv[1], "all") == 0)
1125     {
1126       double e1, e2, e3, e4;
1127
1128       e1 = do_profile_run (&plain_sockets_vtable);
1129       e2 = do_profile_run (&plain_sockets_with_malloc_vtable);
1130       e3 = do_profile_run (&no_bus_vtable);
1131       e4 = do_profile_run (&with_bus_vtable);
1132
1133       g_printerr ("Baseline plain sockets time %g seconds for %d iterations\n",
1134                   e1, N_ITERATIONS);
1135       print_result (&plain_sockets_vtable, e1, e1);
1136       print_result (&plain_sockets_with_malloc_vtable, e2, e1);
1137       print_result (&no_bus_vtable, e3, e1);
1138       print_result (&with_bus_vtable, e4, e1);
1139     }
1140   else
1141     {
1142       g_printerr ("Specify profile type plain_sockets, plain_sockets_with_malloc, no_bus, with_bus, all\n");
1143       exit (1);
1144     }
1145
1146   /* Make valgrind happy */
1147   dbus_shutdown ();
1148   
1149   return 0;
1150 }