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
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
|
// Package trace traces function calls and steps in-between.
package trace
import (
"fmt"
"io"
"reflect"
"runtime"
"strings"
)
// Tracer produces a hierarchical structure of log events.
type Tracer struct {
Tracing bool
Out io.Writer
depth int
}
// Result marks an argument of Tracer.Call as a result of that function.
// It is only logged when exiting from the function but not when entering it.
type Result struct {
pointer interface{}
}
func (t *Tracer) Stepf(format string, args ...interface{}) {
if t.Tracing {
msg := fmt.Sprintf(format, args...)
_, _ = io.WriteString(t.Out, fmt.Sprintf("TRACE: %s %s\n", t.traceIndent(), msg))
}
}
func (t *Tracer) Step1(format string, arg0 string) {
t.Stepf(format, arg0)
}
func (t *Tracer) Step2(format string, arg0, arg1 string) {
t.Stepf(format, arg0, arg1)
}
func (t *Tracer) Call0() func() {
return t.traceCall()
}
func (t *Tracer) Call1(arg1 string) func() {
return t.traceCall(arg1)
}
func (t *Tracer) Call2(arg1, arg2 string) func() {
return t.traceCall(arg1, arg2)
}
// Call records a function call in the tracing log, both when entering and
// when leaving the function.
//
// Usage:
// if trace.Tracing {
// defer trace.Call(arg1, arg2, trace.Result(result1), trace.Result(result2))()
// }
func (t *Tracer) Call(args ...interface{}) func() {
return t.traceCall(args...)
}
// http://stackoverflow.com/questions/13476349/check-for-nil-and-nil-interface-in-go
func isNil(a interface{}) bool {
defer func() {
recover()
}()
return a == nil || reflect.ValueOf(a).IsNil()
}
func argsStr(args []interface{}) string {
rv := ""
for _, arg := range args {
if rv != "" {
rv += ", "
}
if str, ok := arg.(fmt.Stringer); ok && !isNil(str) {
rv += str.String()
} else {
rv += fmt.Sprintf("%#v", arg)
}
}
return rv
}
func (t *Tracer) traceIndent() string {
indent := ""
for i := 0; i < t.depth; i++ {
indent += fmt.Sprintf("%d ", i+1)
}
return indent
}
func (t *Tracer) traceCall(args ...interface{}) func() {
if !t.Tracing {
panic("Internal pkglint error: calls to trace.Call must only occur in tracing mode")
}
funcname := "?"
if pc, _, _, ok := runtime.Caller(2); ok {
if fn := runtime.FuncForPC(pc); fn != nil {
funcname = strings.TrimPrefix(fn.Name(), "netbsd.org/pkglint.")
}
}
indent := t.traceIndent()
_, _ = io.WriteString(t.Out, fmt.Sprintf("TRACE: %s+ %s(%s)\n", indent, funcname, argsStr(withoutResults(args))))
t.depth++
return func() {
t.depth--
_, _ = io.WriteString(t.Out, fmt.Sprintf("TRACE: %s- %s(%s)\n", indent, funcname, argsStr(withResults(args))))
}
}
// Result marks an argument as a result and is only logged when the function returns.
func (t *Tracer) Result(rv interface{}) Result {
if reflect.ValueOf(rv).Kind() != reflect.Ptr {
panic(fmt.Sprintf("Result must be called with a pointer to the result, not %#v.", rv))
}
return Result{rv}
}
func withoutResults(args []interface{}) []interface{} {
for i, arg := range args {
if _, ok := arg.(Result); ok {
return args[0:i]
}
}
return args
}
func withResults(args []interface{}) []interface{} {
for i, arg := range args {
if _, ok := arg.(Result); ok {
var awr []interface{}
awr = append(awr, args[0:i]...)
awr = append(awr, "=>")
for _, res := range args[i:] {
pointer := reflect.ValueOf(res.(Result).pointer)
actual := reflect.Indirect(pointer).Interface()
awr = append(awr, actual)
}
return awr
}
}
return args
}
|