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

context.memTotalPhysicalBytes(): Use a Syscall #171

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
92 changes: 14 additions & 78 deletions memory_linux.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,31 +7,12 @@ package ghw

import (
"bufio"
"compress/gzip"
"fmt"
"io"
"io/ioutil"
"os"
"path/filepath"
"regexp"
"strconv"
"strings"
)

const (
_WARN_CANNOT_DETERMINE_PHYSICAL_MEMORY = `
Could not determine total physical bytes of memory. This may
be due to the host being a virtual machine or container with no
/var/log/syslog file, or the current user may not have necessary
privileges to read the syslog. We are falling back to setting the
total physical amount of memory to the total usable amount of memory
`
)

var (
// System log lines will look similar to the following:
// ... kernel: [0.000000] Memory: 24633272K/25155024K ...
_REGEX_SYSLOG_MEMLINE = regexp.MustCompile(`Memory:\s+\d+K\/(\d+)K`)
"syscall"
)

func (ctx *context) memFillInfo(info *MemoryInfo) error {
Expand All @@ -40,70 +21,25 @@ func (ctx *context) memFillInfo(info *MemoryInfo) error {
return fmt.Errorf("Could not determine total usable bytes of memory")
}
info.TotalUsableBytes = tub
tpb := ctx.memTotalPhysicalBytes()
info.TotalPhysicalBytes = tpb
if tpb < 1 {
warn(_WARN_CANNOT_DETERMINE_PHYSICAL_MEMORY)
tpb, err := ctx.memTotalPhysicalBytes()
if err != nil {
info.TotalPhysicalBytes = tub
errMsg := fmt.Sprintf("fallback to total usable bytes after error\n"+
"getting total physical bytes of RAM:\n"+
"%v", err)
warn(errMsg)
} else {
info.TotalPhysicalBytes = int64(tpb)
}

info.SupportedPageSizes = ctx.memSupportedPageSizes()
return nil
}

func (ctx *context) memTotalPhysicalBytes() int64 {
// In Linux, the total physical memory can be determined by looking at the
// output of dmidecode, however dmidecode requires root privileges to run,
// so instead we examine the system logs for startup information containing
// total physical memory and cache the results of this.
findPhysicalKb := func(line string) int64 {
matches := _REGEX_SYSLOG_MEMLINE.FindStringSubmatch(line)
if len(matches) == 2 {
i, err := strconv.Atoi(matches[1])
if err != nil {
return -1
}
return int64(i * 1024)
}
return -1
}

// /var/log will contain a file called syslog and 0 or more files called
// syslog.$NUMBER or syslog.$NUMBER.gz containing system log records. We
// search each, stopping when we match a system log record line that
// contains physical memory information.
logDir := ctx.pathVarLog()
logFiles, err := ioutil.ReadDir(logDir)
if err != nil {
return -1
}
for _, file := range logFiles {
if strings.HasPrefix(file.Name(), "syslog") {
fullPath := filepath.Join(logDir, file.Name())
unzip := strings.HasSuffix(file.Name(), ".gz")
var r io.ReadCloser
r, err = os.Open(fullPath)
if err != nil {
return -1
}
defer safeClose(r)
if unzip {
r, err = gzip.NewReader(r)
if err != nil {
return -1
}
}

scanner := bufio.NewScanner(r)
for scanner.Scan() {
line := scanner.Text()
size := findPhysicalKb(line)
if size > 0 {
return size
}
}
}
}
return -1
func (ctx *context) memTotalPhysicalBytes() (uint64, error) {
var info syscall.Sysinfo_t
err := syscall.Sysinfo(&info)
return info.Totalram, err
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

actually, sysinfo_t.totalram is the usable memory amount, not the physical memory amount :)

http://man7.org/linux/man-pages/man2/sysinfo.2.html

The results of sysinfo(2) are replicated in /proc/meminfo, which is what is used to get the total usable bytes below. See the comment on (original) line 124 below that explains why we look up the physical memory size using syslog.

That said, I think using the DMI approach (that the digitalocean library uses) would allow us to simply add up the physical size of all DIMMs attached to the host. The only problem with that is, of course, DMI access is a privileged operation so we're back in the same situation as before that caused me to originally write this code... (accessing syslog sometimes requires lower privileges than accessing DMI information, thus the fallback to using syslog instead of DMI)

See the note

}

func (ctx *context) memTotalUsableBytes() int64 {
Expand Down
4 changes: 0 additions & 4 deletions path_linux.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,6 @@ import (
"path/filepath"
)

func (ctx *context) pathVarLog() string {
return filepath.Join(ctx.chroot, "var", "log")
}

func (ctx *context) pathProcMeminfo() string {
return filepath.Join(ctx.chroot, "proc", "meminfo")
}
Expand Down