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