From b7dff82ed9be188150e34bf558f74d10eba67909 Mon Sep 17 00:00:00 2001 From: Li Jie Date: Sun, 24 May 2026 19:21:22 +0800 Subject: [PATCH] runtime: fill basic function metadata --- .../lib/runtime/pprof_runtime_stub_llgo.go | 4 - runtime/internal/lib/runtime/symtab.go | 76 ++++++++++++++++--- test/go/runtime_func_metadata_test.go | 76 +++++++++++++++++++ test/goroot/xfail.yaml | 16 ---- 4 files changed, 142 insertions(+), 30 deletions(-) create mode 100644 test/go/runtime_func_metadata_test.go diff --git a/runtime/internal/lib/runtime/pprof_runtime_stub_llgo.go b/runtime/internal/lib/runtime/pprof_runtime_stub_llgo.go index 8f7045430b..ef3682b6ed 100644 --- a/runtime/internal/lib/runtime/pprof_runtime_stub_llgo.go +++ b/runtime/internal/lib/runtime/pprof_runtime_stub_llgo.go @@ -52,7 +52,3 @@ func NumGoroutine() int { } func SetCPUProfileRate(hz int) {} - -func FuncForPC(pc uintptr) *Func { - return nil -} diff --git a/runtime/internal/lib/runtime/symtab.go b/runtime/internal/lib/runtime/symtab.go index 57ac543cbf..b40493d8d4 100644 --- a/runtime/internal/lib/runtime/symtab.go +++ b/runtime/internal/lib/runtime/symtab.go @@ -105,6 +105,39 @@ func unknownFunctionName(pc uintptr) string { return "pc=" + uintptrHex(pc) } +func normalizeFunctionName(name string) string { + const stubPrefix = "__llgo_stub." + if len(name) > len(stubPrefix) && name[:len(stubPrefix)] == stubPrefix { + name = name[len(stubPrefix):] + } + switch name { + case "": + return "" + case "main": + return "main.main" + } + const mainPkg = "command-line-arguments." + if len(name) > len(mainPkg) && name[:len(mainPkg)] == mainPkg { + return "main." + name[len(mainPkg):] + } + return name +} + +func pcInfo(pc uintptr) (name string, entry uintptr, ok bool) { + info := &clitedebug.Info{} + if clitedebug.Addrinfo(unsafe.Pointer(pc), info) == 0 && pc > 0 { + info = &clitedebug.Info{} + if clitedebug.Addrinfo(unsafe.Pointer(pc-1), info) == 0 { + return "", 0, false + } + } + name = normalizeFunctionName(safeGoString(info.Sname, "")) + if name == "" { + return "", 0, false + } + return name, uintptr(info.Saddr), true +} + func (ci *Frames) Next() (frame Frame, more bool) { for len(ci.frames) < 2 { // Find the next frame. @@ -119,8 +152,8 @@ func (ci *Frames) Next() (frame Frame, more bool) { } else { pc, ci.callers = ci.callers[0], ci.callers[1:] } - info := &clitedebug.Info{} - if clitedebug.Addrinfo(unsafe.Pointer(pc), info) == 0 { + fn := FuncForPC(pc) + if fn == nil { ci.frames = append(ci.frames, Frame{ PC: pc, Function: unknownFunctionName(pc), @@ -131,17 +164,14 @@ func (ci *Frames) Next() (frame Frame, more bool) { }) continue } - fn := safeGoString(info.Sname, "") - if fn == "" { - fn = unknownFunctionName(pc) - } ci.frames = append(ci.frames, Frame{ PC: pc, - Function: fn, + Func: fn, + Function: fn.Name(), File: "", Line: 0, startLine: 0, - Entry: uintptr(info.Saddr), + Entry: fn.Entry(), }) } @@ -176,11 +206,37 @@ func CallersFrames(callers []uintptr) *Frames { // A Func represents a Go function in the running binary. type Func struct { - opaque struct{} // unexported field to disallow conversions + entry uintptr + name string } func (f *Func) Name() string { - panic("todo") + if f == nil { + return "" + } + return f.name +} + +func (f *Func) Entry() uintptr { + if f == nil { + return 0 + } + return f.entry +} + +func (f *Func) FileLine(pc uintptr) (file string, line int) { + return "", 0 +} + +func FuncForPC(pc uintptr) *Func { + if pc == 0 { + return nil + } + name, entry, ok := pcInfo(pc) + if !ok { + return &Func{entry: pc, name: unknownFunctionName(pc)} + } + return &Func{entry: entry, name: name} } // moduledata records information about the layout of the executable diff --git a/test/go/runtime_func_metadata_test.go b/test/go/runtime_func_metadata_test.go new file mode 100644 index 0000000000..c19f4938f8 --- /dev/null +++ b/test/go/runtime_func_metadata_test.go @@ -0,0 +1,76 @@ +/* + * Copyright (c) 2026 The XGo Authors (xgo.dev). All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package gotest + +import ( + "os" + "path/filepath" + "strings" + "testing" +) + +const runtimeFuncMetadataProbe = `package main + +import ( + "fmt" + "reflect" + "runtime" +) + +type T struct { + a, b int +} + +func f(t *T) int { + if t != nil { + return t.b + } + return 0 +} + +func g(t *T) int { + return f(t) + 5 +} + +func check(label, got, want string) { + if got != want { + panic(label + ": got " + got + ", want " + want) + } +} + +func main() { + check("func f", runtime.FuncForPC(reflect.ValueOf(f).Pointer()).Name(), "main.f") + check("func g", runtime.FuncForPC(reflect.ValueOf(g).Pointer()).Name(), "main.g") + fmt.Println("ok") +} +` + +func TestRuntimeFuncMetadataWithLLGo(t *testing.T) { + dir := t.TempDir() + file := filepath.Join(dir, "main.go") + if err := os.WriteFile(file, []byte(runtimeFuncMetadataProbe), 0644); err != nil { + t.Fatal(err) + } + + runGoCmd(t, dir, "run", file) + + root := findLLGoRoot(t) + t.Setenv("LLGO_ROOT", root) + if got := strings.TrimSpace(runGoCmd(t, root, "run", "./cmd/llgo", "run", file)); got != "ok" { + t.Fatalf("llgo probe output = %q, want ok", got) + } +} diff --git a/test/goroot/xfail.yaml b/test/goroot/xfail.yaml index a5a66d10b4..b15c43600f 100644 --- a/test/goroot/xfail.yaml +++ b/test/goroot/xfail.yaml @@ -2415,18 +2415,10 @@ xfails: directive: run case: fixedbugs/issue15281.go reason: latest main goroot run failure on darwin/arm64 - - platform: darwin/arm64 - directive: run - case: fixedbugs/issue17381.go - reason: latest main goroot run failure on darwin/arm64 - platform: darwin/arm64 directive: run case: fixedbugs/issue18149.go reason: latest main goroot run failure on darwin/arm64 - - platform: darwin/arm64 - directive: run - case: fixedbugs/issue21879.go - reason: latest main goroot run failure on darwin/arm64 - platform: darwin/arm64 directive: run case: fixedbugs/issue22083.go @@ -2463,10 +2455,6 @@ xfails: directive: run case: fixedbugs/issue29504.go reason: latest main goroot run failure on darwin/arm64 - - platform: darwin/arm64 - directive: run - case: fixedbugs/issue29735.go - reason: latest main goroot run failure on darwin/arm64 - platform: darwin/arm64 directive: run case: fixedbugs/issue30116.go @@ -2531,10 +2519,6 @@ xfails: directive: run case: fixedbugs/issue58300.go reason: latest main goroot run failure on darwin/arm64 - - platform: darwin/arm64 - directive: run - case: fixedbugs/issue58300b.go - reason: latest main goroot run failure on darwin/arm64 - platform: darwin/arm64 directive: run case: fixedbugs/issue5856.go