blob: 057a3553cb5b1f03eaeca64d42c5e72164b50922 [file] [log] [blame]
/* mpxrt-utils.c -*-C++-*-
*
*************************************************************************
*
* @copyright
* Copyright (C) 2014, Intel Corporation
* All rights reserved.
*
* @copyright
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
* * Neither the name of Intel Corporation nor the names of its
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* @copyright
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY
* WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
**************************************************************************/
#define __STDC_FORMAT_MACROS
#include "config.h"
#include <inttypes.h>
#include <unistd.h>
#include <stdio.h>
#include <stdarg.h>
#include <stdlib.h>
#include <string.h>
#include <limits.h>
#include <pthread.h>
#include "mpxrt-utils.h"
#ifndef HAVE_SECURE_GETENV
#define secure_getenv __secure_getenv
#endif
#define MPX_RT_OUT "CHKP_RT_OUT_FILE"
#define MPX_RT_ERR "CHKP_RT_ERR_FILE"
#define MPX_RT_VERBOSE "CHKP_RT_VERBOSE"
#define MPX_RT_VERBOSE_DEFAULT VERB_BR
#define MPX_RT_MODE "CHKP_RT_MODE"
#define MPX_RT_MODE_DEFAULT MPX_RT_COUNT
#define MPX_RT_MODE_DEFAULT_STR "count"
#define MPX_RT_HELP "CHKP_RT_HELP"
#define MPX_RT_ADDPID "CHKP_RT_ADDPID"
#define MPX_RT_BNDPRESERVE "CHKP_RT_BNDPRESERVE"
#define MPX_RT_BNDPRESERVE_DEFAULT 0
#define MPX_RT_PRINT_SUMMARY "CHKP_RT_PRINT_SUMMARY"
#define MAX_FILE_NAME PATH_MAX
typedef struct env_var_s {
char *env_name;
char *env_val;
struct env_var_s *next;
} env_var_t;
typedef struct {
env_var_t *first;
env_var_t *last;
} env_var_list_t;
/* Following vars are initialized at process startup only
and thus are considered to be thread safe. */
static int summary;
static int add_pid;
static mpx_rt_mode_t mode;
static env_var_list_t env_var_list;
static verbose_type verbose_val;
static FILE *out;
static FILE *err;
static char out_name[MAX_FILE_NAME];
static char err_name[MAX_FILE_NAME];
/* Following vars are read at process finalization only.
All write accesses use the same value and thus are
considered to be thread safe. */
static int out_file_dirty;
static int err_file_dirty;
static int files_overwritten;
/* Mutex used to sync output. */
static pthread_mutex_t lock;
static void *
malloc_check (size_t size)
{
void *res = malloc (size);
if (!res)
__mpxrt_print (VERB_ERROR, "Couldn't allocate %zu bytes.", size);
else
memset (res, 0, size);
return res;
}
static void
env_var_list_add (const char* env, const char* val)
{
env_var_t* n;
if (val == 0)
return;
n = (env_var_t *)malloc_check (sizeof (env_var_t));
if (!n)
return;
if (env_var_list.first == 0)
env_var_list.first = n;
if (env_var_list.last)
env_var_list.last->next = n;
env_var_list.last = n;
n->env_name = (char *)malloc_check (strlen (env) + 1);
n->env_val = (char *)malloc_check (strlen (val) + 1);
if (!n->env_name || !n->env_val)
return;
strcpy (n->env_name, env);
strcpy (n->env_val, val);
}
static void
set_file_stream (FILE** file, char* file_name,
const char* env, FILE* deflt)
{
int pid;
if (env != 0)
{
if (add_pid)
{
pid = getpid ();
snprintf (file_name, MAX_FILE_NAME, "%s.%d", env, pid);
}
else
snprintf (file_name, MAX_FILE_NAME, "%s", env);
*file = fopen (file_name, "we");
if (*file != 0)
return;
}
*file = deflt;
}
/*
* this function will be called after fork in the child
* open new files with pid of the process
*/
static void
open_child_files ()
{
char *out_env;
char *err_env;
out_env = secure_getenv (MPX_RT_OUT);
err_env = secure_getenv (MPX_RT_ERR);
if (add_pid == 0 && (out_env != 0 || err_env != 0))
{
__mpxrt_print (VERB_ERROR, "MPX RUNTIME WARNING: out/err files are "
"overwritten in new processes since %s was not set.\n",
MPX_RT_ADDPID);
files_overwritten = 1;
}
set_file_stream (&out, out_name, out_env, stdout);
if (out_env == 0 || err_env == 0 || (strcmp (out_env, err_env) != 0))
set_file_stream (&err, err_name, err_env, stderr);
else
/* in case we get the same file name for err and out */
err = out;
}
/*
* this function is called after fork in the parent
*/
static void
at_fork_check (void)
{
char *out_env;
char *err_env;
out_env = secure_getenv (MPX_RT_OUT);
err_env = secure_getenv (MPX_RT_ERR);
if (add_pid == 0 && (out_env != 0 || err_env != 0))
files_overwritten = 1;
}
static mpx_rt_mode_t
set_mpx_rt_mode (const char *env)
{
if (env == 0)
return MPX_RT_MODE_DEFAULT;
else if (strcmp (env, "stop") == 0)
return MPX_RT_STOP;
else if (strcmp (env,"count") == 0)
return MPX_RT_COUNT;
{
__mpxrt_print (VERB_ERROR, "Illegal value '%s' for %s. Legal values are"
"[stop | count]\nUsing default value %s\n",
env, MPX_RT_MODE, MPX_RT_MODE_DEFAULT_STR);
return MPX_RT_MODE_DEFAULT;
}
}
static void
print_help (void)
{
fprintf (out, "MPX Runtime environment variables help.\n");
fprintf (out, "%s \t set output file for info & debug [default: stdout]\n",
MPX_RT_OUT);
fprintf (out, "%s \t set output file for error [default: stderr]\n",
MPX_RT_ERR);
fprintf (out, "%s \t set verbosity type [default: %d]\n"
"\t\t\t 0 - print only internal run time errors\n"
"\t\t\t 1 - just print summary\n"
"\t\t\t 2 - print summary and bound violation information\n "
"\t\t\t 3 - print debug information\n",
MPX_RT_VERBOSE, MPX_RT_VERBOSE_DEFAULT);
fprintf (out, "%s \t\t set MPX runtime behavior on #BR exception."
" [stop | count]\n"
"\t\t\t [default: %s]\n", MPX_RT_MODE, MPX_RT_MODE_DEFAULT_STR);
fprintf (out, "%s \t\t generate out,err file for each process.\n"
"\t\t\t generated file will be MPX_RT_{OUT,ERR}_FILE.pid\n"
"\t\t\t [default: no]\n", MPX_RT_ADDPID);
fprintf (out, "%s \t set value for BNDPRESERVE bit.\n"
"\t\t\t BNDPRESERVE = 0 flush bounds on unprefixed call/ret/jmp\n"
"\t\t\t BNDPRESERVE = 1 do NOT flush bounds\n"
"\t\t\t [default: %d]\n", MPX_RT_BNDPRESERVE,
MPX_RT_BNDPRESERVE_DEFAULT);
fprintf (out, "%s \t print summary at the end of the run\n"
"\t\t\t [default: no]\n", MPX_RT_PRINT_SUMMARY);
fprintf (out, "%s \t\t print this help and exit.\n"
"\t\t\t [default: no]\n", MPX_RT_HELP);
exit (0);
}
static void
validate_bndpreserve (const char *env, int *bndpreserve)
{
if (env == 0)
bndpreserve = MPX_RT_BNDPRESERVE_DEFAULT;
else if (strcmp (env, "0") == 0)
*bndpreserve = 0;
else if (strcmp (env, "1") == 0)
*bndpreserve = 1;
else
{
__mpxrt_print (VERB_ERROR, "Illegal value '%s' for %s. Legal values "
"are [0 | 1]\nUsing default value %d\n",
env, MPX_RT_BNDPRESERVE, MPX_RT_BNDPRESERVE_DEFAULT);
*bndpreserve = MPX_RT_BNDPRESERVE_DEFAULT;
}
}
static verbose_type
init_verbose_val (const char *env)
{
if (env == 0)
return MPX_RT_VERBOSE_DEFAULT;
else if (strcmp(env, "0") == 0)
return VERB_ERROR;
else if (strcmp(env, "1") == 0)
return VERB_INFO;
else if (strcmp(env, "2") == 0)
return VERB_BR;
else if (strcmp(env, "3") == 0)
return VERB_DEBUG;
__mpxrt_print (VERB_ERROR, "Illegal value '%s' for %s. Legal values "
"are [0..3]\nUsing default value %d\n",
env, MPX_RT_VERBOSE, (int)MPX_RT_VERBOSE_DEFAULT);
return MPX_RT_VERBOSE_DEFAULT;
}
static void
env_var_print_summary (void)
{
env_var_t* node;
__mpxrt_print (VERB_DEBUG, "Used environment variables:\n");
node = env_var_list.first;
while (node != 0)
{
__mpxrt_print (VERB_DEBUG, " %s = %s\n", node->env_name, node->env_val);
node = node->next;
}
}
/* Return 1 if passes env var value should enable feature. */
static int
check_yes (const char *val)
{
return val && (!strcmp (val, "yes") || !strcmp (val, "1"));
}
void
__mpxrt_init_env_vars (int* bndpreserve)
{
char *out_env;
char *err_env;
char *env;
pthread_mutex_init (&lock, NULL);
out_env = secure_getenv (MPX_RT_OUT);
env_var_list_add (MPX_RT_OUT, out_env);
err_env = secure_getenv (MPX_RT_ERR);
env_var_list_add (MPX_RT_ERR, err_env);
env = secure_getenv (MPX_RT_ADDPID);
env_var_list_add (MPX_RT_ADDPID, env);
add_pid = check_yes (env);
set_file_stream (&out, out_name, out_env, stdout);
if (out_env == 0 || err_env == 0 || (strcmp (out_env, err_env) != 0))
set_file_stream (&err, err_name, err_env, stderr);
else
/* in case we get the same file name for err and out */
err = out;
env = secure_getenv (MPX_RT_VERBOSE);
env_var_list_add (MPX_RT_VERBOSE, env);
verbose_val = init_verbose_val (env);
env = secure_getenv (MPX_RT_MODE);
env_var_list_add (MPX_RT_MODE, env);
mode = set_mpx_rt_mode (env);
env = secure_getenv (MPX_RT_BNDPRESERVE);
env_var_list_add (MPX_RT_BNDPRESERVE, env);
validate_bndpreserve (env, bndpreserve);
env = secure_getenv (MPX_RT_PRINT_SUMMARY);
env_var_list_add (MPX_RT_PRINT_SUMMARY, env);
summary = check_yes (env);
env = secure_getenv (MPX_RT_HELP);
if (check_yes (env))
print_help ();
/*
* at fork - create new files for output and err according
* to the env vars.
*/
pthread_atfork (NULL, at_fork_check, open_child_files);
env_var_print_summary ();
}
void
__mpxrt_utils_free (void)
{
if (files_overwritten)
__mpxrt_print (VERB_INFO, "\nMPX RUNTIME WARNING: out/err files are"
" overwritten in new processes since %s was not set.\n",
MPX_RT_ADDPID);
if (out != stdout)
{
fclose (out);
if (out_file_dirty != 1)
remove (out_name);
}
if (err != stderr)
{
fclose (err);
if (err_file_dirty != 1)
remove (err_name);
}
pthread_mutex_destroy (&lock);
}
void
__mpxrt_write_uint (verbose_type vt, uint64_t val, unsigned base)
{
static const char digits[] = {
'0', '1', '2', '3', '4', '5', '6', '7',
'8', '9', 'a', 'b', 'c', 'd', 'e', 'f' };
char str[65];
int pos = 64;;
str[pos--] = 0;
if (vt > verbose_val || base <= 1 || base > sizeof (digits))
return;
if (val < base)
str[pos--] = digits[val];
else
while (val)
{
str[pos--] = digits[val % base];
val = val / base;
}
__mpxrt_write (vt, str + pos + 1);
}
void
__mpxrt_write (verbose_type vt, const char* str)
{
va_list argp;
FILE *print_to;
if (vt > verbose_val)
return;
if (vt == VERB_ERROR)
{
print_to = err;
err_file_dirty = 1;
}
else
{
print_to = out;
out_file_dirty = 1;
}
pthread_mutex_lock (&lock);
write (fileno (print_to), str, strlen (str));
pthread_mutex_unlock (&lock);
va_end (argp);
}
void
__mpxrt_print (verbose_type vt, const char* frmt, ...)
{
va_list argp;
FILE *print_to;
if (vt > verbose_val)
return;
va_start (argp, frmt);
if (vt == VERB_ERROR)
{
print_to = err;
err_file_dirty = 1;
}
else
{
print_to = out;
out_file_dirty = 1;
}
pthread_mutex_lock (&lock);
vfprintf (print_to, frmt, argp);
fflush (print_to);
pthread_mutex_unlock (&lock);
va_end (argp);
}
mpx_rt_mode_t
__mpxrt_mode (void)
{
return mode;
}
void
__mpxrt_print_summary (uint64_t num_brs, uint64_t l1_size)
{
if (summary == 0)
return;
out_file_dirty = 1;
pthread_mutex_lock (&lock);
fprintf (out, "MPX runtime summary:\n");
fprintf (out, " Number of bounds violations: %" PRIu64 ".\n", num_brs);
fprintf (out, " Size of allocated L1: %" PRIu64 "B\n", l1_size);
fflush (out);
pthread_mutex_unlock (&lock);
}