Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add proportional resident memory size #142

Merged
merged 3 commits into from
May 18, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -263,6 +263,15 @@ Number of bytes of memory used. The extra label `memtype` can have three values

*swapped*: Field VmSwap from /proc/[pid]/status, translated from KB to bytes.

If gathering smaps file is enabled, two additional values for `memtype` are added:

*proportionalResident*: Sum of "Pss" fields from /proc/[pid]/smaps, whose doc says:

> The "proportional set size" (PSS) of a process is the count of pages it has
> in memory, where each page is divided by the number of processes sharing it.

*proportionalSwapped*: Sum of "SwapPss" fields from /proc/[pid]/smaps

### open_filedesc gauge

Number of file descriptors, based on counting how many entries are in the directory
Expand Down
54 changes: 40 additions & 14 deletions cmd/process-exporter/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -293,6 +293,8 @@ func main() {
"if a proc is tracked, track with it any children that aren't part of their own group")
threads = flag.Bool("threads", true,
"report on per-threadname metrics as well")
smaps = flag.Bool("gather-smaps", true,
"gather metrics from smaps file, which contains proportional resident memory size")
man = flag.Bool("man", false,
"print manual")
configPath = flag.String("config.path", "",
Expand Down Expand Up @@ -355,7 +357,17 @@ func main() {
matchnamer = namemapper
}

pc, err := NewProcessCollector(*procfsPath, *children, *threads, matchnamer, *recheck, *debug)
pc, err := NewProcessCollector(
ProcessCollectorOption{
ProcFSPath: *procfsPath,
Children: *children,
Threads: *threads,
GatherSMaps: *smaps,
Namer: matchnamer,
Recheck: *recheck,
Debug: *debug,
},
)
if err != nil {
log.Fatalf("Error initializing: %v", err)
}
Expand Down Expand Up @@ -395,10 +407,21 @@ type (
done chan struct{}
}

ProcessCollectorOption struct {
ProcFSPath string
Children bool
Threads bool
GatherSMaps bool
Namer common.MatchNamer
Recheck bool
Debug bool
}

NamedProcessCollector struct {
scrapeChan chan scrapeRequest
*proc.Grouper
threads bool
smaps bool
source proc.Source
scrapeErrors int
scrapeProcReadErrors int
Expand All @@ -407,29 +430,25 @@ type (
}
)

func NewProcessCollector(
procfsPath string,
children bool,
threads bool,
n common.MatchNamer,
recheck bool,
debug bool,
) (*NamedProcessCollector, error) {
fs, err := proc.NewFS(procfsPath, debug)
func NewProcessCollector(options ProcessCollectorOption) (*NamedProcessCollector, error) {
fs, err := proc.NewFS(options.ProcFSPath, options.Debug)
if err != nil {
return nil, err
}

fs.GatherSMaps = options.GatherSMaps
p := &NamedProcessCollector{
scrapeChan: make(chan scrapeRequest),
Grouper: proc.NewGrouper(n, children, threads, recheck, debug),
Grouper: proc.NewGrouper(options.Namer, options.Children, options.Threads, options.Recheck, options.Debug),
source: fs,
threads: threads,
debug: debug,
threads: options.Threads,
smaps: options.GatherSMaps,
debug: options.Debug,
}

colErrs, _, err := p.Update(p.source.AllProcs())
if err != nil {
if debug {
if options.Debug {
log.Print(err)
}
return nil, err
Expand Down Expand Up @@ -540,6 +559,13 @@ func (p *NamedProcessCollector) scrape(ch chan<- prometheus.Metric) {
prometheus.GaugeValue, float64(count), gname, wchan)
}

if p.smaps {
ch <- prometheus.MustNewConstMetric(membytesDesc,
prometheus.GaugeValue, float64(gcounts.Memory.ProportionalBytes), gname, "proportionalResident")
ch <- prometheus.MustNewConstMetric(membytesDesc,
prometheus.GaugeValue, float64(gcounts.Memory.ProportionalSwapBytes), gname, "proportionalSwapped")
}

if p.threads {
for _, thr := range gcounts.Threads {
ch <- prometheus.MustNewConstMetric(threadCountDesc,
Expand Down
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ require (
github.com/prometheus/client_golang v0.8.0
github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910 // indirect
github.com/prometheus/common v0.0.0-20180801064454-c7de2306084e // indirect
github.com/prometheus/procfs v0.0.12-0.20200430211241-ea64cd222793
github.com/prometheus/procfs v0.0.12-0.20200505152635-9654394ca94a
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127
gopkg.in/yaml.v2 v2.2.1
)
4 changes: 2 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,8 @@ github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910 h1:idejC8f
github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
github.com/prometheus/common v0.0.0-20180801064454-c7de2306084e h1:n/3MEhJQjQxrOUCzh1Y3Re6aJUUWRp2M9+Oc3eVn/54=
github.com/prometheus/common v0.0.0-20180801064454-c7de2306084e/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro=
github.com/prometheus/procfs v0.0.12-0.20200430211241-ea64cd222793 h1:eaK6XvR/+8o6UtStUEOJNg0WOO0WfgnAbhnuokPfnrY=
github.com/prometheus/procfs v0.0.12-0.20200430211241-ea64cd222793/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU=
github.com/prometheus/procfs v0.0.12-0.20200505152635-9654394ca94a h1:zTuAihdFbve/GW/PAf5sLu2vEO5kRYKLxasyYn53o8c=
github.com/prometheus/procfs v0.0.12-0.20200505152635-9654394ca94a/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU=
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e h1:vcxGaoTs7kV8m5Np9uUNQin4BrLOthgV7252N8V+FwY=
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20200106162015-b016eb3dc98e h1:LwyF2AFISC9nVbS6MgzsaQNSUsRXI49GS+YQ5KX/QH0=
Expand Down
2 changes: 2 additions & 0 deletions proc/grouper.go
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,8 @@ func groupadd(grp Group, ts Update) Group {
grp.Memory.ResidentBytes += ts.Memory.ResidentBytes
grp.Memory.VirtualBytes += ts.Memory.VirtualBytes
grp.Memory.VmSwapBytes += ts.Memory.VmSwapBytes
grp.Memory.ProportionalBytes += ts.Memory.ProportionalBytes
grp.Memory.ProportionalSwapBytes += ts.Memory.ProportionalSwapBytes
if ts.Filedesc.Open != -1 {
grp.OpenFDs += uint64(ts.Filedesc.Open)
}
Expand Down
42 changes: 21 additions & 21 deletions proc/grouper_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,30 +45,30 @@ func TestGrouperBasic(t *testing.T) {
}{
{
[]IDInfo{
piinfost(p1, n1, Counts{1, 2, 3, 4, 5, 6, 0, 0}, Memory{7, 8, 0},
piinfost(p1, n1, Counts{1, 2, 3, 4, 5, 6, 0, 0}, Memory{7, 8, 0, 0, 0},
Filedesc{4, 400}, 2, States{Other: 1}),
piinfost(p2, n2, Counts{2, 3, 4, 5, 6, 7, 0, 0}, Memory{8, 9, 0},
piinfost(p2, n2, Counts{2, 3, 4, 5, 6, 7, 0, 0}, Memory{8, 9, 0, 0, 0},
Filedesc{40, 400}, 3, States{Waiting: 1}),
},
GroupByName{
"g1": Group{Counts{}, States{Other: 1}, msi{}, 1, Memory{7, 8, 0}, starttime,
"g1": Group{Counts{}, States{Other: 1}, msi{}, 1, Memory{7, 8, 0, 0, 0}, starttime,
4, 0.01, 2, nil},
"g2": Group{Counts{}, States{Waiting: 1}, msi{}, 1, Memory{8, 9, 0}, starttime,
"g2": Group{Counts{}, States{Waiting: 1}, msi{}, 1, Memory{8, 9, 0, 0, 0}, starttime,
40, 0.1, 3, nil},
},
},
{
[]IDInfo{
piinfost(p1, n1, Counts{2, 3, 4, 5, 6, 7, 0, 0},
Memory{6, 7, 0}, Filedesc{100, 400}, 4, States{Zombie: 1}),
Memory{6, 7, 0, 0, 0}, Filedesc{100, 400}, 4, States{Zombie: 1}),
piinfost(p2, n2, Counts{4, 5, 6, 7, 8, 9, 0, 0},
Memory{9, 8, 0}, Filedesc{400, 400}, 2, States{Running: 1}),
Memory{9, 8, 0, 0, 0}, Filedesc{400, 400}, 2, States{Running: 1}),
},
GroupByName{
"g1": Group{Counts{1, 1, 1, 1, 1, 1, 0, 0}, States{Zombie: 1}, msi{}, 1,
Memory{6, 7, 0}, starttime, 100, 0.25, 4, nil},
Memory{6, 7, 0, 0, 0}, starttime, 100, 0.25, 4, nil},
"g2": Group{Counts{2, 2, 2, 2, 2, 2, 0, 0}, States{Running: 1}, msi{}, 1,
Memory{9, 8, 0}, starttime, 400, 1, 2, nil},
Memory{9, 8, 0, 0, 0}, starttime, 400, 1, 2, nil},
},
},
}
Expand All @@ -95,35 +95,35 @@ func TestGrouperProcJoin(t *testing.T) {
}{
{
[]IDInfo{
piinfo(p1, n1, Counts{1, 2, 3, 4, 5, 6, 0, 0}, Memory{3, 4, 0}, Filedesc{4, 400}, 2),
piinfo(p1, n1, Counts{1, 2, 3, 4, 5, 6, 0, 0}, Memory{3, 4, 0, 0, 0}, Filedesc{4, 400}, 2),
},
GroupByName{
"g1": Group{Counts{}, States{}, msi{}, 1, Memory{3, 4, 0}, starttime, 4, 0.01, 2, nil},
"g1": Group{Counts{}, States{}, msi{}, 1, Memory{3, 4, 0, 0, 0}, starttime, 4, 0.01, 2, nil},
},
}, {
// The counts for pid2 won't be factored into the total yet because we only add
// to counts starting with the second time we see a proc. Memory and FDs are
// affected though.
[]IDInfo{
piinfost(p1, n1, Counts{3, 4, 5, 6, 7, 8, 0, 0},
Memory{3, 4, 0}, Filedesc{4, 400}, 2, States{Running: 1}),
Memory{3, 4, 0, 0, 0}, Filedesc{4, 400}, 2, States{Running: 1}),
piinfost(p2, n2, Counts{1, 1, 1, 1, 1, 1, 0, 0},
Memory{1, 2, 0}, Filedesc{40, 400}, 3, States{Sleeping: 1}),
Memory{1, 2, 0, 0, 0}, Filedesc{40, 400}, 3, States{Sleeping: 1}),
},
GroupByName{
"g1": Group{Counts{2, 2, 2, 2, 2, 2, 0, 0}, States{Running: 1, Sleeping: 1}, msi{}, 2,
Memory{4, 6, 0}, starttime, 44, 0.1, 5, nil},
Memory{4, 6, 0, 0, 0}, starttime, 44, 0.1, 5, nil},
},
}, {
[]IDInfo{
piinfost(p1, n1, Counts{4, 5, 6, 7, 8, 9, 0, 0},
Memory{1, 5, 0}, Filedesc{4, 400}, 2, States{Running: 1}),
Memory{1, 5, 0, 0, 0}, Filedesc{4, 400}, 2, States{Running: 1}),
piinfost(p2, n2, Counts{2, 2, 2, 2, 2, 2, 0, 0},
Memory{2, 4, 0}, Filedesc{40, 400}, 3, States{Running: 1}),
Memory{2, 4, 0, 0, 0}, Filedesc{40, 400}, 3, States{Running: 1}),
},
GroupByName{
"g1": Group{Counts{4, 4, 4, 4, 4, 4, 0, 0}, States{Running: 2}, msi{}, 2,
Memory{3, 9, 0}, starttime, 44, 0.1, 5, nil},
Memory{3, 9, 0, 0, 0}, starttime, 44, 0.1, 5, nil},
},
},
}
Expand All @@ -150,18 +150,18 @@ func TestGrouperNonDecreasing(t *testing.T) {
}{
{
[]IDInfo{
piinfo(p1, n1, Counts{3, 4, 5, 6, 7, 8, 0, 0}, Memory{3, 4, 0}, Filedesc{4, 400}, 2),
piinfo(p2, n2, Counts{1, 1, 1, 1, 1, 1, 0, 0}, Memory{1, 2, 0}, Filedesc{40, 400}, 3),
piinfo(p1, n1, Counts{3, 4, 5, 6, 7, 8, 0, 0}, Memory{3, 4, 0, 0, 0}, Filedesc{4, 400}, 2),
piinfo(p2, n2, Counts{1, 1, 1, 1, 1, 1, 0, 0}, Memory{1, 2, 0, 0, 0}, Filedesc{40, 400}, 3),
},
GroupByName{
"g1": Group{Counts{}, States{}, msi{}, 2, Memory{4, 6, 0}, starttime, 44, 0.1, 5, nil},
"g1": Group{Counts{}, States{}, msi{}, 2, Memory{4, 6, 0, 0, 0}, starttime, 44, 0.1, 5, nil},
},
}, {
[]IDInfo{
piinfo(p1, n1, Counts{4, 5, 6, 7, 8, 9, 0, 0}, Memory{1, 5, 0}, Filedesc{4, 400}, 2),
piinfo(p1, n1, Counts{4, 5, 6, 7, 8, 9, 0, 0}, Memory{1, 5, 0, 0, 0}, Filedesc{4, 400}, 2),
},
GroupByName{
"g1": Group{Counts{1, 1, 1, 1, 1, 1, 0, 0}, States{}, msi{}, 1, Memory{1, 5, 0}, starttime, 4, 0.01, 2, nil},
"g1": Group{Counts{1, 1, 1, 1, 1, 1, 0, 0}, States{}, msi{}, 1, Memory{1, 5, 0, 0, 0}, starttime, 4, 0.01, 2, nil},
},
}, {
[]IDInfo{},
Expand Down
41 changes: 28 additions & 13 deletions proc/read.go
Original file line number Diff line number Diff line change
Expand Up @@ -49,9 +49,11 @@ type (

// Memory describes a proc's memory usage.
Memory struct {
ResidentBytes uint64
VirtualBytes uint64
VmSwapBytes uint64
ResidentBytes uint64
VirtualBytes uint64
VmSwapBytes uint64
ProportionalBytes uint64
ProportionalSwapBytes uint64
}

// Filedesc describes a proc's file descriptor usage and soft limit.
Expand Down Expand Up @@ -187,9 +189,10 @@ type (
// FS implements Source.
FS struct {
procfs.FS
BootTime uint64
MountPoint string
debug bool
BootTime uint64
MountPoint string
GatherSMaps bool
debug bool
}
)

Expand Down Expand Up @@ -474,13 +477,25 @@ func (p proc) GetMetrics() (Metrics, int, error) {
softerrors |= 1
}

memory := Memory{
ResidentBytes: uint64(stat.ResidentMemory()),
VirtualBytes: uint64(stat.VirtualMemory()),
VmSwapBytes: uint64(status.VmSwap),
}

if p.proccache.fs.GatherSMaps {
smaps, err := p.Proc.ProcSMapsRollup()
if err != nil {
softerrors |= 1
} else {
memory.ProportionalBytes = smaps.Pss
memory.ProportionalSwapBytes = smaps.SwapPss
}
}

return Metrics{
Counts: counts,
Memory: Memory{
ResidentBytes: uint64(stat.ResidentMemory()),
VirtualBytes: uint64(stat.VirtualMemory()),
VmSwapBytes: uint64(status.VmSwap),
},
Memory: memory,
Filedesc: Filedesc{
Open: int64(numfds),
Limit: uint64(limits.OpenFiles),
Expand Down Expand Up @@ -554,7 +569,7 @@ func NewFS(mountPoint string, debug bool) (*FS, error) {
if err != nil {
return nil, err
}
return &FS{fs, stat.BootTime, mountPoint, debug}, nil
return &FS{fs, stat.BootTime, mountPoint, false, debug}, nil
}

func (fs *FS) threadFs(pid int) (*FS, error) {
Expand All @@ -563,7 +578,7 @@ func (fs *FS) threadFs(pid int) (*FS, error) {
if err != nil {
return nil, err
}
return &FS{tfs, fs.BootTime, mountPoint, false}, nil
return &FS{tfs, fs.BootTime, mountPoint, fs.GatherSMaps, false}, nil
}

// AllProcs implements Source.
Expand Down
8 changes: 4 additions & 4 deletions proc/tracker_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -99,15 +99,15 @@ func TestTrackerMetrics(t *testing.T) {
want Update
}{
{
piinfost(p, n, Counts{1, 2, 3, 4, 5, 6, 0, 0}, Memory{7, 8, 0},
piinfost(p, n, Counts{1, 2, 3, 4, 5, 6, 0, 0}, Memory{7, 8, 0, 0, 0},
Filedesc{1, 10}, 9, States{Sleeping: 1}),
Update{n, Delta{}, Memory{7, 8, 0}, Filedesc{1, 10}, tm,
Update{n, Delta{}, Memory{7, 8, 0, 0, 0}, Filedesc{1, 10}, tm,
9, States{Sleeping: 1}, msi{}, nil},
},
{
piinfost(p, n, Counts{2, 3, 4, 5, 6, 7, 0, 0}, Memory{1, 2, 0},
piinfost(p, n, Counts{2, 3, 4, 5, 6, 7, 0, 0}, Memory{1, 2, 0, 0, 0},
Filedesc{2, 20}, 1, States{Running: 1}),
Update{n, Delta{1, 1, 1, 1, 1, 1, 0, 0}, Memory{1, 2, 0},
Update{n, Delta{1, 1, 1, 1, 1, 1, 0, 0}, Memory{1, 2, 0, 0, 0},
Filedesc{2, 20}, tm, 1, States{Running: 1}, msi{}, nil},
},
}
Expand Down
Loading