Commit b3890e47 authored by Ingo Molnar's avatar Ingo Molnar

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: default avatarIngo Molnar <mingo@kernel.org>
parents 29bf4dbc 2a2662bf
...@@ -174,6 +174,7 @@ ...@@ -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]; ...@@ -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) { } ...@@ -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 @@ ...@@ -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 @@ ...@@ -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) ...@@ -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) ...@@ -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) ...@@ -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) ...@@ -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) ...@@ -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) ...@@ -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) ...@@ -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) ...@@ -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 ...@@ -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) ...@@ -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[] = { ...@@ -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 { \ ...@@ -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, ...@@ -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) ...@@ -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, ...@@ -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 ...@@ -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 ...@@ -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 ...@@ -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 ...@@ -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;
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment