Documentation
¶
Overview ¶
Package perf provides call-graph-based performance analysis for ELPS source files. It detects hot paths, scaling risks, expensive-in-loop patterns, and recursive cycles by building and solving a call graph.
The analysis has three passes:
- Local scan — per-function AST walk producing FunctionSummary + CallEdge.
- Graph construction — aggregates summaries into a CallGraph.
- Solve — cycle detection, topological sort, score/scaling propagation, and issue generation.
Index ¶
- func FindConfigFile(dir string) string
- func FormatJSON(w io.Writer, issues []Issue) error
- func FormatText(w io.Writer, issues []Issue)
- func Solve(graph *CallGraph, cfg *Config) ([]*SolvedFunction, []Issue)
- type CallContext
- type CallEdge
- type CallGraph
- type Config
- type FunctionSummary
- type Issue
- type Result
- type RuleID
- type Severity
- type SolvedFunction
- type TraceEntry
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func FindConfigFile ¶
FindConfigFile searches for .elps-analyze.yaml starting from dir and walking up to the filesystem root. Returns the path if found, or empty string if not found.
func FormatJSON ¶
FormatJSON writes issues as a JSON array.
func FormatText ¶
FormatText writes issues in a human-readable text format similar to go vet output.
Types ¶
type CallContext ¶
type CallContext struct {
// LoopDepth is the nesting depth of loop forms at the call site.
LoopDepth int
// InLoop is true when LoopDepth > 0.
InLoop bool
}
CallContext describes where a call occurs relative to loops.
type CallEdge ¶
type CallEdge struct {
// Caller is the qualified name of the calling function.
Caller string
// Callee is the qualified name of the called function.
Callee string
// Source is the location of the call expression.
Source *token.Location
// Context describes the loop nesting at the call site.
Context CallContext
// IsExpensive is true if the callee matches an expensive function pattern.
IsExpensive bool
}
CallEdge represents a call from one function to another.
type CallGraph ¶
type CallGraph struct {
// Functions maps qualified function names to their summaries.
Functions map[string]*FunctionSummary
// Edges is the complete set of call edges.
Edges []CallEdge
}
CallGraph holds the complete call graph for a set of source files.
func BuildGraph ¶
func BuildGraph(summaries []*FunctionSummary) *CallGraph
BuildGraph performs Pass 2: aggregates per-function summaries into a CallGraph. Summaries from multiple files are merged by function name.
type Config ¶
type Config struct {
// ExpensiveFunctions are glob patterns matching function names considered
// expensive (e.g., "db-*", "http-*"). Calls to these functions receive
// an additional cost penalty.
ExpensiveFunctions []string `yaml:"expensive_functions"`
// ExpensiveCost is the extra cost added per call to an expensive function.
ExpensiveCost int `yaml:"expensive_cost"`
// LoopKeywords are symbols that introduce loop iteration in ELPS
// (e.g., "dotimes", "map", "foldl"). Embedders may append
// domain-specific iteration forms via config.
LoopKeywords []string `yaml:"loop_keywords"`
// LoopMultiplier is the assumed number of iterations per loop level.
// Used to amplify costs inside loops.
LoopMultiplier int `yaml:"loop_multiplier"`
// MaxScore is the PERF001 threshold: functions with TotalScore above
// this value are flagged as hot paths.
MaxScore int `yaml:"max_score"`
// MaxAcceptableOrder is the PERF002 warning threshold: functions with
// ScalingOrder >= this value receive a warning.
MaxAcceptableOrder int `yaml:"max_acceptable_order"`
// ScalingErrorThreshold is the PERF002 error threshold: functions with
// ScalingOrder >= this value receive an error.
ScalingErrorThreshold int `yaml:"scaling_error_threshold"`
// MaxRecursionOrder caps the scaling order assigned to recursive functions.
MaxRecursionOrder int `yaml:"max_recursion_order"`
// FunctionCosts provides per-function cost overrides. Keys are function
// names (exact match), values are the base cost for a single call.
// Overrides the default cost of 1. Embedders use this to assign
// domain-specific weights (e.g., {"statedb:put": 100}).
FunctionCosts map[string]int `yaml:"function_costs"`
// SuppressionPrefix is the comment text that disables analysis for a
// function. Defaults to "elps-analyze-disable". Embedders can
// override this (e.g., "substrate-analyze-disable").
SuppressionPrefix string `yaml:"suppression_prefix"`
// AmplificationCausesScaling enables N+1 detection mode. When true,
// calling an expensive function inside a loop adds +1 to the caller's
// scaling order beyond the loop depth contribution. This surfaces the
// classic N+1 query pattern more aggressively.
AmplificationCausesScaling bool `yaml:"amplification_causes_scaling"`
// Rules filters which rules to run. When empty, all rules run.
// Valid values: "PERF001", "PERF002", "PERF003", "PERF004", "UNKNOWN001".
Rules []string `yaml:"rules"`
}
Config controls the behavior of the performance analyzer.
func DefaultConfig ¶
func DefaultConfig() *Config
DefaultConfig returns a Config with sensible defaults.
func LoadConfigFile ¶
LoadConfigFile reads a YAML config file and returns a Config with defaults for any unset fields. Returns DefaultConfig() if the file does not exist.
type FunctionSummary ¶
type FunctionSummary struct {
// Name is the qualified function name (package:name or just name).
Name string
// Source is the location of the function definition.
Source *token.Location
// File is the source file containing this function.
File string
// LocalCost is the base cost of the function body (before propagation).
LocalCost int
// MaxLoopDepth is the deepest loop nesting found in the function body.
MaxLoopDepth int
// Calls are the outgoing call edges from this function.
Calls []CallEdge
// Suppressed is true if the function has an elps-analyze-disable comment.
Suppressed bool
}
FunctionSummary holds analysis results for a single function.
type Issue ¶
type Issue struct {
// Rule identifies which performance rule triggered.
Rule RuleID
// Severity indicates the severity level.
Severity Severity
// Message is a human-readable description of the issue.
Message string
// Function is the function where the issue was detected.
Function string
// Source is the source location of the issue.
Source *token.Location
// File is the source file.
File string
// Details provides additional context (e.g., cycle members, call chain).
Details []string
// Fingerprint is a stable identifier for this issue across runs,
// derived from sha256(file:function:rule)[:16].
Fingerprint string
// Trace is the call chain that explains why this issue was raised.
// For PERF001/PERF002, it shows the hot path from caller to expensive leaf.
// For PERF003, it shows the loop context.
Trace []TraceEntry
}
Issue is a performance diagnostic generated by the solver.
func (Issue) MarshalJSON ¶
MarshalJSON implements json.Marshaler for Issue.
type Result ¶
type Result struct {
// Solved contains the propagated scores for each function.
Solved []*SolvedFunction
// Issues contains all performance diagnostics.
Issues []Issue
// Graph is the call graph used for analysis.
Graph *CallGraph
}
Result holds the complete output of a performance analysis run.
type RuleID ¶
type RuleID string
RuleID identifies a performance rule.
const ( // PERF001 flags functions whose total propagated cost exceeds the threshold. PERF001 RuleID = "PERF001" // PERF002 flags functions with scaling order at or above the threshold. PERF002 RuleID = "PERF002" // PERF003 flags expensive function calls nested inside loops. PERF003 RuleID = "PERF003" // PERF004 flags recursive call cycles. PERF004 RuleID = "PERF004" // UNKNOWN001 flags dynamic dispatch where callee cannot be resolved. UNKNOWN001 RuleID = "UNKNOWN001" )
type SolvedFunction ¶
type SolvedFunction struct {
// Name is the qualified function name.
Name string
// Source is the location of the function definition.
Source *token.Location
// File is the source file.
File string
// LocalCost is the base cost before propagation.
LocalCost int
// TotalScore is the propagated cost including callee contributions.
TotalScore int
// ScalingOrder is the propagated loop amplification factor.
// 0 = O(1), 1 = O(N), 2 = O(N²), etc.
ScalingOrder int
// InCycle is true if this function participates in a recursive cycle.
InCycle bool
}
SolvedFunction holds propagated scores for a function after solving.
type TraceEntry ¶
type TraceEntry struct {
// Function is the function name at this step.
Function string
// Source is the source location.
Source *token.Location
// Note describes what happens at this step (e.g., "calls db-put in loop (depth 1)").
Note string
}
TraceEntry is a single step in a call chain trace.