aboutsummaryrefslogtreecommitdiffstats
path: root/pkg
diff options
context:
space:
mode:
authorDmitry Vyukov <dvyukov@google.com>2020-09-12 17:12:12 +0200
committerDmitry Vyukov <dvyukov@google.com>2020-09-12 17:38:54 +0200
commit01622de2d0ec3b6cc18aef5bcbd5e76dd634116e (patch)
tree3bac8083af4a8cd50b641f68deebcc1851bfdad3 /pkg
parent7aa6bd6859a419bfb445a3621a14124fd7cecced (diff)
pkg/host: move machine info functionality from syz-fuzzer
It's better to keep functionality in packages rather than in main. It makes it reusable and better organized. Move machine info functionality to pkg/host and do some cosmetic refactoring.
Diffstat (limited to 'pkg')
-rw-r--r--pkg/host/machine_info.go34
-rw-r--r--pkg/host/machine_info_linux.go122
-rw-r--r--pkg/host/machine_info_test.go149
3 files changed, 305 insertions, 0 deletions
diff --git a/pkg/host/machine_info.go b/pkg/host/machine_info.go
new file mode 100644
index 000000000..7a7cd2612
--- /dev/null
+++ b/pkg/host/machine_info.go
@@ -0,0 +1,34 @@
+// Copyright 2020 syzkaller project authors. All rights reserved.
+// Use of this source code is governed by Apache 2 LICENSE that can be found in the LICENSE file.
+
+package host
+
+import (
+ "bytes"
+ "fmt"
+ "os"
+ "strings"
+)
+
+func CollectMachineInfo() ([]byte, error) {
+ buf := new(bytes.Buffer)
+ for _, pair := range machineInfoFuncs {
+ fmt.Fprintf(buf, "[%s]\n", pair.name)
+ err := pair.fn(buf)
+ if err != nil {
+ if !os.IsNotExist(err) {
+ return nil, err
+ }
+ fmt.Fprintf(buf, "%v\n", err)
+ }
+ fmt.Fprintf(buf, "%v\n\n", strings.Repeat("-", 80))
+ }
+ return buf.Bytes(), nil
+}
+
+var machineInfoFuncs []machineInfoFunc
+
+type machineInfoFunc struct {
+ name string
+ fn func(*bytes.Buffer) error
+}
diff --git a/pkg/host/machine_info_linux.go b/pkg/host/machine_info_linux.go
new file mode 100644
index 000000000..da1ee481a
--- /dev/null
+++ b/pkg/host/machine_info_linux.go
@@ -0,0 +1,122 @@
+// Copyright 2020 syzkaller project authors. All rights reserved.
+// Use of this source code is governed by Apache 2 LICENSE that can be found in the LICENSE file.
+
+package host
+
+import (
+ "bufio"
+ "bytes"
+ "fmt"
+ "io/ioutil"
+ "os"
+ "path/filepath"
+ "strings"
+)
+
+func init() {
+ machineInfoFuncs = []machineInfoFunc{
+ {"CPU Info", readCPUInfo},
+ {"KVM", readKVMInfo},
+ }
+}
+
+func readCPUInfo(buffer *bytes.Buffer) error {
+ file, err := os.Open("/proc/cpuinfo")
+ if err != nil {
+ return err
+ }
+ defer file.Close()
+
+ scanner := bufio.NewScanner(file)
+ scanCPUInfo(buffer, scanner)
+ return nil
+}
+
+func scanCPUInfo(buffer *bytes.Buffer, scanner *bufio.Scanner) {
+ keyIndices := make(map[string]int)
+ type keyValues struct {
+ key string
+ values []string
+ }
+ var info []keyValues
+
+ for scanner.Scan() {
+ splitted := strings.Split(scanner.Text(), ":")
+ if len(splitted) != 2 {
+ continue
+ }
+ key := strings.TrimSpace(splitted[0])
+ val := strings.TrimSpace(splitted[1])
+
+ if idx, ok := keyIndices[key]; !ok {
+ idx = len(keyIndices)
+ keyIndices[key] = idx
+ info = append(info, keyValues{key, []string{val}})
+ } else {
+ info[idx].values = append(info[idx].values, val)
+ }
+ }
+
+ for _, kv := range info {
+ // It is guaranteed that len(vals) >= 1
+ key := kv.key
+ vals := kv.values
+ if allEqual(vals) {
+ fmt.Fprintf(buffer, "%-20s: %s\n", key, vals[0])
+ } else {
+ fmt.Fprintf(buffer, "%-20s: %s\n", key, strings.Join(vals, ", "))
+ }
+ }
+}
+
+func allEqual(slice []string) bool {
+ if len(slice) == 0 {
+ return true
+ }
+ for i := 1; i < len(slice); i++ {
+ if slice[i] != slice[0] {
+ return false
+ }
+ }
+ return true
+}
+
+func readKVMInfo(buffer *bytes.Buffer) error {
+ files, err := ioutil.ReadDir("/sys/module/")
+ if err != nil {
+ return err
+ }
+
+ for _, file := range files {
+ name := file.Name()
+ if !strings.HasPrefix(name, "kvm") {
+ continue
+ }
+
+ paramPath := filepath.Join("/sys", "module", name, "parameters")
+ params, err := ioutil.ReadDir(paramPath)
+ if err != nil {
+ if os.IsNotExist(err) {
+ continue
+ }
+ return err
+ }
+
+ if len(params) == 0 {
+ continue
+ }
+
+ fmt.Fprintf(buffer, "/sys/module/%s:\n", name)
+ for _, key := range params {
+ keyName := key.Name()
+ data, err := ioutil.ReadFile(filepath.Join(paramPath, keyName))
+ if err != nil {
+ return err
+ }
+ fmt.Fprintf(buffer, "\t%s: ", keyName)
+ buffer.Write(data)
+ }
+ buffer.WriteByte('\n')
+ }
+ return nil
+}
diff --git a/pkg/host/machine_info_test.go b/pkg/host/machine_info_test.go
new file mode 100644
index 000000000..49ef93fe0
--- /dev/null
+++ b/pkg/host/machine_info_test.go
@@ -0,0 +1,149 @@
+// Copyright 2020 syzkaller project authors. All rights reserved.
+// Use of this source code is governed by Apache 2 LICENSE that can be found in the LICENSE file.
+
+package host
+
+import (
+ "bufio"
+ "bytes"
+ "runtime"
+ "strings"
+ "testing"
+)
+
+func TestMachineInfoLinux(t *testing.T) {
+ if runtime.GOOS != "linux" {
+ t.Skip()
+ }
+ result, err := CollectMachineInfo()
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ scanner := bufio.NewScanner(bytes.NewReader(result))
+
+ for scanner.Scan() {
+ line := scanner.Text()
+
+ if line == "[CPU Info]" {
+ checkCPUInfo(t, scanner)
+ }
+ if line == "[KVM]" {
+ checkKVMInfo(t, scanner)
+ }
+ }
+}
+
+func checkCPUInfo(t *testing.T, scanner *bufio.Scanner) {
+ keys := make(map[string]bool)
+ for scanner.Scan() {
+ line := scanner.Text()
+ // End of CPU Info section.
+ if strings.HasPrefix(line, "-----") {
+ break
+ }
+ splitted := strings.Split(line, ":")
+ if len(splitted) != 2 {
+ t.Fatalf("the format of line \"%s\" is not correct", line)
+ }
+ key := strings.TrimSpace(splitted[0])
+ keys[key] = true
+ }
+
+ importantKeys := [][]string{
+ {"vendor", "vendor_id", "CPU implementer"},
+ {"model", "CPU part", "cpu model"},
+ {"flags", "features", "Features", "ASEs implemented", "type"},
+ }
+ for _, possibleNames := range importantKeys {
+ exists := false
+ for _, name := range possibleNames {
+ if keys[name] {
+ exists = true
+ break
+ }
+ }
+ if !exists {
+ t.Fatalf("one of {%s} should exists in the output, but not found",
+ strings.Join(possibleNames, ", "))
+ }
+ }
+}
+
+func checkKVMInfo(t *testing.T, scanner *bufio.Scanner) {
+ for scanner.Scan() {
+ line := scanner.Text()
+ if line == "" {
+ continue
+ }
+ if strings.HasPrefix(line, "-----") {
+ break
+ }
+ splitted := strings.Split(line, ":")
+ if len(splitted) != 2 {
+ t.Fatalf("the format of line \"%s\" is not correct", line)
+ }
+ key := strings.TrimSpace(splitted[0])
+ if key == "" {
+ t.Fatalf("empty key")
+ }
+ if key[0] != '/' {
+ continue
+ }
+
+ if !strings.HasPrefix(key, "/sys/module/kvm") {
+ t.Fatalf("the directory does not match /sys/module/kvm*")
+ }
+ }
+}
+
+func TestScanCPUInfo(t *testing.T) {
+ input := `A: a
+B: b
+
+C: c1
+D: d
+C: c1
+D: d
+C: c2
+D: d
+`
+
+ output := []struct {
+ key, val string
+ }{
+ {"A", "a"},
+ {"B", "b"},
+ {"C", "c1, c1, c2"},
+ {"D", "d"},
+ }
+ scanner := bufio.NewScanner(strings.NewReader(input))
+ buffer := new(bytes.Buffer)
+ scanCPUInfo(buffer, scanner)
+ result := bufio.NewScanner(buffer)
+
+ idx := 0
+ for result.Scan() {
+ line := result.Text()
+ splitted := strings.Split(line, ":")
+ if len(splitted) != 2 {
+ t.Fatalf("the format of line \"%s\" is not correct", line)
+ }
+ key := strings.TrimSpace(splitted[0])
+ val := strings.TrimSpace(splitted[1])
+ if idx >= len(output) {
+ t.Fatalf("additional line \"%s: %s\"", key, val)
+ }
+ expected := output[idx]
+ if key != expected.key || val != expected.val {
+ t.Fatalf("expected \"%s: %s\", got \"%s: %s\"",
+ expected.key, expected.val, key, val)
+ }
+ idx++
+ }
+ if idx < len(output) {
+ expected := output[idx]
+ t.Fatalf("expected \"%s: %s\", got end of output",
+ expected.key, expected.val)
+ }
+}