Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
awilliam
GitHub Repository: awilliam/linux-vfio
Path: blob/master/Documentation/accounting/getdelays.c
10821 views
1
/* getdelays.c
2
*
3
* Utility to get per-pid and per-tgid delay accounting statistics
4
* Also illustrates usage of the taskstats interface
5
*
6
* Copyright (C) Shailabh Nagar, IBM Corp. 2005
7
* Copyright (C) Balbir Singh, IBM Corp. 2006
8
* Copyright (c) Jay Lan, SGI. 2006
9
*
10
* Compile with
11
* gcc -I/usr/src/linux/include getdelays.c -o getdelays
12
*/
13
14
#include <stdio.h>
15
#include <stdlib.h>
16
#include <errno.h>
17
#include <unistd.h>
18
#include <poll.h>
19
#include <string.h>
20
#include <fcntl.h>
21
#include <sys/types.h>
22
#include <sys/stat.h>
23
#include <sys/socket.h>
24
#include <sys/wait.h>
25
#include <signal.h>
26
27
#include <linux/genetlink.h>
28
#include <linux/taskstats.h>
29
#include <linux/cgroupstats.h>
30
31
/*
32
* Generic macros for dealing with netlink sockets. Might be duplicated
33
* elsewhere. It is recommended that commercial grade applications use
34
* libnl or libnetlink and use the interfaces provided by the library
35
*/
36
#define GENLMSG_DATA(glh) ((void *)(NLMSG_DATA(glh) + GENL_HDRLEN))
37
#define GENLMSG_PAYLOAD(glh) (NLMSG_PAYLOAD(glh, 0) - GENL_HDRLEN)
38
#define NLA_DATA(na) ((void *)((char*)(na) + NLA_HDRLEN))
39
#define NLA_PAYLOAD(len) (len - NLA_HDRLEN)
40
41
#define err(code, fmt, arg...) \
42
do { \
43
fprintf(stderr, fmt, ##arg); \
44
exit(code); \
45
} while (0)
46
47
int done;
48
int rcvbufsz;
49
char name[100];
50
int dbg;
51
int print_delays;
52
int print_io_accounting;
53
int print_task_context_switch_counts;
54
__u64 stime, utime;
55
56
#define PRINTF(fmt, arg...) { \
57
if (dbg) { \
58
printf(fmt, ##arg); \
59
} \
60
}
61
62
/* Maximum size of response requested or message sent */
63
#define MAX_MSG_SIZE 1024
64
/* Maximum number of cpus expected to be specified in a cpumask */
65
#define MAX_CPUS 32
66
67
struct msgtemplate {
68
struct nlmsghdr n;
69
struct genlmsghdr g;
70
char buf[MAX_MSG_SIZE];
71
};
72
73
char cpumask[100+6*MAX_CPUS];
74
75
static void usage(void)
76
{
77
fprintf(stderr, "getdelays [-dilv] [-w logfile] [-r bufsize] "
78
"[-m cpumask] [-t tgid] [-p pid]\n");
79
fprintf(stderr, " -d: print delayacct stats\n");
80
fprintf(stderr, " -i: print IO accounting (works only with -p)\n");
81
fprintf(stderr, " -l: listen forever\n");
82
fprintf(stderr, " -v: debug on\n");
83
fprintf(stderr, " -C: container path\n");
84
}
85
86
/*
87
* Create a raw netlink socket and bind
88
*/
89
static int create_nl_socket(int protocol)
90
{
91
int fd;
92
struct sockaddr_nl local;
93
94
fd = socket(AF_NETLINK, SOCK_RAW, protocol);
95
if (fd < 0)
96
return -1;
97
98
if (rcvbufsz)
99
if (setsockopt(fd, SOL_SOCKET, SO_RCVBUF,
100
&rcvbufsz, sizeof(rcvbufsz)) < 0) {
101
fprintf(stderr, "Unable to set socket rcv buf size "
102
"to %d\n",
103
rcvbufsz);
104
return -1;
105
}
106
107
memset(&local, 0, sizeof(local));
108
local.nl_family = AF_NETLINK;
109
110
if (bind(fd, (struct sockaddr *) &local, sizeof(local)) < 0)
111
goto error;
112
113
return fd;
114
error:
115
close(fd);
116
return -1;
117
}
118
119
120
static int send_cmd(int sd, __u16 nlmsg_type, __u32 nlmsg_pid,
121
__u8 genl_cmd, __u16 nla_type,
122
void *nla_data, int nla_len)
123
{
124
struct nlattr *na;
125
struct sockaddr_nl nladdr;
126
int r, buflen;
127
char *buf;
128
129
struct msgtemplate msg;
130
131
msg.n.nlmsg_len = NLMSG_LENGTH(GENL_HDRLEN);
132
msg.n.nlmsg_type = nlmsg_type;
133
msg.n.nlmsg_flags = NLM_F_REQUEST;
134
msg.n.nlmsg_seq = 0;
135
msg.n.nlmsg_pid = nlmsg_pid;
136
msg.g.cmd = genl_cmd;
137
msg.g.version = 0x1;
138
na = (struct nlattr *) GENLMSG_DATA(&msg);
139
na->nla_type = nla_type;
140
na->nla_len = nla_len + 1 + NLA_HDRLEN;
141
memcpy(NLA_DATA(na), nla_data, nla_len);
142
msg.n.nlmsg_len += NLMSG_ALIGN(na->nla_len);
143
144
buf = (char *) &msg;
145
buflen = msg.n.nlmsg_len ;
146
memset(&nladdr, 0, sizeof(nladdr));
147
nladdr.nl_family = AF_NETLINK;
148
while ((r = sendto(sd, buf, buflen, 0, (struct sockaddr *) &nladdr,
149
sizeof(nladdr))) < buflen) {
150
if (r > 0) {
151
buf += r;
152
buflen -= r;
153
} else if (errno != EAGAIN)
154
return -1;
155
}
156
return 0;
157
}
158
159
160
/*
161
* Probe the controller in genetlink to find the family id
162
* for the TASKSTATS family
163
*/
164
static int get_family_id(int sd)
165
{
166
struct {
167
struct nlmsghdr n;
168
struct genlmsghdr g;
169
char buf[256];
170
} ans;
171
172
int id = 0, rc;
173
struct nlattr *na;
174
int rep_len;
175
176
strcpy(name, TASKSTATS_GENL_NAME);
177
rc = send_cmd(sd, GENL_ID_CTRL, getpid(), CTRL_CMD_GETFAMILY,
178
CTRL_ATTR_FAMILY_NAME, (void *)name,
179
strlen(TASKSTATS_GENL_NAME)+1);
180
if (rc < 0)
181
return 0; /* sendto() failure? */
182
183
rep_len = recv(sd, &ans, sizeof(ans), 0);
184
if (ans.n.nlmsg_type == NLMSG_ERROR ||
185
(rep_len < 0) || !NLMSG_OK((&ans.n), rep_len))
186
return 0;
187
188
na = (struct nlattr *) GENLMSG_DATA(&ans);
189
na = (struct nlattr *) ((char *) na + NLA_ALIGN(na->nla_len));
190
if (na->nla_type == CTRL_ATTR_FAMILY_ID) {
191
id = *(__u16 *) NLA_DATA(na);
192
}
193
return id;
194
}
195
196
#define average_ms(t, c) (t / 1000000ULL / (c ? c : 1))
197
198
static void print_delayacct(struct taskstats *t)
199
{
200
printf("\n\nCPU %15s%15s%15s%15s%15s\n"
201
" %15llu%15llu%15llu%15llu%15.3fms\n"
202
"IO %15s%15s%15s\n"
203
" %15llu%15llu%15llums\n"
204
"SWAP %15s%15s%15s\n"
205
" %15llu%15llu%15llums\n"
206
"RECLAIM %12s%15s%15s\n"
207
" %15llu%15llu%15llums\n",
208
"count", "real total", "virtual total",
209
"delay total", "delay average",
210
(unsigned long long)t->cpu_count,
211
(unsigned long long)t->cpu_run_real_total,
212
(unsigned long long)t->cpu_run_virtual_total,
213
(unsigned long long)t->cpu_delay_total,
214
average_ms((double)t->cpu_delay_total, t->cpu_count),
215
"count", "delay total", "delay average",
216
(unsigned long long)t->blkio_count,
217
(unsigned long long)t->blkio_delay_total,
218
average_ms(t->blkio_delay_total, t->blkio_count),
219
"count", "delay total", "delay average",
220
(unsigned long long)t->swapin_count,
221
(unsigned long long)t->swapin_delay_total,
222
average_ms(t->swapin_delay_total, t->swapin_count),
223
"count", "delay total", "delay average",
224
(unsigned long long)t->freepages_count,
225
(unsigned long long)t->freepages_delay_total,
226
average_ms(t->freepages_delay_total, t->freepages_count));
227
}
228
229
static void task_context_switch_counts(struct taskstats *t)
230
{
231
printf("\n\nTask %15s%15s\n"
232
" %15llu%15llu\n",
233
"voluntary", "nonvoluntary",
234
(unsigned long long)t->nvcsw, (unsigned long long)t->nivcsw);
235
}
236
237
static void print_cgroupstats(struct cgroupstats *c)
238
{
239
printf("sleeping %llu, blocked %llu, running %llu, stopped %llu, "
240
"uninterruptible %llu\n", (unsigned long long)c->nr_sleeping,
241
(unsigned long long)c->nr_io_wait,
242
(unsigned long long)c->nr_running,
243
(unsigned long long)c->nr_stopped,
244
(unsigned long long)c->nr_uninterruptible);
245
}
246
247
248
static void print_ioacct(struct taskstats *t)
249
{
250
printf("%s: read=%llu, write=%llu, cancelled_write=%llu\n",
251
t->ac_comm,
252
(unsigned long long)t->read_bytes,
253
(unsigned long long)t->write_bytes,
254
(unsigned long long)t->cancelled_write_bytes);
255
}
256
257
int main(int argc, char *argv[])
258
{
259
int c, rc, rep_len, aggr_len, len2;
260
int cmd_type = TASKSTATS_CMD_ATTR_UNSPEC;
261
__u16 id;
262
__u32 mypid;
263
264
struct nlattr *na;
265
int nl_sd = -1;
266
int len = 0;
267
pid_t tid = 0;
268
pid_t rtid = 0;
269
270
int fd = 0;
271
int count = 0;
272
int write_file = 0;
273
int maskset = 0;
274
char *logfile = NULL;
275
int loop = 0;
276
int containerset = 0;
277
char containerpath[1024];
278
int cfd = 0;
279
int forking = 0;
280
sigset_t sigset;
281
282
struct msgtemplate msg;
283
284
while (!forking) {
285
c = getopt(argc, argv, "qdiw:r:m:t:p:vlC:c:");
286
if (c < 0)
287
break;
288
289
switch (c) {
290
case 'd':
291
printf("print delayacct stats ON\n");
292
print_delays = 1;
293
break;
294
case 'i':
295
printf("printing IO accounting\n");
296
print_io_accounting = 1;
297
break;
298
case 'q':
299
printf("printing task/process context switch rates\n");
300
print_task_context_switch_counts = 1;
301
break;
302
case 'C':
303
containerset = 1;
304
strncpy(containerpath, optarg, strlen(optarg) + 1);
305
break;
306
case 'w':
307
logfile = strdup(optarg);
308
printf("write to file %s\n", logfile);
309
write_file = 1;
310
break;
311
case 'r':
312
rcvbufsz = atoi(optarg);
313
printf("receive buf size %d\n", rcvbufsz);
314
if (rcvbufsz < 0)
315
err(1, "Invalid rcv buf size\n");
316
break;
317
case 'm':
318
strncpy(cpumask, optarg, sizeof(cpumask));
319
maskset = 1;
320
printf("cpumask %s maskset %d\n", cpumask, maskset);
321
break;
322
case 't':
323
tid = atoi(optarg);
324
if (!tid)
325
err(1, "Invalid tgid\n");
326
cmd_type = TASKSTATS_CMD_ATTR_TGID;
327
break;
328
case 'p':
329
tid = atoi(optarg);
330
if (!tid)
331
err(1, "Invalid pid\n");
332
cmd_type = TASKSTATS_CMD_ATTR_PID;
333
break;
334
case 'c':
335
336
/* Block SIGCHLD for sigwait() later */
337
if (sigemptyset(&sigset) == -1)
338
err(1, "Failed to empty sigset");
339
if (sigaddset(&sigset, SIGCHLD))
340
err(1, "Failed to set sigchld in sigset");
341
sigprocmask(SIG_BLOCK, &sigset, NULL);
342
343
/* fork/exec a child */
344
tid = fork();
345
if (tid < 0)
346
err(1, "Fork failed\n");
347
if (tid == 0)
348
if (execvp(argv[optind - 1],
349
&argv[optind - 1]) < 0)
350
exit(-1);
351
352
/* Set the command type and avoid further processing */
353
cmd_type = TASKSTATS_CMD_ATTR_PID;
354
forking = 1;
355
break;
356
case 'v':
357
printf("debug on\n");
358
dbg = 1;
359
break;
360
case 'l':
361
printf("listen forever\n");
362
loop = 1;
363
break;
364
default:
365
usage();
366
exit(-1);
367
}
368
}
369
370
if (write_file) {
371
fd = open(logfile, O_WRONLY | O_CREAT | O_TRUNC,
372
S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
373
if (fd == -1) {
374
perror("Cannot open output file\n");
375
exit(1);
376
}
377
}
378
379
if ((nl_sd = create_nl_socket(NETLINK_GENERIC)) < 0)
380
err(1, "error creating Netlink socket\n");
381
382
383
mypid = getpid();
384
id = get_family_id(nl_sd);
385
if (!id) {
386
fprintf(stderr, "Error getting family id, errno %d\n", errno);
387
goto err;
388
}
389
PRINTF("family id %d\n", id);
390
391
if (maskset) {
392
rc = send_cmd(nl_sd, id, mypid, TASKSTATS_CMD_GET,
393
TASKSTATS_CMD_ATTR_REGISTER_CPUMASK,
394
&cpumask, strlen(cpumask) + 1);
395
PRINTF("Sent register cpumask, retval %d\n", rc);
396
if (rc < 0) {
397
fprintf(stderr, "error sending register cpumask\n");
398
goto err;
399
}
400
}
401
402
if (tid && containerset) {
403
fprintf(stderr, "Select either -t or -C, not both\n");
404
goto err;
405
}
406
407
/*
408
* If we forked a child, wait for it to exit. Cannot use waitpid()
409
* as all the delicious data would be reaped as part of the wait
410
*/
411
if (tid && forking) {
412
int sig_received;
413
sigwait(&sigset, &sig_received);
414
}
415
416
if (tid) {
417
rc = send_cmd(nl_sd, id, mypid, TASKSTATS_CMD_GET,
418
cmd_type, &tid, sizeof(__u32));
419
PRINTF("Sent pid/tgid, retval %d\n", rc);
420
if (rc < 0) {
421
fprintf(stderr, "error sending tid/tgid cmd\n");
422
goto done;
423
}
424
}
425
426
if (containerset) {
427
cfd = open(containerpath, O_RDONLY);
428
if (cfd < 0) {
429
perror("error opening container file");
430
goto err;
431
}
432
rc = send_cmd(nl_sd, id, mypid, CGROUPSTATS_CMD_GET,
433
CGROUPSTATS_CMD_ATTR_FD, &cfd, sizeof(__u32));
434
if (rc < 0) {
435
perror("error sending cgroupstats command");
436
goto err;
437
}
438
}
439
if (!maskset && !tid && !containerset) {
440
usage();
441
goto err;
442
}
443
444
do {
445
rep_len = recv(nl_sd, &msg, sizeof(msg), 0);
446
PRINTF("received %d bytes\n", rep_len);
447
448
if (rep_len < 0) {
449
fprintf(stderr, "nonfatal reply error: errno %d\n",
450
errno);
451
continue;
452
}
453
if (msg.n.nlmsg_type == NLMSG_ERROR ||
454
!NLMSG_OK((&msg.n), rep_len)) {
455
struct nlmsgerr *err = NLMSG_DATA(&msg);
456
fprintf(stderr, "fatal reply error, errno %d\n",
457
err->error);
458
goto done;
459
}
460
461
PRINTF("nlmsghdr size=%zu, nlmsg_len=%d, rep_len=%d\n",
462
sizeof(struct nlmsghdr), msg.n.nlmsg_len, rep_len);
463
464
465
rep_len = GENLMSG_PAYLOAD(&msg.n);
466
467
na = (struct nlattr *) GENLMSG_DATA(&msg);
468
len = 0;
469
while (len < rep_len) {
470
len += NLA_ALIGN(na->nla_len);
471
switch (na->nla_type) {
472
case TASKSTATS_TYPE_AGGR_TGID:
473
/* Fall through */
474
case TASKSTATS_TYPE_AGGR_PID:
475
aggr_len = NLA_PAYLOAD(na->nla_len);
476
len2 = 0;
477
/* For nested attributes, na follows */
478
na = (struct nlattr *) NLA_DATA(na);
479
done = 0;
480
while (len2 < aggr_len) {
481
switch (na->nla_type) {
482
case TASKSTATS_TYPE_PID:
483
rtid = *(int *) NLA_DATA(na);
484
if (print_delays)
485
printf("PID\t%d\n", rtid);
486
break;
487
case TASKSTATS_TYPE_TGID:
488
rtid = *(int *) NLA_DATA(na);
489
if (print_delays)
490
printf("TGID\t%d\n", rtid);
491
break;
492
case TASKSTATS_TYPE_STATS:
493
count++;
494
if (print_delays)
495
print_delayacct((struct taskstats *) NLA_DATA(na));
496
if (print_io_accounting)
497
print_ioacct((struct taskstats *) NLA_DATA(na));
498
if (print_task_context_switch_counts)
499
task_context_switch_counts((struct taskstats *) NLA_DATA(na));
500
if (fd) {
501
if (write(fd, NLA_DATA(na), na->nla_len) < 0) {
502
err(1,"write error\n");
503
}
504
}
505
if (!loop)
506
goto done;
507
break;
508
default:
509
fprintf(stderr, "Unknown nested"
510
" nla_type %d\n",
511
na->nla_type);
512
break;
513
}
514
len2 += NLA_ALIGN(na->nla_len);
515
na = (struct nlattr *) ((char *) na + len2);
516
}
517
break;
518
519
case CGROUPSTATS_TYPE_CGROUP_STATS:
520
print_cgroupstats(NLA_DATA(na));
521
break;
522
default:
523
fprintf(stderr, "Unknown nla_type %d\n",
524
na->nla_type);
525
case TASKSTATS_TYPE_NULL:
526
break;
527
}
528
na = (struct nlattr *) (GENLMSG_DATA(&msg) + len);
529
}
530
} while (loop);
531
done:
532
if (maskset) {
533
rc = send_cmd(nl_sd, id, mypid, TASKSTATS_CMD_GET,
534
TASKSTATS_CMD_ATTR_DEREGISTER_CPUMASK,
535
&cpumask, strlen(cpumask) + 1);
536
printf("Sent deregister mask, retval %d\n", rc);
537
if (rc < 0)
538
err(rc, "error sending deregister cpumask\n");
539
}
540
err:
541
close(nl_sd);
542
if (fd)
543
close(fd);
544
if (cfd)
545
close(cfd);
546
return 0;
547
}
548
549