1// Copyright 2020 The Prometheus Authors 2// Licensed under the Apache License, Version 2.0 (the "License"); 3// you may not use this file except in compliance with the License. 4// You may obtain a copy of the License at 5// 6// http://www.apache.org/licenses/LICENSE-2.0 7// 8// Unless required by applicable law or agreed to in writing, software 9// distributed under the License is distributed on an "AS IS" BASIS, 10// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11// See the License for the specific language governing permissions and 12// limitations under the License. 13 14// +build !windows 15 16package procfs 17 18import ( 19 "bufio" 20 "errors" 21 "fmt" 22 "os" 23 "regexp" 24 "strconv" 25 "strings" 26 27 "github.com/prometheus/procfs/internal/util" 28) 29 30var ( 31 // match the header line before each mapped zone in /proc/pid/smaps 32 procSMapsHeaderLine = regexp.MustCompile(`^[a-f0-9].*$`) 33) 34 35type ProcSMapsRollup struct { 36 // Amount of the mapping that is currently resident in RAM 37 Rss uint64 38 // Process's proportional share of this mapping 39 Pss uint64 40 // Size in bytes of clean shared pages 41 SharedClean uint64 42 // Size in bytes of dirty shared pages 43 SharedDirty uint64 44 // Size in bytes of clean private pages 45 PrivateClean uint64 46 // Size in bytes of dirty private pages 47 PrivateDirty uint64 48 // Amount of memory currently marked as referenced or accessed 49 Referenced uint64 50 // Amount of memory that does not belong to any file 51 Anonymous uint64 52 // Amount would-be-anonymous memory currently on swap 53 Swap uint64 54 // Process's proportional memory on swap 55 SwapPss uint64 56} 57 58// ProcSMapsRollup reads from /proc/[pid]/smaps_rollup to get summed memory information of the 59// process. 60// 61// If smaps_rollup does not exists (require kernel >= 4.15), the content of /proc/pid/smaps will 62// we read and summed. 63func (p Proc) ProcSMapsRollup() (ProcSMapsRollup, error) { 64 data, err := util.ReadFileNoStat(p.path("smaps_rollup")) 65 if err != nil && os.IsNotExist(err) { 66 return p.procSMapsRollupManual() 67 } 68 if err != nil { 69 return ProcSMapsRollup{}, err 70 } 71 72 lines := strings.Split(string(data), "\n") 73 smaps := ProcSMapsRollup{} 74 75 // skip first line which don't contains information we need 76 lines = lines[1:] 77 for _, line := range lines { 78 if line == "" { 79 continue 80 } 81 82 if err := smaps.parseLine(line); err != nil { 83 return ProcSMapsRollup{}, err 84 } 85 } 86 87 return smaps, nil 88} 89 90// Read /proc/pid/smaps and do the roll-up in Go code. 91func (p Proc) procSMapsRollupManual() (ProcSMapsRollup, error) { 92 file, err := os.Open(p.path("smaps")) 93 if err != nil { 94 return ProcSMapsRollup{}, err 95 } 96 defer file.Close() 97 98 smaps := ProcSMapsRollup{} 99 scan := bufio.NewScanner(file) 100 101 for scan.Scan() { 102 line := scan.Text() 103 104 if procSMapsHeaderLine.MatchString(line) { 105 continue 106 } 107 108 if err := smaps.parseLine(line); err != nil { 109 return ProcSMapsRollup{}, err 110 } 111 } 112 113 return smaps, nil 114} 115 116func (s *ProcSMapsRollup) parseLine(line string) error { 117 kv := strings.SplitN(line, ":", 2) 118 if len(kv) != 2 { 119 fmt.Println(line) 120 return errors.New("invalid net/dev line, missing colon") 121 } 122 123 k := kv[0] 124 if k == "VmFlags" { 125 return nil 126 } 127 128 v := strings.TrimSpace(kv[1]) 129 v = strings.TrimRight(v, " kB") 130 131 vKBytes, err := strconv.ParseUint(v, 10, 64) 132 if err != nil { 133 return err 134 } 135 vBytes := vKBytes * 1024 136 137 s.addValue(k, v, vKBytes, vBytes) 138 139 return nil 140} 141 142func (s *ProcSMapsRollup) addValue(k string, vString string, vUint uint64, vUintBytes uint64) { 143 switch k { 144 case "Rss": 145 s.Rss += vUintBytes 146 case "Pss": 147 s.Pss += vUintBytes 148 case "Shared_Clean": 149 s.SharedClean += vUintBytes 150 case "Shared_Dirty": 151 s.SharedDirty += vUintBytes 152 case "Private_Clean": 153 s.PrivateClean += vUintBytes 154 case "Private_Dirty": 155 s.PrivateDirty += vUintBytes 156 case "Referenced": 157 s.Referenced += vUintBytes 158 case "Anonymous": 159 s.Anonymous += vUintBytes 160 case "Swap": 161 s.Swap += vUintBytes 162 case "SwapPss": 163 s.SwapPss += vUintBytes 164 } 165} 166