mbox series

[v6,00/27] Constify tool pointers

Message ID 20240718010023.1495687-1-irogers@google.com (mailing list archive)
Headers show
Series Constify tool pointers | expand

Message

Ian Rogers July 18, 2024, 12:59 a.m. UTC
struct perf_tool provides a set of function pointers that are called
through when processing perf data. To make filling the pointers less
cumbersome, if they are NULL perf_tools__fill_defaults will add
default do nothing implementations.

This change refactors struct perf_tool to have an init function that
provides the default implementation. The special use of NULL and
perf_tools__fill_defaults are removed. As a consequence the tool
pointers can then all be made const, which better reflects the
behavior a particular perf command would expect of the tool and to
some extent can reduce the cognitive load on someone working on a
command.

v6: Rebase adding Adrian's reviewed-by/tested-by and Leo's tested-by.
v5: Rebase dropping asan fix merged by Namhyung.
v4: Simplify perf_session__deliver_synth_attr_event following Adrian's
    suggestions.
v3: Just remove auxtrace dummy tools [Adrian] and make s390-cpumsf
    struct removal its own patch [Adrian].
v2: Remove dummy tool initialization [Adrian] and make zero sized. Add
    cs-etm fix for address sanitizer build, found necessary when
    testing dummy tool change.

Ian Rogers (27):
  perf auxtrace: Remove dummy tools
  perf s390-cpumsf: Remove unused struct
  perf tool: Constify tool pointers
  perf tool: Move fill defaults into tool.c
  perf tool: Add perf_tool__init
  perf kmem: Use perf_tool__init
  perf buildid-list: Use perf_tool__init
  perf kvm: Use perf_tool__init
  perf lock: Use perf_tool__init
  perf evlist: Use perf_tool__init
  perf record: Use perf_tool__init
  perf c2c: Use perf_tool__init
  perf script: Use perf_tool__init
  perf inject: Use perf_tool__init
  perf report: Use perf_tool__init
  perf stat: Use perf_tool__init
  perf annotate: Use perf_tool__init
  perf sched: Use perf_tool__init
  perf mem: Use perf_tool__init
  perf timechart: Use perf_tool__init
  perf diff: Use perf_tool__init
  perf data convert json: Use perf_tool__init
  perf data convert ctf: Use perf_tool__init
  perf test event_update: Ensure tools is initialized
  perf kwork: Use perf_tool__init
  perf tool: Remove perf_tool__fill_defaults
  perf session: Constify tool

 tools/perf/arch/x86/util/event.c    |   4 +-
 tools/perf/bench/synthesize.c       |   2 +-
 tools/perf/builtin-annotate.c       |  44 ++--
 tools/perf/builtin-buildid-list.c   |  10 +
 tools/perf/builtin-c2c.c            |  33 ++-
 tools/perf/builtin-diff.c           |  30 ++-
 tools/perf/builtin-evlist.c         |  10 +-
 tools/perf/builtin-inject.c         | 159 ++++++------
 tools/perf/builtin-kmem.c           |  20 +-
 tools/perf/builtin-kvm.c            |  19 +-
 tools/perf/builtin-kwork.c          |  33 ++-
 tools/perf/builtin-lock.c           |  41 ++--
 tools/perf/builtin-mem.c            |  37 +--
 tools/perf/builtin-record.c         |  47 ++--
 tools/perf/builtin-report.c         |  67 +++--
 tools/perf/builtin-sched.c          |  50 ++--
 tools/perf/builtin-script.c         | 106 ++++----
 tools/perf/builtin-stat.c           |  26 +-
 tools/perf/builtin-timechart.c      |  25 +-
 tools/perf/builtin-top.c            |   2 +-
 tools/perf/builtin-trace.c          |   4 +-
 tools/perf/tests/cpumap.c           |   6 +-
 tools/perf/tests/dlfilter-test.c    |   2 +-
 tools/perf/tests/dwarf-unwind.c     |   2 +-
 tools/perf/tests/event_update.c     |   9 +-
 tools/perf/tests/stat.c             |   6 +-
 tools/perf/tests/thread-map.c       |   2 +-
 tools/perf/util/Build               |   1 +
 tools/perf/util/arm-spe.c           |  55 +----
 tools/perf/util/auxtrace.c          |  12 +-
 tools/perf/util/auxtrace.h          |  20 +-
 tools/perf/util/bpf-event.c         |   4 +-
 tools/perf/util/build-id.c          |  34 +--
 tools/perf/util/build-id.h          |   8 +-
 tools/perf/util/cs-etm.c            |  39 +--
 tools/perf/util/data-convert-bt.c   |  34 ++-
 tools/perf/util/data-convert-json.c |  47 ++--
 tools/perf/util/event.c             |  54 ++--
 tools/perf/util/event.h             |  38 +--
 tools/perf/util/header.c            |   6 +-
 tools/perf/util/header.h            |   4 +-
 tools/perf/util/hisi-ptt.c          |   6 +-
 tools/perf/util/intel-bts.c         |  37 +--
 tools/perf/util/intel-pt.c          |  30 +--
 tools/perf/util/jitdump.c           |   4 +-
 tools/perf/util/s390-cpumsf.c       |  11 +-
 tools/perf/util/session.c           | 366 +++-------------------------
 tools/perf/util/session.h           |   9 +-
 tools/perf/util/synthetic-events.c  |  80 +++---
 tools/perf/util/synthetic-events.h  |  70 +++---
 tools/perf/util/tool.c              | 294 ++++++++++++++++++++++
 tools/perf/util/tool.h              |  18 +-
 tools/perf/util/tsc.c               |   2 +-
 53 files changed, 977 insertions(+), 1102 deletions(-)
 create mode 100644 tools/perf/util/tool.c

Comments

Adrian Hunter July 19, 2024, 8:50 a.m. UTC | #1
On 18/07/24 03:59, Ian Rogers wrote:
> struct perf_tool provides a set of function pointers that are called
> through when processing perf data. To make filling the pointers less
> cumbersome, if they are NULL perf_tools__fill_defaults will add
> default do nothing implementations.
> 
> This change refactors struct perf_tool to have an init function that
> provides the default implementation. The special use of NULL and
> perf_tools__fill_defaults are removed. As a consequence the tool
> pointers can then all be made const, which better reflects the
> behavior a particular perf command would expect of the tool and to
> some extent can reduce the cognitive load on someone working on a
> command.
> 
> v6: Rebase adding Adrian's reviewed-by/tested-by and Leo's tested-by.

The tags were really meant only for patch 1, the email that was replied to.

But now for patches 2 and 3:

Reviewed-by: Adrian Hunter <adrian.hunter@intel.com>

Looking at patches 4 to 25, they do not seem to offer any benefit.

Instead of patch 26, presumably perf_tool__fill_defaults() could
be moved to __perf_session__new(), which perhaps would allow
patch 27 as it is.


> v5: Rebase dropping asan fix merged by Namhyung.
> v4: Simplify perf_session__deliver_synth_attr_event following Adrian's
>     suggestions.
> v3: Just remove auxtrace dummy tools [Adrian] and make s390-cpumsf
>     struct removal its own patch [Adrian].
> v2: Remove dummy tool initialization [Adrian] and make zero sized. Add
>     cs-etm fix for address sanitizer build, found necessary when
>     testing dummy tool change.
> 
> Ian Rogers (27):
>   perf auxtrace: Remove dummy tools
>   perf s390-cpumsf: Remove unused struct
>   perf tool: Constify tool pointers
>   perf tool: Move fill defaults into tool.c
>   perf tool: Add perf_tool__init
>   perf kmem: Use perf_tool__init
>   perf buildid-list: Use perf_tool__init
>   perf kvm: Use perf_tool__init
>   perf lock: Use perf_tool__init
>   perf evlist: Use perf_tool__init
>   perf record: Use perf_tool__init
>   perf c2c: Use perf_tool__init
>   perf script: Use perf_tool__init
>   perf inject: Use perf_tool__init
>   perf report: Use perf_tool__init
>   perf stat: Use perf_tool__init
>   perf annotate: Use perf_tool__init
>   perf sched: Use perf_tool__init
>   perf mem: Use perf_tool__init
>   perf timechart: Use perf_tool__init
>   perf diff: Use perf_tool__init
>   perf data convert json: Use perf_tool__init
>   perf data convert ctf: Use perf_tool__init
>   perf test event_update: Ensure tools is initialized
>   perf kwork: Use perf_tool__init
>   perf tool: Remove perf_tool__fill_defaults
>   perf session: Constify tool
> 
>  tools/perf/arch/x86/util/event.c    |   4 +-
>  tools/perf/bench/synthesize.c       |   2 +-
>  tools/perf/builtin-annotate.c       |  44 ++--
>  tools/perf/builtin-buildid-list.c   |  10 +
>  tools/perf/builtin-c2c.c            |  33 ++-
>  tools/perf/builtin-diff.c           |  30 ++-
>  tools/perf/builtin-evlist.c         |  10 +-
>  tools/perf/builtin-inject.c         | 159 ++++++------
>  tools/perf/builtin-kmem.c           |  20 +-
>  tools/perf/builtin-kvm.c            |  19 +-
>  tools/perf/builtin-kwork.c          |  33 ++-
>  tools/perf/builtin-lock.c           |  41 ++--
>  tools/perf/builtin-mem.c            |  37 +--
>  tools/perf/builtin-record.c         |  47 ++--
>  tools/perf/builtin-report.c         |  67 +++--
>  tools/perf/builtin-sched.c          |  50 ++--
>  tools/perf/builtin-script.c         | 106 ++++----
>  tools/perf/builtin-stat.c           |  26 +-
>  tools/perf/builtin-timechart.c      |  25 +-
>  tools/perf/builtin-top.c            |   2 +-
>  tools/perf/builtin-trace.c          |   4 +-
>  tools/perf/tests/cpumap.c           |   6 +-
>  tools/perf/tests/dlfilter-test.c    |   2 +-
>  tools/perf/tests/dwarf-unwind.c     |   2 +-
>  tools/perf/tests/event_update.c     |   9 +-
>  tools/perf/tests/stat.c             |   6 +-
>  tools/perf/tests/thread-map.c       |   2 +-
>  tools/perf/util/Build               |   1 +
>  tools/perf/util/arm-spe.c           |  55 +----
>  tools/perf/util/auxtrace.c          |  12 +-
>  tools/perf/util/auxtrace.h          |  20 +-
>  tools/perf/util/bpf-event.c         |   4 +-
>  tools/perf/util/build-id.c          |  34 +--
>  tools/perf/util/build-id.h          |   8 +-
>  tools/perf/util/cs-etm.c            |  39 +--
>  tools/perf/util/data-convert-bt.c   |  34 ++-
>  tools/perf/util/data-convert-json.c |  47 ++--
>  tools/perf/util/event.c             |  54 ++--
>  tools/perf/util/event.h             |  38 +--
>  tools/perf/util/header.c            |   6 +-
>  tools/perf/util/header.h            |   4 +-
>  tools/perf/util/hisi-ptt.c          |   6 +-
>  tools/perf/util/intel-bts.c         |  37 +--
>  tools/perf/util/intel-pt.c          |  30 +--
>  tools/perf/util/jitdump.c           |   4 +-
>  tools/perf/util/s390-cpumsf.c       |  11 +-
>  tools/perf/util/session.c           | 366 +++-------------------------
>  tools/perf/util/session.h           |   9 +-
>  tools/perf/util/synthetic-events.c  |  80 +++---
>  tools/perf/util/synthetic-events.h  |  70 +++---
>  tools/perf/util/tool.c              | 294 ++++++++++++++++++++++
>  tools/perf/util/tool.h              |  18 +-
>  tools/perf/util/tsc.c               |   2 +-
>  53 files changed, 977 insertions(+), 1102 deletions(-)
>  create mode 100644 tools/perf/util/tool.c
>
Ian Rogers July 19, 2024, 4:26 p.m. UTC | #2
On Fri, Jul 19, 2024 at 1:51 AM Adrian Hunter <adrian.hunter@intel.com> wrote:
>
> On 18/07/24 03:59, Ian Rogers wrote:
> > struct perf_tool provides a set of function pointers that are called
> > through when processing perf data. To make filling the pointers less
> > cumbersome, if they are NULL perf_tools__fill_defaults will add
> > default do nothing implementations.
> >
> > This change refactors struct perf_tool to have an init function that
> > provides the default implementation. The special use of NULL and
> > perf_tools__fill_defaults are removed. As a consequence the tool
> > pointers can then all be made const, which better reflects the
> > behavior a particular perf command would expect of the tool and to
> > some extent can reduce the cognitive load on someone working on a
> > command.
> >
> > v6: Rebase adding Adrian's reviewed-by/tested-by and Leo's tested-by.
>
> The tags were really meant only for patch 1, the email that was replied to.
>
> But now for patches 2 and 3:
>
> Reviewed-by: Adrian Hunter <adrian.hunter@intel.com>

Sorry for that, you'd mentioned that pt and bts testing which is
impacted by more than just patch 1.

> Looking at patches 4 to 25, they do not seem to offer any benefit.
>
> Instead of patch 26, presumably perf_tool__fill_defaults() could
> be moved to __perf_session__new(), which perhaps would allow
> patch 27 as it is.

What I'm trying to do in the series is make it so that the tool isn't
mutated during its use by session. Ideally we'd be passing a const
tool to session_new, that's not possible because there's a hack to fix
ordered events and pipe mode in session__new:
https://git.kernel.org/pub/scm/linux/kernel/git/perf/perf-tools-next.git/tree/tools/perf/util/session.c?h=perf-tools-next#n275
Imo, it isn't great to pass a tool to session__new where you say you
want ordered events and then session just goes to change that for you.
Altering that behavior was beyond the scope of this clean up, so tool
is only const after session__new.

The reason for doing this is to make it so that when I have a tool I
can reason that nobody is doing things to change it under my feet. My
builtin_cmd is in charge of what the tool is rather than some code
buried in util that thought it was going to do me a favor. The code is
a refactor and so the benefit is intended to be for the developer and
how they reason about the use of tool. We generally use _init
functions rather than having _fill_defaults, so there is a consistency
argument. I don't expect any impact in terms of performance... Moving
perf_tool__fill_defaults to __perf_session__new had issues with the
existing code where NULL would be written over a function pointer
expecting the later fill_defaults to fix it up, doesn't address coding
consistency where _init is the norm, and adds another reason the tool
passed to session__new can't be const.

Thanks,
Ian

> > v5: Rebase dropping asan fix merged by Namhyung.
> > v4: Simplify perf_session__deliver_synth_attr_event following Adrian's
> >     suggestions.
> > v3: Just remove auxtrace dummy tools [Adrian] and make s390-cpumsf
> >     struct removal its own patch [Adrian].
> > v2: Remove dummy tool initialization [Adrian] and make zero sized. Add
> >     cs-etm fix for address sanitizer build, found necessary when
> >     testing dummy tool change.
> >
> > Ian Rogers (27):
> >   perf auxtrace: Remove dummy tools
> >   perf s390-cpumsf: Remove unused struct
> >   perf tool: Constify tool pointers
> >   perf tool: Move fill defaults into tool.c
> >   perf tool: Add perf_tool__init
> >   perf kmem: Use perf_tool__init
> >   perf buildid-list: Use perf_tool__init
> >   perf kvm: Use perf_tool__init
> >   perf lock: Use perf_tool__init
> >   perf evlist: Use perf_tool__init
> >   perf record: Use perf_tool__init
> >   perf c2c: Use perf_tool__init
> >   perf script: Use perf_tool__init
> >   perf inject: Use perf_tool__init
> >   perf report: Use perf_tool__init
> >   perf stat: Use perf_tool__init
> >   perf annotate: Use perf_tool__init
> >   perf sched: Use perf_tool__init
> >   perf mem: Use perf_tool__init
> >   perf timechart: Use perf_tool__init
> >   perf diff: Use perf_tool__init
> >   perf data convert json: Use perf_tool__init
> >   perf data convert ctf: Use perf_tool__init
> >   perf test event_update: Ensure tools is initialized
> >   perf kwork: Use perf_tool__init
> >   perf tool: Remove perf_tool__fill_defaults
> >   perf session: Constify tool
> >
> >  tools/perf/arch/x86/util/event.c    |   4 +-
> >  tools/perf/bench/synthesize.c       |   2 +-
> >  tools/perf/builtin-annotate.c       |  44 ++--
> >  tools/perf/builtin-buildid-list.c   |  10 +
> >  tools/perf/builtin-c2c.c            |  33 ++-
> >  tools/perf/builtin-diff.c           |  30 ++-
> >  tools/perf/builtin-evlist.c         |  10 +-
> >  tools/perf/builtin-inject.c         | 159 ++++++------
> >  tools/perf/builtin-kmem.c           |  20 +-
> >  tools/perf/builtin-kvm.c            |  19 +-
> >  tools/perf/builtin-kwork.c          |  33 ++-
> >  tools/perf/builtin-lock.c           |  41 ++--
> >  tools/perf/builtin-mem.c            |  37 +--
> >  tools/perf/builtin-record.c         |  47 ++--
> >  tools/perf/builtin-report.c         |  67 +++--
> >  tools/perf/builtin-sched.c          |  50 ++--
> >  tools/perf/builtin-script.c         | 106 ++++----
> >  tools/perf/builtin-stat.c           |  26 +-
> >  tools/perf/builtin-timechart.c      |  25 +-
> >  tools/perf/builtin-top.c            |   2 +-
> >  tools/perf/builtin-trace.c          |   4 +-
> >  tools/perf/tests/cpumap.c           |   6 +-
> >  tools/perf/tests/dlfilter-test.c    |   2 +-
> >  tools/perf/tests/dwarf-unwind.c     |   2 +-
> >  tools/perf/tests/event_update.c     |   9 +-
> >  tools/perf/tests/stat.c             |   6 +-
> >  tools/perf/tests/thread-map.c       |   2 +-
> >  tools/perf/util/Build               |   1 +
> >  tools/perf/util/arm-spe.c           |  55 +----
> >  tools/perf/util/auxtrace.c          |  12 +-
> >  tools/perf/util/auxtrace.h          |  20 +-
> >  tools/perf/util/bpf-event.c         |   4 +-
> >  tools/perf/util/build-id.c          |  34 +--
> >  tools/perf/util/build-id.h          |   8 +-
> >  tools/perf/util/cs-etm.c            |  39 +--
> >  tools/perf/util/data-convert-bt.c   |  34 ++-
> >  tools/perf/util/data-convert-json.c |  47 ++--
> >  tools/perf/util/event.c             |  54 ++--
> >  tools/perf/util/event.h             |  38 +--
> >  tools/perf/util/header.c            |   6 +-
> >  tools/perf/util/header.h            |   4 +-
> >  tools/perf/util/hisi-ptt.c          |   6 +-
> >  tools/perf/util/intel-bts.c         |  37 +--
> >  tools/perf/util/intel-pt.c          |  30 +--
> >  tools/perf/util/jitdump.c           |   4 +-
> >  tools/perf/util/s390-cpumsf.c       |  11 +-
> >  tools/perf/util/session.c           | 366 +++-------------------------
> >  tools/perf/util/session.h           |   9 +-
> >  tools/perf/util/synthetic-events.c  |  80 +++---
> >  tools/perf/util/synthetic-events.h  |  70 +++---
> >  tools/perf/util/tool.c              | 294 ++++++++++++++++++++++
> >  tools/perf/util/tool.h              |  18 +-
> >  tools/perf/util/tsc.c               |   2 +-
> >  53 files changed, 977 insertions(+), 1102 deletions(-)
> >  create mode 100644 tools/perf/util/tool.c
> >
>
Adrian Hunter July 22, 2024, 8:28 a.m. UTC | #3
On 19/07/24 19:26, Ian Rogers wrote:
> On Fri, Jul 19, 2024 at 1:51 AM Adrian Hunter <adrian.hunter@intel.com> wrote:
>>
>> On 18/07/24 03:59, Ian Rogers wrote:
>>> struct perf_tool provides a set of function pointers that are called
>>> through when processing perf data. To make filling the pointers less
>>> cumbersome, if they are NULL perf_tools__fill_defaults will add
>>> default do nothing implementations.
>>>
>>> This change refactors struct perf_tool to have an init function that
>>> provides the default implementation. The special use of NULL and
>>> perf_tools__fill_defaults are removed. As a consequence the tool
>>> pointers can then all be made const, which better reflects the
>>> behavior a particular perf command would expect of the tool and to
>>> some extent can reduce the cognitive load on someone working on a
>>> command.
>>>
>>> v6: Rebase adding Adrian's reviewed-by/tested-by and Leo's tested-by.
>>
>> The tags were really meant only for patch 1, the email that was replied to.
>>
>> But now for patches 2 and 3:
>>
>> Reviewed-by: Adrian Hunter <adrian.hunter@intel.com>
> 
> Sorry for that, you'd mentioned that pt and bts testing which is
> impacted by more than just patch 1.
> 
>> Looking at patches 4 to 25, they do not seem to offer any benefit.
>>
>> Instead of patch 26, presumably perf_tool__fill_defaults() could
>> be moved to __perf_session__new(), which perhaps would allow
>> patch 27 as it is.
> 
> What I'm trying to do in the series is make it so that the tool isn't
> mutated during its use by session. Ideally we'd be passing a const
> tool to session_new, that's not possible because there's a hack to fix
> ordered events and pipe mode in session__new:
> https://git.kernel.org/pub/scm/linux/kernel/git/perf/perf-tools-next.git/tree/tools/perf/util/session.c?h=perf-tools-next#n275
> Imo, it isn't great to pass a tool to session__new where you say you
> want ordered events and then session just goes to change that for you.
> Altering that behavior was beyond the scope of this clean up, so tool
> is only const after session__new.

Seems like a separate issue.  Since the session is created
by __perf_session__new(), session->tool will always be a pointer
to a const tool once there is:

diff --git a/tools/perf/util/session.h b/tools/perf/util/session.h
index 7f69baeae7fb..7c8dd6956330 100644
--- a/tools/perf/util/session.h
+++ b/tools/perf/util/session.h
@@ -43,7 +43,7 @@ struct perf_session {
 	u64			one_mmap_offset;
 	struct ordered_events	ordered_events;
 	struct perf_data	*data;
-	struct perf_tool	*tool;
+	const struct perf_tool	*tool;
 	u64			bytes_transferred;
 	u64			bytes_compressed;
 	struct zstd_data	zstd_data;

> 
> The reason for doing this is to make it so that when I have a tool I
> can reason that nobody is doing things to change it under my feet.

It still can be changed by the caller of __perf_session__new(), since
the tool itself is not const.

Anything using container_of() like:

static int process_sample_event(const struct perf_tool *tool,
				union perf_event *event,
				struct perf_sample *sample,
				struct evsel *evsel,
				struct machine *machine)
{
	struct perf_script *scr = container_of(tool, struct perf_script, tool);

can then change scr->tool without even having to cast away const.

Really, 'tool' needs to be defined as const in the first place.

> My
> builtin_cmd is in charge of what the tool is rather than some code
> buried in util that thought it was going to do me a favor. The code is
> a refactor and so the benefit is intended to be for the developer and
> how they reason about the use of tool.

It creates another question though: since there is a lot of code
before perf_tool__init() is called, does the caller mistakenly
change tool before calling perf_tool__init()

> how they reason about the use of tool. We generally use _init
> functions rather than having _fill_defaults, so there is a consistency
> argument.

The caller does not need the "defaults", so why would it set them up.
The session could just as easily do:

	if (tool->cb)
		tool->cb(...);
	else
		cb_stub(...);

> I don't expect any impact in terms of performance... Moving
> perf_tool__fill_defaults to __perf_session__new had issues with the
> existing code where NULL would be written over a function pointer
> expecting the later fill_defaults to fix it up, doesn't address coding
> consistency where _init is the norm, and adds another reason the tool
> passed to session__new can't be const.

perf_tool__init() is not a steeping stone to making 'tool' a
const in the first place.
Ian Rogers July 22, 2024, 4:06 p.m. UTC | #4
On Mon, Jul 22, 2024 at 1:29 AM Adrian Hunter <adrian.hunter@intel.com> wrote:
>
> On 19/07/24 19:26, Ian Rogers wrote:
> > On Fri, Jul 19, 2024 at 1:51 AM Adrian Hunter <adrian.hunter@intel.com> wrote:
> >>
> >> On 18/07/24 03:59, Ian Rogers wrote:
> >>> struct perf_tool provides a set of function pointers that are called
> >>> through when processing perf data. To make filling the pointers less
> >>> cumbersome, if they are NULL perf_tools__fill_defaults will add
> >>> default do nothing implementations.
> >>>
> >>> This change refactors struct perf_tool to have an init function that
> >>> provides the default implementation. The special use of NULL and
> >>> perf_tools__fill_defaults are removed. As a consequence the tool
> >>> pointers can then all be made const, which better reflects the
> >>> behavior a particular perf command would expect of the tool and to
> >>> some extent can reduce the cognitive load on someone working on a
> >>> command.
> >>>
> >>> v6: Rebase adding Adrian's reviewed-by/tested-by and Leo's tested-by.
> >>
> >> The tags were really meant only for patch 1, the email that was replied to.
> >>
> >> But now for patches 2 and 3:
> >>
> >> Reviewed-by: Adrian Hunter <adrian.hunter@intel.com>
> >
> > Sorry for that, you'd mentioned that pt and bts testing which is
> > impacted by more than just patch 1.
> >
> >> Looking at patches 4 to 25, they do not seem to offer any benefit.
> >>
> >> Instead of patch 26, presumably perf_tool__fill_defaults() could
> >> be moved to __perf_session__new(), which perhaps would allow
> >> patch 27 as it is.
> >
> > What I'm trying to do in the series is make it so that the tool isn't
> > mutated during its use by session. Ideally we'd be passing a const
> > tool to session_new, that's not possible because there's a hack to fix
> > ordered events and pipe mode in session__new:
> > https://git.kernel.org/pub/scm/linux/kernel/git/perf/perf-tools-next.git/tree/tools/perf/util/session.c?h=perf-tools-next#n275
> > Imo, it isn't great to pass a tool to session__new where you say you
> > want ordered events and then session just goes to change that for you.
> > Altering that behavior was beyond the scope of this clean up, so tool
> > is only const after session__new.
>
> Seems like a separate issue.  Since the session is created
> by __perf_session__new(), session->tool will always be a pointer
> to a const tool once there is:
>
> diff --git a/tools/perf/util/session.h b/tools/perf/util/session.h
> index 7f69baeae7fb..7c8dd6956330 100644
> --- a/tools/perf/util/session.h
> +++ b/tools/perf/util/session.h
> @@ -43,7 +43,7 @@ struct perf_session {
>         u64                     one_mmap_offset;
>         struct ordered_events   ordered_events;
>         struct perf_data        *data;
> -       struct perf_tool        *tool;
> +       const struct perf_tool  *tool;
>         u64                     bytes_transferred;
>         u64                     bytes_compressed;
>         struct zstd_data        zstd_data;

That's the case after these changes, but not before.

> >
> > The reason for doing this is to make it so that when I have a tool I
> > can reason that nobody is doing things to change it under my feet.
>
> It still can be changed by the caller of __perf_session__new(), since
> the tool itself is not const.
>
> Anything using container_of() like:
>
> static int process_sample_event(const struct perf_tool *tool,
>                                 union perf_event *event,
>                                 struct perf_sample *sample,
>                                 struct evsel *evsel,
>                                 struct machine *machine)
> {
>         struct perf_script *scr = container_of(tool, struct perf_script, tool);
>
> can then change scr->tool without even having to cast away const.

Agreed, but such things happen in builtin_cmd where the tool is
defined and presumably they know what they are doing. My objection is
to code in util mutating the tool as I want the tool to have
predictable behavior. As callers that take a tool can call fill in
defaults (not all) then the tool has to be mutable and I don't want
this to be the case.

> Really, 'tool' needs to be defined as const in the first place.

I'd like this. The problem is initializing all the function pointers
and making such initialization robust to extra functions being added
to the tool API. It can be done in a long winded way but I couldn't
devise macro magic to do it. The other problem is around variables
like ordered_events that can't currently be const. The patches move us
closer to this being a possibility.

> > My
> > builtin_cmd is in charge of what the tool is rather than some code
> > buried in util that thought it was going to do me a favor. The code is
> > a refactor and so the benefit is intended to be for the developer and
> > how they reason about the use of tool.
>
> It creates another question though: since there is a lot of code
> before perf_tool__init() is called, does the caller mistakenly
> change tool before calling perf_tool__init()

If they do this their function pointers will be clobbered and their
code won't behave as expected, which I'd expect to be easy to observe.
In C++ if you were to initialize memory and then use the memory for a
placement new to create an object which would call the constructor,
the expected behavior would be that the initialized memory's values
would get overridden. I see the use of _init and _exit in the code as
being our poor man replacements of constructors and destructors.

> > how they reason about the use of tool. We generally use _init
> > functions rather than having _fill_defaults, so there is a consistency
> > argument.
>
> The caller does not need the "defaults", so why would it set them up.
> The session could just as easily do:
>
>         if (tool->cb)
>                 tool->cb(...);
>         else
>                 cb_stub(...);

Multiplied by every stub, we'd probably need a helper function, how to
handle argument passing. There's nothing wrong with this as an idea
but I think of this code as trying to create a visitor pattern and
this is a visitor pattern with a hard time for the caller.

> > I don't expect any impact in terms of performance... Moving
> > perf_tool__fill_defaults to __perf_session__new had issues with the
> > existing code where NULL would be written over a function pointer
> > expecting the later fill_defaults to fix it up, doesn't address coding
> > consistency where _init is the norm, and adds another reason the tool
> > passed to session__new can't be const.
>
> perf_tool__init() is not a steeping stone to making 'tool' a
> const in the first place.

It is because the patch series gets rid of fill in defaults which is
why we have a mutable tool passed around. I don't think this is up for
debate as the patch series clearly goes from a non-const
tool to a const tool at the end. Changing perf_tool__init to make all
the function pointers NULL and then making every caller have to do a:

         if (tool->cb)
                 tool->cb(...);
         else
                 cb_stub(...);

I think it is a less elegant solution at the end, it is also a large
and more invasive change. The various refactorings to make tool const,
changing the aux use of tool, etc. wouldn't be impacted by such a
change but I think it is out of scope for this patch series.

Thanks,
Ian
Namhyung Kim July 22, 2024, 5:45 p.m. UTC | #5
Hi,

On Mon, Jul 22, 2024 at 9:06 AM Ian Rogers <irogers@google.com> wrote:
>
> On Mon, Jul 22, 2024 at 1:29 AM Adrian Hunter <adrian.hunter@intel.com> wrote:
> >
> > On 19/07/24 19:26, Ian Rogers wrote:
> > > On Fri, Jul 19, 2024 at 1:51 AM Adrian Hunter <adrian.hunter@intel.com> wrote:
> > >>
> > >> On 18/07/24 03:59, Ian Rogers wrote:
> > >>> struct perf_tool provides a set of function pointers that are called
> > >>> through when processing perf data. To make filling the pointers less
> > >>> cumbersome, if they are NULL perf_tools__fill_defaults will add
> > >>> default do nothing implementations.
> > >>>
> > >>> This change refactors struct perf_tool to have an init function that
> > >>> provides the default implementation. The special use of NULL and
> > >>> perf_tools__fill_defaults are removed. As a consequence the tool
> > >>> pointers can then all be made const, which better reflects the
> > >>> behavior a particular perf command would expect of the tool and to
> > >>> some extent can reduce the cognitive load on someone working on a
> > >>> command.
> > >>>
> > >>> v6: Rebase adding Adrian's reviewed-by/tested-by and Leo's tested-by.
> > >>
> > >> The tags were really meant only for patch 1, the email that was replied to.
> > >>
> > >> But now for patches 2 and 3:
> > >>
> > >> Reviewed-by: Adrian Hunter <adrian.hunter@intel.com>
> > >
> > > Sorry for that, you'd mentioned that pt and bts testing which is
> > > impacted by more than just patch 1.
> > >
> > >> Looking at patches 4 to 25, they do not seem to offer any benefit.
> > >>
> > >> Instead of patch 26, presumably perf_tool__fill_defaults() could
> > >> be moved to __perf_session__new(), which perhaps would allow
> > >> patch 27 as it is.
> > >
> > > What I'm trying to do in the series is make it so that the tool isn't
> > > mutated during its use by session. Ideally we'd be passing a const
> > > tool to session_new, that's not possible because there's a hack to fix
> > > ordered events and pipe mode in session__new:
> > > https://git.kernel.org/pub/scm/linux/kernel/git/perf/perf-tools-next.git/tree/tools/perf/util/session.c?h=perf-tools-next#n275
> > > Imo, it isn't great to pass a tool to session__new where you say you
> > > want ordered events and then session just goes to change that for you.
> > > Altering that behavior was beyond the scope of this clean up, so tool
> > > is only const after session__new.
> >
> > Seems like a separate issue.  Since the session is created
> > by __perf_session__new(), session->tool will always be a pointer
> > to a const tool once there is:
> >
> > diff --git a/tools/perf/util/session.h b/tools/perf/util/session.h
> > index 7f69baeae7fb..7c8dd6956330 100644
> > --- a/tools/perf/util/session.h
> > +++ b/tools/perf/util/session.h
> > @@ -43,7 +43,7 @@ struct perf_session {
> >         u64                     one_mmap_offset;
> >         struct ordered_events   ordered_events;
> >         struct perf_data        *data;
> > -       struct perf_tool        *tool;
> > +       const struct perf_tool  *tool;
> >         u64                     bytes_transferred;
> >         u64                     bytes_compressed;
> >         struct zstd_data        zstd_data;
>
> That's the case after these changes, but not before.
>
> > >
> > > The reason for doing this is to make it so that when I have a tool I
> > > can reason that nobody is doing things to change it under my feet.
> >
> > It still can be changed by the caller of __perf_session__new(), since
> > the tool itself is not const.
> >
> > Anything using container_of() like:
> >
> > static int process_sample_event(const struct perf_tool *tool,
> >                                 union perf_event *event,
> >                                 struct perf_sample *sample,
> >                                 struct evsel *evsel,
> >                                 struct machine *machine)
> > {
> >         struct perf_script *scr = container_of(tool, struct perf_script, tool);
> >
> > can then change scr->tool without even having to cast away const.
>
> Agreed, but such things happen in builtin_cmd where the tool is
> defined and presumably they know what they are doing. My objection is
> to code in util mutating the tool as I want the tool to have
> predictable behavior. As callers that take a tool can call fill in
> defaults (not all) then the tool has to be mutable and I don't want
> this to be the case.
>
> > Really, 'tool' needs to be defined as const in the first place.
>
> I'd like this. The problem is initializing all the function pointers
> and making such initialization robust to extra functions being added
> to the tool API. It can be done in a long winded way but I couldn't
> devise macro magic to do it. The other problem is around variables
> like ordered_events that can't currently be const. The patches move us
> closer to this being a possibility.
>
> > > My
> > > builtin_cmd is in charge of what the tool is rather than some code
> > > buried in util that thought it was going to do me a favor. The code is
> > > a refactor and so the benefit is intended to be for the developer and
> > > how they reason about the use of tool.
> >
> > It creates another question though: since there is a lot of code
> > before perf_tool__init() is called, does the caller mistakenly
> > change tool before calling perf_tool__init()
>
> If they do this their function pointers will be clobbered and their
> code won't behave as expected, which I'd expect to be easy to observe.
> In C++ if you were to initialize memory and then use the memory for a
> placement new to create an object which would call the constructor,
> the expected behavior would be that the initialized memory's values
> would get overridden. I see the use of _init and _exit in the code as
> being our poor man replacements of constructors and destructors.
>
> > > how they reason about the use of tool. We generally use _init
> > > functions rather than having _fill_defaults, so there is a consistency
> > > argument.
> >
> > The caller does not need the "defaults", so why would it set them up.
> > The session could just as easily do:
> >
> >         if (tool->cb)
> >                 tool->cb(...);
> >         else
> >                 cb_stub(...);
>
> Multiplied by every stub, we'd probably need a helper function, how to
> handle argument passing. There's nothing wrong with this as an idea
> but I think of this code as trying to create a visitor pattern and
> this is a visitor pattern with a hard time for the caller.
>
> > > I don't expect any impact in terms of performance... Moving
> > > perf_tool__fill_defaults to __perf_session__new had issues with the
> > > existing code where NULL would be written over a function pointer
> > > expecting the later fill_defaults to fix it up, doesn't address coding
> > > consistency where _init is the norm, and adds another reason the tool
> > > passed to session__new can't be const.
> >
> > perf_tool__init() is not a steeping stone to making 'tool' a
> > const in the first place.
>
> It is because the patch series gets rid of fill in defaults which is
> why we have a mutable tool passed around. I don't think this is up for
> debate as the patch series clearly goes from a non-const
> tool to a const tool at the end. Changing perf_tool__init to make all
> the function pointers NULL and then making every caller have to do a:
>
>          if (tool->cb)
>                  tool->cb(...);
>          else
>                  cb_stub(...);
>
> I think it is a less elegant solution at the end, it is also a large
> and more invasive change. The various refactorings to make tool const,
> changing the aux use of tool, etc. wouldn't be impacted by such a
> change but I think it is out of scope for this patch series.

I don't think it's a large and invasive change.  The tools are mostly
zero-initialized so we don't need to reset to NULL.  And tool->cb is
called mostly from two functions: machines__deliver_event() and
perf_session__process_user_event().  Can we change them to check
NULL and get rid of perf_tool__fill_defaults() to keep it const?

Thanks,
Namhyung
Ian Rogers July 22, 2024, 5:50 p.m. UTC | #6
On Mon, Jul 22, 2024 at 10:45 AM Namhyung Kim <namhyung@kernel.org> wrote:
>
> Hi,
>
> On Mon, Jul 22, 2024 at 9:06 AM Ian Rogers <irogers@google.com> wrote:
> >
> > On Mon, Jul 22, 2024 at 1:29 AM Adrian Hunter <adrian.hunter@intel.com> wrote:
> > >
> > > On 19/07/24 19:26, Ian Rogers wrote:
> > > > On Fri, Jul 19, 2024 at 1:51 AM Adrian Hunter <adrian.hunter@intel.com> wrote:
> > > >>
> > > >> On 18/07/24 03:59, Ian Rogers wrote:
> > > >>> struct perf_tool provides a set of function pointers that are called
> > > >>> through when processing perf data. To make filling the pointers less
> > > >>> cumbersome, if they are NULL perf_tools__fill_defaults will add
> > > >>> default do nothing implementations.
> > > >>>
> > > >>> This change refactors struct perf_tool to have an init function that
> > > >>> provides the default implementation. The special use of NULL and
> > > >>> perf_tools__fill_defaults are removed. As a consequence the tool
> > > >>> pointers can then all be made const, which better reflects the
> > > >>> behavior a particular perf command would expect of the tool and to
> > > >>> some extent can reduce the cognitive load on someone working on a
> > > >>> command.
> > > >>>
> > > >>> v6: Rebase adding Adrian's reviewed-by/tested-by and Leo's tested-by.
> > > >>
> > > >> The tags were really meant only for patch 1, the email that was replied to.
> > > >>
> > > >> But now for patches 2 and 3:
> > > >>
> > > >> Reviewed-by: Adrian Hunter <adrian.hunter@intel.com>
> > > >
> > > > Sorry for that, you'd mentioned that pt and bts testing which is
> > > > impacted by more than just patch 1.
> > > >
> > > >> Looking at patches 4 to 25, they do not seem to offer any benefit.
> > > >>
> > > >> Instead of patch 26, presumably perf_tool__fill_defaults() could
> > > >> be moved to __perf_session__new(), which perhaps would allow
> > > >> patch 27 as it is.
> > > >
> > > > What I'm trying to do in the series is make it so that the tool isn't
> > > > mutated during its use by session. Ideally we'd be passing a const
> > > > tool to session_new, that's not possible because there's a hack to fix
> > > > ordered events and pipe mode in session__new:
> > > > https://git.kernel.org/pub/scm/linux/kernel/git/perf/perf-tools-next.git/tree/tools/perf/util/session.c?h=perf-tools-next#n275
> > > > Imo, it isn't great to pass a tool to session__new where you say you
> > > > want ordered events and then session just goes to change that for you.
> > > > Altering that behavior was beyond the scope of this clean up, so tool
> > > > is only const after session__new.
> > >
> > > Seems like a separate issue.  Since the session is created
> > > by __perf_session__new(), session->tool will always be a pointer
> > > to a const tool once there is:
> > >
> > > diff --git a/tools/perf/util/session.h b/tools/perf/util/session.h
> > > index 7f69baeae7fb..7c8dd6956330 100644
> > > --- a/tools/perf/util/session.h
> > > +++ b/tools/perf/util/session.h
> > > @@ -43,7 +43,7 @@ struct perf_session {
> > >         u64                     one_mmap_offset;
> > >         struct ordered_events   ordered_events;
> > >         struct perf_data        *data;
> > > -       struct perf_tool        *tool;
> > > +       const struct perf_tool  *tool;
> > >         u64                     bytes_transferred;
> > >         u64                     bytes_compressed;
> > >         struct zstd_data        zstd_data;
> >
> > That's the case after these changes, but not before.
> >
> > > >
> > > > The reason for doing this is to make it so that when I have a tool I
> > > > can reason that nobody is doing things to change it under my feet.
> > >
> > > It still can be changed by the caller of __perf_session__new(), since
> > > the tool itself is not const.
> > >
> > > Anything using container_of() like:
> > >
> > > static int process_sample_event(const struct perf_tool *tool,
> > >                                 union perf_event *event,
> > >                                 struct perf_sample *sample,
> > >                                 struct evsel *evsel,
> > >                                 struct machine *machine)
> > > {
> > >         struct perf_script *scr = container_of(tool, struct perf_script, tool);
> > >
> > > can then change scr->tool without even having to cast away const.
> >
> > Agreed, but such things happen in builtin_cmd where the tool is
> > defined and presumably they know what they are doing. My objection is
> > to code in util mutating the tool as I want the tool to have
> > predictable behavior. As callers that take a tool can call fill in
> > defaults (not all) then the tool has to be mutable and I don't want
> > this to be the case.
> >
> > > Really, 'tool' needs to be defined as const in the first place.
> >
> > I'd like this. The problem is initializing all the function pointers
> > and making such initialization robust to extra functions being added
> > to the tool API. It can be done in a long winded way but I couldn't
> > devise macro magic to do it. The other problem is around variables
> > like ordered_events that can't currently be const. The patches move us
> > closer to this being a possibility.
> >
> > > > My
> > > > builtin_cmd is in charge of what the tool is rather than some code
> > > > buried in util that thought it was going to do me a favor. The code is
> > > > a refactor and so the benefit is intended to be for the developer and
> > > > how they reason about the use of tool.
> > >
> > > It creates another question though: since there is a lot of code
> > > before perf_tool__init() is called, does the caller mistakenly
> > > change tool before calling perf_tool__init()
> >
> > If they do this their function pointers will be clobbered and their
> > code won't behave as expected, which I'd expect to be easy to observe.
> > In C++ if you were to initialize memory and then use the memory for a
> > placement new to create an object which would call the constructor,
> > the expected behavior would be that the initialized memory's values
> > would get overridden. I see the use of _init and _exit in the code as
> > being our poor man replacements of constructors and destructors.
> >
> > > > how they reason about the use of tool. We generally use _init
> > > > functions rather than having _fill_defaults, so there is a consistency
> > > > argument.
> > >
> > > The caller does not need the "defaults", so why would it set them up.
> > > The session could just as easily do:
> > >
> > >         if (tool->cb)
> > >                 tool->cb(...);
> > >         else
> > >                 cb_stub(...);
> >
> > Multiplied by every stub, we'd probably need a helper function, how to
> > handle argument passing. There's nothing wrong with this as an idea
> > but I think of this code as trying to create a visitor pattern and
> > this is a visitor pattern with a hard time for the caller.
> >
> > > > I don't expect any impact in terms of performance... Moving
> > > > perf_tool__fill_defaults to __perf_session__new had issues with the
> > > > existing code where NULL would be written over a function pointer
> > > > expecting the later fill_defaults to fix it up, doesn't address coding
> > > > consistency where _init is the norm, and adds another reason the tool
> > > > passed to session__new can't be const.
> > >
> > > perf_tool__init() is not a steeping stone to making 'tool' a
> > > const in the first place.
> >
> > It is because the patch series gets rid of fill in defaults which is
> > why we have a mutable tool passed around. I don't think this is up for
> > debate as the patch series clearly goes from a non-const
> > tool to a const tool at the end. Changing perf_tool__init to make all
> > the function pointers NULL and then making every caller have to do a:
> >
> >          if (tool->cb)
> >                  tool->cb(...);
> >          else
> >                  cb_stub(...);
> >
> > I think it is a less elegant solution at the end, it is also a large
> > and more invasive change. The various refactorings to make tool const,
> > changing the aux use of tool, etc. wouldn't be impacted by such a
> > change but I think it is out of scope for this patch series.
>
> I don't think it's a large and invasive change.  The tools are mostly
> zero-initialized so we don't need to reset to NULL.  And tool->cb is
> called mostly from two functions: machines__deliver_event() and
> perf_session__process_user_event().  Can we change them to check
> NULL and get rid of perf_tool__fill_defaults() to keep it const?

As I said above, I don't think that is good style and is out of scope
here. It clearly can be done as follow up, but I don't see how that
fixes the style issue.

Thanks,
Ian
Ian Rogers July 22, 2024, 6:04 p.m. UTC | #7
On Mon, Jul 22, 2024 at 10:50 AM Ian Rogers <irogers@google.com> wrote:
>
> On Mon, Jul 22, 2024 at 10:45 AM Namhyung Kim <namhyung@kernel.org> wrote:
> >
> > Hi,
> >
> > On Mon, Jul 22, 2024 at 9:06 AM Ian Rogers <irogers@google.com> wrote:
> > >
> > > On Mon, Jul 22, 2024 at 1:29 AM Adrian Hunter <adrian.hunter@intel.com> wrote:
> > > >
> > > > On 19/07/24 19:26, Ian Rogers wrote:
> > > > > On Fri, Jul 19, 2024 at 1:51 AM Adrian Hunter <adrian.hunter@intel.com> wrote:
> > > > >>
> > > > >> On 18/07/24 03:59, Ian Rogers wrote:
> > > > >>> struct perf_tool provides a set of function pointers that are called
> > > > >>> through when processing perf data. To make filling the pointers less
> > > > >>> cumbersome, if they are NULL perf_tools__fill_defaults will add
> > > > >>> default do nothing implementations.
> > > > >>>
> > > > >>> This change refactors struct perf_tool to have an init function that
> > > > >>> provides the default implementation. The special use of NULL and
> > > > >>> perf_tools__fill_defaults are removed. As a consequence the tool
> > > > >>> pointers can then all be made const, which better reflects the
> > > > >>> behavior a particular perf command would expect of the tool and to
> > > > >>> some extent can reduce the cognitive load on someone working on a
> > > > >>> command.
> > > > >>>
> > > > >>> v6: Rebase adding Adrian's reviewed-by/tested-by and Leo's tested-by.
> > > > >>
> > > > >> The tags were really meant only for patch 1, the email that was replied to.
> > > > >>
> > > > >> But now for patches 2 and 3:
> > > > >>
> > > > >> Reviewed-by: Adrian Hunter <adrian.hunter@intel.com>
> > > > >
> > > > > Sorry for that, you'd mentioned that pt and bts testing which is
> > > > > impacted by more than just patch 1.
> > > > >
> > > > >> Looking at patches 4 to 25, they do not seem to offer any benefit.
> > > > >>
> > > > >> Instead of patch 26, presumably perf_tool__fill_defaults() could
> > > > >> be moved to __perf_session__new(), which perhaps would allow
> > > > >> patch 27 as it is.
> > > > >
> > > > > What I'm trying to do in the series is make it so that the tool isn't
> > > > > mutated during its use by session. Ideally we'd be passing a const
> > > > > tool to session_new, that's not possible because there's a hack to fix
> > > > > ordered events and pipe mode in session__new:
> > > > > https://git.kernel.org/pub/scm/linux/kernel/git/perf/perf-tools-next.git/tree/tools/perf/util/session.c?h=perf-tools-next#n275
> > > > > Imo, it isn't great to pass a tool to session__new where you say you
> > > > > want ordered events and then session just goes to change that for you.
> > > > > Altering that behavior was beyond the scope of this clean up, so tool
> > > > > is only const after session__new.
> > > >
> > > > Seems like a separate issue.  Since the session is created
> > > > by __perf_session__new(), session->tool will always be a pointer
> > > > to a const tool once there is:
> > > >
> > > > diff --git a/tools/perf/util/session.h b/tools/perf/util/session.h
> > > > index 7f69baeae7fb..7c8dd6956330 100644
> > > > --- a/tools/perf/util/session.h
> > > > +++ b/tools/perf/util/session.h
> > > > @@ -43,7 +43,7 @@ struct perf_session {
> > > >         u64                     one_mmap_offset;
> > > >         struct ordered_events   ordered_events;
> > > >         struct perf_data        *data;
> > > > -       struct perf_tool        *tool;
> > > > +       const struct perf_tool  *tool;
> > > >         u64                     bytes_transferred;
> > > >         u64                     bytes_compressed;
> > > >         struct zstd_data        zstd_data;
> > >
> > > That's the case after these changes, but not before.
> > >
> > > > >
> > > > > The reason for doing this is to make it so that when I have a tool I
> > > > > can reason that nobody is doing things to change it under my feet.
> > > >
> > > > It still can be changed by the caller of __perf_session__new(), since
> > > > the tool itself is not const.
> > > >
> > > > Anything using container_of() like:
> > > >
> > > > static int process_sample_event(const struct perf_tool *tool,
> > > >                                 union perf_event *event,
> > > >                                 struct perf_sample *sample,
> > > >                                 struct evsel *evsel,
> > > >                                 struct machine *machine)
> > > > {
> > > >         struct perf_script *scr = container_of(tool, struct perf_script, tool);
> > > >
> > > > can then change scr->tool without even having to cast away const.
> > >
> > > Agreed, but such things happen in builtin_cmd where the tool is
> > > defined and presumably they know what they are doing. My objection is
> > > to code in util mutating the tool as I want the tool to have
> > > predictable behavior. As callers that take a tool can call fill in
> > > defaults (not all) then the tool has to be mutable and I don't want
> > > this to be the case.
> > >
> > > > Really, 'tool' needs to be defined as const in the first place.
> > >
> > > I'd like this. The problem is initializing all the function pointers
> > > and making such initialization robust to extra functions being added
> > > to the tool API. It can be done in a long winded way but I couldn't
> > > devise macro magic to do it. The other problem is around variables
> > > like ordered_events that can't currently be const. The patches move us
> > > closer to this being a possibility.
> > >
> > > > > My
> > > > > builtin_cmd is in charge of what the tool is rather than some code
> > > > > buried in util that thought it was going to do me a favor. The code is
> > > > > a refactor and so the benefit is intended to be for the developer and
> > > > > how they reason about the use of tool.
> > > >
> > > > It creates another question though: since there is a lot of code
> > > > before perf_tool__init() is called, does the caller mistakenly
> > > > change tool before calling perf_tool__init()
> > >
> > > If they do this their function pointers will be clobbered and their
> > > code won't behave as expected, which I'd expect to be easy to observe.
> > > In C++ if you were to initialize memory and then use the memory for a
> > > placement new to create an object which would call the constructor,
> > > the expected behavior would be that the initialized memory's values
> > > would get overridden. I see the use of _init and _exit in the code as
> > > being our poor man replacements of constructors and destructors.
> > >
> > > > > how they reason about the use of tool. We generally use _init
> > > > > functions rather than having _fill_defaults, so there is a consistency
> > > > > argument.
> > > >
> > > > The caller does not need the "defaults", so why would it set them up.
> > > > The session could just as easily do:
> > > >
> > > >         if (tool->cb)
> > > >                 tool->cb(...);
> > > >         else
> > > >                 cb_stub(...);
> > >
> > > Multiplied by every stub, we'd probably need a helper function, how to
> > > handle argument passing. There's nothing wrong with this as an idea
> > > but I think of this code as trying to create a visitor pattern and
> > > this is a visitor pattern with a hard time for the caller.
> > >
> > > > > I don't expect any impact in terms of performance... Moving
> > > > > perf_tool__fill_defaults to __perf_session__new had issues with the
> > > > > existing code where NULL would be written over a function pointer
> > > > > expecting the later fill_defaults to fix it up, doesn't address coding
> > > > > consistency where _init is the norm, and adds another reason the tool
> > > > > passed to session__new can't be const.
> > > >
> > > > perf_tool__init() is not a steeping stone to making 'tool' a
> > > > const in the first place.
> > >
> > > It is because the patch series gets rid of fill in defaults which is
> > > why we have a mutable tool passed around. I don't think this is up for
> > > debate as the patch series clearly goes from a non-const
> > > tool to a const tool at the end. Changing perf_tool__init to make all
> > > the function pointers NULL and then making every caller have to do a:
> > >
> > >          if (tool->cb)
> > >                  tool->cb(...);
> > >          else
> > >                  cb_stub(...);
> > >
> > > I think it is a less elegant solution at the end, it is also a large
> > > and more invasive change. The various refactorings to make tool const,
> > > changing the aux use of tool, etc. wouldn't be impacted by such a
> > > change but I think it is out of scope for this patch series.
> >
> > I don't think it's a large and invasive change.  The tools are mostly
> > zero-initialized so we don't need to reset to NULL.  And tool->cb is
> > called mostly from two functions: machines__deliver_event() and
> > perf_session__process_user_event().  Can we change them to check
> > NULL and get rid of perf_tool__fill_defaults() to keep it const?
>
> As I said above, I don't think that is good style and is out of scope
> here. It clearly can be done as follow up, but I don't see how that
> fixes the style issue.

Just to be clear on what the style issue is. We already have code:
https://git.kernel.org/pub/scm/linux/kernel/git/perf/perf-tools-next.git/tree/tools/perf/builtin-record.c?h=perf-tools-next#n1461
```
if (rec->buildid_all && !rec->timestamp_boundary)
    rec->tool.sample = NULL;
```
that relies on the special behavior of NULL in a function pointer
being changed at dispatch time - a simple reading of that code would
be anyone calling the function pointer would get a segv. I'm trying to
make it so that NULL isn't magic in the context of tool and you can
simply look at the tool to understand what its behavior is, much as a
virtual method table would work if we could do proper object-oriented
development.

Thanks,
Ian

> Thanks,
> Ian
Adrian Hunter July 23, 2024, 9:38 a.m. UTC | #8
On 22/07/24 21:04, Ian Rogers wrote:
> On Mon, Jul 22, 2024 at 10:50 AM Ian Rogers <irogers@google.com> wrote:
>>
>> On Mon, Jul 22, 2024 at 10:45 AM Namhyung Kim <namhyung@kernel.org> wrote:
>>>
>>> Hi,
>>>
>>> On Mon, Jul 22, 2024 at 9:06 AM Ian Rogers <irogers@google.com> wrote:
>>>>
>>>> On Mon, Jul 22, 2024 at 1:29 AM Adrian Hunter <adrian.hunter@intel.com> wrote:
>>>>>
>>>>> On 19/07/24 19:26, Ian Rogers wrote:
>>>>>> On Fri, Jul 19, 2024 at 1:51 AM Adrian Hunter <adrian.hunter@intel.com> wrote:
>>>>>>>
>>>>>>> On 18/07/24 03:59, Ian Rogers wrote:
>>>>>>>> struct perf_tool provides a set of function pointers that are called
>>>>>>>> through when processing perf data. To make filling the pointers less
>>>>>>>> cumbersome, if they are NULL perf_tools__fill_defaults will add
>>>>>>>> default do nothing implementations.
>>>>>>>>
>>>>>>>> This change refactors struct perf_tool to have an init function that
>>>>>>>> provides the default implementation. The special use of NULL and
>>>>>>>> perf_tools__fill_defaults are removed. As a consequence the tool
>>>>>>>> pointers can then all be made const, which better reflects the
>>>>>>>> behavior a particular perf command would expect of the tool and to
>>>>>>>> some extent can reduce the cognitive load on someone working on a
>>>>>>>> command.
>>>>>>>>
>>>>>>>> v6: Rebase adding Adrian's reviewed-by/tested-by and Leo's tested-by.
>>>>>>>
>>>>>>> The tags were really meant only for patch 1, the email that was replied to.
>>>>>>>
>>>>>>> But now for patches 2 and 3:
>>>>>>>
>>>>>>> Reviewed-by: Adrian Hunter <adrian.hunter@intel.com>
>>>>>>
>>>>>> Sorry for that, you'd mentioned that pt and bts testing which is
>>>>>> impacted by more than just patch 1.
>>>>>>
>>>>>>> Looking at patches 4 to 25, they do not seem to offer any benefit.
>>>>>>>
>>>>>>> Instead of patch 26, presumably perf_tool__fill_defaults() could
>>>>>>> be moved to __perf_session__new(), which perhaps would allow
>>>>>>> patch 27 as it is.
>>>>>>
>>>>>> What I'm trying to do in the series is make it so that the tool isn't
>>>>>> mutated during its use by session. Ideally we'd be passing a const
>>>>>> tool to session_new, that's not possible because there's a hack to fix
>>>>>> ordered events and pipe mode in session__new:
>>>>>> https://git.kernel.org/pub/scm/linux/kernel/git/perf/perf-tools-next.git/tree/tools/perf/util/session.c?h=perf-tools-next#n275
>>>>>> Imo, it isn't great to pass a tool to session__new where you say you
>>>>>> want ordered events and then session just goes to change that for you.
>>>>>> Altering that behavior was beyond the scope of this clean up, so tool
>>>>>> is only const after session__new.
>>>>>
>>>>> Seems like a separate issue.  Since the session is created
>>>>> by __perf_session__new(), session->tool will always be a pointer
>>>>> to a const tool once there is:
>>>>>
>>>>> diff --git a/tools/perf/util/session.h b/tools/perf/util/session.h
>>>>> index 7f69baeae7fb..7c8dd6956330 100644
>>>>> --- a/tools/perf/util/session.h
>>>>> +++ b/tools/perf/util/session.h
>>>>> @@ -43,7 +43,7 @@ struct perf_session {
>>>>>         u64                     one_mmap_offset;
>>>>>         struct ordered_events   ordered_events;
>>>>>         struct perf_data        *data;
>>>>> -       struct perf_tool        *tool;
>>>>> +       const struct perf_tool  *tool;
>>>>>         u64                     bytes_transferred;
>>>>>         u64                     bytes_compressed;
>>>>>         struct zstd_data        zstd_data;
>>>>
>>>> That's the case after these changes, but not before.
>>>>
>>>>>>
>>>>>> The reason for doing this is to make it so that when I have a tool I
>>>>>> can reason that nobody is doing things to change it under my feet.
>>>>>
>>>>> It still can be changed by the caller of __perf_session__new(), since
>>>>> the tool itself is not const.
>>>>>
>>>>> Anything using container_of() like:
>>>>>
>>>>> static int process_sample_event(const struct perf_tool *tool,
>>>>>                                 union perf_event *event,
>>>>>                                 struct perf_sample *sample,
>>>>>                                 struct evsel *evsel,
>>>>>                                 struct machine *machine)
>>>>> {
>>>>>         struct perf_script *scr = container_of(tool, struct perf_script, tool);
>>>>>
>>>>> can then change scr->tool without even having to cast away const.
>>>>
>>>> Agreed, but such things happen in builtin_cmd where the tool is
>>>> defined and presumably they know what they are doing. My objection is
>>>> to code in util mutating the tool as I want the tool to have
>>>> predictable behavior. As callers that take a tool can call fill in
>>>> defaults (not all) then the tool has to be mutable and I don't want
>>>> this to be the case.
>>>>
>>>>> Really, 'tool' needs to be defined as const in the first place.
>>>>
>>>> I'd like this. The problem is initializing all the function pointers
>>>> and making such initialization robust to extra functions being added
>>>> to the tool API. It can be done in a long winded way but I couldn't
>>>> devise macro magic to do it. The other problem is around variables
>>>> like ordered_events that can't currently be const. The patches move us
>>>> closer to this being a possibility.
>>>>
>>>>>> My
>>>>>> builtin_cmd is in charge of what the tool is rather than some code
>>>>>> buried in util that thought it was going to do me a favor. The code is
>>>>>> a refactor and so the benefit is intended to be for the developer and
>>>>>> how they reason about the use of tool.
>>>>>
>>>>> It creates another question though: since there is a lot of code
>>>>> before perf_tool__init() is called, does the caller mistakenly
>>>>> change tool before calling perf_tool__init()
>>>>
>>>> If they do this their function pointers will be clobbered and their
>>>> code won't behave as expected, which I'd expect to be easy to observe.
>>>> In C++ if you were to initialize memory and then use the memory for a
>>>> placement new to create an object which would call the constructor,
>>>> the expected behavior would be that the initialized memory's values
>>>> would get overridden. I see the use of _init and _exit in the code as
>>>> being our poor man replacements of constructors and destructors.
>>>>
>>>>>> how they reason about the use of tool. We generally use _init
>>>>>> functions rather than having _fill_defaults, so there is a consistency
>>>>>> argument.
>>>>>
>>>>> The caller does not need the "defaults", so why would it set them up.
>>>>> The session could just as easily do:
>>>>>
>>>>>         if (tool->cb)
>>>>>                 tool->cb(...);
>>>>>         else
>>>>>                 cb_stub(...);
>>>>
>>>> Multiplied by every stub, we'd probably need a helper function, how to
>>>> handle argument passing. There's nothing wrong with this as an idea
>>>> but I think of this code as trying to create a visitor pattern and
>>>> this is a visitor pattern with a hard time for the caller.
>>>>
>>>>>> I don't expect any impact in terms of performance... Moving
>>>>>> perf_tool__fill_defaults to __perf_session__new had issues with the
>>>>>> existing code where NULL would be written over a function pointer
>>>>>> expecting the later fill_defaults to fix it up, doesn't address coding
>>>>>> consistency where _init is the norm, and adds another reason the tool
>>>>>> passed to session__new can't be const.
>>>>>
>>>>> perf_tool__init() is not a steeping stone to making 'tool' a
>>>>> const in the first place.
>>>>
>>>> It is because the patch series gets rid of fill in defaults which is
>>>> why we have a mutable tool passed around. I don't think this is up for
>>>> debate as the patch series clearly goes from a non-const
>>>> tool to a const tool at the end. Changing perf_tool__init to make all
>>>> the function pointers NULL and then making every caller have to do a:
>>>>
>>>>          if (tool->cb)
>>>>                  tool->cb(...);
>>>>          else
>>>>                  cb_stub(...);
>>>>
>>>> I think it is a less elegant solution at the end, it is also a large
>>>> and more invasive change. The various refactorings to make tool const,
>>>> changing the aux use of tool, etc. wouldn't be impacted by such a
>>>> change but I think it is out of scope for this patch series.
>>>
>>> I don't think it's a large and invasive change.  The tools are mostly
>>> zero-initialized so we don't need to reset to NULL.  And tool->cb is
>>> called mostly from two functions: machines__deliver_event() and
>>> perf_session__process_user_event().  Can we change them to check
>>> NULL and get rid of perf_tool__fill_defaults() to keep it const?
>>
>> As I said above, I don't think that is good style and is out of scope
>> here. It clearly can be done as follow up, but I don't see how that
>> fixes the style issue.
> 
> Just to be clear on what the style issue is. We already have code:
> https://git.kernel.org/pub/scm/linux/kernel/git/perf/perf-tools-next.git/tree/tools/perf/builtin-record.c?h=perf-tools-next#n1461
> ```
> if (rec->buildid_all && !rec->timestamp_boundary)
>     rec->tool.sample = NULL;
> ```
> that relies on the special behavior of NULL in a function pointer
> being changed at dispatch time - a simple reading of that code would
> be anyone calling the function pointer would get a segv. I'm trying to
> make it so that NULL isn't magic in the context of tool and you can
> simply look at the tool to understand what its behavior is, much as a
> virtual method table would work if we could do proper object-oriented
> development.

In C, NULL or zero is often used as a special value to mean
no-value.  Optional callbacks that are NULL is also not remarkable.
Ian Rogers July 23, 2024, 12:50 p.m. UTC | #9
On Tue, Jul 23, 2024 at 2:38 AM Adrian Hunter <adrian.hunter@intel.com> wrote:
>
> On 22/07/24 21:04, Ian Rogers wrote:
> > On Mon, Jul 22, 2024 at 10:50 AM Ian Rogers <irogers@google.com> wrote:
> >>
> >> On Mon, Jul 22, 2024 at 10:45 AM Namhyung Kim <namhyung@kernel.org> wrote:
> >>>
> >>> Hi,
> >>>
> >>> On Mon, Jul 22, 2024 at 9:06 AM Ian Rogers <irogers@google.com> wrote:
> >>>>
> >>>> On Mon, Jul 22, 2024 at 1:29 AM Adrian Hunter <adrian.hunter@intel.com> wrote:
> >>>>>
> >>>>> On 19/07/24 19:26, Ian Rogers wrote:
> >>>>>> On Fri, Jul 19, 2024 at 1:51 AM Adrian Hunter <adrian.hunter@intel.com> wrote:
> >>>>>>>
> >>>>>>> On 18/07/24 03:59, Ian Rogers wrote:
> >>>>>>>> struct perf_tool provides a set of function pointers that are called
> >>>>>>>> through when processing perf data. To make filling the pointers less
> >>>>>>>> cumbersome, if they are NULL perf_tools__fill_defaults will add
> >>>>>>>> default do nothing implementations.
> >>>>>>>>
> >>>>>>>> This change refactors struct perf_tool to have an init function that
> >>>>>>>> provides the default implementation. The special use of NULL and
> >>>>>>>> perf_tools__fill_defaults are removed. As a consequence the tool
> >>>>>>>> pointers can then all be made const, which better reflects the
> >>>>>>>> behavior a particular perf command would expect of the tool and to
> >>>>>>>> some extent can reduce the cognitive load on someone working on a
> >>>>>>>> command.
> >>>>>>>>
> >>>>>>>> v6: Rebase adding Adrian's reviewed-by/tested-by and Leo's tested-by.
> >>>>>>>
> >>>>>>> The tags were really meant only for patch 1, the email that was replied to.
> >>>>>>>
> >>>>>>> But now for patches 2 and 3:
> >>>>>>>
> >>>>>>> Reviewed-by: Adrian Hunter <adrian.hunter@intel.com>
> >>>>>>
> >>>>>> Sorry for that, you'd mentioned that pt and bts testing which is
> >>>>>> impacted by more than just patch 1.
> >>>>>>
> >>>>>>> Looking at patches 4 to 25, they do not seem to offer any benefit.
> >>>>>>>
> >>>>>>> Instead of patch 26, presumably perf_tool__fill_defaults() could
> >>>>>>> be moved to __perf_session__new(), which perhaps would allow
> >>>>>>> patch 27 as it is.
> >>>>>>
> >>>>>> What I'm trying to do in the series is make it so that the tool isn't
> >>>>>> mutated during its use by session. Ideally we'd be passing a const
> >>>>>> tool to session_new, that's not possible because there's a hack to fix
> >>>>>> ordered events and pipe mode in session__new:
> >>>>>> https://git.kernel.org/pub/scm/linux/kernel/git/perf/perf-tools-next.git/tree/tools/perf/util/session.c?h=perf-tools-next#n275
> >>>>>> Imo, it isn't great to pass a tool to session__new where you say you
> >>>>>> want ordered events and then session just goes to change that for you.
> >>>>>> Altering that behavior was beyond the scope of this clean up, so tool
> >>>>>> is only const after session__new.
> >>>>>
> >>>>> Seems like a separate issue.  Since the session is created
> >>>>> by __perf_session__new(), session->tool will always be a pointer
> >>>>> to a const tool once there is:
> >>>>>
> >>>>> diff --git a/tools/perf/util/session.h b/tools/perf/util/session.h
> >>>>> index 7f69baeae7fb..7c8dd6956330 100644
> >>>>> --- a/tools/perf/util/session.h
> >>>>> +++ b/tools/perf/util/session.h
> >>>>> @@ -43,7 +43,7 @@ struct perf_session {
> >>>>>         u64                     one_mmap_offset;
> >>>>>         struct ordered_events   ordered_events;
> >>>>>         struct perf_data        *data;
> >>>>> -       struct perf_tool        *tool;
> >>>>> +       const struct perf_tool  *tool;
> >>>>>         u64                     bytes_transferred;
> >>>>>         u64                     bytes_compressed;
> >>>>>         struct zstd_data        zstd_data;
> >>>>
> >>>> That's the case after these changes, but not before.
> >>>>
> >>>>>>
> >>>>>> The reason for doing this is to make it so that when I have a tool I
> >>>>>> can reason that nobody is doing things to change it under my feet.
> >>>>>
> >>>>> It still can be changed by the caller of __perf_session__new(), since
> >>>>> the tool itself is not const.
> >>>>>
> >>>>> Anything using container_of() like:
> >>>>>
> >>>>> static int process_sample_event(const struct perf_tool *tool,
> >>>>>                                 union perf_event *event,
> >>>>>                                 struct perf_sample *sample,
> >>>>>                                 struct evsel *evsel,
> >>>>>                                 struct machine *machine)
> >>>>> {
> >>>>>         struct perf_script *scr = container_of(tool, struct perf_script, tool);
> >>>>>
> >>>>> can then change scr->tool without even having to cast away const.
> >>>>
> >>>> Agreed, but such things happen in builtin_cmd where the tool is
> >>>> defined and presumably they know what they are doing. My objection is
> >>>> to code in util mutating the tool as I want the tool to have
> >>>> predictable behavior. As callers that take a tool can call fill in
> >>>> defaults (not all) then the tool has to be mutable and I don't want
> >>>> this to be the case.
> >>>>
> >>>>> Really, 'tool' needs to be defined as const in the first place.
> >>>>
> >>>> I'd like this. The problem is initializing all the function pointers
> >>>> and making such initialization robust to extra functions being added
> >>>> to the tool API. It can be done in a long winded way but I couldn't
> >>>> devise macro magic to do it. The other problem is around variables
> >>>> like ordered_events that can't currently be const. The patches move us
> >>>> closer to this being a possibility.
> >>>>
> >>>>>> My
> >>>>>> builtin_cmd is in charge of what the tool is rather than some code
> >>>>>> buried in util that thought it was going to do me a favor. The code is
> >>>>>> a refactor and so the benefit is intended to be for the developer and
> >>>>>> how they reason about the use of tool.
> >>>>>
> >>>>> It creates another question though: since there is a lot of code
> >>>>> before perf_tool__init() is called, does the caller mistakenly
> >>>>> change tool before calling perf_tool__init()
> >>>>
> >>>> If they do this their function pointers will be clobbered and their
> >>>> code won't behave as expected, which I'd expect to be easy to observe.
> >>>> In C++ if you were to initialize memory and then use the memory for a
> >>>> placement new to create an object which would call the constructor,
> >>>> the expected behavior would be that the initialized memory's values
> >>>> would get overridden. I see the use of _init and _exit in the code as
> >>>> being our poor man replacements of constructors and destructors.
> >>>>
> >>>>>> how they reason about the use of tool. We generally use _init
> >>>>>> functions rather than having _fill_defaults, so there is a consistency
> >>>>>> argument.
> >>>>>
> >>>>> The caller does not need the "defaults", so why would it set them up.
> >>>>> The session could just as easily do:
> >>>>>
> >>>>>         if (tool->cb)
> >>>>>                 tool->cb(...);
> >>>>>         else
> >>>>>                 cb_stub(...);
> >>>>
> >>>> Multiplied by every stub, we'd probably need a helper function, how to
> >>>> handle argument passing. There's nothing wrong with this as an idea
> >>>> but I think of this code as trying to create a visitor pattern and
> >>>> this is a visitor pattern with a hard time for the caller.
> >>>>
> >>>>>> I don't expect any impact in terms of performance... Moving
> >>>>>> perf_tool__fill_defaults to __perf_session__new had issues with the
> >>>>>> existing code where NULL would be written over a function pointer
> >>>>>> expecting the later fill_defaults to fix it up, doesn't address coding
> >>>>>> consistency where _init is the norm, and adds another reason the tool
> >>>>>> passed to session__new can't be const.
> >>>>>
> >>>>> perf_tool__init() is not a steeping stone to making 'tool' a
> >>>>> const in the first place.
> >>>>
> >>>> It is because the patch series gets rid of fill in defaults which is
> >>>> why we have a mutable tool passed around. I don't think this is up for
> >>>> debate as the patch series clearly goes from a non-const
> >>>> tool to a const tool at the end. Changing perf_tool__init to make all
> >>>> the function pointers NULL and then making every caller have to do a:
> >>>>
> >>>>          if (tool->cb)
> >>>>                  tool->cb(...);
> >>>>          else
> >>>>                  cb_stub(...);
> >>>>
> >>>> I think it is a less elegant solution at the end, it is also a large
> >>>> and more invasive change. The various refactorings to make tool const,
> >>>> changing the aux use of tool, etc. wouldn't be impacted by such a
> >>>> change but I think it is out of scope for this patch series.
> >>>
> >>> I don't think it's a large and invasive change.  The tools are mostly
> >>> zero-initialized so we don't need to reset to NULL.  And tool->cb is
> >>> called mostly from two functions: machines__deliver_event() and
> >>> perf_session__process_user_event().  Can we change them to check
> >>> NULL and get rid of perf_tool__fill_defaults() to keep it const?
> >>
> >> As I said above, I don't think that is good style and is out of scope
> >> here. It clearly can be done as follow up, but I don't see how that
> >> fixes the style issue.
> >
> > Just to be clear on what the style issue is. We already have code:
> > https://git.kernel.org/pub/scm/linux/kernel/git/perf/perf-tools-next.git/tree/tools/perf/builtin-record.c?h=perf-tools-next#n1461
> > ```
> > if (rec->buildid_all && !rec->timestamp_boundary)
> >     rec->tool.sample = NULL;
> > ```
> > that relies on the special behavior of NULL in a function pointer
> > being changed at dispatch time - a simple reading of that code would
> > be anyone calling the function pointer would get a segv. I'm trying to
> > make it so that NULL isn't magic in the context of tool and you can
> > simply look at the tool to understand what its behavior is, much as a
> > virtual method table would work if we could do proper object-oriented
> > development.
>
> In C, NULL or zero is often used as a special value to mean
> no-value.  Optional callbacks that are NULL is also not remarkable.

Sure NULL means optional and has since the dawn of time, I don't see
this adding anything to the conversation. What is remarkable here is
passing around a collection of function pointers, then the caller of
the function pointer is supposed to check whether what you want to be
an optional value (I don't, nor does the existing code) function
pointer is present, and if not present the caller needs to know to
call another special function. This is complicated enough that the
function calls could use helper macros, e.g.:

#define CALL_TOOL_SAMPLE(tool, event, sample, evsel, machine) \
  if (tool->sample) \
    tool->sample(tool, event, sample, machine); \
  else \
    process_event_sample_stub(tool, event, sample, machine);

you need about 38 of them. Now if I'm a buildtin_cmd writer wanting to
know what gets called on tool, I need to check, do all callers of the
tool use the macro? Perhaps the user decided to do their own NULL test
and call a different function. Cases like tool->finished_round already
need to go to one of two different stubs. There is clearly more
cognitive load on the tool API user as now they must check all uses of
38 function pointers across the code base to understand the behavior
of the tool, they can't just rely on the function pointer in the
struct being the place the code will go. A simple function pointer
call has imo become spaghetti.

There is also the overhead checking the optional value - I've never
seen a C programmer wanting to do this in 30 years of doing paid C
development, the use of the function pointer was to avoid checking,
etc. But maybe the kernel does this and it's an existing kernel style?
I can't find it.

Thanks,
Ian
Arnaldo Carvalho de Melo Aug. 12, 2024, 1:53 p.m. UTC | #10
On Fri, Jul 19, 2024 at 09:26:57AM -0700, Ian Rogers wrote:
> On Fri, Jul 19, 2024 at 1:51 AM Adrian Hunter <adrian.hunter@intel.com> wrote:
> >
> > On 18/07/24 03:59, Ian Rogers wrote:
> > > struct perf_tool provides a set of function pointers that are called
> > > through when processing perf data. To make filling the pointers less
> > > cumbersome, if they are NULL perf_tools__fill_defaults will add
> > > default do nothing implementations.
> > >
> > > This change refactors struct perf_tool to have an init function that
> > > provides the default implementation. The special use of NULL and
> > > perf_tools__fill_defaults are removed. As a consequence the tool
> > > pointers can then all be made const, which better reflects the
> > > behavior a particular perf command would expect of the tool and to
> > > some extent can reduce the cognitive load on someone working on a
> > > command.
> > >
> > > v6: Rebase adding Adrian's reviewed-by/tested-by and Leo's tested-by.
> >
> > The tags were really meant only for patch 1, the email that was replied to.
> >
> > But now for patches 2 and 3:
> >
> > Reviewed-by: Adrian Hunter <adrian.hunter@intel.com>

Applied 1-3, 4 is not applying, I'll look at it later.

- Arnaldo

> 
> Sorry for that, you'd mentioned that pt and bts testing which is
> impacted by more than just patch 1.
> 
> > Looking at patches 4 to 25, they do not seem to offer any benefit.
> >
> > Instead of patch 26, presumably perf_tool__fill_defaults() could
> > be moved to __perf_session__new(), which perhaps would allow
> > patch 27 as it is.
> 
> What I'm trying to do in the series is make it so that the tool isn't
> mutated during its use by session. Ideally we'd be passing a const
> tool to session_new, that's not possible because there's a hack to fix
> ordered events and pipe mode in session__new:
> https://git.kernel.org/pub/scm/linux/kernel/git/perf/perf-tools-next.git/tree/tools/perf/util/session.c?h=perf-tools-next#n275
> Imo, it isn't great to pass a tool to session__new where you say you
> want ordered events and then session just goes to change that for you.
> Altering that behavior was beyond the scope of this clean up, so tool
> is only const after session__new.
> 
> The reason for doing this is to make it so that when I have a tool I
> can reason that nobody is doing things to change it under my feet. My
> builtin_cmd is in charge of what the tool is rather than some code
> buried in util that thought it was going to do me a favor. The code is
> a refactor and so the benefit is intended to be for the developer and
> how they reason about the use of tool. We generally use _init
> functions rather than having _fill_defaults, so there is a consistency
> argument. I don't expect any impact in terms of performance... Moving
> perf_tool__fill_defaults to __perf_session__new had issues with the
> existing code where NULL would be written over a function pointer
> expecting the later fill_defaults to fix it up, doesn't address coding
> consistency where _init is the norm, and adds another reason the tool
> passed to session__new can't be const.
> 
> Thanks,
> Ian
> 
> > > v5: Rebase dropping asan fix merged by Namhyung.
> > > v4: Simplify perf_session__deliver_synth_attr_event following Adrian's
> > >     suggestions.
> > > v3: Just remove auxtrace dummy tools [Adrian] and make s390-cpumsf
> > >     struct removal its own patch [Adrian].
> > > v2: Remove dummy tool initialization [Adrian] and make zero sized. Add
> > >     cs-etm fix for address sanitizer build, found necessary when
> > >     testing dummy tool change.
> > >
> > > Ian Rogers (27):
> > >   perf auxtrace: Remove dummy tools
> > >   perf s390-cpumsf: Remove unused struct
> > >   perf tool: Constify tool pointers
> > >   perf tool: Move fill defaults into tool.c
> > >   perf tool: Add perf_tool__init
> > >   perf kmem: Use perf_tool__init
> > >   perf buildid-list: Use perf_tool__init
> > >   perf kvm: Use perf_tool__init
> > >   perf lock: Use perf_tool__init
> > >   perf evlist: Use perf_tool__init
> > >   perf record: Use perf_tool__init
> > >   perf c2c: Use perf_tool__init
> > >   perf script: Use perf_tool__init
> > >   perf inject: Use perf_tool__init
> > >   perf report: Use perf_tool__init
> > >   perf stat: Use perf_tool__init
> > >   perf annotate: Use perf_tool__init
> > >   perf sched: Use perf_tool__init
> > >   perf mem: Use perf_tool__init
> > >   perf timechart: Use perf_tool__init
> > >   perf diff: Use perf_tool__init
> > >   perf data convert json: Use perf_tool__init
> > >   perf data convert ctf: Use perf_tool__init
> > >   perf test event_update: Ensure tools is initialized
> > >   perf kwork: Use perf_tool__init
> > >   perf tool: Remove perf_tool__fill_defaults
> > >   perf session: Constify tool
> > >
> > >  tools/perf/arch/x86/util/event.c    |   4 +-
> > >  tools/perf/bench/synthesize.c       |   2 +-
> > >  tools/perf/builtin-annotate.c       |  44 ++--
> > >  tools/perf/builtin-buildid-list.c   |  10 +
> > >  tools/perf/builtin-c2c.c            |  33 ++-
> > >  tools/perf/builtin-diff.c           |  30 ++-
> > >  tools/perf/builtin-evlist.c         |  10 +-
> > >  tools/perf/builtin-inject.c         | 159 ++++++------
> > >  tools/perf/builtin-kmem.c           |  20 +-
> > >  tools/perf/builtin-kvm.c            |  19 +-
> > >  tools/perf/builtin-kwork.c          |  33 ++-
> > >  tools/perf/builtin-lock.c           |  41 ++--
> > >  tools/perf/builtin-mem.c            |  37 +--
> > >  tools/perf/builtin-record.c         |  47 ++--
> > >  tools/perf/builtin-report.c         |  67 +++--
> > >  tools/perf/builtin-sched.c          |  50 ++--
> > >  tools/perf/builtin-script.c         | 106 ++++----
> > >  tools/perf/builtin-stat.c           |  26 +-
> > >  tools/perf/builtin-timechart.c      |  25 +-
> > >  tools/perf/builtin-top.c            |   2 +-
> > >  tools/perf/builtin-trace.c          |   4 +-
> > >  tools/perf/tests/cpumap.c           |   6 +-
> > >  tools/perf/tests/dlfilter-test.c    |   2 +-
> > >  tools/perf/tests/dwarf-unwind.c     |   2 +-
> > >  tools/perf/tests/event_update.c     |   9 +-
> > >  tools/perf/tests/stat.c             |   6 +-
> > >  tools/perf/tests/thread-map.c       |   2 +-
> > >  tools/perf/util/Build               |   1 +
> > >  tools/perf/util/arm-spe.c           |  55 +----
> > >  tools/perf/util/auxtrace.c          |  12 +-
> > >  tools/perf/util/auxtrace.h          |  20 +-
> > >  tools/perf/util/bpf-event.c         |   4 +-
> > >  tools/perf/util/build-id.c          |  34 +--
> > >  tools/perf/util/build-id.h          |   8 +-
> > >  tools/perf/util/cs-etm.c            |  39 +--
> > >  tools/perf/util/data-convert-bt.c   |  34 ++-
> > >  tools/perf/util/data-convert-json.c |  47 ++--
> > >  tools/perf/util/event.c             |  54 ++--
> > >  tools/perf/util/event.h             |  38 +--
> > >  tools/perf/util/header.c            |   6 +-
> > >  tools/perf/util/header.h            |   4 +-
> > >  tools/perf/util/hisi-ptt.c          |   6 +-
> > >  tools/perf/util/intel-bts.c         |  37 +--
> > >  tools/perf/util/intel-pt.c          |  30 +--
> > >  tools/perf/util/jitdump.c           |   4 +-
> > >  tools/perf/util/s390-cpumsf.c       |  11 +-
> > >  tools/perf/util/session.c           | 366 +++-------------------------
> > >  tools/perf/util/session.h           |   9 +-
> > >  tools/perf/util/synthetic-events.c  |  80 +++---
> > >  tools/perf/util/synthetic-events.h  |  70 +++---
> > >  tools/perf/util/tool.c              | 294 ++++++++++++++++++++++
> > >  tools/perf/util/tool.h              |  18 +-
> > >  tools/perf/util/tsc.c               |   2 +-
> > >  53 files changed, 977 insertions(+), 1102 deletions(-)
> > >  create mode 100644 tools/perf/util/tool.c
> > >
> >
Ian Rogers Aug. 12, 2024, 6:10 p.m. UTC | #11
On Mon, Aug 12, 2024 at 6:53 AM Arnaldo Carvalho de Melo
<acme@kernel.org> wrote:
>
> On Fri, Jul 19, 2024 at 09:26:57AM -0700, Ian Rogers wrote:
> > On Fri, Jul 19, 2024 at 1:51 AM Adrian Hunter <adrian.hunter@intel.com> wrote:
> > >
> > > On 18/07/24 03:59, Ian Rogers wrote:
> > > > struct perf_tool provides a set of function pointers that are called
> > > > through when processing perf data. To make filling the pointers less
> > > > cumbersome, if they are NULL perf_tools__fill_defaults will add
> > > > default do nothing implementations.
> > > >
> > > > This change refactors struct perf_tool to have an init function that
> > > > provides the default implementation. The special use of NULL and
> > > > perf_tools__fill_defaults are removed. As a consequence the tool
> > > > pointers can then all be made const, which better reflects the
> > > > behavior a particular perf command would expect of the tool and to
> > > > some extent can reduce the cognitive load on someone working on a
> > > > command.
> > > >
> > > > v6: Rebase adding Adrian's reviewed-by/tested-by and Leo's tested-by.
> > >
> > > The tags were really meant only for patch 1, the email that was replied to.
> > >
> > > But now for patches 2 and 3:
> > >
> > > Reviewed-by: Adrian Hunter <adrian.hunter@intel.com>
>
> Applied 1-3, 4 is not applying, I'll look at it later.

I have a rebase and can resend. I haven't addressed Adrian's feedback
as I prefer callers of the tool function pointers not to have to do
NULL tests, I'm trying to minimize spaghetti. Not sure if you're
applying the whole series here or just the beginning.

Thanks,
Ian
Arnaldo Carvalho de Melo Aug. 12, 2024, 7:54 p.m. UTC | #12
On Mon, Aug 12, 2024 at 11:10:02AM -0700, Ian Rogers wrote:
> On Mon, Aug 12, 2024 at 6:53 AM Arnaldo Carvalho de Melo
> <acme@kernel.org> wrote:
> >
> > On Fri, Jul 19, 2024 at 09:26:57AM -0700, Ian Rogers wrote:
> > > On Fri, Jul 19, 2024 at 1:51 AM Adrian Hunter <adrian.hunter@intel.com> wrote:
> > > >
> > > > On 18/07/24 03:59, Ian Rogers wrote:
> > > > > struct perf_tool provides a set of function pointers that are called
> > > > > through when processing perf data. To make filling the pointers less
> > > > > cumbersome, if they are NULL perf_tools__fill_defaults will add
> > > > > default do nothing implementations.
> > > > >
> > > > > This change refactors struct perf_tool to have an init function that
> > > > > provides the default implementation. The special use of NULL and
> > > > > perf_tools__fill_defaults are removed. As a consequence the tool
> > > > > pointers can then all be made const, which better reflects the
> > > > > behavior a particular perf command would expect of the tool and to
> > > > > some extent can reduce the cognitive load on someone working on a
> > > > > command.
> > > > >
> > > > > v6: Rebase adding Adrian's reviewed-by/tested-by and Leo's tested-by.
> > > >
> > > > The tags were really meant only for patch 1, the email that was replied to.
> > > >
> > > > But now for patches 2 and 3:
> > > >
> > > > Reviewed-by: Adrian Hunter <adrian.hunter@intel.com>
> >
> > Applied 1-3, 4 is not applying, I'll look at it later.
> 
> I have a rebase and can resend. I haven't addressed Adrian's feedback

Please resend then.

- Arnaldo

> as I prefer callers of the tool function pointers not to have to do
> NULL tests, I'm trying to minimize spaghetti. Not sure if you're
> applying the whole series here or just the beginning.
> 
> Thanks,
> Ian