Merge "Read the proc status file when PID is given for metrics purpose."
This commit is contained in:
commit
175500f016
5 changed files with 334 additions and 0 deletions
37
ui/metrics/proc/Android.bp
Normal file
37
ui/metrics/proc/Android.bp
Normal file
|
@ -0,0 +1,37 @@
|
||||||
|
// Copyright 2020 Google Inc. All rights reserved.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
bootstrap_go_package {
|
||||||
|
name: "soong-ui-metrics-proc",
|
||||||
|
pkgPath: "android/soong/ui/metrics/proc",
|
||||||
|
deps: [
|
||||||
|
"soong-finder-fs",
|
||||||
|
],
|
||||||
|
srcs: [
|
||||||
|
"status.go",
|
||||||
|
],
|
||||||
|
linux: {
|
||||||
|
srcs: [
|
||||||
|
"status_linux.go",
|
||||||
|
],
|
||||||
|
testSrcs: [
|
||||||
|
"status_linux_test.go",
|
||||||
|
],
|
||||||
|
},
|
||||||
|
darwin: {
|
||||||
|
srcs: [
|
||||||
|
"status_darwin.go",
|
||||||
|
],
|
||||||
|
},
|
||||||
|
}
|
128
ui/metrics/proc/status.go
Normal file
128
ui/metrics/proc/status.go
Normal file
|
@ -0,0 +1,128 @@
|
||||||
|
// package proc contains functionality to read proc status files.
|
||||||
|
package proc
|
||||||
|
|
||||||
|
import (
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
// ProcStatus holds information regarding the memory usage of
|
||||||
|
// an executing process. The memory sizes in each of the field
|
||||||
|
// is in bytes.
|
||||||
|
type ProcStatus struct {
|
||||||
|
// Process PID.
|
||||||
|
pid int
|
||||||
|
|
||||||
|
// Peak virtual memory size.
|
||||||
|
VmPeak uint64
|
||||||
|
|
||||||
|
// Virtual memory size.
|
||||||
|
VmSize uint64
|
||||||
|
|
||||||
|
// Locked Memory size.
|
||||||
|
VmLck uint64
|
||||||
|
|
||||||
|
// Pinned memory size.
|
||||||
|
VmPin uint64
|
||||||
|
|
||||||
|
// Peak resident set size.
|
||||||
|
VmHWM uint64
|
||||||
|
|
||||||
|
// Resident set size (sum of RssAnon, RssFile and RssShmem).
|
||||||
|
VmRss uint64
|
||||||
|
|
||||||
|
// Size of resident anonymous memory.
|
||||||
|
RssAnon uint64
|
||||||
|
|
||||||
|
// Size of resident shared memory.
|
||||||
|
RssShmem uint64
|
||||||
|
|
||||||
|
// Size of data segments.
|
||||||
|
VmData uint64
|
||||||
|
|
||||||
|
// Size of stack segments.
|
||||||
|
VmStk uint64
|
||||||
|
|
||||||
|
//Size of text segments.
|
||||||
|
VmExe uint64
|
||||||
|
|
||||||
|
//Shared library code size.
|
||||||
|
VmLib uint64
|
||||||
|
|
||||||
|
// Page table entries size.
|
||||||
|
VmPTE uint64
|
||||||
|
|
||||||
|
// Size of second-level page tables.
|
||||||
|
VmPMD uint64
|
||||||
|
|
||||||
|
// Swapped-out virtual memory size by anonymous private.
|
||||||
|
VmSwap uint64
|
||||||
|
|
||||||
|
// Size of hugetlb memory page size.
|
||||||
|
HugetlbPages uint64
|
||||||
|
}
|
||||||
|
|
||||||
|
// fillProcStatus takes the key and value, converts the value
|
||||||
|
// to the proper size unit and is stored in the ProcStatus.
|
||||||
|
func fillProcStatus(s *ProcStatus, key, value string) {
|
||||||
|
v := strToUint64(value)
|
||||||
|
switch key {
|
||||||
|
case "VmPeak":
|
||||||
|
s.VmPeak = v
|
||||||
|
case "VmSize":
|
||||||
|
s.VmSize = v
|
||||||
|
case "VmLck":
|
||||||
|
s.VmLck = v
|
||||||
|
case "VmPin":
|
||||||
|
s.VmPin = v
|
||||||
|
case "VmHWM":
|
||||||
|
s.VmHWM = v
|
||||||
|
case "VmRSS":
|
||||||
|
s.VmRss = v
|
||||||
|
case "RssAnon":
|
||||||
|
s.RssAnon = v
|
||||||
|
case "RssShmem":
|
||||||
|
s.RssShmem = v
|
||||||
|
case "VmData":
|
||||||
|
s.VmData = v
|
||||||
|
case "VmStk":
|
||||||
|
s.VmStk = v
|
||||||
|
case "VmExe":
|
||||||
|
s.VmExe = v
|
||||||
|
case "VmLib":
|
||||||
|
s.VmLib = v
|
||||||
|
case "VmPTE":
|
||||||
|
s.VmPTE = v
|
||||||
|
case "VmPMD":
|
||||||
|
s.VmPMD = v
|
||||||
|
case "VmSwap":
|
||||||
|
s.VmSwap = v
|
||||||
|
case "HugetlbPages":
|
||||||
|
s.HugetlbPages = v
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// strToUint64 takes the string and converts to unsigned 64-bit integer.
|
||||||
|
// If the string contains a memory unit such as kB and is converted to
|
||||||
|
// bytes.
|
||||||
|
func strToUint64(v string) uint64 {
|
||||||
|
// v could be "1024 kB" so scan for the empty space and
|
||||||
|
// split between the value and the unit.
|
||||||
|
var separatorIndex int
|
||||||
|
if separatorIndex = strings.IndexAny(v, " "); separatorIndex < 0 {
|
||||||
|
separatorIndex = len(v)
|
||||||
|
}
|
||||||
|
value, err := strconv.ParseUint(v[:separatorIndex], 10, 64)
|
||||||
|
if err != nil {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
var scale uint64 = 1
|
||||||
|
switch strings.TrimSpace(v[separatorIndex:]) {
|
||||||
|
case "kB", "KB":
|
||||||
|
scale = 1024
|
||||||
|
case "mB", "MB":
|
||||||
|
scale = 1024 * 1024
|
||||||
|
}
|
||||||
|
return value * scale
|
||||||
|
}
|
11
ui/metrics/proc/status_darwin.go
Normal file
11
ui/metrics/proc/status_darwin.go
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
package proc
|
||||||
|
|
||||||
|
import (
|
||||||
|
"android/soong/finder/fs"
|
||||||
|
)
|
||||||
|
|
||||||
|
// NewProcStatus returns a zero filled value of ProcStatus as it
|
||||||
|
// is not supported for darwin distribution based.
|
||||||
|
func NewProcStatus(pid int, _ fs.FileSystem) (*ProcStatus, error) {
|
||||||
|
return &ProcStatus{}, nil
|
||||||
|
}
|
46
ui/metrics/proc/status_linux.go
Normal file
46
ui/metrics/proc/status_linux.go
Normal file
|
@ -0,0 +1,46 @@
|
||||||
|
package proc
|
||||||
|
|
||||||
|
import (
|
||||||
|
"io/ioutil"
|
||||||
|
"path/filepath"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"android/soong/finder/fs"
|
||||||
|
)
|
||||||
|
|
||||||
|
// NewProcStatus returns an instance of the ProcStatus that contains memory
|
||||||
|
// information of the process. The memory information is extracted from the
|
||||||
|
// "/proc/<pid>/status" text file. This is only available for Linux
|
||||||
|
// distribution that supports /proc.
|
||||||
|
func NewProcStatus(pid int, fileSystem fs.FileSystem) (*ProcStatus, error) {
|
||||||
|
statusFname := filepath.Join("/proc", strconv.Itoa(pid), "status")
|
||||||
|
r, err := fileSystem.Open(statusFname)
|
||||||
|
if err != nil {
|
||||||
|
return &ProcStatus{}, err
|
||||||
|
}
|
||||||
|
defer r.Close()
|
||||||
|
|
||||||
|
data, err := ioutil.ReadAll(r)
|
||||||
|
if err != nil {
|
||||||
|
return &ProcStatus{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
s := &ProcStatus{
|
||||||
|
pid: pid,
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, l := range strings.Split(string(data), "\n") {
|
||||||
|
// If the status file does not contain "key: values", just skip the line
|
||||||
|
// as the information we are looking for is not needed.
|
||||||
|
if !strings.Contains(l, ":") {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// At this point, we're only considering entries that has key, single value pairs.
|
||||||
|
kv := strings.SplitN(l, ":", 2)
|
||||||
|
fillProcStatus(s, strings.TrimSpace(kv[0]), strings.TrimSpace(kv[1]))
|
||||||
|
}
|
||||||
|
|
||||||
|
return s, nil
|
||||||
|
}
|
112
ui/metrics/proc/status_linux_test.go
Normal file
112
ui/metrics/proc/status_linux_test.go
Normal file
|
@ -0,0 +1,112 @@
|
||||||
|
package proc
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"path/filepath"
|
||||||
|
"reflect"
|
||||||
|
"strconv"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"android/soong/finder/fs"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestNewProcStatus(t *testing.T) {
|
||||||
|
fs := fs.NewMockFs(nil)
|
||||||
|
|
||||||
|
pid := 4032827
|
||||||
|
procDir := filepath.Join("/proc", strconv.Itoa(pid))
|
||||||
|
if err := fs.MkDirs(procDir); err != nil {
|
||||||
|
t.Fatalf("failed to create proc pid dir %s: %v", procDir, err)
|
||||||
|
}
|
||||||
|
statusFilename := filepath.Join(procDir, "status")
|
||||||
|
|
||||||
|
if err := fs.WriteFile(statusFilename, statusData, 0644); err != nil {
|
||||||
|
t.Fatalf("failed to write proc file %s: %v", statusFilename, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
status, err := NewProcStatus(pid, fs)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("got %v, want nil for error", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Printf("%d %d\b", status.VmPeak, expectedStatus.VmPeak)
|
||||||
|
if !reflect.DeepEqual(status, expectedStatus) {
|
||||||
|
t.Errorf("got %v, expecting %v for ProcStatus", status, expectedStatus)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var statusData = []byte(`Name: fake_process
|
||||||
|
Umask: 0022
|
||||||
|
State: S (sleeping)
|
||||||
|
Tgid: 4032827
|
||||||
|
Ngid: 0
|
||||||
|
Pid: 4032827
|
||||||
|
PPid: 1
|
||||||
|
TracerPid: 0
|
||||||
|
Uid: 0 0 0 0
|
||||||
|
Gid: 0 0 0 0
|
||||||
|
FDSize: 512
|
||||||
|
Groups:
|
||||||
|
NStgid: 4032827
|
||||||
|
NSpid: 4032827
|
||||||
|
NSpgid: 4032827
|
||||||
|
NSsid: 4032827
|
||||||
|
VmPeak: 733232 kB
|
||||||
|
VmSize: 733232 kB
|
||||||
|
VmLck: 132 kB
|
||||||
|
VmPin: 130 kB
|
||||||
|
VmHWM: 69156 kB
|
||||||
|
VmRSS: 69156 kB
|
||||||
|
RssAnon: 50896 kB
|
||||||
|
RssFile: 18260 kB
|
||||||
|
RssShmem: 122 kB
|
||||||
|
VmData: 112388 kB
|
||||||
|
VmStk: 132 kB
|
||||||
|
VmExe: 9304 kB
|
||||||
|
VmLib: 8 kB
|
||||||
|
VmPTE: 228 kB
|
||||||
|
VmSwap: 10 kB
|
||||||
|
HugetlbPages: 22 kB
|
||||||
|
CoreDumping: 0
|
||||||
|
THP_enabled: 1
|
||||||
|
Threads: 46
|
||||||
|
SigQ: 2/767780
|
||||||
|
SigPnd: 0000000000000000
|
||||||
|
ShdPnd: 0000000000000000
|
||||||
|
SigBlk: fffffffe3bfa3a00
|
||||||
|
SigIgn: 0000000000000000
|
||||||
|
SigCgt: fffffffe7fc1feff
|
||||||
|
CapInh: 0000000000000000
|
||||||
|
CapPrm: 0000003fffffffff
|
||||||
|
CapEff: 0000003fffffffff
|
||||||
|
CapBnd: 0000003fffffffff
|
||||||
|
CapAmb: 0000000000000000
|
||||||
|
NoNewPrivs: 0
|
||||||
|
Seccomp: 0
|
||||||
|
Speculation_Store_Bypass: thread vulnerable
|
||||||
|
Cpus_allowed: ff,ffffffff,ffffffff
|
||||||
|
Cpus_allowed_list: 0-71
|
||||||
|
Mems_allowed: 00000000,00000003
|
||||||
|
Mems_allowed_list: 0-1
|
||||||
|
voluntary_ctxt_switches: 1635
|
||||||
|
nonvoluntary_ctxt_switches: 32
|
||||||
|
`)
|
||||||
|
|
||||||
|
var expectedStatus = &ProcStatus{
|
||||||
|
pid: 4032827,
|
||||||
|
VmPeak: 750829568,
|
||||||
|
VmSize: 750829568,
|
||||||
|
VmLck: 135168,
|
||||||
|
VmPin: 133120,
|
||||||
|
VmHWM: 70815744,
|
||||||
|
VmRss: 70815744,
|
||||||
|
RssAnon: 52117504,
|
||||||
|
RssShmem: 124928,
|
||||||
|
VmData: 115085312,
|
||||||
|
VmStk: 135168,
|
||||||
|
VmExe: 9527296,
|
||||||
|
VmLib: 8192,
|
||||||
|
VmPTE: 233472,
|
||||||
|
VmSwap: 10240,
|
||||||
|
HugetlbPages: 22528,
|
||||||
|
}
|
Loading…
Reference in a new issue