[PATCH] per task delay accounting taskstats interface: documentation fix
[linux-2.6.git] / Documentation / accounting / getdelays.c
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  */
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;
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];
65
66 /*
67  * Create a raw netlink socket and bind
68  */
69 static int create_nl_socket(int protocol)
70 {
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                 }
85
86         memset(&local, 0, sizeof(local));
87         local.nl_family = AF_NETLINK;
88
89         if (bind(fd, (struct sockaddr *) &local, sizeof(local)) < 0)
90                 goto error;
91
92         return fd;
93 error:
94         close(fd);
95         return -1;
96 }
97
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)
102 {
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;
136 }
137
138
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 {
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;
164
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;
171 }
172
173 void print_delayacct(struct taskstats *t)
174 {
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);
187 }
188
189 int main(int argc, char *argv[])
190 {
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;
214
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);
261                 }
262         }
263
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         }
272
273         if ((nl_sd = create_nl_socket(NETLINK_GENERIC)) < 0)
274                 err(1, "error creating Netlink socket\n");
275
276
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;
282         }
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                 }
294         }
295
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                 }
304         }
305
306         do {
307                 int i;
308
309                 rep_len = recv(nl_sd, &msg, sizeof(msg), 0);
310                 PRINTF("received %d bytes\n", rep_len);
311
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;
378                         }
379                         na = (struct nlattr *) (GENLMSG_DATA(&msg) + len);
380                 }
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");
390         }
391 err:
392         close(nl_sd);
393         if (fd)
394                 close(fd);
395         return 0;
396 }