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
|
package trace2
import (
"context"
"fmt"
"sort"
"strings"
"time"
)
// Trace denotes a node in the tree representation of Git Trace2 events. A node is not necessary
// a one-one mapping of an event.
type Trace struct {
// Thread is the name of the thread of the corresponding event. The default thread name is
// "main". A new thread is assigned with a new name.
Thread string
// Name denotes the name of the trace. The node name depends on the event types. Data-type
// trace name is the most significant. It can be used to access the accurate data trace node
// For example: data:index:refresh/sum_scan
Name string
// StartTime is the starting time of the trace
StartTime time.Time
// FinishTime is the starting time of the trace
FinishTime time.Time
// Metadata is a map of metadata and data extracted from the event. A data-type trace always
// stores its data under "data" key of this map
Metadata map[string]string
// ChildID is the unique ID assigned by the parent process when it spawns a sub-process
// The ID of root process is empty.
ChildID string
// Parent points to the parent node of the current trace. The root node's parent is nil
Parent *Trace
// Children stores the list of order-significant traces belong to the current trace
Children []*Trace
// Depth indicates the depth of the trace node
Depth int
}
// IsRoot returns true if the current trace is the root of the tree
func (trace *Trace) IsRoot() bool {
return trace.Parent == nil
}
// Walk performs in-order tree traversal. It stops at each node and trigger handler function with
// the current trace.
func (trace *Trace) Walk(ctx context.Context, handler func(context.Context, *Trace) context.Context) {
if trace == nil {
return
}
ctx = handler(ctx, trace)
for _, child := range trace.Children {
child.Walk(ctx, handler)
}
}
// Inspect returns the formatted string of the tree. It mimics the format for trace2's performance
// target: https://git-scm.com/docs/api-trace2#_perf_format. It's mostly used for testing and
// debugging purpose.
func (trace *Trace) Inspect(detailed bool) string {
var output strings.Builder
trace.Walk(context.Background(), func(ctx context.Context, t *Trace) context.Context {
if output.Len() != 0 {
output.WriteString("\n")
}
if detailed {
output.WriteString(fmt.Sprintf("%s | %s ",
t.StartTime.UTC().Format(time.RFC3339Nano),
t.FinishTime.UTC().Format(time.RFC3339Nano)))
}
output.WriteString(fmt.Sprintf("| %-1s | %s | %s%s",
t.ChildID,
t.Thread,
strings.Repeat(".", t.Depth),
t.Name))
if detailed && len(t.Metadata) > 0 {
output.WriteString(fmt.Sprintf(" %s", t.inspectMetadata()))
}
return ctx
})
return output.String()
}
func (trace *Trace) setName(hints []string) {
var parts []string
for _, s := range hints {
if strings.TrimSpace(s) != "" {
parts = append(parts, s)
}
}
trace.Name = strings.Join(parts, ":")
}
func (trace *Trace) setMetadata(key, value string) {
if trace.Metadata == nil {
trace.Metadata = map[string]string{}
}
trace.Metadata[key] = value
}
func (trace *Trace) inspectMetadata() string {
var metadata strings.Builder
if len(trace.Metadata) > 0 {
metadata.WriteString("(")
keys := make([]string, 0, len(trace.Metadata))
for key := range trace.Metadata {
keys = append(keys, key)
}
// Sort metadata by key to make output deterministic
sort.Strings(keys)
for index, key := range keys {
if index != 0 {
metadata.WriteString(" ")
}
metadata.WriteString(fmt.Sprintf("%s=%q", key, trace.Metadata[key]))
}
metadata.WriteString(")")
}
return metadata.String()
}
|