-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathapi.go
More file actions
104 lines (85 loc) · 3.01 KB
/
api.go
File metadata and controls
104 lines (85 loc) · 3.01 KB
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
package collector
import (
"fmt"
"os"
"os/exec"
"path"
"path/filepath"
"github.com/AlexsanderHamir/prof/config"
"github.com/AlexsanderHamir/prof/shared"
)
const globalSign = "*"
// RunCollector handles data organization without wrapping go test.
func RunCollector(files []string, tag string) error {
if err := ensureDirExists(shared.MainDirOutput); err != nil {
return err
}
tagDir := filepath.Join(shared.MainDirOutput, tag)
err := shared.CleanOrCreateDir(tagDir)
if err != nil {
return fmt.Errorf("CleanOrCreateDir failed: %w", err)
}
cfg, err := config.LoadFromFile(shared.ConfigFilename)
if err != nil {
cfg = &config.Config{}
}
var functionFilter config.FunctionFilter
globalFilter, hasGlobalFilter := cfg.FunctionFilter[globalSign]
if hasGlobalFilter {
functionFilter = globalFilter
}
var profileDirPath string
for _, fullBinaryPath := range files {
fileName := getFileName(fullBinaryPath)
profileDirPath, err = createProfileDirectory(tagDir, fileName)
if err != nil {
return fmt.Errorf("createProfileDirectory failed: %w", err)
}
if !hasGlobalFilter {
localFilter, hasLocalFilter := cfg.FunctionFilter[fileName]
if hasLocalFilter {
functionFilter = localFilter
}
}
outputTextFilePath := path.Join(profileDirPath, fileName+"."+shared.TextExtension)
if err = GenerateProfileTextOutput(fullBinaryPath, outputTextFilePath); err != nil {
return err
}
if err = collectFunctions(outputTextFilePath, profileDirPath, fullBinaryPath, functionFilter); err != nil {
return fmt.Errorf("collectFunctions failed: %w", err)
}
}
return nil
}
func GenerateProfileTextOutput(binaryFile, outputFile string) error {
pprofTextParams := getPprofTextParams()
cmd := append([]string{"go", "tool", "pprof"}, pprofTextParams...)
cmd = append(cmd, binaryFile)
// #nosec G204 -- cmd is constructed internally by generateTextProfile(), not from user input
execCmd := exec.Command(cmd[0], cmd[1:]...)
output, err := execCmd.Output()
if err != nil {
return fmt.Errorf("pprof command failed: %w", err)
}
return os.WriteFile(outputFile, output, shared.PermFile)
}
func GeneratePNGVisualization(binaryFile, outputFile string) error {
cmd := []string{"go", "tool", "pprof", "-png", binaryFile}
// #nosec G204 -- cmd is constructed internally by generatePNGVisualization(), not from user input
execCmd := exec.Command(cmd[0], cmd[1:]...)
output, err := execCmd.Output()
if err != nil {
return fmt.Errorf("pprof PNG generation failed: %w", err)
}
return os.WriteFile(outputFile, output, shared.PermFile)
}
// SaveAllFunctionsPprofContents calls [GetFunctionPprofContent] sequentially.
func SaveAllFunctionsPprofContents(functions []string, binaryPath, basePath string) error {
for _, functionName := range functions {
outputFile := filepath.Join(basePath, functionName+"."+shared.TextExtension)
if err := getFunctionPprofContent(functionName, binaryPath, outputFile); err != nil {
return fmt.Errorf("failed to extract function content for %s: %w", functionName, err)
}
}
return nil
}