]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/libsystemd/sd-bus/test-bus-benchmark.c
sd-bus: do not connect to dbus-1 socket when kdbus is available
[thirdparty/systemd.git] / src / libsystemd / sd-bus / test-bus-benchmark.c
1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
2
3 /***
4 This file is part of systemd.
5
6 Copyright 2013 Lennart Poettering
7
8 systemd is free software; you can redistribute it and/or modify it
9 under the terms of the GNU Lesser General Public License as published by
10 the Free Software Foundation; either version 2.1 of the License, or
11 (at your option) any later version.
12
13 systemd is distributed in the hope that it will be useful, but
14 WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 Lesser General Public License for more details.
17
18 You should have received a copy of the GNU Lesser General Public License
19 along with systemd; If not, see <http://www.gnu.org/licenses/>.
20 ***/
21
22 #include <sys/wait.h>
23
24 #include "def.h"
25 #include "util.h"
26 #include "time-util.h"
27
28 #include "sd-bus.h"
29 #include "bus-kernel.h"
30 #include "bus-internal.h"
31
32 #define MAX_SIZE (2*1024*1024)
33
34 static usec_t arg_loop_usec = 100 * USEC_PER_MSEC;
35
36 typedef enum Type {
37 TYPE_KDBUS,
38 TYPE_LEGACY,
39 TYPE_DIRECT,
40 } Type;
41
42 static void server(sd_bus *b, size_t *result) {
43 int r;
44
45 for (;;) {
46 _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
47
48 r = sd_bus_process(b, &m);
49 assert_se(r >= 0);
50
51 if (r == 0)
52 assert_se(sd_bus_wait(b, USEC_INFINITY) >= 0);
53 if (!m)
54 continue;
55
56 if (sd_bus_message_is_method_call(m, "benchmark.server", "Ping"))
57 assert_se(sd_bus_reply_method_return(m, NULL) >= 0);
58 else if (sd_bus_message_is_method_call(m, "benchmark.server", "Work")) {
59 const void *p;
60 size_t sz;
61
62 /* Make sure the mmap is mapped */
63 assert_se(sd_bus_message_read_array(m, 'y', &p, &sz) > 0);
64
65 r = sd_bus_reply_method_return(m, NULL);
66 assert_se(r >= 0);
67 } else if (sd_bus_message_is_method_call(m, "benchmark.server", "Exit")) {
68 uint64_t res;
69 assert_se(sd_bus_message_read(m, "t", &res) > 0);
70
71 *result = res;
72 return;
73
74 } else if (!sd_bus_message_is_signal(m, NULL, NULL))
75 assert_not_reached("Unknown method");
76 }
77 }
78
79 static void transaction(sd_bus *b, size_t sz, const char *server_name) {
80 _cleanup_bus_message_unref_ sd_bus_message *m = NULL, *reply = NULL;
81 uint8_t *p;
82
83 assert_se(sd_bus_message_new_method_call(b, &m, server_name, "/", "benchmark.server", "Work") >= 0);
84 assert_se(sd_bus_message_append_array_space(m, 'y', sz, (void**) &p) >= 0);
85
86 memset(p, 0x80, sz);
87
88 assert_se(sd_bus_call(b, m, 0, NULL, &reply) >= 0);
89 }
90
91 static void client_bisect(const char *address, const char *server_name) {
92 _cleanup_bus_message_unref_ sd_bus_message *x = NULL;
93 size_t lsize, rsize, csize;
94 sd_bus *b;
95 int r;
96
97 r = sd_bus_new(&b);
98 assert_se(r >= 0);
99
100 r = sd_bus_set_address(b, address);
101 assert_se(r >= 0);
102
103 r = sd_bus_start(b);
104 assert_se(r >= 0);
105
106 r = sd_bus_call_method(b, server_name, "/", "benchmark.server", "Ping", NULL, NULL, NULL);
107 assert_se(r >= 0);
108
109 lsize = 1;
110 rsize = MAX_SIZE;
111
112 printf("SIZE\tCOPY\tMEMFD\n");
113
114 for (;;) {
115 usec_t t;
116 unsigned n_copying, n_memfd;
117
118 csize = (lsize + rsize) / 2;
119
120 if (csize <= lsize)
121 break;
122
123 if (csize <= 0)
124 break;
125
126 printf("%zu\t", csize);
127
128 b->use_memfd = 0;
129
130 t = now(CLOCK_MONOTONIC);
131 for (n_copying = 0;; n_copying++) {
132 transaction(b, csize, server_name);
133 if (now(CLOCK_MONOTONIC) >= t + arg_loop_usec)
134 break;
135 }
136 printf("%u\t", (unsigned) ((n_copying * USEC_PER_SEC) / arg_loop_usec));
137
138 b->use_memfd = -1;
139
140 t = now(CLOCK_MONOTONIC);
141 for (n_memfd = 0;; n_memfd++) {
142 transaction(b, csize, server_name);
143 if (now(CLOCK_MONOTONIC) >= t + arg_loop_usec)
144 break;
145 }
146 printf("%u\n", (unsigned) ((n_memfd * USEC_PER_SEC) / arg_loop_usec));
147
148 if (n_copying == n_memfd)
149 break;
150
151 if (n_copying > n_memfd)
152 lsize = csize;
153 else
154 rsize = csize;
155 }
156
157 b->use_memfd = 1;
158 assert_se(sd_bus_message_new_method_call(b, &x, server_name, "/", "benchmark.server", "Exit") >= 0);
159 assert_se(sd_bus_message_append(x, "t", csize) >= 0);
160 assert_se(sd_bus_send(b, x, NULL) >= 0);
161
162 sd_bus_unref(b);
163 }
164
165 static void client_chart(Type type, const char *address, const char *server_name, int fd) {
166 _cleanup_bus_message_unref_ sd_bus_message *x = NULL;
167 size_t csize;
168 sd_bus *b;
169 int r;
170
171 r = sd_bus_new(&b);
172 assert_se(r >= 0);
173
174 if (type == TYPE_DIRECT) {
175 r = sd_bus_set_fd(b, fd, fd);
176 assert_se(r >= 0);
177 } else {
178 r = sd_bus_set_address(b, address);
179 assert_se(r >= 0);
180
181 r = sd_bus_set_bus_client(b, true);
182 assert_se(r >= 0);
183 }
184
185 r = sd_bus_start(b);
186 assert_se(r >= 0);
187
188 r = sd_bus_call_method(b, server_name, "/", "benchmark.server", "Ping", NULL, NULL, NULL);
189 assert_se(r >= 0);
190
191 switch (type) {
192 case TYPE_KDBUS:
193 printf("SIZE\tCOPY\tMEMFD\n");
194 break;
195 case TYPE_LEGACY:
196 printf("SIZE\tLEGACY\n");
197 break;
198 case TYPE_DIRECT:
199 printf("SIZE\tDIRECT\n");
200 break;
201 }
202
203 for (csize = 1; csize <= MAX_SIZE; csize *= 2) {
204 usec_t t;
205 unsigned n_copying, n_memfd;
206
207 printf("%zu\t", csize);
208
209 if (type == TYPE_KDBUS) {
210 b->use_memfd = 0;
211
212 t = now(CLOCK_MONOTONIC);
213 for (n_copying = 0;; n_copying++) {
214 transaction(b, csize, server_name);
215 if (now(CLOCK_MONOTONIC) >= t + arg_loop_usec)
216 break;
217 }
218
219 printf("%u\t", (unsigned) ((n_copying * USEC_PER_SEC) / arg_loop_usec));
220
221 b->use_memfd = -1;
222 }
223
224 t = now(CLOCK_MONOTONIC);
225 for (n_memfd = 0;; n_memfd++) {
226 transaction(b, csize, server_name);
227 if (now(CLOCK_MONOTONIC) >= t + arg_loop_usec)
228 break;
229 }
230
231 printf("%u\n", (unsigned) ((n_memfd * USEC_PER_SEC) / arg_loop_usec));
232 }
233
234 b->use_memfd = 1;
235 assert_se(sd_bus_message_new_method_call(b, &x, server_name, "/", "benchmark.server", "Exit") >= 0);
236 assert_se(sd_bus_message_append(x, "t", csize) >= 0);
237 assert_se(sd_bus_send(b, x, NULL) >= 0);
238
239 sd_bus_unref(b);
240 }
241
242 int main(int argc, char *argv[]) {
243 enum {
244 MODE_BISECT,
245 MODE_CHART,
246 } mode = MODE_BISECT;
247 Type type = TYPE_KDBUS;
248 int i, pair[2] = { -1, -1 };
249 _cleanup_free_ char *name = NULL, *bus_name = NULL, *address = NULL, *server_name = NULL;
250 _cleanup_close_ int bus_ref = -1;
251 const char *unique;
252 cpu_set_t cpuset;
253 size_t result;
254 sd_bus *b;
255 pid_t pid;
256 int r;
257
258 for (i = 1; i < argc; i++) {
259 if (streq(argv[i], "chart")) {
260 mode = MODE_CHART;
261 continue;
262 } else if (streq(argv[i], "legacy")) {
263 type = TYPE_LEGACY;
264 continue;
265 } else if (streq(argv[i], "direct")) {
266 type = TYPE_DIRECT;
267 continue;
268 }
269
270 assert_se(parse_sec(argv[i], &arg_loop_usec) >= 0);
271 }
272
273 assert_se(!MODE_BISECT || TYPE_KDBUS);
274
275 assert_se(arg_loop_usec > 0);
276
277 if (type == TYPE_KDBUS) {
278 assert_se(asprintf(&name, "deine-mutter-%u", (unsigned) getpid()) >= 0);
279
280 bus_ref = bus_kernel_create_bus(name, false, &bus_name);
281 if (bus_ref == -ENOENT)
282 exit(EXIT_TEST_SKIP);
283
284 assert_se(bus_ref >= 0);
285
286 address = strappend("kernel:path=", bus_name);
287 assert_se(address);
288 } else if (type == TYPE_LEGACY) {
289 const char *e;
290
291 e = secure_getenv("DBUS_SESSION_BUS_ADDRESS");
292 assert_se(e);
293
294 address = strdup(e);
295 assert_se(address);
296 }
297
298 r = sd_bus_new(&b);
299 assert_se(r >= 0);
300
301 if (type == TYPE_DIRECT) {
302 assert_se(socketpair(AF_UNIX, SOCK_STREAM, 0, pair) >= 0);
303
304 r = sd_bus_set_fd(b, pair[0], pair[0]);
305 assert_se(r >= 0);
306
307 r = sd_bus_set_server(b, true, SD_ID128_NULL);
308 assert_se(r >= 0);
309 } else {
310 r = sd_bus_set_address(b, address);
311 assert_se(r >= 0);
312
313 r = sd_bus_set_bus_client(b, true);
314 assert_se(r >= 0);
315 }
316
317 r = sd_bus_start(b);
318 assert_se(r >= 0);
319
320 if (type != TYPE_DIRECT) {
321 r = sd_bus_get_unique_name(b, &unique);
322 assert_se(r >= 0);
323
324 server_name = strdup(unique);
325 assert_se(server_name);
326 }
327
328 sync();
329 setpriority(PRIO_PROCESS, 0, -19);
330
331 pid = fork();
332 assert_se(pid >= 0);
333
334 if (pid == 0) {
335 CPU_ZERO(&cpuset);
336 CPU_SET(0, &cpuset);
337 pthread_setaffinity_np(pthread_self(), sizeof(cpu_set_t), &cpuset);
338
339 safe_close(bus_ref);
340 sd_bus_unref(b);
341
342 switch (mode) {
343 case MODE_BISECT:
344 client_bisect(address, server_name);
345 break;
346
347 case MODE_CHART:
348 client_chart(type, address, server_name, pair[1]);
349 break;
350 }
351
352 _exit(0);
353 }
354
355 CPU_ZERO(&cpuset);
356 CPU_SET(1, &cpuset);
357 pthread_setaffinity_np(pthread_self(), sizeof(cpu_set_t), &cpuset);
358
359 server(b, &result);
360
361 if (mode == MODE_BISECT)
362 printf("Copying/memfd are equally fast at %zu bytes\n", result);
363
364 assert_se(waitpid(pid, NULL, 0) == pid);
365
366 safe_close(pair[1]);
367 sd_bus_unref(b);
368
369 return 0;
370 }