-
Notifications
You must be signed in to change notification settings - Fork 3
/
Copy paththreads.go
140 lines (114 loc) · 3.29 KB
/
threads.go
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
package bt
import (
"fmt"
"os"
"strings"
)
type Thread struct {
Name string `json:"name"`
Fault bool `json:"fault"`
Stacks []StackFrame `json:"stack"`
}
type StackFrame struct {
FuncName string `json:"funcName"`
Library string `json:"library"`
SourceCodeID string `json:"sourceCode"`
Line string `json:"line"`
skipBacktrace bool
}
type SourceCode struct {
Text string `json:"text"`
Path string `json:"path"`
StartLine int `json:"startLine"`
StartColumn int `json:"startColumn"`
StartPos int `json:"startPos"`
TabWidth int `json:"tabWidth"`
}
func ParseThreadsFromStack(stackTrace []byte) (map[string]Thread, map[string]SourceCode) {
splitThreads := strings.Split(string(stackTrace), "\n\n")
sourceCodeID := 0
sourcesPath := make(map[string]int) // key: path, value: unique path number starting from 0.
threads := make(map[string]Thread) // key: index of split string, starting from 0.
sourceCodes := make(map[string]SourceCode) // key: unique path number starting from 0.
for threadID, stackText := range splitThreads {
lines := strings.Split(stackText, "\n")
sf := StackFrame{}
thread := Thread{Name: strings.TrimSuffix(lines[0], ":"), Fault: threadID == 0}
for i := 1; i < len(lines); i++ {
line := strings.TrimSpace(lines[i])
if line == "" {
continue
}
if i%2 != 0 { // odd lines are function paths
line = trimCreatedBy(line)
if strings.HasPrefix(line, "github.com/backtrace-labs/backtrace-go") {
sf.skipBacktrace = true
continue
}
sf.skipBacktrace = false
lastIndex, function := getLastPathIndexAndFunction(line)
if function == "panic" {
sf.FuncName = "panic"
sf.Library = "runtime"
continue
}
sf.FuncName = function
sf.Library = line[:lastIndex]
} else {
if sf.skipBacktrace {
continue
}
line = strings.TrimSpace(line)
line, _, _ = strings.Cut(line, " +")
path := ""
path, sf.Line, _ = strings.Cut(line, ":")
if scID, ok := sourcesPath[path]; ok {
sf.SourceCodeID = fmt.Sprintf("%d", scID)
} else {
strSourceCodeID := fmt.Sprintf("%d", sourceCodeID)
sourcesPath[path] = sourceCodeID
sourceCodes[strSourceCodeID] = readFileGetSourceCode(path)
sf.SourceCodeID = strSourceCodeID
sourceCodeID++
}
thread.Stacks = append(thread.Stacks, sf)
sf = StackFrame{}
}
}
if len(thread.Stacks) > 0 {
threads[fmt.Sprintf("%d", threadID)] = thread
}
}
return threads, sourceCodes
}
func readFileGetSourceCode(path string) SourceCode {
sc := SourceCode{}
bytes, err := os.ReadFile(path)
if err == nil {
sc.Text = string(bytes)
sc.StartLine = 1
sc.StartColumn = 1
sc.StartPos = 0
sc.TabWidth = Options.TabWidth
}
sc.Path = path
return sc
}
func getLastPathIndexAndFunction(line string) (int, string) {
if strings.HasSuffix(line, ")") {
lastIndex := strings.LastIndex(line, "(")
if lastIndex != -1 {
line = line[:lastIndex]
}
}
lastIndex := strings.LastIndex(line, ".")
function, _, _ := strings.Cut(line[lastIndex+1:], "(")
return lastIndex, function
}
func trimCreatedBy(line string) string {
if strings.HasPrefix(line, "created by") {
_, line, _ = strings.Cut(line, " by ")
line, _, _ = strings.Cut(line, " in ")
}
return line
}