From def9b7c83b5ea2f12e64e490026405150925508e Mon Sep 17 00:00:00 2001 From: "Harper, Jason M" Date: Sat, 3 May 2025 17:57:08 -0700 Subject: [PATCH 1/6] Implement concurrent targets in config command Signed-off-by: Harper, Jason M --- cmd/config/config.go | 163 +++++++++++++++++++++++++++----------- internal/common/common.go | 38 ++++----- 2 files changed, 136 insertions(+), 65 deletions(-) diff --git a/cmd/config/config.go b/cmd/config/config.go index a95a74bc..ab865d6a 100644 --- a/cmd/config/config.go +++ b/cmd/config/config.go @@ -100,46 +100,38 @@ func runCmd(cmd *cobra.Command, args []string) error { cmd.SilenceUsage = true return err } - // make requested changes, one target at a time - changeRequested := false - for _, myTarget := range myTargets { - for _, group := range flagGroups { - for _, flag := range group.flags { - if cmd.Flags().Lookup(flag.GetName()).Changed { - changeRequested = true - fmt.Printf("%s setting %s to %s\n", myTarget.GetName(), flag.GetName(), flag.GetValueAsString()) - var err error - switch flag.GetType() { - case "int": - if flag.intSetFunc != nil { - value, _ := cmd.Flags().GetInt(flag.GetName()) - err = flag.intSetFunc(value, myTarget, localTempDir) - } - case "float64": - if flag.floatSetFunc != nil { - value, _ := cmd.Flags().GetFloat64(flag.GetName()) - err = flag.floatSetFunc(value, myTarget, localTempDir) - } - case "string": - if flag.stringSetFunc != nil { - value, _ := cmd.Flags().GetString(flag.GetName()) - err = flag.stringSetFunc(value, myTarget, localTempDir) - } - } - if err != nil { - fmt.Fprintf(os.Stderr, "%s Error: %v\n", myTarget.GetName(), err) - slog.Error(err.Error(), slog.String("target", myTarget.GetName())) - } - } + // if no changes were made, print a message and return + var changeRequested bool + for _, group := range flagGroups { + for _, flag := range group.flags { + if cmd.Flags().Lookup(flag.GetName()).Changed { + changeRequested = true + break } } + if changeRequested { + break + } } if !changeRequested { fmt.Println("No changes requested.") return nil } + // make requested changes on all targets + channelError := make(chan error) + multiSpinner := progress.NewMultiSpinner() + multiSpinner.Start() + for _, myTarget := range myTargets { + multiSpinner.AddSpinner(myTarget.GetName()) + go setOnTarget(cmd, myTarget, flagGroups, localTempDir, channelError, multiSpinner.Status) + } + // wait for all targets to finish + for range myTargets { + <-channelError + } + multiSpinner.Finish() + fmt.Println() // blank line // print config after making changes - fmt.Println("") // blank line if err := printConfig(myTargets, localTempDir); err != nil { fmt.Fprintf(os.Stderr, "Error: %v\n", err) slog.Error(err.Error()) @@ -148,50 +140,129 @@ func runCmd(cmd *cobra.Command, args []string) error { return nil } +func setOnTarget(cmd *cobra.Command, myTarget target.Target, flagGroups []flagGroup, localTempDir string, channelError chan error, statusUpdate progress.MultiSpinnerUpdateFunc) { + for _, group := range flagGroups { + for _, flag := range group.flags { + if cmd.Flags().Lookup(flag.GetName()).Changed { + statusUpdate(myTarget.GetName(), fmt.Sprintf("setting %s to %s", flag.GetName(), flag.GetValueAsString())) + var err error + switch flag.GetType() { + case "int": + if flag.intSetFunc != nil { + value, _ := cmd.Flags().GetInt(flag.GetName()) + err = flag.intSetFunc(value, myTarget, localTempDir) + } + case "float64": + if flag.floatSetFunc != nil { + value, _ := cmd.Flags().GetFloat64(flag.GetName()) + err = flag.floatSetFunc(value, myTarget, localTempDir) + } + case "string": + if flag.stringSetFunc != nil { + value, _ := cmd.Flags().GetString(flag.GetName()) + err = flag.stringSetFunc(value, myTarget, localTempDir) + } + } + if err != nil { + statusUpdate(myTarget.GetName(), fmt.Sprintf("error setting %s to %s: %v", flag.GetName(), flag.GetValueAsString(), err)) + slog.Error(err.Error(), slog.String("target", myTarget.GetName())) + channelError <- err + return + } + } + } + } + statusUpdate(myTarget.GetName(), "configuration complete") + channelError <- nil +} + func printConfig(myTargets []target.Target, localTempDir string) (err error) { scriptNames := report.GetScriptNamesForTable(report.ConfigurationTableName) var scriptsToRun []script.ScriptDefinition for _, scriptName := range scriptNames { scriptsToRun = append(scriptsToRun, script.GetScriptByName(scriptName)) } + multiSpinner := progress.NewMultiSpinner() + multiSpinner.Start() + orderedTargetScriptOutputs := []common.TargetScriptOutputs{} + channelTargetScriptOutputs := make(chan common.TargetScriptOutputs) + channelError := make(chan error) for _, myTarget := range myTargets { - multiSpinner := progress.NewMultiSpinner() err = multiSpinner.AddSpinner(myTarget.GetName()) if err != nil { err = fmt.Errorf("failed to add spinner: %v", err) return } - multiSpinner.Start() - _ = multiSpinner.Status(myTarget.GetName(), "collecting data") - // run the scripts - var scriptOutputs map[string]script.ScriptOutput - if scriptOutputs, err = script.RunScripts(myTarget, scriptsToRun, true, localTempDir); err != nil { - err = fmt.Errorf("failed to run collection scripts: %v", err) - _ = multiSpinner.Status(myTarget.GetName(), "error collecting data") - multiSpinner.Finish() - return + // run the selected scripts on the target + go collectOnTarget(myTarget, scriptsToRun, localTempDir, channelTargetScriptOutputs, channelError, multiSpinner.Status) + } + // wait for scripts to run on all targets + var allTargetScriptOutputs []common.TargetScriptOutputs + for range myTargets { + select { + case scriptOutputs := <-channelTargetScriptOutputs: + allTargetScriptOutputs = append(allTargetScriptOutputs, scriptOutputs) + case err := <-channelError: + slog.Error(err.Error()) } - _ = multiSpinner.Status(myTarget.GetName(), "collection complete") - multiSpinner.Finish() + } + // allTargetScriptOutputs is in the order of data collection completion + // reorder to match order of myTargets + for _, target := range myTargets { + for _, targetScriptOutputs := range allTargetScriptOutputs { + if targetScriptOutputs.TargetName == target.GetName() { + targetScriptOutputs.TableNames = []string{report.ConfigurationTableName} + orderedTargetScriptOutputs = append(orderedTargetScriptOutputs, targetScriptOutputs) + break + } + } + } + multiSpinner.Finish() + // process and print the table for each target + for _, targetScriptOutputs := range orderedTargetScriptOutputs { // process the tables, i.e., get field values from raw script output tableNames := []string{report.ConfigurationTableName} var tableValues []report.TableValues - if tableValues, err = report.ProcessTables(tableNames, scriptOutputs); err != nil { + if tableValues, err = report.ProcessTables(tableNames, targetScriptOutputs.ScriptOutputs); err != nil { err = fmt.Errorf("failed to process collected data: %v", err) return } // create the report for this single table var reportBytes []byte - if reportBytes, err = report.Create("txt", tableValues, scriptOutputs, myTarget.GetName()); err != nil { + if reportBytes, err = report.Create("txt", tableValues, targetScriptOutputs.ScriptOutputs, targetScriptOutputs.TargetName); err != nil { err = fmt.Errorf("failed to create report: %v", err) return } // print the report + if len(orderedTargetScriptOutputs) > 1 { + fmt.Printf("%s\n", targetScriptOutputs.TargetName) + } fmt.Print(string(reportBytes)) } return } +// collectOnTarget runs the scripts on the target and sends the results to the appropriate channels +func collectOnTarget(myTarget target.Target, scriptsToRun []script.ScriptDefinition, localTempDir string, channelTargetScriptOutputs chan common.TargetScriptOutputs, channelError chan error, statusUpdate progress.MultiSpinnerUpdateFunc) { + // run the scripts on the target + if statusUpdate != nil { + _ = statusUpdate(myTarget.GetName(), "collecting configuration") + } + scriptOutputs, err := script.RunScripts(myTarget, scriptsToRun, true, localTempDir) + if err != nil { + if statusUpdate != nil { + _ = statusUpdate(myTarget.GetName(), fmt.Sprintf("error collecting configuration: %v", err)) + } + err = fmt.Errorf("error running data collection scripts on %s: %v", myTarget.GetName(), err) + channelError <- err + return + } + if statusUpdate != nil { + _ = statusUpdate(myTarget.GetName(), "configuration collection complete") + } + channelTargetScriptOutputs <- common.TargetScriptOutputs{TargetName: myTarget.GetName(), ScriptOutputs: scriptOutputs} +} + func setCoreCount(cores int, myTarget target.Target, localTempDir string) error { setScript := script.ScriptDefinition{ Name: "set core count", diff --git a/internal/common/common.go b/internal/common/common.go index e27e18d5..de1f8508 100644 --- a/internal/common/common.go +++ b/internal/common/common.go @@ -44,16 +44,16 @@ type FlagGroup struct { } type TargetScriptOutputs struct { - targetName string - scriptOutputs map[string]script.ScriptOutput - tableNames []string + TargetName string + ScriptOutputs map[string]script.ScriptOutput + TableNames []string } func (tso *TargetScriptOutputs) GetScriptOutputs() map[string]script.ScriptOutput { - return tso.scriptOutputs + return tso.ScriptOutputs } func (tso *TargetScriptOutputs) GetTableNames() []string { - return tso.tableNames + return tso.TableNames } const ( @@ -247,7 +247,7 @@ func (rc *ReportingCommand) Run() error { adhocErrorChannel := make(chan error) for i, t := range myTargets { go func(target target.Target, i int) { - err := rc.AdhocFunc(appContext, orderedTargetScriptOutputs[i].scriptOutputs, target, multiSpinner.Status) + err := rc.AdhocFunc(appContext, orderedTargetScriptOutputs[i].ScriptOutputs, target, multiSpinner.Status) adhocErrorChannel <- err }(t, i) } @@ -295,7 +295,7 @@ func DefaultInsightsFunc(allTableValues []report.TableValues, scriptOutputs map[ // createRawReports creates the raw report(s) from the collected data func (rc *ReportingCommand) createRawReports(appContext AppContext, orderedTargetScriptOutputs []TargetScriptOutputs) error { for _, targetScriptOutputs := range orderedTargetScriptOutputs { - reportBytes, err := report.CreateRawReport(rc.TableNames, targetScriptOutputs.scriptOutputs, targetScriptOutputs.targetName) + reportBytes, err := report.CreateRawReport(rc.TableNames, targetScriptOutputs.ScriptOutputs, targetScriptOutputs.TargetName) if err != nil { err = fmt.Errorf("failed to create raw report: %w", err) return err @@ -304,7 +304,7 @@ func (rc *ReportingCommand) createRawReports(appContext AppContext, orderedTarge if rc.ReportNamePost != "" { post = "_" + rc.ReportNamePost } - reportFilename := fmt.Sprintf("%s%s.%s", targetScriptOutputs.targetName, post, "raw") + reportFilename := fmt.Sprintf("%s%s.%s", targetScriptOutputs.TargetName, post, "raw") reportPath := filepath.Join(appContext.OutputDir, reportFilename) if err = writeReport(reportBytes, reportPath); err != nil { err = fmt.Errorf("failed to write report: %w", err) @@ -332,19 +332,19 @@ func (rc *ReportingCommand) createReports(appContext AppContext, orderedTargetSc allTargetsTableValues := make([][]report.TableValues, 0) for _, targetScriptOutputs := range orderedTargetScriptOutputs { // process the tables, i.e., get field values from script output - allTableValues, err := report.ProcessTables(targetScriptOutputs.tableNames, targetScriptOutputs.scriptOutputs) + allTableValues, err := report.ProcessTables(targetScriptOutputs.TableNames, targetScriptOutputs.ScriptOutputs) if err != nil { err = fmt.Errorf("failed to process collected data: %w", err) return nil, err } // special case - the summary table is built from the post-processed data, i.e., table values if rc.SummaryFunc != nil { - summaryTableValues := rc.SummaryFunc(allTableValues, targetScriptOutputs.scriptOutputs) + summaryTableValues := rc.SummaryFunc(allTableValues, targetScriptOutputs.ScriptOutputs) allTableValues = append(allTableValues, summaryTableValues) } // special case - add tableValues for Insights if rc.InsightsFunc != nil { - insightsTableValues := rc.InsightsFunc(allTableValues, targetScriptOutputs.scriptOutputs) + insightsTableValues := rc.InsightsFunc(allTableValues, targetScriptOutputs.ScriptOutputs) allTableValues = append(allTableValues, insightsTableValues) } // special case - add tableValues for the application version @@ -360,20 +360,20 @@ func (rc *ReportingCommand) createReports(appContext AppContext, orderedTargetSc }) // create the report(s) for _, format := range formats { - reportBytes, err := report.Create(format, allTableValues, targetScriptOutputs.scriptOutputs, targetScriptOutputs.targetName) + reportBytes, err := report.Create(format, allTableValues, targetScriptOutputs.ScriptOutputs, targetScriptOutputs.TargetName) if err != nil { err = fmt.Errorf("failed to create report: %w", err) return nil, err } if len(formats) == 1 && format == report.FormatTxt { - fmt.Printf("%s:\n", targetScriptOutputs.targetName) + fmt.Printf("%s:\n", targetScriptOutputs.TargetName) fmt.Print(string(reportBytes)) } post := "" if rc.ReportNamePost != "" { post = "_" + rc.ReportNamePost } - reportFilename := fmt.Sprintf("%s%s.%s", targetScriptOutputs.targetName, post, format) + reportFilename := fmt.Sprintf("%s%s.%s", targetScriptOutputs.TargetName, post, format) reportPath := filepath.Join(appContext.OutputDir, reportFilename) if err = writeReport(reportBytes, reportPath); err != nil { err = fmt.Errorf("failed to write report: %w", err) @@ -389,7 +389,7 @@ func (rc *ReportingCommand) createReports(appContext AppContext, orderedTargetSc // - only those that we received output from targetNames := make([]string, 0) for _, targetScriptOutputs := range orderedTargetScriptOutputs { - targetNames = append(targetNames, targetScriptOutputs.targetName) + targetNames = append(targetNames, targetScriptOutputs.TargetName) } multiTargetFormats := []string{report.FormatHtml, report.FormatXlsx} for _, format := range multiTargetFormats { @@ -431,7 +431,7 @@ func outputsFromInput(summaryTableName string) ([]TargetScriptOutputs, error) { } tableNames = util.UniqueAppend(tableNames, tableName) } - orderedTargetScriptOutputs = append(orderedTargetScriptOutputs, TargetScriptOutputs{targetName: rawReport.TargetName, scriptOutputs: rawReport.ScriptOutputs, tableNames: tableNames}) + orderedTargetScriptOutputs = append(orderedTargetScriptOutputs, TargetScriptOutputs{TargetName: rawReport.TargetName, ScriptOutputs: rawReport.ScriptOutputs, TableNames: tableNames}) } return orderedTargetScriptOutputs, nil } @@ -484,8 +484,8 @@ func outputsFromTargets(cmd *cobra.Command, myTargets []target.Target, tableName // reorder to match order of myTargets for targetIdx, target := range myTargets { for _, targetScriptOutputs := range allTargetScriptOutputs { - if targetScriptOutputs.targetName == target.GetName() { - targetScriptOutputs.tableNames = targetTableNames[targetIdx] + if targetScriptOutputs.TargetName == target.GetName() { + targetScriptOutputs.TableNames = targetTableNames[targetIdx] orderedTargetScriptOutputs = append(orderedTargetScriptOutputs, targetScriptOutputs) break } @@ -532,5 +532,5 @@ func collectOnTarget(myTarget target.Target, scriptsToRun []script.ScriptDefinit if statusUpdate != nil { _ = statusUpdate(myTarget.GetName(), "collection complete") } - channelTargetScriptOutputs <- TargetScriptOutputs{targetName: myTarget.GetName(), scriptOutputs: scriptOutputs} + channelTargetScriptOutputs <- TargetScriptOutputs{TargetName: myTarget.GetName(), ScriptOutputs: scriptOutputs} } From b4cdf9eff1adb736e93e8c5acabf6481f94b543c Mon Sep 17 00:00:00 2001 From: "Harper, Jason M" Date: Sat, 3 May 2025 18:23:20 -0700 Subject: [PATCH 2/6] Add detailed status messages for configuration changes on target Signed-off-by: Harper, Jason M --- cmd/config/config.go | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/cmd/config/config.go b/cmd/config/config.go index ab865d6a..30c122bb 100644 --- a/cmd/config/config.go +++ b/cmd/config/config.go @@ -141,6 +141,7 @@ func runCmd(cmd *cobra.Command, args []string) error { } func setOnTarget(cmd *cobra.Command, myTarget target.Target, flagGroups []flagGroup, localTempDir string, channelError chan error, statusUpdate progress.MultiSpinnerUpdateFunc) { + var setMessages []string for _, group := range flagGroups { for _, flag := range group.flags { if cmd.Flags().Lookup(flag.GetName()).Changed { @@ -168,11 +169,13 @@ func setOnTarget(cmd *cobra.Command, myTarget target.Target, flagGroups []flagGr slog.Error(err.Error(), slog.String("target", myTarget.GetName())) channelError <- err return + } else { + setMessages = append(setMessages, fmt.Sprintf("set %s to %s", flag.GetName(), flag.GetValueAsString())) } } } } - statusUpdate(myTarget.GetName(), "configuration complete") + statusUpdate(myTarget.GetName(), strings.Join(setMessages, ", ")) channelError <- nil } From d3b45c4d3b4af5bf3e15fe5b384a3014bc48fc6e Mon Sep 17 00:00:00 2001 From: "Harper, Jason M" Date: Sun, 4 May 2025 05:46:36 -0700 Subject: [PATCH 3/6] no-summary option Signed-off-by: Harper, Jason M --- cmd/config/config.go | 37 ++++++++++++++++++++++++------------- cmd/config/flag.go | 13 +++++++++++++ cmd/config/flag_groups.go | 11 +++++++++++ 3 files changed, 48 insertions(+), 13 deletions(-) diff --git a/cmd/config/config.go b/cmd/config/config.go index 30c122bb..06b8223d 100644 --- a/cmd/config/config.go +++ b/cmd/config/config.go @@ -93,18 +93,21 @@ func runCmd(cmd *cobra.Command, args []string) error { cmd.SilenceUsage = true return err } - // print config prior to changes - if err := printConfig(myTargets, localTempDir); err != nil { - fmt.Fprintf(os.Stderr, "Error: %v\n", err) - slog.Error(err.Error()) - cmd.SilenceUsage = true - return err + // print config prior to changes, optionally + if !cmd.Flags().Lookup(flagNoSummaryName).Changed { + if err := printConfig(myTargets, localTempDir); err != nil { + fmt.Fprintf(os.Stderr, "Error: %v\n", err) + slog.Error(err.Error()) + cmd.SilenceUsage = true + return err + } } - // if no changes were made, print a message and return + // if no changes were requested, print a message and return var changeRequested bool for _, group := range flagGroups { for _, flag := range group.flags { - if cmd.Flags().Lookup(flag.GetName()).Changed { + hasSetFunc := flag.intSetFunc != nil || flag.floatSetFunc != nil || flag.stringSetFunc != nil || flag.boolSetFunc != nil + if hasSetFunc && cmd.Flags().Lookup(flag.GetName()).Changed { changeRequested = true break } @@ -132,10 +135,12 @@ func runCmd(cmd *cobra.Command, args []string) error { multiSpinner.Finish() fmt.Println() // blank line // print config after making changes - if err := printConfig(myTargets, localTempDir); err != nil { - fmt.Fprintf(os.Stderr, "Error: %v\n", err) - slog.Error(err.Error()) - return err + if !cmd.Flags().Lookup(flagNoSummaryName).Changed { + if err := printConfig(myTargets, localTempDir); err != nil { + fmt.Fprintf(os.Stderr, "Error: %v\n", err) + slog.Error(err.Error()) + return err + } } return nil } @@ -144,7 +149,8 @@ func setOnTarget(cmd *cobra.Command, myTarget target.Target, flagGroups []flagGr var setMessages []string for _, group := range flagGroups { for _, flag := range group.flags { - if cmd.Flags().Lookup(flag.GetName()).Changed { + hasSetFunc := flag.intSetFunc != nil || flag.floatSetFunc != nil || flag.stringSetFunc != nil || flag.boolSetFunc != nil + if hasSetFunc && cmd.Flags().Lookup(flag.GetName()).Changed { statusUpdate(myTarget.GetName(), fmt.Sprintf("setting %s to %s", flag.GetName(), flag.GetValueAsString())) var err error switch flag.GetType() { @@ -163,6 +169,11 @@ func setOnTarget(cmd *cobra.Command, myTarget target.Target, flagGroups []flagGr value, _ := cmd.Flags().GetString(flag.GetName()) err = flag.stringSetFunc(value, myTarget, localTempDir) } + case "bool": + if flag.boolSetFunc != nil { + value, _ := cmd.Flags().GetBool(flag.GetName()) + err = flag.boolSetFunc(value, myTarget, localTempDir) + } } if err != nil { statusUpdate(myTarget.GetName(), fmt.Sprintf("error setting %s to %s: %v", flag.GetName(), flag.GetValueAsString(), err)) diff --git a/cmd/config/flag.go b/cmd/config/flag.go index 8c4ca109..2599ec31 100644 --- a/cmd/config/flag.go +++ b/cmd/config/flag.go @@ -16,6 +16,7 @@ type flagDefinition struct { intSetFunc func(int, target.Target, string) error floatSetFunc func(float64, target.Target, string) error stringSetFunc func(string, target.Target, string) error + boolSetFunc func(bool, target.Target, string) error validationFunc func(cmd *cobra.Command) bool validationDescription string } @@ -70,3 +71,15 @@ func newStringFlag(cmd *cobra.Command, name string, defaultValue string, setFunc validationDescription: validationDescription, } } + +// newBoolFlag creates a new boolean flag and adds it to the command. +func newBoolFlag(cmd *cobra.Command, name string, defaultValue bool, setFunc func(bool, target.Target, string) error, help string, validationDescription string, validationFunc func(cmd *cobra.Command) bool) flagDefinition { + cmd.Flags().Bool(name, defaultValue, help) + pFlag := cmd.Flags().Lookup(name) + return flagDefinition{ + pflag: pFlag, + boolSetFunc: setFunc, + validationFunc: validationFunc, + validationDescription: validationDescription, + } +} diff --git a/cmd/config/flag_groups.go b/cmd/config/flag_groups.go index 309c2e81..d958cad6 100644 --- a/cmd/config/flag_groups.go +++ b/cmd/config/flag_groups.go @@ -71,6 +71,11 @@ const ( flagPrefetcherLLCName = "pref-llc" ) +// other flag names +const ( + flagNoSummaryName = "no-summary" +) + // governorOptions - list of valid governor options var governorOptions = []string{"performance", "powersave"} @@ -270,6 +275,12 @@ func initializeFlags(cmd *cobra.Command) { return slices.Contains(prefetcherOptions, value) })) flagGroups = append(flagGroups, group) + // other options + group = flagGroup{name: "Other Options", flags: []flagDefinition{}} + group.flags = append(group.flags, + newBoolFlag(cmd, flagNoSummaryName, false, nil, "do not print configuration summary", "", nil), + ) + flagGroups = append(flagGroups, group) common.AddTargetFlags(Cmd) Cmd.SetUsageFunc(usageFunc) From 7a7aac32a95f4ff74c51626aa42f535c582b7bb7 Mon Sep 17 00:00:00 2001 From: "Harper, Jason M" Date: Sun, 4 May 2025 05:58:32 -0700 Subject: [PATCH 4/6] const str Signed-off-by: Harper, Jason M --- cmd/config/flag_groups.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/cmd/config/flag_groups.go b/cmd/config/flag_groups.go index d958cad6..9048ebf7 100644 --- a/cmd/config/flag_groups.go +++ b/cmd/config/flag_groups.go @@ -33,6 +33,7 @@ const ( flagGroupGeneralName = "General Options" flagGroupUncoreFrequencyName = "Uncore Frequency Options" flagGroupPrefetcherName = "Prefetcher Options" + flagGroupOtherName = "Other Options" ) // general flag names @@ -276,7 +277,7 @@ func initializeFlags(cmd *cobra.Command) { })) flagGroups = append(flagGroups, group) // other options - group = flagGroup{name: "Other Options", flags: []flagDefinition{}} + group = flagGroup{name: flagGroupOtherName, flags: []flagDefinition{}} group.flags = append(group.flags, newBoolFlag(cmd, flagNoSummaryName, false, nil, "do not print configuration summary", "", nil), ) From 54b9effe8d0dd7dec6a0942d5f862876a0689aab Mon Sep 17 00:00:00 2001 From: "Harper, Jason M" Date: Sun, 4 May 2025 21:09:32 -0700 Subject: [PATCH 5/6] parallelize the set functions Signed-off-by: Harper, Jason M --- cmd/config/config.go | 241 ++++++++++++++++++++++++-------------- cmd/config/flag.go | 16 +-- cmd/config/flag_groups.go | 64 +++++----- 3 files changed, 191 insertions(+), 130 deletions(-) diff --git a/cmd/config/config.go b/cmd/config/config.go index 06b8223d..088c017a 100644 --- a/cmd/config/config.go +++ b/cmd/config/config.go @@ -125,7 +125,14 @@ func runCmd(cmd *cobra.Command, args []string) error { multiSpinner := progress.NewMultiSpinner() multiSpinner.Start() for _, myTarget := range myTargets { - multiSpinner.AddSpinner(myTarget.GetName()) + err = multiSpinner.AddSpinner(myTarget.GetName()) + if err != nil { + err = fmt.Errorf("failed to add spinner: %v", err) + fmt.Fprintf(os.Stderr, "Error: %v\n", err) + slog.Error(err.Error()) + cmd.SilenceUsage = true + return err + } go setOnTarget(cmd, myTarget, flagGroups, localTempDir, channelError, multiSpinner.Status) } // wait for all targets to finish @@ -139,54 +146,68 @@ func runCmd(cmd *cobra.Command, args []string) error { if err := printConfig(myTargets, localTempDir); err != nil { fmt.Fprintf(os.Stderr, "Error: %v\n", err) slog.Error(err.Error()) + cmd.SilenceUsage = true return err } } return nil } +type setOutput struct { + goRoutineID int + err error +} + func setOnTarget(cmd *cobra.Command, myTarget target.Target, flagGroups []flagGroup, localTempDir string, channelError chan error, statusUpdate progress.MultiSpinnerUpdateFunc) { - var setMessages []string + channelSetComplete := make(chan setOutput) + var successMessages []string + var errorMessages []string + _ = statusUpdate(myTarget.GetName(), "updating configuration") for _, group := range flagGroups { for _, flag := range group.flags { hasSetFunc := flag.intSetFunc != nil || flag.floatSetFunc != nil || flag.stringSetFunc != nil || flag.boolSetFunc != nil if hasSetFunc && cmd.Flags().Lookup(flag.GetName()).Changed { - statusUpdate(myTarget.GetName(), fmt.Sprintf("setting %s to %s", flag.GetName(), flag.GetValueAsString())) - var err error + successMessages = append(successMessages, fmt.Sprintf("set %s to %s", flag.GetName(), flag.GetValueAsString())) + errorMessages = append(errorMessages, fmt.Sprintf("failed to set %s to %s", flag.GetName(), flag.GetValueAsString())) switch flag.GetType() { case "int": if flag.intSetFunc != nil { value, _ := cmd.Flags().GetInt(flag.GetName()) - err = flag.intSetFunc(value, myTarget, localTempDir) + go flag.intSetFunc(value, myTarget, localTempDir, channelSetComplete, len(successMessages)-1) } case "float64": if flag.floatSetFunc != nil { value, _ := cmd.Flags().GetFloat64(flag.GetName()) - err = flag.floatSetFunc(value, myTarget, localTempDir) + go flag.floatSetFunc(value, myTarget, localTempDir, channelSetComplete, len(successMessages)-1) } case "string": if flag.stringSetFunc != nil { value, _ := cmd.Flags().GetString(flag.GetName()) - err = flag.stringSetFunc(value, myTarget, localTempDir) + go flag.stringSetFunc(value, myTarget, localTempDir, channelSetComplete, len(successMessages)-1) } case "bool": if flag.boolSetFunc != nil { value, _ := cmd.Flags().GetBool(flag.GetName()) - err = flag.boolSetFunc(value, myTarget, localTempDir) + go flag.boolSetFunc(value, myTarget, localTempDir, channelSetComplete, len(successMessages)-1) } } - if err != nil { - statusUpdate(myTarget.GetName(), fmt.Sprintf("error setting %s to %s: %v", flag.GetName(), flag.GetValueAsString(), err)) - slog.Error(err.Error(), slog.String("target", myTarget.GetName())) - channelError <- err - return - } else { - setMessages = append(setMessages, fmt.Sprintf("set %s to %s", flag.GetName(), flag.GetValueAsString())) - } } } } - statusUpdate(myTarget.GetName(), strings.Join(setMessages, ", ")) + // wait for all set goroutines to finish + statusMessages := []string{} + for range successMessages { + out := <-channelSetComplete + if out.err != nil { + slog.Error(out.err.Error()) + statusMessages = append(statusMessages, errorMessages[out.goRoutineID]) + } else { + statusMessages = append(statusMessages, successMessages[out.goRoutineID]) + } + } + statusMessage := fmt.Sprintf("configuration update complete: %s", strings.Join(statusMessages, ", ")) + slog.Info(statusMessage, slog.String("target", myTarget.GetName())) + _ = statusUpdate(myTarget.GetName(), statusMessage) channelError <- nil } @@ -277,7 +298,7 @@ func collectOnTarget(myTarget target.Target, scriptsToRun []script.ScriptDefinit channelTargetScriptOutputs <- common.TargetScriptOutputs{TargetName: myTarget.GetName(), ScriptOutputs: scriptOutputs} } -func setCoreCount(cores int, myTarget target.Target, localTempDir string) error { +func setCoreCount(cores int, myTarget target.Target, localTempDir string, completeChannel chan setOutput, goRoutineId int) { setScript := script.ScriptDefinition{ Name: "set core count", ScriptTemplate: fmt.Sprintf(` @@ -368,12 +389,12 @@ done } _, err := runScript(myTarget, setScript, localTempDir) if err != nil { - return fmt.Errorf("failed to set core count: %w", err) + err = fmt.Errorf("failed to set core count: %w", err) } - return nil + completeChannel <- setOutput{goRoutineID: goRoutineId, err: err} } -func setLlcSize(llcSize float64, myTarget target.Target, localTempDir string) error { +func setLlcSize(llcSize float64, myTarget target.Target, localTempDir string, completeChannel chan setOutput, goRoutineId int) { scripts := []script.ScriptDefinition{} scripts = append(scripts, script.GetScriptByName(script.LscpuScriptName)) scripts = append(scripts, script.GetScriptByName(script.LspciBitsScriptName)) @@ -382,31 +403,37 @@ func setLlcSize(llcSize float64, myTarget target.Target, localTempDir string) er outputs, err := script.RunScripts(myTarget, scripts, true, localTempDir) if err != nil { - return fmt.Errorf("failed to run scripts on target: %w", err) + completeChannel <- setOutput{goRoutineID: goRoutineId, err: fmt.Errorf("failed to run scripts on target: %w", err)} + return } maximumLlcSize, _, err := report.GetL3LscpuMB(outputs) if err != nil { - return fmt.Errorf("failed to get maximum LLC size: %w", err) + completeChannel <- setOutput{goRoutineID: goRoutineId, err: fmt.Errorf("failed to get maximum LLC size: %w", err)} + return } // microarchitecture uarch := report.UarchFromOutput(outputs) cacheWays := report.GetCacheWays(uarch) if len(cacheWays) == 0 { - return fmt.Errorf("failed to get cache ways") + completeChannel <- setOutput{goRoutineID: goRoutineId, err: fmt.Errorf("failed to get cache ways")} + return } // current LLC size currentLlcSize, err := report.GetL3MSRMB(outputs) if err != nil { - return fmt.Errorf("failed to get current LLC size: %w", err) + completeChannel <- setOutput{goRoutineID: goRoutineId, err: fmt.Errorf("failed to get current LLC size: %w", err)} + return } if currentLlcSize == llcSize { - return fmt.Errorf("LLC size is already set to %.2f MB", llcSize) + completeChannel <- setOutput{goRoutineID: goRoutineId, err: fmt.Errorf("LLC size is already set to %.2f MB", llcSize)} + return } // calculate the number of ways to set cachePerWay := maximumLlcSize / float64(len(cacheWays)) waysToSet := int(math.Ceil((llcSize / cachePerWay)) - 1) if waysToSet >= len(cacheWays) { - return fmt.Errorf("LLC size is too large, maximum is %.2f MB", maximumLlcSize) + completeChannel <- setOutput{goRoutineID: goRoutineId, err: fmt.Errorf("LLC size is too large, maximum is %.2f MB", maximumLlcSize)} + return } // set the LLC size setScript := script.ScriptDefinition{ @@ -420,26 +447,30 @@ func setLlcSize(llcSize float64, myTarget target.Target, localTempDir string) er } _, err = runScript(myTarget, setScript, localTempDir) if err != nil { - return fmt.Errorf("failed to set LLC size: %w", err) + err = fmt.Errorf("failed to set LLC size: %w", err) } - return nil + completeChannel <- setOutput{goRoutineID: goRoutineId, err: err} } -func setCoreFrequency(coreFrequency float64, myTarget target.Target, localTempDir string) error { +func setCoreFrequency(coreFrequency float64, myTarget target.Target, localTempDir string, completeChannel chan setOutput, goRoutineId int) { targetFamily, err := myTarget.GetFamily() if err != nil { - return fmt.Errorf("failed to get target family: %w", err) + completeChannel <- setOutput{goRoutineID: goRoutineId, err: fmt.Errorf("failed to get target family: %w", err)} + return } targetModel, err := myTarget.GetModel() if err != nil { - return fmt.Errorf("failed to get target model: %w", err) + completeChannel <- setOutput{goRoutineID: goRoutineId, err: fmt.Errorf("failed to get target model: %w", err)} + return } targetVendor, err := myTarget.GetVendor() if err != nil { - return fmt.Errorf("failed to get target vendor: %w", err) + completeChannel <- setOutput{goRoutineID: goRoutineId, err: fmt.Errorf("failed to get target vendor: %w", err)} + return } if targetVendor != "GenuineIntel" { - return fmt.Errorf("core frequency setting not supported on %s due to vendor mismatch", myTarget.GetName()) + completeChannel <- setOutput{goRoutineID: goRoutineId, err: fmt.Errorf("core frequency setting not supported on %s due to vendor mismatch", myTarget.GetName())} + return } var setScript script.ScriptDefinition freqInt := uint64(coreFrequency * 10) @@ -452,7 +483,8 @@ func setCoreFrequency(coreFrequency float64, myTarget target.Target, localTempDi } output, err := runScript(myTarget, getScript, localTempDir) if err != nil { - return fmt.Errorf("failed to get pstate driver: %w", err) + completeChannel <- setOutput{goRoutineID: goRoutineId, err: fmt.Errorf("failed to get pstate driver: %w", err)} + return } if strings.Contains(output, "intel_pstate") { var value uint64 @@ -491,22 +523,25 @@ func setCoreFrequency(coreFrequency float64, myTarget target.Target, localTempDi } _, err = runScript(myTarget, setScript, localTempDir) if err != nil { - return fmt.Errorf("failed to set core frequency: %w", err) + err = fmt.Errorf("failed to set core frequency: %w", err) } - return nil + completeChannel <- setOutput{goRoutineID: goRoutineId, err: err} } -func setUncoreDieFrequency(maxFreq bool, computeDie bool, uncoreFrequency float64, myTarget target.Target, localTempDir string) error { +func setUncoreDieFrequency(maxFreq bool, computeDie bool, uncoreFrequency float64, myTarget target.Target, localTempDir string, completeChannel chan setOutput, goRoutineId int) { targetFamily, err := myTarget.GetFamily() if err != nil { - return fmt.Errorf("failed to get target family: %w", err) + completeChannel <- setOutput{goRoutineID: goRoutineId, err: fmt.Errorf("failed to get target family: %w", err)} + return } targetModel, err := myTarget.GetModel() if err != nil { - return fmt.Errorf("failed to get target model: %w", err) + completeChannel <- setOutput{goRoutineID: goRoutineId, err: fmt.Errorf("failed to get target model: %w", err)} + return } if targetFamily != "6" || (targetFamily == "6" && targetModel != "173" && targetModel != "175" && targetModel != "221") { - return fmt.Errorf("uncore frequency setting not supported on %s due to family/model mismatch", myTarget.GetName()) + completeChannel <- setOutput{goRoutineID: goRoutineId, err: fmt.Errorf("uncore frequency setting not supported on %s due to family/model mismatch", myTarget.GetName())} + return } type dieId struct { instance string @@ -518,7 +553,8 @@ func setUncoreDieFrequency(maxFreq bool, computeDie bool, uncoreFrequency float6 scripts = append(scripts, script.GetScriptByName(script.UncoreDieTypesFromTPMIScriptName)) outputs, err := script.RunScripts(myTarget, scripts, true, localTempDir) if err != nil { - return fmt.Errorf("failed to get uncore die types: %w", err) + completeChannel <- setOutput{goRoutineID: goRoutineId, err: fmt.Errorf("failed to run scripts on target: %w", err)} + return } re := regexp.MustCompile(`Read bits \d+:\d+ value (\d+) from TPMI ID .* for entry (\d+) in instance (\d+)`) for line := range strings.SplitSeq(outputs[script.UncoreDieTypesFromTPMIScriptName].Stdout, "\n") { @@ -552,13 +588,14 @@ func setUncoreDieFrequency(maxFreq bool, computeDie bool, uncoreFrequency float6 } _, err = runScript(myTarget, setScript, localTempDir) if err != nil { - return fmt.Errorf("failed to set uncore frequency: %w", err) + err = fmt.Errorf("failed to set uncore die frequency: %w", err) + break } } - return nil + completeChannel <- setOutput{goRoutineID: goRoutineId, err: err} } -func setUncoreFrequency(maxFreq bool, uncoreFrequency float64, myTarget target.Target, localTempDir string) error { +func setUncoreFrequency(maxFreq bool, uncoreFrequency float64, myTarget target.Target, localTempDir string, completeChannel chan setOutput, goRoutineId int) { scripts := []script.ScriptDefinition{} scripts = append(scripts, script.ScriptDefinition{ Name: "get uncore frequency MSR", @@ -570,23 +607,28 @@ func setUncoreFrequency(maxFreq bool, uncoreFrequency float64, myTarget target.T }) outputs, err := script.RunScripts(myTarget, scripts, true, localTempDir) if err != nil { - return fmt.Errorf("failed to read uncore frequency MSR: %w", err) + completeChannel <- setOutput{goRoutineID: goRoutineId, err: fmt.Errorf("failed to run scripts on target: %w", err)} + return } targetFamily, err := myTarget.GetFamily() if err != nil { - return fmt.Errorf("failed to get target family: %w", err) + completeChannel <- setOutput{goRoutineID: goRoutineId, err: fmt.Errorf("failed to get target family: %w", err)} + return } targetModel, err := myTarget.GetModel() if err != nil { - return fmt.Errorf("failed to get target model: %w", err) + completeChannel <- setOutput{goRoutineID: goRoutineId, err: fmt.Errorf("failed to get target model: %w", err)} + return } if targetFamily != "6" || (targetFamily == "6" && (targetModel == "173" || targetModel == "175" || targetModel == "221")) { // not Intel || not GNR, SRF, CWF - return fmt.Errorf("uncore frequency setting not supported on %s due to family/model mismatch", myTarget.GetName()) + completeChannel <- setOutput{goRoutineID: goRoutineId, err: fmt.Errorf("uncore frequency setting not supported on %s due to family/model mismatch", myTarget.GetName())} + return } msrHex := strings.TrimSpace(outputs["get uncore frequency MSR"].Stdout) msrInt, err := strconv.ParseInt(msrHex, 16, 0) if err != nil { - return fmt.Errorf("failed to read uncore frequency MSR: %w", err) + completeChannel <- setOutput{goRoutineID: goRoutineId, err: fmt.Errorf("failed to parse uncore frequency MSR: %w", err)} + return } newFreq := uint64((uncoreFrequency * 1000) / 100) var newVal uint64 @@ -611,12 +653,12 @@ func setUncoreFrequency(maxFreq bool, uncoreFrequency float64, myTarget target.T } _, err = runScript(myTarget, setScript, localTempDir) if err != nil { - return fmt.Errorf("failed to set uncore frequency: %w", err) + err = fmt.Errorf("failed to set uncore frequency: %w", err) } - return nil + completeChannel <- setOutput{goRoutineID: goRoutineId, err: err} } -func setTDP(power int, myTarget target.Target, localTempDir string) error { +func setTDP(power int, myTarget target.Target, localTempDir string, completeChannel chan setOutput, goRoutineId int) { readScript := script.ScriptDefinition{ Name: "get power MSR", ScriptTemplate: "rdmsr 0x610", @@ -627,12 +669,14 @@ func setTDP(power int, myTarget target.Target, localTempDir string) error { } readOutput, err := script.RunScript(myTarget, readScript, localTempDir) if err != nil { - return fmt.Errorf("failed to read power MSR: %w", err) + completeChannel <- setOutput{goRoutineID: goRoutineId, err: fmt.Errorf("failed to read power MSR: %w", err)} + return } else { msrHex := strings.TrimSpace(readOutput.Stdout) msrInt, err := strconv.ParseInt(msrHex, 16, 0) if err != nil { - return fmt.Errorf("failed to parse power MSR: %w", err) + completeChannel <- setOutput{goRoutineID: goRoutineId, err: fmt.Errorf("failed to parse power MSR: %w", err)} + return } else { // mask out lower 14 bits newVal := uint64(msrInt) & 0xFFFFFFFFFFFFC000 @@ -648,23 +692,26 @@ func setTDP(power int, myTarget target.Target, localTempDir string) error { } _, err := runScript(myTarget, setScript, localTempDir) if err != nil { - return fmt.Errorf("failed to set power: %w", err) + completeChannel <- setOutput{goRoutineID: goRoutineId, err: fmt.Errorf("failed to set power: %w", err)} + return } } } - return nil + completeChannel <- setOutput{goRoutineID: goRoutineId, err: nil} } -func setEPB(epb int, myTarget target.Target, localTempDir string) error { +func setEPB(epb int, myTarget target.Target, localTempDir string, completeChannel chan setOutput, goRoutineId int) { epbSourceScript := script.GetScriptByName(script.EpbSourceScriptName) epbSourceOutput, err := runScript(myTarget, epbSourceScript, localTempDir) if err != nil { - return fmt.Errorf("failed to get EPB source: %w", err) + completeChannel <- setOutput{goRoutineID: goRoutineId, err: fmt.Errorf("failed to get EPB source: %w", err)} + return } epbSource := strings.TrimSpace(epbSourceOutput) source, err := strconv.ParseInt(epbSource, 16, 0) if err != nil { - return fmt.Errorf("failed to parse EPB source: %w", err) + completeChannel <- setOutput{goRoutineID: goRoutineId, err: fmt.Errorf("failed to parse EPB source: %w", err)} + return } var msr string var bitOffset uint @@ -685,11 +732,13 @@ func setEPB(epb int, myTarget target.Target, localTempDir string) error { } readOutput, err := runScript(myTarget, readScript, localTempDir) if err != nil { - return fmt.Errorf("failed to read EPB MSR %s: %w", msr, err) + completeChannel <- setOutput{goRoutineID: goRoutineId, err: fmt.Errorf("failed to read EPB MSR %s: %w", msr, err)} + return } msrValue, err := strconv.ParseUint(strings.TrimSpace(readOutput), 16, 64) if err != nil { - return fmt.Errorf("failed to parse EPB MSR %s: %w", msr, err) + completeChannel <- setOutput{goRoutineID: goRoutineId, err: fmt.Errorf("failed to parse EPB MSR %s: %w", msr, err)} + return } // mask out 4 bits starting at bitOffset maskedValue := msrValue &^ (0xF << bitOffset) @@ -706,12 +755,12 @@ func setEPB(epb int, myTarget target.Target, localTempDir string) error { } _, err = runScript(myTarget, setScript, localTempDir) if err != nil { - return fmt.Errorf("failed to set EPB: %w", err) + err = fmt.Errorf("failed to set EPB: %w", err) } - return nil + completeChannel <- setOutput{goRoutineID: goRoutineId, err: err} } -func setEPP(epp int, myTarget target.Target, localTempDir string) error { +func setEPP(epp int, myTarget target.Target, localTempDir string, completeChannel chan setOutput, goRoutineId int) { // Set both the per-core EPP value and the package EPP value // Reference: 15.4.4 Managing HWP in the Intel SDM @@ -726,11 +775,13 @@ func setEPP(epp int, myTarget target.Target, localTempDir string) error { } stdout, err := runScript(myTarget, getScript, localTempDir) if err != nil { - return fmt.Errorf("failed to read EPP MSR %s: %w", "0x774", err) + completeChannel <- setOutput{goRoutineID: goRoutineId, err: fmt.Errorf("failed to read EPP MSR %s: %w", "0x774", err)} + return } msrValue, err := strconv.ParseUint(strings.TrimSpace(stdout), 16, 64) if err != nil { - return fmt.Errorf("failed to parse EPP MSR %s: %w", "0x774", err) + completeChannel <- setOutput{goRoutineID: goRoutineId, err: fmt.Errorf("failed to parse EPP MSR %s: %w", "0x774", err)} + return } // mask out bits 24-31 IA32_HWP_REQUEST MSR value maskedValue := msrValue & 0xFFFFFFFF00FFFFFF @@ -747,9 +798,9 @@ func setEPP(epp int, myTarget target.Target, localTempDir string) error { } _, err = runScript(myTarget, setScript, localTempDir) if err != nil { - return fmt.Errorf("failed to set EPP: %w", err) + completeChannel <- setOutput{goRoutineID: goRoutineId, err: fmt.Errorf("failed to set EPP: %w", err)} + return } - // get the current value of the IA32_HWP_REQUEST_PKG MSR that includes the current package EPP value getScript = script.ScriptDefinition{ Name: "get epp pkg msr", @@ -761,11 +812,13 @@ func setEPP(epp int, myTarget target.Target, localTempDir string) error { } stdout, err = runScript(myTarget, getScript, localTempDir) if err != nil { - return fmt.Errorf("failed to read EPP pkg MSR %s: %w", "0x772", err) + completeChannel <- setOutput{goRoutineID: goRoutineId, err: fmt.Errorf("failed to read EPP pkg MSR %s: %w", "0x772", err)} + return } msrValue, err = strconv.ParseUint(strings.TrimSpace(stdout), 16, 64) if err != nil { - return fmt.Errorf("failed to parse EPP pkg MSR %s: %w", "0x772", err) + completeChannel <- setOutput{goRoutineID: goRoutineId, err: fmt.Errorf("failed to parse EPP pkg MSR %s: %w", "0x772", err)} + return } // mask out bits 24-31 IA32_HWP_REQUEST_PKG MSR value maskedValue = msrValue & 0xFFFFFFFF00FFFFFF @@ -782,12 +835,12 @@ func setEPP(epp int, myTarget target.Target, localTempDir string) error { } _, err = runScript(myTarget, setScript, localTempDir) if err != nil { - return fmt.Errorf("failed to set EPP pkg: %w", err) + err = fmt.Errorf("failed to set EPP pkg: %w", err) } - return nil + completeChannel <- setOutput{goRoutineID: goRoutineId, err: err} } -func setGovernor(governor string, myTarget target.Target, localTempDir string) error { +func setGovernor(governor string, myTarget target.Target, localTempDir string, completeChannel chan setOutput, goRoutineId int) { setScript := script.ScriptDefinition{ Name: "set governor", ScriptTemplate: fmt.Sprintf("echo %s | tee /sys/devices/system/cpu/cpu*/cpufreq/scaling_governor", governor), @@ -795,19 +848,20 @@ func setGovernor(governor string, myTarget target.Target, localTempDir string) e } _, err := runScript(myTarget, setScript, localTempDir) if err != nil { - return fmt.Errorf("failed to set governor: %w", err) + err = fmt.Errorf("failed to set governor: %w", err) } - return nil + completeChannel <- setOutput{goRoutineID: goRoutineId, err: err} } -func setELC(elc string, myTarget target.Target, localTempDir string) error { +func setELC(elc string, myTarget target.Target, localTempDir string, completeChannel chan setOutput, goRoutineId int) { var mode string if elc == elcOptions[0] { mode = "latency-optimized-mode" } else if elc == elcOptions[1] { mode = "default" } else { - return fmt.Errorf("invalid ELC mode: %s", elc) + completeChannel <- setOutput{goRoutineID: goRoutineId, err: fmt.Errorf("invalid ELC mode: %s", elc)} + return } setScript := script.ScriptDefinition{ Name: "set elc", @@ -819,15 +873,16 @@ func setELC(elc string, myTarget target.Target, localTempDir string) error { } _, err := runScript(myTarget, setScript, localTempDir) if err != nil { - return fmt.Errorf("failed to set ELC mode: %w", err) + err = fmt.Errorf("failed to set ELC mode: %w", err) } - return nil + completeChannel <- setOutput{goRoutineID: goRoutineId, err: err} } -func setPrefetcher(enableDisable string, myTarget target.Target, localTempDir string, prefetcherType string) error { +func setPrefetcher(enableDisable string, myTarget target.Target, localTempDir string, prefetcherType string, completeChannel chan setOutput, goRoutineId int) { pf, err := report.GetPrefetcherDefByName(prefetcherType) if err != nil { - return fmt.Errorf("failed to get prefetcher definition: %w", err) + completeChannel <- setOutput{goRoutineID: goRoutineId, err: fmt.Errorf("failed to get prefetcher definition: %w", err)} + return } // check if the prefetcher is supported on this target's architecture // get the uarch @@ -837,15 +892,18 @@ func setPrefetcher(enableDisable string, myTarget target.Target, localTempDir st scripts = append(scripts, script.GetScriptByName(script.LspciDevicesScriptName)) outputs, err := script.RunScripts(myTarget, scripts, true, localTempDir) if err != nil { - return fmt.Errorf("failed to run target identification scripts on target: %w", err) + completeChannel <- setOutput{goRoutineID: goRoutineId, err: fmt.Errorf("failed to run scripts on target: %w", err)} + return } uarch := report.UarchFromOutput(outputs) if uarch == "" { - return fmt.Errorf("failed to get microarchitecture") + completeChannel <- setOutput{goRoutineID: goRoutineId, err: fmt.Errorf("failed to get microarchitecture")} + return } // is the prefetcher supported on this uarch? if !slices.Contains(pf.Uarchs, "all") && !slices.Contains(pf.Uarchs, uarch[:3]) { - return fmt.Errorf("prefetcher %s is not supported on %s", prefetcherType, uarch) + completeChannel <- setOutput{goRoutineID: goRoutineId, err: fmt.Errorf("prefetcher %s is not supported on %s", prefetcherType, uarch)} + return } // get the current value of the prefetcher MSR getScript := script.ScriptDefinition{ @@ -858,11 +916,13 @@ func setPrefetcher(enableDisable string, myTarget target.Target, localTempDir st } stdout, err := runScript(myTarget, getScript, localTempDir) if err != nil { - return fmt.Errorf("failed to read prefetcher MSR: %w", err) + completeChannel <- setOutput{goRoutineID: goRoutineId, err: fmt.Errorf("failed to read prefetcher MSR: %w", err)} + return } msrValue, err := strconv.ParseUint(strings.TrimSpace(stdout), 16, 64) if err != nil { - return fmt.Errorf("failed to parse prefetcher MSR: %w", err) + completeChannel <- setOutput{goRoutineID: goRoutineId, err: fmt.Errorf("failed to parse prefetcher MSR: %w", err)} + return } // set the prefetcher bit to bitValue determined by the onOff value, note: 0 is enable, 1 is disable var bitVal int @@ -871,7 +931,8 @@ func setPrefetcher(enableDisable string, myTarget target.Target, localTempDir st } else if enableDisable == prefetcherOptions[1] { bitVal = 1 } else { - return fmt.Errorf("invalid prefetcher setting: %s", enableDisable) + completeChannel <- setOutput{goRoutineID: goRoutineId, err: fmt.Errorf("invalid prefetcher setting: %s", enableDisable)} + return } // mask out the prefetcher bit maskedValue := msrValue &^ (1 << pf.Bit) @@ -888,9 +949,9 @@ func setPrefetcher(enableDisable string, myTarget target.Target, localTempDir st } _, err = runScript(myTarget, setScript, localTempDir) if err != nil { - return fmt.Errorf("failed to set %s prefetcher: %w", prefetcherType, err) + err = fmt.Errorf("failed to set %s prefetcher: %w", prefetcherType, err) } - return nil + completeChannel <- setOutput{goRoutineID: goRoutineId, err: err} } func runScript(myTarget target.Target, myScript script.ScriptDefinition, localTempDir string) (string, error) { diff --git a/cmd/config/flag.go b/cmd/config/flag.go index 2599ec31..8df0ded1 100644 --- a/cmd/config/flag.go +++ b/cmd/config/flag.go @@ -13,10 +13,10 @@ import ( // flagDefinition is a struct that defines a command line flag. type flagDefinition struct { pflag *pflag.Flag - intSetFunc func(int, target.Target, string) error - floatSetFunc func(float64, target.Target, string) error - stringSetFunc func(string, target.Target, string) error - boolSetFunc func(bool, target.Target, string) error + intSetFunc func(int, target.Target, string, chan setOutput, int) + floatSetFunc func(float64, target.Target, string, chan setOutput, int) + stringSetFunc func(string, target.Target, string, chan setOutput, int) + boolSetFunc func(bool, target.Target, string, chan setOutput, int) validationFunc func(cmd *cobra.Command) bool validationDescription string } @@ -37,7 +37,7 @@ func (f *flagDefinition) GetValueAsString() string { } // newIntFlag creates a new integer flag and adds it to the command. -func newIntFlag(cmd *cobra.Command, name string, defaultValue int, setFunc func(int, target.Target, string) error, help string, validationDescription string, validationFunc func(cmd *cobra.Command) bool) flagDefinition { +func newIntFlag(cmd *cobra.Command, name string, defaultValue int, setFunc func(int, target.Target, string, chan setOutput, int), help string, validationDescription string, validationFunc func(cmd *cobra.Command) bool) flagDefinition { cmd.Flags().Int(name, defaultValue, help) pFlag := cmd.Flags().Lookup(name) return flagDefinition{ @@ -49,7 +49,7 @@ func newIntFlag(cmd *cobra.Command, name string, defaultValue int, setFunc func( } // newInt64Flag creates a new int64 flag and adds it to the command. -func newFloat64Flag(cmd *cobra.Command, name string, defaultValue float64, setFunc func(float64, target.Target, string) error, help string, validationDescription string, validationFunc func(cmd *cobra.Command) bool) flagDefinition { +func newFloat64Flag(cmd *cobra.Command, name string, defaultValue float64, setFunc func(float64, target.Target, string, chan setOutput, int), help string, validationDescription string, validationFunc func(cmd *cobra.Command) bool) flagDefinition { cmd.Flags().Float64(name, defaultValue, help) pFlag := cmd.Flags().Lookup(name) return flagDefinition{ @@ -61,7 +61,7 @@ func newFloat64Flag(cmd *cobra.Command, name string, defaultValue float64, setFu } // newStringFlag creates a new string flag and adds it to the command. -func newStringFlag(cmd *cobra.Command, name string, defaultValue string, setFunc func(string, target.Target, string) error, help string, validationDescription string, validationFunc func(cmd *cobra.Command) bool) flagDefinition { +func newStringFlag(cmd *cobra.Command, name string, defaultValue string, setFunc func(string, target.Target, string, chan setOutput, int), help string, validationDescription string, validationFunc func(cmd *cobra.Command) bool) flagDefinition { cmd.Flags().String(name, defaultValue, help) pFlag := cmd.Flags().Lookup(name) return flagDefinition{ @@ -73,7 +73,7 @@ func newStringFlag(cmd *cobra.Command, name string, defaultValue string, setFunc } // newBoolFlag creates a new boolean flag and adds it to the command. -func newBoolFlag(cmd *cobra.Command, name string, defaultValue bool, setFunc func(bool, target.Target, string) error, help string, validationDescription string, validationFunc func(cmd *cobra.Command) bool) flagDefinition { +func newBoolFlag(cmd *cobra.Command, name string, defaultValue bool, setFunc func(bool, target.Target, string, chan setOutput, int), help string, validationDescription string, validationFunc func(cmd *cobra.Command) bool) flagDefinition { cmd.Flags().Bool(name, defaultValue, help) pFlag := cmd.Flags().Lookup(name) return flagDefinition{ diff --git a/cmd/config/flag_groups.go b/cmd/config/flag_groups.go index 9048ebf7..3f41269e 100644 --- a/cmd/config/flag_groups.go +++ b/cmd/config/flag_groups.go @@ -128,8 +128,8 @@ func initializeFlags(cmd *cobra.Command) { group = flagGroup{name: flagGroupUncoreFrequencyName, flags: []flagDefinition{}} group.flags = append(group.flags, newFloat64Flag(cmd, flagUncoreMaxFrequencyName, 0, - func(value float64, myTarget target.Target, localTempDir string) error { - return setUncoreFrequency(true, value, myTarget, localTempDir) + func(value float64, myTarget target.Target, localTempDir string, completeChannel chan setOutput, goRoutineId int) { + setUncoreFrequency(true, value, myTarget, localTempDir, completeChannel, goRoutineId) }, "maximum uncore frequency in GHz [EMR-]", "greater than 0.1", func(cmd *cobra.Command) bool { @@ -137,8 +137,8 @@ func initializeFlags(cmd *cobra.Command) { return value > 0.1 }), newFloat64Flag(cmd, flagUncoreMinFrequencyName, 0, - func(value float64, myTarget target.Target, localTempDir string) error { - return setUncoreFrequency(false, value, myTarget, localTempDir) + func(value float64, myTarget target.Target, localTempDir string, completeChannel chan setOutput, goRoutineId int) { + setUncoreFrequency(false, value, myTarget, localTempDir, completeChannel, goRoutineId) }, "minimum uncore frequency in GHz [EMR-]", "greater than 0.1", func(cmd *cobra.Command) bool { @@ -146,8 +146,8 @@ func initializeFlags(cmd *cobra.Command) { return value > 0.1 }), newFloat64Flag(cmd, flagUncoreMaxComputeFrequencyName, 0, - func(value float64, myTarget target.Target, localTempDir string) error { - return setUncoreDieFrequency(true, true, value, myTarget, localTempDir) + func(value float64, myTarget target.Target, localTempDir string, completeChannel chan setOutput, goRoutineId int) { + setUncoreDieFrequency(true, true, value, myTarget, localTempDir, completeChannel, goRoutineId) }, "maximum uncore compute die frequency in GHz [SRF+]", "greater than 0.1", func(cmd *cobra.Command) bool { @@ -155,8 +155,8 @@ func initializeFlags(cmd *cobra.Command) { return value > 0.1 }), newFloat64Flag(cmd, flagUncoreMinComputeFrequencyName, 0, - func(value float64, myTarget target.Target, localTempDir string) error { - return setUncoreDieFrequency(false, true, value, myTarget, localTempDir) + func(value float64, myTarget target.Target, localTempDir string, completeChannel chan setOutput, goRoutineId int) { + setUncoreDieFrequency(false, true, value, myTarget, localTempDir, completeChannel, goRoutineId) }, "minimum uncore compute die frequency in GHz [SRF+]", "greater than 0.1", func(cmd *cobra.Command) bool { @@ -164,8 +164,8 @@ func initializeFlags(cmd *cobra.Command) { return value > 0.1 }), newFloat64Flag(cmd, flagUncoreMaxIOFrequencyName, 0, - func(value float64, myTarget target.Target, localTempDir string) error { - return setUncoreDieFrequency(true, false, value, myTarget, localTempDir) + func(value float64, myTarget target.Target, localTempDir string, completeChannel chan setOutput, goRoutineId int) { + setUncoreDieFrequency(true, false, value, myTarget, localTempDir, completeChannel, goRoutineId) }, "maximum uncore IO die frequency in GHz [SRF+]", "greater than 0.1", func(cmd *cobra.Command) bool { @@ -173,8 +173,8 @@ func initializeFlags(cmd *cobra.Command) { return value > 0.1 }), newFloat64Flag(cmd, flagUncoreMinIOFrequencyName, 0, - func(value float64, myTarget target.Target, localTempDir string) error { - return setUncoreDieFrequency(false, false, value, myTarget, localTempDir) + func(value float64, myTarget target.Target, localTempDir string, completeChannel chan setOutput, goRoutineId int) { + setUncoreDieFrequency(false, false, value, myTarget, localTempDir, completeChannel, goRoutineId) }, "minimum uncore IO die frequency in GHz [SRF+]", "greater than 0.1", func(cmd *cobra.Command) bool { @@ -186,8 +186,8 @@ func initializeFlags(cmd *cobra.Command) { group = flagGroup{name: flagGroupPrefetcherName, flags: []flagDefinition{}} group.flags = append(group.flags, newStringFlag(cmd, flagPrefetcherL2HWName, "", - func(value string, myTarget target.Target, localTempDir string) error { - return setPrefetcher(value, myTarget, localTempDir, report.PrefetcherL2HWName) + func(value string, myTarget target.Target, localTempDir string, completeChannel chan setOutput, goRoutineId int) { + setPrefetcher(value, myTarget, localTempDir, report.PrefetcherL2HWName, completeChannel, goRoutineId) }, "L2 hardware prefetcher ("+strings.Join(prefetcherOptions, ", ")+")", strings.Join(prefetcherOptions, ", "), func(cmd *cobra.Command) bool { @@ -195,8 +195,8 @@ func initializeFlags(cmd *cobra.Command) { return slices.Contains(prefetcherOptions, value) }), newStringFlag(cmd, flagPrefetcherL2AdjName, "", - func(value string, myTarget target.Target, localTempDir string) error { - return setPrefetcher(value, myTarget, localTempDir, report.PrefetcherL2AdjName) + func(value string, myTarget target.Target, localTempDir string, completeChannel chan setOutput, goRoutineId int) { + setPrefetcher(value, myTarget, localTempDir, report.PrefetcherL2AdjName, completeChannel, goRoutineId) }, "L2 adjacent cache line prefetcher ("+strings.Join(prefetcherOptions, ", ")+")", strings.Join(prefetcherOptions, ", "), func(cmd *cobra.Command) bool { @@ -204,8 +204,8 @@ func initializeFlags(cmd *cobra.Command) { return slices.Contains(prefetcherOptions, value) }), newStringFlag(cmd, flagPrefetcherDCUHWName, "", - func(value string, myTarget target.Target, localTempDir string) error { - return setPrefetcher(value, myTarget, localTempDir, report.PrefetcherDCUHWName) + func(value string, myTarget target.Target, localTempDir string, completeChannel chan setOutput, goRoutineId int) { + setPrefetcher(value, myTarget, localTempDir, report.PrefetcherDCUHWName, completeChannel, goRoutineId) }, "DCU hardware prefetcher ("+strings.Join(prefetcherOptions, ", ")+")", strings.Join(prefetcherOptions, ", "), func(cmd *cobra.Command) bool { @@ -213,8 +213,8 @@ func initializeFlags(cmd *cobra.Command) { return slices.Contains(prefetcherOptions, value) }), newStringFlag(cmd, flagPrefetcherDCUIPName, "", - func(value string, myTarget target.Target, localTempDir string) error { - return setPrefetcher(value, myTarget, localTempDir, report.PrefetcherDCUIPName) + func(value string, myTarget target.Target, localTempDir string, completeChannel chan setOutput, goRoutineId int) { + setPrefetcher(value, myTarget, localTempDir, report.PrefetcherDCUIPName, completeChannel, goRoutineId) }, "DCU instruction pointer prefetcher ("+strings.Join(prefetcherOptions, ", ")+")", strings.Join(prefetcherOptions, ", "), func(cmd *cobra.Command) bool { @@ -222,8 +222,8 @@ func initializeFlags(cmd *cobra.Command) { return slices.Contains(prefetcherOptions, value) }), newStringFlag(cmd, flagPrefetcherDCUNPName, "", - func(value string, myTarget target.Target, localTempDir string) error { - return setPrefetcher(value, myTarget, localTempDir, report.PrefetcherDCUNPName) + func(value string, myTarget target.Target, localTempDir string, completeChannel chan setOutput, goRoutineId int) { + setPrefetcher(value, myTarget, localTempDir, report.PrefetcherDCUNPName, completeChannel, goRoutineId) }, "DCU next page prefetcher ("+strings.Join(prefetcherOptions, ", ")+")", strings.Join(prefetcherOptions, ", "), func(cmd *cobra.Command) bool { @@ -231,8 +231,8 @@ func initializeFlags(cmd *cobra.Command) { return slices.Contains(prefetcherOptions, value) }), newStringFlag(cmd, flagPrefetcherAMPName, "", - func(value string, myTarget target.Target, localTempDir string) error { - return setPrefetcher(value, myTarget, localTempDir, report.PrefetcherAMPName) + func(value string, myTarget target.Target, localTempDir string, completeChannel chan setOutput, goRoutineId int) { + setPrefetcher(value, myTarget, localTempDir, report.PrefetcherAMPName, completeChannel, goRoutineId) }, "Adaptive multipath probability prefetcher ("+strings.Join(prefetcherOptions, ", ")+") [SPR,EMR,GNR]", strings.Join(prefetcherOptions, ", "), func(cmd *cobra.Command) bool { @@ -240,8 +240,8 @@ func initializeFlags(cmd *cobra.Command) { return slices.Contains(prefetcherOptions, value) }), newStringFlag(cmd, flagPrefetcherLLCPPName, "", - func(value string, myTarget target.Target, localTempDir string) error { - return setPrefetcher(value, myTarget, localTempDir, report.PrefetcherLLCPPName) + func(value string, myTarget target.Target, localTempDir string, completeChannel chan setOutput, goRoutineId int) { + setPrefetcher(value, myTarget, localTempDir, report.PrefetcherLLCPPName, completeChannel, goRoutineId) }, "LLC page prefetcher ("+strings.Join(prefetcherOptions, ", ")+") [GNR]", strings.Join(prefetcherOptions, ", "), func(cmd *cobra.Command) bool { @@ -249,8 +249,8 @@ func initializeFlags(cmd *cobra.Command) { return slices.Contains(prefetcherOptions, value) }), newStringFlag(cmd, flagPrefetcherAOPName, "", - func(value string, myTarget target.Target, localTempDir string) error { - return setPrefetcher(value, myTarget, localTempDir, report.PrefetcherAOPName) + func(value string, myTarget target.Target, localTempDir string, completeChannel chan setOutput, goRoutineId int) { + setPrefetcher(value, myTarget, localTempDir, report.PrefetcherAOPName, completeChannel, goRoutineId) }, "Array of pointers prefetcher ("+strings.Join(prefetcherOptions, ", ")+") [GNR]", strings.Join(prefetcherOptions, ", "), func(cmd *cobra.Command) bool { @@ -258,8 +258,8 @@ func initializeFlags(cmd *cobra.Command) { return slices.Contains(prefetcherOptions, value) }), newStringFlag(cmd, flagPrefetcherHomelessName, "", - func(value string, myTarget target.Target, localTempDir string) error { - return setPrefetcher(value, myTarget, localTempDir, report.PrefetcherHomelessName) + func(value string, myTarget target.Target, localTempDir string, completeChannel chan setOutput, goRoutineId int) { + setPrefetcher(value, myTarget, localTempDir, report.PrefetcherHomelessName, completeChannel, goRoutineId) }, "Homeless prefetcher ("+strings.Join(prefetcherOptions, ", ")+") [SPR,EMR,GNR]", strings.Join(prefetcherOptions, ", "), func(cmd *cobra.Command) bool { @@ -267,8 +267,8 @@ func initializeFlags(cmd *cobra.Command) { return slices.Contains(prefetcherOptions, value) }), newStringFlag(cmd, flagPrefetcherLLCName, "", - func(value string, myTarget target.Target, localTempDir string) error { - return setPrefetcher(value, myTarget, localTempDir, report.PrefetcherLLCName) + func(value string, myTarget target.Target, localTempDir string, completeChannel chan setOutput, goRoutineId int) { + setPrefetcher(value, myTarget, localTempDir, report.PrefetcherLLCName, completeChannel, goRoutineId) }, "Last level cache prefetcher ("+strings.Join(prefetcherOptions, ", ")+") [SPR,EMR,GNR]", strings.Join(prefetcherOptions, ", "), func(cmd *cobra.Command) bool { From 1c76baa741f7857b3b67d0924ff3ba542e296b1d Mon Sep 17 00:00:00 2001 From: "Harper, Jason M" Date: Sun, 4 May 2025 21:13:42 -0700 Subject: [PATCH 6/6] Refactor setOutput struct definition for clarity and consistency Signed-off-by: Harper, Jason M --- cmd/config/config.go | 5 ----- cmd/config/flag.go | 6 ++++++ 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/cmd/config/config.go b/cmd/config/config.go index 088c017a..b7615b1e 100644 --- a/cmd/config/config.go +++ b/cmd/config/config.go @@ -153,11 +153,6 @@ func runCmd(cmd *cobra.Command, args []string) error { return nil } -type setOutput struct { - goRoutineID int - err error -} - func setOnTarget(cmd *cobra.Command, myTarget target.Target, flagGroups []flagGroup, localTempDir string, channelError chan error, statusUpdate progress.MultiSpinnerUpdateFunc) { channelSetComplete := make(chan setOutput) var successMessages []string diff --git a/cmd/config/flag.go b/cmd/config/flag.go index 8df0ded1..bb8f3334 100644 --- a/cmd/config/flag.go +++ b/cmd/config/flag.go @@ -10,6 +10,12 @@ import ( "github.com/spf13/pflag" ) +// setOutput is a struct that holds the output of a flagDefinition set function +type setOutput struct { + goRoutineID int + err error +} + // flagDefinition is a struct that defines a command line flag. type flagDefinition struct { pflag *pflag.Flag