1// (c) Copyright IBM Corp. 2021
2// (c) Copyright Instana Inc. 2020
3
4package autoprofile
5
6import (
7	"os"
8
9	"github.com/instana/go-sensor/autoprofile/internal"
10	"github.com/instana/go-sensor/autoprofile/internal/logger"
11	instalogger "github.com/instana/go-sensor/logger"
12)
13
14// Profile represents profiler data sent to the host agent
15//
16// The type alias here is needed to expose the type defined inside the internal package.
17// Ideally this type should've been defined in the same package with instana.agentS, however
18// due to the way we activate profiling, this would introduce a circular dependency.
19type Profile internal.AgentProfile
20
21// SendProfilesFunc is a function that submits profiles to the host agent
22type SendProfilesFunc func(profiles []Profile) error
23
24var (
25	profileRecorder     = internal.NewRecorder()
26	cpuSamplerScheduler = internal.NewSamplerScheduler(profileRecorder, internal.NewCPUSampler(), internal.SamplerConfig{
27		LogPrefix:          "CPU sampler:",
28		MaxProfileDuration: 20,
29		MaxSpanDuration:    2,
30		MaxSpanCount:       30,
31		SamplingInterval:   8,
32		ReportInterval:     120,
33	})
34	allocationSamplerScheduler = internal.NewSamplerScheduler(profileRecorder, internal.NewAllocationSampler(), internal.SamplerConfig{
35		LogPrefix:      "Allocation sampler:",
36		ReportOnly:     true,
37		ReportInterval: 120,
38	})
39	blockSamplerScheduler = internal.NewSamplerScheduler(profileRecorder, internal.NewBlockSampler(), internal.SamplerConfig{
40		LogPrefix:          "Block sampler:",
41		MaxProfileDuration: 20,
42		MaxSpanDuration:    4,
43		MaxSpanCount:       30,
44		SamplingInterval:   16,
45		ReportInterval:     120,
46	})
47
48	enabled bool
49)
50
51// SetLogLevel sets the min log level for autoprofiler
52//
53// Deprecated: use autoprofile.SetLogger() to set the logger and configure the min log level directly
54func SetLogLevel(level int) {
55	switch logger.Level(level) {
56	case logger.ErrorLevel:
57		logger.SetLogLevel(instalogger.ErrorLevel)
58	case logger.WarnLevel:
59		logger.SetLogLevel(instalogger.WarnLevel)
60	case logger.InfoLevel:
61		logger.SetLogLevel(instalogger.InfoLevel)
62	default:
63		logger.SetLogLevel(instalogger.DebugLevel)
64	}
65}
66
67// SetLogger sets the leveled logger to use to output the diagnostic messages and errors
68func SetLogger(l logger.LeveledLogger) {
69	logger.SetLogger(l)
70}
71
72// Enable enables the auto profiling (disabled by default)
73func Enable() {
74	if enabled {
75		return
76	}
77
78	profileRecorder.Start()
79	cpuSamplerScheduler.Start()
80	allocationSamplerScheduler.Start()
81	blockSamplerScheduler.Start()
82
83	logger.Debug("profiler enabled")
84}
85
86// Disable disables the auto profiling (default)
87func Disable() {
88	if !enabled {
89		return
90	}
91
92	if _, ok := os.LookupEnv("INSTANA_AUTO_PROFILE"); ok {
93		logger.Info("INSTANA_AUTO_PROFILE is set, ignoring the attempt to disable AutoProfile™")
94		return
95	}
96
97	profileRecorder.Stop()
98	cpuSamplerScheduler.Stop()
99	allocationSamplerScheduler.Stop()
100	blockSamplerScheduler.Stop()
101
102	logger.Debug("profiler disabled")
103}
104
105// SetGetExternalPIDFunc configures the profiler to use provided function to retrieve the current PID
106//
107// Deprecated: this is a noop function, the PID is populated by the agent before sending
108func SetGetExternalPIDFunc(fn func() string) {}
109
110// SetSendProfilesFunc configures the profiler to use provided function to write collected profiles
111func SetSendProfilesFunc(fn SendProfilesFunc) {
112	if fn == nil {
113		profileRecorder.SendProfiles = internal.NoopSendProfiles
114		return
115	}
116
117	profileRecorder.SendProfiles = func(data []internal.AgentProfile) error {
118		profiles := make([]Profile, 0, len(data))
119		for _, p := range data {
120			profiles = append(profiles, Profile(p))
121		}
122
123		return fn(profiles)
124	}
125}
126
127// Options contains profiler configuration
128type Options struct {
129	IncludeProfilerFrames bool
130	MaxBufferedProfiles   int
131}
132
133// DefaultOptions returns profiler defaults
134func DefaultOptions() Options {
135	return Options{
136		MaxBufferedProfiles: internal.DefaultMaxBufferedProfiles,
137	}
138}
139
140// SetOptions configures the profiler with provided settings
141func SetOptions(opts Options) {
142	if opts.MaxBufferedProfiles < 1 {
143		opts.MaxBufferedProfiles = internal.DefaultMaxBufferedProfiles
144	}
145
146	profileRecorder.MaxBufferedProfiles = opts.MaxBufferedProfiles
147	internal.IncludeProfilerFrames = opts.IncludeProfilerFrames
148}
149