gvisor.dev/gvisor@v0.0.0-20240520182842-f9d4d51c7e0f/test/metricsviz/metricsviz_cli/metricsviz_cli_test.go (about) 1 // Copyright 2024 The gVisor Authors. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 // Package metricsviz_cli_test tests metricsviz_cli. 16 package metricsviz_cli_test 17 18 import ( 19 "context" 20 "fmt" 21 "math/rand/v2" 22 "os" 23 "os/exec" 24 "path" 25 "strings" 26 "testing" 27 "time" 28 29 "gvisor.dev/gvisor/pkg/metric" 30 "gvisor.dev/gvisor/pkg/test/testutil" 31 "gvisor.dev/gvisor/test/metricsviz" 32 ) 33 34 func TestMetricsvizCLI(t *testing.T) { 35 ctx := context.Background() 36 cliPath, err := testutil.FindFile("test/metricsviz/metricsviz_cli/metricsviz_cli") 37 if err != nil { 38 t.Fatalf("Failed to find metricsviz_cli: %v", err) 39 } 40 const testMetricName = "/metricsviz_cli_test/counter" 41 testMetric := metric.MustCreateNewUint64Metric(testMetricName, true, fmt.Sprintf("test counter for %s", t.Name())) 42 if err := metric.Initialize(); err != nil { 43 t.Fatalf("Failed to initialize metrics: %v", err) 44 } 45 tempDir := t.TempDir() 46 for _, lossy := range []bool{true, false} { 47 t.Run(fmt.Sprintf("lossy=%v", lossy), func(t *testing.T) { 48 logFilePath := path.Join(tempDir, fmt.Sprintf("lossy=%v.log", lossy)) 49 logFile, err := os.Create(logFilePath) 50 if err != nil { 51 t.Fatalf("Failed to create log file %q: %v", logFilePath, err) 52 } 53 err = metric.StartProfilingMetrics(metric.ProfilingMetricsOptions[*os.File]{ 54 Sink: logFile, 55 Lossy: lossy, 56 Metrics: testMetricName, 57 Rate: time.Millisecond, 58 }) 59 if err != nil { 60 t.Fatalf("Failed to start profiling metrics: %v", err) 61 } 62 63 // Generate some counter increments for 25ms. 64 waitCtx, waitCancel := context.WithTimeout(ctx, 25*time.Millisecond) 65 defer waitCancel() 66 for waitCtx.Err() == nil { 67 testMetric.Increment() 68 select { 69 case <-waitCtx.Done(): 70 case <-time.After(time.Millisecond): 71 if lossy { 72 // Also inject some crap in the logs to verify that it can deal 73 // with text being written in the middle of metrics data. 74 randomLogs := [][]byte{ 75 []byte("some log"), 76 []byte("some log with a newline\n"), 77 []byte("a log with\rcarriage return in the middle"), 78 []byte("a log with\nmultiple\nnewlines"), 79 []byte{0x01, 0x02, 0x00, 0x03}, // Non-ASCII bytes. 80 } 81 if _, err := logFile.Write(randomLogs[rand.IntN(len(randomLogs))]); err != nil { 82 t.Fatalf("Failed to write random log: %v", err) 83 } 84 } 85 } 86 } 87 88 metric.StopProfilingMetrics() 89 logFileContents, err := os.ReadFile(logFilePath) 90 if err != nil { 91 t.Fatalf("Failed to read log file %q: %v", logFilePath, err) 92 } 93 if len(logFileContents) == 0 { 94 t.Fatalf("Log file %q is empty", logFilePath) 95 } 96 t.Logf("Log file %q contents:\n%s\n(end of log file contents)", logFilePath, string(logFileContents)) 97 98 if output, err := exec.CommandContext(ctx, cliPath, logFilePath).CombinedOutput(); err != nil { 99 t.Fatalf("Failed to run metricsviz_cli: %v (output: %s)", err, strings.TrimSpace(string(output))) 100 } 101 if err = metricsviz.FromFile(ctx, logFilePath, t.Logf); err != nil { 102 t.Fatalf("Failed to generate metricsviz from %q: %v", logFilePath, err) 103 } 104 expectedHTMLPath := path.Join(tempDir, fmt.Sprintf("lossy=%v.html", lossy)) 105 htmlStat, err := os.Stat(expectedHTMLPath) 106 if err != nil { 107 t.Fatalf("Failed to stat %q: %v", expectedHTMLPath, err) 108 } 109 if htmlStat.Size() == 0 { 110 t.Fatalf("HTML file %q is empty", expectedHTMLPath) 111 } 112 }) 113 } 114 }