mirror of
https://github.com/torvalds/linux.git
synced 2024-11-16 09:02:00 +00:00
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_PERFCTR_CORE ( 6*32+23) /* core 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 */
|
||||
|
||||
/*
|
||||
@ -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_eager_fpu boot_cpu_has(X86_FEATURE_EAGER_FPU)
|
||||
#define cpu_has_topoext boot_cpu_has(X86_FEATURE_TOPOEXT)
|
||||
#define cpu_has_bpext boot_cpu_has(X86_FEATURE_BPEXT)
|
||||
|
||||
#if __GNUC__ >= 4
|
||||
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) { }
|
||||
#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 */
|
||||
|
@ -12,6 +12,7 @@
|
||||
*/
|
||||
struct arch_hw_breakpoint {
|
||||
unsigned long address;
|
||||
unsigned long mask;
|
||||
u8 len;
|
||||
u8 type;
|
||||
};
|
||||
|
@ -251,6 +251,10 @@
|
||||
/* Fam 16h MSRs */
|
||||
#define MSR_F16H_L2I_PERF_CTL 0xc0010230
|
||||
#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 */
|
||||
#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;
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
set_debugreg(*dr7, 7);
|
||||
if (info->mask)
|
||||
set_dr_addr_mask(info->mask, i);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -161,29 +163,8 @@ void arch_uninstall_hw_breakpoint(struct perf_event *bp)
|
||||
*dr7 &= ~__encode_dr7(i, info->len, info->type);
|
||||
|
||||
set_debugreg(*dr7, 7);
|
||||
}
|
||||
|
||||
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;
|
||||
if (info->mask)
|
||||
set_dr_addr_mask(0, i);
|
||||
}
|
||||
|
||||
/*
|
||||
@ -196,7 +177,7 @@ int arch_check_bp_in_kernelspace(struct perf_event *bp)
|
||||
struct arch_hw_breakpoint *info = counter_arch_bp(bp);
|
||||
|
||||
va = info->address;
|
||||
len = get_hbp_len(info->len);
|
||||
len = bp->attr.bp_len;
|
||||
|
||||
return (va >= TASK_SIZE) && ((va + len - 1) >= TASK_SIZE);
|
||||
}
|
||||
@ -277,6 +258,8 @@ static int arch_build_bp_info(struct perf_event *bp)
|
||||
}
|
||||
|
||||
/* Len */
|
||||
info->mask = 0;
|
||||
|
||||
switch (bp->attr.bp_len) {
|
||||
case HW_BREAKPOINT_LEN_1:
|
||||
info->len = X86_BREAKPOINT_LEN_1;
|
||||
@ -293,11 +276,17 @@ static int arch_build_bp_info(struct perf_event *bp)
|
||||
break;
|
||||
#endif
|
||||
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;
|
||||
}
|
||||
|
||||
/*
|
||||
* Validate the arch-specific HW Breakpoint register settings
|
||||
*/
|
||||
@ -312,11 +301,11 @@ int arch_validate_hwbkpt_settings(struct perf_event *bp)
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = -EINVAL;
|
||||
|
||||
switch (info->len) {
|
||||
case X86_BREAKPOINT_LEN_1:
|
||||
align = 0;
|
||||
if (info->mask)
|
||||
align = info->mask;
|
||||
break;
|
||||
case X86_BREAKPOINT_LEN_2:
|
||||
align = 1;
|
||||
@ -330,7 +319,7 @@ int arch_validate_hwbkpt_settings(struct perf_event *bp)
|
||||
break;
|
||||
#endif
|
||||
default:
|
||||
return ret;
|
||||
WARN_ON_ONCE(1);
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -45,12 +45,15 @@ OPTIONS
|
||||
param1 and param2 are defined as formats for the PMU in:
|
||||
/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.
|
||||
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
|
||||
'mem:0x1000:rw'.
|
||||
If you want to profile write accesses in [0x1000~1008), just set
|
||||
'mem:0x1000/8:w'.
|
||||
|
||||
--filter=<filter>::
|
||||
Event filter.
|
||||
|
@ -1145,6 +1145,49 @@ static int test__pinned_group(struct perf_evlist *evlist)
|
||||
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)
|
||||
{
|
||||
char events_path[PATH_MAX];
|
||||
@ -1420,6 +1463,21 @@ static struct evlist_test test__events[] = {
|
||||
.check = test__pinned_group,
|
||||
.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__)
|
||||
{
|
||||
.name = "kvm-s390:kvm_s390_create_vm",
|
||||
|
@ -526,7 +526,7 @@ do { \
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
@ -536,14 +536,15 @@ int parse_events_add_breakpoint(struct list_head *list, int *idx,
|
||||
if (parse_breakpoint_type(type, &attr))
|
||||
return -EINVAL;
|
||||
|
||||
/*
|
||||
* We should find a nice way to override the access length
|
||||
* Provide some defaults for now
|
||||
*/
|
||||
if (attr.bp_type == HW_BREAKPOINT_X)
|
||||
attr.bp_len = sizeof(long);
|
||||
else
|
||||
attr.bp_len = HW_BREAKPOINT_LEN_4;
|
||||
/* Provide some defaults if len is not specified */
|
||||
if (!len) {
|
||||
if (attr.bp_type == HW_BREAKPOINT_X)
|
||||
len = sizeof(long);
|
||||
else
|
||||
len = HW_BREAKPOINT_LEN_4;
|
||||
}
|
||||
|
||||
attr.bp_len = len;
|
||||
|
||||
attr.type = PERF_TYPE_BREAKPOINT;
|
||||
attr.sample_period = 1;
|
||||
@ -1366,7 +1367,7 @@ void print_events(const char *event_glob, bool name_only)
|
||||
printf("\n");
|
||||
|
||||
printf(" %-50s [%s]\n",
|
||||
"mem:<addr>[:access]",
|
||||
"mem:<addr>[/len][:access]",
|
||||
event_type_descriptors[PERF_TYPE_BREAKPOINT]);
|
||||
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,
|
||||
char *type, char *op_result1, char *op_result2);
|
||||
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,
|
||||
char *pmu , struct list_head *head_config);
|
||||
enum perf_pmu_event_symbol_type
|
||||
|
@ -159,6 +159,7 @@ branch_type { return term(yyscanner, PARSE_EVENTS__TERM_TYPE_BRANCH_SAMPLE_TYPE
|
||||
<mem>{
|
||||
{modifier_bp} { return str(yyscanner, PE_MODIFIER_BP); }
|
||||
: { return ':'; }
|
||||
"/" { return '/'; }
|
||||
{num_dec} { return value(yyscanner, 10); }
|
||||
{num_hex} { return value(yyscanner, 16); }
|
||||
/*
|
||||
|
@ -326,6 +326,28 @@ PE_NAME_CACHE_TYPE
|
||||
}
|
||||
|
||||
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
|
||||
{
|
||||
struct parse_events_evlist *data = _data;
|
||||
@ -333,7 +355,7 @@ PE_PREFIX_MEM PE_VALUE ':' PE_MODIFIER_BP sep_dc
|
||||
|
||||
ALLOC_LIST(list);
|
||||
ABORT_ON(parse_events_add_breakpoint(list, &data->idx,
|
||||
(void *) $2, $4));
|
||||
(void *) $2, $4, 0));
|
||||
$$ = list;
|
||||
}
|
||||
|
|
||||
@ -344,7 +366,7 @@ PE_PREFIX_MEM PE_VALUE sep_dc
|
||||
|
||||
ALLOC_LIST(list);
|
||||
ABORT_ON(parse_events_add_breakpoint(list, &data->idx,
|
||||
(void *) $2, NULL));
|
||||
(void *) $2, NULL, 0));
|
||||
$$ = list;
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user