| OLD | NEW |
| 1 #!/usr/bin/env python | 1 #!/usr/bin/env python |
| 2 # Copyright 2013 The Chromium Authors. All rights reserved. | 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 | 3 # Use of this source code is governed by a BSD-style license that can be |
| 4 # found in the LICENSE file. | 4 # found in the LICENSE file. |
| 5 | 5 |
| 6 # A Python library to read and store procfs (/proc) information on Linux. | 6 # A Python library to read and store procfs (/proc) information on Linux. |
| 7 # | 7 # |
| 8 # Each information storage class in this file stores original data as original | 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 | 9 # as reasonablly possible. Translation is done when requested. It is to make it |
| 10 # always possible to probe the original data. | 10 # always possible to probe the original data. |
| 11 | 11 |
| 12 | 12 |
| 13 import collections | 13 import collections |
| 14 import logging | 14 import logging |
| 15 import os | 15 import os |
| 16 import re | 16 import re |
| 17 import struct | 17 import struct |
| 18 import sys | 18 import sys |
| 19 | 19 |
| 20 | 20 |
| 21 LOGGER = logging.getLogger('procfs') | 21 class _NullHandler(logging.Handler): |
| 22 def emit(self, record): |
| 23 pass |
| 24 |
| 25 |
| 26 _LOGGER = logging.getLogger('procfs') |
| 27 _LOGGER.addHandler(_NullHandler()) |
| 22 | 28 |
| 23 | 29 |
| 24 class ProcStat(object): | 30 class ProcStat(object): |
| 25 """Reads and stores information in /proc/pid/stat.""" | 31 """Reads and stores information in /proc/pid/stat.""" |
| 26 _PATTERN = re.compile(r'^' | 32 _PATTERN = re.compile(r'^' |
| 27 '(?P<PID>-?[0-9]+) ' | 33 '(?P<PID>-?[0-9]+) ' |
| 28 '\((?P<COMM>.+)\) ' | 34 '\((?P<COMM>.+)\) ' |
| 29 '(?P<STATE>[RSDZTW]) ' | 35 '(?P<STATE>[RSDZTW]) ' |
| 30 '(?P<PPID>-?[0-9]+) ' | 36 '(?P<PPID>-?[0-9]+) ' |
| 31 '(?P<PGRP>-?[0-9]+) ' | 37 '(?P<PGRP>-?[0-9]+) ' |
| (...skipping 508 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 540 self._vma_internals = vma_internals | 546 self._vma_internals = vma_internals |
| 541 self._in_process_dup = in_process_dup | 547 self._in_process_dup = in_process_dup |
| 542 | 548 |
| 543 @staticmethod | 549 @staticmethod |
| 544 def load(pid, maps): | 550 def load(pid, maps): |
| 545 total_present = 0 | 551 total_present = 0 |
| 546 total_swapped = 0 | 552 total_swapped = 0 |
| 547 total_vsize = 0 | 553 total_vsize = 0 |
| 548 in_process_dup = 0 | 554 in_process_dup = 0 |
| 549 vma_internals = collections.OrderedDict() | 555 vma_internals = collections.OrderedDict() |
| 556 process_pageframe_set = set() |
| 550 | 557 |
| 551 pagemap_fd = os.open( | 558 pagemap_fd = os.open( |
| 552 os.path.join('/proc', str(pid), 'pagemap'), os.O_RDONLY) | 559 os.path.join('/proc', str(pid), 'pagemap'), os.O_RDONLY) |
| 553 for vma in maps: | 560 for vma in maps: |
| 554 present = 0 | 561 present = 0 |
| 555 swapped = 0 | 562 swapped = 0 |
| 556 vsize = 0 | 563 vsize = 0 |
| 557 pageframes = collections.defaultdict(int) | 564 pageframes = collections.defaultdict(int) |
| 558 pageframes_set = set() | |
| 559 begin_offset = ProcPagemap._offset(vma.begin) | 565 begin_offset = ProcPagemap._offset(vma.begin) |
| 560 chunk_size = ProcPagemap._offset(vma.end) - begin_offset | 566 chunk_size = ProcPagemap._offset(vma.end) - begin_offset |
| 561 os.lseek(pagemap_fd, begin_offset, os.SEEK_SET) | 567 os.lseek(pagemap_fd, begin_offset, os.SEEK_SET) |
| 562 buf = os.read(pagemap_fd, chunk_size) | 568 buf = os.read(pagemap_fd, chunk_size) |
| 563 if len(buf) < chunk_size: | 569 if len(buf) < chunk_size: |
| 564 LOGGER.warn('Failed to read pagemap at 0x%x.' % vma.begin) | 570 _LOGGER.warn('Failed to read pagemap at 0x%x in %d.' % (vma.begin, pid)) |
| 565 pagemap_values = struct.unpack( | 571 pagemap_values = struct.unpack( |
| 566 '=%dQ' % (len(buf) / ProcPagemap._BYTES_PER_PAGEMAP_VALUE), buf) | 572 '=%dQ' % (len(buf) / ProcPagemap._BYTES_PER_PAGEMAP_VALUE), buf) |
| 567 for pagemap_value in pagemap_values: | 573 for pagemap_value in pagemap_values: |
| 568 vsize += ProcPagemap._BYTES_PER_OS_PAGE | 574 vsize += ProcPagemap._BYTES_PER_OS_PAGE |
| 569 if pagemap_value & ProcPagemap._MASK_PRESENT: | 575 if pagemap_value & ProcPagemap._MASK_PRESENT: |
| 570 if (pagemap_value & ProcPagemap._MASK_PFN) in pageframes_set: | 576 if (pagemap_value & ProcPagemap._MASK_PFN) in process_pageframe_set: |
| 571 in_process_dup += ProcPagemap._BYTES_PER_OS_PAGE | 577 in_process_dup += ProcPagemap._BYTES_PER_OS_PAGE |
| 572 else: | 578 else: |
| 573 pageframes_set.add(pagemap_value & ProcPagemap._MASK_PFN) | 579 process_pageframe_set.add(pagemap_value & ProcPagemap._MASK_PFN) |
| 574 if (pagemap_value & ProcPagemap._MASK_PFN) not in pageframes: | 580 if (pagemap_value & ProcPagemap._MASK_PFN) not in pageframes: |
| 575 present += ProcPagemap._BYTES_PER_OS_PAGE | 581 present += ProcPagemap._BYTES_PER_OS_PAGE |
| 576 pageframes[pagemap_value & ProcPagemap._MASK_PFN] += 1 | 582 pageframes[pagemap_value & ProcPagemap._MASK_PFN] += 1 |
| 577 if pagemap_value & ProcPagemap._MASK_SWAPPED: | 583 if pagemap_value & ProcPagemap._MASK_SWAPPED: |
| 578 swapped += ProcPagemap._BYTES_PER_OS_PAGE | 584 swapped += ProcPagemap._BYTES_PER_OS_PAGE |
| 579 vma_internals[vma] = ProcPagemap.VMA(vsize, present, swapped, pageframes) | 585 vma_internals[vma] = ProcPagemap.VMA(vsize, present, swapped, pageframes) |
| 580 total_present += present | 586 total_present += present |
| 581 total_swapped += swapped | 587 total_swapped += swapped |
| 582 total_vsize += vsize | 588 total_vsize += vsize |
| 583 os.close(pagemap_fd) | 589 os.close(pagemap_fd) |
| (...skipping 89 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 673 def statm(self): | 679 def statm(self): |
| 674 return self._statm | 680 return self._statm |
| 675 | 681 |
| 676 @property | 682 @property |
| 677 def status(self): | 683 def status(self): |
| 678 return self._status | 684 return self._status |
| 679 | 685 |
| 680 | 686 |
| 681 def main(argv): | 687 def main(argv): |
| 682 """The main function for manual testing.""" | 688 """The main function for manual testing.""" |
| 683 | 689 _LOGGER.setLevel(logging.WARNING) |
| 684 LOGGER.setLevel(logging.DEBUG) | |
| 685 handler = logging.StreamHandler() | 690 handler = logging.StreamHandler() |
| 686 handler.setLevel(logging.INFO) | 691 handler.setLevel(logging.WARNING) |
| 687 formatter = logging.Formatter('%(message)s') | 692 handler.setFormatter(logging.Formatter( |
| 688 handler.setFormatter(formatter) | 693 '%(asctime)s:%(name)s:%(levelname)s:%(message)s')) |
| 689 LOGGER.addHandler(handler) | 694 _LOGGER.addHandler(handler) |
| 690 | 695 |
| 691 pids = [] | 696 pids = [] |
| 692 for arg in argv[1:]: | 697 for arg in argv[1:]: |
| 693 try: | 698 try: |
| 694 pid = int(arg) | 699 pid = int(arg) |
| 695 except ValueError: | 700 except ValueError: |
| 696 raise SyntaxError("%s is not an integer." % arg) | 701 raise SyntaxError("%s is not an integer." % arg) |
| 697 else: | 702 else: |
| 698 pids.append(pid) | 703 pids.append(pid) |
| 699 | 704 |
| (...skipping 15 matching lines...) Expand all Loading... |
| 715 print ' status: %d (Peak:%d)' % (procs[pid].status.vm_rss * 1024, | 720 print ' status: %d (Peak:%d)' % (procs[pid].status.vm_rss * 1024, |
| 716 procs[pid].status.vm_hwm * 1024) | 721 procs[pid].status.vm_hwm * 1024) |
| 717 print ' smaps: %d' % (procs[pid].smaps.rss * 1024) | 722 print ' smaps: %d' % (procs[pid].smaps.rss * 1024) |
| 718 print 'pagemap: %d' % procs[pid].pagemap.present | 723 print 'pagemap: %d' % procs[pid].pagemap.present |
| 719 | 724 |
| 720 return 0 | 725 return 0 |
| 721 | 726 |
| 722 | 727 |
| 723 if __name__ == '__main__': | 728 if __name__ == '__main__': |
| 724 sys.exit(main(sys.argv)) | 729 sys.exit(main(sys.argv)) |
| OLD | NEW |