...
1 package internal
2
3 import (
4 "bytes"
5 "fmt"
6 "io"
7 "strings"
8 )
9
10
11
12
13
14
15
16 func ErrorWithLog(err error, log []byte) *VerifierError {
17 const whitespace = "\t\r\v\n "
18
19
20
21 truncated := false
22 if i := bytes.IndexByte(log, 0); i != -1 {
23 if i == len(log)-1 && !bytes.HasSuffix(log[:i], []byte{'\n'}) {
24
25
26 truncated = true
27 }
28
29 log = log[:i]
30 } else if len(log) > 0 {
31
32 truncated = true
33 }
34
35 log = bytes.Trim(log, whitespace)
36 logLines := bytes.Split(log, []byte{'\n'})
37 lines := make([]string, 0, len(logLines))
38 for _, line := range logLines {
39
40
41 lines = append(lines, string(bytes.TrimRight(line, whitespace)))
42 }
43
44 return &VerifierError{err, lines, truncated}
45 }
46
47
48
49
50 type VerifierError struct {
51
52 Cause error
53
54 Log []string
55
56 Truncated bool
57 }
58
59 func (le *VerifierError) Unwrap() error {
60 return le.Cause
61 }
62
63 func (le *VerifierError) Error() string {
64 log := le.Log
65 if n := len(log); n > 0 && strings.HasPrefix(log[n-1], "processed ") {
66
67 log = log[:n-1]
68 }
69
70 n := len(log)
71 if n == 0 {
72 return le.Cause.Error()
73 }
74
75 lines := log[n-1:]
76 if n >= 2 && (includePreviousLine(log[n-1]) || le.Truncated) {
77
78 lines = log[n-2:]
79 }
80
81 var b strings.Builder
82 fmt.Fprintf(&b, "%s: ", le.Cause.Error())
83
84 for i, line := range lines {
85 b.WriteString(strings.TrimSpace(line))
86 if i != len(lines)-1 {
87 b.WriteString(": ")
88 }
89 }
90
91 omitted := len(le.Log) - len(lines)
92 if omitted == 0 && !le.Truncated {
93 return b.String()
94 }
95
96 b.WriteString(" (")
97 if le.Truncated {
98 b.WriteString("truncated")
99 }
100
101 if omitted > 0 {
102 if le.Truncated {
103 b.WriteString(", ")
104 }
105 fmt.Fprintf(&b, "%d line(s) omitted", omitted)
106 }
107 b.WriteString(")")
108
109 return b.String()
110 }
111
112
113
114 func includePreviousLine(line string) bool {
115
116
117
118
119 if strings.HasPrefix(line, "\t") {
120
121
122 return true
123 }
124
125 if len(line) >= 2 && line[0] == 'R' && line[1] >= '0' && line[1] <= '9' {
126
127
128 return true
129 }
130
131 if strings.HasPrefix(line, "invalid bpf_context access") {
132
133
134
135 return true
136 }
137
138 return false
139 }
140
141
142
143
144
145
146
147
148
149
150
151 func (le *VerifierError) Format(f fmt.State, verb rune) {
152 switch verb {
153 case 's':
154 _, _ = io.WriteString(f, le.Error())
155
156 case 'v':
157 n, haveWidth := f.Width()
158 if !haveWidth || n > len(le.Log) {
159 n = len(le.Log)
160 }
161
162 if !f.Flag('+') && !f.Flag('-') {
163 if haveWidth {
164 _, _ = io.WriteString(f, "%!v(BADWIDTH)")
165 return
166 }
167
168 _, _ = io.WriteString(f, le.Error())
169 return
170 }
171
172 if f.Flag('+') && f.Flag('-') {
173 _, _ = io.WriteString(f, "%!v(BADFLAG)")
174 return
175 }
176
177 fmt.Fprintf(f, "%s:", le.Cause.Error())
178
179 omitted := len(le.Log) - n
180 lines := le.Log[:n]
181 if f.Flag('-') {
182
183 lines = le.Log[len(le.Log)-n:]
184 if omitted > 0 {
185 fmt.Fprintf(f, "\n\t(%d line(s) omitted)", omitted)
186 }
187 }
188
189 for _, line := range lines {
190 fmt.Fprintf(f, "\n\t%s", line)
191 }
192
193 if !f.Flag('-') {
194 if omitted > 0 {
195 fmt.Fprintf(f, "\n\t(%d line(s) omitted)", omitted)
196 }
197 }
198
199 if le.Truncated {
200 fmt.Fprintf(f, "\n\t(truncated)")
201 }
202
203 default:
204 fmt.Fprintf(f, "%%!%c(BADVERB)", verb)
205 }
206 }
207
View as plain text