xref: /netbsd-src/external/gpl3/binutils/dist/gprofng/src/envsets.cc (revision cb63e24e8d6aae7ddac1859a9015f48b1d8bd90e)
1 /* Copyright (C) 2021-2024 Free Software Foundation, Inc.
2    Contributed by Oracle.
3 
4    This file is part of GNU Binutils.
5 
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.
10 
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.
15 
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.  */
20 
21 #include "config.h"
22 #include <assert.h>
23 #include <ctype.h>
24 #include <sys/param.h>
25 #include <unistd.h>
26 
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"
33 
34 #define	STDEBUFSIZE	24000
35 
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"
43 
44 static const char *LD_AUDIT[] = {
45   //    "LD_AUDIT",	Do not set LD_AUDIT on Linux
46   NULL
47 };
48 
49 static const char *LD_PRELOAD[] = {
50   "LD_PRELOAD",
51   NULL
52 };
53 
54 static const char *SP_PRELOAD[] = {
55   "SP_COLLECTOR_PRELOAD",
56   NULL
57 };
58 
59 static const char *LD_LIBRARY_PATH[] = {
60   "LD_LIBRARY_PATH",
61   NULL,
62 };
63 
64 static int
add_env(char * ev)65 add_env (char *ev)
66 {
67   int r = putenv (ev);
68   if (r != 0)
69     {
70       dbe_write (2, GTXT ("Can't putenv of %s: run aborted\n"), ev);
71       free (ev);
72     }
73   return r;
74 }
75 
76 int
putenv_libcollector_ld_audits()77 collect::putenv_libcollector_ld_audits ()
78 {
79   StringBuilder sb;
80   for (unsigned int ii = 0; ii < ARR_SIZE (LD_AUDIT) && LD_AUDIT[ii]; ++ii)
81     {
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)
86 	{
87 	  while (isspace (*old_val))
88 	    ++old_val;
89 	  if (*old_val != (char) 0)
90 	    {
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.
96 	    }
97 	}
98       if (add_env (sb.toString ()))
99 	return 1;
100     }
101   return 0;
102 }
103 
104 int
putenv_libcollector_ld_preloads()105 collect::putenv_libcollector_ld_preloads ()
106 {
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);
115 
116   // --- putenv SP_COLLECTOR_PRELOAD*
117   int ii;
118   for (ii = 0; SP_PRELOAD[ii]; ii++)
119     {
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;
124     }
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;
133 }
134 
135 int
putenv_libcollector_ld_misc()136 collect::putenv_libcollector_ld_misc ()
137 {
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;
144 
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
148 
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;)
161 	{
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)
167 	    {
168 	      if (++cnt != 1)
169 		sb.append (':');
170 	      sb.append (s);
171 	    }
172 	  free (fname);
173 	  s = s1;
174 	}
175       free (ev);
176       ev = NULL;
177     }
178   if (settings->preload_libdirs == NULL)
179     {
180       settings->read_rc (false);
181       ev = settings->preload_libdirs;
182     }
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)
186     {
187       ++cnt;
188       sb.appendf ("%s/%s", LIBDIR, PACKAGE);
189     }
190   free (fname);
191   for (char *s = ev; s;)
192     {
193       char *s1 = strchr (s, ':');
194       if (s1)
195 	*(s1++) = 0;
196       if (*s == '/')
197 	{
198 	  fname = dbe_sprintf ("%s/%s/%s", s, PACKAGE, LIBGP_COLLECTOR);
199 	  if (access (fname, R_OK | F_OK) == 0)
200 	    {
201 	      if (++cnt != 1)
202 		sb.append (':');
203 	      sb.appendf ("%s", s);
204 	    }
205 	}
206       else
207 	{
208 	  fname = dbe_sprintf ("%s/%s/%s/%s", run_dir, s, PACKAGE, LIBGP_COLLECTOR);
209 	  if (access (fname, R_OK | F_OK) == 0)
210 	    {
211 	      if (++cnt != 1)
212 		sb.append (':');
213 	      sb.appendf ("%s/%s/%s", run_dir, s, PACKAGE);
214 	    }
215 	}
216       free (fname);
217       s = s1;
218     }
219   free (ev);
220   if (cnt == 0)
221     {
222       dbe_write (2, GTXT ("configuration error: can not find %s. run aborted\n"),
223 		 LIBGP_COLLECTOR);
224       return 1;
225     }
226   libpath_list = sb.toString ();
227   if (add_env (libpath_list))
228     return 1;
229   libpath_list += len;
230 
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;
240 }
241 
242 void
add_ld_preload(const char * lib)243 collect::add_ld_preload (const char *lib)
244 {
245   for (int ii = 0; SP_PRELOAD[ii]; ii++)
246     {
247       char *old_sp = sp_preload_list[ii];
248       if (old_sp == NULL)
249 	sp_preload_list[ii] = strdup (lib);
250       else
251 	{
252 	  sp_preload_list[ii] = dbe_sprintf ("%s %s", old_sp, lib);
253 	  free (old_sp);
254 	}
255     }
256 }
257 
258 int
putenv_memso()259 collect::putenv_memso ()
260 {
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 ();
269 }
270 
271 // set LD_PRELOAD and friends to prepend the given library or libraries
272 
273 int
putenv_ld_preloads()274 collect::putenv_ld_preloads ()
275 {
276   for (int ii = 0; LD_PRELOAD[ii]; ii++)
277     {
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;
288     }
289   return 0;
290 }
291 
292 /* copied from linetrace.c */
293 /*
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
302  */
303 int
env_strip(char * env,const char * str)304 collect::env_strip (char *env, const char *str)
305 {
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)
314     {
315       q = p;
316       p += len;
317       if (*p)
318 	{
319 	  while ((*p) && (*p != ':') && (*p != ' '))
320 	    p++;      /* skip the rest of the name*/
321 	  while ((*p == ':') || (*p == ' '))
322 	    p++; /* strip trailing separator */
323 	}
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);
332 	}
333       else
334 	*q = 0;
335       removed++;
336     }
337   return removed;
338 }
339 /*
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.
345  */
346 int
putenv_purged_ld_preloads(const char * var)347 collect::putenv_purged_ld_preloads (const char *var)
348 {
349   int total_removed = 0;
350   if (!var || *var == 0)
351     return 0;
352   for (int ii = 0; LD_PRELOAD[ii]; ii++)
353     {
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;
364     }
365   return total_removed;
366 }
367 /*
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
373  */
374 int
putenv_append(const char * var,const char * val)375 collect::putenv_append (const char *var, const char *val)
376 {
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);
385 
386   // now put the new variable into the environment
387   if (add_env (ev))
388     return 1;
389   return 0;
390 }
391 
392 int
putenv_libcollector(void)393 collect::putenv_libcollector (void)
394 {
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;
401 
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);
407 
408   // set the experiment directory name
409   if (add_env (ev))
410     return 1;
411 
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;
417 
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;
424 
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;
429 
430   // --- set LD_*
431   if (putenv_libcollector_ld_misc ())
432     return 1;
433 
434   // --- set LD_PRELOAD*
435   if (putenv_libcollector_ld_preloads () != 0)
436     return 1;
437 
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;
448 }
449