perf tools: Add hist_entry allocation callbacks
Jiri Olsa
reviewercover perf tools: Add hist_entry allocation callbacks
hi,
this patchset tries to add support provide own allocation zalloc/free methods for hist_entry object.
The reason is to provide a way to be able to store more data within hist_entry object in a transparent way to its current usage by allocating its own hist_entry sub object.
The user/app which wants to allocate its own hist_entry sub object provides following ops struct:
struct hist_entry_ops *ops {
void* (*new)(size_t);
void (*free)(void *);
}via new interface function:
struct hist_entry * hists__add_entry_ops(struct hists *hists,
struct hist_entry_ops *ops,
...
I'm using this for c2c code to enlarge hist_entry object with large stats structure, which has no use to standard perf usage/commands.
It might be used to lower the hist_entry footproint for default perf usage by uing this for things like hierarchy output that adds extra stuff into hist_entry object.
Available at:
git://git.kernel.org/pub/scm/linux/kernel/git/jolsa/perf.git
perf/he_ops
thanks for comments,
jirka
---
Jiri Olsa (4):
perf tools: Introduce hist_entry__init function
perf tools: Do the error path hist_entry release in hist_entry__new perf tools: Introduce hist_entry_ops
perf tools: Introduce hists__add_entry_ops function
tools/perf/util/hist.c | 204 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++--------------------------------------- tools/perf/util/hist.h | 11 ++++++ tools/perf/util/sort.h | 6 ++++ 3 files changed, 148 insertions(+), 73 deletions(-)
Jiri Olsa
reviewerPATCH 1/4 perf tools: Introduce hist_entry__init function
Move hist_entry initialization code into separate function. It'll be useful and more clear for following patches that introduce allocation callbacks.
--- tools/perf/util/hist.c | 142 ++++++++++++++++++++++++++----------------------- 1 file changed, 75 insertions(+), 67 deletions(-)
tools/perf/util/hist.c +75 -67
@@ -352,89 +352,97 @@ void hists__delete_entries(struct hists *hists)* histogram, sorted on item, collects periods */ -static struct hist_entry *hist_entry__new (struct hist_entry *template, -bool sample_self) +static int hist_entry__init (struct hist_entry *he, +struct hist_entry *template, +bool sample_self) { -size_t callchain_size =0 ; -struct hist_entry *he; + *he = *template; -if (symbol_conf.use_callchain) - callchain_size =sizeof (struct callchain_root ); - - he =zalloc (sizeof (*he) + callchain_size); +if (symbol_conf.cumulate_callchain) { + he->stat_acc =malloc (sizeof (he->stat)); +if (he->stat_acc ==NULL ) { +free (he); +return -ENOMEM ; + } +memcpy (he->stat_acc, &he->stat,sizeof (he->stat)); +if (!sample_self) +memset (&he->stat,0 ,sizeof (he->stat)); + } -if (he !=NULL ) { - *he = *template; +map__get (he->ms.map); -if (symbol_conf.cumulate_callchain) { - he->stat_acc =malloc (sizeof (he->stat)); -if (he->stat_acc ==NULL ) { -free (he); -return NULL ; - } -memcpy (he->stat_acc, &he->stat,sizeof (he->stat)); -if (!sample_self) -memset (&he->stat,0 ,sizeof (he->stat)); +if (he->branch_info) { +/* +* This branch info is (a part of) allocated from +* sample__resolve_bstack() and will be freed after +* adding new entries. So we need to save a copy. +*/ + he->branch_info =malloc (sizeof (*he->branch_info)); +if (he->branch_info ==NULL ) { +map__zput (he->ms.map); +free (he->stat_acc); +free (he); +return -ENOMEM ; } -map__get (he->ms.map); +memcpy (he->branch_info, template->branch_info, +sizeof (*he->branch_info)); -if (he->branch_info) { -/* -* This branch info is (a part of) allocated from -* sample__resolve_bstack() and will be freed after -* adding new entries. So we need to save a copy. -*/ - he->branch_info =malloc (sizeof (*he->branch_info)); -if (he->branch_info ==NULL ) { -map__zput (he->ms.map); -free (he->stat_acc); -free (he); -return NULL ; - } +map__get (he->branch_info->from.map); +map__get (he->branch_info->to.map); + } -memcpy (he->branch_info, template->branch_info, -sizeof (*he->branch_info)); +if (he->mem_info) { +map__get (he->mem_info->iaddr.map); +map__get (he->mem_info->daddr.map); + } -map__get (he->branch_info->from.map); -map__get (he->branch_info->to.map); - } +if (symbol_conf.use_callchain) +callchain_init (he->callchain); -if (he->mem_info) { -map__get (he->mem_info->iaddr.map); -map__get (he->mem_info->daddr.map); +if (he->raw_data) { + he->raw_data =memdup (he->raw_data, he->raw_size); + +if (he->raw_data ==NULL ) { +map__put (he->ms.map); +if (he->branch_info) { +map__put (he->branch_info->from.map); +map__put (he->branch_info->to.map); +free (he->branch_info); + } +if (he->mem_info) { +map__put (he->mem_info->iaddr.map); +map__put (he->mem_info->daddr.map); + } +free (he->stat_acc); +free (he); +return -ENOMEM ; } + } +INIT_LIST_HEAD (&he->pairs.node); +thread__get (he->thread); -if (symbol_conf.use_callchain) -callchain_init (he->callchain); +if (!symbol_conf.report_hierarchy) + he->leaf = true; -if (he->raw_data) { - he->raw_data =memdup (he->raw_data, he->raw_size); +return 0 ; +} -if (he->raw_data ==NULL ) { -map__put (he->ms.map); -if (he->branch_info) { -map__put (he->branch_info->from.map); -map__put (he->branch_info->to.map); -free (he->branch_info); - } -if (he->mem_info) { -map__put (he->mem_info->iaddr.map); -map__put (he->mem_info->daddr.map); - } -free (he->stat_acc); -free (he); -return NULL ; - } - } -INIT_LIST_HEAD (&he->pairs.node); -thread__get (he->thread); +static struct hist_entry *hist_entry__new (struct hist_entry *template, +bool sample_self) +{ +size_t callchain_size =0 ; +struct hist_entry *he; +int err =0 ; -if (!symbol_conf.report_hierarchy) - he->leaf = true; - } +if (symbol_conf.use_callchain) + callchain_size =sizeof (struct callchain_root ); -return he; + he =zalloc (sizeof (*he) + callchain_size); +if (he) + err =hist_entry__init (he, template, sample_self); + +return err ?NULL : he; }static u8 symbol__parent_filter (const struct symbol *parent)
Signature · 1 line
2.4.11
Arnaldo Carvalho de Melo
maintainerRe: [PATCH 1/4] perf tools: Introduce hist_entry__init function
Em Mon, Jul 04, 2016 at 04:01:36PM +0200, Jiri Olsa escreveu:
Quoted 164 lines
Move
hist_entryinitialization code into separate function. It'll be useful and more clear for following patches that introduce allocation callbacks.Signed-off-by Jiri Olsa <[email protected]>--- tools/perf/util/hist.c | 142 ++++++++++++++++++++++++++----------------------- 1 file changed, 75 insertions(+), 67 deletions(-)tools/perf/util/hist.c +68
-63@@ -352,89 +352,97 @@ void hists__delete_entries(struct hists *hists)* histogram, sorted on item, collects periods */ -static struct hist_entry *hist_entry__new (struct hist_entry *template, -bool sample_self) +static int hist_entry__init (struct hist_entry *he, +struct hist_entry *template, +bool sample_self) { -size_t callchain_size =0 ; -struct hist_entry *he; + *he = *template; -if (symbol_conf.use_callchain) - callchain_size =sizeof (struct callchain_root ); - - he =zalloc (sizeof (*he) + callchain_size); +if (symbol_conf.cumulate_callchain) { + he->stat_acc =malloc (sizeof (he->stat)); +if (he->stat_acc ==NULL ) { +free (he); +return -ENOMEM ; + } +memcpy (he->stat_acc, &he->stat,sizeof (he->stat)); +if (!sample_self) +memset (&he->stat,0 ,sizeof (he->stat)); + } -if (he !=NULL ) { - *he = *template; +map__get (he->ms.map); -if (symbol_conf.cumulate_callchain) { - he->stat_acc =malloc (sizeof (he->stat)); -if (he->stat_acc ==NULL ) { -free (he); -return NULL ; - } -memcpy (he->stat_acc, &he->stat,sizeof (he->stat)); -if (!sample_self) -memset (&he->stat,0 ,sizeof (he->stat)); +if (he->branch_info) { +/* +* This branch info is (a part of) allocated from +* sample__resolve_bstack() and will be freed after +* adding new entries. So we need to save a copy. +*/ + he->branch_info =malloc (sizeof (*he->branch_info)); +if (he->branch_info ==NULL ) { +map__zput (he->ms.map); +free (he->stat_acc); +free (he); +return -ENOMEM ; } -map__get (he->ms.map); +memcpy (he->branch_info, template->branch_info, +sizeof (*he->branch_info)); -if (he->branch_info) { -/* -* This branch info is (a part of) allocated from -* sample__resolve_bstack() and will be freed after -* adding new entries. So we need to save a copy. -*/ - he->branch_info =malloc (sizeof (*he->branch_info)); -if (he->branch_info ==NULL ) { -map__zput (he->ms.map); -free (he->stat_acc); -free (he); -return NULL ; - } +map__get (he->branch_info->from.map); +map__get (he->branch_info->to.map); + } -memcpy (he->branch_info, template->branch_info, -sizeof (*he->branch_info)); +if (he->mem_info) { +map__get (he->mem_info->iaddr.map); +map__get (he->mem_info->daddr.map); + } -map__get (he->branch_info->from.map); -map__get (he->branch_info->to.map); - } +if (symbol_conf.use_callchain) +callchain_init (he->callchain); -if (he->mem_info) { -map__get (he->mem_info->iaddr.map); -map__get (he->mem_info->daddr.map); +if (he->raw_data) { + he->raw_data =memdup (he->raw_data, he->raw_size); + +if (he->raw_data ==NULL ) { +map__put (he->ms.map); +if (he->branch_info) { +map__put (he->branch_info->from.map); +map__put (he->branch_info->to.map); +free (he->branch_info); + } +if (he->mem_info) { +map__put (he->mem_info->iaddr.map); +map__put (he->mem_info->daddr.map); + } +free (he->stat_acc); +free (he); +return -ENOMEM ; } + } +INIT_LIST_HEAD (&he->pairs.node); +thread__get (he->thread); -if (symbol_conf.use_callchain) -callchain_init (he->callchain); +if (!symbol_conf.report_hierarchy) + he->leaf = true; -if (he->raw_data) { - he->raw_data =memdup (he->raw_data, he->raw_size); +return 0 ; +} -if (he->raw_data ==NULL ) { -map__put (he->ms.map); -if (he->branch_info) { -map__put (he->branch_info->from.map); -map__put (he->branch_info->to.map); -free (he->branch_info); - } -if (he->mem_info) { -map__put (he->mem_info->iaddr.map); -map__put (he->mem_info->daddr.map); - } -free (he->stat_acc); -free (he); -return NULL ; - } - } -INIT_LIST_HEAD (&he->pairs.node); -thread__get (he->thread); +static struct hist_entry *hist_entry__new (struct hist_entry *template, +bool sample_self) +{ +size_t callchain_size =0 ; +struct hist_entry *he; +int err =0 ;
tools/perf/util/hist.c void hists__delete_entries(struct hists *hists) · line 443
Quoted 10 lines
tools/perf/util/hist.c +5
-4-if (!symbol_conf.report_hierarchy) - he->leaf = true; - } +if (symbol_conf.use_callchain) + callchain_size =sizeof (struct callchain_root ); -return he; + he =zalloc (sizeof (*he) + callchain_size); +if (he) + err =hist_entry__init (he, template, sample_self);
An init function shouldn't free the object being initialized, I haven't checked if that is the case, the renaming of new->init made the patch difficult to read, perhaps its just leaking it here in case init() returns !0, please check.
I.e. if init() somehow fails, then new() should detect that, and free what new() allocated ('he' in this case).
Quoted 7 lines
tools/perf/util/hist.c +2
-0+ +return err ?NULL : he; }static u8 symbol__parent_filter (const struct symbol *parent)Signature · 1 line
2.4.11
Jiri Olsa
reviewerPATCH 2/4 perf tools: Do the error path hist_entry release in hist_entry__new
In reply to Jiri OlsaIt's better to release the hist_entry object inhist_entry__new function (where it's allocated)
rather than in hist_entry__init.
--- tools/perf/util/hist.c | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-)
tools/perf/util/hist.c +6 -7
@@ -360,10 +360,8 @@ static int hist_entry__init(struct hist_entry *he,if (symbol_conf.cumulate_callchain) { he->stat_acc =malloc (sizeof (he->stat)); -if (he->stat_acc ==NULL ) { -free (he); +if (he->stat_acc ==NULL )return -ENOMEM ; - }memcpy (he->stat_acc, &he->stat,sizeof (he->stat));if (!sample_self)memset (&he->stat,0 ,sizeof (he->stat));
@@ -381,7 +379,6 @@ static int hist_entry__init(struct hist_entry *he,if (he->branch_info ==NULL ) {map__zput (he->ms.map);free (he->stat_acc); -free (he);return -ENOMEM ; }
@@ -415,7 +412,6 @@ static int hist_entry__init(struct hist_entry *he,map__put (he->mem_info->daddr.map); }free (he->stat_acc); -free (he);return -ENOMEM ; } }
@@ -439,10 +435,13 @@ static struct hist_entry *hist_entry__new(struct hist_entry *template, callchain_size =sizeof (struct callchain_root ); he =zalloc (sizeof (*he) + callchain_size); -if (he) +if (he) { err =hist_entry__init (he, template, sample_self); +if (err) +zfree (&he); + } -return err ?NULL : he; +return he; }static u8 symbol__parent_filter (const struct symbol *parent)
Signature · 1 line
2.4.11
Arnaldo Carvalho de Melo
maintainerRe: [PATCH 2/4] perf tools: Do the error path hist_entry release in hist_entry__new
Em Mon, Jul 04, 2016 at 04:01:37PM +0200, Jiri Olsa escreveu:
Quoted 3 lines
It's better to release the
hist_entryobject inhist_entry__newfunction (where it's allocated)
rather than inhist_entry__init.
Please combine this with the previous patch, that way this all gets clearer :-\
- Arnaldo
Quoted 56 lines
Signed-off-by Jiri Olsa <[email protected]>--- tools/perf/util/hist.c | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-)tools/perf/util/hist.c +6
-7@@ -360,10 +360,8 @@ static int hist_entry__init(struct hist_entry *he,if (symbol_conf.cumulate_callchain) { he->stat_acc =malloc (sizeof (he->stat)); -if (he->stat_acc ==NULL ) { -free (he); +if (he->stat_acc ==NULL )return -ENOMEM ; - }memcpy (he->stat_acc, &he->stat,sizeof (he->stat));if (!sample_self)memset (&he->stat,0 ,sizeof (he->stat));@@ -381,7 +379,6 @@ static int hist_entry__init(struct hist_entry *he,if (he->branch_info ==NULL ) {map__zput (he->ms.map);free (he->stat_acc); -free (he);return -ENOMEM ; }@@ -415,7 +412,6 @@ static int hist_entry__init(struct hist_entry *he,map__put (he->mem_info->daddr.map); }free (he->stat_acc); -free (he);return -ENOMEM ; } }@@ -439,10 +435,13 @@ static struct hist_entry *hist_entry__new(struct hist_entry *template, callchain_size =sizeof (struct callchain_root ); he =zalloc (sizeof (*he) + callchain_size); -if (he) +if (he) { err =hist_entry__init (he, template, sample_self); +if (err) +zfree (&he); + } -return err ?NULL : he; +return he; }static u8 symbol__parent_filter (const struct symbol *parent)Signature · 1 line
2.4.11
Re: [PATCH 2/4] perf tools: Do the error path hist_entry release in hist_entry__new
Quoted 7 lines · Arnaldo Carvalho de Melo
Em Mon, Jul 04, 2016 at 04:01:37PM +0200, Jiri Olsa escreveu:
Quoted 3 lines
It's better to release the
hist_entryobject inhist_entry__newfunction (where it's allocated)
rather than inhist_entry__init.Please combine this with the previous patch, that way this all gets clearer :-\
ok, will change
thanks,
jirka
Jiri Olsa
reviewerPATCH 3/4 perf tools: Introduce hist_entry_ops
In reply to Jiri OlsaIntroducing allocation callbacks, that allows to extend current hist_entry object into objects with special needs without polluting the current hist_entry object.
--- tools/perf/util/hist.c | 31 +++++++++++++++++++++++++++---- tools/perf/util/sort.h | 6 ++++++ 2 files changed, 33 insertions(+), 4 deletions(-)
tools/perf/util/hist.c +27 -4
@@ -424,21 +424,42 @@ static int hist_entry__init(struct hist_entry *he,return 0 ; } +static void *hist_entry__zalloc (size_t size) +{ +return zalloc (size +sizeof (struct hist_entry )); +} + +static void hist_entry__free (void *ptr) +{ +free (ptr); +} + +static struct hist_entry_ops default_ops = { + .new = hist_entry__zalloc, + .free = hist_entry__free, +}; +static struct hist_entry *hist_entry__new (struct hist_entry *template,bool sample_self) { +struct hist_entry_ops *ops = template->ops;size_t callchain_size =0 ;struct hist_entry *he;int err =0 ; +if (!ops) + ops = template->ops = &default_ops; +if (symbol_conf.use_callchain) callchain_size =sizeof (struct callchain_root ); - he =zalloc (sizeof (*he) + callchain_size); + he = ops->new (callchain_size);if (he) { err =hist_entry__init (he, template, sample_self); -if (err) -zfree (&he); +if (err) { + ops->free (he); + he =NULL ; + } }return he;
@@ -1050,6 +1071,8 @@ hist_entry__collapse(struct hist_entry *left, struct hist_entry *right)void hist_entry__delete (struct hist_entry *he) { +struct hist_entry_ops *ops = he->ops; +thread__zput (he->thread);map__zput (he->ms.map);
@@ -1074,7 +1097,7 @@ void hist_entry__delete(struct hist_entry *he)free_callchain (he->callchain);free (he->trace_output);free (he->raw_data); -free (he); + ops->free (he); }/*
tools/perf/util/sort.h +6 -0
@@ -67,6 +67,11 @@ struct hist_entry_diff { }; }; +struct hist_entry_ops { +void * (*new)(size_t ); +void (*free)(void *); +}; +/** * struct hist_entry - histogram entry *
@@ -125,6 +130,7 @@ struct hist_entry {void *trace_output;struct perf_hpp_list *hpp_list;struct hist_entry *parent_he; +struct hist_entry_ops *ops; union { /* this is for hierarchical entry structure */ struct {
Signature · 1 line
2.4.11
Jiri Olsa
reviewerPATCH 4/4 perf tools: Introduce hists__add_entry_ops function
In reply to Jiri OlsaIntroducing hists__add_entry_ops function to allow using the allocation callbacks externally.
--- tools/perf/util/hist.c | 42 +++++++++++++++++++++++++++++++++++------- tools/perf/util/hist.h | 11 +++++++++++ 2 files changed, 46 insertions(+), 7 deletions(-)
tools/perf/util/hist.c +35 -7
@@ -559,13 +559,15 @@ out:return he; } -struct hist_entry *hists__add_entry (struct hists *hists, -struct addr_location *al, -struct symbol *sym_parent, -struct branch_info *bi, -struct mem_info *mi, -struct perf_sample *sample, -bool sample_self) +static struct hist_entry * +__hists__add_entry (struct hists *hists, +struct addr_location *al, +struct symbol *sym_parent, +struct branch_info *bi, +struct mem_info *mi, +struct perf_sample *sample, +bool sample_self, +struct hist_entry_ops *ops) {struct hist_entry entry = { .thread = al->thread,
@@ -592,11 +594,37 @@ struct hist_entry *hists__add_entry(struct hists *hists, .transaction = sample->transaction, .raw_data = sample->raw_data, .raw_size = sample->raw_size, + .ops = ops, };return hists__findnew_entry (hists, &entry, al, sample_self); } +struct hist_entry *hists__add_entry (struct hists *hists, +struct addr_location *al, +struct symbol *sym_parent, +struct branch_info *bi, +struct mem_info *mi, +struct perf_sample *sample, +bool sample_self) +{ +return __hists__add_entry (hists, al, sym_parent, bi, mi, + sample, sample_self,NULL ); +} + +struct hist_entry *hists__add_entry_ops (struct hists *hists, +struct hist_entry_ops *ops, +struct addr_location *al, +struct symbol *sym_parent, +struct branch_info *bi, +struct mem_info *mi, +struct perf_sample *sample, +bool sample_self) +{ +return __hists__add_entry (hists, al, sym_parent, bi, mi, + sample, sample_self, ops); +} +static int iter_next_nop_entry (struct hist_entry_iter *iter__maybe_unused ,struct addr_location *al__maybe_unused )
tools/perf/util/hist.h +11 -0
@@ -10,6 +10,7 @@#include "ui/progress.h" struct hist_entry ; +struct hist_entry_ops ;struct addr_location ;struct symbol ;
@@ -127,6 +128,16 @@ struct hist_entry *hists__add_entry(struct hists *hists,struct mem_info *mi, struct perf_sample *sample, bool sample_self); + +struct hist_entry *hists__add_entry_ops (struct hists *hists, +struct hist_entry_ops *ops, +struct addr_location *al, +struct symbol *sym_parent, +struct branch_info *bi, +struct mem_info *mi, +struct perf_sample *sample, +bool sample_self); +int hist_entry_iter__add (struct hist_entry_iter *iter,struct addr_location *al,int max_stack_depth,void *arg);
Signature · 1 line
2.4.11