• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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  */
die(int exit_code,char * format,...)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 
usage(char * mesg)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  */
_dvprintf(char * format,...)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 
vprint(char * format,...)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  */
signal_handler(int sig,siginfo_t * info,void * vcontext)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  */
set_signals()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 
reset_signal(void)246 void reset_signal(void)
247 {
248 //TODO:  free siginfo if/when malloc'd
249 	glctx.siginfo = NULL;
250 	glctx.sigjmp = false;
251 }
252 
wait_for_signal(const char * mesg)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 
show_siginfo()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 
touch_memory(bool rw,unsigned long * memp,size_t memlen)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 
init_glctx(glctx_t * gcp)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  */
cleanup()386 static void cleanup()
387 {
388 	glctx_t *gcp = &glctx;
389 
390 	segment_cleanup(gcp);
391 }				/* cleanup() */
392 
parse_command_line_args(int argc,char * argv[])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 
main(int argc,char * argv[])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) */
main(void)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