...

Source file src/github.com/prometheus/procfs/proc_smaps.go

Documentation: github.com/prometheus/procfs

     1  // 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  
    14  //go:build !windows
    15  // +build !windows
    16  
    17  package procfs
    18  
    19  import (
    20  	"bufio"
    21  	"errors"
    22  	"fmt"
    23  	"os"
    24  	"regexp"
    25  	"strconv"
    26  	"strings"
    27  
    28  	"github.com/prometheus/procfs/internal/util"
    29  )
    30  
    31  var (
    32  	// match the header line before each mapped zone in `/proc/pid/smaps`.
    33  	procSMapsHeaderLine = regexp.MustCompile(`^[a-f0-9].*$`)
    34  )
    35  
    36  type ProcSMapsRollup struct {
    37  	// Amount of the mapping that is currently resident in RAM.
    38  	Rss uint64
    39  	// Process's proportional share of this mapping.
    40  	Pss uint64
    41  	// Size in bytes of clean shared pages.
    42  	SharedClean uint64
    43  	// Size in bytes of dirty shared pages.
    44  	SharedDirty uint64
    45  	// Size in bytes of clean private pages.
    46  	PrivateClean uint64
    47  	// Size in bytes of dirty private pages.
    48  	PrivateDirty uint64
    49  	// Amount of memory currently marked as referenced or accessed.
    50  	Referenced uint64
    51  	// Amount of memory that does not belong to any file.
    52  	Anonymous uint64
    53  	// Amount would-be-anonymous memory currently on swap.
    54  	Swap uint64
    55  	// Process's proportional memory on swap.
    56  	SwapPss uint64
    57  }
    58  
    59  // ProcSMapsRollup reads from /proc/[pid]/smaps_rollup to get summed memory information of the
    60  // process.
    61  //
    62  // If smaps_rollup does not exists (require kernel >= 4.15), the content of /proc/pid/smaps will
    63  // we read and summed.
    64  func (p Proc) ProcSMapsRollup() (ProcSMapsRollup, error) {
    65  	data, err := util.ReadFileNoStat(p.path("smaps_rollup"))
    66  	if err != nil && os.IsNotExist(err) {
    67  		return p.procSMapsRollupManual()
    68  	}
    69  	if err != nil {
    70  		return ProcSMapsRollup{}, err
    71  	}
    72  
    73  	lines := strings.Split(string(data), "\n")
    74  	smaps := ProcSMapsRollup{}
    75  
    76  	// skip first line which don't contains information we need
    77  	lines = lines[1:]
    78  	for _, line := range lines {
    79  		if line == "" {
    80  			continue
    81  		}
    82  
    83  		if err := smaps.parseLine(line); err != nil {
    84  			return ProcSMapsRollup{}, err
    85  		}
    86  	}
    87  
    88  	return smaps, nil
    89  }
    90  
    91  // Read /proc/pid/smaps and do the roll-up in Go code.
    92  func (p Proc) procSMapsRollupManual() (ProcSMapsRollup, error) {
    93  	file, err := os.Open(p.path("smaps"))
    94  	if err != nil {
    95  		return ProcSMapsRollup{}, err
    96  	}
    97  	defer file.Close()
    98  
    99  	smaps := ProcSMapsRollup{}
   100  	scan := bufio.NewScanner(file)
   101  
   102  	for scan.Scan() {
   103  		line := scan.Text()
   104  
   105  		if procSMapsHeaderLine.MatchString(line) {
   106  			continue
   107  		}
   108  
   109  		if err := smaps.parseLine(line); err != nil {
   110  			return ProcSMapsRollup{}, err
   111  		}
   112  	}
   113  
   114  	return smaps, nil
   115  }
   116  
   117  func (s *ProcSMapsRollup) parseLine(line string) error {
   118  	kv := strings.SplitN(line, ":", 2)
   119  	if len(kv) != 2 {
   120  		fmt.Println(line)
   121  		return errors.New("invalid net/dev line, missing colon")
   122  	}
   123  
   124  	k := kv[0]
   125  	if k == "VmFlags" {
   126  		return nil
   127  	}
   128  
   129  	v := strings.TrimSpace(kv[1])
   130  	v = strings.TrimRight(v, " kB")
   131  
   132  	vKBytes, err := strconv.ParseUint(v, 10, 64)
   133  	if err != nil {
   134  		return err
   135  	}
   136  	vBytes := vKBytes * 1024
   137  
   138  	s.addValue(k, vBytes)
   139  
   140  	return nil
   141  }
   142  
   143  func (s *ProcSMapsRollup) addValue(k string, vUintBytes uint64) {
   144  	switch k {
   145  	case "Rss":
   146  		s.Rss += vUintBytes
   147  	case "Pss":
   148  		s.Pss += vUintBytes
   149  	case "Shared_Clean":
   150  		s.SharedClean += vUintBytes
   151  	case "Shared_Dirty":
   152  		s.SharedDirty += vUintBytes
   153  	case "Private_Clean":
   154  		s.PrivateClean += vUintBytes
   155  	case "Private_Dirty":
   156  		s.PrivateDirty += vUintBytes
   157  	case "Referenced":
   158  		s.Referenced += vUintBytes
   159  	case "Anonymous":
   160  		s.Anonymous += vUintBytes
   161  	case "Swap":
   162  		s.Swap += vUintBytes
   163  	case "SwapPss":
   164  		s.SwapPss += vUintBytes
   165  	}
   166  }
   167  

View as plain text