cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

procacct.c (10163B)


      1// SPDX-License-Identifier: GPL-2.0
      2/* procacct.c
      3 *
      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.
      9 *
     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.
     13 *
     14 * Written (changed) by Thomas Orgis, University of Hamburg in 2022
     15 *
     16 * This is a cheap derivation (inheriting the style) of getdelays.c:
     17 *
     18 * Utility to get per-pid and per-tgid delay accounting statistics
     19 * Also illustrates usage of the taskstats interface
     20 *
     21 * Copyright (C) Shailabh Nagar, IBM Corp. 2005
     22 * Copyright (C) Balbir Singh, IBM Corp. 2006
     23 * Copyright (c) Jay Lan, SGI. 2006
     24 */
     25
     26#include <stdio.h>
     27#include <stdlib.h>
     28#include <errno.h>
     29#include <unistd.h>
     30#include <poll.h>
     31#include <string.h>
     32#include <fcntl.h>
     33#include <sys/types.h>
     34#include <sys/stat.h>
     35#include <sys/socket.h>
     36#include <sys/wait.h>
     37#include <signal.h>
     38
     39#include <linux/genetlink.h>
     40#include <linux/acct.h>
     41#include <linux/taskstats.h>
     42#include <linux/kdev_t.h>
     43
     44/*
     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
     48 */
     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)
     53
     54#define err(code, fmt, arg...)			\
     55	do {					\
     56		fprintf(stderr, fmt, ##arg);	\
     57		exit(code);			\
     58	} while (0)
     59
     60int rcvbufsz;
     61char name[100];
     62int dbg;
     63int print_delays;
     64int print_io_accounting;
     65int print_task_context_switch_counts;
     66
     67#define PRINTF(fmt, arg...) {			\
     68		if (dbg) {			\
     69			printf(fmt, ##arg);	\
     70		}				\
     71	}
     72
     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 */
     76#define MAX_CPUS	32
     77
     78struct msgtemplate {
     79	struct nlmsghdr n;
     80	struct genlmsghdr g;
     81	char buf[MAX_MSG_SIZE];
     82};
     83
     84char cpumask[100+6*MAX_CPUS];
     85
     86static void usage(void)
     87{
     88	fprintf(stderr, "procacct [-v] [-w logfile] [-r bufsize] [-m cpumask]\n");
     89	fprintf(stderr, "  -v: debug on\n");
     90}
     91
     92/*
     93 * Create a raw netlink socket and bind
     94 */
     95static int create_nl_socket(int protocol)
     96{
     97	int fd;
     98	struct sockaddr_nl local;
     99
    100	fd = socket(AF_NETLINK, SOCK_RAW, protocol);
    101	if (fd < 0)
    102		return -1;
    103
    104	if (rcvbufsz)
    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",
    108				rcvbufsz);
    109			goto error;
    110		}
    111
    112	memset(&local, 0, sizeof(local));
    113	local.nl_family = AF_NETLINK;
    114
    115	if (bind(fd, (struct sockaddr *) &local, sizeof(local)) < 0)
    116		goto error;
    117
    118	return fd;
    119error:
    120	close(fd);
    121	return -1;
    122}
    123
    124
    125static 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)
    128{
    129	struct nlattr *na;
    130	struct sockaddr_nl nladdr;
    131	int r, buflen;
    132	char *buf;
    133
    134	struct msgtemplate msg;
    135
    136	msg.n.nlmsg_len = NLMSG_LENGTH(GENL_HDRLEN);
    137	msg.n.nlmsg_type = nlmsg_type;
    138	msg.n.nlmsg_flags = NLM_F_REQUEST;
    139	msg.n.nlmsg_seq = 0;
    140	msg.n.nlmsg_pid = nlmsg_pid;
    141	msg.g.cmd = genl_cmd;
    142	msg.g.version = 0x1;
    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);
    148
    149	buf = (char *) &msg;
    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) {
    155		if (r > 0) {
    156			buf += r;
    157			buflen -= r;
    158		} else if (errno != EAGAIN)
    159			return -1;
    160	}
    161	return 0;
    162}
    163
    164
    165/*
    166 * Probe the controller in genetlink to find the family id
    167 * for the TASKSTATS family
    168 */
    169static int get_family_id(int sd)
    170{
    171	struct {
    172		struct nlmsghdr n;
    173		struct genlmsghdr g;
    174		char buf[256];
    175	} ans;
    176
    177	int id = 0, rc;
    178	struct nlattr *na;
    179	int rep_len;
    180
    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);
    185	if (rc < 0)
    186		return 0;	/* sendto() failure? */
    187
    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))
    191		return 0;
    192
    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);
    197
    198	return id;
    199}
    200
    201#define average_ms(t, c) (t / 1000000ULL / (c ? c : 1))
    202
    203static void print_procacct(struct taskstats *t)
    204{
    205	/* First letter: T is a mere thread, G the last in a group, U  unknown. */
    206	printf(
    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)
    220	,	t->ac_comm
    221	);
    222}
    223
    224void handle_aggr(int mother, struct nlattr *na, int fd)
    225{
    226	int aggr_len = NLA_PAYLOAD(na->nla_len);
    227	int len2 = 0;
    228	pid_t rtid = 0;
    229
    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);
    236			break;
    237		case TASKSTATS_TYPE_TGID:
    238			rtid = *(int *) NLA_DATA(na);
    239			PRINTF("TGID\t%d\n", rtid);
    240			break;
    241		case TASKSTATS_TYPE_STATS:
    242			if (mother == TASKSTATS_TYPE_AGGR_PID)
    243				print_procacct((struct taskstats *) NLA_DATA(na));
    244			if (fd) {
    245				if (write(fd, NLA_DATA(na), na->nla_len) < 0)
    246					err(1, "write error\n");
    247			}
    248			break;
    249		case TASKSTATS_TYPE_NULL:
    250			break;
    251		default:
    252			fprintf(stderr, "Unknown nested nla_type %d\n",
    253				na->nla_type);
    254			break;
    255		}
    256		len2 += NLA_ALIGN(na->nla_len);
    257		na = (struct nlattr *)((char *)na +
    258						 NLA_ALIGN(na->nla_len));
    259	}
    260}
    261
    262int main(int argc, char *argv[])
    263{
    264	int c, rc, rep_len, aggr_len, len2;
    265	int cmd_type = TASKSTATS_CMD_ATTR_UNSPEC;
    266	__u16 id;
    267	__u32 mypid;
    268
    269	struct nlattr *na;
    270	int nl_sd = -1;
    271	int len = 0;
    272	pid_t tid = 0;
    273
    274	int fd = 0;
    275	int write_file = 0;
    276	int maskset = 0;
    277	char *logfile = NULL;
    278	int containerset = 0;
    279	char *containerpath = NULL;
    280	int cfd = 0;
    281	int forking = 0;
    282	sigset_t sigset;
    283
    284	struct msgtemplate msg;
    285
    286	while (!forking) {
    287		c = getopt(argc, argv, "m:vr:");
    288		if (c < 0)
    289			break;
    290
    291		switch (c) {
    292		case 'w':
    293			logfile = strdup(optarg);
    294			printf("write to file %s\n", logfile);
    295			write_file = 1;
    296			break;
    297		case 'r':
    298			rcvbufsz = atoi(optarg);
    299			printf("receive buf size %d\n", rcvbufsz);
    300			if (rcvbufsz < 0)
    301				err(1, "Invalid rcv buf size\n");
    302			break;
    303		case 'm':
    304			strncpy(cpumask, optarg, sizeof(cpumask));
    305			cpumask[sizeof(cpumask) - 1] = '\0';
    306			maskset = 1;
    307			break;
    308		case 'v':
    309			printf("debug on\n");
    310			dbg = 1;
    311			break;
    312		default:
    313			usage();
    314			exit(-1);
    315		}
    316	}
    317	if (!maskset) {
    318		maskset = 1;
    319		strncpy(cpumask, "1", sizeof(cpumask));
    320		cpumask[sizeof(cpumask) - 1] = '\0';
    321	}
    322	printf("cpumask %s maskset %d\n", cpumask, maskset);
    323
    324	if (write_file) {
    325		fd = open(logfile, O_WRONLY | O_CREAT | O_TRUNC, 0644);
    326		if (fd == -1) {
    327			perror("Cannot open output file\n");
    328			exit(1);
    329		}
    330	}
    331
    332	nl_sd = create_nl_socket(NETLINK_GENERIC);
    333	if (nl_sd < 0)
    334		err(1, "error creating Netlink socket\n");
    335
    336	mypid = getpid();
    337	id = get_family_id(nl_sd);
    338	if (!id) {
    339		fprintf(stderr, "Error getting family id, errno %d\n", errno);
    340		goto err;
    341	}
    342	PRINTF("family id %d\n", id);
    343
    344	if (maskset) {
    345		rc = send_cmd(nl_sd, id, mypid, TASKSTATS_CMD_GET,
    346			      TASKSTATS_CMD_ATTR_REGISTER_CPUMASK,
    347			      &cpumask, strlen(cpumask) + 1);
    348		PRINTF("Sent register cpumask, retval %d\n", rc);
    349		if (rc < 0) {
    350			fprintf(stderr, "error sending register cpumask\n");
    351			goto err;
    352		}
    353	}
    354
    355	do {
    356		rep_len = recv(nl_sd, &msg, sizeof(msg), 0);
    357		PRINTF("received %d bytes\n", rep_len);
    358
    359		if (rep_len < 0) {
    360			fprintf(stderr, "nonfatal reply error: errno %d\n",
    361				errno);
    362			continue;
    363		}
    364		if (msg.n.nlmsg_type == NLMSG_ERROR ||
    365		    !NLMSG_OK((&msg.n), rep_len)) {
    366			struct nlmsgerr *err = NLMSG_DATA(&msg);
    367
    368			fprintf(stderr, "fatal reply error,  errno %d\n",
    369				err->error);
    370			goto done;
    371		}
    372
    373		PRINTF("nlmsghdr size=%zu, nlmsg_len=%d, rep_len=%d\n",
    374		       sizeof(struct nlmsghdr), msg.n.nlmsg_len, rep_len);
    375
    376
    377		rep_len = GENLMSG_PAYLOAD(&msg.n);
    378
    379		na = (struct nlattr *) GENLMSG_DATA(&msg);
    380		len = 0;
    381		while (len < rep_len) {
    382			len += NLA_ALIGN(na->nla_len);
    383			int mother = na->nla_type;
    384
    385			PRINTF("mother=%i\n", mother);
    386			switch (na->nla_type) {
    387			case TASKSTATS_TYPE_AGGR_PID:
    388			case TASKSTATS_TYPE_AGGR_TGID:
    389				/* For nested attributes, na follows */
    390				handle_aggr(mother, na, fd);
    391				break;
    392			default:
    393				fprintf(stderr, "Unexpected nla_type %d\n",
    394					na->nla_type);
    395			case TASKSTATS_TYPE_NULL:
    396				break;
    397			}
    398			na = (struct nlattr *) (GENLMSG_DATA(&msg) + len);
    399		}
    400	} while (1);
    401done:
    402	if (maskset) {
    403		rc = send_cmd(nl_sd, id, mypid, TASKSTATS_CMD_GET,
    404			      TASKSTATS_CMD_ATTR_DEREGISTER_CPUMASK,
    405			      &cpumask, strlen(cpumask) + 1);
    406		printf("Sent deregister mask, retval %d\n", rc);
    407		if (rc < 0)
    408			err(rc, "error sending deregister cpumask\n");
    409	}
    410err:
    411	close(nl_sd);
    412	if (fd)
    413		close(fd);
    414	if (cfd)
    415		close(cfd);
    416	return 0;
    417}