-
Notifications
You must be signed in to change notification settings - Fork 134
/
options.go
462 lines (396 loc) · 12.5 KB
/
options.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package holmes
import (
"sync"
"sync/atomic"
"time"
mlog "mosn.io/pkg/log"
)
type options struct {
logger mlog.ErrorLogger
UseGoProcAsCPUCore bool // use the go max procs number as the CPU core number when it's true
UseCGroup bool // use the CGroup to calc cpu/memory when it's true
// overwrite the system level memory limitation when > 0.
memoryLimit uint64
cpuCore float64
*ShrinkThrOptions
*DumpOptions
// interval for dump loop, default 5s
CollectInterval time.Duration
intervalResetting chan struct{}
// if current cpu usage percent is greater than CPUMaxPercent,
// holmes would not dump all types profile, cuz this
// move may result of the system crash.
CPUMaxPercent int
// if write lock is held mean holmes's
// configuration is being modified.
L *sync.RWMutex
// the cooldown time after every type of dump
// interval for cooldown,default 1m
// each check type have different cooldowns of their own
grOpts *grOptions
memOpts *typeOption
gCHeapOpts *typeOption
cpuOpts *typeOption
threadOpts *typeOption
// profile reporter
rptOpts *ReporterOptions
}
type ReporterOptions struct {
reporter ProfileReporter
active int32 // switch
}
// newReporterOpts returns ReporterOptions。
func newReporterOpts() *ReporterOptions {
opts := &ReporterOptions{}
return opts
}
// DumpOptions contains configuration about dump file.
type DumpOptions struct {
// full path to put the profile files, default /tmp
DumpPath string
// default dump to binary profile, set to true if you want a text profile
DumpProfileType dumpProfileType
// only dump top 10 if set to false, otherwise dump all, only effective when in_text = true
DumpFullStack bool
// dump profile to logger. It will make huge log output if enable DumpToLogger option. issues/90
DumpToLogger bool
}
// ShrinkThrOptions contains the configuration about shrink thread
type ShrinkThrOptions struct {
// shrink the thread number when it exceeds the max threshold that specified in Threshold
Enable bool
Threshold int
Delay time.Duration // start to shrink thread after the delay time.
}
// GetReporterOpts returns a copy of rptOpts.
func (o *options) GetReporterOpts() ReporterOptions {
o.L.RLock()
defer o.L.RUnlock()
return *o.rptOpts
}
// GetShrinkThreadOpts return a copy of ShrinkThrOptions.
func (o *options) GetShrinkThreadOpts() ShrinkThrOptions {
o.L.RLock()
defer o.L.RUnlock()
return *o.ShrinkThrOptions
}
// GetMemOpts return a copy of typeOption.
func (o *options) GetMemOpts() typeOption {
o.L.RLock()
defer o.L.RUnlock()
return *o.memOpts
}
// GetCPUOpts return a copy of typeOption
// if cpuOpts not exist return a empty typeOption and false.
func (o *options) GetCPUOpts() typeOption {
o.L.RLock()
defer o.L.RUnlock()
return *o.cpuOpts
}
// GetGrOpts return a copy of grOptions
// if grOpts not exist return a empty grOptions and false.
func (o *options) GetGrOpts() grOptions {
o.L.RLock()
defer o.L.RUnlock()
return *o.grOpts
}
// GetThreadOpts return a copy of typeOption
// if threadOpts not exist return a empty typeOption and false.
func (o *options) GetThreadOpts() typeOption {
o.L.RLock()
defer o.L.RUnlock()
return *o.threadOpts
}
// GetGcHeapOpts return a copy of typeOption
// if gCHeapOpts not exist return a empty typeOption and false.
func (o *options) GetGcHeapOpts() typeOption {
o.L.RLock()
defer o.L.RUnlock()
return *o.gCHeapOpts
}
// Option holmes option type.
type Option interface {
apply(*options) error
}
type optionFunc func(*options) error
func (f optionFunc) apply(opts *options) error {
return f(opts)
}
func newOptions() *options {
o := &options{
logger: NewStdLogger(),
grOpts: newGrOptions(),
memOpts: newMemOptions(),
gCHeapOpts: newGCHeapOptions(),
cpuOpts: newCPUOptions(),
threadOpts: newThreadOptions(),
CollectInterval: defaultInterval,
intervalResetting: make(chan struct{}, 1),
DumpOptions: &DumpOptions{
DumpPath: defaultDumpPath,
DumpProfileType: defaultDumpProfileType,
DumpFullStack: false,
},
ShrinkThrOptions: &ShrinkThrOptions{
Enable: false,
},
L: &sync.RWMutex{},
rptOpts: newReporterOpts(),
}
return o
}
// WithLogger set the logger
// logger can be created by: NewFileLog("/path/to/log/file", level)
func WithLogger(logger mlog.ErrorLogger) Option {
return optionFunc(func(opts *options) (err error) {
opts.logger = logger
return
})
}
// WithDumpPath set the dump path for holmes.
func WithDumpPath(dumpPath string) Option {
return optionFunc(func(opts *options) (err error) {
opts.DumpPath = dumpPath
return
})
}
// WithCollectInterval : interval must be valid time duration string,
// eg. "ns", "us" (or "µs"), "ms", "s", "m", "h".
func WithCollectInterval(interval string) Option {
return optionFunc(func(opts *options) (err error) {
// CollectInterval wouldn't be zero value, because it
// will be initialized as defaultInterval at newOptions()
newInterval, err := time.ParseDuration(interval)
if err != nil || opts.CollectInterval.Seconds() == newInterval.Seconds() {
return
}
opts.CollectInterval = newInterval
opts.intervalResetting <- struct{}{}
return
})
}
// WithCPUMax : set the CPUMaxPercent parameter as max
func WithCPUMax(max int) Option {
return optionFunc(func(opts *options) (err error) {
opts.CPUMaxPercent = max
return
})
}
// WithBinaryDump set dump mode to binary.
func WithBinaryDump() Option {
return withDumpProfileType(binaryDump)
}
// WithTextDump set dump mode to text.
func WithTextDump() Option {
return withDumpProfileType(textDump)
}
// WithFullStack set to dump full stack or top 10 stack, when dump in text mode.
func WithFullStack(isFull bool) Option {
return optionFunc(func(opts *options) (err error) {
opts.DumpFullStack = isFull
return
})
}
func withDumpProfileType(profileType dumpProfileType) Option {
return optionFunc(func(opts *options) (err error) {
opts.DumpProfileType = profileType
return
})
}
type grOptions struct {
// enable the goroutine dumper, should dump if one of the following requirements is matched
// 1. goroutine_num > TriggerMin && goroutine_num < GoroutineTriggerNumMax && goroutine diff percent > TriggerDiff
// 2. goroutine_num > GoroutineTriggerNumAbsNum && goroutine_num < GoroutineTriggerNumMax
*typeOption
GoroutineTriggerNumMax int // goroutine trigger max in number
}
func newGrOptions() *grOptions {
base := newTypeOpts(
defaultGoroutineTriggerMin,
defaultGoroutineTriggerAbs,
defaultGoroutineTriggerDiff,
defaultGoroutineCoolDown,
)
return &grOptions{typeOption: base}
}
// WithGoroutineDump set the goroutine dump options.
func WithGoroutineDump(min int, diff int, abs int, max int, coolDown time.Duration) Option {
return optionFunc(func(opts *options) (err error) {
opts.grOpts.Set(min, abs, diff, coolDown)
opts.grOpts.GoroutineTriggerNumMax = max
return
})
}
func WithDumpToLogger(new bool) Option {
return optionFunc(func(opts *options) (err error) {
opts.DumpToLogger = new
return
})
}
type typeOption struct {
Enable bool
// mem/cpu/gcheap trigger minimum in percent, goroutine/thread trigger minimum in number
TriggerMin int
// mem/cpu/gcheap trigger abs in percent, goroutine/thread trigger abs in number
TriggerAbs int
// mem/cpu/gcheap/goroutine/thread trigger diff in percent
TriggerDiff int
// CoolDown skip profile for CoolDown time after done a profile
CoolDown time.Duration
}
func newTypeOpts(triggerMin, triggerAbs, triggerDiff int, coolDown time.Duration) *typeOption {
return &typeOption{
Enable: false,
TriggerMin: triggerMin,
TriggerAbs: triggerAbs,
TriggerDiff: triggerDiff,
CoolDown: coolDown,
}
}
func (base *typeOption) Set(min, abs, diff int, coolDown time.Duration) {
base.TriggerMin, base.TriggerAbs, base.TriggerDiff, base.CoolDown = min, abs, diff, coolDown
}
// newMemOptions
// enable the heap dumper, should dump if one of the following requirements is matched
// 1. memory usage > TriggerMin && memory usage diff > TriggerDiff
// 2. memory usage > TriggerAbs.
func newMemOptions() *typeOption {
return newTypeOpts(
defaultMemTriggerMin,
defaultMemTriggerAbs,
defaultMemTriggerDiff,
defaultCooldown,
)
}
// WithMemDump set the memory dump options.
func WithMemDump(min int, diff int, abs int, coolDown time.Duration) Option {
return optionFunc(func(opts *options) (err error) {
opts.memOpts.Set(min, abs, diff, coolDown)
return
})
}
// newGCHeapOptions
// enable the heap dumper, should dump if one of the following requirements is matched
// 1. GC heap usage > TriggerMin && GC heap usage diff > TriggerDiff
// 2. GC heap usage > TriggerAbs
// in percent.
func newGCHeapOptions() *typeOption {
return newTypeOpts(
defaultGCHeapTriggerMin,
defaultGCHeapTriggerAbs,
defaultGCHeapTriggerDiff,
defaultCooldown,
)
}
// WithGCHeapDump set the GC heap dump options.
func WithGCHeapDump(min int, diff int, abs int, coolDown time.Duration) Option {
return optionFunc(func(opts *options) (err error) {
opts.gCHeapOpts.Set(min, abs, diff, coolDown)
return
})
}
// WithCPUCore overwrite the system level CPU core number when it > 0.
// it's not a good idea to modify it on fly since it affects the CPU percent caculation.
func WithCPUCore(cpuCore float64) Option {
return optionFunc(func(opts *options) (err error) {
opts.cpuCore = cpuCore
return
})
}
// WithMemoryLimit overwrite the system level memory limit when it > 0.
func WithMemoryLimit(limit uint64) Option {
return optionFunc(func(opts *options) (err error) {
opts.memoryLimit = limit
return
})
}
func newThreadOptions() *typeOption {
return newTypeOpts(
defaultThreadTriggerMin,
defaultThreadTriggerAbs,
defaultThreadTriggerDiff,
defaultThreadCoolDown,
)
}
// WithThreadDump set the thread dump options.
func WithThreadDump(min, diff, abs int, coolDown time.Duration) Option {
return optionFunc(func(opts *options) (err error) {
opts.threadOpts.Set(min, abs, diff, coolDown)
return
})
}
// newCPUOptions
// enable the cpu dumper, should dump if one of the following requirements is matched
// in percent
// 1. cpu usage > CPUTriggerMin && cpu usage diff > CPUTriggerDiff
// 2. cpu usage > CPUTriggerAbs
// in percent.
func newCPUOptions() *typeOption {
return newTypeOpts(
defaultCPUTriggerMin,
defaultCPUTriggerAbs,
defaultCPUTriggerDiff,
defaultCooldown,
)
}
// WithCPUDump set the cpu dump options.
func WithCPUDump(min int, diff int, abs int, coolDown time.Duration) Option {
return optionFunc(func(opts *options) (err error) {
opts.cpuOpts.Set(min, abs, diff, coolDown)
return
})
}
// WithGoProcAsCPUCore set holmes use cgroup or not.
func WithGoProcAsCPUCore(enabled bool) Option {
return optionFunc(func(opts *options) (err error) {
opts.UseGoProcAsCPUCore = enabled
return
})
}
// WithCGroup set holmes use cgroup or not.
// Use CGroup are best used when resource limits are set.
// refer to: https://github.com/mosn/holmes/issues/135
func WithCGroup(useCGroup bool) Option {
return optionFunc(func(opts *options) (err error) {
opts.UseCGroup = useCGroup
return
})
}
// WithShrinkThread enable/disable shrink thread when the thread number exceed the max threshold.
func WithShrinkThread(threshold int, delay time.Duration) Option {
return optionFunc(func(opts *options) (err error) {
if threshold > 0 {
opts.ShrinkThrOptions.Threshold = threshold
}
opts.ShrinkThrOptions.Delay = delay
return
})
}
// WithProfileReporter will enable reporter
// reopens profile reporter through WithProfileReporter(h.opts.rptOpts.reporter)
func WithProfileReporter(r ProfileReporter) Option {
return optionFunc(func(opts *options) (err error) {
if r == nil {
return nil
}
opts.rptOpts.reporter = r
atomic.StoreInt32(&opts.rptOpts.active, 1)
return
})
}