Automatic date update in version.in
[binutils-gdb.git] / gprofng / src / envsets.cc
blob9cef7452df72f00e7099d0121344171b1191da9f
1 /* Copyright (C) 2021-2024 Free Software Foundation, Inc.
2 Contributed by Oracle.
4 This file is part of GNU Binutils.
6 This program is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 3, or (at your option)
9 any later version.
11 This program is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
16 You should have received a copy of the GNU General Public License
17 along with this program; if not, write to the Free Software
18 Foundation, 51 Franklin Street - Fifth Floor, Boston,
19 MA 02110-1301, USA. */
21 #include "config.h"
22 #include <assert.h>
23 #include <ctype.h>
24 #include <sys/param.h>
25 #include <unistd.h>
27 #include "gp-defs.h"
28 #include "util.h"
29 #include "collctrl.h"
30 #include "collect.h"
31 #include "StringBuilder.h"
32 #include "Settings.h"
34 #define STDEBUFSIZE 24000
36 #define LIBGP_COLLECTOR "libgp-collector.so"
37 #define GPROFNG_PRELOAD_LIBDIRS "GPROFNG_PRELOAD_LIBDIRS"
38 #define SP_COLLECTOR_EXPNAME "SP_COLLECTOR_EXPNAME"
39 #define SP_COLLECTOR_FOLLOW_SPEC "SP_COLLECTOR_FOLLOW_SPEC"
40 #define SP_COLLECTOR_PARAMS "SP_COLLECTOR_PARAMS"
41 #define SP_COLLECTOR_FOUNDER "SP_COLLECTOR_FOUNDER"
42 #define SP_COLLECTOR_ORIGIN_COLLECT "SP_COLLECTOR_ORIGIN_COLLECT"
44 static const char *LD_AUDIT[] = {
45 // "LD_AUDIT", Do not set LD_AUDIT on Linux
46 NULL
49 static const char *LD_PRELOAD[] = {
50 "LD_PRELOAD",
51 NULL
54 static const char *SP_PRELOAD[] = {
55 "SP_COLLECTOR_PRELOAD",
56 NULL
59 static const char *LD_LIBRARY_PATH[] = {
60 "LD_LIBRARY_PATH",
61 NULL,
64 static int
65 add_env (char *ev)
67 int r = putenv (ev);
68 if (r != 0)
70 dbe_write (2, GTXT ("Can't putenv of %s: run aborted\n"), ev);
71 free (ev);
73 return r;
76 int
77 collect::putenv_libcollector_ld_audits ()
79 StringBuilder sb;
80 for (unsigned int ii = 0; ii < ARR_SIZE (LD_AUDIT) && LD_AUDIT[ii]; ++ii)
82 sb.sprintf ("%s=%s", LD_AUDIT[ii], SP_LIBAUDIT_NAME);
83 // Append the current value. Check if already set
84 char *old_val = getenv (LD_AUDIT[ii]);
85 if (old_val != NULL)
87 while (isspace (*old_val))
88 ++old_val;
89 if (*old_val != (char) 0)
91 int fromIdx = sb.length ();
92 sb.append (" ");
93 sb.append (old_val);
94 if (sb.indexOf (SP_LIBAUDIT_NAME, fromIdx) >= 0)
95 continue; // Already set. Do nothing.
98 if (add_env (sb.toString ()))
99 return 1;
101 return 0;
105 collect::putenv_libcollector_ld_preloads ()
107 // for those data types that get extra libs LD_PRELOAD'd, add them
108 if (cc->get_synctrace_mode () != 0)
109 add_ld_preload ("libgp-sync.so");
110 if (cc->get_heaptrace_mode () != 0)
111 add_ld_preload ("libgp-heap.so");
112 if (cc->get_iotrace_mode () != 0)
113 add_ld_preload ("libgp-iotrace.so");
114 add_ld_preload (SP_LIBCOLLECTOR_NAME);
116 // --- putenv SP_COLLECTOR_PRELOAD*
117 int ii;
118 for (ii = 0; SP_PRELOAD[ii]; ii++)
120 // construct the SP_PRELOAD_* environment variables
121 // and put them into the environment
122 if (add_env (dbe_sprintf ("%s=%s", SP_PRELOAD[ii], sp_preload_list[ii])))
123 return 1;
125 // --- putenv LD_PRELOADS
126 /* purge LD_PRELOAD* of values containing contents of SP_LIBCOLLECTOR_NAME */
127 if (putenv_purged_ld_preloads (SP_LIBCOLLECTOR_NAME))
128 dbe_write (2, GTXT ("Warning: %s is already defined in one or more LD_PRELOAD environment variables\n"),
129 SP_LIBCOLLECTOR_NAME);
130 if (putenv_ld_preloads ())
131 return 1;
132 return 0;
136 collect::putenv_libcollector_ld_misc ()
138 #if 0 // XXX 1 turns on LD_DEBUG
139 putenv (strdup ("LD_DEBUG=audit,bindings,detail"));
140 #endif
141 // workaround to have the dynamic linker use absolute names
142 if (add_env (dbe_strdup ("LD_ORIGIN=yes")))
143 return 1;
145 // On Linux we have to provide SP_COLLECTOR_LIBRARY_PATH and LD_LIBRARY_PATH
146 // so that -agentlib:gp-collector works
147 // and so that collect -F works with 32/64-bit mix of processes
149 StringBuilder sb;
150 sb.append ("SP_COLLECTOR_LIBRARY_PATH=");
151 int len = sb.length ();
152 int cnt = 0;
153 char *fname;
154 char *ev = getenv (GPROFNG_PRELOAD_LIBDIRS);
155 char *libpath_list = NULL;
156 if (ev)
157 { /* GPROFNG_PRELOAD_LIBDIRS is used only in the gprofng testing.
158 * Use these directories first. */
159 ev = strdup (ev);
160 for (char *s = ev; s;)
162 char *s1 = strchr (s, ':');
163 if (s1)
164 *(s1++) = 0;
165 fname = dbe_sprintf ("%s/%s", s, LIBGP_COLLECTOR);
166 if (access (fname, R_OK | F_OK) == 0)
168 if (++cnt != 1)
169 sb.append (':');
170 sb.append (s);
172 free (fname);
173 s = s1;
175 free (ev);
176 ev = NULL;
178 if (settings->preload_libdirs == NULL)
180 settings->read_rc (false);
181 ev = settings->preload_libdirs;
183 ev = dbe_strdup (ev);
184 fname = dbe_sprintf ("%s/%s/%s", LIBDIR, PACKAGE, LIBGP_COLLECTOR);
185 if (access (fname, R_OK | F_OK) == 0)
187 ++cnt;
188 sb.appendf ("%s/%s", LIBDIR, PACKAGE);
190 free (fname);
191 for (char *s = ev; s;)
193 char *s1 = strchr (s, ':');
194 if (s1)
195 *(s1++) = 0;
196 if (*s == '/')
198 fname = dbe_sprintf ("%s/%s/%s", s, PACKAGE, LIBGP_COLLECTOR);
199 if (access (fname, R_OK | F_OK) == 0)
201 if (++cnt != 1)
202 sb.append (':');
203 sb.appendf ("%s", s);
206 else
208 fname = dbe_sprintf ("%s/%s/%s/%s", run_dir, s, PACKAGE, LIBGP_COLLECTOR);
209 if (access (fname, R_OK | F_OK) == 0)
211 if (++cnt != 1)
212 sb.append (':');
213 sb.appendf ("%s/%s/%s", run_dir, s, PACKAGE);
216 free (fname);
217 s = s1;
219 free (ev);
220 if (cnt == 0)
222 dbe_write (2, GTXT ("configuration error: can not find %s. run aborted\n"),
223 LIBGP_COLLECTOR);
224 return 1;
226 libpath_list = sb.toString ();
227 if (add_env (libpath_list))
228 return 1;
229 libpath_list += len;
231 // --- set LD_LIBRARY_PATH using libpath_list
232 char *old = getenv (LD_LIBRARY_PATH[0]);
233 if (old)
234 ev = dbe_sprintf ("%s=%s:%s", LD_LIBRARY_PATH[0], libpath_list, old);
235 else
236 ev = dbe_sprintf ("%s=%s", LD_LIBRARY_PATH[0], libpath_list);
237 if (add_env (ev))
238 return 1;
239 return 0;
242 void
243 collect::add_ld_preload (const char *lib)
245 for (int ii = 0; SP_PRELOAD[ii]; ii++)
247 char *old_sp = sp_preload_list[ii];
248 if (old_sp == NULL)
249 sp_preload_list[ii] = strdup (lib);
250 else
252 sp_preload_list[ii] = dbe_sprintf ("%s %s", old_sp, lib);
253 free (old_sp);
259 collect::putenv_memso ()
261 // Set environment variable "MEM_USE_LOG" to 1, to keep it out of stderr
262 if (add_env (dbe_strdup ("MEM_USE_LOG=1")))
263 return 1;
264 // Set environment variable "MEM_ABORT_ON_ERROR", to force a core dump
265 if (add_env (dbe_strdup ("MEM_ABORT_ON_ERROR=1")))
266 return 1;
267 add_ld_preload ("mem.so");
268 return putenv_ld_preloads ();
271 // set LD_PRELOAD and friends to prepend the given library or libraries
274 collect::putenv_ld_preloads ()
276 for (int ii = 0; LD_PRELOAD[ii]; ii++)
278 char *old_val = getenv (LD_PRELOAD[ii]);
279 int sp_num = ii;
280 assert (SP_PRELOAD[sp_num]);
281 char *preload_def;
282 if (old_val)
283 preload_def = dbe_sprintf ("%s=%s %s", LD_PRELOAD[ii], sp_preload_list[sp_num], old_val);
284 else
285 preload_def = dbe_sprintf ("%s=%s", LD_PRELOAD[ii], sp_preload_list[sp_num]);
286 if (add_env (preload_def))
287 return 1;
289 return 0;
292 /* copied from linetrace.c */
294 function: env_strip()
295 Finds str in env; Removes
296 all characters from previous ':' or ' '
297 up to and including any trailing ':' or ' '.
298 params:
299 env: environment variable
300 str: substring to find
301 return: count of instances removed from env
304 collect::env_strip (char *env, const char *str)
306 int removed = 0;
307 char *p, *q;
308 if (env == NULL || str == NULL || *str == 0)
309 return 0;
310 size_t maxlen = strlen (env);
311 size_t len = strlen (str);
312 q = env;
313 while ((p = strstr (q, str)) != NULL)
315 q = p;
316 p += len;
317 if (*p)
319 while ((*p) && (*p != ':') && (*p != ' '))
320 p++; /* skip the rest of the name*/
321 while ((*p == ':') || (*p == ' '))
322 p++; /* strip trailing separator */
324 while (*q != ':' && *q != ' ' && *q != '=' && q != env)
325 q--; /* strip path */
326 if (*p)
327 { /* copy the rest of the string */
328 if (q != env)
329 q++; /* restore leading separator (if any) */
330 size_t n = (maxlen - (q - env));
331 strncpy (q, p, n);
333 else
334 *q = 0;
335 removed++;
337 return removed;
340 function: putenv_purged_ld_preloads()
341 Remove selected preload strings from all LD_PRELOAD* env vars.
342 params:
343 var: executable name (leading characters don't have to match)
344 return: number of instances removed from all PRELOAD vars.
347 collect::putenv_purged_ld_preloads (const char *var)
349 int total_removed = 0;
350 if (!var || *var == 0)
351 return 0;
352 for (int ii = 0; LD_PRELOAD[ii]; ii++)
354 char *ev = getenv (LD_PRELOAD[ii]);
355 int removed = 0;
356 if (!ev)
357 continue;
358 removed = env_strip (ev, var);
359 if (!removed)
360 continue;
361 if (putenv (ev) != 0)
362 dbe_write (2, GTXT ("Can't putenv of %s\n"), ev);
363 total_removed += removed;
365 return total_removed;
368 function: putenv_append()
369 append string to current enviroment variable setting and then do a putenv()
370 params:
371 var: environment variable name
372 val: string to append
375 collect::putenv_append (const char *var, const char *val)
377 char *ev;
378 if (!var || !val)
379 return 1;
380 const char *old_val = getenv (var);
381 if (old_val == NULL || *old_val == 0)
382 ev = dbe_sprintf ("%s=%s", var, val);
383 else
384 ev = dbe_sprintf ("%s=%s %s", var, old_val, val);
386 // now put the new variable into the environment
387 if (add_env (ev))
388 return 1;
389 return 0;
393 collect::putenv_libcollector (void)
395 char buf[MAXPATHLEN + 1];
396 // --- set SP_COLLECTOR_EXPNAME
397 // fetch the experiment name and CWD
398 char *exp = cc->get_experiment ();
399 char *cwd = getcwd (buf, MAXPATHLEN);
400 char *ev;
402 // format the environment variable for the experiment directory name
403 if (cwd != NULL && exp[0] != '/') // experiment is a relative path
404 ev = dbe_sprintf ("%s=%s/%s", SP_COLLECTOR_EXPNAME, cwd, exp);
405 else // getcwd failed or experiment is a fullpath
406 ev = dbe_sprintf ("%s=%s", SP_COLLECTOR_EXPNAME, exp);
408 // set the experiment directory name
409 if (add_env (ev))
410 return 1;
412 // --- set SP_COLLECTOR_PARAMS
413 // set the data descriptor
414 exp = cc->get_data_desc ();
415 if (add_env (dbe_sprintf ("%s=%s", SP_COLLECTOR_PARAMS, exp)))
416 return 1;
418 // --- set SP_COLLECTOR_FOLLOW_SPEC
419 const char *follow_spec = cc->get_follow_cmp_spec ();
420 if (follow_spec)
421 // selective following has been enabled
422 if (add_env (dbe_sprintf ("%s=%s", SP_COLLECTOR_FOLLOW_SPEC, follow_spec)))
423 return 1;
425 if (add_env (dbe_sprintf ("%s=%d", SP_COLLECTOR_FOUNDER, getpid ())))
426 return 1;
427 if (add_env (dbe_sprintf ("%s=1", SP_COLLECTOR_ORIGIN_COLLECT)))
428 return 1;
430 // --- set LD_*
431 if (putenv_libcollector_ld_misc ())
432 return 1;
434 // --- set LD_PRELOAD*
435 if (putenv_libcollector_ld_preloads () != 0)
436 return 1;
438 // --- set JAVA_TOOL_OPTIONS
439 if (cc->get_java_mode () == 1)
440 if (putenv_append ("JAVA_TOOL_OPTIONS", "-agentlib:gp-collector"))
441 exit (1);
442 #if 0
443 // --- set LD_AUDIT*
444 if (putenv_libcollector_ld_audits () != 0)
445 return 1;
446 #endif
447 return 0;