// SPDX-License-Identifier: GPL-2.0
#include <asm /insn.h>
#include <linux/mm.h>
#include <asm /msr.h>
#include "perf_event.h"
static int decode_branch_type(struct insn *insn)
{
int ext;
if (insn_get_opcode(insn))
return X86_BR_ABORT;
switch (insn->opcode.bytes[0 ]) {
case 0 xf:
switch (insn->opcode.bytes[1 ]) {
case 0 x05: /* syscall */
case 0 x34: /* sysenter */
return X86_BR_SYSCALL;
case 0 x07: /* sysret */
case 0 x35: /* sysexit */
return X86_BR_SYSRET;
case 0 x80 ... 0 x8f: /* conditional */
return X86_BR_JCC;
}
return X86_BR_NONE;
case 0 x70 ... 0 x7f: /* conditional */
return X86_BR_JCC;
case 0 xc2: /* near ret */
case 0 xc3: /* near ret */
case 0 xca: /* far ret */
case 0 xcb: /* far ret */
return X86_BR_RET;
case 0 xcf: /* iret */
return X86_BR_IRET;
case 0 xcc ... 0 xce: /* int */
return X86_BR_INT;
case 0 xe8: /* call near rel */
if (insn_get_immediate(insn) || insn->immediate1.value == 0 ) {
/* zero length call */
return X86_BR_ZERO_CALL;
}
fallthrough;
case 0 x9a: /* call far absolute */
return X86_BR_CALL;
case 0 xe0 ... 0 xe3: /* loop jmp */
return X86_BR_JCC;
case 0 xe9 ... 0 xeb: /* jmp */
return X86_BR_JMP;
case 0 xff: /* call near absolute, call far absolute ind */
if (insn_get_modrm(insn))
return X86_BR_ABORT;
ext = (insn->modrm.bytes[0 ] >> 3 ) & 0 x7;
switch (ext) {
case 2 : /* near ind call */
case 3 : /* far ind call */
return X86_BR_IND_CALL;
case 4 :
case 5 :
return X86_BR_IND_JMP;
}
return X86_BR_NONE;
}
return X86_BR_NONE;
}
/*
* return the type of control flow change at address " from "
* instruction is not necessarily a branch ( in case of interrupt ) .
*
* The branch type returned also includes the priv level of the
* target of the control flow change ( X86_BR_USER , X86_BR_KERNEL ) .
*
* If a branch type is unknown OR the instruction cannot be
* decoded ( e . g . , text page not present ) , then X86_BR_NONE is
* returned .
*
* While recording branches , some processors can report the " from "
* address to be that of an instruction preceding the actual branch
* when instruction fusion occurs . If fusion is expected , attempt to
* find the type of the first branch instruction within the next
* MAX_INSN_SIZE bytes and if found , provide the offset between the
* reported " from " address and the actual branch instruction address .
*/
static int get_branch_type(unsigned long from, unsigned long to, int abort,
bool fused, int *offset)
{
struct insn insn;
void *addr;
int bytes_read, bytes_left, insn_offset;
int ret = X86_BR_NONE;
int to_plm, from_plm;
u8 buf[MAX_INSN_SIZE];
int is64 = 0 ;
/* make sure we initialize offset */
if (offset)
*offset = 0 ;
to_plm = kernel_ip(to) ? X86_BR_KERNEL : X86_BR_USER;
from_plm = kernel_ip(from) ? X86_BR_KERNEL : X86_BR_USER;
/*
* maybe zero if lbr did not fill up after a reset by the time
* we get a PMU interrupt
*/
if (from == 0 || to == 0 )
return X86_BR_NONE;
if (abort)
return X86_BR_ABORT | to_plm;
if (from_plm == X86_BR_USER) {
/*
* can happen if measuring at the user level only
* and we interrupt in a kernel thread , e . g . , idle .
*/
if (!current->mm)
return X86_BR_NONE;
/* may fail if text not present */
bytes_left = copy_from_user_nmi(buf, (void __user *)from,
MAX_INSN_SIZE);
bytes_read = MAX_INSN_SIZE - bytes_left;
if (!bytes_read)
return X86_BR_NONE;
addr = buf;
} else {
/*
* The LBR logs any address in the IP , even if the IP just
* faulted . This means userspace can control the from address .
* Ensure we don ' t blindly read any address by validating it is
* a known text address and not a vsyscall address .
*/
if (kernel_text_address(from) && !in_gate_area_no_mm(from)) {
addr = (void *)from;
/*
* Assume we can get the maximum possible size
* when grabbing kernel data . This is not
* _ strictly_ true since we could possibly be
* executing up next to a memory hole , but
* it is very unlikely to be a problem .
*/
bytes_read = MAX_INSN_SIZE;
} else {
return X86_BR_NONE;
}
}
/*
* decoder needs to know the ABI especially
* on 64 - bit systems running 32 - bit apps
*/
#ifdef CONFIG_X86_64
is64 = kernel_ip((unsigned long )addr) || any_64bit_mode(current_pt_regs());
#endif
insn_init(&insn, addr, bytes_read, is64);
ret = decode_branch_type(&insn);
insn_offset = 0 ;
/* Check for the possibility of branch fusion */
while (fused && ret == X86_BR_NONE) {
/* Check for decoding errors */
if (insn_get_length(&insn) || !insn.length)
break ;
insn_offset += insn.length;
bytes_read -= insn.length;
if (bytes_read < 0 )
break ;
insn_init(&insn, addr + insn_offset, bytes_read, is64);
ret = decode_branch_type(&insn);
}
if (offset)
*offset = insn_offset;
/*
* interrupts , traps , faults ( and thus ring transition ) may
* occur on any instructions . Thus , to classify them correctly ,
* we need to first look at the from and to priv levels . If they
* are different and to is in the kernel , then it indicates
* a ring transition . If the from instruction is not a ring
* transition instr ( syscall , systenter , int ) , then it means
* it was a irq , trap or fault .
*
* we have no way of detecting kernel to kernel faults .
*/
if (from_plm == X86_BR_USER && to_plm == X86_BR_KERNEL
&& ret != X86_BR_SYSCALL && ret != X86_BR_INT)
ret = X86_BR_IRQ;
/*
* branch priv level determined by target as
* is done by HW when LBR_SELECT is implemented
*/
if (ret != X86_BR_NONE)
ret |= to_plm;
return ret;
}
int branch_type(unsigned long from, unsigned long to, int abort)
{
return get_branch_type(from, to, abort, false , NULL);
}
int branch_type_fused(unsigned long from, unsigned long to, int abort,
int *offset)
{
return get_branch_type(from, to, abort, true , offset);
}
#define X86_BR_TYPE_MAP_MAX 16
static int branch_map[X86_BR_TYPE_MAP_MAX] = {
PERF_BR_CALL, /* X86_BR_CALL */
PERF_BR_RET, /* X86_BR_RET */
PERF_BR_SYSCALL, /* X86_BR_SYSCALL */
PERF_BR_SYSRET, /* X86_BR_SYSRET */
PERF_BR_UNKNOWN, /* X86_BR_INT */
PERF_BR_ERET, /* X86_BR_IRET */
PERF_BR_COND, /* X86_BR_JCC */
PERF_BR_UNCOND, /* X86_BR_JMP */
PERF_BR_IRQ, /* X86_BR_IRQ */
PERF_BR_IND_CALL, /* X86_BR_IND_CALL */
PERF_BR_UNKNOWN, /* X86_BR_ABORT */
PERF_BR_UNKNOWN, /* X86_BR_IN_TX */
PERF_BR_NO_TX, /* X86_BR_NO_TX */
PERF_BR_CALL, /* X86_BR_ZERO_CALL */
PERF_BR_UNKNOWN, /* X86_BR_CALL_STACK */
PERF_BR_IND, /* X86_BR_IND_JMP */
};
int common_branch_type(int type)
{
int i;
type >>= 2 ; /* skip X86_BR_USER and X86_BR_KERNEL */
if (type) {
i = __ffs(type);
if (i < X86_BR_TYPE_MAP_MAX)
return branch_map[i];
}
return PERF_BR_UNKNOWN;
}
Messung V0.5 in Prozent C=83 H=99 G=91
¤ Dauer der Verarbeitung: 0.10 Sekunden
(vorverarbeitet am 2026-09-27)
¤
*© Formatika GbR, Deutschland