Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

parseMemInfo() ignores unit, leading to incorrect results from /proc/meminfo #565

Closed
tjhop opened this issue Sep 13, 2023 · 4 comments
Closed

Comments

@tjhop
Copy link
Contributor

tjhop commented Sep 13, 2023

Hi all 👋

Is there a reason that the parseMemInfo() function ignores the optional unit field? This can lead to incorrect results on systems that do supply the field, as the units are obviously not adjusted accordingly.

A small example program/output:
meminfo.go

package main

import (
	"bufio"
	"fmt"
	"os"
	"strings"

	"github.com/dustin/go-humanize"
	"github.com/prometheus/procfs"
)

func main() {
	fmt.Println("Meminfo sample entries:")

	meminfoFile, err := os.Open("/proc/meminfo")
	if err != nil {
		panic(err)
	}
	defer meminfoFile.Close()

	scanner := bufio.NewScanner(meminfoFile)
	for scanner.Scan() {
		line := scanner.Text()
		fields := strings.Fields(line)
		key := strings.TrimSuffix(fields[0], ":")
		if key == "MemTotal" || key == "MemFree" {
			fmt.Println(line)

			val, err := humanize.ParseBytes(fmt.Sprintf("%s %s", fields[1], fields[2]))
			if err != nil {
				panic(err)
			}
			fmt.Printf("%s humanized: %s\n", key, humanize.Bytes(val))
		}
	}

	if scanner.Err() != nil {
		if err != nil {
			panic(err)
		}
	}

	fmt.Println()
	fmt.Println("procfs lib values:")
	fs, err := procfs.NewFS("/proc")
	if err != nil {
		panic(err)
	}

	meminfo, err := fs.Meminfo()
	if err != nil {
		panic(err)
	}

	fmt.Printf("MemTotal: %d bytes\n", *meminfo.MemTotal)
	fmt.Printf("MemTotal humanized: %s\n", humanize.Bytes(*meminfo.MemTotal))
	fmt.Printf("MemFree: %d bytes\n", *meminfo.MemFree)
	fmt.Printf("MemFree humanized: %s\n", humanize.Bytes(*meminfo.MemFree))
}

output:

/tmp/procfs -> go run meminfo.go
Meminfo sample entries:
MemTotal:       32588196 kB
MemTotal humanized: 33 GB
MemFree:         9787012 kB
MemFree humanized: 9.8 GB

procfs lib values:
MemTotal: 32588196 bytes
MemTotal humanized: 33 MB
MemFree: 9787012 bytes
MemFree humanized: 9.8 MB

This also looks like a good reason why the node exporter chooses to do (similar) but custom parsing of /proc/meminfo to account for the optional unit field, rather than use this library for meminfo as it's used in other collectors.

Using the humanize lib that's used elsewhere in other prometheus projects, I have what I believe to be a functional commit here that I'd be happy to contribute if this is determined to be a bug/should be fixed.

Example output with the patched lib:

/tmp/procfs -> go run meminfo.go
Meminfo sample entries:
MemTotal:       32588196 kB
MemTotal humanized: 33 GB
MemFree:         9685964 kB
MemFree humanized: 9.7 GB

procfs lib values:
MemTotal: 32588196000 bytes
MemTotal humanized: 33 GB
MemFree: 9685964000 bytes
MemFree humanized: 9.7 GB
@discordianfish
Copy link
Member

Guess we were under the assumption that it's always the same unit. If not, that's an issue..

tjhop added a commit to tjhop/procfs that referenced this issue Sep 15, 2023
Addresses: prometheus#565

Previously, this function was ignoring the optional unit field, leading
to incorrect results on systems that do report the field. This uses the
humanize lib used elsewhere within the Prometheus ecosystem to normalize
the value to bytes, including when a unit is provided.

Signed-off-by: TJ Hoplock <[email protected]>
tjhop added a commit to tjhop/procfs that referenced this issue Sep 15, 2023
Addresses: prometheus#565

Previously, this function was ignoring the optional unit field, leading
to incorrect results on systems that do report the field. This uses the
humanize lib used elsewhere within the Prometheus ecosystem to normalize
the value to bytes, including when a unit is provided.

Signed-off-by: TJ Hoplock <[email protected]>
@tjhop
Copy link
Contributor Author

tjhop commented Sep 15, 2023

No worries, I've opened a PR to fix it 👍

tjhop added a commit to tjhop/procfs that referenced this issue Sep 23, 2023
Addresses: prometheus#565

Previously, this function was ignoring the optional unit field, leading
to incorrect results on systems that do report the field. This uses the
humanize lib used elsewhere within the Prometheus ecosystem to normalize
the value to bytes, including when a unit is provided.

To try and maintain existing behavior, the fixed/unit-scaled values are
stored as new struct fields.

Signed-off-by: TJ Hoplock <[email protected]>
@SuperQ
Copy link
Member

SuperQ commented Sep 25, 2023

The humanize library produces incorrect results, as the kernel is exposing kB to mean 1024. But the humanize library kB means 1000.

tjhop added a commit to tjhop/procfs that referenced this issue Sep 25, 2023
Addresses: prometheus#565

Previously, this function was ignoring the optional unit field, leading
to incorrect results on systems that do report the field. This uses the
humanize lib used elsewhere within the Prometheus ecosystem to normalize
the value to bytes, including when a unit is provided.

To try and maintain existing behavior, the fixed/unit-scaled values are
stored as new struct fields.

Signed-off-by: TJ Hoplock <[email protected]>
tjhop added a commit to tjhop/procfs that referenced this issue Sep 25, 2023
Addresses: prometheus#565

Previously, this function was ignoring the optional unit field, leading
to incorrect results on systems that do report the field. This uses the
humanize lib used elsewhere within the Prometheus ecosystem to normalize
the value to bytes, including when a unit is provided.

To try and maintain existing behavior, the fixed/unit-scaled values are
stored as new struct fields.

Signed-off-by: TJ Hoplock <[email protected]>
discordianfish pushed a commit that referenced this issue Oct 11, 2023
Addresses: #565

Previously, this function was ignoring the optional unit field, leading
to incorrect results on systems that do report the field. This uses the
humanize lib used elsewhere within the Prometheus ecosystem to normalize
the value to bytes, including when a unit is provided.

To try and maintain existing behavior, the fixed/unit-scaled values are
stored as new struct fields.

Signed-off-by: TJ Hoplock <[email protected]>
@tjhop
Copy link
Contributor Author

tjhop commented Jan 12, 2024

Closing since #569 was merged

@tjhop tjhop closed this as completed Jan 12, 2024
jritter pushed a commit to jritter/procfs that referenced this issue Jul 15, 2024
Addresses: prometheus#565

Previously, this function was ignoring the optional unit field, leading
to incorrect results on systems that do report the field. This uses the
humanize lib used elsewhere within the Prometheus ecosystem to normalize
the value to bytes, including when a unit is provided.

To try and maintain existing behavior, the fixed/unit-scaled values are
stored as new struct fields.

Signed-off-by: TJ Hoplock <[email protected]>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

3 participants