Merge branch 'perf/core-v3' of git://git.kernel.org/pub/scm/linux/kernel/git/frederic/linux-dynticks into perf/hw_breakpoints

Pull AMD range breakpoints support from Frederic Weisbecker:

" - Extend breakpoint tools and core to support address range through perf
    event with initial backend support for AMD extended breakpoints.

    Syntax is:

           perf record -e mem:addr/len:type

    For example set write breakpoint from 0x1000 to 0x1200 (0x1000 + 512)

           perf record -e mem:0x1000/512:w

 - Clean up a bit breakpoint code validation

 It has been acked by Jiri and Oleg. "

Signed-off-by: Ingo Molnar <mingo@kernel.org>
This commit is contained in:
Ingo Molnar 2014-12-08 11:50:24 +01:00
commit 2a2662bf88
104 changed files with 4416 additions and 884 deletions

View File

@ -174,6 +174,7 @@
#define X86_FEATURE_TOPOEXT ( 6*32+22) /* topology extensions CPUID leafs */ #define X86_FEATURE_TOPOEXT ( 6*32+22) /* topology extensions CPUID leafs */
#define X86_FEATURE_PERFCTR_CORE ( 6*32+23) /* core performance counter extensions */ #define X86_FEATURE_PERFCTR_CORE ( 6*32+23) /* core performance counter extensions */
#define X86_FEATURE_PERFCTR_NB ( 6*32+24) /* NB performance counter extensions */ #define X86_FEATURE_PERFCTR_NB ( 6*32+24) /* NB performance counter extensions */
#define X86_FEATURE_BPEXT (6*32+26) /* data breakpoint extension */
#define X86_FEATURE_PERFCTR_L2 ( 6*32+28) /* L2 performance counter extensions */ #define X86_FEATURE_PERFCTR_L2 ( 6*32+28) /* L2 performance counter extensions */
/* /*
@ -383,6 +384,7 @@ extern const char * const x86_bug_flags[NBUGINTS*32];
#define cpu_has_cx16 boot_cpu_has(X86_FEATURE_CX16) #define cpu_has_cx16 boot_cpu_has(X86_FEATURE_CX16)
#define cpu_has_eager_fpu boot_cpu_has(X86_FEATURE_EAGER_FPU) #define cpu_has_eager_fpu boot_cpu_has(X86_FEATURE_EAGER_FPU)
#define cpu_has_topoext boot_cpu_has(X86_FEATURE_TOPOEXT) #define cpu_has_topoext boot_cpu_has(X86_FEATURE_TOPOEXT)
#define cpu_has_bpext boot_cpu_has(X86_FEATURE_BPEXT)
#if __GNUC__ >= 4 #if __GNUC__ >= 4
extern void warn_pre_alternatives(void); extern void warn_pre_alternatives(void);

View File

@ -114,5 +114,10 @@ static inline void debug_stack_usage_inc(void) { }
static inline void debug_stack_usage_dec(void) { } static inline void debug_stack_usage_dec(void) { }
#endif /* X86_64 */ #endif /* X86_64 */
#ifdef CONFIG_CPU_SUP_AMD
extern void set_dr_addr_mask(unsigned long mask, int dr);
#else
static inline void set_dr_addr_mask(unsigned long mask, int dr) { }
#endif
#endif /* _ASM_X86_DEBUGREG_H */ #endif /* _ASM_X86_DEBUGREG_H */

View File

@ -12,6 +12,7 @@
*/ */
struct arch_hw_breakpoint { struct arch_hw_breakpoint {
unsigned long address; unsigned long address;
unsigned long mask;
u8 len; u8 len;
u8 type; u8 type;
}; };

View File

@ -177,6 +177,9 @@ struct x86_pmu_capability {
#define IBS_CAPS_BRNTRGT (1U<<5) #define IBS_CAPS_BRNTRGT (1U<<5)
#define IBS_CAPS_OPCNTEXT (1U<<6) #define IBS_CAPS_OPCNTEXT (1U<<6)
#define IBS_CAPS_RIPINVALIDCHK (1U<<7) #define IBS_CAPS_RIPINVALIDCHK (1U<<7)
#define IBS_CAPS_OPBRNFUSE (1U<<8)
#define IBS_CAPS_FETCHCTLEXTD (1U<<9)
#define IBS_CAPS_OPDATA4 (1U<<10)
#define IBS_CAPS_DEFAULT (IBS_CAPS_AVAIL \ #define IBS_CAPS_DEFAULT (IBS_CAPS_AVAIL \
| IBS_CAPS_FETCHSAM \ | IBS_CAPS_FETCHSAM \

View File

@ -206,11 +206,16 @@
#define MSR_AMD64_IBSOP_REG_MASK ((1UL<<MSR_AMD64_IBSOP_REG_COUNT)-1) #define MSR_AMD64_IBSOP_REG_MASK ((1UL<<MSR_AMD64_IBSOP_REG_COUNT)-1)
#define MSR_AMD64_IBSCTL 0xc001103a #define MSR_AMD64_IBSCTL 0xc001103a
#define MSR_AMD64_IBSBRTARGET 0xc001103b #define MSR_AMD64_IBSBRTARGET 0xc001103b
#define MSR_AMD64_IBSOPDATA4 0xc001103d
#define MSR_AMD64_IBS_REG_COUNT_MAX 8 /* includes MSR_AMD64_IBSBRTARGET */ #define MSR_AMD64_IBS_REG_COUNT_MAX 8 /* includes MSR_AMD64_IBSBRTARGET */
/* Fam 16h MSRs */ /* Fam 16h MSRs */
#define MSR_F16H_L2I_PERF_CTL 0xc0010230 #define MSR_F16H_L2I_PERF_CTL 0xc0010230
#define MSR_F16H_L2I_PERF_CTR 0xc0010231 #define MSR_F16H_L2I_PERF_CTR 0xc0010231
#define MSR_F16H_DR1_ADDR_MASK 0xc0011019
#define MSR_F16H_DR2_ADDR_MASK 0xc001101a
#define MSR_F16H_DR3_ADDR_MASK 0xc001101b
#define MSR_F16H_DR0_ADDR_MASK 0xc0011027
/* Fam 15h MSRs */ /* Fam 15h MSRs */
#define MSR_F15H_PERF_CTL 0xc0010200 #define MSR_F15H_PERF_CTL 0xc0010200

View File

@ -870,3 +870,22 @@ static bool cpu_has_amd_erratum(struct cpuinfo_x86 *cpu, const int *erratum)
return false; return false;
} }
void set_dr_addr_mask(unsigned long mask, int dr)
{
if (!cpu_has_bpext)
return;
switch (dr) {
case 0:
wrmsr(MSR_F16H_DR0_ADDR_MASK, mask, 0);
break;
case 1:
case 2:
case 3:
wrmsr(MSR_F16H_DR1_ADDR_MASK - 1 + dr, mask, 0);
break;
default:
break;
}
}

View File

@ -253,6 +253,10 @@ struct cpu_hw_events {
#define INTEL_UEVENT_CONSTRAINT(c, n) \ #define INTEL_UEVENT_CONSTRAINT(c, n) \
EVENT_CONSTRAINT(c, n, INTEL_ARCH_EVENT_MASK) EVENT_CONSTRAINT(c, n, INTEL_ARCH_EVENT_MASK)
/* Like UEVENT_CONSTRAINT, but match flags too */
#define INTEL_FLAGS_UEVENT_CONSTRAINT(c, n) \
EVENT_CONSTRAINT(c, n, INTEL_ARCH_EVENT_MASK|X86_ALL_EVENT_FLAGS)
#define INTEL_PLD_CONSTRAINT(c, n) \ #define INTEL_PLD_CONSTRAINT(c, n) \
__EVENT_CONSTRAINT(c, n, INTEL_ARCH_EVENT_MASK|X86_ALL_EVENT_FLAGS, \ __EVENT_CONSTRAINT(c, n, INTEL_ARCH_EVENT_MASK|X86_ALL_EVENT_FLAGS, \
HWEIGHT(n), 0, PERF_X86_EVENT_PEBS_LDLAT) HWEIGHT(n), 0, PERF_X86_EVENT_PEBS_LDLAT)

View File

@ -565,6 +565,21 @@ static int perf_ibs_handle_irq(struct perf_ibs *perf_ibs, struct pt_regs *iregs)
perf_ibs->offset_max, perf_ibs->offset_max,
offset + 1); offset + 1);
} while (offset < offset_max); } while (offset < offset_max);
if (event->attr.sample_type & PERF_SAMPLE_RAW) {
/*
* Read IbsBrTarget and IbsOpData4 separately
* depending on their availability.
* Can't add to offset_max as they are staggered
*/
if (ibs_caps & IBS_CAPS_BRNTRGT) {
rdmsrl(MSR_AMD64_IBSBRTARGET, *buf++);
size++;
}
if (ibs_caps & IBS_CAPS_OPDATA4) {
rdmsrl(MSR_AMD64_IBSOPDATA4, *buf++);
size++;
}
}
ibs_data.size = sizeof(u64) * size; ibs_data.size = sizeof(u64) * size;
regs = *iregs; regs = *iregs;

View File

@ -552,18 +552,18 @@ int intel_pmu_drain_bts_buffer(void)
* PEBS * PEBS
*/ */
struct event_constraint intel_core2_pebs_event_constraints[] = { struct event_constraint intel_core2_pebs_event_constraints[] = {
INTEL_UEVENT_CONSTRAINT(0x00c0, 0x1), /* INST_RETIRED.ANY */ INTEL_FLAGS_UEVENT_CONSTRAINT(0x00c0, 0x1), /* INST_RETIRED.ANY */
INTEL_UEVENT_CONSTRAINT(0xfec1, 0x1), /* X87_OPS_RETIRED.ANY */ INTEL_FLAGS_UEVENT_CONSTRAINT(0xfec1, 0x1), /* X87_OPS_RETIRED.ANY */
INTEL_UEVENT_CONSTRAINT(0x00c5, 0x1), /* BR_INST_RETIRED.MISPRED */ INTEL_FLAGS_UEVENT_CONSTRAINT(0x00c5, 0x1), /* BR_INST_RETIRED.MISPRED */
INTEL_UEVENT_CONSTRAINT(0x1fc7, 0x1), /* SIMD_INST_RETURED.ANY */ INTEL_FLAGS_UEVENT_CONSTRAINT(0x1fc7, 0x1), /* SIMD_INST_RETURED.ANY */
INTEL_EVENT_CONSTRAINT(0xcb, 0x1), /* MEM_LOAD_RETIRED.* */ INTEL_FLAGS_EVENT_CONSTRAINT(0xcb, 0x1), /* MEM_LOAD_RETIRED.* */
EVENT_CONSTRAINT_END EVENT_CONSTRAINT_END
}; };
struct event_constraint intel_atom_pebs_event_constraints[] = { struct event_constraint intel_atom_pebs_event_constraints[] = {
INTEL_UEVENT_CONSTRAINT(0x00c0, 0x1), /* INST_RETIRED.ANY */ INTEL_FLAGS_UEVENT_CONSTRAINT(0x00c0, 0x1), /* INST_RETIRED.ANY */
INTEL_UEVENT_CONSTRAINT(0x00c5, 0x1), /* MISPREDICTED_BRANCH_RETIRED */ INTEL_FLAGS_UEVENT_CONSTRAINT(0x00c5, 0x1), /* MISPREDICTED_BRANCH_RETIRED */
INTEL_EVENT_CONSTRAINT(0xcb, 0x1), /* MEM_LOAD_RETIRED.* */ INTEL_FLAGS_EVENT_CONSTRAINT(0xcb, 0x1), /* MEM_LOAD_RETIRED.* */
EVENT_CONSTRAINT_END EVENT_CONSTRAINT_END
}; };
@ -577,36 +577,36 @@ struct event_constraint intel_slm_pebs_event_constraints[] = {
struct event_constraint intel_nehalem_pebs_event_constraints[] = { struct event_constraint intel_nehalem_pebs_event_constraints[] = {
INTEL_PLD_CONSTRAINT(0x100b, 0xf), /* MEM_INST_RETIRED.* */ INTEL_PLD_CONSTRAINT(0x100b, 0xf), /* MEM_INST_RETIRED.* */
INTEL_EVENT_CONSTRAINT(0x0f, 0xf), /* MEM_UNCORE_RETIRED.* */ INTEL_FLAGS_EVENT_CONSTRAINT(0x0f, 0xf), /* MEM_UNCORE_RETIRED.* */
INTEL_UEVENT_CONSTRAINT(0x010c, 0xf), /* MEM_STORE_RETIRED.DTLB_MISS */ INTEL_FLAGS_UEVENT_CONSTRAINT(0x010c, 0xf), /* MEM_STORE_RETIRED.DTLB_MISS */
INTEL_EVENT_CONSTRAINT(0xc0, 0xf), /* INST_RETIRED.ANY */ INTEL_FLAGS_EVENT_CONSTRAINT(0xc0, 0xf), /* INST_RETIRED.ANY */
INTEL_EVENT_CONSTRAINT(0xc2, 0xf), /* UOPS_RETIRED.* */ INTEL_EVENT_CONSTRAINT(0xc2, 0xf), /* UOPS_RETIRED.* */
INTEL_EVENT_CONSTRAINT(0xc4, 0xf), /* BR_INST_RETIRED.* */ INTEL_FLAGS_EVENT_CONSTRAINT(0xc4, 0xf), /* BR_INST_RETIRED.* */
INTEL_UEVENT_CONSTRAINT(0x02c5, 0xf), /* BR_MISP_RETIRED.NEAR_CALL */ INTEL_FLAGS_UEVENT_CONSTRAINT(0x02c5, 0xf), /* BR_MISP_RETIRED.NEAR_CALL */
INTEL_EVENT_CONSTRAINT(0xc7, 0xf), /* SSEX_UOPS_RETIRED.* */ INTEL_FLAGS_EVENT_CONSTRAINT(0xc7, 0xf), /* SSEX_UOPS_RETIRED.* */
INTEL_UEVENT_CONSTRAINT(0x20c8, 0xf), /* ITLB_MISS_RETIRED */ INTEL_FLAGS_UEVENT_CONSTRAINT(0x20c8, 0xf), /* ITLB_MISS_RETIRED */
INTEL_EVENT_CONSTRAINT(0xcb, 0xf), /* MEM_LOAD_RETIRED.* */ INTEL_FLAGS_EVENT_CONSTRAINT(0xcb, 0xf), /* MEM_LOAD_RETIRED.* */
INTEL_EVENT_CONSTRAINT(0xf7, 0xf), /* FP_ASSIST.* */ INTEL_FLAGS_EVENT_CONSTRAINT(0xf7, 0xf), /* FP_ASSIST.* */
EVENT_CONSTRAINT_END EVENT_CONSTRAINT_END
}; };
struct event_constraint intel_westmere_pebs_event_constraints[] = { struct event_constraint intel_westmere_pebs_event_constraints[] = {
INTEL_PLD_CONSTRAINT(0x100b, 0xf), /* MEM_INST_RETIRED.* */ INTEL_PLD_CONSTRAINT(0x100b, 0xf), /* MEM_INST_RETIRED.* */
INTEL_EVENT_CONSTRAINT(0x0f, 0xf), /* MEM_UNCORE_RETIRED.* */ INTEL_FLAGS_EVENT_CONSTRAINT(0x0f, 0xf), /* MEM_UNCORE_RETIRED.* */
INTEL_UEVENT_CONSTRAINT(0x010c, 0xf), /* MEM_STORE_RETIRED.DTLB_MISS */ INTEL_FLAGS_UEVENT_CONSTRAINT(0x010c, 0xf), /* MEM_STORE_RETIRED.DTLB_MISS */
INTEL_EVENT_CONSTRAINT(0xc0, 0xf), /* INSTR_RETIRED.* */ INTEL_FLAGS_EVENT_CONSTRAINT(0xc0, 0xf), /* INSTR_RETIRED.* */
INTEL_EVENT_CONSTRAINT(0xc2, 0xf), /* UOPS_RETIRED.* */ INTEL_EVENT_CONSTRAINT(0xc2, 0xf), /* UOPS_RETIRED.* */
INTEL_EVENT_CONSTRAINT(0xc4, 0xf), /* BR_INST_RETIRED.* */ INTEL_FLAGS_EVENT_CONSTRAINT(0xc4, 0xf), /* BR_INST_RETIRED.* */
INTEL_EVENT_CONSTRAINT(0xc5, 0xf), /* BR_MISP_RETIRED.* */ INTEL_FLAGS_EVENT_CONSTRAINT(0xc5, 0xf), /* BR_MISP_RETIRED.* */
INTEL_EVENT_CONSTRAINT(0xc7, 0xf), /* SSEX_UOPS_RETIRED.* */ INTEL_FLAGS_EVENT_CONSTRAINT(0xc7, 0xf), /* SSEX_UOPS_RETIRED.* */
INTEL_UEVENT_CONSTRAINT(0x20c8, 0xf), /* ITLB_MISS_RETIRED */ INTEL_FLAGS_UEVENT_CONSTRAINT(0x20c8, 0xf), /* ITLB_MISS_RETIRED */
INTEL_EVENT_CONSTRAINT(0xcb, 0xf), /* MEM_LOAD_RETIRED.* */ INTEL_FLAGS_EVENT_CONSTRAINT(0xcb, 0xf), /* MEM_LOAD_RETIRED.* */
INTEL_EVENT_CONSTRAINT(0xf7, 0xf), /* FP_ASSIST.* */ INTEL_FLAGS_EVENT_CONSTRAINT(0xf7, 0xf), /* FP_ASSIST.* */
EVENT_CONSTRAINT_END EVENT_CONSTRAINT_END
}; };
struct event_constraint intel_snb_pebs_event_constraints[] = { struct event_constraint intel_snb_pebs_event_constraints[] = {
INTEL_UEVENT_CONSTRAINT(0x01c0, 0x2), /* INST_RETIRED.PRECDIST */ INTEL_FLAGS_UEVENT_CONSTRAINT(0x01c0, 0x2), /* INST_RETIRED.PRECDIST */
INTEL_PLD_CONSTRAINT(0x01cd, 0x8), /* MEM_TRANS_RETIRED.LAT_ABOVE_THR */ INTEL_PLD_CONSTRAINT(0x01cd, 0x8), /* MEM_TRANS_RETIRED.LAT_ABOVE_THR */
INTEL_PST_CONSTRAINT(0x02cd, 0x8), /* MEM_TRANS_RETIRED.PRECISE_STORES */ INTEL_PST_CONSTRAINT(0x02cd, 0x8), /* MEM_TRANS_RETIRED.PRECISE_STORES */
/* UOPS_RETIRED.ALL, inv=1, cmask=16 (cycles:p). */ /* UOPS_RETIRED.ALL, inv=1, cmask=16 (cycles:p). */
@ -617,7 +617,7 @@ struct event_constraint intel_snb_pebs_event_constraints[] = {
}; };
struct event_constraint intel_ivb_pebs_event_constraints[] = { struct event_constraint intel_ivb_pebs_event_constraints[] = {
INTEL_UEVENT_CONSTRAINT(0x01c0, 0x2), /* INST_RETIRED.PRECDIST */ INTEL_FLAGS_UEVENT_CONSTRAINT(0x01c0, 0x2), /* INST_RETIRED.PRECDIST */
INTEL_PLD_CONSTRAINT(0x01cd, 0x8), /* MEM_TRANS_RETIRED.LAT_ABOVE_THR */ INTEL_PLD_CONSTRAINT(0x01cd, 0x8), /* MEM_TRANS_RETIRED.LAT_ABOVE_THR */
INTEL_PST_CONSTRAINT(0x02cd, 0x8), /* MEM_TRANS_RETIRED.PRECISE_STORES */ INTEL_PST_CONSTRAINT(0x02cd, 0x8), /* MEM_TRANS_RETIRED.PRECISE_STORES */
/* UOPS_RETIRED.ALL, inv=1, cmask=16 (cycles:p). */ /* UOPS_RETIRED.ALL, inv=1, cmask=16 (cycles:p). */
@ -628,7 +628,7 @@ struct event_constraint intel_ivb_pebs_event_constraints[] = {
}; };
struct event_constraint intel_hsw_pebs_event_constraints[] = { struct event_constraint intel_hsw_pebs_event_constraints[] = {
INTEL_UEVENT_CONSTRAINT(0x01c0, 0x2), /* INST_RETIRED.PRECDIST */ INTEL_FLAGS_UEVENT_CONSTRAINT(0x01c0, 0x2), /* INST_RETIRED.PRECDIST */
INTEL_PLD_CONSTRAINT(0x01cd, 0xf), /* MEM_TRANS_RETIRED.* */ INTEL_PLD_CONSTRAINT(0x01cd, 0xf), /* MEM_TRANS_RETIRED.* */
/* UOPS_RETIRED.ALL, inv=1, cmask=16 (cycles:p). */ /* UOPS_RETIRED.ALL, inv=1, cmask=16 (cycles:p). */
INTEL_FLAGS_EVENT_CONSTRAINT(0x108001c2, 0xf), INTEL_FLAGS_EVENT_CONSTRAINT(0x108001c2, 0xf),
@ -886,6 +886,29 @@ static void __intel_pmu_pebs_event(struct perf_event *event,
regs.bp = pebs->bp; regs.bp = pebs->bp;
regs.sp = pebs->sp; regs.sp = pebs->sp;
if (sample_type & PERF_SAMPLE_REGS_INTR) {
regs.ax = pebs->ax;
regs.bx = pebs->bx;
regs.cx = pebs->cx;
regs.dx = pebs->dx;
regs.si = pebs->si;
regs.di = pebs->di;
regs.bp = pebs->bp;
regs.sp = pebs->sp;
regs.flags = pebs->flags;
#ifndef CONFIG_X86_32
regs.r8 = pebs->r8;
regs.r9 = pebs->r9;
regs.r10 = pebs->r10;
regs.r11 = pebs->r11;
regs.r12 = pebs->r12;
regs.r13 = pebs->r13;
regs.r14 = pebs->r14;
regs.r15 = pebs->r15;
#endif
}
if (event->attr.precise_ip > 1 && x86_pmu.intel_cap.pebs_format >= 2) { if (event->attr.precise_ip > 1 && x86_pmu.intel_cap.pebs_format >= 2) {
regs.ip = pebs->real_ip; regs.ip = pebs->real_ip;
regs.flags |= PERF_EFLAGS_EXACT; regs.flags |= PERF_EFLAGS_EXACT;

View File

@ -449,7 +449,11 @@ static struct attribute *snbep_uncore_qpi_formats_attr[] = {
static struct uncore_event_desc snbep_uncore_imc_events[] = { static struct uncore_event_desc snbep_uncore_imc_events[] = {
INTEL_UNCORE_EVENT_DESC(clockticks, "event=0xff,umask=0x00"), INTEL_UNCORE_EVENT_DESC(clockticks, "event=0xff,umask=0x00"),
INTEL_UNCORE_EVENT_DESC(cas_count_read, "event=0x04,umask=0x03"), INTEL_UNCORE_EVENT_DESC(cas_count_read, "event=0x04,umask=0x03"),
INTEL_UNCORE_EVENT_DESC(cas_count_read.scale, "6.103515625e-5"),
INTEL_UNCORE_EVENT_DESC(cas_count_read.unit, "MiB"),
INTEL_UNCORE_EVENT_DESC(cas_count_write, "event=0x04,umask=0x0c"), INTEL_UNCORE_EVENT_DESC(cas_count_write, "event=0x04,umask=0x0c"),
INTEL_UNCORE_EVENT_DESC(cas_count_write.scale, "6.103515625e-5"),
INTEL_UNCORE_EVENT_DESC(cas_count_write.unit, "MiB"),
{ /* end: all zeroes */ }, { /* end: all zeroes */ },
}; };
@ -2036,7 +2040,11 @@ static struct intel_uncore_type hswep_uncore_ha = {
static struct uncore_event_desc hswep_uncore_imc_events[] = { static struct uncore_event_desc hswep_uncore_imc_events[] = {
INTEL_UNCORE_EVENT_DESC(clockticks, "event=0x00,umask=0x00"), INTEL_UNCORE_EVENT_DESC(clockticks, "event=0x00,umask=0x00"),
INTEL_UNCORE_EVENT_DESC(cas_count_read, "event=0x04,umask=0x03"), INTEL_UNCORE_EVENT_DESC(cas_count_read, "event=0x04,umask=0x03"),
INTEL_UNCORE_EVENT_DESC(cas_count_read.scale, "6.103515625e-5"),
INTEL_UNCORE_EVENT_DESC(cas_count_read.unit, "MiB"),
INTEL_UNCORE_EVENT_DESC(cas_count_write, "event=0x04,umask=0x0c"), INTEL_UNCORE_EVENT_DESC(cas_count_write, "event=0x04,umask=0x0c"),
INTEL_UNCORE_EVENT_DESC(cas_count_write.scale, "6.103515625e-5"),
INTEL_UNCORE_EVENT_DESC(cas_count_write.unit, "MiB"),
{ /* end: all zeroes */ }, { /* end: all zeroes */ },
}; };

View File

@ -126,6 +126,8 @@ int arch_install_hw_breakpoint(struct perf_event *bp)
*dr7 |= encode_dr7(i, info->len, info->type); *dr7 |= encode_dr7(i, info->len, info->type);
set_debugreg(*dr7, 7); set_debugreg(*dr7, 7);
if (info->mask)
set_dr_addr_mask(info->mask, i);
return 0; return 0;
} }
@ -161,29 +163,8 @@ void arch_uninstall_hw_breakpoint(struct perf_event *bp)
*dr7 &= ~__encode_dr7(i, info->len, info->type); *dr7 &= ~__encode_dr7(i, info->len, info->type);
set_debugreg(*dr7, 7); set_debugreg(*dr7, 7);
} if (info->mask)
set_dr_addr_mask(0, i);
static int get_hbp_len(u8 hbp_len)
{
unsigned int len_in_bytes = 0;
switch (hbp_len) {
case X86_BREAKPOINT_LEN_1:
len_in_bytes = 1;
break;
case X86_BREAKPOINT_LEN_2:
len_in_bytes = 2;
break;
case X86_BREAKPOINT_LEN_4:
len_in_bytes = 4;
break;
#ifdef CONFIG_X86_64
case X86_BREAKPOINT_LEN_8:
len_in_bytes = 8;
break;
#endif
}
return len_in_bytes;
} }
/* /*
@ -196,7 +177,7 @@ int arch_check_bp_in_kernelspace(struct perf_event *bp)
struct arch_hw_breakpoint *info = counter_arch_bp(bp); struct arch_hw_breakpoint *info = counter_arch_bp(bp);
va = info->address; va = info->address;
len = get_hbp_len(info->len); len = bp->attr.bp_len;
return (va >= TASK_SIZE) && ((va + len - 1) >= TASK_SIZE); return (va >= TASK_SIZE) && ((va + len - 1) >= TASK_SIZE);
} }
@ -277,6 +258,8 @@ static int arch_build_bp_info(struct perf_event *bp)
} }
/* Len */ /* Len */
info->mask = 0;
switch (bp->attr.bp_len) { switch (bp->attr.bp_len) {
case HW_BREAKPOINT_LEN_1: case HW_BREAKPOINT_LEN_1:
info->len = X86_BREAKPOINT_LEN_1; info->len = X86_BREAKPOINT_LEN_1;
@ -293,11 +276,17 @@ static int arch_build_bp_info(struct perf_event *bp)
break; break;
#endif #endif
default: default:
return -EINVAL; if (!is_power_of_2(bp->attr.bp_len))
return -EINVAL;
if (!cpu_has_bpext)
return -EOPNOTSUPP;
info->mask = bp->attr.bp_len - 1;
info->len = X86_BREAKPOINT_LEN_1;
} }
return 0; return 0;
} }
/* /*
* Validate the arch-specific HW Breakpoint register settings * Validate the arch-specific HW Breakpoint register settings
*/ */
@ -312,11 +301,11 @@ int arch_validate_hwbkpt_settings(struct perf_event *bp)
if (ret) if (ret)
return ret; return ret;
ret = -EINVAL;
switch (info->len) { switch (info->len) {
case X86_BREAKPOINT_LEN_1: case X86_BREAKPOINT_LEN_1:
align = 0; align = 0;
if (info->mask)
align = info->mask;
break; break;
case X86_BREAKPOINT_LEN_2: case X86_BREAKPOINT_LEN_2:
align = 1; align = 1;
@ -330,7 +319,7 @@ int arch_validate_hwbkpt_settings(struct perf_event *bp)
break; break;
#endif #endif
default: default:
return ret; WARN_ON_ONCE(1);
} }
/* /*

View File

@ -79,7 +79,7 @@ struct perf_branch_stack {
struct perf_branch_entry entries[0]; struct perf_branch_entry entries[0];
}; };
struct perf_regs_user { struct perf_regs {
__u64 abi; __u64 abi;
struct pt_regs *regs; struct pt_regs *regs;
}; };
@ -580,34 +580,40 @@ extern u64 perf_event_read_value(struct perf_event *event,
struct perf_sample_data { struct perf_sample_data {
u64 type; /*
* Fields set by perf_sample_data_init(), group so as to
* minimize the cachelines touched.
*/
u64 addr;
struct perf_raw_record *raw;
struct perf_branch_stack *br_stack;
u64 period;
u64 weight;
u64 txn;
union perf_mem_data_src data_src;
/*
* The other fields, optionally {set,used} by
* perf_{prepare,output}_sample().
*/
u64 type;
u64 ip; u64 ip;
struct { struct {
u32 pid; u32 pid;
u32 tid; u32 tid;
} tid_entry; } tid_entry;
u64 time; u64 time;
u64 addr;
u64 id; u64 id;
u64 stream_id; u64 stream_id;
struct { struct {
u32 cpu; u32 cpu;
u32 reserved; u32 reserved;
} cpu_entry; } cpu_entry;
u64 period;
union perf_mem_data_src data_src;
struct perf_callchain_entry *callchain; struct perf_callchain_entry *callchain;
struct perf_raw_record *raw; struct perf_regs regs_user;
struct perf_branch_stack *br_stack; struct perf_regs regs_intr;
struct perf_regs_user regs_user;
u64 stack_user_size; u64 stack_user_size;
u64 weight; } ____cacheline_aligned;
/*
* Transaction flags for abort events:
*/
u64 txn;
};
/* default value for data source */ /* default value for data source */
#define PERF_MEM_NA (PERF_MEM_S(OP, NA) |\ #define PERF_MEM_NA (PERF_MEM_S(OP, NA) |\
@ -624,9 +630,6 @@ static inline void perf_sample_data_init(struct perf_sample_data *data,
data->raw = NULL; data->raw = NULL;
data->br_stack = NULL; data->br_stack = NULL;
data->period = period; data->period = period;
data->regs_user.abi = PERF_SAMPLE_REGS_ABI_NONE;
data->regs_user.regs = NULL;
data->stack_user_size = 0;
data->weight = 0; data->weight = 0;
data->data_src.val = PERF_MEM_NA; data->data_src.val = PERF_MEM_NA;
data->txn = 0; data->txn = 0;

View File

@ -137,8 +137,9 @@ enum perf_event_sample_format {
PERF_SAMPLE_DATA_SRC = 1U << 15, PERF_SAMPLE_DATA_SRC = 1U << 15,
PERF_SAMPLE_IDENTIFIER = 1U << 16, PERF_SAMPLE_IDENTIFIER = 1U << 16,
PERF_SAMPLE_TRANSACTION = 1U << 17, PERF_SAMPLE_TRANSACTION = 1U << 17,
PERF_SAMPLE_REGS_INTR = 1U << 18,
PERF_SAMPLE_MAX = 1U << 18, /* non-ABI */ PERF_SAMPLE_MAX = 1U << 19, /* non-ABI */
}; };
/* /*
@ -238,6 +239,7 @@ enum perf_event_read_format {
#define PERF_ATTR_SIZE_VER2 80 /* add: branch_sample_type */ #define PERF_ATTR_SIZE_VER2 80 /* add: branch_sample_type */
#define PERF_ATTR_SIZE_VER3 96 /* add: sample_regs_user */ #define PERF_ATTR_SIZE_VER3 96 /* add: sample_regs_user */
/* add: sample_stack_user */ /* add: sample_stack_user */
#define PERF_ATTR_SIZE_VER4 104 /* add: sample_regs_intr */
/* /*
* Hardware event_id to monitor via a performance monitoring event: * Hardware event_id to monitor via a performance monitoring event:
@ -334,6 +336,15 @@ struct perf_event_attr {
/* Align to u64. */ /* Align to u64. */
__u32 __reserved_2; __u32 __reserved_2;
/*
* Defines set of regs to dump for each sample
* state captured on:
* - precise = 0: PMU interrupt
* - precise > 0: sampled instruction
*
* See asm/perf_regs.h for details.
*/
__u64 sample_regs_intr;
}; };
#define perf_flags(attr) (*(&(attr)->read_format + 1)) #define perf_flags(attr) (*(&(attr)->read_format + 1))
@ -686,6 +697,8 @@ enum perf_event_type {
* { u64 weight; } && PERF_SAMPLE_WEIGHT * { u64 weight; } && PERF_SAMPLE_WEIGHT
* { u64 data_src; } && PERF_SAMPLE_DATA_SRC * { u64 data_src; } && PERF_SAMPLE_DATA_SRC
* { u64 transaction; } && PERF_SAMPLE_TRANSACTION * { u64 transaction; } && PERF_SAMPLE_TRANSACTION
* { u64 abi; # enum perf_sample_regs_abi
* u64 regs[weight(mask)]; } && PERF_SAMPLE_REGS_INTR
* }; * };
*/ */
PERF_RECORD_SAMPLE = 9, PERF_RECORD_SAMPLE = 9,

View File

@ -4460,7 +4460,7 @@ perf_output_sample_regs(struct perf_output_handle *handle,
} }
} }
static void perf_sample_regs_user(struct perf_regs_user *regs_user, static void perf_sample_regs_user(struct perf_regs *regs_user,
struct pt_regs *regs) struct pt_regs *regs)
{ {
if (!user_mode(regs)) { if (!user_mode(regs)) {
@ -4471,11 +4471,22 @@ static void perf_sample_regs_user(struct perf_regs_user *regs_user,
} }
if (regs) { if (regs) {
regs_user->regs = regs;
regs_user->abi = perf_reg_abi(current); regs_user->abi = perf_reg_abi(current);
regs_user->regs = regs;
} else {
regs_user->abi = PERF_SAMPLE_REGS_ABI_NONE;
regs_user->regs = NULL;
} }
} }
static void perf_sample_regs_intr(struct perf_regs *regs_intr,
struct pt_regs *regs)
{
regs_intr->regs = regs;
regs_intr->abi = perf_reg_abi(current);
}
/* /*
* Get remaining task size from user stack pointer. * Get remaining task size from user stack pointer.
* *
@ -4857,6 +4868,23 @@ void perf_output_sample(struct perf_output_handle *handle,
if (sample_type & PERF_SAMPLE_TRANSACTION) if (sample_type & PERF_SAMPLE_TRANSACTION)
perf_output_put(handle, data->txn); perf_output_put(handle, data->txn);
if (sample_type & PERF_SAMPLE_REGS_INTR) {
u64 abi = data->regs_intr.abi;
/*
* If there are no regs to dump, notice it through
* first u64 being zero (PERF_SAMPLE_REGS_ABI_NONE).
*/
perf_output_put(handle, abi);
if (abi) {
u64 mask = event->attr.sample_regs_intr;
perf_output_sample_regs(handle,
data->regs_intr.regs,
mask);
}
}
if (!event->attr.watermark) { if (!event->attr.watermark) {
int wakeup_events = event->attr.wakeup_events; int wakeup_events = event->attr.wakeup_events;
@ -4922,12 +4950,13 @@ void perf_prepare_sample(struct perf_event_header *header,
header->size += size; header->size += size;
} }
if (sample_type & (PERF_SAMPLE_REGS_USER | PERF_SAMPLE_STACK_USER))
perf_sample_regs_user(&data->regs_user, regs);
if (sample_type & PERF_SAMPLE_REGS_USER) { if (sample_type & PERF_SAMPLE_REGS_USER) {
/* regs dump ABI info */ /* regs dump ABI info */
int size = sizeof(u64); int size = sizeof(u64);
perf_sample_regs_user(&data->regs_user, regs);
if (data->regs_user.regs) { if (data->regs_user.regs) {
u64 mask = event->attr.sample_regs_user; u64 mask = event->attr.sample_regs_user;
size += hweight64(mask) * sizeof(u64); size += hweight64(mask) * sizeof(u64);
@ -4943,15 +4972,11 @@ void perf_prepare_sample(struct perf_event_header *header,
* in case new sample type is added, because we could eat * in case new sample type is added, because we could eat
* up the rest of the sample size. * up the rest of the sample size.
*/ */
struct perf_regs_user *uregs = &data->regs_user;
u16 stack_size = event->attr.sample_stack_user; u16 stack_size = event->attr.sample_stack_user;
u16 size = sizeof(u64); u16 size = sizeof(u64);
if (!uregs->abi)
perf_sample_regs_user(uregs, regs);
stack_size = perf_sample_ustack_size(stack_size, header->size, stack_size = perf_sample_ustack_size(stack_size, header->size,
uregs->regs); data->regs_user.regs);
/* /*
* If there is something to dump, add space for the dump * If there is something to dump, add space for the dump
@ -4964,6 +4989,21 @@ void perf_prepare_sample(struct perf_event_header *header,
data->stack_user_size = stack_size; data->stack_user_size = stack_size;
header->size += size; header->size += size;
} }
if (sample_type & PERF_SAMPLE_REGS_INTR) {
/* regs dump ABI info */
int size = sizeof(u64);
perf_sample_regs_intr(&data->regs_intr, regs);
if (data->regs_intr.regs) {
u64 mask = event->attr.sample_regs_intr;
size += hweight64(mask) * sizeof(u64);
}
header->size += size;
}
} }
static void perf_event_output(struct perf_event *event, static void perf_event_output(struct perf_event *event,
@ -7151,6 +7191,8 @@ static int perf_copy_attr(struct perf_event_attr __user *uattr,
ret = -EINVAL; ret = -EINVAL;
} }
if (attr->sample_type & PERF_SAMPLE_REGS_INTR)
ret = perf_reg_validate(attr->sample_regs_intr);
out: out:
return ret; return ret;

View File

@ -2,6 +2,8 @@ PERF-CFLAGS
PERF-GUI-VARS PERF-GUI-VARS
PERF-VERSION-FILE PERF-VERSION-FILE
perf perf
perf-read-vdso32
perf-read-vdsox32
perf-help perf-help
perf-record perf-record
perf-report perf-report

View File

@ -33,12 +33,15 @@ OPTIONS
- a raw PMU event (eventsel+umask) in the form of rNNN where NNN is a - a raw PMU event (eventsel+umask) in the form of rNNN where NNN is a
hexadecimal event descriptor. hexadecimal event descriptor.
- a hardware breakpoint event in the form of '\mem:addr[:access]' - a hardware breakpoint event in the form of '\mem:addr[/len][:access]'
where addr is the address in memory you want to break in. where addr is the address in memory you want to break in.
Access is the memory access type (read, write, execute) it can Access is the memory access type (read, write, execute) it can
be passed as follows: '\mem:addr[:[r][w][x]]'. be passed as follows: '\mem:addr[:[r][w][x]]'. len is the range,
number of bytes from specified addr, which the breakpoint will cover.
If you want to profile read-write accesses in 0x1000, just set If you want to profile read-write accesses in 0x1000, just set
'mem:0x1000:rw'. 'mem:0x1000:rw'.
If you want to profile write accesses in [0x1000~1008), just set
'mem:0x1000/8:w'.
--filter=<filter>:: --filter=<filter>::
Event filter. Event filter.
@ -214,6 +217,12 @@ if combined with -a or -C options.
After starting the program, wait msecs before measuring. This is useful to After starting the program, wait msecs before measuring. This is useful to
filter out the startup phase of the program, which is often very different. filter out the startup phase of the program, which is often very different.
-I::
--intr-regs::
Capture machine state (registers) at interrupt, i.e., on counter overflows for
each sample. List of captured registers depends on the architecture. This option
is off by default.
SEE ALSO SEE ALSO
-------- --------
linkperf:perf-stat[1], linkperf:perf-list[1] linkperf:perf-stat[1], linkperf:perf-list[1]

View File

@ -60,6 +60,15 @@ include config/utilities.mak
# #
# Define NO_LIBDW_DWARF_UNWIND if you do not want libdw support # Define NO_LIBDW_DWARF_UNWIND if you do not want libdw support
# for dwarf backtrace post unwind. # for dwarf backtrace post unwind.
#
# Define NO_PERF_READ_VDSO32 if you do not want to build perf-read-vdso32
# for reading the 32-bit compatibility VDSO in 64-bit mode
#
# Define NO_PERF_READ_VDSOX32 if you do not want to build perf-read-vdsox32
# for reading the x32 mode 32-bit compatibility VDSO in 64-bit mode
#
# Define NO_ZLIB if you do not want to support compressed kernel modules
ifeq ($(srctree),) ifeq ($(srctree),)
srctree := $(patsubst %/,%,$(dir $(shell pwd))) srctree := $(patsubst %/,%,$(dir $(shell pwd)))
@ -171,11 +180,16 @@ $(OUTPUT)python/perf.so: $(PYTHON_EXT_SRCS) $(PYTHON_EXT_DEPS)
SCRIPTS = $(patsubst %.sh,%,$(SCRIPT_SH)) SCRIPTS = $(patsubst %.sh,%,$(SCRIPT_SH))
#
# Single 'perf' binary right now:
#
PROGRAMS += $(OUTPUT)perf PROGRAMS += $(OUTPUT)perf
ifndef NO_PERF_READ_VDSO32
PROGRAMS += $(OUTPUT)perf-read-vdso32
endif
ifndef NO_PERF_READ_VDSOX32
PROGRAMS += $(OUTPUT)perf-read-vdsox32
endif
# what 'all' will build and 'install' will install, in perfexecdir # what 'all' will build and 'install' will install, in perfexecdir
ALL_PROGRAMS = $(PROGRAMS) $(SCRIPTS) ALL_PROGRAMS = $(PROGRAMS) $(SCRIPTS)
@ -247,12 +261,14 @@ LIB_H += util/annotate.h
LIB_H += util/cache.h LIB_H += util/cache.h
LIB_H += util/callchain.h LIB_H += util/callchain.h
LIB_H += util/build-id.h LIB_H += util/build-id.h
LIB_H += util/db-export.h
LIB_H += util/debug.h LIB_H += util/debug.h
LIB_H += util/pmu.h LIB_H += util/pmu.h
LIB_H += util/event.h LIB_H += util/event.h
LIB_H += util/evsel.h LIB_H += util/evsel.h
LIB_H += util/evlist.h LIB_H += util/evlist.h
LIB_H += util/exec_cmd.h LIB_H += util/exec_cmd.h
LIB_H += util/find-vdso-map.c
LIB_H += util/levenshtein.h LIB_H += util/levenshtein.h
LIB_H += util/machine.h LIB_H += util/machine.h
LIB_H += util/map.h LIB_H += util/map.h
@ -304,6 +320,7 @@ LIB_H += ui/util.h
LIB_H += ui/ui.h LIB_H += ui/ui.h
LIB_H += util/data.h LIB_H += util/data.h
LIB_H += util/kvm-stat.h LIB_H += util/kvm-stat.h
LIB_H += util/thread-stack.h
LIB_OBJS += $(OUTPUT)util/abspath.o LIB_OBJS += $(OUTPUT)util/abspath.o
LIB_OBJS += $(OUTPUT)util/alias.o LIB_OBJS += $(OUTPUT)util/alias.o
@ -311,6 +328,7 @@ LIB_OBJS += $(OUTPUT)util/annotate.o
LIB_OBJS += $(OUTPUT)util/build-id.o LIB_OBJS += $(OUTPUT)util/build-id.o
LIB_OBJS += $(OUTPUT)util/config.o LIB_OBJS += $(OUTPUT)util/config.o
LIB_OBJS += $(OUTPUT)util/ctype.o LIB_OBJS += $(OUTPUT)util/ctype.o
LIB_OBJS += $(OUTPUT)util/db-export.o
LIB_OBJS += $(OUTPUT)util/pmu.o LIB_OBJS += $(OUTPUT)util/pmu.o
LIB_OBJS += $(OUTPUT)util/environment.o LIB_OBJS += $(OUTPUT)util/environment.o
LIB_OBJS += $(OUTPUT)util/event.o LIB_OBJS += $(OUTPUT)util/event.o
@ -380,6 +398,7 @@ LIB_OBJS += $(OUTPUT)util/srcline.o
LIB_OBJS += $(OUTPUT)util/data.o LIB_OBJS += $(OUTPUT)util/data.o
LIB_OBJS += $(OUTPUT)util/tsc.o LIB_OBJS += $(OUTPUT)util/tsc.o
LIB_OBJS += $(OUTPUT)util/cloexec.o LIB_OBJS += $(OUTPUT)util/cloexec.o
LIB_OBJS += $(OUTPUT)util/thread-stack.o
LIB_OBJS += $(OUTPUT)ui/setup.o LIB_OBJS += $(OUTPUT)ui/setup.o
LIB_OBJS += $(OUTPUT)ui/helpline.o LIB_OBJS += $(OUTPUT)ui/helpline.o
@ -478,8 +497,6 @@ ifneq ($(OUTPUT),)
endif endif
ifdef NO_LIBELF ifdef NO_LIBELF
EXTLIBS := $(filter-out -lelf,$(EXTLIBS))
# Remove ELF/DWARF dependent codes # Remove ELF/DWARF dependent codes
LIB_OBJS := $(filter-out $(OUTPUT)util/symbol-elf.o,$(LIB_OBJS)) LIB_OBJS := $(filter-out $(OUTPUT)util/symbol-elf.o,$(LIB_OBJS))
LIB_OBJS := $(filter-out $(OUTPUT)util/dwarf-aux.o,$(LIB_OBJS)) LIB_OBJS := $(filter-out $(OUTPUT)util/dwarf-aux.o,$(LIB_OBJS))
@ -568,6 +585,10 @@ ifndef NO_LIBNUMA
BUILTIN_OBJS += $(OUTPUT)bench/numa.o BUILTIN_OBJS += $(OUTPUT)bench/numa.o
endif endif
ifndef NO_ZLIB
LIB_OBJS += $(OUTPUT)util/zlib.o
endif
ifdef ASCIIDOC8 ifdef ASCIIDOC8
export ASCIIDOC8 export ASCIIDOC8
endif endif
@ -732,6 +753,16 @@ $(OUTPUT)scripts/python/Perf-Trace-Util/Context.o: scripts/python/Perf-Trace-Uti
$(OUTPUT)perf-%: %.o $(PERFLIBS) $(OUTPUT)perf-%: %.o $(PERFLIBS)
$(QUIET_LINK)$(CC) $(CFLAGS) -o $@ $(LDFLAGS) $(filter %.o,$^) $(LIBS) $(QUIET_LINK)$(CC) $(CFLAGS) -o $@ $(LDFLAGS) $(filter %.o,$^) $(LIBS)
ifndef NO_PERF_READ_VDSO32
$(OUTPUT)perf-read-vdso32: perf-read-vdso.c util/find-vdso-map.c
$(QUIET_CC)$(CC) -m32 $(filter -static,$(LDFLAGS)) -Wall -Werror -o $@ perf-read-vdso.c
endif
ifndef NO_PERF_READ_VDSOX32
$(OUTPUT)perf-read-vdsox32: perf-read-vdso.c util/find-vdso-map.c
$(QUIET_CC)$(CC) -mx32 $(filter -static,$(LDFLAGS)) -Wall -Werror -o $@ perf-read-vdso.c
endif
$(LIB_OBJS) $(BUILTIN_OBJS): $(LIB_H) $(LIB_OBJS) $(BUILTIN_OBJS): $(LIB_H)
$(patsubst perf-%,%.o,$(PROGRAMS)): $(LIB_H) $(wildcard */*.h) $(patsubst perf-%,%.o,$(PROGRAMS)): $(LIB_H) $(wildcard */*.h)
@ -876,6 +907,14 @@ install-bin: all install-gtk
$(INSTALL) -d -m 755 '$(DESTDIR_SQ)$(bindir_SQ)'; \ $(INSTALL) -d -m 755 '$(DESTDIR_SQ)$(bindir_SQ)'; \
$(INSTALL) $(OUTPUT)perf '$(DESTDIR_SQ)$(bindir_SQ)'; \ $(INSTALL) $(OUTPUT)perf '$(DESTDIR_SQ)$(bindir_SQ)'; \
$(LN) '$(DESTDIR_SQ)$(bindir_SQ)/perf' '$(DESTDIR_SQ)$(bindir_SQ)/trace' $(LN) '$(DESTDIR_SQ)$(bindir_SQ)/perf' '$(DESTDIR_SQ)$(bindir_SQ)/trace'
ifndef NO_PERF_READ_VDSO32
$(call QUIET_INSTALL, perf-read-vdso32) \
$(INSTALL) $(OUTPUT)perf-read-vdso32 '$(DESTDIR_SQ)$(bindir_SQ)';
endif
ifndef NO_PERF_READ_VDSOX32
$(call QUIET_INSTALL, perf-read-vdsox32) \
$(INSTALL) $(OUTPUT)perf-read-vdsox32 '$(DESTDIR_SQ)$(bindir_SQ)';
endif
$(call QUIET_INSTALL, libexec) \ $(call QUIET_INSTALL, libexec) \
$(INSTALL) -d -m 755 '$(DESTDIR_SQ)$(perfexec_instdir_SQ)' $(INSTALL) -d -m 755 '$(DESTDIR_SQ)$(perfexec_instdir_SQ)'
$(call QUIET_INSTALL, perf-archive) \ $(call QUIET_INSTALL, perf-archive) \
@ -928,7 +967,7 @@ config-clean:
clean: $(LIBTRACEEVENT)-clean $(LIBAPIKFS)-clean config-clean clean: $(LIBTRACEEVENT)-clean $(LIBAPIKFS)-clean config-clean
$(call QUIET_CLEAN, core-objs) $(RM) $(LIB_OBJS) $(BUILTIN_OBJS) $(LIB_FILE) $(OUTPUT)perf-archive $(OUTPUT)perf-with-kcore $(OUTPUT)perf.o $(LANG_BINDINGS) $(GTK_OBJS) $(call QUIET_CLEAN, core-objs) $(RM) $(LIB_OBJS) $(BUILTIN_OBJS) $(LIB_FILE) $(OUTPUT)perf-archive $(OUTPUT)perf-with-kcore $(OUTPUT)perf.o $(LANG_BINDINGS) $(GTK_OBJS)
$(call QUIET_CLEAN, core-progs) $(RM) $(ALL_PROGRAMS) perf $(call QUIET_CLEAN, core-progs) $(RM) $(ALL_PROGRAMS) perf perf-read-vdso32 perf-read-vdsox32
$(call QUIET_CLEAN, core-gen) $(RM) *.spec *.pyc *.pyo */*.pyc */*.pyo $(OUTPUT)common-cmds.h TAGS tags cscope* $(OUTPUT)PERF-VERSION-FILE $(OUTPUT)PERF-CFLAGS $(OUTPUT)PERF-FEATURES $(OUTPUT)util/*-bison* $(OUTPUT)util/*-flex* $(call QUIET_CLEAN, core-gen) $(RM) *.spec *.pyc *.pyo */*.pyc */*.pyo $(OUTPUT)common-cmds.h TAGS tags cscope* $(OUTPUT)PERF-VERSION-FILE $(OUTPUT)PERF-CFLAGS $(OUTPUT)PERF-FEATURES $(OUTPUT)util/*-bison* $(OUTPUT)util/*-flex*
$(QUIET_SUBDIR0)Documentation $(QUIET_SUBDIR1) clean $(QUIET_SUBDIR0)Documentation $(QUIET_SUBDIR1) clean
$(python-clean) $(python-clean)

View File

@ -145,7 +145,7 @@ static Dwarf_Frame *get_dwarf_frame(Dwfl_Module *mod, Dwarf_Addr pc)
* yet used) * yet used)
* -1 in case of errors * -1 in case of errors
*/ */
static int check_return_addr(const char *exec_file, Dwarf_Addr pc) static int check_return_addr(struct dso *dso, Dwarf_Addr pc)
{ {
int rc = -1; int rc = -1;
Dwfl *dwfl; Dwfl *dwfl;
@ -156,15 +156,27 @@ static int check_return_addr(const char *exec_file, Dwarf_Addr pc)
Dwarf_Addr end = pc; Dwarf_Addr end = pc;
bool signalp; bool signalp;
dwfl = dwfl_begin(&offline_callbacks); dwfl = dso->dwfl;
if (!dwfl) {
pr_debug("dwfl_begin() failed: %s\n", dwarf_errmsg(-1));
return -1;
}
if (dwfl_report_offline(dwfl, "", exec_file, -1) == NULL) { if (!dwfl) {
pr_debug("dwfl_report_offline() failed %s\n", dwarf_errmsg(-1)); dwfl = dwfl_begin(&offline_callbacks);
goto out; if (!dwfl) {
pr_debug("dwfl_begin() failed: %s\n", dwarf_errmsg(-1));
return -1;
}
if (dwfl_report_offline(dwfl, "", dso->long_name, -1) == NULL) {
pr_debug("dwfl_report_offline() failed %s\n",
dwarf_errmsg(-1));
/*
* We normally cache the DWARF debug info and never
* call dwfl_end(). But to prevent fd leak, free in
* case of error.
*/
dwfl_end(dwfl);
goto out;
}
dso->dwfl = dwfl;
} }
mod = dwfl_addrmodule(dwfl, pc); mod = dwfl_addrmodule(dwfl, pc);
@ -194,7 +206,6 @@ static int check_return_addr(const char *exec_file, Dwarf_Addr pc)
rc = check_return_reg(ra_regno, frame); rc = check_return_reg(ra_regno, frame);
out: out:
dwfl_end(dwfl);
return rc; return rc;
} }
@ -221,8 +232,7 @@ out:
* index: of callchain entry that needs to be ignored (if any) * index: of callchain entry that needs to be ignored (if any)
* -1 if no entry needs to be ignored or in case of errors * -1 if no entry needs to be ignored or in case of errors
*/ */
int arch_skip_callchain_idx(struct machine *machine, struct thread *thread, int arch_skip_callchain_idx(struct thread *thread, struct ip_callchain *chain)
struct ip_callchain *chain)
{ {
struct addr_location al; struct addr_location al;
struct dso *dso = NULL; struct dso *dso = NULL;
@ -235,7 +245,7 @@ int arch_skip_callchain_idx(struct machine *machine, struct thread *thread,
ip = chain->ips[2]; ip = chain->ips[2];
thread__find_addr_location(thread, machine, PERF_RECORD_MISC_USER, thread__find_addr_location(thread, PERF_RECORD_MISC_USER,
MAP__FUNCTION, ip, &al); MAP__FUNCTION, ip, &al);
if (al.map) if (al.map)
@ -246,7 +256,7 @@ int arch_skip_callchain_idx(struct machine *machine, struct thread *thread,
return skip_slot; return skip_slot;
} }
rc = check_return_addr(dso->long_name, ip); rc = check_return_addr(dso, ip);
pr_debug("DSO %s, nr %" PRIx64 ", ip 0x%" PRIx64 "rc %d\n", pr_debug("DSO %s, nr %" PRIx64 ", ip 0x%" PRIx64 "rc %d\n",
dso->long_name, chain->nr, ip, rc); dso->long_name, chain->nr, ip, rc);

View File

@ -357,6 +357,7 @@ static int diff__process_sample_event(struct perf_tool *tool __maybe_unused,
static struct perf_tool tool = { static struct perf_tool tool = {
.sample = diff__process_sample_event, .sample = diff__process_sample_event,
.mmap = perf_event__process_mmap, .mmap = perf_event__process_mmap,
.mmap2 = perf_event__process_mmap2,
.comm = perf_event__process_comm, .comm = perf_event__process_comm,
.exit = perf_event__process_exit, .exit = perf_event__process_exit,
.fork = perf_event__process_fork, .fork = perf_event__process_fork,

View File

@ -217,8 +217,7 @@ static int perf_event__inject_buildid(struct perf_tool *tool,
goto repipe; goto repipe;
} }
thread__find_addr_map(thread, machine, cpumode, MAP__FUNCTION, thread__find_addr_map(thread, cpumode, MAP__FUNCTION, sample->ip, &al);
sample->ip, &al);
if (al.map != NULL) { if (al.map != NULL) {
if (!al.map->dso->hit) { if (!al.map->dso->hit) {
@ -410,6 +409,7 @@ int cmd_inject(int argc, const char **argv, const char *prefix __maybe_unused)
.tracing_data = perf_event__repipe_op2_synth, .tracing_data = perf_event__repipe_op2_synth,
.finished_round = perf_event__repipe_op2_synth, .finished_round = perf_event__repipe_op2_synth,
.build_id = perf_event__repipe_op2_synth, .build_id = perf_event__repipe_op2_synth,
.id_index = perf_event__repipe_op2_synth,
}, },
.input_name = "-", .input_name = "-",
.samples = LIST_HEAD_INIT(inject.samples), .samples = LIST_HEAD_INIT(inject.samples),

View File

@ -1132,6 +1132,10 @@ kvm_events_record(struct perf_kvm_stat *kvm, int argc, const char **argv)
"-m", "1024", "-m", "1024",
"-c", "1", "-c", "1",
}; };
const char * const kvm_stat_record_usage[] = {
"perf kvm stat record [<options>]",
NULL
};
const char * const *events_tp; const char * const *events_tp;
events_tp_size = 0; events_tp_size = 0;
@ -1159,6 +1163,27 @@ kvm_events_record(struct perf_kvm_stat *kvm, int argc, const char **argv)
for (j = 1; j < (unsigned int)argc; j++, i++) for (j = 1; j < (unsigned int)argc; j++, i++)
rec_argv[i] = argv[j]; rec_argv[i] = argv[j];
set_option_flag(record_options, 'e', "event", PARSE_OPT_HIDDEN);
set_option_flag(record_options, 0, "filter", PARSE_OPT_HIDDEN);
set_option_flag(record_options, 'R', "raw-samples", PARSE_OPT_HIDDEN);
set_option_flag(record_options, 'F', "freq", PARSE_OPT_DISABLED);
set_option_flag(record_options, 0, "group", PARSE_OPT_DISABLED);
set_option_flag(record_options, 'g', NULL, PARSE_OPT_DISABLED);
set_option_flag(record_options, 0, "call-graph", PARSE_OPT_DISABLED);
set_option_flag(record_options, 'd', "data", PARSE_OPT_DISABLED);
set_option_flag(record_options, 'T', "timestamp", PARSE_OPT_DISABLED);
set_option_flag(record_options, 'P', "period", PARSE_OPT_DISABLED);
set_option_flag(record_options, 'n', "no-samples", PARSE_OPT_DISABLED);
set_option_flag(record_options, 'N', "no-buildid-cache", PARSE_OPT_DISABLED);
set_option_flag(record_options, 'B', "no-buildid", PARSE_OPT_DISABLED);
set_option_flag(record_options, 'G', "cgroup", PARSE_OPT_DISABLED);
set_option_flag(record_options, 'b', "branch-any", PARSE_OPT_DISABLED);
set_option_flag(record_options, 'j', "branch-filter", PARSE_OPT_DISABLED);
set_option_flag(record_options, 'W', "weight", PARSE_OPT_DISABLED);
set_option_flag(record_options, 0, "transaction", PARSE_OPT_DISABLED);
record_usage = kvm_stat_record_usage;
return cmd_record(i, rec_argv, NULL); return cmd_record(i, rec_argv, NULL);
} }

View File

@ -55,6 +55,7 @@ static struct {
bool show_funcs; bool show_funcs;
bool mod_events; bool mod_events;
bool uprobes; bool uprobes;
bool quiet;
int nevents; int nevents;
struct perf_probe_event events[MAX_PROBES]; struct perf_probe_event events[MAX_PROBES];
struct strlist *dellist; struct strlist *dellist;
@ -312,9 +313,11 @@ __cmd_probe(int argc, const char **argv, const char *prefix __maybe_unused)
#endif #endif
NULL NULL
}; };
const struct option options[] = { struct option options[] = {
OPT_INCR('v', "verbose", &verbose, OPT_INCR('v', "verbose", &verbose,
"be more verbose (show parsed arguments, etc)"), "be more verbose (show parsed arguments, etc)"),
OPT_BOOLEAN('q', "quiet", &params.quiet,
"be quiet (do not show any mesages)"),
OPT_BOOLEAN('l', "list", &params.list_events, OPT_BOOLEAN('l', "list", &params.list_events,
"list up current probe events"), "list up current probe events"),
OPT_CALLBACK('d', "del", NULL, "[GROUP:]EVENT", "delete a probe event.", OPT_CALLBACK('d', "del", NULL, "[GROUP:]EVENT", "delete a probe event.",
@ -382,6 +385,14 @@ __cmd_probe(int argc, const char **argv, const char *prefix __maybe_unused)
}; };
int ret; int ret;
set_option_flag(options, 'a', "add", PARSE_OPT_EXCLUSIVE);
set_option_flag(options, 'd', "del", PARSE_OPT_EXCLUSIVE);
set_option_flag(options, 'l', "list", PARSE_OPT_EXCLUSIVE);
#ifdef HAVE_DWARF_SUPPORT
set_option_flag(options, 'L', "line", PARSE_OPT_EXCLUSIVE);
set_option_flag(options, 'V', "vars", PARSE_OPT_EXCLUSIVE);
#endif
argc = parse_options(argc, argv, options, probe_usage, argc = parse_options(argc, argv, options, probe_usage,
PARSE_OPT_STOP_AT_NON_OPTION); PARSE_OPT_STOP_AT_NON_OPTION);
if (argc > 0) { if (argc > 0) {
@ -396,6 +407,14 @@ __cmd_probe(int argc, const char **argv, const char *prefix __maybe_unused)
} }
} }
if (params.quiet) {
if (verbose != 0) {
pr_err(" Error: -v and -q are exclusive.\n");
return -EINVAL;
}
verbose = -1;
}
if (params.max_probe_points == 0) if (params.max_probe_points == 0)
params.max_probe_points = MAX_PROBES; params.max_probe_points = MAX_PROBES;
@ -409,22 +428,6 @@ __cmd_probe(int argc, const char **argv, const char *prefix __maybe_unused)
symbol_conf.try_vmlinux_path = (symbol_conf.vmlinux_name == NULL); symbol_conf.try_vmlinux_path = (symbol_conf.vmlinux_name == NULL);
if (params.list_events) { if (params.list_events) {
if (params.mod_events) {
pr_err(" Error: Don't use --list with --add/--del.\n");
usage_with_options(probe_usage, options);
}
if (params.show_lines) {
pr_err(" Error: Don't use --list with --line.\n");
usage_with_options(probe_usage, options);
}
if (params.show_vars) {
pr_err(" Error: Don't use --list with --vars.\n");
usage_with_options(probe_usage, options);
}
if (params.show_funcs) {
pr_err(" Error: Don't use --list with --funcs.\n");
usage_with_options(probe_usage, options);
}
if (params.uprobes) { if (params.uprobes) {
pr_warning(" Error: Don't use --list with --exec.\n"); pr_warning(" Error: Don't use --list with --exec.\n");
usage_with_options(probe_usage, options); usage_with_options(probe_usage, options);
@ -435,19 +438,6 @@ __cmd_probe(int argc, const char **argv, const char *prefix __maybe_unused)
return ret; return ret;
} }
if (params.show_funcs) { if (params.show_funcs) {
if (params.nevents != 0 || params.dellist) {
pr_err(" Error: Don't use --funcs with"
" --add/--del.\n");
usage_with_options(probe_usage, options);
}
if (params.show_lines) {
pr_err(" Error: Don't use --funcs with --line.\n");
usage_with_options(probe_usage, options);
}
if (params.show_vars) {
pr_err(" Error: Don't use --funcs with --vars.\n");
usage_with_options(probe_usage, options);
}
if (!params.filter) if (!params.filter)
params.filter = strfilter__new(DEFAULT_FUNC_FILTER, params.filter = strfilter__new(DEFAULT_FUNC_FILTER,
NULL); NULL);
@ -462,16 +452,6 @@ __cmd_probe(int argc, const char **argv, const char *prefix __maybe_unused)
#ifdef HAVE_DWARF_SUPPORT #ifdef HAVE_DWARF_SUPPORT
if (params.show_lines) { if (params.show_lines) {
if (params.mod_events) {
pr_err(" Error: Don't use --line with"
" --add/--del.\n");
usage_with_options(probe_usage, options);
}
if (params.show_vars) {
pr_err(" Error: Don't use --line with --vars.\n");
usage_with_options(probe_usage, options);
}
ret = show_line_range(&params.line_range, params.target, ret = show_line_range(&params.line_range, params.target,
params.uprobes); params.uprobes);
if (ret < 0) if (ret < 0)
@ -479,11 +459,6 @@ __cmd_probe(int argc, const char **argv, const char *prefix __maybe_unused)
return ret; return ret;
} }
if (params.show_vars) { if (params.show_vars) {
if (params.mod_events) {
pr_err(" Error: Don't use --vars with"
" --add/--del.\n");
usage_with_options(probe_usage, options);
}
if (!params.filter) if (!params.filter)
params.filter = strfilter__new(DEFAULT_VAR_FILTER, params.filter = strfilter__new(DEFAULT_VAR_FILTER,
NULL); NULL);

View File

@ -200,6 +200,17 @@ static int process_buildids(struct record *rec)
if (size == 0) if (size == 0)
return 0; return 0;
/*
* During this process, it'll load kernel map and replace the
* dso->long_name to a real pathname it found. In this case
* we prefer the vmlinux path like
* /lib/modules/3.16.4/build/vmlinux
*
* rather than build-id path (in debug directory).
* $HOME/.debug/.build-id/f0/6e17aa50adf4d00b88925e03775de107611551
*/
symbol_conf.ignore_vmlinux_buildid = true;
return __perf_session__process_events(session, start, return __perf_session__process_events(session, start,
size - start, size - start,
size, &build_id__mark_dso_hit_ops); size, &build_id__mark_dso_hit_ops);
@ -680,11 +691,12 @@ static int perf_record_config(const char *var, const char *value, void *cb)
return perf_default_config(var, value, cb); return perf_default_config(var, value, cb);
} }
static const char * const record_usage[] = { static const char * const __record_usage[] = {
"perf record [<options>] [<command>]", "perf record [<options>] [<command>]",
"perf record [<options>] -- <command> [<options>]", "perf record [<options>] -- <command> [<options>]",
NULL NULL
}; };
const char * const *record_usage = __record_usage;
/* /*
* XXX Ideally would be local to cmd_record() and passed to a record__new * XXX Ideally would be local to cmd_record() and passed to a record__new
@ -725,7 +737,7 @@ const char record_callchain_help[] = CALLCHAIN_HELP "fp";
* perf_evlist__prepare_workload, etc instead of fork+exec'in 'perf record', * perf_evlist__prepare_workload, etc instead of fork+exec'in 'perf record',
* using pipes, etc. * using pipes, etc.
*/ */
const struct option record_options[] = { struct option __record_options[] = {
OPT_CALLBACK('e', "event", &record.evlist, "event", OPT_CALLBACK('e', "event", &record.evlist, "event",
"event selector. use 'perf list' to list available events", "event selector. use 'perf list' to list available events",
parse_events_option), parse_events_option),
@ -799,9 +811,13 @@ const struct option record_options[] = {
"sample transaction flags (special events only)"), "sample transaction flags (special events only)"),
OPT_BOOLEAN(0, "per-thread", &record.opts.target.per_thread, OPT_BOOLEAN(0, "per-thread", &record.opts.target.per_thread,
"use per-thread mmaps"), "use per-thread mmaps"),
OPT_BOOLEAN('I', "intr-regs", &record.opts.sample_intr_regs,
"Sample machine registers on interrupt"),
OPT_END() OPT_END()
}; };
struct option *record_options = __record_options;
int cmd_record(int argc, const char **argv, const char *prefix __maybe_unused) int cmd_record(int argc, const char **argv, const char *prefix __maybe_unused)
{ {
int err = -ENOMEM; int err = -ENOMEM;

View File

@ -23,7 +23,6 @@ static char const *generate_script_lang;
static bool debug_mode; static bool debug_mode;
static u64 last_timestamp; static u64 last_timestamp;
static u64 nr_unordered; static u64 nr_unordered;
extern const struct option record_options[];
static bool no_callchain; static bool no_callchain;
static bool latency_format; static bool latency_format;
static bool system_wide; static bool system_wide;
@ -379,7 +378,6 @@ static void print_sample_start(struct perf_sample *sample,
static void print_sample_addr(union perf_event *event, static void print_sample_addr(union perf_event *event,
struct perf_sample *sample, struct perf_sample *sample,
struct machine *machine,
struct thread *thread, struct thread *thread,
struct perf_event_attr *attr) struct perf_event_attr *attr)
{ {
@ -390,7 +388,7 @@ static void print_sample_addr(union perf_event *event,
if (!sample_addr_correlates_sym(attr)) if (!sample_addr_correlates_sym(attr))
return; return;
perf_event__preprocess_sample_addr(event, sample, machine, thread, &al); perf_event__preprocess_sample_addr(event, sample, thread, &al);
if (PRINT_FIELD(SYM)) { if (PRINT_FIELD(SYM)) {
printf(" "); printf(" ");
@ -438,7 +436,7 @@ static void print_sample_bts(union perf_event *event,
((evsel->attr.sample_type & PERF_SAMPLE_ADDR) && ((evsel->attr.sample_type & PERF_SAMPLE_ADDR) &&
!output[attr->type].user_set)) { !output[attr->type].user_set)) {
printf(" => "); printf(" => ");
print_sample_addr(event, sample, al->machine, thread, attr); print_sample_addr(event, sample, thread, attr);
} }
if (print_srcline_last) if (print_srcline_last)
@ -475,7 +473,7 @@ static void process_event(union perf_event *event, struct perf_sample *sample,
event_format__print(evsel->tp_format, sample->cpu, event_format__print(evsel->tp_format, sample->cpu,
sample->raw_data, sample->raw_size); sample->raw_data, sample->raw_size);
if (PRINT_FIELD(ADDR)) if (PRINT_FIELD(ADDR))
print_sample_addr(event, sample, al->machine, thread, attr); print_sample_addr(event, sample, thread, attr);
if (PRINT_FIELD(IP)) { if (PRINT_FIELD(IP)) {
if (!symbol_conf.use_callchain) if (!symbol_conf.use_callchain)

View File

@ -528,7 +528,7 @@ static const char *cat_backtrace(union perf_event *event,
} }
tal.filtered = 0; tal.filtered = 0;
thread__find_addr_location(al.thread, machine, cpumode, thread__find_addr_location(al.thread, cpumode,
MAP__FUNCTION, ip, &tal); MAP__FUNCTION, ip, &tal);
if (tal.sym) if (tal.sym)
@ -1963,7 +1963,7 @@ int cmd_timechart(int argc, const char **argv,
NULL NULL
}; };
const struct option record_options[] = { const struct option timechart_record_options[] = {
OPT_BOOLEAN('P', "power-only", &tchart.power_only, "output power data only"), OPT_BOOLEAN('P', "power-only", &tchart.power_only, "output power data only"),
OPT_BOOLEAN('T', "tasks-only", &tchart.tasks_only, OPT_BOOLEAN('T', "tasks-only", &tchart.tasks_only,
"output processes data only"), "output processes data only"),
@ -1972,7 +1972,7 @@ int cmd_timechart(int argc, const char **argv,
OPT_BOOLEAN('g', "callchain", &tchart.with_backtrace, "record callchain"), OPT_BOOLEAN('g', "callchain", &tchart.with_backtrace, "record callchain"),
OPT_END() OPT_END()
}; };
const char * const record_usage[] = { const char * const timechart_record_usage[] = {
"perf timechart record [<options>]", "perf timechart record [<options>]",
NULL NULL
}; };
@ -1985,7 +1985,8 @@ int cmd_timechart(int argc, const char **argv,
} }
if (argc && !strncmp(argv[0], "rec", 3)) { if (argc && !strncmp(argv[0], "rec", 3)) {
argc = parse_options(argc, argv, record_options, record_usage, argc = parse_options(argc, argv, timechart_record_options,
timechart_record_usage,
PARSE_OPT_STOP_AT_NON_OPTION); PARSE_OPT_STOP_AT_NON_OPTION);
if (tchart.power_only && tchart.tasks_only) { if (tchart.power_only && tchart.tasks_only) {

View File

@ -1846,7 +1846,7 @@ static int trace__pgfault(struct trace *trace,
if (trace->summary_only) if (trace->summary_only)
return 0; return 0;
thread__find_addr_location(thread, trace->host, cpumode, MAP__FUNCTION, thread__find_addr_location(thread, cpumode, MAP__FUNCTION,
sample->ip, &al); sample->ip, &al);
trace__fprintf_entry_head(trace, thread, 0, sample->time, trace->output); trace__fprintf_entry_head(trace, thread, 0, sample->time, trace->output);
@ -1859,11 +1859,11 @@ static int trace__pgfault(struct trace *trace,
fprintf(trace->output, "] => "); fprintf(trace->output, "] => ");
thread__find_addr_location(thread, trace->host, cpumode, MAP__VARIABLE, thread__find_addr_location(thread, cpumode, MAP__VARIABLE,
sample->addr, &al); sample->addr, &al);
if (!al.map) { if (!al.map) {
thread__find_addr_location(thread, trace->host, cpumode, thread__find_addr_location(thread, cpumode,
MAP__FUNCTION, sample->addr, &al); MAP__FUNCTION, sample->addr, &al);
if (al.map) if (al.map)

View File

@ -150,7 +150,7 @@ CFLAGS += -std=gnu99
# adding assembler files missing the .GNU-stack linker note. # adding assembler files missing the .GNU-stack linker note.
LDFLAGS += -Wl,-z,noexecstack LDFLAGS += -Wl,-z,noexecstack
EXTLIBS = -lelf -lpthread -lrt -lm -ldl EXTLIBS = -lpthread -lrt -lm -ldl
ifneq ($(OUTPUT),) ifneq ($(OUTPUT),)
OUTPUT_FEATURES = $(OUTPUT)config/feature-checks/ OUTPUT_FEATURES = $(OUTPUT)config/feature-checks/
@ -200,7 +200,8 @@ CORE_FEATURE_TESTS = \
libunwind \ libunwind \
stackprotector-all \ stackprotector-all \
timerfd \ timerfd \
libdw-dwarf-unwind libdw-dwarf-unwind \
zlib
LIB_FEATURE_TESTS = \ LIB_FEATURE_TESTS = \
dwarf \ dwarf \
@ -214,7 +215,8 @@ LIB_FEATURE_TESTS = \
libpython \ libpython \
libslang \ libslang \
libunwind \ libunwind \
libdw-dwarf-unwind libdw-dwarf-unwind \
zlib
VF_FEATURE_TESTS = \ VF_FEATURE_TESTS = \
backtrace \ backtrace \
@ -230,7 +232,9 @@ VF_FEATURE_TESTS = \
bionic \ bionic \
liberty \ liberty \
liberty-z \ liberty-z \
cplus-demangle cplus-demangle \
compile-32 \
compile-x32
# Set FEATURE_CHECK_(C|LD)FLAGS-all for all CORE_FEATURE_TESTS features. # Set FEATURE_CHECK_(C|LD)FLAGS-all for all CORE_FEATURE_TESTS features.
# If in the future we need per-feature checks/flags for features not # If in the future we need per-feature checks/flags for features not
@ -350,6 +354,7 @@ endif # NO_LIBELF
ifndef NO_LIBELF ifndef NO_LIBELF
CFLAGS += -DHAVE_LIBELF_SUPPORT CFLAGS += -DHAVE_LIBELF_SUPPORT
EXTLIBS += -lelf
ifeq ($(feature-libelf-mmap), 1) ifeq ($(feature-libelf-mmap), 1)
CFLAGS += -DHAVE_LIBELF_MMAP_SUPPORT CFLAGS += -DHAVE_LIBELF_MMAP_SUPPORT
@ -369,7 +374,7 @@ ifndef NO_LIBELF
else else
CFLAGS += -DHAVE_DWARF_SUPPORT $(LIBDW_CFLAGS) CFLAGS += -DHAVE_DWARF_SUPPORT $(LIBDW_CFLAGS)
LDFLAGS += $(LIBDW_LDFLAGS) LDFLAGS += $(LIBDW_LDFLAGS)
EXTLIBS += -lelf -ldw EXTLIBS += -ldw
endif # PERF_HAVE_DWARF_REGS endif # PERF_HAVE_DWARF_REGS
endif # NO_DWARF endif # NO_DWARF
endif # NO_LIBELF endif # NO_LIBELF
@ -602,6 +607,15 @@ ifneq ($(filter -lbfd,$(EXTLIBS)),)
CFLAGS += -DHAVE_LIBBFD_SUPPORT CFLAGS += -DHAVE_LIBBFD_SUPPORT
endif endif
ifndef NO_ZLIB
ifeq ($(feature-zlib), 1)
CFLAGS += -DHAVE_ZLIB_SUPPORT
EXTLIBS += -lz
else
NO_ZLIB := 1
endif
endif
ifndef NO_BACKTRACE ifndef NO_BACKTRACE
ifeq ($(feature-backtrace), 1) ifeq ($(feature-backtrace), 1)
CFLAGS += -DHAVE_BACKTRACE_SUPPORT CFLAGS += -DHAVE_BACKTRACE_SUPPORT
@ -622,6 +636,31 @@ ifdef HAVE_KVM_STAT_SUPPORT
CFLAGS += -DHAVE_KVM_STAT_SUPPORT CFLAGS += -DHAVE_KVM_STAT_SUPPORT
endif endif
ifeq (${IS_64_BIT}, 1)
ifndef NO_PERF_READ_VDSO32
$(call feature_check,compile-32)
ifeq ($(feature-compile-32), 1)
CFLAGS += -DHAVE_PERF_READ_VDSO32
else
NO_PERF_READ_VDSO32 := 1
endif
endif
ifneq (${IS_X86_64}, 1)
NO_PERF_READ_VDSOX32 := 1
endif
ifndef NO_PERF_READ_VDSOX32
$(call feature_check,compile-x32)
ifeq ($(feature-compile-x32), 1)
CFLAGS += -DHAVE_PERF_READ_VDSOX32
else
NO_PERF_READ_VDSOX32 := 1
endif
endif
else
NO_PERF_READ_VDSO32 := 1
NO_PERF_READ_VDSOX32 := 1
endif
# Among the variables below, these: # Among the variables below, these:
# perfexecdir # perfexecdir
# template_dir # template_dir

View File

@ -21,3 +21,11 @@ ifeq ($(ARCH),x86_64)
RAW_ARCH := x86_64 RAW_ARCH := x86_64
endif endif
endif endif
ifeq (${IS_X86_64}, 1)
IS_64_BIT := 1
else ifeq ($(ARCH),x86)
IS_64_BIT := 0
else
IS_64_BIT := $(shell echo __LP64__ | ${CC} ${CFLAGS} -E -x c - | tail -n 1)
endif

View File

@ -27,7 +27,10 @@ FILES= \
test-libunwind-debug-frame.bin \ test-libunwind-debug-frame.bin \
test-stackprotector-all.bin \ test-stackprotector-all.bin \
test-timerfd.bin \ test-timerfd.bin \
test-libdw-dwarf-unwind.bin test-libdw-dwarf-unwind.bin \
test-compile-32.bin \
test-compile-x32.bin \
test-zlib.bin
CC := $(CROSS_COMPILE)gcc -MD CC := $(CROSS_COMPILE)gcc -MD
PKG_CONFIG := $(CROSS_COMPILE)pkg-config PKG_CONFIG := $(CROSS_COMPILE)pkg-config
@ -39,7 +42,7 @@ BUILD = $(CC) $(CFLAGS) -o $(OUTPUT)$@ $(patsubst %.bin,%.c,$@) $(LDFLAGS)
############################### ###############################
test-all.bin: test-all.bin:
$(BUILD) -Werror -fstack-protector-all -O2 -Werror -D_FORTIFY_SOURCE=2 -ldw -lelf -lnuma -lelf -laudit -I/usr/include/slang -lslang $(shell $(PKG_CONFIG) --libs --cflags gtk+-2.0 2>/dev/null) $(FLAGS_PERL_EMBED) $(FLAGS_PYTHON_EMBED) -DPACKAGE='"perf"' -lbfd -ldl $(BUILD) -Werror -fstack-protector-all -O2 -Werror -D_FORTIFY_SOURCE=2 -ldw -lelf -lnuma -lelf -laudit -I/usr/include/slang -lslang $(shell $(PKG_CONFIG) --libs --cflags gtk+-2.0 2>/dev/null) $(FLAGS_PERL_EMBED) $(FLAGS_PYTHON_EMBED) -DPACKAGE='"perf"' -lbfd -ldl -lz
test-hello.bin: test-hello.bin:
$(BUILD) $(BUILD)
@ -131,6 +134,15 @@ test-libdw-dwarf-unwind.bin:
test-sync-compare-and-swap.bin: test-sync-compare-and-swap.bin:
$(BUILD) -Werror $(BUILD) -Werror
test-compile-32.bin:
$(CC) -m32 -o $(OUTPUT)$@ test-compile.c
test-compile-x32.bin:
$(CC) -mx32 -o $(OUTPUT)$@ test-compile.c
test-zlib.bin:
$(BUILD) -lz
-include *.d -include *.d
############################### ###############################

View File

@ -93,6 +93,10 @@
# include "test-sync-compare-and-swap.c" # include "test-sync-compare-and-swap.c"
#undef main #undef main
#define main main_test_zlib
# include "test-zlib.c"
#undef main
int main(int argc, char *argv[]) int main(int argc, char *argv[])
{ {
main_test_libpython(); main_test_libpython();
@ -116,6 +120,7 @@ int main(int argc, char *argv[])
main_test_stackprotector_all(); main_test_stackprotector_all();
main_test_libdw_dwarf_unwind(); main_test_libdw_dwarf_unwind();
main_test_sync_compare_and_swap(argc, argv); main_test_sync_compare_and_swap(argc, argv);
main_test_zlib();
return 0; return 0;
} }

View File

@ -0,0 +1,4 @@
int main(void)
{
return 0;
}

View File

@ -0,0 +1,9 @@
#include <zlib.h>
int main(void)
{
z_stream zs;
inflateInit(&zs);
return 0;
}

View File

@ -0,0 +1,34 @@
#include <stdio.h>
#include <string.h>
#define VDSO__MAP_NAME "[vdso]"
/*
* Include definition of find_vdso_map() also used in util/vdso.c for
* building perf.
*/
#include "util/find-vdso-map.c"
int main(void)
{
void *start, *end;
size_t size, written;
if (find_vdso_map(&start, &end))
return 1;
size = end - start;
while (size) {
written = fwrite(start, 1, size, stdout);
if (!written)
return 1;
start += written;
size -= written;
}
if (fflush(stdout))
return 1;
return 0;
}

View File

@ -52,6 +52,7 @@ struct record_opts {
bool sample_weight; bool sample_weight;
bool sample_time; bool sample_time;
bool period; bool period;
bool sample_intr_regs;
unsigned int freq; unsigned int freq;
unsigned int mmap_pages; unsigned int mmap_pages;
unsigned int user_freq; unsigned int user_freq;
@ -62,4 +63,7 @@ struct record_opts {
unsigned initial_delay; unsigned initial_delay;
}; };
struct option;
extern const char * const *record_usage;
extern struct option *record_options;
#endif #endif

View File

@ -0,0 +1,8 @@
#!/bin/bash
#
# export perf data to a postgresql database. Can cover
# perf ip samples (excluding the tracepoints). No special
# record requirements, just record what you want to export.
#
perf record $@

View File

@ -0,0 +1,29 @@
#!/bin/bash
# description: export perf data to a postgresql database
# args: [database name] [columns] [calls]
n_args=0
for i in "$@"
do
if expr match "$i" "-" > /dev/null ; then
break
fi
n_args=$(( $n_args + 1 ))
done
if [ "$n_args" -gt 3 ] ; then
echo "usage: export-to-postgresql-report [database name] [columns] [calls]"
exit
fi
if [ "$n_args" -gt 2 ] ; then
dbname=$1
columns=$2
calls=$3
shift 3
elif [ "$n_args" -gt 1 ] ; then
dbname=$1
columns=$2
shift 2
elif [ "$n_args" -gt 0 ] ; then
dbname=$1
shift
fi
perf script $@ -s "$PERF_EXEC_PATH"/scripts/python/export-to-postgresql.py $dbname $columns $calls

View File

@ -0,0 +1,444 @@
# export-to-postgresql.py: export perf data to a postgresql database
# Copyright (c) 2014, Intel Corporation.
#
# This program is free software; you can redistribute it and/or modify it
# under the terms and conditions of the GNU General Public License,
# version 2, as published by the Free Software Foundation.
#
# This program is distributed in the hope 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.
import os
import sys
import struct
import datetime
from PySide.QtSql import *
# Need to access PostgreSQL C library directly to use COPY FROM STDIN
from ctypes import *
libpq = CDLL("libpq.so.5")
PQconnectdb = libpq.PQconnectdb
PQconnectdb.restype = c_void_p
PQfinish = libpq.PQfinish
PQstatus = libpq.PQstatus
PQexec = libpq.PQexec
PQexec.restype = c_void_p
PQresultStatus = libpq.PQresultStatus
PQputCopyData = libpq.PQputCopyData
PQputCopyData.argtypes = [ c_void_p, c_void_p, c_int ]
PQputCopyEnd = libpq.PQputCopyEnd
PQputCopyEnd.argtypes = [ c_void_p, c_void_p ]
sys.path.append(os.environ['PERF_EXEC_PATH'] + \
'/scripts/python/Perf-Trace-Util/lib/Perf/Trace')
# These perf imports are not used at present
#from perf_trace_context import *
#from Core import *
perf_db_export_mode = True
perf_db_export_calls = False
def usage():
print >> sys.stderr, "Usage is: export-to-postgresql.py <database name> [<columns>] [<calls>]"
print >> sys.stderr, "where: columns 'all' or 'branches'"
print >> sys.stderr, " calls 'calls' => create calls table"
raise Exception("Too few arguments")
if (len(sys.argv) < 2):
usage()
dbname = sys.argv[1]
if (len(sys.argv) >= 3):
columns = sys.argv[2]
else:
columns = "all"
if columns not in ("all", "branches"):
usage()
branches = (columns == "branches")
if (len(sys.argv) >= 4):
if (sys.argv[3] == "calls"):
perf_db_export_calls = True
else:
usage()
output_dir_name = os.getcwd() + "/" + dbname + "-perf-data"
os.mkdir(output_dir_name)
def do_query(q, s):
if (q.exec_(s)):
return
raise Exception("Query failed: " + q.lastError().text())
print datetime.datetime.today(), "Creating database..."
db = QSqlDatabase.addDatabase('QPSQL')
query = QSqlQuery(db)
db.setDatabaseName('postgres')
db.open()
try:
do_query(query, 'CREATE DATABASE ' + dbname)
except:
os.rmdir(output_dir_name)
raise
query.finish()
query.clear()
db.close()
db.setDatabaseName(dbname)
db.open()
query = QSqlQuery(db)
do_query(query, 'SET client_min_messages TO WARNING')
do_query(query, 'CREATE TABLE selected_events ('
'id bigint NOT NULL,'
'name varchar(80))')
do_query(query, 'CREATE TABLE machines ('
'id bigint NOT NULL,'
'pid integer,'
'root_dir varchar(4096))')
do_query(query, 'CREATE TABLE threads ('
'id bigint NOT NULL,'
'machine_id bigint,'
'process_id bigint,'
'pid integer,'
'tid integer)')
do_query(query, 'CREATE TABLE comms ('
'id bigint NOT NULL,'
'comm varchar(16))')
do_query(query, 'CREATE TABLE comm_threads ('
'id bigint NOT NULL,'
'comm_id bigint,'
'thread_id bigint)')
do_query(query, 'CREATE TABLE dsos ('
'id bigint NOT NULL,'
'machine_id bigint,'
'short_name varchar(256),'
'long_name varchar(4096),'
'build_id varchar(64))')
do_query(query, 'CREATE TABLE symbols ('
'id bigint NOT NULL,'
'dso_id bigint,'
'sym_start bigint,'
'sym_end bigint,'
'binding integer,'
'name varchar(2048))')
do_query(query, 'CREATE TABLE branch_types ('
'id integer NOT NULL,'
'name varchar(80))')
if branches:
do_query(query, 'CREATE TABLE samples ('
'id bigint NOT NULL,'
'evsel_id bigint,'
'machine_id bigint,'
'thread_id bigint,'
'comm_id bigint,'
'dso_id bigint,'
'symbol_id bigint,'
'sym_offset bigint,'
'ip bigint,'
'time bigint,'
'cpu integer,'
'to_dso_id bigint,'
'to_symbol_id bigint,'
'to_sym_offset bigint,'
'to_ip bigint,'
'branch_type integer,'
'in_tx boolean)')
else:
do_query(query, 'CREATE TABLE samples ('
'id bigint NOT NULL,'
'evsel_id bigint,'
'machine_id bigint,'
'thread_id bigint,'
'comm_id bigint,'
'dso_id bigint,'
'symbol_id bigint,'
'sym_offset bigint,'
'ip bigint,'
'time bigint,'
'cpu integer,'
'to_dso_id bigint,'
'to_symbol_id bigint,'
'to_sym_offset bigint,'
'to_ip bigint,'
'period bigint,'
'weight bigint,'
'transaction bigint,'
'data_src bigint,'
'branch_type integer,'
'in_tx boolean)')
if perf_db_export_calls:
do_query(query, 'CREATE TABLE call_paths ('
'id bigint NOT NULL,'
'parent_id bigint,'
'symbol_id bigint,'
'ip bigint)')
do_query(query, 'CREATE TABLE calls ('
'id bigint NOT NULL,'
'thread_id bigint,'
'comm_id bigint,'
'call_path_id bigint,'
'call_time bigint,'
'return_time bigint,'
'branch_count bigint,'
'call_id bigint,'
'return_id bigint,'
'parent_call_path_id bigint,'
'flags integer)')
do_query(query, 'CREATE VIEW samples_view AS '
'SELECT '
'id,'
'time,'
'cpu,'
'(SELECT pid FROM threads WHERE id = thread_id) AS pid,'
'(SELECT tid FROM threads WHERE id = thread_id) AS tid,'
'(SELECT comm FROM comms WHERE id = comm_id) AS command,'
'(SELECT name FROM selected_events WHERE id = evsel_id) AS event,'
'to_hex(ip) AS ip_hex,'
'(SELECT name FROM symbols WHERE id = symbol_id) AS symbol,'
'sym_offset,'
'(SELECT short_name FROM dsos WHERE id = dso_id) AS dso_short_name,'
'to_hex(to_ip) AS to_ip_hex,'
'(SELECT name FROM symbols WHERE id = to_symbol_id) AS to_symbol,'
'to_sym_offset,'
'(SELECT short_name FROM dsos WHERE id = to_dso_id) AS to_dso_short_name,'
'(SELECT name FROM branch_types WHERE id = branch_type) AS branch_type_name,'
'in_tx'
' FROM samples')
file_header = struct.pack("!11sii", "PGCOPY\n\377\r\n\0", 0, 0)
file_trailer = "\377\377"
def open_output_file(file_name):
path_name = output_dir_name + "/" + file_name
file = open(path_name, "w+")
file.write(file_header)
return file
def close_output_file(file):
file.write(file_trailer)
file.close()
def copy_output_file_direct(file, table_name):
close_output_file(file)
sql = "COPY " + table_name + " FROM '" + file.name + "' (FORMAT 'binary')"
do_query(query, sql)
# Use COPY FROM STDIN because security may prevent postgres from accessing the files directly
def copy_output_file(file, table_name):
conn = PQconnectdb("dbname = " + dbname)
if (PQstatus(conn)):
raise Exception("COPY FROM STDIN PQconnectdb failed")
file.write(file_trailer)
file.seek(0)
sql = "COPY " + table_name + " FROM STDIN (FORMAT 'binary')"
res = PQexec(conn, sql)
if (PQresultStatus(res) != 4):
raise Exception("COPY FROM STDIN PQexec failed")
data = file.read(65536)
while (len(data)):
ret = PQputCopyData(conn, data, len(data))
if (ret != 1):
raise Exception("COPY FROM STDIN PQputCopyData failed, error " + str(ret))
data = file.read(65536)
ret = PQputCopyEnd(conn, None)
if (ret != 1):
raise Exception("COPY FROM STDIN PQputCopyEnd failed, error " + str(ret))
PQfinish(conn)
def remove_output_file(file):
name = file.name
file.close()
os.unlink(name)
evsel_file = open_output_file("evsel_table.bin")
machine_file = open_output_file("machine_table.bin")
thread_file = open_output_file("thread_table.bin")
comm_file = open_output_file("comm_table.bin")
comm_thread_file = open_output_file("comm_thread_table.bin")
dso_file = open_output_file("dso_table.bin")
symbol_file = open_output_file("symbol_table.bin")
branch_type_file = open_output_file("branch_type_table.bin")
sample_file = open_output_file("sample_table.bin")
if perf_db_export_calls:
call_path_file = open_output_file("call_path_table.bin")
call_file = open_output_file("call_table.bin")
def trace_begin():
print datetime.datetime.today(), "Writing to intermediate files..."
# id == 0 means unknown. It is easier to create records for them than replace the zeroes with NULLs
evsel_table(0, "unknown")
machine_table(0, 0, "unknown")
thread_table(0, 0, 0, -1, -1)
comm_table(0, "unknown")
dso_table(0, 0, "unknown", "unknown", "")
symbol_table(0, 0, 0, 0, 0, "unknown")
sample_table(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0)
if perf_db_export_calls:
call_path_table(0, 0, 0, 0)
unhandled_count = 0
def trace_end():
print datetime.datetime.today(), "Copying to database..."
copy_output_file(evsel_file, "selected_events")
copy_output_file(machine_file, "machines")
copy_output_file(thread_file, "threads")
copy_output_file(comm_file, "comms")
copy_output_file(comm_thread_file, "comm_threads")
copy_output_file(dso_file, "dsos")
copy_output_file(symbol_file, "symbols")
copy_output_file(branch_type_file, "branch_types")
copy_output_file(sample_file, "samples")
if perf_db_export_calls:
copy_output_file(call_path_file, "call_paths")
copy_output_file(call_file, "calls")
print datetime.datetime.today(), "Removing intermediate files..."
remove_output_file(evsel_file)
remove_output_file(machine_file)
remove_output_file(thread_file)
remove_output_file(comm_file)
remove_output_file(comm_thread_file)
remove_output_file(dso_file)
remove_output_file(symbol_file)
remove_output_file(branch_type_file)
remove_output_file(sample_file)
if perf_db_export_calls:
remove_output_file(call_path_file)
remove_output_file(call_file)
os.rmdir(output_dir_name)
print datetime.datetime.today(), "Adding primary keys"
do_query(query, 'ALTER TABLE selected_events ADD PRIMARY KEY (id)')
do_query(query, 'ALTER TABLE machines ADD PRIMARY KEY (id)')
do_query(query, 'ALTER TABLE threads ADD PRIMARY KEY (id)')
do_query(query, 'ALTER TABLE comms ADD PRIMARY KEY (id)')
do_query(query, 'ALTER TABLE comm_threads ADD PRIMARY KEY (id)')
do_query(query, 'ALTER TABLE dsos ADD PRIMARY KEY (id)')
do_query(query, 'ALTER TABLE symbols ADD PRIMARY KEY (id)')
do_query(query, 'ALTER TABLE branch_types ADD PRIMARY KEY (id)')
do_query(query, 'ALTER TABLE samples ADD PRIMARY KEY (id)')
if perf_db_export_calls:
do_query(query, 'ALTER TABLE call_paths ADD PRIMARY KEY (id)')
do_query(query, 'ALTER TABLE calls ADD PRIMARY KEY (id)')
print datetime.datetime.today(), "Adding foreign keys"
do_query(query, 'ALTER TABLE threads '
'ADD CONSTRAINT machinefk FOREIGN KEY (machine_id) REFERENCES machines (id),'
'ADD CONSTRAINT processfk FOREIGN KEY (process_id) REFERENCES threads (id)')
do_query(query, 'ALTER TABLE comm_threads '
'ADD CONSTRAINT commfk FOREIGN KEY (comm_id) REFERENCES comms (id),'
'ADD CONSTRAINT threadfk FOREIGN KEY (thread_id) REFERENCES threads (id)')
do_query(query, 'ALTER TABLE dsos '
'ADD CONSTRAINT machinefk FOREIGN KEY (machine_id) REFERENCES machines (id)')
do_query(query, 'ALTER TABLE symbols '
'ADD CONSTRAINT dsofk FOREIGN KEY (dso_id) REFERENCES dsos (id)')
do_query(query, 'ALTER TABLE samples '
'ADD CONSTRAINT evselfk FOREIGN KEY (evsel_id) REFERENCES selected_events (id),'
'ADD CONSTRAINT machinefk FOREIGN KEY (machine_id) REFERENCES machines (id),'
'ADD CONSTRAINT threadfk FOREIGN KEY (thread_id) REFERENCES threads (id),'
'ADD CONSTRAINT commfk FOREIGN KEY (comm_id) REFERENCES comms (id),'
'ADD CONSTRAINT dsofk FOREIGN KEY (dso_id) REFERENCES dsos (id),'
'ADD CONSTRAINT symbolfk FOREIGN KEY (symbol_id) REFERENCES symbols (id),'
'ADD CONSTRAINT todsofk FOREIGN KEY (to_dso_id) REFERENCES dsos (id),'
'ADD CONSTRAINT tosymbolfk FOREIGN KEY (to_symbol_id) REFERENCES symbols (id)')
if perf_db_export_calls:
do_query(query, 'ALTER TABLE call_paths '
'ADD CONSTRAINT parentfk FOREIGN KEY (parent_id) REFERENCES call_paths (id),'
'ADD CONSTRAINT symbolfk FOREIGN KEY (symbol_id) REFERENCES symbols (id)')
do_query(query, 'ALTER TABLE calls '
'ADD CONSTRAINT threadfk FOREIGN KEY (thread_id) REFERENCES threads (id),'
'ADD CONSTRAINT commfk FOREIGN KEY (comm_id) REFERENCES comms (id),'
'ADD CONSTRAINT call_pathfk FOREIGN KEY (call_path_id) REFERENCES call_paths (id),'
'ADD CONSTRAINT callfk FOREIGN KEY (call_id) REFERENCES samples (id),'
'ADD CONSTRAINT returnfk FOREIGN KEY (return_id) REFERENCES samples (id),'
'ADD CONSTRAINT parent_call_pathfk FOREIGN KEY (parent_call_path_id) REFERENCES call_paths (id)')
do_query(query, 'CREATE INDEX pcpid_idx ON calls (parent_call_path_id)')
if (unhandled_count):
print datetime.datetime.today(), "Warning: ", unhandled_count, " unhandled events"
print datetime.datetime.today(), "Done"
def trace_unhandled(event_name, context, event_fields_dict):
global unhandled_count
unhandled_count += 1
def sched__sched_switch(*x):
pass
def evsel_table(evsel_id, evsel_name, *x):
n = len(evsel_name)
fmt = "!hiqi" + str(n) + "s"
value = struct.pack(fmt, 2, 8, evsel_id, n, evsel_name)
evsel_file.write(value)
def machine_table(machine_id, pid, root_dir, *x):
n = len(root_dir)
fmt = "!hiqiii" + str(n) + "s"
value = struct.pack(fmt, 3, 8, machine_id, 4, pid, n, root_dir)
machine_file.write(value)
def thread_table(thread_id, machine_id, process_id, pid, tid, *x):
value = struct.pack("!hiqiqiqiiii", 5, 8, thread_id, 8, machine_id, 8, process_id, 4, pid, 4, tid)
thread_file.write(value)
def comm_table(comm_id, comm_str, *x):
n = len(comm_str)
fmt = "!hiqi" + str(n) + "s"
value = struct.pack(fmt, 2, 8, comm_id, n, comm_str)
comm_file.write(value)
def comm_thread_table(comm_thread_id, comm_id, thread_id, *x):
fmt = "!hiqiqiq"
value = struct.pack(fmt, 3, 8, comm_thread_id, 8, comm_id, 8, thread_id)
comm_thread_file.write(value)
def dso_table(dso_id, machine_id, short_name, long_name, build_id, *x):
n1 = len(short_name)
n2 = len(long_name)
n3 = len(build_id)
fmt = "!hiqiqi" + str(n1) + "si" + str(n2) + "si" + str(n3) + "s"
value = struct.pack(fmt, 5, 8, dso_id, 8, machine_id, n1, short_name, n2, long_name, n3, build_id)
dso_file.write(value)
def symbol_table(symbol_id, dso_id, sym_start, sym_end, binding, symbol_name, *x):
n = len(symbol_name)
fmt = "!hiqiqiqiqiii" + str(n) + "s"
value = struct.pack(fmt, 6, 8, symbol_id, 8, dso_id, 8, sym_start, 8, sym_end, 4, binding, n, symbol_name)
symbol_file.write(value)
def branch_type_table(branch_type, name, *x):
n = len(name)
fmt = "!hiii" + str(n) + "s"
value = struct.pack(fmt, 2, 4, branch_type, n, name)
branch_type_file.write(value)
def sample_table(sample_id, evsel_id, machine_id, thread_id, comm_id, dso_id, symbol_id, sym_offset, ip, time, cpu, to_dso_id, to_symbol_id, to_sym_offset, to_ip, period, weight, transaction, data_src, branch_type, in_tx, *x):
if branches:
value = struct.pack("!hiqiqiqiqiqiqiqiqiqiqiiiqiqiqiqiiiB", 17, 8, sample_id, 8, evsel_id, 8, machine_id, 8, thread_id, 8, comm_id, 8, dso_id, 8, symbol_id, 8, sym_offset, 8, ip, 8, time, 4, cpu, 8, to_dso_id, 8, to_symbol_id, 8, to_sym_offset, 8, to_ip, 4, branch_type, 1, in_tx)
else:
value = struct.pack("!hiqiqiqiqiqiqiqiqiqiqiiiqiqiqiqiqiqiqiqiiiB", 21, 8, sample_id, 8, evsel_id, 8, machine_id, 8, thread_id, 8, comm_id, 8, dso_id, 8, symbol_id, 8, sym_offset, 8, ip, 8, time, 4, cpu, 8, to_dso_id, 8, to_symbol_id, 8, to_sym_offset, 8, to_ip, 8, period, 8, weight, 8, transaction, 8, data_src, 4, branch_type, 1, in_tx)
sample_file.write(value)
def call_path_table(cp_id, parent_id, symbol_id, ip, *x):
fmt = "!hiqiqiqiq"
value = struct.pack(fmt, 4, 8, cp_id, 8, parent_id, 8, symbol_id, 8, ip)
call_path_file.write(value)
def call_return_table(cr_id, thread_id, comm_id, call_path_id, call_time, return_time, branch_count, call_id, return_id, parent_call_path_id, flags, *x):
fmt = "!hiqiqiqiqiqiqiqiqiqiqii"
value = struct.pack(fmt, 11, 8, cr_id, 8, thread_id, 8, comm_id, 8, call_path_id, 8, call_time, 8, return_time, 8, branch_count, 8, call_id, 8, return_id, 8, parent_call_path_id, 4, flags)
call_file.write(value)

View File

@ -85,7 +85,7 @@ static struct test {
.func = test__hists_link, .func = test__hists_link,
}, },
{ {
.desc = "Try 'use perf' in python, checking link problems", .desc = "Try 'import perf' in python, checking link problems",
.func = test__python_use, .func = test__python_use,
}, },
{ {

View File

@ -133,8 +133,7 @@ static int read_via_objdump(const char *filename, u64 addr, void *buf,
} }
static int read_object_code(u64 addr, size_t len, u8 cpumode, static int read_object_code(u64 addr, size_t len, u8 cpumode,
struct thread *thread, struct machine *machine, struct thread *thread, struct state *state)
struct state *state)
{ {
struct addr_location al; struct addr_location al;
unsigned char buf1[BUFSZ]; unsigned char buf1[BUFSZ];
@ -145,8 +144,7 @@ static int read_object_code(u64 addr, size_t len, u8 cpumode,
pr_debug("Reading object code for memory address: %#"PRIx64"\n", addr); pr_debug("Reading object code for memory address: %#"PRIx64"\n", addr);
thread__find_addr_map(thread, machine, cpumode, MAP__FUNCTION, addr, thread__find_addr_map(thread, cpumode, MAP__FUNCTION, addr, &al);
&al);
if (!al.map || !al.map->dso) { if (!al.map || !al.map->dso) {
pr_debug("thread__find_addr_map failed\n"); pr_debug("thread__find_addr_map failed\n");
return -1; return -1;
@ -170,8 +168,8 @@ static int read_object_code(u64 addr, size_t len, u8 cpumode,
len = al.map->end - addr; len = al.map->end - addr;
/* Read the object code using perf */ /* Read the object code using perf */
ret_len = dso__data_read_offset(al.map->dso, machine, al.addr, buf1, ret_len = dso__data_read_offset(al.map->dso, thread->mg->machine,
len); al.addr, buf1, len);
if (ret_len != len) { if (ret_len != len) {
pr_debug("dso__data_read_offset failed\n"); pr_debug("dso__data_read_offset failed\n");
return -1; return -1;
@ -264,8 +262,7 @@ static int process_sample_event(struct machine *machine,
cpumode = event->header.misc & PERF_RECORD_MISC_CPUMODE_MASK; cpumode = event->header.misc & PERF_RECORD_MISC_CPUMODE_MASK;
return read_object_code(sample.ip, READLEN, cpumode, thread, machine, return read_object_code(sample.ip, READLEN, cpumode, thread, state);
state);
} }
static int process_event(struct machine *machine, struct perf_evlist *evlist, static int process_event(struct machine *machine, struct perf_evlist *evlist,

View File

@ -59,7 +59,7 @@ static int unwind_entry(struct unwind_entry *entry, void *arg)
} }
__attribute__ ((noinline)) __attribute__ ((noinline))
static int unwind_thread(struct thread *thread, struct machine *machine) static int unwind_thread(struct thread *thread)
{ {
struct perf_sample sample; struct perf_sample sample;
unsigned long cnt = 0; unsigned long cnt = 0;
@ -72,7 +72,7 @@ static int unwind_thread(struct thread *thread, struct machine *machine)
goto out; goto out;
} }
err = unwind__get_entries(unwind_entry, &cnt, machine, thread, err = unwind__get_entries(unwind_entry, &cnt, thread,
&sample, MAX_STACK); &sample, MAX_STACK);
if (err) if (err)
pr_debug("unwind failed\n"); pr_debug("unwind failed\n");
@ -89,21 +89,21 @@ static int unwind_thread(struct thread *thread, struct machine *machine)
} }
__attribute__ ((noinline)) __attribute__ ((noinline))
static int krava_3(struct thread *thread, struct machine *machine) static int krava_3(struct thread *thread)
{ {
return unwind_thread(thread, machine); return unwind_thread(thread);
} }
__attribute__ ((noinline)) __attribute__ ((noinline))
static int krava_2(struct thread *thread, struct machine *machine) static int krava_2(struct thread *thread)
{ {
return krava_3(thread, machine); return krava_3(thread);
} }
__attribute__ ((noinline)) __attribute__ ((noinline))
static int krava_1(struct thread *thread, struct machine *machine) static int krava_1(struct thread *thread)
{ {
return krava_2(thread, machine); return krava_2(thread);
} }
int test__dwarf_unwind(void) int test__dwarf_unwind(void)
@ -137,7 +137,7 @@ int test__dwarf_unwind(void)
goto out; goto out;
} }
err = krava_1(thread, machine); err = krava_1(thread);
out: out:
machine__delete_threads(machine); machine__delete_threads(machine);

View File

@ -43,7 +43,7 @@ static struct sample fake_samples[] = {
}; };
static int add_hist_entries(struct perf_evlist *evlist, static int add_hist_entries(struct perf_evlist *evlist,
struct machine *machine __maybe_unused) struct machine *machine)
{ {
struct perf_evsel *evsel; struct perf_evsel *evsel;
struct addr_location al; struct addr_location al;

View File

@ -187,7 +187,7 @@ static int mmap_events(synth_cb synth)
pr_debug("looking for map %p\n", td->map); pr_debug("looking for map %p\n", td->map);
thread__find_addr_map(thread, machine, thread__find_addr_map(thread,
PERF_RECORD_MISC_USER, MAP__FUNCTION, PERF_RECORD_MISC_USER, MAP__FUNCTION,
(unsigned long) (td->map + 1), &al); (unsigned long) (td->map + 1), &al);

View File

@ -1145,6 +1145,49 @@ static int test__pinned_group(struct perf_evlist *evlist)
return 0; return 0;
} }
static int test__checkevent_breakpoint_len(struct perf_evlist *evlist)
{
struct perf_evsel *evsel = perf_evlist__first(evlist);
TEST_ASSERT_VAL("wrong number of entries", 1 == evlist->nr_entries);
TEST_ASSERT_VAL("wrong type", PERF_TYPE_BREAKPOINT == evsel->attr.type);
TEST_ASSERT_VAL("wrong config", 0 == evsel->attr.config);
TEST_ASSERT_VAL("wrong bp_type", (HW_BREAKPOINT_R | HW_BREAKPOINT_W) ==
evsel->attr.bp_type);
TEST_ASSERT_VAL("wrong bp_len", HW_BREAKPOINT_LEN_1 ==
evsel->attr.bp_len);
return 0;
}
static int test__checkevent_breakpoint_len_w(struct perf_evlist *evlist)
{
struct perf_evsel *evsel = perf_evlist__first(evlist);
TEST_ASSERT_VAL("wrong number of entries", 1 == evlist->nr_entries);
TEST_ASSERT_VAL("wrong type", PERF_TYPE_BREAKPOINT == evsel->attr.type);
TEST_ASSERT_VAL("wrong config", 0 == evsel->attr.config);
TEST_ASSERT_VAL("wrong bp_type", HW_BREAKPOINT_W ==
evsel->attr.bp_type);
TEST_ASSERT_VAL("wrong bp_len", HW_BREAKPOINT_LEN_2 ==
evsel->attr.bp_len);
return 0;
}
static int
test__checkevent_breakpoint_len_rw_modifier(struct perf_evlist *evlist)
{
struct perf_evsel *evsel = perf_evlist__first(evlist);
TEST_ASSERT_VAL("wrong exclude_user", !evsel->attr.exclude_user);
TEST_ASSERT_VAL("wrong exclude_kernel", evsel->attr.exclude_kernel);
TEST_ASSERT_VAL("wrong exclude_hv", evsel->attr.exclude_hv);
TEST_ASSERT_VAL("wrong precise_ip", !evsel->attr.precise_ip);
return test__checkevent_breakpoint_rw(evlist);
}
static int count_tracepoints(void) static int count_tracepoints(void)
{ {
char events_path[PATH_MAX]; char events_path[PATH_MAX];
@ -1420,6 +1463,21 @@ static struct evlist_test test__events[] = {
.check = test__pinned_group, .check = test__pinned_group,
.id = 41, .id = 41,
}, },
{
.name = "mem:0/1",
.check = test__checkevent_breakpoint_len,
.id = 42,
},
{
.name = "mem:0/2:w",
.check = test__checkevent_breakpoint_len_w,
.id = 43,
},
{
.name = "mem:0/4:rw:u",
.check = test__checkevent_breakpoint_len_rw_modifier,
.id = 44
},
#if defined(__s390x__) #if defined(__s390x__)
{ {
.name = "kvm-s390:kvm_s390_create_vm", .name = "kvm-s390:kvm_s390_create_vm",

View File

@ -126,16 +126,28 @@ static bool samples_same(const struct perf_sample *s1,
if (type & PERF_SAMPLE_TRANSACTION) if (type & PERF_SAMPLE_TRANSACTION)
COMP(transaction); COMP(transaction);
if (type & PERF_SAMPLE_REGS_INTR) {
size_t sz = hweight_long(s1->intr_regs.mask) * sizeof(u64);
COMP(intr_regs.mask);
COMP(intr_regs.abi);
if (s1->intr_regs.abi &&
(!s1->intr_regs.regs || !s2->intr_regs.regs ||
memcmp(s1->intr_regs.regs, s2->intr_regs.regs, sz))) {
pr_debug("Samples differ at 'intr_regs'\n");
return false;
}
}
return true; return true;
} }
static int do_test(u64 sample_type, u64 sample_regs_user, u64 read_format) static int do_test(u64 sample_type, u64 sample_regs, u64 read_format)
{ {
struct perf_evsel evsel = { struct perf_evsel evsel = {
.needs_swap = false, .needs_swap = false,
.attr = { .attr = {
.sample_type = sample_type, .sample_type = sample_type,
.sample_regs_user = sample_regs_user,
.read_format = read_format, .read_format = read_format,
}, },
}; };
@ -154,7 +166,7 @@ static int do_test(u64 sample_type, u64 sample_regs_user, u64 read_format)
/* 1 branch_entry */ /* 1 branch_entry */
.data = {1, 211, 212, 213}, .data = {1, 211, 212, 213},
}; };
u64 user_regs[64]; u64 regs[64];
const u64 raw_data[] = {0x123456780a0b0c0dULL, 0x1102030405060708ULL}; const u64 raw_data[] = {0x123456780a0b0c0dULL, 0x1102030405060708ULL};
const u64 data[] = {0x2211443366558877ULL, 0, 0xaabbccddeeff4321ULL}; const u64 data[] = {0x2211443366558877ULL, 0, 0xaabbccddeeff4321ULL};
struct perf_sample sample = { struct perf_sample sample = {
@ -176,8 +188,8 @@ static int do_test(u64 sample_type, u64 sample_regs_user, u64 read_format)
.branch_stack = &branch_stack.branch_stack, .branch_stack = &branch_stack.branch_stack,
.user_regs = { .user_regs = {
.abi = PERF_SAMPLE_REGS_ABI_64, .abi = PERF_SAMPLE_REGS_ABI_64,
.mask = sample_regs_user, .mask = sample_regs,
.regs = user_regs, .regs = regs,
}, },
.user_stack = { .user_stack = {
.size = sizeof(data), .size = sizeof(data),
@ -187,14 +199,25 @@ static int do_test(u64 sample_type, u64 sample_regs_user, u64 read_format)
.time_enabled = 0x030a59d664fca7deULL, .time_enabled = 0x030a59d664fca7deULL,
.time_running = 0x011b6ae553eb98edULL, .time_running = 0x011b6ae553eb98edULL,
}, },
.intr_regs = {
.abi = PERF_SAMPLE_REGS_ABI_64,
.mask = sample_regs,
.regs = regs,
},
}; };
struct sample_read_value values[] = {{1, 5}, {9, 3}, {2, 7}, {6, 4},}; struct sample_read_value values[] = {{1, 5}, {9, 3}, {2, 7}, {6, 4},};
struct perf_sample sample_out; struct perf_sample sample_out;
size_t i, sz, bufsz; size_t i, sz, bufsz;
int err, ret = -1; int err, ret = -1;
for (i = 0; i < sizeof(user_regs); i++) if (sample_type & PERF_SAMPLE_REGS_USER)
*(i + (u8 *)user_regs) = i & 0xfe; evsel.attr.sample_regs_user = sample_regs;
if (sample_type & PERF_SAMPLE_REGS_INTR)
evsel.attr.sample_regs_intr = sample_regs;
for (i = 0; i < sizeof(regs); i++)
*(i + (u8 *)regs) = i & 0xfe;
if (read_format & PERF_FORMAT_GROUP) { if (read_format & PERF_FORMAT_GROUP) {
sample.read.group.nr = 4; sample.read.group.nr = 4;
@ -271,7 +294,7 @@ int test__sample_parsing(void)
{ {
const u64 rf[] = {4, 5, 6, 7, 12, 13, 14, 15}; const u64 rf[] = {4, 5, 6, 7, 12, 13, 14, 15};
u64 sample_type; u64 sample_type;
u64 sample_regs_user; u64 sample_regs;
size_t i; size_t i;
int err; int err;
@ -280,7 +303,7 @@ int test__sample_parsing(void)
* were added. Please actually update the test rather than just change * were added. Please actually update the test rather than just change
* the condition below. * the condition below.
*/ */
if (PERF_SAMPLE_MAX > PERF_SAMPLE_TRANSACTION << 1) { if (PERF_SAMPLE_MAX > PERF_SAMPLE_REGS_INTR << 1) {
pr_debug("sample format has changed, some new PERF_SAMPLE_ bit was introduced - test needs updating\n"); pr_debug("sample format has changed, some new PERF_SAMPLE_ bit was introduced - test needs updating\n");
return -1; return -1;
} }
@ -297,22 +320,24 @@ int test__sample_parsing(void)
} }
continue; continue;
} }
sample_regs = 0;
if (sample_type == PERF_SAMPLE_REGS_USER) if (sample_type == PERF_SAMPLE_REGS_USER)
sample_regs_user = 0x3fff; sample_regs = 0x3fff;
else
sample_regs_user = 0;
err = do_test(sample_type, sample_regs_user, 0); if (sample_type == PERF_SAMPLE_REGS_INTR)
sample_regs = 0xff0fff;
err = do_test(sample_type, sample_regs, 0);
if (err) if (err)
return err; return err;
} }
/* Test all sample format bits together */ /* Test all sample format bits together */
sample_type = PERF_SAMPLE_MAX - 1; sample_type = PERF_SAMPLE_MAX - 1;
sample_regs_user = 0x3fff; sample_regs = 0x3fff; /* shared yb intr and user regs */
for (i = 0; i < ARRAY_SIZE(rf); i++) { for (i = 0; i < ARRAY_SIZE(rf); i++) {
err = do_test(sample_type, sample_regs_user, rf[i]); err = do_test(sample_type, sample_regs, rf[i]);
if (err) if (err)
return err; return err;
} }

View File

@ -27,6 +27,7 @@ static struct annotate_browser_opt {
bool hide_src_code, bool hide_src_code,
use_offset, use_offset,
jump_arrows, jump_arrows,
show_linenr,
show_nr_jumps; show_nr_jumps;
} annotate_browser__opts = { } annotate_browser__opts = {
.use_offset = true, .use_offset = true,
@ -128,7 +129,11 @@ static void annotate_browser__write(struct ui_browser *browser, void *entry, int
if (!*dl->line) if (!*dl->line)
slsmg_write_nstring(" ", width - pcnt_width); slsmg_write_nstring(" ", width - pcnt_width);
else if (dl->offset == -1) { else if (dl->offset == -1) {
printed = scnprintf(bf, sizeof(bf), "%*s ", if (dl->line_nr && annotate_browser__opts.show_linenr)
printed = scnprintf(bf, sizeof(bf), "%-*d ",
ab->addr_width + 1, dl->line_nr);
else
printed = scnprintf(bf, sizeof(bf), "%*s ",
ab->addr_width, " "); ab->addr_width, " ");
slsmg_write_nstring(bf, printed); slsmg_write_nstring(bf, printed);
slsmg_write_nstring(dl->line, width - printed - pcnt_width + 1); slsmg_write_nstring(dl->line, width - printed - pcnt_width + 1);
@ -733,6 +738,7 @@ static int annotate_browser__run(struct annotate_browser *browser,
"o Toggle disassembler output/simplified view\n" "o Toggle disassembler output/simplified view\n"
"s Toggle source code view\n" "s Toggle source code view\n"
"/ Search string\n" "/ Search string\n"
"k Toggle line numbers\n"
"r Run available scripts\n" "r Run available scripts\n"
"? Search string backwards\n"); "? Search string backwards\n");
continue; continue;
@ -741,6 +747,10 @@ static int annotate_browser__run(struct annotate_browser *browser,
script_browse(NULL); script_browse(NULL);
continue; continue;
} }
case 'k':
annotate_browser__opts.show_linenr =
!annotate_browser__opts.show_linenr;
break;
case 'H': case 'H':
nd = browser->curr_hot; nd = browser->curr_hot;
break; break;
@ -984,6 +994,7 @@ static struct annotate_config {
} annotate__configs[] = { } annotate__configs[] = {
ANNOTATE_CFG(hide_src_code), ANNOTATE_CFG(hide_src_code),
ANNOTATE_CFG(jump_arrows), ANNOTATE_CFG(jump_arrows),
ANNOTATE_CFG(show_linenr),
ANNOTATE_CFG(show_nr_jumps), ANNOTATE_CFG(show_nr_jumps),
ANNOTATE_CFG(use_offset), ANNOTATE_CFG(use_offset),
}; };

View File

@ -463,23 +463,6 @@ out:
return key; return key;
} }
static char *callchain_list__sym_name(struct callchain_list *cl,
char *bf, size_t bfsize, bool show_dso)
{
int printed;
if (cl->ms.sym)
printed = scnprintf(bf, bfsize, "%s", cl->ms.sym->name);
else
printed = scnprintf(bf, bfsize, "%#" PRIx64, cl->ip);
if (show_dso)
scnprintf(bf + printed, bfsize - printed, " %s",
cl->ms.map ? cl->ms.map->dso->short_name : "unknown");
return bf;
}
struct callchain_print_arg { struct callchain_print_arg {
/* for hists browser */ /* for hists browser */
off_t row_offset; off_t row_offset;

View File

@ -89,15 +89,6 @@ void perf_gtk__init_hpp(void)
perf_gtk__hpp_color_overhead_acc; perf_gtk__hpp_color_overhead_acc;
} }
static void callchain_list__sym_name(struct callchain_list *cl,
char *bf, size_t bfsize)
{
if (cl->ms.sym)
scnprintf(bf, bfsize, "%s", cl->ms.sym->name);
else
scnprintf(bf, bfsize, "%#" PRIx64, cl->ip);
}
static void perf_gtk__add_callchain(struct rb_root *root, GtkTreeStore *store, static void perf_gtk__add_callchain(struct rb_root *root, GtkTreeStore *store,
GtkTreeIter *parent, int col, u64 total) GtkTreeIter *parent, int col, u64 total)
{ {
@ -128,7 +119,7 @@ static void perf_gtk__add_callchain(struct rb_root *root, GtkTreeStore *store,
scnprintf(buf, sizeof(buf), "%5.2f%%", percent); scnprintf(buf, sizeof(buf), "%5.2f%%", percent);
gtk_tree_store_set(store, &iter, 0, buf, -1); gtk_tree_store_set(store, &iter, 0, buf, -1);
callchain_list__sym_name(chain, buf, sizeof(buf)); callchain_list__sym_name(chain, buf, sizeof(buf), false);
gtk_tree_store_set(store, &iter, col, buf, -1); gtk_tree_store_set(store, &iter, col, buf, -1);
if (need_new_parent) { if (need_new_parent) {

View File

@ -41,6 +41,7 @@ static size_t ipchain__fprintf_graph(FILE *fp, struct callchain_list *chain,
{ {
int i; int i;
size_t ret = 0; size_t ret = 0;
char bf[1024];
ret += callchain__fprintf_left_margin(fp, left_margin); ret += callchain__fprintf_left_margin(fp, left_margin);
for (i = 0; i < depth; i++) { for (i = 0; i < depth; i++) {
@ -56,11 +57,8 @@ static size_t ipchain__fprintf_graph(FILE *fp, struct callchain_list *chain,
} else } else
ret += fprintf(fp, "%s", " "); ret += fprintf(fp, "%s", " ");
} }
if (chain->ms.sym) fputs(callchain_list__sym_name(chain, bf, sizeof(bf), false), fp);
ret += fprintf(fp, "%s\n", chain->ms.sym->name); fputc('\n', fp);
else
ret += fprintf(fp, "0x%0" PRIx64 "\n", chain->ip);
return ret; return ret;
} }
@ -168,6 +166,7 @@ static size_t callchain__fprintf_graph(FILE *fp, struct rb_root *root,
struct rb_node *node; struct rb_node *node;
int i = 0; int i = 0;
int ret = 0; int ret = 0;
char bf[1024];
/* /*
* If have one single callchain root, don't bother printing * If have one single callchain root, don't bother printing
@ -196,10 +195,8 @@ static size_t callchain__fprintf_graph(FILE *fp, struct rb_root *root,
} else } else
ret += callchain__fprintf_left_margin(fp, left_margin); ret += callchain__fprintf_left_margin(fp, left_margin);
if (chain->ms.sym) ret += fprintf(fp, "%s\n", callchain_list__sym_name(chain, bf, sizeof(bf),
ret += fprintf(fp, " %s\n", chain->ms.sym->name); false));
else
ret += fprintf(fp, " %p\n", (void *)(long)chain->ip);
if (++entries_printed == callchain_param.print_limit) if (++entries_printed == callchain_param.print_limit)
break; break;
@ -219,6 +216,7 @@ static size_t __callchain__fprintf_flat(FILE *fp, struct callchain_node *node,
{ {
struct callchain_list *chain; struct callchain_list *chain;
size_t ret = 0; size_t ret = 0;
char bf[1024];
if (!node) if (!node)
return 0; return 0;
@ -229,11 +227,8 @@ static size_t __callchain__fprintf_flat(FILE *fp, struct callchain_node *node,
list_for_each_entry(chain, &node->val, list) { list_for_each_entry(chain, &node->val, list) {
if (chain->ip >= PERF_CONTEXT_MAX) if (chain->ip >= PERF_CONTEXT_MAX)
continue; continue;
if (chain->ms.sym) ret += fprintf(fp, " %s\n", callchain_list__sym_name(chain,
ret += fprintf(fp, " %s\n", chain->ms.sym->name); bf, sizeof(bf), false));
else
ret += fprintf(fp, " %p\n",
(void *)(long)chain->ip);
} }
return ret; return ret;

View File

@ -17,11 +17,13 @@
#include "debug.h" #include "debug.h"
#include "annotate.h" #include "annotate.h"
#include "evsel.h" #include "evsel.h"
#include <regex.h>
#include <pthread.h> #include <pthread.h>
#include <linux/bitops.h> #include <linux/bitops.h>
const char *disassembler_style; const char *disassembler_style;
const char *objdump_path; const char *objdump_path;
static regex_t file_lineno;
static struct ins *ins__find(const char *name); static struct ins *ins__find(const char *name);
static int disasm_line__parse(char *line, char **namep, char **rawp); static int disasm_line__parse(char *line, char **namep, char **rawp);
@ -570,13 +572,15 @@ out_free_name:
return -1; return -1;
} }
static struct disasm_line *disasm_line__new(s64 offset, char *line, size_t privsize) static struct disasm_line *disasm_line__new(s64 offset, char *line,
size_t privsize, int line_nr)
{ {
struct disasm_line *dl = zalloc(sizeof(*dl) + privsize); struct disasm_line *dl = zalloc(sizeof(*dl) + privsize);
if (dl != NULL) { if (dl != NULL) {
dl->offset = offset; dl->offset = offset;
dl->line = strdup(line); dl->line = strdup(line);
dl->line_nr = line_nr;
if (dl->line == NULL) if (dl->line == NULL)
goto out_delete; goto out_delete;
@ -788,13 +792,15 @@ static int disasm_line__print(struct disasm_line *dl, struct symbol *sym, u64 st
* The ops.raw part will be parsed further according to type of the instruction. * The ops.raw part will be parsed further according to type of the instruction.
*/ */
static int symbol__parse_objdump_line(struct symbol *sym, struct map *map, static int symbol__parse_objdump_line(struct symbol *sym, struct map *map,
FILE *file, size_t privsize) FILE *file, size_t privsize,
int *line_nr)
{ {
struct annotation *notes = symbol__annotation(sym); struct annotation *notes = symbol__annotation(sym);
struct disasm_line *dl; struct disasm_line *dl;
char *line = NULL, *parsed_line, *tmp, *tmp2, *c; char *line = NULL, *parsed_line, *tmp, *tmp2, *c;
size_t line_len; size_t line_len;
s64 line_ip, offset = -1; s64 line_ip, offset = -1;
regmatch_t match[2];
if (getline(&line, &line_len, file) < 0) if (getline(&line, &line_len, file) < 0)
return -1; return -1;
@ -812,6 +818,12 @@ static int symbol__parse_objdump_line(struct symbol *sym, struct map *map,
line_ip = -1; line_ip = -1;
parsed_line = line; parsed_line = line;
/* /filename:linenr ? Save line number and ignore. */
if (regexec(&file_lineno, line, 2, match, 0) == 0) {
*line_nr = atoi(line + match[1].rm_so);
return 0;
}
/* /*
* Strip leading spaces: * Strip leading spaces:
*/ */
@ -842,8 +854,9 @@ static int symbol__parse_objdump_line(struct symbol *sym, struct map *map,
parsed_line = tmp2 + 1; parsed_line = tmp2 + 1;
} }
dl = disasm_line__new(offset, parsed_line, privsize); dl = disasm_line__new(offset, parsed_line, privsize, *line_nr);
free(line); free(line);
(*line_nr)++;
if (dl == NULL) if (dl == NULL)
return -1; return -1;
@ -869,6 +882,11 @@ static int symbol__parse_objdump_line(struct symbol *sym, struct map *map,
return 0; return 0;
} }
static __attribute__((constructor)) void symbol__init_regexpr(void)
{
regcomp(&file_lineno, "^/[^:]+:([0-9]+)", REG_EXTENDED);
}
static void delete_last_nop(struct symbol *sym) static void delete_last_nop(struct symbol *sym)
{ {
struct annotation *notes = symbol__annotation(sym); struct annotation *notes = symbol__annotation(sym);
@ -904,6 +922,7 @@ int symbol__annotate(struct symbol *sym, struct map *map, size_t privsize)
char symfs_filename[PATH_MAX]; char symfs_filename[PATH_MAX];
struct kcore_extract kce; struct kcore_extract kce;
bool delete_extract = false; bool delete_extract = false;
int lineno = 0;
if (filename) if (filename)
symbol__join_symfs(symfs_filename, filename); symbol__join_symfs(symfs_filename, filename);
@ -915,6 +934,8 @@ int symbol__annotate(struct symbol *sym, struct map *map, size_t privsize)
return -ENOMEM; return -ENOMEM;
} }
goto fallback; goto fallback;
} else if (dso__is_kcore(dso)) {
goto fallback;
} else if (readlink(symfs_filename, command, sizeof(command)) < 0 || } else if (readlink(symfs_filename, command, sizeof(command)) < 0 ||
strstr(command, "[kernel.kallsyms]") || strstr(command, "[kernel.kallsyms]") ||
access(symfs_filename, R_OK)) { access(symfs_filename, R_OK)) {
@ -982,7 +1003,7 @@ fallback:
snprintf(command, sizeof(command), snprintf(command, sizeof(command),
"%s %s%s --start-address=0x%016" PRIx64 "%s %s%s --start-address=0x%016" PRIx64
" --stop-address=0x%016" PRIx64 " --stop-address=0x%016" PRIx64
" -d %s %s -C %s 2>/dev/null|grep -v %s|expand", " -l -d %s %s -C %s 2>/dev/null|grep -v %s|expand",
objdump_path ? objdump_path : "objdump", objdump_path ? objdump_path : "objdump",
disassembler_style ? "-M " : "", disassembler_style ? "-M " : "",
disassembler_style ? disassembler_style : "", disassembler_style ? disassembler_style : "",
@ -999,7 +1020,8 @@ fallback:
goto out_free_filename; goto out_free_filename;
while (!feof(file)) while (!feof(file))
if (symbol__parse_objdump_line(sym, map, file, privsize) < 0) if (symbol__parse_objdump_line(sym, map, file, privsize,
&lineno) < 0)
break; break;
/* /*

View File

@ -58,6 +58,7 @@ struct disasm_line {
char *line; char *line;
char *name; char *name;
struct ins *ins; struct ins *ins;
int line_nr;
struct ins_operands ops; struct ins_operands ops;
}; };

View File

@ -15,6 +15,11 @@
#include "debug.h" #include "debug.h"
#include "session.h" #include "session.h"
#include "tool.h" #include "tool.h"
#include "header.h"
#include "vdso.h"
static bool no_buildid_cache;
int build_id__mark_dso_hit(struct perf_tool *tool __maybe_unused, int build_id__mark_dso_hit(struct perf_tool *tool __maybe_unused,
union perf_event *event, union perf_event *event,
@ -33,8 +38,7 @@ int build_id__mark_dso_hit(struct perf_tool *tool __maybe_unused,
return -1; return -1;
} }
thread__find_addr_map(thread, machine, cpumode, MAP__FUNCTION, thread__find_addr_map(thread, cpumode, MAP__FUNCTION, sample->ip, &al);
sample->ip, &al);
if (al.map != NULL) if (al.map != NULL)
al.map->dso->hit = 1; al.map->dso->hit = 1;
@ -106,3 +110,343 @@ char *dso__build_id_filename(const struct dso *dso, char *bf, size_t size)
build_id_hex, build_id_hex + 2); build_id_hex, build_id_hex + 2);
return bf; return bf;
} }
#define dsos__for_each_with_build_id(pos, head) \
list_for_each_entry(pos, head, node) \
if (!pos->has_build_id) \
continue; \
else
static int write_buildid(const char *name, size_t name_len, u8 *build_id,
pid_t pid, u16 misc, int fd)
{
int err;
struct build_id_event b;
size_t len;
len = name_len + 1;
len = PERF_ALIGN(len, NAME_ALIGN);
memset(&b, 0, sizeof(b));
memcpy(&b.build_id, build_id, BUILD_ID_SIZE);
b.pid = pid;
b.header.misc = misc;
b.header.size = sizeof(b) + len;
err = writen(fd, &b, sizeof(b));
if (err < 0)
return err;
return write_padded(fd, name, name_len + 1, len);
}
static int __dsos__write_buildid_table(struct list_head *head,
struct machine *machine,
pid_t pid, u16 misc, int fd)
{
char nm[PATH_MAX];
struct dso *pos;
dsos__for_each_with_build_id(pos, head) {
int err;
const char *name;
size_t name_len;
if (!pos->hit)
continue;
if (dso__is_vdso(pos)) {
name = pos->short_name;
name_len = pos->short_name_len + 1;
} else if (dso__is_kcore(pos)) {
machine__mmap_name(machine, nm, sizeof(nm));
name = nm;
name_len = strlen(nm) + 1;
} else {
name = pos->long_name;
name_len = pos->long_name_len + 1;
}
err = write_buildid(name, name_len, pos->build_id,
pid, misc, fd);
if (err)
return err;
}
return 0;
}
static int machine__write_buildid_table(struct machine *machine, int fd)
{
int err;
u16 kmisc = PERF_RECORD_MISC_KERNEL,
umisc = PERF_RECORD_MISC_USER;
if (!machine__is_host(machine)) {
kmisc = PERF_RECORD_MISC_GUEST_KERNEL;
umisc = PERF_RECORD_MISC_GUEST_USER;
}
err = __dsos__write_buildid_table(&machine->kernel_dsos.head, machine,
machine->pid, kmisc, fd);
if (err == 0)
err = __dsos__write_buildid_table(&machine->user_dsos.head,
machine, machine->pid, umisc,
fd);
return err;
}
int perf_session__write_buildid_table(struct perf_session *session, int fd)
{
struct rb_node *nd;
int err = machine__write_buildid_table(&session->machines.host, fd);
if (err)
return err;
for (nd = rb_first(&session->machines.guests); nd; nd = rb_next(nd)) {
struct machine *pos = rb_entry(nd, struct machine, rb_node);
err = machine__write_buildid_table(pos, fd);
if (err)
break;
}
return err;
}
static int __dsos__hit_all(struct list_head *head)
{
struct dso *pos;
list_for_each_entry(pos, head, node)
pos->hit = true;
return 0;
}
static int machine__hit_all_dsos(struct machine *machine)
{
int err;
err = __dsos__hit_all(&machine->kernel_dsos.head);
if (err)
return err;
return __dsos__hit_all(&machine->user_dsos.head);
}
int dsos__hit_all(struct perf_session *session)
{
struct rb_node *nd;
int err;
err = machine__hit_all_dsos(&session->machines.host);
if (err)
return err;
for (nd = rb_first(&session->machines.guests); nd; nd = rb_next(nd)) {
struct machine *pos = rb_entry(nd, struct machine, rb_node);
err = machine__hit_all_dsos(pos);
if (err)
return err;
}
return 0;
}
void disable_buildid_cache(void)
{
no_buildid_cache = true;
}
int build_id_cache__add_s(const char *sbuild_id, const char *debugdir,
const char *name, bool is_kallsyms, bool is_vdso)
{
const size_t size = PATH_MAX;
char *realname, *filename = zalloc(size),
*linkname = zalloc(size), *targetname;
int len, err = -1;
bool slash = is_kallsyms || is_vdso;
if (is_kallsyms) {
if (symbol_conf.kptr_restrict) {
pr_debug("Not caching a kptr_restrict'ed /proc/kallsyms\n");
err = 0;
goto out_free;
}
realname = (char *) name;
} else
realname = realpath(name, NULL);
if (realname == NULL || filename == NULL || linkname == NULL)
goto out_free;
len = scnprintf(filename, size, "%s%s%s",
debugdir, slash ? "/" : "",
is_vdso ? DSO__NAME_VDSO : realname);
if (mkdir_p(filename, 0755))
goto out_free;
snprintf(filename + len, size - len, "/%s", sbuild_id);
if (access(filename, F_OK)) {
if (is_kallsyms) {
if (copyfile("/proc/kallsyms", filename))
goto out_free;
} else if (link(realname, filename) && copyfile(name, filename))
goto out_free;
}
len = scnprintf(linkname, size, "%s/.build-id/%.2s",
debugdir, sbuild_id);
if (access(linkname, X_OK) && mkdir_p(linkname, 0755))
goto out_free;
snprintf(linkname + len, size - len, "/%s", sbuild_id + 2);
targetname = filename + strlen(debugdir) - 5;
memcpy(targetname, "../..", 5);
if (symlink(targetname, linkname) == 0)
err = 0;
out_free:
if (!is_kallsyms)
free(realname);
free(filename);
free(linkname);
return err;
}
static int build_id_cache__add_b(const u8 *build_id, size_t build_id_size,
const char *name, const char *debugdir,
bool is_kallsyms, bool is_vdso)
{
char sbuild_id[BUILD_ID_SIZE * 2 + 1];
build_id__sprintf(build_id, build_id_size, sbuild_id);
return build_id_cache__add_s(sbuild_id, debugdir, name,
is_kallsyms, is_vdso);
}
int build_id_cache__remove_s(const char *sbuild_id, const char *debugdir)
{
const size_t size = PATH_MAX;
char *filename = zalloc(size),
*linkname = zalloc(size);
int err = -1;
if (filename == NULL || linkname == NULL)
goto out_free;
snprintf(linkname, size, "%s/.build-id/%.2s/%s",
debugdir, sbuild_id, sbuild_id + 2);
if (access(linkname, F_OK))
goto out_free;
if (readlink(linkname, filename, size - 1) < 0)
goto out_free;
if (unlink(linkname))
goto out_free;
/*
* Since the link is relative, we must make it absolute:
*/
snprintf(linkname, size, "%s/.build-id/%.2s/%s",
debugdir, sbuild_id, filename);
if (unlink(linkname))
goto out_free;
err = 0;
out_free:
free(filename);
free(linkname);
return err;
}
static int dso__cache_build_id(struct dso *dso, struct machine *machine,
const char *debugdir)
{
bool is_kallsyms = dso->kernel && dso->long_name[0] != '/';
bool is_vdso = dso__is_vdso(dso);
const char *name = dso->long_name;
char nm[PATH_MAX];
if (dso__is_kcore(dso)) {
is_kallsyms = true;
machine__mmap_name(machine, nm, sizeof(nm));
name = nm;
}
return build_id_cache__add_b(dso->build_id, sizeof(dso->build_id), name,
debugdir, is_kallsyms, is_vdso);
}
static int __dsos__cache_build_ids(struct list_head *head,
struct machine *machine, const char *debugdir)
{
struct dso *pos;
int err = 0;
dsos__for_each_with_build_id(pos, head)
if (dso__cache_build_id(pos, machine, debugdir))
err = -1;
return err;
}
static int machine__cache_build_ids(struct machine *machine, const char *debugdir)
{
int ret = __dsos__cache_build_ids(&machine->kernel_dsos.head, machine,
debugdir);
ret |= __dsos__cache_build_ids(&machine->user_dsos.head, machine,
debugdir);
return ret;
}
int perf_session__cache_build_ids(struct perf_session *session)
{
struct rb_node *nd;
int ret;
char debugdir[PATH_MAX];
if (no_buildid_cache)
return 0;
snprintf(debugdir, sizeof(debugdir), "%s", buildid_dir);
if (mkdir(debugdir, 0755) != 0 && errno != EEXIST)
return -1;
ret = machine__cache_build_ids(&session->machines.host, debugdir);
for (nd = rb_first(&session->machines.guests); nd; nd = rb_next(nd)) {
struct machine *pos = rb_entry(nd, struct machine, rb_node);
ret |= machine__cache_build_ids(pos, debugdir);
}
return ret ? -1 : 0;
}
static bool machine__read_build_ids(struct machine *machine, bool with_hits)
{
bool ret;
ret = __dsos__read_build_ids(&machine->kernel_dsos.head, with_hits);
ret |= __dsos__read_build_ids(&machine->user_dsos.head, with_hits);
return ret;
}
bool perf_session__read_build_ids(struct perf_session *session, bool with_hits)
{
struct rb_node *nd;
bool ret = machine__read_build_ids(&session->machines.host, with_hits);
for (nd = rb_first(&session->machines.guests); nd; nd = rb_next(nd)) {
struct machine *pos = rb_entry(nd, struct machine, rb_node);
ret |= machine__read_build_ids(pos, with_hits);
}
return ret;
}

View File

@ -15,4 +15,16 @@ char *dso__build_id_filename(const struct dso *dso, char *bf, size_t size);
int build_id__mark_dso_hit(struct perf_tool *tool, union perf_event *event, int build_id__mark_dso_hit(struct perf_tool *tool, union perf_event *event,
struct perf_sample *sample, struct perf_evsel *evsel, struct perf_sample *sample, struct perf_evsel *evsel,
struct machine *machine); struct machine *machine);
int dsos__hit_all(struct perf_session *session);
bool perf_session__read_build_ids(struct perf_session *session, bool with_hits);
int perf_session__write_buildid_table(struct perf_session *session, int fd);
int perf_session__cache_build_ids(struct perf_session *session);
int build_id_cache__add_s(const char *sbuild_id, const char *debugdir,
const char *name, bool is_kallsyms, bool is_vdso);
int build_id_cache__remove_s(const char *sbuild_id, const char *debugdir);
void disable_buildid_cache(void);
#endif #endif

View File

@ -754,8 +754,8 @@ int sample__resolve_callchain(struct perf_sample *sample, struct symbol **parent
if (symbol_conf.use_callchain || symbol_conf.cumulate_callchain || if (symbol_conf.use_callchain || symbol_conf.cumulate_callchain ||
sort__has_parent) { sort__has_parent) {
return machine__resolve_callchain(al->machine, evsel, al->thread, return thread__resolve_callchain(al->thread, evsel, sample,
sample, parent, al, max_stack); parent, al, max_stack);
} }
return 0; return 0;
} }
@ -808,3 +808,22 @@ int fill_callchain_info(struct addr_location *al, struct callchain_cursor_node *
out: out:
return 1; return 1;
} }
char *callchain_list__sym_name(struct callchain_list *cl,
char *bf, size_t bfsize, bool show_dso)
{
int printed;
if (cl->ms.sym) {
printed = scnprintf(bf, bfsize, "%s", cl->ms.sym->name);
} else
printed = scnprintf(bf, bfsize, "%#" PRIx64, cl->ip);
if (show_dso)
scnprintf(bf + printed, bfsize - printed, " %s",
cl->ms.map ?
cl->ms.map->dso->short_name :
"unknown");
return bf;
}

View File

@ -184,15 +184,16 @@ static inline void callchain_cursor_snapshot(struct callchain_cursor *dest,
} }
#ifdef HAVE_SKIP_CALLCHAIN_IDX #ifdef HAVE_SKIP_CALLCHAIN_IDX
extern int arch_skip_callchain_idx(struct machine *machine, extern int arch_skip_callchain_idx(struct thread *thread, struct ip_callchain *chain);
struct thread *thread, struct ip_callchain *chain);
#else #else
static inline int arch_skip_callchain_idx(struct machine *machine __maybe_unused, static inline int arch_skip_callchain_idx(struct thread *thread __maybe_unused,
struct thread *thread __maybe_unused,
struct ip_callchain *chain __maybe_unused) struct ip_callchain *chain __maybe_unused)
{ {
return -1; return -1;
} }
#endif #endif
char *callchain_list__sym_name(struct callchain_list *cl,
char *bf, size_t bfsize, bool show_dso);
#endif /* __PERF_CALLCHAIN_H */ #endif /* __PERF_CALLCHAIN_H */

View File

@ -12,6 +12,10 @@ struct comm {
u64 start; u64 start;
struct list_head list; struct list_head list;
bool exec; bool exec;
union { /* Tool specific area */
void *priv;
u64 db_id;
};
}; };
void comm__free(struct comm *comm); void comm__free(struct comm *comm);

428
tools/perf/util/db-export.c Normal file
View File

@ -0,0 +1,428 @@
/*
* db-export.c: Support for exporting data suitable for import to a database
* Copyright (c) 2014, Intel Corporation.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope 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.
*
*/
#include <errno.h>
#include "evsel.h"
#include "machine.h"
#include "thread.h"
#include "comm.h"
#include "symbol.h"
#include "event.h"
#include "util.h"
#include "thread-stack.h"
#include "db-export.h"
struct deferred_export {
struct list_head node;
struct comm *comm;
};
static int db_export__deferred(struct db_export *dbe)
{
struct deferred_export *de;
int err;
while (!list_empty(&dbe->deferred)) {
de = list_entry(dbe->deferred.next, struct deferred_export,
node);
err = dbe->export_comm(dbe, de->comm);
list_del(&de->node);
free(de);
if (err)
return err;
}
return 0;
}
static void db_export__free_deferred(struct db_export *dbe)
{
struct deferred_export *de;
while (!list_empty(&dbe->deferred)) {
de = list_entry(dbe->deferred.next, struct deferred_export,
node);
list_del(&de->node);
free(de);
}
}
static int db_export__defer_comm(struct db_export *dbe, struct comm *comm)
{
struct deferred_export *de;
de = zalloc(sizeof(struct deferred_export));
if (!de)
return -ENOMEM;
de->comm = comm;
list_add_tail(&de->node, &dbe->deferred);
return 0;
}
int db_export__init(struct db_export *dbe)
{
memset(dbe, 0, sizeof(struct db_export));
INIT_LIST_HEAD(&dbe->deferred);
return 0;
}
int db_export__flush(struct db_export *dbe)
{
return db_export__deferred(dbe);
}
void db_export__exit(struct db_export *dbe)
{
db_export__free_deferred(dbe);
call_return_processor__free(dbe->crp);
dbe->crp = NULL;
}
int db_export__evsel(struct db_export *dbe, struct perf_evsel *evsel)
{
if (evsel->db_id)
return 0;
evsel->db_id = ++dbe->evsel_last_db_id;
if (dbe->export_evsel)
return dbe->export_evsel(dbe, evsel);
return 0;
}
int db_export__machine(struct db_export *dbe, struct machine *machine)
{
if (machine->db_id)
return 0;
machine->db_id = ++dbe->machine_last_db_id;
if (dbe->export_machine)
return dbe->export_machine(dbe, machine);
return 0;
}
int db_export__thread(struct db_export *dbe, struct thread *thread,
struct machine *machine, struct comm *comm)
{
u64 main_thread_db_id = 0;
int err;
if (thread->db_id)
return 0;
thread->db_id = ++dbe->thread_last_db_id;
if (thread->pid_ != -1) {
struct thread *main_thread;
if (thread->pid_ == thread->tid) {
main_thread = thread;
} else {
main_thread = machine__findnew_thread(machine,
thread->pid_,
thread->pid_);
if (!main_thread)
return -ENOMEM;
err = db_export__thread(dbe, main_thread, machine,
comm);
if (err)
return err;
if (comm) {
err = db_export__comm_thread(dbe, comm, thread);
if (err)
return err;
}
}
main_thread_db_id = main_thread->db_id;
}
if (dbe->export_thread)
return dbe->export_thread(dbe, thread, main_thread_db_id,
machine);
return 0;
}
int db_export__comm(struct db_export *dbe, struct comm *comm,
struct thread *main_thread)
{
int err;
if (comm->db_id)
return 0;
comm->db_id = ++dbe->comm_last_db_id;
if (dbe->export_comm) {
if (main_thread->comm_set)
err = dbe->export_comm(dbe, comm);
else
err = db_export__defer_comm(dbe, comm);
if (err)
return err;
}
return db_export__comm_thread(dbe, comm, main_thread);
}
int db_export__comm_thread(struct db_export *dbe, struct comm *comm,
struct thread *thread)
{
u64 db_id;
db_id = ++dbe->comm_thread_last_db_id;
if (dbe->export_comm_thread)
return dbe->export_comm_thread(dbe, db_id, comm, thread);
return 0;
}
int db_export__dso(struct db_export *dbe, struct dso *dso,
struct machine *machine)
{
if (dso->db_id)
return 0;
dso->db_id = ++dbe->dso_last_db_id;
if (dbe->export_dso)
return dbe->export_dso(dbe, dso, machine);
return 0;
}
int db_export__symbol(struct db_export *dbe, struct symbol *sym,
struct dso *dso)
{
u64 *sym_db_id = symbol__priv(sym);
if (*sym_db_id)
return 0;
*sym_db_id = ++dbe->symbol_last_db_id;
if (dbe->export_symbol)
return dbe->export_symbol(dbe, sym, dso);
return 0;
}
static struct thread *get_main_thread(struct machine *machine, struct thread *thread)
{
if (thread->pid_ == thread->tid)
return thread;
if (thread->pid_ == -1)
return NULL;
return machine__find_thread(machine, thread->pid_, thread->pid_);
}
static int db_ids_from_al(struct db_export *dbe, struct addr_location *al,
u64 *dso_db_id, u64 *sym_db_id, u64 *offset)
{
int err;
if (al->map) {
struct dso *dso = al->map->dso;
err = db_export__dso(dbe, dso, al->machine);
if (err)
return err;
*dso_db_id = dso->db_id;
if (!al->sym) {
al->sym = symbol__new(al->addr, 0, 0, "unknown");
if (al->sym)
symbols__insert(&dso->symbols[al->map->type],
al->sym);
}
if (al->sym) {
u64 *db_id = symbol__priv(al->sym);
err = db_export__symbol(dbe, al->sym, dso);
if (err)
return err;
*sym_db_id = *db_id;
*offset = al->addr - al->sym->start;
}
}
return 0;
}
int db_export__branch_type(struct db_export *dbe, u32 branch_type,
const char *name)
{
if (dbe->export_branch_type)
return dbe->export_branch_type(dbe, branch_type, name);
return 0;
}
int db_export__sample(struct db_export *dbe, union perf_event *event,
struct perf_sample *sample, struct perf_evsel *evsel,
struct thread *thread, struct addr_location *al)
{
struct export_sample es = {
.event = event,
.sample = sample,
.evsel = evsel,
.thread = thread,
.al = al,
};
struct thread *main_thread;
struct comm *comm = NULL;
int err;
err = db_export__evsel(dbe, evsel);
if (err)
return err;
err = db_export__machine(dbe, al->machine);
if (err)
return err;
main_thread = get_main_thread(al->machine, thread);
if (main_thread)
comm = machine__thread_exec_comm(al->machine, main_thread);
err = db_export__thread(dbe, thread, al->machine, comm);
if (err)
return err;
if (comm) {
err = db_export__comm(dbe, comm, main_thread);
if (err)
return err;
es.comm_db_id = comm->db_id;
}
es.db_id = ++dbe->sample_last_db_id;
err = db_ids_from_al(dbe, al, &es.dso_db_id, &es.sym_db_id, &es.offset);
if (err)
return err;
if ((evsel->attr.sample_type & PERF_SAMPLE_ADDR) &&
sample_addr_correlates_sym(&evsel->attr)) {
struct addr_location addr_al;
perf_event__preprocess_sample_addr(event, sample, thread, &addr_al);
err = db_ids_from_al(dbe, &addr_al, &es.addr_dso_db_id,
&es.addr_sym_db_id, &es.addr_offset);
if (err)
return err;
if (dbe->crp) {
err = thread_stack__process(thread, comm, sample, al,
&addr_al, es.db_id,
dbe->crp);
if (err)
return err;
}
}
if (dbe->export_sample)
return dbe->export_sample(dbe, &es);
return 0;
}
static struct {
u32 branch_type;
const char *name;
} branch_types[] = {
{0, "no branch"},
{PERF_IP_FLAG_BRANCH | PERF_IP_FLAG_CALL, "call"},
{PERF_IP_FLAG_BRANCH | PERF_IP_FLAG_RETURN, "return"},
{PERF_IP_FLAG_BRANCH | PERF_IP_FLAG_CONDITIONAL, "conditional jump"},
{PERF_IP_FLAG_BRANCH, "unconditional jump"},
{PERF_IP_FLAG_BRANCH | PERF_IP_FLAG_CALL | PERF_IP_FLAG_INTERRUPT,
"software interrupt"},
{PERF_IP_FLAG_BRANCH | PERF_IP_FLAG_RETURN | PERF_IP_FLAG_INTERRUPT,
"return from interrupt"},
{PERF_IP_FLAG_BRANCH | PERF_IP_FLAG_CALL | PERF_IP_FLAG_SYSCALLRET,
"system call"},
{PERF_IP_FLAG_BRANCH | PERF_IP_FLAG_RETURN | PERF_IP_FLAG_SYSCALLRET,
"return from system call"},
{PERF_IP_FLAG_BRANCH | PERF_IP_FLAG_ASYNC, "asynchronous branch"},
{PERF_IP_FLAG_BRANCH | PERF_IP_FLAG_CALL | PERF_IP_FLAG_ASYNC |
PERF_IP_FLAG_INTERRUPT, "hardware interrupt"},
{PERF_IP_FLAG_BRANCH | PERF_IP_FLAG_TX_ABORT, "transaction abort"},
{PERF_IP_FLAG_BRANCH | PERF_IP_FLAG_TRACE_BEGIN, "trace begin"},
{PERF_IP_FLAG_BRANCH | PERF_IP_FLAG_TRACE_END, "trace end"},
{0, NULL}
};
int db_export__branch_types(struct db_export *dbe)
{
int i, err = 0;
for (i = 0; branch_types[i].name ; i++) {
err = db_export__branch_type(dbe, branch_types[i].branch_type,
branch_types[i].name);
if (err)
break;
}
return err;
}
int db_export__call_path(struct db_export *dbe, struct call_path *cp)
{
int err;
if (cp->db_id)
return 0;
if (cp->parent) {
err = db_export__call_path(dbe, cp->parent);
if (err)
return err;
}
cp->db_id = ++dbe->call_path_last_db_id;
if (dbe->export_call_path)
return dbe->export_call_path(dbe, cp);
return 0;
}
int db_export__call_return(struct db_export *dbe, struct call_return *cr)
{
int err;
if (cr->db_id)
return 0;
err = db_export__call_path(dbe, cr->cp);
if (err)
return err;
cr->db_id = ++dbe->call_return_last_db_id;
if (dbe->export_call_return)
return dbe->export_call_return(dbe, cr);
return 0;
}

107
tools/perf/util/db-export.h Normal file
View File

@ -0,0 +1,107 @@
/*
* db-export.h: Support for exporting data suitable for import to a database
* Copyright (c) 2014, Intel Corporation.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope 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.
*
*/
#ifndef __PERF_DB_EXPORT_H
#define __PERF_DB_EXPORT_H
#include <linux/types.h>
#include <linux/list.h>
struct perf_evsel;
struct machine;
struct thread;
struct comm;
struct dso;
struct perf_sample;
struct addr_location;
struct call_return_processor;
struct call_path;
struct call_return;
struct export_sample {
union perf_event *event;
struct perf_sample *sample;
struct perf_evsel *evsel;
struct thread *thread;
struct addr_location *al;
u64 db_id;
u64 comm_db_id;
u64 dso_db_id;
u64 sym_db_id;
u64 offset; /* ip offset from symbol start */
u64 addr_dso_db_id;
u64 addr_sym_db_id;
u64 addr_offset; /* addr offset from symbol start */
};
struct db_export {
int (*export_evsel)(struct db_export *dbe, struct perf_evsel *evsel);
int (*export_machine)(struct db_export *dbe, struct machine *machine);
int (*export_thread)(struct db_export *dbe, struct thread *thread,
u64 main_thread_db_id, struct machine *machine);
int (*export_comm)(struct db_export *dbe, struct comm *comm);
int (*export_comm_thread)(struct db_export *dbe, u64 db_id,
struct comm *comm, struct thread *thread);
int (*export_dso)(struct db_export *dbe, struct dso *dso,
struct machine *machine);
int (*export_symbol)(struct db_export *dbe, struct symbol *sym,
struct dso *dso);
int (*export_branch_type)(struct db_export *dbe, u32 branch_type,
const char *name);
int (*export_sample)(struct db_export *dbe, struct export_sample *es);
int (*export_call_path)(struct db_export *dbe, struct call_path *cp);
int (*export_call_return)(struct db_export *dbe,
struct call_return *cr);
struct call_return_processor *crp;
u64 evsel_last_db_id;
u64 machine_last_db_id;
u64 thread_last_db_id;
u64 comm_last_db_id;
u64 comm_thread_last_db_id;
u64 dso_last_db_id;
u64 symbol_last_db_id;
u64 sample_last_db_id;
u64 call_path_last_db_id;
u64 call_return_last_db_id;
struct list_head deferred;
};
int db_export__init(struct db_export *dbe);
int db_export__flush(struct db_export *dbe);
void db_export__exit(struct db_export *dbe);
int db_export__evsel(struct db_export *dbe, struct perf_evsel *evsel);
int db_export__machine(struct db_export *dbe, struct machine *machine);
int db_export__thread(struct db_export *dbe, struct thread *thread,
struct machine *machine, struct comm *comm);
int db_export__comm(struct db_export *dbe, struct comm *comm,
struct thread *main_thread);
int db_export__comm_thread(struct db_export *dbe, struct comm *comm,
struct thread *thread);
int db_export__dso(struct db_export *dbe, struct dso *dso,
struct machine *machine);
int db_export__symbol(struct db_export *dbe, struct symbol *sym,
struct dso *dso);
int db_export__branch_type(struct db_export *dbe, u32 branch_type,
const char *name);
int db_export__sample(struct db_export *dbe, union perf_event *event,
struct perf_sample *sample, struct perf_evsel *evsel,
struct thread *thread, struct addr_location *al);
int db_export__branch_types(struct db_export *dbe);
int db_export__call_path(struct db_export *dbe, struct call_path *cp);
int db_export__call_return(struct db_export *dbe, struct call_return *cr);
#endif

View File

@ -21,8 +21,10 @@ char dso__symtab_origin(const struct dso *dso)
[DSO_BINARY_TYPE__BUILDID_DEBUGINFO] = 'b', [DSO_BINARY_TYPE__BUILDID_DEBUGINFO] = 'b',
[DSO_BINARY_TYPE__SYSTEM_PATH_DSO] = 'd', [DSO_BINARY_TYPE__SYSTEM_PATH_DSO] = 'd',
[DSO_BINARY_TYPE__SYSTEM_PATH_KMODULE] = 'K', [DSO_BINARY_TYPE__SYSTEM_PATH_KMODULE] = 'K',
[DSO_BINARY_TYPE__SYSTEM_PATH_KMODULE_COMP] = 'm',
[DSO_BINARY_TYPE__GUEST_KALLSYMS] = 'g', [DSO_BINARY_TYPE__GUEST_KALLSYMS] = 'g',
[DSO_BINARY_TYPE__GUEST_KMODULE] = 'G', [DSO_BINARY_TYPE__GUEST_KMODULE] = 'G',
[DSO_BINARY_TYPE__GUEST_KMODULE_COMP] = 'M',
[DSO_BINARY_TYPE__GUEST_VMLINUX] = 'V', [DSO_BINARY_TYPE__GUEST_VMLINUX] = 'V',
}; };
@ -112,11 +114,13 @@ int dso__read_binary_type_filename(const struct dso *dso,
break; break;
case DSO_BINARY_TYPE__GUEST_KMODULE: case DSO_BINARY_TYPE__GUEST_KMODULE:
case DSO_BINARY_TYPE__GUEST_KMODULE_COMP:
path__join3(filename, size, symbol_conf.symfs, path__join3(filename, size, symbol_conf.symfs,
root_dir, dso->long_name); root_dir, dso->long_name);
break; break;
case DSO_BINARY_TYPE__SYSTEM_PATH_KMODULE: case DSO_BINARY_TYPE__SYSTEM_PATH_KMODULE:
case DSO_BINARY_TYPE__SYSTEM_PATH_KMODULE_COMP:
__symbol__join_symfs(filename, size, dso->long_name); __symbol__join_symfs(filename, size, dso->long_name);
break; break;
@ -137,6 +141,73 @@ int dso__read_binary_type_filename(const struct dso *dso,
return ret; return ret;
} }
static const struct {
const char *fmt;
int (*decompress)(const char *input, int output);
} compressions[] = {
#ifdef HAVE_ZLIB_SUPPORT
{ "gz", gzip_decompress_to_file },
#endif
{ NULL, NULL },
};
bool is_supported_compression(const char *ext)
{
unsigned i;
for (i = 0; compressions[i].fmt; i++) {
if (!strcmp(ext, compressions[i].fmt))
return true;
}
return false;
}
bool is_kmodule_extension(const char *ext)
{
if (strncmp(ext, "ko", 2))
return false;
if (ext[2] == '\0' || (ext[2] == '.' && is_supported_compression(ext+3)))
return true;
return false;
}
bool is_kernel_module(const char *pathname, bool *compressed)
{
const char *ext = strrchr(pathname, '.');
if (ext == NULL)
return false;
if (is_supported_compression(ext + 1)) {
if (compressed)
*compressed = true;
ext -= 3;
} else if (compressed)
*compressed = false;
return is_kmodule_extension(ext + 1);
}
bool decompress_to_file(const char *ext, const char *filename, int output_fd)
{
unsigned i;
for (i = 0; compressions[i].fmt; i++) {
if (!strcmp(ext, compressions[i].fmt))
return !compressions[i].decompress(filename,
output_fd);
}
return false;
}
bool dso__needs_decompress(struct dso *dso)
{
return dso->symtab_type == DSO_BINARY_TYPE__SYSTEM_PATH_KMODULE_COMP ||
dso->symtab_type == DSO_BINARY_TYPE__GUEST_KMODULE_COMP;
}
/* /*
* Global list of open DSOs and the counter. * Global list of open DSOs and the counter.
*/ */

View File

@ -22,7 +22,9 @@ enum dso_binary_type {
DSO_BINARY_TYPE__BUILDID_DEBUGINFO, DSO_BINARY_TYPE__BUILDID_DEBUGINFO,
DSO_BINARY_TYPE__SYSTEM_PATH_DSO, DSO_BINARY_TYPE__SYSTEM_PATH_DSO,
DSO_BINARY_TYPE__GUEST_KMODULE, DSO_BINARY_TYPE__GUEST_KMODULE,
DSO_BINARY_TYPE__GUEST_KMODULE_COMP,
DSO_BINARY_TYPE__SYSTEM_PATH_KMODULE, DSO_BINARY_TYPE__SYSTEM_PATH_KMODULE,
DSO_BINARY_TYPE__SYSTEM_PATH_KMODULE_COMP,
DSO_BINARY_TYPE__KCORE, DSO_BINARY_TYPE__KCORE,
DSO_BINARY_TYPE__GUEST_KCORE, DSO_BINARY_TYPE__GUEST_KCORE,
DSO_BINARY_TYPE__OPENEMBEDDED_DEBUGINFO, DSO_BINARY_TYPE__OPENEMBEDDED_DEBUGINFO,
@ -127,6 +129,7 @@ struct dso {
const char *long_name; const char *long_name;
u16 long_name_len; u16 long_name_len;
u16 short_name_len; u16 short_name_len;
void *dwfl; /* DWARF debug info */
/* dso data file */ /* dso data file */
struct { struct {
@ -138,6 +141,11 @@ struct dso {
struct list_head open_entry; struct list_head open_entry;
} data; } data;
union { /* Tool specific area */
void *priv;
u64 db_id;
};
char name[0]; char name[0];
}; };
@ -179,6 +187,11 @@ int dso__kernel_module_get_build_id(struct dso *dso, const char *root_dir);
char dso__symtab_origin(const struct dso *dso); char dso__symtab_origin(const struct dso *dso);
int dso__read_binary_type_filename(const struct dso *dso, enum dso_binary_type type, int dso__read_binary_type_filename(const struct dso *dso, enum dso_binary_type type,
char *root_dir, char *filename, size_t size); char *root_dir, char *filename, size_t size);
bool is_supported_compression(const char *ext);
bool is_kmodule_extension(const char *ext);
bool is_kernel_module(const char *pathname, bool *compressed);
bool decompress_to_file(const char *ext, const char *filename, int output_fd);
bool dso__needs_decompress(struct dso *dso);
/* /*
* The dso__data_* external interface provides following functions: * The dso__data_* external interface provides following functions:

View File

@ -28,6 +28,7 @@ static const char *perf_event__names[] = {
[PERF_RECORD_HEADER_TRACING_DATA] = "TRACING_DATA", [PERF_RECORD_HEADER_TRACING_DATA] = "TRACING_DATA",
[PERF_RECORD_HEADER_BUILD_ID] = "BUILD_ID", [PERF_RECORD_HEADER_BUILD_ID] = "BUILD_ID",
[PERF_RECORD_FINISHED_ROUND] = "FINISHED_ROUND", [PERF_RECORD_FINISHED_ROUND] = "FINISHED_ROUND",
[PERF_RECORD_ID_INDEX] = "ID_INDEX",
}; };
const char *perf_event__name(unsigned int id) const char *perf_event__name(unsigned int id)
@ -730,12 +731,12 @@ int perf_event__process(struct perf_tool *tool __maybe_unused,
return machine__process_event(machine, event, sample); return machine__process_event(machine, event, sample);
} }
void thread__find_addr_map(struct thread *thread, void thread__find_addr_map(struct thread *thread, u8 cpumode,
struct machine *machine, u8 cpumode,
enum map_type type, u64 addr, enum map_type type, u64 addr,
struct addr_location *al) struct addr_location *al)
{ {
struct map_groups *mg = thread->mg; struct map_groups *mg = thread->mg;
struct machine *machine = mg->machine;
bool load_map = false; bool load_map = false;
al->machine = machine; al->machine = machine;
@ -806,14 +807,14 @@ try_again:
} }
} }
void thread__find_addr_location(struct thread *thread, struct machine *machine, void thread__find_addr_location(struct thread *thread,
u8 cpumode, enum map_type type, u64 addr, u8 cpumode, enum map_type type, u64 addr,
struct addr_location *al) struct addr_location *al)
{ {
thread__find_addr_map(thread, machine, cpumode, type, addr, al); thread__find_addr_map(thread, cpumode, type, addr, al);
if (al->map != NULL) if (al->map != NULL)
al->sym = map__find_symbol(al->map, al->addr, al->sym = map__find_symbol(al->map, al->addr,
machine->symbol_filter); thread->mg->machine->symbol_filter);
else else
al->sym = NULL; al->sym = NULL;
} }
@ -842,8 +843,7 @@ int perf_event__preprocess_sample(const union perf_event *event,
machine->vmlinux_maps[MAP__FUNCTION] == NULL) machine->vmlinux_maps[MAP__FUNCTION] == NULL)
machine__create_kernel_maps(machine); machine__create_kernel_maps(machine);
thread__find_addr_map(thread, machine, cpumode, MAP__FUNCTION, thread__find_addr_map(thread, cpumode, MAP__FUNCTION, sample->ip, al);
sample->ip, al);
dump_printf(" ...... dso: %s\n", dump_printf(" ...... dso: %s\n",
al->map ? al->map->dso->long_name : al->map ? al->map->dso->long_name :
al->level == 'H' ? "[hypervisor]" : "<not found>"); al->level == 'H' ? "[hypervisor]" : "<not found>");
@ -902,16 +902,14 @@ bool sample_addr_correlates_sym(struct perf_event_attr *attr)
void perf_event__preprocess_sample_addr(union perf_event *event, void perf_event__preprocess_sample_addr(union perf_event *event,
struct perf_sample *sample, struct perf_sample *sample,
struct machine *machine,
struct thread *thread, struct thread *thread,
struct addr_location *al) struct addr_location *al)
{ {
u8 cpumode = event->header.misc & PERF_RECORD_MISC_CPUMODE_MASK; u8 cpumode = event->header.misc & PERF_RECORD_MISC_CPUMODE_MASK;
thread__find_addr_map(thread, machine, cpumode, MAP__FUNCTION, thread__find_addr_map(thread, cpumode, MAP__FUNCTION, sample->addr, al);
sample->addr, al);
if (!al->map) if (!al->map)
thread__find_addr_map(thread, machine, cpumode, MAP__VARIABLE, thread__find_addr_map(thread, cpumode, MAP__VARIABLE,
sample->addr, al); sample->addr, al);
al->cpu = sample->cpu; al->cpu = sample->cpu;

View File

@ -143,6 +143,32 @@ struct branch_stack {
struct branch_entry entries[0]; struct branch_entry entries[0];
}; };
enum {
PERF_IP_FLAG_BRANCH = 1ULL << 0,
PERF_IP_FLAG_CALL = 1ULL << 1,
PERF_IP_FLAG_RETURN = 1ULL << 2,
PERF_IP_FLAG_CONDITIONAL = 1ULL << 3,
PERF_IP_FLAG_SYSCALLRET = 1ULL << 4,
PERF_IP_FLAG_ASYNC = 1ULL << 5,
PERF_IP_FLAG_INTERRUPT = 1ULL << 6,
PERF_IP_FLAG_TX_ABORT = 1ULL << 7,
PERF_IP_FLAG_TRACE_BEGIN = 1ULL << 8,
PERF_IP_FLAG_TRACE_END = 1ULL << 9,
PERF_IP_FLAG_IN_TX = 1ULL << 10,
};
#define PERF_BRANCH_MASK (\
PERF_IP_FLAG_BRANCH |\
PERF_IP_FLAG_CALL |\
PERF_IP_FLAG_RETURN |\
PERF_IP_FLAG_CONDITIONAL |\
PERF_IP_FLAG_SYSCALLRET |\
PERF_IP_FLAG_ASYNC |\
PERF_IP_FLAG_INTERRUPT |\
PERF_IP_FLAG_TX_ABORT |\
PERF_IP_FLAG_TRACE_BEGIN |\
PERF_IP_FLAG_TRACE_END)
struct perf_sample { struct perf_sample {
u64 ip; u64 ip;
u32 pid, tid; u32 pid, tid;
@ -162,6 +188,7 @@ struct perf_sample {
struct ip_callchain *callchain; struct ip_callchain *callchain;
struct branch_stack *branch_stack; struct branch_stack *branch_stack;
struct regs_dump user_regs; struct regs_dump user_regs;
struct regs_dump intr_regs;
struct stack_dump user_stack; struct stack_dump user_stack;
struct sample_read read; struct sample_read read;
}; };
@ -187,6 +214,7 @@ enum perf_user_event_type { /* above any possible kernel type */
PERF_RECORD_HEADER_TRACING_DATA = 66, PERF_RECORD_HEADER_TRACING_DATA = 66,
PERF_RECORD_HEADER_BUILD_ID = 67, PERF_RECORD_HEADER_BUILD_ID = 67,
PERF_RECORD_FINISHED_ROUND = 68, PERF_RECORD_FINISHED_ROUND = 68,
PERF_RECORD_ID_INDEX = 69,
PERF_RECORD_HEADER_MAX PERF_RECORD_HEADER_MAX
}; };
@ -239,6 +267,19 @@ struct tracing_data_event {
u32 size; u32 size;
}; };
struct id_index_entry {
u64 id;
u64 idx;
u64 cpu;
u64 tid;
};
struct id_index_event {
struct perf_event_header header;
u64 nr;
struct id_index_entry entries[0];
};
union perf_event { union perf_event {
struct perf_event_header header; struct perf_event_header header;
struct mmap_event mmap; struct mmap_event mmap;
@ -253,6 +294,7 @@ union perf_event {
struct event_type_event event_type; struct event_type_event event_type;
struct tracing_data_event tracing_data; struct tracing_data_event tracing_data;
struct build_id_event build_id; struct build_id_event build_id;
struct id_index_event id_index;
}; };
void perf_event__print_totals(void); void perf_event__print_totals(void);
@ -322,7 +364,6 @@ bool is_bts_event(struct perf_event_attr *attr);
bool sample_addr_correlates_sym(struct perf_event_attr *attr); bool sample_addr_correlates_sym(struct perf_event_attr *attr);
void perf_event__preprocess_sample_addr(union perf_event *event, void perf_event__preprocess_sample_addr(union perf_event *event,
struct perf_sample *sample, struct perf_sample *sample,
struct machine *machine,
struct thread *thread, struct thread *thread,
struct addr_location *al); struct addr_location *al);

View File

@ -413,7 +413,7 @@ int perf_evlist__alloc_pollfd(struct perf_evlist *evlist)
int nfds = 0; int nfds = 0;
struct perf_evsel *evsel; struct perf_evsel *evsel;
list_for_each_entry(evsel, &evlist->entries, node) { evlist__for_each(evlist, evsel) {
if (evsel->system_wide) if (evsel->system_wide)
nfds += nr_cpus; nfds += nr_cpus;
else else
@ -527,6 +527,22 @@ static int perf_evlist__id_add_fd(struct perf_evlist *evlist,
return 0; return 0;
} }
static void perf_evlist__set_sid_idx(struct perf_evlist *evlist,
struct perf_evsel *evsel, int idx, int cpu,
int thread)
{
struct perf_sample_id *sid = SID(evsel, cpu, thread);
sid->idx = idx;
if (evlist->cpus && cpu >= 0)
sid->cpu = evlist->cpus->map[cpu];
else
sid->cpu = -1;
if (!evsel->system_wide && evlist->threads && thread >= 0)
sid->tid = evlist->threads->map[thread];
else
sid->tid = -1;
}
struct perf_sample_id *perf_evlist__id2sid(struct perf_evlist *evlist, u64 id) struct perf_sample_id *perf_evlist__id2sid(struct perf_evlist *evlist, u64 id)
{ {
struct hlist_head *head; struct hlist_head *head;
@ -800,14 +816,26 @@ static int perf_evlist__mmap_per_evsel(struct perf_evlist *evlist, int idx,
perf_evlist__mmap_get(evlist, idx); perf_evlist__mmap_get(evlist, idx);
} }
if (__perf_evlist__add_pollfd(evlist, fd, idx) < 0) { /*
* The system_wide flag causes a selected event to be opened
* always without a pid. Consequently it will never get a
* POLLHUP, but it is used for tracking in combination with
* other events, so it should not need to be polled anyway.
* Therefore don't add it for polling.
*/
if (!evsel->system_wide &&
__perf_evlist__add_pollfd(evlist, fd, idx) < 0) {
perf_evlist__mmap_put(evlist, idx); perf_evlist__mmap_put(evlist, idx);
return -1; return -1;
} }
if ((evsel->attr.read_format & PERF_FORMAT_ID) && if (evsel->attr.read_format & PERF_FORMAT_ID) {
perf_evlist__id_add_fd(evlist, evsel, cpu, thread, fd) < 0) if (perf_evlist__id_add_fd(evlist, evsel, cpu, thread,
return -1; fd) < 0)
return -1;
perf_evlist__set_sid_idx(evlist, evsel, idx, cpu,
thread);
}
} }
return 0; return 0;

View File

@ -658,9 +658,22 @@ void perf_evsel__config(struct perf_evsel *evsel, struct record_opts *opts)
attr->mmap_data = track; attr->mmap_data = track;
} }
/*
* We don't allow user space callchains for function trace
* event, due to issues with page faults while tracing page
* fault handler and its overall trickiness nature.
*/
if (perf_evsel__is_function_event(evsel))
evsel->attr.exclude_callchain_user = 1;
if (callchain_param.enabled && !evsel->no_aux_samples) if (callchain_param.enabled && !evsel->no_aux_samples)
perf_evsel__config_callgraph(evsel); perf_evsel__config_callgraph(evsel);
if (opts->sample_intr_regs) {
attr->sample_regs_intr = PERF_REGS_MASK;
perf_evsel__set_sample_bit(evsel, REGS_INTR);
}
if (target__has_cpu(&opts->target)) if (target__has_cpu(&opts->target))
perf_evsel__set_sample_bit(evsel, CPU); perf_evsel__set_sample_bit(evsel, CPU);
@ -853,8 +866,6 @@ void perf_evsel__exit(struct perf_evsel *evsel)
perf_evsel__free_id(evsel); perf_evsel__free_id(evsel);
close_cgroup(evsel->cgrp); close_cgroup(evsel->cgrp);
zfree(&evsel->group_name); zfree(&evsel->group_name);
if (evsel->tp_format)
pevent_free_format(evsel->tp_format);
zfree(&evsel->name); zfree(&evsel->name);
perf_evsel__object.fini(evsel); perf_evsel__object.fini(evsel);
} }
@ -1039,6 +1050,7 @@ static size_t perf_event_attr__fprintf(struct perf_event_attr *attr, FILE *fp)
ret += PRINT_ATTR_X64(branch_sample_type); ret += PRINT_ATTR_X64(branch_sample_type);
ret += PRINT_ATTR_X64(sample_regs_user); ret += PRINT_ATTR_X64(sample_regs_user);
ret += PRINT_ATTR_U32(sample_stack_user); ret += PRINT_ATTR_U32(sample_stack_user);
ret += PRINT_ATTR_X64(sample_regs_intr);
ret += fprintf(fp, "%.60s\n", graph_dotted_line); ret += fprintf(fp, "%.60s\n", graph_dotted_line);
@ -1538,6 +1550,23 @@ int perf_evsel__parse_sample(struct perf_evsel *evsel, union perf_event *event,
array++; array++;
} }
data->intr_regs.abi = PERF_SAMPLE_REGS_ABI_NONE;
if (type & PERF_SAMPLE_REGS_INTR) {
OVERFLOW_CHECK_u64(array);
data->intr_regs.abi = *array;
array++;
if (data->intr_regs.abi != PERF_SAMPLE_REGS_ABI_NONE) {
u64 mask = evsel->attr.sample_regs_intr;
sz = hweight_long(mask) * sizeof(u64);
OVERFLOW_CHECK(array, sz, max_size);
data->intr_regs.mask = mask;
data->intr_regs.regs = (u64 *)array;
array = (void *)array + sz;
}
}
return 0; return 0;
} }
@ -1633,6 +1662,16 @@ size_t perf_event__sample_event_size(const struct perf_sample *sample, u64 type,
if (type & PERF_SAMPLE_TRANSACTION) if (type & PERF_SAMPLE_TRANSACTION)
result += sizeof(u64); result += sizeof(u64);
if (type & PERF_SAMPLE_REGS_INTR) {
if (sample->intr_regs.abi) {
result += sizeof(u64);
sz = hweight_long(sample->intr_regs.mask) * sizeof(u64);
result += sz;
} else {
result += sizeof(u64);
}
}
return result; return result;
} }
@ -1811,6 +1850,17 @@ int perf_event__synthesize_sample(union perf_event *event, u64 type,
array++; array++;
} }
if (type & PERF_SAMPLE_REGS_INTR) {
if (sample->intr_regs.abi) {
*array++ = sample->intr_regs.abi;
sz = hweight_long(sample->intr_regs.mask) * sizeof(u64);
memcpy(array, sample->intr_regs.regs, sz);
array = (void *)array + sz;
} else {
*array++ = 0;
}
}
return 0; return 0;
} }
@ -1940,7 +1990,7 @@ static int sample_type__fprintf(FILE *fp, bool *first, u64 value)
bit_name(READ), bit_name(CALLCHAIN), bit_name(ID), bit_name(CPU), bit_name(READ), bit_name(CALLCHAIN), bit_name(ID), bit_name(CPU),
bit_name(PERIOD), bit_name(STREAM_ID), bit_name(RAW), bit_name(PERIOD), bit_name(STREAM_ID), bit_name(RAW),
bit_name(BRANCH_STACK), bit_name(REGS_USER), bit_name(STACK_USER), bit_name(BRANCH_STACK), bit_name(REGS_USER), bit_name(STACK_USER),
bit_name(IDENTIFIER), bit_name(IDENTIFIER), bit_name(REGS_INTR),
{ .name = NULL, } { .name = NULL, }
}; };
#undef bit_name #undef bit_name

View File

@ -36,6 +36,9 @@ struct perf_sample_id {
struct hlist_node node; struct hlist_node node;
u64 id; u64 id;
struct perf_evsel *evsel; struct perf_evsel *evsel;
int idx;
int cpu;
pid_t tid;
/* Holds total ID period value for PERF_SAMPLE_READ processing. */ /* Holds total ID period value for PERF_SAMPLE_READ processing. */
u64 period; u64 period;
@ -54,6 +57,7 @@ struct cgroup_sel;
* @is_pos: the position (counting backwards) of the event id (PERF_SAMPLE_ID or * @is_pos: the position (counting backwards) of the event id (PERF_SAMPLE_ID or
* PERF_SAMPLE_IDENTIFIER) in a non-sample event i.e. if sample_id_all * PERF_SAMPLE_IDENTIFIER) in a non-sample event i.e. if sample_id_all
* is used there is an id sample appended to non-sample events * is used there is an id sample appended to non-sample events
* @priv: And what is in its containing unnamed union are tool specific
*/ */
struct perf_evsel { struct perf_evsel {
struct list_head node; struct list_head node;
@ -73,6 +77,7 @@ struct perf_evsel {
union { union {
void *priv; void *priv;
off_t id_offset; off_t id_offset;
u64 db_id;
}; };
struct cgroup_sel *cgrp; struct cgroup_sel *cgrp;
void *handler; void *handler;

View File

@ -0,0 +1,30 @@
static int find_vdso_map(void **start, void **end)
{
FILE *maps;
char line[128];
int found = 0;
maps = fopen("/proc/self/maps", "r");
if (!maps) {
fprintf(stderr, "vdso: cannot open maps\n");
return -1;
}
while (!found && fgets(line, sizeof(line), maps)) {
int m = -1;
/* We care only about private r-x mappings. */
if (2 != sscanf(line, "%p-%p r-xp %*x %*x:%*x %*u %n",
start, end, &m))
continue;
if (m < 0)
continue;
if (!strncmp(&line[m], VDSO__MAP_NAME,
sizeof(VDSO__MAP_NAME) - 1))
found = 1;
}
fclose(maps);
return !found;
}

View File

@ -24,8 +24,6 @@
#include "build-id.h" #include "build-id.h"
#include "data.h" #include "data.h"
static bool no_buildid_cache = false;
static u32 header_argc; static u32 header_argc;
static const char **header_argv; static const char **header_argv;
@ -79,10 +77,7 @@ static int do_write(int fd, const void *buf, size_t size)
return 0; return 0;
} }
#define NAME_ALIGN 64 int write_padded(int fd, const void *bf, size_t count, size_t count_aligned)
static int write_padded(int fd, const void *bf, size_t count,
size_t count_aligned)
{ {
static const char zero_buf[NAME_ALIGN]; static const char zero_buf[NAME_ALIGN];
int err = do_write(fd, bf, count); int err = do_write(fd, bf, count);
@ -171,340 +166,6 @@ perf_header__set_cmdline(int argc, const char **argv)
return 0; return 0;
} }
#define dsos__for_each_with_build_id(pos, head) \
list_for_each_entry(pos, head, node) \
if (!pos->has_build_id) \
continue; \
else
static int write_buildid(const char *name, size_t name_len, u8 *build_id,
pid_t pid, u16 misc, int fd)
{
int err;
struct build_id_event b;
size_t len;
len = name_len + 1;
len = PERF_ALIGN(len, NAME_ALIGN);
memset(&b, 0, sizeof(b));
memcpy(&b.build_id, build_id, BUILD_ID_SIZE);
b.pid = pid;
b.header.misc = misc;
b.header.size = sizeof(b) + len;
err = do_write(fd, &b, sizeof(b));
if (err < 0)
return err;
return write_padded(fd, name, name_len + 1, len);
}
static int __dsos__hit_all(struct list_head *head)
{
struct dso *pos;
list_for_each_entry(pos, head, node)
pos->hit = true;
return 0;
}
static int machine__hit_all_dsos(struct machine *machine)
{
int err;
err = __dsos__hit_all(&machine->kernel_dsos.head);
if (err)
return err;
return __dsos__hit_all(&machine->user_dsos.head);
}
int dsos__hit_all(struct perf_session *session)
{
struct rb_node *nd;
int err;
err = machine__hit_all_dsos(&session->machines.host);
if (err)
return err;
for (nd = rb_first(&session->machines.guests); nd; nd = rb_next(nd)) {
struct machine *pos = rb_entry(nd, struct machine, rb_node);
err = machine__hit_all_dsos(pos);
if (err)
return err;
}
return 0;
}
static int __dsos__write_buildid_table(struct list_head *head,
struct machine *machine,
pid_t pid, u16 misc, int fd)
{
char nm[PATH_MAX];
struct dso *pos;
dsos__for_each_with_build_id(pos, head) {
int err;
const char *name;
size_t name_len;
if (!pos->hit)
continue;
if (dso__is_vdso(pos)) {
name = pos->short_name;
name_len = pos->short_name_len + 1;
} else if (dso__is_kcore(pos)) {
machine__mmap_name(machine, nm, sizeof(nm));
name = nm;
name_len = strlen(nm) + 1;
} else {
name = pos->long_name;
name_len = pos->long_name_len + 1;
}
err = write_buildid(name, name_len, pos->build_id,
pid, misc, fd);
if (err)
return err;
}
return 0;
}
static int machine__write_buildid_table(struct machine *machine, int fd)
{
int err;
u16 kmisc = PERF_RECORD_MISC_KERNEL,
umisc = PERF_RECORD_MISC_USER;
if (!machine__is_host(machine)) {
kmisc = PERF_RECORD_MISC_GUEST_KERNEL;
umisc = PERF_RECORD_MISC_GUEST_USER;
}
err = __dsos__write_buildid_table(&machine->kernel_dsos.head, machine,
machine->pid, kmisc, fd);
if (err == 0)
err = __dsos__write_buildid_table(&machine->user_dsos.head,
machine, machine->pid, umisc,
fd);
return err;
}
static int dsos__write_buildid_table(struct perf_header *header, int fd)
{
struct perf_session *session = container_of(header,
struct perf_session, header);
struct rb_node *nd;
int err = machine__write_buildid_table(&session->machines.host, fd);
if (err)
return err;
for (nd = rb_first(&session->machines.guests); nd; nd = rb_next(nd)) {
struct machine *pos = rb_entry(nd, struct machine, rb_node);
err = machine__write_buildid_table(pos, fd);
if (err)
break;
}
return err;
}
int build_id_cache__add_s(const char *sbuild_id, const char *debugdir,
const char *name, bool is_kallsyms, bool is_vdso)
{
const size_t size = PATH_MAX;
char *realname, *filename = zalloc(size),
*linkname = zalloc(size), *targetname;
int len, err = -1;
bool slash = is_kallsyms || is_vdso;
if (is_kallsyms) {
if (symbol_conf.kptr_restrict) {
pr_debug("Not caching a kptr_restrict'ed /proc/kallsyms\n");
err = 0;
goto out_free;
}
realname = (char *) name;
} else
realname = realpath(name, NULL);
if (realname == NULL || filename == NULL || linkname == NULL)
goto out_free;
len = scnprintf(filename, size, "%s%s%s",
debugdir, slash ? "/" : "",
is_vdso ? DSO__NAME_VDSO : realname);
if (mkdir_p(filename, 0755))
goto out_free;
snprintf(filename + len, size - len, "/%s", sbuild_id);
if (access(filename, F_OK)) {
if (is_kallsyms) {
if (copyfile("/proc/kallsyms", filename))
goto out_free;
} else if (link(realname, filename) && copyfile(name, filename))
goto out_free;
}
len = scnprintf(linkname, size, "%s/.build-id/%.2s",
debugdir, sbuild_id);
if (access(linkname, X_OK) && mkdir_p(linkname, 0755))
goto out_free;
snprintf(linkname + len, size - len, "/%s", sbuild_id + 2);
targetname = filename + strlen(debugdir) - 5;
memcpy(targetname, "../..", 5);
if (symlink(targetname, linkname) == 0)
err = 0;
out_free:
if (!is_kallsyms)
free(realname);
free(filename);
free(linkname);
return err;
}
static int build_id_cache__add_b(const u8 *build_id, size_t build_id_size,
const char *name, const char *debugdir,
bool is_kallsyms, bool is_vdso)
{
char sbuild_id[BUILD_ID_SIZE * 2 + 1];
build_id__sprintf(build_id, build_id_size, sbuild_id);
return build_id_cache__add_s(sbuild_id, debugdir, name,
is_kallsyms, is_vdso);
}
int build_id_cache__remove_s(const char *sbuild_id, const char *debugdir)
{
const size_t size = PATH_MAX;
char *filename = zalloc(size),
*linkname = zalloc(size);
int err = -1;
if (filename == NULL || linkname == NULL)
goto out_free;
snprintf(linkname, size, "%s/.build-id/%.2s/%s",
debugdir, sbuild_id, sbuild_id + 2);
if (access(linkname, F_OK))
goto out_free;
if (readlink(linkname, filename, size - 1) < 0)
goto out_free;
if (unlink(linkname))
goto out_free;
/*
* Since the link is relative, we must make it absolute:
*/
snprintf(linkname, size, "%s/.build-id/%.2s/%s",
debugdir, sbuild_id, filename);
if (unlink(linkname))
goto out_free;
err = 0;
out_free:
free(filename);
free(linkname);
return err;
}
static int dso__cache_build_id(struct dso *dso, struct machine *machine,
const char *debugdir)
{
bool is_kallsyms = dso->kernel && dso->long_name[0] != '/';
bool is_vdso = dso__is_vdso(dso);
const char *name = dso->long_name;
char nm[PATH_MAX];
if (dso__is_kcore(dso)) {
is_kallsyms = true;
machine__mmap_name(machine, nm, sizeof(nm));
name = nm;
}
return build_id_cache__add_b(dso->build_id, sizeof(dso->build_id), name,
debugdir, is_kallsyms, is_vdso);
}
static int __dsos__cache_build_ids(struct list_head *head,
struct machine *machine, const char *debugdir)
{
struct dso *pos;
int err = 0;
dsos__for_each_with_build_id(pos, head)
if (dso__cache_build_id(pos, machine, debugdir))
err = -1;
return err;
}
static int machine__cache_build_ids(struct machine *machine, const char *debugdir)
{
int ret = __dsos__cache_build_ids(&machine->kernel_dsos.head, machine,
debugdir);
ret |= __dsos__cache_build_ids(&machine->user_dsos.head, machine,
debugdir);
return ret;
}
static int perf_session__cache_build_ids(struct perf_session *session)
{
struct rb_node *nd;
int ret;
char debugdir[PATH_MAX];
snprintf(debugdir, sizeof(debugdir), "%s", buildid_dir);
if (mkdir(debugdir, 0755) != 0 && errno != EEXIST)
return -1;
ret = machine__cache_build_ids(&session->machines.host, debugdir);
for (nd = rb_first(&session->machines.guests); nd; nd = rb_next(nd)) {
struct machine *pos = rb_entry(nd, struct machine, rb_node);
ret |= machine__cache_build_ids(pos, debugdir);
}
return ret ? -1 : 0;
}
static bool machine__read_build_ids(struct machine *machine, bool with_hits)
{
bool ret;
ret = __dsos__read_build_ids(&machine->kernel_dsos.head, with_hits);
ret |= __dsos__read_build_ids(&machine->user_dsos.head, with_hits);
return ret;
}
static bool perf_session__read_build_ids(struct perf_session *session, bool with_hits)
{
struct rb_node *nd;
bool ret = machine__read_build_ids(&session->machines.host, with_hits);
for (nd = rb_first(&session->machines.guests); nd; nd = rb_next(nd)) {
struct machine *pos = rb_entry(nd, struct machine, rb_node);
ret |= machine__read_build_ids(pos, with_hits);
}
return ret;
}
static int write_tracing_data(int fd, struct perf_header *h __maybe_unused, static int write_tracing_data(int fd, struct perf_header *h __maybe_unused,
struct perf_evlist *evlist) struct perf_evlist *evlist)
{ {
@ -523,13 +184,12 @@ static int write_build_id(int fd, struct perf_header *h,
if (!perf_session__read_build_ids(session, true)) if (!perf_session__read_build_ids(session, true))
return -1; return -1;
err = dsos__write_buildid_table(h, fd); err = perf_session__write_buildid_table(session, fd);
if (err < 0) { if (err < 0) {
pr_debug("failed to write buildid table\n"); pr_debug("failed to write buildid table\n");
return err; return err;
} }
if (!no_buildid_cache) perf_session__cache_build_ids(session);
perf_session__cache_build_ids(session);
return 0; return 0;
} }
@ -601,8 +261,10 @@ static int __write_cpudesc(int fd, const char *cpuinfo_proc)
break; break;
} }
if (ret) if (ret) {
ret = -1;
goto done; goto done;
}
s = buf; s = buf;
@ -965,7 +627,8 @@ static int write_total_mem(int fd, struct perf_header *h __maybe_unused,
n = sscanf(buf, "%*s %"PRIu64, &mem); n = sscanf(buf, "%*s %"PRIu64, &mem);
if (n == 1) if (n == 1)
ret = do_write(fd, &mem, sizeof(mem)); ret = do_write(fd, &mem, sizeof(mem));
} } else
ret = -1;
free(buf); free(buf);
fclose(fp); fclose(fp);
return ret; return ret;
@ -1603,7 +1266,7 @@ static int __event_process_build_id(struct build_id_event *bev,
dso__set_build_id(dso, &bev->build_id); dso__set_build_id(dso, &bev->build_id);
if (filename[0] == '[') if (!is_kernel_module(filename, NULL))
dso->kernel = dso_type; dso->kernel = dso_type;
build_id__sprintf(dso->build_id, sizeof(dso->build_id), build_id__sprintf(dso->build_id, sizeof(dso->build_id),
@ -2477,6 +2140,7 @@ static const int attr_file_abi_sizes[] = {
[1] = PERF_ATTR_SIZE_VER1, [1] = PERF_ATTR_SIZE_VER1,
[2] = PERF_ATTR_SIZE_VER2, [2] = PERF_ATTR_SIZE_VER2,
[3] = PERF_ATTR_SIZE_VER3, [3] = PERF_ATTR_SIZE_VER3,
[4] = PERF_ATTR_SIZE_VER4,
0, 0,
}; };
@ -3124,8 +2788,3 @@ int perf_event__process_build_id(struct perf_tool *tool __maybe_unused,
session); session);
return 0; return 0;
} }
void disable_buildid_cache(void)
{
no_buildid_cache = true;
}

View File

@ -122,10 +122,6 @@ int perf_header__process_sections(struct perf_header *header, int fd,
int perf_header__fprintf_info(struct perf_session *s, FILE *fp, bool full); int perf_header__fprintf_info(struct perf_session *s, FILE *fp, bool full);
int build_id_cache__add_s(const char *sbuild_id, const char *debugdir,
const char *name, bool is_kallsyms, bool is_vdso);
int build_id_cache__remove_s(const char *sbuild_id, const char *debugdir);
int perf_event__synthesize_attr(struct perf_tool *tool, int perf_event__synthesize_attr(struct perf_tool *tool,
struct perf_event_attr *attr, u32 ids, u64 *id, struct perf_event_attr *attr, u32 ids, u64 *id,
perf_event__handler_t process); perf_event__handler_t process);
@ -151,7 +147,9 @@ int perf_event__process_build_id(struct perf_tool *tool,
struct perf_session *session); struct perf_session *session);
bool is_perf_magic(u64 magic); bool is_perf_magic(u64 magic);
int dsos__hit_all(struct perf_session *session); #define NAME_ALIGN 64
int write_padded(int fd, const void *bf, size_t count, size_t count_aligned);
/* /*
* arch specific callback * arch specific callback

View File

@ -46,4 +46,21 @@ static inline void bitmap_or(unsigned long *dst, const unsigned long *src1,
__bitmap_or(dst, src1, src2, nbits); __bitmap_or(dst, src1, src2, nbits);
} }
/**
* test_and_set_bit - Set a bit and return its old value
* @nr: Bit to set
* @addr: Address to count from
*/
static inline int test_and_set_bit(int nr, unsigned long *addr)
{
unsigned long mask = BIT_MASK(nr);
unsigned long *p = ((unsigned long *)addr) + BIT_WORD(nr);
unsigned long old;
old = *p;
*p = old | mask;
return (old & mask) != 0;
}
#endif /* _PERF_BITOPS_H */ #endif /* _PERF_BITOPS_H */

View File

@ -15,6 +15,8 @@
#define BITS_TO_U64(nr) DIV_ROUND_UP(nr, BITS_PER_BYTE * sizeof(u64)) #define BITS_TO_U64(nr) DIV_ROUND_UP(nr, BITS_PER_BYTE * sizeof(u64))
#define BITS_TO_U32(nr) DIV_ROUND_UP(nr, BITS_PER_BYTE * sizeof(u32)) #define BITS_TO_U32(nr) DIV_ROUND_UP(nr, BITS_PER_BYTE * sizeof(u32))
#define BITS_TO_BYTES(nr) DIV_ROUND_UP(nr, BITS_PER_BYTE) #define BITS_TO_BYTES(nr) DIV_ROUND_UP(nr, BITS_PER_BYTE)
#define BIT_WORD(nr) ((nr) / BITS_PER_LONG)
#define BIT_MASK(nr) (1UL << ((nr) % BITS_PER_LONG))
#define for_each_set_bit(bit, addr, size) \ #define for_each_set_bit(bit, addr, size) \
for ((bit) = find_first_bit((addr), (size)); \ for ((bit) = find_first_bit((addr), (size)); \

View File

@ -21,7 +21,7 @@ static void dsos__init(struct dsos *dsos)
int machine__init(struct machine *machine, const char *root_dir, pid_t pid) int machine__init(struct machine *machine, const char *root_dir, pid_t pid)
{ {
map_groups__init(&machine->kmaps); map_groups__init(&machine->kmaps, machine);
RB_CLEAR_NODE(&machine->rb_node); RB_CLEAR_NODE(&machine->rb_node);
dsos__init(&machine->user_dsos); dsos__init(&machine->user_dsos);
dsos__init(&machine->kernel_dsos); dsos__init(&machine->kernel_dsos);
@ -32,7 +32,6 @@ int machine__init(struct machine *machine, const char *root_dir, pid_t pid)
machine->vdso_info = NULL; machine->vdso_info = NULL;
machine->kmaps.machine = machine;
machine->pid = pid; machine->pid = pid;
machine->symbol_filter = NULL; machine->symbol_filter = NULL;
@ -319,7 +318,7 @@ static void machine__update_thread_pid(struct machine *machine,
goto out_err; goto out_err;
if (!leader->mg) if (!leader->mg)
leader->mg = map_groups__new(); leader->mg = map_groups__new(machine);
if (!leader->mg) if (!leader->mg)
goto out_err; goto out_err;
@ -465,6 +464,7 @@ struct map *machine__new_module(struct machine *machine, u64 start,
{ {
struct map *map; struct map *map;
struct dso *dso = __dsos__findnew(&machine->kernel_dsos, filename); struct dso *dso = __dsos__findnew(&machine->kernel_dsos, filename);
bool compressed;
if (dso == NULL) if (dso == NULL)
return NULL; return NULL;
@ -477,6 +477,11 @@ struct map *machine__new_module(struct machine *machine, u64 start,
dso->symtab_type = DSO_BINARY_TYPE__SYSTEM_PATH_KMODULE; dso->symtab_type = DSO_BINARY_TYPE__SYSTEM_PATH_KMODULE;
else else
dso->symtab_type = DSO_BINARY_TYPE__GUEST_KMODULE; dso->symtab_type = DSO_BINARY_TYPE__GUEST_KMODULE;
/* _KMODULE_COMP should be next to _KMODULE */
if (is_kernel_module(filename, &compressed) && compressed)
dso->symtab_type++;
map_groups__insert(&machine->kmaps, map); map_groups__insert(&machine->kmaps, map);
return map; return map;
} }
@ -862,8 +867,14 @@ static int map_groups__set_modules_path_dir(struct map_groups *mg,
struct map *map; struct map *map;
char *long_name; char *long_name;
if (dot == NULL || strcmp(dot, ".ko")) if (dot == NULL)
continue; continue;
/* On some system, modules are compressed like .ko.gz */
if (is_supported_compression(dot + 1) &&
is_kmodule_extension(dot - 2))
dot -= 3;
snprintf(dso_name, sizeof(dso_name), "[%.*s]", snprintf(dso_name, sizeof(dso_name), "[%.*s]",
(int)(dot - dent->d_name), dent->d_name); (int)(dot - dent->d_name), dent->d_name);
@ -1045,6 +1056,11 @@ static int machine__process_kernel_mmap_event(struct machine *machine,
dot = strrchr(name, '.'); dot = strrchr(name, '.');
if (dot == NULL) if (dot == NULL)
goto out_problem; goto out_problem;
/* On some system, modules are compressed like .ko.gz */
if (is_supported_compression(dot + 1))
dot -= 3;
if (!is_kmodule_extension(dot + 1))
goto out_problem;
snprintf(short_module_name, sizeof(short_module_name), snprintf(short_module_name, sizeof(short_module_name),
"[%.*s]", (int)(dot - name), name); "[%.*s]", (int)(dot - name), name);
strxfrchar(short_module_name, '-', '_'); strxfrchar(short_module_name, '-', '_');
@ -1069,8 +1085,20 @@ static int machine__process_kernel_mmap_event(struct machine *machine,
* Should be there already, from the build-id table in * Should be there already, from the build-id table in
* the header. * the header.
*/ */
struct dso *kernel = __dsos__findnew(&machine->kernel_dsos, struct dso *kernel = NULL;
kmmap_prefix); struct dso *dso;
list_for_each_entry(dso, &machine->kernel_dsos.head, node) {
if (is_kernel_module(dso->long_name, NULL))
continue;
kernel = dso;
break;
}
if (kernel == NULL)
kernel = __dsos__findnew(&machine->kernel_dsos,
kmmap_prefix);
if (kernel == NULL) if (kernel == NULL)
goto out_problem; goto out_problem;
@ -1078,6 +1106,9 @@ static int machine__process_kernel_mmap_event(struct machine *machine,
if (__machine__create_kernel_maps(machine, kernel) < 0) if (__machine__create_kernel_maps(machine, kernel) < 0)
goto out_problem; goto out_problem;
if (strstr(dso->long_name, "vmlinux"))
dso__set_short_name(dso, "[kernel.vmlinux]", false);
machine__set_kernel_mmap_len(machine, event); machine__set_kernel_mmap_len(machine, event);
/* /*
@ -1290,7 +1321,7 @@ static bool symbol__match_regex(struct symbol *sym, regex_t *regex)
return 0; return 0;
} }
static void ip__resolve_ams(struct machine *machine, struct thread *thread, static void ip__resolve_ams(struct thread *thread,
struct addr_map_symbol *ams, struct addr_map_symbol *ams,
u64 ip) u64 ip)
{ {
@ -1304,7 +1335,7 @@ static void ip__resolve_ams(struct machine *machine, struct thread *thread,
* Thus, we have to try consecutively until we find a match * Thus, we have to try consecutively until we find a match
* or else, the symbol is unknown * or else, the symbol is unknown
*/ */
thread__find_cpumode_addr_location(thread, machine, MAP__FUNCTION, ip, &al); thread__find_cpumode_addr_location(thread, MAP__FUNCTION, ip, &al);
ams->addr = ip; ams->addr = ip;
ams->al_addr = al.addr; ams->al_addr = al.addr;
@ -1312,23 +1343,21 @@ static void ip__resolve_ams(struct machine *machine, struct thread *thread,
ams->map = al.map; ams->map = al.map;
} }
static void ip__resolve_data(struct machine *machine, struct thread *thread, static void ip__resolve_data(struct thread *thread,
u8 m, struct addr_map_symbol *ams, u64 addr) u8 m, struct addr_map_symbol *ams, u64 addr)
{ {
struct addr_location al; struct addr_location al;
memset(&al, 0, sizeof(al)); memset(&al, 0, sizeof(al));
thread__find_addr_location(thread, machine, m, MAP__VARIABLE, addr, thread__find_addr_location(thread, m, MAP__VARIABLE, addr, &al);
&al);
if (al.map == NULL) { if (al.map == NULL) {
/* /*
* some shared data regions have execute bit set which puts * some shared data regions have execute bit set which puts
* their mapping in the MAP__FUNCTION type array. * their mapping in the MAP__FUNCTION type array.
* Check there as a fallback option before dropping the sample. * Check there as a fallback option before dropping the sample.
*/ */
thread__find_addr_location(thread, machine, m, MAP__FUNCTION, addr, thread__find_addr_location(thread, m, MAP__FUNCTION, addr, &al);
&al);
} }
ams->addr = addr; ams->addr = addr;
@ -1345,14 +1374,41 @@ struct mem_info *sample__resolve_mem(struct perf_sample *sample,
if (!mi) if (!mi)
return NULL; return NULL;
ip__resolve_ams(al->machine, al->thread, &mi->iaddr, sample->ip); ip__resolve_ams(al->thread, &mi->iaddr, sample->ip);
ip__resolve_data(al->machine, al->thread, al->cpumode, ip__resolve_data(al->thread, al->cpumode, &mi->daddr, sample->addr);
&mi->daddr, sample->addr);
mi->data_src.val = sample->data_src; mi->data_src.val = sample->data_src;
return mi; return mi;
} }
static int add_callchain_ip(struct thread *thread,
struct symbol **parent,
struct addr_location *root_al,
int cpumode,
u64 ip)
{
struct addr_location al;
al.filtered = 0;
al.sym = NULL;
thread__find_addr_location(thread, cpumode, MAP__FUNCTION,
ip, &al);
if (al.sym != NULL) {
if (sort__has_parent && !*parent &&
symbol__match_regex(al.sym, &parent_regex))
*parent = al.sym;
else if (have_ignore_callees && root_al &&
symbol__match_regex(al.sym, &ignore_callees_regex)) {
/* Treat this symbol as the root,
forgetting its callees. */
*root_al = al;
callchain_cursor_reset(&callchain_cursor);
}
}
return callchain_cursor_append(&callchain_cursor, al.addr, al.map, al.sym);
}
struct branch_info *sample__resolve_bstack(struct perf_sample *sample, struct branch_info *sample__resolve_bstack(struct perf_sample *sample,
struct addr_location *al) struct addr_location *al)
{ {
@ -1364,15 +1420,14 @@ struct branch_info *sample__resolve_bstack(struct perf_sample *sample,
return NULL; return NULL;
for (i = 0; i < bs->nr; i++) { for (i = 0; i < bs->nr; i++) {
ip__resolve_ams(al->machine, al->thread, &bi[i].to, bs->entries[i].to); ip__resolve_ams(al->thread, &bi[i].to, bs->entries[i].to);
ip__resolve_ams(al->machine, al->thread, &bi[i].from, bs->entries[i].from); ip__resolve_ams(al->thread, &bi[i].from, bs->entries[i].from);
bi[i].flags = bs->entries[i].flags; bi[i].flags = bs->entries[i].flags;
} }
return bi; return bi;
} }
static int machine__resolve_callchain_sample(struct machine *machine, static int thread__resolve_callchain_sample(struct thread *thread,
struct thread *thread,
struct ip_callchain *chain, struct ip_callchain *chain,
struct symbol **parent, struct symbol **parent,
struct addr_location *root_al, struct addr_location *root_al,
@ -1396,11 +1451,10 @@ static int machine__resolve_callchain_sample(struct machine *machine,
* Based on DWARF debug information, some architectures skip * Based on DWARF debug information, some architectures skip
* a callchain entry saved by the kernel. * a callchain entry saved by the kernel.
*/ */
skip_idx = arch_skip_callchain_idx(machine, thread, chain); skip_idx = arch_skip_callchain_idx(thread, chain);
for (i = 0; i < chain_nr; i++) { for (i = 0; i < chain_nr; i++) {
u64 ip; u64 ip;
struct addr_location al;
if (callchain_param.order == ORDER_CALLEE) if (callchain_param.order == ORDER_CALLEE)
j = i; j = i;
@ -1437,24 +1491,10 @@ static int machine__resolve_callchain_sample(struct machine *machine,
continue; continue;
} }
al.filtered = 0; err = add_callchain_ip(thread, parent, root_al,
thread__find_addr_location(thread, machine, cpumode, cpumode, ip);
MAP__FUNCTION, ip, &al); if (err == -EINVAL)
if (al.sym != NULL) { break;
if (sort__has_parent && !*parent &&
symbol__match_regex(al.sym, &parent_regex))
*parent = al.sym;
else if (have_ignore_callees && root_al &&
symbol__match_regex(al.sym, &ignore_callees_regex)) {
/* Treat this symbol as the root,
forgetting its callees. */
*root_al = al;
callchain_cursor_reset(&callchain_cursor);
}
}
err = callchain_cursor_append(&callchain_cursor,
ip, al.map, al.sym);
if (err) if (err)
return err; return err;
} }
@ -1469,19 +1509,15 @@ static int unwind_entry(struct unwind_entry *entry, void *arg)
entry->map, entry->sym); entry->map, entry->sym);
} }
int machine__resolve_callchain(struct machine *machine, int thread__resolve_callchain(struct thread *thread,
struct perf_evsel *evsel, struct perf_evsel *evsel,
struct thread *thread, struct perf_sample *sample,
struct perf_sample *sample, struct symbol **parent,
struct symbol **parent, struct addr_location *root_al,
struct addr_location *root_al, int max_stack)
int max_stack)
{ {
int ret; int ret = thread__resolve_callchain_sample(thread, sample->callchain,
parent, root_al, max_stack);
ret = machine__resolve_callchain_sample(machine, thread,
sample->callchain, parent,
root_al, max_stack);
if (ret) if (ret)
return ret; return ret;
@ -1495,7 +1531,7 @@ int machine__resolve_callchain(struct machine *machine,
(!sample->user_stack.size)) (!sample->user_stack.size))
return 0; return 0;
return unwind__get_entries(unwind_entry, &callchain_cursor, machine, return unwind__get_entries(unwind_entry, &callchain_cursor,
thread, sample, max_stack); thread, sample, max_stack);
} }

View File

@ -40,6 +40,10 @@ struct machine {
u64 kernel_start; u64 kernel_start;
symbol_filter_t symbol_filter; symbol_filter_t symbol_filter;
pid_t *current_tid; pid_t *current_tid;
union { /* Tool specific area */
void *priv;
u64 db_id;
};
}; };
static inline static inline
@ -122,13 +126,12 @@ struct branch_info *sample__resolve_bstack(struct perf_sample *sample,
struct addr_location *al); struct addr_location *al);
struct mem_info *sample__resolve_mem(struct perf_sample *sample, struct mem_info *sample__resolve_mem(struct perf_sample *sample,
struct addr_location *al); struct addr_location *al);
int machine__resolve_callchain(struct machine *machine, int thread__resolve_callchain(struct thread *thread,
struct perf_evsel *evsel, struct perf_evsel *evsel,
struct thread *thread, struct perf_sample *sample,
struct perf_sample *sample, struct symbol **parent,
struct symbol **parent, struct addr_location *root_al,
struct addr_location *root_al, int max_stack);
int max_stack);
/* /*
* Default guest kernel is defined by parameter --guestkallsyms * Default guest kernel is defined by parameter --guestkallsyms

View File

@ -413,14 +413,14 @@ u64 map__objdump_2mem(struct map *map, u64 ip)
return ip + map->reloc; return ip + map->reloc;
} }
void map_groups__init(struct map_groups *mg) void map_groups__init(struct map_groups *mg, struct machine *machine)
{ {
int i; int i;
for (i = 0; i < MAP__NR_TYPES; ++i) { for (i = 0; i < MAP__NR_TYPES; ++i) {
mg->maps[i] = RB_ROOT; mg->maps[i] = RB_ROOT;
INIT_LIST_HEAD(&mg->removed_maps[i]); INIT_LIST_HEAD(&mg->removed_maps[i]);
} }
mg->machine = NULL; mg->machine = machine;
mg->refcnt = 1; mg->refcnt = 1;
} }
@ -471,12 +471,12 @@ bool map_groups__empty(struct map_groups *mg)
return true; return true;
} }
struct map_groups *map_groups__new(void) struct map_groups *map_groups__new(struct machine *machine)
{ {
struct map_groups *mg = malloc(sizeof(*mg)); struct map_groups *mg = malloc(sizeof(*mg));
if (mg != NULL) if (mg != NULL)
map_groups__init(mg); map_groups__init(mg, machine);
return mg; return mg;
} }

View File

@ -64,7 +64,7 @@ struct map_groups {
int refcnt; int refcnt;
}; };
struct map_groups *map_groups__new(void); struct map_groups *map_groups__new(struct machine *machine);
void map_groups__delete(struct map_groups *mg); void map_groups__delete(struct map_groups *mg);
bool map_groups__empty(struct map_groups *mg); bool map_groups__empty(struct map_groups *mg);
@ -150,7 +150,7 @@ void maps__remove(struct rb_root *maps, struct map *map);
struct map *maps__find(struct rb_root *maps, u64 addr); struct map *maps__find(struct rb_root *maps, u64 addr);
struct map *maps__first(struct rb_root *maps); struct map *maps__first(struct rb_root *maps);
struct map *maps__next(struct map *map); struct map *maps__next(struct map *map);
void map_groups__init(struct map_groups *mg); void map_groups__init(struct map_groups *mg, struct machine *machine);
void map_groups__exit(struct map_groups *mg); void map_groups__exit(struct map_groups *mg);
int map_groups__clone(struct map_groups *mg, int map_groups__clone(struct map_groups *mg,
struct map_groups *parent, enum map_type type); struct map_groups *parent, enum map_type type);

View File

@ -526,7 +526,7 @@ do { \
} }
int parse_events_add_breakpoint(struct list_head *list, int *idx, int parse_events_add_breakpoint(struct list_head *list, int *idx,
void *ptr, char *type) void *ptr, char *type, u64 len)
{ {
struct perf_event_attr attr; struct perf_event_attr attr;
@ -536,14 +536,15 @@ int parse_events_add_breakpoint(struct list_head *list, int *idx,
if (parse_breakpoint_type(type, &attr)) if (parse_breakpoint_type(type, &attr))
return -EINVAL; return -EINVAL;
/* /* Provide some defaults if len is not specified */
* We should find a nice way to override the access length if (!len) {
* Provide some defaults for now if (attr.bp_type == HW_BREAKPOINT_X)
*/ len = sizeof(long);
if (attr.bp_type == HW_BREAKPOINT_X) else
attr.bp_len = sizeof(long); len = HW_BREAKPOINT_LEN_4;
else }
attr.bp_len = HW_BREAKPOINT_LEN_4;
attr.bp_len = len;
attr.type = PERF_TYPE_BREAKPOINT; attr.type = PERF_TYPE_BREAKPOINT;
attr.sample_period = 1; attr.sample_period = 1;
@ -1364,7 +1365,7 @@ void print_events(const char *event_glob, bool name_only)
printf("\n"); printf("\n");
printf(" %-50s [%s]\n", printf(" %-50s [%s]\n",
"mem:<addr>[:access]", "mem:<addr>[/len][:access]",
event_type_descriptors[PERF_TYPE_BREAKPOINT]); event_type_descriptors[PERF_TYPE_BREAKPOINT]);
printf("\n"); printf("\n");
} }

View File

@ -104,7 +104,7 @@ int parse_events_add_numeric(struct list_head *list, int *idx,
int parse_events_add_cache(struct list_head *list, int *idx, int parse_events_add_cache(struct list_head *list, int *idx,
char *type, char *op_result1, char *op_result2); char *type, char *op_result1, char *op_result2);
int parse_events_add_breakpoint(struct list_head *list, int *idx, int parse_events_add_breakpoint(struct list_head *list, int *idx,
void *ptr, char *type); void *ptr, char *type, u64 len);
int parse_events_add_pmu(struct list_head *list, int *idx, int parse_events_add_pmu(struct list_head *list, int *idx,
char *pmu , struct list_head *head_config); char *pmu , struct list_head *head_config);
enum perf_pmu_event_symbol_type enum perf_pmu_event_symbol_type

View File

@ -159,6 +159,7 @@ branch_type { return term(yyscanner, PARSE_EVENTS__TERM_TYPE_BRANCH_SAMPLE_TYPE
<mem>{ <mem>{
{modifier_bp} { return str(yyscanner, PE_MODIFIER_BP); } {modifier_bp} { return str(yyscanner, PE_MODIFIER_BP); }
: { return ':'; } : { return ':'; }
"/" { return '/'; }
{num_dec} { return value(yyscanner, 10); } {num_dec} { return value(yyscanner, 10); }
{num_hex} { return value(yyscanner, 16); } {num_hex} { return value(yyscanner, 16); }
/* /*

View File

@ -326,6 +326,28 @@ PE_NAME_CACHE_TYPE
} }
event_legacy_mem: event_legacy_mem:
PE_PREFIX_MEM PE_VALUE '/' PE_VALUE ':' PE_MODIFIER_BP sep_dc
{
struct parse_events_evlist *data = _data;
struct list_head *list;
ALLOC_LIST(list);
ABORT_ON(parse_events_add_breakpoint(list, &data->idx,
(void *) $2, $6, $4));
$$ = list;
}
|
PE_PREFIX_MEM PE_VALUE '/' PE_VALUE sep_dc
{
struct parse_events_evlist *data = _data;
struct list_head *list;
ALLOC_LIST(list);
ABORT_ON(parse_events_add_breakpoint(list, &data->idx,
(void *) $2, NULL, $4));
$$ = list;
}
|
PE_PREFIX_MEM PE_VALUE ':' PE_MODIFIER_BP sep_dc PE_PREFIX_MEM PE_VALUE ':' PE_MODIFIER_BP sep_dc
{ {
struct parse_events_evlist *data = _data; struct parse_events_evlist *data = _data;
@ -333,7 +355,7 @@ PE_PREFIX_MEM PE_VALUE ':' PE_MODIFIER_BP sep_dc
ALLOC_LIST(list); ALLOC_LIST(list);
ABORT_ON(parse_events_add_breakpoint(list, &data->idx, ABORT_ON(parse_events_add_breakpoint(list, &data->idx,
(void *) $2, $4)); (void *) $2, $4, 0));
$$ = list; $$ = list;
} }
| |
@ -344,7 +366,7 @@ PE_PREFIX_MEM PE_VALUE sep_dc
ALLOC_LIST(list); ALLOC_LIST(list);
ABORT_ON(parse_events_add_breakpoint(list, &data->idx, ABORT_ON(parse_events_add_breakpoint(list, &data->idx,
(void *) $2, NULL)); (void *) $2, NULL, 0));
$$ = list; $$ = list;
} }

View File

@ -42,7 +42,26 @@ static int get_value(struct parse_opt_ctx_t *p,
return opterror(opt, "takes no value", flags); return opterror(opt, "takes no value", flags);
if (unset && (opt->flags & PARSE_OPT_NONEG)) if (unset && (opt->flags & PARSE_OPT_NONEG))
return opterror(opt, "isn't available", flags); return opterror(opt, "isn't available", flags);
if (opt->flags & PARSE_OPT_DISABLED)
return opterror(opt, "is not usable", flags);
if (opt->flags & PARSE_OPT_EXCLUSIVE) {
if (p->excl_opt) {
char msg[128];
if (((flags & OPT_SHORT) && p->excl_opt->short_name) ||
p->excl_opt->long_name == NULL) {
scnprintf(msg, sizeof(msg), "cannot be used with switch `%c'",
p->excl_opt->short_name);
} else {
scnprintf(msg, sizeof(msg), "cannot be used with %s",
p->excl_opt->long_name);
}
opterror(opt, msg, flags);
return -3;
}
p->excl_opt = opt;
}
if (!(flags & OPT_SHORT) && p->opt) { if (!(flags & OPT_SHORT) && p->opt) {
switch (opt->type) { switch (opt->type) {
case OPTION_CALLBACK: case OPTION_CALLBACK:
@ -343,13 +362,14 @@ int parse_options_step(struct parse_opt_ctx_t *ctx,
const char * const usagestr[]) const char * const usagestr[])
{ {
int internal_help = !(ctx->flags & PARSE_OPT_NO_INTERNAL_HELP); int internal_help = !(ctx->flags & PARSE_OPT_NO_INTERNAL_HELP);
int excl_short_opt = 1;
const char *arg;
/* we must reset ->opt, unknown short option leave it dangling */ /* we must reset ->opt, unknown short option leave it dangling */
ctx->opt = NULL; ctx->opt = NULL;
for (; ctx->argc; ctx->argc--, ctx->argv++) { for (; ctx->argc; ctx->argc--, ctx->argv++) {
const char *arg = ctx->argv[0]; arg = ctx->argv[0];
if (*arg != '-' || !arg[1]) { if (*arg != '-' || !arg[1]) {
if (ctx->flags & PARSE_OPT_STOP_AT_NON_OPTION) if (ctx->flags & PARSE_OPT_STOP_AT_NON_OPTION)
break; break;
@ -358,19 +378,21 @@ int parse_options_step(struct parse_opt_ctx_t *ctx,
} }
if (arg[1] != '-') { if (arg[1] != '-') {
ctx->opt = arg + 1; ctx->opt = ++arg;
if (internal_help && *ctx->opt == 'h') if (internal_help && *ctx->opt == 'h')
return usage_with_options_internal(usagestr, options, 0); return usage_with_options_internal(usagestr, options, 0);
switch (parse_short_opt(ctx, options)) { switch (parse_short_opt(ctx, options)) {
case -1: case -1:
return parse_options_usage(usagestr, options, arg + 1, 1); return parse_options_usage(usagestr, options, arg, 1);
case -2: case -2:
goto unknown; goto unknown;
case -3:
goto exclusive;
default: default:
break; break;
} }
if (ctx->opt) if (ctx->opt)
check_typos(arg + 1, options); check_typos(arg, options);
while (ctx->opt) { while (ctx->opt) {
if (internal_help && *ctx->opt == 'h') if (internal_help && *ctx->opt == 'h')
return usage_with_options_internal(usagestr, options, 0); return usage_with_options_internal(usagestr, options, 0);
@ -387,6 +409,8 @@ int parse_options_step(struct parse_opt_ctx_t *ctx,
ctx->argv[0] = strdup(ctx->opt - 1); ctx->argv[0] = strdup(ctx->opt - 1);
*(char *)ctx->argv[0] = '-'; *(char *)ctx->argv[0] = '-';
goto unknown; goto unknown;
case -3:
goto exclusive;
default: default:
break; break;
} }
@ -402,19 +426,23 @@ int parse_options_step(struct parse_opt_ctx_t *ctx,
break; break;
} }
if (internal_help && !strcmp(arg + 2, "help-all")) arg += 2;
if (internal_help && !strcmp(arg, "help-all"))
return usage_with_options_internal(usagestr, options, 1); return usage_with_options_internal(usagestr, options, 1);
if (internal_help && !strcmp(arg + 2, "help")) if (internal_help && !strcmp(arg, "help"))
return usage_with_options_internal(usagestr, options, 0); return usage_with_options_internal(usagestr, options, 0);
if (!strcmp(arg + 2, "list-opts")) if (!strcmp(arg, "list-opts"))
return PARSE_OPT_LIST_OPTS; return PARSE_OPT_LIST_OPTS;
if (!strcmp(arg + 2, "list-cmds")) if (!strcmp(arg, "list-cmds"))
return PARSE_OPT_LIST_SUBCMDS; return PARSE_OPT_LIST_SUBCMDS;
switch (parse_long_opt(ctx, arg + 2, options)) { switch (parse_long_opt(ctx, arg, options)) {
case -1: case -1:
return parse_options_usage(usagestr, options, arg + 2, 0); return parse_options_usage(usagestr, options, arg, 0);
case -2: case -2:
goto unknown; goto unknown;
case -3:
excl_short_opt = 0;
goto exclusive;
default: default:
break; break;
} }
@ -426,6 +454,17 @@ unknown:
ctx->opt = NULL; ctx->opt = NULL;
} }
return PARSE_OPT_DONE; return PARSE_OPT_DONE;
exclusive:
parse_options_usage(usagestr, options, arg, excl_short_opt);
if ((excl_short_opt && ctx->excl_opt->short_name) ||
ctx->excl_opt->long_name == NULL) {
char opt = ctx->excl_opt->short_name;
parse_options_usage(NULL, options, &opt, 1);
} else {
parse_options_usage(NULL, options, ctx->excl_opt->long_name, 0);
}
return PARSE_OPT_HELP;
} }
int parse_options_end(struct parse_opt_ctx_t *ctx) int parse_options_end(struct parse_opt_ctx_t *ctx)
@ -509,6 +548,8 @@ static void print_option_help(const struct option *opts, int full)
} }
if (!full && (opts->flags & PARSE_OPT_HIDDEN)) if (!full && (opts->flags & PARSE_OPT_HIDDEN))
return; return;
if (opts->flags & PARSE_OPT_DISABLED)
return;
pos = fprintf(stderr, " "); pos = fprintf(stderr, " ");
if (opts->short_name) if (opts->short_name)
@ -679,3 +720,16 @@ int parse_opt_verbosity_cb(const struct option *opt,
} }
return 0; return 0;
} }
void set_option_flag(struct option *opts, int shortopt, const char *longopt,
int flag)
{
for (; opts->type != OPTION_END; opts++) {
if ((shortopt && opts->short_name == shortopt) ||
(opts->long_name && longopt &&
!strcmp(opts->long_name, longopt))) {
opts->flags |= flag;
break;
}
}
}

View File

@ -38,6 +38,8 @@ enum parse_opt_option_flags {
PARSE_OPT_NONEG = 4, PARSE_OPT_NONEG = 4,
PARSE_OPT_HIDDEN = 8, PARSE_OPT_HIDDEN = 8,
PARSE_OPT_LASTARG_DEFAULT = 16, PARSE_OPT_LASTARG_DEFAULT = 16,
PARSE_OPT_DISABLED = 32,
PARSE_OPT_EXCLUSIVE = 64,
}; };
struct option; struct option;
@ -173,6 +175,7 @@ struct parse_opt_ctx_t {
const char **out; const char **out;
int argc, cpidx; int argc, cpidx;
const char *opt; const char *opt;
const struct option *excl_opt;
int flags; int flags;
}; };
@ -211,4 +214,5 @@ extern int parse_opt_verbosity_cb(const struct option *, const char *, int);
extern const char *parse_options_fix_filename(const char *prefix, const char *file); extern const char *parse_options_fix_filename(const char *prefix, const char *file);
void set_option_flag(struct option *opts, int sopt, const char *lopt, int flag);
#endif /* __PERF_PARSE_OPTIONS_H */ #endif /* __PERF_PARSE_OPTIONS_H */

View File

@ -747,15 +747,18 @@ void print_pmu_events(const char *event_glob, bool name_only)
pmu = NULL; pmu = NULL;
len = 0; len = 0;
while ((pmu = perf_pmu__scan(pmu)) != NULL) while ((pmu = perf_pmu__scan(pmu)) != NULL) {
list_for_each_entry(alias, &pmu->aliases, list) list_for_each_entry(alias, &pmu->aliases, list)
len++; len++;
aliases = malloc(sizeof(char *) * len); if (pmu->selectable)
len++;
}
aliases = zalloc(sizeof(char *) * len);
if (!aliases) if (!aliases)
return; goto out_enomem;
pmu = NULL; pmu = NULL;
j = 0; j = 0;
while ((pmu = perf_pmu__scan(pmu)) != NULL) while ((pmu = perf_pmu__scan(pmu)) != NULL) {
list_for_each_entry(alias, &pmu->aliases, list) { list_for_each_entry(alias, &pmu->aliases, list) {
char *name = format_alias(buf, sizeof(buf), pmu, alias); char *name = format_alias(buf, sizeof(buf), pmu, alias);
bool is_cpu = !strcmp(pmu->name, "cpu"); bool is_cpu = !strcmp(pmu->name, "cpu");
@ -765,13 +768,23 @@ void print_pmu_events(const char *event_glob, bool name_only)
(!is_cpu && strglobmatch(alias->name, (!is_cpu && strglobmatch(alias->name,
event_glob)))) event_glob))))
continue; continue;
aliases[j] = name;
if (is_cpu && !name_only) if (is_cpu && !name_only)
aliases[j] = format_alias_or(buf, sizeof(buf), name = format_alias_or(buf, sizeof(buf), pmu, alias);
pmu, alias);
aliases[j] = strdup(aliases[j]); aliases[j] = strdup(name);
if (aliases[j] == NULL)
goto out_enomem;
j++; j++;
} }
if (pmu->selectable) {
char *s;
if (asprintf(&s, "%s//", pmu->name) < 0)
goto out_enomem;
aliases[j] = s;
j++;
}
}
len = j; len = j;
qsort(aliases, len, sizeof(char *), cmp_string); qsort(aliases, len, sizeof(char *), cmp_string);
for (j = 0; j < len; j++) { for (j = 0; j < len; j++) {
@ -780,12 +793,20 @@ void print_pmu_events(const char *event_glob, bool name_only)
continue; continue;
} }
printf(" %-50s [Kernel PMU event]\n", aliases[j]); printf(" %-50s [Kernel PMU event]\n", aliases[j]);
zfree(&aliases[j]);
printed++; printed++;
} }
if (printed) if (printed)
printf("\n"); printf("\n");
free(aliases); out_free:
for (j = 0; j < len; j++)
zfree(&aliases[j]);
zfree(&aliases);
return;
out_enomem:
printf("FATAL: not enough memory to print PMU events\n");
if (aliases)
goto out_free;
} }
bool pmu_have_event(const char *pname, const char *name) bool pmu_have_event(const char *pname, const char *name)

View File

@ -18,6 +18,7 @@ struct perf_event_attr;
struct perf_pmu { struct perf_pmu {
char *name; char *name;
__u32 type; __u32 type;
bool selectable;
struct perf_event_attr *default_config; struct perf_event_attr *default_config;
struct cpu_map *cpus; struct cpu_map *cpus;
struct list_head format; /* HEAD struct perf_pmu_format -> list */ struct list_head format; /* HEAD struct perf_pmu_format -> list */

View File

@ -1910,21 +1910,21 @@ static int show_perf_probe_event(struct perf_probe_event *pev,
if (ret < 0) if (ret < 0)
return ret; return ret;
printf(" %-20s (on %s", buf, place); pr_info(" %-20s (on %s", buf, place);
if (module) if (module)
printf(" in %s", module); pr_info(" in %s", module);
if (pev->nargs > 0) { if (pev->nargs > 0) {
printf(" with"); pr_info(" with");
for (i = 0; i < pev->nargs; i++) { for (i = 0; i < pev->nargs; i++) {
ret = synthesize_perf_probe_arg(&pev->args[i], ret = synthesize_perf_probe_arg(&pev->args[i],
buf, 128); buf, 128);
if (ret < 0) if (ret < 0)
break; break;
printf(" %s", buf); pr_info(" %s", buf);
} }
} }
printf(")\n"); pr_info(")\n");
free(place); free(place);
return ret; return ret;
} }
@ -2124,7 +2124,7 @@ static int __add_probe_trace_events(struct perf_probe_event *pev,
} }
ret = 0; ret = 0;
printf("Added new event%s\n", (ntevs > 1) ? "s:" : ":"); pr_info("Added new event%s\n", (ntevs > 1) ? "s:" : ":");
for (i = 0; i < ntevs; i++) { for (i = 0; i < ntevs; i++) {
tev = &tevs[i]; tev = &tevs[i];
if (pev->event) if (pev->event)
@ -2179,8 +2179,8 @@ static int __add_probe_trace_events(struct perf_probe_event *pev,
if (ret >= 0) { if (ret >= 0) {
/* Show how to use the event. */ /* Show how to use the event. */
printf("\nYou can now use it in all perf tools, such as:\n\n"); pr_info("\nYou can now use it in all perf tools, such as:\n\n");
printf("\tperf record -e %s:%s -aR sleep 1\n\n", tev->group, pr_info("\tperf record -e %s:%s -aR sleep 1\n\n", tev->group,
tev->event); tev->event);
} }
@ -2444,7 +2444,7 @@ static int __del_trace_probe_event(int fd, struct str_node *ent)
goto error; goto error;
} }
printf("Removed event: %s\n", ent->s); pr_info("Removed event: %s\n", ent->s);
return 0; return 0;
error: error:
pr_warning("Failed to delete event: %s\n", pr_warning("Failed to delete event: %s\n",

View File

@ -24,6 +24,7 @@
#include <string.h> #include <string.h>
#include <ctype.h> #include <ctype.h>
#include <errno.h> #include <errno.h>
#include <linux/bitmap.h>
#include "../util.h" #include "../util.h"
#include <EXTERN.h> #include <EXTERN.h>
@ -57,7 +58,7 @@ INTERP my_perl;
#define FTRACE_MAX_EVENT \ #define FTRACE_MAX_EVENT \
((1 << (sizeof(unsigned short) * 8)) - 1) ((1 << (sizeof(unsigned short) * 8)) - 1)
struct event_format *events[FTRACE_MAX_EVENT]; static DECLARE_BITMAP(events_defined, FTRACE_MAX_EVENT);
extern struct scripting_context *scripting_context; extern struct scripting_context *scripting_context;
@ -238,35 +239,15 @@ static void define_event_symbols(struct event_format *event,
define_event_symbols(event, ev_name, args->next); define_event_symbols(event, ev_name, args->next);
} }
static inline struct event_format *find_cache_event(struct perf_evsel *evsel)
{
static char ev_name[256];
struct event_format *event;
int type = evsel->attr.config;
if (events[type])
return events[type];
events[type] = event = evsel->tp_format;
if (!event)
return NULL;
sprintf(ev_name, "%s::%s", event->system, event->name);
define_event_symbols(event, ev_name, event->print_fmt.args);
return event;
}
static void perl_process_tracepoint(struct perf_sample *sample, static void perl_process_tracepoint(struct perf_sample *sample,
struct perf_evsel *evsel, struct perf_evsel *evsel,
struct thread *thread) struct thread *thread)
{ {
struct event_format *event = evsel->tp_format;
struct format_field *field; struct format_field *field;
static char handler[256]; static char handler[256];
unsigned long long val; unsigned long long val;
unsigned long s, ns; unsigned long s, ns;
struct event_format *event;
int pid; int pid;
int cpu = sample->cpu; int cpu = sample->cpu;
void *data = sample->raw_data; void *data = sample->raw_data;
@ -278,7 +259,6 @@ static void perl_process_tracepoint(struct perf_sample *sample,
if (evsel->attr.type != PERF_TYPE_TRACEPOINT) if (evsel->attr.type != PERF_TYPE_TRACEPOINT)
return; return;
event = find_cache_event(evsel);
if (!event) if (!event)
die("ug! no event found for type %" PRIu64, (u64)evsel->attr.config); die("ug! no event found for type %" PRIu64, (u64)evsel->attr.config);
@ -286,6 +266,9 @@ static void perl_process_tracepoint(struct perf_sample *sample,
sprintf(handler, "%s::%s", event->system, event->name); sprintf(handler, "%s::%s", event->system, event->name);
if (!test_and_set_bit(event->id, events_defined))
define_event_symbols(event, handler, event->print_fmt.args);
s = nsecs / NSECS_PER_SEC; s = nsecs / NSECS_PER_SEC;
ns = nsecs - s * NSECS_PER_SEC; ns = nsecs - s * NSECS_PER_SEC;

View File

@ -24,7 +24,9 @@
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
#include <stdbool.h>
#include <errno.h> #include <errno.h>
#include <linux/bitmap.h>
#include "../../perf.h" #include "../../perf.h"
#include "../debug.h" #include "../debug.h"
@ -33,6 +35,10 @@
#include "../util.h" #include "../util.h"
#include "../event.h" #include "../event.h"
#include "../thread.h" #include "../thread.h"
#include "../comm.h"
#include "../machine.h"
#include "../db-export.h"
#include "../thread-stack.h"
#include "../trace-event.h" #include "../trace-event.h"
#include "../machine.h" #include "../machine.h"
@ -41,7 +47,7 @@ PyMODINIT_FUNC initperf_trace_context(void);
#define FTRACE_MAX_EVENT \ #define FTRACE_MAX_EVENT \
((1 << (sizeof(unsigned short) * 8)) - 1) ((1 << (sizeof(unsigned short) * 8)) - 1)
struct event_format *events[FTRACE_MAX_EVENT]; static DECLARE_BITMAP(events_defined, FTRACE_MAX_EVENT);
#define MAX_FIELDS 64 #define MAX_FIELDS 64
#define N_COMMON_FIELDS 7 #define N_COMMON_FIELDS 7
@ -53,6 +59,24 @@ static int zero_flag_atom;
static PyObject *main_module, *main_dict; static PyObject *main_module, *main_dict;
struct tables {
struct db_export dbe;
PyObject *evsel_handler;
PyObject *machine_handler;
PyObject *thread_handler;
PyObject *comm_handler;
PyObject *comm_thread_handler;
PyObject *dso_handler;
PyObject *symbol_handler;
PyObject *branch_type_handler;
PyObject *sample_handler;
PyObject *call_path_handler;
PyObject *call_return_handler;
bool db_export_mode;
};
static struct tables tables_global;
static void handler_call_die(const char *handler_name) NORETURN; static void handler_call_die(const char *handler_name) NORETURN;
static void handler_call_die(const char *handler_name) static void handler_call_die(const char *handler_name)
{ {
@ -232,31 +256,6 @@ static void define_event_symbols(struct event_format *event,
define_event_symbols(event, ev_name, args->next); define_event_symbols(event, ev_name, args->next);
} }
static inline struct event_format *find_cache_event(struct perf_evsel *evsel)
{
static char ev_name[256];
struct event_format *event;
int type = evsel->attr.config;
/*
* XXX: Do we really need to cache this since now we have evsel->tp_format
* cached already? Need to re-read this "cache" routine that as well calls
* define_event_symbols() :-\
*/
if (events[type])
return events[type];
events[type] = event = evsel->tp_format;
if (!event)
return NULL;
sprintf(ev_name, "%s__%s", event->system, event->name);
define_event_symbols(event, ev_name, event->print_fmt.args);
return event;
}
static PyObject *get_field_numeric_entry(struct event_format *event, static PyObject *get_field_numeric_entry(struct event_format *event,
struct format_field *field, void *data) struct format_field *field, void *data)
{ {
@ -312,9 +311,9 @@ static PyObject *python_process_callchain(struct perf_sample *sample,
if (!symbol_conf.use_callchain || !sample->callchain) if (!symbol_conf.use_callchain || !sample->callchain)
goto exit; goto exit;
if (machine__resolve_callchain(al->machine, evsel, al->thread, if (thread__resolve_callchain(al->thread, evsel,
sample, NULL, NULL, sample, NULL, NULL,
PERF_MAX_STACK_DEPTH) != 0) { PERF_MAX_STACK_DEPTH) != 0) {
pr_err("Failed to resolve callchain. Skipping\n"); pr_err("Failed to resolve callchain. Skipping\n");
goto exit; goto exit;
} }
@ -380,12 +379,12 @@ static void python_process_tracepoint(struct perf_sample *sample,
struct thread *thread, struct thread *thread,
struct addr_location *al) struct addr_location *al)
{ {
struct event_format *event = evsel->tp_format;
PyObject *handler, *context, *t, *obj, *callchain; PyObject *handler, *context, *t, *obj, *callchain;
PyObject *dict = NULL; PyObject *dict = NULL;
static char handler_name[256]; static char handler_name[256];
struct format_field *field; struct format_field *field;
unsigned long s, ns; unsigned long s, ns;
struct event_format *event;
unsigned n = 0; unsigned n = 0;
int pid; int pid;
int cpu = sample->cpu; int cpu = sample->cpu;
@ -397,7 +396,6 @@ static void python_process_tracepoint(struct perf_sample *sample,
if (!t) if (!t)
Py_FatalError("couldn't create Python tuple"); Py_FatalError("couldn't create Python tuple");
event = find_cache_event(evsel);
if (!event) if (!event)
die("ug! no event found for type %d", (int)evsel->attr.config); die("ug! no event found for type %d", (int)evsel->attr.config);
@ -405,6 +403,9 @@ static void python_process_tracepoint(struct perf_sample *sample,
sprintf(handler_name, "%s__%s", event->system, event->name); sprintf(handler_name, "%s__%s", event->system, event->name);
if (!test_and_set_bit(event->id, events_defined))
define_event_symbols(event, handler_name, event->print_fmt.args);
handler = get_handler(handler_name); handler = get_handler(handler_name);
if (!handler) { if (!handler) {
dict = PyDict_New(); dict = PyDict_New();
@ -475,6 +476,289 @@ static void python_process_tracepoint(struct perf_sample *sample,
Py_DECREF(t); Py_DECREF(t);
} }
static PyObject *tuple_new(unsigned int sz)
{
PyObject *t;
t = PyTuple_New(sz);
if (!t)
Py_FatalError("couldn't create Python tuple");
return t;
}
static int tuple_set_u64(PyObject *t, unsigned int pos, u64 val)
{
#if BITS_PER_LONG == 64
return PyTuple_SetItem(t, pos, PyInt_FromLong(val));
#endif
#if BITS_PER_LONG == 32
return PyTuple_SetItem(t, pos, PyLong_FromLongLong(val));
#endif
}
static int tuple_set_s32(PyObject *t, unsigned int pos, s32 val)
{
return PyTuple_SetItem(t, pos, PyInt_FromLong(val));
}
static int tuple_set_string(PyObject *t, unsigned int pos, const char *s)
{
return PyTuple_SetItem(t, pos, PyString_FromString(s));
}
static int python_export_evsel(struct db_export *dbe, struct perf_evsel *evsel)
{
struct tables *tables = container_of(dbe, struct tables, dbe);
PyObject *t;
t = tuple_new(2);
tuple_set_u64(t, 0, evsel->db_id);
tuple_set_string(t, 1, perf_evsel__name(evsel));
call_object(tables->evsel_handler, t, "evsel_table");
Py_DECREF(t);
return 0;
}
static int python_export_machine(struct db_export *dbe,
struct machine *machine)
{
struct tables *tables = container_of(dbe, struct tables, dbe);
PyObject *t;
t = tuple_new(3);
tuple_set_u64(t, 0, machine->db_id);
tuple_set_s32(t, 1, machine->pid);
tuple_set_string(t, 2, machine->root_dir ? machine->root_dir : "");
call_object(tables->machine_handler, t, "machine_table");
Py_DECREF(t);
return 0;
}
static int python_export_thread(struct db_export *dbe, struct thread *thread,
u64 main_thread_db_id, struct machine *machine)
{
struct tables *tables = container_of(dbe, struct tables, dbe);
PyObject *t;
t = tuple_new(5);
tuple_set_u64(t, 0, thread->db_id);
tuple_set_u64(t, 1, machine->db_id);
tuple_set_u64(t, 2, main_thread_db_id);
tuple_set_s32(t, 3, thread->pid_);
tuple_set_s32(t, 4, thread->tid);
call_object(tables->thread_handler, t, "thread_table");
Py_DECREF(t);
return 0;
}
static int python_export_comm(struct db_export *dbe, struct comm *comm)
{
struct tables *tables = container_of(dbe, struct tables, dbe);
PyObject *t;
t = tuple_new(2);
tuple_set_u64(t, 0, comm->db_id);
tuple_set_string(t, 1, comm__str(comm));
call_object(tables->comm_handler, t, "comm_table");
Py_DECREF(t);
return 0;
}
static int python_export_comm_thread(struct db_export *dbe, u64 db_id,
struct comm *comm, struct thread *thread)
{
struct tables *tables = container_of(dbe, struct tables, dbe);
PyObject *t;
t = tuple_new(3);
tuple_set_u64(t, 0, db_id);
tuple_set_u64(t, 1, comm->db_id);
tuple_set_u64(t, 2, thread->db_id);
call_object(tables->comm_thread_handler, t, "comm_thread_table");
Py_DECREF(t);
return 0;
}
static int python_export_dso(struct db_export *dbe, struct dso *dso,
struct machine *machine)
{
struct tables *tables = container_of(dbe, struct tables, dbe);
char sbuild_id[BUILD_ID_SIZE * 2 + 1];
PyObject *t;
build_id__sprintf(dso->build_id, sizeof(dso->build_id), sbuild_id);
t = tuple_new(5);
tuple_set_u64(t, 0, dso->db_id);
tuple_set_u64(t, 1, machine->db_id);
tuple_set_string(t, 2, dso->short_name);
tuple_set_string(t, 3, dso->long_name);
tuple_set_string(t, 4, sbuild_id);
call_object(tables->dso_handler, t, "dso_table");
Py_DECREF(t);
return 0;
}
static int python_export_symbol(struct db_export *dbe, struct symbol *sym,
struct dso *dso)
{
struct tables *tables = container_of(dbe, struct tables, dbe);
u64 *sym_db_id = symbol__priv(sym);
PyObject *t;
t = tuple_new(6);
tuple_set_u64(t, 0, *sym_db_id);
tuple_set_u64(t, 1, dso->db_id);
tuple_set_u64(t, 2, sym->start);
tuple_set_u64(t, 3, sym->end);
tuple_set_s32(t, 4, sym->binding);
tuple_set_string(t, 5, sym->name);
call_object(tables->symbol_handler, t, "symbol_table");
Py_DECREF(t);
return 0;
}
static int python_export_branch_type(struct db_export *dbe, u32 branch_type,
const char *name)
{
struct tables *tables = container_of(dbe, struct tables, dbe);
PyObject *t;
t = tuple_new(2);
tuple_set_s32(t, 0, branch_type);
tuple_set_string(t, 1, name);
call_object(tables->branch_type_handler, t, "branch_type_table");
Py_DECREF(t);
return 0;
}
static int python_export_sample(struct db_export *dbe,
struct export_sample *es)
{
struct tables *tables = container_of(dbe, struct tables, dbe);
PyObject *t;
t = tuple_new(21);
tuple_set_u64(t, 0, es->db_id);
tuple_set_u64(t, 1, es->evsel->db_id);
tuple_set_u64(t, 2, es->al->machine->db_id);
tuple_set_u64(t, 3, es->thread->db_id);
tuple_set_u64(t, 4, es->comm_db_id);
tuple_set_u64(t, 5, es->dso_db_id);
tuple_set_u64(t, 6, es->sym_db_id);
tuple_set_u64(t, 7, es->offset);
tuple_set_u64(t, 8, es->sample->ip);
tuple_set_u64(t, 9, es->sample->time);
tuple_set_s32(t, 10, es->sample->cpu);
tuple_set_u64(t, 11, es->addr_dso_db_id);
tuple_set_u64(t, 12, es->addr_sym_db_id);
tuple_set_u64(t, 13, es->addr_offset);
tuple_set_u64(t, 14, es->sample->addr);
tuple_set_u64(t, 15, es->sample->period);
tuple_set_u64(t, 16, es->sample->weight);
tuple_set_u64(t, 17, es->sample->transaction);
tuple_set_u64(t, 18, es->sample->data_src);
tuple_set_s32(t, 19, es->sample->flags & PERF_BRANCH_MASK);
tuple_set_s32(t, 20, !!(es->sample->flags & PERF_IP_FLAG_IN_TX));
call_object(tables->sample_handler, t, "sample_table");
Py_DECREF(t);
return 0;
}
static int python_export_call_path(struct db_export *dbe, struct call_path *cp)
{
struct tables *tables = container_of(dbe, struct tables, dbe);
PyObject *t;
u64 parent_db_id, sym_db_id;
parent_db_id = cp->parent ? cp->parent->db_id : 0;
sym_db_id = cp->sym ? *(u64 *)symbol__priv(cp->sym) : 0;
t = tuple_new(4);
tuple_set_u64(t, 0, cp->db_id);
tuple_set_u64(t, 1, parent_db_id);
tuple_set_u64(t, 2, sym_db_id);
tuple_set_u64(t, 3, cp->ip);
call_object(tables->call_path_handler, t, "call_path_table");
Py_DECREF(t);
return 0;
}
static int python_export_call_return(struct db_export *dbe,
struct call_return *cr)
{
struct tables *tables = container_of(dbe, struct tables, dbe);
u64 comm_db_id = cr->comm ? cr->comm->db_id : 0;
PyObject *t;
t = tuple_new(11);
tuple_set_u64(t, 0, cr->db_id);
tuple_set_u64(t, 1, cr->thread->db_id);
tuple_set_u64(t, 2, comm_db_id);
tuple_set_u64(t, 3, cr->cp->db_id);
tuple_set_u64(t, 4, cr->call_time);
tuple_set_u64(t, 5, cr->return_time);
tuple_set_u64(t, 6, cr->branch_count);
tuple_set_u64(t, 7, cr->call_ref);
tuple_set_u64(t, 8, cr->return_ref);
tuple_set_u64(t, 9, cr->cp->parent->db_id);
tuple_set_s32(t, 10, cr->flags);
call_object(tables->call_return_handler, t, "call_return_table");
Py_DECREF(t);
return 0;
}
static int python_process_call_return(struct call_return *cr, void *data)
{
struct db_export *dbe = data;
return db_export__call_return(dbe, cr);
}
static void python_process_general_event(struct perf_sample *sample, static void python_process_general_event(struct perf_sample *sample,
struct perf_evsel *evsel, struct perf_evsel *evsel,
struct thread *thread, struct thread *thread,
@ -551,19 +835,25 @@ exit:
Py_DECREF(t); Py_DECREF(t);
} }
static void python_process_event(union perf_event *event __maybe_unused, static void python_process_event(union perf_event *event,
struct perf_sample *sample, struct perf_sample *sample,
struct perf_evsel *evsel, struct perf_evsel *evsel,
struct thread *thread, struct thread *thread,
struct addr_location *al) struct addr_location *al)
{ {
struct tables *tables = &tables_global;
switch (evsel->attr.type) { switch (evsel->attr.type) {
case PERF_TYPE_TRACEPOINT: case PERF_TYPE_TRACEPOINT:
python_process_tracepoint(sample, evsel, thread, al); python_process_tracepoint(sample, evsel, thread, al);
break; break;
/* Reserve for future process_hw/sw/raw APIs */ /* Reserve for future process_hw/sw/raw APIs */
default: default:
python_process_general_event(sample, evsel, thread, al); if (tables->db_export_mode)
db_export__sample(&tables->dbe, event, sample, evsel,
thread, al);
else
python_process_general_event(sample, evsel, thread, al);
} }
} }
@ -589,11 +879,79 @@ error:
return -1; return -1;
} }
#define SET_TABLE_HANDLER_(name, handler_name, table_name) do { \
tables->handler_name = get_handler(#table_name); \
if (tables->handler_name) \
tables->dbe.export_ ## name = python_export_ ## name; \
} while (0)
#define SET_TABLE_HANDLER(name) \
SET_TABLE_HANDLER_(name, name ## _handler, name ## _table)
static void set_table_handlers(struct tables *tables)
{
const char *perf_db_export_mode = "perf_db_export_mode";
const char *perf_db_export_calls = "perf_db_export_calls";
PyObject *db_export_mode, *db_export_calls;
bool export_calls = false;
int ret;
memset(tables, 0, sizeof(struct tables));
if (db_export__init(&tables->dbe))
Py_FatalError("failed to initialize export");
db_export_mode = PyDict_GetItemString(main_dict, perf_db_export_mode);
if (!db_export_mode)
return;
ret = PyObject_IsTrue(db_export_mode);
if (ret == -1)
handler_call_die(perf_db_export_mode);
if (!ret)
return;
tables->dbe.crp = NULL;
db_export_calls = PyDict_GetItemString(main_dict, perf_db_export_calls);
if (db_export_calls) {
ret = PyObject_IsTrue(db_export_calls);
if (ret == -1)
handler_call_die(perf_db_export_calls);
export_calls = !!ret;
}
if (export_calls) {
tables->dbe.crp =
call_return_processor__new(python_process_call_return,
&tables->dbe);
if (!tables->dbe.crp)
Py_FatalError("failed to create calls processor");
}
tables->db_export_mode = true;
/*
* Reserve per symbol space for symbol->db_id via symbol__priv()
*/
symbol_conf.priv_size = sizeof(u64);
SET_TABLE_HANDLER(evsel);
SET_TABLE_HANDLER(machine);
SET_TABLE_HANDLER(thread);
SET_TABLE_HANDLER(comm);
SET_TABLE_HANDLER(comm_thread);
SET_TABLE_HANDLER(dso);
SET_TABLE_HANDLER(symbol);
SET_TABLE_HANDLER(branch_type);
SET_TABLE_HANDLER(sample);
SET_TABLE_HANDLER(call_path);
SET_TABLE_HANDLER(call_return);
}
/* /*
* Start trace script * Start trace script
*/ */
static int python_start_script(const char *script, int argc, const char **argv) static int python_start_script(const char *script, int argc, const char **argv)
{ {
struct tables *tables = &tables_global;
const char **command_line; const char **command_line;
char buf[PATH_MAX]; char buf[PATH_MAX];
int i, err = 0; int i, err = 0;
@ -632,6 +990,14 @@ static int python_start_script(const char *script, int argc, const char **argv)
free(command_line); free(command_line);
set_table_handlers(tables);
if (tables->db_export_mode) {
err = db_export__branch_types(&tables->dbe);
if (err)
goto error;
}
return err; return err;
error: error:
Py_Finalize(); Py_Finalize();
@ -642,7 +1008,9 @@ error:
static int python_flush_script(void) static int python_flush_script(void)
{ {
return 0; struct tables *tables = &tables_global;
return db_export__flush(&tables->dbe);
} }
/* /*
@ -650,8 +1018,12 @@ static int python_flush_script(void)
*/ */
static int python_stop_script(void) static int python_stop_script(void)
{ {
struct tables *tables = &tables_global;
try_call_object("trace_end", NULL); try_call_object("trace_end", NULL);
db_export__exit(&tables->dbe);
Py_XDECREF(main_dict); Py_XDECREF(main_dict);
Py_XDECREF(main_module); Py_XDECREF(main_module);
Py_Finalize(); Py_Finalize();

View File

@ -228,6 +228,15 @@ static int process_finished_round(struct perf_tool *tool,
union perf_event *event, union perf_event *event,
struct perf_session *session); struct perf_session *session);
static int process_id_index_stub(struct perf_tool *tool __maybe_unused,
union perf_event *event __maybe_unused,
struct perf_session *perf_session
__maybe_unused)
{
dump_printf(": unhandled!\n");
return 0;
}
void perf_tool__fill_defaults(struct perf_tool *tool) void perf_tool__fill_defaults(struct perf_tool *tool)
{ {
if (tool->sample == NULL) if (tool->sample == NULL)
@ -262,6 +271,8 @@ void perf_tool__fill_defaults(struct perf_tool *tool)
else else
tool->finished_round = process_finished_round_stub; tool->finished_round = process_finished_round_stub;
} }
if (tool->id_index == NULL)
tool->id_index = process_id_index_stub;
} }
static void swap_sample_id_all(union perf_event *event, void *data) static void swap_sample_id_all(union perf_event *event, void *data)
@ -460,6 +471,7 @@ static perf_event__swap_op perf_event__swap_ops[] = {
[PERF_RECORD_HEADER_EVENT_TYPE] = perf_event__event_type_swap, [PERF_RECORD_HEADER_EVENT_TYPE] = perf_event__event_type_swap,
[PERF_RECORD_HEADER_TRACING_DATA] = perf_event__tracing_data_swap, [PERF_RECORD_HEADER_TRACING_DATA] = perf_event__tracing_data_swap,
[PERF_RECORD_HEADER_BUILD_ID] = NULL, [PERF_RECORD_HEADER_BUILD_ID] = NULL,
[PERF_RECORD_ID_INDEX] = perf_event__all64_swap,
[PERF_RECORD_HEADER_MAX] = NULL, [PERF_RECORD_HEADER_MAX] = NULL,
}; };
@ -580,15 +592,46 @@ static void regs_dump__printf(u64 mask, u64 *regs)
} }
} }
static const char *regs_abi[] = {
[PERF_SAMPLE_REGS_ABI_NONE] = "none",
[PERF_SAMPLE_REGS_ABI_32] = "32-bit",
[PERF_SAMPLE_REGS_ABI_64] = "64-bit",
};
static inline const char *regs_dump_abi(struct regs_dump *d)
{
if (d->abi > PERF_SAMPLE_REGS_ABI_64)
return "unknown";
return regs_abi[d->abi];
}
static void regs__printf(const char *type, struct regs_dump *regs)
{
u64 mask = regs->mask;
printf("... %s regs: mask 0x%" PRIx64 " ABI %s\n",
type,
mask,
regs_dump_abi(regs));
regs_dump__printf(mask, regs->regs);
}
static void regs_user__printf(struct perf_sample *sample) static void regs_user__printf(struct perf_sample *sample)
{ {
struct regs_dump *user_regs = &sample->user_regs; struct regs_dump *user_regs = &sample->user_regs;
if (user_regs->regs) { if (user_regs->regs)
u64 mask = user_regs->mask; regs__printf("user", user_regs);
printf("... user regs: mask 0x%" PRIx64 "\n", mask); }
regs_dump__printf(mask, user_regs->regs);
} static void regs_intr__printf(struct perf_sample *sample)
{
struct regs_dump *intr_regs = &sample->intr_regs;
if (intr_regs->regs)
regs__printf("intr", intr_regs);
} }
static void stack_user__printf(struct stack_dump *dump) static void stack_user__printf(struct stack_dump *dump)
@ -687,6 +730,9 @@ static void dump_sample(struct perf_evsel *evsel, union perf_event *event,
if (sample_type & PERF_SAMPLE_REGS_USER) if (sample_type & PERF_SAMPLE_REGS_USER)
regs_user__printf(sample); regs_user__printf(sample);
if (sample_type & PERF_SAMPLE_REGS_INTR)
regs_intr__printf(sample);
if (sample_type & PERF_SAMPLE_STACK_USER) if (sample_type & PERF_SAMPLE_STACK_USER)
stack_user__printf(&sample->user_stack); stack_user__printf(&sample->user_stack);
@ -888,11 +934,26 @@ static s64 perf_session__process_user_event(struct perf_session *session,
return tool->build_id(tool, event, session); return tool->build_id(tool, event, session);
case PERF_RECORD_FINISHED_ROUND: case PERF_RECORD_FINISHED_ROUND:
return tool->finished_round(tool, event, session); return tool->finished_round(tool, event, session);
case PERF_RECORD_ID_INDEX:
return tool->id_index(tool, event, session);
default: default:
return -EINVAL; return -EINVAL;
} }
} }
int perf_session__deliver_synth_event(struct perf_session *session,
union perf_event *event,
struct perf_sample *sample,
struct perf_tool *tool)
{
events_stats__inc(&session->stats, event->header.type);
if (event->header.type >= PERF_RECORD_USER_TYPE_START)
return perf_session__process_user_event(session, event, tool, 0);
return perf_session__deliver_event(session, event, sample, tool, 0);
}
static void event_swap(union perf_event *event, bool sample_id_all) static void event_swap(union perf_event *event, bool sample_id_all)
{ {
perf_event__swap_op swap; perf_event__swap_op swap;
@ -1417,9 +1478,9 @@ void perf_evsel__print_ip(struct perf_evsel *evsel, struct perf_sample *sample,
if (symbol_conf.use_callchain && sample->callchain) { if (symbol_conf.use_callchain && sample->callchain) {
struct addr_location node_al; struct addr_location node_al;
if (machine__resolve_callchain(al->machine, evsel, al->thread, if (thread__resolve_callchain(al->thread, evsel,
sample, NULL, NULL, sample, NULL, NULL,
PERF_MAX_STACK_DEPTH) != 0) { PERF_MAX_STACK_DEPTH) != 0) {
if (verbose) if (verbose)
error("Failed to resolve callchain. Skipping\n"); error("Failed to resolve callchain. Skipping\n");
return; return;
@ -1594,3 +1655,111 @@ int __perf_session__set_tracepoints_handlers(struct perf_session *session,
out: out:
return err; return err;
} }
int perf_event__process_id_index(struct perf_tool *tool __maybe_unused,
union perf_event *event,
struct perf_session *session)
{
struct perf_evlist *evlist = session->evlist;
struct id_index_event *ie = &event->id_index;
size_t i, nr, max_nr;
max_nr = (ie->header.size - sizeof(struct id_index_event)) /
sizeof(struct id_index_entry);
nr = ie->nr;
if (nr > max_nr)
return -EINVAL;
if (dump_trace)
fprintf(stdout, " nr: %zu\n", nr);
for (i = 0; i < nr; i++) {
struct id_index_entry *e = &ie->entries[i];
struct perf_sample_id *sid;
if (dump_trace) {
fprintf(stdout, " ... id: %"PRIu64, e->id);
fprintf(stdout, " idx: %"PRIu64, e->idx);
fprintf(stdout, " cpu: %"PRId64, e->cpu);
fprintf(stdout, " tid: %"PRId64"\n", e->tid);
}
sid = perf_evlist__id2sid(evlist, e->id);
if (!sid)
return -ENOENT;
sid->idx = e->idx;
sid->cpu = e->cpu;
sid->tid = e->tid;
}
return 0;
}
int perf_event__synthesize_id_index(struct perf_tool *tool,
perf_event__handler_t process,
struct perf_evlist *evlist,
struct machine *machine)
{
union perf_event *ev;
struct perf_evsel *evsel;
size_t nr = 0, i = 0, sz, max_nr, n;
int err;
pr_debug2("Synthesizing id index\n");
max_nr = (UINT16_MAX - sizeof(struct id_index_event)) /
sizeof(struct id_index_entry);
evlist__for_each(evlist, evsel)
nr += evsel->ids;
n = nr > max_nr ? max_nr : nr;
sz = sizeof(struct id_index_event) + n * sizeof(struct id_index_entry);
ev = zalloc(sz);
if (!ev)
return -ENOMEM;
ev->id_index.header.type = PERF_RECORD_ID_INDEX;
ev->id_index.header.size = sz;
ev->id_index.nr = n;
evlist__for_each(evlist, evsel) {
u32 j;
for (j = 0; j < evsel->ids; j++) {
struct id_index_entry *e;
struct perf_sample_id *sid;
if (i >= n) {
err = process(tool, ev, NULL, machine);
if (err)
goto out_err;
nr -= n;
i = 0;
}
e = &ev->id_index.entries[i++];
e->id = evsel->id[j];
sid = perf_evlist__id2sid(evlist, e->id);
if (!sid) {
free(ev);
return -ENOENT;
}
e->idx = sid->idx;
e->cpu = sid->cpu;
e->tid = sid->tid;
}
}
sz = sizeof(struct id_index_event) + nr * sizeof(struct id_index_entry);
ev->id_index.header.size = sz;
ev->id_index.nr = nr;
err = process(tool, ev, NULL, machine);
out_err:
free(ev);
return err;
}

View File

@ -126,4 +126,19 @@ int __perf_session__set_tracepoints_handlers(struct perf_session *session,
extern volatile int session_done; extern volatile int session_done;
#define session_done() ACCESS_ONCE(session_done) #define session_done() ACCESS_ONCE(session_done)
int perf_session__deliver_synth_event(struct perf_session *session,
union perf_event *event,
struct perf_sample *sample,
struct perf_tool *tool);
int perf_event__process_id_index(struct perf_tool *tool,
union perf_event *event,
struct perf_session *session);
int perf_event__synthesize_id_index(struct perf_tool *tool,
perf_event__handler_t process,
struct perf_evlist *evlist,
struct machine *machine);
#endif /* __PERF_SESSION_H */ #endif /* __PERF_SESSION_H */

View File

@ -309,7 +309,7 @@ sort__srcline_cmp(struct hist_entry *left, struct hist_entry *right)
static int hist_entry__srcline_snprintf(struct hist_entry *he, char *bf, static int hist_entry__srcline_snprintf(struct hist_entry *he, char *bf,
size_t size, unsigned int width) size_t size, unsigned int width)
{ {
return repsep_snprintf(bf, size, "%*.*-s", width, width, he->srcline); return repsep_snprintf(bf, size, "%-*.*s", width, width, he->srcline);
} }
struct sort_entry sort_srcline = { struct sort_entry sort_srcline = {

View File

@ -274,7 +274,7 @@ char *get_srcline(struct dso *dso, unsigned long addr)
if (!addr2line(dso_name, addr, &file, &line, dso)) if (!addr2line(dso_name, addr, &file, &line, dso))
goto out; goto out;
if (asprintf(&srcline, "%s:%u", file, line) < 0) { if (asprintf(&srcline, "%s:%u", basename(file), line) < 0) {
free(file); free(file);
goto out; goto out;
} }

View File

@ -546,6 +546,35 @@ static int dso__swap_init(struct dso *dso, unsigned char eidata)
return 0; return 0;
} }
static int decompress_kmodule(struct dso *dso, const char *name,
enum dso_binary_type type)
{
int fd;
const char *ext = strrchr(name, '.');
char tmpbuf[] = "/tmp/perf-kmod-XXXXXX";
if ((type != DSO_BINARY_TYPE__SYSTEM_PATH_KMODULE_COMP &&
type != DSO_BINARY_TYPE__GUEST_KMODULE_COMP) ||
type != dso->symtab_type)
return -1;
if (!ext || !is_supported_compression(ext + 1))
return -1;
fd = mkstemp(tmpbuf);
if (fd < 0)
return -1;
if (!decompress_to_file(ext + 1, name, fd)) {
close(fd);
fd = -1;
}
unlink(tmpbuf);
return fd;
}
bool symsrc__possibly_runtime(struct symsrc *ss) bool symsrc__possibly_runtime(struct symsrc *ss)
{ {
return ss->dynsym || ss->opdsec; return ss->dynsym || ss->opdsec;
@ -571,7 +600,11 @@ int symsrc__init(struct symsrc *ss, struct dso *dso, const char *name,
Elf *elf; Elf *elf;
int fd; int fd;
fd = open(name, O_RDONLY); if (dso__needs_decompress(dso))
fd = decompress_kmodule(dso, name, type);
else
fd = open(name, O_RDONLY);
if (fd < 0) if (fd < 0)
return -1; return -1;

View File

@ -341,7 +341,6 @@ int dso__load_sym(struct dso *dso, struct map *map __maybe_unused,
if (filename__read_build_id(ss->name, build_id, BUILD_ID_SIZE) > 0) { if (filename__read_build_id(ss->name, build_id, BUILD_ID_SIZE) > 0) {
dso__set_build_id(dso, build_id); dso__set_build_id(dso, build_id);
return 1;
} }
return 0; return 0;
} }

View File

@ -51,7 +51,9 @@ static enum dso_binary_type binary_type_symtab[] = {
DSO_BINARY_TYPE__BUILDID_DEBUGINFO, DSO_BINARY_TYPE__BUILDID_DEBUGINFO,
DSO_BINARY_TYPE__SYSTEM_PATH_DSO, DSO_BINARY_TYPE__SYSTEM_PATH_DSO,
DSO_BINARY_TYPE__GUEST_KMODULE, DSO_BINARY_TYPE__GUEST_KMODULE,
DSO_BINARY_TYPE__GUEST_KMODULE_COMP,
DSO_BINARY_TYPE__SYSTEM_PATH_KMODULE, DSO_BINARY_TYPE__SYSTEM_PATH_KMODULE,
DSO_BINARY_TYPE__SYSTEM_PATH_KMODULE_COMP,
DSO_BINARY_TYPE__OPENEMBEDDED_DEBUGINFO, DSO_BINARY_TYPE__OPENEMBEDDED_DEBUGINFO,
DSO_BINARY_TYPE__NOT_FOUND, DSO_BINARY_TYPE__NOT_FOUND,
}; };
@ -1300,7 +1302,9 @@ static bool dso__is_compatible_symtab_type(struct dso *dso, bool kmod,
return dso->kernel == DSO_TYPE_GUEST_KERNEL; return dso->kernel == DSO_TYPE_GUEST_KERNEL;
case DSO_BINARY_TYPE__GUEST_KMODULE: case DSO_BINARY_TYPE__GUEST_KMODULE:
case DSO_BINARY_TYPE__GUEST_KMODULE_COMP:
case DSO_BINARY_TYPE__SYSTEM_PATH_KMODULE: case DSO_BINARY_TYPE__SYSTEM_PATH_KMODULE:
case DSO_BINARY_TYPE__SYSTEM_PATH_KMODULE_COMP:
/* /*
* kernel modules know their symtab type - it's set when * kernel modules know their symtab type - it's set when
* creating a module dso in machine__new_module(). * creating a module dso in machine__new_module().
@ -1368,7 +1372,9 @@ int dso__load(struct dso *dso, struct map *map, symbol_filter_t filter)
return -1; return -1;
kmod = dso->symtab_type == DSO_BINARY_TYPE__SYSTEM_PATH_KMODULE || kmod = dso->symtab_type == DSO_BINARY_TYPE__SYSTEM_PATH_KMODULE ||
dso->symtab_type == DSO_BINARY_TYPE__GUEST_KMODULE; dso->symtab_type == DSO_BINARY_TYPE__SYSTEM_PATH_KMODULE_COMP ||
dso->symtab_type == DSO_BINARY_TYPE__GUEST_KMODULE ||
dso->symtab_type == DSO_BINARY_TYPE__GUEST_KMODULE_COMP;
/* /*
* Iterate over candidate debug images. * Iterate over candidate debug images.
@ -1505,12 +1511,10 @@ int dso__load_vmlinux_path(struct dso *dso, struct map *map,
symbol_filter_t filter) symbol_filter_t filter)
{ {
int i, err = 0; int i, err = 0;
char *filename; char *filename = NULL;
pr_debug("Looking at the vmlinux_path (%d entries long)\n", if (!symbol_conf.ignore_vmlinux_buildid)
vmlinux_path__nr_entries + 1); filename = dso__build_id_filename(dso, NULL, 0);
filename = dso__build_id_filename(dso, NULL, 0);
if (filename != NULL) { if (filename != NULL) {
err = dso__load_vmlinux(dso, map, filename, true, filter); err = dso__load_vmlinux(dso, map, filename, true, filter);
if (err > 0) if (err > 0)
@ -1518,6 +1522,9 @@ int dso__load_vmlinux_path(struct dso *dso, struct map *map,
free(filename); free(filename);
} }
pr_debug("Looking at the vmlinux_path (%d entries long)\n",
vmlinux_path__nr_entries + 1);
for (i = 0; i < vmlinux_path__nr_entries; ++i) { for (i = 0; i < vmlinux_path__nr_entries; ++i) {
err = dso__load_vmlinux(dso, map, vmlinux_path[i], false, filter); err = dso__load_vmlinux(dso, map, vmlinux_path[i], false, filter);
if (err > 0) if (err > 0)

View File

@ -105,6 +105,7 @@ struct symbol_conf {
unsigned short nr_events; unsigned short nr_events;
bool try_vmlinux_path, bool try_vmlinux_path,
ignore_vmlinux, ignore_vmlinux,
ignore_vmlinux_buildid,
show_kernel_path, show_kernel_path,
use_modules, use_modules,
sort_by_name, sort_by_name,

View File

@ -0,0 +1,747 @@
/*
* thread-stack.c: Synthesize a thread's stack using call / return events
* Copyright (c) 2014, Intel Corporation.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope 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.
*
*/
#include <linux/rbtree.h>
#include <linux/list.h>
#include "thread.h"
#include "event.h"
#include "machine.h"
#include "util.h"
#include "debug.h"
#include "symbol.h"
#include "comm.h"
#include "thread-stack.h"
#define CALL_PATH_BLOCK_SHIFT 8
#define CALL_PATH_BLOCK_SIZE (1 << CALL_PATH_BLOCK_SHIFT)
#define CALL_PATH_BLOCK_MASK (CALL_PATH_BLOCK_SIZE - 1)
struct call_path_block {
struct call_path cp[CALL_PATH_BLOCK_SIZE];
struct list_head node;
};
/**
* struct call_path_root - root of all call paths.
* @call_path: root call path
* @blocks: list of blocks to store call paths
* @next: next free space
* @sz: number of spaces
*/
struct call_path_root {
struct call_path call_path;
struct list_head blocks;
size_t next;
size_t sz;
};
/**
* struct call_return_processor - provides a call-back to consume call-return
* information.
* @cpr: call path root
* @process: call-back that accepts call/return information
* @data: anonymous data for call-back
*/
struct call_return_processor {
struct call_path_root *cpr;
int (*process)(struct call_return *cr, void *data);
void *data;
};
#define STACK_GROWTH 2048
/**
* struct thread_stack_entry - thread stack entry.
* @ret_addr: return address
* @timestamp: timestamp (if known)
* @ref: external reference (e.g. db_id of sample)
* @branch_count: the branch count when the entry was created
* @cp: call path
* @no_call: a 'call' was not seen
*/
struct thread_stack_entry {
u64 ret_addr;
u64 timestamp;
u64 ref;
u64 branch_count;
struct call_path *cp;
bool no_call;
};
/**
* struct thread_stack - thread stack constructed from 'call' and 'return'
* branch samples.
* @stack: array that holds the stack
* @cnt: number of entries in the stack
* @sz: current maximum stack size
* @trace_nr: current trace number
* @branch_count: running branch count
* @kernel_start: kernel start address
* @last_time: last timestamp
* @crp: call/return processor
* @comm: current comm
*/
struct thread_stack {
struct thread_stack_entry *stack;
size_t cnt;
size_t sz;
u64 trace_nr;
u64 branch_count;
u64 kernel_start;
u64 last_time;
struct call_return_processor *crp;
struct comm *comm;
};
static int thread_stack__grow(struct thread_stack *ts)
{
struct thread_stack_entry *new_stack;
size_t sz, new_sz;
new_sz = ts->sz + STACK_GROWTH;
sz = new_sz * sizeof(struct thread_stack_entry);
new_stack = realloc(ts->stack, sz);
if (!new_stack)
return -ENOMEM;
ts->stack = new_stack;
ts->sz = new_sz;
return 0;
}
static struct thread_stack *thread_stack__new(struct thread *thread,
struct call_return_processor *crp)
{
struct thread_stack *ts;
ts = zalloc(sizeof(struct thread_stack));
if (!ts)
return NULL;
if (thread_stack__grow(ts)) {
free(ts);
return NULL;
}
if (thread->mg && thread->mg->machine)
ts->kernel_start = machine__kernel_start(thread->mg->machine);
else
ts->kernel_start = 1ULL << 63;
ts->crp = crp;
return ts;
}
static int thread_stack__push(struct thread_stack *ts, u64 ret_addr)
{
int err = 0;
if (ts->cnt == ts->sz) {
err = thread_stack__grow(ts);
if (err) {
pr_warning("Out of memory: discarding thread stack\n");
ts->cnt = 0;
}
}
ts->stack[ts->cnt++].ret_addr = ret_addr;
return err;
}
static void thread_stack__pop(struct thread_stack *ts, u64 ret_addr)
{
size_t i;
/*
* In some cases there may be functions which are not seen to return.
* For example when setjmp / longjmp has been used. Or the perf context
* switch in the kernel which doesn't stop and start tracing in exactly
* the same code path. When that happens the return address will be
* further down the stack. If the return address is not found at all,
* we assume the opposite (i.e. this is a return for a call that wasn't
* seen for some reason) and leave the stack alone.
*/
for (i = ts->cnt; i; ) {
if (ts->stack[--i].ret_addr == ret_addr) {
ts->cnt = i;
return;
}
}
}
static bool thread_stack__in_kernel(struct thread_stack *ts)
{
if (!ts->cnt)
return false;
return ts->stack[ts->cnt - 1].cp->in_kernel;
}
static int thread_stack__call_return(struct thread *thread,
struct thread_stack *ts, size_t idx,
u64 timestamp, u64 ref, bool no_return)
{
struct call_return_processor *crp = ts->crp;
struct thread_stack_entry *tse;
struct call_return cr = {
.thread = thread,
.comm = ts->comm,
.db_id = 0,
};
tse = &ts->stack[idx];
cr.cp = tse->cp;
cr.call_time = tse->timestamp;
cr.return_time = timestamp;
cr.branch_count = ts->branch_count - tse->branch_count;
cr.call_ref = tse->ref;
cr.return_ref = ref;
if (tse->no_call)
cr.flags |= CALL_RETURN_NO_CALL;
if (no_return)
cr.flags |= CALL_RETURN_NO_RETURN;
return crp->process(&cr, crp->data);
}
static int thread_stack__flush(struct thread *thread, struct thread_stack *ts)
{
struct call_return_processor *crp = ts->crp;
int err;
if (!crp) {
ts->cnt = 0;
return 0;
}
while (ts->cnt) {
err = thread_stack__call_return(thread, ts, --ts->cnt,
ts->last_time, 0, true);
if (err) {
pr_err("Error flushing thread stack!\n");
ts->cnt = 0;
return err;
}
}
return 0;
}
int thread_stack__event(struct thread *thread, u32 flags, u64 from_ip,
u64 to_ip, u16 insn_len, u64 trace_nr)
{
if (!thread)
return -EINVAL;
if (!thread->ts) {
thread->ts = thread_stack__new(thread, NULL);
if (!thread->ts) {
pr_warning("Out of memory: no thread stack\n");
return -ENOMEM;
}
thread->ts->trace_nr = trace_nr;
}
/*
* When the trace is discontinuous, the trace_nr changes. In that case
* the stack might be completely invalid. Better to report nothing than
* to report something misleading, so flush the stack.
*/
if (trace_nr != thread->ts->trace_nr) {
if (thread->ts->trace_nr)
thread_stack__flush(thread, thread->ts);
thread->ts->trace_nr = trace_nr;
}
/* Stop here if thread_stack__process() is in use */
if (thread->ts->crp)
return 0;
if (flags & PERF_IP_FLAG_CALL) {
u64 ret_addr;
if (!to_ip)
return 0;
ret_addr = from_ip + insn_len;
if (ret_addr == to_ip)
return 0; /* Zero-length calls are excluded */
return thread_stack__push(thread->ts, ret_addr);
} else if (flags & PERF_IP_FLAG_RETURN) {
if (!from_ip)
return 0;
thread_stack__pop(thread->ts, to_ip);
}
return 0;
}
void thread_stack__set_trace_nr(struct thread *thread, u64 trace_nr)
{
if (!thread || !thread->ts)
return;
if (trace_nr != thread->ts->trace_nr) {
if (thread->ts->trace_nr)
thread_stack__flush(thread, thread->ts);
thread->ts->trace_nr = trace_nr;
}
}
void thread_stack__free(struct thread *thread)
{
if (thread->ts) {
thread_stack__flush(thread, thread->ts);
zfree(&thread->ts->stack);
zfree(&thread->ts);
}
}
void thread_stack__sample(struct thread *thread, struct ip_callchain *chain,
size_t sz, u64 ip)
{
size_t i;
if (!thread || !thread->ts)
chain->nr = 1;
else
chain->nr = min(sz, thread->ts->cnt + 1);
chain->ips[0] = ip;
for (i = 1; i < chain->nr; i++)
chain->ips[i] = thread->ts->stack[thread->ts->cnt - i].ret_addr;
}
static void call_path__init(struct call_path *cp, struct call_path *parent,
struct symbol *sym, u64 ip, bool in_kernel)
{
cp->parent = parent;
cp->sym = sym;
cp->ip = sym ? 0 : ip;
cp->db_id = 0;
cp->in_kernel = in_kernel;
RB_CLEAR_NODE(&cp->rb_node);
cp->children = RB_ROOT;
}
static struct call_path_root *call_path_root__new(void)
{
struct call_path_root *cpr;
cpr = zalloc(sizeof(struct call_path_root));
if (!cpr)
return NULL;
call_path__init(&cpr->call_path, NULL, NULL, 0, false);
INIT_LIST_HEAD(&cpr->blocks);
return cpr;
}
static void call_path_root__free(struct call_path_root *cpr)
{
struct call_path_block *pos, *n;
list_for_each_entry_safe(pos, n, &cpr->blocks, node) {
list_del(&pos->node);
free(pos);
}
free(cpr);
}
static struct call_path *call_path__new(struct call_path_root *cpr,
struct call_path *parent,
struct symbol *sym, u64 ip,
bool in_kernel)
{
struct call_path_block *cpb;
struct call_path *cp;
size_t n;
if (cpr->next < cpr->sz) {
cpb = list_last_entry(&cpr->blocks, struct call_path_block,
node);
} else {
cpb = zalloc(sizeof(struct call_path_block));
if (!cpb)
return NULL;
list_add_tail(&cpb->node, &cpr->blocks);
cpr->sz += CALL_PATH_BLOCK_SIZE;
}
n = cpr->next++ & CALL_PATH_BLOCK_MASK;
cp = &cpb->cp[n];
call_path__init(cp, parent, sym, ip, in_kernel);
return cp;
}
static struct call_path *call_path__findnew(struct call_path_root *cpr,
struct call_path *parent,
struct symbol *sym, u64 ip, u64 ks)
{
struct rb_node **p;
struct rb_node *node_parent = NULL;
struct call_path *cp;
bool in_kernel = ip >= ks;
if (sym)
ip = 0;
if (!parent)
return call_path__new(cpr, parent, sym, ip, in_kernel);
p = &parent->children.rb_node;
while (*p != NULL) {
node_parent = *p;
cp = rb_entry(node_parent, struct call_path, rb_node);
if (cp->sym == sym && cp->ip == ip)
return cp;
if (sym < cp->sym || (sym == cp->sym && ip < cp->ip))
p = &(*p)->rb_left;
else
p = &(*p)->rb_right;
}
cp = call_path__new(cpr, parent, sym, ip, in_kernel);
if (!cp)
return NULL;
rb_link_node(&cp->rb_node, node_parent, p);
rb_insert_color(&cp->rb_node, &parent->children);
return cp;
}
struct call_return_processor *
call_return_processor__new(int (*process)(struct call_return *cr, void *data),
void *data)
{
struct call_return_processor *crp;
crp = zalloc(sizeof(struct call_return_processor));
if (!crp)
return NULL;
crp->cpr = call_path_root__new();
if (!crp->cpr)
goto out_free;
crp->process = process;
crp->data = data;
return crp;
out_free:
free(crp);
return NULL;
}
void call_return_processor__free(struct call_return_processor *crp)
{
if (crp) {
call_path_root__free(crp->cpr);
free(crp);
}
}
static int thread_stack__push_cp(struct thread_stack *ts, u64 ret_addr,
u64 timestamp, u64 ref, struct call_path *cp,
bool no_call)
{
struct thread_stack_entry *tse;
int err;
if (ts->cnt == ts->sz) {
err = thread_stack__grow(ts);
if (err)
return err;
}
tse = &ts->stack[ts->cnt++];
tse->ret_addr = ret_addr;
tse->timestamp = timestamp;
tse->ref = ref;
tse->branch_count = ts->branch_count;
tse->cp = cp;
tse->no_call = no_call;
return 0;
}
static int thread_stack__pop_cp(struct thread *thread, struct thread_stack *ts,
u64 ret_addr, u64 timestamp, u64 ref,
struct symbol *sym)
{
int err;
if (!ts->cnt)
return 1;
if (ts->cnt == 1) {
struct thread_stack_entry *tse = &ts->stack[0];
if (tse->cp->sym == sym)
return thread_stack__call_return(thread, ts, --ts->cnt,
timestamp, ref, false);
}
if (ts->stack[ts->cnt - 1].ret_addr == ret_addr) {
return thread_stack__call_return(thread, ts, --ts->cnt,
timestamp, ref, false);
} else {
size_t i = ts->cnt - 1;
while (i--) {
if (ts->stack[i].ret_addr != ret_addr)
continue;
i += 1;
while (ts->cnt > i) {
err = thread_stack__call_return(thread, ts,
--ts->cnt,
timestamp, ref,
true);
if (err)
return err;
}
return thread_stack__call_return(thread, ts, --ts->cnt,
timestamp, ref, false);
}
}
return 1;
}
static int thread_stack__bottom(struct thread *thread, struct thread_stack *ts,
struct perf_sample *sample,
struct addr_location *from_al,
struct addr_location *to_al, u64 ref)
{
struct call_path_root *cpr = ts->crp->cpr;
struct call_path *cp;
struct symbol *sym;
u64 ip;
if (sample->ip) {
ip = sample->ip;
sym = from_al->sym;
} else if (sample->addr) {
ip = sample->addr;
sym = to_al->sym;
} else {
return 0;
}
cp = call_path__findnew(cpr, &cpr->call_path, sym, ip,
ts->kernel_start);
if (!cp)
return -ENOMEM;
return thread_stack__push_cp(thread->ts, ip, sample->time, ref, cp,
true);
}
static int thread_stack__no_call_return(struct thread *thread,
struct thread_stack *ts,
struct perf_sample *sample,
struct addr_location *from_al,
struct addr_location *to_al, u64 ref)
{
struct call_path_root *cpr = ts->crp->cpr;
struct call_path *cp, *parent;
u64 ks = ts->kernel_start;
int err;
if (sample->ip >= ks && sample->addr < ks) {
/* Return to userspace, so pop all kernel addresses */
while (thread_stack__in_kernel(ts)) {
err = thread_stack__call_return(thread, ts, --ts->cnt,
sample->time, ref,
true);
if (err)
return err;
}
/* If the stack is empty, push the userspace address */
if (!ts->cnt) {
cp = call_path__findnew(cpr, &cpr->call_path,
to_al->sym, sample->addr,
ts->kernel_start);
if (!cp)
return -ENOMEM;
return thread_stack__push_cp(ts, 0, sample->time, ref,
cp, true);
}
} else if (thread_stack__in_kernel(ts) && sample->ip < ks) {
/* Return to userspace, so pop all kernel addresses */
while (thread_stack__in_kernel(ts)) {
err = thread_stack__call_return(thread, ts, --ts->cnt,
sample->time, ref,
true);
if (err)
return err;
}
}
if (ts->cnt)
parent = ts->stack[ts->cnt - 1].cp;
else
parent = &cpr->call_path;
/* This 'return' had no 'call', so push and pop top of stack */
cp = call_path__findnew(cpr, parent, from_al->sym, sample->ip,
ts->kernel_start);
if (!cp)
return -ENOMEM;
err = thread_stack__push_cp(ts, sample->addr, sample->time, ref, cp,
true);
if (err)
return err;
return thread_stack__pop_cp(thread, ts, sample->addr, sample->time, ref,
to_al->sym);
}
static int thread_stack__trace_begin(struct thread *thread,
struct thread_stack *ts, u64 timestamp,
u64 ref)
{
struct thread_stack_entry *tse;
int err;
if (!ts->cnt)
return 0;
/* Pop trace end */
tse = &ts->stack[ts->cnt - 1];
if (tse->cp->sym == NULL && tse->cp->ip == 0) {
err = thread_stack__call_return(thread, ts, --ts->cnt,
timestamp, ref, false);
if (err)
return err;
}
return 0;
}
static int thread_stack__trace_end(struct thread_stack *ts,
struct perf_sample *sample, u64 ref)
{
struct call_path_root *cpr = ts->crp->cpr;
struct call_path *cp;
u64 ret_addr;
/* No point having 'trace end' on the bottom of the stack */
if (!ts->cnt || (ts->cnt == 1 && ts->stack[0].ref == ref))
return 0;
cp = call_path__findnew(cpr, ts->stack[ts->cnt - 1].cp, NULL, 0,
ts->kernel_start);
if (!cp)
return -ENOMEM;
ret_addr = sample->ip + sample->insn_len;
return thread_stack__push_cp(ts, ret_addr, sample->time, ref, cp,
false);
}
int thread_stack__process(struct thread *thread, struct comm *comm,
struct perf_sample *sample,
struct addr_location *from_al,
struct addr_location *to_al, u64 ref,
struct call_return_processor *crp)
{
struct thread_stack *ts = thread->ts;
int err = 0;
if (ts) {
if (!ts->crp) {
/* Supersede thread_stack__event() */
thread_stack__free(thread);
thread->ts = thread_stack__new(thread, crp);
if (!thread->ts)
return -ENOMEM;
ts = thread->ts;
ts->comm = comm;
}
} else {
thread->ts = thread_stack__new(thread, crp);
if (!thread->ts)
return -ENOMEM;
ts = thread->ts;
ts->comm = comm;
}
/* Flush stack on exec */
if (ts->comm != comm && thread->pid_ == thread->tid) {
err = thread_stack__flush(thread, ts);
if (err)
return err;
ts->comm = comm;
}
/* If the stack is empty, put the current symbol on the stack */
if (!ts->cnt) {
err = thread_stack__bottom(thread, ts, sample, from_al, to_al,
ref);
if (err)
return err;
}
ts->branch_count += 1;
ts->last_time = sample->time;
if (sample->flags & PERF_IP_FLAG_CALL) {
struct call_path_root *cpr = ts->crp->cpr;
struct call_path *cp;
u64 ret_addr;
if (!sample->ip || !sample->addr)
return 0;
ret_addr = sample->ip + sample->insn_len;
if (ret_addr == sample->addr)
return 0; /* Zero-length calls are excluded */
cp = call_path__findnew(cpr, ts->stack[ts->cnt - 1].cp,
to_al->sym, sample->addr,
ts->kernel_start);
if (!cp)
return -ENOMEM;
err = thread_stack__push_cp(ts, ret_addr, sample->time, ref,
cp, false);
} else if (sample->flags & PERF_IP_FLAG_RETURN) {
if (!sample->ip || !sample->addr)
return 0;
err = thread_stack__pop_cp(thread, ts, sample->addr,
sample->time, ref, from_al->sym);
if (err) {
if (err < 0)
return err;
err = thread_stack__no_call_return(thread, ts, sample,
from_al, to_al, ref);
}
} else if (sample->flags & PERF_IP_FLAG_TRACE_BEGIN) {
err = thread_stack__trace_begin(thread, ts, sample->time, ref);
} else if (sample->flags & PERF_IP_FLAG_TRACE_END) {
err = thread_stack__trace_end(ts, sample, ref);
}
return err;
}

View File

@ -0,0 +1,111 @@
/*
* thread-stack.h: Synthesize a thread's stack using call / return events
* Copyright (c) 2014, Intel Corporation.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope 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.
*
*/
#ifndef __PERF_THREAD_STACK_H
#define __PERF_THREAD_STACK_H
#include <sys/types.h>
#include <linux/types.h>
#include <linux/rbtree.h>
struct thread;
struct comm;
struct ip_callchain;
struct symbol;
struct dso;
struct call_return_processor;
struct comm;
struct perf_sample;
struct addr_location;
/*
* Call/Return flags.
*
* CALL_RETURN_NO_CALL: 'return' but no matching 'call'
* CALL_RETURN_NO_RETURN: 'call' but no matching 'return'
*/
enum {
CALL_RETURN_NO_CALL = 1 << 0,
CALL_RETURN_NO_RETURN = 1 << 1,
};
/**
* struct call_return - paired call/return information.
* @thread: thread in which call/return occurred
* @comm: comm in which call/return occurred
* @cp: call path
* @call_time: timestamp of call (if known)
* @return_time: timestamp of return (if known)
* @branch_count: number of branches seen between call and return
* @call_ref: external reference to 'call' sample (e.g. db_id)
* @return_ref: external reference to 'return' sample (e.g. db_id)
* @db_id: id used for db-export
* @flags: Call/Return flags
*/
struct call_return {
struct thread *thread;
struct comm *comm;
struct call_path *cp;
u64 call_time;
u64 return_time;
u64 branch_count;
u64 call_ref;
u64 return_ref;
u64 db_id;
u32 flags;
};
/**
* struct call_path - node in list of calls leading to a function call.
* @parent: call path to the parent function call
* @sym: symbol of function called
* @ip: only if sym is null, the ip of the function
* @db_id: id used for db-export
* @in_kernel: whether function is a in the kernel
* @rb_node: node in parent's tree of called functions
* @children: tree of call paths of functions called
*
* In combination with the call_return structure, the call_path structure
* defines a context-sensitve call-graph.
*/
struct call_path {
struct call_path *parent;
struct symbol *sym;
u64 ip;
u64 db_id;
bool in_kernel;
struct rb_node rb_node;
struct rb_root children;
};
int thread_stack__event(struct thread *thread, u32 flags, u64 from_ip,
u64 to_ip, u16 insn_len, u64 trace_nr);
void thread_stack__set_trace_nr(struct thread *thread, u64 trace_nr);
void thread_stack__sample(struct thread *thread, struct ip_callchain *chain,
size_t sz, u64 ip);
void thread_stack__free(struct thread *thread);
struct call_return_processor *
call_return_processor__new(int (*process)(struct call_return *cr, void *data),
void *data);
void call_return_processor__free(struct call_return_processor *crp);
int thread_stack__process(struct thread *thread, struct comm *comm,
struct perf_sample *sample,
struct addr_location *from_al,
struct addr_location *to_al, u64 ref,
struct call_return_processor *crp);
#endif

View File

@ -4,6 +4,7 @@
#include <string.h> #include <string.h>
#include "session.h" #include "session.h"
#include "thread.h" #include "thread.h"
#include "thread-stack.h"
#include "util.h" #include "util.h"
#include "debug.h" #include "debug.h"
#include "comm.h" #include "comm.h"
@ -15,7 +16,7 @@ int thread__init_map_groups(struct thread *thread, struct machine *machine)
pid_t pid = thread->pid_; pid_t pid = thread->pid_;
if (pid == thread->tid || pid == -1) { if (pid == thread->tid || pid == -1) {
thread->mg = map_groups__new(); thread->mg = map_groups__new(machine);
} else { } else {
leader = machine__findnew_thread(machine, pid, pid); leader = machine__findnew_thread(machine, pid, pid);
if (leader) if (leader)
@ -66,6 +67,8 @@ void thread__delete(struct thread *thread)
{ {
struct comm *comm, *tmp; struct comm *comm, *tmp;
thread_stack__free(thread);
if (thread->mg) { if (thread->mg) {
map_groups__put(thread->mg); map_groups__put(thread->mg);
thread->mg = NULL; thread->mg = NULL;
@ -100,15 +103,14 @@ struct comm *thread__exec_comm(const struct thread *thread)
return last; return last;
} }
/* CHECKME: time should always be 0 if event aren't ordered */
int __thread__set_comm(struct thread *thread, const char *str, u64 timestamp, int __thread__set_comm(struct thread *thread, const char *str, u64 timestamp,
bool exec) bool exec)
{ {
struct comm *new, *curr = thread__comm(thread); struct comm *new, *curr = thread__comm(thread);
int err; int err;
/* Override latest entry if it had no specific time coverage */ /* Override the default :tid entry */
if (!curr->start && !curr->exec) { if (!thread->comm_set) {
err = comm__override(curr, str, timestamp, exec); err = comm__override(curr, str, timestamp, exec);
if (err) if (err)
return err; return err;
@ -198,7 +200,6 @@ int thread__fork(struct thread *thread, struct thread *parent, u64 timestamp)
} }
void thread__find_cpumode_addr_location(struct thread *thread, void thread__find_cpumode_addr_location(struct thread *thread,
struct machine *machine,
enum map_type type, u64 addr, enum map_type type, u64 addr,
struct addr_location *al) struct addr_location *al)
{ {
@ -211,8 +212,7 @@ void thread__find_cpumode_addr_location(struct thread *thread,
}; };
for (i = 0; i < ARRAY_SIZE(cpumodes); i++) { for (i = 0; i < ARRAY_SIZE(cpumodes); i++) {
thread__find_addr_location(thread, machine, cpumodes[i], type, thread__find_addr_location(thread, cpumodes[i], type, addr, al);
addr, al);
if (al->map) if (al->map)
break; break;
} }

View File

@ -8,6 +8,8 @@
#include "symbol.h" #include "symbol.h"
#include <strlist.h> #include <strlist.h>
struct thread_stack;
struct thread { struct thread {
union { union {
struct rb_node rb_node; struct rb_node rb_node;
@ -23,8 +25,10 @@ struct thread {
bool dead; /* if set thread has exited */ bool dead; /* if set thread has exited */
struct list_head comm_list; struct list_head comm_list;
int comm_len; int comm_len;
u64 db_id;
void *priv; void *priv;
struct thread_stack *ts;
}; };
struct machine; struct machine;
@ -54,16 +58,15 @@ void thread__insert_map(struct thread *thread, struct map *map);
int thread__fork(struct thread *thread, struct thread *parent, u64 timestamp); int thread__fork(struct thread *thread, struct thread *parent, u64 timestamp);
size_t thread__fprintf(struct thread *thread, FILE *fp); size_t thread__fprintf(struct thread *thread, FILE *fp);
void thread__find_addr_map(struct thread *thread, struct machine *machine, void thread__find_addr_map(struct thread *thread,
u8 cpumode, enum map_type type, u64 addr, u8 cpumode, enum map_type type, u64 addr,
struct addr_location *al); struct addr_location *al);
void thread__find_addr_location(struct thread *thread, struct machine *machine, void thread__find_addr_location(struct thread *thread,
u8 cpumode, enum map_type type, u64 addr, u8 cpumode, enum map_type type, u64 addr,
struct addr_location *al); struct addr_location *al);
void thread__find_cpumode_addr_location(struct thread *thread, void thread__find_cpumode_addr_location(struct thread *thread,
struct machine *machine,
enum map_type type, u64 addr, enum map_type type, u64 addr,
struct addr_location *al); struct addr_location *al);

View File

@ -39,7 +39,8 @@ struct perf_tool {
event_attr_op attr; event_attr_op attr;
event_op2 tracing_data; event_op2 tracing_data;
event_op2 finished_round, event_op2 finished_round,
build_id; build_id,
id_index;
bool ordered_events; bool ordered_events;
bool ordering_requires_timestamps; bool ordering_requires_timestamps;
}; };

View File

@ -26,7 +26,7 @@ static int __report_module(struct addr_location *al, u64 ip,
Dwfl_Module *mod; Dwfl_Module *mod;
struct dso *dso = NULL; struct dso *dso = NULL;
thread__find_addr_location(ui->thread, ui->machine, thread__find_addr_location(ui->thread,
PERF_RECORD_MISC_USER, PERF_RECORD_MISC_USER,
MAP__FUNCTION, ip, al); MAP__FUNCTION, ip, al);
@ -89,7 +89,7 @@ static int access_dso_mem(struct unwind_info *ui, Dwarf_Addr addr,
struct addr_location al; struct addr_location al;
ssize_t size; ssize_t size;
thread__find_addr_map(ui->thread, ui->machine, PERF_RECORD_MISC_USER, thread__find_addr_map(ui->thread, PERF_RECORD_MISC_USER,
MAP__FUNCTION, addr, &al); MAP__FUNCTION, addr, &al);
if (!al.map) { if (!al.map) {
pr_debug("unwind: no map for %lx\n", (unsigned long)addr); pr_debug("unwind: no map for %lx\n", (unsigned long)addr);
@ -164,14 +164,14 @@ frame_callback(Dwfl_Frame *state, void *arg)
} }
int unwind__get_entries(unwind_entry_cb_t cb, void *arg, int unwind__get_entries(unwind_entry_cb_t cb, void *arg,
struct machine *machine, struct thread *thread, struct thread *thread,
struct perf_sample *data, struct perf_sample *data,
int max_stack) int max_stack)
{ {
struct unwind_info ui = { struct unwind_info ui = {
.sample = data, .sample = data,
.thread = thread, .thread = thread,
.machine = machine, .machine = thread->mg->machine,
.cb = cb, .cb = cb,
.arg = arg, .arg = arg,
.max_stack = max_stack, .max_stack = max_stack,

View File

@ -284,7 +284,7 @@ static struct map *find_map(unw_word_t ip, struct unwind_info *ui)
{ {
struct addr_location al; struct addr_location al;
thread__find_addr_map(ui->thread, ui->machine, PERF_RECORD_MISC_USER, thread__find_addr_map(ui->thread, PERF_RECORD_MISC_USER,
MAP__FUNCTION, ip, &al); MAP__FUNCTION, ip, &al);
return al.map; return al.map;
} }
@ -374,7 +374,7 @@ static int access_dso_mem(struct unwind_info *ui, unw_word_t addr,
struct addr_location al; struct addr_location al;
ssize_t size; ssize_t size;
thread__find_addr_map(ui->thread, ui->machine, PERF_RECORD_MISC_USER, thread__find_addr_map(ui->thread, PERF_RECORD_MISC_USER,
MAP__FUNCTION, addr, &al); MAP__FUNCTION, addr, &al);
if (!al.map) { if (!al.map) {
pr_debug("unwind: no map for %lx\n", (unsigned long)addr); pr_debug("unwind: no map for %lx\n", (unsigned long)addr);
@ -476,14 +476,13 @@ static void put_unwind_info(unw_addr_space_t __maybe_unused as,
pr_debug("unwind: put_unwind_info called\n"); pr_debug("unwind: put_unwind_info called\n");
} }
static int entry(u64 ip, struct thread *thread, struct machine *machine, static int entry(u64 ip, struct thread *thread,
unwind_entry_cb_t cb, void *arg) unwind_entry_cb_t cb, void *arg)
{ {
struct unwind_entry e; struct unwind_entry e;
struct addr_location al; struct addr_location al;
thread__find_addr_location(thread, machine, thread__find_addr_location(thread, PERF_RECORD_MISC_USER,
PERF_RECORD_MISC_USER,
MAP__FUNCTION, ip, &al); MAP__FUNCTION, ip, &al);
e.ip = ip; e.ip = ip;
@ -586,21 +585,21 @@ static int get_entries(struct unwind_info *ui, unwind_entry_cb_t cb,
unw_word_t ip; unw_word_t ip;
unw_get_reg(&c, UNW_REG_IP, &ip); unw_get_reg(&c, UNW_REG_IP, &ip);
ret = ip ? entry(ip, ui->thread, ui->machine, cb, arg) : 0; ret = ip ? entry(ip, ui->thread, cb, arg) : 0;
} }
return ret; return ret;
} }
int unwind__get_entries(unwind_entry_cb_t cb, void *arg, int unwind__get_entries(unwind_entry_cb_t cb, void *arg,
struct machine *machine, struct thread *thread, struct thread *thread,
struct perf_sample *data, int max_stack) struct perf_sample *data, int max_stack)
{ {
u64 ip; u64 ip;
struct unwind_info ui = { struct unwind_info ui = {
.sample = data, .sample = data,
.thread = thread, .thread = thread,
.machine = machine, .machine = thread->mg->machine,
}; };
int ret; int ret;
@ -611,7 +610,7 @@ int unwind__get_entries(unwind_entry_cb_t cb, void *arg,
if (ret) if (ret)
return ret; return ret;
ret = entry(ip, thread, machine, cb, arg); ret = entry(ip, thread, cb, arg);
if (ret) if (ret)
return -ENOMEM; return -ENOMEM;

View File

@ -16,7 +16,6 @@ typedef int (*unwind_entry_cb_t)(struct unwind_entry *entry, void *arg);
#ifdef HAVE_DWARF_UNWIND_SUPPORT #ifdef HAVE_DWARF_UNWIND_SUPPORT
int unwind__get_entries(unwind_entry_cb_t cb, void *arg, int unwind__get_entries(unwind_entry_cb_t cb, void *arg,
struct machine *machine,
struct thread *thread, struct thread *thread,
struct perf_sample *data, int max_stack); struct perf_sample *data, int max_stack);
/* libunwind specific */ /* libunwind specific */
@ -38,7 +37,6 @@ static inline void unwind__finish_access(struct thread *thread __maybe_unused) {
static inline int static inline int
unwind__get_entries(unwind_entry_cb_t cb __maybe_unused, unwind__get_entries(unwind_entry_cb_t cb __maybe_unused,
void *arg __maybe_unused, void *arg __maybe_unused,
struct machine *machine __maybe_unused,
struct thread *thread __maybe_unused, struct thread *thread __maybe_unused,
struct perf_sample *data __maybe_unused, struct perf_sample *data __maybe_unused,
int max_stack __maybe_unused) int max_stack __maybe_unused)

Some files were not shown because too many files have changed in this diff Show More