예제 #1
0
void
ltrace_init(int argc, char **argv) {
	struct opt_p_t *opt_p_tmp;

	atexit(normal_exit);
	signal(SIGINT, signal_exit);	/* Detach processes when interrupted */
	signal(SIGTERM, signal_exit);	/*  ... or killed */

	argv = process_options(argc, argv);
	init_global_config();
	while (opt_F) {
		/* If filename begins with ~, expand it to the user's home */
		/* directory. This does not correctly handle ~yoda, but that */
		/* isn't as bad as it seems because the shell will normally */
		/* be doing the expansion for us; only the hardcoded */
		/* ~/.ltrace.conf should ever use this code. */
		if (opt_F->filename[0] == '~') {
			char path[PATH_MAX];
			char *home_dir = getenv("HOME");
			if (home_dir) {
				strncpy(path, home_dir, PATH_MAX - 1);
				path[PATH_MAX - 1] = '\0';
				strncat(path, opt_F->filename + 1,
						PATH_MAX - strlen(path) - 1);
				read_config_file(path);
			}
		} else {
			read_config_file(opt_F->filename);
		}

		struct opt_F_t *next = opt_F->next;
		if (opt_F->own_filename)
			free(opt_F->filename);
		free(opt_F);
		opt_F = next;
	}
	if (command) {
		/* Check that the binary ABI is supported before
		 * calling execute_program.  */
		struct ltelf lte = {};
		open_elf(&lte, command);
		do_close_elf(&lte);

		pid_t pid = execute_program(command, argv);
		struct Process *proc = open_program(command, pid);
		if (proc == NULL) {
			fprintf(stderr, "couldn't open program '%s': %s\n",
				command, strerror(errno));
			exit(EXIT_FAILURE);
		}

		trace_set_options(proc);
		continue_process(pid);
	}
	opt_p_tmp = opt_p;
	while (opt_p_tmp) {
		open_pid(opt_p_tmp->pid);
		opt_p_tmp = opt_p_tmp->next;
	}
}
void
ltrace_init(int argc, char **argv)
{
	setlocale(LC_ALL, "");

	struct opt_p_t *opt_p_tmp;

	atexit(normal_exit);
	signal(SIGINT, signal_exit);	/* Detach processes when interrupted */
	signal(SIGTERM, signal_exit);	/*  ... or killed */

	argv = process_options(argc, argv);
	init_global_config();

	if (command) {
		/* Check that the binary ABI is supported before
		 * calling execute_program.  */
		{
			struct ltelf lte;
			if (ltelf_init(&lte, command) == 0)
				ltelf_destroy(&lte);
			else
				exit(EXIT_FAILURE);
		}

		pid_t pid = execute_program(command, argv);
		struct process *proc = open_program(command, pid);
		if (proc == NULL) {
			fprintf(stderr, "couldn't open program '%s': %s\n",
				command, strerror(errno));
			exit(EXIT_FAILURE);
		}

		trace_set_options(proc);
		continue_process(pid);
	}
	opt_p_tmp = opt_p;
	while (opt_p_tmp) {
		open_pid(opt_p_tmp->pid);
		opt_p_tmp = opt_p_tmp->next;
	}
}
예제 #3
0
파일: tracer.c 프로젝트: ReproNim/reprozip
static int trace(pid_t first_proc, int *first_exit_code)
{
    for(;;)
    {
        int status;
        pid_t tid;
        int cpu_time;
        struct Process *process;

        /* Wait for a process */
#if NO_WAIT3
        tid = waitpid(-1, &status, __WALL);
        cpu_time = -1;
#else
        {
            struct rusage res;
            tid = wait3(&status, __WALL, &res);
            cpu_time = (res.ru_utime.tv_sec * 1000 +
                        res.ru_utime.tv_usec / 1000);
        }
#endif
        if(tid == -1)
        {
            /* LCOV_EXCL_START : internal error: waitpid() won't fail unless we
             * mistakingly call it while there is no child to wait for */
            log_critical(0, "waitpid failed: %s", strerror(errno));
            return -1;
            /* LCOV_EXCL_END */
        }
        if(WIFEXITED(status) || WIFSIGNALED(status))
        {
            unsigned int nprocs, unknown;
            int exitcode;
            if(WIFSIGNALED(status))
                /* exit codes are 8 bits */
                exitcode = 0x0100 | WTERMSIG(status);
            else
                exitcode = WEXITSTATUS(status);

            if(tid == first_proc && first_exit_code != NULL)
                *first_exit_code = exitcode;
            process = trace_find_process(tid);
            if(process != NULL)
            {
                int cpu_time_val = -1;
                if(process->tid == process->threadgroup->tgid)
                    cpu_time_val = cpu_time;
                if(db_add_exit(process->identifier, exitcode,
                               cpu_time_val) != 0)
                    return -1;
                trace_free_process(process);
            }
            trace_count_processes(&nprocs, &unknown);
            if(verbosity >= 2)
                log_info(tid, "process exited (%s %d), CPU time %.2f, "
                         "%d processes remain",
                         (exitcode & 0x0100)?"signal":"code", exitcode & 0xFF,
                         cpu_time * 0.001f, (unsigned int)nprocs);
            if(nprocs <= 0)
                break;
            if(unknown >= nprocs)
            {
                /* LCOV_EXCL_START : This can't happen because UNKNOWN
                 * processes are the forked processes whose creator has not
                 * returned yet. Therefore, if there is an UNKNOWN process, its
                 * creator has to exist as well (and it is not UNKNOWN). */
                log_critical(0, "only UNKNOWN processes remaining (%d)",
                             (unsigned int)nprocs);
                return -1;
                /* LCOV_EXCL_END */
            }
            continue;
        }

        process = trace_find_process(tid);
        if(process == NULL)
        {
            if(verbosity >= 3)
                log_debug(tid, "process appeared");
            process = trace_get_empty_process();
            process->status = PROCSTAT_UNKNOWN;
            process->flags = 0;
            process->tid = tid;
            process->threadgroup = NULL;
            process->in_syscall = 0;
            trace_set_options(tid);
            /* Don't resume, it will be set to ATTACHED and resumed when fork()
             * returns */
            continue;
        }
        else if(process->status == PROCSTAT_ALLOCATED)
        {
            process->status = PROCSTAT_ATTACHED;

            if(verbosity >= 3)
                log_debug(tid, "process attached");
            trace_set_options(tid);
            ptrace(PTRACE_SYSCALL, tid, NULL, NULL);
            if(verbosity >= 2)
            {
                unsigned int nproc, unknown;
                trace_count_processes(&nproc, &unknown);
                log_info(0, "%d processes (inc. %d unattached)",
                         nproc, unknown);
            }
            continue;
        }

        if(WIFSTOPPED(status) && WSTOPSIG(status) & 0x80)
        {
            size_t len = 0;
#ifdef I386
            struct i386_regs regs;
#else /* def X86_64 */
            struct x86_64_regs regs;
#endif
            /* Try to use GETREGSET first, since iov_len allows us to know if
             * 32bit or 64bit mode was used */
#ifdef PTRACE_GETREGSET
#ifndef NT_PRSTATUS
#define NT_PRSTATUS  1
#endif
            {
                struct iovec iov;
                iov.iov_base = &regs;
                iov.iov_len = sizeof(regs);
                if(ptrace(PTRACE_GETREGSET, tid, NT_PRSTATUS, &iov) == 0)
                    len = iov.iov_len;
            }
            if(len == 0)
#endif
            /* GETREGSET undefined or call failed, fallback on GETREGS */
            {
                /* LCOV_EXCL_START : GETREGSET was added by Linux 2.6.34 in
                 * May 2010 (2225a122) */
                ptrace(PTRACE_GETREGS, tid, NULL, &regs);
                /* LCOV_EXCL_END */
            }
#if defined(I386)
            if(!process->in_syscall)
                process->current_syscall = regs.orig_eax;
            if(process->in_syscall)
                get_i386_reg(&process->retvalue, regs.eax);
            else
            {
                get_i386_reg(&process->params[0], regs.ebx);
                get_i386_reg(&process->params[1], regs.ecx);
                get_i386_reg(&process->params[2], regs.edx);
                get_i386_reg(&process->params[3], regs.esi);
                get_i386_reg(&process->params[4], regs.edi);
                get_i386_reg(&process->params[5], regs.ebp);
            }
            process->mode = MODE_I386;
#elif defined(X86_64)
            /* On x86_64, process might be 32 or 64 bits */
            /* If len is known (not 0) and not that of x86_64 registers,
             * or if len is not known (0) and CS is 0x23 (not as reliable) */
            if( (len != 0 && len != sizeof(regs))
             || (len == 0 && regs.cs == 0x23) )
            {
                /* 32 bit mode */
                struct i386_regs *x86regs = (struct i386_regs*)&regs;
                if(!process->in_syscall)
                    process->current_syscall = x86regs->orig_eax;
                if(process->in_syscall)
                    get_i386_reg(&process->retvalue, x86regs->eax);
                else
                {
                    get_i386_reg(&process->params[0], x86regs->ebx);
                    get_i386_reg(&process->params[1], x86regs->ecx);
                    get_i386_reg(&process->params[2], x86regs->edx);
                    get_i386_reg(&process->params[3], x86regs->esi);
                    get_i386_reg(&process->params[4], x86regs->edi);
                    get_i386_reg(&process->params[5], x86regs->ebp);
                }
                process->mode = MODE_I386;
            }
            else
            {
                /* 64 bit mode */
                if(!process->in_syscall)
                    process->current_syscall = regs.orig_rax;
                if(process->in_syscall)
                    get_x86_64_reg(&process->retvalue, regs.rax);
                else
                {
                    get_x86_64_reg(&process->params[0], regs.rdi);
                    get_x86_64_reg(&process->params[1], regs.rsi);
                    get_x86_64_reg(&process->params[2], regs.rdx);
                    get_x86_64_reg(&process->params[3], regs.r10);
                    get_x86_64_reg(&process->params[4], regs.r8);
                    get_x86_64_reg(&process->params[5], regs.r9);
                }
                /* Might still be either native x64 or Linux's x32 layer */
                process->mode = MODE_X86_64;
            }
#endif
            if(syscall_handle(process) != 0)
                return -1;
        }
        /* Handle signals */
        else if(WIFSTOPPED(status))
        {
            int signum = WSTOPSIG(status) & 0x7F;

            /* Synthetic signal for ptrace event: resume */
            if(signum == SIGTRAP && status & 0xFF0000)
            {
                int event = status >> 16;
                if(event == PTRACE_EVENT_EXEC)
                {
                    log_debug(tid,
                             "got EVENT_EXEC, an execve() was successful and "
                             "will return soon");
                    if(syscall_execve_event(process) != 0)
                        return -1;
                }
                else if( (event == PTRACE_EVENT_FORK)
                      || (event == PTRACE_EVENT_VFORK)
                      || (event == PTRACE_EVENT_CLONE))
                {
                    if(syscall_fork_event(process, event) != 0)
                        return -1;
                }
                ptrace(PTRACE_SYSCALL, tid, NULL, NULL);
            }
            else if(signum == SIGTRAP)
            {
                /* LCOV_EXCL_START : Processes shouldn't be getting SIGTRAPs */
                log_error(0,
                          "NOT delivering SIGTRAP to %d\n"
                          "    waitstatus=0x%X", tid, status);
                ptrace(PTRACE_SYSCALL, tid, NULL, NULL);
                /* LCOV_EXCL_END */
            }
            /* Other signal, let the process handle it */
            else
            {
                siginfo_t si;
                if(verbosity >= 2)
                    log_info(tid, "caught signal %d", signum);
                if(ptrace(PTRACE_GETSIGINFO, tid, 0, (long)&si) >= 0)
                    ptrace(PTRACE_SYSCALL, tid, NULL, signum);
                else
                {
                    /* LCOV_EXCL_START : Not sure what this is for... doesn't
                     * seem to happen in practice */
                    log_error(tid, "    NOT delivering: %s", strerror(errno));
                    if(signum != SIGSTOP)
                        ptrace(PTRACE_SYSCALL, tid, NULL, NULL);
                    /* LCOV_EXCL_END */
                }
            }
        }
예제 #4
0
파일: proc.c 프로젝트: mer-tools/ltrace
int
process_clone(struct Process *retp, struct Process *proc, pid_t pid)
{
	if (process_bare_init(retp, proc->filename, pid, 0) < 0) {
	fail1:
		fprintf(stderr, "failed to clone process %d->%d : %s\n",
			proc->pid, pid, strerror(errno));
		return -1;
	}

	retp->tracesysgood = proc->tracesysgood;
	retp->e_machine = proc->e_machine;
	retp->e_class = proc->e_class;

	/* For non-leader processes, that's all we need to do.  */
	if (retp->leader != retp)
		return 0;

	/* Clone symbols first so that we can clone and relink
	 * breakpoints.  */
	struct library *lib;
	struct library **nlibp = &retp->libraries;
	for (lib = proc->leader->libraries; lib != NULL; lib = lib->next) {
		*nlibp = malloc(sizeof(**nlibp));
		if (*nlibp == NULL
		    || library_clone(*nlibp, lib) < 0) {
		fail2:
			process_bare_destroy(retp, 0);

			/* Error when cloning.  Unroll what was done.  */
			for (lib = retp->libraries; lib != NULL; ) {
				struct library *next = lib->next;
				library_destroy(lib);
				free(lib);
				lib = next;
			}
			goto fail1;
		}

		nlibp = &(*nlibp)->next;
	}

	/* Now clone breakpoints.  Symbol relinking is done in
	 * clone_single_bp.  */
	struct clone_single_bp_data data = {
		.old_proc = proc,
		.new_proc = retp,
		.error = 0,
	};
	dict_apply_to_all(proc->leader->breakpoints, &clone_single_bp, &data);
	if (data.error < 0)
		goto fail2;

	/* And finally the call stack.  */
	/* XXX clearly the callstack handling should be moved to a
	 * separate module and this whole business extracted to
	 * callstack_clone, or callstack_element_clone.  */
	memcpy(retp->callstack, proc->callstack, sizeof(retp->callstack));
	retp->callstack_depth = proc->callstack_depth;

	size_t i;
	for (i = 0; i < retp->callstack_depth; ++i) {
		struct callstack_element *elem = &retp->callstack[i];
		struct fetch_context *ctx = elem->fetch_context;
		if (ctx != NULL) {
			struct fetch_context *nctx = fetch_arg_clone(retp, ctx);
			if (nctx == NULL) {
				size_t j;
			fail3:
				for (j = 0; j < i; ++j) {
					nctx = elem->fetch_context;
					fetch_arg_done(nctx);
					elem->fetch_context = NULL;
				}
				goto fail2;
			}
			elem->fetch_context = nctx;
		}

		struct value_dict *args = elem->arguments;
		if (args != NULL) {
			struct value_dict *nargs = malloc(sizeof(*nargs));
			if (nargs == NULL
			    || val_dict_clone(nargs, args) < 0) {
				size_t j;
				for (j = 0; j < i; ++j) {
					nargs = elem->arguments;
					val_dict_destroy(nargs);
					free(nargs);
					elem->arguments = NULL;
				}

				/* Pretend that this round went well,
				 * so that fail3 frees I-th
				 * fetch_context.  */
				++i;
				goto fail3;
			}
			elem->arguments = nargs;
		}

		/* If it's not a syscall, we need to find the
		 * corresponding library symbol in the cloned
		 * library.  */
		if (!elem->is_syscall && elem->c_un.libfunc != NULL) {
			struct library_symbol *libfunc = elem->c_un.libfunc;
			int rc = proc_find_symbol(retp, libfunc,
						  NULL, &elem->c_un.libfunc);
			assert(rc == 0);
		}
	}

	/* At this point, retp is fully initialized, except for OS and
	 * arch parts, and we can call private_process_destroy.  */
	if (os_process_clone(retp, proc) < 0) {
		private_process_destroy(retp, 0);
		return -1;
	}
	if (arch_process_clone(retp, proc) < 0) {
		os_process_destroy(retp);
		private_process_destroy(retp, 0);
		return -1;
	}

	return 0;
}

static int
open_one_pid(pid_t pid)
{
	Process *proc;
	char *filename;
	debug(DEBUG_PROCESS, "open_one_pid(pid=%d)", pid);

	/* Get the filename first.  Should the trace_pid fail, we can
	 * easily free it, untracing is more work.  */
	if ((filename = pid2name(pid)) == NULL
	    || trace_pid(pid) < 0) {
	fail:
		free(filename);
		return -1;
	}

	proc = open_program(filename, pid);
	if (proc == NULL)
		goto fail;
	free(filename);
	trace_set_options(proc);

	return 0;
}