Chromium Code Reviews| Index: appengine/findit/crash/project.py |
| diff --git a/appengine/findit/crash/project.py b/appengine/findit/crash/project.py |
| new file mode 100644 |
| index 0000000000000000000000000000000000000000..9660c6029129abc97953e07a3456e82eed673c20 |
| --- /dev/null |
| +++ b/appengine/findit/crash/project.py |
| @@ -0,0 +1,121 @@ |
| +# Copyright 2017 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. |
| + |
| +from collections import namedtuple |
| +import os |
| +import re |
| + |
| +from libs.gitiles.diff import ChangeType |
| + |
| +# Some projects like "chromium", it has many dependency projects like |
|
Martin Barbella
2017/01/27 23:30:58
Nit: s/it has/have/
Sharu Jiang
2017/01/30 22:23:52
Done.
|
| +# "chromium-blink", "chromium-skia", "chromium-pdfium"...etc., for those |
| +# dep projects, the "chromium" ``Project`` can derive the names from their |
| +# dep paths. |
| +# Some other projects like "android_os", "clank", they don't have any dependency |
| +# projects that are relavent to Predator. |
| +_PROJECTS_WITH_DEP_PROJECTS = ['chromium'] |
| + |
| + |
| +# TODO(http://crbug.com/659346): write the coverage tests. |
| +class Project(namedtuple('Project', |
| + ['name', 'path_regexs', 'function_regexs', |
|
wrengr
2017/01/30 19:14:19
Nit: "regexes" is the standard way to pluralize "r
Sharu Jiang
2017/01/30 22:23:52
Done.
|
| + 'host_directories'])): |
| + """A representation of a "project". |
|
wrengr
2017/01/30 19:14:19
This line doesn't explain anything beyond what the
Sharu Jiang
2017/01/30 22:23:52
Done.
|
| + |
| + For example: 'android_os', 'clank' or 'chromium'. Notably, a project knows |
| + how to identify itself. Hence, given a stack frame, file path or dependency |
| + path or whatever, we ask the ``Project`` whether it matches that frame, |
| + CL, etc. |
| + |
| + Properties: |
| + name (str): The name of the project, like "chromium", "android_os". |
| + path_regexs (list of re.RegexObject): Patterns of paths that project has. |
| + function_regexs (list of re.RegexObject): Patterns of functions that project |
| + has. |
| + host_directories (list of str): The root directoris of this project and its |
|
wrengr
2017/01/30 19:14:19
(s/directoris/directories/ && s/and its/and their/
Sharu Jiang
2017/01/30 22:23:52
Done.
|
| + dependency projects. |
| + N.B. If ``host_directories`` is availabe, this project can match |
|
wrengr
2017/01/30 19:14:19
s/availabe/available/
Sharu Jiang
2017/01/30 22:23:52
Done.
|
| + it with the passed-in ``dep_path`` to tell whether a suspect or stack is |
| + from this project, else if this information is missing, the project cannot |
|
wrengr
2017/01/30 19:14:19
s/, else if/. If/
Sharu Jiang
2017/01/30 22:23:52
Done.
|
| + tell that from ``dep_path``, however that doesn't mean the suspect or |
|
wrengr
2017/01/30 19:14:19
s/, however/; however,/
Sharu Jiang
2017/01/30 22:23:52
Done.
|
| + stack does not belong to this project, we can use other information like |
| + ``path_regexs`` or ``function_regexs`` to analyze. |
| + """ |
| + __slots__ = () |
| + |
| + def __new__(cls, name, path_regexs=None, |
| + function_regexs=None, host_directories=None): |
| + path_regexs = [re.compile(path_regex) for path_regex in |
|
wrengr
2017/01/30 19:14:19
can replace this line with ``map(re.compile, path_
Sharu Jiang
2017/01/30 22:23:52
Done.
|
| + path_regexs] if path_regexs else [] |
| + function_regexs = [re.compile(function_regex) for function_regex in |
| + function_regexs] if function_regexs else [] |
|
wrengr
2017/01/30 19:14:19
ditto
Sharu Jiang
2017/01/30 22:23:52
Done.
|
| + host_directories = host_directories or [] |
| + |
| + return super(cls, Project).__new__( |
| + cls, name, path_regexs, function_regexs, host_directories) |
| + |
| + def MatchesStackFrame(self, frame): |
| + """Returns true if this project matches the frame.""" |
| + # Sometimes some marker information are in the frame.raw_file_path. |
| + # An example, the path_regex for android_os is -- |
| + # "https___googleplex-android.googlesource.com_a_platform_manifest.git/". |
| + # It can only be found in frame.raw_file_path, since the frame.file_path |
| + # has those kind of information stripped. |
| + for path_regex in self.path_regexs: |
| + if (path_regex.match(os.path.join(frame.dep_path, frame.file_path)) or |
| + path_regex.match(frame.raw_file_path)): |
| + return True |
| + |
| + for function_regex in self.function_regexs: |
| + if function_regex.match(frame.function): |
| + return True |
| + |
| + for host_directory in self.host_directories: |
| + if frame.dep_path.startswith(host_directory): |
| + return True |
| + |
| + return False |
| + |
| + def MatchesTouchedFile(self, dep_path, touched_file): |
| + """Returns true if this project matches the file path.""" |
| + if touched_file.change_type == ChangeType.DELETE: |
|
wrengr
2017/01/30 19:14:19
Since this conditional shows up repeatedly, should
Sharu Jiang
2017/01/30 22:23:52
Done.
|
| + path = touched_file.old_path |
| + else: |
| + path = touched_file.new_path |
| + |
| + # If the path matches the path patterns. |
| + for path_regex in self.path_regexs: |
| + if path_regex.match(os.path.join(dep_path, path)): |
| + return True |
| + |
| + # If the dep_path hosted by this project. |
| + for host_directory in self.host_directories: |
| + if dep_path.startswith(host_directory): |
| + return True |
| + |
| + return False |
| + |
| + def GetName(self, dep_path=None): |
| + """Returns the project name based on dep path. |
| + |
| + N.B. (1) If this project doesn't have dep projects, just return the project |
| + name. (2) If this project does, return the derived dep project name based on |
| + the self.host_directories. |
| + """ |
| + if self.name not in _PROJECTS_WITH_DEP_PROJECTS or dep_path is None: |
| + return self.name |
| + |
| + # For chromium project, get the name of the sub project from ``dep_path``. |
| + for host_directory in self.host_directories: |
| + if dep_path.startswith(host_directory): |
| + path = dep_path[len(host_directory):] |
| + if not path: |
| + return self.name |
| + |
| + return '%s-%s' % (self.name, path.split('/')[0].lower()) |
| + |
| + # Unknown path, return the whole path as project name. |
| + return '%s-%s' % (self.name, |
| + '_'.join([dep_part for dep_part in dep_path.split('/') |
| + if dep_part])) |