From 7ea6d8fdf1bd8dcf6e49e879e18f98db005b4e40 Mon Sep 17 00:00:00 2001 From: Alessandro Arzilli Date: Tue, 18 Feb 2025 03:44:05 +0100 Subject: [PATCH] *: remove uses of reflect.MethodByName from all of Delve (#3916) When reflect.MethodByName is used the linker can not fully perform deadcode elimination. This commit updates cobra and rewrites the suitableMethods part of service/rpccommon so that reflect.MethodByName is not used and the linker can fully execute deadcode elimination. The executable size on go1.24.0 on linux is reduced from 25468606 bytes to 22453382 bytes or a reduction of approximately 12%. See also: https://github.com/spf13/cobra/pull/1956 https://github.com/aarzilli/whydeadcode --- _scripts/gen-suitablemethods.go | 81 ++ cmd/dlv/dlv_test.go | 14 +- go.mod | 6 +- go.sum | 12 +- pkg/version/buildinfo.go | 17 +- service/rpccommon/server.go | 114 +-- service/rpccommon/suitablemethods.go | 68 ++ .../cpuguy83/go-md2man/v2/md2man/debug.go | 62 ++ .../cpuguy83/go-md2man/v2/md2man/md2man.go | 13 +- .../cpuguy83/go-md2man/v2/md2man/roff.go | 209 +++-- vendor/github.com/spf13/cobra/.golangci.yml | 29 +- vendor/github.com/spf13/cobra/README.md | 11 +- vendor/github.com/spf13/cobra/active_help.go | 15 +- vendor/github.com/spf13/cobra/active_help.md | 157 ---- vendor/github.com/spf13/cobra/args.go | 4 +- .../spf13/cobra/bash_completions.go | 27 +- .../spf13/cobra/bash_completions.md | 93 --- .../spf13/cobra/bash_completionsV2.go | 154 +++- vendor/github.com/spf13/cobra/cobra.go | 31 +- vendor/github.com/spf13/cobra/command.go | 423 +++++++--- vendor/github.com/spf13/cobra/completions.go | 213 +++-- vendor/github.com/spf13/cobra/doc/README.md | 17 - vendor/github.com/spf13/cobra/doc/man_docs.go | 2 +- vendor/github.com/spf13/cobra/doc/man_docs.md | 31 - vendor/github.com/spf13/cobra/doc/md_docs.go | 10 +- vendor/github.com/spf13/cobra/doc/md_docs.md | 115 --- .../github.com/spf13/cobra/doc/rest_docs.go | 2 +- .../github.com/spf13/cobra/doc/rest_docs.md | 114 --- vendor/github.com/spf13/cobra/doc/util.go | 2 +- .../github.com/spf13/cobra/doc/yaml_docs.md | 112 --- .../spf13/cobra/fish_completions.go | 2 +- .../spf13/cobra/fish_completions.md | 4 - vendor/github.com/spf13/cobra/flag_groups.go | 86 ++- .../spf13/cobra/powershell_completions.go | 45 +- .../spf13/cobra/powershell_completions.md | 3 - .../spf13/cobra/projects_using_cobra.md | 64 -- .../spf13/cobra/shell_completions.md | 576 -------------- vendor/github.com/spf13/cobra/user_guide.md | 726 ------------------ .../github.com/spf13/cobra/zsh_completions.md | 48 -- vendor/github.com/spf13/pflag/.editorconfig | 12 + vendor/github.com/spf13/pflag/.golangci.yaml | 4 + vendor/github.com/spf13/pflag/flag.go | 29 +- vendor/github.com/spf13/pflag/ip.go | 3 + vendor/github.com/spf13/pflag/ipnet_slice.go | 147 ++++ vendor/github.com/spf13/pflag/string_array.go | 4 - vendor/modules.txt | 8 +- 46 files changed, 1401 insertions(+), 2518 deletions(-) create mode 100644 _scripts/gen-suitablemethods.go create mode 100644 service/rpccommon/suitablemethods.go create mode 100644 vendor/github.com/cpuguy83/go-md2man/v2/md2man/debug.go delete mode 100644 vendor/github.com/spf13/cobra/active_help.md delete mode 100644 vendor/github.com/spf13/cobra/bash_completions.md delete mode 100644 vendor/github.com/spf13/cobra/doc/README.md delete mode 100644 vendor/github.com/spf13/cobra/doc/man_docs.md delete mode 100644 vendor/github.com/spf13/cobra/doc/md_docs.md delete mode 100644 vendor/github.com/spf13/cobra/doc/rest_docs.md delete mode 100644 vendor/github.com/spf13/cobra/doc/yaml_docs.md delete mode 100644 vendor/github.com/spf13/cobra/fish_completions.md delete mode 100644 vendor/github.com/spf13/cobra/powershell_completions.md delete mode 100644 vendor/github.com/spf13/cobra/projects_using_cobra.md delete mode 100644 vendor/github.com/spf13/cobra/shell_completions.md delete mode 100644 vendor/github.com/spf13/cobra/user_guide.md delete mode 100644 vendor/github.com/spf13/cobra/zsh_completions.md create mode 100644 vendor/github.com/spf13/pflag/.editorconfig create mode 100644 vendor/github.com/spf13/pflag/.golangci.yaml create mode 100644 vendor/github.com/spf13/pflag/ipnet_slice.go diff --git a/_scripts/gen-suitablemethods.go b/_scripts/gen-suitablemethods.go new file mode 100644 index 0000000000..48e9bda80f --- /dev/null +++ b/_scripts/gen-suitablemethods.go @@ -0,0 +1,81 @@ +package main + +import ( + "bytes" + "fmt" + "go/format" + "go/token" + "go/types" + "io" + "log" + "os" + "strings" + + "golang.org/x/tools/go/packages" +) + +func must(err error, fmtstr string, args ...interface{}) { + if err != nil { + log.Fatalf(fmtstr, args...) + } +} + +func main() { + buf := bytes.NewBuffer([]byte{}) + fmt.Fprintf(buf, "// DO NOT EDIT: auto-generated using _scripts/gen-suitablemethods.go\n\n") + fmt.Fprintf(buf, "package rpccommon\n\n") + fmt.Fprintf(buf, "import ( \"reflect\"; \"github.com/go-delve/delve/service/rpc2\" )\n") + + dorpc(buf, "rpc2.RPCServer", "rpc2", "2") + dorpc(buf, "RPCServer", "rpccommon", "Common") + + src, err := format.Source(buf.Bytes()) + must(err, "error formatting source: %v", err) + + out := os.Stdout + if os.Args[1] != "-" { + if !strings.HasSuffix(os.Args[1], ".go") { + os.Args[1] += ".go" + } + out, err = os.Create(os.Args[1]) + must(err, "error creating %s: %v", os.Args[1], err) + } + _, err = out.Write(src) + must(err, "error writing output: %v", err) + if out != os.Stdout { + must(out.Close(), "error closing %s: %v", os.Args[1], err) + } +} + +func dorpc(buf io.Writer, typename, pkgname, outname string) { + fmt.Fprintf(buf, "func suitableMethods%s(s *%s, methods map[string]*methodType) {\n", outname, typename) + + fset := &token.FileSet{} + cfg := &packages.Config{ + Mode: packages.NeedSyntax | packages.NeedTypesInfo | packages.NeedName | packages.NeedCompiledGoFiles | packages.NeedTypes, + Fset: fset, + } + pkgs, err := packages.Load(cfg, fmt.Sprintf("github.com/go-delve/delve/service/%s", pkgname)) + must(err, "error loading packages: %v", err) + packages.Visit(pkgs, func(pkg *packages.Package) bool { + if pkg.PkgPath != fmt.Sprintf("github.com/go-delve/delve/service/%s", pkgname) { + return true + } + mset := types.NewMethodSet(types.NewPointer(pkg.Types.Scope().Lookup("RPCServer").Type())) + for i := 0; i < mset.Len(); i++ { + fn := mset.At(i).Obj().(*types.Func) + name := fn.Name() + if name[0] >= 'a' && name[0] <= 'z' { + continue + } + sig := fn.Type().(*types.Signature) + if sig.Params().Len() != 2 { + continue + } + fmt.Fprintf(buf, "methods[\"RPCServer.%s\"] = &methodType{ method: reflect.ValueOf(s.%s) }\n", name, name) + + } + return true + }, nil) + fmt.Fprintf(buf, "}\n\n") +} diff --git a/cmd/dlv/dlv_test.go b/cmd/dlv/dlv_test.go index 633b31ea80..06e8e0b7af 100644 --- a/cmd/dlv/dlv_test.go +++ b/cmd/dlv/dlv_test.go @@ -374,6 +374,7 @@ func TestGeneratedDoc(t *testing.T) { checkAutogenDoc(t, "pkg/terminal/starbind/starlark_mapping.go", "'go generate' inside pkg/terminal/starbind", runScript("_scripts/gen-starlark-bindings.go", "go", "-")) checkAutogenDoc(t, "Documentation/cli/starlark.md", "'go generate' inside pkg/terminal/starbind", runScript("_scripts/gen-starlark-bindings.go", "doc/dummy", "Documentation/cli/starlark.md")) checkAutogenDoc(t, "Documentation/faq.md", "'go run _scripts/gen-faq-toc.go Documentation/faq.md Documentation/faq.md'", runScript("_scripts/gen-faq-toc.go", "Documentation/faq.md", "-")) + checkAutogenDoc(t, "service/rpccommon/suitablemethods.go", "'go generate' inside service/rpccommon", runScript("_scripts/gen-suitablemethods.go", "-")) if goversion.VersionAfterOrEqual(runtime.Version(), 1, 18) { checkAutogenDoc(t, "_scripts/rtype-out.txt", "go run _scripts/rtype.go report _scripts/rtype-out.txt", runScript("_scripts/rtype.go", "report")) runScript("_scripts/rtype.go", "check") @@ -1346,7 +1347,7 @@ func TestVersion(t *testing.T) { want1 := []byte("mod\tgithub.com/go-delve/delve") want2 := []byte("dep\tgithub.com/google/go-dap") if !bytes.Contains(got, want1) || !bytes.Contains(got, want2) { - t.Errorf("got %s\nwant %v and %v in the output", got, want1, want2) + t.Errorf("got %s\nwant %s and %s in the output", got, want1, want2) } } @@ -1462,3 +1463,14 @@ func TestUnixDomainSocket(t *testing.T) { client.Detach(true) cmd.Wait() } + +func TestDeadcodeEliminated(t *testing.T) { + dlvbin := protest.GetDlvBinary(t) + buf, err := exec.Command("go", "tool", "nm", dlvbin).CombinedOutput() + if err != nil { + t.Fatalf("error executing go tool nm %s: %v", dlvbin, err) + } + if strings.Contains(string(buf), "MethodByName") { + t.Fatal("output of go tool nm contains MethodByName") + } +} diff --git a/go.mod b/go.mod index 572759119b..05f0bbd3ec 100644 --- a/go.mod +++ b/go.mod @@ -13,8 +13,8 @@ require ( github.com/mattn/go-colorable v0.1.13 github.com/mattn/go-isatty v0.0.20 github.com/sirupsen/logrus v1.9.3 - github.com/spf13/cobra v1.7.0 - github.com/spf13/pflag v1.0.5 + github.com/spf13/cobra v1.9.1 + github.com/spf13/pflag v1.0.6 go.starlark.net v0.0.0-20231101134539-556fd59b42f6 golang.org/x/arch v0.11.0 golang.org/x/sys v0.26.0 @@ -24,7 +24,7 @@ require ( ) require ( - github.com/cpuguy83/go-md2man/v2 v2.0.2 // indirect + github.com/cpuguy83/go-md2man/v2 v2.0.6 // indirect github.com/inconshreveable/mousetrap v1.1.0 // indirect github.com/mattn/go-runewidth v0.0.13 // indirect github.com/rivo/uniseg v0.2.0 // indirect diff --git a/go.sum b/go.sum index 06e5fa1bd3..9f4ed5e79c 100644 --- a/go.sum +++ b/go.sum @@ -2,8 +2,8 @@ github.com/cilium/ebpf v0.11.0 h1:V8gS/bTCCjX9uUnkUFUpPsksM8n1lXBAvHcpiFk1X2Y= github.com/cilium/ebpf v0.11.0/go.mod h1:WE7CZAnqOL2RouJ4f1uyNhqr2P4CCvXFIqdRDUgWsVs= github.com/cosiner/argv v0.1.0 h1:BVDiEL32lwHukgJKP87btEPenzrrHUjajs/8yzaqcXg= github.com/cosiner/argv v0.1.0/go.mod h1:EusR6TucWKX+zFgtdUsKT2Cvg45K5rtpCcWz4hK06d8= -github.com/cpuguy83/go-md2man/v2 v2.0.2 h1:p1EgwI/C7NhT0JmVkwCD2ZBK8j4aeHQX2pMHHBfMQ6w= -github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= +github.com/cpuguy83/go-md2man/v2 v2.0.6 h1:XJtiaUW6dEEqVuZiMTn1ldk455QWwEIsMIJlo5vtkx0= +github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g= github.com/creack/pty v1.1.20 h1:VIPb/a2s17qNeQgDnkfZC35RScx+blkKF8GV68n80J4= github.com/creack/pty v1.1.20/go.mod h1:MOBLtS5ELjhRRrroQr9kyvTxUAFNvYEK993ew/Vr4O4= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= @@ -45,10 +45,10 @@ github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ= github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= -github.com/spf13/cobra v1.7.0 h1:hyqWnYt1ZQShIddO5kBpj3vu05/++x6tJ6dg8EC572I= -github.com/spf13/cobra v1.7.0/go.mod h1:uLxZILRyS/50WlhOIKD7W6V5bgeIt+4sICxh6uRMrb0= -github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= -github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= +github.com/spf13/cobra v1.9.1 h1:CXSaggrXdbHK9CF+8ywj8Amf7PBRmPCOJugH954Nnlo= +github.com/spf13/cobra v1.9.1/go.mod h1:nDyEzZ8ogv936Cinf6g1RU9MRY64Ir93oCnqb9wxYW0= +github.com/spf13/pflag v1.0.6 h1:jFzHGLGAlb3ruxLB8MhbI6A8+AQX/2eW4qeyNZXNp2o= +github.com/spf13/pflag v1.0.6/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= diff --git a/pkg/version/buildinfo.go b/pkg/version/buildinfo.go index 6c34a453d7..150bea52bc 100644 --- a/pkg/version/buildinfo.go +++ b/pkg/version/buildinfo.go @@ -2,19 +2,14 @@ package version import ( "bytes" + "fmt" "runtime/debug" - "text/template" ) func init() { buildInfo = moduleBuildInfo } -var buildInfoTmpl = ` mod {{.Main.Path}} {{.Main.Version}} {{.Main.Sum}} -{{range .Deps}} dep {{.Path}} {{.Version}} {{.Sum}}{{if .Replace}} - => {{.Replace.Path}} {{.Replace.Version}} {{.Replace.Sum}}{{end}} -{{end}}` - func moduleBuildInfo() string { info, ok := debug.ReadBuildInfo() if !ok { @@ -22,9 +17,13 @@ func moduleBuildInfo() string { } buf := new(bytes.Buffer) - err := template.Must(template.New("buildinfo").Parse(buildInfoTmpl)).Execute(buf, info) - if err != nil { - panic(err) + fmt.Fprintf(buf, " mod\t%s\t%s\t%s\n", info.Main.Path, info.Main.Version, info.Main.Sum) + for _, dep := range info.Deps { + fmt.Fprintf(buf, " dep\t%s\t%s\t%s", dep.Path, dep.Version, dep.Sum) + if dep.Replace != nil { + fmt.Fprintf(buf, "\t=> %s\t%s\t%s", dep.Replace.Path, dep.Replace.Version, dep.Replace.Sum) + } + fmt.Fprintf(buf, "\n") } return buf.String() } diff --git a/service/rpccommon/server.go b/service/rpccommon/server.go index 3d202374a5..d0b2ad258e 100644 --- a/service/rpccommon/server.go +++ b/service/rpccommon/server.go @@ -14,8 +14,6 @@ import ( "reflect" "runtime" "sync" - "unicode" - "unicode/utf8" "github.com/go-delve/delve/pkg/logflags" "github.com/go-delve/delve/pkg/version" @@ -27,6 +25,8 @@ import ( "github.com/go-delve/delve/service/rpc2" ) +//go:generate go run ../../_scripts/gen-suitablemethods.go suitablemethods + // ServerImpl implements a JSON-RPC server that can switch between two // versions of the API. type ServerImpl struct { @@ -62,8 +62,7 @@ type RPCServer struct { } type methodType struct { - method reflect.Method - Rcvr reflect.Value + method reflect.Value ArgType reflect.Type ReplyType reflect.Type Synchronous bool @@ -128,8 +127,10 @@ func (s *ServerImpl) Run() error { s.methodMaps = make([]map[string]*methodType, 2) s.methodMaps[1] = map[string]*methodType{} - suitableMethods(s.s2, s.methodMaps[1], s.log) - suitableMethods(rpcServer, s.methodMaps[1], s.log) + + suitableMethods2(s.s2, s.methodMaps[1]) + suitableMethodsCommon(rpcServer, s.methodMaps[1]) + finishMethodsMapInit(s.methodMaps[1]) go func() { defer s.listener.Close() @@ -183,92 +184,15 @@ func (s *ServerImpl) serveConnectionDemux(c io.ReadWriteCloser) { } } -// Precompute the reflect type for error. Can't use error directly -// because Typeof takes an empty interface value. This is annoying. -var typeOfError = reflect.TypeOf((*error)(nil)).Elem() - -// Is this an exported - upper case - name? -func isExported(name string) bool { - ch, _ := utf8.DecodeRuneInString(name) - return unicode.IsUpper(ch) -} - -// Is this type exported or a builtin? -func isExportedOrBuiltinType(t reflect.Type) bool { - for t.Kind() == reflect.Ptr { - t = t.Elem() - } - // PkgPath will be non-empty even for an exported type, - // so we need to check the type name as well. - return isExported(t.Name()) || t.PkgPath() == "" -} - -// Fills methods map with the methods of receiver that should be made -// available through the RPC interface. -// These are all the public methods of rcvr that have one of those -// two signatures: -// -// func (rcvr ReceiverType) Method(in InputType, out *ReplyType) error -// func (rcvr ReceiverType) Method(in InputType, cb service.RPCCallback) -func suitableMethods(rcvr interface{}, methods map[string]*methodType, log logflags.Logger) { - typ := reflect.TypeOf(rcvr) - rcvrv := reflect.ValueOf(rcvr) - sname := reflect.Indirect(rcvrv).Type().Name() - if sname == "" { - log.Debugf("rpc.Register: no service name for type %s", typ) - return - } - for m := 0; m < typ.NumMethod(); m++ { - method := typ.Method(m) - mname := method.Name - mtype := method.Type - // method must be exported - if method.PkgPath != "" { - continue - } - // Method needs three ins: (receive, *args, *reply) or (receiver, *args, *RPCCallback) - if mtype.NumIn() != 3 { - log.Warn("method", mname, "has wrong number of ins:", mtype.NumIn()) - continue - } - // First arg need not be a pointer. - argType := mtype.In(1) - if !isExportedOrBuiltinType(argType) { - log.Warn(mname, "argument type not exported:", argType) - continue - } - - replyType := mtype.In(2) - synchronous := replyType.String() != "service.RPCCallback" - - if synchronous { - // Second arg must be a pointer. - if replyType.Kind() != reflect.Ptr { - log.Warn("method", mname, "reply type not a pointer:", replyType) - continue - } - // Reply type must be exported. - if !isExportedOrBuiltinType(replyType) { - log.Warn("method", mname, "reply type not exported:", replyType) - continue - } - - // Method needs one out. - if mtype.NumOut() != 1 { - log.Warn("method", mname, "has wrong number of outs:", mtype.NumOut()) - continue - } - // The return type of the method must be error. - if returnType := mtype.Out(0); returnType != typeOfError { - log.Warn("method", mname, "returns", returnType.String(), "not error") - continue - } - } else if mtype.NumOut() != 0 { - // Method needs zero outs. - log.Warn("method", mname, "has wrong number of outs:", mtype.NumOut()) - continue +func finishMethodsMapInit(methods map[string]*methodType) { + for name, method := range methods { + mtype := method.method.Type() + if mtype.NumIn() != 2 { + panic(fmt.Errorf("wrong number of inputs for method %s (%d)", name, mtype.NumIn())) } - methods[sname+"."+mname] = &methodType{method: method, ArgType: argType, ReplyType: replyType, Synchronous: synchronous, Rcvr: rcvrv} + method.ArgType = mtype.In(0) + method.ReplyType = mtype.In(1) + method.Synchronous = method.ReplyType.String() != "service.RPCCallback" } } @@ -326,7 +250,7 @@ func (s *ServerImpl) serveJSONCodec(conn io.ReadWriteCloser) { s.log.Debugf("<- %s(%T%s)", req.ServiceMethod, argv.Interface(), argvbytes) } replyv = reflect.New(mtype.ReplyType.Elem()) - function := mtype.method.Func + function := mtype.method var returnValues []reflect.Value var errInter interface{} func() { @@ -335,7 +259,7 @@ func (s *ServerImpl) serveJSONCodec(conn io.ReadWriteCloser) { errInter = newInternalError(ierr, 2) } }() - returnValues = function.Call([]reflect.Value{mtype.Rcvr, argv, replyv}) + returnValues = function.Call([]reflect.Value{argv, replyv}) errInter = returnValues[0].Interface() }() @@ -358,7 +282,7 @@ func (s *ServerImpl) serveJSONCodec(conn io.ReadWriteCloser) { argvbytes, _ := json.Marshal(argv.Interface()) s.log.Debugf("(async %d) <- %s(%T%s)", req.Seq, req.ServiceMethod, argv.Interface(), argvbytes) } - function := mtype.method.Func + function := mtype.method ctl := &RPCCallback{s, sending, codec, req, make(chan struct{}), clientDisconnectChan} go func() { defer func() { @@ -366,7 +290,7 @@ func (s *ServerImpl) serveJSONCodec(conn io.ReadWriteCloser) { ctl.Return(nil, newInternalError(ierr, 2)) } }() - function.Call([]reflect.Value{mtype.Rcvr, argv, reflect.ValueOf(ctl)}) + function.Call([]reflect.Value{argv, reflect.ValueOf(ctl)}) }() <-ctl.setupDone } diff --git a/service/rpccommon/suitablemethods.go b/service/rpccommon/suitablemethods.go new file mode 100644 index 0000000000..0b4e80538f --- /dev/null +++ b/service/rpccommon/suitablemethods.go @@ -0,0 +1,68 @@ +// DO NOT EDIT: auto-generated using _scripts/gen-suitablemethods.go + +package rpccommon + +import ( + "github.com/go-delve/delve/service/rpc2" + "reflect" +) + +func suitableMethods2(s *rpc2.RPCServer, methods map[string]*methodType) { + methods["RPCServer.AmendBreakpoint"] = &methodType{method: reflect.ValueOf(s.AmendBreakpoint)} + methods["RPCServer.Ancestors"] = &methodType{method: reflect.ValueOf(s.Ancestors)} + methods["RPCServer.AttachedToExistingProcess"] = &methodType{method: reflect.ValueOf(s.AttachedToExistingProcess)} + methods["RPCServer.BuildID"] = &methodType{method: reflect.ValueOf(s.BuildID)} + methods["RPCServer.CancelNext"] = &methodType{method: reflect.ValueOf(s.CancelNext)} + methods["RPCServer.Checkpoint"] = &methodType{method: reflect.ValueOf(s.Checkpoint)} + methods["RPCServer.ClearBreakpoint"] = &methodType{method: reflect.ValueOf(s.ClearBreakpoint)} + methods["RPCServer.ClearCheckpoint"] = &methodType{method: reflect.ValueOf(s.ClearCheckpoint)} + methods["RPCServer.Command"] = &methodType{method: reflect.ValueOf(s.Command)} + methods["RPCServer.CreateBreakpoint"] = &methodType{method: reflect.ValueOf(s.CreateBreakpoint)} + methods["RPCServer.CreateEBPFTracepoint"] = &methodType{method: reflect.ValueOf(s.CreateEBPFTracepoint)} + methods["RPCServer.CreateWatchpoint"] = &methodType{method: reflect.ValueOf(s.CreateWatchpoint)} + methods["RPCServer.DebugInfoDirectories"] = &methodType{method: reflect.ValueOf(s.DebugInfoDirectories)} + methods["RPCServer.Detach"] = &methodType{method: reflect.ValueOf(s.Detach)} + methods["RPCServer.Disassemble"] = &methodType{method: reflect.ValueOf(s.Disassemble)} + methods["RPCServer.DumpCancel"] = &methodType{method: reflect.ValueOf(s.DumpCancel)} + methods["RPCServer.DumpStart"] = &methodType{method: reflect.ValueOf(s.DumpStart)} + methods["RPCServer.DumpWait"] = &methodType{method: reflect.ValueOf(s.DumpWait)} + methods["RPCServer.Eval"] = &methodType{method: reflect.ValueOf(s.Eval)} + methods["RPCServer.ExamineMemory"] = &methodType{method: reflect.ValueOf(s.ExamineMemory)} + methods["RPCServer.FindLocation"] = &methodType{method: reflect.ValueOf(s.FindLocation)} + methods["RPCServer.FollowExec"] = &methodType{method: reflect.ValueOf(s.FollowExec)} + methods["RPCServer.FollowExecEnabled"] = &methodType{method: reflect.ValueOf(s.FollowExecEnabled)} + methods["RPCServer.FunctionReturnLocations"] = &methodType{method: reflect.ValueOf(s.FunctionReturnLocations)} + methods["RPCServer.GetBreakpoint"] = &methodType{method: reflect.ValueOf(s.GetBreakpoint)} + methods["RPCServer.GetBufferedTracepoints"] = &methodType{method: reflect.ValueOf(s.GetBufferedTracepoints)} + methods["RPCServer.GetThread"] = &methodType{method: reflect.ValueOf(s.GetThread)} + methods["RPCServer.GuessSubstitutePath"] = &methodType{method: reflect.ValueOf(s.GuessSubstitutePath)} + methods["RPCServer.IsMulticlient"] = &methodType{method: reflect.ValueOf(s.IsMulticlient)} + methods["RPCServer.LastModified"] = &methodType{method: reflect.ValueOf(s.LastModified)} + methods["RPCServer.ListBreakpoints"] = &methodType{method: reflect.ValueOf(s.ListBreakpoints)} + methods["RPCServer.ListCheckpoints"] = &methodType{method: reflect.ValueOf(s.ListCheckpoints)} + methods["RPCServer.ListDynamicLibraries"] = &methodType{method: reflect.ValueOf(s.ListDynamicLibraries)} + methods["RPCServer.ListFunctionArgs"] = &methodType{method: reflect.ValueOf(s.ListFunctionArgs)} + methods["RPCServer.ListFunctions"] = &methodType{method: reflect.ValueOf(s.ListFunctions)} + methods["RPCServer.ListGoroutines"] = &methodType{method: reflect.ValueOf(s.ListGoroutines)} + methods["RPCServer.ListLocalVars"] = &methodType{method: reflect.ValueOf(s.ListLocalVars)} + methods["RPCServer.ListPackageVars"] = &methodType{method: reflect.ValueOf(s.ListPackageVars)} + methods["RPCServer.ListPackagesBuildInfo"] = &methodType{method: reflect.ValueOf(s.ListPackagesBuildInfo)} + methods["RPCServer.ListRegisters"] = &methodType{method: reflect.ValueOf(s.ListRegisters)} + methods["RPCServer.ListSources"] = &methodType{method: reflect.ValueOf(s.ListSources)} + methods["RPCServer.ListTargets"] = &methodType{method: reflect.ValueOf(s.ListTargets)} + methods["RPCServer.ListThreads"] = &methodType{method: reflect.ValueOf(s.ListThreads)} + methods["RPCServer.ListTypes"] = &methodType{method: reflect.ValueOf(s.ListTypes)} + methods["RPCServer.ProcessPid"] = &methodType{method: reflect.ValueOf(s.ProcessPid)} + methods["RPCServer.Recorded"] = &methodType{method: reflect.ValueOf(s.Recorded)} + methods["RPCServer.Restart"] = &methodType{method: reflect.ValueOf(s.Restart)} + methods["RPCServer.Set"] = &methodType{method: reflect.ValueOf(s.Set)} + methods["RPCServer.Stacktrace"] = &methodType{method: reflect.ValueOf(s.Stacktrace)} + methods["RPCServer.State"] = &methodType{method: reflect.ValueOf(s.State)} + methods["RPCServer.StopRecording"] = &methodType{method: reflect.ValueOf(s.StopRecording)} + methods["RPCServer.ToggleBreakpoint"] = &methodType{method: reflect.ValueOf(s.ToggleBreakpoint)} +} + +func suitableMethodsCommon(s *RPCServer, methods map[string]*methodType) { + methods["RPCServer.GetVersion"] = &methodType{method: reflect.ValueOf(s.GetVersion)} + methods["RPCServer.SetApiVersion"] = &methodType{method: reflect.ValueOf(s.SetApiVersion)} +} diff --git a/vendor/github.com/cpuguy83/go-md2man/v2/md2man/debug.go b/vendor/github.com/cpuguy83/go-md2man/v2/md2man/debug.go new file mode 100644 index 0000000000..0ec4b12c75 --- /dev/null +++ b/vendor/github.com/cpuguy83/go-md2man/v2/md2man/debug.go @@ -0,0 +1,62 @@ +package md2man + +import ( + "fmt" + "io" + "os" + "strings" + + "github.com/russross/blackfriday/v2" +) + +func fmtListFlags(flags blackfriday.ListType) string { + knownFlags := []struct { + name string + flag blackfriday.ListType + }{ + {"ListTypeOrdered", blackfriday.ListTypeOrdered}, + {"ListTypeDefinition", blackfriday.ListTypeDefinition}, + {"ListTypeTerm", blackfriday.ListTypeTerm}, + {"ListItemContainsBlock", blackfriday.ListItemContainsBlock}, + {"ListItemBeginningOfList", blackfriday.ListItemBeginningOfList}, + {"ListItemEndOfList", blackfriday.ListItemEndOfList}, + } + + var f []string + for _, kf := range knownFlags { + if flags&kf.flag != 0 { + f = append(f, kf.name) + flags &^= kf.flag + } + } + if flags != 0 { + f = append(f, fmt.Sprintf("Unknown(%#x)", flags)) + } + return strings.Join(f, "|") +} + +type debugDecorator struct { + blackfriday.Renderer +} + +func depth(node *blackfriday.Node) int { + d := 0 + for n := node.Parent; n != nil; n = n.Parent { + d++ + } + return d +} + +func (d *debugDecorator) RenderNode(w io.Writer, node *blackfriday.Node, entering bool) blackfriday.WalkStatus { + fmt.Fprintf(os.Stderr, "%s%s %v %v\n", + strings.Repeat(" ", depth(node)), + map[bool]string{true: "+", false: "-"}[entering], + node, + fmtListFlags(node.ListFlags)) + var b strings.Builder + status := d.Renderer.RenderNode(io.MultiWriter(&b, w), node, entering) + if b.Len() > 0 { + fmt.Fprintf(os.Stderr, ">> %q\n", b.String()) + } + return status +} diff --git a/vendor/github.com/cpuguy83/go-md2man/v2/md2man/md2man.go b/vendor/github.com/cpuguy83/go-md2man/v2/md2man/md2man.go index b480056734..62d91b77d5 100644 --- a/vendor/github.com/cpuguy83/go-md2man/v2/md2man/md2man.go +++ b/vendor/github.com/cpuguy83/go-md2man/v2/md2man/md2man.go @@ -1,14 +1,23 @@ package md2man import ( + "os" + "strconv" + "github.com/russross/blackfriday/v2" ) // Render converts a markdown document into a roff formatted document. func Render(doc []byte) []byte { renderer := NewRoffRenderer() + var r blackfriday.Renderer = renderer + if v, _ := strconv.ParseBool(os.Getenv("MD2MAN_DEBUG")); v { + r = &debugDecorator{Renderer: r} + } return blackfriday.Run(doc, - []blackfriday.Option{blackfriday.WithRenderer(renderer), - blackfriday.WithExtensions(renderer.GetExtensions())}...) + []blackfriday.Option{ + blackfriday.WithRenderer(r), + blackfriday.WithExtensions(renderer.GetExtensions()), + }...) } diff --git a/vendor/github.com/cpuguy83/go-md2man/v2/md2man/roff.go b/vendor/github.com/cpuguy83/go-md2man/v2/md2man/roff.go index be2b343606..96a80c99b8 100644 --- a/vendor/github.com/cpuguy83/go-md2man/v2/md2man/roff.go +++ b/vendor/github.com/cpuguy83/go-md2man/v2/md2man/roff.go @@ -1,6 +1,8 @@ package md2man import ( + "bufio" + "bytes" "fmt" "io" "os" @@ -12,68 +14,72 @@ import ( // roffRenderer implements the blackfriday.Renderer interface for creating // roff format (manpages) from markdown text type roffRenderer struct { - extensions blackfriday.Extensions listCounters []int firstHeader bool - firstDD bool listDepth int } const ( - titleHeader = ".TH " - topLevelHeader = "\n\n.SH " - secondLevelHdr = "\n.SH " - otherHeader = "\n.SS " - crTag = "\n" - emphTag = "\\fI" - emphCloseTag = "\\fP" - strongTag = "\\fB" - strongCloseTag = "\\fP" - breakTag = "\n.br\n" - paraTag = "\n.PP\n" - hruleTag = "\n.ti 0\n\\l'\\n(.lu'\n" - linkTag = "\n\\[la]" - linkCloseTag = "\\[ra]" - codespanTag = "\\fB\\fC" - codespanCloseTag = "\\fR" - codeTag = "\n.PP\n.RS\n\n.nf\n" - codeCloseTag = "\n.fi\n.RE\n" - quoteTag = "\n.PP\n.RS\n" - quoteCloseTag = "\n.RE\n" - listTag = "\n.RS\n" - listCloseTag = "\n.RE\n" - dtTag = "\n.TP\n" - dd2Tag = "\n" - tableStart = "\n.TS\nallbox;\n" - tableEnd = ".TE\n" - tableCellStart = "T{\n" - tableCellEnd = "\nT}\n" + titleHeader = ".TH " + topLevelHeader = "\n\n.SH " + secondLevelHdr = "\n.SH " + otherHeader = "\n.SS " + crTag = "\n" + emphTag = "\\fI" + emphCloseTag = "\\fP" + strongTag = "\\fB" + strongCloseTag = "\\fP" + breakTag = "\n.br\n" + paraTag = "\n.PP\n" + hruleTag = "\n.ti 0\n\\l'\\n(.lu'\n" + linkTag = "\n\\[la]" + linkCloseTag = "\\[ra]" + codespanTag = "\\fB" + codespanCloseTag = "\\fR" + codeTag = "\n.EX\n" + codeCloseTag = ".EE\n" // Do not prepend a newline character since code blocks, by definition, include a newline already (or at least as how blackfriday gives us on). + quoteTag = "\n.PP\n.RS\n" + quoteCloseTag = "\n.RE\n" + listTag = "\n.RS\n" + listCloseTag = ".RE\n" + dtTag = "\n.TP\n" + dd2Tag = "\n" + tableStart = "\n.TS\nallbox;\n" + tableEnd = ".TE\n" + tableCellStart = "T{\n" + tableCellEnd = "\nT}\n" + tablePreprocessor = `'\" t` ) // NewRoffRenderer creates a new blackfriday Renderer for generating roff documents // from markdown func NewRoffRenderer() *roffRenderer { // nolint: golint - var extensions blackfriday.Extensions - - extensions |= blackfriday.NoIntraEmphasis - extensions |= blackfriday.Tables - extensions |= blackfriday.FencedCode - extensions |= blackfriday.SpaceHeadings - extensions |= blackfriday.Footnotes - extensions |= blackfriday.Titleblock - extensions |= blackfriday.DefinitionLists - return &roffRenderer{ - extensions: extensions, - } + return &roffRenderer{} } // GetExtensions returns the list of extensions used by this renderer implementation -func (r *roffRenderer) GetExtensions() blackfriday.Extensions { - return r.extensions +func (*roffRenderer) GetExtensions() blackfriday.Extensions { + return blackfriday.NoIntraEmphasis | + blackfriday.Tables | + blackfriday.FencedCode | + blackfriday.SpaceHeadings | + blackfriday.Footnotes | + blackfriday.Titleblock | + blackfriday.DefinitionLists } // RenderHeader handles outputting the header at document start func (r *roffRenderer) RenderHeader(w io.Writer, ast *blackfriday.Node) { + // We need to walk the tree to check if there are any tables. + // If there are, we need to enable the roff table preprocessor. + ast.Walk(func(node *blackfriday.Node, entering bool) blackfriday.WalkStatus { + if node.Type == blackfriday.Table { + out(w, tablePreprocessor+"\n") + return blackfriday.Terminate + } + return blackfriday.GoToNext + }) + // disable hyphenation out(w, ".nh\n") } @@ -86,12 +92,27 @@ func (r *roffRenderer) RenderFooter(w io.Writer, ast *blackfriday.Node) { // RenderNode is called for each node in a markdown document; based on the node // type the equivalent roff output is sent to the writer func (r *roffRenderer) RenderNode(w io.Writer, node *blackfriday.Node, entering bool) blackfriday.WalkStatus { - - var walkAction = blackfriday.GoToNext + walkAction := blackfriday.GoToNext switch node.Type { case blackfriday.Text: - escapeSpecialChars(w, node.Literal) + // Special case: format the NAME section as required for proper whatis parsing. + // Refer to the lexgrog(1) and groff_man(7) manual pages for details. + if node.Parent != nil && + node.Parent.Type == blackfriday.Paragraph && + node.Parent.Prev != nil && + node.Parent.Prev.Type == blackfriday.Heading && + node.Parent.Prev.FirstChild != nil && + bytes.EqualFold(node.Parent.Prev.FirstChild.Literal, []byte("NAME")) { + before, after, found := bytesCut(node.Literal, []byte(" - ")) + escapeSpecialChars(w, before) + if found { + out(w, ` \- `) + escapeSpecialChars(w, after) + } + } else { + escapeSpecialChars(w, node.Literal) + } case blackfriday.Softbreak: out(w, crTag) case blackfriday.Hardbreak: @@ -109,9 +130,16 @@ func (r *roffRenderer) RenderNode(w io.Writer, node *blackfriday.Node, entering out(w, strongCloseTag) } case blackfriday.Link: - if !entering { - out(w, linkTag+string(node.LinkData.Destination)+linkCloseTag) + // Don't render the link text for automatic links, because this + // will only duplicate the URL in the roff output. + // See https://daringfireball.net/projects/markdown/syntax#autolink + if !bytes.Equal(node.LinkData.Destination, node.FirstChild.Literal) { + out(w, string(node.FirstChild.Literal)) } + // Hyphens in a link must be escaped to avoid word-wrap in the rendered man page. + escapedLink := strings.ReplaceAll(string(node.LinkData.Destination), "-", "\\-") + out(w, linkTag+escapedLink+linkCloseTag) + walkAction = blackfriday.SkipChildren case blackfriday.Image: // ignore images walkAction = blackfriday.SkipChildren @@ -122,14 +150,25 @@ func (r *roffRenderer) RenderNode(w io.Writer, node *blackfriday.Node, entering case blackfriday.Document: break case blackfriday.Paragraph: - // roff .PP markers break lists - if r.listDepth > 0 { - return blackfriday.GoToNext - } if entering { - out(w, paraTag) + if r.listDepth > 0 { + // roff .PP markers break lists + if node.Prev != nil { // continued paragraph + if node.Prev.Type == blackfriday.List && node.Prev.ListFlags&blackfriday.ListTypeDefinition == 0 { + out(w, ".IP\n") + } else { + out(w, crTag) + } + } + } else if node.Prev != nil && node.Prev.Type == blackfriday.Heading { + out(w, crTag) + } else { + out(w, paraTag) + } } else { - out(w, crTag) + if node.Next == nil || node.Next.Type != blackfriday.List { + out(w, crTag) + } } case blackfriday.BlockQuote: if entering { @@ -160,6 +199,11 @@ func (r *roffRenderer) RenderNode(w io.Writer, node *blackfriday.Node, entering r.handleTableCell(w, node, entering) case blackfriday.HTMLSpan: // ignore other HTML tags + case blackfriday.HTMLBlock: + if bytes.HasPrefix(node.Literal, []byte("