Home | History | Annotate | Download | only in linux
      1 #!/usr/bin/env python
      2 # Copyright 2013 The Chromium Authors. All rights reserved.
      3 # Use of this source code is governed by a BSD-style license that can be
      4 # found in the LICENSE file.
      5 
      6 # A Python library to read and store procfs (/proc) information on Linux.
      7 #
      8 # Each information storage class in this file stores original data as original
      9 # as reasonablly possible. Translation is done when requested. It is to make it
     10 # always possible to probe the original data.
     11 
     12 
     13 import collections
     14 import logging
     15 import os
     16 import re
     17 import struct
     18 import sys
     19 
     20 
     21 class _NullHandler(logging.Handler):
     22   def emit(self, record):
     23     pass
     24 
     25 
     26 _LOGGER = logging.getLogger('procfs')
     27 _LOGGER.addHandler(_NullHandler())
     28 
     29 
     30 class ProcStat(object):
     31   """Reads and stores information in /proc/pid/stat."""
     32   _PATTERN = re.compile(r'^'
     33                         '(?P<PID>-?[0-9]+) '
     34                         '\((?P<COMM>.+)\) '
     35                         '(?P<STATE>[RSDZTW]) '
     36                         '(?P<PPID>-?[0-9]+) '
     37                         '(?P<PGRP>-?[0-9]+) '
     38                         '(?P<SESSION>-?[0-9]+) '
     39                         '(?P<TTY_NR>-?[0-9]+) '
     40                         '(?P<TPGID>-?[0-9]+) '
     41                         '(?P<FLAGS>[0-9]+) '
     42                         '(?P<MINFIT>[0-9]+) '
     43                         '(?P<CMINFIT>[0-9]+) '
     44                         '(?P<MAJFIT>[0-9]+) '
     45                         '(?P<CMAJFIT>[0-9]+) '
     46                         '(?P<UTIME>[0-9]+) '
     47                         '(?P<STIME>[0-9]+) '
     48                         '(?P<CUTIME>[0-9]+) '
     49                         '(?P<CSTIME>[0-9]+) '
     50                         '(?P<PRIORITY>[0-9]+) '
     51                         '(?P<NICE>[0-9]+) '
     52                         '(?P<NUM_THREADS>[0-9]+) '
     53                         '(?P<ITREALVALUE>[0-9]+) '
     54                         '(?P<STARTTIME>[0-9]+) '
     55                         '(?P<VSIZE>[0-9]+) '
     56                         '(?P<RSS>[0-9]+) '
     57                         '(?P<RSSLIM>[0-9]+) '
     58                         '(?P<STARTCODE>[0-9]+) '
     59                         '(?P<ENDCODE>[0-9]+) '
     60                         '(?P<STARTSTACK>[0-9]+) '
     61                         '(?P<KSTKESP>[0-9]+) '
     62                         '(?P<KSTKEIP>[0-9]+) '
     63                         '(?P<SIGNAL>[0-9]+) '
     64                         '(?P<BLOCKED>[0-9]+) '
     65                         '(?P<SIGIGNORE>[0-9]+) '
     66                         '(?P<SIGCATCH>[0-9]+) '
     67                         '(?P<WCHAN>[0-9]+) '
     68                         '(?P<NSWAP>[0-9]+) '
     69                         '(?P<CNSWAP>[0-9]+) '
     70                         '(?P<EXIT_SIGNAL>[0-9]+) '
     71                         '(?P<PROCESSOR>[0-9]+) '
     72                         '(?P<RT_PRIORITY>[0-9]+) '
     73                         '(?P<POLICY>[0-9]+) '
     74                         '(?P<DELAYACCT_BLKIO_TICKS>[0-9]+) '
     75                         '(?P<GUEST_TIME>[0-9]+) '
     76                         '(?P<CGUEST_TIME>[0-9]+)', re.IGNORECASE)
     77 
     78   def __init__(self, raw, pid, vsize, rss):
     79     self._raw = raw
     80     self._pid = pid
     81     self._vsize = vsize
     82     self._rss = rss
     83 
     84   @staticmethod
     85   def load_file(stat_f):
     86     raw = stat_f.readlines()
     87     stat = ProcStat._PATTERN.match(raw[0])
     88     return ProcStat(raw,
     89                     stat.groupdict().get('PID'),
     90                     stat.groupdict().get('VSIZE'),
     91                     stat.groupdict().get('RSS'))
     92 
     93   @staticmethod
     94   def load(pid):
     95     try:
     96       with open(os.path.join('/proc', str(pid), 'stat'), 'r') as stat_f:
     97         return ProcStat.load_file(stat_f)
     98     except IOError:
     99       return None
    100 
    101   @property
    102   def raw(self):
    103     return self._raw
    104 
    105   @property
    106   def pid(self):
    107     return int(self._pid)
    108 
    109   @property
    110   def vsize(self):
    111     return int(self._vsize)
    112 
    113   @property
    114   def rss(self):
    115     return int(self._rss)
    116 
    117 
    118 class ProcStatm(object):
    119   """Reads and stores information in /proc/pid/statm."""
    120   _PATTERN = re.compile(r'^'
    121                         '(?P<SIZE>[0-9]+) '
    122                         '(?P<RESIDENT>[0-9]+) '
    123                         '(?P<SHARE>[0-9]+) '
    124                         '(?P<TEXT>[0-9]+) '
    125                         '(?P<LIB>[0-9]+) '
    126                         '(?P<DATA>[0-9]+) '
    127                         '(?P<DT>[0-9]+)', re.IGNORECASE)
    128 
    129   def __init__(self, raw, size, resident, share, text, lib, data, dt):
    130     self._raw = raw
    131     self._size = size
    132     self._resident = resident
    133     self._share = share
    134     self._text = text
    135     self._lib = lib
    136     self._data = data
    137     self._dt = dt
    138 
    139   @staticmethod
    140   def load_file(statm_f):
    141     try:
    142       raw = statm_f.readlines()
    143     except (IOError, OSError):
    144       return None
    145     statm = ProcStatm._PATTERN.match(raw[0])
    146     return ProcStatm(raw,
    147                      statm.groupdict().get('SIZE'),
    148                      statm.groupdict().get('RESIDENT'),
    149                      statm.groupdict().get('SHARE'),
    150                      statm.groupdict().get('TEXT'),
    151                      statm.groupdict().get('LIB'),
    152                      statm.groupdict().get('DATA'),
    153                      statm.groupdict().get('DT'))
    154 
    155   @staticmethod
    156   def load(pid):
    157     try:
    158       with open(os.path.join('/proc', str(pid), 'statm'), 'r') as statm_f:
    159         return ProcStatm.load_file(statm_f)
    160     except (IOError, OSError):
    161       return None
    162 
    163   @property
    164   def raw(self):
    165     return self._raw
    166 
    167   @property
    168   def size(self):
    169     return int(self._size)
    170 
    171   @property
    172   def resident(self):
    173     return int(self._resident)
    174 
    175   @property
    176   def share(self):
    177     return int(self._share)
    178 
    179   @property
    180   def text(self):
    181     return int(self._text)
    182 
    183   @property
    184   def lib(self):
    185     return int(self._lib)
    186 
    187   @property
    188   def data(self):
    189     return int(self._data)
    190 
    191   @property
    192   def dt(self):
    193     return int(self._dt)
    194 
    195 
    196 class ProcStatus(object):
    197   """Reads and stores information in /proc/pid/status."""
    198   _PATTERN = re.compile(r'^(?P<NAME>[A-Za-z0-9_]+):\s+(?P<VALUE>.*)')
    199 
    200   def __init__(self, raw, dct):
    201     self._raw = raw
    202     self._pid = dct.get('Pid')
    203     self._name = dct.get('Name')
    204     self._vm_peak = dct.get('VmPeak')
    205     self._vm_size = dct.get('VmSize')
    206     self._vm_lck = dct.get('VmLck')
    207     self._vm_pin = dct.get('VmPin')
    208     self._vm_hwm = dct.get('VmHWM')
    209     self._vm_rss = dct.get('VmRSS')
    210     self._vm_data = dct.get('VmData')
    211     self._vm_stack = dct.get('VmStk')
    212     self._vm_exe = dct.get('VmExe')
    213     self._vm_lib = dct.get('VmLib')
    214     self._vm_pte = dct.get('VmPTE')
    215     self._vm_swap = dct.get('VmSwap')
    216 
    217   @staticmethod
    218   def load_file(status_f):
    219     raw = status_f.readlines()
    220     dct = {}
    221     for line in raw:
    222       status_match = ProcStatus._PATTERN.match(line)
    223       if status_match:
    224         match_dict = status_match.groupdict()
    225         dct[match_dict['NAME']] = match_dict['VALUE']
    226       else:
    227         raise SyntaxError('Unknown /proc/pid/status format.')
    228     return ProcStatus(raw, dct)
    229 
    230   @staticmethod
    231   def load(pid):
    232     with open(os.path.join('/proc', str(pid), 'status'), 'r') as status_f:
    233       return ProcStatus.load_file(status_f)
    234 
    235   @property
    236   def raw(self):
    237     return self._raw
    238 
    239   @property
    240   def pid(self):
    241     return int(self._pid)
    242 
    243   @property
    244   def vm_peak(self):
    245     """Returns a high-water (peak) virtual memory size in kilo-bytes."""
    246     if self._vm_peak.endswith('kB'):
    247       return int(self._vm_peak.split()[0])
    248     raise ValueError('VmPeak is not in kB.')
    249 
    250   @property
    251   def vm_size(self):
    252     """Returns a virtual memory size in kilo-bytes."""
    253     if self._vm_size.endswith('kB'):
    254       return int(self._vm_size.split()[0])
    255     raise ValueError('VmSize is not in kB.')
    256 
    257   @property
    258   def vm_hwm(self):
    259     """Returns a high-water (peak) resident set size (RSS) in kilo-bytes."""
    260     if self._vm_hwm.endswith('kB'):
    261       return int(self._vm_hwm.split()[0])
    262     raise ValueError('VmHWM is not in kB.')
    263 
    264   @property
    265   def vm_rss(self):
    266     """Returns a resident set size (RSS) in kilo-bytes."""
    267     if self._vm_rss.endswith('kB'):
    268       return int(self._vm_rss.split()[0])
    269     raise ValueError('VmRSS is not in kB.')
    270 
    271 
    272 class ProcMapsEntry(object):
    273   """A class representing one line in /proc/pid/maps."""
    274 
    275   def __init__(
    276       self, begin, end, readable, writable, executable, private, offset,
    277       major, minor, inode, name):
    278     self.begin = begin
    279     self.end = end
    280     self.readable = readable
    281     self.writable = writable
    282     self.executable = executable
    283     self.private = private
    284     self.offset = offset
    285     self.major = major
    286     self.minor = minor
    287     self.inode = inode
    288     self.name = name
    289 
    290   def as_dict(self):
    291     return {
    292         'begin': self.begin,
    293         'end': self.end,
    294         'readable': self.readable,
    295         'writable': self.writable,
    296         'executable': self.executable,
    297         'private': self.private,
    298         'offset': self.offset,
    299         'major': self.major,
    300         'minor': self.minor,
    301         'inode': self.inode,
    302         'name': self.name,
    303     }
    304 
    305 
    306 class ProcMaps(object):
    307   """Reads and stores information in /proc/pid/maps."""
    308 
    309   MAPS_PATTERN = re.compile(
    310       r'^([a-f0-9]+)-([a-f0-9]+)\s+(.)(.)(.)(.)\s+([a-f0-9]+)\s+(\S+):(\S+)\s+'
    311       r'(\d+)\s*(.*)$', re.IGNORECASE)
    312 
    313   EXECUTABLE_PATTERN = re.compile(
    314       r'\S+\.(so|dll|dylib|bundle)((\.\d+)+\w*(\.\d+){0,3})?')
    315 
    316   def __init__(self):
    317     self._sorted_indexes = []
    318     self._dictionary = {}
    319     self._sorted = True
    320 
    321   def iter(self, condition):
    322     if not self._sorted:
    323       self._sorted_indexes.sort()
    324       self._sorted = True
    325     for index in self._sorted_indexes:
    326       if not condition or condition(self._dictionary[index]):
    327         yield self._dictionary[index]
    328 
    329   def __iter__(self):
    330     if not self._sorted:
    331       self._sorted_indexes.sort()
    332       self._sorted = True
    333     for index in self._sorted_indexes:
    334       yield self._dictionary[index]
    335 
    336   @staticmethod
    337   def load_file(maps_f):
    338     table = ProcMaps()
    339     for line in maps_f:
    340       table.append_line(line)
    341     return table
    342 
    343   @staticmethod
    344   def load(pid):
    345     try:
    346       with open(os.path.join('/proc', str(pid), 'maps'), 'r') as maps_f:
    347         return ProcMaps.load_file(maps_f)
    348     except (IOError, OSError):
    349       return None
    350 
    351   def append_line(self, line):
    352     entry = self.parse_line(line)
    353     if entry:
    354       self._append_entry(entry)
    355     return entry
    356 
    357   @staticmethod
    358   def parse_line(line):
    359     matched = ProcMaps.MAPS_PATTERN.match(line)
    360     if matched:
    361       return ProcMapsEntry(  # pylint: disable=W0212
    362           int(matched.group(1), 16),  # begin
    363           int(matched.group(2), 16),  # end
    364           matched.group(3),           # readable
    365           matched.group(4),           # writable
    366           matched.group(5),           # executable
    367           matched.group(6),           # private
    368           int(matched.group(7), 16),  # offset
    369           matched.group(8),           # major
    370           matched.group(9),           # minor
    371           int(matched.group(10), 10), # inode
    372           matched.group(11)           # name
    373           )
    374     else:
    375       return None
    376 
    377   @staticmethod
    378   def constants(entry):
    379     return entry.writable == '-' and entry.executable == '-'
    380 
    381   @staticmethod
    382   def executable(entry):
    383     return entry.executable == 'x'
    384 
    385   @staticmethod
    386   def executable_and_constants(entry):
    387     return ((entry.writable == '-' and entry.executable == '-') or
    388             entry.executable == 'x')
    389 
    390   def _append_entry(self, entry):
    391     if self._sorted_indexes and self._sorted_indexes[-1] > entry.begin:
    392       self._sorted = False
    393     self._sorted_indexes.append(entry.begin)
    394     self._dictionary[entry.begin] = entry
    395 
    396 
    397 class ProcSmaps(object):
    398   """Reads and stores information in /proc/pid/smaps."""
    399   _SMAPS_PATTERN = re.compile(r'^(?P<NAME>[A-Za-z0-9_]+):\s+(?P<VALUE>.*)')
    400 
    401   class VMA(object):
    402     def __init__(self):
    403       self._size = 0
    404       self._rss = 0
    405       self._pss = 0
    406 
    407     def append(self, name, value):
    408       dct = {
    409         'Size': '_size',
    410         'Rss': '_rss',
    411         'Pss': '_pss',
    412         'Referenced': '_referenced',
    413         'Private_Clean': '_private_clean',
    414         'Shared_Clean': '_shared_clean',
    415         'KernelPageSize': '_kernel_page_size',
    416         'MMUPageSize': '_mmu_page_size',
    417         }
    418       if name in dct:
    419         self.__setattr__(dct[name], value)
    420 
    421     @property
    422     def size(self):
    423       if self._size.endswith('kB'):
    424         return int(self._size.split()[0])
    425       return int(self._size)
    426 
    427     @property
    428     def rss(self):
    429       if self._rss.endswith('kB'):
    430         return int(self._rss.split()[0])
    431       return int(self._rss)
    432 
    433     @property
    434     def pss(self):
    435       if self._pss.endswith('kB'):
    436         return int(self._pss.split()[0])
    437       return int(self._pss)
    438 
    439   def __init__(self, raw, total_dct, maps, vma_internals):
    440     self._raw = raw
    441     self._size = total_dct['Size']
    442     self._rss = total_dct['Rss']
    443     self._pss = total_dct['Pss']
    444     self._referenced = total_dct['Referenced']
    445     self._shared_clean = total_dct['Shared_Clean']
    446     self._private_clean = total_dct['Private_Clean']
    447     self._kernel_page_size = total_dct['KernelPageSize']
    448     self._mmu_page_size = total_dct['MMUPageSize']
    449     self._maps = maps
    450     self._vma_internals = vma_internals
    451 
    452   @staticmethod
    453   def load(pid):
    454     with open(os.path.join('/proc', str(pid), 'smaps'), 'r') as smaps_f:
    455       raw = smaps_f.readlines()
    456 
    457     vma = None
    458     vma_internals = collections.OrderedDict()
    459     total_dct = collections.defaultdict(int)
    460     maps = ProcMaps()
    461     for line in raw:
    462       maps_match = ProcMaps.MAPS_PATTERN.match(line)
    463       if maps_match:
    464         vma = maps.append_line(line.strip())
    465         vma_internals[vma] = ProcSmaps.VMA()
    466       else:
    467         smaps_match = ProcSmaps._SMAPS_PATTERN.match(line)
    468         if smaps_match:
    469           match_dict = smaps_match.groupdict()
    470           vma_internals[vma].append(match_dict['NAME'], match_dict['VALUE'])
    471           total_dct[match_dict['NAME']] += int(match_dict['VALUE'].split()[0])
    472 
    473     return ProcSmaps(raw, total_dct, maps, vma_internals)
    474 
    475   @property
    476   def size(self):
    477     return self._size
    478 
    479   @property
    480   def rss(self):
    481     return self._rss
    482 
    483   @property
    484   def referenced(self):
    485     return self._referenced
    486 
    487   @property
    488   def pss(self):
    489     return self._pss
    490 
    491   @property
    492   def private_clean(self):
    493     return self._private_clean
    494 
    495   @property
    496   def shared_clean(self):
    497     return self._shared_clean
    498 
    499   @property
    500   def kernel_page_size(self):
    501     return self._kernel_page_size
    502 
    503   @property
    504   def mmu_page_size(self):
    505     return self._mmu_page_size
    506 
    507   @property
    508   def vma_internals(self):
    509     return self._vma_internals
    510 
    511 
    512 class ProcPagemap(object):
    513   """Reads and stores partial information in /proc/pid/pagemap.
    514 
    515   It picks up virtual addresses to read based on ProcMaps (/proc/pid/maps).
    516   See https://www.kernel.org/doc/Documentation/vm/pagemap.txt for details.
    517   """
    518   _BYTES_PER_PAGEMAP_VALUE = 8
    519   _BYTES_PER_OS_PAGE = 4096
    520   _VIRTUAL_TO_PAGEMAP_OFFSET = _BYTES_PER_OS_PAGE / _BYTES_PER_PAGEMAP_VALUE
    521 
    522   _MASK_PRESENT = 1 << 63
    523   _MASK_SWAPPED = 1 << 62
    524   _MASK_FILEPAGE_OR_SHAREDANON = 1 << 61
    525   _MASK_SOFTDIRTY = 1 << 55
    526   _MASK_PFN = (1 << 55) - 1
    527 
    528   class VMA(object):
    529     def __init__(self, vsize, present, swapped, pageframes):
    530       self._vsize = vsize
    531       self._present = present
    532       self._swapped = swapped
    533       self._pageframes = pageframes
    534 
    535     @property
    536     def vsize(self):
    537       return int(self._vsize)
    538 
    539     @property
    540     def present(self):
    541       return int(self._present)
    542 
    543     @property
    544     def swapped(self):
    545       return int(self._swapped)
    546 
    547     @property
    548     def pageframes(self):
    549       return self._pageframes
    550 
    551   def __init__(self, vsize, present, swapped, vma_internals, in_process_dup):
    552     self._vsize = vsize
    553     self._present = present
    554     self._swapped = swapped
    555     self._vma_internals = vma_internals
    556     self._in_process_dup = in_process_dup
    557 
    558   @staticmethod
    559   def load(pid, maps):
    560     total_present = 0
    561     total_swapped = 0
    562     total_vsize = 0
    563     in_process_dup = 0
    564     vma_internals = collections.OrderedDict()
    565     process_pageframe_set = set()
    566 
    567     try:
    568       pagemap_fd = os.open(
    569           os.path.join('/proc', str(pid), 'pagemap'), os.O_RDONLY)
    570     except (IOError, OSError):
    571       return None
    572     for vma in maps:
    573       present = 0
    574       swapped = 0
    575       vsize = 0
    576       pageframes = collections.defaultdict(int)
    577       begin_offset = ProcPagemap._offset(vma.begin)
    578       chunk_size = ProcPagemap._offset(vma.end) - begin_offset
    579       try:
    580         os.lseek(pagemap_fd, begin_offset, os.SEEK_SET)
    581         buf = os.read(pagemap_fd, chunk_size)
    582       except (IOError, OSError):
    583         return None
    584       if len(buf) < chunk_size:
    585         _LOGGER.warn('Failed to read pagemap at 0x%x in %d.' % (vma.begin, pid))
    586       pagemap_values = struct.unpack(
    587           '=%dQ' % (len(buf) / ProcPagemap._BYTES_PER_PAGEMAP_VALUE), buf)
    588       for pagemap_value in pagemap_values:
    589         vsize += ProcPagemap._BYTES_PER_OS_PAGE
    590         if pagemap_value & ProcPagemap._MASK_PRESENT:
    591           if (pagemap_value & ProcPagemap._MASK_PFN) in process_pageframe_set:
    592             in_process_dup += ProcPagemap._BYTES_PER_OS_PAGE
    593           else:
    594             process_pageframe_set.add(pagemap_value & ProcPagemap._MASK_PFN)
    595           if (pagemap_value & ProcPagemap._MASK_PFN) not in pageframes:
    596             present += ProcPagemap._BYTES_PER_OS_PAGE
    597           pageframes[pagemap_value & ProcPagemap._MASK_PFN] += 1
    598         if pagemap_value & ProcPagemap._MASK_SWAPPED:
    599           swapped += ProcPagemap._BYTES_PER_OS_PAGE
    600       vma_internals[vma] = ProcPagemap.VMA(vsize, present, swapped, pageframes)
    601       total_present += present
    602       total_swapped += swapped
    603       total_vsize += vsize
    604     try:
    605       os.close(pagemap_fd)
    606     except OSError:
    607       return None
    608 
    609     return ProcPagemap(total_vsize, total_present, total_swapped,
    610                        vma_internals, in_process_dup)
    611 
    612   @staticmethod
    613   def _offset(virtual_address):
    614     return virtual_address / ProcPagemap._VIRTUAL_TO_PAGEMAP_OFFSET
    615 
    616   @property
    617   def vsize(self):
    618     return int(self._vsize)
    619 
    620   @property
    621   def present(self):
    622     return int(self._present)
    623 
    624   @property
    625   def swapped(self):
    626     return int(self._swapped)
    627 
    628   @property
    629   def vma_internals(self):
    630     return self._vma_internals
    631 
    632 
    633 class _ProcessMemory(object):
    634   """Aggregates process memory information from /proc for manual testing."""
    635   def __init__(self, pid):
    636     self._pid = pid
    637     self._maps = None
    638     self._pagemap = None
    639     self._stat = None
    640     self._status = None
    641     self._statm = None
    642     self._smaps = []
    643 
    644   def _read(self, proc_file):
    645     lines = []
    646     with open(os.path.join('/proc', str(self._pid), proc_file), 'r') as proc_f:
    647       lines = proc_f.readlines()
    648     return lines
    649 
    650   def read_all(self):
    651     self.read_stat()
    652     self.read_statm()
    653     self.read_status()
    654     self.read_smaps()
    655     self.read_maps()
    656     self.read_pagemap(self._maps)
    657 
    658   def read_maps(self):
    659     self._maps = ProcMaps.load(self._pid)
    660 
    661   def read_pagemap(self, maps):
    662     self._pagemap = ProcPagemap.load(self._pid, maps)
    663 
    664   def read_smaps(self):
    665     self._smaps = ProcSmaps.load(self._pid)
    666 
    667   def read_stat(self):
    668     self._stat = ProcStat.load(self._pid)
    669 
    670   def read_statm(self):
    671     self._statm = ProcStatm.load(self._pid)
    672 
    673   def read_status(self):
    674     self._status = ProcStatus.load(self._pid)
    675 
    676   @property
    677   def pid(self):
    678     return self._pid
    679 
    680   @property
    681   def maps(self):
    682     return self._maps
    683 
    684   @property
    685   def pagemap(self):
    686     return self._pagemap
    687 
    688   @property
    689   def smaps(self):
    690     return self._smaps
    691 
    692   @property
    693   def stat(self):
    694     return self._stat
    695 
    696   @property
    697   def statm(self):
    698     return self._statm
    699 
    700   @property
    701   def status(self):
    702     return self._status
    703 
    704 
    705 def main(argv):
    706   """The main function for manual testing."""
    707   _LOGGER.setLevel(logging.WARNING)
    708   handler = logging.StreamHandler()
    709   handler.setLevel(logging.WARNING)
    710   handler.setFormatter(logging.Formatter(
    711       '%(asctime)s:%(name)s:%(levelname)s:%(message)s'))
    712   _LOGGER.addHandler(handler)
    713 
    714   pids = []
    715   for arg in argv[1:]:
    716     try:
    717       pid = int(arg)
    718     except ValueError:
    719       raise SyntaxError("%s is not an integer." % arg)
    720     else:
    721       pids.append(pid)
    722 
    723   procs = {}
    724   for pid in pids:
    725     procs[pid] = _ProcessMemory(pid)
    726     procs[pid].read_all()
    727 
    728     print '=== PID: %d ===' % pid
    729 
    730     print '   stat: %d' % procs[pid].stat.vsize
    731     print '  statm: %d' % (procs[pid].statm.size * 4096)
    732     print ' status: %d (Peak:%d)' % (procs[pid].status.vm_size * 1024,
    733                                      procs[pid].status.vm_peak * 1024)
    734     print '  smaps: %d' % (procs[pid].smaps.size * 1024)
    735     print 'pagemap: %d' % procs[pid].pagemap.vsize
    736     print '   stat: %d' % (procs[pid].stat.rss * 4096)
    737     print '  statm: %d' % (procs[pid].statm.resident * 4096)
    738     print ' status: %d (Peak:%d)' % (procs[pid].status.vm_rss * 1024,
    739                                      procs[pid].status.vm_hwm * 1024)
    740     print '  smaps: %d' % (procs[pid].smaps.rss * 1024)
    741     print 'pagemap: %d' % procs[pid].pagemap.present
    742 
    743   return 0
    744 
    745 
    746 if __name__ == '__main__':
    747   sys.exit(main(sys.argv))
    748