davecgh / go-spew

Implements a deep pretty printer for Go data structures to aid in debugging

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Stringer : runtime: goroutine stack exceeds 1000000000-byte limit

jerome-laforge opened this issue · comments

Hello,
I want to use go-spew into String() string.
For example:

type Example struct {
    foo string
}

func (e Example) String() string {
  return spew.Sprintf("%+v", e)
}

Unfortunately, that generates:

runtime: goroutine stack exceeds 1000000000-byte limit
fatal error: stack overflow

runtime stack:
runtime.throw(0x55c280, 0xe)
    /usr/lib/go/src/runtime/panic.go:547 +0x90
runtime.newstack()
    /usr/lib/go/src/runtime/stack.go:940 +0xb11
runtime.morestack()
    /usr/lib/go/src/runtime/asm_amd64.s:359 +0x7f

goroutine 1 [stack growth]:
fmt.(*pp).argNumber(0xc826df4820, 0x0, 0x553da0, 0x3, 0x2, 0x1, 0x0, 0x0, 0x0)
    /usr/lib/go/src/fmt/print.go:1088 fp=0xc8401002b8 sp=0xc8401002b0
fmt.(*pp).doPrintf(0xc826df4820, 0x553da0, 0x3, 0xc826dbf530, 0x1, 0x1)
    /usr/lib/go/src/fmt/print.go:1144 +0xc87 fp=0xc840100640 sp=0xc8401002b8
fmt.Sprintf(0x553da0, 0x3, 0xc826dbf530, 0x1, 0x1, 0x0, 0x0)
    /usr/lib/go/src/fmt/print.go:203 +0x6f fp=0xc840100690 sp=0xc840100640
github.com/davecgh/go-spew/spew.Sprintf(0x553da0, 0x3, 0xc840100730, 0x1, 0x1, 0x0, 0x0)
    .../github.com/davecgh/go-spew/spew/spew.go:126 +0x90 fp=0xc8401006e8 sp=0xc840100690
main.Person.String(0x26, 0x5544e0, 0x6, 0x554540, 0x7, 0xc82000e0c0, 0x3, 0x3, 0x0, 0x0)
    .../src/cmd/spew/main.go:17 

Do you know a elegant manner to do this ?

Thx

That causes infinite recursion in the exact same way as the following would:

type Example struct {
    foo string
}

func (e Example) String() string {
  return fmt.Sprintf("%v", e)
}

I'd recommend not using spew in a stringer like this. The elegant way to handle it is to simply move the call to spew outside of the stringer to the caller like so:

package main

import "github.com/davecgh/go-spew/spew"

type Example struct {
    foo string
}

func main() {
    e := Example{"foostring"}
    spew.Printf("%+v\n", e)
}

Output:

{foo:foostring}

ok, thx again for this confirmation

I know this is a user error and should not be supported by spew, but I wonder if it's possible and a good idea for spew to detect when it's indirectly calling itself a second time, and print a more helpful error message?

Or is the overhead of doing that too much (in terms of code to maintain and run-time performance) to outweigh the benefit?

@shurcooL: That's not a bad idea, but I'm really not sure if that could be done without having undesirable side effects.

spew does detect recursion within in a single call by keeping track of the pointers in a local map that is part of the instance, but the issue here is it's effectively calling the upper-level print routine over and over.

Adding some type of global tracking to detect this would mean that code such as the following wouldn't work:

e := &Example{"foostring"}
spew.Println(foo)
e.foo = "Something new"
spew.Println(foo)   //  <---  spew would now filter this because it would be in the global already printed map

I don't have it completely figured out, it was just a thought.

I was imagining something that would look at the stack trace whenever a top-level spew function is called, to check that it's not being called a second time (from spew).

It may very well not be feasible; I just wanted us to consider it.

I wonder if it's possible and a good idea for spew to detect when it's indirectly calling itself a second time, and print a more helpful error message?

If this modification is possible, is it applicable to print directly the structure as spew usually rather than to display this helpful error message?
Or let to user by configuration the choice to display helpful error or print the structure directly without error.