khenaidoo | 2672188 | 2021-08-11 17:42:52 -0400 | [diff] [blame] | 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 | |
| 16 | package procfs |
| 17 | |
| 18 | import ( |
| 19 | "bufio" |
| 20 | "errors" |
| 21 | "fmt" |
| 22 | "os" |
| 23 | "regexp" |
| 24 | "strconv" |
| 25 | "strings" |
| 26 | |
| 27 | "github.com/prometheus/procfs/internal/util" |
| 28 | ) |
| 29 | |
| 30 | var ( |
| 31 | // match the header line before each mapped zone in /proc/pid/smaps |
| 32 | procSMapsHeaderLine = regexp.MustCompile(`^[a-f0-9].*$`) |
| 33 | ) |
| 34 | |
| 35 | type 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. |
| 63 | func (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. |
| 91 | func (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 | |
| 116 | func (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 | |
| 142 | func (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 | } |