1 /* 2 * memtoy.c -- toy/tool for investigating Linux [Numa] VM behavior 3 */ 4 /* 5 * Copyright (c) 2005 Hewlett-Packard, Inc 6 * All rights reserved. 7 */ 8 9 /* 10 * This program is free software; you can redistribute it and/or modify 11 * it under the terms of the GNU General Public License as published by 12 * the Free Software Foundation; either version 2 of the License, or 13 * (at your option) any later version. 14 * 15 * This program is distributed in the hope that it will be useful, 16 * but WITHOUT ANY WARRANTY; without even the implied warranty of 17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 18 * GNU General Public License for more details. 19 * 20 * You should have received a copy of the GNU General Public License 21 * along with this program; if not, write to the Free Software 22 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 23 */ 24 25 #include <stdio.h> 26 #include "config.h" 27 /* Shortcut because the test requires numa and mempolicy support. */ 28 #if HAVE_NUMA_H && HAVE_NUMAIF_H && HAVE_LINUX_MEMPOLICY_H 29 #include <sys/types.h> 30 #include <sys/time.h> 31 #include <sys/mman.h> 32 #include <libgen.h> 33 #include <errno.h> 34 #include <numa.h> 35 #include <signal.h> 36 #include <stdarg.h> 37 #include <stdlib.h> 38 #include <string.h> 39 #include <unistd.h> 40 #include "memtoy.h" 41 42 /* 43 * global context 44 */ 45 glctx_t glctx; /* global context */ 46 47 /* 48 * command line options: 49 * 50 * -v = verbose 51 * -V = display version 52 * -h|x = display help. 53 */ 54 #define OPTIONS "Vhvx" 55 56 /* 57 * usage/help message 58 */ 59 char *USAGE = "\nUsage: %s [-v] [-V] [-{h|x}]\n\n\ 60 Where:\n\ 61 \t-v enable verbosity\n\ 62 \t-V display version info\n\ 63 \t-h|x show this usage/help message\n\ 64 \n\ 65 More info - TODO\n\ 66 "; 67 68 /* 69 * die() - emit error message and exit w/ specified return code. 70 * if exit_code < 0, save current errno, and fetch associated 71 * error string. Print error string after app error message. 72 * Then exit with abs(exit_code). 73 */ 74 void die(int exit_code, char *format, ...) 75 { 76 va_list ap; 77 char *errstr; 78 int saverrno; 79 80 va_start(ap, format); 81 82 if (exit_code < 0) { 83 saverrno = errno; 84 errstr = strerror(errno); 85 } 86 87 (void)vfprintf(stderr, format, ap); 88 va_end(ap); 89 90 if (exit_code < 0) 91 fprintf(stderr, "Error = (%d) %s\n", saverrno, errstr); 92 93 exit(abs(exit_code)); 94 } 95 96 void usage(char *mesg) 97 { 98 if (mesg != NULL) { 99 fprintf(stderr, "%s\n", mesg); 100 } 101 fprintf(stderr, USAGE, glctx.program_name); 102 exit(1); 103 } 104 105 #ifdef _DEBUG 106 /* 107 * This function is a wrapper around "fprintf(stderr, ...)" so that we 108 * can use the DPRINTF(<flag>, (<[f]printf arguments>)) macro for debug 109 * prints. See the definition of DPRINTF in XXX.h 110 */ 111 int _dvprintf(char *format, ...) 112 { 113 va_list ap; 114 int retval; 115 116 va_start(ap, format); 117 118 retval = vfprintf(stderr, format, ap); 119 120 va_end(ap); 121 122 fflush(stderr); 123 return (retval); 124 } 125 #endif 126 127 void vprint(char *format, ...) 128 { 129 va_list ap; 130 glctx_t *gcp = &glctx; 131 132 va_start(ap, format); 133 134 if (!is_option(VERBOSE)) 135 goto out; 136 137 (void)vfprintf(stderr, format, ap); 138 fflush(stderr); 139 140 out: 141 va_end(ap); 142 return; 143 144 } 145 146 /* 147 * ========================================================================= 148 */ 149 static int signals_to_handle[] = { 150 SIGINT, SIGQUIT, SIGSEGV, SIGBUS, 151 SIGUSR1, SIGUSR2, 0 152 }; 153 154 static char *sig_names[] = { 155 "SIGINT", "SIGQUIT", "SIGSEGV", "SIGBUS", 156 "SIGUSR1", "SIGUSR2", "unknown", 0 157 }; 158 159 /* 160 * signal_handler() 161 * 162 * save siginfo and name in global context 163 */ 164 void signal_handler(int sig, siginfo_t * info, void *vcontext) 165 { 166 glctx_t *gcp = &glctx; 167 int isig = 0, *sigp = signals_to_handle; 168 static siginfo_t infocopy; 169 170 /* 171 * static copy of signal info. 172 * Note, additional signals, before use, can overwrite 173 */ 174 infocopy = *info; 175 gcp->siginfo = &infocopy; 176 177 while (*sigp) { 178 if (*sigp == sig) 179 break; 180 ++isig; 181 ++sigp; 182 } 183 gcp->signame = sig_names[isig]; 184 185 vprint("signal hander entered for sig %s\n", gcp->signame); 186 187 switch (sig) { 188 case SIGSEGV: 189 case SIGBUS: 190 if (gcp->sigjmp) { 191 gcp->sigjmp = false; 192 siglongjmp(gcp->sigjmp_env, 1); 193 } 194 195 die(8, "\n%s: signal %s, but siglongjmp not armed\n", 196 gcp->program_name, gcp->signame); 197 break; 198 199 case SIGINT: 200 case SIGQUIT: 201 break; 202 203 default: 204 die(8, "\n%s: Unexpected signal: %d\n", 205 gcp->program_name, sig); 206 break; 207 } 208 } 209 210 /* 211 * set_signals() 212 * 213 * Setup signal dispositions to catch selected signals 214 */ 215 void set_signals() 216 { 217 glctx_t *gcp = &glctx; 218 int *sigp = signals_to_handle; 219 char **namep = sig_names; 220 221 struct sigaction act = { 222 .sa_sigaction = signal_handler, 223 .sa_flags = SA_SIGINFO 224 }; 225 226 (void)sigfillset(&(act.sa_mask)); 227 228 while (*sigp) { 229 char *sig_name = *(namep++); 230 int sig = *(sigp++); 231 232 if (0 != sigaction(sig, &act, NULL)) { 233 die(-1, "%s: Failed to set sigaction for %s\n", 234 gcp->program_name, sig_name); 235 } else 236 #if 0 237 vprint("%s: established handler for %s\n", 238 gcp->program_name, sig_name) 239 #endif 240 ; 241 } 242 243 return; 244 } 245 246 void reset_signal(void) 247 { 248 //TODO: free siginfo if/when malloc'd 249 glctx.siginfo = NULL; 250 glctx.sigjmp = false; 251 } 252 253 void wait_for_signal(const char *mesg) 254 { 255 printf("%s ... ", mesg); 256 fflush(stdout); 257 pause(); 258 vprint("%s: wakened by signal %s\n", __FUNCTION__, glctx.signame); 259 reset_signal(); 260 printf("\n"); 261 fflush(stdout); 262 } 263 264 void show_siginfo() 265 { 266 glctx_t *gcp = &glctx; 267 siginfo_t *info = gcp->siginfo; 268 void *badaddr = info->si_addr; 269 char *sigcode; 270 271 switch (info->si_signo) { 272 case SIGSEGV: 273 switch (info->si_code) { 274 case SEGV_MAPERR: 275 sigcode = "address not mapped"; 276 break; 277 278 case SEGV_ACCERR: 279 sigcode = "invalid access error"; 280 break; 281 282 default: 283 sigcode = "unknown"; 284 break; 285 } 286 break; 287 288 case SIGBUS: 289 switch (info->si_code) { 290 case BUS_ADRALN: 291 sigcode = "invalid address alignment"; 292 break; 293 294 case BUS_ADRERR: 295 sigcode = "non-existent physical address"; 296 break; 297 298 default: 299 sigcode = "unknown"; 300 break; 301 } 302 break; 303 304 default: 305 /* 306 * ignore SIGINT/SIGQUIT 307 */ 308 return; 309 } 310 311 printf("Signal %s @ 0x%lx - %s\n", gcp->signame, badaddr, sigcode); 312 313 } 314 315 /* 316 * ========================================================================= 317 */ 318 319 void touch_memory(bool rw, unsigned long *memp, size_t memlen) 320 { 321 glctx_t *gcp = &glctx; 322 323 unsigned long *memend, *pp, sink; 324 unsigned long longs_in_page = gcp->pagesize / sizeof(unsigned long); 325 326 memend = memp + memlen / sizeof(unsigned long); 327 vprint("!!!%s from 0x%lx thru 0x%lx\n", 328 rw ? "Writing" : "Reading", memp, memend); 329 330 for (pp = memp; pp < memend; pp += longs_in_page) { 331 // vprint("%s: touching 0x%lx\n", __FUNCTION__, pp); 332 if (!sigsetjmp(gcp->sigjmp_env, true)) { 333 gcp->sigjmp = true; 334 335 /* 336 * Mah-ahm! He's touching me! 337 */ 338 if (rw) 339 *pp = (unsigned long)pp; 340 else 341 sink = *pp; 342 343 gcp->sigjmp = false; 344 } else { 345 show_siginfo(); 346 reset_signal(); 347 break; 348 } 349 350 /* 351 * Any [handled] signal breaks the loop 352 */ 353 if (gcp->siginfo != NULL) { 354 reset_signal(); 355 break; 356 } 357 } 358 } 359 360 /* 361 * ========================================================================= 362 */ 363 364 void init_glctx(glctx_t * gcp) 365 { 366 367 bzero(gcp, sizeof(glctx_t)); 368 369 gcp->pagesize = (size_t) sysconf(_SC_PAGESIZE); 370 371 if (numa_available() >= 0) { 372 gcp->numa_max_node = numa_max_node(); 373 } else 374 gcp->numa_max_node = -1; 375 376 segment_init(gcp); 377 378 if (isatty(fileno(stdin))) 379 set_option(INTERACTIVE); 380 381 } 382 383 /* 384 * cleanup() - at exit cleanup routine 385 */ 386 static void cleanup() 387 { 388 glctx_t *gcp = &glctx; 389 390 segment_cleanup(gcp); 391 } /* cleanup() */ 392 393 int parse_command_line_args(int argc, char *argv[]) 394 { 395 extern int optind; 396 extern char *optarg; 397 398 glctx_t *gcp = &glctx; 399 int argval; 400 int error = 0; 401 402 char c; 403 404 gcp->program_name = basename(argv[0]); 405 406 /* 407 * process command line options. 408 */ 409 while ((c = getopt(argc, argv, OPTIONS)) != (char)EOF) { 410 char *next; 411 412 switch (c) { 413 414 case 'v': 415 set_option(VERBOSE); 416 break; 417 418 case 'h': 419 case 'x': 420 usage(NULL); 421 422 break; 423 424 case 'V': 425 printf("memtoy " MEMTOY_VERSION " built " 426 __DATE__ " @ " __TIME__ "\n"); 427 exit(0); 428 break; 429 430 #ifdef _DEBUG 431 case '0': 432 argval = strtoul(optarg, &next, 0); 433 if (*next != '\0') { 434 fprintf(stderr, 435 "-D <debug-mask> must be unsigned hex/decimal integer\n"); 436 ++error; 437 } else 438 gcp->debug = argval; 439 break; 440 #endif 441 442 default: 443 error = 1; 444 break; 445 } 446 } 447 done: 448 449 return (error); 450 } 451 452 int main(int argc, char *argv[]) 453 { 454 glctx_t *gcp = &glctx; 455 bool user_is_super; 456 int error; 457 458 init_glctx(gcp); 459 if (!is_option(INTERACTIVE)) 460 setbuf(stdout, NULL); 461 462 /* 463 * Register cleanup handler 464 */ 465 if (atexit(cleanup) != 0) { 466 die(-1, "%s: atexit(cleanup) registration failed\n", argv[0]); 467 } 468 469 user_is_super = (geteuid() == 0); 470 471 error = parse_command_line_args(argc, argv); 472 473 if (error /* || argc==1 */ ) { 474 usage(NULL); 475 476 } 477 478 /* 479 * actual program logic starts here 480 */ 481 printf("memtoy pid: %d\n", getpid()); 482 vprint("%s: pagesize = %d\n", gcp->program_name, gcp->pagesize); 483 if (gcp->numa_max_node >= 0) 484 vprint("%s: NUMA available - max node: %d\n", 485 gcp->program_name, gcp->numa_max_node); 486 487 set_signals(); 488 489 process_commands(); 490 491 return 0; 492 493 } 494 #else /* ! (HAVE_NUMA_H && HAVE_NUMAIF_H) */ 495 int main(void) 496 { 497 printf("System doesn't have required numa support.\n"); 498 return 0; 499 } 500 #endif /* HAVE_NUMA_H && HAVE_NUMAIF_H */ 501