Skip to content

Commit 2dd13e4

Browse files
committed
scripts module handles perf copy
Signed-off-by: Harper, Jason M <[email protected]>
1 parent 66c5219 commit 2dd13e4

File tree

3 files changed

+51
-115
lines changed

3 files changed

+51
-115
lines changed

cmd/metrics/metadata.go

Lines changed: 26 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,7 @@ type Metadata struct {
7777

7878
// LoadMetadata - populates and returns a Metadata structure containing state of the
7979
// system.
80-
func LoadMetadata(myTarget target.Target, noRoot bool, noSystemSummary bool, perfPath string, localTempDir string) (Metadata, error) {
80+
func LoadMetadata(myTarget target.Target, noRoot bool, noSystemSummary bool, localTempDir string) (Metadata, error) {
8181
uarch, err := myTarget.GetArchitecture()
8282
if err != nil {
8383
return Metadata{}, fmt.Errorf("failed to get target architecture: %v", err)
@@ -86,11 +86,11 @@ func LoadMetadata(myTarget target.Target, noRoot bool, noSystemSummary bool, per
8686
if err != nil {
8787
return Metadata{}, fmt.Errorf("failed to create metadata collector: %v", err)
8888
}
89-
return collector.CollectMetadata(myTarget, noRoot, noSystemSummary, perfPath, localTempDir)
89+
return collector.CollectMetadata(myTarget, noRoot, noSystemSummary, localTempDir)
9090
}
9191

9292
type MetadataCollector interface {
93-
CollectMetadata(myTarget target.Target, noRoot bool, noSystemSummary bool, perfPath string, localTempDir string) (Metadata, error)
93+
CollectMetadata(myTarget target.Target, noRoot bool, noSystemSummary bool, localTempDir string) (Metadata, error)
9494
}
9595

9696
func NewMetadataCollector(architecture string) (MetadataCollector, error) {
@@ -112,7 +112,7 @@ type X86MetadataCollector struct {
112112
type ARMMetadataCollector struct {
113113
}
114114

115-
func (c *X86MetadataCollector) CollectMetadata(myTarget target.Target, noRoot bool, noSystemSummary bool, perfPath string, localTempDir string) (Metadata, error) {
115+
func (c *X86MetadataCollector) CollectMetadata(myTarget target.Target, noRoot bool, noSystemSummary bool, localTempDir string) (Metadata, error) {
116116
var metadata Metadata
117117
var err error
118118
// Hostname
@@ -158,7 +158,7 @@ func (c *X86MetadataCollector) CollectMetadata(myTarget target.Target, noRoot bo
158158
return Metadata{}, fmt.Errorf("failed to get number of general purpose counters: %v", err)
159159
}
160160
// the rest of the metadata is retrieved by running scripts in parallel
161-
metadataScripts, err := getMetadataScripts(noRoot, perfPath, noSystemSummary, metadata.NumGeneralPurposeCounters)
161+
metadataScripts, err := getMetadataScripts(noRoot, noSystemSummary, metadata.NumGeneralPurposeCounters)
162162
if err != nil {
163163
return Metadata{}, fmt.Errorf("failed to get metadata scripts: %v", err)
164164
}
@@ -282,7 +282,7 @@ func (c *X86MetadataCollector) CollectMetadata(myTarget target.Target, noRoot bo
282282
}
283283
return metadata, nil
284284
}
285-
func (c *ARMMetadataCollector) CollectMetadata(myTarget target.Target, noRoot bool, noSystemSummary bool, perfPath string, localTempDir string) (Metadata, error) {
285+
func (c *ARMMetadataCollector) CollectMetadata(myTarget target.Target, noRoot bool, noSystemSummary bool, localTempDir string) (Metadata, error) {
286286
var metadata Metadata
287287
// Hostname
288288
metadata.Hostname = myTarget.GetName()
@@ -342,7 +342,7 @@ func (c *ARMMetadataCollector) CollectMetadata(myTarget target.Target, noRoot bo
342342
return Metadata{}, fmt.Errorf("failed to get number of general purpose counters: %v", err)
343343
}
344344
// the rest of the metadata is retrieved by running scripts in parallel and then parsing the output
345-
metadataScripts, err := getMetadataScripts(noRoot, perfPath, noSystemSummary, metadata.NumGeneralPurposeCounters)
345+
metadataScripts, err := getMetadataScripts(noRoot, noSystemSummary, metadata.NumGeneralPurposeCounters)
346346
if err != nil {
347347
return Metadata{}, fmt.Errorf("failed to get metadata scripts: %v", err)
348348
}
@@ -397,7 +397,7 @@ func (c *ARMMetadataCollector) CollectMetadata(myTarget target.Target, noRoot bo
397397
return metadata, nil
398398
}
399399

400-
func getMetadataScripts(noRoot bool, perfPath string, noSystemSummary bool, numGPCounters int) (metadataScripts []script.ScriptDefinition, err error) {
400+
func getMetadataScripts(noRoot bool, noSystemSummary bool, numGPCounters int) (metadataScripts []script.ScriptDefinition, err error) {
401401
// reduce startup time by running the metadata scripts in parallel
402402
metadataScriptDefs := []script.ScriptDefinition{
403403
{
@@ -407,8 +407,9 @@ func getMetadataScripts(noRoot bool, perfPath string, noSystemSummary bool, numG
407407
},
408408
{
409409
Name: "perf supported events",
410-
ScriptTemplate: perfPath + " list",
410+
ScriptTemplate: "perf list",
411411
Superuser: !noRoot,
412+
Depends: []string{"perf"},
412413
},
413414
{
414415
Name: "list uncore devices",
@@ -418,46 +419,54 @@ func getMetadataScripts(noRoot bool, perfPath string, noSystemSummary bool, numG
418419
},
419420
{
420421
Name: "perf stat instructions",
421-
ScriptTemplate: perfPath + " stat -a -e instructions sleep 1",
422+
ScriptTemplate: "perf stat -a -e instructions sleep 1",
422423
Superuser: !noRoot,
424+
Depends: []string{"perf"},
423425
},
424426
{
425427
Name: "perf stat ref-cycles",
426-
ScriptTemplate: perfPath + " stat -a -e ref-cycles sleep 1",
428+
ScriptTemplate: "perf stat -a -e ref-cycles sleep 1",
427429
Superuser: !noRoot,
430+
Depends: []string{"perf"},
428431
},
429432
{
430433
Name: "perf stat pebs",
431-
ScriptTemplate: perfPath + " stat -a -e INT_MISC.UNKNOWN_BRANCH_CYCLES sleep 1",
434+
ScriptTemplate: "perf stat -a -e INT_MISC.UNKNOWN_BRANCH_CYCLES sleep 1",
432435
Superuser: !noRoot,
433436
Architectures: []string{cpus.X86Architecture},
437+
Depends: []string{"perf"},
434438
},
435439
{
436440
Name: "perf stat ocr",
437-
ScriptTemplate: perfPath + " stat -a -e OCR.READS_TO_CORE.LOCAL_DRAM sleep 1",
441+
ScriptTemplate: "perf stat -a -e OCR.READS_TO_CORE.LOCAL_DRAM sleep 1",
438442
Superuser: !noRoot,
439443
Architectures: []string{cpus.X86Architecture},
444+
Depends: []string{"perf"},
440445
},
441446
{
442447
Name: "perf stat tma",
443-
ScriptTemplate: perfPath + " stat -a -e '{topdown.slots, topdown-bad-spec}' sleep 1",
448+
ScriptTemplate: "perf stat -a -e '{topdown.slots, topdown-bad-spec}' sleep 1",
444449
Superuser: !noRoot,
445450
Architectures: []string{cpus.X86Architecture},
451+
Depends: []string{"perf"},
446452
},
447453
{
448454
Name: "perf stat fixed instructions",
449-
ScriptTemplate: perfPath + " stat -a -e '{{{.InstructionsList}}}' sleep 1",
455+
ScriptTemplate: "perf stat -a -e '{{{.InstructionsList}}}' sleep 1",
450456
Superuser: !noRoot,
457+
Depends: []string{"perf"},
451458
},
452459
{
453460
Name: "perf stat fixed cpu-cycles",
454-
ScriptTemplate: perfPath + " stat -a -e '{{{.CpuCyclesList}}}' sleep 1",
461+
ScriptTemplate: "perf stat -a -e '{{{.CpuCyclesList}}}' sleep 1",
455462
Superuser: !noRoot,
463+
Depends: []string{"perf"},
456464
},
457465
{
458466
Name: "perf stat fixed ref-cycles",
459-
ScriptTemplate: perfPath + " stat -a -e '{{{.RefCyclesList}}}' sleep 1",
467+
ScriptTemplate: "perf stat -a -e '{{{.RefCyclesList}}}' sleep 1",
460468
Superuser: !noRoot,
469+
Depends: []string{"perf"},
461470
},
462471
{
463472
Name: "pmu driver version",

cmd/metrics/metrics.go

Lines changed: 5 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -652,7 +652,6 @@ func validateFlags(cmd *cobra.Command, args []string) error {
652652
type targetContext struct {
653653
target target.Target
654654
err error
655-
perfPath string
656655
metadata Metadata
657656
nmiDisabled bool
658657
perfMuxIntervalsSet bool
@@ -974,22 +973,14 @@ func runCmd(cmd *cobra.Command, args []string) error {
974973
return err
975974
}
976975
}
977-
// extract perf into local temp directory (assumes all targets have the same architecture)
978-
localPerfPath, err := extractPerf(myTargets[0], localTempDir)
979-
if err != nil {
980-
err = fmt.Errorf("failed to extract perf: %w", err)
981-
fmt.Fprintf(os.Stderr, "Error: %v\n", err)
982-
cmd.SilenceUsage = true
983-
return err
984-
}
985976
// prepare the targets
986977
channelTargetError := make(chan targetError)
987978
var targetContexts []targetContext
988979
for _, myTarget := range myTargets {
989980
targetContexts = append(targetContexts, targetContext{target: myTarget})
990981
}
991982
for i := range targetContexts {
992-
go prepareTarget(&targetContexts[i], localTempDir, localPerfPath, channelTargetError, multiSpinner.Status, !cmd.Flags().Lookup(flagPerfMuxIntervalName).Changed)
983+
go prepareTarget(&targetContexts[i], localTempDir, channelTargetError, multiSpinner.Status, !cmd.Flags().Lookup(flagPerfMuxIntervalName).Changed)
993984
}
994985
// wait for all targets to be prepared
995986
numPreparedTargets := 0
@@ -1144,7 +1135,7 @@ func runCmd(cmd *cobra.Command, args []string) error {
11441135
return err
11451136
}
11461137

1147-
func prepareTarget(targetContext *targetContext, localTempDir string, localPerfPath string, channelError chan targetError, statusUpdate progress.MultiSpinnerUpdateFunc, useDefaultMuxInterval bool) {
1138+
func prepareTarget(targetContext *targetContext, localTempDir string, channelError chan targetError, statusUpdate progress.MultiSpinnerUpdateFunc, useDefaultMuxInterval bool) {
11481139
myTarget := targetContext.target
11491140
var err error
11501141
_ = statusUpdate(myTarget.GetName(), "configuring target")
@@ -1215,15 +1206,6 @@ func prepareTarget(targetContext *targetContext, localTempDir string, localPerfP
12151206
}
12161207
targetContext.perfMuxIntervalsSet = true
12171208
}
1218-
// get the full path to the perf binary
1219-
if targetContext.perfPath, err = getPerfPath(myTarget, localPerfPath); err != nil {
1220-
err = fmt.Errorf("failed to find perf: %w", err)
1221-
_ = statusUpdate(myTarget.GetName(), fmt.Sprintf("Error: %v", err))
1222-
targetContext.err = err
1223-
channelError <- targetError{target: myTarget, err: err}
1224-
return
1225-
}
1226-
slog.Debug("Using Linux perf", slog.String("target", targetContext.target.GetName()), slog.String("path", targetContext.perfPath))
12271209
channelError <- targetError{target: myTarget, err: nil}
12281210
}
12291211

@@ -1240,7 +1222,7 @@ func prepareMetrics(targetContext *targetContext, localTempDir string, channelEr
12401222
if flagLive {
12411223
skipSystemSummary = true // no system summary when live, it doesn't get used/printed
12421224
}
1243-
if targetContext.metadata, err = LoadMetadata(myTarget, flagNoRoot, skipSystemSummary, targetContext.perfPath, localTempDir); err != nil {
1225+
if targetContext.metadata, err = LoadMetadata(myTarget, flagNoRoot, skipSystemSummary, localTempDir); err != nil {
12441226
_ = statusUpdate(myTarget.GetName(), fmt.Sprintf("Error: %s", err.Error()))
12451227
targetContext.err = err
12461228
channelError <- targetError{target: myTarget, err: err}
@@ -1379,8 +1361,7 @@ func collectOnTarget(targetContext *targetContext, localTempDir string, localOut
13791361
break
13801362
}
13811363
}
1382-
var perfCommand *exec.Cmd
1383-
perfCommand, err = getPerfCommand(targetContext.perfPath, targetContext.groupDefinitions, pids, cids, flagCpuRange)
1364+
perfCommand, err := getPerfCommand(targetContext.groupDefinitions, pids, cids, flagCpuRange)
13841365
if err != nil {
13851366
err = fmt.Errorf("failed to get perf command: %w", err)
13861367
_ = statusUpdate(myTarget.GetName(), fmt.Sprintf("Error: %s", err.Error()))
@@ -1417,9 +1398,8 @@ func collectOnTarget(targetContext *targetContext, localTempDir string, localOut
14171398
// until perf stops. When collecting for cgroups, perf will be manually terminated if/when the
14181399
// run duration exceeds the collection time or the time when the cgroup list needs
14191400
// to be refreshed.
1420-
func runPerf(myTarget target.Target, noRoot bool, processes []Process, cmd *exec.Cmd, eventGroupDefinitions []GroupDefinition, metricDefinitions []MetricDefinition, metadata Metadata, localTempDir string, outputDir string, frameChannel chan []MetricFrame, errorChannel chan error, signalMgr *signalManager) {
1401+
func runPerf(myTarget target.Target, noRoot bool, processes []Process, perfCommand string, eventGroupDefinitions []GroupDefinition, metricDefinitions []MetricDefinition, metadata Metadata, localTempDir string, outputDir string, frameChannel chan []MetricFrame, errorChannel chan error, signalMgr *signalManager) {
14211402
// start perf
1422-
perfCommand := strings.Join(cmd.Args, " ")
14231403
stdoutChannel := make(chan []byte)
14241404
stderrChannel := make(chan []byte)
14251405
exitcodeChannel := make(chan int)

cmd/metrics/perf.go

Lines changed: 20 additions & 73 deletions
Original file line numberDiff line numberDiff line change
@@ -5,59 +5,32 @@ package metrics
55

66
import (
77
"fmt"
8-
"log/slog"
9-
"os/exec"
10-
"path"
11-
"perfspect/internal/script"
12-
"perfspect/internal/target"
13-
"perfspect/internal/util"
148
"strings"
159
)
1610

17-
// extractPerf extracts the perf binary from the resources to the local temporary directory.
18-
func extractPerf(myTarget target.Target, localTempDir string) (string, error) {
19-
// get the target architecture
20-
arch, err := myTarget.GetArchitecture()
21-
if err != nil {
22-
return "", fmt.Errorf("failed to get target architecture: %w", err)
11+
// getPerfCommand is responsible for assembling the command that will be
12+
// executed to collect event data
13+
func getPerfCommand(eventGroups []GroupDefinition, pids []string, cids []string, cpuRange string) (string, error) {
14+
var duration int
15+
switch flagScope {
16+
case scopeSystem:
17+
duration = flagDuration
18+
case scopeProcess:
19+
if flagDuration > 0 {
20+
duration = flagDuration
21+
} else if len(flagPidList) == 0 { // don't refresh if PIDs are specified
22+
duration = flagRefresh // refresh hot processes every flagRefresh seconds
23+
}
24+
case scopeCgroup:
25+
duration = 0
2326
}
24-
// extract the perf binary
25-
return util.ExtractResource(script.Resources, path.Join("resources", arch, "perf"), localTempDir)
26-
}
2727

28-
// getPerfPath determines the path to the `perf` binary for the given target.
29-
// If the target is a local target, it uses the provided localPerfPath.
30-
// If the target is remote, it checks if `perf` version 6.1 or later is available on the target.
31-
// If available, it uses the `perf` binary on the target.
32-
// If not available, it pushes the local `perf` binary to the target's temporary directory and uses that.
33-
//
34-
// Parameters:
35-
// - myTarget: The target system where the `perf` binary is needed.
36-
// - localPerfPath: The local path to the `perf` binary.
37-
//
38-
// Returns:
39-
// - perfPath: The path to the `perf` binary on the target.
40-
// - err: An error if any occurred during the process.
41-
func getPerfPath(myTarget target.Target, localPerfPath string) (string, error) {
42-
if localPerfPath == "" {
43-
slog.Error("local perf path is empty, cannot determine perf path")
44-
return "", fmt.Errorf("local perf path is empty")
45-
}
46-
// local target
47-
if _, ok := myTarget.(*target.LocalTarget); ok {
48-
return localPerfPath, nil
49-
}
50-
// remote target
51-
targetTempDir := myTarget.GetTempDirectory()
52-
if targetTempDir == "" {
53-
slog.Error("target temporary directory is empty for remote target", slog.String("target", myTarget.GetName()))
54-
return "", fmt.Errorf("target temporary directory is empty for remote target %s", myTarget.GetName())
55-
}
56-
if err := myTarget.PushFile(localPerfPath, targetTempDir); err != nil {
57-
slog.Error("failed to push perf binary to remote directory", slog.String("error", err.Error()))
58-
return "", fmt.Errorf("failed to push perf binary to remote directory %s: %w", targetTempDir, err)
28+
args, err := getPerfCommandArgs(pids, cids, duration, eventGroups, cpuRange)
29+
if err != nil {
30+
err = fmt.Errorf("failed to assemble perf args: %v", err)
31+
return "", err
5932
}
60-
return path.Join(targetTempDir, "perf"), nil
33+
return strings.Join(append([]string{"perf"}, args...), " "), nil
6134
}
6235

6336
// getPerfCommandArgs returns the command arguments for the 'perf stat' command
@@ -120,29 +93,3 @@ func getPerfCommandArgs(pids []string, cgroups []string, timeout int, eventGroup
12093
}
12194
return
12295
}
123-
124-
// getPerfCommand is responsible for assembling the command that will be
125-
// executed to collect event data
126-
func getPerfCommand(perfPath string, eventGroups []GroupDefinition, pids []string, cids []string, cpuRange string) (*exec.Cmd, error) {
127-
var duration int
128-
switch flagScope {
129-
case scopeSystem:
130-
duration = flagDuration
131-
case scopeProcess:
132-
if flagDuration > 0 {
133-
duration = flagDuration
134-
} else if len(flagPidList) == 0 { // don't refresh if PIDs are specified
135-
duration = flagRefresh // refresh hot processes every flagRefresh seconds
136-
}
137-
case scopeCgroup:
138-
duration = 0
139-
}
140-
141-
args, err := getPerfCommandArgs(pids, cids, duration, eventGroups, cpuRange)
142-
if err != nil {
143-
err = fmt.Errorf("failed to assemble perf args: %v", err)
144-
return nil, err
145-
}
146-
perfCommand := exec.Command(perfPath, args...) // #nosec G204 // nosemgrep
147-
return perfCommand, nil
148-
}

0 commit comments

Comments
 (0)