1 /* Copyright (C) 2021-2024 Free Software Foundation, Inc.
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)
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. */
24 #include <sys/param.h>
31 #include "StringBuilder.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
49 static const char *LD_PRELOAD
[] = {
54 static const char *SP_PRELOAD
[] = {
55 "SP_COLLECTOR_PRELOAD",
59 static const char *LD_LIBRARY_PATH
[] = {
70 dbe_write (2, GTXT ("Can't putenv of %s: run aborted\n"), ev
);
77 collect::putenv_libcollector_ld_audits ()
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
]);
87 while (isspace (*old_val
))
89 if (*old_val
!= (char) 0)
91 int fromIdx
= sb
.length ();
94 if (sb
.indexOf (SP_LIBAUDIT_NAME
, fromIdx
) >= 0)
95 continue; // Already set. Do nothing.
98 if (add_env (sb
.toString ()))
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*
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
])))
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 ())
136 collect::putenv_libcollector_ld_misc ()
138 #if 0 // XXX 1 turns on LD_DEBUG
139 putenv (strdup ("LD_DEBUG=audit,bindings,detail"));
141 // workaround to have the dynamic linker use absolute names
142 if (add_env (dbe_strdup ("LD_ORIGIN=yes")))
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
150 sb
.append ("SP_COLLECTOR_LIBRARY_PATH=");
151 int len
= sb
.length ();
154 char *ev
= getenv (GPROFNG_PRELOAD_LIBDIRS
);
155 char *libpath_list
= NULL
;
157 { /* GPROFNG_PRELOAD_LIBDIRS is used only in the gprofng testing.
158 * Use these directories first. */
160 for (char *s
= ev
; s
;)
162 char *s1
= strchr (s
, ':');
165 fname
= dbe_sprintf ("%s/%s", s
, LIBGP_COLLECTOR
);
166 if (access (fname
, R_OK
| F_OK
) == 0)
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)
188 sb
.appendf ("%s/%s", LIBDIR
, PACKAGE
);
191 for (char *s
= ev
; s
;)
193 char *s1
= strchr (s
, ':');
198 fname
= dbe_sprintf ("%s/%s/%s", s
, PACKAGE
, LIBGP_COLLECTOR
);
199 if (access (fname
, R_OK
| F_OK
) == 0)
203 sb
.appendf ("%s", s
);
208 fname
= dbe_sprintf ("%s/%s/%s/%s", run_dir
, s
, PACKAGE
, LIBGP_COLLECTOR
);
209 if (access (fname
, R_OK
| F_OK
) == 0)
213 sb
.appendf ("%s/%s/%s", run_dir
, s
, PACKAGE
);
222 dbe_write (2, GTXT ("configuration error: can not find %s. run aborted\n"),
226 libpath_list
= sb
.toString ();
227 if (add_env (libpath_list
))
231 // --- set LD_LIBRARY_PATH using libpath_list
232 char *old
= getenv (LD_LIBRARY_PATH
[0]);
234 ev
= dbe_sprintf ("%s=%s:%s", LD_LIBRARY_PATH
[0], libpath_list
, old
);
236 ev
= dbe_sprintf ("%s=%s", LD_LIBRARY_PATH
[0], libpath_list
);
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
];
249 sp_preload_list
[ii
] = strdup (lib
);
252 sp_preload_list
[ii
] = dbe_sprintf ("%s %s", old_sp
, lib
);
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")))
264 // Set environment variable "MEM_ABORT_ON_ERROR", to force a core dump
265 if (add_env (dbe_strdup ("MEM_ABORT_ON_ERROR=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
]);
280 assert (SP_PRELOAD
[sp_num
]);
283 preload_def
= dbe_sprintf ("%s=%s %s", LD_PRELOAD
[ii
], sp_preload_list
[sp_num
], old_val
);
285 preload_def
= dbe_sprintf ("%s=%s", LD_PRELOAD
[ii
], sp_preload_list
[sp_num
]);
286 if (add_env (preload_def
))
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 ' '.
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
)
308 if (env
== NULL
|| str
== NULL
|| *str
== 0)
310 size_t maxlen
= strlen (env
);
311 size_t len
= strlen (str
);
313 while ((p
= strstr (q
, str
)) != NULL
)
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 */
327 { /* copy the rest of the string */
329 q
++; /* restore leading separator (if any) */
330 size_t n
= (maxlen
- (q
- env
));
340 function: putenv_purged_ld_preloads()
341 Remove selected preload strings from all LD_PRELOAD* env vars.
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)
352 for (int ii
= 0; LD_PRELOAD
[ii
]; ii
++)
354 char *ev
= getenv (LD_PRELOAD
[ii
]);
358 removed
= env_strip (ev
, var
);
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()
371 var: environment variable name
372 val: string to append
375 collect::putenv_append (const char *var
, const char *val
)
380 const char *old_val
= getenv (var
);
381 if (old_val
== NULL
|| *old_val
== 0)
382 ev
= dbe_sprintf ("%s=%s", var
, val
);
384 ev
= dbe_sprintf ("%s=%s %s", var
, old_val
, val
);
386 // now put the new variable into the environment
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
);
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
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
)))
418 // --- set SP_COLLECTOR_FOLLOW_SPEC
419 const char *follow_spec
= cc
->get_follow_cmp_spec ();
421 // selective following has been enabled
422 if (add_env (dbe_sprintf ("%s=%s", SP_COLLECTOR_FOLLOW_SPEC
, follow_spec
)))
425 if (add_env (dbe_sprintf ("%s=%d", SP_COLLECTOR_FOUNDER
, getpid ())))
427 if (add_env (dbe_sprintf ("%s=1", SP_COLLECTOR_ORIGIN_COLLECT
)))
431 if (putenv_libcollector_ld_misc ())
434 // --- set LD_PRELOAD*
435 if (putenv_libcollector_ld_preloads () != 0)
438 // --- set JAVA_TOOL_OPTIONS
439 if (cc
->get_java_mode () == 1)
440 if (putenv_append ("JAVA_TOOL_OPTIONS", "-agentlib:gp-collector"))
444 if (putenv_libcollector_ld_audits () != 0)