| 
									
										
										
										
											2019-05-07 04:21:41 -07:00
										 |  |  | // Copyright 2019 The Prometheus Authors
 | 
					
						
							|  |  |  | // Licensed 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.
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-08-15 02:38:13 -07:00
										 |  |  | //go:build !noperf
 | 
					
						
							|  |  |  | // +build !noperf
 | 
					
						
							| 
									
										
										
										
											2019-05-07 04:21:41 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  | package collector | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | import ( | 
					
						
							| 
									
										
										
										
											2024-09-11 01:51:28 -07:00
										 |  |  | 	"io" | 
					
						
							|  |  |  | 	"log/slog" | 
					
						
							| 
									
										
										
										
											2022-07-27 11:59:39 -07:00
										 |  |  | 	"os" | 
					
						
							| 
									
										
										
										
											2020-04-17 02:59:07 -07:00
										 |  |  | 	"runtime" | 
					
						
							| 
									
										
										
										
											2019-05-07 04:21:41 -07:00
										 |  |  | 	"strconv" | 
					
						
							|  |  |  | 	"strings" | 
					
						
							|  |  |  | 	"testing" | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	"github.com/prometheus/client_golang/prometheus" | 
					
						
							|  |  |  | ) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-04-17 02:59:07 -07:00
										 |  |  | func canTestPerf(t *testing.T) { | 
					
						
							| 
									
										
										
										
											2022-07-27 11:59:39 -07:00
										 |  |  | 	paranoidBytes, err := os.ReadFile("/proc/sys/kernel/perf_event_paranoid") | 
					
						
							| 
									
										
										
										
											2019-05-07 04:21:41 -07:00
										 |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		t.Skip("Procfs not mounted, skipping perf tests") | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	paranoidStr := strings.Replace(string(paranoidBytes), "\n", "", -1) | 
					
						
							|  |  |  | 	paranoid, err := strconv.Atoi(paranoidStr) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		t.Fatalf("Expected perf_event_paranoid to be an int, got: %s", paranoidStr) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	if paranoid >= 1 { | 
					
						
							|  |  |  | 		t.Skip("Skipping perf tests, set perf_event_paranoid to 0") | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2020-04-17 02:59:07 -07:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func TestPerfCollector(t *testing.T) { | 
					
						
							|  |  |  | 	canTestPerf(t) | 
					
						
							| 
									
										
										
										
											2024-09-11 01:51:28 -07:00
										 |  |  | 	collector, err := NewPerfCollector(slog.New(slog.NewTextHandler(io.Discard, nil))) | 
					
						
							| 
									
										
										
										
											2019-05-07 04:21:41 -07:00
										 |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		t.Fatal(err) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// Setup background goroutine to capture metrics.
 | 
					
						
							|  |  |  | 	metrics := make(chan prometheus.Metric) | 
					
						
							|  |  |  | 	defer close(metrics) | 
					
						
							|  |  |  | 	go func() { | 
					
						
							| 
									
										
										
										
											2023-07-18 01:46:59 -07:00
										 |  |  | 		i := 0 | 
					
						
							| 
									
										
										
										
											2019-05-07 04:21:41 -07:00
										 |  |  | 		for range metrics { | 
					
						
							| 
									
										
										
										
											2023-07-18 01:46:59 -07:00
										 |  |  | 			i++ | 
					
						
							| 
									
										
										
										
											2019-05-07 04:21:41 -07:00
										 |  |  | 		} | 
					
						
							|  |  |  | 	}() | 
					
						
							|  |  |  | 	if err := collector.Update(metrics); err != nil { | 
					
						
							|  |  |  | 		t.Fatal(err) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2020-02-20 02:36:33 -08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-04-17 02:59:07 -07:00
										 |  |  | func TestPerfCollectorStride(t *testing.T) { | 
					
						
							|  |  |  | 	canTestPerf(t) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	tests := []struct { | 
					
						
							|  |  |  | 		name   string | 
					
						
							|  |  |  | 		flag   string | 
					
						
							| 
									
										
										
										
											2020-09-03 10:39:19 -07:00
										 |  |  | 		exCPUs []int | 
					
						
							| 
									
										
										
										
											2020-04-17 02:59:07 -07:00
										 |  |  | 	}{ | 
					
						
							|  |  |  | 		{ | 
					
						
							| 
									
										
										
										
											2020-09-03 10:39:19 -07:00
										 |  |  | 			name:   "valid single CPU", | 
					
						
							| 
									
										
										
										
											2020-04-17 02:59:07 -07:00
										 |  |  | 			flag:   "1", | 
					
						
							| 
									
										
										
										
											2020-09-03 10:39:19 -07:00
										 |  |  | 			exCPUs: []int{1}, | 
					
						
							| 
									
										
										
										
											2020-04-17 02:59:07 -07:00
										 |  |  | 		}, | 
					
						
							|  |  |  | 		{ | 
					
						
							| 
									
										
										
										
											2020-09-03 10:39:19 -07:00
										 |  |  | 			name:   "valid range CPUs", | 
					
						
							| 
									
										
										
										
											2020-04-17 02:59:07 -07:00
										 |  |  | 			flag:   "1-5", | 
					
						
							| 
									
										
										
										
											2020-09-03 10:39:19 -07:00
										 |  |  | 			exCPUs: []int{1, 2, 3, 4, 5}, | 
					
						
							| 
									
										
										
										
											2020-04-17 02:59:07 -07:00
										 |  |  | 		}, | 
					
						
							|  |  |  | 		{ | 
					
						
							|  |  |  | 			name:   "valid stride", | 
					
						
							|  |  |  | 			flag:   "1-8:2", | 
					
						
							| 
									
										
										
										
											2020-09-03 10:39:19 -07:00
										 |  |  | 			exCPUs: []int{1, 3, 5, 7}, | 
					
						
							| 
									
										
										
										
											2020-04-17 02:59:07 -07:00
										 |  |  | 		}, | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	for _, test := range tests { | 
					
						
							|  |  |  | 		t.Run(test.name, func(t *testing.T) { | 
					
						
							|  |  |  | 			ncpu := runtime.NumCPU() | 
					
						
							| 
									
										
										
										
											2020-09-03 10:39:19 -07:00
										 |  |  | 			for _, cpu := range test.exCPUs { | 
					
						
							| 
									
										
										
										
											2020-04-17 02:59:07 -07:00
										 |  |  | 				if cpu > ncpu { | 
					
						
							|  |  |  | 					t.Skipf("Skipping test because runtime.NumCPU < %d", cpu) | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 			perfCPUsFlag = &test.flag | 
					
						
							| 
									
										
										
										
											2024-09-11 01:51:28 -07:00
										 |  |  | 			collector, err := NewPerfCollector(slog.New(slog.NewTextHandler(io.Discard, nil))) | 
					
						
							| 
									
										
										
										
											2020-04-17 02:59:07 -07:00
										 |  |  | 			if err != nil { | 
					
						
							|  |  |  | 				t.Fatal(err) | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			c := collector.(*perfCollector) | 
					
						
							| 
									
										
										
										
											2020-09-03 10:39:19 -07:00
										 |  |  | 			for _, cpu := range test.exCPUs { | 
					
						
							| 
									
										
										
										
											2020-04-17 02:59:07 -07:00
										 |  |  | 				if _, ok := c.perfHwProfilers[cpu]; !ok { | 
					
						
							|  |  |  | 					t.Fatalf("Expected CPU %v in hardware profilers", cpu) | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 				if _, ok := c.perfSwProfilers[cpu]; !ok { | 
					
						
							|  |  |  | 					t.Fatalf("Expected CPU %v in software profilers", cpu) | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 				if _, ok := c.perfCacheProfilers[cpu]; !ok { | 
					
						
							|  |  |  | 					t.Fatalf("Expected CPU %v in cache profilers", cpu) | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		}) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-02-20 02:36:33 -08:00
										 |  |  | func TestPerfCPUFlagToCPUs(t *testing.T) { | 
					
						
							|  |  |  | 	tests := []struct { | 
					
						
							|  |  |  | 		name   string | 
					
						
							|  |  |  | 		flag   string | 
					
						
							|  |  |  | 		exCpus []int | 
					
						
							|  |  |  | 		errStr string | 
					
						
							|  |  |  | 	}{ | 
					
						
							|  |  |  | 		{ | 
					
						
							| 
									
										
										
										
											2020-09-03 10:39:19 -07:00
										 |  |  | 			name:   "valid single CPU", | 
					
						
							| 
									
										
										
										
											2020-02-20 02:36:33 -08:00
										 |  |  | 			flag:   "1", | 
					
						
							|  |  |  | 			exCpus: []int{1}, | 
					
						
							|  |  |  | 		}, | 
					
						
							|  |  |  | 		{ | 
					
						
							| 
									
										
										
										
											2020-09-03 10:39:19 -07:00
										 |  |  | 			name:   "valid range CPUs", | 
					
						
							| 
									
										
										
										
											2020-02-20 02:36:33 -08:00
										 |  |  | 			flag:   "1-5", | 
					
						
							|  |  |  | 			exCpus: []int{1, 2, 3, 4, 5}, | 
					
						
							|  |  |  | 		}, | 
					
						
							|  |  |  | 		{ | 
					
						
							|  |  |  | 			name:   "valid double digit", | 
					
						
							|  |  |  | 			flag:   "10", | 
					
						
							|  |  |  | 			exCpus: []int{10}, | 
					
						
							|  |  |  | 		}, | 
					
						
							|  |  |  | 		{ | 
					
						
							|  |  |  | 			name:   "valid double digit range", | 
					
						
							|  |  |  | 			flag:   "10-12", | 
					
						
							|  |  |  | 			exCpus: []int{10, 11, 12}, | 
					
						
							|  |  |  | 		}, | 
					
						
							|  |  |  | 		{ | 
					
						
							|  |  |  | 			name:   "valid double digit stride", | 
					
						
							|  |  |  | 			flag:   "10-20:5", | 
					
						
							|  |  |  | 			exCpus: []int{10, 15, 20}, | 
					
						
							|  |  |  | 		}, | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	for _, test := range tests { | 
					
						
							|  |  |  | 		t.Run(test.name, func(t *testing.T) { | 
					
						
							|  |  |  | 			cpus, err := perfCPUFlagToCPUs(test.flag) | 
					
						
							|  |  |  | 			if test.errStr != "" { | 
					
						
							|  |  |  | 				if err != nil { | 
					
						
							|  |  |  | 					t.Fatal("expected error to not be nil") | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 				if test.errStr != err.Error() { | 
					
						
							|  |  |  | 					t.Fatalf( | 
					
						
							|  |  |  | 						"expected error %q, got %q", | 
					
						
							|  |  |  | 						test.errStr, | 
					
						
							|  |  |  | 						err.Error(), | 
					
						
							|  |  |  | 					) | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 				return | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 			if err != nil { | 
					
						
							|  |  |  | 				t.Fatal(err) | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 			if len(cpus) != len(test.exCpus) { | 
					
						
							|  |  |  | 				t.Fatalf( | 
					
						
							| 
									
										
										
										
											2020-09-03 10:39:19 -07:00
										 |  |  | 					"expected CPUs %v, got %v", | 
					
						
							| 
									
										
										
										
											2020-02-20 02:36:33 -08:00
										 |  |  | 					test.exCpus, | 
					
						
							|  |  |  | 					cpus, | 
					
						
							|  |  |  | 				) | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 			for i := range cpus { | 
					
						
							|  |  |  | 				if test.exCpus[i] != cpus[i] { | 
					
						
							|  |  |  | 					t.Fatalf( | 
					
						
							| 
									
										
										
										
											2020-09-03 10:39:19 -07:00
										 |  |  | 						"expected CPUs %v, got %v", | 
					
						
							| 
									
										
										
										
											2020-04-17 03:02:08 -07:00
										 |  |  | 						test.exCpus[i], | 
					
						
							|  |  |  | 						cpus[i], | 
					
						
							|  |  |  | 					) | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		}) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func TestPerfTracepointFlagToTracepoints(t *testing.T) { | 
					
						
							|  |  |  | 	tests := []struct { | 
					
						
							|  |  |  | 		name          string | 
					
						
							|  |  |  | 		flag          []string | 
					
						
							|  |  |  | 		exTracepoints []*perfTracepoint | 
					
						
							|  |  |  | 		errStr        string | 
					
						
							|  |  |  | 	}{ | 
					
						
							|  |  |  | 		{ | 
					
						
							|  |  |  | 			name: "valid single tracepoint", | 
					
						
							|  |  |  | 			flag: []string{"sched:sched_kthread_stop"}, | 
					
						
							|  |  |  | 			exTracepoints: []*perfTracepoint{ | 
					
						
							|  |  |  | 				{ | 
					
						
							|  |  |  | 					subsystem: "sched", | 
					
						
							|  |  |  | 					event:     "sched_kthread_stop", | 
					
						
							|  |  |  | 				}, | 
					
						
							|  |  |  | 			}, | 
					
						
							|  |  |  | 		}, | 
					
						
							|  |  |  | 		{ | 
					
						
							|  |  |  | 			name: "valid multiple tracepoints", | 
					
						
							|  |  |  | 			flag: []string{"sched:sched_kthread_stop", "sched:sched_process_fork"}, | 
					
						
							|  |  |  | 			exTracepoints: []*perfTracepoint{ | 
					
						
							|  |  |  | 				{ | 
					
						
							|  |  |  | 					subsystem: "sched", | 
					
						
							|  |  |  | 					event:     "sched_kthread_stop", | 
					
						
							|  |  |  | 				}, | 
					
						
							|  |  |  | 				{ | 
					
						
							|  |  |  | 					subsystem: "sched", | 
					
						
							|  |  |  | 					event:     "sched_process_fork", | 
					
						
							|  |  |  | 				}, | 
					
						
							|  |  |  | 			}, | 
					
						
							|  |  |  | 		}, | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	for _, test := range tests { | 
					
						
							|  |  |  | 		t.Run(test.name, func(t *testing.T) { | 
					
						
							|  |  |  | 			tracepoints, err := perfTracepointFlagToTracepoints(test.flag) | 
					
						
							|  |  |  | 			if test.errStr != "" { | 
					
						
							|  |  |  | 				if err != nil { | 
					
						
							|  |  |  | 					t.Fatal("expected error to not be nil") | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 				if test.errStr != err.Error() { | 
					
						
							|  |  |  | 					t.Fatalf( | 
					
						
							|  |  |  | 						"expected error %q, got %q", | 
					
						
							|  |  |  | 						test.errStr, | 
					
						
							|  |  |  | 						err.Error(), | 
					
						
							|  |  |  | 					) | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 				return | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 			if err != nil { | 
					
						
							|  |  |  | 				t.Fatal(err) | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 			for i := range tracepoints { | 
					
						
							|  |  |  | 				if test.exTracepoints[i].event != tracepoints[i].event && | 
					
						
							|  |  |  | 					test.exTracepoints[i].subsystem != tracepoints[i].subsystem { | 
					
						
							|  |  |  | 					t.Fatalf( | 
					
						
							|  |  |  | 						"expected tracepoint %v, got %v", | 
					
						
							|  |  |  | 						test.exTracepoints[i], | 
					
						
							|  |  |  | 						tracepoints[i], | 
					
						
							| 
									
										
										
										
											2020-02-20 02:36:33 -08:00
										 |  |  | 					) | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		}) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } |