blob: 9a297afcf89e6989d6ea2d237cd5172011c7d7e3 [file] [log] [blame]
khenaidood948f772021-08-11 17:49:24 -04001// 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
Abhay Kumara2ae5992025-11-10 14:02:24 +000014//go:build !windows
khenaidood948f772021-08-11 17:49:24 -040015// +build !windows
16
17package procfs
18
19import (
20 "bufio"
21 "errors"
khenaidood948f772021-08-11 17:49:24 -040022 "os"
23 "regexp"
24 "strconv"
25 "strings"
26
27 "github.com/prometheus/procfs/internal/util"
28)
29
30var (
Abhay Kumara2ae5992025-11-10 14:02:24 +000031 // Match the header line before each mapped zone in `/proc/pid/smaps`.
khenaidood948f772021-08-11 17:49:24 -040032 procSMapsHeaderLine = regexp.MustCompile(`^[a-f0-9].*$`)
33)
34
35type ProcSMapsRollup struct {
Abhay Kumara2ae5992025-11-10 14:02:24 +000036 // Amount of the mapping that is currently resident in RAM.
khenaidood948f772021-08-11 17:49:24 -040037 Rss uint64
Abhay Kumara2ae5992025-11-10 14:02:24 +000038 // Process's proportional share of this mapping.
khenaidood948f772021-08-11 17:49:24 -040039 Pss uint64
Abhay Kumara2ae5992025-11-10 14:02:24 +000040 // Size in bytes of clean shared pages.
khenaidood948f772021-08-11 17:49:24 -040041 SharedClean uint64
Abhay Kumara2ae5992025-11-10 14:02:24 +000042 // Size in bytes of dirty shared pages.
khenaidood948f772021-08-11 17:49:24 -040043 SharedDirty uint64
Abhay Kumara2ae5992025-11-10 14:02:24 +000044 // Size in bytes of clean private pages.
khenaidood948f772021-08-11 17:49:24 -040045 PrivateClean uint64
Abhay Kumara2ae5992025-11-10 14:02:24 +000046 // Size in bytes of dirty private pages.
khenaidood948f772021-08-11 17:49:24 -040047 PrivateDirty uint64
Abhay Kumara2ae5992025-11-10 14:02:24 +000048 // Amount of memory currently marked as referenced or accessed.
khenaidood948f772021-08-11 17:49:24 -040049 Referenced uint64
Abhay Kumara2ae5992025-11-10 14:02:24 +000050 // Amount of memory that does not belong to any file.
khenaidood948f772021-08-11 17:49:24 -040051 Anonymous uint64
Abhay Kumara2ae5992025-11-10 14:02:24 +000052 // Amount would-be-anonymous memory currently on swap.
khenaidood948f772021-08-11 17:49:24 -040053 Swap uint64
Abhay Kumara2ae5992025-11-10 14:02:24 +000054 // Process's proportional memory on swap.
khenaidood948f772021-08-11 17:49:24 -040055 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 {
khenaidood948f772021-08-11 17:49:24 -0400119 return errors.New("invalid net/dev line, missing colon")
120 }
121
122 k := kv[0]
123 if k == "VmFlags" {
124 return nil
125 }
126
127 v := strings.TrimSpace(kv[1])
Abhay Kumara2ae5992025-11-10 14:02:24 +0000128 v = strings.TrimSuffix(v, " kB")
khenaidood948f772021-08-11 17:49:24 -0400129
130 vKBytes, err := strconv.ParseUint(v, 10, 64)
131 if err != nil {
132 return err
133 }
134 vBytes := vKBytes * 1024
135
Abhay Kumara2ae5992025-11-10 14:02:24 +0000136 s.addValue(k, vBytes)
khenaidood948f772021-08-11 17:49:24 -0400137
138 return nil
139}
140
Abhay Kumara2ae5992025-11-10 14:02:24 +0000141func (s *ProcSMapsRollup) addValue(k string, vUintBytes uint64) {
khenaidood948f772021-08-11 17:49:24 -0400142 switch k {
143 case "Rss":
144 s.Rss += vUintBytes
145 case "Pss":
146 s.Pss += vUintBytes
147 case "Shared_Clean":
148 s.SharedClean += vUintBytes
149 case "Shared_Dirty":
150 s.SharedDirty += vUintBytes
151 case "Private_Clean":
152 s.PrivateClean += vUintBytes
153 case "Private_Dirty":
154 s.PrivateDirty += vUintBytes
155 case "Referenced":
156 s.Referenced += vUintBytes
157 case "Anonymous":
158 s.Anonymous += vUintBytes
159 case "Swap":
160 s.Swap += vUintBytes
161 case "SwapPss":
162 s.SwapPss += vUintBytes
163 }
164}