1 // SPDX-License-Identifier: GPL-2.0
4 * Demonstrator of fetching resource data on task exit, as a way
5 * to accumulate accurate program resource usage statistics, without
6 * prior identification of the programs. For that, the fields for
7 * device and inode of the program executable binary file are also
8 * extracted in addition to the command string.
10 * The TGID together with the PID and the AGROUP flag allow
11 * identification of threads in a process and single-threaded processes.
12 * The ac_tgetime field gives proper whole-process walltime.
14 * Written (changed) by Thomas Orgis, University of Hamburg in 2022
16 * This is a cheap derivation (inheriting the style) of getdelays.c:
18 * Utility to get per-pid and per-tgid delay accounting statistics
19 * Also illustrates usage of the taskstats interface
21 * Copyright (C) Shailabh Nagar, IBM Corp. 2005
22 * Copyright (C) Balbir Singh, IBM Corp. 2006
23 * Copyright (c) Jay Lan, SGI. 2006
33 #include <sys/types.h>
35 #include <sys/socket.h>
39 #include <linux/genetlink.h>
40 #include <linux/acct.h>
41 #include <linux/taskstats.h>
42 #include <linux/kdev_t.h>
45 * Generic macros for dealing with netlink sockets. Might be duplicated
46 * elsewhere. It is recommended that commercial grade applications use
47 * libnl or libnetlink and use the interfaces provided by the library
49 #define GENLMSG_DATA(glh) ((void *)(NLMSG_DATA(glh) + GENL_HDRLEN))
50 #define GENLMSG_PAYLOAD(glh) (NLMSG_PAYLOAD(glh, 0) - GENL_HDRLEN)
51 #define NLA_DATA(na) ((void *)((char *)(na) + NLA_HDRLEN))
52 #define NLA_PAYLOAD(len) (len - NLA_HDRLEN)
54 #define err(code, fmt, arg...) \
56 fprintf(stderr, fmt, ##arg); \
64 int print_io_accounting
;
65 int print_task_context_switch_counts
;
67 #define PRINTF(fmt, arg...) { \
73 /* Maximum size of response requested or message sent */
74 #define MAX_MSG_SIZE 1024
75 /* Maximum number of cpus expected to be specified in a cpumask */
81 char buf
[MAX_MSG_SIZE
];
84 char cpumask
[100+6*MAX_CPUS
];
86 static void usage(void)
88 fprintf(stderr
, "procacct [-v] [-w logfile] [-r bufsize] [-m cpumask]\n");
89 fprintf(stderr
, " -v: debug on\n");
93 * Create a raw netlink socket and bind
95 static int create_nl_socket(int protocol
)
98 struct sockaddr_nl local
;
100 fd
= socket(AF_NETLINK
, SOCK_RAW
, protocol
);
105 if (setsockopt(fd
, SOL_SOCKET
, SO_RCVBUF
,
106 &rcvbufsz
, sizeof(rcvbufsz
)) < 0) {
107 fprintf(stderr
, "Unable to set socket rcv buf size to %d\n",
112 memset(&local
, 0, sizeof(local
));
113 local
.nl_family
= AF_NETLINK
;
115 if (bind(fd
, (struct sockaddr
*) &local
, sizeof(local
)) < 0)
125 static int send_cmd(int sd
, __u16 nlmsg_type
, __u32 nlmsg_pid
,
126 __u8 genl_cmd
, __u16 nla_type
,
127 void *nla_data
, int nla_len
)
130 struct sockaddr_nl nladdr
;
134 struct msgtemplate msg
;
136 msg
.n
.nlmsg_len
= NLMSG_LENGTH(GENL_HDRLEN
);
137 msg
.n
.nlmsg_type
= nlmsg_type
;
138 msg
.n
.nlmsg_flags
= NLM_F_REQUEST
;
140 msg
.n
.nlmsg_pid
= nlmsg_pid
;
141 msg
.g
.cmd
= genl_cmd
;
143 na
= (struct nlattr
*) GENLMSG_DATA(&msg
);
144 na
->nla_type
= nla_type
;
145 na
->nla_len
= nla_len
+ 1 + NLA_HDRLEN
;
146 memcpy(NLA_DATA(na
), nla_data
, nla_len
);
147 msg
.n
.nlmsg_len
+= NLMSG_ALIGN(na
->nla_len
);
150 buflen
= msg
.n
.nlmsg_len
;
151 memset(&nladdr
, 0, sizeof(nladdr
));
152 nladdr
.nl_family
= AF_NETLINK
;
153 while ((r
= sendto(sd
, buf
, buflen
, 0, (struct sockaddr
*) &nladdr
,
154 sizeof(nladdr
))) < buflen
) {
158 } else if (errno
!= EAGAIN
)
166 * Probe the controller in genetlink to find the family id
167 * for the TASKSTATS family
169 static int get_family_id(int sd
)
181 strcpy(name
, TASKSTATS_GENL_NAME
);
182 rc
= send_cmd(sd
, GENL_ID_CTRL
, getpid(), CTRL_CMD_GETFAMILY
,
183 CTRL_ATTR_FAMILY_NAME
, (void *)name
,
184 strlen(TASKSTATS_GENL_NAME
)+1);
186 return 0; /* sendto() failure? */
188 rep_len
= recv(sd
, &ans
, sizeof(ans
), 0);
189 if (ans
.n
.nlmsg_type
== NLMSG_ERROR
||
190 (rep_len
< 0) || !NLMSG_OK((&ans
.n
), rep_len
))
193 na
= (struct nlattr
*) GENLMSG_DATA(&ans
);
194 na
= (struct nlattr
*) ((char *) na
+ NLA_ALIGN(na
->nla_len
));
195 if (na
->nla_type
== CTRL_ATTR_FAMILY_ID
)
196 id
= *(__u16
*) NLA_DATA(na
);
201 #define average_ms(t, c) (t / 1000000ULL / (c ? c : 1))
203 static void print_procacct(struct taskstats
*t
)
205 /* First letter: T is a mere thread, G the last in a group, U unknown. */
207 "%c pid=%lu tgid=%lu uid=%lu wall=%llu gwall=%llu cpu=%llu vmpeak=%llu rsspeak=%llu dev=%lu:%lu inode=%llu comm=%s\n"
208 , t
->version
>= 12 ? (t
->ac_flag
& AGROUP
? 'P' : 'T') : '?'
209 , (unsigned long)t
->ac_pid
210 , (unsigned long)(t
->version
>= 12 ? t
->ac_tgid
: 0)
211 , (unsigned long)t
->ac_uid
212 , (unsigned long long)t
->ac_etime
213 , (unsigned long long)(t
->version
>= 12 ? t
->ac_tgetime
: 0)
214 , (unsigned long long)(t
->ac_utime
+t
->ac_stime
)
215 , (unsigned long long)t
->hiwater_vm
216 , (unsigned long long)t
->hiwater_rss
217 , (unsigned long)(t
->version
>= 12 ? MAJOR(t
->ac_exe_dev
) : 0)
218 , (unsigned long)(t
->version
>= 12 ? MINOR(t
->ac_exe_dev
) : 0)
219 , (unsigned long long)(t
->version
>= 12 ? t
->ac_exe_inode
: 0)
224 void handle_aggr(int mother
, struct nlattr
*na
, int fd
)
226 int aggr_len
= NLA_PAYLOAD(na
->nla_len
);
230 na
= (struct nlattr
*) NLA_DATA(na
);
231 while (len2
< aggr_len
) {
232 switch (na
->nla_type
) {
233 case TASKSTATS_TYPE_PID
:
234 rtid
= *(int *) NLA_DATA(na
);
235 PRINTF("PID\t%d\n", rtid
);
237 case TASKSTATS_TYPE_TGID
:
238 rtid
= *(int *) NLA_DATA(na
);
239 PRINTF("TGID\t%d\n", rtid
);
241 case TASKSTATS_TYPE_STATS
:
242 if (mother
== TASKSTATS_TYPE_AGGR_PID
)
243 print_procacct((struct taskstats
*) NLA_DATA(na
));
245 if (write(fd
, NLA_DATA(na
), na
->nla_len
) < 0)
246 err(1, "write error\n");
249 case TASKSTATS_TYPE_NULL
:
252 fprintf(stderr
, "Unknown nested nla_type %d\n",
256 len2
+= NLA_ALIGN(na
->nla_len
);
257 na
= (struct nlattr
*)((char *)na
+
258 NLA_ALIGN(na
->nla_len
));
262 int main(int argc
, char *argv
[])
275 char *logfile
= NULL
;
279 struct msgtemplate msg
;
282 c
= getopt(argc
, argv
, "m:vr:");
288 logfile
= strdup(optarg
);
289 printf("write to file %s\n", logfile
);
293 rcvbufsz
= atoi(optarg
);
294 printf("receive buf size %d\n", rcvbufsz
);
296 err(1, "Invalid rcv buf size\n");
299 strncpy(cpumask
, optarg
, sizeof(cpumask
));
300 cpumask
[sizeof(cpumask
) - 1] = '\0';
304 printf("debug on\n");
314 strncpy(cpumask
, "1", sizeof(cpumask
));
315 cpumask
[sizeof(cpumask
) - 1] = '\0';
317 printf("cpumask %s maskset %d\n", cpumask
, maskset
);
320 fd
= open(logfile
, O_WRONLY
| O_CREAT
| O_TRUNC
, 0644);
322 perror("Cannot open output file\n");
327 nl_sd
= create_nl_socket(NETLINK_GENERIC
);
329 err(1, "error creating Netlink socket\n");
332 id
= get_family_id(nl_sd
);
334 fprintf(stderr
, "Error getting family id, errno %d\n", errno
);
337 PRINTF("family id %d\n", id
);
340 rc
= send_cmd(nl_sd
, id
, mypid
, TASKSTATS_CMD_GET
,
341 TASKSTATS_CMD_ATTR_REGISTER_CPUMASK
,
342 &cpumask
, strlen(cpumask
) + 1);
343 PRINTF("Sent register cpumask, retval %d\n", rc
);
345 fprintf(stderr
, "error sending register cpumask\n");
351 rep_len
= recv(nl_sd
, &msg
, sizeof(msg
), 0);
352 PRINTF("received %d bytes\n", rep_len
);
355 fprintf(stderr
, "nonfatal reply error: errno %d\n",
359 if (msg
.n
.nlmsg_type
== NLMSG_ERROR
||
360 !NLMSG_OK((&msg
.n
), rep_len
)) {
361 struct nlmsgerr
*err
= NLMSG_DATA(&msg
);
363 fprintf(stderr
, "fatal reply error, errno %d\n",
368 PRINTF("nlmsghdr size=%zu, nlmsg_len=%d, rep_len=%d\n",
369 sizeof(struct nlmsghdr
), msg
.n
.nlmsg_len
, rep_len
);
372 rep_len
= GENLMSG_PAYLOAD(&msg
.n
);
374 na
= (struct nlattr
*) GENLMSG_DATA(&msg
);
376 while (len
< rep_len
) {
377 len
+= NLA_ALIGN(na
->nla_len
);
378 int mother
= na
->nla_type
;
380 PRINTF("mother=%i\n", mother
);
381 switch (na
->nla_type
) {
382 case TASKSTATS_TYPE_AGGR_PID
:
383 case TASKSTATS_TYPE_AGGR_TGID
:
384 /* For nested attributes, na follows */
385 handle_aggr(mother
, na
, fd
);
388 fprintf(stderr
, "Unexpected nla_type %d\n",
390 case TASKSTATS_TYPE_NULL
:
393 na
= (struct nlattr
*) (GENLMSG_DATA(&msg
) + len
);
398 rc
= send_cmd(nl_sd
, id
, mypid
, TASKSTATS_CMD_GET
,
399 TASKSTATS_CMD_ATTR_DEREGISTER_CPUMASK
,
400 &cpumask
, strlen(cpumask
) + 1);
401 printf("Sent deregister mask, retval %d\n", rc
);
403 err(rc
, "error sending deregister cpumask\n");