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

getdelays.c (14516B)


      1// SPDX-License-Identifier: GPL-2.0
      2/* getdelays.c
      3 *
      4 * Utility to get per-pid and per-tgid delay accounting statistics
      5 * Also illustrates usage of the taskstats interface
      6 *
      7 * Copyright (C) Shailabh Nagar, IBM Corp. 2005
      8 * Copyright (C) Balbir Singh, IBM Corp. 2006
      9 * Copyright (c) Jay Lan, SGI. 2006
     10 *
     11 * Compile with
     12 *	gcc -I/usr/src/linux/include getdelays.c -o getdelays
     13 */
     14
     15#include <stdio.h>
     16#include <stdlib.h>
     17#include <errno.h>
     18#include <unistd.h>
     19#include <poll.h>
     20#include <string.h>
     21#include <fcntl.h>
     22#include <sys/types.h>
     23#include <sys/stat.h>
     24#include <sys/socket.h>
     25#include <sys/wait.h>
     26#include <signal.h>
     27
     28#include <linux/genetlink.h>
     29#include <linux/taskstats.h>
     30#include <linux/cgroupstats.h>
     31
     32/*
     33 * Generic macros for dealing with netlink sockets. Might be duplicated
     34 * elsewhere. It is recommended that commercial grade applications use
     35 * libnl or libnetlink and use the interfaces provided by the library
     36 */
     37#define GENLMSG_DATA(glh)	((void *)(NLMSG_DATA(glh) + GENL_HDRLEN))
     38#define GENLMSG_PAYLOAD(glh)	(NLMSG_PAYLOAD(glh, 0) - GENL_HDRLEN)
     39#define NLA_DATA(na)		((void *)((char*)(na) + NLA_HDRLEN))
     40#define NLA_PAYLOAD(len)	(len - NLA_HDRLEN)
     41
     42#define err(code, fmt, arg...)			\
     43	do {					\
     44		fprintf(stderr, fmt, ##arg);	\
     45		exit(code);			\
     46	} while (0)
     47
     48int done;
     49int rcvbufsz;
     50char name[100];
     51int dbg;
     52int print_delays;
     53int print_io_accounting;
     54int print_task_context_switch_counts;
     55
     56#define PRINTF(fmt, arg...) {			\
     57	    if (dbg) {				\
     58		printf(fmt, ##arg);		\
     59	    }					\
     60	}
     61
     62/* Maximum size of response requested or message sent */
     63#define MAX_MSG_SIZE	1024
     64/* Maximum number of cpus expected to be specified in a cpumask */
     65#define MAX_CPUS	32
     66
     67struct msgtemplate {
     68	struct nlmsghdr n;
     69	struct genlmsghdr g;
     70	char buf[MAX_MSG_SIZE];
     71};
     72
     73char cpumask[100+6*MAX_CPUS];
     74
     75static void usage(void)
     76{
     77	fprintf(stderr, "getdelays [-dilv] [-w logfile] [-r bufsize] "
     78			"[-m cpumask] [-t tgid] [-p pid]\n");
     79	fprintf(stderr, "  -d: print delayacct stats\n");
     80	fprintf(stderr, "  -i: print IO accounting (works only with -p)\n");
     81	fprintf(stderr, "  -l: listen forever\n");
     82	fprintf(stderr, "  -v: debug on\n");
     83	fprintf(stderr, "  -C: container path\n");
     84}
     85
     86/*
     87 * Create a raw netlink socket and bind
     88 */
     89static int create_nl_socket(int protocol)
     90{
     91	int fd;
     92	struct sockaddr_nl local;
     93
     94	fd = socket(AF_NETLINK, SOCK_RAW, protocol);
     95	if (fd < 0)
     96		return -1;
     97
     98	if (rcvbufsz)
     99		if (setsockopt(fd, SOL_SOCKET, SO_RCVBUF,
    100				&rcvbufsz, sizeof(rcvbufsz)) < 0) {
    101			fprintf(stderr, "Unable to set socket rcv buf size to %d\n",
    102				rcvbufsz);
    103			goto error;
    104		}
    105
    106	memset(&local, 0, sizeof(local));
    107	local.nl_family = AF_NETLINK;
    108
    109	if (bind(fd, (struct sockaddr *) &local, sizeof(local)) < 0)
    110		goto error;
    111
    112	return fd;
    113error:
    114	close(fd);
    115	return -1;
    116}
    117
    118
    119static int send_cmd(int sd, __u16 nlmsg_type, __u32 nlmsg_pid,
    120	     __u8 genl_cmd, __u16 nla_type,
    121	     void *nla_data, int nla_len)
    122{
    123	struct nlattr *na;
    124	struct sockaddr_nl nladdr;
    125	int r, buflen;
    126	char *buf;
    127
    128	struct msgtemplate msg;
    129
    130	msg.n.nlmsg_len = NLMSG_LENGTH(GENL_HDRLEN);
    131	msg.n.nlmsg_type = nlmsg_type;
    132	msg.n.nlmsg_flags = NLM_F_REQUEST;
    133	msg.n.nlmsg_seq = 0;
    134	msg.n.nlmsg_pid = nlmsg_pid;
    135	msg.g.cmd = genl_cmd;
    136	msg.g.version = 0x1;
    137	na = (struct nlattr *) GENLMSG_DATA(&msg);
    138	na->nla_type = nla_type;
    139	na->nla_len = nla_len + NLA_HDRLEN;
    140	memcpy(NLA_DATA(na), nla_data, nla_len);
    141	msg.n.nlmsg_len += NLMSG_ALIGN(na->nla_len);
    142
    143	buf = (char *) &msg;
    144	buflen = msg.n.nlmsg_len ;
    145	memset(&nladdr, 0, sizeof(nladdr));
    146	nladdr.nl_family = AF_NETLINK;
    147	while ((r = sendto(sd, buf, buflen, 0, (struct sockaddr *) &nladdr,
    148			   sizeof(nladdr))) < buflen) {
    149		if (r > 0) {
    150			buf += r;
    151			buflen -= r;
    152		} else if (errno != EAGAIN)
    153			return -1;
    154	}
    155	return 0;
    156}
    157
    158
    159/*
    160 * Probe the controller in genetlink to find the family id
    161 * for the TASKSTATS family
    162 */
    163static int get_family_id(int sd)
    164{
    165	struct {
    166		struct nlmsghdr n;
    167		struct genlmsghdr g;
    168		char buf[256];
    169	} ans;
    170
    171	int id = 0, rc;
    172	struct nlattr *na;
    173	int rep_len;
    174
    175	strcpy(name, TASKSTATS_GENL_NAME);
    176	rc = send_cmd(sd, GENL_ID_CTRL, getpid(), CTRL_CMD_GETFAMILY,
    177			CTRL_ATTR_FAMILY_NAME, (void *)name,
    178			strlen(TASKSTATS_GENL_NAME)+1);
    179	if (rc < 0)
    180		return 0;	/* sendto() failure? */
    181
    182	rep_len = recv(sd, &ans, sizeof(ans), 0);
    183	if (ans.n.nlmsg_type == NLMSG_ERROR ||
    184	    (rep_len < 0) || !NLMSG_OK((&ans.n), rep_len))
    185		return 0;
    186
    187	na = (struct nlattr *) GENLMSG_DATA(&ans);
    188	na = (struct nlattr *) ((char *) na + NLA_ALIGN(na->nla_len));
    189	if (na->nla_type == CTRL_ATTR_FAMILY_ID) {
    190		id = *(__u16 *) NLA_DATA(na);
    191	}
    192	return id;
    193}
    194
    195#define average_ms(t, c) (t / 1000000ULL / (c ? c : 1))
    196
    197static void print_delayacct(struct taskstats *t)
    198{
    199	printf("\n\nCPU   %15s%15s%15s%15s%15s\n"
    200	       "      %15llu%15llu%15llu%15llu%15.3fms\n"
    201	       "IO    %15s%15s%15s\n"
    202	       "      %15llu%15llu%15llums\n"
    203	       "SWAP  %15s%15s%15s\n"
    204	       "      %15llu%15llu%15llums\n"
    205	       "RECLAIM  %12s%15s%15s\n"
    206	       "      %15llu%15llu%15llums\n"
    207	       "THRASHING%12s%15s%15s\n"
    208	       "      %15llu%15llu%15llums\n"
    209	       "COMPACT  %12s%15s%15s\n"
    210	       "      %15llu%15llu%15llums\n"
    211	       "WPCOPY   %12s%15s%15s\n"
    212	       "      %15llu%15llu%15llums\n",
    213	       "count", "real total", "virtual total",
    214	       "delay total", "delay average",
    215	       (unsigned long long)t->cpu_count,
    216	       (unsigned long long)t->cpu_run_real_total,
    217	       (unsigned long long)t->cpu_run_virtual_total,
    218	       (unsigned long long)t->cpu_delay_total,
    219	       average_ms((double)t->cpu_delay_total, t->cpu_count),
    220	       "count", "delay total", "delay average",
    221	       (unsigned long long)t->blkio_count,
    222	       (unsigned long long)t->blkio_delay_total,
    223	       average_ms(t->blkio_delay_total, t->blkio_count),
    224	       "count", "delay total", "delay average",
    225	       (unsigned long long)t->swapin_count,
    226	       (unsigned long long)t->swapin_delay_total,
    227	       average_ms(t->swapin_delay_total, t->swapin_count),
    228	       "count", "delay total", "delay average",
    229	       (unsigned long long)t->freepages_count,
    230	       (unsigned long long)t->freepages_delay_total,
    231	       average_ms(t->freepages_delay_total, t->freepages_count),
    232	       "count", "delay total", "delay average",
    233	       (unsigned long long)t->thrashing_count,
    234	       (unsigned long long)t->thrashing_delay_total,
    235	       average_ms(t->thrashing_delay_total, t->thrashing_count),
    236	       "count", "delay total", "delay average",
    237	       (unsigned long long)t->compact_count,
    238	       (unsigned long long)t->compact_delay_total,
    239	       average_ms(t->compact_delay_total, t->compact_count),
    240	       "count", "delay total", "delay average",
    241	       (unsigned long long)t->wpcopy_count,
    242	       (unsigned long long)t->wpcopy_delay_total,
    243	       average_ms(t->wpcopy_delay_total, t->wpcopy_count));
    244}
    245
    246static void task_context_switch_counts(struct taskstats *t)
    247{
    248	printf("\n\nTask   %15s%15s\n"
    249	       "       %15llu%15llu\n",
    250	       "voluntary", "nonvoluntary",
    251	       (unsigned long long)t->nvcsw, (unsigned long long)t->nivcsw);
    252}
    253
    254static void print_cgroupstats(struct cgroupstats *c)
    255{
    256	printf("sleeping %llu, blocked %llu, running %llu, stopped %llu, "
    257		"uninterruptible %llu\n", (unsigned long long)c->nr_sleeping,
    258		(unsigned long long)c->nr_io_wait,
    259		(unsigned long long)c->nr_running,
    260		(unsigned long long)c->nr_stopped,
    261		(unsigned long long)c->nr_uninterruptible);
    262}
    263
    264
    265static void print_ioacct(struct taskstats *t)
    266{
    267	printf("%s: read=%llu, write=%llu, cancelled_write=%llu\n",
    268		t->ac_comm,
    269		(unsigned long long)t->read_bytes,
    270		(unsigned long long)t->write_bytes,
    271		(unsigned long long)t->cancelled_write_bytes);
    272}
    273
    274int main(int argc, char *argv[])
    275{
    276	int c, rc, rep_len, aggr_len, len2;
    277	int cmd_type = TASKSTATS_CMD_ATTR_UNSPEC;
    278	__u16 id;
    279	__u32 mypid;
    280
    281	struct nlattr *na;
    282	int nl_sd = -1;
    283	int len = 0;
    284	pid_t tid = 0;
    285	pid_t rtid = 0;
    286
    287	int fd = 0;
    288	int count = 0;
    289	int write_file = 0;
    290	int maskset = 0;
    291	char *logfile = NULL;
    292	int loop = 0;
    293	int containerset = 0;
    294	char *containerpath = NULL;
    295	int cfd = 0;
    296	int forking = 0;
    297	sigset_t sigset;
    298
    299	struct msgtemplate msg;
    300
    301	while (!forking) {
    302		c = getopt(argc, argv, "qdiw:r:m:t:p:vlC:c:");
    303		if (c < 0)
    304			break;
    305
    306		switch (c) {
    307		case 'd':
    308			printf("print delayacct stats ON\n");
    309			print_delays = 1;
    310			break;
    311		case 'i':
    312			printf("printing IO accounting\n");
    313			print_io_accounting = 1;
    314			break;
    315		case 'q':
    316			printf("printing task/process context switch rates\n");
    317			print_task_context_switch_counts = 1;
    318			break;
    319		case 'C':
    320			containerset = 1;
    321			containerpath = optarg;
    322			break;
    323		case 'w':
    324			logfile = strdup(optarg);
    325			printf("write to file %s\n", logfile);
    326			write_file = 1;
    327			break;
    328		case 'r':
    329			rcvbufsz = atoi(optarg);
    330			printf("receive buf size %d\n", rcvbufsz);
    331			if (rcvbufsz < 0)
    332				err(1, "Invalid rcv buf size\n");
    333			break;
    334		case 'm':
    335			strncpy(cpumask, optarg, sizeof(cpumask));
    336			cpumask[sizeof(cpumask) - 1] = '\0';
    337			maskset = 1;
    338			printf("cpumask %s maskset %d\n", cpumask, maskset);
    339			break;
    340		case 't':
    341			tid = atoi(optarg);
    342			if (!tid)
    343				err(1, "Invalid tgid\n");
    344			cmd_type = TASKSTATS_CMD_ATTR_TGID;
    345			break;
    346		case 'p':
    347			tid = atoi(optarg);
    348			if (!tid)
    349				err(1, "Invalid pid\n");
    350			cmd_type = TASKSTATS_CMD_ATTR_PID;
    351			break;
    352		case 'c':
    353
    354			/* Block SIGCHLD for sigwait() later */
    355			if (sigemptyset(&sigset) == -1)
    356				err(1, "Failed to empty sigset");
    357			if (sigaddset(&sigset, SIGCHLD))
    358				err(1, "Failed to set sigchld in sigset");
    359			sigprocmask(SIG_BLOCK, &sigset, NULL);
    360
    361			/* fork/exec a child */
    362			tid = fork();
    363			if (tid < 0)
    364				err(1, "Fork failed\n");
    365			if (tid == 0)
    366				if (execvp(argv[optind - 1],
    367				    &argv[optind - 1]) < 0)
    368					exit(-1);
    369
    370			/* Set the command type and avoid further processing */
    371			cmd_type = TASKSTATS_CMD_ATTR_PID;
    372			forking = 1;
    373			break;
    374		case 'v':
    375			printf("debug on\n");
    376			dbg = 1;
    377			break;
    378		case 'l':
    379			printf("listen forever\n");
    380			loop = 1;
    381			break;
    382		default:
    383			usage();
    384			exit(-1);
    385		}
    386	}
    387
    388	if (write_file) {
    389		fd = open(logfile, O_WRONLY | O_CREAT | O_TRUNC,
    390			  S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
    391		if (fd == -1) {
    392			perror("Cannot open output file\n");
    393			exit(1);
    394		}
    395	}
    396
    397	nl_sd = create_nl_socket(NETLINK_GENERIC);
    398	if (nl_sd < 0)
    399		err(1, "error creating Netlink socket\n");
    400
    401
    402	mypid = getpid();
    403	id = get_family_id(nl_sd);
    404	if (!id) {
    405		fprintf(stderr, "Error getting family id, errno %d\n", errno);
    406		goto err;
    407	}
    408	PRINTF("family id %d\n", id);
    409
    410	if (maskset) {
    411		rc = send_cmd(nl_sd, id, mypid, TASKSTATS_CMD_GET,
    412			      TASKSTATS_CMD_ATTR_REGISTER_CPUMASK,
    413			      &cpumask, strlen(cpumask) + 1);
    414		PRINTF("Sent register cpumask, retval %d\n", rc);
    415		if (rc < 0) {
    416			fprintf(stderr, "error sending register cpumask\n");
    417			goto err;
    418		}
    419	}
    420
    421	if (tid && containerset) {
    422		fprintf(stderr, "Select either -t or -C, not both\n");
    423		goto err;
    424	}
    425
    426	/*
    427	 * If we forked a child, wait for it to exit. Cannot use waitpid()
    428	 * as all the delicious data would be reaped as part of the wait
    429	 */
    430	if (tid && forking) {
    431		int sig_received;
    432		sigwait(&sigset, &sig_received);
    433	}
    434
    435	if (tid) {
    436		rc = send_cmd(nl_sd, id, mypid, TASKSTATS_CMD_GET,
    437			      cmd_type, &tid, sizeof(__u32));
    438		PRINTF("Sent pid/tgid, retval %d\n", rc);
    439		if (rc < 0) {
    440			fprintf(stderr, "error sending tid/tgid cmd\n");
    441			goto done;
    442		}
    443	}
    444
    445	if (containerset) {
    446		cfd = open(containerpath, O_RDONLY);
    447		if (cfd < 0) {
    448			perror("error opening container file");
    449			goto err;
    450		}
    451		rc = send_cmd(nl_sd, id, mypid, CGROUPSTATS_CMD_GET,
    452			      CGROUPSTATS_CMD_ATTR_FD, &cfd, sizeof(__u32));
    453		if (rc < 0) {
    454			perror("error sending cgroupstats command");
    455			goto err;
    456		}
    457	}
    458	if (!maskset && !tid && !containerset) {
    459		usage();
    460		goto err;
    461	}
    462
    463	do {
    464		rep_len = recv(nl_sd, &msg, sizeof(msg), 0);
    465		PRINTF("received %d bytes\n", rep_len);
    466
    467		if (rep_len < 0) {
    468			fprintf(stderr, "nonfatal reply error: errno %d\n",
    469				errno);
    470			continue;
    471		}
    472		if (msg.n.nlmsg_type == NLMSG_ERROR ||
    473		    !NLMSG_OK((&msg.n), rep_len)) {
    474			struct nlmsgerr *err = NLMSG_DATA(&msg);
    475			fprintf(stderr, "fatal reply error,  errno %d\n",
    476				err->error);
    477			goto done;
    478		}
    479
    480		PRINTF("nlmsghdr size=%zu, nlmsg_len=%d, rep_len=%d\n",
    481		       sizeof(struct nlmsghdr), msg.n.nlmsg_len, rep_len);
    482
    483
    484		rep_len = GENLMSG_PAYLOAD(&msg.n);
    485
    486		na = (struct nlattr *) GENLMSG_DATA(&msg);
    487		len = 0;
    488		while (len < rep_len) {
    489			len += NLA_ALIGN(na->nla_len);
    490			switch (na->nla_type) {
    491			case TASKSTATS_TYPE_AGGR_TGID:
    492				/* Fall through */
    493			case TASKSTATS_TYPE_AGGR_PID:
    494				aggr_len = NLA_PAYLOAD(na->nla_len);
    495				len2 = 0;
    496				/* For nested attributes, na follows */
    497				na = (struct nlattr *) NLA_DATA(na);
    498				done = 0;
    499				while (len2 < aggr_len) {
    500					switch (na->nla_type) {
    501					case TASKSTATS_TYPE_PID:
    502						rtid = *(int *) NLA_DATA(na);
    503						if (print_delays)
    504							printf("PID\t%d\n", rtid);
    505						break;
    506					case TASKSTATS_TYPE_TGID:
    507						rtid = *(int *) NLA_DATA(na);
    508						if (print_delays)
    509							printf("TGID\t%d\n", rtid);
    510						break;
    511					case TASKSTATS_TYPE_STATS:
    512						count++;
    513						if (print_delays)
    514							print_delayacct((struct taskstats *) NLA_DATA(na));
    515						if (print_io_accounting)
    516							print_ioacct((struct taskstats *) NLA_DATA(na));
    517						if (print_task_context_switch_counts)
    518							task_context_switch_counts((struct taskstats *) NLA_DATA(na));
    519						if (fd) {
    520							if (write(fd, NLA_DATA(na), na->nla_len) < 0) {
    521								err(1,"write error\n");
    522							}
    523						}
    524						if (!loop)
    525							goto done;
    526						break;
    527					case TASKSTATS_TYPE_NULL:
    528						break;
    529					default:
    530						fprintf(stderr, "Unknown nested"
    531							" nla_type %d\n",
    532							na->nla_type);
    533						break;
    534					}
    535					len2 += NLA_ALIGN(na->nla_len);
    536					na = (struct nlattr *)((char *)na +
    537							       NLA_ALIGN(na->nla_len));
    538				}
    539				break;
    540
    541			case CGROUPSTATS_TYPE_CGROUP_STATS:
    542				print_cgroupstats(NLA_DATA(na));
    543				break;
    544			default:
    545				fprintf(stderr, "Unknown nla_type %d\n",
    546					na->nla_type);
    547			case TASKSTATS_TYPE_NULL:
    548				break;
    549			}
    550			na = (struct nlattr *) (GENLMSG_DATA(&msg) + len);
    551		}
    552	} while (loop);
    553done:
    554	if (maskset) {
    555		rc = send_cmd(nl_sd, id, mypid, TASKSTATS_CMD_GET,
    556			      TASKSTATS_CMD_ATTR_DEREGISTER_CPUMASK,
    557			      &cpumask, strlen(cpumask) + 1);
    558		printf("Sent deregister mask, retval %d\n", rc);
    559		if (rc < 0)
    560			err(rc, "error sending deregister cpumask\n");
    561	}
    562err:
    563	close(nl_sd);
    564	if (fd)
    565		close(fd);
    566	if (cfd)
    567		close(cfd);
    568	return 0;
    569}