diff options
Diffstat (limited to 'gprofng/src/envsets.cc')
-rw-r--r-- | gprofng/src/envsets.cc | 420 |
1 files changed, 420 insertions, 0 deletions
diff --git a/gprofng/src/envsets.cc b/gprofng/src/envsets.cc new file mode 100644 index 0000000..de06fbf --- /dev/null +++ b/gprofng/src/envsets.cc @@ -0,0 +1,420 @@ +/* Copyright (C) 2021 Free Software Foundation, Inc. + Contributed by Oracle. + + This file is part of GNU Binutils. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, 51 Franklin Street - Fifth Floor, Boston, + MA 02110-1301, USA. */ + +#include "config.h" +#include <assert.h> +#include <ctype.h> +#include <sys/param.h> +#include <unistd.h> + +#include "gp-defs.h" +#include "util.h" +#include "collctrl.h" +#include "collect.h" +#include "StringBuilder.h" +#include "Settings.h" + +#define STDEBUFSIZE 24000 + +#define LIBGP_COLLECTOR "libgp-collector.so" +#define GPROFNG_PRELOAD_LIBDIRS "GPROFNG_PRELOAD_LIBDIRS" +#define SP_COLLECTOR_EXPNAME "SP_COLLECTOR_EXPNAME" +#define SP_COLLECTOR_FOLLOW_SPEC "SP_COLLECTOR_FOLLOW_SPEC" +#define SP_COLLECTOR_PARAMS "SP_COLLECTOR_PARAMS" +#define SP_COLLECTOR_FOUNDER "SP_COLLECTOR_FOUNDER" +#define SP_COLLECTOR_ORIGIN_COLLECT "SP_COLLECTOR_ORIGIN_COLLECT" + +static const char *LD_AUDIT[] = { + // "LD_AUDIT", Do not set LD_AUDIT on Linux + NULL +}; + +static const char *LD_PRELOAD[] = { + "LD_PRELOAD", + NULL +}; + +static const char *SP_PRELOAD[] = { + "SP_COLLECTOR_PRELOAD", + NULL +}; + +static const char *LD_LIBRARY_PATH[] = { + "LD_LIBRARY_PATH", + NULL, +}; + +static int +add_env (char *ev) +{ + int r = putenv (ev); + if (r != 0) + { + dbe_write (2, GTXT ("Can't putenv of %s: run aborted\n"), ev); + free (ev); + } + return r; +} + +int +collect::putenv_libcollector_ld_audits () +{ + StringBuilder sb; + for (unsigned int ii = 0; ii < ARR_SIZE (LD_AUDIT) && LD_AUDIT[ii]; ++ii) + { + sb.sprintf ("%s=%s", LD_AUDIT[ii], SP_LIBAUDIT_NAME); + // Append the current value. Check if already set + char *old_val = getenv (LD_AUDIT[ii]); + if (old_val != NULL) + { + while (isspace (*old_val)) + ++old_val; + if (*old_val != (char) 0) + { + int fromIdx = sb.length (); + sb.append (" "); + sb.append (old_val); + if (sb.indexOf (SP_LIBAUDIT_NAME, fromIdx) >= 0) + continue; // Already set. Do nothing. + } + } + if (add_env (sb.toString ())) + return 1; + } + return 0; +} + +int +collect::putenv_libcollector_ld_preloads () +{ + // for those data types that get extra libs LD_PRELOAD'd, add them + if (cc->get_synctrace_mode () != 0) + add_ld_preload ("libgp-sync.so"); + if (cc->get_heaptrace_mode () != 0) + add_ld_preload ("libgp-heap.so"); + if (cc->get_iotrace_mode () != 0) + add_ld_preload ("libgp-iotrace.so"); + add_ld_preload (SP_LIBCOLLECTOR_NAME); + + // --- putenv SP_COLLECTOR_PRELOAD* + int ii; + for (ii = 0; SP_PRELOAD[ii]; ii++) + { + // construct the SP_PRELOAD_* environment variables + // and put them into the environment + if (add_env (dbe_sprintf ("%s=%s", SP_PRELOAD[ii], sp_preload_list[ii]))) + return 1; + } + // --- putenv LD_PRELOADS + /* purge LD_PRELOAD* of values containing contents of SP_LIBCOLLECTOR_NAME */ + if (putenv_purged_ld_preloads (SP_LIBCOLLECTOR_NAME)) + dbe_write (2, GTXT ("Warning: %s is already defined in one or more LD_PRELOAD environment variables\n"), + SP_LIBCOLLECTOR_NAME); + if (putenv_ld_preloads ()) + return 1; + return 0; +} + +int +collect::putenv_libcollector_ld_misc () +{ +#if 0 // XXX 1 turns on LD_DEBUG + putenv (strdup ("LD_DEBUG=audit,bindings,detail")); +#endif + // workaround to have the dynamic linker use absolute names + if (add_env (dbe_strdup ("LD_ORIGIN=yes"))) + return 1; + + // On Linux we have to provide SP_COLLECTOR_LIBRARY_PATH and LD_LIBRARY_PATH + // so that -agentlib:gp-collector works + // and so that collect -F works with 32/64-bit mix of processes + + // Set GPROFNG_PRELOAD_LIBDIRS + char *ev = getenv (GPROFNG_PRELOAD_LIBDIRS); + char *libpath_list = NULL; + if (ev == NULL && settings->preload_libdirs == NULL) + { + settings->read_rc (false); + ev = settings->preload_libdirs; + } + ev = dbe_strdup (ev); + StringBuilder sb; + sb.appendf ("%s=", "SP_COLLECTOR_LIBRARY_PATH"); + int len = sb.length (); + int cnt = 0; + for (char *s = ev; s;) + { + char *s1 = strchr (s, ':'); + if (s1) + *(s1++) = 0; + char *fname; + if (*s == '/') + { + fname = dbe_sprintf ("%s/%s", s, LIBGP_COLLECTOR); + if (access (fname, R_OK | F_OK) == 0) + { + if (++cnt != 1) + sb.append (':'); + sb.appendf ("%s", s); + } + } + else + { + fname = dbe_sprintf ("%s/%s/%s", run_dir, s, LIBGP_COLLECTOR); + if (access (fname, R_OK | F_OK) == 0) + { + if (++cnt != 1) + sb.append (':'); + sb.appendf ("%s/%s", run_dir, s); + } + } + free (fname); + s = s1; + } + free (ev); + if (cnt == 0) + { + dbe_write (2, GTXT ("configuration error: can not find %s. run aborted\n"), + LIBGP_COLLECTOR); + return 1; + } + libpath_list = sb.toString (); + if (add_env (libpath_list)) + return 1; + libpath_list += len; + + // --- set LD_LIBRARY_PATH using libpath_list + char *old = getenv (LD_LIBRARY_PATH[0]); + if (old) + ev = dbe_sprintf ("%s=%s:%s", LD_LIBRARY_PATH[0], libpath_list, old); + else + ev = dbe_sprintf ("%s=%s", LD_LIBRARY_PATH[0], libpath_list); + if (add_env (ev)) + return 1; + return 0; +} + +void +collect::add_ld_preload (const char *lib) +{ + for (int ii = 0; SP_PRELOAD[ii]; ii++) + { + char *old_sp = sp_preload_list[ii]; + if (old_sp == NULL) + sp_preload_list[ii] = strdup (lib); + else + { + sp_preload_list[ii] = dbe_sprintf ("%s %s", old_sp, lib); + free (old_sp); + } + } +} + +int +collect::putenv_memso () +{ + // Set environment variable "MEM_USE_LOG" to 1, to keep it out of stderr + if (add_env (dbe_strdup ("MEM_USE_LOG=1"))) + return 1; + // Set environment variable "MEM_ABORT_ON_ERROR", to force a core dump + if (add_env (dbe_strdup ("MEM_ABORT_ON_ERROR=1"))) + return 1; + add_ld_preload ("mem.so"); + return putenv_ld_preloads (); +} + +// set LD_PRELOAD and friends to prepend the given library or libraries + +int +collect::putenv_ld_preloads () +{ + for (int ii = 0; LD_PRELOAD[ii]; ii++) + { + char *old_val = getenv (LD_PRELOAD[ii]); + int sp_num = ii; + assert (SP_PRELOAD[sp_num]); + char *preload_def; + if (old_val) + preload_def = dbe_sprintf ("%s=%s %s", LD_PRELOAD[ii], sp_preload_list[sp_num], old_val); + else + preload_def = dbe_sprintf ("%s=%s", LD_PRELOAD[ii], sp_preload_list[sp_num]); + if (add_env (preload_def)) + return 1; + } + return 0; +} + +/* copied from linetrace.c */ +/* + function: env_strip() + Finds str in env; Removes + all characters from previous ':' or ' ' + up to and including any trailing ':' or ' '. + params: + env: environment variable + str: substring to find + return: count of instances removed from env + */ +int +collect::env_strip (char *env, const char *str) +{ + int removed = 0; + char *p, *q; + if (env == NULL || str == NULL || *str == 0) + return 0; + size_t maxlen = strlen (env); + size_t len = strlen (str); + q = env; + while ((p = strstr (q, str)) != NULL) + { + q = p; + p += len; + if (*p) + { + while ((*p) && (*p != ':') && (*p != ' ')) + p++; /* skip the rest of the name*/ + while ((*p == ':') || (*p == ' ')) + p++; /* strip trailing separator */ + } + while (*q != ':' && *q != ' ' && *q != '=' && q != env) + q--; /* strip path */ + if (*p) + { /* copy the rest of the string */ + if (q != env) + q++; /* restore leading separator (if any) */ + size_t n = (maxlen - (q - env)); + strncpy (q, p, n); + } + else + *q = 0; + removed++; + } + return removed; +} +/* + function: putenv_purged_ld_preloads() + Remove selected preload strings from all LD_PRELOAD* env vars. + params: + var: executable name (leading characters don't have to match) + return: number of instances removed from all PRELOAD vars. + */ +int +collect::putenv_purged_ld_preloads (const char *var) +{ + int total_removed = 0; + if (!var || *var == 0) + return 0; + for (int ii = 0; LD_PRELOAD[ii]; ii++) + { + char *ev = getenv (LD_PRELOAD[ii]); + int removed = 0; + if (!ev) + continue; + removed = env_strip (ev, var); + if (!removed) + continue; + if (putenv (ev) != 0) + dbe_write (2, GTXT ("Can't putenv of %s\n"), ev); + total_removed += removed; + } + return total_removed; +} +/* + function: putenv_append() + append string to current enviroment variable setting and then do a putenv() + params: + var: environment variable name + val: string to append + */ +int +collect::putenv_append (const char *var, const char *val) +{ + char *ev; + if (!var || !val) + return 1; + const char *old_val = getenv (var); + if (old_val == NULL || *old_val == 0) + ev = dbe_sprintf ("%s=%s", var, val); + else + ev = dbe_sprintf ("%s=%s %s", var, old_val, val); + + // now put the new variable into the environment + if (add_env (ev)) + return 1; + return 0; +} + +int +collect::putenv_libcollector (void) +{ + char buf[MAXPATHLEN + 1]; + // --- set SP_COLLECTOR_EXPNAME + // fetch the experiment name and CWD + char *exp = cc->get_experiment (); + char *cwd = getcwd (buf, MAXPATHLEN); + char *ev; + + // format the environment variable for the experiment directory name + if (cwd != NULL && exp[0] != '/') // experiment is a relative path + ev = dbe_sprintf ("%s=%s/%s", SP_COLLECTOR_EXPNAME, cwd, exp); + else // getcwd failed or experiment is a fullpath + ev = dbe_sprintf ("%s=%s", SP_COLLECTOR_EXPNAME, exp); + + // set the experiment directory name + if (add_env (ev)) + return 1; + + // --- set SP_COLLECTOR_PARAMS + // set the data descriptor + exp = cc->get_data_desc (); + if (add_env (dbe_sprintf ("%s=%s", SP_COLLECTOR_PARAMS, exp))) + return 1; + + // --- set SP_COLLECTOR_FOLLOW_SPEC + const char *follow_spec = cc->get_follow_cmp_spec (); + if (follow_spec) + // selective following has been enabled + if (add_env (dbe_sprintf ("%s=%s", SP_COLLECTOR_FOLLOW_SPEC, follow_spec))) + return 1; + + if (add_env (dbe_sprintf ("%s=%d", SP_COLLECTOR_FOUNDER, getpid ()))) + return 1; + if (add_env (dbe_sprintf ("%s=1", SP_COLLECTOR_ORIGIN_COLLECT))) + return 1; + + // --- set LD_* + if (putenv_libcollector_ld_misc ()) + return 1; + + // --- set LD_PRELOAD* + if (putenv_libcollector_ld_preloads () != 0) + return 1; + + // --- set JAVA_TOOL_OPTIONS + if (cc->get_java_mode () == 1) + if (putenv_append ("JAVA_TOOL_OPTIONS", "-agentlib:gp-collector")) + exit (1); +#if 0 + // --- set LD_AUDIT* + if (putenv_libcollector_ld_audits () != 0) + return 1; +#endif + return 0; +} |