summaryrefslogtreecommitdiff
path: root/debugon.go
blob: cf94f75f38085fa77653884e8c003c3c203ebb3a (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
// +build debug

package goparsify

import (
	"fmt"
	"runtime"
	"sort"
	"strings"
	"time"
)

var parsers []*debugParser

type debugParser struct {
	Description string
	Caller      string
	Next        Parser
	Time        time.Duration
	Calls       int
}

func (dp *debugParser) Parse(ps *State) Result {
	start := time.Now()

	ret := dp.Next(ps)

	dp.Time = dp.Time + time.Since(start)
	dp.Calls++

	return ret
}

func getPackageName(f *runtime.Func) string {
	parts := strings.Split(f.Name(), ".")
	pl := len(parts)

	if parts[pl-2][0] == '(' {
		return strings.Join(parts[0:pl-2], ".")
	} else {
		return strings.Join(parts[0:pl-1], ".")
	}
}

// NewParser should be called around the creation of every Parser.
// It does nothing normally and should incur no runtime overhead, but when building with -tags debug
// it will instrument every parser to collect valuable timing information displayable with DumpDebugStats.
func NewParser(description string, p Parser) Parser {
	fpcs := make([]uintptr, 1)
	caller := ""

	for i := 1; i < 10; i++ {
		n := runtime.Callers(i, fpcs)

		if n != 0 {
			frame := runtime.FuncForPC(fpcs[0] - 1)
			pkg := getPackageName(frame)

			if pkg != "github.com/vektah/goparsify" {
				file, line := frame.FileLine(fpcs[0] - 1)
				caller = fmt.Sprintf("%s:%d", file, line)
				break
			}
		}
	}

	dp := &debugParser{
		Description: description,
		Next:        p,
		Caller:      caller,
	}

	parsers = append(parsers, dp)
	return dp.Parse
}

// DumpDebugStats will print out the curring timings for each parser if built with -tags debug
func DumpDebugStats() {
	sort.Slice(parsers, func(i, j int) bool {
		return parsers[i].Time >= parsers[j].Time
	})

	fmt.Println("Parser stats:")
	for _, parser := range parsers {
		fmt.Printf("%20s\t%10s\t%10d\tcalls\t%s\n", parser.Description, parser.Time.String(), parser.Calls, parser.Caller)
	}
}