Skip to content

Commit ed5dcc2

Browse files
authored
Merge pull request #179 from intel/processwatch
add instruction mix reporting to telemetry command
2 parents 3b9c5be + 6b94eee commit ed5dcc2

File tree

10 files changed

+257
-60
lines changed

10 files changed

+257
-60
lines changed

cmd/flame/flame.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import (
99
"os"
1010
"perfspect/internal/common"
1111
"perfspect/internal/report"
12+
"perfspect/internal/script"
1213
"perfspect/internal/util"
1314
"strings"
1415

@@ -138,8 +139,7 @@ func runCmd(cmd *cobra.Command, args []string) error {
138139
reportingCommand := common.ReportingCommand{
139140
Cmd: cmd,
140141
ReportNamePost: "flame",
141-
Frequency: flagFrequency,
142-
Duration: flagDuration,
142+
ScriptParams: script.ScriptParams{Frequency: flagFrequency, Duration: flagDuration},
143143
TableNames: []string{report.CodePathFrequencyTableName},
144144
}
145145
return reportingCommand.Run()

cmd/lock/lock.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import (
99
"os"
1010
"perfspect/internal/common"
1111
"perfspect/internal/report"
12+
"perfspect/internal/script"
1213
"strings"
1314

1415
"github.com/spf13/cobra"
@@ -115,8 +116,7 @@ func runCmd(cmd *cobra.Command, args []string) error {
115116
reportingCommand := common.ReportingCommand{
116117
Cmd: cmd,
117118
ReportNamePost: "lock",
118-
Frequency: flagFrequency,
119-
Duration: flagDuration,
119+
ScriptParams: script.ScriptParams{Frequency: flagFrequency, Duration: flagDuration},
120120
TableNames: []string{report.KernelLockAnalysisTableName},
121121
}
122122
return reportingCommand.Run()

cmd/report/report.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -333,7 +333,7 @@ func runCmd(cmd *cobra.Command, args []string) error {
333333
}
334334
reportingCommand := common.ReportingCommand{
335335
Cmd: cmd,
336-
StorageDir: flagStorageDir,
336+
ScriptParams: script.ScriptParams{StorageDir: flagStorageDir},
337337
TableNames: tableNames,
338338
SummaryFunc: summaryFunc,
339339
SummaryTableName: benchmarkSummaryTableName,

cmd/telemetry/telemetry.go

Lines changed: 34 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -50,13 +50,17 @@ var (
5050

5151
flagAll bool
5252

53-
flagCpu bool
54-
flagCpuAvg bool
55-
flagIrq bool
56-
flagNetwork bool
57-
flagStorage bool
58-
flagMemory bool
59-
flagPower bool
53+
flagCpu bool
54+
flagCpuAvg bool
55+
flagIrq bool
56+
flagNetwork bool
57+
flagStorage bool
58+
flagMemory bool
59+
flagPower bool
60+
flagInstrMix bool
61+
62+
flagInstrMixPid int
63+
flagInstrMixFilter []string
6064
)
6165

6266
const (
@@ -65,20 +69,25 @@ const (
6569

6670
flagAllName = "all"
6771

68-
flagCpuName = "cpu"
69-
flagCpuAvgName = "cpuavg"
70-
flagIrqName = "irq"
71-
flagNetworkName = "network"
72-
flagStorageName = "storage"
73-
flagMemoryName = "memory"
74-
flagPowerName = "power"
72+
flagCpuName = "cpu"
73+
flagCpuAvgName = "cpuavg"
74+
flagIrqName = "irq"
75+
flagNetworkName = "network"
76+
flagStorageName = "storage"
77+
flagMemoryName = "memory"
78+
flagPowerName = "power"
79+
flagInstrMixName = "instrmix"
80+
81+
flagInstrMixPidName = "instrmix-pid"
82+
flagInstrMixFilterName = "instrmix-filter"
7583
)
7684

7785
var telemetrySummaryTableName = "Telemetry Summary"
7886

7987
var categories = []common.Category{
8088
{FlagName: flagCpuName, FlagVar: &flagCpu, DefaultValue: false, Help: "monitor cpu", TableNames: []string{report.CPUUtilizationTableName}},
8189
{FlagName: flagCpuAvgName, FlagVar: &flagCpuAvg, DefaultValue: false, Help: "monitor cpu average", TableNames: []string{report.AverageCPUUtilizationTableName}},
90+
{FlagName: flagInstrMixName, FlagVar: &flagInstrMix, DefaultValue: false, Help: "monitor instruction mix", TableNames: []string{report.InstructionMixTableName}},
8291
{FlagName: flagIrqName, FlagVar: &flagIrq, DefaultValue: false, Help: "monitor irq", TableNames: []string{report.IRQRateTableName}},
8392
{FlagName: flagStorageName, FlagVar: &flagStorage, DefaultValue: false, Help: "monitor storage", TableNames: []string{report.DriveStatsTableName}},
8493
{FlagName: flagNetworkName, FlagVar: &flagNetwork, DefaultValue: false, Help: "monitor network", TableNames: []string{report.NetworkStatsTableName}},
@@ -96,6 +105,8 @@ func init() {
96105
Cmd.Flags().StringSliceVar(&common.FlagFormat, common.FlagFormatName, []string{report.FormatAll}, "")
97106
Cmd.Flags().IntVar(&flagDuration, flagDurationName, 30, "")
98107
Cmd.Flags().IntVar(&flagInterval, flagIntervalName, 2, "")
108+
Cmd.Flags().IntVar(&flagInstrMixPid, flagInstrMixPidName, 0, "")
109+
Cmd.Flags().StringSliceVar(&flagInstrMixFilter, flagInstrMixFilterName, []string{"SSE", "AVX", "AVX2", "AVX512", "AMX_TILE"}, "")
99110

100111
common.AddTargetFlags(Cmd)
101112

@@ -158,6 +169,14 @@ func getFlagGroups() []common.FlagGroup {
158169
Name: flagIntervalName,
159170
Help: "number of seconds between each sample",
160171
},
172+
{
173+
Name: flagInstrMixPidName,
174+
Help: "pid to monitor for instruction mix, no pid means all processes",
175+
},
176+
{
177+
Name: flagInstrMixFilterName,
178+
Help: "filter to apply to instruction mix",
179+
},
161180
}
162181
groups = append(groups, common.FlagGroup{
163182
GroupName: "Others Options",
@@ -229,8 +248,7 @@ func runCmd(cmd *cobra.Command, args []string) error {
229248
reportingCommand := common.ReportingCommand{
230249
Cmd: cmd,
231250
ReportNamePost: "telem",
232-
Interval: flagInterval,
233-
Duration: flagDuration,
251+
ScriptParams: script.ScriptParams{Interval: flagInterval, Duration: flagDuration, PID: flagInstrMixPid, Filter: flagInstrMixFilter},
234252
TableNames: tableNames,
235253
SummaryFunc: summaryFunc,
236254
SummaryTableName: telemetrySummaryTableName,

internal/common/common.go

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -80,10 +80,7 @@ type ReportingCommand struct {
8080
Cmd *cobra.Command
8181
ReportNamePost string
8282
TableNames []string
83-
Duration int
84-
Interval int
85-
Frequency int
86-
StorageDir string
83+
ScriptParams script.ScriptParams
8784
SummaryFunc SummaryFunc
8885
SummaryTableName string
8986
InsightsFunc InsightsFunc
@@ -138,7 +135,7 @@ func (rc *ReportingCommand) Run() error {
138135
// make a list of unique script definitions
139136
var scriptsToRun []script.ScriptDefinition
140137
for _, scriptName := range scriptNames {
141-
scriptsToRun = append(scriptsToRun, script.GetParameterizedScriptByName(scriptName, rc.Duration, rc.Interval, rc.Frequency, rc.StorageDir))
138+
scriptsToRun = append(scriptsToRun, script.GetParameterizedScriptByName(scriptName, rc.ScriptParams))
142139
}
143140
// do any of the scripts require elevated privileges?
144141
elevated := false
@@ -188,7 +185,7 @@ func (rc *ReportingCommand) Run() error {
188185
channelTargetScriptOutputs := make(chan TargetScriptOutputs)
189186
channelError := make(chan error)
190187
for _, target := range myTargets {
191-
go collectOnTarget(rc.Cmd, rc.Duration, target, scriptsToRun, localTempDir, channelTargetScriptOutputs, channelError, multiSpinner.Status)
188+
go collectOnTarget(rc.Cmd, rc.ScriptParams.Duration, target, scriptsToRun, localTempDir, channelTargetScriptOutputs, channelError, multiSpinner.Status)
192189
}
193190
// wait for scripts to run on all targets
194191
var allTargetScriptOutputs []TargetScriptOutputs

internal/report/html.go

Lines changed: 42 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -743,7 +743,7 @@ func cpuUtilizationTableHTMLRenderer(tableValues TableValues, targetName string)
743743
}
744744
chartConfig := scatterChartTemplateStruct{
745745
ID: fmt.Sprintf("cpuUtilization%d", rand.Intn(10000)),
746-
XaxisText: "Time/Samples",
746+
XaxisText: "Time",
747747
YaxisText: "% Utilization",
748748
TitleText: "",
749749
DisplayTitle: "false",
@@ -778,7 +778,7 @@ func averageCPUUtilizationTableHTMLRenderer(tableValues TableValues, targetName
778778
}
779779
chartConfig := scatterChartTemplateStruct{
780780
ID: fmt.Sprintf("avgCPUUtil%d", rand.Intn(10000)),
781-
XaxisText: "Time/Samples",
781+
XaxisText: "Time",
782782
YaxisText: "% Utilization",
783783
TitleText: "",
784784
DisplayTitle: "false",
@@ -819,7 +819,7 @@ func irqRateTableHTMLRenderer(tableValues TableValues, targetName string) string
819819
}
820820
chartConfig := scatterChartTemplateStruct{
821821
ID: fmt.Sprintf("irqRate%d", rand.Intn(10000)),
822-
XaxisText: "Time/Samples",
822+
XaxisText: "Time",
823823
YaxisText: "IRQ/s",
824824
TitleText: "",
825825
DisplayTitle: "false",
@@ -875,7 +875,7 @@ func driveStatsTableHTMLRenderer(tableValues TableValues, targetName string) str
875875
}
876876
chartConfig := scatterChartTemplateStruct{
877877
ID: fmt.Sprintf("driveStats%d", rand.Intn(10000)),
878-
XaxisText: "Time/Samples",
878+
XaxisText: "Time",
879879
YaxisText: "",
880880
TitleText: drive,
881881
DisplayTitle: "true",
@@ -933,7 +933,7 @@ func networkStatsTableHTMLRenderer(tableValues TableValues, targetName string) s
933933
}
934934
chartConfig := scatterChartTemplateStruct{
935935
ID: fmt.Sprintf("nicStats%d", rand.Intn(10000)),
936-
XaxisText: "Time/Samples",
936+
XaxisText: "Time",
937937
YaxisText: "",
938938
TitleText: nic,
939939
DisplayTitle: "true",
@@ -970,7 +970,7 @@ func memoryStatsTableHTMLRenderer(tableValues TableValues, targetName string) st
970970
}
971971
chartConfig := scatterChartTemplateStruct{
972972
ID: fmt.Sprintf("memoryStats%d", rand.Intn(10000)),
973-
XaxisText: "Time/Samples",
973+
XaxisText: "Time",
974974
YaxisText: "kilobytes",
975975
TitleText: "",
976976
DisplayTitle: "false",
@@ -1005,7 +1005,7 @@ func powerStatsTableHTMLRenderer(tableValues TableValues, targetName string) str
10051005
}
10061006
chartConfig := scatterChartTemplateStruct{
10071007
ID: fmt.Sprintf("powerStats%d", rand.Intn(10000)),
1008-
XaxisText: "Time/Samples",
1008+
XaxisText: "Time",
10091009
YaxisText: "Watts",
10101010
TitleText: "",
10111011
DisplayTitle: "false",
@@ -1061,3 +1061,38 @@ func kernelLockAnalysisHTMLRenderer(tableValues TableValues, targetName string)
10611061
}
10621062
return renderHTMLTable([]string{}, values, "pure-table pure-table-striped", tableValueStyles)
10631063
}
1064+
1065+
func instructionMixTableHTMLRenderer(tableValues TableValues, targetname string) string {
1066+
data := [][]scatterPoint{}
1067+
datasetNames := []string{}
1068+
for _, field := range tableValues.Fields[1:] {
1069+
points := []scatterPoint{}
1070+
for i, val := range field.Values {
1071+
if val == "" {
1072+
break
1073+
}
1074+
stat, err := strconv.ParseFloat(val, 64)
1075+
if err != nil {
1076+
slog.Error("error parsing stat", slog.String("error", err.Error()))
1077+
return ""
1078+
}
1079+
points = append(points, scatterPoint{float64(i), stat})
1080+
}
1081+
if len(points) > 0 {
1082+
data = append(data, points)
1083+
datasetNames = append(datasetNames, field.Name)
1084+
}
1085+
}
1086+
chartConfig := scatterChartTemplateStruct{
1087+
ID: fmt.Sprintf("instrMix%d", rand.Intn(10000)),
1088+
XaxisText: "Time",
1089+
YaxisText: "% Samples",
1090+
TitleText: "",
1091+
DisplayTitle: "false",
1092+
DisplayLegend: "true",
1093+
AspectRatio: "2",
1094+
SuggestedMin: "0",
1095+
SuggestedMax: "0",
1096+
}
1097+
return renderScatterChart(data, datasetNames, chartConfig)
1098+
}

internal/report/table_defs.go

Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,14 @@ package report
66
// table_defs.go defines the tables used for generating reports
77

88
import (
9+
"encoding/csv"
910
"fmt"
1011
"log/slog"
1112
"math"
1213
"regexp"
1314
"strconv"
1415
"strings"
16+
"time"
1517

1618
"perfspect/internal/cpudb"
1719
"perfspect/internal/script"
@@ -118,6 +120,8 @@ const (
118120
CodePathFrequencyTableName = "Code Path Frequency"
119121
// lock table names
120122
KernelLockAnalysisTableName = "Kernel Lock Analysis"
123+
// process watch instruction mix table names
124+
InstructionMixTableName = "Instruction Mix"
121125
)
122126

123127
const (
@@ -612,6 +616,16 @@ var tableDefinitions = map[string]TableDefinition{
612616
},
613617
FieldsFunc: powerStatsTableValues,
614618
HTMLTableRendererFunc: powerStatsTableHTMLRenderer},
619+
InstructionMixTableName: {
620+
Name: InstructionMixTableName,
621+
MenuLabel: InstructionMixTableName,
622+
HasRows: true,
623+
ScriptNames: []string{
624+
script.InstructionMixScriptName,
625+
},
626+
FieldsFunc: instructionMixTableValues,
627+
HTMLTableRendererFunc: instructionMixTableHTMLRenderer,
628+
},
615629
//
616630
// flamegraph tables
617631
//
@@ -1937,3 +1951,84 @@ func kernelLockAnalysisTableValues(outputs map[string]script.ScriptOutput) []Fie
19371951
}
19381952
return fields
19391953
}
1954+
1955+
func instructionMixTableValues(outputs map[string]script.ScriptOutput) []Field {
1956+
// first two lines are not part of the CSV output, they are the start time and interval
1957+
var startTime time.Time
1958+
var interval int
1959+
for i, line := range strings.Split(outputs[script.InstructionMixScriptName].Stdout, "\n") {
1960+
if i == 0 {
1961+
if !strings.HasPrefix(line, "TIME") {
1962+
slog.Error("instruction mix output is not in expected format, missing TIME")
1963+
return []Field{}
1964+
} else {
1965+
val := strings.Split(line, " ")[1]
1966+
var err error
1967+
startTime, err = time.Parse("15:04:05", val)
1968+
if err != nil {
1969+
slog.Error(fmt.Sprintf("unable to parse instruction mix start time: %s", val))
1970+
return []Field{}
1971+
}
1972+
}
1973+
} else if i == 1 {
1974+
if !strings.HasPrefix(line, "INTERVAL") {
1975+
slog.Error("instruction mix output is not in expected format, missing INTERVAL")
1976+
return []Field{}
1977+
} else {
1978+
val := strings.Split(line, " ")[1]
1979+
var err error
1980+
interval, err = strconv.Atoi(val)
1981+
if err != nil {
1982+
slog.Error(fmt.Sprintf("unable to convert instruction mix interval to int: %s", val))
1983+
return []Field{}
1984+
}
1985+
}
1986+
} else {
1987+
break
1988+
}
1989+
}
1990+
// parse the CSV output
1991+
csvOutput := strings.Join(strings.Split(outputs[script.InstructionMixScriptName].Stdout, "\n")[2:], "\n")
1992+
r := csv.NewReader(strings.NewReader(csvOutput))
1993+
rows, err := r.ReadAll()
1994+
if err != nil {
1995+
slog.Error(err.Error())
1996+
return []Field{}
1997+
}
1998+
if len(rows) < 2 {
1999+
slog.Error("instruction mix output is not in expected format")
2000+
return []Field{}
2001+
}
2002+
fields := []Field{{Name: "Time"}}
2003+
// first row is the header, extract field names, skip the first three fields (interval, pid, name)
2004+
if len(rows[0]) < 3 {
2005+
slog.Error("instruction mix output is not in expected format")
2006+
return []Field{}
2007+
}
2008+
for _, field := range rows[0][3:] {
2009+
fields = append(fields, Field{Name: field})
2010+
}
2011+
sample := -1
2012+
// values start in 2nd row, we're only interested in the first row of the sample
2013+
for _, row := range rows[1:] {
2014+
if len(row) < 2+len(fields) {
2015+
continue
2016+
}
2017+
rowSample, err := strconv.Atoi(row[0])
2018+
if err != nil {
2019+
slog.Error(fmt.Sprintf("unable to convert instruction mix sample to int: %s", row[0]))
2020+
continue
2021+
}
2022+
if rowSample != sample { // new sample
2023+
sample = rowSample
2024+
for i := range fields {
2025+
if i == 0 {
2026+
fields[i].Values = append(fields[i].Values, startTime.Add(time.Duration(sample*interval)*time.Second).Format("15:04:05"))
2027+
} else {
2028+
fields[i].Values = append(fields[i].Values, row[i+2])
2029+
}
2030+
}
2031+
}
2032+
}
2033+
return fields
2034+
}

0 commit comments

Comments
 (0)