Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
torvalds
GitHub Repository: torvalds/linux
Path: blob/master/tools/perf/bench/inject-buildid.c
26285 views
1
// SPDX-License-Identifier: GPL-2.0
2
#include <stdlib.h>
3
#include <stddef.h>
4
#include <ftw.h>
5
#include <fcntl.h>
6
#include <errno.h>
7
#include <unistd.h>
8
#include <pthread.h>
9
#include <sys/mman.h>
10
#include <sys/wait.h>
11
#include <linux/kernel.h>
12
#include <linux/time64.h>
13
#include <linux/list.h>
14
#include <linux/err.h>
15
#include <linux/zalloc.h>
16
#include <internal/lib.h>
17
#include <subcmd/parse-options.h>
18
19
#include "bench.h"
20
#include "util/data.h"
21
#include "util/stat.h"
22
#include "util/debug.h"
23
#include "util/symbol.h"
24
#include "util/session.h"
25
#include "util/build-id.h"
26
#include "util/sample.h"
27
#include "util/synthetic-events.h"
28
29
#define MMAP_DEV_MAJOR 8
30
#define DSO_MMAP_RATIO 4
31
32
static unsigned int iterations = 100;
33
static unsigned int nr_mmaps = 100;
34
static unsigned int nr_samples = 100; /* samples per mmap */
35
36
static u64 bench_sample_type;
37
static u16 bench_id_hdr_size;
38
39
struct bench_data {
40
int pid;
41
int input_pipe[2];
42
int output_pipe[2];
43
pthread_t th;
44
};
45
46
struct bench_dso {
47
struct list_head list;
48
char *name;
49
int ino;
50
};
51
52
static int nr_dsos;
53
static struct bench_dso *dsos;
54
55
extern int main(int argc, const char **argv);
56
57
static const struct option options[] = {
58
OPT_UINTEGER('i', "iterations", &iterations,
59
"Number of iterations used to compute average (default: 100)"),
60
OPT_UINTEGER('m', "nr-mmaps", &nr_mmaps,
61
"Number of mmap events for each iteration (default: 100)"),
62
OPT_UINTEGER('n', "nr-samples", &nr_samples,
63
"Number of sample events per mmap event (default: 100)"),
64
OPT_INCR('v', "verbose", &verbose,
65
"be more verbose (show iteration count, DSO name, etc)"),
66
OPT_END()
67
};
68
69
static const char *const bench_usage[] = {
70
"perf bench internals inject-build-id <options>",
71
NULL
72
};
73
74
/*
75
* Helper for collect_dso that adds the given file as a dso to dso_list
76
* if it contains a build-id. Stops after collecting 4 times more than
77
* we need (for MMAP2 events).
78
*/
79
static int add_dso(const char *fpath, const struct stat *sb __maybe_unused,
80
int typeflag, struct FTW *ftwbuf __maybe_unused)
81
{
82
struct bench_dso *dso = &dsos[nr_dsos];
83
struct build_id bid = { .size = 0, };
84
85
if (typeflag == FTW_D || typeflag == FTW_SL)
86
return 0;
87
88
if (filename__read_build_id(fpath, &bid, /*block=*/true) < 0)
89
return 0;
90
91
dso->name = realpath(fpath, NULL);
92
if (dso->name == NULL)
93
return -1;
94
95
dso->ino = nr_dsos++;
96
pr_debug2(" Adding DSO: %s\n", fpath);
97
98
/* stop if we collected enough DSOs */
99
if ((unsigned int)nr_dsos == DSO_MMAP_RATIO * nr_mmaps)
100
return 1;
101
102
return 0;
103
}
104
105
static void collect_dso(void)
106
{
107
dsos = calloc(nr_mmaps * DSO_MMAP_RATIO, sizeof(*dsos));
108
if (dsos == NULL) {
109
printf(" Memory allocation failed\n");
110
exit(1);
111
}
112
113
if (nftw("/usr/lib/", add_dso, 10, FTW_PHYS) < 0)
114
return;
115
116
pr_debug(" Collected %d DSOs\n", nr_dsos);
117
}
118
119
static void release_dso(void)
120
{
121
int i;
122
123
for (i = 0; i < nr_dsos; i++) {
124
struct bench_dso *dso = &dsos[i];
125
126
zfree(&dso->name);
127
}
128
free(dsos);
129
}
130
131
/* Fake address used by mmap and sample events */
132
static u64 dso_map_addr(struct bench_dso *dso)
133
{
134
return 0x400000ULL + dso->ino * 8192ULL;
135
}
136
137
static ssize_t synthesize_attr(struct bench_data *data)
138
{
139
union perf_event event;
140
141
memset(&event, 0, sizeof(event.attr) + sizeof(u64));
142
143
event.header.type = PERF_RECORD_HEADER_ATTR;
144
event.header.size = sizeof(event.attr) + sizeof(u64);
145
146
event.attr.attr.type = PERF_TYPE_SOFTWARE;
147
event.attr.attr.config = PERF_COUNT_SW_TASK_CLOCK;
148
event.attr.attr.exclude_kernel = 1;
149
event.attr.attr.sample_id_all = 1;
150
event.attr.attr.sample_type = bench_sample_type;
151
152
return writen(data->input_pipe[1], &event, event.header.size);
153
}
154
155
static ssize_t synthesize_fork(struct bench_data *data)
156
{
157
union perf_event event;
158
159
memset(&event, 0, sizeof(event.fork) + bench_id_hdr_size);
160
161
event.header.type = PERF_RECORD_FORK;
162
event.header.misc = PERF_RECORD_MISC_FORK_EXEC;
163
event.header.size = sizeof(event.fork) + bench_id_hdr_size;
164
165
event.fork.ppid = 1;
166
event.fork.ptid = 1;
167
event.fork.pid = data->pid;
168
event.fork.tid = data->pid;
169
170
return writen(data->input_pipe[1], &event, event.header.size);
171
}
172
173
static ssize_t synthesize_mmap(struct bench_data *data, struct bench_dso *dso, u64 timestamp)
174
{
175
union perf_event event;
176
size_t len = offsetof(struct perf_record_mmap2, filename);
177
u64 *id_hdr_ptr = (void *)&event;
178
int ts_idx;
179
180
len += roundup(strlen(dso->name) + 1, 8) + bench_id_hdr_size;
181
182
memset(&event, 0, min(len, sizeof(event.mmap2)));
183
184
event.header.type = PERF_RECORD_MMAP2;
185
event.header.misc = PERF_RECORD_MISC_USER;
186
event.header.size = len;
187
188
event.mmap2.pid = data->pid;
189
event.mmap2.tid = data->pid;
190
event.mmap2.maj = MMAP_DEV_MAJOR;
191
event.mmap2.ino = dso->ino;
192
193
strcpy(event.mmap2.filename, dso->name);
194
195
event.mmap2.start = dso_map_addr(dso);
196
event.mmap2.len = 4096;
197
event.mmap2.prot = PROT_EXEC;
198
199
if (len > sizeof(event.mmap2)) {
200
/* write mmap2 event first */
201
if (writen(data->input_pipe[1], &event, len - bench_id_hdr_size) < 0)
202
return -1;
203
/* zero-fill sample id header */
204
memset(id_hdr_ptr, 0, bench_id_hdr_size);
205
/* put timestamp in the right position */
206
ts_idx = (bench_id_hdr_size / sizeof(u64)) - 2;
207
id_hdr_ptr[ts_idx] = timestamp;
208
if (writen(data->input_pipe[1], id_hdr_ptr, bench_id_hdr_size) < 0)
209
return -1;
210
211
return len;
212
}
213
214
ts_idx = (len / sizeof(u64)) - 2;
215
id_hdr_ptr[ts_idx] = timestamp;
216
return writen(data->input_pipe[1], &event, len);
217
}
218
219
static ssize_t synthesize_sample(struct bench_data *data, struct bench_dso *dso, u64 timestamp)
220
{
221
union perf_event event;
222
struct perf_sample sample = {
223
.tid = data->pid,
224
.pid = data->pid,
225
.ip = dso_map_addr(dso),
226
.time = timestamp,
227
};
228
229
event.header.type = PERF_RECORD_SAMPLE;
230
event.header.misc = PERF_RECORD_MISC_USER;
231
event.header.size = perf_event__sample_event_size(&sample, bench_sample_type, 0);
232
233
perf_event__synthesize_sample(&event, bench_sample_type, 0, &sample);
234
235
return writen(data->input_pipe[1], &event, event.header.size);
236
}
237
238
static ssize_t synthesize_flush(struct bench_data *data)
239
{
240
struct perf_event_header header = {
241
.size = sizeof(header),
242
.type = PERF_RECORD_FINISHED_ROUND,
243
};
244
245
return writen(data->input_pipe[1], &header, header.size);
246
}
247
248
static void *data_reader(void *arg)
249
{
250
struct bench_data *data = arg;
251
char buf[8192];
252
int flag;
253
int n;
254
255
flag = fcntl(data->output_pipe[0], F_GETFL);
256
fcntl(data->output_pipe[0], F_SETFL, flag | O_NONBLOCK);
257
258
/* read out data from child */
259
while (true) {
260
n = read(data->output_pipe[0], buf, sizeof(buf));
261
if (n > 0)
262
continue;
263
if (n == 0)
264
break;
265
266
if (errno != EINTR && errno != EAGAIN)
267
break;
268
269
usleep(100);
270
}
271
272
close(data->output_pipe[0]);
273
return NULL;
274
}
275
276
static int setup_injection(struct bench_data *data, bool build_id_all)
277
{
278
int ready_pipe[2];
279
int dev_null_fd;
280
char buf;
281
282
if (pipe(ready_pipe) < 0)
283
return -1;
284
285
if (pipe(data->input_pipe) < 0)
286
return -1;
287
288
if (pipe(data->output_pipe) < 0)
289
return -1;
290
291
data->pid = fork();
292
if (data->pid < 0)
293
return -1;
294
295
if (data->pid == 0) {
296
const char **inject_argv;
297
int inject_argc = 3;
298
299
close(data->input_pipe[1]);
300
close(data->output_pipe[0]);
301
close(ready_pipe[0]);
302
303
dup2(data->input_pipe[0], STDIN_FILENO);
304
close(data->input_pipe[0]);
305
dup2(data->output_pipe[1], STDOUT_FILENO);
306
close(data->output_pipe[1]);
307
308
dev_null_fd = open("/dev/null", O_WRONLY);
309
if (dev_null_fd < 0)
310
exit(1);
311
312
dup2(dev_null_fd, STDERR_FILENO);
313
314
if (build_id_all)
315
inject_argc++;
316
317
inject_argv = calloc(inject_argc + 1, sizeof(*inject_argv));
318
if (inject_argv == NULL)
319
exit(1);
320
321
inject_argv[0] = strdup("perf");
322
inject_argv[1] = strdup("inject");
323
inject_argv[2] = strdup("-b");
324
if (build_id_all)
325
inject_argv[3] = strdup("--buildid-all");
326
327
/* signal that we're ready to go */
328
close(ready_pipe[1]);
329
330
main(inject_argc, inject_argv);
331
332
exit(0);
333
}
334
335
pthread_create(&data->th, NULL, data_reader, data);
336
337
close(ready_pipe[1]);
338
close(data->input_pipe[0]);
339
close(data->output_pipe[1]);
340
341
/* wait for child ready */
342
if (read(ready_pipe[0], &buf, 1) < 0)
343
return -1;
344
close(ready_pipe[0]);
345
346
return 0;
347
}
348
349
static int inject_build_id(struct bench_data *data, u64 *max_rss)
350
{
351
int status;
352
unsigned int i, k;
353
struct rusage rusage;
354
355
/* this makes the child to run */
356
if (perf_header__write_pipe(data->input_pipe[1]) < 0)
357
return -1;
358
359
if (synthesize_attr(data) < 0)
360
return -1;
361
362
if (synthesize_fork(data) < 0)
363
return -1;
364
365
for (i = 0; i < nr_mmaps; i++) {
366
int idx = rand() % nr_dsos;
367
struct bench_dso *dso = &dsos[idx];
368
u64 timestamp = rand() % 1000000;
369
370
pr_debug2(" [%d] injecting: %s\n", i+1, dso->name);
371
if (synthesize_mmap(data, dso, timestamp) < 0)
372
return -1;
373
374
for (k = 0; k < nr_samples; k++) {
375
if (synthesize_sample(data, dso, timestamp + k * 1000) < 0)
376
return -1;
377
}
378
379
if ((i + 1) % 10 == 0) {
380
if (synthesize_flush(data) < 0)
381
return -1;
382
}
383
}
384
385
/* this makes the child to finish */
386
close(data->input_pipe[1]);
387
388
wait4(data->pid, &status, 0, &rusage);
389
*max_rss = rusage.ru_maxrss;
390
391
pr_debug(" Child %d exited with %d\n", data->pid, status);
392
393
return 0;
394
}
395
396
static void do_inject_loop(struct bench_data *data, bool build_id_all)
397
{
398
unsigned int i;
399
struct stats time_stats, mem_stats;
400
double time_average, time_stddev;
401
double mem_average, mem_stddev;
402
403
init_stats(&time_stats);
404
init_stats(&mem_stats);
405
406
pr_debug(" Build-id%s injection benchmark\n", build_id_all ? "-all" : "");
407
408
for (i = 0; i < iterations; i++) {
409
struct timeval start, end, diff;
410
u64 runtime_us, max_rss;
411
412
pr_debug(" Iteration #%d\n", i+1);
413
414
if (setup_injection(data, build_id_all) < 0) {
415
printf(" Build-id injection setup failed\n");
416
break;
417
}
418
419
gettimeofday(&start, NULL);
420
if (inject_build_id(data, &max_rss) < 0) {
421
printf(" Build-id injection failed\n");
422
break;
423
}
424
425
gettimeofday(&end, NULL);
426
timersub(&end, &start, &diff);
427
runtime_us = diff.tv_sec * USEC_PER_SEC + diff.tv_usec;
428
update_stats(&time_stats, runtime_us);
429
update_stats(&mem_stats, max_rss);
430
431
pthread_join(data->th, NULL);
432
}
433
434
time_average = avg_stats(&time_stats) / USEC_PER_MSEC;
435
time_stddev = stddev_stats(&time_stats) / USEC_PER_MSEC;
436
printf(" Average build-id%s injection took: %.3f msec (+- %.3f msec)\n",
437
build_id_all ? "-all" : "", time_average, time_stddev);
438
439
/* each iteration, it processes MMAP2 + BUILD_ID + nr_samples * SAMPLE */
440
time_average = avg_stats(&time_stats) / (nr_mmaps * (nr_samples + 2));
441
time_stddev = stddev_stats(&time_stats) / (nr_mmaps * (nr_samples + 2));
442
printf(" Average time per event: %.3f usec (+- %.3f usec)\n",
443
time_average, time_stddev);
444
445
mem_average = avg_stats(&mem_stats);
446
mem_stddev = stddev_stats(&mem_stats);
447
printf(" Average memory usage: %.0f KB (+- %.0f KB)\n",
448
mem_average, mem_stddev);
449
}
450
451
static int do_inject_loops(struct bench_data *data)
452
{
453
454
srand(time(NULL));
455
symbol__init(NULL);
456
457
bench_sample_type = PERF_SAMPLE_IDENTIFIER | PERF_SAMPLE_IP;
458
bench_sample_type |= PERF_SAMPLE_TID | PERF_SAMPLE_TIME;
459
bench_id_hdr_size = 32;
460
461
collect_dso();
462
if (nr_dsos == 0) {
463
printf(" Cannot collect DSOs for injection\n");
464
return -1;
465
}
466
467
do_inject_loop(data, false);
468
do_inject_loop(data, true);
469
470
release_dso();
471
return 0;
472
}
473
474
int bench_inject_build_id(int argc, const char **argv)
475
{
476
struct bench_data data;
477
478
argc = parse_options(argc, argv, options, bench_usage, 0);
479
if (argc) {
480
usage_with_options(bench_usage, options);
481
exit(EXIT_FAILURE);
482
}
483
484
return do_inject_loops(&data);
485
}
486
487
488