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