Home | History | Annotate | Download | only in pmu
      1 /*
      2  * Copyright 2014, Michael Ellerman, IBM Corp.
      3  * Licensed under GPLv2.
      4  */
      5 
      6 #define _GNU_SOURCE	/* For CPU_ZERO etc. */
      7 
      8 #include <errno.h>
      9 #include <sched.h>
     10 #include <setjmp.h>
     11 #include <stdlib.h>
     12 #include <sys/wait.h>
     13 
     14 #include "utils.h"
     15 #include "lib.h"
     16 
     17 
     18 int bind_to_cpu(int cpu)
     19 {
     20 	cpu_set_t mask;
     21 
     22 	printf("Binding to cpu %d\n", cpu);
     23 
     24 	CPU_ZERO(&mask);
     25 	CPU_SET(cpu, &mask);
     26 
     27 	return sched_setaffinity(0, sizeof(mask), &mask);
     28 }
     29 
     30 #define PARENT_TOKEN	0xAA
     31 #define CHILD_TOKEN	0x55
     32 
     33 int sync_with_child(union pipe read_pipe, union pipe write_pipe)
     34 {
     35 	char c = PARENT_TOKEN;
     36 
     37 	FAIL_IF(write(write_pipe.write_fd, &c, 1) != 1);
     38 	FAIL_IF(read(read_pipe.read_fd, &c, 1) != 1);
     39 	if (c != CHILD_TOKEN) /* sometimes expected */
     40 		return 1;
     41 
     42 	return 0;
     43 }
     44 
     45 int wait_for_parent(union pipe read_pipe)
     46 {
     47 	char c;
     48 
     49 	FAIL_IF(read(read_pipe.read_fd, &c, 1) != 1);
     50 	FAIL_IF(c != PARENT_TOKEN);
     51 
     52 	return 0;
     53 }
     54 
     55 int notify_parent(union pipe write_pipe)
     56 {
     57 	char c = CHILD_TOKEN;
     58 
     59 	FAIL_IF(write(write_pipe.write_fd, &c, 1) != 1);
     60 
     61 	return 0;
     62 }
     63 
     64 int notify_parent_of_error(union pipe write_pipe)
     65 {
     66 	char c = ~CHILD_TOKEN;
     67 
     68 	FAIL_IF(write(write_pipe.write_fd, &c, 1) != 1);
     69 
     70 	return 0;
     71 }
     72 
     73 int wait_for_child(pid_t child_pid)
     74 {
     75 	int rc;
     76 
     77 	if (waitpid(child_pid, &rc, 0) == -1) {
     78 		perror("waitpid");
     79 		return 1;
     80 	}
     81 
     82 	if (WIFEXITED(rc))
     83 		rc = WEXITSTATUS(rc);
     84 	else
     85 		rc = 1; /* Signal or other */
     86 
     87 	return rc;
     88 }
     89 
     90 int kill_child_and_wait(pid_t child_pid)
     91 {
     92 	kill(child_pid, SIGTERM);
     93 
     94 	return wait_for_child(child_pid);
     95 }
     96 
     97 static int eat_cpu_child(union pipe read_pipe, union pipe write_pipe)
     98 {
     99 	volatile int i = 0;
    100 
    101 	/*
    102 	 * We are just here to eat cpu and die. So make sure we can be killed,
    103 	 * and also don't do any custom SIGTERM handling.
    104 	 */
    105 	signal(SIGTERM, SIG_DFL);
    106 
    107 	notify_parent(write_pipe);
    108 	wait_for_parent(read_pipe);
    109 
    110 	/* Soak up cpu forever */
    111 	while (1) i++;
    112 
    113 	return 0;
    114 }
    115 
    116 pid_t eat_cpu(int (test_function)(void))
    117 {
    118 	union pipe read_pipe, write_pipe;
    119 	int cpu, rc;
    120 	pid_t pid;
    121 
    122 	cpu = pick_online_cpu();
    123 	FAIL_IF(cpu < 0);
    124 	FAIL_IF(bind_to_cpu(cpu));
    125 
    126 	if (pipe(read_pipe.fds) == -1)
    127 		return -1;
    128 
    129 	if (pipe(write_pipe.fds) == -1)
    130 		return -1;
    131 
    132 	pid = fork();
    133 	if (pid == 0)
    134 		exit(eat_cpu_child(write_pipe, read_pipe));
    135 
    136 	if (sync_with_child(read_pipe, write_pipe)) {
    137 		rc = -1;
    138 		goto out;
    139 	}
    140 
    141 	printf("main test running as pid %d\n", getpid());
    142 
    143 	rc = test_function();
    144 out:
    145 	kill(pid, SIGKILL);
    146 
    147 	return rc;
    148 }
    149 
    150 struct addr_range libc, vdso;
    151 
    152 int parse_proc_maps(void)
    153 {
    154 	unsigned long start, end;
    155 	char execute, name[128];
    156 	FILE *f;
    157 	int rc;
    158 
    159 	f = fopen("/proc/self/maps", "r");
    160 	if (!f) {
    161 		perror("fopen");
    162 		return -1;
    163 	}
    164 
    165 	do {
    166 		/* This skips line with no executable which is what we want */
    167 		rc = fscanf(f, "%lx-%lx %*c%*c%c%*c %*x %*d:%*d %*d %127s\n",
    168 			    &start, &end, &execute, name);
    169 		if (rc <= 0)
    170 			break;
    171 
    172 		if (execute != 'x')
    173 			continue;
    174 
    175 		if (strstr(name, "libc")) {
    176 			libc.first = start;
    177 			libc.last = end - 1;
    178 		} else if (strstr(name, "[vdso]")) {
    179 			vdso.first = start;
    180 			vdso.last = end - 1;
    181 		}
    182 	} while(1);
    183 
    184 	fclose(f);
    185 
    186 	return 0;
    187 }
    188 
    189 #define PARANOID_PATH	"/proc/sys/kernel/perf_event_paranoid"
    190 
    191 bool require_paranoia_below(int level)
    192 {
    193 	long current;
    194 	char *end, buf[16];
    195 	FILE *f;
    196 	bool rc;
    197 
    198 	rc = false;
    199 
    200 	f = fopen(PARANOID_PATH, "r");
    201 	if (!f) {
    202 		perror("fopen");
    203 		goto out;
    204 	}
    205 
    206 	if (!fgets(buf, sizeof(buf), f)) {
    207 		printf("Couldn't read " PARANOID_PATH "?\n");
    208 		goto out_close;
    209 	}
    210 
    211 	current = strtol(buf, &end, 10);
    212 
    213 	if (end == buf) {
    214 		printf("Couldn't parse " PARANOID_PATH "?\n");
    215 		goto out_close;
    216 	}
    217 
    218 	if (current >= level)
    219 		goto out_close;
    220 
    221 	rc = true;
    222 out_close:
    223 	fclose(f);
    224 out:
    225 	return rc;
    226 }
    227 
    228