You've already forked opentelemetry-go
mirror of
https://github.com/open-telemetry/opentelemetry-go.git
synced 2025-07-17 01:12:45 +02:00
Add comments and test for 64-bit field alignment (#418)
* Add comments on needed filed alignment Add comment about alignment requirements to all struct fields who's values are passed to 64-bit atomic operations. Update any struct's field ordering if one or more of those fields has alignment requirements to support 64-bit atomic operations. * Add 64-bit alignment tests Most `struct` that have field alignment requirements are now statically validated prior to testing. The only `struct`s not validated that have these requirements are ones defined in tests themselves where multiple `TestMain` functions would be needed to test them. Given the fields are already identified with comments specifying the alignment requirements and they are in the test themselves, this seems like an OK omission. Co-authored-by: Liz Fong-Jones <elizabeth@ctyalcove.org>
This commit is contained in:
committed by
Liz Fong-Jones
parent
aefc49cfe6
commit
91091b44eb
@ -67,12 +67,14 @@ func TestNumber(t *testing.T) {
|
|||||||
cmpsForPos := [3]int{1, 1, 0}
|
cmpsForPos := [3]int{1, 1, 0}
|
||||||
|
|
||||||
type testcase struct {
|
type testcase struct {
|
||||||
n Number
|
// n needs to be aligned for 64-bit atomic operations.
|
||||||
|
n Number
|
||||||
|
// nums needs to be aligned for 64-bit atomic operations.
|
||||||
|
nums [3]Number
|
||||||
kind NumberKind
|
kind NumberKind
|
||||||
pos bool
|
pos bool
|
||||||
zero bool
|
zero bool
|
||||||
neg bool
|
neg bool
|
||||||
nums [3]Number
|
|
||||||
cmps [3]int
|
cmps [3]int
|
||||||
}
|
}
|
||||||
testcases := []testcase{
|
testcases := []testcase{
|
||||||
|
24
api/metric/alignment_test.go
Normal file
24
api/metric/alignment_test.go
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
package metric
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
"testing"
|
||||||
|
"unsafe"
|
||||||
|
|
||||||
|
ottest "go.opentelemetry.io/otel/internal/testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Ensure struct alignment prior to running tests.
|
||||||
|
func TestMain(m *testing.M) {
|
||||||
|
fields := []ottest.FieldOffset{
|
||||||
|
{
|
||||||
|
Name: "Measurement.number",
|
||||||
|
Offset: unsafe.Offsetof(Measurement{}.number),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
if !ottest.Aligned8Byte(fields, os.Stderr) {
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
os.Exit(m.Run())
|
||||||
|
}
|
@ -85,8 +85,9 @@ type MeasureOptionApplier interface {
|
|||||||
// values. Instances of this type should be created by instruments
|
// values. Instances of this type should be created by instruments
|
||||||
// (e.g., Int64Counter.Measurement()).
|
// (e.g., Int64Counter.Measurement()).
|
||||||
type Measurement struct {
|
type Measurement struct {
|
||||||
instrument InstrumentImpl
|
// number needs to be aligned for 64-bit atomic operations.
|
||||||
number core.Number
|
number core.Number
|
||||||
|
instrument InstrumentImpl
|
||||||
}
|
}
|
||||||
|
|
||||||
// Instrument returns the instrument that created this measurement.
|
// Instrument returns the instrument that created this measurement.
|
||||||
|
@ -41,9 +41,10 @@ type (
|
|||||||
}
|
}
|
||||||
|
|
||||||
Batch struct {
|
Batch struct {
|
||||||
|
// Measurement needs to be aligned for 64-bit atomic operations.
|
||||||
|
Measurements []Measurement
|
||||||
Ctx context.Context
|
Ctx context.Context
|
||||||
LabelSet *LabelSet
|
LabelSet *LabelSet
|
||||||
Measurements []Measurement
|
|
||||||
}
|
}
|
||||||
|
|
||||||
MeterProvider struct {
|
MeterProvider struct {
|
||||||
@ -58,8 +59,9 @@ type (
|
|||||||
Kind int8
|
Kind int8
|
||||||
|
|
||||||
Measurement struct {
|
Measurement struct {
|
||||||
Instrument *Instrument
|
// Number needs to be aligned for 64-bit atomic operations.
|
||||||
Number core.Number
|
Number core.Number
|
||||||
|
Instrument *Instrument
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
28
internal/metric/mock_test.go
Normal file
28
internal/metric/mock_test.go
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
package metric
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
"testing"
|
||||||
|
"unsafe"
|
||||||
|
|
||||||
|
ottest "go.opentelemetry.io/otel/internal/testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Ensure struct alignment prior to running tests.
|
||||||
|
func TestMain(m *testing.M) {
|
||||||
|
fields := []ottest.FieldOffset{
|
||||||
|
{
|
||||||
|
Name: "Batch.Measurments",
|
||||||
|
Offset: unsafe.Offsetof(Batch{}.Measurements),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "Measurement.Number",
|
||||||
|
Offset: unsafe.Offsetof(Measurement{}.Number),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
if !ottest.Aligned8Byte(fields, os.Stderr) {
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
os.Exit(m.Run())
|
||||||
|
}
|
57
internal/testing/alignment.go
Normal file
57
internal/testing/alignment.go
Normal file
@ -0,0 +1,57 @@
|
|||||||
|
package testing
|
||||||
|
|
||||||
|
/*
|
||||||
|
This file contains common utilities and objects to validate memory alignment
|
||||||
|
of Go types. The primary use of this functionality is intended to ensure
|
||||||
|
`struct` fields that need to be 64-bit aligned so they can be passed as
|
||||||
|
arguments to 64-bit atomic operations.
|
||||||
|
|
||||||
|
The common workflow is to define a slice of `FieldOffset` and pass them to the
|
||||||
|
`Aligned8Byte` function from within a `TestMain` function from a package's
|
||||||
|
tests. It is important to make this call from the `TestMain` function prior
|
||||||
|
to running the rest of the test suit as it can provide useful diagnostics
|
||||||
|
about field alignment instead of ambiguous nil pointer dereference and runtime
|
||||||
|
panic.
|
||||||
|
|
||||||
|
For more information:
|
||||||
|
https://github.com/open-telemetry/opentelemetry-go/issues/341
|
||||||
|
*/
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
)
|
||||||
|
|
||||||
|
// FieldOffset is a preprocessor representation of a struct field alignment.
|
||||||
|
type FieldOffset struct {
|
||||||
|
// Name of the field.
|
||||||
|
Name string
|
||||||
|
|
||||||
|
// Offset of the field in bytes.
|
||||||
|
//
|
||||||
|
// To compute this at compile time use unsafe.Offsetof.
|
||||||
|
Offset uintptr
|
||||||
|
}
|
||||||
|
|
||||||
|
// Aligned8Byte returns if all fields are aligned modulo 8-bytes.
|
||||||
|
//
|
||||||
|
// Error messaging is printed to out for any fileds determined misaligned.
|
||||||
|
func Aligned8Byte(fields []FieldOffset, out io.Writer) bool {
|
||||||
|
misaligned := make([]FieldOffset, 0)
|
||||||
|
for _, f := range fields {
|
||||||
|
if f.Offset%8 != 0 {
|
||||||
|
misaligned = append(misaligned, f)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(misaligned) == 0 {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Fprintln(out, "struct fields not aligned for 64-bit atomic operations:")
|
||||||
|
for _, f := range misaligned {
|
||||||
|
fmt.Fprintf(out, " %s: %d-byte offset\n", f.Name, f.Offset)
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
@ -28,12 +28,14 @@ import (
|
|||||||
// It only supports ChildOf option. SpanId is atomically increased every time a
|
// It only supports ChildOf option. SpanId is atomically increased every time a
|
||||||
// new span is created.
|
// new span is created.
|
||||||
type MockTracer struct {
|
type MockTracer struct {
|
||||||
// Sampled specifies if the new span should be sampled or not.
|
|
||||||
Sampled bool
|
|
||||||
|
|
||||||
// StartSpanID is used to initialize spanId. It is incremented by one
|
// StartSpanID is used to initialize spanId. It is incremented by one
|
||||||
// every time a new span is created.
|
// every time a new span is created.
|
||||||
|
//
|
||||||
|
// StartSpanID has to be aligned for 64-bit atomic operations.
|
||||||
StartSpanID *uint64
|
StartSpanID *uint64
|
||||||
|
|
||||||
|
// Sampled specifies if the new span should be sampled or not.
|
||||||
|
Sampled bool
|
||||||
}
|
}
|
||||||
|
|
||||||
var _ apitrace.Tracer = (*MockTracer)(nil)
|
var _ apitrace.Tracer = (*MockTracer)(nil)
|
||||||
|
24
internal/trace/mock_tracer_test.go
Normal file
24
internal/trace/mock_tracer_test.go
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
package trace
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
"testing"
|
||||||
|
"unsafe"
|
||||||
|
|
||||||
|
ottest "go.opentelemetry.io/otel/internal/testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Ensure struct alignment prior to running tests.
|
||||||
|
func TestMain(m *testing.M) {
|
||||||
|
fields := []ottest.FieldOffset{
|
||||||
|
{
|
||||||
|
Name: "MockTracer.StartSpanID",
|
||||||
|
Offset: unsafe.Offsetof(MockTracer{}.StartSpanID),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
if !ottest.Aligned8Byte(fields, os.Stderr) {
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
os.Exit(m.Run())
|
||||||
|
}
|
@ -28,10 +28,11 @@ import (
|
|||||||
|
|
||||||
type (
|
type (
|
||||||
Aggregator struct {
|
Aggregator struct {
|
||||||
|
// ckptSum needs to be aligned for 64-bit atomic operations.
|
||||||
|
ckptSum core.Number
|
||||||
lock sync.Mutex
|
lock sync.Mutex
|
||||||
current points
|
current points
|
||||||
checkpoint points
|
checkpoint points
|
||||||
ckptSum core.Number
|
|
||||||
}
|
}
|
||||||
|
|
||||||
points []core.Number
|
points []core.Number
|
||||||
|
@ -18,16 +18,34 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
"math"
|
"math"
|
||||||
|
"os"
|
||||||
"testing"
|
"testing"
|
||||||
|
"unsafe"
|
||||||
|
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
|
|
||||||
"go.opentelemetry.io/otel/api/core"
|
"go.opentelemetry.io/otel/api/core"
|
||||||
|
ottest "go.opentelemetry.io/otel/internal/testing"
|
||||||
export "go.opentelemetry.io/otel/sdk/export/metric"
|
export "go.opentelemetry.io/otel/sdk/export/metric"
|
||||||
"go.opentelemetry.io/otel/sdk/export/metric/aggregator"
|
"go.opentelemetry.io/otel/sdk/export/metric/aggregator"
|
||||||
"go.opentelemetry.io/otel/sdk/metric/aggregator/test"
|
"go.opentelemetry.io/otel/sdk/metric/aggregator/test"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// Ensure struct alignment prior to running tests.
|
||||||
|
func TestMain(m *testing.M) {
|
||||||
|
fields := []ottest.FieldOffset{
|
||||||
|
{
|
||||||
|
Name: "Aggregator.ckptSum",
|
||||||
|
Offset: unsafe.Offsetof(Aggregator{}.ckptSum),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
if !ottest.Aligned8Byte(fields, os.Stderr) {
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
os.Exit(m.Run())
|
||||||
|
}
|
||||||
|
|
||||||
type updateTest struct {
|
type updateTest struct {
|
||||||
count int
|
count int
|
||||||
absolute bool
|
absolute bool
|
||||||
|
@ -25,9 +25,11 @@ import (
|
|||||||
// Aggregator aggregates counter events.
|
// Aggregator aggregates counter events.
|
||||||
type Aggregator struct {
|
type Aggregator struct {
|
||||||
// current holds current increments to this counter record
|
// current holds current increments to this counter record
|
||||||
|
// current needs to be aligned for 64-bit atomic operations.
|
||||||
current core.Number
|
current core.Number
|
||||||
|
|
||||||
// checkpoint is a temporary used during Checkpoint()
|
// checkpoint is a temporary used during Checkpoint()
|
||||||
|
// checkpoint needs to be aligned for 64-bit atomic operations.
|
||||||
checkpoint core.Number
|
checkpoint core.Number
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -16,17 +16,39 @@ package counter
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"os"
|
||||||
"testing"
|
"testing"
|
||||||
|
"unsafe"
|
||||||
|
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
|
|
||||||
"go.opentelemetry.io/otel/api/core"
|
"go.opentelemetry.io/otel/api/core"
|
||||||
|
ottest "go.opentelemetry.io/otel/internal/testing"
|
||||||
export "go.opentelemetry.io/otel/sdk/export/metric"
|
export "go.opentelemetry.io/otel/sdk/export/metric"
|
||||||
"go.opentelemetry.io/otel/sdk/metric/aggregator/test"
|
"go.opentelemetry.io/otel/sdk/metric/aggregator/test"
|
||||||
)
|
)
|
||||||
|
|
||||||
const count = 100
|
const count = 100
|
||||||
|
|
||||||
|
// Ensure struct alignment prior to running tests.
|
||||||
|
func TestMain(m *testing.M) {
|
||||||
|
fields := []ottest.FieldOffset{
|
||||||
|
{
|
||||||
|
Name: "Aggregator.current",
|
||||||
|
Offset: unsafe.Offsetof(Aggregator{}.current),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "Aggregator.checkpoint",
|
||||||
|
Offset: unsafe.Offsetof(Aggregator{}.checkpoint),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
if !ottest.Aligned8Byte(fields, os.Stderr) {
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
os.Exit(m.Run())
|
||||||
|
}
|
||||||
|
|
||||||
func TestCounterMonotonic(t *testing.T) {
|
func TestCounterMonotonic(t *testing.T) {
|
||||||
ctx := context.Background()
|
ctx := context.Background()
|
||||||
|
|
||||||
|
@ -45,6 +45,8 @@ type (
|
|||||||
// a sequence number to determine the winner of a race.
|
// a sequence number to determine the winner of a race.
|
||||||
gaugeData struct {
|
gaugeData struct {
|
||||||
// value is the int64- or float64-encoded Set() data
|
// value is the int64- or float64-encoded Set() data
|
||||||
|
//
|
||||||
|
// value needs to be aligned for 64-bit atomic operations.
|
||||||
value core.Number
|
value core.Number
|
||||||
|
|
||||||
// timestamp indicates when this record was submitted.
|
// timestamp indicates when this record was submitted.
|
||||||
|
@ -17,11 +17,14 @@ package gauge
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"math/rand"
|
"math/rand"
|
||||||
|
"os"
|
||||||
"testing"
|
"testing"
|
||||||
|
"unsafe"
|
||||||
|
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
|
|
||||||
"go.opentelemetry.io/otel/api/core"
|
"go.opentelemetry.io/otel/api/core"
|
||||||
|
ottest "go.opentelemetry.io/otel/internal/testing"
|
||||||
export "go.opentelemetry.io/otel/sdk/export/metric"
|
export "go.opentelemetry.io/otel/sdk/export/metric"
|
||||||
"go.opentelemetry.io/otel/sdk/export/metric/aggregator"
|
"go.opentelemetry.io/otel/sdk/export/metric/aggregator"
|
||||||
"go.opentelemetry.io/otel/sdk/metric/aggregator/test"
|
"go.opentelemetry.io/otel/sdk/metric/aggregator/test"
|
||||||
@ -31,6 +34,21 @@ const count = 100
|
|||||||
|
|
||||||
var _ export.Aggregator = &Aggregator{}
|
var _ export.Aggregator = &Aggregator{}
|
||||||
|
|
||||||
|
// Ensure struct alignment prior to running tests.
|
||||||
|
func TestMain(m *testing.M) {
|
||||||
|
fields := []ottest.FieldOffset{
|
||||||
|
{
|
||||||
|
Name: "gaugeData.value",
|
||||||
|
Offset: unsafe.Offsetof(gaugeData{}.value),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
if !ottest.Aligned8Byte(fields, os.Stderr) {
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
os.Exit(m.Run())
|
||||||
|
}
|
||||||
|
|
||||||
func TestGaugeNonMonotonic(t *testing.T) {
|
func TestGaugeNonMonotonic(t *testing.T) {
|
||||||
ctx := context.Background()
|
ctx := context.Background()
|
||||||
|
|
||||||
|
@ -26,12 +26,15 @@ type (
|
|||||||
// Aggregator aggregates measure events, keeping only the max,
|
// Aggregator aggregates measure events, keeping only the max,
|
||||||
// sum, and count.
|
// sum, and count.
|
||||||
Aggregator struct {
|
Aggregator struct {
|
||||||
current state
|
// current has to be aligned for 64-bit atomic operations.
|
||||||
|
current state
|
||||||
|
// checkpoint has to be aligned for 64-bit atomic operations.
|
||||||
checkpoint state
|
checkpoint state
|
||||||
kind core.NumberKind
|
kind core.NumberKind
|
||||||
}
|
}
|
||||||
|
|
||||||
state struct {
|
state struct {
|
||||||
|
// all fields have to be aligned for 64-bit atomic operations.
|
||||||
count core.Number
|
count core.Number
|
||||||
sum core.Number
|
sum core.Number
|
||||||
min core.Number
|
min core.Number
|
||||||
|
@ -18,11 +18,14 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"math"
|
"math"
|
||||||
"math/rand"
|
"math/rand"
|
||||||
|
"os"
|
||||||
"testing"
|
"testing"
|
||||||
|
"unsafe"
|
||||||
|
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
|
|
||||||
"go.opentelemetry.io/otel/api/core"
|
"go.opentelemetry.io/otel/api/core"
|
||||||
|
ottest "go.opentelemetry.io/otel/internal/testing"
|
||||||
export "go.opentelemetry.io/otel/sdk/export/metric"
|
export "go.opentelemetry.io/otel/sdk/export/metric"
|
||||||
"go.opentelemetry.io/otel/sdk/export/metric/aggregator"
|
"go.opentelemetry.io/otel/sdk/export/metric/aggregator"
|
||||||
"go.opentelemetry.io/otel/sdk/metric/aggregator/test"
|
"go.opentelemetry.io/otel/sdk/metric/aggregator/test"
|
||||||
@ -59,6 +62,41 @@ var (
|
|||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// Ensure struct alignment prior to running tests.
|
||||||
|
func TestMain(m *testing.M) {
|
||||||
|
fields := []ottest.FieldOffset{
|
||||||
|
{
|
||||||
|
Name: "Aggregator.current",
|
||||||
|
Offset: unsafe.Offsetof(Aggregator{}.current),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "Aggregator.checkpoint",
|
||||||
|
Offset: unsafe.Offsetof(Aggregator{}.checkpoint),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "state.count",
|
||||||
|
Offset: unsafe.Offsetof(state{}.count),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "state.sum",
|
||||||
|
Offset: unsafe.Offsetof(state{}.sum),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "state.min",
|
||||||
|
Offset: unsafe.Offsetof(state{}.min),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "state.max",
|
||||||
|
Offset: unsafe.Offsetof(state{}.max),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
if !ottest.Aligned8Byte(fields, os.Stderr) {
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
os.Exit(m.Run())
|
||||||
|
}
|
||||||
|
|
||||||
func TestMinMaxSumCountAbsolute(t *testing.T) {
|
func TestMinMaxSumCountAbsolute(t *testing.T) {
|
||||||
test.RunProfiles(t, func(t *testing.T, profile test.Profile) {
|
test.RunProfiles(t, func(t *testing.T, profile test.Profile) {
|
||||||
minMaxSumCount(t, profile, positiveOnly)
|
minMaxSumCount(t, profile, positiveOnly)
|
||||||
|
@ -17,10 +17,13 @@ package test
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"math/rand"
|
"math/rand"
|
||||||
|
"os"
|
||||||
"sort"
|
"sort"
|
||||||
"testing"
|
"testing"
|
||||||
|
"unsafe"
|
||||||
|
|
||||||
"go.opentelemetry.io/otel/api/core"
|
"go.opentelemetry.io/otel/api/core"
|
||||||
|
ottest "go.opentelemetry.io/otel/internal/testing"
|
||||||
export "go.opentelemetry.io/otel/sdk/export/metric"
|
export "go.opentelemetry.io/otel/sdk/export/metric"
|
||||||
"go.opentelemetry.io/otel/sdk/export/metric/aggregator"
|
"go.opentelemetry.io/otel/sdk/export/metric/aggregator"
|
||||||
)
|
)
|
||||||
@ -63,9 +66,25 @@ func RunProfiles(t *testing.T, f func(*testing.T, Profile)) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Ensure local struct alignment prior to running tests.
|
||||||
|
func TestMain(m *testing.M) {
|
||||||
|
fields := []ottest.FieldOffset{
|
||||||
|
{
|
||||||
|
Name: "Numbers.numbers",
|
||||||
|
Offset: unsafe.Offsetof(Numbers{}.numbers),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
if !ottest.Aligned8Byte(fields, os.Stderr) {
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
os.Exit(m.Run())
|
||||||
|
}
|
||||||
|
|
||||||
type Numbers struct {
|
type Numbers struct {
|
||||||
kind core.NumberKind
|
// numbers has to be aligned for 64-bit atomic operations.
|
||||||
numbers []core.Number
|
numbers []core.Number
|
||||||
|
kind core.NumberKind
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewNumbers(kind core.NumberKind) Numbers {
|
func NewNumbers(kind core.NumberKind) Numbers {
|
||||||
|
36
sdk/metric/alignment_test.go
Normal file
36
sdk/metric/alignment_test.go
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
package metric
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
"testing"
|
||||||
|
"unsafe"
|
||||||
|
|
||||||
|
ottest "go.opentelemetry.io/otel/internal/testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Ensure struct alignment prior to running tests.
|
||||||
|
func TestMain(m *testing.M) {
|
||||||
|
fields := []ottest.FieldOffset{
|
||||||
|
{
|
||||||
|
Name: "record.refcount",
|
||||||
|
Offset: unsafe.Offsetof(record{}.refcount),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "record.collectedEpoch",
|
||||||
|
Offset: unsafe.Offsetof(record{}.collectedEpoch),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "record.modifiedEpoch",
|
||||||
|
Offset: unsafe.Offsetof(record{}.modifiedEpoch),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "record.reclaim",
|
||||||
|
Offset: unsafe.Offsetof(record{}.reclaim),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
if !ottest.Aligned8Byte(fields, os.Stderr) {
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
os.Exit(m.Run())
|
||||||
|
}
|
@ -31,11 +31,12 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type monotoneBatcher struct {
|
type monotoneBatcher struct {
|
||||||
t *testing.T
|
// currentValue needs to be aligned for 64-bit atomic operations.
|
||||||
|
|
||||||
collections int
|
|
||||||
currentValue *core.Number
|
currentValue *core.Number
|
||||||
|
collections int
|
||||||
currentTime *time.Time
|
currentTime *time.Time
|
||||||
|
|
||||||
|
t *testing.T
|
||||||
}
|
}
|
||||||
|
|
||||||
func (*monotoneBatcher) AggregatorFor(*export.Descriptor) export.Aggregator {
|
func (*monotoneBatcher) AggregatorFor(*export.Descriptor) export.Aggregator {
|
||||||
|
@ -97,31 +97,39 @@ type (
|
|||||||
// `record` in existence at a time, although at most one can
|
// `record` in existence at a time, although at most one can
|
||||||
// be referenced from the `SDK.current` map.
|
// be referenced from the `SDK.current` map.
|
||||||
record struct {
|
record struct {
|
||||||
// labels is the LabelSet passed by the user.
|
|
||||||
labels *labels
|
|
||||||
|
|
||||||
// descriptor describes the metric instrument.
|
|
||||||
descriptor *export.Descriptor
|
|
||||||
|
|
||||||
// refcount counts the number of active handles on
|
// refcount counts the number of active handles on
|
||||||
// referring to this record. active handles prevent
|
// referring to this record. active handles prevent
|
||||||
// removing the record from the current map.
|
// removing the record from the current map.
|
||||||
|
//
|
||||||
|
// refcount has to be aligned for 64-bit atomic operations.
|
||||||
refcount int64
|
refcount int64
|
||||||
|
|
||||||
// collectedEpoch is the epoch number for which this
|
// collectedEpoch is the epoch number for which this
|
||||||
// record has been exported. This is modified by the
|
// record has been exported. This is modified by the
|
||||||
// `Collect()` method.
|
// `Collect()` method.
|
||||||
|
//
|
||||||
|
// collectedEpoch has to be aligned for 64-bit atomic operations.
|
||||||
collectedEpoch int64
|
collectedEpoch int64
|
||||||
|
|
||||||
// modifiedEpoch is the latest epoch number for which
|
// modifiedEpoch is the latest epoch number for which
|
||||||
// this record was updated. Generally, if
|
// this record was updated. Generally, if
|
||||||
// modifiedEpoch is less than collectedEpoch, this
|
// modifiedEpoch is less than collectedEpoch, this
|
||||||
// record is due for reclaimation.
|
// record is due for reclaimation.
|
||||||
|
//
|
||||||
|
// modifiedEpoch has to be aligned for 64-bit atomic operations.
|
||||||
modifiedEpoch int64
|
modifiedEpoch int64
|
||||||
|
|
||||||
// reclaim is an atomic to control the start of reclaiming.
|
// reclaim is an atomic to control the start of reclaiming.
|
||||||
|
//
|
||||||
|
// reclaim has to be aligned for 64-bit atomic operations.
|
||||||
reclaim int64
|
reclaim int64
|
||||||
|
|
||||||
|
// labels is the LabelSet passed by the user.
|
||||||
|
labels *labels
|
||||||
|
|
||||||
|
// descriptor describes the metric instrument.
|
||||||
|
descriptor *export.Descriptor
|
||||||
|
|
||||||
// recorder implements the actual RecordOne() API,
|
// recorder implements the actual RecordOne() API,
|
||||||
// depending on the type of aggregation. If nil, the
|
// depending on the type of aggregation. If nil, the
|
||||||
// metric was disabled by the exporter.
|
// metric was disabled by the exporter.
|
||||||
|
@ -51,6 +51,7 @@ const (
|
|||||||
|
|
||||||
type (
|
type (
|
||||||
testFixture struct {
|
testFixture struct {
|
||||||
|
// stop has to be aligned for 64-bit atomic operations.
|
||||||
stop int64
|
stop int64
|
||||||
expected sync.Map
|
expected sync.Map
|
||||||
received sync.Map // Note: doesn't require synchronization
|
received sync.Map // Note: doesn't require synchronization
|
||||||
@ -93,6 +94,7 @@ type (
|
|||||||
// where a race condition causes duplicate records. We always
|
// where a race condition causes duplicate records. We always
|
||||||
// take the later timestamp.
|
// take the later timestamp.
|
||||||
gaugeState struct {
|
gaugeState struct {
|
||||||
|
// raw has to be aligned for 64-bit atomic operations.
|
||||||
raw core.Number
|
raw core.Number
|
||||||
ts time.Time
|
ts time.Time
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user