summaryrefslogtreecommitdiffstats
path: root/chromium/tools/linux/procfs.py
diff options
context:
space:
mode:
Diffstat (limited to 'chromium/tools/linux/procfs.py')
-rwxr-xr-xchromium/tools/linux/procfs.py729
1 files changed, 0 insertions, 729 deletions
diff --git a/chromium/tools/linux/procfs.py b/chromium/tools/linux/procfs.py
deleted file mode 100755
index 6308fdd93da..00000000000
--- a/chromium/tools/linux/procfs.py
+++ /dev/null
@@ -1,729 +0,0 @@
-#!/usr/bin/env python
-# Copyright 2013 The Chromium Authors. All rights reserved.
-# Use of this source code is governed by a BSD-style license that can be
-# found in the LICENSE file.
-
-# A Python library to read and store procfs (/proc) information on Linux.
-#
-# Each information storage class in this file stores original data as original
-# as reasonablly possible. Translation is done when requested. It is to make it
-# always possible to probe the original data.
-
-
-import collections
-import logging
-import os
-import re
-import struct
-import sys
-
-
-class _NullHandler(logging.Handler):
- def emit(self, record):
- pass
-
-
-_LOGGER = logging.getLogger('procfs')
-_LOGGER.addHandler(_NullHandler())
-
-
-class ProcStat(object):
- """Reads and stores information in /proc/pid/stat."""
- _PATTERN = re.compile(r'^'
- '(?P<PID>-?[0-9]+) '
- '\((?P<COMM>.+)\) '
- '(?P<STATE>[RSDZTW]) '
- '(?P<PPID>-?[0-9]+) '
- '(?P<PGRP>-?[0-9]+) '
- '(?P<SESSION>-?[0-9]+) '
- '(?P<TTY_NR>-?[0-9]+) '
- '(?P<TPGID>-?[0-9]+) '
- '(?P<FLAGS>[0-9]+) '
- '(?P<MINFIT>[0-9]+) '
- '(?P<CMINFIT>[0-9]+) '
- '(?P<MAJFIT>[0-9]+) '
- '(?P<CMAJFIT>[0-9]+) '
- '(?P<UTIME>[0-9]+) '
- '(?P<STIME>[0-9]+) '
- '(?P<CUTIME>[0-9]+) '
- '(?P<CSTIME>[0-9]+) '
- '(?P<PRIORITY>[0-9]+) '
- '(?P<NICE>[0-9]+) '
- '(?P<NUM_THREADS>[0-9]+) '
- '(?P<ITREALVALUE>[0-9]+) '
- '(?P<STARTTIME>[0-9]+) '
- '(?P<VSIZE>[0-9]+) '
- '(?P<RSS>[0-9]+) '
- '(?P<RSSLIM>[0-9]+) '
- '(?P<STARTCODE>[0-9]+) '
- '(?P<ENDCODE>[0-9]+) '
- '(?P<STARTSTACK>[0-9]+) '
- '(?P<KSTKESP>[0-9]+) '
- '(?P<KSTKEIP>[0-9]+) '
- '(?P<SIGNAL>[0-9]+) '
- '(?P<BLOCKED>[0-9]+) '
- '(?P<SIGIGNORE>[0-9]+) '
- '(?P<SIGCATCH>[0-9]+) '
- '(?P<WCHAN>[0-9]+) '
- '(?P<NSWAP>[0-9]+) '
- '(?P<CNSWAP>[0-9]+) '
- '(?P<EXIT_SIGNAL>[0-9]+) '
- '(?P<PROCESSOR>[0-9]+) '
- '(?P<RT_PRIORITY>[0-9]+) '
- '(?P<POLICY>[0-9]+) '
- '(?P<DELAYACCT_BLKIO_TICKS>[0-9]+) '
- '(?P<GUEST_TIME>[0-9]+) '
- '(?P<CGUEST_TIME>[0-9]+)', re.IGNORECASE)
-
- def __init__(self, raw, pid, vsize, rss):
- self._raw = raw
- self._pid = pid
- self._vsize = vsize
- self._rss = rss
-
- @staticmethod
- def load_file(stat_f):
- raw = stat_f.readlines()
- stat = ProcStat._PATTERN.match(raw[0])
- return ProcStat(raw,
- stat.groupdict().get('PID'),
- stat.groupdict().get('VSIZE'),
- stat.groupdict().get('RSS'))
-
- @staticmethod
- def load(pid):
- with open(os.path.join('/proc', str(pid), 'stat'), 'r') as stat_f:
- return ProcStat.load_file(stat_f)
-
- @property
- def raw(self):
- return self._raw
-
- @property
- def pid(self):
- return int(self._pid)
-
- @property
- def vsize(self):
- return int(self._vsize)
-
- @property
- def rss(self):
- return int(self._rss)
-
-
-class ProcStatm(object):
- """Reads and stores information in /proc/pid/statm."""
- _PATTERN = re.compile(r'^'
- '(?P<SIZE>[0-9]+) '
- '(?P<RESIDENT>[0-9]+) '
- '(?P<SHARE>[0-9]+) '
- '(?P<TEXT>[0-9]+) '
- '(?P<LIB>[0-9]+) '
- '(?P<DATA>[0-9]+) '
- '(?P<DT>[0-9]+)', re.IGNORECASE)
-
- def __init__(self, raw, size, resident, share, text, lib, data, dt):
- self._raw = raw
- self._size = size
- self._resident = resident
- self._share = share
- self._text = text
- self._lib = lib
- self._data = data
- self._dt = dt
-
- @staticmethod
- def load_file(statm_f):
- raw = statm_f.readlines()
- statm = ProcStatm._PATTERN.match(raw[0])
- return ProcStatm(raw,
- statm.groupdict().get('SIZE'),
- statm.groupdict().get('RESIDENT'),
- statm.groupdict().get('SHARE'),
- statm.groupdict().get('TEXT'),
- statm.groupdict().get('LIB'),
- statm.groupdict().get('DATA'),
- statm.groupdict().get('DT'))
-
- @staticmethod
- def load(pid):
- with open(os.path.join('/proc', str(pid), 'statm'), 'r') as statm_f:
- return ProcStatm.load_file(statm_f)
-
- @property
- def raw(self):
- return self._raw
-
- @property
- def size(self):
- return int(self._size)
-
- @property
- def resident(self):
- return int(self._resident)
-
- @property
- def share(self):
- return int(self._share)
-
- @property
- def text(self):
- return int(self._text)
-
- @property
- def lib(self):
- return int(self._lib)
-
- @property
- def data(self):
- return int(self._data)
-
- @property
- def dt(self):
- return int(self._dt)
-
-
-class ProcStatus(object):
- """Reads and stores information in /proc/pid/status."""
- _PATTERN = re.compile(r'^(?P<NAME>[A-Za-z0-9_]+):\s+(?P<VALUE>.*)')
-
- def __init__(self, raw, dct):
- self._raw = raw
- self._pid = dct.get('Pid')
- self._name = dct.get('Name')
- self._vm_peak = dct.get('VmPeak')
- self._vm_size = dct.get('VmSize')
- self._vm_lck = dct.get('VmLck')
- self._vm_pin = dct.get('VmPin')
- self._vm_hwm = dct.get('VmHWM')
- self._vm_rss = dct.get('VmRSS')
- self._vm_data = dct.get('VmData')
- self._vm_stack = dct.get('VmStk')
- self._vm_exe = dct.get('VmExe')
- self._vm_lib = dct.get('VmLib')
- self._vm_pte = dct.get('VmPTE')
- self._vm_swap = dct.get('VmSwap')
-
- @staticmethod
- def load_file(status_f):
- raw = status_f.readlines()
- dct = {}
- for line in raw:
- status_match = ProcStatus._PATTERN.match(line)
- if status_match:
- match_dict = status_match.groupdict()
- dct[match_dict['NAME']] = match_dict['VALUE']
- else:
- raise SyntaxError('Unknown /proc/pid/status format.')
- return ProcStatus(raw, dct)
-
- @staticmethod
- def load(pid):
- with open(os.path.join('/proc', str(pid), 'status'), 'r') as status_f:
- return ProcStatus.load_file(status_f)
-
- @property
- def raw(self):
- return self._raw
-
- @property
- def pid(self):
- return int(self._pid)
-
- @property
- def vm_peak(self):
- """Returns a high-water (peak) virtual memory size in kilo-bytes."""
- if self._vm_peak.endswith('kB'):
- return int(self._vm_peak.split()[0])
- raise ValueError('VmPeak is not in kB.')
-
- @property
- def vm_size(self):
- """Returns a virtual memory size in kilo-bytes."""
- if self._vm_size.endswith('kB'):
- return int(self._vm_size.split()[0])
- raise ValueError('VmSize is not in kB.')
-
- @property
- def vm_hwm(self):
- """Returns a high-water (peak) resident set size (RSS) in kilo-bytes."""
- if self._vm_hwm.endswith('kB'):
- return int(self._vm_hwm.split()[0])
- raise ValueError('VmHWM is not in kB.')
-
- @property
- def vm_rss(self):
- """Returns a resident set size (RSS) in kilo-bytes."""
- if self._vm_rss.endswith('kB'):
- return int(self._vm_rss.split()[0])
- raise ValueError('VmRSS is not in kB.')
-
-
-class ProcMapsEntry(object):
- """A class representing one line in /proc/pid/maps."""
-
- def __init__(
- self, begin, end, readable, writable, executable, private, offset,
- major, minor, inode, name):
- self.begin = begin
- self.end = end
- self.readable = readable
- self.writable = writable
- self.executable = executable
- self.private = private
- self.offset = offset
- self.major = major
- self.minor = minor
- self.inode = inode
- self.name = name
-
- def as_dict(self):
- return {
- 'begin': self.begin,
- 'end': self.end,
- 'readable': self.readable,
- 'writable': self.writable,
- 'executable': self.executable,
- 'private': self.private,
- 'offset': self.offset,
- 'major': self.major,
- 'minor': self.minor,
- 'inode': self.inode,
- 'name': self.name,
- }
-
-
-class ProcMaps(object):
- """Reads and stores information in /proc/pid/maps."""
-
- MAPS_PATTERN = re.compile(
- r'^([a-f0-9]+)-([a-f0-9]+)\s+(.)(.)(.)(.)\s+([a-f0-9]+)\s+(\S+):(\S+)\s+'
- r'(\d+)\s*(.*)$', re.IGNORECASE)
-
- def __init__(self):
- self._sorted_indexes = []
- self._dictionary = {}
- self._sorted = True
-
- def iter(self, condition):
- if not self._sorted:
- self._sorted_indexes.sort()
- self._sorted = True
- for index in self._sorted_indexes:
- if not condition or condition(self._dictionary[index]):
- yield self._dictionary[index]
-
- def __iter__(self):
- if not self._sorted:
- self._sorted_indexes.sort()
- self._sorted = True
- for index in self._sorted_indexes:
- yield self._dictionary[index]
-
- @staticmethod
- def load_file(maps_f):
- table = ProcMaps()
- for line in maps_f:
- table.append_line(line)
- return table
-
- @staticmethod
- def load(pid):
- with open(os.path.join('/proc', str(pid), 'maps'), 'r') as maps_f:
- return ProcMaps.load_file(maps_f)
-
- def append_line(self, line):
- entry = self.parse_line(line)
- if entry:
- self._append_entry(entry)
- return entry
-
- @staticmethod
- def parse_line(line):
- matched = ProcMaps.MAPS_PATTERN.match(line)
- if matched:
- return ProcMapsEntry( # pylint: disable=W0212
- int(matched.group(1), 16), # begin
- int(matched.group(2), 16), # end
- matched.group(3), # readable
- matched.group(4), # writable
- matched.group(5), # executable
- matched.group(6), # private
- int(matched.group(7), 16), # offset
- matched.group(8), # major
- matched.group(9), # minor
- int(matched.group(10), 10), # inode
- matched.group(11) # name
- )
- else:
- return None
-
- @staticmethod
- def constants(entry):
- return (entry.writable == '-' and entry.executable == '-' and re.match(
- '\S+(\.(so|dll|dylib|bundle)|chrome)((\.\d+)+\w*(\.\d+){0,3})?',
- entry.name))
-
- @staticmethod
- def executable(entry):
- return (entry.executable == 'x' and re.match(
- '\S+(\.(so|dll|dylib|bundle)|chrome)((\.\d+)+\w*(\.\d+){0,3})?',
- entry.name))
-
- @staticmethod
- def executable_and_constants(entry):
- return (((entry.writable == '-' and entry.executable == '-') or
- entry.executable == 'x') and re.match(
- '\S+(\.(so|dll|dylib|bundle)|chrome)((\.\d+)+\w*(\.\d+){0,3})?',
- entry.name))
-
- def _append_entry(self, entry):
- if self._sorted_indexes and self._sorted_indexes[-1] > entry.begin:
- self._sorted = False
- self._sorted_indexes.append(entry.begin)
- self._dictionary[entry.begin] = entry
-
-
-class ProcSmaps(object):
- """Reads and stores information in /proc/pid/smaps."""
- _SMAPS_PATTERN = re.compile(r'^(?P<NAME>[A-Za-z0-9_]+):\s+(?P<VALUE>.*)')
-
- class VMA(object):
- def __init__(self):
- self._size = 0
- self._rss = 0
- self._pss = 0
-
- def append(self, name, value):
- dct = {
- 'Size': '_size',
- 'Rss': '_rss',
- 'Pss': '_pss',
- 'Referenced': '_referenced',
- 'Private_Clean': '_private_clean',
- 'Shared_Clean': '_shared_clean',
- 'KernelPageSize': '_kernel_page_size',
- 'MMUPageSize': '_mmu_page_size',
- }
- if name in dct:
- self.__setattr__(dct[name], value)
-
- @property
- def size(self):
- if self._size.endswith('kB'):
- return int(self._size.split()[0])
- return int(self._size)
-
- @property
- def rss(self):
- if self._rss.endswith('kB'):
- return int(self._rss.split()[0])
- return int(self._rss)
-
- @property
- def pss(self):
- if self._pss.endswith('kB'):
- return int(self._pss.split()[0])
- return int(self._pss)
-
- def __init__(self, raw, total_dct, maps, vma_internals):
- self._raw = raw
- self._size = total_dct['Size']
- self._rss = total_dct['Rss']
- self._pss = total_dct['Pss']
- self._referenced = total_dct['Referenced']
- self._shared_clean = total_dct['Shared_Clean']
- self._private_clean = total_dct['Private_Clean']
- self._kernel_page_size = total_dct['KernelPageSize']
- self._mmu_page_size = total_dct['MMUPageSize']
- self._maps = maps
- self._vma_internals = vma_internals
-
- @staticmethod
- def load(pid):
- with open(os.path.join('/proc', str(pid), 'smaps'), 'r') as smaps_f:
- raw = smaps_f.readlines()
-
- vma = None
- vma_internals = collections.OrderedDict()
- total_dct = collections.defaultdict(int)
- maps = ProcMaps()
- for line in raw:
- maps_match = ProcMaps.MAPS_PATTERN.match(line)
- if maps_match:
- vma = maps.append_line(line.strip())
- vma_internals[vma] = ProcSmaps.VMA()
- else:
- smaps_match = ProcSmaps._SMAPS_PATTERN.match(line)
- if smaps_match:
- match_dict = smaps_match.groupdict()
- vma_internals[vma].append(match_dict['NAME'], match_dict['VALUE'])
- total_dct[match_dict['NAME']] += int(match_dict['VALUE'].split()[0])
-
- return ProcSmaps(raw, total_dct, maps, vma_internals)
-
- @property
- def size(self):
- return self._size
-
- @property
- def rss(self):
- return self._rss
-
- @property
- def referenced(self):
- return self._referenced
-
- @property
- def pss(self):
- return self._pss
-
- @property
- def private_clean(self):
- return self._private_clean
-
- @property
- def shared_clean(self):
- return self._shared_clean
-
- @property
- def kernel_page_size(self):
- return self._kernel_page_size
-
- @property
- def mmu_page_size(self):
- return self._mmu_page_size
-
- @property
- def vma_internals(self):
- return self._vma_internals
-
-
-class ProcPagemap(object):
- """Reads and stores partial information in /proc/pid/pagemap.
-
- It picks up virtual addresses to read based on ProcMaps (/proc/pid/maps).
- See https://www.kernel.org/doc/Documentation/vm/pagemap.txt for details.
- """
- _BYTES_PER_PAGEMAP_VALUE = 8
- _BYTES_PER_OS_PAGE = 4096
- _VIRTUAL_TO_PAGEMAP_OFFSET = _BYTES_PER_OS_PAGE / _BYTES_PER_PAGEMAP_VALUE
-
- _MASK_PRESENT = 1 << 63
- _MASK_SWAPPED = 1 << 62
- _MASK_FILEPAGE_OR_SHAREDANON = 1 << 61
- _MASK_SOFTDIRTY = 1 << 55
- _MASK_PFN = (1 << 55) - 1
-
- class VMA(object):
- def __init__(self, vsize, present, swapped, pageframes):
- self._vsize = vsize
- self._present = present
- self._swapped = swapped
- self._pageframes = pageframes
-
- @property
- def vsize(self):
- return int(self._vsize)
-
- @property
- def present(self):
- return int(self._present)
-
- @property
- def swapped(self):
- return int(self._swapped)
-
- @property
- def pageframes(self):
- return self._pageframes
-
- def __init__(self, vsize, present, swapped, vma_internals, in_process_dup):
- self._vsize = vsize
- self._present = present
- self._swapped = swapped
- self._vma_internals = vma_internals
- self._in_process_dup = in_process_dup
-
- @staticmethod
- def load(pid, maps):
- total_present = 0
- total_swapped = 0
- total_vsize = 0
- in_process_dup = 0
- vma_internals = collections.OrderedDict()
- process_pageframe_set = set()
-
- pagemap_fd = os.open(
- os.path.join('/proc', str(pid), 'pagemap'), os.O_RDONLY)
- for vma in maps:
- present = 0
- swapped = 0
- vsize = 0
- pageframes = collections.defaultdict(int)
- begin_offset = ProcPagemap._offset(vma.begin)
- chunk_size = ProcPagemap._offset(vma.end) - begin_offset
- os.lseek(pagemap_fd, begin_offset, os.SEEK_SET)
- buf = os.read(pagemap_fd, chunk_size)
- if len(buf) < chunk_size:
- _LOGGER.warn('Failed to read pagemap at 0x%x in %d.' % (vma.begin, pid))
- pagemap_values = struct.unpack(
- '=%dQ' % (len(buf) / ProcPagemap._BYTES_PER_PAGEMAP_VALUE), buf)
- for pagemap_value in pagemap_values:
- vsize += ProcPagemap._BYTES_PER_OS_PAGE
- if pagemap_value & ProcPagemap._MASK_PRESENT:
- if (pagemap_value & ProcPagemap._MASK_PFN) in process_pageframe_set:
- in_process_dup += ProcPagemap._BYTES_PER_OS_PAGE
- else:
- process_pageframe_set.add(pagemap_value & ProcPagemap._MASK_PFN)
- if (pagemap_value & ProcPagemap._MASK_PFN) not in pageframes:
- present += ProcPagemap._BYTES_PER_OS_PAGE
- pageframes[pagemap_value & ProcPagemap._MASK_PFN] += 1
- if pagemap_value & ProcPagemap._MASK_SWAPPED:
- swapped += ProcPagemap._BYTES_PER_OS_PAGE
- vma_internals[vma] = ProcPagemap.VMA(vsize, present, swapped, pageframes)
- total_present += present
- total_swapped += swapped
- total_vsize += vsize
- os.close(pagemap_fd)
-
- return ProcPagemap(total_vsize, total_present, total_swapped,
- vma_internals, in_process_dup)
-
- @staticmethod
- def _offset(virtual_address):
- return virtual_address / ProcPagemap._VIRTUAL_TO_PAGEMAP_OFFSET
-
- @property
- def vsize(self):
- return int(self._vsize)
-
- @property
- def present(self):
- return int(self._present)
-
- @property
- def swapped(self):
- return int(self._swapped)
-
- @property
- def vma_internals(self):
- return self._vma_internals
-
-
-class _ProcessMemory(object):
- """Aggregates process memory information from /proc for manual testing."""
- def __init__(self, pid):
- self._pid = pid
- self._maps = None
- self._pagemap = None
- self._stat = None
- self._status = None
- self._statm = None
- self._smaps = []
-
- def _read(self, proc_file):
- lines = []
- with open(os.path.join('/proc', str(self._pid), proc_file), 'r') as proc_f:
- lines = proc_f.readlines()
- return lines
-
- def read_all(self):
- self.read_stat()
- self.read_statm()
- self.read_status()
- self.read_smaps()
- self.read_maps()
- self.read_pagemap(self._maps)
-
- def read_maps(self):
- self._maps = ProcMaps.load(self._pid)
-
- def read_pagemap(self, maps):
- self._pagemap = ProcPagemap.load(self._pid, maps)
-
- def read_smaps(self):
- self._smaps = ProcSmaps.load(self._pid)
-
- def read_stat(self):
- self._stat = ProcStat.load(self._pid)
-
- def read_statm(self):
- self._statm = ProcStatm.load(self._pid)
-
- def read_status(self):
- self._status = ProcStatus.load(self._pid)
-
- @property
- def pid(self):
- return self._pid
-
- @property
- def maps(self):
- return self._maps
-
- @property
- def pagemap(self):
- return self._pagemap
-
- @property
- def smaps(self):
- return self._smaps
-
- @property
- def stat(self):
- return self._stat
-
- @property
- def statm(self):
- return self._statm
-
- @property
- def status(self):
- return self._status
-
-
-def main(argv):
- """The main function for manual testing."""
- _LOGGER.setLevel(logging.WARNING)
- handler = logging.StreamHandler()
- handler.setLevel(logging.WARNING)
- handler.setFormatter(logging.Formatter(
- '%(asctime)s:%(name)s:%(levelname)s:%(message)s'))
- _LOGGER.addHandler(handler)
-
- pids = []
- for arg in argv[1:]:
- try:
- pid = int(arg)
- except ValueError:
- raise SyntaxError("%s is not an integer." % arg)
- else:
- pids.append(pid)
-
- procs = {}
- for pid in pids:
- procs[pid] = _ProcessMemory(pid)
- procs[pid].read_all()
-
- print '=== PID: %d ===' % pid
-
- print ' stat: %d' % procs[pid].stat.vsize
- print ' statm: %d' % (procs[pid].statm.size * 4096)
- print ' status: %d (Peak:%d)' % (procs[pid].status.vm_size * 1024,
- procs[pid].status.vm_peak * 1024)
- print ' smaps: %d' % (procs[pid].smaps.size * 1024)
- print 'pagemap: %d' % procs[pid].pagemap.vsize
- print ' stat: %d' % (procs[pid].stat.rss * 4096)
- print ' statm: %d' % (procs[pid].statm.resident * 4096)
- print ' status: %d (Peak:%d)' % (procs[pid].status.vm_rss * 1024,
- procs[pid].status.vm_hwm * 1024)
- print ' smaps: %d' % (procs[pid].smaps.rss * 1024)
- print 'pagemap: %d' % procs[pid].pagemap.present
-
- return 0
-
-
-if __name__ == '__main__':
- sys.exit(main(sys.argv))