Repository: go
Issue: proposal: runtime/trace: add Helper() to hide helpers from callers stack for region API
Labels: Proposal

Add an `Helper()` function to allow clients of the [`runtime/trace`](https://pkg.go.dev/runtime/trace@master) region API to create helpers but hide them from the stack traces.
The API would be similar to [`testing.Helper()`](https://pkg.go.dev/testing@master#Helper).

## Use case

The region API ([`trace.WithRegion`](https://pkg.go.dev/runtime/trace@master#WithRegion), [`trace.StartRegion`](https://pkg.go.dev/runtime/trace@master#StartRegion)) is quite intrusive: to enhance existing code with regions, one must wrap the existing block in a `func()`. It is even more verbose if the existing block returned values to use after the block (ex: error).

To limit the verbosity, I used generics to write wrappers around the [`trace.StartRegion`](https://pkg.go.dev/runtime/trace@master#StartRegion) API (see below).

Unfortunately, with Go 1.20 the `go tool trace` output shows the helper function as the encapsulated code instead of the real user's code. This makes the tracing annotations much less useful.

It would be helpful to have a way to declare a caller of the [`trace.StartRegion`](https://pkg.go.dev/runtime/trace@master#StartRegion) as an helper. The `testing` package has [`Helper()`](https://pkg.go.dev/testing@master#Helper) which would be a good inspiration (but a different API might be needed as `runtime/trace` performance is critical). 

## Example helpers

Example wrappers in user code that would be declared as helpers:
```go
// traceWithRegionErr wraps [trace.WithRegion] to ease handling of failure cases.
func traceWithRegionErr(ctx context.Context, regionType string, fn func() error) error {
	defer trace.StartRegion(ctx, regionType).End()
	err := fn()
	if err != nil {
		return fmt.Errorf("%s: %w", regionType, err)
	}
	return nil
}

// traceWithRegion1Err1 wraps [trace.WithRegion] to ease defining a region for a function
// with one input parameter and returning a value and an error.
func traceWithRegion1Err1[I1 any, O1 any](ctx context.Context, regionType string, fn func(I1) (O1, error)) func(I1) (O1, error) {
	return func(i1 I1) (O1, error) {
		defer trace.StartRegion(ctx, regionType).End()
		o1, err := fn(i1)
		if err != nil {
			return o1, fmt.Errorf("%s: %w", regionType, err)
		}
		return o1, nil
	}
}

// traceWithRegionCtx1Err1 wraps [trace.WithRegion] to ease defining a region for a function
// with a context, another input parameter and returning a value and an error.
func traceWithRegionCtx1Err1[I1 any, O1 any](ctx context.Context, regionType string, fn func(context.Context, I1) (O1, error)) func(I1) (O1, error) {
	return func(i1 I1) (O1, error) {
		defer trace.StartRegion(ctx, regionType).End()
		o1, err := fn(ctx, i1)
		if err != nil {
			return o1, fmt.Errorf("%s: %w", regionType, err)
		}
		return o1, nil
	}
}

// traceWithRegionCtx31 wraps [trace.WithRegion] to ease defining a region for a function
// with a context, 3 other input parameters and returning one value.
func traceWithRegionCtx31[I1 any, I2 any, I3 any, O1 any](ctx context.Context, regionType string, fn func(context.Context, I1, I2, I3) O1) func(I1, I2, I3) O1 {
	return func(i1 I1, i2 I2, i3 I3) O1 {
		defer trace.StartRegion(ctx, regionType).End()
		return fn(ctx, i1, i2, i3)
	}
}
```