github.com/waldiirawan/apm-agent-go/v2@v2.2.2/profiling_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_test 19 20 import ( 21 "bufio" 22 "bytes" 23 "io/ioutil" 24 "os" 25 "os/exec" 26 "strings" 27 "testing" 28 "time" 29 30 "github.com/stretchr/testify/assert" 31 32 "github.com/waldiirawan/apm-agent-go/v2/apmtest" 33 ) 34 35 func TestTracerCPUProfiling(t *testing.T) { 36 os.Setenv("ELASTIC_APM_CPU_PROFILE_INTERVAL", "100ms") 37 os.Setenv("ELASTIC_APM_CPU_PROFILE_DURATION", "1s") 38 defer os.Unsetenv("ELASTIC_APM_CPU_PROFILE_INTERVAL") 39 defer os.Unsetenv("ELASTIC_APM_CPU_PROFILE_DURATION") 40 41 tracer := apmtest.NewRecordingTracer() 42 defer tracer.Close() 43 44 timeout := time.After(10 * time.Second) 45 var profiles [][]byte 46 for len(profiles) == 0 { 47 select { 48 case <-timeout: 49 t.Fatal("timed out waiting for profile") 50 default: // busy loop so we get some CPU samples 51 } 52 profiles = tracer.Payloads().Profiles 53 } 54 55 info := parseProfile(profiles[0]) 56 assert.EqualValues(t, []string{"samples/count", "cpu/nanoseconds"}, info.sampleTypes) 57 } 58 59 func TestTracerHeapProfiling(t *testing.T) { 60 os.Setenv("ELASTIC_APM_HEAP_PROFILE_INTERVAL", "100ms") 61 defer os.Unsetenv("ELASTIC_APM_HEAP_PROFILE_INTERVAL") 62 63 tracer := apmtest.NewRecordingTracer() 64 defer tracer.Close() 65 66 timeout := time.After(10 * time.Second) 67 var profiles [][]byte 68 69 tick := time.Tick(50 * time.Millisecond) 70 for len(profiles) == 0 { 71 select { 72 case <-timeout: 73 t.Fatal("timed out waiting for profile") 74 case <-tick: 75 } 76 profiles = tracer.Payloads().Profiles 77 } 78 79 info := parseProfile(profiles[0]) 80 assert.EqualValues(t, []string{ 81 "alloc_objects/count", "alloc_space/bytes", 82 "inuse_objects/count", "inuse_space/bytes", 83 }, info.sampleTypes) 84 } 85 86 // parseProfile parses the profile data using "go tool pprof". 87 // 88 // We could use github.com/google/pprof, but prefer not to add 89 // a dependency for users just to run these unit test. More 90 // thorough integration testing should be performed elsewhere. 91 func parseProfile(data []byte) profileInfo { 92 f, err := ioutil.TempFile("", "apm_profiletest") 93 if err != nil { 94 panic(err) 95 } 96 defer os.Remove(f.Name()) 97 defer f.Close() 98 if _, err := f.Write(data); err != nil { 99 panic(err) 100 } 101 102 cmd := exec.Command("go", "tool", "pprof", "-raw", f.Name()) 103 cmd.Stderr = os.Stderr 104 out, err := cmd.Output() 105 if err != nil { 106 panic(err) 107 } 108 109 var info profileInfo 110 scanner := bufio.NewScanner(bytes.NewReader(out)) 111 for scanner.Scan() { 112 if scanner.Text() == "Samples:" && scanner.Scan() { 113 info.sampleTypes = strings.Fields(scanner.Text()) 114 return info 115 } 116 } 117 if err := scanner.Err(); err != nil { 118 panic(err) 119 } 120 panic("failed to locate sample types") 121 } 122 123 type profileInfo struct { 124 sampleTypes []string 125 }