• 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 #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  */
die(int exit_code,char * format,...)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 
usage(char * mesg)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  */
_dvprintf(char * format,...)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 
vprint(char * format,...)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  */
signal_handler(int sig,siginfo_t * info,void * vcontext)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  */
set_signals()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 
reset_signal(void)252 void reset_signal(void)
253 {
254 //TODO:  free siginfo if/when malloc'd
255 	glctx.siginfo = NULL;
256 	glctx.sigjmp = false;
257 }
258 
wait_for_signal(const char * mesg)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 
show_siginfo()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 
touch_memory(bool rw,unsigned long * memp,size_t memlen)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 
init_glctx(glctx_t * gcp)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  */
cleanup()392 static void cleanup()
393 {
394 	glctx_t *gcp = &glctx;
395 
396 	segment_cleanup(gcp);
397 }				/* cleanup() */
398 
parse_command_line_args(int argc,char * argv[])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 
main(int argc,char * argv[])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
main(void)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