Home | History | Annotate | Download | only in memory_hotplug
      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