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