308 lines
8.3 KiB
Python
Executable File
308 lines
8.3 KiB
Python
Executable File
#!/usr/bin/python3
|
|
|
|
"""Get Hardware Enablement related logs"""
|
|
|
|
# TODO: Address following pylint complaints
|
|
# pylint: disable=invalid-name,missing-function-docstring
|
|
|
|
import gzip
|
|
import os
|
|
import re
|
|
import shutil
|
|
import subprocess
|
|
import sys
|
|
import tempfile
|
|
import time
|
|
import zipfile
|
|
from argparse import ArgumentParser
|
|
from glob import glob
|
|
from io import BytesIO
|
|
|
|
import apport
|
|
from apport import hookutils
|
|
from problem_report import CompressedValue
|
|
|
|
opt_debug = False
|
|
|
|
|
|
# Apport helper routines
|
|
def debug(text):
|
|
if opt_debug:
|
|
print(f"{text}\n")
|
|
|
|
|
|
def attach_command_output(report, command_list, key):
|
|
debug(" ".join(command_list))
|
|
log = hookutils.command_output(command_list)
|
|
if not log or log[:5] == "Error":
|
|
return
|
|
report[key] = log
|
|
|
|
|
|
def attach_pathglob_as_zip(report, pathglob, key, data_filter=None, mode="b"):
|
|
"""Use zip file here because tarfile module in linux can't
|
|
properly handle file size 0 with content in /sys directory like
|
|
edid file. zipfile module works fine here. So we use it.
|
|
|
|
mode:
|
|
a: for ascii mode of data
|
|
b: for binary mode of data
|
|
"""
|
|
filelist = []
|
|
for pg in pathglob:
|
|
for file in glob(pg):
|
|
filelist.append(file)
|
|
|
|
zipf = BytesIO()
|
|
with zipfile.ZipFile(zipf, mode="w", compression=zipfile.ZIP_DEFLATED) as zipobj:
|
|
for f in filelist:
|
|
if opt_debug:
|
|
print(key, f)
|
|
if not os.path.isfile(f):
|
|
if opt_debug:
|
|
print(f, "is not a file")
|
|
continue
|
|
if mode == "a":
|
|
with open(f, encoding="ascii") as f_fd:
|
|
data = f_fd.read()
|
|
if data_filter is None:
|
|
zipobj.writestr(f, data)
|
|
else:
|
|
zipobj.writestr(f, data_filter(data))
|
|
else:
|
|
zipobj.write(f)
|
|
cvalue = CompressedValue()
|
|
cvalue.set_value(zipf.getbuffer())
|
|
report[key + ".zip"] = cvalue
|
|
|
|
|
|
def attach_nvidia_debug_logs(report, keep_locale=False):
|
|
# check if nvidia-bug-report.sh exists
|
|
nv_debug_command = "nvidia-bug-report.sh"
|
|
|
|
if shutil.which(nv_debug_command) is None:
|
|
if opt_debug:
|
|
print(nv_debug_command, "does not exist.")
|
|
return
|
|
|
|
env = os.environ.copy()
|
|
if not keep_locale:
|
|
env["LC_MESSAGES"] = "C"
|
|
|
|
# output result to temp directory
|
|
nv_tempdir = tempfile.mkdtemp()
|
|
nv_debug_file = "nvidia-bug-report"
|
|
nv_debug_fullfile = os.path.join(nv_tempdir, nv_debug_file)
|
|
nv_debug_cmd = [nv_debug_command, "--output-file", nv_debug_fullfile]
|
|
try:
|
|
subprocess.run(
|
|
nv_debug_cmd,
|
|
env=env,
|
|
check=False,
|
|
stdout=subprocess.DEVNULL,
|
|
stderr=subprocess.DEVNULL,
|
|
)
|
|
nv_debug_fullfile_gz = nv_debug_fullfile + ".gz"
|
|
hookutils.attach_file_if_exists(
|
|
report, nv_debug_fullfile_gz, "nvidia-bug-report.gz"
|
|
)
|
|
os.unlink(nv_debug_fullfile_gz)
|
|
os.rmdir(nv_tempdir)
|
|
except OSError as e:
|
|
print("Error:", str(e))
|
|
print("Fail on cleanup", nv_tempdir, ". Please file a bug for it.")
|
|
|
|
|
|
def dot():
|
|
print(".", end="", flush=True)
|
|
|
|
|
|
def build_packages():
|
|
# related packages
|
|
packages = ["apt", "grub2"]
|
|
|
|
# display
|
|
packages.append("xorg")
|
|
packages.append("gnome-shell")
|
|
|
|
# audio
|
|
packages.append("alsa-base")
|
|
|
|
# hotkey and hotplugs
|
|
packages.append("udev")
|
|
|
|
# networking issues
|
|
packages.append("network-manager")
|
|
|
|
return packages
|
|
|
|
|
|
def helper_url_credential_filter(string_with_urls):
|
|
return re.sub(r"://\w+?:\w+?@", "://USER:SECRET@", string_with_urls)
|
|
|
|
|
|
def add_info(report):
|
|
# Check if the DCD file is exist in the installer.
|
|
attach_command_output(report, ["ubuntu-report", "show"], "UbuntuReport")
|
|
dot()
|
|
hookutils.attach_file_if_exists(report, "/etc/buildstamp", "BuildStamp")
|
|
dot()
|
|
attach_pathglob_as_zip(
|
|
report,
|
|
["/sys/firmware/acpi/tables/*", "/sys/firmware/acpi/tables/*/*"],
|
|
"acpitables",
|
|
)
|
|
dot()
|
|
|
|
# Basic hardare information
|
|
hookutils.attach_hardware(report)
|
|
dot()
|
|
hookutils.attach_wifi(report)
|
|
dot()
|
|
|
|
hwe_system_commands = {
|
|
"lspci--xxxx": ["lspci", "-xxxx"],
|
|
"lshw.json": ["lshw", "-json", "-numeric"],
|
|
"dmidecode": ["dmidecode"],
|
|
"fwupdmgr_get-devices": [
|
|
"fwupdmgr",
|
|
"get-devices",
|
|
"--show-all-devices",
|
|
"--no-unreported-check",
|
|
],
|
|
"boltctl-list": ["boltctl", "list"],
|
|
"mokutil---sb-state": ["mokutil", "--sb-state"],
|
|
"tlp-stat": ["tlp-stat"],
|
|
}
|
|
for name, command_list in hwe_system_commands.items():
|
|
attach_command_output(report, command_list, name)
|
|
dot()
|
|
|
|
# More audio related
|
|
hookutils.attach_alsa(report)
|
|
dot()
|
|
audio_system_commands = {
|
|
"pactl-list": ["pactl", "list"],
|
|
"aplay-l": ["aplay", "-l"],
|
|
"aplay-L": ["aplay", "-L"],
|
|
"arecord-l": ["arecord", "-l"],
|
|
"arecord-L": ["arecord", "-L"],
|
|
}
|
|
for name, command_list in audio_system_commands.items():
|
|
attach_command_output(report, command_list, name)
|
|
dot()
|
|
attach_pathglob_as_zip(
|
|
report,
|
|
[
|
|
"/usr/share/alsa/ucm/*/*",
|
|
"/usr/share/alsa/ucm2/*",
|
|
"/usr/share/alsa/ucm2/*/*",
|
|
"/usr/share/alsa/ucm2/*/*/*",
|
|
],
|
|
"ALSA-UCM",
|
|
)
|
|
dot()
|
|
|
|
# FIXME: should be included in xorg in the future
|
|
gfx_system_commands = {
|
|
"glxinfo": ["glxinfo"],
|
|
"xrandr": ["xrandr"],
|
|
"xinput": ["xinput"],
|
|
}
|
|
for name, command_list in gfx_system_commands.items():
|
|
attach_command_output(report, command_list, name)
|
|
dot()
|
|
attach_pathglob_as_zip(report, ["/sys/devices/*/*/drm/card?/*/edid"], "EDID")
|
|
dot()
|
|
|
|
# nvidia-bug-reports.sh
|
|
attach_nvidia_debug_logs(report)
|
|
dot()
|
|
|
|
# FIXME: should be included in thermald in the future
|
|
attach_pathglob_as_zip(
|
|
report,
|
|
["/etc/thermald/*", "/sys/devices/virtual/thermal/*", "/sys/class/thermal/*"],
|
|
"THERMALD",
|
|
)
|
|
dot()
|
|
|
|
# all kernel and system messages
|
|
attach_pathglob_as_zip(report, ["/var/log/*", "/var/log/*/*"], "VAR_LOG")
|
|
dot()
|
|
|
|
# apt configs
|
|
attach_pathglob_as_zip(
|
|
report,
|
|
[
|
|
"/etc/apt/apt.conf.d/*",
|
|
"/etc/apt/sources.list",
|
|
"/etc/apt/sources.list.d/*.list",
|
|
"/etc/apt/preferences.d/*",
|
|
],
|
|
"APT_CONFIGS",
|
|
mode="a",
|
|
data_filter=helper_url_credential_filter,
|
|
)
|
|
dot()
|
|
|
|
# TODO: debug information for suspend or hibernate
|
|
|
|
# packages installed.
|
|
attach_command_output(report, ["dpkg", "-l"], "dpkg-l")
|
|
dot()
|
|
|
|
# FIXME: should be included in bluez in the future
|
|
attach_command_output(report, ["hciconfig", "-a"], "hciconfig-a")
|
|
dot()
|
|
|
|
# FIXME: should be included in dkms in the future
|
|
attach_command_output(report, ["dkms", "status"], "dkms_status")
|
|
dot()
|
|
|
|
# enable when the feature to include data from package hooks exists.
|
|
# packages = build_packages()
|
|
# attach_related_packages(report, packages)
|
|
|
|
|
|
def main():
|
|
parser = ArgumentParser(
|
|
prog="oem-getlogs",
|
|
usage="Useage: sudo -E oem-getlogs [-c CASE_ID]",
|
|
description=__doc__,
|
|
)
|
|
parser.add_argument(
|
|
"-c", "--case-id", help="optional CASE_ID", dest="cid", default=""
|
|
)
|
|
args = parser.parse_args()
|
|
|
|
# check if we got root permission
|
|
if os.geteuid() != 0:
|
|
print("Error: you need to run this program as root")
|
|
parser.print_help()
|
|
sys.exit(1)
|
|
|
|
print("Start to collect logs: ", end="", flush=True)
|
|
# create report
|
|
report = apport.Report()
|
|
add_info(report)
|
|
|
|
# generate filename
|
|
hostname = os.uname()[1]
|
|
date_time = time.strftime("%Y%m%d%H%M%S%z", time.localtime())
|
|
filename_lst = ["oemlogs", hostname]
|
|
if len(args.cid) > 0:
|
|
filename_lst.append(args.cid)
|
|
filename_lst.append(date_time + ".apport.gz")
|
|
filename = "-".join(filename_lst)
|
|
|
|
with gzip.open(filename, "wb") as f:
|
|
report.write(f)
|
|
print("\nSaved log to", filename)
|
|
print("The owner of the file is root. You might want to")
|
|
print(" chown [user]:[group]", filename)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|