1 /* 2 * Copyright 2012 Intel Corporation 3 * 4 * Permission is hereby granted, free of charge, to any person obtaining 5 * a copy of this software and associated documentation files (the 6 * "Software"), to deal in the Software without restriction, including 7 * without limitation the rights to use, copy, modify, merge, publish, 8 * distribute, sublicense, and/or sell copies of the Software, and to 9 * permit persons to whom the Software is furnished to do so, subject to 10 * the following conditions: 11 * 12 * The above copyright notice and this permission notice (including the 13 * next paragraph) shall be included in all copies or substantial 14 * portions of the Software. 15 * 16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 17 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 19 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS 20 * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 21 * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 22 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 * SOFTWARE. 24 */ 25 26 #define _GNU_SOURCE 27 28 #include <unistd.h> 29 #include <stdio.h> 30 #include <stdlib.h> 31 #include <sys/types.h> 32 #include <sys/wait.h> 33 #include <sys/stat.h> 34 #include <string.h> 35 #include <assert.h> 36 #include <dlfcn.h> 37 #include <errno.h> 38 #include <limits.h> 39 #include <sys/ptrace.h> 40 #include <sys/prctl.h> 41 #ifndef PR_SET_PTRACER 42 # define PR_SET_PTRACER 0x59616d61 43 #endif 44 45 #include "test-runner.h" 46 47 static int num_alloc; 48 static void* (*sys_malloc)(size_t); 49 static void (*sys_free)(void*); 50 static void* (*sys_realloc)(void*, size_t); 51 static void* (*sys_calloc)(size_t, size_t); 52 53 /* when set to 1, check if tests are not leaking memory and opened files. 54 * It is turned on by default. It can be turned off by 55 * WAYLAND_TEST_NO_LEAK_CHECK environment variable. */ 56 int leak_check_enabled; 57 58 /* when this var is set to 0, every call to test_set_timeout() is 59 * suppressed - handy when debugging the test. Can be set by 60 * WAYLAND_TEST_NO_TIMEOUTS environment variable. */ 61 static int timeouts_enabled = 1; 62 63 /* set to one if the output goes to the terminal */ 64 static int is_atty = 0; 65 66 extern const struct test __start_test_section, __stop_test_section; 67 68 __attribute__ ((visibility("default"))) void * 69 malloc(size_t size) 70 { 71 num_alloc++; 72 return sys_malloc(size); 73 } 74 75 __attribute__ ((visibility("default"))) void 76 free(void* mem) 77 { 78 if (mem != NULL) 79 num_alloc--; 80 sys_free(mem); 81 } 82 83 __attribute__ ((visibility("default"))) void * 84 realloc(void* mem, size_t size) 85 { 86 if (mem == NULL) 87 num_alloc++; 88 return sys_realloc(mem, size); 89 } 90 91 __attribute__ ((visibility("default"))) void * 92 calloc(size_t nmemb, size_t size) 93 { 94 if (sys_calloc == NULL) 95 return NULL; 96 97 num_alloc++; 98 99 return sys_calloc(nmemb, size); 100 } 101 102 static const struct test * 103 find_test(const char *name) 104 { 105 const struct test *t; 106 107 for (t = &__start_test_section; t < &__stop_test_section; t++) 108 if (strcmp(t->name, name) == 0) 109 return t; 110 111 return NULL; 112 } 113 114 static void 115 usage(const char *name, int status) 116 { 117 const struct test *t; 118 119 fprintf(stderr, "Usage: %s [TEST]\n\n" 120 "With no arguments, run all test. Specify test case to run\n" 121 "only that test without forking. Available tests:\n\n", 122 name); 123 124 for (t = &__start_test_section; t < &__stop_test_section; t++) 125 fprintf(stderr, " %s\n", t->name); 126 127 fprintf(stderr, "\n"); 128 129 exit(status); 130 } 131 132 void 133 test_set_timeout(unsigned int to) 134 { 135 int re; 136 137 if (!timeouts_enabled) { 138 fprintf(stderr, "Timeouts suppressed.\n"); 139 return; 140 } 141 142 re = alarm(to); 143 fprintf(stderr, "Timeout was %sset", re ? "re-" : ""); 144 145 if (to != 0) 146 fprintf(stderr, " to %d second%s from now.\n", 147 to, to > 1 ? "s" : ""); 148 else 149 fprintf(stderr, " off.\n"); 150 } 151 152 static void 153 sigalrm_handler(int signum) 154 { 155 fprintf(stderr, "Test timed out.\n"); 156 abort(); 157 } 158 159 int 160 get_current_alloc_num(void) 161 { 162 return num_alloc; 163 } 164 165 void 166 check_leaks(int supposed_alloc, int supposed_fds) 167 { 168 int num_fds; 169 170 if (leak_check_enabled) { 171 if (supposed_alloc != num_alloc) { 172 fprintf(stderr, "Memory leak detected in test. " 173 "Allocated %d blocks, unfreed %d\n", num_alloc, 174 num_alloc - supposed_alloc); 175 abort(); 176 } 177 178 num_fds = count_open_fds(); 179 if (supposed_fds != num_fds) { 180 fprintf(stderr, "fd leak detected in test. " 181 "Opened %d files, unclosed %d\n", num_fds, 182 num_fds - supposed_fds); 183 abort(); 184 } 185 } else { 186 fprintf(stderr, "Leak checks disabled\n"); 187 } 188 } 189 190 static void 191 run_test(const struct test *t) 192 { 193 int cur_alloc, cur_fds; 194 struct sigaction sa; 195 196 if (timeouts_enabled) { 197 sa.sa_handler = sigalrm_handler; 198 sa.sa_flags = 0; 199 sigemptyset(&sa.sa_mask); 200 assert(sigaction(SIGALRM, &sa, NULL) == 0); 201 } 202 203 cur_alloc = get_current_alloc_num(); 204 cur_fds = count_open_fds(); 205 206 t->run(); 207 208 /* turn off timeout (if any) after test completion */ 209 if (timeouts_enabled) 210 alarm(0); 211 212 check_leaks(cur_alloc, cur_fds); 213 214 exit(EXIT_SUCCESS); 215 } 216 217 #ifndef PATH_MAX 218 #define PATH_MAX 256 219 #endif 220 221 static void 222 set_xdg_runtime_dir(void) 223 { 224 char xdg_runtime_dir[PATH_MAX]; 225 const char *xrd_env; 226 227 xrd_env = getenv("XDG_RUNTIME_DIR"); 228 /* if XDG_RUNTIME_DIR is not set in environ, fallback to /tmp */ 229 assert((snprintf(xdg_runtime_dir, PATH_MAX, "%s/wayland-tests-XXXXXX", 230 xrd_env ? xrd_env : "/tmp") < PATH_MAX) 231 && "test error: XDG_RUNTIME_DIR too long"); 232 233 assert(mkdtemp(xdg_runtime_dir) && "test error: mkdtemp failed"); 234 if (mkdir(xdg_runtime_dir, 0700) == -1) 235 if (errno != EEXIST) { 236 perror("Creating XDG_RUNTIME_DIR"); 237 abort(); 238 } 239 240 if (setenv("XDG_RUNTIME_DIR", xdg_runtime_dir, 1) == -1) { 241 perror("Setting XDG_RUNTIME_DIR"); 242 abort(); 243 } 244 } 245 246 static void 247 rmdir_xdg_runtime_dir(void) 248 { 249 const char *xrd_env = getenv("XDG_RUNTIME_DIR"); 250 assert(xrd_env && "No XDG_RUNTIME_DIR set"); 251 252 /* rmdir may fail if some test didn't do clean up */ 253 if (rmdir(xrd_env) == -1) 254 perror("Cleaning XDG_RUNTIME_DIR"); 255 } 256 257 #define RED "\033[31m" 258 #define GREEN "\033[32m" 259 260 static void 261 stderr_set_color(const char *color) 262 { 263 /* use colors only when the output is connected to 264 * the terminal */ 265 if (is_atty) 266 fprintf(stderr, "%s", color); 267 } 268 269 static void 270 stderr_reset_color(void) 271 { 272 if (is_atty) 273 fprintf(stderr, "\033[0m"); 274 } 275 276 /* this function is taken from libinput/test/litest.c 277 * (rev 028513a0a723e97941c39) 278 * 279 * Returns: 1 if a debugger is confirmed present; 0 if no debugger is 280 * present or if it can't be determined. 281 */ 282 static int 283 is_debugger_attached(void) 284 { 285 int status; 286 int rc; 287 pid_t pid; 288 int pipefd[2]; 289 290 if (pipe(pipefd) == -1) { 291 perror("pipe"); 292 return 0; 293 } 294 295 pid = fork(); 296 if (pid == -1) { 297 perror("fork"); 298 close(pipefd[0]); 299 close(pipefd[1]); 300 return 0; 301 } else if (pid == 0) { 302 char buf; 303 pid_t ppid = getppid(); 304 305 /* Wait until parent is ready */ 306 close(pipefd[1]); /* Close unused write end */ 307 read(pipefd[0], &buf, 1); 308 close(pipefd[0]); 309 if (buf == '-') 310 _exit(1); 311 if (ptrace(PTRACE_ATTACH, ppid, NULL, NULL) != 0) 312 _exit(1); 313 if (!waitpid(-1, NULL, 0)) 314 _exit(1); 315 ptrace(PTRACE_CONT, NULL, NULL); 316 ptrace(PTRACE_DETACH, ppid, NULL, NULL); 317 _exit(0); 318 } else { 319 close(pipefd[0]); 320 321 /* Enable child to ptrace the parent process */ 322 rc = prctl(PR_SET_PTRACER, pid); 323 if (rc != 0 && errno != EINVAL) { 324 /* An error prevents us from telling if a debugger is attached. 325 * Instead of propagating the error, assume no debugger present. 326 * But note the error to the log as a clue for troubleshooting. 327 * Then flag the error state to the client by sending '-'. 328 */ 329 perror("prctl"); 330 write(pipefd[1], "-", 1); 331 } else { 332 /* Signal to client that parent is ready by passing '+' */ 333 write(pipefd[1], "+", 1); 334 } 335 close(pipefd[1]); 336 337 waitpid(pid, &status, 0); 338 rc = WEXITSTATUS(status); 339 } 340 341 return rc; 342 } 343 344 int main(int argc, char *argv[]) 345 { 346 const struct test *t; 347 pid_t pid; 348 int total, pass; 349 siginfo_t info; 350 351 /* Load system malloc, free, and realloc */ 352 sys_calloc = dlsym(RTLD_NEXT, "calloc"); 353 sys_realloc = dlsym(RTLD_NEXT, "realloc"); 354 sys_malloc = dlsym(RTLD_NEXT, "malloc"); 355 sys_free = dlsym(RTLD_NEXT, "free"); 356 357 if (isatty(fileno(stderr))) 358 is_atty = 1; 359 360 if (is_debugger_attached()) { 361 leak_check_enabled = 0; 362 timeouts_enabled = 0; 363 } else { 364 leak_check_enabled = !getenv("WAYLAND_TEST_NO_LEAK_CHECK"); 365 timeouts_enabled = !getenv("WAYLAND_TEST_NO_TIMEOUTS"); 366 } 367 368 if (argc == 2 && strcmp(argv[1], "--help") == 0) 369 usage(argv[0], EXIT_SUCCESS); 370 371 if (argc == 2) { 372 t = find_test(argv[1]); 373 if (t == NULL) { 374 fprintf(stderr, "unknown test: \"%s\"\n", argv[1]); 375 usage(argv[0], EXIT_FAILURE); 376 } 377 378 set_xdg_runtime_dir(); 379 /* run_test calls exit() */ 380 assert(atexit(rmdir_xdg_runtime_dir) == 0); 381 382 run_test(t); 383 } 384 385 /* set our own XDG_RUNTIME_DIR */ 386 set_xdg_runtime_dir(); 387 388 pass = 0; 389 for (t = &__start_test_section; t < &__stop_test_section; t++) { 390 int success = 0; 391 392 pid = fork(); 393 assert(pid >= 0); 394 395 if (pid == 0) 396 run_test(t); /* never returns */ 397 398 if (waitid(P_PID, pid, &info, WEXITED)) { 399 stderr_set_color(RED); 400 fprintf(stderr, "waitid failed: %m\n"); 401 stderr_reset_color(); 402 403 abort(); 404 } 405 406 switch (info.si_code) { 407 case CLD_EXITED: 408 if (info.si_status == EXIT_SUCCESS) 409 success = !t->must_fail; 410 else 411 success = t->must_fail; 412 413 stderr_set_color(success ? GREEN : RED); 414 fprintf(stderr, "test \"%s\":\texit status %d", 415 t->name, info.si_status); 416 417 break; 418 case CLD_KILLED: 419 case CLD_DUMPED: 420 if (t->must_fail) 421 success = 1; 422 423 stderr_set_color(success ? GREEN : RED); 424 fprintf(stderr, "test \"%s\":\tsignal %d", 425 t->name, info.si_status); 426 427 break; 428 } 429 430 if (success) { 431 pass++; 432 fprintf(stderr, ", pass.\n"); 433 } else 434 fprintf(stderr, ", fail.\n"); 435 436 stderr_reset_color(); 437 438 /* print separator line */ 439 fprintf(stderr, "----------------------------------------\n"); 440 } 441 442 total = &__stop_test_section - &__start_test_section; 443 fprintf(stderr, "%d tests, %d pass, %d fail\n", 444 total, pass, total - pass); 445 446 /* cleaning */ 447 rmdir_xdg_runtime_dir(); 448 449 return pass == total ? EXIT_SUCCESS : EXIT_FAILURE; 450 } 451