Skip to content

Commit

Permalink
feat: add reportAll option and deprecate reportBoth option
Browse files Browse the repository at this point in the history
  • Loading branch information
jake-shin0 committed Jul 24, 2024
1 parent b91e419 commit 6d6adb6
Show file tree
Hide file tree
Showing 3 changed files with 137 additions and 41 deletions.
54 changes: 52 additions & 2 deletions autopprof.go
Original file line number Diff line number Diff line change
Expand Up @@ -58,10 +58,15 @@ type autoPprof struct {
// reporter is the reporter to send the profiling reports.
reporter report.Reporter

// deprecated: use reportAll instead.
// reportBoth sets whether to trigger reports for both CPU and memory when either threshold is exceeded.
// If some profiling is disabled, exclude it.
reportBoth bool

// reportAll sets whether to trigger reports for all profiling types when any threshold is exceeded.
// If some profiling is disabled, exclude it.
reportAll bool

// Flags to disable the profiling.
disableCPUProf bool
disableMemProf bool
Expand Down Expand Up @@ -191,7 +196,8 @@ func (ap *autoPprof) watchCPUUsage() {
"autopprof: failed to report the cpu profile: %w", err,
))
}
if ap.reportBoth && !ap.disableMemProf {

if (ap.reportBoth || ap.reportAll) && !ap.disableMemProf {
memUsage, err := ap.cgroupQueryer.MemUsage()
if err != nil {
log.Println(err)
Expand All @@ -203,6 +209,15 @@ func (ap *autoPprof) watchCPUUsage() {
))
}
}

if ap.reportAll && !ap.disableGoroutineProf {
goroutineCount := ap.runtimeQueryer.GoroutineCount()
if err := ap.reportGoroutineProfile(goroutineCount); err != nil {
log.Println(fmt.Errorf(
"autopprof: failed to report the goroutine profile: %w", err,
))
}
}
}

consecutiveOverThresholdCnt++
Expand Down Expand Up @@ -268,7 +283,7 @@ func (ap *autoPprof) watchMemUsage() {
"autopprof: failed to report the heap profile: %w", err,
))
}
if ap.reportBoth && !ap.disableCPUProf {
if (ap.reportBoth || ap.reportAll) && !ap.disableCPUProf {
cpuUsage, err := ap.cgroupQueryer.CPUUsage()
if err != nil {
log.Println(err)
Expand All @@ -280,6 +295,15 @@ func (ap *autoPprof) watchMemUsage() {
))
}
}

if ap.reportAll && !ap.disableGoroutineProf {
goroutineCount := ap.runtimeQueryer.GoroutineCount()
if err := ap.reportGoroutineProfile(goroutineCount); err != nil {
log.Println(fmt.Errorf(
"autopprof: failed to report the goroutine profile: %w", err,
))
}
}
}

consecutiveOverThresholdCnt++
Expand Down Expand Up @@ -342,6 +366,32 @@ func (ap *autoPprof) watchGoroutineCount() {
"autopprof: failed to report the goroutine profile: %w", err,
))
}

if ap.reportAll && !ap.disableCPUProf {
cpuUsage, err := ap.cgroupQueryer.CPUUsage()
if err != nil {
log.Println(err)
return
}
if err := ap.reportCPUProfile(cpuUsage); err != nil {
log.Println(fmt.Errorf(
"autopprof: failed to report the cpu profile: %w", err,
))
}
}

if ap.reportAll && !ap.disableMemProf {
memUsage, err := ap.cgroupQueryer.MemUsage()
if err != nil {
log.Println(err)
return
}
if err := ap.reportHeapProfile(memUsage); err != nil {
log.Println(fmt.Errorf(
"autopprof: failed to report the heap profile: %w", err,
))
}
}
}

consecutiveOverThresholdCnt++
Expand Down
119 changes: 80 additions & 39 deletions autopprof_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -699,37 +699,39 @@ func TestAutoPprof_watchMemUsage_consecutive(t *testing.T) {

func TestAutoPprof_watchMemUsage_reportBoth(t *testing.T) {
type fields struct {
watchInterval time.Duration
memThreshold float64
reportBoth bool
disableCPUProf bool
stopC chan struct{}
watchInterval time.Duration
memThreshold float64
reportAll bool
disableCPUProf bool
disableGoroutineProf bool
stopC chan struct{}
}
testCases := []struct {
name string
fields fields
mockFunc func(*queryer.MockCgroupsQueryer, *Mockprofiler, *report.MockReporter)
mockFunc func(*queryer.MockCgroupsQueryer, *queryer.MockRuntimeQueryer, *Mockprofiler, *report.MockReporter)
}{
{
name: "reportBoth: true",
name: "reportAll: true",
fields: fields{
watchInterval: 1 * time.Second,
memThreshold: 0.5, // 50%.
reportBoth: true,
disableCPUProf: false,
stopC: make(chan struct{}),
watchInterval: 1 * time.Second,
memThreshold: 0.5, // 50%.
reportAll: true,
disableCPUProf: false,
disableGoroutineProf: false,
stopC: make(chan struct{}),
},
mockFunc: func(mockQueryer *queryer.MockCgroupsQueryer, mockProfiler *Mockprofiler, mockReporter *report.MockReporter) {
mockFunc: func(mockCgroupsQueryer *queryer.MockCgroupsQueryer, mockRuntimeQueryer *queryer.MockRuntimeQueryer, mockProfiler *Mockprofiler, mockReporter *report.MockReporter) {
gomock.InOrder(
mockQueryer.EXPECT().
mockCgroupsQueryer.EXPECT().
MemUsage().
AnyTimes().
Return(0.6, nil),

mockProfiler.EXPECT().
profileHeap().
AnyTimes().
Return([]byte("cpu_prof"), nil),
Return([]byte("mem_prof"), nil),

mockReporter.EXPECT().
ReportHeapProfile(gomock.Any(), gomock.Any(), report.MemInfo{
Expand All @@ -739,15 +741,15 @@ func TestAutoPprof_watchMemUsage_reportBoth(t *testing.T) {
AnyTimes().
Return(nil),

mockQueryer.EXPECT().
mockCgroupsQueryer.EXPECT().
CPUUsage().
AnyTimes().
Return(0.2, nil),

mockProfiler.EXPECT().
profileCPU().
AnyTimes().
Return([]byte("mem_prof"), nil),
Return([]byte("cpu_prof"), nil),

mockReporter.EXPECT().
ReportCPUProfile(gomock.Any(), gomock.Any(), report.CPUInfo{
Expand All @@ -756,29 +758,48 @@ func TestAutoPprof_watchMemUsage_reportBoth(t *testing.T) {
}).
AnyTimes().
Return(nil),

mockRuntimeQueryer.EXPECT().
GoroutineCount().
AnyTimes().
Return(200, nil),

mockProfiler.EXPECT().
profileGoroutine().
AnyTimes().
Return([]byte("goroutine_prof"), nil),

mockReporter.EXPECT().
ReportGoroutineProfile(gomock.Any(), gomock.Any(), report.GoroutineInfo{
ThresholdCount: 500,
Count: 200,
}).
AnyTimes().
Return(nil),
)
},
},
{
name: "reportBoth: true, disableCPUProf: true",
name: "reportAll: true, disableCPUProf: true",
fields: fields{
watchInterval: 1 * time.Second,
memThreshold: 0.5, // 50%.
reportBoth: true,
disableCPUProf: true,
stopC: make(chan struct{}),
watchInterval: 1 * time.Second,
memThreshold: 0.5, // 50%.
reportAll: true,
disableCPUProf: true,
disableGoroutineProf: false,
stopC: make(chan struct{}),
},
mockFunc: func(mockQueryer *queryer.MockCgroupsQueryer, mockProfiler *Mockprofiler, mockReporter *report.MockReporter) {
mockFunc: func(mockCgroupsQueryer *queryer.MockCgroupsQueryer, mockRuntimeQueryer *queryer.MockRuntimeQueryer, mockProfiler *Mockprofiler, mockReporter *report.MockReporter) {
gomock.InOrder(
mockQueryer.EXPECT().
mockCgroupsQueryer.EXPECT().
MemUsage().
AnyTimes().
Return(0.6, nil),

mockProfiler.EXPECT().
profileHeap().
AnyTimes().
Return([]byte("cpu_prof"), nil),
Return([]byte("mem_prof"), nil),

mockReporter.EXPECT().
ReportHeapProfile(gomock.Any(), gomock.Any(), report.MemInfo{
Expand All @@ -787,29 +808,48 @@ func TestAutoPprof_watchMemUsage_reportBoth(t *testing.T) {
}).
AnyTimes().
Return(nil),

mockRuntimeQueryer.EXPECT().
GoroutineCount().
AnyTimes().
Return(200, nil),

mockProfiler.EXPECT().
profileGoroutine().
AnyTimes().
Return([]byte("goroutine_prof"), nil),

mockReporter.EXPECT().
ReportGoroutineProfile(gomock.Any(), gomock.Any(), report.GoroutineInfo{
ThresholdCount: 500,
Count: 200,
}).
AnyTimes().
Return(nil),
)
},
},
{
name: "reportBoth: false",
name: "reportAll: false",
fields: fields{
watchInterval: 1 * time.Second,
memThreshold: 0.5, // 50%.
reportBoth: false,
disableCPUProf: false,
stopC: make(chan struct{}),
watchInterval: 1 * time.Second,
memThreshold: 0.5, // 50%.
reportAll: false,
disableCPUProf: false,
disableGoroutineProf: false,
stopC: make(chan struct{}),
},
mockFunc: func(mockQueryer *queryer.MockCgroupsQueryer, mockProfiler *Mockprofiler, mockReporter *report.MockReporter) {
mockFunc: func(mockCgroupsQueryer *queryer.MockCgroupsQueryer, mockRuntimeQueryer *queryer.MockRuntimeQueryer, mockProfiler *Mockprofiler, mockReporter *report.MockReporter) {
gomock.InOrder(
mockQueryer.EXPECT().
mockCgroupsQueryer.EXPECT().
MemUsage().
AnyTimes().
Return(0.6, nil),

mockProfiler.EXPECT().
profileHeap().
AnyTimes().
Return([]byte("cpu_prof"), nil),
Return([]byte("mem_prof"), nil),

mockReporter.EXPECT().
ReportHeapProfile(gomock.Any(), gomock.Any(), report.MemInfo{
Expand All @@ -826,23 +866,24 @@ func TestAutoPprof_watchMemUsage_reportBoth(t *testing.T) {
t.Run(tc.name, func(t *testing.T) {
ctrl := gomock.NewController(t)

mockQueryer := queryer.NewMockCgroupsQueryer(ctrl)
mockCgroupsQueryer := queryer.NewMockCgroupsQueryer(ctrl)
mockRuntimeQueryer := queryer.NewMockRuntimeQueryer(ctrl)
mockProfiler := NewMockprofiler(ctrl)
mockReporter := report.NewMockReporter(ctrl)

ap := &autoPprof{
watchInterval: tc.fields.watchInterval,
cpuThreshold: 0.5, // 50%.
memThreshold: tc.fields.memThreshold,
cgroupQueryer: mockQueryer,
cgroupQueryer: mockCgroupsQueryer,
profiler: mockProfiler,
reporter: mockReporter,
reportBoth: tc.fields.reportBoth,
reportAll: tc.fields.reportAll,
disableCPUProf: tc.fields.disableCPUProf,
stopC: tc.fields.stopC,
}

tc.mockFunc(mockQueryer, mockProfiler, mockReporter)
tc.mockFunc(mockCgroupsQueryer, mockRuntimeQueryer, mockProfiler, mockReporter)

go ap.watchMemUsage()
defer ap.stop()
Expand Down
5 changes: 5 additions & 0 deletions option.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,10 +42,15 @@ type Option struct {
// is higher than this threshold.
GoroutineThreshold int

// deprecated: use reportAll instead.
// ReportBoth sets whether to trigger reports for both CPU and memory when either threshold is exceeded.
// If some profiling is disabled, exclude it.
ReportBoth bool

// ReportAll sets whether to trigger reports for all profiling types when any threshold is exceeded.
// If some profiling is disabled, exclude it.
ReportAll bool

// Reporter is the reporter to send the profiling report implementing
// the report.Reporter interface.
Reporter report.Reporter
Expand Down

0 comments on commit 6d6adb6

Please sign in to comment.