tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

commit 2d763ae17a22d3c3ced19e000ae2219015c47900
parent 56b805b2dc4f18718173577886959ca75ddf160b
Author: Florian Quèze <florian@queze.net>
Date:   Tue,  2 Dec 2025 06:54:05 +0000

Bug 2003359 - include failure message and crash signatures in the xpcshell-date.json files, r=jmaher.

Differential Revision: https://phabricator.services.mozilla.com/D274637

Diffstat:
Mtesting/timings/JSON_FORMAT.md | 14+++++++++++---
Mtesting/timings/fetch-xpcshell-data.js | 17+++++++++++------
Mtesting/timings/profile-worker.js | 55++++++++++++++++++++++++++++++++++++++++++++++++++++---
3 files changed, 74 insertions(+), 12 deletions(-)

diff --git a/testing/timings/JSON_FORMAT.md b/testing/timings/JSON_FORMAT.md @@ -85,9 +85,10 @@ String tables for efficient storage. All strings are deduplicated and stored onc "XPPf5b1DRJrcBndDp9o74x.1", // Retry 1 ... ], - "messages": [ // SKIP status messages (only for tests that were skipped) + "messages": [ // Test messages (for SKIP and FAIL statuses) "skip-if: os == 'linux'", "disabled due to bug 123456", + "Expected 5, got 10", // Failure message ... ], "crashSignatures": [ // Crash signatures (only for crashed tests) @@ -162,9 +163,16 @@ Each `testRuns[testId][statusId]` contains data for all runs of that test with t "taskIdIds": [45, 67, ...], "durations": [0, 0, ...], "timestamps": [100, 200, ...], - "messageIds": [5, 5, ...] // Only present for SKIP status - indices into tables.messages (null if no message) + "messageIds": [5, 5, ...] // Present for SKIP and FAIL statuses - indices into tables.messages (null if no message) }, - // statusId 3 (e.g., "CRASH") + // statusId 3 (e.g., "FAIL-PARALLEL") + { + "taskIdIds": [78, ...], + "durations": [1234, ...], + "timestamps": [250, ...], + "messageIds": [12, ...] // Present for SKIP and FAIL statuses - indices into tables.messages (null if no message) + }, + // statusId 4 (e.g., "CRASH") { "taskIdIds": [89, ...], "durations": [5678, ...], diff --git a/testing/timings/fetch-xpcshell-data.js b/testing/timings/fetch-xpcshell-data.js @@ -271,6 +271,11 @@ async function processJobsWithWorkers(jobs, targetDate = null) { }); } +// Helper function to determine if a status should include message data +function shouldIncludeMessage(status) { + return status === "SKIP" || status.startsWith("FAIL"); +} + // Create string tables and store raw data efficiently function createDataTables(jobResults) { const tables = { @@ -387,8 +392,8 @@ function createDataTables(jobResults) { durations: [], timestamps: [], }; - // Only include messageIds array for SKIP status - if (timing.status === "SKIP") { + // Include messageIds array for statuses that should have messages + if (shouldIncludeMessage(timing.status)) { statusGroup.messageIds = []; } // Only include crash data arrays for CRASH status @@ -404,8 +409,8 @@ function createDataTables(jobResults) { statusGroup.durations.push(Math.round(timing.duration)); statusGroup.timestamps.push(timing.timestamp); - // Store message ID for SKIP status (or null if no message) - if (timing.status === "SKIP") { + // Store message ID for statuses that should include messages (or null if no message) + if (shouldIncludeMessage(timing.status)) { const messageId = timing.message ? findStringIndex("messages", timing.message) : null; @@ -582,7 +587,7 @@ function sortStringTablesByFrequency(dataStructure) { timestamps: statusGroup.timestamps, }; - // Remap message IDs for SKIP status + // Remap message IDs for status groups that have messages if (statusGroup.messageIds) { remapped.messageIds = statusGroup.messageIds.map(oldId => oldId === null ? null : indexMaps.messages.get(oldId) @@ -852,7 +857,7 @@ async function processJobsAndCreateData( if (statusGroup.minidumps) { run.minidump = statusGroup.minidumps[i]; } - // Include message data if this is a SKIP status group + // Include message data if this status group has messages if (statusGroup.messageIds) { run.messageId = statusGroup.messageIds[i]; } diff --git a/testing/timings/profile-worker.js b/testing/timings/profile-worker.js @@ -7,6 +7,13 @@ const fs = require("fs"); const path = require("path"); const zlib = require("zlib"); +// Normalize failure messages to remove task-specific and time-specific information +function normalizeMessage(message) { + return message + ?.replace(/task_\d+/g, "task_id") + .replace(/\nRejection date: [^\n]+/g, ""); +} + // Extract parallel execution time ranges from markers function extractParallelRanges(markers) { const parallelRanges = []; @@ -150,6 +157,34 @@ function extractTestTimings(profile) { }); } + // Extract TestStatus markers (FAIL, ERROR) for failure messages + const failStringId = stringArray.indexOf("FAIL"); + const errorStringId = stringArray.indexOf("ERROR"); + const testStatusMarkers = []; + + for (let i = 0; i < markers.length; i++) { + const nameId = markers.name[i]; + if (nameId !== failStringId && nameId !== errorStringId) { + continue; + } + const data = markers.data[i]; + if (!data || data.type !== "TestStatus" || !data.test) { + continue; + } + + testStatusMarkers.push({ + test: data.test, + nameId, + time: markers.startTime[i], + message: normalizeMessage(data.message), + }); + } + + // Sort TestStatus markers by test and then time for efficient lookup + testStatusMarkers.sort( + (a, b) => a.test.localeCompare(b.test) || a.time - b.time + ); + const testStringId = stringArray.indexOf("test"); const timings = []; @@ -170,10 +205,13 @@ function extractTestTimings(profile) { // Handle both structured and plain text logs if (data.type === "Test") { // Structured log format - testPath = data.test || data.name; + const fullTestId = data.test || data.name; + testPath = fullTestId; status = data.status || "UNKNOWN"; - // Normalize line breaks in message (convert \r\n to \n) - message = data.message ? data.message.replace(/\r\n/g, "\n") : null; + // Normalize line breaks in message (convert \r\n to \n) and apply normalizations + message = normalizeMessage( + data.message ? data.message.replace(/\r\n/g, "\n") : null + ); // Check if this is an expected failure (FAIL status but green color) if (status === "FAIL" && data.color === "green") { @@ -194,6 +232,17 @@ function extractTestTimings(profile) { } // Keep other statuses as-is + // For failure statuses, look up the message from TestStatus markers + if (status.startsWith("FAIL")) { + const testStartTime = markers.startTime[i]; + const statusMarker = testStatusMarkers.find( + m => m.test === fullTestId && m.time >= testStartTime + ); + if (statusMarker && statusMarker.message) { + message = statusMarker.message; + } + } + // Extract the actual test file path from the test field // Format: "xpcshell-parent-process.toml:dom/indexedDB/test/unit/test_fileListUpgrade.js" if (testPath && testPath.includes(":")) {