|
1 /* |
|
2 * linux and CPU test |
|
3 * |
|
4 * Copyright (c) 2003 Fabrice Bellard |
|
5 * |
|
6 * This program is free software; you can redistribute it and/or modify |
|
7 * it under the terms of the GNU General Public License as published by |
|
8 * the Free Software Foundation; either version 2 of the License, or |
|
9 * (at your option) any later version. |
|
10 * |
|
11 * This program is distributed in the hope that it will be useful, |
|
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|
14 * GNU General Public License for more details. |
|
15 * |
|
16 * You should have received a copy of the GNU General Public License |
|
17 * along with this program; if not, write to the Free Software |
|
18 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. |
|
19 */ |
|
20 #include <stdarg.h> |
|
21 #include <stdlib.h> |
|
22 #include <stdio.h> |
|
23 #include <unistd.h> |
|
24 #include <fcntl.h> |
|
25 #include <inttypes.h> |
|
26 #include <string.h> |
|
27 #include <sys/types.h> |
|
28 #include <sys/stat.h> |
|
29 #include <sys/wait.h> |
|
30 #include <errno.h> |
|
31 #include <utime.h> |
|
32 #include <time.h> |
|
33 #include <sys/time.h> |
|
34 #include <sys/uio.h> |
|
35 #include <sys/socket.h> |
|
36 #include <netinet/in.h> |
|
37 #include <arpa/inet.h> |
|
38 #include <sched.h> |
|
39 #include <dirent.h> |
|
40 #include <setjmp.h> |
|
41 #include <sys/shm.h> |
|
42 |
|
43 #define TESTPATH "/tmp/linux-test.tmp" |
|
44 #define TESTPORT 7654 |
|
45 #define STACK_SIZE 16384 |
|
46 |
|
47 void error1(const char *filename, int line, const char *fmt, ...) |
|
48 { |
|
49 va_list ap; |
|
50 va_start(ap, fmt); |
|
51 fprintf(stderr, "%s:%d: ", filename, line); |
|
52 vfprintf(stderr, fmt, ap); |
|
53 fprintf(stderr, "\n"); |
|
54 va_end(ap); |
|
55 exit(1); |
|
56 } |
|
57 |
|
58 int __chk_error(const char *filename, int line, int ret) |
|
59 { |
|
60 if (ret < 0) { |
|
61 error1(filename, line, "%m (ret=%d, errno=%d)", |
|
62 ret, errno); |
|
63 } |
|
64 return ret; |
|
65 } |
|
66 |
|
67 #define error(fmt, args...) error1(__FILE__, __LINE__, fmt, ##args) |
|
68 |
|
69 #define chk_error(ret) __chk_error(__FILE__, __LINE__, (ret)) |
|
70 |
|
71 /*******************************************************/ |
|
72 |
|
73 #define FILE_BUF_SIZE 300 |
|
74 |
|
75 void test_file(void) |
|
76 { |
|
77 int fd, i, len, ret; |
|
78 uint8_t buf[FILE_BUF_SIZE]; |
|
79 uint8_t buf2[FILE_BUF_SIZE]; |
|
80 uint8_t buf3[FILE_BUF_SIZE]; |
|
81 char cur_dir[1024]; |
|
82 struct stat st; |
|
83 struct utimbuf tbuf; |
|
84 struct iovec vecs[2]; |
|
85 DIR *dir; |
|
86 struct dirent *de; |
|
87 |
|
88 /* clean up, just in case */ |
|
89 unlink(TESTPATH "/file1"); |
|
90 unlink(TESTPATH "/file2"); |
|
91 unlink(TESTPATH "/file3"); |
|
92 rmdir(TESTPATH); |
|
93 |
|
94 if (getcwd(cur_dir, sizeof(cur_dir)) == NULL) |
|
95 error("getcwd"); |
|
96 |
|
97 chk_error(mkdir(TESTPATH, 0755)); |
|
98 |
|
99 chk_error(chdir(TESTPATH)); |
|
100 |
|
101 /* open/read/write/close/readv/writev/lseek */ |
|
102 |
|
103 fd = chk_error(open("file1", O_WRONLY | O_TRUNC | O_CREAT, 0644)); |
|
104 for(i=0;i < FILE_BUF_SIZE; i++) |
|
105 buf[i] = i; |
|
106 len = chk_error(write(fd, buf, FILE_BUF_SIZE / 2)); |
|
107 if (len != (FILE_BUF_SIZE / 2)) |
|
108 error("write"); |
|
109 vecs[0].iov_base = buf + (FILE_BUF_SIZE / 2); |
|
110 vecs[0].iov_len = 16; |
|
111 vecs[1].iov_base = buf + (FILE_BUF_SIZE / 2) + 16; |
|
112 vecs[1].iov_len = (FILE_BUF_SIZE / 2) - 16; |
|
113 len = chk_error(writev(fd, vecs, 2)); |
|
114 if (len != (FILE_BUF_SIZE / 2)) |
|
115 error("writev"); |
|
116 chk_error(close(fd)); |
|
117 |
|
118 chk_error(rename("file1", "file2")); |
|
119 |
|
120 fd = chk_error(open("file2", O_RDONLY)); |
|
121 |
|
122 len = chk_error(read(fd, buf2, FILE_BUF_SIZE)); |
|
123 if (len != FILE_BUF_SIZE) |
|
124 error("read"); |
|
125 if (memcmp(buf, buf2, FILE_BUF_SIZE) != 0) |
|
126 error("memcmp"); |
|
127 |
|
128 #define FOFFSET 16 |
|
129 ret = chk_error(lseek(fd, FOFFSET, SEEK_SET)); |
|
130 if (ret != 16) |
|
131 error("lseek"); |
|
132 vecs[0].iov_base = buf3; |
|
133 vecs[0].iov_len = 32; |
|
134 vecs[1].iov_base = buf3 + 32; |
|
135 vecs[1].iov_len = FILE_BUF_SIZE - FOFFSET - 32; |
|
136 len = chk_error(readv(fd, vecs, 2)); |
|
137 if (len != FILE_BUF_SIZE - FOFFSET) |
|
138 error("readv"); |
|
139 if (memcmp(buf + FOFFSET, buf3, FILE_BUF_SIZE - FOFFSET) != 0) |
|
140 error("memcmp"); |
|
141 |
|
142 chk_error(close(fd)); |
|
143 |
|
144 /* access */ |
|
145 chk_error(access("file2", R_OK)); |
|
146 |
|
147 /* stat/chmod/utime/truncate */ |
|
148 |
|
149 chk_error(chmod("file2", 0600)); |
|
150 tbuf.actime = 1001; |
|
151 tbuf.modtime = 1000; |
|
152 chk_error(truncate("file2", 100)); |
|
153 chk_error(utime("file2", &tbuf)); |
|
154 chk_error(stat("file2", &st)); |
|
155 if (st.st_size != 100) |
|
156 error("stat size"); |
|
157 if (!S_ISREG(st.st_mode)) |
|
158 error("stat mode"); |
|
159 if ((st.st_mode & 0777) != 0600) |
|
160 error("stat mode2"); |
|
161 if (st.st_atime != 1001 || |
|
162 st.st_mtime != 1000) |
|
163 error("stat time"); |
|
164 |
|
165 chk_error(stat(TESTPATH, &st)); |
|
166 if (!S_ISDIR(st.st_mode)) |
|
167 error("stat mode"); |
|
168 |
|
169 /* fstat */ |
|
170 fd = chk_error(open("file2", O_RDWR)); |
|
171 chk_error(ftruncate(fd, 50)); |
|
172 chk_error(fstat(fd, &st)); |
|
173 chk_error(close(fd)); |
|
174 |
|
175 if (st.st_size != 50) |
|
176 error("stat size"); |
|
177 if (!S_ISREG(st.st_mode)) |
|
178 error("stat mode"); |
|
179 |
|
180 /* symlink/lstat */ |
|
181 chk_error(symlink("file2", "file3")); |
|
182 chk_error(lstat("file3", &st)); |
|
183 if (!S_ISLNK(st.st_mode)) |
|
184 error("stat mode"); |
|
185 |
|
186 /* getdents */ |
|
187 dir = opendir(TESTPATH); |
|
188 if (!dir) |
|
189 error("opendir"); |
|
190 len = 0; |
|
191 for(;;) { |
|
192 de = readdir(dir); |
|
193 if (!de) |
|
194 break; |
|
195 if (strcmp(de->d_name, ".") != 0 && |
|
196 strcmp(de->d_name, "..") != 0 && |
|
197 strcmp(de->d_name, "file2") != 0 && |
|
198 strcmp(de->d_name, "file3") != 0) |
|
199 error("readdir"); |
|
200 len++; |
|
201 } |
|
202 closedir(dir); |
|
203 if (len != 4) |
|
204 error("readdir"); |
|
205 |
|
206 chk_error(unlink("file3")); |
|
207 chk_error(unlink("file2")); |
|
208 chk_error(chdir(cur_dir)); |
|
209 chk_error(rmdir(TESTPATH)); |
|
210 } |
|
211 |
|
212 void test_fork(void) |
|
213 { |
|
214 int pid, status; |
|
215 |
|
216 pid = chk_error(fork()); |
|
217 if (pid == 0) { |
|
218 /* child */ |
|
219 exit(2); |
|
220 } |
|
221 chk_error(waitpid(pid, &status, 0)); |
|
222 if (!WIFEXITED(status) || WEXITSTATUS(status) != 2) |
|
223 error("waitpid status=0x%x", status); |
|
224 } |
|
225 |
|
226 void test_time(void) |
|
227 { |
|
228 struct timeval tv, tv2; |
|
229 struct timespec ts, rem; |
|
230 struct rusage rusg1, rusg2; |
|
231 int ti, i; |
|
232 |
|
233 chk_error(gettimeofday(&tv, NULL)); |
|
234 rem.tv_sec = 1; |
|
235 ts.tv_sec = 0; |
|
236 ts.tv_nsec = 20 * 1000000; |
|
237 chk_error(nanosleep(&ts, &rem)); |
|
238 if (rem.tv_sec != 1) |
|
239 error("nanosleep"); |
|
240 chk_error(gettimeofday(&tv2, NULL)); |
|
241 ti = tv2.tv_sec - tv.tv_sec; |
|
242 if (ti >= 2) |
|
243 error("gettimeofday"); |
|
244 |
|
245 chk_error(getrusage(RUSAGE_SELF, &rusg1)); |
|
246 for(i = 0;i < 10000; i++); |
|
247 chk_error(getrusage(RUSAGE_SELF, &rusg2)); |
|
248 if ((rusg2.ru_utime.tv_sec - rusg1.ru_utime.tv_sec) < 0 || |
|
249 (rusg2.ru_stime.tv_sec - rusg1.ru_stime.tv_sec) < 0) |
|
250 error("getrusage"); |
|
251 } |
|
252 |
|
253 void pstrcpy(char *buf, int buf_size, const char *str) |
|
254 { |
|
255 int c; |
|
256 char *q = buf; |
|
257 |
|
258 if (buf_size <= 0) |
|
259 return; |
|
260 |
|
261 for(;;) { |
|
262 c = *str++; |
|
263 if (c == 0 || q >= buf + buf_size - 1) |
|
264 break; |
|
265 *q++ = c; |
|
266 } |
|
267 *q = '\0'; |
|
268 } |
|
269 |
|
270 /* strcat and truncate. */ |
|
271 char *pstrcat(char *buf, int buf_size, const char *s) |
|
272 { |
|
273 int len; |
|
274 len = strlen(buf); |
|
275 if (len < buf_size) |
|
276 pstrcpy(buf + len, buf_size - len, s); |
|
277 return buf; |
|
278 } |
|
279 |
|
280 int server_socket(void) |
|
281 { |
|
282 int val, fd; |
|
283 struct sockaddr_in sockaddr; |
|
284 |
|
285 /* server socket */ |
|
286 fd = chk_error(socket(PF_INET, SOCK_STREAM, 0)); |
|
287 |
|
288 val = 1; |
|
289 chk_error(setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &val, sizeof(val))); |
|
290 |
|
291 sockaddr.sin_family = AF_INET; |
|
292 sockaddr.sin_port = htons(TESTPORT); |
|
293 sockaddr.sin_addr.s_addr = 0; |
|
294 chk_error(bind(fd, (struct sockaddr *)&sockaddr, sizeof(sockaddr))); |
|
295 chk_error(listen(fd, 0)); |
|
296 return fd; |
|
297 |
|
298 } |
|
299 |
|
300 int client_socket(void) |
|
301 { |
|
302 int fd; |
|
303 struct sockaddr_in sockaddr; |
|
304 |
|
305 /* server socket */ |
|
306 fd = chk_error(socket(PF_INET, SOCK_STREAM, 0)); |
|
307 sockaddr.sin_family = AF_INET; |
|
308 sockaddr.sin_port = htons(TESTPORT); |
|
309 inet_aton("127.0.0.1", &sockaddr.sin_addr); |
|
310 chk_error(connect(fd, (struct sockaddr *)&sockaddr, sizeof(sockaddr))); |
|
311 return fd; |
|
312 } |
|
313 |
|
314 const char socket_msg[] = "hello socket\n"; |
|
315 |
|
316 void test_socket(void) |
|
317 { |
|
318 int server_fd, client_fd, fd, pid, ret, val; |
|
319 struct sockaddr_in sockaddr; |
|
320 socklen_t len; |
|
321 char buf[512]; |
|
322 |
|
323 server_fd = server_socket(); |
|
324 |
|
325 /* test a few socket options */ |
|
326 len = sizeof(val); |
|
327 chk_error(getsockopt(server_fd, SOL_SOCKET, SO_TYPE, &val, &len)); |
|
328 if (val != SOCK_STREAM) |
|
329 error("getsockopt"); |
|
330 |
|
331 pid = chk_error(fork()); |
|
332 if (pid == 0) { |
|
333 client_fd = client_socket(); |
|
334 send(client_fd, socket_msg, sizeof(socket_msg), 0); |
|
335 close(client_fd); |
|
336 exit(0); |
|
337 } |
|
338 len = sizeof(sockaddr); |
|
339 fd = chk_error(accept(server_fd, (struct sockaddr *)&sockaddr, &len)); |
|
340 |
|
341 ret = chk_error(recv(fd, buf, sizeof(buf), 0)); |
|
342 if (ret != sizeof(socket_msg)) |
|
343 error("recv"); |
|
344 if (memcmp(buf, socket_msg, sizeof(socket_msg)) != 0) |
|
345 error("socket_msg"); |
|
346 chk_error(close(fd)); |
|
347 chk_error(close(server_fd)); |
|
348 } |
|
349 |
|
350 #define WCOUNT_MAX 512 |
|
351 |
|
352 void test_pipe(void) |
|
353 { |
|
354 fd_set rfds, wfds; |
|
355 int fds[2], fd_max, ret; |
|
356 uint8_t ch; |
|
357 int wcount, rcount; |
|
358 |
|
359 chk_error(pipe(fds)); |
|
360 chk_error(fcntl(fds[0], F_SETFL, O_NONBLOCK)); |
|
361 chk_error(fcntl(fds[1], F_SETFL, O_NONBLOCK)); |
|
362 wcount = 0; |
|
363 rcount = 0; |
|
364 for(;;) { |
|
365 FD_ZERO(&rfds); |
|
366 fd_max = fds[0]; |
|
367 FD_SET(fds[0], &rfds); |
|
368 |
|
369 FD_ZERO(&wfds); |
|
370 FD_SET(fds[1], &wfds); |
|
371 if (fds[1] > fd_max) |
|
372 fd_max = fds[1]; |
|
373 |
|
374 ret = chk_error(select(fd_max + 1, &rfds, &wfds, NULL, NULL)); |
|
375 if (ret > 0) { |
|
376 if (FD_ISSET(fds[0], &rfds)) { |
|
377 chk_error(read(fds[0], &ch, 1)); |
|
378 rcount++; |
|
379 if (rcount >= WCOUNT_MAX) |
|
380 break; |
|
381 } |
|
382 if (FD_ISSET(fds[1], &wfds)) { |
|
383 ch = 'a'; |
|
384 chk_error(write(fds[0], &ch, 1)); |
|
385 wcount++; |
|
386 } |
|
387 } |
|
388 } |
|
389 chk_error(close(fds[0])); |
|
390 chk_error(close(fds[1])); |
|
391 } |
|
392 |
|
393 int thread1_res; |
|
394 int thread2_res; |
|
395 |
|
396 int thread1_func(void *arg) |
|
397 { |
|
398 int i; |
|
399 for(i=0;i<5;i++) { |
|
400 thread1_res++; |
|
401 usleep(10 * 1000); |
|
402 } |
|
403 return 0; |
|
404 } |
|
405 |
|
406 int thread2_func(void *arg) |
|
407 { |
|
408 int i; |
|
409 for(i=0;i<6;i++) { |
|
410 thread2_res++; |
|
411 usleep(10 * 1000); |
|
412 } |
|
413 return 0; |
|
414 } |
|
415 |
|
416 void test_clone(void) |
|
417 { |
|
418 uint8_t *stack1, *stack2; |
|
419 int pid1, pid2, status1, status2; |
|
420 |
|
421 stack1 = malloc(STACK_SIZE); |
|
422 pid1 = chk_error(clone(thread1_func, stack1 + STACK_SIZE, |
|
423 CLONE_VM | CLONE_FS | CLONE_FILES | SIGCHLD, "hello1")); |
|
424 |
|
425 stack2 = malloc(STACK_SIZE); |
|
426 pid2 = chk_error(clone(thread2_func, stack2 + STACK_SIZE, |
|
427 CLONE_VM | CLONE_FS | CLONE_FILES | SIGCHLD, "hello2")); |
|
428 |
|
429 while (waitpid(pid1, &status1, 0) != pid1); |
|
430 while (waitpid(pid2, &status2, 0) != pid2); |
|
431 if (thread1_res != 5 || |
|
432 thread2_res != 6) |
|
433 error("clone"); |
|
434 } |
|
435 |
|
436 /***********************************/ |
|
437 |
|
438 volatile int alarm_count; |
|
439 jmp_buf jmp_env; |
|
440 |
|
441 void sig_alarm(int sig) |
|
442 { |
|
443 if (sig != SIGALRM) |
|
444 error("signal"); |
|
445 alarm_count++; |
|
446 } |
|
447 |
|
448 void sig_segv(int sig, siginfo_t *info, void *puc) |
|
449 { |
|
450 if (sig != SIGSEGV) |
|
451 error("signal"); |
|
452 longjmp(jmp_env, 1); |
|
453 } |
|
454 |
|
455 void test_signal(void) |
|
456 { |
|
457 struct sigaction act; |
|
458 struct itimerval it, oit; |
|
459 |
|
460 /* timer test */ |
|
461 |
|
462 alarm_count = 0; |
|
463 |
|
464 act.sa_handler = sig_alarm; |
|
465 sigemptyset(&act.sa_mask); |
|
466 act.sa_flags = 0; |
|
467 chk_error(sigaction(SIGALRM, &act, NULL)); |
|
468 |
|
469 it.it_interval.tv_sec = 0; |
|
470 it.it_interval.tv_usec = 10 * 1000; |
|
471 it.it_value.tv_sec = 0; |
|
472 it.it_value.tv_usec = 10 * 1000; |
|
473 chk_error(setitimer(ITIMER_REAL, &it, NULL)); |
|
474 chk_error(getitimer(ITIMER_REAL, &oit)); |
|
475 if (oit.it_value.tv_sec != it.it_value.tv_sec || |
|
476 oit.it_value.tv_usec != it.it_value.tv_usec) |
|
477 error("itimer"); |
|
478 |
|
479 while (alarm_count < 5) { |
|
480 usleep(10 * 1000); |
|
481 } |
|
482 |
|
483 it.it_interval.tv_sec = 0; |
|
484 it.it_interval.tv_usec = 0; |
|
485 it.it_value.tv_sec = 0; |
|
486 it.it_value.tv_usec = 0; |
|
487 memset(&oit, 0xff, sizeof(oit)); |
|
488 chk_error(setitimer(ITIMER_REAL, &it, &oit)); |
|
489 if (oit.it_value.tv_sec != 0 || |
|
490 oit.it_value.tv_usec != 10 * 1000) |
|
491 error("setitimer"); |
|
492 |
|
493 /* SIGSEGV test */ |
|
494 act.sa_sigaction = sig_segv; |
|
495 sigemptyset(&act.sa_mask); |
|
496 act.sa_flags = SA_SIGINFO; |
|
497 chk_error(sigaction(SIGSEGV, &act, NULL)); |
|
498 if (setjmp(jmp_env) == 0) { |
|
499 *(uint8_t *)0 = 0; |
|
500 } |
|
501 |
|
502 act.sa_handler = SIG_DFL; |
|
503 sigemptyset(&act.sa_mask); |
|
504 act.sa_flags = 0; |
|
505 chk_error(sigaction(SIGSEGV, &act, NULL)); |
|
506 } |
|
507 |
|
508 #define SHM_SIZE 32768 |
|
509 |
|
510 void test_shm(void) |
|
511 { |
|
512 void *ptr; |
|
513 int shmid; |
|
514 |
|
515 shmid = chk_error(shmget(IPC_PRIVATE, SHM_SIZE, IPC_CREAT | 0777)); |
|
516 ptr = shmat(shmid, NULL, 0); |
|
517 if (!ptr) |
|
518 error("shmat"); |
|
519 |
|
520 memset(ptr, 0, SHM_SIZE); |
|
521 |
|
522 chk_error(shmctl(shmid, IPC_RMID, 0)); |
|
523 chk_error(shmdt(ptr)); |
|
524 } |
|
525 |
|
526 int main(int argc, char **argv) |
|
527 { |
|
528 test_file(); |
|
529 test_fork(); |
|
530 test_time(); |
|
531 test_socket(); |
|
532 // test_clone(); |
|
533 test_signal(); |
|
534 test_shm(); |
|
535 return 0; |
|
536 } |