[LINUX] Lire arc / bras / oprofile / common.c

https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git/tree/arch/arm/oprofile/common.c

0. Pilotes de l'appelant / oprofile / oprof.c

L'appel de l'implémentation spécifique à arch est drivers / oprofile / oprof.c

drivers/oprofile/oprof.c


static int __init oprofile_init(void)
{
    int err;

    /* always init architecture to setup backtrace support */
    timer_mode = 0;
    err = oprofile_arch_init(&oprofile_ops);
    if (!err) {
        if (!timer && !oprofilefs_register())
            return 0;
        oprofile_arch_exit();
    }

    /* setup timer mode: */
    timer_mode = 1;
    /* no nmi timer mode if oprofile.timer is set */
    if (timer || op_nmi_timer_init(&oprofile_ops)) {
        err = oprofile_timer_init(&oprofile_ops);
        if (err)
            return err;
    }

    return oprofilefs_register();
}


static void __exit oprofile_exit(void)
{
    oprofilefs_unregister();
    if (!timer_mode)
        oprofile_arch_exit();
}
  1. oprofile_arch_init()

Dans bras, la trace arrière pour bras est enregistrée.

arch/arm/oprofile/common.c


int __init oprofile_arch_init(struct oprofile_operations *ops)
{
    /* provide backtrace support also in timer mode: */
    ops->backtrace      = arm_backtrace;

    return oprofile_perf_init(ops);
}

1.1 arm_backtrace

arch/arm/oprofile/common.c



static void arm_backtrace(struct pt_regs * const regs, unsigned int depth)
{
    struct frame_tail *tail = ((struct frame_tail *) regs->ARM_fp) - 1;

    if (!user_mode(regs)) {
        struct stackframe frame;
        arm_get_current_stackframe(regs, &frame);
        walk_stackframe(&frame, report_trace, &depth);
        return;
    }

    while (depth-- && tail && !((unsigned long) tail & 3))
        tail = user_backtrace(tail);
}

2. Sinon user_mode () (walk_stackframe ())

2.1 arm_get_current_stackframe()

arm_get_current_stackframe () écrit juste le contenu des regs dans le cadre, alors oui, ok! Se sentir comme ça.

arch/arm/include/asm/stacktrace.h


static __always_inline
void arm_get_current_stackframe(struct pt_regs *regs, struct stackframe *frame)
{
        frame->fp = frame_pointer(regs);
        frame->sp = regs->ARM_sp;
        frame->lr = regs->ARM_lr;
        frame->pc = regs->ARM_pc;
}

2.2 walk_stackframe()

C'est le travail d'exécuter le pointeur de fonction fn tout en descendant le cadre.

arch/arm/kernel/stacktrace.c


void notrace walk_stackframe(struct stackframe *frame,
             int (*fn)(struct stackframe *, void *), void *data)
{
    while (1) {
        int ret;

        if (fn(frame, data))
            break;
        ret = unwind_frame(frame);
        if (ret < 0)
            break;
    }
}
EXPORT_SYMBOL(walk_stackframe);

2.3 unwind_frame()

Travaillez pour sortir fp, sp, pc du framepointer. Maintenant, quand vous faites référence à la pile séquentielle ...

arch/arm/kernel/stacktrace.c


#if defined(CONFIG_FRAME_POINTER) && !defined(CONFIG_ARM_UNWIND)
/*
 * Unwind the current stack frame and store the new register values in the
 * structure passed as argument. Unwinding is equivalent to a function return,
 * hence the new PC value rather than LR should be used for backtrace.
 *
 * With framepointer enabled, a simple function prologue looks like this:
 *  mov ip, sp
 *  stmdb   sp!, {fp, ip, lr, pc}
 *  sub fp, ip, #4
 *
 * A simple function epilogue looks like this:
 *  ldm sp, {fp, sp, pc}
 *
 * Note that with framepointer enabled, even the leaf functions have the same
 * prologue and epilogue, therefore we can ignore the LR value in this case.
 */

int notrace unwind_frame(struct stackframe *frame)
{
    unsigned long high, low;
    unsigned long fp = frame->fp;

    /* only go to a higher address on the stack */
    low = frame->sp;
    high = ALIGN(low, THREAD_SIZE);

    /* check current frame pointer is within bounds */
    if (fp < low + 12 || fp > high - 4)
        return -EINVAL;

    /* restore the registers from the stack frame */
    frame->fp = *(unsigned long *)(fp - 12);
    frame->sp = *(unsigned long *)(fp - 8);
    frame->pc = *(unsigned long *)(fp - 4);

    return 0;
}
#endif

2.4 report_trace()

Le processus d'enregistrement des traces dans oprofile.

arch/arm/oprofile/common.c


static int report_trace(struct stackframe *frame, void *d)
{
    unsigned int *depth = d;

    if (*depth) {
        oprofile_add_trace(frame->pc);
        (*depth)--;
    }

    return *depth == 0;
}

3. Pour user_mode () (user_backtrace ())

3.1 user_backtrace

Dans la couche supérieure, il est ajouté à chaque pile et cette user_backtrace () est appelée, donc ici, il traite pour une seule pile.

Alors inscrivez-vous dans oprofile et terminez!

arch/arm/oprofile/common.c


/*
 * The registers we're interested in are at the end of the variable
 * length saved register structure. The fp points at the end of this
 * structure so the address of this struct is:
 * (struct frame_tail *)(xxx->fp)-1
 */
struct frame_tail {
    struct frame_tail *fp;
    unsigned long sp;
    unsigned long lr;
} __attribute__((packed));

static struct frame_tail* user_backtrace(struct frame_tail *tail)
{
    struct frame_tail buftail[2];

    /* Also check accessibility of one struct frame_tail beyond */
    if (!access_ok(tail, sizeof(buftail)))
        return NULL;
    if (__copy_from_user_inatomic(buftail, tail, sizeof(buftail)))
        return NULL;

    oprofile_add_trace(buftail[0].lr);

    /* frame pointers should strictly progress back up the stack
     * (towards higher addresses) */
    if (tail + 1 >= buftail[0].fp)
        return NULL;

    return buftail[0].fp-1;
}

  1. oprofile_arch_exit()

Cela appelle simplement le principal oprofile_perf_exit ().

arch/arm/oprofile/common.c


void oprofile_arch_exit(void)
{
    oprofile_perf_exit();
}

5. (Supplément) pt_regs

En parlant de cela, je me demande s'il est compatible avec d'autres processeurs, mais pt_regs est divisé pour chaque arc ...

5.1 ARM pt_regs

arch/arm/include/asm/ptrace.h


struct pt_regs {
    unsigned long uregs[18];
};

5.1 ARM64 pt_regs

arch/arm64/include/asm/ptrace.h



/*
 * This struct defines the way the registers are stored on the stack during an
 * exception. Note that sizeof(struct pt_regs) has to be a multiple of 16 (for
 * stack alignment). struct user_pt_regs must form a prefix of struct pt_regs.
 */
struct pt_regs {
    union {
        struct user_pt_regs user_regs;
        struct {
            u64 regs[31];
            u64 sp;
            u64 pc;
            u64 pstate;
        };
    };
    u64 orig_x0;
#ifdef __AARCH64EB__
    u32 unused2;
    s32 syscallno;
#else
    s32 syscallno;
    u32 unused2;
#endif

    u64 orig_addr_limit;
    /* Only valid when ARM64_HAS_IRQ_PRIO_MASKING is enabled. */
    u64 pmr_save;
    u64 stackframe[2];
};

5.3 RISC-V pt_regs

arch/riscv/include/asm/ptrace.h


struct pt_regs {
    unsigned long epc;
    unsigned long ra;
    unsigned long sp;
    unsigned long gp;
    unsigned long tp;
    unsigned long t0;
    unsigned long t1;
    unsigned long t2;
    unsigned long s0;
    unsigned long s1;
    unsigned long a0;
    unsigned long a1;
    unsigned long a2;
    unsigned long a3;
    unsigned long a4;
    unsigned long a5;
    unsigned long a6;
    unsigned long a7;
    unsigned long s2;
    unsigned long s3;
    unsigned long s4;
    unsigned long s5;
    unsigned long s6;
    unsigned long s7;
    unsigned long s8;
    unsigned long s9;
    unsigned long s10;
    unsigned long s11;
    unsigned long t3;
    unsigned long t4;
    unsigned long t5;
    unsigned long t6;
    /* Supervisor/Machine CSRs */
    unsigned long status;
    unsigned long badaddr;
    unsigned long cause;
    /* a0 value before the syscall */
    unsigned long orig_a0;
};

C'est tout.


À l'origine, il fait partie du code source du noyau Linux, il sera donc traité comme GPLv2 (reconnaissance qu'il devrait l'être).

https://www.kernel.org/doc/html/latest/index.html

Licensing documentation

The following describes the license of the Linux kernel source code (GPLv2), how to properly mark the license of individual files in the source tree, as well as links to the full license text.

https://www.kernel.org/doc/html/latest/process/license-rules.html#kernel-licensing

Recommended Posts

Lire arc / bras / oprofile / common.c
Lire un peu plus arch / arm / boot / compressé / Makefile
Lisez également le contenu de arch / arm / kernel / swp_emulate.c