Index: tools/linux/procfs.py |
diff --git a/tools/linux/procfs.py b/tools/linux/procfs.py |
new file mode 100755 |
index 0000000000000000000000000000000000000000..ef19b25ea57eaee63e9c2f2202567b442317d572 |
--- /dev/null |
+++ b/tools/linux/procfs.py |
@@ -0,0 +1,747 @@ |
+#!/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)) |