summaryrefslogtreecommitdiffstats
path: root/tools
diff options
context:
space:
mode:
Diffstat (limited to 'tools')
-rw-r--r--tools/deep_memory_profiler/lib/dump.py6
-rw-r--r--tools/deep_memory_profiler/lib/subcommand.py4
-rw-r--r--tools/deep_memory_profiler/lib/symbol.py6
-rwxr-xr-xtools/find_runtime_symbols/find_runtime_symbols.py17
-rwxr-xr-xtools/find_runtime_symbols/prepare_symbol_info.py11
-rw-r--r--tools/find_runtime_symbols/proc_maps.py125
-rw-r--r--tools/linux/PRESUBMIT.py45
-rwxr-xr-xtools/linux/procfs.py724
-rwxr-xr-xtools/linux/tests/procfs_tests.py (renamed from tools/find_runtime_symbols/tests/proc_maps_test.py)12
9 files changed, 805 insertions, 145 deletions
diff --git a/tools/deep_memory_profiler/lib/dump.py b/tools/deep_memory_profiler/lib/dump.py
index 115979e..dc4b898 100644
--- a/tools/deep_memory_profiler/lib/dump.py
+++ b/tools/deep_memory_profiler/lib/dump.py
@@ -14,7 +14,7 @@ from lib.exceptions import EmptyDumpException, InvalidDumpException
from lib.exceptions import ObsoleteDumpVersionException, ParsingException
from lib.pageframe import PageFrame
from lib.range_dict import ExclusiveRangeDict
-from lib.symbol import proc_maps
+from lib.symbol import procfs
LOGGER = logging.getLogger('dmprof')
@@ -295,7 +295,7 @@ class Dump(object):
current_vma = {}
pageframe_list = []
while True:
- entry = proc_maps.ProcMaps.parse_line(self._lines[ln])
+ entry = procfs.ProcMaps.parse_line(self._lines[ln])
if entry:
current_vma = {}
for _, _, attr in self._procmaps.iter_range(entry.begin, entry.end):
@@ -433,7 +433,7 @@ class DumpList(object):
class ProcMapsEntryAttribute(ExclusiveRangeDict.RangeAttribute):
"""Represents an entry of /proc/maps in range_dict.ExclusiveRangeDict."""
- _DUMMY_ENTRY = proc_maps.ProcMapsEntry(
+ _DUMMY_ENTRY = procfs.ProcMapsEntry(
0, # begin
0, # end
'-', # readable
diff --git a/tools/deep_memory_profiler/lib/subcommand.py b/tools/deep_memory_profiler/lib/subcommand.py
index 25416f6..3924109 100644
--- a/tools/deep_memory_profiler/lib/subcommand.py
+++ b/tools/deep_memory_profiler/lib/subcommand.py
@@ -10,7 +10,7 @@ import re
from lib.bucket import BucketSet
from lib.dump import Dump, DumpList
from lib.symbol import SymbolDataSources, SymbolMappingCache, SymbolFinder
-from lib.symbol import proc_maps
+from lib.symbol import procfs
from lib.symbol import FUNCTION_SYMBOLS, SOURCEFILE_SYMBOLS, TYPEINFO_SYMBOLS
@@ -95,7 +95,7 @@ class SubCommand(object):
device_lib_path_candidates = set()
with open(prefix + '.maps') as maps_f:
- maps = proc_maps.ProcMaps.load(maps_f)
+ maps = procfs.ProcMaps.load_file(maps_f)
for entry in maps:
name = entry.as_dict()['name']
if any([base_dir in name for base_dir in SubCommand._DEVICE_BINDIRS]):
diff --git a/tools/deep_memory_profiler/lib/symbol.py b/tools/deep_memory_profiler/lib/symbol.py
index 897d409..86dd44b 100644
--- a/tools/deep_memory_profiler/lib/symbol.py
+++ b/tools/deep_memory_profiler/lib/symbol.py
@@ -10,11 +10,15 @@ _BASE_PATH = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
_FIND_RUNTIME_SYMBOLS_PATH = os.path.join(_BASE_PATH,
os.pardir,
'find_runtime_symbols')
+_TOOLS_LINUX_PATH = os.path.join(_BASE_PATH,
+ os.pardir,
+ 'linux')
sys.path.append(_FIND_RUNTIME_SYMBOLS_PATH)
+sys.path.append(_TOOLS_LINUX_PATH)
import find_runtime_symbols
import prepare_symbol_info
-import proc_maps # pylint: disable=W0611
+import procfs # pylint: disable=W0611,F0401
LOGGER = logging.getLogger('dmprof')
diff --git a/tools/find_runtime_symbols/find_runtime_symbols.py b/tools/find_runtime_symbols/find_runtime_symbols.py
index bed9e80..e96db4f 100755
--- a/tools/find_runtime_symbols/find_runtime_symbols.py
+++ b/tools/find_runtime_symbols/find_runtime_symbols.py
@@ -14,14 +14,21 @@ import os
import sys
from static_symbols import StaticSymbolsInFile
-from proc_maps import ProcMaps
+
+
+_BASE_PATH = os.path.dirname(os.path.abspath(__file__))
+_TOOLS_LINUX_PATH = os.path.join(_BASE_PATH, os.pardir, 'linux')
+sys.path.insert(0, _TOOLS_LINUX_PATH)
+
+
+from procfs import ProcMaps # pylint: disable=F0401
try:
from collections import OrderedDict # pylint: disable=E0611
except ImportError:
- BASE_PATH = os.path.dirname(os.path.abspath(__file__))
- SIMPLEJSON_PATH = os.path.join(BASE_PATH, os.pardir, os.pardir, 'third_party')
- sys.path.insert(0, SIMPLEJSON_PATH)
+ _SIMPLEJSON_PATH = os.path.join(_BASE_PATH, os.pardir, os.pardir,
+ 'third_party')
+ sys.path.insert(0, _SIMPLEJSON_PATH)
from simplejson import OrderedDict
@@ -76,7 +83,7 @@ class RuntimeSymbolsInProcess(object):
symbols_in_process = RuntimeSymbolsInProcess()
with open(os.path.join(prepared_data_dir, _MAPS_FILENAME), mode='r') as f:
- symbols_in_process._maps = ProcMaps.load(f)
+ symbols_in_process._maps = ProcMaps.load_file(f)
with open(os.path.join(prepared_data_dir, _FILES_FILENAME), mode='r') as f:
files = json.load(f)
diff --git a/tools/find_runtime_symbols/prepare_symbol_info.py b/tools/find_runtime_symbols/prepare_symbol_info.py
index d550388..9bce545 100755
--- a/tools/find_runtime_symbols/prepare_symbol_info.py
+++ b/tools/find_runtime_symbols/prepare_symbol_info.py
@@ -13,11 +13,16 @@ import subprocess
import sys
import tempfile
-from proc_maps import ProcMaps
-
BASE_PATH = os.path.dirname(os.path.abspath(__file__))
REDUCE_DEBUGLINE_PATH = os.path.join(BASE_PATH, 'reduce_debugline.py')
+_TOOLS_LINUX_PATH = os.path.join(BASE_PATH, os.pardir, 'linux')
+sys.path.insert(0, _TOOLS_LINUX_PATH)
+
+
+from procfs import ProcMaps # pylint: disable=F0401
+
+
LOGGER = logging.getLogger('prepare_symbol_info')
@@ -138,7 +143,7 @@ def prepare_symbol_info(maps_path,
shutil.copyfile(maps_path, os.path.join(output_dir_path, 'maps'))
with open(maps_path, mode='r') as f:
- maps = ProcMaps.load(f)
+ maps = ProcMaps.load_file(f)
LOGGER.debug('Listing up symbols.')
files = {}
diff --git a/tools/find_runtime_symbols/proc_maps.py b/tools/find_runtime_symbols/proc_maps.py
deleted file mode 100644
index 2d917b3..0000000
--- a/tools/find_runtime_symbols/proc_maps.py
+++ /dev/null
@@ -1,125 +0,0 @@
-# Copyright (c) 2012 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.
-
-import re
-
-
-_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)
-
-
-class ProcMapsEntry(object):
- """A class representing one line in /proc/.../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):
- """A class representing contents in /proc/.../maps."""
-
- 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(f):
- table = ProcMaps()
- for line in f:
- table.append_line(line)
- return table
-
- def append_line(self, line):
- entry = self.parse_line(line)
- if entry:
- self._append_entry(entry)
-
- @staticmethod
- def parse_line(line):
- matched = _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 == '-' and re.match(
- '\S+(\.(so|dll|dylib|bundle)|chrome)((\.\d+)+\w*(\.\d+){0,3})?',
- entry.name))
-
- @staticmethod
- def executable(entry):
- return (entry.executable == 'x' and re.match(
- '\S+(\.(so|dll|dylib|bundle)|chrome)((\.\d+)+\w*(\.\d+){0,3})?',
- entry.name))
-
- @staticmethod
- def executable_and_constants(entry):
- return (((entry.writable == '-' and entry.executable == '-') or
- entry.executable == 'x') and re.match(
- '\S+(\.(so|dll|dylib|bundle)|chrome)((\.\d+)+\w*(\.\d+){0,3})?',
- entry.name))
-
- 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
diff --git a/tools/linux/PRESUBMIT.py b/tools/linux/PRESUBMIT.py
new file mode 100644
index 0000000..d4d8601
--- /dev/null
+++ b/tools/linux/PRESUBMIT.py
@@ -0,0 +1,45 @@
+# 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.
+
+"""Top-level presubmit script for linux.
+
+See http://dev.chromium.org/developers/how-tos/depottools/presubmit-scripts for
+details on the presubmit API built into gcl.
+"""
+
+
+def CommonChecks(input_api, output_api):
+ import sys
+ def join(*args):
+ return input_api.os_path.join(input_api.PresubmitLocalPath(), *args)
+
+ output = []
+ sys_path_backup = sys.path
+ try:
+ sys.path = [
+ join('..', 'linux'),
+ ] + sys.path
+ output.extend(input_api.canned_checks.RunPylint(input_api, output_api))
+ finally:
+ sys.path = sys_path_backup
+
+ output.extend(
+ input_api.canned_checks.RunUnitTestsInDirectory(
+ input_api, output_api,
+ input_api.os_path.join(input_api.PresubmitLocalPath(), 'tests'),
+ whitelist=[r'.+_tests\.py$']))
+
+ if input_api.is_committing:
+ output.extend(input_api.canned_checks.PanProjectChecks(input_api,
+ output_api,
+ owners_check=False))
+ return output
+
+
+def CheckChangeOnUpload(input_api, output_api):
+ return CommonChecks(input_api, output_api)
+
+
+def CheckChangeOnCommit(input_api, output_api):
+ return CommonChecks(input_api, output_api)
diff --git a/tools/linux/procfs.py b/tools/linux/procfs.py
new file mode 100755
index 0000000..bbf2019
--- /dev/null
+++ b/tools/linux/procfs.py
@@ -0,0 +1,724 @@
+#!/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
+
+
+LOGGER = logging.getLogger('procfs')
+
+
+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):
+ with open(os.path.join('/proc', str(pid), 'stat'), 'r') as stat_f:
+ return ProcStat.load_file(stat_f)
+
+ @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):
+ raw = statm_f.readlines()
+ 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):
+ with open(os.path.join('/proc', str(pid), 'statm'), 'r') as statm_f:
+ return ProcStatm.load_file(statm_f)
+
+ @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)
+
+ 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):
+ with open(os.path.join('/proc', str(pid), 'maps'), 'r') as maps_f:
+ return ProcMaps.load_file(maps_f)
+
+ 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 == '-' and re.match(
+ '\S+(\.(so|dll|dylib|bundle)|chrome)((\.\d+)+\w*(\.\d+){0,3})?',
+ entry.name))
+
+ @staticmethod
+ def executable(entry):
+ return (entry.executable == 'x' and re.match(
+ '\S+(\.(so|dll|dylib|bundle)|chrome)((\.\d+)+\w*(\.\d+){0,3})?',
+ entry.name))
+
+ @staticmethod
+ def executable_and_constants(entry):
+ return (((entry.writable == '-' and entry.executable == '-') or
+ entry.executable == 'x') and re.match(
+ '\S+(\.(so|dll|dylib|bundle)|chrome)((\.\d+)+\w*(\.\d+){0,3})?',
+ entry.name))
+
+ 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()
+
+ pagemap_fd = os.open(
+ os.path.join('/proc', str(pid), 'pagemap'), os.O_RDONLY)
+ for vma in maps:
+ present = 0
+ swapped = 0
+ vsize = 0
+ pageframes = collections.defaultdict(int)
+ pageframes_set = set()
+ begin_offset = ProcPagemap._offset(vma.begin)
+ chunk_size = ProcPagemap._offset(vma.end) - begin_offset
+ os.lseek(pagemap_fd, begin_offset, os.SEEK_SET)
+ buf = os.read(pagemap_fd, chunk_size)
+ if len(buf) < chunk_size:
+ LOGGER.warn('Failed to read pagemap at 0x%x.' % vma.begin)
+ 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 pageframes_set:
+ in_process_dup += ProcPagemap._BYTES_PER_OS_PAGE
+ else:
+ pageframes_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
+ os.close(pagemap_fd)
+
+ 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.DEBUG)
+ handler = logging.StreamHandler()
+ handler.setLevel(logging.INFO)
+ formatter = logging.Formatter('%(message)s')
+ handler.setFormatter(formatter)
+ 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))
diff --git a/tools/find_runtime_symbols/tests/proc_maps_test.py b/tools/linux/tests/procfs_tests.py
index 502f252..e1e837a 100755
--- a/tools/find_runtime_symbols/tests/proc_maps_test.py
+++ b/tools/linux/tests/procfs_tests.py
@@ -1,5 +1,5 @@
#!/usr/bin/env python
-# Copyright (c) 2012 The Chromium Authors. All rights reserved.
+# 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.
@@ -12,7 +12,7 @@ import unittest
ROOT_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
sys.path.insert(0, ROOT_DIR)
-from proc_maps import ProcMaps
+from procfs import ProcMaps
class ProcMapsTest(unittest.TestCase):
@@ -77,26 +77,26 @@ class ProcMapsTest(unittest.TestCase):
}
def test_load(self):
- maps = ProcMaps.load(cStringIO.StringIO(self._TEST_PROCMAPS))
+ maps = ProcMaps.load_file(cStringIO.StringIO(self._TEST_PROCMAPS))
for index, entry in enumerate(maps):
self.assertEqual(entry.as_dict(), self._expected_as_dict(index))
def test_constants(self):
- maps = ProcMaps.load(cStringIO.StringIO(self._TEST_PROCMAPS))
+ maps = ProcMaps.load_file(cStringIO.StringIO(self._TEST_PROCMAPS))
selected = [4, 7]
for index, entry in enumerate(maps.iter(ProcMaps.constants)):
self.assertEqual(entry.as_dict(),
self._expected_as_dict(selected[index]))
def test_executable(self):
- maps = ProcMaps.load(cStringIO.StringIO(self._TEST_PROCMAPS))
+ maps = ProcMaps.load_file(cStringIO.StringIO(self._TEST_PROCMAPS))
selected = [3, 6]
for index, entry in enumerate(maps.iter(ProcMaps.executable)):
self.assertEqual(entry.as_dict(),
self._expected_as_dict(selected[index]))
def test_executable_and_constants(self):
- maps = ProcMaps.load(cStringIO.StringIO(self._TEST_PROCMAPS))
+ maps = ProcMaps.load_file(cStringIO.StringIO(self._TEST_PROCMAPS))
selected = [3, 4, 6, 7]
for index, entry in enumerate(maps.iter(ProcMaps.executable_and_constants)):
self.assertEqual(entry.as_dict(),