commit c7555adfab9c73d02330ca150c9092474ff00c33
parent d42e513eece97d4533b37fee646a2918a7fd1349
Author: Florian Quèze <florian@queze.net>
Date: Tue, 7 Oct 2025 06:56:15 +0000
Bug 1992181 - Resource usage profiles should show test_status and log messages, r=ahal.
Differential Revision: https://phabricator.services.mozilla.com/D267451
Diffstat:
3 files changed, 119 insertions(+), 11 deletions(-)
diff --git a/testing/mozbase/mozlog/mozlog/handlers/resourcehandler.py b/testing/mozbase/mozlog/mozlog/handlers/resourcehandler.py
@@ -53,7 +53,8 @@ class ResourceHandler(LogHandler):
def test_end(self, data):
SystemResourceMonitor.end_test(data)
+ def test_status(self, data):
+ SystemResourceMonitor.test_status(data)
+
def log(self, data):
- level = data.get("level").upper()
- time = self.resources.convert_to_monotonic_time(data.get("time") / 1000)
- SystemResourceMonitor.record_marker(level, time, time, data.get("message"))
+ SystemResourceMonitor.test_status(data)
diff --git a/testing/mozbase/mozsystemmonitor/mozsystemmonitor/resourcemonitor.py b/testing/mozbase/mozsystemmonitor/mozsystemmonitor/resourcemonitor.py
@@ -507,14 +507,24 @@ class SystemResourceMonitor:
# Methods to record events alongside the monitored data.
@staticmethod
- def record_event(name):
+ def record_event(name, timestamp=None, data=None):
"""Record an event as occuring now.
Events are actions that occur at a specific point in time. If you are
looking for an action that has a duration, see the phase API below.
+
+ Args:
+ name: Name of the event (string)
+ timestamp: Optional timestamp (monotonic time). If not provided, uses current time.
+ data: Optional marker payload dictionary (e.g., {"type": "TestStatus", ...})
"""
if SystemResourceMonitor.instance:
- SystemResourceMonitor.instance.events.append((time.monotonic(), name))
+ if timestamp is None:
+ timestamp = time.monotonic()
+ if data:
+ SystemResourceMonitor.instance.events.append((timestamp, name, data))
+ else:
+ SystemResourceMonitor.instance.events.append((timestamp, name))
@staticmethod
def record_marker(name, start, end, data):
@@ -612,6 +622,8 @@ class SystemResourceMonitor:
if status in ("SKIP", "TIMEOUT"):
marker_data["color"] = "yellow"
+ if message:
+ marker_data["message"] = message
elif status in ("CRASH", "ERROR"):
marker_data["color"] = "red"
elif expected is None and not will_retry:
@@ -622,6 +634,54 @@ class SystemResourceMonitor:
SystemResourceMonitor.instance.record_marker("test", start, end, marker_data)
+ @staticmethod
+ def test_status(data):
+ """Record a test_status event with color based on status.
+
+ Args:
+ data: Dictionary containing test_status data including:
+ - "test": test name (optional)
+ - "subtest": subtest name (optional)
+ - "status" or "level": status ("PASS", "FAIL", "ERROR", "INFO", etc.)
+ - "time": timestamp in milliseconds
+ - "message": optional message
+ """
+ if not SystemResourceMonitor.instance:
+ return
+
+ time_sec = data["time"] / 1000
+ timestamp = SystemResourceMonitor.instance.convert_to_monotonic_time(time_sec)
+
+ # Accept either "status" or "level" field
+ status = (data.get("status") or data.get("level")).upper()
+
+ # Create marker data
+ marker_data = {
+ "type": "TestStatus",
+ }
+
+ test_name = data.get("test")
+ if test_name:
+ marker_data["test"] = test_name
+
+ subtest = data.get("subtest")
+ if subtest:
+ marker_data["subtest"] = subtest
+
+ # Determine color based on status
+ if status == "PASS":
+ marker_data["color"] = "green"
+ elif status == "FAIL":
+ marker_data["color"] = "orange"
+ elif status == "ERROR":
+ marker_data["color"] = "red"
+
+ message = data.get("message")
+ if message:
+ marker_data["message"] = message
+
+ SystemResourceMonitor.record_event(status, timestamp, marker_data)
+
@contextmanager
def phase(self, name):
"""Context manager for recording an active phase."""
@@ -1030,7 +1090,7 @@ class SystemResourceMonitor:
{
"name": "Test",
"tooltipLabel": "{marker.data.name}",
- "tableLabel": "{marker.data.test} — {marker.data.status}",
+ "tableLabel": "{marker.data.status} — {marker.data.test}",
"chartLabel": "{marker.data.name}",
"display": ["marker-chart", "marker-table"],
"colorField": "color",
@@ -1057,6 +1117,38 @@ class SystemResourceMonitor:
"format": "string",
},
{
+ "key": "message",
+ "label": "Message",
+ "format": "string",
+ },
+ {
+ "key": "color",
+ "hidden": True,
+ },
+ ],
+ },
+ {
+ "name": "TestStatus",
+ "tableLabel": "{marker.data.message} — {marker.data.test} {marker.data.subtest}",
+ "display": ["marker-chart", "marker-table"],
+ "colorField": "color",
+ "data": [
+ {
+ "key": "message",
+ "label": "Message",
+ "format": "string",
+ },
+ {
+ "key": "test",
+ "label": "Test Name",
+ "format": "string",
+ },
+ {
+ "key": "subtest",
+ "label": "Subtest",
+ "format": "string",
+ },
+ {
"key": "color",
"hidden": True,
},
@@ -1423,14 +1515,27 @@ class SystemResourceMonitor:
add_marker(get_string_index(name), start, end, markerData, TASK_CATEGORY, 3)
if self.events:
event_string_index = get_string_index("Event")
- for event_time, text in self.events:
- if text:
+ for event in self.events:
+ if len(event) == 3:
+ # Event with payload: (time, name, data)
+ event_time, name, data = event
+ add_marker(
+ get_string_index(name),
+ event_time,
+ None,
+ data,
+ OTHER_CATEGORY,
+ 3,
+ )
+ elif len(event) == 2:
+ # Simple event: (time, text)
+ event_time, text = event
add_marker(
event_string_index,
event_time,
None,
{"type": "Text", "text": text},
- TASK_CATEGORY,
+ OTHER_CATEGORY,
3,
)
diff --git a/testing/mozharness/mozharness/mozilla/structuredlog.py b/testing/mozharness/mozharness/mozilla/structuredlog.py
@@ -106,6 +106,10 @@ class StructuredOutputParser(OutputParser):
SystemResourceMonitor.begin_test(data)
elif action == "test_end":
SystemResourceMonitor.end_test(data)
+ elif action == "test_status":
+ SystemResourceMonitor.test_status(data)
+ elif action == "log":
+ SystemResourceMonitor.test_status(data)
elif action == "suite_start":
SystemResourceMonitor.begin_marker("suite", data["source"])
elif action == "suite_end":
@@ -114,8 +118,6 @@ class StructuredOutputParser(OutputParser):
SystemResourceMonitor.begin_marker("test", data["name"])
elif action == "group_end":
SystemResourceMonitor.end_marker("test", data["name"])
- if line.startswith("TEST-UNEXPECTED-FAIL"):
- SystemResourceMonitor.record_event(line)
if action in ("log", "process_output"):
if action == "log":