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 |