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 
     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