binutils-gdb/gprofng/libcollector/linetrace.c
H.J. Lu f4be26838d gprofng: Use symver attribute if available
Use symver attribute if available, instead of asm statement, to support
LTO build.

	PR gprof/28962
	* libcollector/dispatcher.c (timer_create@@GLIBC_2.3.3): Use
	SYMVER_ATTRIBUTE.
	(timer_create@GLIBC_2.2): Likewise.
	(timer_create@GLIBC_2.2.5): Likewise.
	(pthread_create@@GLIBC_2.1): Likewise.
	(pthread_create@GLIBC_2.0): Likewise.
	* libcollector/iotrace.c (open64@@GLIBC_2.2): Likewise.
	(open64@GLIBC_2.1): Likewise.
	(fopen@@GLIBC_2.1): Likewise.
	(fopen@GLIBC_2.0): Likewise.
	(fclose@@GLIBC_2.1): Likewise.
	(fclose@GLIBC_2.0): Likewise.
	(fdopen@@GLIBC_2.1): Likewise.
	(fdopen@GLIBC_2.0): Likewise.
	(pread@@GLIBC_2.2): Likewise.
	(pread@GLIBC_2.1): Likewise.
	(pwrite@@GLIBC_2.2): Likewise.
	(pwrite@GLIBC_2.1): Likewise.
	(pwrite64@@GLIBC_2.2): Likewise.
	(pwrite64@GLIBC_2.1): Likewise.
	(fgetpos@@GLIBC_2.2): Likewise.
	(fgetpos@GLIBC_2.0): Likewise.
	(fgetpos64@@GLIBC_2.2): Likewise.
	(fgetpos64@GLIBC_2.1): Likewise.
	(fsetpos@@GLIBC_2.2): Likewise.
	(fsetpos@GLIBC_2.0): Likewise.
	(fsetpos64@@GLIBC_2.2): Likewise.
	(fsetpos64@GLIBC_2.1): Likewise.
	* libcollector/linetrace.c (posix_spawn@@GLIBC_2.15): Likewise.
	(posix_spawn@GLIBC_2.2): Likewise.
	(posix_spawn@GLIBC_2.2.5): Likewise.
	(posix_spawnp@@GLIBC_2.15): Likewise.
	(posix_spawnp@GLIBC_2.2): Likewise.
	(posix_spawnp@GLIBC_2.2.5): Likewise.
	(popen@@GLIBC_2.1): Likewise.
	(popen@GLIBC_2.0): Likewise.
	(_popen@@GLIBC_2.1): Likewise.
	(_popen@GLIBC_2.0): Likewise.
	* libcollector/mmaptrace.c (dlopen@@GLIBC_2.1): Likewise.
	(dlopen@GLIBC_2.0): Likewise.
	* libcollector/synctrace.c (pthread_cond_wait@@GLIBC_2.3.2):
	Likewise.
	(pthread_cond_wait@GLIBC_2.0): Likewise.
	(pthread_cond_wait@GLIBC_2.2.5): Likewise.
	(pthread_cond_wait@GLIBC_2.2): Likewise.
	(pthread_cond_timedwait@@GLIBC_2.3.2): Likewise.
	(pthread_cond_timedwait@GLIBC_2.0): Likewise.
	(pthread_cond_timedwait@GLIBC_2.2.5): Likewise.
	(pthread_cond_timedwait@GLIBC_2.2): Likewise.
	(sem_wait@@GLIBC_2.1): Likewise.
	(sem_wait@GLIBC_2.0): Likewise.
	* src/collector_module.h (SYMVER_ATTRIBUTE): New.
2022-03-16 06:45:06 -07:00

1999 lines
73 KiB
C

/* 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. */
/*
* Lineage events for process fork, exec, etc.
*/
#include "config.h"
#include <string.h>
#include <elf.h>
#include <regex.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/mman.h>
#include "descendants.h"
/* TprintfT(<level>,...) definitions. Adjust per module as needed */
#define DBG_LT0 0 // for high-level configuration, unexpected errors/warnings
#define DBG_LTT 0 // for interposition on GLIBC functions
#define DBG_LT1 1 // for configuration details, warnings
#define DBG_LT2 2
#define DBG_LT3 3
#define LT_MAXNAMELEN 1024
#define LT_MAXPATHLEN 1024
int __collector_linetrace_shutdown_hwcs_6830763_XXXX = 0;
int dbg_current_mode = FOLLOW_NONE; /* for debug only */
unsigned line_key = COLLECTOR_TSD_INVALID_KEY;
line_mode_t line_mode = LM_DORMANT;
int user_follow_mode = FOLLOW_ON;
int java_mode = 0;
static char *user_follow_spec;
static char new_lineage[LT_MAXNAMELEN];
static char curr_lineage[LT_MAXNAMELEN];
static char linetrace_exp_dir_name[LT_MAXPATHLEN + 1]; // experiment directory
/* lineage tracking for descendants of this process */
static int fork_linenum = 0;
static int line_initted = 0;
static collector_mutex_t fork_lineage_lock = COLLECTOR_MUTEX_INITIALIZER;
static collector_mutex_t clone_lineage_lock = COLLECTOR_MUTEX_INITIALIZER;
/* interposition */
#define CALL_REAL(x) (*(int(*)())__real_##x)
#define CALL_REALC(x) (*(char*(*)())__real_##x)
#define CALL_REALF(x) (*(FILE*(*)())__real_##x)
#define NULL_PTR(x) ( __real_##x == NULL )
// For a given Linux, which lib functions have more than one GLIBC version? Do this:
// objdump -T `find /lib /lib64 -name "*.so*"` | grep GLIBC | grep text | grep \(
static void *__real_fork = NULL;
static void *__real_vfork = NULL;
static void *__real_execve = NULL;
static void *__real_execvp = NULL;
static void *__real_execv = NULL;
static void *__real_execle = NULL;
static void *__real_execlp = NULL;
static void *__real_execl = NULL;
static void *__real_clone = NULL;
static void *__real_grantpt = NULL;
static void *__real_ptsname = NULL;
static void *__real_popen = NULL;
static int clone_linenum = 0;
#if ARCH(Intel)
#if WSIZE(32)
static void *__real_popen_2_1 = NULL;
static void *__real_popen_2_0 = NULL;
static void *__real_posix_spawn_2_15 = NULL;
static void *__real_posix_spawnp_2_15 = NULL;
static void *__real_posix_spawn_2_2 = NULL;
static void *__real_posix_spawnp_2_2 = NULL;
#elif WSIZE(64)
static void *__real_posix_spawn_2_15 = NULL;
static void *__real_posix_spawnp_2_15 = NULL;
static void *__real_posix_spawn_2_2_5 = NULL;
static void *__real_posix_spawnp_2_2_5 = NULL;
#endif /* WSIZE() */
#endif /* ARCH(Intel) */
static void *__real_system = NULL;
static void *__real_posix_spawn = NULL;
static void *__real_posix_spawnp = NULL;
static void *__real_setuid = NULL;
static void *__real_seteuid = NULL;
static void *__real_setreuid = NULL;
static void *__real_setgid = NULL;
static void *__real_setegid = NULL;
static void *__real_setregid = NULL;
static void linetrace_dormant ();
static int check_follow_fork ();
static int check_follow_exec (const char *execfile);
static int check_follow_combo (const char *execfile);
static int path_collectable (const char *execfile);
static char * build_experiment_path (char *instring, size_t instring_sz, const char *lineage_str);
static int init_lineage_intf ();
/* ------- "Previously dbx-visible" function prototypes ----------------- */
static int linetrace_follow_experiment (const char *follow_spec, const char *lineage_str, const char *execfile);
static char *lineage_from_expname (char *lineage_str, size_t lstr_sz, const char *expname);
static void linetrace_ext_fork_prologue (const char *variant, char * new_lineage, int *following_fork);
static void linetrace_ext_fork_epilogue (const char *variant, const pid_t ret, char * new_lineage, int *following_fork);
static char **linetrace_ext_exec_prologue (const char *variant,
const char* path, char *const argv[], char *const envp[], int *following_exec);
static void linetrace_ext_exec_epilogue (const char *variant, char *const envp[], const int ret, int *following_exec);
static void linetrace_ext_combo_prologue (const char *variant, const char *cmd, int *following_combo);
static void linetrace_ext_combo_epilogue (const char *variant, const int ret, int *following_combo);
#ifdef DEBUG
static int
get_combo_flag ()
{
int * guard = NULL;
int combo_flag = ((line_mode == LM_TRACK_LINEAGE) ? ((CHCK_REENTRANCE (guard)) ? 1 : 0) : 0);
return combo_flag;
}
#endif /* DEBUG */
/* must be called for potentially live experiment */
int
__collector_ext_line_init (int *precord_this_experiment,
const char * progspec, const char * progname)
{
*precord_this_experiment = 1;
TprintfT (DBG_LT0, "__collector_ext_line_init(%s)\n", progspec);
if (NULL_PTR (fork))
if (init_lineage_intf ())
{
TprintfT (DBG_LT0, "__collector_ext_line_init() ERROR: initialization failed.\n");
return COL_ERROR_LINEINIT;
}
/* check the follow spec */
user_follow_spec = CALL_UTIL (getenv)(SP_COLLECTOR_FOLLOW_SPEC);
if (user_follow_spec != NULL)
{
TprintfT (DBG_LT0, "collector: %s=%s\n", SP_COLLECTOR_FOLLOW_SPEC, user_follow_spec);
if (!linetrace_follow_experiment (user_follow_spec, curr_lineage, progname))
{
*precord_this_experiment = 0;
TprintfT (DBG_LT0, "collector: -F =<regex> does not match, will NOT be followed\n");
}
else
TprintfT (DBG_LT0, "collector: -F =<regex> matches, will be followed\n");
user_follow_mode = FOLLOW_ALL;
}
__collector_env_save_preloads ();
TprintfT (DBG_LT0, "__collector_ext_line_init(), progname=%s, followspec=%s, followthis=%d\n",
progname, user_follow_spec ? user_follow_spec : "NULL",
*precord_this_experiment);
line_mode = LM_TRACK_LINEAGE; /* even if we don't follow, we report the interposition */
line_initted = 1;
return COL_ERROR_NONE;
}
/*
* int __collector_ext_line_install(args)
* Check args to determine which line events to follow.
* Create tsd key for combo flag.
*/
int
__collector_ext_line_install (char *args, const char * expname)
{
if (!line_initted)
{
TprintfT (DBG_LT0, "__collector_ext_line_install(%s) ERROR: init hasn't be called yet\n", args);
return COL_ERROR_EXPOPEN;
}
TprintfT (DBG_LT0, "__collector_ext_line_install(%s, %s)\n", args, expname);
line_key = __collector_tsd_create_key (sizeof (int), NULL, NULL);
/* determine experiment name */
__collector_strlcpy (linetrace_exp_dir_name, expname, sizeof (linetrace_exp_dir_name));
lineage_from_expname (curr_lineage, sizeof (curr_lineage), linetrace_exp_dir_name);
user_follow_mode = CALL_UTIL (atoi)(args);
TprintfT (DBG_LT0, "__collector_ext_line_install() user_follow_mode=0x%X, linetrace_exp_dir_name=%s\n",
user_follow_mode, linetrace_exp_dir_name);
// determine java mode
char * java_follow_env = CALL_UTIL (getenv)(JAVA_TOOL_OPTIONS);
if (java_follow_env != NULL && CALL_UTIL (strstr)(java_follow_env, COLLECTOR_JVMTI_OPTION))
java_mode = 1;
// backup collector specific env
if (sp_env_backup == NULL)
{
sp_env_backup = __collector_env_backup ();
TprintfT (DBG_LT0, "__collector_ext_line_install creating sp_env_backup -- 0x%p\n", sp_env_backup);
}
else
TprintfT (DBG_LT0, "__collector_ext_line_install existing sp_env_backup -- 0x%p\n", sp_env_backup);
if (user_follow_mode == FOLLOW_NONE)
__collector_env_unset (NULL);
char logmsg[256];
logmsg[0] = '\0';
if (user_follow_mode != FOLLOW_NONE)
CALL_UTIL (strlcat)(logmsg, "fork|exec|combo", sizeof (logmsg));
size_t slen = __collector_strlen (logmsg);
if (slen > 0)
logmsg[slen] = '\0';
else
CALL_UTIL (strlcat)(logmsg, "none", sizeof (logmsg));
/* report which line events are followed */
(void) __collector_log_write ("<setting %s=\"%s\"/>\n", SP_JCMD_LINETRACE, logmsg);
TprintfT (DBG_LT0, "__collector_ext_line_install(%s): %s \n", expname, logmsg);
return COL_ERROR_NONE;
}
char *
lineage_from_expname (char *lineage_str, size_t lstr_sz, const char *expname)
{
TprintfT (DBG_LT0, "lineage_from_expname(%s, %s)\n", lineage_str, expname);
char *p = NULL;
if (lstr_sz < 1 || !lineage_str || !expname)
{
TprintfT (DBG_LT0, "lineage_from_expname(): ERROR, null string\n");
return NULL;
}
/* determine lineage from experiment name */
p = __collector_strrchr (expname, '/');
if ((p == NULL) || (*++p != '_'))
{
lineage_str[0] = 0;
TprintfT (DBG_LT2, "lineage_from_expname(): expt=%s lineage=\".\" (founder)\n", expname);
}
else
{
size_t tmp = __collector_strlcpy (lineage_str, p, lstr_sz);
if (tmp >= lstr_sz)
TprintfT (DBG_LT0, "lineage_from_expname(): ERROR: expt=%s lineage=\"%s\" truncated %ld characters\n",
expname, lineage_str, (long) (tmp - lstr_sz));
lineage_str[lstr_sz - 1] = 0;
p = __collector_strchr (lineage_str, '.');
if (p != NULL)
*p = '\0';
TprintfT (DBG_LT2, "lineage_from_expname(): expt=%s lineage=\"%s\"\n", expname, lineage_str);
}
return lineage_str;
}
/*
* void __collector_line_cleanup (void)
* Disable logging. Clear backup ENV.
*/
void
__collector_line_cleanup (void)
{
if (line_mode == LM_CLOSED)
{
TprintfT (DBG_LT0, "__collector_line_cleanup(): WARNING, line is already closed\n");
return;
}
else if (line_mode == LM_DORMANT)
TprintfT (DBG_LT0, "__collector_line_cleanup(): ERROR, line is DORMANT\n");
else
TprintfT (DBG_LT0, "__collector_line_cleanup()\n");
line_mode = LM_CLOSED;
user_follow_mode = FOLLOW_NONE;
dbg_current_mode = FOLLOW_NONE; /* for debug only */
line_key = COLLECTOR_TSD_INVALID_KEY;
java_mode = 0;
if (sp_env_backup != NULL)
{
__collector_env_backup_free ();
sp_env_backup = NULL;
}
return;
}
/*
* void __collector_ext_line_close (void)
* Disable logging. Cleans ENV vars. Clear backup ENV.
*/
void
__collector_ext_line_close (void)
{
TprintfT (DBG_LT0, "__collector_ext_line_close()\n");
__collector_line_cleanup ();
__collector_env_unset (NULL);
return;
}
/*
* void linetrace_dormant(void)
* Disable logging. Preserve ENV vars.
*/
static void
linetrace_dormant (void)
{
if (line_mode == LM_DORMANT)
{
TprintfT (DBG_LT0, "linetrace_dormant() -- already dormant\n");
return;
}
else if (line_mode == LM_CLOSED)
{
TprintfT (DBG_LT0, "linetrace_dormant(): ERROR, line is already CLOSED\n");
return;
}
else
TprintfT (DBG_LT0, "linetrace_dormant()\n");
line_mode = LM_DORMANT;
return;
}
static int
check_follow_fork ()
{
int follow = (user_follow_mode != FOLLOW_NONE);
TprintfT (DBG_LT0, "check_follow_fork()=%d\n", follow);
return follow;
}
static int
check_follow_exec (const char *execfile)
{
int follow = (user_follow_mode != FOLLOW_NONE);
if (follow)
{
/* revise based on collectability of execfile */
follow = path_collectable (execfile);
}
TprintfT (DBG_LT0, "check_follow_exec(%s)=%d\n", execfile, follow);
return follow;
}
static int
check_follow_combo (const char *execfile)
{
int follow = (user_follow_mode != FOLLOW_NONE);
TprintfT (DBG_LT0, "check_follow_combo(%s)=%d\n", execfile, follow);
return follow;
}
static int
check_fd_dynamic (int fd)
{
TprintfT (DBG_LT0, "check_fd_dynamic(%d)\n", fd);
off_t off = CALL_UTIL (lseek)(fd, (off_t) 0, SEEK_END);
size_t sz = (size_t) 8192; /* one page should suffice */
if (sz > off)
sz = off;
char *p = CALL_UTIL (mmap64)((char *) 0, sz, PROT_READ, MAP_PRIVATE, fd, (off64_t) 0);
if (p == MAP_FAILED)
{
TprintfT (DBG_LT0, "check_fd_dynamic(): ERROR/WARNING: mmap failed for `%d'\n", fd);
(void) __collector_log_write ("<event kind=\"%s\" id=\"%d\">%s</event>\n", SP_JCMD_CWARN,
COL_WARN_NOFOLLOW, "mmap-failed");
return 0;
}
char elfclass = p[EI_CLASS];
if ((p[EI_MAG0] != ELFMAG0) ||
(p[EI_MAG1] != ELFMAG1) ||
(p[EI_MAG2] != ELFMAG2) ||
(p[EI_MAG3] != ELFMAG3) ||
(elfclass != ELFCLASS32 && elfclass != ELFCLASS64)
)
{
TprintfT (DBG_LT0, "check_fd_dynamic(): WARNING: Command `%d' is not executable ELF!\n", fd);
CALL_UTIL (munmap)(p, sz);
return 1;
}
Elf32_Ehdr *ehdr32 = (Elf32_Ehdr*) p;
Elf64_Ehdr *ehdr64 = (Elf64_Ehdr*) p;
Elf64_Off e_phoff;
Elf64_Half e_phnum;
Elf64_Half e_phentsize;
if (elfclass == ELFCLASS32)
{
e_phoff = ehdr32->e_phoff;
e_phnum = ehdr32->e_phnum;
e_phentsize = ehdr32->e_phentsize;
}
else
{
e_phoff = ehdr64->e_phoff;
e_phnum = ehdr64->e_phnum;
e_phentsize = ehdr64->e_phentsize;
}
if ((sizeof (Elf32_Ehdr) > sz) ||
(sizeof (Elf64_Ehdr) > sz) ||
(e_phoff + e_phentsize * (e_phnum - 1) > sz))
{
TprintfT (DBG_LT0, "check_fd_dynamic(): WARNING: Command `%d' ELF file did not fit in page!\n", fd);
#if 0
(void) __collector_log_write ("<event kind=\"%s\" id=\"%d\">%s</event>\n", SP_JCMD_CWARN,
COL_WARN_RISKYFOLLOW, "ELF header size");
#endif
CALL_UTIL (munmap)(p, sz);
return 1;
}
TprintfT (DBG_LT2, "check_fd_dynamic(): elfclass=%d, e_phoff=%lu e_phnum=%lu e_phentsize=%lu\n",
(int) elfclass, (unsigned long) e_phoff, (unsigned long) e_phnum,
(unsigned long) e_phentsize);
int dynamic = 0;
Elf64_Half i;
for (i = 0; i < e_phnum; i++)
{
if (elfclass == ELFCLASS32)
{
if (PT_DYNAMIC ==
((Elf32_Phdr*) (p + e_phoff + e_phentsize * i))->p_type)
{
dynamic = 1;
break;
}
}
else
{
if (PT_DYNAMIC ==
((Elf64_Phdr*) (p + e_phoff + e_phentsize * i))->p_type)
{
dynamic = 1;
break;
}
}
}
if (!dynamic)
{
TprintfT (DBG_LT0, "check_fd_dynamic(): ERROR/WARNING: Command `%d' is not a dynamic executable!\n", fd);
#if 0
(void) __collector_log_write ("<event kind=\"%s\" id=\"%d\">%s</event>\n", SP_JCMD_CWARN,
COL_WARN_NOFOLLOW, "!dynamic");
#endif
}
else
TprintfT (DBG_LT2, "check_fd_dynamic(): Command `%d' is a dynamic executable!\n", fd);
CALL_UTIL (munmap)(p, sz);
return dynamic;
}
static int
check_dynamic (const char *execfile)
{
TprintfT (DBG_LT2, "check_dynamic(%s)\n", execfile);
int fd = CALL_UTIL (open)(execfile, O_RDONLY);
if (fd == -1)
{
TprintfT (DBG_LT0, "check_dynamic(): ERROR/WARNING: Command `%s' could not be opened!\n", execfile);
(void) __collector_log_write ("<event kind=\"%s\" id=\"%d\">%s</event>\n", SP_JCMD_CWARN,
COL_WARN_RISKYFOLLOW, "open");
return 1; /* follow, though exec will presumably fail */
}
int ret = check_fd_dynamic (fd);
CALL_UTIL (close)(fd);
return ret;
}
static int
path_collectable (const char *execfile)
{
TprintfT (DBG_LT0, "path_collectable(%s)\n", execfile);
/* Check that execfile exists and is a collectable executable */
/* logging warning when collection is likely to be unsuccessful */
/* (if check isn't accurate, generally best not to include it) */
if (execfile && !__collector_strchr (execfile, '/'))
{ /* got an unqualified name */
/* XXXX locate execfile on PATH to be able to check it */
TprintfT (DBG_LT0, "path_collectable(): WARNING: Can't check unqualified executable `%s'\n", execfile);
#if 0
(void) __collector_log_write ("<event kind=\"%s\" id=\"%d\">%s</event>\n", SP_JCMD_CWARN,
COL_WARN_RISKYFOLLOW, "path");
#endif
return 1; /* follow unqualified execfile unchecked */
}
struct stat sbuf;
if (stat (execfile, &sbuf))
{ /* can't stat it */
TprintfT (DBG_LT0, "path_collectable(): WARNING: Can't stat `%s'\n", execfile);
#if 0
(void) __collector_log_write ("<event kind=\"%s\" id=\"%d\">%s</event>\n", SP_JCMD_CWARN,
COL_WARN_RISKYFOLLOW, "stat");
#endif
return 1; /* follow, though exec will probably fail */
}
TprintfT (DBG_LT2, "path_collectable(%s) mode=0%o uid=%d gid=%d\n",
execfile, sbuf.st_mode, sbuf.st_uid, sbuf.st_gid);
if (((sbuf.st_mode & S_IXUSR) == 0) || ((sbuf.st_mode & S_IFMT) == S_IFDIR))
{
TprintfT (DBG_LT0, "path_collectable(): WARNING: Command `%s' is NOT an executable file!\n", execfile);
#if 0
(void) __collector_log_write ("<event kind=\"%s\" id=\"%d\">%s</event>\n", SP_JCMD_CWARN,
COL_WARN_RISKYFOLLOW, "mode");
#endif
return 1; /* follow, though exec will presumably fail */
}
/* XXXX setxid(root) is OK iff libcollector is registered as secure */
/* XXXX setxid(non-root) is OK iff umask is accomodating */
if (((sbuf.st_mode & S_ISUID) != 0) || ((sbuf.st_mode & S_ISGID) != 0))
{
TprintfT (DBG_LT0, "path_collectable(): WARNING: Command `%s' is SetXID!\n", execfile);
#if 0
(void) __collector_log_write ("<event kind=\"%s\" id=\"%d\">%s</event>\n", SP_JCMD_CWARN,
COL_WARN_RISKYFOLLOW, "setxid");
#endif
return 1; /* follow, though collection may be unreliable */
}
if (!check_dynamic (execfile))
{
TprintfT (DBG_LT0, "path_collectable(%s) WARNING/ERROR: not dynamic, not collectng!\n", execfile);
return 0; /* don't follow, collection will fail unpredictably */
}
TprintfT (DBG_LT2, "path_collectable(%s) OK!\n", execfile);
return 1; /* OK to follow */
}
static char *
build_experiment_path (char * instring, size_t instring_sz, const char *lineage_str)
{
TprintfT (DBG_LT0, "build_experiment_path(,%ld, %s)\n",
(long) instring_sz, lineage_str);
const char *p = CALL_UTIL (strstr)(linetrace_exp_dir_name, DESCENDANT_EXPT_KEY);
int basedir_sz;
if (p)
basedir_sz = p - linetrace_exp_dir_name + 4; /* +3 because of DESCENDANT_EXPT_KEY */
else
basedir_sz = __collector_strlen (linetrace_exp_dir_name) + 1;
int additional_sz = __collector_strlen (lineage_str) + 4;
if (basedir_sz + additional_sz > instring_sz)
{
TprintfT (DBG_LT0, "build_experiment_path(%s,%s): ERROR: path too long: %d > %ld\n",
linetrace_exp_dir_name, lineage_str,
basedir_sz + additional_sz, (long) instring_sz);
*instring = 0;
return NULL;
}
__collector_strlcpy (instring, linetrace_exp_dir_name, basedir_sz);
size_t slen = __collector_strlen (instring);
CALL_UTIL (snprintf)(instring + slen, instring_sz - slen, "/%s.er", lineage_str);
assert (__collector_strlen (instring) + 1 == basedir_sz + additional_sz);
return instring;
}
static void
check_reuid_change (uid_t ruid, uid_t euid)
{
uid_t curr_ruid = getuid ();
uid_t curr_euid = geteuid ();
mode_t curr_umask = umask (0);
umask (curr_umask); /* restore original umask */
int W_oth = !(curr_umask & S_IWOTH);
TprintfT (DBG_LT0, "check_reuid_change(%d,%d): umask=%03o\n", ruid, euid, curr_umask);
TprintfT (DBG_LT0, "check_reuid_change(): umask W usr=%d grp=%d oth=%d\n",
(int) (!(curr_umask & S_IWUSR)), (int) (!(curr_umask & S_IWGRP)), W_oth);
if (ruid != -1)
{
TprintfT (DBG_LT0, "check_reuid_change(%d->%d)\n", curr_ruid, ruid);
if ((curr_euid == 0) && (ruid != 0) && !W_oth)
{
/* changing to non-root ID, with umask blocking writes by other */
TprintfT (DBG_LT0, "check_reuid_change(): ERROR/WARNING: umask blocks write other after ruid change (%d->%d)\n",
curr_ruid, ruid);
(void) __collector_log_write ("<event kind=\"%s\" id=\"%d\">umask %03o ruid %d->%d</event>\n",
SP_JCMD_CWARN, COL_WARN_IDCHNG, curr_umask, curr_ruid, ruid);
}
}
if (euid != -1)
{
TprintfT (DBG_LT0, "check_reuid_change(%d->%d)\n", curr_euid, euid);
if ((curr_euid == 0) && (euid != 0) && !W_oth)
{
/* changing to non-root ID, with umask blocking writes by other */
TprintfT (DBG_LT0, "check_reuid_change(): ERROR/WARNING: umask blocks write other after euid change (%d->%d)\n",
curr_euid, euid);
(void) __collector_log_write ("<event kind=\"%s\" id=\"%d\">umask %03o euid %d->%d</event>\n",
SP_JCMD_CWARN, COL_WARN_IDCHNG, curr_umask, curr_euid, euid);
}
}
}
static void
check_regid_change (gid_t rgid, gid_t egid)
{
gid_t curr_rgid = getgid ();
gid_t curr_egid = getegid ();
uid_t curr_euid = geteuid ();
mode_t curr_umask = umask (0);
umask (curr_umask); /* restore original umask */
int W_oth = !(curr_umask & S_IWOTH);
TprintfT (DBG_LT0, "check_regid_change(%d,%d): umask=%03o euid=%d\n",
rgid, egid, curr_umask, curr_euid);
TprintfT (DBG_LT0, "umask W usr=%d grp=%d oth=%d\n",
(int) (!(curr_umask & S_IWUSR)), (int) (!(curr_umask & S_IWGRP)), W_oth);
if (rgid != -1)
{
TprintfT (DBG_LT0, "check_regid_change(%d->%d)\n", curr_rgid, rgid);
if ((curr_euid == 0) && (rgid != 0) && !W_oth)
{
/* changing to non-root ID, with umask blocking writes by other */
TprintfT (DBG_LT0, "check_regid_change(): WARNING/ERROR: umask blocks write other after rgid change (%d->%d)\n",
curr_rgid, rgid);
(void) __collector_log_write ("<event kind=\"%s\" id=\"%d\">umask %03o rgid %d->%d</event>\n",
SP_JCMD_CWARN, COL_WARN_IDCHNG, curr_umask, curr_rgid, rgid);
}
}
if (egid != -1)
{
TprintfT (DBG_LT0, "check_regid_change(): check_egid_change(%d->%d)\n", curr_egid, egid);
if ((curr_euid == 0) && (egid != 0) && !W_oth)
{
/* changing to non-root ID, with umask blocking writes by other */
TprintfT (DBG_LT0, "check_regid_change(): WARNING/ERROR: umask blocks write other after egid change (%d->%d)\n",
curr_egid, egid);
(void) __collector_log_write ("<event kind=\"%s\" id=\"%d\">umask %03o egid %d->%d</event>\n",
SP_JCMD_CWARN, COL_WARN_IDCHNG, curr_umask, curr_egid, egid);
}
}
}
static int
init_lineage_intf ()
{
void *dlflag;
TprintfT (DBG_LT2, "init_lineage_intf()\n");
static int nesting_check = 0;
if (nesting_check >= 2)
{
/* segv before stack blows up */
nesting_check /= (nesting_check - 2);
}
nesting_check++;
__real_fork = dlsym (RTLD_NEXT, "fork");
if (__real_fork == NULL)
{
__real_fork = dlsym (RTLD_DEFAULT, "fork");
if (__real_fork == NULL)
return 1;
dlflag = RTLD_DEFAULT;
}
else
dlflag = RTLD_NEXT;
TprintfT (DBG_LT2, "init_lineage_intf() using RTLD_%s\n",
dlflag == RTLD_DEFAULT ? "DEFAULT" : "NEXT");
TprintfT (DBG_LT2, "init_lineage_intf() @0x%p __real_fork\n", __real_fork);
__real_vfork = dlsym (dlflag, "vfork");
TprintfT (DBG_LT2, "init_lineage_intf() @0x%p __real_vfork\n", __real_vfork);
__real_execve = dlsym (dlflag, "execve");
TprintfT (DBG_LT2, "init_lineage_intf() @0x%p __real_execve\n", __real_execve);
__real_execvp = dlsym (dlflag, "execvp");
TprintfT (DBG_LT2, "init_lineage_intf() @0x%p __real_execvp\n", __real_execvp);
__real_execv = dlsym (dlflag, "execv");
TprintfT (DBG_LT2, "init_lineage_intf() @0x%p __real_execv\n", __real_execv);
__real_execle = dlsym (dlflag, "execle");
TprintfT (DBG_LT2, "init_lineage_intf() @0x%p __real_execle\n", __real_execle);
__real_execlp = dlsym (dlflag, "execlp");
TprintfT (DBG_LT2, "init_lineage_intf() @0x%p __real_execlp\n", __real_execlp);
__real_execl = dlsym (dlflag, "execl");
TprintfT (DBG_LT2, "init_lineage_intf() @0x%p __real_execl\n", __real_execl);
__real_clone = dlsym (dlflag, "clone");
TprintfT (DBG_LT2, "init_lineage_intf() @0x%p __real_clone\n", __real_clone);
__real_posix_spawn = dlsym (dlflag, "posix_spawn");
TprintfT (DBG_LT2, "init_lineage_intf() @0x%p __real_posix_spawn\n",
__real_posix_spawn);
__real_posix_spawnp = dlsym (dlflag, "posix_spawnp");
TprintfT (DBG_LT2, "init_lineage_intf() @0x%p __real_posix_spawnp\n",
__real_posix_spawnp);
__real_popen = dlvsym (dlflag, "popen", SYS_POPEN_VERSION);
TprintfT (DBG_LT2, "init_lineage_intf()[%s] @0x%p __real_popen\n",
SYS_POPEN_VERSION, __real_popen);
#if ARCH(Intel)
__real_posix_spawn_2_15 = dlvsym (dlflag, "posix_spawn", SYS_POSIX_SPAWN_VERSION);
__real_posix_spawnp_2_15 = dlvsym (dlflag, "posix_spawnp", SYS_POSIX_SPAWN_VERSION);
#if WSIZE(32)
__real_popen_2_1 = __real_popen;
__real_popen_2_0 = dlvsym (dlflag, "popen", "GLIBC_2.0");
__real_posix_spawn_2_2 = dlvsym (dlflag, "posix_spawn", "GLIBC_2.2");
__real_posix_spawnp_2_2 = dlvsym (dlflag, "posix_spawnp", "GLIBC_2.2");
#elif WSIZE(64)
__real_posix_spawn_2_2_5 = dlvsym (dlflag, "posix_spawn", "GLIBC_2.2.5");
__real_posix_spawnp_2_2_5 = dlvsym (dlflag, "posix_spawnp", "GLIBC_2.2.5");
#endif /* WSIZE() */
#endif /* ARCH(Intel) */
__real_grantpt = dlsym (dlflag, "grantpt");
TprintfT (DBG_LT2, "init_lineage_intf() @0x%p __real_grantpt\n", __real_grantpt);
__real_ptsname = dlsym (dlflag, "ptsname");
TprintfT (DBG_LT2, "init_lineage_intf() @0x%p __real_ptsname\n", __real_ptsname);
__real_system = dlsym (dlflag, "system");
TprintfT (DBG_LT2, "init_lineage_intf() @0x%p __real_system\n", __real_system);
__real_setuid = dlsym (dlflag, "setuid");
TprintfT (DBG_LT2, "init_lineage_intf() @0x%p __real_setuid\n", __real_setuid);
__real_seteuid = dlsym (dlflag, "seteuid");
TprintfT (DBG_LT2, "init_lineage_intf() @0x%p __real_seteuid\n", __real_seteuid);
__real_setreuid = dlsym (dlflag, "setreuid");
TprintfT (DBG_LT2, "init_lineage_intf() @0x%p __real_setreuid\n", __real_setreuid);
__real_setgid = dlsym (dlflag, "setgid");
TprintfT (DBG_LT2, "init_lineage_intf() @0x%p __real_setgid\n", __real_setgid);
__real_setegid = dlsym (dlflag, "setegid");
TprintfT (DBG_LT2, "init_lineage_intf() @0x%p __real_setegid\n", __real_setegid);
__real_setregid = dlsym (dlflag, "setregid");
TprintfT (DBG_LT2, "init_lineage_intf() @0x%p __real_setregid\n", __real_setregid);
return 0;
}
/*------------------------------------------------------------------------ */
/* Note: The following _prologue and _epilogue functions used to be dbx-visible.
They are used to appropriately manage lineage-changing events, by
quiescing and re-enabling/re-setting experiment collection before and after,
and logging the lineage-change in the process/experiment undertaking it.
As shown by the interposition functions for fork, exec, etc., which follow,
the _prologue should be called immediately prior (such as a breakpoint
action defined at function entry) and the _epilogue called immediately
after (such as a breakpoint action defined at function return).
*/
/*
Notes on MT from Solaris 10 man pthread_atfork:
All multithreaded applications that call fork() in a POSIX
threads program and do more than simply call exec(2) in the
child of the fork need to ensure that the child is protected
from deadlock.
Since the "fork-one" model results in duplicating only the
thread that called fork(), it is possible that at the time
of the call another thread in the parent owns a lock. This
thread is not duplicated in the child, so no thread will
unlock this lock in the child. Deadlock occurs if the sin-
gle thread in the child needs this lock.
The problem is more serious with locks in libraries. Since
a library writer does not know if the application using the
library calls fork(), the library must protect itself from
such a deadlock scenario. If the application that links
with this library calls fork() and does not call exec() in
the child, and if it needs a library lock that may be held
by some other thread in the parent that is inside the
library at the time of the fork, the application deadlocks
inside the library.
*/
static void
linetrace_ext_fork_prologue (const char *variant, char * n_lineage, int *following_fork)
{
TprintfT (DBG_LT0, "linetrace_ext_fork_prologue; variant=%s; new_lineage=%s; follow=%d\n",
variant, n_lineage, *following_fork);
__collector_env_print ("fork_prologue start");
if (dbg_current_mode != FOLLOW_NONE)
TprintfT (DBG_LT0, "linetrace_ext_fork_prologue(%s) ERROR: dbg_current_mode=%d, changing to FOLLOW_FORK!\n",
variant, dbg_current_mode);
dbg_current_mode = FOLLOW_ON;
if (__collector_strncmp ((char *) variant, "clone", sizeof ("clone") - 1) == 0)
{
__collector_mutex_lock (&clone_lineage_lock);
CALL_UTIL (snprintf)(n_lineage, LT_MAXNAMELEN, "%s_C%d", curr_lineage, ++clone_linenum);
__collector_mutex_unlock (&clone_lineage_lock);
}
else
{
__collector_mutex_lock (&fork_lineage_lock);
CALL_UTIL (snprintf)(n_lineage, LT_MAXNAMELEN, "%s_f%d", curr_lineage, ++fork_linenum);
__collector_mutex_unlock (&fork_lineage_lock);
}
*following_fork = check_follow_fork ();
/* write message before suspending, or it won't be written */
hrtime_t ts = GETRELTIME ();
TprintfT (DBG_LT0, "linetrace_ext_fork_prologue; variant=%s; new_lineage=%s; follow=%d\n",
variant, n_lineage, *following_fork);
__collector_log_write ("<event kind=\"%s\" tstamp=\"%u.%09u\" variant=\"%s\" lineage=\"%s\" follow=\"%d\"/>\n",
SP_JCMD_DESC_START,
(unsigned) (ts / NANOSEC), (unsigned) (ts % NANOSEC),
variant, n_lineage, *following_fork);
__collector_ext_dispatcher_thread_timer_suspend ();
__collector_ext_hwc_lwp_suspend ();
__collector_env_print ("fork_prologue end");
}
static void
linetrace_ext_fork_epilogue (const char *variant, const pid_t ret, char * n_lineage, int *following_fork)
{
if (dbg_current_mode == FOLLOW_NONE)
TprintfT (DBG_LT0, "linetrace_ext_fork_epilogue(%s) ERROR: dbg_current_mode=%d!\n",
variant, dbg_current_mode);
/* compute descendant experiment name */
char new_exp_name[LT_MAXPATHLEN];
/* save exp_name to global var */
if (!build_experiment_path (new_exp_name, sizeof (new_exp_name), n_lineage))
TprintfT (DBG_LT0, "linetrace_ext_fork_epilogue(%s): ERROR SP_COLLECTOR_EXPNAME not set\n", n_lineage);
TprintfT (DBG_LT0, "linetrace_ext_fork_epilogue(%s):%d() returned %d %s; child experiment name = %s\n",
variant, *following_fork, ret, (ret ? "parent" : "child"), new_exp_name);
if (ret == 0)
{
/* *************************************child */
__collector_env_print ("fork_epilogue child at start");
/* start a new line */
fork_linenum = 0;
__collector_mutex_init (&fork_lineage_lock);
clone_linenum = 0;
__collector_mutex_init (&clone_lineage_lock);
__collector_env_update (NULL);
__collector_env_print ("fork_epilogue child after env_update");
__collector_clean_state ();
__collector_env_print ("fork_epilogue child after clean_slate");
__collector_line_cleanup ();
__collector_env_print ("fork_epilogue child after line_cleanup");
if (*following_fork)
{
/* stop recording this experiment, but preserve env vars */
linetrace_dormant ();
__collector_env_print ("fork_epilogue child after linetrace_dormant");
//static char exp_name_env[LT_MAXPATHLEN];
char * exp_name_env = CALL_UTIL (calloc)(LT_MAXPATHLEN, 1);
CALL_UTIL (snprintf)(exp_name_env, LT_MAXPATHLEN, "%s=%s", SP_COLLECTOR_EXPNAME, new_exp_name);
CALL_UTIL (putenv)(exp_name_env);
const char *params = CALL_UTIL (getenv)(SP_COLLECTOR_PARAMS);
int ret;
if (new_exp_name == NULL)
TprintfT (DBG_LT0, "linetrace_ext_fork_epilogue: ERROR: getenv(%s) undefined -- new expt aborted!\n",
SP_COLLECTOR_EXPNAME);
else if (params == NULL)
TprintfT (DBG_LT0, "linetrace_ext_fork_epilogue: ERROR: getenv(%s) undefined -- new expt aborted!\n",
SP_COLLECTOR_PARAMS);
else if ((ret = __collector_open_experiment (new_exp_name, params, SP_ORIGIN_FORK)))
TprintfT (DBG_LT0, "linetrace_ext_fork_epilogue: ERROR: '%s' open failed, ret=%d\n",
new_exp_name, ret);
else
TprintfT (DBG_LT0, "linetrace_ext_fork_epilogue: opened(%s)\n", new_exp_name);
TprintfT (DBG_LT0, "linetrace_ext_fork_epilogue(%s) returning to *child*\n", variant);
}
else
{
/* disable current and further linetrace experiment resumption */
TprintfT (DBG_LT0, "linetrace_ext_fork_epilogue(%s) child calling line_close\n", variant);
__collector_ext_line_close ();
}
__collector_env_print ("fork_epilogue child at end");
/* *************************************end child */
}
else
{
/* *************************************parent */
__collector_env_print ("fork_epilogue parent at start");
__collector_ext_dispatcher_thread_timer_resume ();
__collector_ext_hwc_lwp_resume ();
hrtime_t ts = GETRELTIME ();
char msg[256 + LT_MAXPATHLEN];
if (ret >= 0)
CALL_UTIL (snprintf)(msg, sizeof (msg), "pid=%d", ret);
else
{
/* delete stillborn experiment? */
char errmsg[256];
strerror_r (errno, errmsg, sizeof (errmsg));
CALL_UTIL (snprintf)(msg, sizeof (msg), "err %s", errmsg);
}
__collector_log_write ("<event kind=\"%s\" tstamp=\"%u.%09u\" variant=\"%s\" lineage=\"%s\" follow=\"%d\" msg=\"%s\"/>\n",
SP_JCMD_DESC_STARTED,
(unsigned) (ts / NANOSEC), (unsigned) (ts % NANOSEC),
variant, n_lineage, *following_fork, msg);
/* environment remains set for collection */
__collector_env_print ("fork_epilogue parent at end");
/* *************************************end parent */
}
dbg_current_mode = FOLLOW_NONE;
*following_fork = 0;
}
static char**
linetrace_ext_exec_prologue_end (const char *variant, const char* cmd_string,
char *const envp[], int following_exec)
{
char **coll_env;
TprintfT (DBG_LT0, "linetrace_ext_exec_prologue_end; variant=%s; cmd_string=%s; follow=%d\n",
variant, cmd_string, following_exec);
/* write message before suspending, or it won't be written */
hrtime_t ts = GETRELTIME ();
__collector_log_write ("<event kind=\"%s\" tstamp=\"%u.%09u\" variant=\"%s\" lineage=\"%s\" follow=\"%d\" msg=\"%s\"/>\n",
SP_JCMD_EXEC_START,
(unsigned) (ts / NANOSEC), (unsigned) (ts % NANOSEC),
variant, new_lineage, following_exec, cmd_string);
if (following_exec)
{
coll_env = __collector_env_allocate (envp, 0);
__collector_env_update (coll_env);
extern char **environ; /* the process' actual environment */
if (environ == envp) /* user selected process environment */
environ = coll_env;
}
else
coll_env = (char**) envp;
__collector_env_printall ("linetrace_ext_exec_prologue_end", coll_env);
if (!CALL_UTIL (strstr)(variant, "posix_spawn"))
{
__collector_linetrace_shutdown_hwcs_6830763_XXXX = 1;
__collector_suspend_experiment ("suspend_for_exec");
__collector_linetrace_shutdown_hwcs_6830763_XXXX = 0;
}
if (CALL_UTIL (strstr)(variant, "posix_spawn"))
{
__collector_ext_dispatcher_thread_timer_suspend ();
__collector_linetrace_shutdown_hwcs_6830763_XXXX = 1;
__collector_ext_hwc_lwp_suspend ();
__collector_linetrace_shutdown_hwcs_6830763_XXXX = 0;
}
return (coll_env);
}
static char**
linetrace_ext_exec_prologue (const char *variant,
const char* path, char *const argv[],
char *const envp[], int *following_exec)
{
char cmd_string[_POSIX_ARG_MAX] = {'\0'};
if (dbg_current_mode != FOLLOW_NONE)
TprintfT (DBG_LT0, "linetrace_ext_exec_prologue() ERROR: dbg_current_mode=%d, changing to FOLLOW_EXEC!\n", dbg_current_mode);
dbg_current_mode = FOLLOW_ON;
*following_exec = check_follow_exec (path);
if (path != NULL)
{
/* escape any newline, " or \ characters in the exec command */
TprintfT (DBG_LT3, "linetrace_ext_exec_prologue(): arg0=%s\n", path);
/* leave space in log message for terminator (and header) */
__collector_strlcpy (cmd_string, path, sizeof (cmd_string));
size_t len;
unsigned argn = 0;
if (argv[0])
{
char *p;
while (((p = argv[++argn]) != 0) &&
(len = __collector_strlen (cmd_string)) < sizeof (cmd_string) - 2)
{
cmd_string[len++] = ' ';
__collector_strlcpy (cmd_string + len, p, sizeof (cmd_string) - len);
}
}
}
TprintfT (DBG_LT0, "linetrace_ext_exec_prologue(%s), lineage=%s, follow=%d, prog=%s, path=%s \n",
variant, new_lineage, *following_exec, cmd_string, path);
return linetrace_ext_exec_prologue_end (variant, cmd_string, envp, *following_exec);
}
static void
linetrace_ext_exec_epilogue (const char *variant, char *const envp[], const int ret, int *following_exec)
{
/* For exec, this routine is only entered if the exec failed */
/* However, posix_spawn() is expected to return */
if (dbg_current_mode == FOLLOW_NONE)
TprintfT (DBG_LT0, "linetrace_ext_exec_epilogue() ERROR: dbg_current_mode=%d!\n", dbg_current_mode);
TprintfT (DBG_LT0, "linetrace_ext_exec_epilogue(%s):%d returned: %d, errno=%d\n",
variant, *following_exec, ret, errno);
if (!CALL_UTIL (strstr)(variant, "posix_spawn"))
{
__collector_linetrace_shutdown_hwcs_6830763_XXXX = 1;
__collector_resume_experiment ();
__collector_linetrace_shutdown_hwcs_6830763_XXXX = 0;
}
if (CALL_UTIL (strstr)(variant, "posix_spawn"))
{
__collector_ext_dispatcher_thread_timer_resume ();
__collector_linetrace_shutdown_hwcs_6830763_XXXX = 1;
__collector_ext_hwc_lwp_resume ();
__collector_linetrace_shutdown_hwcs_6830763_XXXX = 0;
}
hrtime_t ts = GETRELTIME ();
char msg[256];
if (ret)
{
char errmsg[256];
strerror_r (errno, errmsg, sizeof (errmsg));
CALL_UTIL (snprintf)(msg, sizeof (msg), "err %s", errmsg);
}
else
CALL_UTIL (snprintf)(msg, sizeof (msg), "rc=%d", ret);
if (!CALL_UTIL (strstr)(variant, "posix_spawn"))
__collector_log_write ("<event kind=\"%s\" tstamp=\"%u.%09u\" variant=\"%s\" lineage=\"%s\" follow=\"%d\" msg=\"%s\"/>\n",
SP_JCMD_EXEC_ERROR,
(unsigned) (ts / NANOSEC), (unsigned) (ts % NANOSEC),
variant, new_lineage, *following_exec, msg);
if (envp == NULL)
TprintfT (DBG_LT0, "linetrace_ext_exec_epilogue() ERROR: envp NULL after %s!\n", variant);
dbg_current_mode = FOLLOW_NONE;
*following_exec = 0;
}
static void
linetrace_ext_combo_prologue (const char *variant, const char *cmd, int *following_combo)
{
char cmd_string[_POSIX_ARG_MAX] = {'\0'};
char execfile[_POSIX_ARG_MAX] = {'\0'};
if (dbg_current_mode != FOLLOW_NONE)
TprintfT (DBG_LT0, "linetrace_ext_combo_prologue() ERROR: dbg_current_mode=%d! changing to FOLLOW_ON\n",
dbg_current_mode);
dbg_current_mode = FOLLOW_ON;
if (cmd != NULL)
{
/* extract executable name from combo command */
unsigned len = strcspn (cmd, " ");
__collector_strlcpy (execfile, cmd, len + 1);
/* escape any newline, " or \ characters in the combo command */
/* leave space in log message for terminator (and header) */
__collector_strlcpy (cmd_string, cmd, sizeof (cmd_string));
}
*following_combo = check_follow_combo (execfile);
TprintfT (DBG_LT0, "linetrace_ext_combo_prologue(%s) follow=%d, prog=%s\n\n",
variant, *following_combo, cmd_string);
/* Construct the lineage string for the new image */
new_lineage[0] = 0;
__collector_strcat (new_lineage, "XXX");
/* write message before suspending, or it won't be written */
hrtime_t ts = GETRELTIME ();
__collector_log_write ("<event kind=\"%s\" tstamp=\"%u.%09u\" variant=\"%s\" lineage=\"%s\" follow=\"%d\" msg=\"%s\"/>\n",
SP_JCMD_DESC_START,
(unsigned) (ts / NANOSEC), (unsigned) (ts % NANOSEC),
variant, new_lineage, *following_combo, cmd_string);
if (*following_combo)
{
__collector_env_update (NULL);
TprintfT (DBG_LT0, "linetrace_ext_combo_prologue(): Following %s(\"%s\")\n", variant, execfile);
}
__collector_ext_dispatcher_thread_timer_suspend ();
__collector_linetrace_shutdown_hwcs_6830763_XXXX = 1;
__collector_ext_hwc_lwp_suspend ();
__collector_linetrace_shutdown_hwcs_6830763_XXXX = 0;
}
static void
linetrace_ext_combo_epilogue (const char *variant, const int ret, int *following_combo)
{
if (dbg_current_mode == FOLLOW_NONE)
TprintfT (DBG_LT0, "linetrace_ext_combo_epilogue() ERROR: dbg_current_mode=FOLLOW_NONE\n");
TprintfT (DBG_LT0, "linetrace_ext_combo_epilogue(%s):%d() returned %d\n",
variant, *following_combo, ret);
__collector_ext_dispatcher_thread_timer_resume ();
__collector_linetrace_shutdown_hwcs_6830763_XXXX = 1;
__collector_ext_hwc_lwp_resume ();
__collector_linetrace_shutdown_hwcs_6830763_XXXX = 0;
hrtime_t ts = GETRELTIME ();
__collector_log_write ("<event kind=\"%s\" tstamp=\"%u.%09u\" variant=\"%s\" follow=\"%d\" msg=\"rc=%d\"/>\n",
SP_JCMD_DESC_STARTED,
(unsigned) (ts / NANOSEC), (unsigned) (ts % NANOSEC),
variant, *following_combo, ret);
dbg_current_mode = FOLLOW_NONE;
*following_combo = 0;
}
/*------------------------------------------------------------- fork */
pid_t fork () __attribute__ ((weak, alias ("__collector_fork")));
pid_t _fork () __attribute__ ((weak, alias ("__collector_fork")));
pid_t
__collector_fork (void)
{
pid_t ret;
if (NULL_PTR (fork))
{
TprintfT (DBG_LT0, "__collector_fork() calling init_lineage_intf()\n");
init_lineage_intf ();
}
__collector_env_print ("__collector_fork start");
int * guard = NULL;
int combo_flag = (line_mode == LM_TRACK_LINEAGE) ? ((CHCK_REENTRANCE (guard)) ? 1 : 0) : 0;
TprintfT (DBG_LT0, "__collector_fork() interposition: line_mode=%d combo=%d\n",
line_mode, combo_flag);
if ((line_mode != LM_TRACK_LINEAGE) || combo_flag)
{
TprintfT (DBG_LT0, "__collector_fork() not following, returning CALL_REAL(fork)()\n");
return CALL_REAL (fork)();
}
int following_fork = 0;
linetrace_ext_fork_prologue ("fork", new_lineage, &following_fork);
/* since libpthread/fork ends up calling fork1, it's a combo */
PUSH_REENTRANCE (guard);
ret = CALL_REAL (fork)();
POP_REENTRANCE (guard);
linetrace_ext_fork_epilogue ("fork", ret, new_lineage, &following_fork);
return ret;
}
/*------------------------------------------------------------- vfork */
/* vfork interposition in the usual sense is not possible, since vfork(2)
relies on specifics of the stack frames in the parent and child which
only work when the child's exec (or _exit) are in the same stack frame
as the vfork: this isn't the case when there's interposition on exec.
As a workaround, the interposing vfork calls fork1 instead of the real
vfork. Note that fork1 is somewhat less efficient than vfork, and requires
additional memory, which may result in a change of application behaviour
when libcollector is loaded (even when collection is not active),
affecting not only direct use of vfork by the subject application,
but also indirect use through system, popen, and the other combos.
*/
pid_t vfork () __attribute__ ((weak, alias ("__collector_vfork")));
pid_t _vfork () __attribute__ ((weak, alias ("__collector_vfork")));
pid_t
__collector_vfork (void)
{
if (NULL_PTR (vfork))
init_lineage_intf ();
int * guard = NULL;
int combo_flag = (line_mode == LM_TRACK_LINEAGE) ? ((CHCK_REENTRANCE (guard)) ? 1 : 0) : 0;
TprintfT (DBG_LT0, "__collector_vfork() interposing: line_mode=%d combo=%d\n",
line_mode, combo_flag);
if ((line_mode != LM_TRACK_LINEAGE) || combo_flag)
return CALL_REAL (fork)();
/* this warning is also appropriate for combos which use vfork,
however, let's assume this is achieved elsewhere */
(void) __collector_log_write ("<event kind=\"%s\" id=\"%d\">%s</event>\n", SP_JCMD_CWARN,
COL_WARN_VFORK, "fork");
char new_lineage[LT_MAXNAMELEN];
new_lineage[0] = 0;
int following_fork = 0;
linetrace_ext_fork_prologue ("vfork", new_lineage, &following_fork);
pid_t ret = CALL_REAL (fork)();
linetrace_ext_fork_epilogue ("vfork", ret, new_lineage, &following_fork);
return ret;
}
/*------------------------------------------------------------- execve */
int execve () __attribute__ ((weak, alias ("__collector_execve")));
int
__collector_execve (const char* path, char *const argv[], char *const envp[])
{
static char **coll_env = NULL; /* environment for collection */
if (NULL_PTR (execve))
init_lineage_intf ();
int * guard = NULL;
int combo_flag = (line_mode == LM_TRACK_LINEAGE) ? ((CHCK_REENTRANCE (guard)) ? 1 : 0) : 0;
TprintfT (DBG_LT0,
"__collector_execve(path=%s, argv[0]=%s, env[0]=%s) interposing: line_mode=%d combo=%d\n",
path ? path : "NULL",
argv ? (argv[0] ? argv[0] : "NULL") : "NULL",
envp ? (envp[0] ? envp[0] : "NULL") : "NULL",
line_mode, combo_flag);
if (line_mode == LM_CLOSED) /* ensure collection environment is sanitised */
__collector_env_unset ((char**) envp);
if (line_mode != LM_TRACK_LINEAGE || combo_flag)
return CALL_REAL (execve)(path, argv, envp);
int following_exec = 0;
coll_env = linetrace_ext_exec_prologue ("execve", path, argv, envp, &following_exec);
TprintfT (DBG_LT2, "__collector_execve(): coll_env=0x%p\n", coll_env);
__collector_env_printall ("__collector_execve", coll_env);
int ret = CALL_REAL (execve)(path, argv, coll_env);
linetrace_ext_exec_epilogue ("execve", envp, ret, &following_exec);
return ret;
}
int execvp () __attribute__ ((weak, alias ("__collector_execvp")));
int
__collector_execvp (const char* file, char *const argv[])
{
extern char **environ; /* the process' actual environment */
char ** envp = environ;
if (NULL_PTR (execvp))
init_lineage_intf ();
int * guard = NULL;
int combo_flag = (line_mode == LM_TRACK_LINEAGE) ? ((CHCK_REENTRANCE (guard)) ? 1 : 0) : 0;
TprintfT (DBG_LT0,
"__collector_execvp(file=%s, argv[0]=%s) interposing: line_mode=%d combo=%d\n",
file ? file : "NULL", argv ? (argv[0] ? argv[0] : "NULL") : "NULL",
line_mode, combo_flag);
if (line_mode == LM_CLOSED) /* ensure collection environment is sanitised */
__collector_env_unset ((char**) envp);
if ((line_mode != LM_TRACK_LINEAGE) || combo_flag)
return CALL_REAL (execvp)(file, argv);
int following_exec = 0;
#ifdef DEBUG
char **coll_env = /* environment for collection */
#endif /* DEBUG */
linetrace_ext_exec_prologue ("execvp", file, argv, envp, &following_exec);
TprintfT (DBG_LT0, "__collector_execvp(): coll_env=0x%p\n", coll_env);
int ret = CALL_REAL (execvp)(file, argv);
linetrace_ext_exec_epilogue ("execvp", envp, ret, &following_exec);
return ret;
}
int execv () __attribute__ ((weak, alias ("__collector_execv")));
int
__collector_execv (const char* path, char *const argv[])
{
int ret;
extern char **environ; /* the process' actual environment */
char ** envp = environ;
TprintfT (DBG_LT0, "__collector_execv(path=%s, argv[0]=%s) interposing: line_mode=%d combo=%d\n",
path ? path : "NULL", argv ? (argv[0] ? argv[0] : "NULL") : "NULL",
line_mode, get_combo_flag ());
ret = __collector_execve (path, argv, envp);
return ret;
}
int execle (const char* path, const char *arg0, ...) __attribute__ ((weak, alias ("__collector_execle")));
int
__collector_execle (const char* path, const char *arg0, ...)
{
TprintfT (DBG_LT0,
"__collector_execle(path=%s, arg0=%s) interposing: line_mode=%d combo=%d\n",
path ? path : "NULL", arg0 ? arg0 : "NULL",
line_mode, get_combo_flag ());
char **argp;
va_list args;
char **argvec;
register char **environmentp;
int nargs = 0;
char *nextarg;
va_start (args, arg0);
while (va_arg (args, char *) != (char *) 0)
nargs++;
/*
* save the environment pointer, which is at the end of the
* variable argument list
*/
environmentp = va_arg (args, char **);
va_end (args);
/*
* load the arguments in the variable argument list
* into the argument vector, and add the terminating null pointer
*/
va_start (args, arg0);
/* workaround for bugid 1242839 */
argvec = (char **) alloca ((size_t) ((nargs + 2) * sizeof (char *)));
argp = argvec;
*argp++ = (char *) arg0;
while ((nextarg = va_arg (args, char *)) != (char *) 0)
*argp++ = nextarg;
va_end (args);
*argp = (char *) 0;
return __collector_execve (path, argvec, environmentp);
}
int execlp (const char* file, const char *arg0, ...) __attribute__ ((weak, alias ("__collector_execlp")));
int
__collector_execlp (const char* file, const char *arg0, ...)
{
TprintfT (DBG_LT0,
"__collector_execlp(file=%s, arg0=%s) interposing: line_mode=%d combo=%d\n",
file ? file : "NULL", arg0 ? arg0 : "NULL",
line_mode, get_combo_flag ());
char **argp;
va_list args;
char **argvec;
int nargs = 0;
char *nextarg;
va_start (args, arg0);
while (va_arg (args, char *) != (char *) 0)
nargs++;
va_end (args);
/*
* load the arguments in the variable argument list
* into the argument vector and add the terminating null pointer
*/
va_start (args, arg0);
/* workaround for bugid 1242839 */
argvec = (char **) alloca ((size_t) ((nargs + 2) * sizeof (char *)));
argp = argvec;
*argp++ = (char *) arg0;
while ((nextarg = va_arg (args, char *)) != (char *) 0)
*argp++ = nextarg;
va_end (args);
*argp = (char *) 0;
return __collector_execvp (file, argvec);
}
int execl (const char* path, const char *arg0, ...) __attribute__ ((weak, alias ("__collector_execl")));
int
__collector_execl (const char* path, const char *arg0, ...)
{
TprintfT (DBG_LT0,
"__collector_execl(path=%s, arg0=%s) interposing: line_mode=%d combo=%d\n",
path ? path : "NULL", arg0 ? arg0 : "NULL",
line_mode, get_combo_flag ());
char **argp;
va_list args;
char **argvec;
extern char **environ;
int nargs = 0;
char *nextarg;
va_start (args, arg0);
while (va_arg (args, char *) != (char *) 0)
nargs++;
va_end (args);
/*
* load the arguments in the variable argument list
* into the argument vector and add the terminating null pointer
*/
va_start (args, arg0);
/* workaround for bugid 1242839 */
argvec = (char **) alloca ((size_t) ((nargs + 2) * sizeof (char *)));
argp = argvec;
*argp++ = (char *) arg0;
while ((nextarg = va_arg (args, char *)) != (char *) 0)
*argp++ = nextarg;
va_end (args);
*argp = (char *) 0;
return __collector_execve (path, argvec, environ);
}
#include <spawn.h>
/*-------------------------------------------------------- posix_spawn */
#if ARCH(Intel)
// map interposed symbol versions
static int
__collector_posix_spawn_symver (int(real_posix_spawn) (),
pid_t *pidp, const char *path,
const posix_spawn_file_actions_t *file_actions,
const posix_spawnattr_t *attrp,
char *const argv[], char *const envp[]);
SYMVER_ATTRIBUTE (__collector_posix_spawn_2_15, posix_spawn@@GLIBC_2.15)
int
__collector_posix_spawn_2_15 (pid_t *pidp, const char *path,
const posix_spawn_file_actions_t *file_actions,
const posix_spawnattr_t *attrp,
char *const argv[], char *const envp[])
{
TprintfT (DBG_LTT, "linetrace: GLIBC: __collector_posix_spawn_2_15@%p(path=%s, argv[0]=%s, env[0]=%s)\n",
CALL_REAL (posix_spawn_2_15), path ? path : "NULL", argv ? (argv[0] ? argv[0] : "NULL") : "NULL", envp ? (envp[0] ? envp[0] : "NULL") : "NULL");
if (NULL_PTR (posix_spawn))
init_lineage_intf ();
return __collector_posix_spawn_symver (CALL_REAL (posix_spawn_2_15), pidp,
path, file_actions, attrp, argv, envp);
}
#if WSIZE(32)
SYMVER_ATTRIBUTE (__collector_posix_spawn_2_2, posix_spawn@GLIBC_2.2)
int
__collector_posix_spawn_2_2 (pid_t *pidp, const char *path,
const posix_spawn_file_actions_t *file_actions,
const posix_spawnattr_t *attrp,
char *const argv[], char *const envp[])
{
TprintfT (DBG_LTT, "linetrace: GLIBC: __collector_posix_spawn_2_2@%p(path=%s, argv[0]=%s, env[0]=%s)\n",
CALL_REAL (posix_spawn_2_2), path ? path : "NULL", argv ? (argv[0] ? argv[0] : "NULL") : "NULL", envp ? (envp[0] ? envp[0] : "NULL") : "NULL");
if (NULL_PTR (posix_spawn))
init_lineage_intf ();
return __collector_posix_spawn_symver (CALL_REAL (posix_spawn_2_2), pidp,
path, file_actions, attrp, argv, envp);
}
#else /* ^WSIZE(32) */
SYMVER_ATTRIBUTE (__collector_posix_spawn_2_2_5, posix_spawn@GLIBC_2.2.5)
int
__collector_posix_spawn_2_2_5 (pid_t *pidp, const char *path,
const posix_spawn_file_actions_t *file_actions,
const posix_spawnattr_t *attrp,
char *const argv[], char *const envp[])
{
TprintfT (DBG_LTT, "linetrace: GLIBC: __collector_posix_spawn_2_2_5@%p(path=%s, argv[0]=%s, env[0]=%s)\n",
CALL_REAL (posix_spawn_2_2_5), path ? path : "NULL", argv ? (argv[0] ? argv[0] : "NULL") : "NULL", envp ? (envp[0] ? envp[0] : "NULL") : "NULL");
if (NULL_PTR (posix_spawn))
init_lineage_intf ();
return __collector_posix_spawn_symver (CALL_REAL (posix_spawn_2_2_5), pidp,
path, file_actions, attrp, argv, envp);
}
#endif /* ^WSIZE(32) */
static int
__collector_posix_spawn_symver (int(real_posix_spawn) (),
#else /* ^ARCH(Intel) */
int
__collector_posix_spawn (
#endif /* ARCH() */
pid_t *pidp, const char *path,
const posix_spawn_file_actions_t *file_actions,
const posix_spawnattr_t *attrp,
char *const argv[], char *const envp[])
{
int ret;
static char **coll_env = NULL; /* environment for collection */
if (NULL_PTR (posix_spawn))
init_lineage_intf ();
if (NULL_PTR (posix_spawn))
{
TprintfT (DBG_LT0, "__collector_posix_spawn(path=%s) interposing: ERROR, posix_spawn() not found by dlsym\n",
path ? path : "NULL");
return -1; /* probably should set errno */
}
int * guard = NULL;
int combo_flag = (line_mode == LM_TRACK_LINEAGE) ? ((CHCK_REENTRANCE (guard)) ? 1 : 0) : 0;
TprintfT (DBG_LT0, "__collector_posix_spawn(path=%s, argv[0]=%s, env[0]=%s) interposing: line_mode=%d combo=%d\n",
path ? path : "NULL", argv ? (argv[0] ? argv[0] : "NULL") : "NULL", envp ? (envp[0] ? envp[0] : "NULL") : "NULL", line_mode, combo_flag);
if (line_mode == LM_CLOSED) /* ensure collection environment is sanitised */
__collector_env_unset ((char**) envp);
if ((line_mode != LM_TRACK_LINEAGE) || combo_flag)
{
#if ARCH(Intel)
return (real_posix_spawn) (pidp, path, file_actions, attrp, argv, envp);
#else
return CALL_REAL (posix_spawn)(pidp, path, file_actions, attrp, argv, envp);
#endif
}
int following_exec = 0;
coll_env = linetrace_ext_exec_prologue ("posix_spawn", path, argv, envp, &following_exec);
TprintfT (DBG_LT0, "__collector_posix_spawn(): coll_env=0x%p\n", coll_env);
__collector_env_printall ("__collector_posix_spawn", coll_env);
PUSH_REENTRANCE (guard);
#if ARCH(Intel)
ret = (real_posix_spawn) (pidp, path, file_actions, attrp, argv, coll_env);
#else
ret = CALL_REAL (posix_spawn)(pidp, path, file_actions, attrp, argv, coll_env);
#endif
POP_REENTRANCE (guard);
linetrace_ext_exec_epilogue ("posix_spawn", envp, ret, &following_exec);
return ret;
}
/*-------------------------------------------------------- posix_spawnp */
#if ARCH(Intel)
// map interposed symbol versions
static int
__collector_posix_spawnp_symver (int(real_posix_spawnp) (), pid_t *pidp,
const char *path,
const posix_spawn_file_actions_t *file_actions,
const posix_spawnattr_t *attrp,
char *const argv[], char *const envp[]);
SYMVER_ATTRIBUTE (__collector_posix_spawnp_2_15, posix_spawnp@@GLIBC_2.15)
int // Common interposition
__collector_posix_spawnp_2_15 (pid_t *pidp, const char *path,
const posix_spawn_file_actions_t *file_actions,
const posix_spawnattr_t *attrp,
char *const argv[], char *const envp[])
{
TprintfT (DBG_LTT, "linetrace: GLIBC: __collector_posix_spawnp_2_15@%p(path=%s, argv[0]=%s, env[0]=%s)\n",
CALL_REAL (posix_spawnp_2_15), path ? path : "NULL", argv ? (argv[0] ? argv[0] : "NULL") : "NULL", envp ? (envp[0] ? envp[0] : "NULL") : "NULL");
if (NULL_PTR (posix_spawnp))
init_lineage_intf ();
return __collector_posix_spawnp_symver (CALL_REAL (posix_spawnp_2_15), pidp,
path, file_actions, attrp, argv, envp);
}
#if WSIZE(32)
SYMVER_ATTRIBUTE (__collector_posix_spawnp_2_2, posix_spawnp@GLIBC_2.2)
int
__collector_posix_spawnp_2_2 (pid_t *pidp, const char *path,
const posix_spawn_file_actions_t *file_actions,
const posix_spawnattr_t *attrp,
char *const argv[], char *const envp[])
{
TprintfT (DBG_LTT, "linetrace: GLIBC: __collector_posix_spawnp_2_2@%p(path=%s, argv[0]=%s, env[0]=%s)\n",
CALL_REAL (posix_spawnp_2_2), path ? path : "NULL", argv ? (argv[0] ? argv[0] : "NULL") : "NULL", envp ? (envp[0] ? envp[0] : "NULL") : "NULL");
if (NULL_PTR (posix_spawnp))
init_lineage_intf ();
return __collector_posix_spawnp_symver (CALL_REAL (posix_spawnp_2_2), pidp,
path, file_actions, attrp, argv, envp);
}
#else /* ^WSIZE(32) */
SYMVER_ATTRIBUTE (__collector_posix_spawnp_2_2_5, posix_spawnp@GLIBC_2.2.5)
int
__collector_posix_spawnp_2_2_5 (pid_t *pidp, const char *path,
const posix_spawn_file_actions_t *file_actions,
const posix_spawnattr_t *attrp,
char *const argv[], char *const envp[])
{
TprintfT (DBG_LTT, "linetrace: GLIBC: __collector_posix_spawnp_2_2_5@%p(path=%s, argv[0]=%s, env[0]=%s)\n",
CALL_REAL (posix_spawnp_2_2_5), path ? path : "NULL", argv ? (argv[0] ? argv[0] : "NULL") : "NULL", envp ? (envp[0] ? envp[0] : "NULL") : "NULL");
if (NULL_PTR (posix_spawnp))
init_lineage_intf ();
return __collector_posix_spawnp_symver (CALL_REAL (posix_spawnp_2_2_5), pidp,
path, file_actions, attrp, argv, envp);
}
#endif /* ^WSIZE(32) */
static int
__collector_posix_spawnp_symver (int(real_posix_spawnp) (),
#else /* ^ARCH(Intel) */
int
__collector_posix_spawnp (
#endif /* ARCH() */
pid_t *pidp, const char *path,
const posix_spawn_file_actions_t *file_actions,
const posix_spawnattr_t *attrp,
char *const argv[], char *const envp[]){
int ret;
static char **coll_env = NULL; /* environment for collection */
if (NULL_PTR (posix_spawnp))
init_lineage_intf ();
if (NULL_PTR (posix_spawnp))
{
TprintfT (DBG_LT0, "__collector_posix_spawnp(path=%s) interposing: ERROR, posix_spawnp() not found by dlsym\n",
path ? path : "NULL");
return -1; /* probably should set errno */
}
int * guard = NULL;
int combo_flag = (line_mode == LM_TRACK_LINEAGE) ? ((CHCK_REENTRANCE (guard)) ? 1 : 0) : 0;
TprintfT (DBG_LT0, "__collector_posix_spawnp(path=%s, argv[0]=%s, env[0]=%s) interposing: line_mode=%d combo=%d\n",
path ? path : "NULL", argv ? (argv[0] ? argv[0] : "NULL") : "NULL",
envp ? (envp[0] ? envp[0] : "NULL") : "NULL", line_mode, combo_flag);
if (line_mode == LM_CLOSED) /* ensure collection environment is sanitised */
__collector_env_unset ((char**) envp);
if (line_mode != LM_TRACK_LINEAGE || combo_flag)
{
#if ARCH(Intel)
return (real_posix_spawnp) (pidp, path, file_actions, attrp, argv, envp);
#else
return CALL_REAL (posix_spawnp)(pidp, path, file_actions, attrp, argv, envp);
#endif
}
int following_exec = 0;
coll_env = linetrace_ext_exec_prologue ("posix_spawnp", path, argv, envp, &following_exec);
TprintfT (DBG_LT0, "__collector_posix_spawnp(): coll_env=0x%p\n", coll_env);
__collector_env_printall ("__collector_posix_spawnp", coll_env);
PUSH_REENTRANCE (guard);
#if ARCH(Intel)
ret = (real_posix_spawnp) (pidp, path, file_actions, attrp, argv, coll_env);
#else
ret = CALL_REAL (posix_spawnp)(pidp, path, file_actions, attrp, argv, coll_env);
#endif
POP_REENTRANCE (guard);
linetrace_ext_exec_epilogue ("posix_spawnp", envp, ret, &following_exec);
return ret;
}
/*------------------------------------------------------------- system */
int system () __attribute__ ((weak, alias ("__collector_system")));
int
__collector_system (const char *cmd)
{
if (NULL_PTR (system))
init_lineage_intf ();
TprintfT (DBG_LT0,
"__collector_system(cmd=%s) interposing: line_mode=%d combo=%d\n",
cmd ? cmd : "NULL", line_mode, get_combo_flag ());
int *guard = NULL;
if (line_mode == LM_TRACK_LINEAGE)
INIT_REENTRANCE (guard);
if (guard == NULL)
return CALL_REAL (system)(cmd);
int following_combo = 0;
linetrace_ext_combo_prologue ("system", cmd, &following_combo);
PUSH_REENTRANCE (guard);
int ret = CALL_REAL (system)(cmd);
POP_REENTRANCE (guard);
linetrace_ext_combo_epilogue ("system", ret, &following_combo);
return ret;
}
/*------------------------------------------------------------- popen */
// map interposed symbol versions
#if ARCH(Intel) && WSIZE(32)
static FILE *
__collector_popen_symver (FILE*(real_popen) (), const char *cmd, const char *mode);
SYMVER_ATTRIBUTE (__collector_popen_2_1, popen@@GLIBC_2.1)
FILE *
__collector_popen_2_1 (const char *cmd, const char *mode)
{
if (NULL_PTR (popen))
init_lineage_intf ();
TprintfT (DBG_LTT, "linetrace: GLIBC: __collector_popen_2_1@%p\n", CALL_REAL (popen_2_1));
return __collector_popen_symver (CALL_REALF (popen_2_1), cmd, mode);
}
SYMVER_ATTRIBUTE (__collector_popen_2_0, popen@GLIBC_2.0)
FILE *
__collector_popen_2_0 (const char *cmd, const char *mode)
{
if (NULL_PTR (popen))
init_lineage_intf ();
TprintfT (DBG_LTT, "linetrace: GLIBC: __collector_popen_2_0@%p\n", CALL_REAL (popen_2_0));
return __collector_popen_symver (CALL_REALF (popen_2_0), cmd, mode);
}
SYMVER_ATTRIBUTE (__collector__popen_2_1, _popen@@GLIBC_2.1)
FILE *
__collector__popen_2_1 (const char *cmd, const char *mode)
{
if (NULL_PTR (popen))
init_lineage_intf ();
TprintfT (DBG_LTT, "linetrace: GLIBC: __collector__popen_2_1@%p\n", CALL_REAL (popen_2_1));
return __collector_popen_symver (CALL_REALF (popen_2_1), cmd, mode);
}
SYMVER_ATTRIBUTE (__collector__popen_2_0, _popen@GLIBC_2.0)
FILE *
__collector__popen_2_0 (const char *cmd, const char *mode)
{
if (NULL_PTR (popen))
init_lineage_intf ();
return __collector_popen_symver (CALL_REALF (popen_2_0), cmd, mode);
}
#else // WSIZE(64)
FILE * popen () __attribute__ ((weak, alias ("__collector_popen")));
#endif
#if ARCH(Intel) && WSIZE(32)
static FILE *
__collector_popen_symver (FILE*(real_popen) (), const char *cmd, const char *mode)
#else
FILE *
__collector_popen (const char *cmd, const char *mode)
#endif
{
FILE *ret;
if (NULL_PTR (popen))
init_lineage_intf ();
TprintfT (DBG_LT0,
"__collector_popen(cmd=%s) interposing: line_mode=%d combo=%d\n",
cmd ? cmd : "NULL", line_mode, get_combo_flag ());
int *guard = NULL;
if (line_mode == LM_TRACK_LINEAGE)
INIT_REENTRANCE (guard);
if (guard == NULL)
{
#if ARCH(Intel) && WSIZE(32)
return (real_popen) (cmd, mode);
#else
return CALL_REALF (popen)(cmd, mode);
#endif
}
int following_combo = 0;
linetrace_ext_combo_prologue ("popen", cmd, &following_combo);
PUSH_REENTRANCE (guard);
#if ARCH(Intel) && WSIZE(32)
ret = (real_popen) (cmd, mode);
#else
ret = CALL_REALF (popen)(cmd, mode);
#endif
POP_REENTRANCE (guard);
linetrace_ext_combo_epilogue ("popen", (ret == NULL) ? (-1) : 0, &following_combo);
return ret;
}
/*------------------------------------------------------------- grantpt */
int grantpt () __attribute__ ((weak, alias ("__collector_grantpt")));
int
__collector_grantpt (const int fildes)
{
if (NULL_PTR (grantpt))
init_lineage_intf ();
TprintfT (DBG_LT0,
"__collector_grantpt(%d) interposing: line_mode=%d combo=%d\n",
fildes, line_mode, get_combo_flag ());
int *guard = NULL;
if (line_mode == LM_TRACK_LINEAGE)
INIT_REENTRANCE (guard);
if (guard == NULL)
return CALL_REAL (grantpt)(fildes);
int following_combo = 0;
linetrace_ext_combo_prologue ("grantpt", "/usr/lib/pt_chmod", &following_combo);
PUSH_REENTRANCE (guard);
int ret = CALL_REAL (grantpt)(fildes);
POP_REENTRANCE (guard);
linetrace_ext_combo_epilogue ("grantpt", ret, &following_combo);
return ret;
}
/*------------------------------------------------------------- ptsname */
char *ptsname () __attribute__ ((weak, alias ("__collector_ptsname")));
char *
__collector_ptsname (const int fildes)
{
if (NULL_PTR (ptsname))
init_lineage_intf ();
TprintfT (DBG_LT0,
"__collector_ptsname(%d) interposing: line_mode=%d combo=%d\n",
fildes, line_mode, get_combo_flag ());
int *guard = NULL;
if (line_mode == LM_TRACK_LINEAGE)
INIT_REENTRANCE (guard);
if (guard == NULL)
return CALL_REALC (ptsname)(fildes);
int following_combo = 0;
linetrace_ext_combo_prologue ("ptsname", "/usr/lib/pt_chmod", &following_combo);
PUSH_REENTRANCE (guard);
char *ret = CALL_REALC (ptsname)(fildes);
POP_REENTRANCE (guard);
linetrace_ext_combo_epilogue ("ptsname", (ret == NULL) ? (-1) : 1, &following_combo);
return ret;
}
/*------------------------------------------------------------- clone */
/* clone can be fork-like or pthread_create-like, depending on whether
* the flag CLONE_VM is set. If CLONE_VM is not set, then we interpose
* clone in the way similar to interposing fork; if CLONE_VM is set,
* then we interpose clone in the way similar to interposing pthread_create.
* One special case is not handled: when CLONE_VM is set but CLONE_THREAD
* is not, if the parent process exits earlier than the child process,
* experiment will close, losing data from child process.
*/
typedef struct __collector_clone_arg
{
int (*fn)(void *);
void * arg;
char * new_lineage;
int following_fork;
} __collector_clone_arg_t;
static int
__collector_clone_fn (void *fn_arg)
{
int (*fn)(void *) = ((__collector_clone_arg_t*) fn_arg)->fn;
void * arg = ((__collector_clone_arg_t*) fn_arg)->arg;
char * new_lineage = ((__collector_clone_arg_t*) fn_arg)->new_lineage;
int following_fork = ((__collector_clone_arg_t*) fn_arg)->following_fork;
__collector_freeCSize (__collector_heap, fn_arg, sizeof (__collector_clone_arg_t));
linetrace_ext_fork_epilogue ("clone", 0, new_lineage, &following_fork);
return fn (arg);
}
int clone (int (*fn)(void *), void *, int, void *, ...) __attribute__ ((weak, alias ("__collector_clone")));
int
__collector_clone (int (*fn)(void *), void *child_stack, int flags, void *arg,
... /* pid_t *ptid, struct user_desc *tls, pid_t *" ctid" */)
{
int ret;
va_list va;
if (flags & CLONE_VM)
{
va_start (va, arg);
ret = __collector_ext_clone_pthread (fn, child_stack, flags, arg, va);
va_end (va);
}
else
{
if (NULL_PTR (clone))
init_lineage_intf ();
int *guard = NULL;
int combo_flag = (line_mode == LM_TRACK_LINEAGE) ? ((CHCK_REENTRANCE (guard)) ? 1 : 0) : 0;
TprintfT (DBG_LT0, "__collector_clone() interposition: line_mode=%d combo=%d\n",
line_mode, combo_flag);
char new_lineage[LT_MAXNAMELEN];
int following_fork = 0;
__collector_clone_arg_t *funcinfo = __collector_allocCSize (__collector_heap, sizeof (__collector_clone_arg_t), 1);
(*funcinfo).fn = fn;
(*funcinfo).arg = arg;
(*funcinfo).new_lineage = new_lineage;
(*funcinfo).following_fork = 0;
pid_t * ptid = NULL;
struct user_desc * tls = NULL;
pid_t * ctid = NULL;
int num_args = 0;
va_start (va, arg);
if (flags & (CLONE_CHILD_SETTID | CLONE_CHILD_CLEARTID))
{
ptid = va_arg (va, pid_t *);
tls = va_arg (va, struct user_desc*);
ctid = va_arg (va, pid_t *);
num_args = 3;
}
else if (flags & CLONE_SETTLS)
{
ptid = va_arg (va, pid_t *);
tls = va_arg (va, struct user_desc*);
num_args = 2;
}
else if (flags & CLONE_PARENT_SETTID)
{
ptid = va_arg (va, pid_t *);
num_args = 1;
}
if ((line_mode != LM_TRACK_LINEAGE) || combo_flag || funcinfo == NULL)
{
switch (num_args)
{
case 3:
ret = CALL_REAL (clone)(fn, child_stack, flags, arg, ptid, tls, ctid);
break;
case 2:
ret = CALL_REAL (clone)(fn, child_stack, flags, arg, ptid, tls);
break;
case 1:
ret = CALL_REAL (clone)(fn, child_stack, flags, arg, ptid);
break;
default:
ret = CALL_REAL (clone)(fn, child_stack, flags, arg);
break;
}
va_end (va);
return ret;
}
linetrace_ext_fork_prologue ("clone", new_lineage, &following_fork);
(*funcinfo).following_fork = following_fork;
switch (num_args)
{
case 3:
ret = CALL_REAL (clone)(__collector_clone_fn, child_stack, flags, funcinfo, ptid, tls, ctid);
break;
case 2:
ret = CALL_REAL (clone)(__collector_clone_fn, child_stack, flags, funcinfo, ptid, tls);
break;
case 1:
ret = CALL_REAL (clone)(__collector_clone_fn, child_stack, flags, funcinfo, ptid);
break;
default:
ret = CALL_REAL (clone)(__collector_clone_fn, child_stack, flags, funcinfo);
break;
}
va_end (va);
if (ret < 0)
__collector_freeCSize (__collector_heap, funcinfo, sizeof (__collector_clone_arg_t));
TprintfT (DBG_LT0, "__collector_clone() interposing: pid=%d\n", ret);
linetrace_ext_fork_epilogue ("clone", ret, new_lineage, &following_fork);
}
return ret;
}
/*-------------------------------------------------------------------- setuid */
int setuid () __attribute__ ((weak, alias ("__collector_setuid")));
int _setuid () __attribute__ ((weak, alias ("__collector_setuid")));
int
__collector_setuid (uid_t ruid)
{
if (NULL_PTR (setuid))
init_lineage_intf ();
TprintfT (DBG_LT0, "__collector_setuid(0x%x) interposing\n", ruid);
check_reuid_change (ruid, -1);
int ret = CALL_REAL (setuid)(ruid);
TprintfT (DBG_LT0, "__collector_setuid(0x%x) returning %d\n", ruid, ret);
return ret;
}
/*------------------------------------------------------------------- seteuid */
int seteuid () __attribute__ ((weak, alias ("__collector_seteuid")));
int _seteuid () __attribute__ ((weak, alias ("__collector_seteuid")));
int
__collector_seteuid (uid_t euid)
{
if (NULL_PTR (seteuid))
init_lineage_intf ();
TprintfT (DBG_LT0, "__collector_seteuid(0x%x) interposing\n", euid);
check_reuid_change (-1, euid);
int ret = CALL_REAL (seteuid)(euid);
TprintfT (DBG_LT0, "__collector_seteuid(0x%x) returning %d\n", euid, ret);
return ret;
}
/*------------------------------------------------------------------ setreuid */
int setreuid () __attribute__ ((weak, alias ("__collector_setreuid")));
int _setreuid () __attribute__ ((weak, alias ("__collector_setreuid")));
int
__collector_setreuid (uid_t ruid, uid_t euid)
{
if (NULL_PTR (setreuid))
init_lineage_intf ();
TprintfT (DBG_LT0, "__collector_setreuid(0x%x,0x%x) interposing\n", ruid, euid);
check_reuid_change (ruid, euid);
int ret = CALL_REAL (setreuid)(ruid, euid);
TprintfT (DBG_LT0, "__collector_setreuid(0x%x,0x%x) returning %d\n", ruid, euid, ret);
return ret;
}
/*-------------------------------------------------------------------- setgid */
int setgid () __attribute__ ((weak, alias ("__collector_setgid")));
int _setgid () __attribute__ ((weak, alias ("__collector_setgid")));
int
__collector_setgid (gid_t rgid)
{
if (NULL_PTR (setgid))
init_lineage_intf ();
TprintfT (DBG_LT0, "__collector_setgid(0x%x) interposing\n", rgid);
check_regid_change (rgid, -1);
int ret = CALL_REAL (setgid)(rgid);
TprintfT (DBG_LT0, "__collector_setgid(0x%x) returning %d\n", rgid, ret);
return ret;
}
/*------------------------------------------------------------------- setegid */
int setegid () __attribute__ ((weak, alias ("__collector_setegid")));
int _setegid () __attribute__ ((weak, alias ("__collector_setegid")));
int
__collector_setegid (gid_t egid)
{
if (NULL_PTR (setegid))
init_lineage_intf ();
TprintfT (DBG_LT0, "__collector_setegid(0x%x) interposing\n", egid);
check_regid_change (-1, egid);
int ret = CALL_REAL (setegid)(egid);
TprintfT (DBG_LT0, "__collector_setegid(0x%x) returning %d\n", egid, ret);
return ret;
}
/*------------------------------------------------------------------ setregid */
int setregid () __attribute__ ((weak, alias ("__collector_setregid")));
int _setregid () __attribute__ ((weak, alias ("__collector_setregid")));
int
__collector_setregid (gid_t rgid, gid_t egid)
{
if (NULL_PTR (setregid))
init_lineage_intf ();
TprintfT (DBG_LT0, "__collector_setregid(0x%x,0x%x) interposing\n", rgid, egid);
check_regid_change (rgid, egid);
int ret = CALL_REAL (setregid)(rgid, egid);
TprintfT (DBG_LT0, "__collector_setregid(0x%x,0x%x) returning %d\n", rgid, egid, ret);
return ret;
}
/*------------------------------------------------------- selective following */
static int
linetrace_follow_experiment (const char *follow_spec, const char *lineage_str, const char *progname)
{
regex_t regex_desc;
if (!follow_spec)
{
TprintfT (DBG_LT0, "linetrace_follow_experiment(): MATCHES NULL follow_spec\n");
return 1;
}
int ercode = regcomp (&regex_desc, follow_spec, REG_EXTENDED | REG_NOSUB | REG_NEWLINE);
if (ercode)
{
// syntax error in parsing string
#ifdef DEBUG
char errbuf[256];
regerror (ercode, &regex_desc, errbuf, sizeof (errbuf));
TprintfT (DBG_LT0, "linetrace_follow_experiment: regerror()=%s\n", errbuf);
#endif
return 1;
}
TprintfT (DBG_LT0, "linetrace_follow_experiment(): compiled spec='%s'\n", follow_spec);
if (lineage_str)
{
if (!regexec (&regex_desc, lineage_str, 0, NULL, 0))
{
TprintfT (DBG_LT0, "linetrace_follow_experiment(): MATCHES lineage (follow_spec=%s,lineage=%s)\n",
follow_spec, lineage_str);
return 1;
}
}
if (progname)
{
if (!regexec (&regex_desc, progname, 0, NULL, 0))
{
TprintfT (DBG_LT0, "linetrace_follow_experiment(): MATCHES progname (follow_spec=%s,progname=%s)\n",
follow_spec, progname);
return 1;
}
}
TprintfT (DBG_LT0, "linetrace_follow_experiment(): DOES NOT MATCH (follow_spec=%s,lineage=%s,progname=%s)\n",
follow_spec, lineage_str ? lineage_str : "NULL",
progname ? progname : "NULL");
return 0;
}