xref: /linux/tools/testing/selftests/breakpoints/step_after_suspend_test.c (revision 48e42f91c10482992b474cc0874c0e33d76cb509)
1 /*
2  * Copyright (C) 2016 Google, Inc.
3  *
4  * This software is licensed under the terms of the GNU General Public
5  * License version 2, as published by the Free Software Foundation, and
6  * may be copied, distributed, and modified under those terms.
7  *
8  * This program is distributed in the hope that it will be useful,
9  * but WITHOUT ANY WARRANTY; without even the implied warranty of
10  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11  * GNU General Public License for more details.
12  *
13  */
14 
15 #define _GNU_SOURCE
16 
17 #include <errno.h>
18 #include <fcntl.h>
19 #include <sched.h>
20 #include <signal.h>
21 #include <stdbool.h>
22 #include <stdio.h>
23 #include <string.h>
24 #include <unistd.h>
25 #include <sys/ptrace.h>
26 #include <sys/stat.h>
27 #include <sys/timerfd.h>
28 #include <sys/types.h>
29 #include <sys/wait.h>
30 
31 #include "../kselftest.h"
32 
33 void child(int cpu)
34 {
35 	cpu_set_t set;
36 
37 	CPU_ZERO(&set);
38 	CPU_SET(cpu, &set);
39 	if (sched_setaffinity(0, sizeof(set), &set) != 0) {
40 		perror("sched_setaffinity() failed");
41 		_exit(1);
42 	}
43 
44 	if (ptrace(PTRACE_TRACEME, 0, NULL, NULL) != 0) {
45 		perror("ptrace(PTRACE_TRACEME) failed");
46 		_exit(1);
47 	}
48 
49 	if (raise(SIGSTOP) != 0) {
50 		perror("raise(SIGSTOP) failed");
51 		_exit(1);
52 	}
53 
54 	_exit(0);
55 }
56 
57 bool run_test(int cpu)
58 {
59 	int status;
60 	pid_t pid = fork();
61 	pid_t wpid;
62 
63 	if (pid < 0) {
64 		perror("fork() failed");
65 		return false;
66 	}
67 	if (pid == 0)
68 		child(cpu);
69 
70 	wpid = waitpid(pid, &status, __WALL);
71 	if (wpid != pid) {
72 		perror("waitpid() failed");
73 		return false;
74 	}
75 	if (!WIFSTOPPED(status)) {
76 		printf("child did not stop\n");
77 		return false;
78 	}
79 	if (WSTOPSIG(status) != SIGSTOP) {
80 		printf("child did not stop with SIGSTOP\n");
81 		return false;
82 	}
83 
84 	if (ptrace(PTRACE_SINGLESTEP, pid, NULL, NULL) < 0) {
85 		if (errno == EIO) {
86 			printf("ptrace(PTRACE_SINGLESTEP) not supported on this architecture\n");
87 			ksft_exit_skip();
88 		}
89 		perror("ptrace(PTRACE_SINGLESTEP) failed");
90 		return false;
91 	}
92 
93 	wpid = waitpid(pid, &status, __WALL);
94 	if (wpid != pid) {
95 		perror("waitpid() failed");
96 		return false;
97 	}
98 	if (WIFEXITED(status)) {
99 		printf("child did not single-step\n");
100 		return false;
101 	}
102 	if (!WIFSTOPPED(status)) {
103 		printf("child did not stop\n");
104 		return false;
105 	}
106 	if (WSTOPSIG(status) != SIGTRAP) {
107 		printf("child did not stop with SIGTRAP\n");
108 		return false;
109 	}
110 
111 	if (ptrace(PTRACE_CONT, pid, NULL, NULL) < 0) {
112 		perror("ptrace(PTRACE_CONT) failed");
113 		return false;
114 	}
115 
116 	wpid = waitpid(pid, &status, __WALL);
117 	if (wpid != pid) {
118 		perror("waitpid() failed");
119 		return false;
120 	}
121 	if (!WIFEXITED(status)) {
122 		printf("child did not exit after PTRACE_CONT\n");
123 		return false;
124 	}
125 
126 	return true;
127 }
128 
129 void suspend(void)
130 {
131 	int power_state_fd;
132 	struct sigevent event = {};
133 	int timerfd;
134 	int err;
135 	struct itimerspec spec = {};
136 
137 	power_state_fd = open("/sys/power/state", O_RDWR);
138 	if (power_state_fd < 0)
139 		ksft_exit_fail_msg(
140 			"open(\"/sys/power/state\") failed (is this test running as root?)");
141 
142 	timerfd = timerfd_create(CLOCK_BOOTTIME_ALARM, 0);
143 	if (timerfd < 0)
144 		ksft_exit_fail_msg("timerfd_create() failed");
145 
146 	spec.it_value.tv_sec = 5;
147 	err = timerfd_settime(timerfd, 0, &spec, NULL);
148 	if (err < 0)
149 		ksft_exit_fail_msg("timerfd_settime() failed");
150 
151 	if (write(power_state_fd, "mem", strlen("mem")) != strlen("mem"))
152 		ksft_exit_fail_msg("entering suspend failed");
153 
154 	close(timerfd);
155 	close(power_state_fd);
156 }
157 
158 int main(int argc, char **argv)
159 {
160 	int opt;
161 	bool do_suspend = true;
162 	bool succeeded = true;
163 	cpu_set_t available_cpus;
164 	int err;
165 	int cpu;
166 	char buf[10];
167 
168 	ksft_print_header();
169 
170 	while ((opt = getopt(argc, argv, "n")) != -1) {
171 		switch (opt) {
172 		case 'n':
173 			do_suspend = false;
174 			break;
175 		default:
176 			printf("Usage: %s [-n]\n", argv[0]);
177 			printf("        -n: do not trigger a suspend/resume cycle before the test\n");
178 			return -1;
179 		}
180 	}
181 
182 	if (do_suspend)
183 		suspend();
184 
185 	err = sched_getaffinity(0, sizeof(available_cpus), &available_cpus);
186 	if (err < 0)
187 		ksft_exit_fail_msg("sched_getaffinity() failed");
188 
189 	for (cpu = 0; cpu < CPU_SETSIZE; cpu++) {
190 		bool test_success;
191 
192 		if (!CPU_ISSET(cpu, &available_cpus))
193 			continue;
194 
195 		test_success = run_test(cpu);
196 		sprintf(buf, "CPU %d", cpu);
197 		if (test_success) {
198 			ksft_test_result_pass(buf);
199 		} else {
200 			ksft_test_result_fail(buf);
201 			succeeded = false;
202 		}
203 	}
204 
205 	if (succeeded)
206 		ksft_exit_pass();
207 	else
208 		ksft_exit_fail();
209 }
210