Beispiel #1
0
static void __stp_dwarf_stack_user_print(struct pt_regs *regs, int verbose,
			      int levels, struct unwind_context *uwcontext,
			      struct uretprobe_instance *ri, int uregs_valid)
{
	struct unwind_frame_info *info = &uwcontext->info;
	arch_unw_init_frame_info(info, regs, ! uregs_valid);

	while (levels) {
		int ret = unwind(uwcontext, 1);
#ifdef STAPCONF_UPROBE_GET_PC
                unsigned long maybe_pc = 0;
                if (ri) {
                        maybe_pc = uprobe_get_pc(ri, UNW_PC(info),
                                                 UNW_SP(info));
                        if (!maybe_pc)
                                printk("SYSTEMTAP ERROR: uprobe_get_return returned 0\n");
                        else
                                UNW_PC(info) = maybe_pc;
                }
#endif
		dbug_unwind(1, "ret=%d PC=%lx SP=%lx\n", ret, UNW_PC(info), UNW_SP(info));
		if (ret == 0 && _stp_valid_pc_addr(UNW_PC(info), current)) {
			_stp_print_addr(UNW_PC(info), verbose, current);
			levels--;
			continue;
		}

		/* If an error happened or the PC becomes invalid, then
		 * we are done, _stp_stack_print_fallback is only for
		 * kernel space. */
		return;
	}
}
Beispiel #2
0
static void __stp_dwarf_stack_kernel_print(struct pt_regs *regs, int verbose,
			      int levels, struct unwind_context *uwcontext)
{
	struct unwind_frame_info *info = &uwcontext->info;
	arch_unw_init_frame_info(info, regs, 0);

	while (levels) {
		int ret = unwind(uwcontext, 0);
		dbug_unwind(1, "ret=%d PC=%lx SP=%lx\n", ret, UNW_PC(info), UNW_SP(info));
		if (ret == 0 && _stp_valid_pc_addr(UNW_PC(info), NULL)) {
			_stp_print_addr(UNW_PC(info), verbose, NULL);
			levels--;
			if (UNW_PC(info) != _stp_kretprobe_trampoline)
			  continue;
		}
		/* If an error happened or we hit a kretprobe trampoline,
		 * and the current pc frame address is still valid kernel
		 * address use fallback backtrace, unless user task backtrace.
		 * FIXME: is there a way to unwind across kretprobe
		 * trampolines? PR9999. */
		if ((ret < 0 || UNW_PC(info) == _stp_kretprobe_trampoline))
			_stp_stack_print_fallback(UNW_SP(info),
						  verbose, levels, 0);
		return;
	}
}
static unsigned long _stp_stack_unwind_one_user(struct context *c, unsigned depth)
{
	struct pt_regs *regs = NULL;
	int uregs_valid = 0;
	struct uretprobe_instance *ri = NULL;
	struct unwind_frame_info *info = NULL;
	int ret;
#ifdef STAPCONF_UPROBE_GET_PC
	unsigned long maybe_pc;
#endif

	if (c->probe_type == stp_probe_type_uretprobe)
		ri = c->ips.ri;
#ifdef STAPCONF_UPROBE_GET_PC
	else if (c->probe_type == stp_probe_type_uprobe)
		ri = GET_PC_URETPROBE_NONE;
#endif

	/* XXX: The computation that gives this is cached, so calling
	 * _stp_get_uregs multiple times is okay... probably. */
	regs = _stp_get_uregs(c);
	uregs_valid = c->full_uregs_p;

	if (! current->mm || ! regs)
		return 0; // no user backtrace at this probe point

	if (depth == 0) { /* Start by fetching the current PC. */
		dbug_unwind(1, "STARTING user unwind\n");

#ifdef STAPCONF_UPROBE_GET_PC
		if (c->probe_type == stp_probe_type_uretprobe && ri) {
			return ri->ret_addr;
		} else {
			return REG_IP(regs);
		}
#else
		return REG_IP(regs);
#endif
	}

#ifdef STP_USE_DWARF_UNWINDER
	info = &c->uwcontext_user.info;

	dbug_unwind(1, "CONTINUING user unwind to depth %d\n", depth);

	if (depth == 1) { /* need to clear uregs & set up uwcontext->info */
		if (c->uregs == &c->uwcontext_user.info.regs) {
			dbug_unwind(1, "clearing uregs\n");
			/* Unwinder needs the reg state, clear uregs ref. */
			c->uregs = NULL;
			c->full_uregs_p = 0;
		}

		arch_unw_init_frame_info(info, regs, 0);
	}

	ret = unwind(&c->uwcontext_user, 1);
#ifdef STAPCONF_UPROBE_GET_PC
	maybe_pc = 0;
	if (ri) {
		maybe_pc = uprobe_get_pc(ri, UNW_PC(info), UNW_SP(info));
		if (!maybe_pc)
			printk("SYSTEMTAP ERROR: uprobe_get_return returned 0\n");
		else
			UNW_PC(info) = maybe_pc;
	}
#endif
	dbug_unwind(1, "ret=%d PC=%llx SP=%llx\n", ret,
		    (unsigned long long) UNW_PC(info), (unsigned long long) UNW_SP(info));

	/* check if unwind hit an error */
	if (ret || ! _stp_valid_pc_addr(UNW_PC(info), current)) {
		return 0;
	}

	return UNW_PC(info);
#else
	/* User stack traces only supported for arches with dwarf unwinder. */
	return 0;
#endif
}
static unsigned long _stp_stack_unwind_one_kernel(struct context *c, unsigned depth)
{
	struct pt_regs *regs = NULL;
	struct unwind_frame_info *info = NULL;
	int ret;

	if (depth == 0) { /* Start by fetching the current PC. */
		dbug_unwind(1, "STARTING kernel unwind\n");

		if (! c->kregs) {
			/* Even the current PC is unknown; so we have
			 * absolutely no data at any depth.
			 *
			 * Note that unlike _stp_stack_kernel_print(),
			 * we can't fall back to calling dump_trace()
			 * to obtain the backtrace -- since that
			 * returns a string, which we would have to
			 * tokenize. Callers that want to use the
			 * dump_trace() fallback should call
			 * _stp_stack_kernel_print() and do their own
			 * tokenization of the result. */
#if defined (__i386__) || defined (__x86_64__)
		        arch_unw_init_frame_info(&c->uwcontext_kernel.info, NULL, 0);
		        return UNW_PC(&c->uwcontext_kernel.info);
#else
			return 0;
#endif
		} else if (c->probe_type == stp_probe_type_kretprobe
			   && c->ips.krp.pi) {
			return (unsigned long)_stp_ret_addr_r(c->ips.krp.pi);
		} else {
			return REG_IP(c->kregs);
		}
	}

#ifdef STP_USE_DWARF_UNWINDER
	/* Otherwise, use the DWARF unwinder to unwind one step. */

	regs = c->kregs;

	info = &c->uwcontext_kernel.info;

	dbug_unwind(1, "CONTINUING kernel unwind to depth %d\n", depth);

	if (depth == 1) {
                /* First step of actual DWARF unwind;
		   need to clear uregs& set up uwcontext->info. */
		if (c->uregs == &c->uwcontext_kernel.info.regs) {
			dbug_unwind(1, "clearing uregs\n");
			/* Unwinder needs the reg state, clear uregs ref. */
			c->uregs = NULL;
			c->full_uregs_p = 0;
		}

		arch_unw_init_frame_info(info, regs, 0);
	}

	ret = unwind(&c->uwcontext_kernel, 0);
	dbug_unwind(1, "ret=%d PC=%llx SP=%llx\n", ret,
		    (unsigned long long) UNW_PC(info),
		    (unsigned long long) UNW_SP(info));

	/* check if unwind hit an error */
	if (ret || ! _stp_valid_pc_addr(UNW_PC(info), NULL)) {
		return 0;
	}

	return UNW_PC(info);
#else
	return 0;
#endif
}