davecgh / go-spew

Implements a deep pretty printer for Go data structures to aid in debugging
ISC License
5.97k stars 361 forks source link

Stringer : runtime: goroutine stack exceeds 1000000000-byte limit #45

Closed jerome-laforge closed 8 years ago

jerome-laforge commented 8 years ago

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

davecgh commented 8 years ago

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}

jerome-laforge commented 8 years ago

ok, thx again for this confirmation

dmitshur commented 8 years ago

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?

davecgh commented 8 years ago

@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
dmitshur commented 8 years ago

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.

jerome-laforge commented 8 years ago

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.