From 6d6adb613c36c9793ef3339a117b476538453259 Mon Sep 17 00:00:00 2001 From: jake-shin0 Date: Wed, 24 Jul 2024 17:37:32 +0900 Subject: [PATCH] feat: add reportAll option and deprecate reportBoth option --- autopprof.go | 54 ++++++++++++++++++++- autopprof_test.go | 119 +++++++++++++++++++++++++++++++--------------- option.go | 5 ++ 3 files changed, 137 insertions(+), 41 deletions(-) diff --git a/autopprof.go b/autopprof.go index 17a25fb..9210d8b 100644 --- a/autopprof.go +++ b/autopprof.go @@ -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 @@ -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) @@ -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++ @@ -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) @@ -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++ @@ -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++ diff --git a/autopprof_test.go b/autopprof_test.go index 5903204..c6719d8 100644 --- a/autopprof_test.go +++ b/autopprof_test.go @@ -699,29 +699,31 @@ 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), @@ -729,7 +731,7 @@ func TestAutoPprof_watchMemUsage_reportBoth(t *testing.T) { mockProfiler.EXPECT(). profileHeap(). AnyTimes(). - Return([]byte("cpu_prof"), nil), + Return([]byte("mem_prof"), nil), mockReporter.EXPECT(). ReportHeapProfile(gomock.Any(), gomock.Any(), report.MemInfo{ @@ -739,7 +741,7 @@ func TestAutoPprof_watchMemUsage_reportBoth(t *testing.T) { AnyTimes(). Return(nil), - mockQueryer.EXPECT(). + mockCgroupsQueryer.EXPECT(). CPUUsage(). AnyTimes(). Return(0.2, nil), @@ -747,7 +749,7 @@ func TestAutoPprof_watchMemUsage_reportBoth(t *testing.T) { mockProfiler.EXPECT(). profileCPU(). AnyTimes(). - Return([]byte("mem_prof"), nil), + Return([]byte("cpu_prof"), nil), mockReporter.EXPECT(). ReportCPUProfile(gomock.Any(), gomock.Any(), report.CPUInfo{ @@ -756,21 +758,40 @@ 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), @@ -778,7 +799,7 @@ func TestAutoPprof_watchMemUsage_reportBoth(t *testing.T) { mockProfiler.EXPECT(). profileHeap(). AnyTimes(). - Return([]byte("cpu_prof"), nil), + Return([]byte("mem_prof"), nil), mockReporter.EXPECT(). ReportHeapProfile(gomock.Any(), gomock.Any(), report.MemInfo{ @@ -787,21 +808,40 @@ 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), @@ -809,7 +849,7 @@ func TestAutoPprof_watchMemUsage_reportBoth(t *testing.T) { mockProfiler.EXPECT(). profileHeap(). AnyTimes(). - Return([]byte("cpu_prof"), nil), + Return([]byte("mem_prof"), nil), mockReporter.EXPECT(). ReportHeapProfile(gomock.Any(), gomock.Any(), report.MemInfo{ @@ -826,7 +866,8 @@ 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) @@ -834,15 +875,15 @@ func TestAutoPprof_watchMemUsage_reportBoth(t *testing.T) { 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() diff --git a/option.go b/option.go index 6b836a0..e4b161f 100644 --- a/option.go +++ b/option.go @@ -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