github.com/m3db/m3@v1.5.1-0.20231129193456-75a402aa583b/src/x/debug/triggering_profile_test.go (about)

     1  // Copyright (c) 2020 Uber Technologies, Inc.
     2  //
     3  // Permission is hereby granted, free of charge, to any person obtaining a copy
     4  // of this software and associated documentation files (the "Software"), to deal
     5  // in the Software without restriction, including without limitation the rights
     6  // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
     7  // copies of the Software, and to permit persons to whom the Software is
     8  // furnished to do so, subject to the following conditions:
     9  //
    10  // The above copyright notice and this permission notice shall be included in
    11  // all copies or substantial portions of the Software.
    12  //
    13  // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
    14  // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
    15  // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
    16  // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
    17  // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
    18  // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
    19  // THE SOFTWARE.
    20  
    21  package debug
    22  
    23  import (
    24  	"fmt"
    25  	"io/ioutil"
    26  	"os"
    27  	"path"
    28  	"regexp"
    29  	"strings"
    30  	"testing"
    31  	"time"
    32  
    33  	"github.com/m3db/m3/src/x/instrument"
    34  
    35  	"github.com/stretchr/testify/require"
    36  	"go.uber.org/atomic"
    37  )
    38  
    39  func TestContinuousFileProfile(t *testing.T) {
    40  	for _, test := range []struct {
    41  		name     string
    42  		duration time.Duration
    43  		debug    int
    44  	}{
    45  		{
    46  			name:     ContinuousCPUProfileName,
    47  			duration: time.Second,
    48  		},
    49  		{
    50  			name:  "goroutine",
    51  			debug: 2,
    52  		},
    53  	} {
    54  		test := test
    55  		name := fmt.Sprintf("%s_%s_%d", test.name, test.duration, test.debug)
    56  		t.Run(name, func(t *testing.T) {
    57  			dir, err := ioutil.TempDir("", "")
    58  			require.NoError(t, err)
    59  
    60  			defer os.RemoveAll(dir)
    61  
    62  			ext := ".profile"
    63  
    64  			cond := atomic.NewBool(false)
    65  			opts := ContinuousFileProfileOptions{
    66  				FilePathTemplate:  path.Join(dir, "{{.ProfileName}}-{{.UnixTime}}"+ext),
    67  				ProfileName:       test.name,
    68  				ProfileDuration:   test.duration,
    69  				ProfileDebug:      test.debug,
    70  				Interval:          20 * time.Millisecond,
    71  				Conditional:       cond.Load,
    72  				InstrumentOptions: instrument.NewTestOptions(t),
    73  			}
    74  
    75  			profile, err := NewContinuousFileProfile(opts)
    76  			require.NoError(t, err)
    77  
    78  			require.NoError(t, profile.Start())
    79  
    80  			for i := 0; i < 10; i++ {
    81  				// Make sure doesn't create files until conditional returns true
    82  				time.Sleep(opts.Interval)
    83  
    84  				files, err := ioutil.ReadDir(dir)
    85  				require.NoError(t, err)
    86  
    87  				for _, f := range files {
    88  					if strings.HasSuffix(f.Name(), ext) {
    89  						require.FailNow(t, "conditional false, profile generated")
    90  					}
    91  				}
    92  			}
    93  
    94  			// Trigger profile.
    95  			cond.Store(true)
    96  
    97  			for start := time.Now(); time.Since(start) < 10*time.Second; {
    98  				files, err := ioutil.ReadDir(dir)
    99  				require.NoError(t, err)
   100  
   101  				profiles := 0
   102  				for _, f := range files {
   103  					if strings.HasSuffix(f.Name(), ext) {
   104  						pattern := test.name + `-[0-9]+\` + ext
   105  						re := regexp.MustCompile(pattern)
   106  						require.True(t, re.MatchString(f.Name()),
   107  							fmt.Sprintf(
   108  								"file should match pattern: pattern=%s, actual=%s",
   109  								pattern, f.Name()))
   110  						profiles++
   111  					}
   112  				}
   113  
   114  				if profiles >= 2 {
   115  					// Successfully continuously emitting.
   116  					return
   117  				}
   118  			}
   119  
   120  			require.FailNow(t, "did not generate profiles")
   121  		})
   122  	}
   123  }