aboutsummaryrefslogtreecommitdiffstats
path: root/vm
diff options
context:
space:
mode:
authorDmitry Vyukov <dvyukov@google.com>2016-01-19 17:17:40 +0100
committerDmitry Vyukov <dvyukov@google.com>2016-01-19 17:22:36 +0100
commit891b46a9a5a32dba986a8ba7e08d9192a2f9d894 (patch)
treed1f1e998cba1ee04c14a14e5e7f8a03af0814516 /vm
parentdfd341e3493321cb5de483efe7b2d78877aef344 (diff)
vm: faster output oops grepping
Use manual parsing instead of a regexp. Regexp takes ~220ms for typical output size. New code takes ~2ms. Brings manager CPU consumption from ~250% down to ~25%.
Diffstat (limited to 'vm')
-rw-r--r--vm/vm.go49
-rw-r--r--vm/vm_test.go21
2 files changed, 54 insertions, 16 deletions
diff --git a/vm/vm.go b/vm/vm.go
index 39f5989da..a3b077fd6 100644
--- a/vm/vm.go
+++ b/vm/vm.go
@@ -4,9 +4,9 @@
package vm
import (
+ "bytes"
"errors"
"fmt"
- "regexp"
"time"
)
@@ -60,9 +60,50 @@ func Create(typ string, cfg *Config) (Instance, error) {
return ctor(cfg)
}
+// FindCrash searches kernel console output for oops messages.
+// Desc contains a more-or-less representative description of the first oops,
+// start and end denote region of output with oops message(s).
+func FindCrash(output []byte) (desc string, start int, end int, found bool) {
+ for pos := 0; pos < len(output); {
+ next := bytes.IndexByte(output[pos:], '\n')
+ if next != -1 {
+ next += pos
+ } else {
+ next = len(output)
+ }
+ for _, oops := range oopses {
+ match := bytes.Index(output[pos:next], oops)
+ if match == -1 {
+ continue
+ }
+ if !found {
+ found = true
+ start = pos
+ desc = string(output[pos+match : next])
+ if desc[len(desc)-1] == '\r' {
+ desc = desc[:len(desc)-1]
+ }
+ }
+ end = next
+ }
+ pos = next + 1
+ }
+ return
+}
+
var (
- CrashRe = regexp.MustCompile("Kernel panic[^\r\n]*|BUG:[^\r\n]*|kernel BUG[^\r\n]*|WARNING:[^\r\n]*|" +
- "INFO:[^\r\n]*|unable to handle|Unable to handle kernel[^\r\n]*|general protection fault|UBSAN:[^\r\n]*|" +
- "unreferenced object[^\r\n]*")
+ oopses = [][]byte{
+ []byte("Kernel panic"),
+ []byte("BUG:"),
+ []byte("kernel BUG"),
+ []byte("WARNING:"),
+ []byte("INFO:"),
+ []byte("unable to handle"),
+ []byte("Unable to handle kernel"),
+ []byte("general protection fault"),
+ []byte("UBSAN:"),
+ []byte("unreferenced object"),
+ }
+
TimeoutErr = errors.New("timeout")
)
diff --git a/vm/vm_test.go b/vm/vm_test.go
index 896db8e57..d83ad98fa 100644
--- a/vm/vm_test.go
+++ b/vm/vm_test.go
@@ -8,7 +8,7 @@ import (
"testing"
)
-func TestCrashRe(t *testing.T) {
+func TestFindCrash(t *testing.T) {
tests := map[string]string{
`
[ 50.583499] something
@@ -23,7 +23,7 @@ func TestCrashRe(t *testing.T) {
`
[ 50.583499] general protection fault: 0000 [#1] SMP KASAN
[ 50.583499] Modules linked in:
-`: "general protection fault",
+`: "general protection fault: 0000 [#1] SMP KASAN",
`
[ 50.583499] BUG: unable to handle kernel NULL pointer dereference at 000000000000003a
[ 50.583499] Modules linked in:
@@ -82,19 +82,16 @@ locks_free_lock_context+0x118/0x180()
tests[strings.Replace(log, "\n", "\r\n", -1)] = crash
}
for log, crash := range tests {
- loc := CrashRe.FindStringIndex(log)
- if loc == nil && crash != "" {
+ desc, _, _, found := FindCrash([]byte(log))
+ //t.Logf("%v\nexpect '%v', found '%v'\n", log, crash, desc)
+ if !found && crash != "" {
t.Fatalf("did not find crash message '%v' in:\n%v", crash, log)
}
- if loc != nil && crash == "" {
- t.Fatalf("found bogus crash message '%v' in:\n%v", log[loc[0]:loc[1]], log)
+ if found && crash == "" {
+ t.Fatalf("found bogus crash message '%v' in:\n%v", desc, log)
}
- if loc == nil {
- continue
- }
- crash1 := log[loc[0]:loc[1]]
- if crash1 != crash {
- t.Fatalf("extracted bad crash message:\n%v\nwant:\n%v", crash1, crash)
+ if desc != crash {
+ t.Fatalf("extracted bad crash message:\n%v\nwant:\n%v", desc, crash)
}
}
}