From 652c36b0b16a592c001be1cc2c548e95997e1245 Mon Sep 17 00:00:00 2001 From: fearful-symmetry Date: Wed, 15 Nov 2023 13:42:29 -0800 Subject: [PATCH 1/4] init commit --- metric/system/process/process_linux_common.go | 44 +++++++++++++++++++ metric/system/process/process_linux_test.go | 18 ++++++++ metric/system/process/process_types.go | 11 +++++ metric/system/process/testdata/proc/42/io | 7 +++ 4 files changed, 80 insertions(+) create mode 100644 metric/system/process/testdata/proc/42/io diff --git a/metric/system/process/process_linux_common.go b/metric/system/process/process_linux_common.go index bc47adb490..7d63f8be94 100644 --- a/metric/system/process/process_linux_common.go +++ b/metric/system/process/process_linux_common.go @@ -124,6 +124,12 @@ func FillPidMetrics(hostfs resolve.Resolver, pid int, state ProcState, filter fu if err != nil { return state, fmt.Errorf("error creating username for pid %d: %w", pid, err) } + + state.IO, err = getIOData(hostfs, pid) + if err != nil { + return state, fmt.Errorf("error fetching IO metrics for pid %d: %w", pid, err) + } + return state, nil } @@ -302,6 +308,44 @@ func getMemData(hostfs resolve.Resolver, pid int) (ProcMemInfo, error) { return state, nil } +func getIOData(hostfs resolve.Resolver, pid int) (ProcIOInfo, error) { + state := ProcIOInfo{} + path := hostfs.Join("proc", strconv.Itoa(pid), "io") + data, err := os.ReadFile(path) + if err != nil { + return state, fmt.Errorf("error fetching IO metrics: %w", err) + } + + for _, metric := range strings.Split(string(data), "\n") { + raw := strings.Split(metric, ": ") + if len(raw) < 2 { + continue + } + value, err := strconv.ParseUint(raw[1], 10, 64) + if err != nil { + return state, fmt.Errorf("error converting counters '%s' in io stat file: %w", raw, err) + } + + switch raw[0] { + case "rchar": + state.ReadChar = opt.UintWith(value) + case "wchar": + state.WriteChar = opt.UintWith(value) + case "syscr": + state.ReadSyscalls = opt.UintWith(value) + case "syscw": + state.WriteSyscalls = opt.UintWith(value) + case "read_bytes": + state.ReadBytes = opt.UintWith(value) + case "write_bytes": + state.WriteBytes = opt.UintWith(value) + case "cancelled_write_bytes": + state.CancelledWriteBites = opt.UintWith(value) + } + } + return state, nil +} + func getCPUTime(hostfs resolve.Resolver, pid int) (ProcCPUInfo, error) { state := ProcCPUInfo{} diff --git a/metric/system/process/process_linux_test.go b/metric/system/process/process_linux_test.go index 401b0533cf..61bf6a9701 100644 --- a/metric/system/process/process_linux_test.go +++ b/metric/system/process/process_linux_test.go @@ -133,3 +133,21 @@ func TestParseProcStat(t *testing.T) { assert.Equal(t, want, got, "") } + +func TestParseIO(t *testing.T) { + path := resolve.NewTestResolver("testdata/") + data, err := getIOData(path, 42) + require.NoError(t, err) + + good := ProcIOInfo{ + ReadChar: opt.UintWith(10418), + WriteChar: opt.UintWith(8), + ReadSyscalls: opt.UintWith(14), + WriteSyscalls: opt.UintWith(1), + ReadBytes: opt.UintWith(5243), + WriteBytes: opt.UintWith(128), + CancelledWriteBites: opt.UintWith(4), + } + + require.Equal(t, good, data) +} diff --git a/metric/system/process/process_types.go b/metric/system/process/process_types.go index 27740b23cf..2738bb901c 100644 --- a/metric/system/process/process_types.go +++ b/metric/system/process/process_types.go @@ -49,6 +49,7 @@ type ProcState struct { CPU ProcCPUInfo `struct:"cpu,omitempty"` FD ProcFDInfo `struct:"fd,omitempty"` Network *sysinfotypes.NetworkCountersInfo `struct:"-,omitempty"` + IO ProcIOInfo `struct:"io,omitempty"` // cgroups Cgroup cgroup.CGStats `struct:"cgroup,omitempty"` @@ -79,6 +80,16 @@ type CPUTotal struct { Norm opt.PctOpt `struct:"norm,omitempty"` } +type ProcIOInfo struct { + ReadChar opt.Uint `struct:"rchar,omitempty"` + WriteChar opt.Uint `struct:"wchar,omitempty"` + ReadSyscalls opt.Uint `struct:"syscr,omitempty"` + WriteSyscalls opt.Uint `struct:"syscw,omitempty"` + ReadBytes opt.Uint `struct:"read_bytes,omitempty"` + WriteBytes opt.Uint `struct:"write_bytes,omitempty"` + CancelledWriteBites opt.Uint `struct:"cancelled_write_bytes,omitempty"` +} + // ProcMemInfo is the struct for cpu.memory metrics type ProcMemInfo struct { Size opt.Uint `struct:"size,omitempty"` diff --git a/metric/system/process/testdata/proc/42/io b/metric/system/process/testdata/proc/42/io new file mode 100644 index 0000000000..862e22c6e5 --- /dev/null +++ b/metric/system/process/testdata/proc/42/io @@ -0,0 +1,7 @@ +rchar: 10418 +wchar: 8 +syscr: 14 +syscw: 1 +read_bytes: 5243 +write_bytes: 128 +cancelled_write_bytes: 4 From 06dc9d251172d2157fffc4b011426b74ce1a4962 Mon Sep 17 00:00:00 2001 From: fearful-symmetry Date: Thu, 16 Nov 2023 10:11:34 -0800 Subject: [PATCH 2/4] init commit --- metric/system/process/process_types.go | 20 ++++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/metric/system/process/process_types.go b/metric/system/process/process_types.go index 2738bb901c..88ea322b53 100644 --- a/metric/system/process/process_types.go +++ b/metric/system/process/process_types.go @@ -80,13 +80,21 @@ type CPUTotal struct { Norm opt.PctOpt `struct:"norm,omitempty"` } +// ProcIOInfo is the struct for I/O counters from /proc/[pid]/io type ProcIOInfo struct { - ReadChar opt.Uint `struct:"rchar,omitempty"` - WriteChar opt.Uint `struct:"wchar,omitempty"` - ReadSyscalls opt.Uint `struct:"syscr,omitempty"` - WriteSyscalls opt.Uint `struct:"syscw,omitempty"` - ReadBytes opt.Uint `struct:"read_bytes,omitempty"` - WriteBytes opt.Uint `struct:"write_bytes,omitempty"` + // ReadChar is bytes read from the system, as passed from read() and similar syscalls + ReadChar opt.Uint `struct:"read_char,omitempty"` + // WriteChar is bytes written to the system, as passed to various syscalls + WriteChar opt.Uint `struct:"write_char,omitempty"` + //ReadSyscalls counts the number of read operations + ReadSyscalls opt.Uint `struct:"read_ops,omitempty"` + //WriteSyscalls counts the number of write operations + WriteSyscalls opt.Uint `struct:"write_ops,omitempty"` + // ReadBytes is the count of bytes that were actually fetched from the storage layer + ReadBytes opt.Uint `struct:"read_bytes,omitempty"` + // WriteBytes is the count of bytes that were actually written to the storage layer + WriteBytes opt.Uint `struct:"write_bytes,omitempty"` + // the number of bytes which this process caused to not happen, by truncating pagecache CancelledWriteBites opt.Uint `struct:"cancelled_write_bytes,omitempty"` } From f48edf6b4190c01673b4ff56b5f5238c1d42d08f Mon Sep 17 00:00:00 2001 From: fearful-symmetry Date: Thu, 16 Nov 2023 10:23:23 -0800 Subject: [PATCH 3/4] add changelog --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4c95a06a90..6d95db0438 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,8 @@ This project adheres to [Semantic Versioning](http://semver.org/). ### Added +- Add metrics from `/proc/[pid]/io` + ### Changed ### Deprecated From 592398e6daae17ab5c2756029988c59382129aa9 Mon Sep 17 00:00:00 2001 From: fearful-symmetry Date: Wed, 22 Nov 2023 09:35:47 -0800 Subject: [PATCH 4/4] fix type in struct field --- metric/system/process/process_linux_common.go | 2 +- metric/system/process/process_linux_test.go | 2 +- metric/system/process/process_types.go | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/metric/system/process/process_linux_common.go b/metric/system/process/process_linux_common.go index 85047d9b8f..e5a4a498ee 100644 --- a/metric/system/process/process_linux_common.go +++ b/metric/system/process/process_linux_common.go @@ -339,7 +339,7 @@ func getIOData(hostfs resolve.Resolver, pid int) (ProcIOInfo, error) { case "write_bytes": state.WriteBytes = opt.UintWith(value) case "cancelled_write_bytes": - state.CancelledWriteBites = opt.UintWith(value) + state.CancelledWriteBytes = opt.UintWith(value) } } return state, nil diff --git a/metric/system/process/process_linux_test.go b/metric/system/process/process_linux_test.go index 61bf6a9701..dbd36d96af 100644 --- a/metric/system/process/process_linux_test.go +++ b/metric/system/process/process_linux_test.go @@ -146,7 +146,7 @@ func TestParseIO(t *testing.T) { WriteSyscalls: opt.UintWith(1), ReadBytes: opt.UintWith(5243), WriteBytes: opt.UintWith(128), - CancelledWriteBites: opt.UintWith(4), + CancelledWriteBytes: opt.UintWith(4), } require.Equal(t, good, data) diff --git a/metric/system/process/process_types.go b/metric/system/process/process_types.go index 88ea322b53..5603ad6a36 100644 --- a/metric/system/process/process_types.go +++ b/metric/system/process/process_types.go @@ -95,7 +95,7 @@ type ProcIOInfo struct { // WriteBytes is the count of bytes that were actually written to the storage layer WriteBytes opt.Uint `struct:"write_bytes,omitempty"` // the number of bytes which this process caused to not happen, by truncating pagecache - CancelledWriteBites opt.Uint `struct:"cancelled_write_bytes,omitempty"` + CancelledWriteBytes opt.Uint `struct:"cancelled_write_bytes,omitempty"` } // ProcMemInfo is the struct for cpu.memory metrics