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