diff --git a/fixtures.ttar b/fixtures.ttar index 8b27c0d9d..38b71fe32 100644 --- a/fixtures.ttar +++ b/fixtures.ttar @@ -1674,6 +1674,52 @@ md101 : active (read-only) raid0 sdb[2] sdd[1] sdc[0] unused devices: Mode: 644 # ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Path: fixtures/proc/meminfo +Lines: 42 +MemTotal: 15666184 kB +MemFree: 440324 kB +Buffers: 1020128 kB +Cached: 12007640 kB +SwapCached: 0 kB +Active: 6761276 kB +Inactive: 6532708 kB +Active(anon): 267256 kB +Inactive(anon): 268 kB +Active(file): 6494020 kB +Inactive(file): 6532440 kB +Unevictable: 0 kB +Mlocked: 0 kB +SwapTotal: 0 kB +SwapFree: 0 kB +Dirty: 768 kB +Writeback: 0 kB +AnonPages: 266216 kB +Mapped: 44204 kB +Shmem: 1308 kB +Slab: 1807264 kB +SReclaimable: 1738124 kB +SUnreclaim: 69140 kB +KernelStack: 1616 kB +PageTables: 5288 kB +NFS_Unstable: 0 kB +Bounce: 0 kB +WritebackTmp: 0 kB +CommitLimit: 7833092 kB +Committed_AS: 530844 kB +VmallocTotal: 34359738367 kB +VmallocUsed: 36596 kB +VmallocChunk: 34359637840 kB +HardwareCorrupted: 0 kB +AnonHugePages: 12288 kB +HugePages_Total: 0 +HugePages_Free: 0 +HugePages_Rsvd: 0 +HugePages_Surp: 0 +Hugepagesize: 2048 kB +DirectMap4k: 91136 kB +DirectMap2M: 16039936 kB +Mode: 664 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Directory: fixtures/proc/net Mode: 755 # ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/fixtures/meminfo b/fixtures/meminfo deleted file mode 100644 index 5dec80e81..000000000 --- a/fixtures/meminfo +++ /dev/null @@ -1,42 +0,0 @@ -MemTotal: 15666184 kB -MemFree: 440324 kB -Buffers: 1020128 kB -Cached: 12007640 kB -SwapCached: 0 kB -Active: 6761276 kB -Inactive: 6532708 kB -Active(anon): 267256 kB -Inactive(anon): 268 kB -Active(file): 6494020 kB -Inactive(file): 6532440 kB -Unevictable: 0 kB -Mlocked: 0 kB -SwapTotal: 0 kB -SwapFree: 0 kB -Dirty: 768 kB -Writeback: 0 kB -AnonPages: 266216 kB -Mapped: 44204 kB -Shmem: 1308 kB -Slab: 1807264 kB -SReclaimable: 1738124 kB -SUnreclaim: 69140 kB -KernelStack: 1616 kB -PageTables: 5288 kB -NFS_Unstable: 0 kB -Bounce: 0 kB -WritebackTmp: 0 kB -CommitLimit: 7833092 kB -Committed_AS: 530844 kB -VmallocTotal: 34359738367 kB -VmallocUsed: 36596 kB -VmallocChunk: 34359637840 kB -HardwareCorrupted: 0 kB -AnonHugePages: 12288 kB -HugePages_Total: 0 -HugePages_Free: 0 -HugePages_Rsvd: 0 -HugePages_Surp: 0 -Hugepagesize: 2048 kB -DirectMap4k: 91136 kB -DirectMap2M: 16039936 kB diff --git a/meminfo.go b/meminfo.go index 3d86de50f..35b7a0d72 100644 --- a/meminfo.go +++ b/meminfo.go @@ -1,20 +1,35 @@ +// Copyright 2019 The Prometheus Authors +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + package procfs import ( "bufio" - "os" - "reflect" - "regexp" + "bytes" + "log" "strconv" + "strings" + + "github.com/prometheus/procfs/internal/util" ) // Meminfo represents memory statistics. type Meminfo struct { // Total usable ram (i.e. physical ram minus a few reserved // bits and the kernel binary code) - MemTotal int64 `meminfo:"MemTotal"` + MemTotal uint64 // The sum of LowFree+HighFree - MemFree int64 `meminfo:"MemFree"` + MemFree uint64 // An estimate of how much memory is available for starting // new applications, without swapping. Calculated from // MemFree, SReclaimable, the size of the file LRU lists, and @@ -23,59 +38,59 @@ type Meminfo struct { // well, and that not all reclaimable slab will be // reclaimable, due to items being in use. The impact of those // factors will vary from system to system. - MemAvailable int64 `meminfo:"MemAvailable"` + MemAvailable uint64 // Relatively temporary storage for raw disk blocks shouldn't // get tremendously large (20MB or so) - Buffers int64 `meminfo:"Buffers"` - Cached int64 `meminfo:"Cached"` + Buffers uint64 + Cached uint64 // Memory that once was swapped out, is swapped back in but // still also is in the swapfile (if memory is needed it // doesn't need to be swapped out AGAIN because it is already // in the swapfile. This saves I/O) - SwapCached int64 `meminfo:"SwapCached"` + SwapCached uint64 // Memory that has been used more recently and usually not // reclaimed unless absolutely necessary. - Active int64 `meminfo:"Active"` + Active uint64 // Memory which has been less recently used. It is more // eligible to be reclaimed for other purposes - Inactive int64 `meminfo:"Inactive"` - ActiveAnon int64 `meminfo:"Active(anon)"` - InactiveAnon int64 `meminfo:"Inactive(anon)"` - ActiveFile int64 `meminfo:"Active(file)"` - InactiveFile int64 `meminfo:"Inactive(file)"` - Unevictable int64 `meminfo:"Unevictable"` - Mlocked int64 `meminfo:"Mlocked"` + Inactive uint64 + ActiveAnon uint64 + InactiveAnon uint64 + ActiveFile uint64 + InactiveFile uint64 + Unevictable uint64 + Mlocked uint64 // total amount of swap space available - SwapTotal int64 `meminfo:"SwapTotal"` + SwapTotal uint64 // Memory which has been evicted from RAM, and is temporarily // on the disk - SwapFree int64 `meminfo:"SwapFree"` + SwapFree uint64 // Memory which is waiting to get written back to the disk - Dirty int64 `meminfo:"Dirty"` + Dirty uint64 // Memory which is actively being written back to the disk - Writeback int64 `meminfo:"Writeback"` + Writeback uint64 // Non-file backed pages mapped into userspace page tables - AnonPages int64 `meminfo:"AnonPages"` - // files which have been mmaped, such as libraries - Mapped int64 `meminfo:"Mapped"` - Shmem int64 `meminfo:"Shmem"` + AnonPages uint64 + // files which have been mapped, such as libraries + Mapped uint64 + Shmem uint64 // in-kernel data structures cache - Slab int64 `meminfo:"Slab"` + Slab uint64 // Part of Slab, that might be reclaimed, such as caches - SReclaimable int64 `meminfo:"SReclaimable"` + SReclaimable uint64 // Part of Slab, that cannot be reclaimed on memory pressure - SUnreclaim int64 `meminfo:"SUnreclaim"` - KernelStack int64 `meminfo:"KernelStack"` + SUnreclaim uint64 + KernelStack uint64 // amount of memory dedicated to the lowest level of page // tables. - PageTables int64 `meminfo:"PageTables"` + PageTables uint64 // NFS pages sent to the server, but not yet committed to // stable storage - NFSUnstable int64 `meminfo:"NFS_Unstable"` + NFSUnstable uint64 // Memory used for block device "bounce buffers" - Bounce int64 `meminfo:"Bounce"` + Bounce uint64 // Memory used by FUSE for temporary writeback buffers - WritebackTmp int64 `meminfo:"WritebackTmp"` + WritebackTmp uint64 // Based on the overcommit ratio ('vm.overcommit_ratio'), // this is the total amount of memory currently available to // be allocated on the system. This limit is only adhered to @@ -89,7 +104,7 @@ type Meminfo struct { // yield a CommitLimit of 7.3G. // For more details, see the memory overcommit documentation // in vm/overcommit-accounting. - CommitLimit int64 `meminfo:"CommitLimit"` + CommitLimit uint64 // The amount of memory presently allocated on the system. // The committed memory is a sum of all of the memory which // has been allocated by processes, even if it has not been @@ -103,72 +118,339 @@ type Meminfo struct { // This is useful if one needs to guarantee that processes will // not fail due to lack of memory once that memory has been // successfully allocated. - CommittedAS int64 `meminfo:"Committed_AS"` + CommittedAS uint64 // total size of vmalloc memory area - VmallocTotal int64 `meminfo:"VmallocTotal"` + VmallocTotal uint64 // amount of vmalloc area which is used - VmallocUsed int64 `meminfo:"VmallocUsed"` + VmallocUsed uint64 // largest contiguous block of vmalloc area which is free - VmallocChunk int64 `meminfo:"VmallocChunk"` - HardwareCorrupted int64 `meminfo:"HardwareCorrupted"` - AnonHugePages int64 `meminfo:"AnonHugePages"` - HugePagesTotal int64 `meminfo:"HugePages_Total"` - HugePagesFree int64 `meminfo:"HugePages_Free"` - HugePagesRsvd int64 `meminfo:"HugePages_Rsvd"` - HugePagesSurp int64 `meminfo:"HugePages_Surp"` - Hugepagesize int64 `meminfo:"Hugepagesize"` - DirectMap4k int64 `meminfo:"DirectMap4k"` - DirectMap2M int64 `meminfo:"DirectMap2M"` + VmallocChunk uint64 + HardwareCorrupted uint64 + AnonHugePages uint64 + ShmemHugePages uint64 + ShmemPmdMapped uint64 + CmaTotal uint64 + CmaFree uint64 + HugePagesTotal uint64 + HugePagesFree uint64 + HugePagesRsvd uint64 + HugePagesSurp uint64 + Hugepagesize uint64 + DirectMap4k uint64 + DirectMap2M uint64 + DirectMap1G uint64 } -// NewMeminfo returns kernel/system statistics read from /proc/stat. -func NewMeminfo() (Meminfo, error) { - fs, err := NewFS(DefaultMountPoint) +// Meminfo returns an information about current kernel/system memory statistics. +// See https://www.kernel.org/doc/Documentation/filesystems/proc.txt +func (fs FS) Meminfo() (Meminfo, error) { + data, err := util.ReadFileNoStat(fs.proc.Path("meminfo")) if err != nil { return Meminfo{}, err } - - return fs.NewMeminfo() + return parseMemInfo(data) } -// NewMeminfo returns an information about current kernel/system statistics. -func (fs FS) NewMeminfo() (m Meminfo, err error) { - f, err := os.Open(fs.Path("meminfo")) - if err != nil { - return Meminfo{}, err - } - defer f.Close() - - st := reflect.TypeOf(m) - re := regexp.MustCompile(m.regex()) - s := bufio.NewScanner(f) +func parseMemInfo(info []byte) (m Meminfo, err error) { + scanner := bufio.NewScanner(bytes.NewReader(info)) - for s.Scan() { - line := s.Text() + var line string + for scanner.Scan() { + line = scanner.Text() + log.Println(line) - submatch := re.FindAllStringSubmatch(line, 1) - if submatch == nil { - continue - } - - key := submatch[0][1] - val := submatch[0][2] - - for i := 0; i < st.NumField(); i++ { - field := st.Field(i) - if field.Tag.Get("meminfo") == key { - v, err := strconv.ParseInt(val, 10, 64) - if err != nil { - // no op - } - reflect.ValueOf(&m).Elem().Field(i).SetInt(v) + field := strings.Fields(line) + log.Println(field[0]) + switch field[0] { + case "MemTotal:": + v, err := strconv.ParseUint(field[1], 0, 64) + if err != nil { + return Meminfo{}, err + } + m.MemTotal = v + case "MemFree:": + v, err := strconv.ParseUint(field[1], 0, 64) + if err != nil { + return Meminfo{}, err + } + m.MemFree = v + case "MemAvailable:": + v, err := strconv.ParseUint(field[1], 0, 64) + if err != nil { + return Meminfo{}, err + } + m.MemAvailable = v + case "Buffers:": + v, err := strconv.ParseUint(field[1], 0, 64) + if err != nil { + return Meminfo{}, err + } + m.Buffers = v + case "Cached:": + v, err := strconv.ParseUint(field[1], 0, 64) + if err != nil { + return Meminfo{}, err + } + m.Cached = v + case "SwapCached:": + v, err := strconv.ParseUint(field[1], 0, 64) + if err != nil { + return Meminfo{}, err + } + m.SwapCached = v + case "Active:": + v, err := strconv.ParseUint(field[1], 0, 64) + if err != nil { + return Meminfo{}, err + } + m.Active = v + case "Inactive:": + v, err := strconv.ParseUint(field[1], 0, 64) + if err != nil { + return Meminfo{}, err + } + m.Inactive = v + case "Active(anon):": + v, err := strconv.ParseUint(field[1], 0, 64) + if err != nil { + return Meminfo{}, err + } + m.ActiveAnon = v + case "Inactive(anon):": + v, err := strconv.ParseUint(field[1], 0, 64) + if err != nil { + return Meminfo{}, err + } + m.InactiveAnon = v + case "Active(file):": + v, err := strconv.ParseUint(field[1], 0, 64) + if err != nil { + return Meminfo{}, err + } + m.ActiveFile = v + case "Inactive(file):": + v, err := strconv.ParseUint(field[1], 0, 64) + if err != nil { + return Meminfo{}, err + } + m.InactiveFile = v + case "Unevictable:": + v, err := strconv.ParseUint(field[1], 0, 64) + if err != nil { + return Meminfo{}, err + } + m.Unevictable = v + case "Mlocked:": + v, err := strconv.ParseUint(field[1], 0, 64) + if err != nil { + return Meminfo{}, err + } + m.Mlocked = v + case "SwapTotal:": + v, err := strconv.ParseUint(field[1], 0, 64) + if err != nil { + return Meminfo{}, err + } + m.SwapTotal = v + case "SwapFree:": + v, err := strconv.ParseUint(field[1], 0, 64) + if err != nil { + return Meminfo{}, err + } + m.SwapFree = v + case "Dirty:": + v, err := strconv.ParseUint(field[1], 0, 64) + if err != nil { + return Meminfo{}, err + } + m.Dirty = v + case "Writeback:": + v, err := strconv.ParseUint(field[1], 0, 64) + if err != nil { + return Meminfo{}, err + } + m.Writeback = v + case "AnonPages:": + v, err := strconv.ParseUint(field[1], 0, 64) + if err != nil { + return Meminfo{}, err + } + m.AnonPages = v + case "Mapped:": + v, err := strconv.ParseUint(field[1], 0, 64) + if err != nil { + return Meminfo{}, err + } + m.Mapped = v + case "Shmem:": + v, err := strconv.ParseUint(field[1], 0, 64) + if err != nil { + return Meminfo{}, err } + m.Shmem = v + case "Slab:": + v, err := strconv.ParseUint(field[1], 0, 64) + if err != nil { + return Meminfo{}, err + } + m.Slab = v + case "SReclaimable:": + v, err := strconv.ParseUint(field[1], 0, 64) + if err != nil { + return Meminfo{}, err + } + m.SReclaimable = v + case "SUnreclaim:": + v, err := strconv.ParseUint(field[1], 0, 64) + if err != nil { + return Meminfo{}, err + } + m.SUnreclaim = v + case "KernelStack:": + v, err := strconv.ParseUint(field[1], 0, 64) + if err != nil { + return Meminfo{}, err + } + m.KernelStack = v + case "PageTables:": + v, err := strconv.ParseUint(field[1], 0, 64) + if err != nil { + return Meminfo{}, err + } + m.PageTables = v + case "NFS_Unstable:": + v, err := strconv.ParseUint(field[1], 0, 64) + if err != nil { + return Meminfo{}, err + } + m.NFSUnstable = v + case "Bounce:": + v, err := strconv.ParseUint(field[1], 0, 64) + if err != nil { + return Meminfo{}, err + } + m.Bounce = v + case "WritebackTmp:": + v, err := strconv.ParseUint(field[1], 0, 64) + if err != nil { + return Meminfo{}, err + } + m.WritebackTmp = v + case "CommitLimit:": + v, err := strconv.ParseUint(field[1], 0, 64) + if err != nil { + return Meminfo{}, err + } + m.CommitLimit = v + case "Committed_AS:": + v, err := strconv.ParseUint(field[1], 0, 64) + if err != nil { + return Meminfo{}, err + } + m.CommittedAS = v + case "VmallocTotal:": + v, err := strconv.ParseUint(field[1], 0, 64) + if err != nil { + return Meminfo{}, err + } + m.VmallocTotal = v + case "VmallocUsed:": + v, err := strconv.ParseUint(field[1], 0, 64) + if err != nil { + return Meminfo{}, err + } + m.VmallocUsed = v + case "VmallocChunk:": + v, err := strconv.ParseUint(field[1], 0, 64) + if err != nil { + return Meminfo{}, err + } + m.VmallocChunk = v + case "HardwareCorrupted:": + v, err := strconv.ParseUint(field[1], 0, 64) + if err != nil { + return Meminfo{}, err + } + m.HardwareCorrupted = v + case "AnonHugePages:": + v, err := strconv.ParseUint(field[1], 0, 64) + if err != nil { + return Meminfo{}, err + } + m.AnonHugePages = v + case "ShmemHugePages:": + v, err := strconv.ParseUint(field[1], 0, 64) + if err != nil { + return Meminfo{}, err + } + m.ShmemHugePages = v + case "ShmemPmdMapped:": + v, err := strconv.ParseUint(field[1], 0, 64) + if err != nil { + return Meminfo{}, err + } + m.ShmemPmdMapped = v + case "CmaTotal:": + v, err := strconv.ParseUint(field[1], 0, 64) + if err != nil { + return Meminfo{}, err + } + m.CmaTotal = v + case "CmaFree:": + v, err := strconv.ParseUint(field[1], 0, 64) + if err != nil { + return Meminfo{}, err + } + m.CmaFree = v + case "HugePages_Total:": + v, err := strconv.ParseUint(field[1], 0, 64) + if err != nil { + return Meminfo{}, err + } + m.HugePagesTotal = v + case "HugePages_Free:": + v, err := strconv.ParseUint(field[1], 0, 64) + if err != nil { + return Meminfo{}, err + } + m.HugePagesFree = v + case "HugePages_Rsvd:": + v, err := strconv.ParseUint(field[1], 0, 64) + if err != nil { + return Meminfo{}, err + } + m.HugePagesRsvd = v + case "HugePages_Surp:": + v, err := strconv.ParseUint(field[1], 0, 64) + if err != nil { + return Meminfo{}, err + } + m.HugePagesSurp = v + case "Hugepagesize:": + v, err := strconv.ParseUint(field[1], 0, 64) + if err != nil { + return Meminfo{}, err + } + m.Hugepagesize = v + case "DirectMap4k:": + v, err := strconv.ParseUint(field[1], 0, 64) + if err != nil { + return Meminfo{}, err + } + m.DirectMap4k = v + case "DirectMap2M:": + v, err := strconv.ParseUint(field[1], 0, 64) + if err != nil { + return Meminfo{}, err + } + m.DirectMap2M = v + case "DirectMap1G:": + v, err := strconv.ParseUint(field[1], 0, 64) + if err != nil { + return Meminfo{}, err + } + m.DirectMap1G = v } } - return m, nil } - -func (m Meminfo) regex() string { - return "([A-Za-z0-9()_]*): *([0-9]*).*$" -} diff --git a/meminfo_test.go b/meminfo_test.go index 0e8f9dd3d..34b43bd9e 100644 --- a/meminfo_test.go +++ b/meminfo_test.go @@ -1,66 +1,23 @@ +// Copyright 2019 The Prometheus Authors +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + package procfs import ( "reflect" - "regexp" "testing" ) -func TestMeminfo_regex(t *testing.T) { - - var expected = []struct { - Line string - Key string - Value string - }{ - { - Line: "Inactive(file): 6532440 kB", - Key: "Inactive(file)", - Value: "6532440", - }, - { - Line: "Writeback: 0 kB", - - Key: "Writeback", - Value: "0", - }, - { - Line: "Writeback: 123", - Key: "Writeback", - Value: "123", - }, - { - Line: "HugePages_Total: 0", - Key: "HugePages_Total", - Value: "0", - }, - { - Line: "DirectMap2M: 16039936 kB", - Key: "DirectMap2M", - Value: "16039936", - }, - } - - m := Meminfo{} - re := regexp.MustCompile(m.regex()) - - for _, i := range expected { - submatch := re.FindAllStringSubmatch(i.Line, 1) - if submatch == nil { - t.Errorf("regex fail") - } - - if submatch[0][1] != i.Key { - t.Errorf("Key failed for %s", i.Key) - } - if submatch[0][2] != i.Value { - t.Errorf("Value failed for %s", i.Value) - } - - } - -} - func TestMeminfo(t *testing.T) { expected := Meminfo{ MemTotal: 15666184, @@ -107,12 +64,14 @@ func TestMeminfo(t *testing.T) { DirectMap2M: 16039936, } - have, err := FS("fixtures").NewMeminfo() + have, err := getProcFixtures(t).Meminfo() if err != nil { t.Fatal(err) } if !reflect.DeepEqual(have, expected) { + t.Logf("have: %+v", have) + t.Logf("expected: %+v", expected) t.Errorf("structs are not equal") } }