1 | /* Test program for process CPU clocks. |
2 | Copyright (C) 2004-2022 Free Software Foundation, Inc. |
3 | This file is part of the GNU C Library. |
4 | |
5 | The GNU C Library is free software; you can redistribute it and/or |
6 | modify it under the terms of the GNU Lesser General Public |
7 | License as published by the Free Software Foundation; either |
8 | version 2.1 of the License, or (at your option) any later version. |
9 | |
10 | The GNU C Library is distributed in the hope that it will be useful, |
11 | but WITHOUT ANY WARRANTY; without even the implied warranty of |
12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
13 | Lesser General Public License for more details. |
14 | |
15 | You should have received a copy of the GNU Lesser General Public |
16 | License along with the GNU C Library; if not, see |
17 | <https://www.gnu.org/licenses/>. */ |
18 | |
19 | #include <stdio.h> |
20 | #include <stdlib.h> |
21 | #include <time.h> |
22 | #include <unistd.h> |
23 | #include <fcntl.h> |
24 | #include <string.h> |
25 | #include <errno.h> |
26 | #include <signal.h> |
27 | #include <stdint.h> |
28 | #include <sys/wait.h> |
29 | |
30 | /* This function is intended to rack up both user and system time. */ |
31 | static void |
32 | chew_cpu (void) |
33 | { |
34 | while (1) |
35 | { |
36 | static volatile char buf[4096]; |
37 | for (int i = 0; i < 100; ++i) |
38 | for (size_t j = 0; j < sizeof buf; ++j) |
39 | buf[j] = 0xaa; |
40 | int nullfd = open (file: "/dev/null" , O_WRONLY); |
41 | for (int i = 0; i < 100; ++i) |
42 | for (size_t j = 0; j < sizeof buf; ++j) |
43 | buf[j] = 0xbb; |
44 | write (nullfd, (char *) buf, sizeof buf); |
45 | close (fd: nullfd); |
46 | if (getppid () == 1) |
47 | _exit (2); |
48 | } |
49 | } |
50 | |
51 | static int |
52 | do_test (void) |
53 | { |
54 | int result = 0; |
55 | clockid_t cl; |
56 | int e; |
57 | pid_t dead_child, child; |
58 | |
59 | /* Fork a child and let it die, to give us a PID known not be valid |
60 | (assuming PIDs don't wrap around during the test). */ |
61 | { |
62 | dead_child = fork (); |
63 | if (dead_child == 0) |
64 | _exit (0); |
65 | if (dead_child < 0) |
66 | { |
67 | perror ("fork" ); |
68 | return 1; |
69 | } |
70 | int x; |
71 | if (wait (stat_loc: &x) != dead_child) |
72 | { |
73 | perror ("wait" ); |
74 | return 2; |
75 | } |
76 | } |
77 | |
78 | /* POSIX says we should get ESRCH for this. */ |
79 | e = clock_getcpuclockid (pid: dead_child, clock_id: &cl); |
80 | if (e != ENOSYS && e != ESRCH && e != EPERM) |
81 | { |
82 | printf (format: "clock_getcpuclockid on dead PID %d => %s\n" , |
83 | dead_child, strerror (errnum: e)); |
84 | result = 1; |
85 | } |
86 | |
87 | /* Now give us a live child eating up CPU time. */ |
88 | child = fork (); |
89 | if (child == 0) |
90 | { |
91 | chew_cpu (); |
92 | _exit (1); |
93 | } |
94 | if (child < 0) |
95 | { |
96 | perror ("fork" ); |
97 | return 1; |
98 | } |
99 | |
100 | e = clock_getcpuclockid (pid: child, clock_id: &cl); |
101 | if (e == EPERM) |
102 | { |
103 | puts (s: "clock_getcpuclockid does not support other processes" ); |
104 | goto done; |
105 | } |
106 | if (e != 0) |
107 | { |
108 | printf (format: "clock_getcpuclockid on live PID %d => %s\n" , |
109 | child, strerror (errnum: e)); |
110 | result = 1; |
111 | goto done; |
112 | } |
113 | |
114 | const clockid_t child_clock = cl; |
115 | struct timespec res; |
116 | if (clock_getres (clock_id: child_clock, res: &res) < 0) |
117 | { |
118 | printf (format: "clock_getres on live PID %d clock %lx => %s\n" , |
119 | child, (unsigned long int) child_clock, strerror (errno)); |
120 | result = 1; |
121 | goto done; |
122 | } |
123 | printf (format: "live PID %d clock %lx resolution %ju.%.9ju\n" , |
124 | child, (unsigned long int) child_clock, |
125 | (uintmax_t) res.tv_sec, (uintmax_t) res.tv_nsec); |
126 | |
127 | struct timespec before; |
128 | if (clock_gettime (clock_id: child_clock, tp: &before) < 0) |
129 | { |
130 | printf (format: "clock_gettime on live PID %d clock %lx => %s\n" , |
131 | child, (unsigned long int) child_clock, strerror (errno)); |
132 | result = 1; |
133 | goto done; |
134 | } |
135 | /* Should be close to 0.0. */ |
136 | printf (format: "live PID %d before sleep => %ju.%.9ju\n" , |
137 | child, (uintmax_t) before.tv_sec, (uintmax_t) before.tv_nsec); |
138 | |
139 | struct timespec sleeptime = { .tv_nsec = 100000000 }; |
140 | e = clock_nanosleep (clock_id: child_clock, flags: 0, req: &sleeptime, NULL); |
141 | if (e == EINVAL || e == ENOTSUP || e == ENOSYS) |
142 | { |
143 | printf (format: "clock_nanosleep not supported for other process clock: %s\n" , |
144 | strerror (errnum: e)); |
145 | } |
146 | else if (e != 0) |
147 | { |
148 | printf (format: "clock_nanosleep on other process clock: %s\n" , strerror (errnum: e)); |
149 | result = 1; |
150 | } |
151 | else |
152 | { |
153 | struct timespec afterns; |
154 | if (clock_gettime (clock_id: child_clock, tp: &afterns) < 0) |
155 | { |
156 | printf (format: "clock_gettime on live PID %d clock %lx => %s\n" , |
157 | child, (unsigned long int) child_clock, strerror (errno)); |
158 | result = 1; |
159 | } |
160 | else |
161 | { |
162 | printf (format: "live PID %d after sleep => %ju.%.9ju\n" , |
163 | child, (uintmax_t) afterns.tv_sec, |
164 | (uintmax_t) afterns.tv_nsec); |
165 | } |
166 | } |
167 | |
168 | if (kill (pid: child, SIGKILL) != 0) |
169 | { |
170 | perror ("kill" ); |
171 | result = 2; |
172 | goto done; |
173 | } |
174 | |
175 | /* Wait long enough to let the child finish dying. */ |
176 | |
177 | sleeptime.tv_nsec = 200000000; |
178 | if (nanosleep (requested_time: &sleeptime, NULL) != 0) |
179 | { |
180 | perror ("nanosleep" ); |
181 | result = 1; |
182 | goto done; |
183 | } |
184 | |
185 | struct timespec dead; |
186 | if (clock_gettime (clock_id: child_clock, tp: &dead) < 0) |
187 | { |
188 | printf (format: "clock_gettime on dead PID %d clock %lx => %s\n" , |
189 | child, (unsigned long int) child_clock, strerror (errno)); |
190 | result = 1; |
191 | goto done; |
192 | } |
193 | /* Should be close to 0.1. */ |
194 | printf (format: "dead PID %d => %ju.%.9ju\n" , |
195 | child, (uintmax_t) dead.tv_sec, (uintmax_t) dead.tv_nsec); |
196 | |
197 | /* Now reap the child and verify that its clock is no longer valid. */ |
198 | { |
199 | int x; |
200 | if (waitpid (pid: child, stat_loc: &x, options: 0) != child) |
201 | { |
202 | perror ("waitpid" ); |
203 | result = 1; |
204 | } |
205 | } |
206 | |
207 | if (clock_gettime (clock_id: child_clock, tp: &dead) == 0) |
208 | { |
209 | printf (format: "clock_gettime on reaped PID %d clock %lx => %ju%.9ju\n" , |
210 | child, (unsigned long int) child_clock, |
211 | (uintmax_t) dead.tv_sec, (uintmax_t) dead.tv_nsec); |
212 | result = 1; |
213 | } |
214 | else |
215 | { |
216 | if (errno != EINVAL) |
217 | result = 1; |
218 | printf (format: "clock_gettime on reaped PID %d clock %lx => %s\n" , |
219 | child, (unsigned long int) child_clock, strerror (errno)); |
220 | } |
221 | |
222 | if (clock_getres (clock_id: child_clock, res: &dead) == 0) |
223 | { |
224 | printf (format: "clock_getres on reaped PID %d clock %lx => %ju%.9ju\n" , |
225 | child, (unsigned long int) child_clock, |
226 | (uintmax_t) dead.tv_sec, (uintmax_t) dead.tv_nsec); |
227 | result = 1; |
228 | } |
229 | else |
230 | { |
231 | if (errno != EINVAL) |
232 | result = 1; |
233 | printf (format: "clock_getres on reaped PID %d clock %lx => %s\n" , |
234 | child, (unsigned long int) child_clock, strerror (errno)); |
235 | } |
236 | |
237 | return result; |
238 | |
239 | done: |
240 | { |
241 | if (kill (pid: child, SIGKILL) != 0 && errno != ESRCH) |
242 | { |
243 | perror ("kill" ); |
244 | return 2; |
245 | } |
246 | int x; |
247 | if (waitpid (pid: child, stat_loc: &x, options: 0) != child && errno != ECHILD) |
248 | { |
249 | perror ("waitpid" ); |
250 | return 2; |
251 | } |
252 | } |
253 | |
254 | return result; |
255 | } |
256 | |
257 | |
258 | #define TEST_FUNCTION do_test () |
259 | #include "../test-skeleton.c" |
260 | |