aboutsummaryrefslogtreecommitdiff
path: root/ui
diff options
context:
space:
mode:
authorPatrice Arruda <patricearruda@google.com>2020-12-18 04:14:09 +0000
committerGerrit Code Review <noreply-gerritcodereview@google.com>2020-12-18 04:14:09 +0000
commit175500f016fc940473fd7524481c796738ae8a58 (patch)
treead70114e31e069c9852cbd5d9ce6eb23a2077437 /ui
parentd9d59fb57c63fbf01dc5e4a5e4915868ccf5bb4c (diff)
parent04157e186fc0d2a62d014d30db383f2e70d4571b (diff)
downloadsoong-175500f016fc940473fd7524481c796738ae8a58.tar.gz
Merge "Read the proc status file when PID is given for metrics purpose."
Diffstat (limited to 'ui')
-rw-r--r--ui/metrics/proc/Android.bp37
-rw-r--r--ui/metrics/proc/status.go128
-rw-r--r--ui/metrics/proc/status_darwin.go11
-rw-r--r--ui/metrics/proc/status_linux.go46
-rw-r--r--ui/metrics/proc/status_linux_test.go112
5 files changed, 334 insertions, 0 deletions
diff --git a/ui/metrics/proc/Android.bp b/ui/metrics/proc/Android.bp
new file mode 100644
index 000000000..32d821750
--- /dev/null
+++ b/ui/metrics/proc/Android.bp
@@ -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",
+ ],
+ },
+}
diff --git a/ui/metrics/proc/status.go b/ui/metrics/proc/status.go
new file mode 100644
index 000000000..f8b734c02
--- /dev/null
+++ b/ui/metrics/proc/status.go
@@ -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
+}
diff --git a/ui/metrics/proc/status_darwin.go b/ui/metrics/proc/status_darwin.go
new file mode 100644
index 000000000..5c788a5a0
--- /dev/null
+++ b/ui/metrics/proc/status_darwin.go
@@ -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
+}
diff --git a/ui/metrics/proc/status_linux.go b/ui/metrics/proc/status_linux.go
new file mode 100644
index 000000000..dc0f943dd
--- /dev/null
+++ b/ui/metrics/proc/status_linux.go
@@ -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
+}
diff --git a/ui/metrics/proc/status_linux_test.go b/ui/metrics/proc/status_linux_test.go
new file mode 100644
index 000000000..67098502b
--- /dev/null
+++ b/ui/metrics/proc/status_linux_test.go
@@ -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,
+}