github.com/anonymouse64/snapd@v0.0.0-20210824153203-04c4c42d842d/osutil/meminfo.go (about) 1 // -*- Mode: Go; indent-tabs-mode: t -*- 2 3 /* 4 * Copyright (C) 2021 Canonical Ltd 5 * 6 * This program is free software: you can redistribute it and/or modify 7 * it under the terms of the GNU General Public License version 3 as 8 * published by the Free Software Foundation. 9 * 10 * This program is distributed in the hope that it will be useful, 11 * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 * GNU General Public License for more details. 14 * 15 * You should have received a copy of the GNU General Public License 16 * along with this program. If not, see <http://www.gnu.org/licenses/>. 17 * 18 */ 19 20 package osutil 21 22 import ( 23 "bufio" 24 "fmt" 25 "os" 26 "strconv" 27 "strings" 28 ) 29 30 var ( 31 procMeminfo = "/proc/meminfo" 32 ) 33 34 // TotalUsableMemory returns the total usable memory in the system in bytes. 35 // 36 // Usable means (MemTotal - CmaTotal), i.e. the total amount of memory 37 // minus the space reserved for the CMA (Contiguous Memory Allocator). 38 // 39 // CMA memory is taken up by e.g. the framebuffer on the Raspberry Pi or 40 // by DSPs on specific boards. 41 func TotalUsableMemory() (totalMem uint64, err error) { 42 f, err := os.Open(procMeminfo) 43 if err != nil { 44 return 0, err 45 } 46 defer f.Close() 47 s := bufio.NewScanner(f) 48 49 var memTotal, cmaTotal uint64 50 for s.Scan() { 51 var p *uint64 52 l := strings.TrimSpace(s.Text()) 53 switch { 54 case strings.HasPrefix(l, "MemTotal:"): 55 p = &memTotal 56 case strings.HasPrefix(l, "CmaTotal:"): 57 p = &cmaTotal 58 default: 59 continue 60 } 61 fields := strings.Fields(l) 62 if len(fields) != 3 || fields[2] != "kB" { 63 return 0, fmt.Errorf("cannot process unexpected meminfo entry %q", l) 64 } 65 v, err := strconv.ParseUint(fields[1], 10, 64) 66 if err != nil { 67 return 0, fmt.Errorf("cannot convert memory size value: %v", err) 68 } 69 *p = v * 1024 70 } 71 if err := s.Err(); err != nil { 72 return 0, err 73 } 74 if memTotal == 0 { 75 return 0, fmt.Errorf("cannot determine the total amount of memory in the system from %s", procMeminfo) 76 } 77 return memTotal - cmaTotal, nil 78 } 79 80 func MockProcMeminfo(newPath string) (restore func()) { 81 MustBeTestBinary("mocking can only be done from tests") 82 oldProcMeminfo := procMeminfo 83 procMeminfo = newPath 84 return func() { 85 procMeminfo = oldProcMeminfo 86 } 87 }