Commit | Line | Data |
---|---|---|
a3baf649 SN |
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 | |
9e06d3f9 | 8 | * Copyright (c) Jay Lan, SGI. 2006 |
a3baf649 SN |
9 | * |
10 | */ | |
11 | ||
12 | #include <stdio.h> | |
13 | #include <stdlib.h> | |
14 | #include <errno.h> | |
15 | #include <unistd.h> | |
16 | #include <poll.h> | |
17 | #include <string.h> | |
18 | #include <fcntl.h> | |
19 | #include <sys/types.h> | |
20 | #include <sys/stat.h> | |
21 | #include <sys/socket.h> | |
22 | #include <sys/types.h> | |
23 | #include <signal.h> | |
24 | ||
25 | #include <linux/genetlink.h> | |
26 | #include <linux/taskstats.h> | |
27 | ||
28 | /* | |
29 | * Generic macros for dealing with netlink sockets. Might be duplicated | |
30 | * elsewhere. It is recommended that commercial grade applications use | |
31 | * libnl or libnetlink and use the interfaces provided by the library | |
32 | */ | |
33 | #define GENLMSG_DATA(glh) ((void *)(NLMSG_DATA(glh) + GENL_HDRLEN)) | |
34 | #define GENLMSG_PAYLOAD(glh) (NLMSG_PAYLOAD(glh, 0) - GENL_HDRLEN) | |
35 | #define NLA_DATA(na) ((void *)((char*)(na) + NLA_HDRLEN)) | |
36 | #define NLA_PAYLOAD(len) (len - NLA_HDRLEN) | |
37 | ||
38 | #define err(code, fmt, arg...) do { printf(fmt, ##arg); exit(code); } while (0) | |
39 | int done = 0; | |
9e06d3f9 SN |
40 | int rcvbufsz=0; |
41 | ||
42 | char name[100]; | |
43 | int dbg=0, print_delays=0; | |
44 | __u64 stime, utime; | |
45 | #define PRINTF(fmt, arg...) { \ | |
46 | if (dbg) { \ | |
47 | printf(fmt, ##arg); \ | |
48 | } \ | |
49 | } | |
50 | ||
51 | /* Maximum size of response requested or message sent */ | |
52 | #define MAX_MSG_SIZE 256 | |
53 | /* Maximum number of cpus expected to be specified in a cpumask */ | |
54 | #define MAX_CPUS 32 | |
55 | /* Maximum length of pathname to log file */ | |
56 | #define MAX_FILENAME 256 | |
57 | ||
58 | struct msgtemplate { | |
59 | struct nlmsghdr n; | |
60 | struct genlmsghdr g; | |
61 | char buf[MAX_MSG_SIZE]; | |
62 | }; | |
63 | ||
64 | char cpumask[100+6*MAX_CPUS]; | |
a3baf649 SN |
65 | |
66 | /* | |
67 | * Create a raw netlink socket and bind | |
68 | */ | |
9e06d3f9 | 69 | static int create_nl_socket(int protocol) |
a3baf649 | 70 | { |
9e06d3f9 SN |
71 | int fd; |
72 | struct sockaddr_nl local; | |
73 | ||
74 | fd = socket(AF_NETLINK, SOCK_RAW, protocol); | |
75 | if (fd < 0) | |
76 | return -1; | |
77 | ||
78 | if (rcvbufsz) | |
79 | if (setsockopt(fd, SOL_SOCKET, SO_RCVBUF, | |
80 | &rcvbufsz, sizeof(rcvbufsz)) < 0) { | |
81 | printf("Unable to set socket rcv buf size to %d\n", | |
82 | rcvbufsz); | |
83 | return -1; | |
84 | } | |
a3baf649 | 85 | |
9e06d3f9 SN |
86 | memset(&local, 0, sizeof(local)); |
87 | local.nl_family = AF_NETLINK; | |
a3baf649 | 88 | |
9e06d3f9 SN |
89 | if (bind(fd, (struct sockaddr *) &local, sizeof(local)) < 0) |
90 | goto error; | |
a3baf649 | 91 | |
9e06d3f9 SN |
92 | return fd; |
93 | error: | |
94 | close(fd); | |
95 | return -1; | |
a3baf649 SN |
96 | } |
97 | ||
9e06d3f9 SN |
98 | |
99 | int send_cmd(int sd, __u16 nlmsg_type, __u32 nlmsg_pid, | |
100 | __u8 genl_cmd, __u16 nla_type, | |
101 | void *nla_data, int nla_len) | |
a3baf649 | 102 | { |
9e06d3f9 SN |
103 | struct nlattr *na; |
104 | struct sockaddr_nl nladdr; | |
105 | int r, buflen; | |
106 | char *buf; | |
107 | ||
108 | struct msgtemplate msg; | |
109 | ||
110 | msg.n.nlmsg_len = NLMSG_LENGTH(GENL_HDRLEN); | |
111 | msg.n.nlmsg_type = nlmsg_type; | |
112 | msg.n.nlmsg_flags = NLM_F_REQUEST; | |
113 | msg.n.nlmsg_seq = 0; | |
114 | msg.n.nlmsg_pid = nlmsg_pid; | |
115 | msg.g.cmd = genl_cmd; | |
116 | msg.g.version = 0x1; | |
117 | na = (struct nlattr *) GENLMSG_DATA(&msg); | |
118 | na->nla_type = nla_type; | |
119 | na->nla_len = nla_len + 1 + NLA_HDRLEN; | |
120 | memcpy(NLA_DATA(na), nla_data, nla_len); | |
121 | msg.n.nlmsg_len += NLMSG_ALIGN(na->nla_len); | |
122 | ||
123 | buf = (char *) &msg; | |
124 | buflen = msg.n.nlmsg_len ; | |
125 | memset(&nladdr, 0, sizeof(nladdr)); | |
126 | nladdr.nl_family = AF_NETLINK; | |
127 | while ((r = sendto(sd, buf, buflen, 0, (struct sockaddr *) &nladdr, | |
128 | sizeof(nladdr))) < buflen) { | |
129 | if (r > 0) { | |
130 | buf += r; | |
131 | buflen -= r; | |
132 | } else if (errno != EAGAIN) | |
133 | return -1; | |
134 | } | |
135 | return 0; | |
a3baf649 SN |
136 | } |
137 | ||
9e06d3f9 | 138 | |
a3baf649 SN |
139 | /* |
140 | * Probe the controller in genetlink to find the family id | |
141 | * for the TASKSTATS family | |
142 | */ | |
143 | int get_family_id(int sd) | |
144 | { | |
9e06d3f9 SN |
145 | struct { |
146 | struct nlmsghdr n; | |
147 | struct genlmsghdr g; | |
148 | char buf[256]; | |
149 | } ans; | |
150 | ||
151 | int id, rc; | |
152 | struct nlattr *na; | |
153 | int rep_len; | |
154 | ||
155 | strcpy(name, TASKSTATS_GENL_NAME); | |
156 | rc = send_cmd(sd, GENL_ID_CTRL, getpid(), CTRL_CMD_GETFAMILY, | |
157 | CTRL_ATTR_FAMILY_NAME, (void *)name, | |
158 | strlen(TASKSTATS_GENL_NAME)+1); | |
159 | ||
160 | rep_len = recv(sd, &ans, sizeof(ans), 0); | |
161 | if (ans.n.nlmsg_type == NLMSG_ERROR || | |
162 | (rep_len < 0) || !NLMSG_OK((&ans.n), rep_len)) | |
163 | return 0; | |
a3baf649 | 164 | |
9e06d3f9 SN |
165 | na = (struct nlattr *) GENLMSG_DATA(&ans); |
166 | na = (struct nlattr *) ((char *) na + NLA_ALIGN(na->nla_len)); | |
167 | if (na->nla_type == CTRL_ATTR_FAMILY_ID) { | |
168 | id = *(__u16 *) NLA_DATA(na); | |
169 | } | |
170 | return id; | |
a3baf649 SN |
171 | } |
172 | ||
9e06d3f9 | 173 | void print_delayacct(struct taskstats *t) |
a3baf649 | 174 | { |
9e06d3f9 SN |
175 | printf("\n\nCPU %15s%15s%15s%15s\n" |
176 | " %15llu%15llu%15llu%15llu\n" | |
177 | "IO %15s%15s\n" | |
178 | " %15llu%15llu\n" | |
179 | "MEM %15s%15s\n" | |
180 | " %15llu%15llu\n\n", | |
181 | "count", "real total", "virtual total", "delay total", | |
182 | t->cpu_count, t->cpu_run_real_total, t->cpu_run_virtual_total, | |
183 | t->cpu_delay_total, | |
184 | "count", "delay total", | |
185 | t->blkio_count, t->blkio_delay_total, | |
186 | "count", "delay total", t->swapin_count, t->swapin_delay_total); | |
a3baf649 SN |
187 | } |
188 | ||
189 | int main(int argc, char *argv[]) | |
190 | { | |
9e06d3f9 SN |
191 | int c, rc, rep_len, aggr_len, len2, cmd_type; |
192 | __u16 id; | |
193 | __u32 mypid; | |
194 | ||
195 | struct nlattr *na; | |
196 | int nl_sd = -1; | |
197 | int len = 0; | |
198 | pid_t tid = 0; | |
199 | pid_t rtid = 0; | |
200 | ||
201 | int fd = 0; | |
202 | int count = 0; | |
203 | int write_file = 0; | |
204 | int maskset = 0; | |
205 | char logfile[128]; | |
206 | int loop = 0; | |
207 | ||
208 | struct msgtemplate msg; | |
209 | ||
210 | while (1) { | |
211 | c = getopt(argc, argv, "dw:r:m:t:p:v:l"); | |
212 | if (c < 0) | |
213 | break; | |
a3baf649 | 214 | |
9e06d3f9 SN |
215 | switch (c) { |
216 | case 'd': | |
217 | printf("print delayacct stats ON\n"); | |
218 | print_delays = 1; | |
219 | break; | |
220 | case 'w': | |
221 | strncpy(logfile, optarg, MAX_FILENAME); | |
222 | printf("write to file %s\n", logfile); | |
223 | write_file = 1; | |
224 | break; | |
225 | case 'r': | |
226 | rcvbufsz = atoi(optarg); | |
227 | printf("receive buf size %d\n", rcvbufsz); | |
228 | if (rcvbufsz < 0) | |
229 | err(1, "Invalid rcv buf size\n"); | |
230 | break; | |
231 | case 'm': | |
232 | strncpy(cpumask, optarg, sizeof(cpumask)); | |
233 | maskset = 1; | |
234 | printf("cpumask %s maskset %d\n", cpumask, maskset); | |
235 | break; | |
236 | case 't': | |
237 | tid = atoi(optarg); | |
238 | if (!tid) | |
239 | err(1, "Invalid tgid\n"); | |
240 | cmd_type = TASKSTATS_CMD_ATTR_TGID; | |
241 | print_delays = 1; | |
242 | break; | |
243 | case 'p': | |
244 | tid = atoi(optarg); | |
245 | if (!tid) | |
246 | err(1, "Invalid pid\n"); | |
247 | cmd_type = TASKSTATS_CMD_ATTR_PID; | |
248 | print_delays = 1; | |
249 | break; | |
250 | case 'v': | |
251 | printf("debug on\n"); | |
252 | dbg = 1; | |
253 | break; | |
254 | case 'l': | |
255 | printf("listen forever\n"); | |
256 | loop = 1; | |
257 | break; | |
258 | default: | |
259 | printf("Unknown option %d\n", c); | |
260 | exit(-1); | |
a3baf649 | 261 | } |
a3baf649 | 262 | } |
a3baf649 | 263 | |
9e06d3f9 SN |
264 | if (write_file) { |
265 | fd = open(logfile, O_WRONLY | O_CREAT | O_TRUNC, | |
266 | S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); | |
267 | if (fd == -1) { | |
268 | perror("Cannot open output file\n"); | |
269 | exit(1); | |
270 | } | |
271 | } | |
a3baf649 | 272 | |
9e06d3f9 SN |
273 | if ((nl_sd = create_nl_socket(NETLINK_GENERIC)) < 0) |
274 | err(1, "error creating Netlink socket\n"); | |
a3baf649 | 275 | |
a3baf649 | 276 | |
9e06d3f9 SN |
277 | mypid = getpid(); |
278 | id = get_family_id(nl_sd); | |
279 | if (!id) { | |
280 | printf("Error getting family id, errno %d", errno); | |
281 | goto err; | |
a3baf649 | 282 | } |
9e06d3f9 SN |
283 | PRINTF("family id %d\n", id); |
284 | ||
285 | if (maskset) { | |
286 | rc = send_cmd(nl_sd, id, mypid, TASKSTATS_CMD_GET, | |
287 | TASKSTATS_CMD_ATTR_REGISTER_CPUMASK, | |
288 | &cpumask, sizeof(cpumask)); | |
289 | PRINTF("Sent register cpumask, retval %d\n", rc); | |
290 | if (rc < 0) { | |
291 | printf("error sending register cpumask\n"); | |
292 | goto err; | |
293 | } | |
a3baf649 SN |
294 | } |
295 | ||
9e06d3f9 SN |
296 | if (tid) { |
297 | rc = send_cmd(nl_sd, id, mypid, TASKSTATS_CMD_GET, | |
298 | cmd_type, &tid, sizeof(__u32)); | |
299 | PRINTF("Sent pid/tgid, retval %d\n", rc); | |
300 | if (rc < 0) { | |
301 | printf("error sending tid/tgid cmd\n"); | |
302 | goto done; | |
303 | } | |
a3baf649 SN |
304 | } |
305 | ||
9e06d3f9 SN |
306 | do { |
307 | int i; | |
a3baf649 | 308 | |
9e06d3f9 SN |
309 | rep_len = recv(nl_sd, &msg, sizeof(msg), 0); |
310 | PRINTF("received %d bytes\n", rep_len); | |
a3baf649 | 311 | |
9e06d3f9 SN |
312 | if (rep_len < 0) { |
313 | printf("nonfatal reply error: errno %d\n", errno); | |
314 | continue; | |
315 | } | |
316 | if (msg.n.nlmsg_type == NLMSG_ERROR || | |
317 | !NLMSG_OK((&msg.n), rep_len)) { | |
318 | printf("fatal reply error, errno %d\n", errno); | |
319 | goto done; | |
320 | } | |
321 | ||
322 | PRINTF("nlmsghdr size=%d, nlmsg_len=%d, rep_len=%d\n", | |
323 | sizeof(struct nlmsghdr), msg.n.nlmsg_len, rep_len); | |
324 | ||
325 | ||
326 | rep_len = GENLMSG_PAYLOAD(&msg.n); | |
327 | ||
328 | na = (struct nlattr *) GENLMSG_DATA(&msg); | |
329 | len = 0; | |
330 | i = 0; | |
331 | while (len < rep_len) { | |
332 | len += NLA_ALIGN(na->nla_len); | |
333 | switch (na->nla_type) { | |
334 | case TASKSTATS_TYPE_AGGR_TGID: | |
335 | /* Fall through */ | |
336 | case TASKSTATS_TYPE_AGGR_PID: | |
337 | aggr_len = NLA_PAYLOAD(na->nla_len); | |
338 | len2 = 0; | |
339 | /* For nested attributes, na follows */ | |
340 | na = (struct nlattr *) NLA_DATA(na); | |
341 | done = 0; | |
342 | while (len2 < aggr_len) { | |
343 | switch (na->nla_type) { | |
344 | case TASKSTATS_TYPE_PID: | |
345 | rtid = *(int *) NLA_DATA(na); | |
346 | if (print_delays) | |
347 | printf("PID\t%d\n", rtid); | |
348 | break; | |
349 | case TASKSTATS_TYPE_TGID: | |
350 | rtid = *(int *) NLA_DATA(na); | |
351 | if (print_delays) | |
352 | printf("TGID\t%d\n", rtid); | |
353 | break; | |
354 | case TASKSTATS_TYPE_STATS: | |
355 | count++; | |
356 | if (print_delays) | |
357 | print_delayacct((struct taskstats *) NLA_DATA(na)); | |
358 | if (fd) { | |
359 | if (write(fd, NLA_DATA(na), na->nla_len) < 0) { | |
360 | err(1,"write error\n"); | |
361 | } | |
362 | } | |
363 | if (!loop) | |
364 | goto done; | |
365 | break; | |
366 | default: | |
367 | printf("Unknown nested nla_type %d\n", na->nla_type); | |
368 | break; | |
369 | } | |
370 | len2 += NLA_ALIGN(na->nla_len); | |
371 | na = (struct nlattr *) ((char *) na + len2); | |
372 | } | |
373 | break; | |
374 | ||
375 | default: | |
376 | printf("Unknown nla_type %d\n", na->nla_type); | |
377 | break; | |
a3baf649 | 378 | } |
9e06d3f9 | 379 | na = (struct nlattr *) (GENLMSG_DATA(&msg) + len); |
a3baf649 | 380 | } |
9e06d3f9 SN |
381 | } while (loop); |
382 | done: | |
383 | if (maskset) { | |
384 | rc = send_cmd(nl_sd, id, mypid, TASKSTATS_CMD_GET, | |
385 | TASKSTATS_CMD_ATTR_DEREGISTER_CPUMASK, | |
386 | &cpumask, sizeof(cpumask)); | |
387 | printf("Sent deregister mask, retval %d\n", rc); | |
388 | if (rc < 0) | |
389 | err(rc, "error sending deregister cpumask\n"); | |
a3baf649 | 390 | } |
9e06d3f9 SN |
391 | err: |
392 | close(nl_sd); | |
393 | if (fd) | |
394 | close(fd); | |
395 | return 0; | |
a3baf649 | 396 | } |