1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
2 
3 #include <sys/wait.h>
4 #include <unistd.h>
5 
6 #include "sd-bus.h"
7 
8 #include "alloc-util.h"
9 #include "bus-internal.h"
10 #include "bus-kernel.h"
11 #include "def.h"
12 #include "fd-util.h"
13 #include "missing_resource.h"
14 #include "string-util.h"
15 #include "time-util.h"
16 #include "util.h"
17 
18 #define MAX_SIZE (2*1024*1024)
19 
20 static usec_t arg_loop_usec = 100 * USEC_PER_MSEC;
21 
22 typedef enum Type {
23         TYPE_LEGACY,
24         TYPE_DIRECT,
25 } Type;
26 
server(sd_bus * b,size_t * result)27 static void server(sd_bus *b, size_t *result) {
28         int r;
29 
30         for (;;) {
31                 _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
32 
33                 r = sd_bus_process(b, &m);
34                 assert_se(r >= 0);
35 
36                 if (r == 0)
37                         assert_se(sd_bus_wait(b, USEC_INFINITY) >= 0);
38                 if (!m)
39                         continue;
40 
41                 if (sd_bus_message_is_method_call(m, "benchmark.server", "Ping"))
42                         assert_se(sd_bus_reply_method_return(m, NULL) >= 0);
43                 else if (sd_bus_message_is_method_call(m, "benchmark.server", "Work")) {
44                         const void *p;
45                         size_t sz;
46 
47                         /* Make sure the mmap is mapped */
48                         assert_se(sd_bus_message_read_array(m, 'y', &p, &sz) > 0);
49 
50                         r = sd_bus_reply_method_return(m, NULL);
51                         assert_se(r >= 0);
52                 } else if (sd_bus_message_is_method_call(m, "benchmark.server", "Exit")) {
53                         uint64_t res;
54                         assert_se(sd_bus_message_read(m, "t", &res) > 0);
55 
56                         *result = res;
57                         return;
58 
59                 } else if (!sd_bus_message_is_signal(m, NULL, NULL))
60                         assert_not_reached();
61         }
62 }
63 
transaction(sd_bus * b,size_t sz,const char * server_name)64 static void transaction(sd_bus *b, size_t sz, const char *server_name) {
65         _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL, *reply = NULL;
66         uint8_t *p;
67 
68         assert_se(sd_bus_message_new_method_call(b, &m, server_name, "/", "benchmark.server", "Work") >= 0);
69         assert_se(sd_bus_message_append_array_space(m, 'y', sz, (void**) &p) >= 0);
70 
71         memset(p, 0x80, sz);
72 
73         assert_se(sd_bus_call(b, m, 0, NULL, &reply) >= 0);
74 }
75 
client_bisect(const char * address,const char * server_name)76 static void client_bisect(const char *address, const char *server_name) {
77         _cleanup_(sd_bus_message_unrefp) sd_bus_message *x = NULL;
78         size_t lsize, rsize, csize;
79         sd_bus *b;
80         int r;
81 
82         r = sd_bus_new(&b);
83         assert_se(r >= 0);
84 
85         r = sd_bus_set_address(b, address);
86         assert_se(r >= 0);
87 
88         r = sd_bus_start(b);
89         assert_se(r >= 0);
90 
91         r = sd_bus_call_method(b, server_name, "/", "benchmark.server", "Ping", NULL, NULL, NULL);
92         assert_se(r >= 0);
93 
94         lsize = 1;
95         rsize = MAX_SIZE;
96 
97         printf("SIZE\tCOPY\tMEMFD\n");
98 
99         for (;;) {
100                 usec_t t;
101                 unsigned n_copying, n_memfd;
102 
103                 csize = (lsize + rsize) / 2;
104 
105                 if (csize <= lsize)
106                         break;
107 
108                 if (csize <= 0)
109                         break;
110 
111                 printf("%zu\t", csize);
112 
113                 b->use_memfd = 0;
114 
115                 t = now(CLOCK_MONOTONIC);
116                 for (n_copying = 0;; n_copying++) {
117                         transaction(b, csize, server_name);
118                         if (now(CLOCK_MONOTONIC) >= t + arg_loop_usec)
119                                 break;
120                 }
121                 printf("%u\t", (unsigned) ((n_copying * USEC_PER_SEC) / arg_loop_usec));
122 
123                 b->use_memfd = -1;
124 
125                 t = now(CLOCK_MONOTONIC);
126                 for (n_memfd = 0;; n_memfd++) {
127                         transaction(b, csize, server_name);
128                         if (now(CLOCK_MONOTONIC) >= t + arg_loop_usec)
129                                 break;
130                 }
131                 printf("%u\n", (unsigned) ((n_memfd * USEC_PER_SEC) / arg_loop_usec));
132 
133                 if (n_copying == n_memfd)
134                         break;
135 
136                 if (n_copying > n_memfd)
137                         lsize = csize;
138                 else
139                         rsize = csize;
140         }
141 
142         b->use_memfd = 1;
143         assert_se(sd_bus_message_new_method_call(b, &x, server_name, "/", "benchmark.server", "Exit") >= 0);
144         assert_se(sd_bus_message_append(x, "t", csize) >= 0);
145         assert_se(sd_bus_send(b, x, NULL) >= 0);
146 
147         sd_bus_unref(b);
148 }
149 
client_chart(Type type,const char * address,const char * server_name,int fd)150 static void client_chart(Type type, const char *address, const char *server_name, int fd) {
151         _cleanup_(sd_bus_message_unrefp) sd_bus_message *x = NULL;
152         size_t csize;
153         sd_bus *b;
154         int r;
155 
156         r = sd_bus_new(&b);
157         assert_se(r >= 0);
158 
159         if (type == TYPE_DIRECT) {
160                 r = sd_bus_set_fd(b, fd, fd);
161                 assert_se(r >= 0);
162         } else {
163                 r = sd_bus_set_address(b, address);
164                 assert_se(r >= 0);
165 
166                 r = sd_bus_set_bus_client(b, true);
167                 assert_se(r >= 0);
168         }
169 
170         r = sd_bus_start(b);
171         assert_se(r >= 0);
172 
173         r = sd_bus_call_method(b, server_name, "/", "benchmark.server", "Ping", NULL, NULL, NULL);
174         assert_se(r >= 0);
175 
176         switch (type) {
177         case TYPE_LEGACY:
178                 printf("SIZE\tLEGACY\n");
179                 break;
180         case TYPE_DIRECT:
181                 printf("SIZE\tDIRECT\n");
182                 break;
183         }
184 
185         for (csize = 1; csize <= MAX_SIZE; csize *= 2) {
186                 usec_t t;
187                 unsigned n_memfd;
188 
189                 printf("%zu\t", csize);
190 
191                 t = now(CLOCK_MONOTONIC);
192                 for (n_memfd = 0;; n_memfd++) {
193                         transaction(b, csize, server_name);
194                         if (now(CLOCK_MONOTONIC) >= t + arg_loop_usec)
195                                 break;
196                 }
197 
198                 printf("%u\n", (unsigned) ((n_memfd * USEC_PER_SEC) / arg_loop_usec));
199         }
200 
201         b->use_memfd = 1;
202         assert_se(sd_bus_message_new_method_call(b, &x, server_name, "/", "benchmark.server", "Exit") >= 0);
203         assert_se(sd_bus_message_append(x, "t", csize) >= 0);
204         assert_se(sd_bus_send(b, x, NULL) >= 0);
205 
206         sd_bus_unref(b);
207 }
208 
main(int argc,char * argv[])209 int main(int argc, char *argv[]) {
210         enum {
211                 MODE_BISECT,
212                 MODE_CHART,
213         } mode = MODE_BISECT;
214         Type type = TYPE_LEGACY;
215         int i, pair[2] = { -1, -1 };
216         _cleanup_free_ char *address = NULL, *server_name = NULL;
217         _cleanup_close_ int bus_ref = -1;
218         const char *unique;
219         cpu_set_t cpuset;
220         size_t result;
221         sd_bus *b;
222         pid_t pid;
223         int r;
224 
225         for (i = 1; i < argc; i++) {
226                 if (streq(argv[i], "chart")) {
227                         mode = MODE_CHART;
228                         continue;
229                 } else if (streq(argv[i], "legacy")) {
230                         type = TYPE_LEGACY;
231                         continue;
232                 } else if (streq(argv[i], "direct")) {
233                         type = TYPE_DIRECT;
234                         continue;
235                 }
236 
237                 assert_se(parse_sec(argv[i], &arg_loop_usec) >= 0);
238         }
239 
240         assert_se(arg_loop_usec > 0);
241 
242         if (type == TYPE_LEGACY) {
243                 const char *e;
244 
245                 e = secure_getenv("DBUS_SESSION_BUS_ADDRESS");
246                 assert_se(e);
247 
248                 address = strdup(e);
249                 assert_se(address);
250         }
251 
252         r = sd_bus_new(&b);
253         assert_se(r >= 0);
254 
255         if (type == TYPE_DIRECT) {
256                 assert_se(socketpair(AF_UNIX, SOCK_STREAM, 0, pair) >= 0);
257 
258                 r = sd_bus_set_fd(b, pair[0], pair[0]);
259                 assert_se(r >= 0);
260 
261                 r = sd_bus_set_server(b, true, SD_ID128_NULL);
262                 assert_se(r >= 0);
263         } else {
264                 r = sd_bus_set_address(b, address);
265                 assert_se(r >= 0);
266 
267                 r = sd_bus_set_bus_client(b, true);
268                 assert_se(r >= 0);
269         }
270 
271         r = sd_bus_start(b);
272         assert_se(r >= 0);
273 
274         if (type != TYPE_DIRECT) {
275                 r = sd_bus_get_unique_name(b, &unique);
276                 assert_se(r >= 0);
277 
278                 server_name = strdup(unique);
279                 assert_se(server_name);
280         }
281 
282         sync();
283         setpriority(PRIO_PROCESS, 0, -19);
284 
285         pid = fork();
286         assert_se(pid >= 0);
287 
288         if (pid == 0) {
289                 CPU_ZERO(&cpuset);
290                 CPU_SET(0, &cpuset);
291                 pthread_setaffinity_np(pthread_self(), sizeof(cpu_set_t), &cpuset);
292 
293                 safe_close(bus_ref);
294                 sd_bus_unref(b);
295 
296                 switch (mode) {
297                 case MODE_BISECT:
298                         client_bisect(address, server_name);
299                         break;
300 
301                 case MODE_CHART:
302                         client_chart(type, address, server_name, pair[1]);
303                         break;
304                 }
305 
306                 _exit(EXIT_SUCCESS);
307         }
308 
309         CPU_ZERO(&cpuset);
310         CPU_SET(1, &cpuset);
311         pthread_setaffinity_np(pthread_self(), sizeof(cpu_set_t), &cpuset);
312 
313         server(b, &result);
314 
315         if (mode == MODE_BISECT)
316                 printf("Copying/memfd are equally fast at %zu bytes\n", result);
317 
318         assert_se(waitpid(pid, NULL, 0) == pid);
319 
320         safe_close(pair[1]);
321         sd_bus_unref(b);
322 
323         return 0;
324 }
325