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