Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 175500f0 authored by Patrice Arruda's avatar Patrice Arruda Committed by Gerrit Code Review
Browse files

Merge "Read the proc status file when PID is given for metrics purpose."

parents d9d59fb5 04157e18
Loading
Loading
Loading
Loading
+37 −0
Original line number Diff line number Diff line
// 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 −0
Original line number Diff line number Diff line
// 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 −0
Original line number Diff line number Diff line
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 −0
Original line number Diff line number Diff line
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 −0
Original line number Diff line number Diff line
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,
}