diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index b091456..107bdb5 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -22,7 +22,7 @@ jobs: - name: golangci-lint uses: golangci/golangci-lint-action@v9 with: - version: v2.11 + version: v2.13 # Run go test to ensure tests pass gotest: name: tests diff --git a/cmd/stackwhere/list_test.go b/cmd/stackwhere/list_test.go index 4fe1bf3..aedaa72 100644 --- a/cmd/stackwhere/list_test.go +++ b/cmd/stackwhere/list_test.go @@ -110,6 +110,32 @@ func TestListCollectionIncludesInstructionOnlyStackUsage(t *testing.T) { } } +func TestListCollectionSortsAfterInferringStackUsage(t *testing.T) { + cmd := root() + cmd.SetArgs([]string{"list", "../../testdata/noinline.o", "-j"}) + + var stdout bytes.Buffer + cmd.SetOut(&stdout) + + if err := cmd.Execute(); err != nil { + t.Fatalf("list command failed: %v", err) + } + + var got []stackview.ProgramStackUsage + if err := json.Unmarshal(stdout.Bytes(), &got); err != nil { + t.Fatalf("failed to decode JSON output: %v", err) + } + + want := []stackview.ProgramStackUsage{ + {Name: "entry", StackUsage: 8}, + {Name: "z_known", StackUsage: 8}, + {Name: "helper", StackUsage: 0}, + } + if !slices.Equal(got, want) { + t.Fatalf("unexpected JSON output: got %#v want %#v", got, want) + } +} + func TestListCollectionIncludesVoidFunction(t *testing.T) { cmd := root() cmd.SetArgs([]string{"list", "../../testdata/noinline.o"}) diff --git a/internal/stackview/stackview.go b/internal/stackview/stackview.go index aeb1c48..fc7fcb6 100644 --- a/internal/stackview/stackview.go +++ b/internal/stackview/stackview.go @@ -112,7 +112,7 @@ func (a *Analyzer) CollectionSummaryInCollection() ([]ProgramStackUsage, error) } summary := a.CollectionSummary() - filtered := make([]ProgramStackUsage, 0, len(summary)) + stackUsagePerProgram := make(map[string]int64, len(summary)) subProgsDwarf := a.tree.ByType(dbgdwarf.TagSubprogram) for _, prog := range summary { fn, ok := a.functions[prog.Name] @@ -128,10 +128,10 @@ func (a *Analyzer) CollectionSummaryInCollection() ([]ProgramStackUsage, error) prog.StackUsage = max(prog.StackUsage, inferredUsage) } - filtered = append(filtered, prog) + stackUsagePerProgram[prog.Name] = prog.StackUsage } - return filtered, nil + return SortProgramStackUsage(stackUsagePerProgram), nil } func stackUsageFromSlots(slots slotList) int64 { diff --git a/testdata/noinline.c b/testdata/noinline.c index cff74aa..becd6c1 100644 --- a/testdata/noinline.c +++ b/testdata/noinline.c @@ -1,5 +1,6 @@ #define __section(X) __attribute__((section(X), used)) #define __noinline __attribute__((noinline)) +#define force_on_stack(x) asm volatile("" :: "r"(&(x))) static __noinline void helper(int *value) { @@ -13,3 +14,11 @@ __section("tc") int entry(void *ctx) helper(&value); return value; } + +__section("tc/known") int z_known(void *ctx) +{ + long value = 0; + + force_on_stack(value); + return value; +} diff --git a/testdata/noinline.o b/testdata/noinline.o index abcb079..13fe0ba 100644 Binary files a/testdata/noinline.o and b/testdata/noinline.o differ