Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -276,3 +276,4 @@ Use `builder/build.sh` to build the dependencies and the application in Docker c
### Subsequent Builds

`make` builds the app. It assumes the dependencies have been built previously and that you have Go installed on your development system. See [go.mod](go.mod) for the minimum Go version.

2 changes: 1 addition & 1 deletion cmd/metrics/loader.go
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,7 @@ func NewLoader(uarch string, useLegacyLoader bool) (Loader, error) {
case cpus.UarchCWF, cpus.UarchGNR, cpus.UarchGNR_X1, cpus.UarchGNR_X2, cpus.UarchGNR_X3, cpus.UarchGNR_D, cpus.UarchSRF, cpus.UarchSRF_SP, cpus.UarchSRF_AP, cpus.UarchEMR, cpus.UarchEMR_MCC, cpus.UarchEMR_XCC, cpus.UarchSPR, cpus.UarchSPR_MCC, cpus.UarchSPR_XCC, cpus.UarchICX:
slog.Debug("Using perfmon loader for microarchitecture", slog.String("uarch", uarch))
return newPerfmonLoader(), nil
case cpus.UarchGraviton2, cpus.UarchGraviton3, cpus.UarchGraviton4, cpus.UarchAxion, cpus.UarchAltraFamily, cpus.UarchAmpereOneAC03, cpus.UarchAmpereOneAC04, cpus.UarchAmpereOneAC04_1:
case cpus.UarchGraviton2, cpus.UarchGraviton3, cpus.UarchGraviton4, cpus.UarchGraviton5, cpus.UarchAxion, cpus.UarchAltraFamily, cpus.UarchAmpereOneAC03, cpus.UarchAmpereOneAC04, cpus.UarchAmpereOneAC04_1:
slog.Debug("Using component loader for microarchitecture", slog.String("uarch", uarch))
return newComponentLoader(), nil
default:
Expand Down
17 changes: 13 additions & 4 deletions cmd/metrics/loader_component.go
Original file line number Diff line number Diff line change
Expand Up @@ -228,20 +228,27 @@ func (l *ComponentLoader) formEventGroups(metrics []MetricDefinition, events []C
slog.Warn("Could not find event definition for metric variable, skipping variable", slog.String("metric", metric.Name), slog.String("variable", variable))
continue
}
if event.ArchStdEvent != "" {
// Choose how to name the event to perf, in order of preference: the architectural
// standard name, the vendor-specific name, then the raw event code.
if event.ArchStdEvent != "" && util.HasLineIgnoreCase(metadata.PerfAllSupportedEvents, event.ArchStdEvent) {
// perf knows this architectural event by name. Keep the case as-is; perf echoes
// the name back verbatim and the metric expressions are written against it.
perfRaw = event.ArchStdEvent
} else if event.EventName != "" {
// if the event name is supported by perf, use that. Otherwise, fall back to using the event code with the armv8_pmuv3_0/config= raw event format
if strings.Contains(metadata.PerfSupportedEvents, event.EventName) {
if util.HasLineIgnoreCase(metadata.PerfSupportedEvents, event.EventName) {
// perf knows this vendor-specific event by name
perfRaw = event.EventName
} else if event.EventCode != "" {
// perf doesn't know the name, so program the counter directly and alias
// the result back to the variable name the metric expression expects
perfRaw = fmt.Sprintf("armv8_pmuv3_0/config=%s,name=%s/", event.EventCode, variable)
} else {
// nothing left to try
slog.Warn("Event definition for metric variable does not have ArchStdEvent, EventName supported by perf, or EventCode, skipping variable", slog.String("metric", metric.Name), slog.String("variable", variable))
continue
}
} else {
// this shouldn't happen since the variable should match either ArchStdEvent or EventName, but log just in case
// findEventByName matched on one of the two name fields, so one must be set
slog.Warn("Event definition for metric variable does not have EventName or ArchStdEvent, skipping variable", slog.String("metric", metric.Name), slog.String("variable", variable))
continue
}
Expand Down Expand Up @@ -545,6 +552,8 @@ func initializeComponentMetricEvaluable(expression string, evaluatorFunctions ma
// the cpus module, to the directory where the associated events and metrics reside
func getUarchDir(uarch string) (string, error) {
switch uarch {
case cpus.UarchGraviton5:
return "neoverse-v3", nil
case cpus.UarchGraviton4, cpus.UarchAxion:
return "neoverse-n2-v2", nil
case cpus.UarchGraviton2:
Expand Down
5 changes: 4 additions & 1 deletion cmd/metrics/loader_legacy.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import (
"log/slog"
"os"
"path/filepath"
"perfspect/internal/util"
"strings"

mapset "github.com/deckarep/golang-set/v2"
Expand Down Expand Up @@ -228,7 +229,9 @@ func isCollectableEvent(event EventDefinition, metadata Metadata) bool {
}
// finally, if it isn't in the perf list output, it isn't collectable
name := strings.Split(event.Name, ":")[0]
if !strings.Contains(metadata.PerfSupportedEvents, name) {
// ensure that the event name is in the perf supported events list, ignoring case and making
// sure that the event name is a whole line match (not a substring match)
if !util.HasLineIgnoreCase(metadata.PerfSupportedEvents, name) {
slog.Debug("Event not supported by perf", slog.String("event", name))
return false
}
Expand Down
6 changes: 4 additions & 2 deletions cmd/metrics/loader_perfmon_event_other.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ package metrics
import (
"fmt"
"log/slog"
"perfspect/internal/util"
"strings"
)

Expand Down Expand Up @@ -49,8 +50,9 @@ func (event OtherEvent) IsCollectable(metadata Metadata) bool {
slog.Debug("System level events not supported in CPU granularity", slog.String("event", event.EventName))
return false
}
// check if the event is supported by perf
if !strings.Contains(metadata.PerfSupportedEvents, event.EventName) {
// ensure that the event name is in the perf supported events list, ignoring case and making
// sure that the event name is a whole line match (not a substring match)
if !util.HasLineIgnoreCase(metadata.PerfSupportedEvents, event.EventName) {
slog.Debug("Other event is not supported by perf", slog.String("event", event.EventName))
return false // other events are not supported
}
Expand Down
46 changes: 46 additions & 0 deletions cmd/metrics/metadata.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ import (
const (
scriptGetArchitecture = "get architecture"
scriptPerfSupportedEvents = "perf supported events"
scriptPerfAllSupportedEvents = "all perf supported events"
scriptListUncoreDevices = "list uncore devices"
scriptPerfStatInstructions = "perf stat instructions"
scriptPerfStatRefCycles = "perf stat ref-cycles"
Expand Down Expand Up @@ -65,6 +66,7 @@ type CommonMetadata struct {
Hostname string
ModelName string
PerfSupportedEvents string
PerfAllSupportedEvents string
SystemSummaryFields [][]string // slice of key-value pairs
SupportsInstructions bool
}
Expand Down Expand Up @@ -178,6 +180,40 @@ BEGIN {
event_name = ""
}
' # end of awk
`,
Depends: []string{"perf"},
},
{
Name: scriptGetArchitecture,
ScriptTemplate: "uname -m",
},
{
Name: scriptPerfAllSupportedEvents,
ScriptTemplate: `# Parse perf list JSON output to extract Hardware events and cstate/power events
perf list --json 2>/dev/null | awk '
BEGIN {
event_name = ""
}

# Capture EventName
/"EventName":/ {
# Extract the value between quotes after "EventName":
line = $0
sub(/.*"EventName": "/, "", line)
sub(/".*/, "", line)
event_name = line
}

# At end of object (closing brace), check if we should print
/^}/ {

if (event_name != "") {
print event_name
}
# Reset for next object
event_name = ""
}
' # end of awk
`,
Depends: []string{"perf"},
},
Expand Down Expand Up @@ -400,6 +436,16 @@ func getArchitecture(scriptOutputs map[string]script.ScriptOutput) (arch string,
return
}

// getPerfAllSupportedEvents returns the output from 'perf list'.
func getPerfAllSupportedEvents(scriptOutputs map[string]script.ScriptOutput) (supportedEvents string, err error) {
supportedEvents = scriptOutputs[scriptPerfAllSupportedEvents].Stdout
if scriptOutputs[scriptPerfAllSupportedEvents].Exitcode != 0 {
err = fmt.Errorf("failed to get all perf supported events: %s", scriptOutputs[scriptPerfAllSupportedEvents].Stderr)
return
}
return
}

// getPerfSupportedEvents returns the output from 'perf list'.
func getPerfSupportedEvents(scriptOutputs map[string]script.ScriptOutput) (supportedEvents string, err error) {
supportedEvents = scriptOutputs[scriptPerfSupportedEvents].Stdout
Expand Down
7 changes: 6 additions & 1 deletion cmd/metrics/metadata_aarch.go
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,11 @@ func (c *ARMMetadataCollector) CollectMetadata(t target.Target, noRoot bool, noS
return Metadata{}, fmt.Errorf("failed to retrieve architecture: %v", err)
}

// perf full list
if metadata.PerfAllSupportedEvents, err = getPerfAllSupportedEvents(scriptOutputs); err != nil {
return Metadata{}, fmt.Errorf("failed to load all perf supported events: %v", err)
}

// perf list
if metadata.PerfSupportedEvents, err = getPerfSupportedEvents(scriptOutputs); err != nil {
return Metadata{}, fmt.Errorf("failed to load perf list: %v", err)
Expand Down Expand Up @@ -229,7 +234,7 @@ func getARMSlots(scriptOutputs map[string]script.ScriptOutput) (slots int, err e
// Used as a fallback when we cannot read the slots from sysfs.
func getARMSlotsByArchitecture(uarch string) (slots int, err error) {
switch uarch {
case cpus.UarchGraviton4, cpus.UarchAxion:
case cpus.UarchGraviton4, cpus.UarchGraviton5, cpus.UarchAxion:
slots = 8
case cpus.UarchGraviton2, cpus.UarchGraviton3:
slots = 6
Expand Down
6 changes: 6 additions & 0 deletions cmd/metrics/resources/component/neoverse-v3/brbe.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
[
{
"ArchStdEvent": "BRB_FILTRATE",
"PublicDescription": "Counts branch records captured which are not removed by filtering."
}
]
18 changes: 18 additions & 0 deletions cmd/metrics/resources/component/neoverse-v3/bus.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
[
{
"ArchStdEvent": "BUS_ACCESS",
"PublicDescription": "Counts memory transactions issued by the CPU to the external bus, including snoop requests and snoop responses. Each beat of data is counted individually."
},
{
"ArchStdEvent": "BUS_CYCLES",
"PublicDescription": "Counts bus cycles in the CPU. Bus cycles represent a clock cycle in which a transaction could be sent or received on the interface from the CPU to the external bus. Since that interface is driven at the same clock speed as the CPU, this event is a duplicate of CPU_CYCLES."
},
{
"ArchStdEvent": "BUS_ACCESS_RD",
"PublicDescription": "Counts memory read transactions seen on the external bus. Each beat of data is counted individually."
},
{
"ArchStdEvent": "BUS_ACCESS_WR",
"PublicDescription": "Counts memory write transactions seen on the external bus. Each beat of data is counted individually."
}
]
62 changes: 62 additions & 0 deletions cmd/metrics/resources/component/neoverse-v3/exception.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
[
{
"ArchStdEvent": "EXC_TAKEN",
"PublicDescription": "Counts any taken architecturally visible exceptions such as IRQ, FIQ, SError, and other synchronous exceptions. Exceptions are counted whether or not they are taken locally."
},
{
"ArchStdEvent": "EXC_RETURN",
"PublicDescription": "Counts any architecturally executed exception return instructions. For example: AArch64: ERET"
},
{
"ArchStdEvent": "EXC_UNDEF",
"PublicDescription": "Counts the number of synchronous exceptions which are taken locally that are due to attempting to execute an instruction that is UNDEFINED. Attempting to execute instruction bit patterns that have not been allocated. Attempting to execute instructions when they are disabled. Attempting to execute instructions at an inappropriate Exception level. Attempting to execute an instruction when the value of PSTATE.IL is 1."
},
{
"ArchStdEvent": "EXC_SVC",
"PublicDescription": "Counts SVC exceptions taken locally."
},
{
"ArchStdEvent": "EXC_PABORT",
"PublicDescription": "Counts synchronous exceptions that are taken locally and caused by Instruction Aborts."
},
{
"ArchStdEvent": "EXC_DABORT",
"PublicDescription": "Counts exceptions that are taken locally and are caused by data aborts or SErrors. Conditions that could cause those exceptions are attempting to read or write memory where the MMU generates a fault, attempting to read or write memory with a misaligned address, interrupts from the nSEI inputs and internally generated SErrors."
},
{
"ArchStdEvent": "EXC_IRQ",
"PublicDescription": "Counts IRQ exceptions including the virtual IRQs that are taken locally."
},
{
"ArchStdEvent": "EXC_FIQ",
"PublicDescription": "Counts FIQ exceptions including the virtual FIQs that are taken locally."
},
{
"ArchStdEvent": "EXC_SMC",
"PublicDescription": "Counts SMC exceptions take to EL3."
},
{
"ArchStdEvent": "EXC_HVC",
"PublicDescription": "Counts HVC exceptions taken to EL2."
},
{
"ArchStdEvent": "EXC_TRAP_PABORT",
"PublicDescription": "Counts exceptions which are traps not taken locally and are caused by Instruction Aborts. For example, attempting to execute an instruction with a misaligned PC."
},
{
"ArchStdEvent": "EXC_TRAP_DABORT",
"PublicDescription": "Counts exceptions which are traps not taken locally and are caused by Data Aborts or SError interrupts. Conditions that could cause those exceptions are:\n\n1. Attempting to read or write memory where the MMU generates a fault,\n2. Attempting to read or write memory with a misaligned address,\n3. Interrupts from the SEI input.\n4. internally generated SErrors."
},
{
"ArchStdEvent": "EXC_TRAP_OTHER",
"PublicDescription": "Counts the number of synchronous trap exceptions which are not taken locally and are not SVC, SMC, HVC, data aborts, Instruction Aborts, or interrupts."
},
{
"ArchStdEvent": "EXC_TRAP_IRQ",
"PublicDescription": "Counts IRQ exceptions including the virtual IRQs that are not taken locally."
},
{
"ArchStdEvent": "EXC_TRAP_FIQ",
"PublicDescription": "Counts FIQs which are not taken locally but taken from EL0, EL1,\n or EL2 to EL3 (which would be the normal behavior for FIQs when not executing\n in EL3)."
}
]
22 changes: 22 additions & 0 deletions cmd/metrics/resources/component/neoverse-v3/fp_operation.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
[
{
"ArchStdEvent": "FP_HP_SPEC",
"PublicDescription": "Counts speculatively executed half precision floating point operations."
},
{
"ArchStdEvent": "FP_SP_SPEC",
"PublicDescription": "Counts speculatively executed single precision floating point operations."
},
{
"ArchStdEvent": "FP_DP_SPEC",
"PublicDescription": "Counts speculatively executed double precision floating point operations."
},
{
"ArchStdEvent": "FP_SCALE_OPS_SPEC",
"PublicDescription": "Counts speculatively executed scalable single precision floating point operations."
},
{
"ArchStdEvent": "FP_FIXED_OPS_SPEC",
"PublicDescription": "Counts speculatively executed non-scalable single precision floating point operations."
}
]
40 changes: 40 additions & 0 deletions cmd/metrics/resources/component/neoverse-v3/general.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
[
{
"ArchStdEvent": "CPU_CYCLES",
"PublicDescription": "Counts CPU clock cycles (not timer cycles). The clock measured by this event is defined as the physical clock driving the CPU logic."
},
{
"PublicDescription": "Count of RXDAT or RXRSP responses received with indication completer fullness indicator set to 0",
"EventCode": "0x198",
"EventName": "L2_CHI_CBUSY0",
"BriefDescription": "Number of RXDAT or RXRSP response received with CBusy of 0"
},
{
"PublicDescription": "Count of RXDAT or RXRSP responses received with indication completer fullness indicator set to 1",
"EventCode": "0x199",
"EventName": "L2_CHI_CBUSY1",
"BriefDescription": "Number of RXDAT or RXRSP response received with CBusy of 1"
},
{
"PublicDescription": "Count of RXDAT or RXRSP responses received with indication completer fullness indicator set to 2",
"EventCode": "0x19A",
"EventName": "L2_CHI_CBUSY2",
"BriefDescription": "Number of RXDAT or RXRSP response received with CBusy of 2"
},
{
"PublicDescription": "Count of RXDAT or RXRSP responses received with indication completer fullness indicator set to 3",
"EventCode": "0x19B",
"EventName": "L2_CHI_CBUSY3",
"BriefDescription": "Number of RXDAT or RXRSP response received with CBusy of 3"
},
{
"PublicDescription": "Count of RXDAT or RXRSP responses received with indication completer indicating multiple cores actively making requests",
"EventCode": "0x19C",
"EventName": "L2_CHI_CBUSY_MT",
"BriefDescription": "Number of RXDAT or RXRSP response received with CBusy Multi-threaded set"
},
{
"ArchStdEvent": "CNT_CYCLES",
"PublicDescription": "Increments at a constant frequency equal to the rate of increment of the System Counter, CNTPCT_EL0."
}
]
Loading