111
|
1 /* mpxrt-utils.c -*-C++-*-
|
|
2 *
|
|
3 *************************************************************************
|
|
4 *
|
|
5 * @copyright
|
|
6 * Copyright (C) 2014, Intel Corporation
|
|
7 * All rights reserved.
|
|
8 *
|
|
9 * @copyright
|
|
10 * Redistribution and use in source and binary forms, with or without
|
|
11 * modification, are permitted provided that the following conditions
|
|
12 * are met:
|
|
13 *
|
|
14 * * Redistributions of source code must retain the above copyright
|
|
15 * notice, this list of conditions and the following disclaimer.
|
|
16 * * Redistributions in binary form must reproduce the above copyright
|
|
17 * notice, this list of conditions and the following disclaimer in
|
|
18 * the documentation and/or other materials provided with the
|
|
19 * distribution.
|
|
20 * * Neither the name of Intel Corporation nor the names of its
|
|
21 * contributors may be used to endorse or promote products derived
|
|
22 * from this software without specific prior written permission.
|
|
23 *
|
|
24 * @copyright
|
|
25 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
|
26 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
|
27 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
|
28 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
|
29 * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
|
|
30 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
|
31 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
|
|
32 * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
|
|
33 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
|
34 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY
|
|
35 * WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
|
36 * POSSIBILITY OF SUCH DAMAGE.
|
|
37 *
|
|
38 **************************************************************************/
|
|
39
|
|
40 #define __STDC_FORMAT_MACROS
|
|
41 #include "config.h"
|
|
42 #include <inttypes.h>
|
|
43 #include <unistd.h>
|
|
44 #include <stdio.h>
|
|
45 #include <stdarg.h>
|
|
46 #include <stdlib.h>
|
|
47 #include <string.h>
|
|
48 #include <limits.h>
|
|
49 #include <pthread.h>
|
|
50 #include "mpxrt-utils.h"
|
|
51
|
|
52 #ifndef HAVE_SECURE_GETENV
|
|
53 #define secure_getenv __secure_getenv
|
|
54 #endif
|
|
55
|
|
56 #define MPX_RT_OUT "CHKP_RT_OUT_FILE"
|
|
57 #define MPX_RT_ERR "CHKP_RT_ERR_FILE"
|
|
58 #define MPX_RT_VERBOSE "CHKP_RT_VERBOSE"
|
|
59 #define MPX_RT_VERBOSE_DEFAULT VERB_BR
|
|
60 #define MPX_RT_MODE "CHKP_RT_MODE"
|
|
61 #define MPX_RT_MODE_DEFAULT MPX_RT_COUNT
|
|
62 #define MPX_RT_MODE_DEFAULT_STR "count"
|
|
63 #define MPX_RT_STOP_HANDLER "CHKP_RT_STOP_HANDLER"
|
|
64 #define MPX_RT_STOP_HANDLER_DEFAULT MPX_RT_STOP_HANDLER_ABORT
|
|
65 #define MPX_RT_STOP_HANDLER_DEFAULT_STR "abort"
|
|
66 #define MPX_RT_HELP "CHKP_RT_HELP"
|
|
67 #define MPX_RT_ADDPID "CHKP_RT_ADDPID"
|
|
68 #define MPX_RT_BNDPRESERVE "CHKP_RT_BNDPRESERVE"
|
|
69 #define MPX_RT_BNDPRESERVE_DEFAULT 0
|
|
70 #define MPX_RT_PRINT_SUMMARY "CHKP_RT_PRINT_SUMMARY"
|
|
71
|
|
72 #define MAX_FILE_NAME PATH_MAX
|
|
73
|
|
74 typedef struct env_var_s {
|
|
75 char *env_name;
|
|
76 char *env_val;
|
|
77 struct env_var_s *next;
|
|
78 } env_var_t;
|
|
79
|
|
80 typedef struct {
|
|
81 env_var_t *first;
|
|
82 env_var_t *last;
|
|
83 } env_var_list_t;
|
|
84
|
|
85 /* Following vars are initialized at process startup only
|
|
86 and thus are considered to be thread safe. */
|
|
87 static int summary;
|
|
88 static int add_pid;
|
|
89 static mpx_rt_mode_t mode;
|
|
90 static mpx_rt_stop_mode_handler_t stop_handler;
|
|
91 static env_var_list_t env_var_list;
|
|
92 static verbose_type verbose_val;
|
|
93 static FILE *out;
|
|
94 static FILE *err;
|
|
95 static char out_name[MAX_FILE_NAME];
|
|
96 static char err_name[MAX_FILE_NAME];
|
|
97
|
|
98 /* Following vars are read at process finalization only.
|
|
99 All write accesses use the same value and thus are
|
|
100 considered to be thread safe. */
|
|
101 static int out_file_dirty;
|
|
102 static int err_file_dirty;
|
|
103 static int files_overwritten;
|
|
104
|
|
105 /* Mutex used to sync output. */
|
|
106 static pthread_mutex_t lock;
|
|
107
|
|
108 static void *
|
|
109 malloc_check (size_t size)
|
|
110 {
|
|
111 void *res = malloc (size);
|
|
112 if (!res)
|
|
113 __mpxrt_print (VERB_ERROR, "Couldn't allocate %zu bytes.", size);
|
|
114 else
|
|
115 memset (res, 0, size);
|
|
116 return res;
|
|
117 }
|
|
118
|
|
119 static void
|
|
120 env_var_list_add (const char* env, const char* val)
|
|
121 {
|
|
122 env_var_t* n;
|
|
123
|
|
124 if (val == 0)
|
|
125 return;
|
|
126
|
|
127 n = (env_var_t *)malloc_check (sizeof (env_var_t));
|
|
128 if (!n)
|
|
129 return;
|
|
130
|
|
131 if (env_var_list.first == 0)
|
|
132 env_var_list.first = n;
|
|
133
|
|
134 if (env_var_list.last)
|
|
135 env_var_list.last->next = n;
|
|
136
|
|
137 env_var_list.last = n;
|
|
138
|
|
139 n->env_name = (char *)malloc_check (strlen (env) + 1);
|
|
140 n->env_val = (char *)malloc_check (strlen (val) + 1);
|
|
141
|
|
142 if (!n->env_name || !n->env_val)
|
|
143 return;
|
|
144
|
|
145 strcpy (n->env_name, env);
|
|
146 strcpy (n->env_val, val);
|
|
147 }
|
|
148
|
|
149 static void
|
|
150 set_file_stream (FILE** file, char* file_name,
|
|
151 const char* env, FILE* deflt)
|
|
152 {
|
|
153 int pid;
|
|
154 if (env != 0)
|
|
155 {
|
|
156 if (add_pid)
|
|
157 {
|
|
158 pid = getpid ();
|
|
159 snprintf (file_name, MAX_FILE_NAME, "%s.%d", env, pid);
|
|
160 }
|
|
161 else
|
|
162 snprintf (file_name, MAX_FILE_NAME, "%s", env);
|
|
163
|
|
164 *file = fopen (file_name, "we");
|
|
165 if (*file != 0)
|
|
166 return;
|
|
167 }
|
|
168 *file = deflt;
|
|
169 }
|
|
170
|
|
171 /*
|
|
172 * this function will be called after fork in the child
|
|
173 * open new files with pid of the process
|
|
174 */
|
|
175 static void
|
|
176 open_child_files ()
|
|
177 {
|
|
178 char *out_env;
|
|
179 char *err_env;
|
|
180
|
|
181 out_env = secure_getenv (MPX_RT_OUT);
|
|
182 err_env = secure_getenv (MPX_RT_ERR);
|
|
183
|
|
184 if (add_pid == 0 && (out_env != 0 || err_env != 0))
|
|
185 {
|
|
186 __mpxrt_print (VERB_ERROR, "MPX RUNTIME WARNING: out/err files are "
|
|
187 "overwritten in new processes since %s was not set.\n",
|
|
188 MPX_RT_ADDPID);
|
|
189 files_overwritten = 1;
|
|
190 }
|
|
191
|
|
192 set_file_stream (&out, out_name, out_env, stdout);
|
|
193 if (out_env == 0 || err_env == 0 || (strcmp (out_env, err_env) != 0))
|
|
194 set_file_stream (&err, err_name, err_env, stderr);
|
|
195 else
|
|
196 /* in case we get the same file name for err and out */
|
|
197 err = out;
|
|
198 }
|
|
199
|
|
200 /*
|
|
201 * this function is called after fork in the parent
|
|
202 */
|
|
203 static void
|
|
204 at_fork_check (void)
|
|
205 {
|
|
206 char *out_env;
|
|
207 char *err_env;
|
|
208
|
|
209 out_env = secure_getenv (MPX_RT_OUT);
|
|
210 err_env = secure_getenv (MPX_RT_ERR);
|
|
211
|
|
212 if (add_pid == 0 && (out_env != 0 || err_env != 0))
|
|
213 files_overwritten = 1;
|
|
214 }
|
|
215
|
|
216 static mpx_rt_mode_t
|
|
217 set_mpx_rt_mode (const char *env)
|
|
218 {
|
|
219 if (env == 0)
|
|
220 return MPX_RT_MODE_DEFAULT;
|
|
221 else if (strcmp (env, "stop") == 0)
|
|
222 return MPX_RT_STOP;
|
|
223 else if (strcmp (env,"count") == 0)
|
|
224 return MPX_RT_COUNT;
|
|
225 {
|
|
226 __mpxrt_print (VERB_ERROR, "Illegal value '%s' for %s. Legal values are"
|
|
227 "[stop | count]\nUsing default value %s\n",
|
|
228 env, MPX_RT_MODE, MPX_RT_MODE_DEFAULT_STR);
|
|
229 return MPX_RT_MODE_DEFAULT;
|
|
230 }
|
|
231 }
|
|
232
|
|
233 static mpx_rt_stop_mode_handler_t
|
|
234 set_mpx_rt_stop_handler (const char *env)
|
|
235 {
|
|
236 if (env == 0)
|
|
237 return MPX_RT_STOP_HANDLER_DEFAULT;
|
|
238 else if (strcmp (env, "abort") == 0)
|
|
239 return MPX_RT_STOP_HANDLER_ABORT;
|
|
240 else if (strcmp (env, "exit") == 0)
|
|
241 return MPX_RT_STOP_HANDLER_EXIT;
|
|
242 {
|
|
243 __mpxrt_print (VERB_ERROR, "Illegal value '%s' for %s. Legal values are"
|
|
244 "[abort | exit]\nUsing default value %s\n",
|
|
245 env, MPX_RT_STOP_HANDLER, MPX_RT_STOP_HANDLER_DEFAULT);
|
|
246 return MPX_RT_STOP_HANDLER_DEFAULT;
|
|
247 }
|
|
248 }
|
|
249
|
|
250 static void
|
|
251 print_help (void)
|
|
252 {
|
|
253 fprintf (out, "MPX Runtime environment variables help.\n");
|
|
254
|
|
255 fprintf (out, "%s \t set output file for info & debug [default: stdout]\n",
|
|
256 MPX_RT_OUT);
|
|
257 fprintf (out, "%s \t set output file for error [default: stderr]\n",
|
|
258 MPX_RT_ERR);
|
|
259 fprintf (out, "%s \t set verbosity type [default: %d]\n"
|
|
260 "\t\t\t 0 - print only internal run time errors\n"
|
|
261 "\t\t\t 1 - just print summary\n"
|
|
262 "\t\t\t 2 - print summary and bound violation information\n "
|
|
263 "\t\t\t 3 - print debug information\n",
|
|
264 MPX_RT_VERBOSE, MPX_RT_VERBOSE_DEFAULT);
|
|
265 fprintf (out, "%s \t\t set MPX runtime behavior on #BR exception."
|
|
266 " [stop | count]\n"
|
|
267 "\t\t\t [default: %s]\n", MPX_RT_MODE, MPX_RT_MODE_DEFAULT_STR);
|
|
268 fprintf (out, "%s \t set the handler function MPX runtime will call\n"
|
|
269 "\t\t\t on #BR exception when %s is set to \'stop\'."
|
|
270 " [abort | exit]\n"
|
|
271 "\t\t\t [default: %s]\n", MPX_RT_STOP_HANDLER, MPX_RT_MODE,
|
|
272 MPX_RT_STOP_HANDLER_DEFAULT_STR);
|
|
273 fprintf (out, "%s \t\t generate out,err file for each process.\n"
|
|
274 "\t\t\t generated file will be MPX_RT_{OUT,ERR}_FILE.pid\n"
|
|
275 "\t\t\t [default: no]\n", MPX_RT_ADDPID);
|
|
276 fprintf (out, "%s \t set value for BNDPRESERVE bit.\n"
|
|
277 "\t\t\t BNDPRESERVE = 0 flush bounds on unprefixed call/ret/jmp\n"
|
|
278 "\t\t\t BNDPRESERVE = 1 do NOT flush bounds\n"
|
|
279 "\t\t\t [default: %d]\n", MPX_RT_BNDPRESERVE,
|
|
280 MPX_RT_BNDPRESERVE_DEFAULT);
|
|
281 fprintf (out, "%s \t print summary at the end of the run\n"
|
|
282 "\t\t\t [default: no]\n", MPX_RT_PRINT_SUMMARY);
|
|
283
|
|
284 fprintf (out, "%s \t\t print this help and exit.\n"
|
|
285 "\t\t\t [default: no]\n", MPX_RT_HELP);
|
|
286
|
|
287 exit (0);
|
|
288 }
|
|
289
|
|
290 static void
|
|
291 validate_bndpreserve (const char *env, int *bndpreserve)
|
|
292 {
|
|
293 if (env == 0)
|
|
294 bndpreserve = MPX_RT_BNDPRESERVE_DEFAULT;
|
|
295 else if (strcmp (env, "0") == 0)
|
|
296 *bndpreserve = 0;
|
|
297 else if (strcmp (env, "1") == 0)
|
|
298 *bndpreserve = 1;
|
|
299 else
|
|
300 {
|
|
301 __mpxrt_print (VERB_ERROR, "Illegal value '%s' for %s. Legal values "
|
|
302 "are [0 | 1]\nUsing default value %d\n",
|
|
303 env, MPX_RT_BNDPRESERVE, MPX_RT_BNDPRESERVE_DEFAULT);
|
|
304 *bndpreserve = MPX_RT_BNDPRESERVE_DEFAULT;
|
|
305 }
|
|
306 }
|
|
307
|
|
308 static verbose_type
|
|
309 init_verbose_val (const char *env)
|
|
310 {
|
|
311 if (env == 0)
|
|
312 return MPX_RT_VERBOSE_DEFAULT;
|
|
313 else if (strcmp(env, "0") == 0)
|
|
314 return VERB_ERROR;
|
|
315 else if (strcmp(env, "1") == 0)
|
|
316 return VERB_INFO;
|
|
317 else if (strcmp(env, "2") == 0)
|
|
318 return VERB_BR;
|
|
319 else if (strcmp(env, "3") == 0)
|
|
320 return VERB_DEBUG;
|
|
321
|
|
322 __mpxrt_print (VERB_ERROR, "Illegal value '%s' for %s. Legal values "
|
|
323 "are [0..3]\nUsing default value %d\n",
|
|
324 env, MPX_RT_VERBOSE, (int)MPX_RT_VERBOSE_DEFAULT);
|
|
325
|
|
326 return MPX_RT_VERBOSE_DEFAULT;
|
|
327 }
|
|
328
|
|
329 static void
|
|
330 env_var_print_summary (void)
|
|
331 {
|
|
332 env_var_t* node;
|
|
333
|
|
334 __mpxrt_print (VERB_DEBUG, "Used environment variables:\n");
|
|
335
|
|
336 node = env_var_list.first;
|
|
337 while (node != 0)
|
|
338 {
|
|
339 __mpxrt_print (VERB_DEBUG, " %s = %s\n", node->env_name, node->env_val);
|
|
340 node = node->next;
|
|
341 }
|
|
342 }
|
|
343
|
|
344 /* Return 1 if passes env var value should enable feature. */
|
|
345
|
|
346 static int
|
|
347 check_yes (const char *val)
|
|
348 {
|
|
349 return val && (!strcmp (val, "yes") || !strcmp (val, "1"));
|
|
350 }
|
|
351
|
|
352 void
|
|
353 __mpxrt_init_env_vars (int* bndpreserve)
|
|
354 {
|
|
355 char *out_env;
|
|
356 char *err_env;
|
|
357 char *env;
|
|
358
|
|
359 pthread_mutex_init (&lock, NULL);
|
|
360
|
|
361 out_env = secure_getenv (MPX_RT_OUT);
|
|
362 env_var_list_add (MPX_RT_OUT, out_env);
|
|
363
|
|
364 err_env = secure_getenv (MPX_RT_ERR);
|
|
365 env_var_list_add (MPX_RT_ERR, err_env);
|
|
366
|
|
367 env = secure_getenv (MPX_RT_ADDPID);
|
|
368 env_var_list_add (MPX_RT_ADDPID, env);
|
|
369 add_pid = check_yes (env);
|
|
370
|
|
371 set_file_stream (&out, out_name, out_env, stdout);
|
|
372 if (out_env == 0 || err_env == 0 || (strcmp (out_env, err_env) != 0))
|
|
373 set_file_stream (&err, err_name, err_env, stderr);
|
|
374 else
|
|
375 /* in case we get the same file name for err and out */
|
|
376 err = out;
|
|
377
|
|
378 env = secure_getenv (MPX_RT_VERBOSE);
|
|
379 env_var_list_add (MPX_RT_VERBOSE, env);
|
|
380 verbose_val = init_verbose_val (env);
|
|
381
|
|
382 env = secure_getenv (MPX_RT_MODE);
|
|
383 env_var_list_add (MPX_RT_MODE, env);
|
|
384 mode = set_mpx_rt_mode (env);
|
|
385
|
|
386 env = secure_getenv (MPX_RT_STOP_HANDLER);
|
|
387 env_var_list_add (MPX_RT_STOP_HANDLER, env);
|
|
388 stop_handler = set_mpx_rt_stop_handler (env);
|
|
389
|
|
390 env = secure_getenv (MPX_RT_BNDPRESERVE);
|
|
391 env_var_list_add (MPX_RT_BNDPRESERVE, env);
|
|
392 validate_bndpreserve (env, bndpreserve);
|
|
393
|
|
394 env = secure_getenv (MPX_RT_PRINT_SUMMARY);
|
|
395 env_var_list_add (MPX_RT_PRINT_SUMMARY, env);
|
|
396 summary = check_yes (env);
|
|
397
|
|
398 env = secure_getenv (MPX_RT_HELP);
|
|
399 if (check_yes (env))
|
|
400 print_help ();
|
|
401
|
|
402 /*
|
|
403 * at fork - create new files for output and err according
|
|
404 * to the env vars.
|
|
405 */
|
|
406 pthread_atfork (NULL, at_fork_check, open_child_files);
|
|
407
|
|
408 env_var_print_summary ();
|
|
409 }
|
|
410
|
|
411 void
|
|
412 __mpxrt_utils_free (void)
|
|
413 {
|
|
414 if (files_overwritten)
|
|
415 __mpxrt_print (VERB_INFO, "\nMPX RUNTIME WARNING: out/err files are"
|
|
416 " overwritten in new processes since %s was not set.\n",
|
|
417 MPX_RT_ADDPID);
|
|
418
|
|
419 if (out != stdout)
|
|
420 {
|
|
421 fclose (out);
|
|
422 if (out_file_dirty != 1)
|
|
423 remove (out_name);
|
|
424 }
|
|
425
|
|
426 if (err != stderr)
|
|
427 {
|
|
428 fclose (err);
|
|
429 if (err_file_dirty != 1)
|
|
430 remove (err_name);
|
|
431 }
|
|
432
|
|
433 pthread_mutex_destroy (&lock);
|
|
434 }
|
|
435
|
|
436 void
|
|
437 __mpxrt_write_uint (verbose_type vt, uint64_t val, unsigned base)
|
|
438 {
|
|
439 static const char digits[] = {
|
|
440 '0', '1', '2', '3', '4', '5', '6', '7',
|
|
441 '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' };
|
|
442 char str[65];
|
|
443 int pos = 64;;
|
|
444
|
|
445 str[pos--] = 0;
|
|
446
|
|
447 if (vt > verbose_val || base <= 1 || base > sizeof (digits))
|
|
448 return;
|
|
449
|
|
450 if (val < base)
|
|
451 str[pos--] = digits[val];
|
|
452 else
|
|
453 while (val)
|
|
454 {
|
|
455 str[pos--] = digits[val % base];
|
|
456 val = val / base;
|
|
457 }
|
|
458
|
|
459 __mpxrt_write (vt, str + pos + 1);
|
|
460 }
|
|
461
|
|
462 void
|
|
463 __mpxrt_write (verbose_type vt, const char* str)
|
|
464 {
|
|
465 va_list argp;
|
|
466 FILE *print_to;
|
|
467
|
|
468 if (vt > verbose_val)
|
|
469 return;
|
|
470
|
|
471 if (vt == VERB_ERROR)
|
|
472 {
|
|
473 print_to = err;
|
|
474 err_file_dirty = 1;
|
|
475 }
|
|
476 else
|
|
477 {
|
|
478 print_to = out;
|
|
479 out_file_dirty = 1;
|
|
480 }
|
|
481 pthread_mutex_lock (&lock);
|
|
482 write (fileno (print_to), str, strlen (str));
|
|
483 pthread_mutex_unlock (&lock);
|
|
484 va_end (argp);
|
|
485 }
|
|
486
|
|
487 void
|
|
488 __mpxrt_print (verbose_type vt, const char* frmt, ...)
|
|
489 {
|
|
490 va_list argp;
|
|
491 FILE *print_to;
|
|
492
|
|
493 if (vt > verbose_val)
|
|
494 return;
|
|
495
|
|
496 va_start (argp, frmt);
|
|
497 if (vt == VERB_ERROR)
|
|
498 {
|
|
499 print_to = err;
|
|
500 err_file_dirty = 1;
|
|
501 }
|
|
502 else
|
|
503 {
|
|
504 print_to = out;
|
|
505 out_file_dirty = 1;
|
|
506 }
|
|
507 pthread_mutex_lock (&lock);
|
|
508 vfprintf (print_to, frmt, argp);
|
|
509 fflush (print_to);
|
|
510 pthread_mutex_unlock (&lock);
|
|
511 va_end (argp);
|
|
512 }
|
|
513
|
|
514 mpx_rt_mode_t
|
|
515 __mpxrt_mode (void)
|
|
516 {
|
|
517 return mode;
|
|
518 }
|
|
519
|
|
520 mpx_rt_mode_t
|
|
521 __mpxrt_stop_handler (void)
|
|
522 {
|
|
523 return stop_handler;
|
|
524 }
|
|
525
|
|
526 void __attribute__ ((noreturn))
|
|
527 __mpxrt_stop (void)
|
|
528 {
|
|
529 if (__mpxrt_stop_handler () == MPX_RT_STOP_HANDLER_ABORT)
|
|
530 abort ();
|
|
531 else if (__mpxrt_stop_handler () == MPX_RT_STOP_HANDLER_EXIT)
|
|
532 exit (255);
|
|
533 __builtin_unreachable ();
|
|
534 }
|
|
535
|
|
536 void
|
|
537 __mpxrt_print_summary (uint64_t num_brs, uint64_t l1_size)
|
|
538 {
|
|
539
|
|
540 if (summary == 0)
|
|
541 return;
|
|
542
|
|
543 out_file_dirty = 1;
|
|
544
|
|
545 pthread_mutex_lock (&lock);
|
|
546 fprintf (out, "MPX runtime summary:\n");
|
|
547 fprintf (out, " Number of bounds violations: %" PRIu64 ".\n", num_brs);
|
|
548 fprintf (out, " Size of allocated L1: %" PRIu64 "B\n", l1_size);
|
|
549 fflush (out);
|
|
550 pthread_mutex_unlock (&lock);
|
|
551 }
|