blob: b28b4688338cba1e28966db34dd900132ef41304 [file] [log] [blame]
Abhay Kumara61c5222025-11-10 07:32:50 +00001// Copyright 2016 CoreOS, Inc.
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7// http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15package runtime
16
17import (
18 "fmt"
19 "math/rand"
20 "os"
21 "os/exec"
22 "strings"
23 "sync"
24 "time"
25)
26
27var (
28 ErrExhausted = fmt.Errorf("failpoint: terms exhausted")
29 ErrBadParse = fmt.Errorf("failpoint: could not parse terms")
30)
31
32// terms encodes the state for a failpoint term string (see fail(9) for examples)
33// <fp> :: <term> ( "->" <term> )*
34type terms struct {
35 // chain is a slice of all the terms from desc
36 chain []*term
37 // desc is the full term given for the failpoint
38 desc string
39 // fpath is the failpoint path for these terms
40 fpath string
41
42 // mu protects the state of the terms chain
43 mu sync.Mutex
44 // tracks executions count of terms that are actually evaluated
45 counter int
46}
47
48// term is an executable unit of the failpoint terms chain
49type term struct {
50 desc string
51
52 mods mod
53 act actFunc
54 val interface{}
55
56 parent *terms
57}
58
59type mod interface {
60 allow() bool
61}
62
63type modCount struct{ c int }
64
65func (mc *modCount) allow() bool {
66 if mc.c > 0 {
67 mc.c--
68 return true
69 }
70 return false
71}
72
73type modProb struct{ p float64 }
74
75func (mp *modProb) allow() bool { return rand.Float64() <= mp.p }
76
77type modList struct{ l []mod }
78
79func (ml *modList) allow() bool {
80 for _, m := range ml.l {
81 if !m.allow() {
82 return false
83 }
84 }
85 return true
86}
87
88func newTerms(fpath, desc string) (*terms, error) {
89 chain := parse(desc)
90 if len(chain) == 0 {
91 return nil, ErrBadParse
92 }
93 t := &terms{chain: chain, desc: desc, fpath: fpath}
94 for _, c := range chain {
95 c.parent = t
96 }
97 return t, nil
98}
99
100func (t *terms) String() string { return t.desc }
101
102func (t *terms) eval() interface{} {
103 t.mu.Lock()
104 defer t.mu.Unlock()
105 for _, term := range t.chain {
106 if term.mods.allow() {
107 t.counter++
108 return term.do()
109 }
110 }
111 return nil
112}
113
114// split terms from a -> b -> ... into [a, b, ...]
115func parse(desc string) (chain []*term) {
116 origDesc := desc
117 for len(desc) != 0 {
118 t := parseTerm(desc)
119 if t == nil {
120 fmt.Printf("failed to parse %q past %q\n", origDesc, desc)
121 return nil
122 }
123 desc = desc[len(t.desc):]
124 chain = append(chain, t)
125 if len(desc) >= 2 {
126 if !strings.HasPrefix(desc, "->") {
127 fmt.Printf("failed to parse %q past %q, expected \"->\"\n", origDesc, desc)
128 return nil
129 }
130 desc = desc[2:]
131 }
132 }
133 return chain
134}
135
136// <term> :: <mod> <act> [ "(" <val> ")" ]
137func parseTerm(desc string) *term {
138 t := &term{}
139 modStr, mods := parseMod(desc)
140 t.mods = &modList{mods}
141 actStr, act := parseAct(desc[len(modStr):])
142 t.act = act
143 valStr, val := parseVal(desc[len(modStr)+len(actStr):])
144 t.val = val
145 t.desc = desc[:len(modStr)+len(actStr)+len(valStr)]
146 if len(t.desc) == 0 {
147 return nil
148 }
149 return t
150}
151
152// <mod> :: ((<float> "%")|(<int> "*" ))*
153func parseMod(desc string) (ret string, mods []mod) {
154 for {
155 s, v := parseIntFloat(desc)
156 if len(s) == 0 {
157 break
158 }
159 if len(s) == len(desc) {
160 return "", nil
161 }
162 switch v := v.(type) {
163 case float64:
164 if desc[len(s)] != '%' {
165 return "", nil
166 }
167 ret = ret + desc[:len(s)+1]
168 mods = append(mods, &modProb{v / 100.0})
169 desc = desc[len(s)+1:]
170 case int:
171 if desc[len(s)] != '*' {
172 return "", nil
173 }
174 ret = ret + desc[:len(s)+1]
175 mods = append(mods, &modCount{v})
176 desc = desc[len(s)+1:]
177 default:
178 panic("???")
179 }
180 }
181 return ret, mods
182}
183
184// parseIntFloat parses an int or float from a string and returns the string
185// it parsed it from (unlike scanf).
186func parseIntFloat(desc string) (string, interface{}) {
187 // parse for ints
188 i := 0
189 for i < len(desc) {
190 if desc[i] < '0' || desc[i] > '9' {
191 break
192 }
193 i++
194 }
195 if i == 0 {
196 return "", nil
197 }
198
199 intVal := int(0)
200 _, err := fmt.Sscanf(desc[:i], "%d", &intVal)
201 if err != nil {
202 return "", nil
203 }
204 if len(desc) == i {
205 return desc[:i], intVal
206 }
207 if desc[i] != '.' {
208 return desc[:i], intVal
209 }
210
211 // parse for floats
212 i++
213 if i == len(desc) {
214 return desc[:i], float64(intVal)
215 }
216
217 j := i
218 for i < len(desc) {
219 if desc[i] < '0' || desc[i] > '9' {
220 break
221 }
222 i++
223 }
224 if j == i {
225 return desc[:i], float64(intVal)
226 }
227
228 f := float64(0)
229 if _, err = fmt.Sscanf(desc[:i], "%f", &f); err != nil {
230 return "", nil
231 }
232 return desc[:i], f
233}
234
235// parseAct parses an action
236// <act> :: "off" | "return" | "sleep" | "panic" | "break" | "print"
237func parseAct(desc string) (string, actFunc) {
238 for k, v := range actMap {
239 if strings.HasPrefix(desc, k) {
240 return k, v
241 }
242 }
243 return "", nil
244}
245
246// <val> :: <int> | <string> | <bool> | <nothing>
247func parseVal(desc string) (string, interface{}) {
248 // return => struct{}
249 if len(desc) == 0 {
250 return "", struct{}{}
251 }
252 // malformed
253 if len(desc) == 1 || desc[0] != '(' {
254 return "", nil
255 }
256 // return() => struct{}
257 if desc[1] == ')' {
258 return "()", struct{}{}
259 }
260 // return("s") => string
261 s := ""
262 n, err := fmt.Sscanf(desc[1:], "%q", &s)
263 if n == 1 && err == nil {
264 return desc[:len(s)+4], s
265 }
266 // return(1) => int
267 v := 0
268 n, err = fmt.Sscanf(desc[1:], "%d", &v)
269 if n == 1 && err == nil {
270 return desc[:len(fmt.Sprintf("%d", v))+2], v
271 }
272 // return(true) => bool
273 b := false
274 n, err = fmt.Sscanf(desc[1:], "%t", &b)
275 if n == 1 && err == nil {
276 return desc[:len(fmt.Sprintf("%t", b))+2], b
277 }
278 // unknown type; malformed input?
279 return "", nil
280}
281
282type actFunc func(*term) interface{}
283
284var actMap = map[string]actFunc{
285 "off": actOff,
286 "return": actReturn,
287 "sleep": actSleep,
288 "panic": actPanic,
289 "break": actBreak,
290 "print": actPrint,
291}
292
293func (t *term) do() interface{} { return t.act(t) }
294
295func actOff(_ *term) interface{} { return nil }
296
297func actReturn(t *term) interface{} { return t.val }
298
299func actSleep(t *term) interface{} {
300 var dur time.Duration
301 switch v := t.val.(type) {
302 case int:
303 dur = time.Duration(v) * time.Millisecond
304 case string:
305 vDur, err := time.ParseDuration(v)
306 if err != nil {
307 fmt.Printf("failpoint: could not parse sleep(%v) on %s\n", v, t.parent.fpath)
308 return nil
309 }
310 dur = vDur
311 default:
312 fmt.Printf("failpoint: ignoring sleep(%v) on %s\n", v, t.parent.fpath)
313 return nil
314 }
315 time.Sleep(dur)
316 return nil
317}
318
319func actPanic(t *term) interface{} {
320 panicMu.Lock()
321 defer panicMu.Unlock()
322
323 if t.val != nil {
324 panic(fmt.Sprintf("failpoint panic: %v", t.val))
325 }
326 panic("failpoint panic: " + t.parent.fpath)
327}
328
329func actBreak(_ *term) interface{} {
330 p, perr := exec.LookPath(os.Args[0])
331 if perr != nil {
332 panic(perr)
333 }
334 cmd := exec.Command("gdb", p, fmt.Sprintf("%d", os.Getpid()))
335 cmd.Stdin = os.Stdin
336 cmd.Stdout = os.Stdout
337 cmd.Stderr = os.Stderr
338 if err := cmd.Start(); err != nil {
339 panic(err)
340 }
341
342 // wait for gdb prompt
343 // XXX: tried doing this by piping stdout here and waiting on "(gdb) "
344 // but the the output won't appear since the process is STOPed and
345 // can't copy it back to the actual stdout
346 time.Sleep(3 * time.Second)
347
348 // don't zombie gdb
349 go cmd.Wait()
350 return nil
351}
352
353func actPrint(t *term) interface{} {
354 fmt.Println("failpoint print:", t.parent.fpath)
355 return nil
356}