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  }