[crash/analyzer] Refactor to use the bug analyzer

This commit is contained in:
Suhaib Mujahid 2023-07-10 13:40:37 -04:00 коммит произвёл Suhaib Mujahid
Родитель a507e36b98
Коммит 88fea6fbe5
2 изменённых файлов: 50 добавлений и 29 удалений

Просмотреть файл

@ -10,6 +10,7 @@ from libmozdata import versions as lmdversions
from libmozdata.bugzilla import Bugzilla
from bugbot import utils
from bugbot.components import ComponentName
class VersionStatus(NamedTuple):
@ -37,6 +38,21 @@ class BugAnalyzer:
self._bug = bug
self._store = store
@property
def id(self) -> int:
"""The bug id."""
return self._bug["id"]
@property
def component(self) -> ComponentName:
"""The component that the bug is in."""
return ComponentName(self._bug["product"], self._bug["component"])
@property
def is_security(self) -> bool:
"""Whether the bug is a security bug."""
return any("core-security" in group for group in self._bug["groups"])
@property
def regressed_by_bugs(self) -> list["BugAnalyzer"]:
"""The bugs that regressed the bug."""
@ -155,7 +171,7 @@ class BugNotInStoreError(LookupError):
class BugsStore:
"""A class to retrieve bugs."""
def __init__(self, bugs: Iterable[dict], versions_map: dict[str, int] = None):
def __init__(self, bugs: Iterable[dict] = (), versions_map: dict[str, int] = None):
self.bugs = {bug["id"]: BugAnalyzer(bug, self) for bug in bugs}
self.versions_map = versions_map
@ -182,16 +198,12 @@ class BugsStore:
Args:
include_fields: The fields to include when fetching the bugs.
"""
bug_ids = {
bug_ids = (
bug_id
for bug in self.bugs.values()
if bug.get_field("regressed_by")
for bug_id in bug.get_field("regressed_by")
if bug_id not in self.bugs
}
if not bug_ids:
return
)
self.fetch_bugs(bug_ids, include_fields)
@ -202,6 +214,17 @@ class BugsStore:
bug_ids: The ids of the bugs to fetch.
include_fields: The fields to include when fetching the bugs.
"""
bug_ids = {
bug_id
for bug_id in bug_ids
# TODO: We only fetch bugs that aren't already in the store.
# However, the new fetch request might be specifying fields that
# aren't in the existing bug. We need at some point to handle such
# cases (currently, we do not have this requirement).
if bug_id not in self.bugs
}
if not bug_ids:
return
def bug_handler(bugs):
for bug in bugs:

Просмотреть файл

@ -15,6 +15,7 @@ from libmozdata.bugzilla import Bugzilla
from libmozdata.connection import Connection
from bugbot import logger, utils
from bugbot.bug.analyzer import BugAnalyzer, BugsStore
from bugbot.components import ComponentName
from bugbot.crash import socorro_util
@ -119,8 +120,9 @@ class ClouseauDataAnalyzer:
MINIMUM_CLOUSEAU_SCORE_THRESHOLD: int = 8
DEFAULT_CRASH_COMPONENT = ComponentName("Core", "General")
def __init__(self, reports: Iterable[dict]):
def __init__(self, reports: Iterable[dict], bugs_store: BugsStore):
self._clouseau_reports = reports
self.bugs_store = bugs_store
@cached_property
def max_clouseau_score(self):
@ -177,27 +179,22 @@ class ClouseauDataAnalyzer:
return None
@cached_property
def regressed_by_potential_bugs(self) -> list[dict]:
def regressed_by_potential_bugs(self) -> list[BugAnalyzer]:
"""The bugs whose patches could have caused the crash."""
def handler(bug: dict, data: list):
data.append(bug)
bugs: list[dict] = []
Bugzilla(
bugids=self.regressed_by_potential_bug_ids,
include_fields=[
self.bugs_store.fetch_bugs(
self.regressed_by_potential_bug_ids,
[
"id",
"groups",
"assigned_to",
"product",
"component",
],
bughandler=handler,
bugdata=bugs,
).wait()
return bugs
)
return [
self.bugs_store.get_bug_by_id(bug_id)
for bug_id in self.regressed_by_potential_bug_ids
]
@cached_property
def regressed_by_author(self) -> dict | None:
@ -213,8 +210,8 @@ class ClouseauDataAnalyzer:
return None
bug = self.regressed_by_potential_bugs[0]
assert bug["id"] == self.regressed_by
return bug["assigned_to_detail"]
assert bug.id == self.regressed_by
return bug.get_field("assigned_to_detail")
@cached_property
def crash_component(self) -> ComponentName:
@ -223,8 +220,7 @@ class ClouseauDataAnalyzer:
If there are multiple components, the value will be the default one.
"""
potential_components = {
ComponentName(bug["product"], bug["component"])
for bug in self.regressed_by_potential_bugs
bug.component for bug in self.regressed_by_potential_bugs
}
if len(potential_components) == 1:
return next(iter(potential_components))
@ -476,9 +472,10 @@ class SignatureAnalyzer(SocorroDataAnalyzer, ClouseauDataAnalyzer):
socorro_signature: dict,
num_total_crashes: int,
clouseau_reports: list[dict],
bugs_store: BugsStore,
):
SocorroDataAnalyzer.__init__(self, socorro_signature, num_total_crashes)
ClouseauDataAnalyzer.__init__(self, clouseau_reports)
ClouseauDataAnalyzer.__init__(self, clouseau_reports, bugs_store)
def _fetch_crash_reports(
self,
@ -550,8 +547,7 @@ class SignatureAnalyzer(SocorroDataAnalyzer, ClouseauDataAnalyzer):
- one of the potential regressors is a security bug
"""
return self.is_near_allocator_related_crash or any(
any("core-security" in group for group in bug["groups"])
for bug in self.regressed_by_potential_bugs
bug.is_security for bug in self.regressed_by_potential_bugs
)
@ -899,12 +895,14 @@ class SignaturesDataFetcher:
self._signatures.intersection_update(clouseau_reports.keys())
signatures, num_total_crashes = self.fetch_socorro_info()
bugs_store = BugsStore()
return [
SignatureAnalyzer(
signature,
num_total_crashes,
clouseau_reports[signature["term"]],
bugs_store,
)
for signature in signatures
]