1/* Copyright (C) 2021 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 44static const char *LD_AUDIT[] = { 45 // "LD_AUDIT", Do not set LD_AUDIT on Linux 46 NULL 47}; 48 49static const char *LD_PRELOAD[] = { 50 "LD_PRELOAD", 51 NULL 52}; 53 54static const char *SP_PRELOAD[] = { 55 "SP_COLLECTOR_PRELOAD", 56 NULL 57}; 58 59static const char *LD_LIBRARY_PATH[] = { 60 "LD_LIBRARY_PATH", 61 NULL, 62}; 63 64static int 65add_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 76int 77collect::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 104int 105collect::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 135int 136collect::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 // Set GPROFNG_PRELOAD_LIBDIRS 150 char *ev = getenv (GPROFNG_PRELOAD_LIBDIRS); 151 char *libpath_list = NULL; 152 if (ev == NULL && settings->preload_libdirs == NULL) 153 { 154 settings->read_rc (false); 155 ev = settings->preload_libdirs; 156 } 157 ev = dbe_strdup (ev); 158 StringBuilder sb; 159 sb.appendf ("%s=", "SP_COLLECTOR_LIBRARY_PATH"); 160 int len = sb.length (); 161 int cnt = 0; 162 for (char *s = ev; s;) 163 { 164 char *s1 = strchr (s, ':'); 165 if (s1) 166 *(s1++) = 0; 167 char *fname; 168 if (*s == '/') 169 { 170 fname = dbe_sprintf ("%s/%s/%s", s, PACKAGE, LIBGP_COLLECTOR); 171 if (access (fname, R_OK | F_OK) == 0) 172 { 173 if (++cnt != 1) 174 sb.append (':'); 175 sb.appendf ("%s", s); 176 } 177 } 178 else 179 { 180 fname = dbe_sprintf ("%s/%s/%s/%s", run_dir, s, PACKAGE, LIBGP_COLLECTOR); 181 if (access (fname, R_OK | F_OK) == 0) 182 { 183 if (++cnt != 1) 184 sb.append (':'); 185 sb.appendf ("%s/%s/%s", run_dir, s, PACKAGE); 186 } 187 } 188 free (fname); 189 s = s1; 190 } 191 free (ev); 192 if (cnt == 0) 193 { 194 dbe_write (2, GTXT ("configuration error: can not find %s. run aborted\n"), 195 LIBGP_COLLECTOR); 196 return 1; 197 } 198 libpath_list = sb.toString (); 199 if (add_env (libpath_list)) 200 return 1; 201 libpath_list += len; 202 203 // --- set LD_LIBRARY_PATH using libpath_list 204 char *old = getenv (LD_LIBRARY_PATH[0]); 205 if (old) 206 ev = dbe_sprintf ("%s=%s:%s", LD_LIBRARY_PATH[0], libpath_list, old); 207 else 208 ev = dbe_sprintf ("%s=%s", LD_LIBRARY_PATH[0], libpath_list); 209 if (add_env (ev)) 210 return 1; 211 return 0; 212} 213 214void 215collect::add_ld_preload (const char *lib) 216{ 217 for (int ii = 0; SP_PRELOAD[ii]; ii++) 218 { 219 char *old_sp = sp_preload_list[ii]; 220 if (old_sp == NULL) 221 sp_preload_list[ii] = strdup (lib); 222 else 223 { 224 sp_preload_list[ii] = dbe_sprintf ("%s %s", old_sp, lib); 225 free (old_sp); 226 } 227 } 228} 229 230int 231collect::putenv_memso () 232{ 233 // Set environment variable "MEM_USE_LOG" to 1, to keep it out of stderr 234 if (add_env (dbe_strdup ("MEM_USE_LOG=1"))) 235 return 1; 236 // Set environment variable "MEM_ABORT_ON_ERROR", to force a core dump 237 if (add_env (dbe_strdup ("MEM_ABORT_ON_ERROR=1"))) 238 return 1; 239 add_ld_preload ("mem.so"); 240 return putenv_ld_preloads (); 241} 242 243// set LD_PRELOAD and friends to prepend the given library or libraries 244 245int 246collect::putenv_ld_preloads () 247{ 248 for (int ii = 0; LD_PRELOAD[ii]; ii++) 249 { 250 char *old_val = getenv (LD_PRELOAD[ii]); 251 int sp_num = ii; 252 assert (SP_PRELOAD[sp_num]); 253 char *preload_def; 254 if (old_val) 255 preload_def = dbe_sprintf ("%s=%s %s", LD_PRELOAD[ii], sp_preload_list[sp_num], old_val); 256 else 257 preload_def = dbe_sprintf ("%s=%s", LD_PRELOAD[ii], sp_preload_list[sp_num]); 258 if (add_env (preload_def)) 259 return 1; 260 } 261 return 0; 262} 263 264/* copied from linetrace.c */ 265/* 266 function: env_strip() 267 Finds str in env; Removes 268 all characters from previous ':' or ' ' 269 up to and including any trailing ':' or ' '. 270 params: 271 env: environment variable 272 str: substring to find 273 return: count of instances removed from env 274 */ 275int 276collect::env_strip (char *env, const char *str) 277{ 278 int removed = 0; 279 char *p, *q; 280 if (env == NULL || str == NULL || *str == 0) 281 return 0; 282 size_t maxlen = strlen (env); 283 size_t len = strlen (str); 284 q = env; 285 while ((p = strstr (q, str)) != NULL) 286 { 287 q = p; 288 p += len; 289 if (*p) 290 { 291 while ((*p) && (*p != ':') && (*p != ' ')) 292 p++; /* skip the rest of the name*/ 293 while ((*p == ':') || (*p == ' ')) 294 p++; /* strip trailing separator */ 295 } 296 while (*q != ':' && *q != ' ' && *q != '=' && q != env) 297 q--; /* strip path */ 298 if (*p) 299 { /* copy the rest of the string */ 300 if (q != env) 301 q++; /* restore leading separator (if any) */ 302 size_t n = (maxlen - (q - env)); 303 strncpy (q, p, n); 304 } 305 else 306 *q = 0; 307 removed++; 308 } 309 return removed; 310} 311/* 312 function: putenv_purged_ld_preloads() 313 Remove selected preload strings from all LD_PRELOAD* env vars. 314 params: 315 var: executable name (leading characters don't have to match) 316 return: number of instances removed from all PRELOAD vars. 317 */ 318int 319collect::putenv_purged_ld_preloads (const char *var) 320{ 321 int total_removed = 0; 322 if (!var || *var == 0) 323 return 0; 324 for (int ii = 0; LD_PRELOAD[ii]; ii++) 325 { 326 char *ev = getenv (LD_PRELOAD[ii]); 327 int removed = 0; 328 if (!ev) 329 continue; 330 removed = env_strip (ev, var); 331 if (!removed) 332 continue; 333 if (putenv (ev) != 0) 334 dbe_write (2, GTXT ("Can't putenv of %s\n"), ev); 335 total_removed += removed; 336 } 337 return total_removed; 338} 339/* 340 function: putenv_append() 341 append string to current enviroment variable setting and then do a putenv() 342 params: 343 var: environment variable name 344 val: string to append 345 */ 346int 347collect::putenv_append (const char *var, const char *val) 348{ 349 char *ev; 350 if (!var || !val) 351 return 1; 352 const char *old_val = getenv (var); 353 if (old_val == NULL || *old_val == 0) 354 ev = dbe_sprintf ("%s=%s", var, val); 355 else 356 ev = dbe_sprintf ("%s=%s %s", var, old_val, val); 357 358 // now put the new variable into the environment 359 if (add_env (ev)) 360 return 1; 361 return 0; 362} 363 364int 365collect::putenv_libcollector (void) 366{ 367 char buf[MAXPATHLEN + 1]; 368 // --- set SP_COLLECTOR_EXPNAME 369 // fetch the experiment name and CWD 370 char *exp = cc->get_experiment (); 371 char *cwd = getcwd (buf, MAXPATHLEN); 372 char *ev; 373 374 // format the environment variable for the experiment directory name 375 if (cwd != NULL && exp[0] != '/') // experiment is a relative path 376 ev = dbe_sprintf ("%s=%s/%s", SP_COLLECTOR_EXPNAME, cwd, exp); 377 else // getcwd failed or experiment is a fullpath 378 ev = dbe_sprintf ("%s=%s", SP_COLLECTOR_EXPNAME, exp); 379 380 // set the experiment directory name 381 if (add_env (ev)) 382 return 1; 383 384 // --- set SP_COLLECTOR_PARAMS 385 // set the data descriptor 386 exp = cc->get_data_desc (); 387 if (add_env (dbe_sprintf ("%s=%s", SP_COLLECTOR_PARAMS, exp))) 388 return 1; 389 390 // --- set SP_COLLECTOR_FOLLOW_SPEC 391 const char *follow_spec = cc->get_follow_cmp_spec (); 392 if (follow_spec) 393 // selective following has been enabled 394 if (add_env (dbe_sprintf ("%s=%s", SP_COLLECTOR_FOLLOW_SPEC, follow_spec))) 395 return 1; 396 397 if (add_env (dbe_sprintf ("%s=%d", SP_COLLECTOR_FOUNDER, getpid ()))) 398 return 1; 399 if (add_env (dbe_sprintf ("%s=1", SP_COLLECTOR_ORIGIN_COLLECT))) 400 return 1; 401 402 // --- set LD_* 403 if (putenv_libcollector_ld_misc ()) 404 return 1; 405 406 // --- set LD_PRELOAD* 407 if (putenv_libcollector_ld_preloads () != 0) 408 return 1; 409 410 // --- set JAVA_TOOL_OPTIONS 411 if (cc->get_java_mode () == 1) 412 if (putenv_append ("JAVA_TOOL_OPTIONS", "-agentlib:gp-collector")) 413 exit (1); 414#if 0 415 // --- set LD_AUDIT* 416 if (putenv_libcollector_ld_audits () != 0) 417 return 1; 418#endif 419 return 0; 420} 421