diff --git a/CHANGELOG.md b/CHANGELOG.md index 28ce825c5..4f7d245b2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,4 @@ -## v0.10.5 [unreleased] +## v0.11.0 [unreleased] ### Release Notes @@ -16,6 +16,7 @@ - [#776](https://github.com/influxdata/telegraf/pull/776): Add Zookeeper chroot option to kafka_consumer. Thanks @prune998! - [#811](https://github.com/influxdata/telegraf/pull/811): Add processes plugin for classifying total procs on system. Thanks @titilambert! - [#235](https://github.com/influxdata/telegraf/issues/235): Add number of users to the `system` input plugin. +- [#826](https://github.com/influxdata/telegraf/pull/826): "kernel" linux plugin for /proc/stat metrics (context switches, interrupts, etc.) ### Bugfixes - [#748](https://github.com/influxdata/telegraf/issues/748): Fix sensor plugin split on ":" @@ -27,6 +28,7 @@ - [#708](https://github.com/influxdata/telegraf/issues/708): packaging: build ARM package - [#713](https://github.com/influxdata/telegraf/issues/713): packaging: insecure permissions error on log directory - [#816](https://github.com/influxdata/telegraf/issues/816): Fix phpfpm panic if fcgi endpoint unreachable. +- [#828](https://github.com/influxdata/telegraf/issues/828): fix net_response plugin overwriting host tag. ## v0.10.4.1 diff --git a/README.md b/README.md index fb9363100..8f9b0bc33 100644 --- a/README.md +++ b/README.md @@ -215,6 +215,7 @@ Currently implemented sources: * diskio * swap * processes + * kernel (/proc/stat) Telegraf can also collect metrics via the following service plugins: diff --git a/etc/telegraf.conf b/etc/telegraf.conf index 3deb7f895..0e740f5c8 100644 --- a/etc/telegraf.conf +++ b/etc/telegraf.conf @@ -119,6 +119,10 @@ # Uncomment the following line if you do not need disk serial numbers. # skip_serial_number = true +# Get kernel statistics from /proc/stat +[[inputs.kernel]] + # no configuration + # Read metrics about memory usage [[inputs.mem]] # no configuration diff --git a/plugins/inputs/net_response/README.md b/plugins/inputs/net_response/README.md index 69e72a379..d6a0e1278 100644 --- a/plugins/inputs/net_response/README.md +++ b/plugins/inputs/net_response/README.md @@ -52,7 +52,7 @@ It can also check response text. ### Tags: - All measurements have the following tags: - - host + - server - port - protocol @@ -60,7 +60,7 @@ It can also check response text. ``` $ ./telegraf -config telegraf.conf -input-filter net_response -test -net_response,host=127.0.0.1,port=22,protocol=tcp response_time=0.18070360500000002,string_found=true 1454785464182527094 -net_response,host=127.0.0.1,port=2222,protocol=tcp response_time=1.090124776,string_found=false 1454784433658942325 +net_response,server=192.168.2.2,port=22,protocol=tcp response_time=0.18070360500000002,string_found=true 1454785464182527094 +net_response,server=192.168.2.2,port=2222,protocol=tcp response_time=1.090124776,string_found=false 1454784433658942325 ``` diff --git a/plugins/inputs/net_response/net_response.go b/plugins/inputs/net_response/net_response.go index 66bf2ae7b..7b5cfa785 100644 --- a/plugins/inputs/net_response/net_response.go +++ b/plugins/inputs/net_response/net_response.go @@ -169,7 +169,7 @@ func (c *NetResponse) Gather(acc telegraf.Accumulator) error { return errors.New("Bad port") } // Prepare data - tags := map[string]string{"host": host, "port": port} + tags := map[string]string{"server": host, "port": port} var fields map[string]interface{} // Gather data if c.Protocol == "tcp" { diff --git a/plugins/inputs/net_response/net_response_test.go b/plugins/inputs/net_response/net_response_test.go index 538d059c0..a6dfbcc94 100644 --- a/plugins/inputs/net_response/net_response_test.go +++ b/plugins/inputs/net_response/net_response_test.go @@ -69,7 +69,7 @@ func TestTCPOK1(t *testing.T) { "string_found": true, "response_time": 1.0, }, - map[string]string{"host": "127.0.0.1", + map[string]string{"server": "127.0.0.1", "port": "2004", "protocol": "tcp", }, @@ -109,7 +109,7 @@ func TestTCPOK2(t *testing.T) { "string_found": false, "response_time": 1.0, }, - map[string]string{"host": "127.0.0.1", + map[string]string{"server": "127.0.0.1", "port": "2004", "protocol": "tcp", }, @@ -164,7 +164,7 @@ func TestUDPOK1(t *testing.T) { "string_found": true, "response_time": 1.0, }, - map[string]string{"host": "127.0.0.1", + map[string]string{"server": "127.0.0.1", "port": "2004", "protocol": "udp", }, diff --git a/plugins/inputs/snmp/snmp.go b/plugins/inputs/snmp/snmp.go index 2af293d57..ba270cb1d 100644 --- a/plugins/inputs/snmp/snmp.go +++ b/plugins/inputs/snmp/snmp.go @@ -23,6 +23,13 @@ type Snmp struct { Table []Table Subtable []Subtable SnmptranslateFile string + + nameToOid map[string]string + initNode Node + subTableMap map[string]Subtable + + // TODO change as unexportable + //OidInstanceMapping map[string]map[string]string } type Host struct { @@ -110,16 +117,7 @@ type Node struct { subnodes map[string]Node } -var initNode = Node{ - id: "1", - name: "", - subnodes: make(map[string]Node), -} - -var SubTableMap = make(map[string]Subtable) - -var NameToOid = make(map[string]string) - +// TODO move this var to snmp struct var OidInstanceMapping = make(map[string]map[string]string) var sampleConfig = ` @@ -286,14 +284,24 @@ func findnodename(node Node, ids []string) (string, string) { } func (s *Snmp) Gather(acc telegraf.Accumulator) error { + // TODO put this in cache on first run // Create subtables mapping - if len(SubTableMap) == 0 { + if len(s.subTableMap) == 0 { + s.subTableMap = make(map[string]Subtable) for _, sb := range s.Subtable { - SubTableMap[sb.Name] = sb + s.subTableMap[sb.Name] = sb } } + // TODO put this in cache on first run // Create oid tree - if s.SnmptranslateFile != "" && len(initNode.subnodes) == 0 { + if s.SnmptranslateFile != "" && len(s.initNode.subnodes) == 0 { + s.nameToOid = make(map[string]string) + s.initNode = Node{ + id: "1", + name: "", + subnodes: make(map[string]Node), + } + data, err := ioutil.ReadFile(s.SnmptranslateFile) if err != nil { log.Printf("Reading SNMPtranslate file error: %s", err) @@ -305,8 +313,8 @@ func (s *Snmp) Gather(acc telegraf.Accumulator) error { if oids[2] != "" { oid_name := oids[1] oid := oids[2] - fillnode(initNode, oid_name, strings.Split(string(oid), ".")) - NameToOid[oid_name] = oid + fillnode(s.initNode, oid_name, strings.Split(string(oid), ".")) + s.nameToOid[oid_name] = oid } } } @@ -330,7 +338,7 @@ func (s *Snmp) Gather(acc telegraf.Accumulator) error { // Get Easy GET oids for _, oidstring := range host.GetOids { oid := Data{} - if val, ok := NameToOid[oidstring]; ok { + if val, ok := s.nameToOid[oidstring]; ok { // TODO should we add the 0 instance ? oid.Name = oidstring oid.Oid = val @@ -351,7 +359,7 @@ func (s *Snmp) Gather(acc telegraf.Accumulator) error { // Get GET oids for _, oid := range s.Get { if oid.Name == oid_name { - if val, ok := NameToOid[oid.Oid]; ok { + if val, ok := s.nameToOid[oid.Oid]; ok { // TODO should we add the 0 instance ? if oid.Instance != "" { oid.rawOid = "." + val + "." + oid.Instance @@ -367,7 +375,7 @@ func (s *Snmp) Gather(acc telegraf.Accumulator) error { // Get GETBULK oids for _, oid := range s.Bulk { if oid.Name == oid_name { - if val, ok := NameToOid[oid.Oid]; ok { + if val, ok := s.nameToOid[oid.Oid]; ok { oid.rawOid = "." + val } else { oid.rawOid = oid.Oid @@ -389,26 +397,27 @@ func (s *Snmp) Gather(acc telegraf.Accumulator) error { } } // Launch Mapping + // TODO put this in cache on first run // TODO save mapping and computed oids // to do it only the first time - // only if len(OidInstanceMapping) == 0 + // only if len(s.OidInstanceMapping) == 0 if len(OidInstanceMapping) >= 0 { - if err := host.SNMPMap(acc); err != nil { + if err := host.SNMPMap(acc, s.nameToOid, s.subTableMap); err != nil { return err } } // Launch Get requests - if err := host.SNMPGet(acc); err != nil { + if err := host.SNMPGet(acc, s.initNode); err != nil { return err } - if err := host.SNMPBulk(acc); err != nil { + if err := host.SNMPBulk(acc, s.initNode); err != nil { return err } } return nil } -func (h *Host) SNMPMap(acc telegraf.Accumulator) error { +func (h *Host) SNMPMap(acc telegraf.Accumulator, nameToOid map[string]string, subTableMap map[string]Subtable) error { // Get snmp client snmpClient, err := h.GetSNMPClient() if err != nil { @@ -426,7 +435,7 @@ func (h *Host) SNMPMap(acc telegraf.Accumulator) error { // This is just a bulk request oid := Data{} oid.Oid = table.oid - if val, ok := NameToOid[oid.Oid]; ok { + if val, ok := nameToOid[oid.Oid]; ok { oid.rawOid = "." + val } else { oid.rawOid = oid.Oid @@ -441,7 +450,7 @@ func (h *Host) SNMPMap(acc telegraf.Accumulator) error { // ... we create a new Data (oid) object oid := Data{} // Looking for more information about this subtable - ssb, exists := SubTableMap[sb] + ssb, exists := subTableMap[sb] if exists { // We found a subtable section in config files oid.Oid = ssb.Oid @@ -528,7 +537,7 @@ func (h *Host) SNMPMap(acc telegraf.Accumulator) error { // Add table oid in bulk oid list oid := Data{} oid.Oid = table.oid - if val, ok := NameToOid[oid.Oid]; ok { + if val, ok := nameToOid[oid.Oid]; ok { oid.rawOid = "." + val } else { oid.rawOid = oid.Oid @@ -545,7 +554,7 @@ func (h *Host) SNMPMap(acc telegraf.Accumulator) error { // ... we create a new Data (oid) object oid := Data{} // Looking for more information about this subtable - ssb, exists := SubTableMap[sb] + ssb, exists := subTableMap[sb] if exists { // We found a subtable section in config files oid.Oid = ssb.Oid + key @@ -587,7 +596,7 @@ func (h *Host) SNMPMap(acc telegraf.Accumulator) error { return nil } -func (h *Host) SNMPGet(acc telegraf.Accumulator) error { +func (h *Host) SNMPGet(acc telegraf.Accumulator, initNode Node) error { // Get snmp client snmpClient, err := h.GetSNMPClient() if err != nil { @@ -620,7 +629,7 @@ func (h *Host) SNMPGet(acc telegraf.Accumulator) error { return err3 } // Handle response - _, err = h.HandleResponse(oidsList, result, acc) + _, err = h.HandleResponse(oidsList, result, acc, initNode) if err != nil { return err } @@ -628,7 +637,7 @@ func (h *Host) SNMPGet(acc telegraf.Accumulator) error { return nil } -func (h *Host) SNMPBulk(acc telegraf.Accumulator) error { +func (h *Host) SNMPBulk(acc telegraf.Accumulator, initNode Node) error { // Get snmp client snmpClient, err := h.GetSNMPClient() if err != nil { @@ -663,7 +672,7 @@ func (h *Host) SNMPBulk(acc telegraf.Accumulator) error { return err3 } // Handle response - last_oid, err := h.HandleResponse(oidsList, result, acc) + last_oid, err := h.HandleResponse(oidsList, result, acc, initNode) if err != nil { return err } @@ -715,7 +724,7 @@ func (h *Host) GetSNMPClient() (*gosnmp.GoSNMP, error) { return snmpClient, nil } -func (h *Host) HandleResponse(oids map[string]Data, result *gosnmp.SnmpPacket, acc telegraf.Accumulator) (string, error) { +func (h *Host) HandleResponse(oids map[string]Data, result *gosnmp.SnmpPacket, acc telegraf.Accumulator, initNode Node) (string, error) { var lastOid string for _, variable := range result.Variables { lastOid = variable.Name diff --git a/plugins/inputs/system/KERNEL_README.md b/plugins/inputs/system/KERNEL_README.md new file mode 100644 index 000000000..3285e59ef --- /dev/null +++ b/plugins/inputs/system/KERNEL_README.md @@ -0,0 +1,64 @@ +# Kernel Input Plugin + +This plugin is only available on Linux. + +The kernel plugin gathers info about the kernel that doesn't fit into other +plugins. In general, it is the statistics available in `/proc/stat` that are +not covered by other plugins. + +The metrics are documented in `man proc` under the `/proc/stat` section. + +``` +/proc/stat +kernel/system statistics. Varies with architecture. Common entries include: + +page 5741 1808 +The number of pages the system paged in and the number that were paged out (from disk). + +swap 1 0 +The number of swap pages that have been brought in and out. + +intr 1462898 +This line shows counts of interrupts serviced since boot time, for each of +the possible system interrupts. The first column is the total of all +interrupts serviced; each subsequent column is the total for a particular interrupt. + +ctxt 115315 +The number of context switches that the system underwent. + +btime 769041601 +boot time, in seconds since the Epoch, 1970-01-01 00:00:00 +0000 (UTC). + +processes 86031 +Number of forks since boot. +``` + +### Configuration: + +```toml +# Get kernel statistics from /proc/stat +[[inputs.kernel]] + # no configuration +``` + +### Measurements & Fields: + +- kernel + - boot_time (integer, seconds since epoch, `btime`) + - context_switches (integer, `ctxt`) + - disk_pages_in (integer, `page (0)`) + - disk_pages_out (integer, `page (1)`) + - interrupts (integer, `intr`) + - processes_forked (integer, `processes`) + +### Tags: + +None + +### Example Output: + +``` +$ telegraf -config ~/ws/telegraf.conf -input-filter kernel -test +* Plugin: kernel, Collection 1 +> kernel boot_time=1457505775i,context_switches=2626618i,disk_pages_in=5741i,disk_pages_out=1808i,interrupts=1472736i,processes_forked=10673i 1457613402960879816 +``` diff --git a/plugins/inputs/system/kernel.go b/plugins/inputs/system/kernel.go new file mode 100644 index 000000000..900400146 --- /dev/null +++ b/plugins/inputs/system/kernel.go @@ -0,0 +1,110 @@ +// +build linux + +package system + +import ( + "bytes" + "fmt" + "io/ioutil" + "os" + "strconv" + + "github.com/influxdata/telegraf" + "github.com/influxdata/telegraf/plugins/inputs" +) + +// /proc/stat file line prefixes to gather stats on: +var ( + interrupts = []byte("intr") + context_switches = []byte("ctxt") + processes_forked = []byte("processes") + disk_pages = []byte("page") + boot_time = []byte("btime") +) + +type Kernel struct { + statFile string +} + +func (k *Kernel) Description() string { + return "Get kernel statistics from /proc/stat" +} + +func (k *Kernel) SampleConfig() string { return "" } + +func (k *Kernel) Gather(acc telegraf.Accumulator) error { + data, err := k.getProcStat() + if err != nil { + return err + } + + fields := make(map[string]interface{}) + + dataFields := bytes.Fields(data) + for i, field := range dataFields { + switch { + case bytes.Equal(field, interrupts): + m, err := strconv.Atoi(string(dataFields[i+1])) + if err != nil { + return err + } + fields["interrupts"] = int64(m) + case bytes.Equal(field, context_switches): + m, err := strconv.Atoi(string(dataFields[i+1])) + if err != nil { + return err + } + fields["context_switches"] = int64(m) + case bytes.Equal(field, processes_forked): + m, err := strconv.Atoi(string(dataFields[i+1])) + if err != nil { + return err + } + fields["processes_forked"] = int64(m) + case bytes.Equal(field, boot_time): + m, err := strconv.Atoi(string(dataFields[i+1])) + if err != nil { + return err + } + fields["boot_time"] = int64(m) + case bytes.Equal(field, disk_pages): + in, err := strconv.Atoi(string(dataFields[i+1])) + if err != nil { + return err + } + out, err := strconv.Atoi(string(dataFields[i+2])) + if err != nil { + return err + } + fields["disk_pages_in"] = int64(in) + fields["disk_pages_out"] = int64(out) + } + } + + acc.AddFields("kernel", fields, map[string]string{}) + + return nil +} + +func (k *Kernel) getProcStat() ([]byte, error) { + if _, err := os.Stat(k.statFile); os.IsNotExist(err) { + return nil, fmt.Errorf("kernel: %s does not exist!", k.statFile) + } else if err != nil { + return nil, err + } + + data, err := ioutil.ReadFile(k.statFile) + if err != nil { + return nil, err + } + + return data, nil +} + +func init() { + inputs.Add("kernel", func() telegraf.Input { + return &Kernel{ + statFile: "/proc/stat", + } + }) +} diff --git a/plugins/inputs/system/kernel_test.go b/plugins/inputs/system/kernel_test.go new file mode 100644 index 000000000..398cba4cc --- /dev/null +++ b/plugins/inputs/system/kernel_test.go @@ -0,0 +1,164 @@ +// +build linux + +package system + +import ( + "io/ioutil" + "os" + "testing" + + "github.com/influxdata/telegraf/testutil" + + "github.com/stretchr/testify/assert" +) + +func TestFullProcFile(t *testing.T) { + tmpfile := makeFakeStatFile([]byte(statFile_Full)) + defer os.Remove(tmpfile) + + k := Kernel{ + statFile: tmpfile, + } + + acc := testutil.Accumulator{} + err := k.Gather(&acc) + assert.NoError(t, err) + + fields := map[string]interface{}{ + "boot_time": int64(1457505775), + "context_switches": int64(2626618), + "disk_pages_in": int64(5741), + "disk_pages_out": int64(1808), + "interrupts": int64(1472736), + "processes_forked": int64(10673), + } + acc.AssertContainsFields(t, "kernel", fields) +} + +func TestPartialProcFile(t *testing.T) { + tmpfile := makeFakeStatFile([]byte(statFile_Partial)) + defer os.Remove(tmpfile) + + k := Kernel{ + statFile: tmpfile, + } + + acc := testutil.Accumulator{} + err := k.Gather(&acc) + assert.NoError(t, err) + + fields := map[string]interface{}{ + "boot_time": int64(1457505775), + "context_switches": int64(2626618), + "disk_pages_in": int64(5741), + "disk_pages_out": int64(1808), + "interrupts": int64(1472736), + } + acc.AssertContainsFields(t, "kernel", fields) +} + +func TestInvalidProcFile1(t *testing.T) { + tmpfile := makeFakeStatFile([]byte(statFile_Invalid)) + defer os.Remove(tmpfile) + + k := Kernel{ + statFile: tmpfile, + } + + acc := testutil.Accumulator{} + err := k.Gather(&acc) + assert.Error(t, err) +} + +func TestInvalidProcFile2(t *testing.T) { + tmpfile := makeFakeStatFile([]byte(statFile_Invalid2)) + defer os.Remove(tmpfile) + + k := Kernel{ + statFile: tmpfile, + } + + acc := testutil.Accumulator{} + err := k.Gather(&acc) + assert.Error(t, err) +} + +func TestNoProcFile(t *testing.T) { + tmpfile := makeFakeStatFile([]byte(statFile_Invalid2)) + os.Remove(tmpfile) + + k := Kernel{ + statFile: tmpfile, + } + + acc := testutil.Accumulator{} + err := k.Gather(&acc) + assert.Error(t, err) + assert.Contains(t, err.Error(), "does not exist") +} + +const statFile_Full = `cpu 6796 252 5655 10444977 175 0 101 0 0 0 +cpu0 6796 252 5655 10444977 175 0 101 0 0 0 +intr 1472736 57 10 0 0 0 0 0 0 0 0 0 0 156 0 0 0 0 0 0 111551 42541 12356 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +ctxt 2626618 +btime 1457505775 +processes 10673 +procs_running 2 +procs_blocked 0 +softirq 1031662 0 649485 20946 111071 11620 0 1 0 994 237545 +page 5741 1808 +swap 1 0 +` + +const statFile_Partial = `cpu 6796 252 5655 10444977 175 0 101 0 0 0 +cpu0 6796 252 5655 10444977 175 0 101 0 0 0 +intr 1472736 57 10 0 0 0 0 0 0 0 0 0 0 156 0 0 0 0 0 0 111551 42541 12356 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +ctxt 2626618 +btime 1457505775 +procs_running 2 +procs_blocked 0 +softirq 1031662 0 649485 20946 111071 11620 0 1 0 994 237545 +page 5741 1808 +` + +// missing btime measurement +const statFile_Invalid = `cpu 6796 252 5655 10444977 175 0 101 0 0 0 +cpu0 6796 252 5655 10444977 175 0 101 0 0 0 +intr 1472736 57 10 0 0 0 0 0 0 0 0 0 0 156 0 0 0 0 0 0 111551 42541 12356 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +ctxt 2626618 +btime +processes 10673 +procs_running 2 +procs_blocked 0 +softirq 1031662 0 649485 20946 111071 11620 0 1 0 994 237545 +page 5741 1808 +swap 1 0 +` + +// missing second page measurement +const statFile_Invalid2 = `cpu 6796 252 5655 10444977 175 0 101 0 0 0 +cpu0 6796 252 5655 10444977 175 0 101 0 0 0 +intr 1472736 57 10 0 0 0 0 0 0 0 0 0 0 156 0 0 0 0 0 0 111551 42541 12356 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +ctxt 2626618 +processes 10673 +procs_running 2 +page 5741 +procs_blocked 0 +softirq 1031662 0 649485 20946 111071 11620 0 1 0 994 237545 +` + +func makeFakeStatFile(content []byte) string { + tmpfile, err := ioutil.TempFile("", "kerneltest") + if err != nil { + panic(err) + } + + if _, err := tmpfile.Write(content); err != nil { + panic(err) + } + if err := tmpfile.Close(); err != nil { + panic(err) + } + + return tmpfile.Name() +}