qemu-devel.nongnu.org archive mirror
 help / color / mirror / Atom feed
From: "Alex Bennée" <alex.bennee@linaro.org>
To: qemu-devel@nongnu.org
Cc: "Palmer Dabbelt" <palmer@dabbelt.com>,
	"Alexandre Iooss" <erdnaxe@crans.org>,
	"Mahmoud Mandour" <ma.mandourr@gmail.com>,
	qemu-riscv@nongnu.org,
	"Daniel Henrique Barboza" <dbarboza@ventanamicro.com>,
	"Thomas Huth" <thuth@redhat.com>,
	"Peter Maydell" <peter.maydell@linaro.org>,
	"Alex Bennée" <alex.bennee@linaro.org>,
	"Alistair Francis" <alistair.francis@wdc.com>,
	qemu-arm@nongnu.org, "Philippe Mathieu-Daudé" <philmd@linaro.org>,
	"Weiwei Li" <liwei1518@gmail.com>,
	"Pierrick Bouvier" <pierrick.bouvier@linaro.org>,
	"Liu Zhiwei" <zhiwei_liu@linux.alibaba.com>
Subject: [PATCH 02/25] scripts/ci: add gitlab-failure-analysis script
Date: Mon, 22 Sep 2025 10:36:47 +0100	[thread overview]
Message-ID: <20250922093711.2768983-3-alex.bennee@linaro.org> (raw)
In-Reply-To: <20250922093711.2768983-1-alex.bennee@linaro.org>

This is a script designed to collect data from multiple pipelines and
analyse the failure modes they have. By default it will probe the last
3 failed jobs on the staging branch. However this can all be
controlled by the CLI:

  ./scripts/ci/gitlab-failure-analysis --count 2 --branch=testing/next --id 39915562 --status=
  running pipeline 2028486060, total jobs 125, skipped 5, failed 0,  39742 tests, 0 failed tests
  success pipeline 2015018135, total jobs 125, skipped 5, failed 0,  49219 tests, 0 failed tests

You can also skip failing jobs and just dump the tests:

  ./scripts/ci/gitlab-failure-analysis --branch= --id 39915562 --status= --skip-jobs --pipeline 1946202491 1919542960
  failed pipeline 1946202491, total jobs 127, skipped 5, failed 26,  38742 tests, 278 skipped tests, 2 failed tests
    Failed test qemu.qemu:qtest+qtest-s390x / qtest-s390x/boot-serial-test, check-system-opensuse, 1 /s390x/boot-serial/s390-ccw-virtio - FATAL-ERROR: Failed to find expected string. Please check '/tmp/qtest-boot-serial-sW77EA3'
    Failed test qemu.qemu:qtest+qtest-aarch64 / qtest-aarch64/arm-cpu-features, check-system-opensuse, 1 /aarch64/arm/query-cpu-model-expansion - ERROR:../tests/qtest/arm-cpu-features.c:459:test_query_cpu_model_expansion: assertion failed (_error == "The CPU type 'host' requires KVM"): ("The CPU type 'host' requires hardware accelerator" == "The CPU type 'host' requires KVM")
  failed pipeline 1919542960, total jobs 127, skipped 5, failed 2,  48753 tests, 441 skipped tests, 1 failed tests
    Failed test qemu.qemu:unit / test-aio, msys2-64bit, 12 /aio/timer/schedule - ERROR:../tests/unit/test-aio.c:413:test_timer_schedule: assertion failed: (aio_poll(ctx, true))

Signed-off-by: Alex Bennée <alex.bennee@linaro.org>

---
v2
  - allow status selection, handle empty strings as None
  - allow individual pipeline selection
  - extract individual tests
  - allow skipping of jobs
---
 scripts/ci/gitlab-failure-analysis | 117 +++++++++++++++++++++++++++++
 1 file changed, 117 insertions(+)
 create mode 100755 scripts/ci/gitlab-failure-analysis

diff --git a/scripts/ci/gitlab-failure-analysis b/scripts/ci/gitlab-failure-analysis
new file mode 100755
index 00000000000..906725be973
--- /dev/null
+++ b/scripts/ci/gitlab-failure-analysis
@@ -0,0 +1,117 @@
+#!/usr/bin/env python3
+#
+# A script to analyse failures in the gitlab pipelines. It requires an
+# API key from gitlab with the following permissions:
+#  - api
+#  - read_repository
+#  - read_user
+#
+
+import argparse
+import gitlab
+import os
+
+#
+# Arguments
+#
+class NoneForEmptyStringAction(argparse.Action):
+    def __call__(self, parser, namespace, value, option_string=None):
+        if value == '':
+            setattr(namespace, self.dest, None)
+        else:
+            setattr(namespace, self.dest, value)
+
+
+parser = argparse.ArgumentParser(description="Analyse failed GitLab CI runs.")
+
+parser.add_argument("--gitlab",
+                    default="https://gitlab.com",
+                    help="GitLab instance URL (default: https://gitlab.com).")
+parser.add_argument("--id", default=11167699,
+                    type=int,
+                    help="GitLab project id (default: 11167699 for qemu-project/qemu)")
+parser.add_argument("--token",
+                    default=os.getenv("GITLAB_TOKEN"),
+                    help="Your personal access token with 'api' scope.")
+parser.add_argument("--branch",
+                    type=str,
+                    default="staging",
+                    action=NoneForEmptyStringAction,
+                    help="The name of the branch (default: 'staging')")
+parser.add_argument("--status",
+                    type=str,
+                    action=NoneForEmptyStringAction,
+                    default="failed",
+                    help="Filter by branch status (default: 'failed')")
+parser.add_argument("--count", type=int,
+                    default=3,
+                    help="The number of failed runs to fetch.")
+parser.add_argument("--skip-jobs",
+                    default=False,
+                    action='store_true',
+                    help="Skip dumping the job info")
+parser.add_argument("--pipeline", type=int,
+                    nargs="+",
+                    default=None,
+                    help="Explicit pipeline ID(s) to fetch.")
+
+
+if __name__ == "__main__":
+    args = parser.parse_args()
+
+    gl = gitlab.Gitlab(url=args.gitlab, private_token=args.token)
+    project = gl.projects.get(args.id)
+
+
+    pipelines_to_process = []
+
+    # Use explicit pipeline IDs if provided, otherwise fetch a list
+    if args.pipeline:
+        args.count = len(args.pipeline)
+        for p_id in args.pipeline:
+            pipelines_to_process.append(project.pipelines.get(p_id))
+    else:
+        # Use an iterator to fetch the pipelines
+        pipe_iter = project.pipelines.list(iterator=True,
+                                           status=args.status,
+                                           ref=args.branch)
+        # Check each failed pipeline
+        pipelines_to_process = [next(pipe_iter) for _ in range(args.count)]
+
+    # Check each pipeline
+    for p in pipelines_to_process:
+
+        jobs = p.jobs.list(get_all=True)
+        failed_jobs = [j for j in jobs if j.status == "failed"]
+        skipped_jobs = [j for j in jobs if j.status == "skipped"]
+        manual_jobs = [j for j in jobs if j.status == "manual"]
+
+        trs = p.test_report_summary.get()
+        total = trs.total["count"]
+        skipped = trs.total["skipped"]
+        failed = trs.total["failed"]
+
+        print(f"{p.status} pipeline {p.id}, total jobs {len(jobs)}, "
+              f"skipped {len(skipped_jobs)}, "
+              f"failed {len(failed_jobs)}, ",
+              f"{total} tests, "
+              f"{skipped} skipped tests, "
+              f"{failed} failed tests")
+
+        if not args.skip_jobs:
+            for j in failed_jobs:
+                print(f"  Failed job {j.id}, {j.name}, {j.web_url}")
+
+        # It seems we can only extract failing tests from the full
+        # test report, maybe there is some way to filter it.
+
+        if failed > 0:
+            ftr = p.test_report.get()
+            failed_suites = [s for s in ftr.test_suites if
+                             s["failed_count"] > 0]
+            for fs in failed_suites:
+                name = fs["name"]
+                tests = fs["test_cases"]
+                failed_tests = [t for t in tests if t["status"] == 'failed']
+                for t in failed_tests:
+                    print(f"  Failed test {t["classname"]}, {name}, {t["name"]}")
-- 
2.47.3



  parent reply	other threads:[~2025-09-22  9:42 UTC|newest]

Thread overview: 32+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2025-09-22  9:36 [PATCH 00/25] September maintainer updates (docs, plugins, semihosting) pre-PR Alex Bennée
2025-09-22  9:36 ` [PATCH 01/25] docs/devel: fix typo in code-provenance.rst Alex Bennée
2025-09-22 11:32   ` Thomas Huth
2025-09-22 19:37   ` Richard Henderson
2025-09-22  9:36 ` Alex Bennée [this message]
2025-09-22  9:36 ` [PATCH 03/25] checkpatch: Ignore removed lines in license check Alex Bennée
2025-09-22  9:36 ` [PATCH 04/25] semihosting/syscalls: compile once in system and per target for user mode Alex Bennée
2025-09-22  9:36 ` [PATCH 05/25] semihosting/syscalls: replace uint64_t with vaddr where appropriate Alex Bennée
2025-09-22  9:36 ` [PATCH 06/25] semihosting/guestfd: compile once for system/user Alex Bennée
2025-09-22  9:36 ` [PATCH 07/25] semihosting/arm-compat-semi: change common_semi_sys_exit_extended Alex Bennée
2025-09-22  9:36 ` [PATCH 08/25] target/riscv/common-semi-target: remove sizeof(target_ulong) Alex Bennée
2025-09-22  9:36 ` [PATCH 09/25] target/{arm, riscv}/common-semi-target: eradicate target_ulong Alex Bennée
2025-09-22  9:36 ` [PATCH 10/25] include/semihosting/common-semi: extract common_semi API Alex Bennée
2025-09-22  9:36 ` [PATCH 11/25] semihosting/arm-compat-semi: eradicate sizeof(target_ulong) Alex Bennée
2025-09-22 11:52   ` Philippe Mathieu-Daudé
2025-09-22  9:36 ` [PATCH 12/25] semihosting/arm-compat-semi: replace target_ulong with vaddr Alex Bennée
2025-09-22 11:53   ` Philippe Mathieu-Daudé
2025-09-22 12:43     ` Alex Bennée
2025-09-25 21:13       ` Pierrick Bouvier
2025-09-22  9:36 ` [PATCH 13/25] semihosting/arm-compat-semi: eradicate target_long Alex Bennée
2025-09-22  9:36 ` [PATCH 14/25] semihosting/arm-compat-semi: remove dependency on cpu.h Alex Bennée
2025-09-22  9:37 ` [PATCH 15/25] semihosting/arm-compat-semi: compile once in system and per target for user mode Alex Bennée
2025-09-22  9:37 ` [PATCH 16/25] contrib/plugins/execlog: Explicitly check for qemu_plugin_read_register() failure Alex Bennée
2025-09-22  9:37 ` [PATCH 17/25] contrib/plugins/uftrace: skeleton file Alex Bennée
2025-09-22  9:37 ` [PATCH 18/25] contrib/plugins/uftrace: define cpu operations and implement aarch64 Alex Bennée
2025-09-22  9:37 ` [PATCH 19/25] contrib/plugins/uftrace: track callstack Alex Bennée
2025-09-22  9:37 ` [PATCH 20/25] contrib/plugins/uftrace: implement tracing Alex Bennée
2025-09-22  9:37 ` [PATCH 21/25] contrib/plugins/uftrace: implement privilege level tracing Alex Bennée
2025-09-22  9:37 ` [PATCH 22/25] contrib/plugins/uftrace: generate additional files for uftrace Alex Bennée
2025-09-22  9:37 ` [PATCH 23/25] contrib/plugins/uftrace: implement x64 support Alex Bennée
2025-09-22  9:37 ` [PATCH 24/25] contrib/plugins/uftrace_symbols.py Alex Bennée
2025-09-22  9:37 ` [PATCH 25/25] contrib/plugins/uftrace: add documentation Alex Bennée

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20250922093711.2768983-3-alex.bennee@linaro.org \
    --to=alex.bennee@linaro.org \
    --cc=alistair.francis@wdc.com \
    --cc=dbarboza@ventanamicro.com \
    --cc=erdnaxe@crans.org \
    --cc=liwei1518@gmail.com \
    --cc=ma.mandourr@gmail.com \
    --cc=palmer@dabbelt.com \
    --cc=peter.maydell@linaro.org \
    --cc=philmd@linaro.org \
    --cc=pierrick.bouvier@linaro.org \
    --cc=qemu-arm@nongnu.org \
    --cc=qemu-devel@nongnu.org \
    --cc=qemu-riscv@nongnu.org \
    --cc=thuth@redhat.com \
    --cc=zhiwei_liu@linux.alibaba.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).