//go:build !windows package processes import ( "errors" "fmt" "runtime" "testing" "time" "github.com/google/go-cmp/cmp" "github.com/stretchr/testify/require" "github.com/influxdata/telegraf" "github.com/influxdata/telegraf/testutil" ) func TestProcesses(t *testing.T) { tester := tester{} processes := &Processes{ Log: testutil.Logger{}, execPS: testExecPS( "STAT\n Ss \n S \n Z \n R \n S< \n SNs \n Ss+ \n \n \n", ), readProcFile: tester.testProcFile, } var acc testutil.Accumulator require.NoError(t, processes.Gather(&acc)) require.True(t, acc.HasInt64Field("processes", "running")) require.True(t, acc.HasInt64Field("processes", "sleeping")) require.True(t, acc.HasInt64Field("processes", "stopped")) require.True(t, acc.HasInt64Field("processes", "total")) total, ok := acc.Get("processes") require.True(t, ok) require.Positive(t, total.Fields["total"]) } func TestFromPS(t *testing.T) { processes := &Processes{ Log: testutil.Logger{}, execPS: testExecPS("\nSTAT\nD\nI\nL\nR\nR+\nS\nS+\nSNs\nSs\nU\nZ\n"), forcePS: true, } var acc testutil.Accumulator require.NoError(t, processes.Gather(&acc)) fields := getEmptyFields() fields["blocked"] = int64(3) fields["zombies"] = int64(1) fields["running"] = int64(2) fields["sleeping"] = int64(4) fields["idle"] = int64(1) fields["total"] = int64(11) acc.AssertContainsTaggedFields(t, "processes", fields, map[string]string{}) } func TestFromPSError(t *testing.T) { processes := &Processes{ Log: testutil.Logger{}, execPS: testExecPSError, forcePS: true, } var acc testutil.Accumulator require.Error(t, processes.Gather(&acc)) } func TestFromProcFiles(t *testing.T) { if runtime.GOOS != "linux" { t.Skip("This test only runs on linux") } tester := tester{} processes := &Processes{ Log: testutil.Logger{}, readProcFile: tester.testProcFile, forceProc: true, } var acc testutil.Accumulator require.NoError(t, processes.Gather(&acc)) fields := getEmptyFields() fields["sleeping"] = tester.calls fields["total_threads"] = tester.calls * 2 fields["total"] = tester.calls acc.AssertContainsTaggedFields(t, "processes", fields, map[string]string{}) } func TestFromProcFilesWithSpaceInCmd(t *testing.T) { if runtime.GOOS != "linux" { t.Skip("This test only runs on linux") } tester := tester{} processes := &Processes{ Log: testutil.Logger{}, readProcFile: tester.testProcFile2, forceProc: true, } var acc testutil.Accumulator require.NoError(t, processes.Gather(&acc)) fields := getEmptyFields() fields["sleeping"] = tester.calls fields["total_threads"] = tester.calls * 2 fields["total"] = tester.calls acc.AssertContainsTaggedFields(t, "processes", fields, map[string]string{}) } // Based on `man 5 proc`, parked processes an be found in a // limited range of Linux versions: // // > P Parked (Linux 3.9 to 3.13 only) // // However, we have had reports of this process state on Ubuntu // Bionic w/ Linux 4.15 (#6270) func TestParkedProcess(t *testing.T) { if runtime.GOOS != "linux" { t.Skip("Parked process test only relevant on linux") } procstat := `88 (watchdog/13) P 2 0 0 0 -1 69238848 0 0 0 0 0 0 0 0 20 0 1 0 20 0 0 18446744073709551615 0 0 0 0 0 0 0 ` + `2147483647 0 1 0 0 17 0 0 0 0 0 0 0 0 0 0 0 0 0 0 ` plugin := &Processes{ Log: testutil.Logger{}, readProcFile: func(string) ([]byte, error) { return []byte(procstat), nil }, forceProc: true, } var acc testutil.Accumulator require.NoError(t, plugin.Gather(&acc)) expected := []telegraf.Metric{ testutil.MustMetric( "processes", map[string]string{}, map[string]interface{}{ "blocked": 0, "dead": 0, "idle": 0, "paging": 0, "parked": 1, "running": 0, "sleeping": 0, "stopped": 0, "unknown": 0, "zombies": 0, }, time.Unix(0, 0), telegraf.Gauge, ), } options := []cmp.Option{ testutil.IgnoreTime(), testutil.IgnoreFields("total", "total_threads"), } testutil.RequireMetricsEqual(t, expected, acc.GetTelegrafMetrics(), options...) } func testExecPS(out string) func(_ bool) ([]byte, error) { return func(_ bool) ([]byte, error) { return []byte(out), nil } } // struct for counting calls to testProcFile type tester struct { calls int64 } func (t *tester) testProcFile(_ string) ([]byte, error) { t.calls++ return []byte(fmt.Sprintf(testProcStat, "S", "2")), nil } func (t *tester) testProcFile2(_ string) ([]byte, error) { t.calls++ return []byte(fmt.Sprintf(testProcStat2, "S", "2")), nil } func testExecPSError(_ bool) ([]byte, error) { return []byte("\nSTAT\nD\nI\nL\nR\nR+\nS\nS+\nSNs\nSs\nU\nZ\n"), errors.New("error") } const testProcStat = `10 (rcuob/0) %s 2 0 0 0 -1 2129984 0 0 0 0 0 0 0 0 20 0 %s 0 11 0 0 18446744073709551615 0 0 0 0 0 0 0 ` + `2147483647 0 18446744073709551615 0 0 17 0 0 0 0 0 0 0 0 0 0 0 0 0 0 ` const testProcStat2 = `10 (rcuob 0) %s 2 0 0 0 -1 2129984 0 0 0 0 0 0 0 0 20 0 %s 0 11 0 0 18446744073709551615 0 0 0 0 0 0 0 ` + `2147483647 0 18446744073709551615 0 0 17 0 0 0 0 0 0 0 0 0 0 0 0 0 0 `