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