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 | |
8 | * | |
9 | */ | |
10 | ||
11 | #include <stdio.h> | |
12 | #include <stdlib.h> | |
13 | #include <errno.h> | |
14 | #include <unistd.h> | |
15 | #include <poll.h> | |
16 | #include <string.h> | |
17 | #include <fcntl.h> | |
18 | #include <sys/types.h> | |
19 | #include <sys/stat.h> | |
20 | #include <sys/socket.h> | |
21 | #include <sys/types.h> | |
22 | #include <signal.h> | |
23 | ||
24 | #include <linux/genetlink.h> | |
25 | #include <linux/taskstats.h> | |
26 | ||
27 | /* | |
28 | * Generic macros for dealing with netlink sockets. Might be duplicated | |
29 | * elsewhere. It is recommended that commercial grade applications use | |
30 | * libnl or libnetlink and use the interfaces provided by the library | |
31 | */ | |
32 | #define GENLMSG_DATA(glh) ((void *)(NLMSG_DATA(glh) + GENL_HDRLEN)) | |
33 | #define GENLMSG_PAYLOAD(glh) (NLMSG_PAYLOAD(glh, 0) - GENL_HDRLEN) | |
34 | #define NLA_DATA(na) ((void *)((char*)(na) + NLA_HDRLEN)) | |
35 | #define NLA_PAYLOAD(len) (len - NLA_HDRLEN) | |
36 | ||
37 | #define err(code, fmt, arg...) do { printf(fmt, ##arg); exit(code); } while (0) | |
38 | int done = 0; | |
39 | ||
40 | /* | |
41 | * Create a raw netlink socket and bind | |
42 | */ | |
43 | static int create_nl_socket(int protocol, int groups) | |
44 | { | |
45 | socklen_t addr_len; | |
46 | int fd; | |
47 | struct sockaddr_nl local; | |
48 | ||
49 | fd = socket(AF_NETLINK, SOCK_RAW, protocol); | |
50 | if (fd < 0) | |
51 | return -1; | |
52 | ||
53 | memset(&local, 0, sizeof(local)); | |
54 | local.nl_family = AF_NETLINK; | |
55 | local.nl_groups = groups; | |
56 | ||
57 | if (bind(fd, (struct sockaddr *) &local, sizeof(local)) < 0) | |
58 | goto error; | |
59 | ||
60 | return fd; | |
61 | error: | |
62 | close(fd); | |
63 | return -1; | |
64 | } | |
65 | ||
66 | int sendto_fd(int s, const char *buf, int bufLen) | |
67 | { | |
68 | struct sockaddr_nl nladdr; | |
69 | int r; | |
70 | ||
71 | memset(&nladdr, 0, sizeof(nladdr)); | |
72 | nladdr.nl_family = AF_NETLINK; | |
73 | ||
74 | while ((r = sendto(s, buf, bufLen, 0, (struct sockaddr *) &nladdr, | |
75 | sizeof(nladdr))) < bufLen) { | |
76 | if (r > 0) { | |
77 | buf += r; | |
78 | bufLen -= r; | |
79 | } else if (errno != EAGAIN) | |
80 | return -1; | |
81 | } | |
82 | return 0; | |
83 | } | |
84 | ||
85 | /* | |
86 | * Probe the controller in genetlink to find the family id | |
87 | * for the TASKSTATS family | |
88 | */ | |
89 | int get_family_id(int sd) | |
90 | { | |
91 | struct { | |
92 | struct nlmsghdr n; | |
93 | struct genlmsghdr g; | |
94 | char buf[256]; | |
95 | } family_req; | |
96 | struct { | |
97 | struct nlmsghdr n; | |
98 | struct genlmsghdr g; | |
99 | char buf[256]; | |
100 | } ans; | |
101 | ||
102 | int id; | |
103 | struct nlattr *na; | |
104 | int rep_len; | |
105 | ||
106 | /* Get family name */ | |
107 | family_req.n.nlmsg_type = GENL_ID_CTRL; | |
108 | family_req.n.nlmsg_flags = NLM_F_REQUEST; | |
109 | family_req.n.nlmsg_seq = 0; | |
110 | family_req.n.nlmsg_pid = getpid(); | |
111 | family_req.n.nlmsg_len = NLMSG_LENGTH(GENL_HDRLEN); | |
112 | family_req.g.cmd = CTRL_CMD_GETFAMILY; | |
113 | family_req.g.version = 0x1; | |
114 | na = (struct nlattr *) GENLMSG_DATA(&family_req); | |
115 | na->nla_type = CTRL_ATTR_FAMILY_NAME; | |
116 | na->nla_len = strlen(TASKSTATS_GENL_NAME) + 1 + NLA_HDRLEN; | |
117 | strcpy(NLA_DATA(na), TASKSTATS_GENL_NAME); | |
118 | family_req.n.nlmsg_len += NLMSG_ALIGN(na->nla_len); | |
119 | ||
120 | if (sendto_fd(sd, (char *) &family_req, family_req.n.nlmsg_len) < 0) | |
121 | err(1, "error sending message via Netlink\n"); | |
122 | ||
123 | rep_len = recv(sd, &ans, sizeof(ans), 0); | |
124 | ||
125 | if (rep_len < 0) | |
126 | err(1, "error receiving reply message via Netlink\n"); | |
127 | ||
128 | ||
129 | /* Validate response message */ | |
130 | if (!NLMSG_OK((&ans.n), rep_len)) | |
131 | err(1, "invalid reply message received via Netlink\n"); | |
132 | ||
133 | if (ans.n.nlmsg_type == NLMSG_ERROR) { /* error */ | |
134 | printf("error received NACK - leaving\n"); | |
135 | exit(1); | |
136 | } | |
137 | ||
138 | ||
139 | na = (struct nlattr *) GENLMSG_DATA(&ans); | |
140 | na = (struct nlattr *) ((char *) na + NLA_ALIGN(na->nla_len)); | |
141 | if (na->nla_type == CTRL_ATTR_FAMILY_ID) { | |
142 | id = *(__u16 *) NLA_DATA(na); | |
143 | } | |
144 | return id; | |
145 | } | |
146 | ||
147 | void print_taskstats(struct taskstats *t) | |
148 | { | |
149 | printf("\n\nCPU %15s%15s%15s%15s\n" | |
150 | " %15llu%15llu%15llu%15llu\n" | |
151 | "IO %15s%15s\n" | |
152 | " %15llu%15llu\n" | |
153 | "MEM %15s%15s\n" | |
154 | " %15llu%15llu\n\n", | |
155 | "count", "real total", "virtual total", "delay total", | |
156 | t->cpu_count, t->cpu_run_real_total, t->cpu_run_virtual_total, | |
157 | t->cpu_delay_total, | |
158 | "count", "delay total", | |
159 | t->blkio_count, t->blkio_delay_total, | |
160 | "count", "delay total", t->swapin_count, t->swapin_delay_total); | |
161 | } | |
162 | ||
163 | void sigchld(int sig) | |
164 | { | |
165 | done = 1; | |
166 | } | |
167 | ||
168 | int main(int argc, char *argv[]) | |
169 | { | |
170 | int rc; | |
171 | int sk_nl; | |
172 | struct nlmsghdr *nlh; | |
173 | struct genlmsghdr *genlhdr; | |
174 | char *buf; | |
175 | struct taskstats_cmd_param *param; | |
176 | __u16 id; | |
177 | struct nlattr *na; | |
178 | ||
179 | /* For receiving */ | |
180 | struct sockaddr_nl kern_nla, from_nla; | |
181 | socklen_t from_nla_len; | |
182 | int recv_len; | |
183 | struct taskstats_reply *reply; | |
184 | ||
185 | struct { | |
186 | struct nlmsghdr n; | |
187 | struct genlmsghdr g; | |
188 | char buf[256]; | |
189 | } req; | |
190 | ||
191 | struct { | |
192 | struct nlmsghdr n; | |
193 | struct genlmsghdr g; | |
194 | char buf[256]; | |
195 | } ans; | |
196 | ||
197 | int nl_sd = -1; | |
198 | int rep_len; | |
199 | int len = 0; | |
200 | int aggr_len, len2; | |
201 | struct sockaddr_nl nladdr; | |
202 | pid_t tid = 0; | |
203 | pid_t rtid = 0; | |
204 | int cmd_type = TASKSTATS_TYPE_TGID; | |
205 | int c, status; | |
206 | int forking = 0; | |
207 | struct sigaction act = { | |
208 | .sa_handler = SIG_IGN, | |
209 | .sa_mask = SA_NOMASK, | |
210 | }; | |
211 | struct sigaction tact ; | |
212 | ||
213 | if (argc < 3) { | |
214 | printf("usage %s [-t tgid][-p pid][-c cmd]\n", argv[0]); | |
215 | exit(-1); | |
216 | } | |
217 | ||
218 | tact.sa_handler = sigchld; | |
219 | sigemptyset(&tact.sa_mask); | |
220 | if (sigaction(SIGCHLD, &tact, NULL) < 0) | |
221 | err(1, "sigaction failed for SIGCHLD\n"); | |
222 | ||
223 | while (1) { | |
224 | ||
225 | c = getopt(argc, argv, "t:p:c:"); | |
226 | if (c < 0) | |
227 | break; | |
228 | ||
229 | switch (c) { | |
230 | case 't': | |
231 | tid = atoi(optarg); | |
232 | if (!tid) | |
233 | err(1, "Invalid tgid\n"); | |
234 | cmd_type = TASKSTATS_CMD_ATTR_TGID; | |
235 | break; | |
236 | case 'p': | |
237 | tid = atoi(optarg); | |
238 | if (!tid) | |
239 | err(1, "Invalid pid\n"); | |
240 | cmd_type = TASKSTATS_CMD_ATTR_TGID; | |
241 | break; | |
242 | case 'c': | |
243 | opterr = 0; | |
244 | tid = fork(); | |
245 | if (tid < 0) | |
246 | err(1, "fork failed\n"); | |
247 | ||
248 | if (tid == 0) { /* child process */ | |
249 | if (execvp(argv[optind - 1], &argv[optind - 1]) < 0) { | |
250 | exit(-1); | |
251 | } | |
252 | } | |
253 | forking = 1; | |
254 | break; | |
255 | default: | |
256 | printf("usage %s [-t tgid][-p pid][-c cmd]\n", argv[0]); | |
257 | exit(-1); | |
258 | break; | |
259 | } | |
260 | if (c == 'c') | |
261 | break; | |
262 | } | |
263 | ||
264 | /* Construct Netlink request message */ | |
265 | ||
266 | /* Send Netlink request message & get reply */ | |
267 | ||
268 | if ((nl_sd = | |
269 | create_nl_socket(NETLINK_GENERIC, TASKSTATS_LISTEN_GROUP)) < 0) | |
270 | err(1, "error creating Netlink socket\n"); | |
271 | ||
272 | ||
273 | id = get_family_id(nl_sd); | |
274 | ||
275 | /* Send command needed */ | |
276 | req.n.nlmsg_len = NLMSG_LENGTH(GENL_HDRLEN); | |
277 | req.n.nlmsg_type = id; | |
278 | req.n.nlmsg_flags = NLM_F_REQUEST; | |
279 | req.n.nlmsg_seq = 0; | |
280 | req.n.nlmsg_pid = tid; | |
281 | req.g.cmd = TASKSTATS_CMD_GET; | |
282 | na = (struct nlattr *) GENLMSG_DATA(&req); | |
283 | na->nla_type = cmd_type; | |
284 | na->nla_len = sizeof(unsigned int) + NLA_HDRLEN; | |
285 | *(__u32 *) NLA_DATA(na) = tid; | |
286 | req.n.nlmsg_len += NLMSG_ALIGN(na->nla_len); | |
287 | ||
288 | ||
289 | if (!forking && sendto_fd(nl_sd, (char *) &req, req.n.nlmsg_len) < 0) | |
290 | err(1, "error sending message via Netlink\n"); | |
291 | ||
292 | act.sa_handler = SIG_IGN; | |
293 | sigemptyset(&act.sa_mask); | |
294 | if (sigaction(SIGINT, &act, NULL) < 0) | |
295 | err(1, "sigaction failed for SIGINT\n"); | |
296 | ||
297 | do { | |
298 | int i; | |
299 | struct pollfd pfd; | |
300 | int pollres; | |
301 | ||
302 | pfd.events = 0xffff & ~POLLOUT; | |
303 | pfd.fd = nl_sd; | |
304 | pollres = poll(&pfd, 1, 5000); | |
305 | if (pollres < 0 || done) { | |
306 | break; | |
307 | } | |
308 | ||
309 | rep_len = recv(nl_sd, &ans, sizeof(ans), 0); | |
310 | nladdr.nl_family = AF_NETLINK; | |
311 | nladdr.nl_groups = TASKSTATS_LISTEN_GROUP; | |
312 | ||
313 | if (ans.n.nlmsg_type == NLMSG_ERROR) { /* error */ | |
314 | printf("error received NACK - leaving\n"); | |
315 | exit(1); | |
316 | } | |
317 | ||
318 | if (rep_len < 0) { | |
319 | err(1, "error receiving reply message via Netlink\n"); | |
320 | break; | |
321 | } | |
322 | ||
323 | /* Validate response message */ | |
324 | if (!NLMSG_OK((&ans.n), rep_len)) | |
325 | err(1, "invalid reply message received via Netlink\n"); | |
326 | ||
327 | rep_len = GENLMSG_PAYLOAD(&ans.n); | |
328 | ||
329 | na = (struct nlattr *) GENLMSG_DATA(&ans); | |
330 | len = 0; | |
331 | i = 0; | |
332 | while (len < rep_len) { | |
333 | len += NLA_ALIGN(na->nla_len); | |
334 | switch (na->nla_type) { | |
335 | case TASKSTATS_TYPE_AGGR_PID: | |
336 | /* Fall through */ | |
337 | case TASKSTATS_TYPE_AGGR_TGID: | |
338 | aggr_len = NLA_PAYLOAD(na->nla_len); | |
339 | len2 = 0; | |
340 | /* For nested attributes, na follows */ | |
341 | na = (struct nlattr *) NLA_DATA(na); | |
342 | done = 0; | |
343 | while (len2 < aggr_len) { | |
344 | switch (na->nla_type) { | |
345 | case TASKSTATS_TYPE_PID: | |
346 | rtid = *(int *) NLA_DATA(na); | |
347 | break; | |
348 | case TASKSTATS_TYPE_TGID: | |
349 | rtid = *(int *) NLA_DATA(na); | |
350 | break; | |
351 | case TASKSTATS_TYPE_STATS: | |
352 | if (rtid == tid) { | |
353 | print_taskstats((struct taskstats *) | |
354 | NLA_DATA(na)); | |
355 | done = 1; | |
356 | } | |
357 | break; | |
358 | } | |
359 | len2 += NLA_ALIGN(na->nla_len); | |
360 | na = (struct nlattr *) ((char *) na + len2); | |
361 | if (done) | |
362 | break; | |
363 | } | |
364 | } | |
365 | na = (struct nlattr *) (GENLMSG_DATA(&ans) + len); | |
366 | if (done) | |
367 | break; | |
368 | } | |
369 | if (done) | |
370 | break; | |
371 | } | |
372 | while (1); | |
373 | ||
374 | close(nl_sd); | |
375 | return 0; | |
376 | } |