[Development report #5] Improve the kinst DTrace provider

From: Christos Margiolis <christos_at_freebsd.org>
Date: Sat, 18 Mar 2023 20:37:36 UTC
I changed the implementation of inline function tracing in dt_sugar to
append new probes to the main clause list rather than create separate
probes for each one of the inline copies found.

Before:
	# dtrace -dn 'kinst::cam_iosched_has_more_trim:entry'
	dtrace:::ERROR
	{
		((self->%error) = 0x1);
	}

	kinst::cam_iosched_get_trim:13
	{
	}

	kinst::cam_iosched_next_bio:13
	{
	}

	kinst::cam_iosched_schedule:40
	{
	}

After:

	# dtrace -dn 'kinst::cam_iosched_has_more_trim:entry'
	kinst::cam_iosched_get_trim:13,
	kinst::cam_iosched_next_bio:13,
	kinst::cam_iosched_schedule:40
	{
	}


This turned out to be way simpler and cleaner than the original, broken,
mechanism, which was to get a deep copy of the main clause list and copy
its predicates and actions to the new clauses.

In my last report I mentioned that for return probes, the return offset
given by DWARF corresponds to the instruction after the return one, and
so I manually went one instruction back to get the correct offset. I
realized that doing this unconditionally didn't exactly work as
expected, but not doing this didn't work either, because sometimes we
end up outside the inline copy's and/or the caller function's bounds.
To fix this, we go back one instruction if one of the following
conditions is met:

- The address returned by DWARF corresponds to the last instruction of
  the caller function, which in this case (see previous report) is
  outside the caller function altogether.
- If the inline low/high boundaries are given by DW_AT_ranges, we go
  back one instruction only at the last range.
- If the boundaries are given by DW_AT_lowpc/DW_AT_highpc, we always go
  back one instruction, otherwise we'll end up outside the inline
  function's bounds.

kinst up until now was ignoring any function whose first instruction
wasn't `push %rbp`, but doing this means we couldn't trace functions
that just happen to `push %rbp` after the first instruction. I modified
kinst to instead look if there's a `push %rbp` anywhere in the function.
This however still isn't quite complete because kinst did and still does
ignore functions that do not `push %rbp` at all, which in some cases is
correct, because such functions usually correspond to exception
handlers, but there are functions such as cpu_switch(), or leaf
functions with their frame pointer omitted (if compiled without
`-mno-omit-leaf-frame-pointer`) which are safe to trace, but kinst
cannot.

Christos