[PATCH] per-task-delay-accounting: documentation
[usb.git] / Documentation / accounting / getdelays.c
blob33de89e56a3d38993fc12aab0a9f79b00b469fc0
1 /* getdelays.c
3 * Utility to get per-pid and per-tgid delay accounting statistics
4 * Also illustrates usage of the taskstats interface
6 * Copyright (C) Shailabh Nagar, IBM Corp. 2005
7 * Copyright (C) Balbir Singh, IBM Corp. 2006
9 */
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>
24 #include <linux/genetlink.h>
25 #include <linux/taskstats.h>
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
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)
37 #define err(code, fmt, arg...) do { printf(fmt, ##arg); exit(code); } while (0)
38 int done = 0;
41 * Create a raw netlink socket and bind
43 static int create_nl_socket(int protocol, int groups)
45 socklen_t addr_len;
46 int fd;
47 struct sockaddr_nl local;
49 fd = socket(AF_NETLINK, SOCK_RAW, protocol);
50 if (fd < 0)
51 return -1;
53 memset(&local, 0, sizeof(local));
54 local.nl_family = AF_NETLINK;
55 local.nl_groups = groups;
57 if (bind(fd, (struct sockaddr *) &local, sizeof(local)) < 0)
58 goto error;
60 return fd;
61 error:
62 close(fd);
63 return -1;
66 int sendto_fd(int s, const char *buf, int bufLen)
68 struct sockaddr_nl nladdr;
69 int r;
71 memset(&nladdr, 0, sizeof(nladdr));
72 nladdr.nl_family = AF_NETLINK;
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;
82 return 0;
86 * Probe the controller in genetlink to find the family id
87 * for the TASKSTATS family
89 int get_family_id(int sd)
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;
102 int id;
103 struct nlattr *na;
104 int rep_len;
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);
120 if (sendto_fd(sd, (char *) &family_req, family_req.n.nlmsg_len) < 0)
121 err(1, "error sending message via Netlink\n");
123 rep_len = recv(sd, &ans, sizeof(ans), 0);
125 if (rep_len < 0)
126 err(1, "error receiving reply message via Netlink\n");
129 /* Validate response message */
130 if (!NLMSG_OK((&ans.n), rep_len))
131 err(1, "invalid reply message received via Netlink\n");
133 if (ans.n.nlmsg_type == NLMSG_ERROR) { /* error */
134 printf("error received NACK - leaving\n");
135 exit(1);
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);
144 return id;
147 void print_taskstats(struct taskstats *t)
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);
163 void sigchld(int sig)
165 done = 1;
168 int main(int argc, char *argv[])
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;
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;
185 struct {
186 struct nlmsghdr n;
187 struct genlmsghdr g;
188 char buf[256];
189 } req;
191 struct {
192 struct nlmsghdr n;
193 struct genlmsghdr g;
194 char buf[256];
195 } ans;
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,
211 struct sigaction tact ;
213 if (argc < 3) {
214 printf("usage %s [-t tgid][-p pid][-c cmd]\n", argv[0]);
215 exit(-1);
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");
223 while (1) {
225 c = getopt(argc, argv, "t:p:c:");
226 if (c < 0)
227 break;
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");
248 if (tid == 0) { /* child process */
249 if (execvp(argv[optind - 1], &argv[optind - 1]) < 0) {
250 exit(-1);
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;
260 if (c == 'c')
261 break;
264 /* Construct Netlink request message */
266 /* Send Netlink request message & get reply */
268 if ((nl_sd =
269 create_nl_socket(NETLINK_GENERIC, TASKSTATS_LISTEN_GROUP)) < 0)
270 err(1, "error creating Netlink socket\n");
273 id = get_family_id(nl_sd);
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);
289 if (!forking && sendto_fd(nl_sd, (char *) &req, req.n.nlmsg_len) < 0)
290 err(1, "error sending message via Netlink\n");
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");
297 do {
298 int i;
299 struct pollfd pfd;
300 int pollres;
302 pfd.events = 0xffff & ~POLLOUT;
303 pfd.fd = nl_sd;
304 pollres = poll(&pfd, 1, 5000);
305 if (pollres < 0 || done) {
306 break;
309 rep_len = recv(nl_sd, &ans, sizeof(ans), 0);
310 nladdr.nl_family = AF_NETLINK;
311 nladdr.nl_groups = TASKSTATS_LISTEN_GROUP;
313 if (ans.n.nlmsg_type == NLMSG_ERROR) { /* error */
314 printf("error received NACK - leaving\n");
315 exit(1);
318 if (rep_len < 0) {
319 err(1, "error receiving reply message via Netlink\n");
320 break;
323 /* Validate response message */
324 if (!NLMSG_OK((&ans.n), rep_len))
325 err(1, "invalid reply message received via Netlink\n");
327 rep_len = GENLMSG_PAYLOAD(&ans.n);
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;
357 break;
359 len2 += NLA_ALIGN(na->nla_len);
360 na = (struct nlattr *) ((char *) na + len2);
361 if (done)
362 break;
365 na = (struct nlattr *) (GENLMSG_DATA(&ans) + len);
366 if (done)
367 break;
369 if (done)
370 break;
372 while (1);
374 close(nl_sd);
375 return 0;