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_HELP "CHKP_RT_HELP"
64#define MPX_RT_ADDPID "CHKP_RT_ADDPID"
65#define MPX_RT_BNDPRESERVE "CHKP_RT_BNDPRESERVE"
66#define MPX_RT_BNDPRESERVE_DEFAULT 0
67#define MPX_RT_PRINT_SUMMARY "CHKP_RT_PRINT_SUMMARY"
68
69#define MAX_FILE_NAME PATH_MAX
70
71typedef struct env_var_s {
72  char *env_name;
73  char *env_val;
74  struct env_var_s *next;
75} env_var_t;
76
77typedef struct {
78  env_var_t *first;
79  env_var_t *last;
80} env_var_list_t;
81
82/* Following vars are initialized at process startup only
83   and thus are considered to be thread safe.  */
84static int summary;
85static int add_pid;
86static mpx_rt_mode_t mode;
87static env_var_list_t env_var_list;
88static verbose_type verbose_val;
89static FILE *out;
90static FILE *err;
91static char out_name[MAX_FILE_NAME];
92static char err_name[MAX_FILE_NAME];
93
94/* Following vars are read at process finalization only.
95   All write accesses use the same value and thus are
96   considered to be thread safe.  */
97static int out_file_dirty;
98static int err_file_dirty;
99static int files_overwritten;
100
101/* Mutex used to sync output.  */
102static pthread_mutex_t lock;
103
104static void *
105malloc_check (size_t size)
106{
107  void *res = malloc (size);
108  if (!res)
109    __mpxrt_print (VERB_ERROR, "Couldn't allocate %zu bytes.", size);
110  else
111    memset (res, 0, size);
112  return res;
113}
114
115static void
116env_var_list_add (const char* env, const char* val)
117{
118  env_var_t* n;
119
120  if (val == 0)
121    return;
122
123  n = (env_var_t *)malloc_check (sizeof (env_var_t));
124  if (!n)
125    return;
126
127  if (env_var_list.first == 0)
128    env_var_list.first = n;
129
130  if (env_var_list.last)
131    env_var_list.last->next = n;
132
133  env_var_list.last = n;
134
135  n->env_name = (char *)malloc_check (strlen (env) + 1);
136  n->env_val = (char *)malloc_check (strlen (val) + 1);
137
138  if (!n->env_name || !n->env_val)
139    return;
140
141  strcpy (n->env_name, env);
142  strcpy (n->env_val, val);
143}
144
145static void
146set_file_stream (FILE** file, char* file_name,
147		 const char* env, FILE* deflt)
148{
149  int pid;
150  if (env != 0)
151    {
152      if (add_pid)
153	{
154	  pid = getpid ();
155	  snprintf (file_name, MAX_FILE_NAME, "%s.%d", env, pid);
156	}
157      else
158	snprintf (file_name, MAX_FILE_NAME, "%s", env);
159
160      *file = fopen (file_name, "we");
161      if (*file != 0)
162	return;
163    }
164  *file = deflt;
165}
166
167/*
168 * this function will be called after fork in the child
169 * open new files with pid of the process
170 */
171static void
172open_child_files ()
173{
174  char *out_env;
175  char *err_env;
176
177  out_env = secure_getenv (MPX_RT_OUT);
178  err_env = secure_getenv (MPX_RT_ERR);
179
180  if (add_pid == 0 && (out_env != 0 || err_env != 0))
181    {
182      __mpxrt_print (VERB_ERROR, "MPX RUNTIME WARNING: out/err files are "
183		     "overwritten in new processes since %s was not set.\n",
184		     MPX_RT_ADDPID);
185      files_overwritten = 1;
186    }
187
188  set_file_stream (&out, out_name, out_env, stdout);
189  if (out_env == 0 || err_env == 0 || (strcmp (out_env, err_env) != 0))
190    set_file_stream (&err, err_name, err_env, stderr);
191  else
192    /* in case we get the same file name for err and out */
193    err = out;
194}
195
196/*
197 * this function is called after fork in the parent
198 */
199static void
200at_fork_check (void)
201{
202  char *out_env;
203  char *err_env;
204
205  out_env = secure_getenv (MPX_RT_OUT);
206  err_env = secure_getenv (MPX_RT_ERR);
207
208  if (add_pid == 0 && (out_env != 0 || err_env != 0))
209    files_overwritten = 1;
210}
211
212static mpx_rt_mode_t
213set_mpx_rt_mode (const char *env)
214{
215  if (env == 0)
216    return MPX_RT_MODE_DEFAULT;
217  else if (strcmp (env, "stop") == 0)
218    return MPX_RT_STOP;
219  else if (strcmp (env,"count") == 0)
220    return MPX_RT_COUNT;
221  {
222    __mpxrt_print (VERB_ERROR, "Illegal value '%s' for %s. Legal values are"
223		   "[stop | count]\nUsing default value %s\n",
224		   env, MPX_RT_MODE, MPX_RT_MODE_DEFAULT_STR);
225    return MPX_RT_MODE_DEFAULT;
226  }
227}
228
229static void
230print_help (void)
231{
232  fprintf (out, "MPX Runtime environment variables help.\n");
233
234  fprintf (out, "%s \t set output file for info & debug [default: stdout]\n",
235	   MPX_RT_OUT);
236  fprintf (out, "%s \t set output file for error [default: stderr]\n",
237	   MPX_RT_ERR);
238  fprintf (out, "%s \t set verbosity type [default: %d]\n"
239	   "\t\t\t 0 - print only internal run time errors\n"
240	   "\t\t\t 1 - just print summary\n"
241	   "\t\t\t 2 - print summary and bound violation information\n "
242	   "\t\t\t 3 - print debug information\n",
243	   MPX_RT_VERBOSE, MPX_RT_VERBOSE_DEFAULT);
244  fprintf (out, "%s \t\t set MPX runtime behavior on #BR exception."
245	   " [stop | count]\n"
246	   "\t\t\t [default: %s]\n", MPX_RT_MODE, MPX_RT_MODE_DEFAULT_STR);
247  fprintf (out, "%s \t\t generate out,err file for each process.\n"
248	   "\t\t\t generated file will be MPX_RT_{OUT,ERR}_FILE.pid\n"
249	   "\t\t\t [default: no]\n", MPX_RT_ADDPID);
250  fprintf (out, "%s \t set value for BNDPRESERVE bit.\n"
251	   "\t\t\t BNDPRESERVE = 0 flush bounds on unprefixed call/ret/jmp\n"
252	   "\t\t\t BNDPRESERVE = 1 do NOT flush bounds\n"
253	   "\t\t\t [default: %d]\n", MPX_RT_BNDPRESERVE,
254	   MPX_RT_BNDPRESERVE_DEFAULT);
255  fprintf (out, "%s \t print summary at the end of the run\n"
256	   "\t\t\t [default: no]\n", MPX_RT_PRINT_SUMMARY);
257
258  fprintf (out, "%s \t\t print this help and exit.\n"
259	   "\t\t\t [default: no]\n", MPX_RT_HELP);
260
261  exit (0);
262}
263
264static void
265validate_bndpreserve (const char *env, int *bndpreserve)
266{
267  if (env == 0)
268    bndpreserve = MPX_RT_BNDPRESERVE_DEFAULT;
269  else if (strcmp (env, "0") == 0)
270    *bndpreserve = 0;
271  else if (strcmp (env, "1") == 0)
272    *bndpreserve = 1;
273  else
274    {
275      __mpxrt_print (VERB_ERROR, "Illegal value '%s' for %s. Legal values "
276		     "are [0 | 1]\nUsing default value %d\n",
277		     env, MPX_RT_BNDPRESERVE, MPX_RT_BNDPRESERVE_DEFAULT);
278      *bndpreserve = MPX_RT_BNDPRESERVE_DEFAULT;
279    }
280}
281
282static verbose_type
283init_verbose_val (const char *env)
284{
285  if (env == 0)
286    return MPX_RT_VERBOSE_DEFAULT;
287  else if (strcmp(env, "0") == 0)
288    return VERB_ERROR;
289  else if (strcmp(env, "1") == 0)
290    return VERB_INFO;
291  else if (strcmp(env, "2") == 0)
292    return VERB_BR;
293  else if (strcmp(env, "3") == 0)
294    return VERB_DEBUG;
295
296  __mpxrt_print (VERB_ERROR, "Illegal value '%s' for %s. Legal values "
297		 "are [0..3]\nUsing default value %d\n",
298		 env, MPX_RT_VERBOSE, (int)MPX_RT_VERBOSE_DEFAULT);
299
300  return MPX_RT_VERBOSE_DEFAULT;
301}
302
303static void
304env_var_print_summary (void)
305{
306  env_var_t* node;
307
308  __mpxrt_print (VERB_DEBUG, "Used environment variables:\n");
309
310  node = env_var_list.first;
311  while (node != 0)
312    {
313      __mpxrt_print (VERB_DEBUG, "  %s = %s\n", node->env_name, node->env_val);
314      node = node->next;
315    }
316}
317
318/* Return 1 if passes env var value should enable feature.  */
319
320static int
321check_yes (const char *val)
322{
323  return val && (!strcmp (val, "yes") || !strcmp (val, "1"));
324}
325
326void
327__mpxrt_init_env_vars (int* bndpreserve)
328{
329  char *out_env;
330  char *err_env;
331  char *env;
332
333  pthread_mutex_init (&lock, NULL);
334
335  out_env = secure_getenv (MPX_RT_OUT);
336  env_var_list_add (MPX_RT_OUT, out_env);
337
338  err_env = secure_getenv (MPX_RT_ERR);
339  env_var_list_add (MPX_RT_ERR, err_env);
340
341  env = secure_getenv (MPX_RT_ADDPID);
342  env_var_list_add (MPX_RT_ADDPID, env);
343  add_pid = check_yes (env);
344
345  set_file_stream (&out, out_name, out_env, stdout);
346  if (out_env == 0 || err_env == 0 || (strcmp (out_env, err_env) != 0))
347    set_file_stream (&err, err_name, err_env, stderr);
348  else
349    /* in case we get the same file name for err and out */
350    err = out;
351
352  env = secure_getenv (MPX_RT_VERBOSE);
353  env_var_list_add (MPX_RT_VERBOSE, env);
354  verbose_val = init_verbose_val (env);
355
356  env = secure_getenv (MPX_RT_MODE);
357  env_var_list_add (MPX_RT_MODE, env);
358  mode = set_mpx_rt_mode (env);
359
360  env = secure_getenv (MPX_RT_BNDPRESERVE);
361  env_var_list_add (MPX_RT_BNDPRESERVE, env);
362  validate_bndpreserve (env, bndpreserve);
363
364  env = secure_getenv (MPX_RT_PRINT_SUMMARY);
365  env_var_list_add (MPX_RT_PRINT_SUMMARY, env);
366  summary = check_yes (env);
367
368  env = secure_getenv (MPX_RT_HELP);
369  if (check_yes (env))
370    print_help ();
371
372  /*
373   * at fork - create new files for output and err according
374   * to the env vars.
375   */
376  pthread_atfork (NULL, at_fork_check, open_child_files);
377
378  env_var_print_summary ();
379}
380
381void
382__mpxrt_utils_free (void)
383{
384  if (files_overwritten)
385    __mpxrt_print (VERB_INFO, "\nMPX RUNTIME WARNING: out/err files are"
386		   " overwritten in new processes since %s was not set.\n",
387		   MPX_RT_ADDPID);
388
389  if (out != stdout)
390    {
391      fclose (out);
392      if (out_file_dirty != 1)
393	remove (out_name);
394    }
395
396  if (err != stderr)
397    {
398      fclose (err);
399      if (err_file_dirty != 1)
400	remove (err_name);
401    }
402
403  pthread_mutex_destroy (&lock);
404}
405
406void
407__mpxrt_write_uint (verbose_type vt, uint64_t val, unsigned base)
408{
409  static const char digits[] = {
410    '0', '1', '2', '3', '4', '5', '6', '7',
411    '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' };
412  char str[65];
413  int pos = 64;;
414
415  str[pos--] = 0;
416
417  if (vt > verbose_val || base <= 1 || base > sizeof (digits))
418    return;
419
420  if (val < base)
421    str[pos--] = digits[val];
422  else
423    while (val)
424      {
425	str[pos--] = digits[val % base];
426	val = val / base;
427      }
428
429  __mpxrt_write (vt, str + pos + 1);
430}
431
432void
433__mpxrt_write (verbose_type vt, const char* str)
434{
435  va_list argp;
436  FILE *print_to;
437
438  if (vt > verbose_val)
439    return;
440
441  if (vt == VERB_ERROR)
442    {
443      print_to = err;
444      err_file_dirty = 1;
445    }
446  else
447    {
448      print_to = out;
449      out_file_dirty = 1;
450    }
451  pthread_mutex_lock (&lock);
452  write (fileno (print_to), str, strlen (str));
453  pthread_mutex_unlock (&lock);
454  va_end (argp);
455}
456
457void
458__mpxrt_print (verbose_type vt, const char* frmt, ...)
459{
460  va_list argp;
461  FILE *print_to;
462
463  if (vt > verbose_val)
464    return;
465
466  va_start (argp, frmt);
467  if (vt == VERB_ERROR)
468    {
469      print_to = err;
470      err_file_dirty = 1;
471    }
472  else
473    {
474      print_to = out;
475      out_file_dirty = 1;
476    }
477  pthread_mutex_lock (&lock);
478  vfprintf (print_to, frmt, argp);
479  fflush (print_to);
480  pthread_mutex_unlock (&lock);
481  va_end (argp);
482}
483
484mpx_rt_mode_t
485__mpxrt_mode (void)
486{
487  return mode;
488}
489
490void
491__mpxrt_print_summary (uint64_t num_brs, uint64_t l1_size)
492{
493
494  if (summary == 0)
495    return;
496
497  out_file_dirty = 1;
498
499  pthread_mutex_lock (&lock);
500  fprintf (out, "MPX runtime summary:\n");
501  fprintf (out, "  Number of bounds violations: %" PRIu64 ".\n", num_brs);
502  fprintf (out, "  Size of allocated L1: %" PRIu64 "B\n", l1_size);
503  fflush (out);
504  pthread_mutex_unlock (&lock);
505}
506