|  | #!/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): | 
|  | try: | 
|  | with open(os.path.join('/proc', str(pid), 'stat'), 'r') as stat_f: | 
|  | return ProcStat.load_file(stat_f) | 
|  | except IOError: | 
|  | return None | 
|  |  | 
|  | @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): | 
|  | try: | 
|  | raw = statm_f.readlines() | 
|  | except (IOError, OSError): | 
|  | return None | 
|  | 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): | 
|  | try: | 
|  | with open(os.path.join('/proc', str(pid), 'statm'), 'r') as statm_f: | 
|  | return ProcStatm.load_file(statm_f) | 
|  | except (IOError, OSError): | 
|  | return None | 
|  |  | 
|  | @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) | 
|  |  | 
|  | EXECUTABLE_PATTERN = re.compile( | 
|  | r'\S+\.(so|dll|dylib|bundle)((\.\d+)+\w*(\.\d+){0,3})?') | 
|  |  | 
|  | 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): | 
|  | try: | 
|  | with open(os.path.join('/proc', str(pid), 'maps'), 'r') as maps_f: | 
|  | return ProcMaps.load_file(maps_f) | 
|  | except (IOError, OSError): | 
|  | return None | 
|  |  | 
|  | 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 == '-' | 
|  |  | 
|  | @staticmethod | 
|  | def executable(entry): | 
|  | return entry.executable == 'x' | 
|  |  | 
|  | @staticmethod | 
|  | def executable_and_constants(entry): | 
|  | return ((entry.writable == '-' and entry.executable == '-') or | 
|  | entry.executable == 'x') | 
|  |  | 
|  | 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() | 
|  |  | 
|  | try: | 
|  | pagemap_fd = os.open( | 
|  | os.path.join('/proc', str(pid), 'pagemap'), os.O_RDONLY) | 
|  | except (IOError, OSError): | 
|  | return None | 
|  | 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 | 
|  | try: | 
|  | os.lseek(pagemap_fd, begin_offset, os.SEEK_SET) | 
|  | buf = os.read(pagemap_fd, chunk_size) | 
|  | except (IOError, OSError): | 
|  | return None | 
|  | 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 | 
|  | try: | 
|  | os.close(pagemap_fd) | 
|  | except OSError: | 
|  | return None | 
|  |  | 
|  | 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)) |