• 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 
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  */
die(int exit_code,char * format,...)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 
usage(char * mesg)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  */
_dvprintf(char * format,...)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 
vprint(char * format,...)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  */
signal_handler(int sig,siginfo_t * info,void * vcontext)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  */
set_signals()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 
reset_signal(void)249 void reset_signal(void)
250 {
251 //TODO:  free siginfo if/when malloc'd
252 	glctx.siginfo = NULL;
253 	glctx.sigjmp = false;
254 }
255 
wait_for_signal(const char * mesg)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 
show_siginfo()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 
touch_memory(bool rw,unsigned long * memp,size_t memlen)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 
init_glctx(glctx_t * gcp)367 void init_glctx(glctx_t * gcp)
368 {
369 
370 	memset(gcp, 0, 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  */
cleanup()389 static void cleanup()
390 {
391 	glctx_t *gcp = &glctx;
392 
393 	segment_cleanup(gcp);
394 }				/* cleanup() */
395 
parse_command_line_args(int argc,char * argv[])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 
main(int argc,char * argv[])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
main(void)498 int main(void)
499 {
500 	fprintf(stderr, NUMA_ERROR_MSG "\n");
501 	return TCONF;
502 }
503 #endif
504