summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rwxr-xr-xtools/isolate/data/trace_inputs/touch_only.py30
-rwxr-xr-xtools/isolate/trace_inputs.py78
-rwxr-xr-xtools/isolate/trace_inputs_smoke_test.py35
-rwxr-xr-xtools/isolate/trace_inputs_test.py4
4 files changed, 114 insertions, 33 deletions
diff --git a/tools/isolate/data/trace_inputs/touch_only.py b/tools/isolate/data/trace_inputs/touch_only.py
new file mode 100755
index 0000000..58eba2c
--- /dev/null
+++ b/tools/isolate/data/trace_inputs/touch_only.py
@@ -0,0 +1,30 @@
+#!/usr/bin/env python
+# 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.
+
+"""Uses different APIs to touch a file."""
+
+import os
+import sys
+
+
+BASE_DIR = os.path.dirname(os.path.abspath(__file__))
+
+
+def main():
+ print 'Only look if a file exists but do not open it.'
+ assert len(sys.argv) == 2
+ path = os.path.join(BASE_DIR, 'test_file.txt')
+ command = sys.argv[1]
+ if command == 'access':
+ return not os.access(path, os.R_OK)
+ elif command == 'isfile':
+ return not os.path.isfile(path)
+ elif command == 'stat':
+ return not os.stat(path).st_size
+ return 1
+
+
+if __name__ == '__main__':
+ sys.exit(main())
diff --git a/tools/isolate/trace_inputs.py b/tools/isolate/trace_inputs.py
index e04d94a..6878d49 100755
--- a/tools/isolate/trace_inputs.py
+++ b/tools/isolate/trace_inputs.py
@@ -569,14 +569,18 @@ class Results(object):
class _TouchedObject(object):
"""Something, a file or a directory, that was accessed."""
- def __init__(self, root, path, tainted):
- logging.debug('%s(%s, %s)' % (self.__class__.__name__, root, path))
+ def __init__(self, root, path, tainted, size, nb_files):
+ logging.debug(
+ '%s(%s, %s, %s, %s, %s)' %
+ (self.__class__.__name__, root, path, tainted, size, nb_files))
self.root = root
self.path = path
self.tainted = tainted
+ self.nb_files = nb_files
+ # Can be used as a cache or a default value, depending on context.
+ self._size = size
# These are cache only.
self._real_path = None
- self._size = None
# Check internal consistency.
assert path, path
@@ -614,7 +618,10 @@ class Results(object):
return self._size
def flatten(self):
- """Returns a dict representing this object."""
+ """Returns a dict representing this object.
+
+ A 'size' of 0 means the file was only touched and not read.
+ """
return {
'path': self.path,
'size': self.size,
@@ -650,25 +657,25 @@ class Results(object):
raise NotImplementedError(self.__class__.__name__)
class File(_TouchedObject):
- """A file that was accessed.
+ """A file that was accessed. May not be present anymore.
If tainted is true, it means it is not a real path anymore as a variable
replacement occured.
+
+ If touched_only is True, this means the file was probed for existence, and
+ it is existent, but was never _opened_. If touched_only is True, the file
+ must have existed.
"""
- def __init__(self, root, path, tainted):
- """Represents a file accessed. May not be present anymore."""
- super(Results.File, self).__init__(root, path, tainted)
- # For compatibility with Directory object interface.
- # Shouldn't be used normally, only exists to simplify algorithms.
- self.nb_files = 1
+ def __init__(self, root, path, tainted, size):
+ super(Results.File, self).__init__(root, path, tainted, size, 1)
def _clone(self, new_root, new_path, tainted):
"""Clones itself keeping meta-data."""
- out = self.__class__(new_root, new_path, tainted)
- # Keep the cache for performance reason. It is also important when the
- # file becomes tainted (with a variable instead of the real path) since
- # self.path is not an on-disk path anymore so out._size cannot be updated.
- out._size = self.size
+ # Keep the self.size and self._real_path caches for performance reason. It
+ # is also important when the file becomes tainted (with a variable instead
+ # of the real path) since self.path is not an on-disk path anymore so
+ # out._size cannot be updated.
+ out = self.__class__(new_root, new_path, tainted, self.size)
out._real_path = self._real_path
return out
@@ -677,12 +684,12 @@ class Results(object):
def __init__(self, root, path, tainted, size, nb_files):
"""path='.' is a valid value and must be handled appropriately."""
assert not path.endswith(os.path.sep), path
- super(Results.Directory, self).__init__(root, path + os.path.sep, tainted)
- self.nb_files = nb_files
+ super(Results.Directory, self).__init__(
+ root, path + os.path.sep, tainted, size, nb_files)
# In that case, it's not a cache, it's an actual value that is never
- # modified.
+ # modified and represents the total size of the files contained in this
+ # directory.
assert size
- self._size = size
def flatten(self):
out = super(Results.Directory, self).flatten()
@@ -705,20 +712,18 @@ class Results(object):
Contains references to the files accessed by this process and its children.
"""
- def __init__(
- self, pid, files, executable, command, initial_cwd, children):
+ def __init__(self, pid, files, executable, command, initial_cwd, children):
logging.debug('Process(%s, %d, ...)' % (pid, len(files)))
self.pid = pid
- self.files = sorted(
- (Results.File(None, f, False) for f in files), key=lambda x: x.path)
+ self.files = sorted(files, key=lambda x: x.path)
self.children = children
self.executable = executable
self.command = command
self.initial_cwd = initial_cwd
# Check internal consistency.
- assert len(set(f.path for f in self.files)) == len(self.files), [
- f.path for f in self.files]
+ assert len(set(f.path for f in self.files)) == len(self.files), sorted(
+ f.path for f in self.files)
assert isinstance(self.children, list)
assert isinstance(self.files, list)
@@ -741,20 +746,18 @@ class Results(object):
def strip_root(self, root):
assert isabs(root) and root.endswith(os.path.sep), root
+ # Loads the files after since they are constructed as objects.
out = self.__class__(
self.pid,
- [],
+ filter(None, (f.strip_root(root) for f in self.files)),
self.executable,
self.command,
self.initial_cwd,
[c.strip_root(root) for c in self.children])
- # Override the files property.
- out.files = filter(None, (f.strip_root(root) for f in self.files))
logging.debug(
'strip_root(%s) %d -> %d' % (root, len(self.files), len(out.files)))
return out
-
def __init__(self, process):
self.process = process
# Cache.
@@ -815,6 +818,7 @@ class ApiBase(object):
self.initial_cwd = initial_cwd
self.cwd = None
self.files = set()
+ self.only_touched = set()
self.executable = None
self.command = None
@@ -843,10 +847,22 @@ class ApiBase(object):
return x
return get_native_path_case(x)
+ # Filters out directories. Some may have passed through.
+ files = set(map(render_to_string_and_fix_case, self.files))
+ only_touched = set(
+ map(render_to_string_and_fix_case, self.only_touched))
+ only_touched -= files
+
files = [
- f for f in set(map(render_to_string_and_fix_case, self.files))
+ Results.File(None, f, False, None) for f in files
if not os.path.isdir(f)
]
+ # Using 0 as size means the file's content is ignored since the file was
+ # never opened for I/O.
+ files.extend(
+ Results.File(None, f, False, 0) for f in only_touched
+ if not os.path.isdir(f)
+ )
return Results.Process(
self.pid,
files,
diff --git a/tools/isolate/trace_inputs_smoke_test.py b/tools/isolate/trace_inputs_smoke_test.py
index dde74d4..c4fdca7 100755
--- a/tools/isolate/trace_inputs_smoke_test.py
+++ b/tools/isolate/trace_inputs_smoke_test.py
@@ -559,6 +559,41 @@ class TraceInputsImport(TraceInputsBase):
self.assertTrue(actual['root'].pop('pid'))
self.assertEquals(expected, actual)
+ def _touch_expected(self, command):
+ # Look for file that were touched but not opened, using different APIs.
+ results = self._execute_trace(
+ [sys.executable, os.path.join('trace_inputs', 'touch_only.py'), command])
+ expected = {
+ 'root': {
+ 'children': [],
+ 'command': [
+ self.executable,
+ os.path.join('trace_inputs', 'touch_only.py'),
+ command,
+ ],
+ 'executable': self.real_executable,
+ 'files': [
+ {
+ 'path': os.path.join(u'data', 'trace_inputs', 'touch_only.py'),
+ 'size': self._size('data', 'trace_inputs', 'touch_only.py'),
+ },
+ ],
+ 'initial_cwd': self.initial_cwd,
+ },
+ }
+ actual = results.flatten()
+ self.assertTrue(actual['root'].pop('pid'))
+ self.assertEquals(expected, actual)
+
+ def test_trace_touch_only_access(self):
+ self._touch_expected('access')
+
+ def test_trace_touch_only_isfile(self):
+ self._touch_expected('isfile')
+
+ def test_trace_touch_only_stat(self):
+ self._touch_expected('stat')
+
if __name__ == '__main__':
VERBOSE = '-v' in sys.argv
diff --git a/tools/isolate/trace_inputs_test.py b/tools/isolate/trace_inputs_test.py
index 3301b27..d9c8c15 100755
--- a/tools/isolate/trace_inputs_test.py
+++ b/tools/isolate/trace_inputs_test.py
@@ -51,14 +51,14 @@ class TraceInputs(unittest.TestCase):
def test_variable_abs(self):
- value = trace_inputs.Results.File(None, '/foo/bar', False)
+ value = trace_inputs.Results.File(None, '/foo/bar', False, False)
actual = value.replace_variables({'$FOO': '/foo'})
self.assertEquals('$FOO/bar', actual.path)
self.assertEquals('$FOO/bar', actual.full_path)
self.assertEquals(True, actual.tainted)
def test_variable_rel(self):
- value = trace_inputs.Results.File('/usr', 'foo/bar', False)
+ value = trace_inputs.Results.File('/usr', 'foo/bar', False, False)
actual = value.replace_variables({'$FOO': 'foo'})
self.assertEquals('$FOO/bar', actual.path)
self.assertEquals(os.path.join('/usr', '$FOO/bar'), actual.full_path)