Merge branch 'perf/hw_breakpoints' into perf/core
The new hw_breakpoint bits are now ready for v3.20, merge them into the main branch, to avoid conflicts. Conflicts: tools/perf/Documentation/perf-record.txt Signed-off-by: Ingo Molnar <mingo@kernel.org>
This commit is contained in:
commit
b3890e4704
@ -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 */
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -388,6 +389,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);
|
||||||
|
@ -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 */
|
||||||
|
@ -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;
|
||||||
};
|
};
|
||||||
|
@ -251,6 +251,10 @@
|
|||||||
/* 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
|
||||||
|
@ -869,3 +869,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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -45,12 +45,15 @@ OPTIONS
|
|||||||
param1 and param2 are defined as formats for the PMU in:
|
param1 and param2 are defined as formats for the PMU in:
|
||||||
/sys/bus/event_sources/devices/<pmu>/format/*
|
/sys/bus/event_sources/devices/<pmu>/format/*
|
||||||
|
|
||||||
- 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.
|
||||||
|
@ -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",
|
||||||
|
@ -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;
|
||||||
@ -1366,7 +1367,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");
|
||||||
}
|
}
|
||||||
|
@ -105,7 +105,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
|
||||||
|
@ -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); }
|
||||||
/*
|
/*
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user