github.com/waldiirawan/apm-agent-go/v2@v2.2.2/breakdown_internal_test.go (about) 1 // Licensed to Elasticsearch B.V. under one or more contributor 2 // license agreements. See the NOTICE file distributed with 3 // this work for additional information regarding copyright 4 // ownership. Elasticsearch B.V. licenses this file to you under 5 // the Apache License, Version 2.0 (the "License"); you may 6 // not use this file except in compliance with the License. 7 // You may obtain a copy of the License at 8 // 9 // http://www.apache.org/licenses/LICENSE-2.0 10 // 11 // Unless required by applicable law or agreed to in writing, 12 // software distributed under the License is distributed on an 13 // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14 // KIND, either express or implied. See the License for the 15 // specific language governing permissions and limitations 16 // under the License. 17 18 package apm 19 20 import ( 21 "go/ast" 22 "go/importer" 23 "go/parser" 24 "go/token" 25 "go/types" 26 "os" 27 "os/exec" 28 "runtime" 29 "strings" 30 "testing" 31 32 "github.com/stretchr/testify/assert" 33 "github.com/stretchr/testify/require" 34 ) 35 36 func TestBreakdownMetricsAlignment(t *testing.T) { 37 // This test is to ensure the alignment properties 38 // of the breakdownMetricsMapEntry are maintained 39 // for both 32-bit and 64-bit systems, since we use 40 // sync/atomic operations on them. 41 if runtime.GOOS != "darwin" { 42 // Go 1.15 dropped support for darwin/386 43 t.Run("32-bit", func(t *testing.T) { testBreakdownMetricsAlignment(t, "386") }) 44 } 45 t.Run("64-bit", func(t *testing.T) { testBreakdownMetricsAlignment(t, "amd64") }) 46 } 47 48 func testBreakdownMetricsAlignment(t *testing.T, arch string) { 49 cfg := types.Config{ 50 IgnoreFuncBodies: true, 51 Importer: importer.For("source", nil), 52 Sizes: types.SizesFor("gc", arch), 53 } 54 55 cmd := exec.Command("go", "list", "-f", "{{.GoFiles}}") 56 cmd.Env = os.Environ() 57 cmd.Env = append(cmd.Env, "GOARCH="+arch) 58 output, err := cmd.Output() 59 require.NoError(t, err, string(output)) 60 filenames := strings.Fields(string(output[1 : len(output)-2])) // strip "[" and "]" 61 62 fset := token.NewFileSet() 63 files := make([]*ast.File, len(filenames)) 64 for i, filename := range filenames { 65 f, err := parser.ParseFile(fset, filename, nil, 0) 66 require.NoError(t, err) 67 files[i] = f 68 } 69 70 pkg, err := cfg.Check("github.com/waldiirawan/apm-agent-go/v2", fset, files, nil) 71 require.NoError(t, err) 72 73 // breakdownMetricsMapEntry's size must be multiple of 8, 74 // as it is used in a slice. This ensures that the embedded 75 // fields are always aligned. 76 breakdownMetricsMapEntryObj := pkg.Scope().Lookup("breakdownMetricsMapEntry") 77 require.NotNil(t, breakdownMetricsMapEntryObj) 78 assert.Equal(t, int64(0), cfg.Sizes.Sizeof(breakdownMetricsMapEntryObj.Type())%8) 79 80 // breakdownMetricsMapEntry.breakdownTiming must be the first field, 81 // to ensure it remains 64-bit aligned. 82 breakdownTimingObj, breakdownTimingFieldIndex, _ := types.LookupFieldOrMethod( 83 breakdownMetricsMapEntryObj.Type(), false, pkg, "breakdownTiming", 84 ) 85 require.NotNil(t, breakdownTimingObj) 86 assert.Equal(t, []int{1}, breakdownTimingFieldIndex) 87 88 // breakdownTiming.transaction.duration and breakdownTiming.span.duration 89 // should be 64-bit aligned. We know that the breakdownTiming type is 90 // 64-bit aligned, so check that its transaction/span fields are also, 91 // and that spanTiming's duration field is its first field. 92 93 spanTimingObj := pkg.Scope().Lookup("spanTiming") 94 require.NotNil(t, spanTimingObj) 95 _, durationFieldIndex, _ := types.LookupFieldOrMethod(spanTimingObj.Type(), false, pkg, "duration") 96 assert.Equal(t, []int{0}, durationFieldIndex) 97 98 breakdownTimingStruct := breakdownTimingObj.Type().Underlying().(*types.Struct) 99 var spanTimingFieldIndices []int 100 fields := make([]*types.Var, breakdownTimingStruct.NumFields()) 101 for i := range fields { 102 field := breakdownTimingStruct.Field(i) 103 fields[i] = field 104 if field.Type() == spanTimingObj.Type() { 105 spanTimingFieldIndices = append(spanTimingFieldIndices, i) 106 } 107 } 108 require.NotEmpty(t, spanTimingFieldIndices) 109 offsets := cfg.Sizes.Offsetsof(fields) 110 for _, fieldIndex := range spanTimingFieldIndices { 111 assert.Equal(t, int64(0), offsets[fieldIndex]%8) 112 } 113 }