tor-browser

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

commit 196adee60bc254da57bd343c4e8aec1797a6619f
parent fdf2c194a37b424be8e39bb6e6d408f8ee6bca48
Author: Segun Famisa <sfamisa@mozilla.com>
Date:   Fri,  5 Dec 2025 15:36:14 +0000

Bug 1968045 - Upload memory leak traces as artifacts r=aaronmt,tthibaud

This patch introduces the ability to collect memory leak artifacts from Firebase Test Lab (FTL) after a test run.

The `test-lab.py` script is updated with a new `--artifact_type` argument. This allows specifying the type of artifact to be collected, making the process more generic. The `copy-artifacts-from-ftl.py` script is enhanced to recognize and process `memory_leaks` as a new artifact type, copying any found leak files to the designated worker directory.

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

Diffstat:
Mtaskcluster/kinds/ui-test-apk/kind.yml | 2+-
Mtaskcluster/scripts/tests/copy-artifacts-from-ftl.py | 25++++++++++++++++++++++++-
Mtaskcluster/scripts/tests/test-lab.py | 28+++++++++++++++++++++-------
3 files changed, 46 insertions(+), 9 deletions(-)

diff --git a/taskcluster/kinds/ui-test-apk/kind.yml b/taskcluster/kinds/ui-test-apk/kind.yml @@ -349,7 +349,7 @@ tasks: path: mobile/android/test_infra/.firebase_token.json json: true commands: - - [python3, taskcluster/scripts/tests/test-lab.py, fenix/arm64-v8a-detect-leaks.yml, /builds/worker/fetches/target.arm64-v8a.apk, --apk_test, /builds/worker/fetches/target.noarch.apk] + - [python3, taskcluster/scripts/tests/test-lab.py, fenix/arm64-v8a-detect-leaks.yml, /builds/worker/fetches/target.arm64-v8a.apk, --apk_test, /builds/worker/fetches/target.noarch.apk, --artifact_type, "memory_leaks"] treeherder: platform: 'fenix-android-all/opt' symbol: fenix-debug(detect-leaks-arm) diff --git a/taskcluster/scripts/tests/copy-artifacts-from-ftl.py b/taskcluster/scripts/tests/copy-artifacts-from-ftl.py @@ -60,6 +60,7 @@ class Worker(Enum): BASELINE_PROFILE_DIR = "/builds/worker/workspace/baselineProfile" MACROBENCHMARK_DEST = "/builds/worker/artifacts/build/macrobenchmark.json" MACROBENCHMARK_DIR = "/builds/worker/artifacts/build/macrobenchmark" + MEMORY_LEAKS_DIR = "/builds/worker/artifacts/build/memory_leaks" ARTIFACTS_DIR = "/builds/worker/artifacts/build" @@ -76,6 +77,7 @@ class ArtifactType(Enum): "artifacts/sdcard/Android/media/org.mozilla.fenix.benchmark/*benchmarkData.json" ) MATRIX_IDS = "matrix_ids.json" + MEMORY_LEAKS = "artifacts/sdcard/Download/memory_leaks/*.txt" def load_matrix_ids_artifact(matrix_file_path): @@ -254,6 +256,8 @@ def process_artifacts(artifact_type): return process_baseline_profile_artifacts(root_gcs_path, device_names) elif artifact_type == ArtifactType.MACROBENCHMARK: return process_macrobenchmark_artifact(root_gcs_path, device_names) + elif artifact_type == ArtifactType.MEMORY_LEAKS: + return process_memory_leaks_artifacts(root_gcs_path, device_names) else: return process_crash_artifacts(root_gcs_path, device_names) @@ -313,6 +317,21 @@ def process_macrobenchmark_artifact(root_gcs_path, device_names): downloaded_files.append(dest_path) +def process_memory_leaks_artifacts(root_gcs_path, device_names): + for device in device_names: + artifacts = fetch_artifacts( + root_gcs_path, device, ArtifactType.MEMORY_LEAKS.value + ) + if not artifacts: + logging.info(f"No artifacts found for device: {device}") + continue + for artifact in artifacts: + base_name = os.path.basename(artifact) + dest_path = os.path.join(Worker.MEMORY_LEAKS_DIR.value, f"leak_{base_name}") + + gsutil_cp(artifact, dest_path) + + def process_crash_artifacts(root_gcs_path, failed_device_names): crashes_reported = 0 for device in failed_device_names: @@ -350,8 +369,12 @@ def main(): process_artifacts(ArtifactType.MACROBENCHMARK) elif artifact_type_arg == "crash_log": process_artifacts(ArtifactType.CRASH_LOG) + elif artifact_type_arg == "memory_leaks": + process_artifacts(ArtifactType.MEMORY_LEAKS) else: - logging.error("Invalid artifact type. Use 'baseline_profile' or 'crash_log'.") + logging.error( + "Invalid artifact type. Use one of 'baseline_profile', 'macrobenchmark', 'crash_log or 'memory_leaks." + ) sys.exit(1) diff --git a/taskcluster/scripts/tests/test-lab.py b/taskcluster/scripts/tests/test-lab.py @@ -148,24 +148,25 @@ def execute_tests( return exit_code -def process_results(flank_config: str, test_type: str = "instrumentation") -> None: +def process_results( + flank_config: str, test_type: str = "instrumentation", artifact_type: str = None +) -> None: """Process and parse test results. Args: flank_config: The YML configuration for Flank to use e.g, automation/taskcluster/androidTest/flank-<config>.yml test_type: The type of test executed: 'instrumentation' or 'robo' + artifact_type: The type of the artifacts to copy after the test run """ parse_junit_results_artifact = os.path.join(SCRIPT_DIR, "parse-junit-results.py") - copy_robo_crash_artifacts_script = os.path.join( - SCRIPT_DIR, "copy-artifacts-from-ftl.py" - ) + copy_artifacts_script = os.path.join(SCRIPT_DIR, "copy-artifacts-from-ftl.py") generate_flaky_report_script = os.path.join( SCRIPT_DIR, "generate-flaky-report-from-ftl.py" ) os.chmod(parse_junit_results_artifact, 0o755) - os.chmod(copy_robo_crash_artifacts_script, 0o755) + os.chmod(copy_artifacts_script, 0o755) os.chmod(generate_flaky_report_script, 0o755) # Process the results differently based on the test type: instrumentation or robo @@ -183,8 +184,12 @@ def process_results(flank_config: str, test_type: str = "instrumentation") -> No "flank.log", ) + # Copy artifacts if specified + if artifact_type: + run_command([copy_artifacts_script, artifact_type]) + if test_type == "robo": - run_command([copy_robo_crash_artifacts_script, "crash_log"]) + run_command([copy_artifacts_script, "crash_log"]) def main(): @@ -205,6 +210,11 @@ def main(): help="Absolute path to a Android APK androidTest package", default=None, ) + parser.add_argument( + "--artifact_type", + help="Type of artifact to copy after running the tests", + default=None, + ) args = parser.parse_args() setup_environment() @@ -219,7 +229,11 @@ def main(): # Determine the instrumentation type to process the results differently instrumentation_type = "instrumentation" if args.apk_test else "robo" - process_results(flank_config=args.flank_config, test_type=instrumentation_type) + process_results( + flank_config=args.flank_config, + test_type=instrumentation_type, + artifact_type=args.artifact_type, + ) sys.exit(exit_code)