perf callchain: Add mask into struct regs_dump
Adding mask info into struct regs_dump to make the registers information compact. The mask was always passed along, so logically the mask info fits more into the struct regs_dump. Signed-off-by: Jiri Olsa <jolsa@redhat.com> Acked-by: Jean Pihet <jean.pihet@linaro.org> Cc: Adrian Hunter <adrian.hunter@intel.com> Cc: Corey Ashford <cjashfor@linux.vnet.ibm.com> Cc: David Ahern <dsahern@gmail.com> Cc: Frederic Weisbecker <fweisbec@gmail.com> Cc: Ingo Molnar <mingo@kernel.org> Cc: Jean Pihet <jean.pihet@linaro.org> Cc: Namhyung Kim <namhyung@kernel.org> Cc: Paul Mackerras <paulus@samba.org> Cc: Peter Zijlstra <a.p.zijlstra@chello.nl> Link: http://lkml.kernel.org/r/1389098853-14466-9-git-send-email-jolsa@redhat.com Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
This commit is contained in:
parent
1cf0382af9
commit
352ea45a72
|
@ -53,6 +53,7 @@ int test__arch_unwind_sample(struct perf_sample *sample,
|
||||||
perf_regs_load(buf);
|
perf_regs_load(buf);
|
||||||
regs->abi = PERF_SAMPLE_REGS_ABI;
|
regs->abi = PERF_SAMPLE_REGS_ABI;
|
||||||
regs->regs = buf;
|
regs->regs = buf;
|
||||||
|
regs->mask = PERF_REGS_MASK;
|
||||||
|
|
||||||
return sample_ustack(sample, thread, buf);
|
return sample_ustack(sample, thread, buf);
|
||||||
}
|
}
|
||||||
|
|
|
@ -312,7 +312,6 @@ found:
|
||||||
sample_sw.period = sample->period;
|
sample_sw.period = sample->period;
|
||||||
sample_sw.time = sample->time;
|
sample_sw.time = sample->time;
|
||||||
perf_event__synthesize_sample(event_sw, evsel->attr.sample_type,
|
perf_event__synthesize_sample(event_sw, evsel->attr.sample_type,
|
||||||
evsel->attr.sample_regs_user,
|
|
||||||
evsel->attr.read_format, &sample_sw,
|
evsel->attr.read_format, &sample_sw,
|
||||||
false);
|
false);
|
||||||
build_id__mark_dso_hit(tool, event_sw, &sample_sw, evsel, machine);
|
build_id__mark_dso_hit(tool, event_sw, &sample_sw, evsel, machine);
|
||||||
|
|
|
@ -72,7 +72,7 @@ static int unwind_thread(struct thread *thread, struct machine *machine)
|
||||||
}
|
}
|
||||||
|
|
||||||
err = unwind__get_entries(unwind_entry, &cnt, machine, thread,
|
err = unwind__get_entries(unwind_entry, &cnt, machine, thread,
|
||||||
PERF_REGS_MASK, &sample, MAX_STACK);
|
&sample, MAX_STACK);
|
||||||
if (err)
|
if (err)
|
||||||
pr_debug("unwind failed\n");
|
pr_debug("unwind failed\n");
|
||||||
else if (cnt != MAX_STACK) {
|
else if (cnt != MAX_STACK) {
|
||||||
|
|
|
@ -22,8 +22,8 @@
|
||||||
} while (0)
|
} while (0)
|
||||||
|
|
||||||
static bool samples_same(const struct perf_sample *s1,
|
static bool samples_same(const struct perf_sample *s1,
|
||||||
const struct perf_sample *s2, u64 type, u64 regs_user,
|
const struct perf_sample *s2,
|
||||||
u64 read_format)
|
u64 type, u64 read_format)
|
||||||
{
|
{
|
||||||
size_t i;
|
size_t i;
|
||||||
|
|
||||||
|
@ -95,8 +95,9 @@ static bool samples_same(const struct perf_sample *s1,
|
||||||
}
|
}
|
||||||
|
|
||||||
if (type & PERF_SAMPLE_REGS_USER) {
|
if (type & PERF_SAMPLE_REGS_USER) {
|
||||||
size_t sz = hweight_long(regs_user) * sizeof(u64);
|
size_t sz = hweight_long(s1->user_regs.mask) * sizeof(u64);
|
||||||
|
|
||||||
|
COMP(user_regs.mask);
|
||||||
COMP(user_regs.abi);
|
COMP(user_regs.abi);
|
||||||
if (s1->user_regs.abi &&
|
if (s1->user_regs.abi &&
|
||||||
(!s1->user_regs.regs || !s2->user_regs.regs ||
|
(!s1->user_regs.regs || !s2->user_regs.regs ||
|
||||||
|
@ -174,6 +175,7 @@ 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,
|
||||||
.regs = user_regs,
|
.regs = user_regs,
|
||||||
},
|
},
|
||||||
.user_stack = {
|
.user_stack = {
|
||||||
|
@ -201,8 +203,7 @@ static int do_test(u64 sample_type, u64 sample_regs_user, u64 read_format)
|
||||||
sample.read.one.id = 99;
|
sample.read.one.id = 99;
|
||||||
}
|
}
|
||||||
|
|
||||||
sz = perf_event__sample_event_size(&sample, sample_type,
|
sz = perf_event__sample_event_size(&sample, sample_type, read_format);
|
||||||
sample_regs_user, read_format);
|
|
||||||
bufsz = sz + 4096; /* Add a bit for overrun checking */
|
bufsz = sz + 4096; /* Add a bit for overrun checking */
|
||||||
event = malloc(bufsz);
|
event = malloc(bufsz);
|
||||||
if (!event) {
|
if (!event) {
|
||||||
|
@ -215,8 +216,7 @@ static int do_test(u64 sample_type, u64 sample_regs_user, u64 read_format)
|
||||||
event->header.misc = 0;
|
event->header.misc = 0;
|
||||||
event->header.size = sz;
|
event->header.size = sz;
|
||||||
|
|
||||||
err = perf_event__synthesize_sample(event, sample_type,
|
err = perf_event__synthesize_sample(event, sample_type, read_format,
|
||||||
sample_regs_user, read_format,
|
|
||||||
&sample, false);
|
&sample, false);
|
||||||
if (err) {
|
if (err) {
|
||||||
pr_debug("%s failed for sample_type %#"PRIx64", error %d\n",
|
pr_debug("%s failed for sample_type %#"PRIx64", error %d\n",
|
||||||
|
@ -244,8 +244,7 @@ static int do_test(u64 sample_type, u64 sample_regs_user, u64 read_format)
|
||||||
goto out_free;
|
goto out_free;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!samples_same(&sample, &sample_out, sample_type,
|
if (!samples_same(&sample, &sample_out, sample_type, read_format)) {
|
||||||
sample_regs_user, read_format)) {
|
|
||||||
pr_debug("parsing failed for sample_type %#"PRIx64"\n",
|
pr_debug("parsing failed for sample_type %#"PRIx64"\n",
|
||||||
sample_type);
|
sample_type);
|
||||||
goto out_free;
|
goto out_free;
|
||||||
|
|
|
@ -85,6 +85,7 @@ struct sample_event {
|
||||||
|
|
||||||
struct regs_dump {
|
struct regs_dump {
|
||||||
u64 abi;
|
u64 abi;
|
||||||
|
u64 mask;
|
||||||
u64 *regs;
|
u64 *regs;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -259,9 +260,9 @@ int perf_event__preprocess_sample(const union perf_event *event,
|
||||||
const char *perf_event__name(unsigned int id);
|
const char *perf_event__name(unsigned int id);
|
||||||
|
|
||||||
size_t perf_event__sample_event_size(const struct perf_sample *sample, u64 type,
|
size_t perf_event__sample_event_size(const struct perf_sample *sample, u64 type,
|
||||||
u64 sample_regs_user, u64 read_format);
|
u64 read_format);
|
||||||
int perf_event__synthesize_sample(union perf_event *event, u64 type,
|
int perf_event__synthesize_sample(union perf_event *event, u64 type,
|
||||||
u64 sample_regs_user, u64 read_format,
|
u64 read_format,
|
||||||
const struct perf_sample *sample,
|
const struct perf_sample *sample,
|
||||||
bool swapped);
|
bool swapped);
|
||||||
|
|
||||||
|
|
|
@ -1396,10 +1396,11 @@ int perf_evsel__parse_sample(struct perf_evsel *evsel, union perf_event *event,
|
||||||
array++;
|
array++;
|
||||||
|
|
||||||
if (data->user_regs.abi) {
|
if (data->user_regs.abi) {
|
||||||
u64 regs_user = evsel->attr.sample_regs_user;
|
u64 mask = evsel->attr.sample_regs_user;
|
||||||
|
|
||||||
sz = hweight_long(regs_user) * sizeof(u64);
|
sz = hweight_long(mask) * sizeof(u64);
|
||||||
OVERFLOW_CHECK(array, sz, max_size);
|
OVERFLOW_CHECK(array, sz, max_size);
|
||||||
|
data->user_regs.mask = mask;
|
||||||
data->user_regs.regs = (u64 *)array;
|
data->user_regs.regs = (u64 *)array;
|
||||||
array = (void *)array + sz;
|
array = (void *)array + sz;
|
||||||
}
|
}
|
||||||
|
@ -1451,7 +1452,7 @@ int perf_evsel__parse_sample(struct perf_evsel *evsel, union perf_event *event,
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t perf_event__sample_event_size(const struct perf_sample *sample, u64 type,
|
size_t perf_event__sample_event_size(const struct perf_sample *sample, u64 type,
|
||||||
u64 sample_regs_user, u64 read_format)
|
u64 read_format)
|
||||||
{
|
{
|
||||||
size_t sz, result = sizeof(struct sample_event);
|
size_t sz, result = sizeof(struct sample_event);
|
||||||
|
|
||||||
|
@ -1517,7 +1518,7 @@ size_t perf_event__sample_event_size(const struct perf_sample *sample, u64 type,
|
||||||
if (type & PERF_SAMPLE_REGS_USER) {
|
if (type & PERF_SAMPLE_REGS_USER) {
|
||||||
if (sample->user_regs.abi) {
|
if (sample->user_regs.abi) {
|
||||||
result += sizeof(u64);
|
result += sizeof(u64);
|
||||||
sz = hweight_long(sample_regs_user) * sizeof(u64);
|
sz = hweight_long(sample->user_regs.mask) * sizeof(u64);
|
||||||
result += sz;
|
result += sz;
|
||||||
} else {
|
} else {
|
||||||
result += sizeof(u64);
|
result += sizeof(u64);
|
||||||
|
@ -1546,7 +1547,7 @@ size_t perf_event__sample_event_size(const struct perf_sample *sample, u64 type,
|
||||||
}
|
}
|
||||||
|
|
||||||
int perf_event__synthesize_sample(union perf_event *event, u64 type,
|
int perf_event__synthesize_sample(union perf_event *event, u64 type,
|
||||||
u64 sample_regs_user, u64 read_format,
|
u64 read_format,
|
||||||
const struct perf_sample *sample,
|
const struct perf_sample *sample,
|
||||||
bool swapped)
|
bool swapped)
|
||||||
{
|
{
|
||||||
|
@ -1687,7 +1688,7 @@ int perf_event__synthesize_sample(union perf_event *event, u64 type,
|
||||||
if (type & PERF_SAMPLE_REGS_USER) {
|
if (type & PERF_SAMPLE_REGS_USER) {
|
||||||
if (sample->user_regs.abi) {
|
if (sample->user_regs.abi) {
|
||||||
*array++ = sample->user_regs.abi;
|
*array++ = sample->user_regs.abi;
|
||||||
sz = hweight_long(sample_regs_user) * sizeof(u64);
|
sz = hweight_long(sample->user_regs.mask) * sizeof(u64);
|
||||||
memcpy(array, sample->user_regs.regs, sz);
|
memcpy(array, sample->user_regs.regs, sz);
|
||||||
array = (void *)array + sz;
|
array = (void *)array + sz;
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -1383,8 +1383,7 @@ int machine__resolve_callchain(struct machine *machine,
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
return unwind__get_entries(unwind_entry, &callchain_cursor, machine,
|
return unwind__get_entries(unwind_entry, &callchain_cursor, machine,
|
||||||
thread, evsel->attr.sample_regs_user,
|
thread, sample, max_stack);
|
||||||
sample, max_stack);
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -702,11 +702,12 @@ static void regs_dump__printf(u64 mask, u64 *regs)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void regs_user__printf(struct perf_sample *sample, u64 mask)
|
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;
|
||||||
printf("... user regs: mask 0x%" PRIx64 "\n", mask);
|
printf("... user regs: mask 0x%" PRIx64 "\n", mask);
|
||||||
regs_dump__printf(mask, user_regs->regs);
|
regs_dump__printf(mask, user_regs->regs);
|
||||||
}
|
}
|
||||||
|
@ -806,7 +807,7 @@ static void dump_sample(struct perf_evsel *evsel, union perf_event *event,
|
||||||
branch_stack__printf(sample);
|
branch_stack__printf(sample);
|
||||||
|
|
||||||
if (sample_type & PERF_SAMPLE_REGS_USER)
|
if (sample_type & PERF_SAMPLE_REGS_USER)
|
||||||
regs_user__printf(sample, evsel->attr.sample_regs_user);
|
regs_user__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);
|
||||||
|
|
|
@ -86,7 +86,6 @@ struct unwind_info {
|
||||||
struct perf_sample *sample;
|
struct perf_sample *sample;
|
||||||
struct machine *machine;
|
struct machine *machine;
|
||||||
struct thread *thread;
|
struct thread *thread;
|
||||||
u64 sample_uregs;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#define dw_read(ptr, type, end) ({ \
|
#define dw_read(ptr, type, end) ({ \
|
||||||
|
@ -391,16 +390,16 @@ static int access_dso_mem(struct unwind_info *ui, unw_word_t addr,
|
||||||
return !(size == sizeof(*data));
|
return !(size == sizeof(*data));
|
||||||
}
|
}
|
||||||
|
|
||||||
static int reg_value(unw_word_t *valp, struct regs_dump *regs, int id,
|
static int reg_value(unw_word_t *valp, struct regs_dump *regs, int id)
|
||||||
u64 sample_regs)
|
|
||||||
{
|
{
|
||||||
int i, idx = 0;
|
int i, idx = 0;
|
||||||
|
u64 mask = regs->mask;
|
||||||
|
|
||||||
if (!(sample_regs & (1 << id)))
|
if (!(mask & (1 << id)))
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
|
||||||
for (i = 0; i < id; i++) {
|
for (i = 0; i < id; i++) {
|
||||||
if (sample_regs & (1 << i))
|
if (mask & (1 << i))
|
||||||
idx++;
|
idx++;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -424,8 +423,7 @@ static int access_mem(unw_addr_space_t __maybe_unused as,
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
ret = reg_value(&start, &ui->sample->user_regs, PERF_REG_SP,
|
ret = reg_value(&start, &ui->sample->user_regs, PERF_REG_SP);
|
||||||
ui->sample_uregs);
|
|
||||||
if (ret)
|
if (ret)
|
||||||
return ret;
|
return ret;
|
||||||
|
|
||||||
|
@ -475,7 +473,7 @@ static int access_reg(unw_addr_space_t __maybe_unused as,
|
||||||
if (id < 0)
|
if (id < 0)
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
|
||||||
ret = reg_value(valp, &ui->sample->user_regs, id, ui->sample_uregs);
|
ret = reg_value(valp, &ui->sample->user_regs, id);
|
||||||
if (ret) {
|
if (ret) {
|
||||||
pr_err("unwind: can't read reg %d\n", regnum);
|
pr_err("unwind: can't read reg %d\n", regnum);
|
||||||
return ret;
|
return ret;
|
||||||
|
@ -572,13 +570,11 @@ static int get_entries(struct unwind_info *ui, unwind_entry_cb_t cb,
|
||||||
|
|
||||||
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 machine *machine, struct thread *thread,
|
||||||
u64 sample_uregs, struct perf_sample *data,
|
struct perf_sample *data, int max_stack)
|
||||||
int max_stack)
|
|
||||||
{
|
{
|
||||||
unw_word_t ip;
|
unw_word_t ip;
|
||||||
struct unwind_info ui = {
|
struct unwind_info ui = {
|
||||||
.sample = data,
|
.sample = data,
|
||||||
.sample_uregs = sample_uregs,
|
|
||||||
.thread = thread,
|
.thread = thread,
|
||||||
.machine = machine,
|
.machine = machine,
|
||||||
};
|
};
|
||||||
|
@ -587,7 +583,7 @@ int unwind__get_entries(unwind_entry_cb_t cb, void *arg,
|
||||||
if (!data->user_regs.regs)
|
if (!data->user_regs.regs)
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
|
||||||
ret = reg_value(&ip, &data->user_regs, PERF_REG_IP, sample_uregs);
|
ret = reg_value(&ip, &data->user_regs, PERF_REG_IP);
|
||||||
if (ret)
|
if (ret)
|
||||||
return ret;
|
return ret;
|
||||||
|
|
||||||
|
|
|
@ -17,7 +17,6 @@ typedef int (*unwind_entry_cb_t)(struct unwind_entry *entry, 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 machine *machine,
|
||||||
struct thread *thread,
|
struct thread *thread,
|
||||||
u64 sample_uregs,
|
|
||||||
struct perf_sample *data, int max_stack);
|
struct perf_sample *data, int max_stack);
|
||||||
int unwind__arch_reg_id(int regnum);
|
int unwind__arch_reg_id(int regnum);
|
||||||
#else
|
#else
|
||||||
|
@ -26,7 +25,6 @@ unwind__get_entries(unwind_entry_cb_t cb __maybe_unused,
|
||||||
void *arg __maybe_unused,
|
void *arg __maybe_unused,
|
||||||
struct machine *machine __maybe_unused,
|
struct machine *machine __maybe_unused,
|
||||||
struct thread *thread __maybe_unused,
|
struct thread *thread __maybe_unused,
|
||||||
u64 sample_uregs __maybe_unused,
|
|
||||||
struct perf_sample *data __maybe_unused,
|
struct perf_sample *data __maybe_unused,
|
||||||
int max_stack __maybe_unused)
|
int max_stack __maybe_unused)
|
||||||
{
|
{
|
||||||
|
|
Loading…
Reference in a new issue