aboutsummaryrefslogtreecommitdiff
path: root/gprofng/libcollector/linetrace.c
diff options
context:
space:
mode:
authorVladimir Mezentsev <vladimir.mezentsev@oracle.com>2022-03-11 08:58:31 +0000
committerNick Clifton <nickc@redhat.com>2022-03-11 08:58:31 +0000
commitbb368aad297fe3ad40cf397e6fc85aa471429a28 (patch)
tree0ab25909b8fe789d676bbdb00d501d4d485e4afe /gprofng/libcollector/linetrace.c
parenta655f19af95eb685ba64f48ee8fc2b3b7a3d886a (diff)
downloadgdb-bb368aad297fe3ad40cf397e6fc85aa471429a28.zip
gdb-bb368aad297fe3ad40cf397e6fc85aa471429a28.tar.gz
gdb-bb368aad297fe3ad40cf397e6fc85aa471429a28.tar.bz2
gprofng: a new GNU profiler
top-level * Makefile.def: Add gprofng module. * configure.ac: Add --enable-gprofng option. * src-release.sh: Add gprofng. * Makefile.in: Regenerate. * configure: Regenerate. * gprofng: New directory. binutils * MAINTAINERS: Add gprofng maintainer. * README-how-to-make-a-release: Add gprofng. include. * collectorAPI.h: New file. * libcollector.h: New file. * libfcollector.h: New file.
Diffstat (limited to 'gprofng/libcollector/linetrace.c')
-rw-r--r--gprofng/libcollector/linetrace.c2005
1 files changed, 2005 insertions, 0 deletions
diff --git a/gprofng/libcollector/linetrace.c b/gprofng/libcollector/linetrace.c
new file mode 100644
index 0000000..970d68c
--- /dev/null
+++ b/gprofng/libcollector/linetrace.c
@@ -0,0 +1,2005 @@
+/* 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[]);
+
+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);
+}
+
+__asm__(".symver __collector_posix_spawn_2_15,posix_spawn@@GLIBC_2.15");
+
+#if WSIZE(32)
+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);
+}
+
+__asm__(".symver __collector_posix_spawn_2_2,posix_spawn@GLIBC_2.2");
+
+#else /* ^WSIZE(32) */
+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);
+}
+
+__asm__(".symver __collector_posix_spawn_2_2_5,posix_spawn@GLIBC_2.2.5");
+#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[]);
+
+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);
+}
+
+__asm__(".symver __collector_posix_spawnp_2_15,posix_spawnp@@GLIBC_2.15");
+
+#if WSIZE(32)
+
+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);
+}
+
+__asm__(".symver __collector_posix_spawnp_2_2,posix_spawnp@GLIBC_2.2");
+
+#else /* ^WSIZE(32) */
+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);
+}
+
+__asm__(".symver __collector_posix_spawnp_2_2_5,posix_spawnp@GLIBC_2.2.5");
+
+#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);
+
+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);
+}
+
+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);
+}
+
+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);
+}
+
+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);
+}
+
+__asm__(".symver __collector_popen_2_1,popen@@GLIBC_2.1");
+__asm__(".symver __collector_popen_2_0,popen@GLIBC_2.0");
+__asm__(".symver __collector__popen_2_1,_popen@@GLIBC_2.1");
+__asm__(".symver __collector__popen_2_0,_popen@GLIBC_2.0");
+#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;
+}