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