mirror of
https://github.com/flatpak/flatpak.git
synced 2026-10-06 21:12:44 -04:00
Help messages were inconsistent about whether they ended with periods or not. I chose to remove the periods from those that had them rather than the other way around because the help messages automatically generated by argparse do not have periods.
121 lines
4.5 KiB
Python
Executable File
121 lines
4.5 KiB
Python
Executable File
#!/usr/bin/env python3
|
|
import sys
|
|
|
|
if sys.version_info < (3, 10):
|
|
print(
|
|
f'A minimum Python version of at least 3.10 is required, '
|
|
f'{sys.version_info.major}.{sys.version_info.minor}.{sys.version_info.micro} was found',
|
|
file=sys.stderr
|
|
)
|
|
sys.exit(1)
|
|
|
|
import argparse
|
|
import re
|
|
import shutil
|
|
import subprocess
|
|
import shlex
|
|
import tempfile
|
|
from dataclasses import dataclass
|
|
|
|
class CoreDumperArgs:
|
|
@dataclass
|
|
class AppId:
|
|
app_id: str
|
|
|
|
@dataclass
|
|
class BuildDir:
|
|
directory: str
|
|
|
|
def __init__(self, ns: argparse.Namespace) -> None:
|
|
if ns.app is None and ns.build_directory is None:
|
|
raise ValueError("Either `--build-directory` or `APP` must be specified.")
|
|
if ns.app is not None and ns.build_directory is not None:
|
|
raise ValueError("`--build-directory` and `APP` are mutually exclusive.")
|
|
if ns.app is not None:
|
|
self.target = self.AppId(ns.app)
|
|
else:
|
|
self.target = self.BuildDir(ns.build_directory)
|
|
self.coredumpctl_matches: str = ns.coredumpctl_matches
|
|
self.extra_flatpak_args: str = ns.extra_flatpak_args
|
|
self.gdb_arguments: str = ns.gdb_arguments
|
|
|
|
def run(args: CoreDumperArgs) -> int:
|
|
if not shutil.which("coredumpctl"):
|
|
print("'coredumpctl' not present on the system, can't run.",
|
|
file=sys.stderr)
|
|
return 1
|
|
|
|
match args.target:
|
|
case CoreDumperArgs.AppId(app_id):
|
|
flatpak_subcommand = "run"
|
|
target_args = ["--command=gdb", "--devel", app_id]
|
|
case CoreDumperArgs.BuildDir(build_dir):
|
|
flatpak_subcommand = "build"
|
|
target_args = [build_dir, "gdb"]
|
|
|
|
# We need access to the host from the sandbox to run.
|
|
flatpak_command = [
|
|
"flatpak",
|
|
flatpak_subcommand,
|
|
"--filesystem=home",
|
|
f"--filesystem={tempfile.gettempdir()}",
|
|
*shlex.split(args.extra_flatpak_args),
|
|
*target_args
|
|
]
|
|
|
|
with tempfile.NamedTemporaryFile() as coredump:
|
|
dumpres = subprocess.run(
|
|
["coredumpctl", "dump"] + shlex.split(args.coredumpctl_matches),
|
|
stdout=coredump, stderr=subprocess.PIPE, text=True
|
|
)
|
|
if dumpres.returncode != 0:
|
|
print("Failed to retrieve coredump via coredumpctl.", file=sys.stderr)
|
|
if dumpres.stderr:
|
|
print(f"Reason:\n{dumpres.stderr}", file=sys.stderr)
|
|
|
|
return dumpres.returncode
|
|
|
|
matches = re.findall(r".*Executable: (.*)", dumpres.stderr)
|
|
if len(matches) != 1:
|
|
print(f"Could not determine executable from coredumpctl output "
|
|
f"(found {len(matches)} 'Executable:' line(s)).",
|
|
file=sys.stderr)
|
|
return 1
|
|
executable = matches[0]
|
|
if not executable.startswith(("/newroot/", "/app/")):
|
|
print(f"Executable {executable} doesn't seem to be a flatpaked application.",
|
|
file=sys.stderr)
|
|
executable = executable.replace("/newroot", "", 1)
|
|
flatpak_command.extend([executable, coredump.name])
|
|
flatpak_command.extend(shlex.split(args.gdb_arguments))
|
|
|
|
print(f"Running: `{shlex.join(flatpak_command)}`")
|
|
return subprocess.run(flatpak_command).returncode
|
|
|
|
if __name__ == "__main__":
|
|
parser = argparse.ArgumentParser(description=
|
|
"Debug in gdb an application that crashed inside flatpak."
|
|
" It uses (and thus requires) coredumpctl to retrieve the coredump file.")
|
|
parser.add_argument("-b", "--build-directory", default=None,
|
|
help="The build directory to use. It allows you to retrieve a coredump"
|
|
" for applications being built")
|
|
parser.add_argument("--extra-flatpak-args", default="",
|
|
help="Extra argument to pass to flatpak")
|
|
parser.add_argument("app", nargs="?",
|
|
help="The flatpak application to use. eg. `org.gnome.Epiphany//3.28`")
|
|
parser.add_argument("-m", "--coredumpctl-matches", default="",
|
|
help="Coredumpctl matches, see `man coredumpctl` for more information")
|
|
parser.add_argument("--gdb-arguments", default="",
|
|
help="Arguments to pass to gdb")
|
|
|
|
args = parser.parse_args()
|
|
try:
|
|
validated_args = CoreDumperArgs(args)
|
|
except ValueError as e:
|
|
print(e, file=sys.stderr)
|
|
parser.print_help(sys.stderr)
|
|
sys.exit(1)
|
|
|
|
exit_code = run(validated_args)
|
|
sys.exit(exit_code)
|