Added more warnings in log reader (#17)

* Added warnings:

- File permissions fix
- File not found fix
- Missing services fix
- Graphics Backend threading warning
- Software memory manager warning severity upgraded

* Small fixes:
- Use file size to track duplicate files
- Use logging module for exception warning

* Fix log analysis not showing on no game boot

* Formatting for clarity on mobile, minor rewording

* Add graphics backend to visible output

* Suggest Vulkan for AMD and Intel GPU

* Reword pr-testing message

* Remove anisotropic filtering warning

* Deduplicate mods information

* Nit: spelling error in comment

* Shorten log time message

* Increase bytes read to handle large amounts of DLC

* Add Texture Recompression to output

* Fix shadowed variables

* Recompression suggestion for Vulkan memory error

* Improve regex to deal with MB/MiB RAM measurement

* Rename Expand DRAM warning to match rewording

* Update LDN regex to detect new versions
This commit is contained in:
Mark 2022-11-26 09:34:00 +00:00 committed by TSR Berry
parent ade0917985
commit 367cc8fe9d
No known key found for this signature in database
GPG key ID: 52353C0A4CCA15E2

View file

@ -2,10 +2,11 @@ import logging
import re
import aiohttp
import config
from discord import Colour, Embed
from discord.ext.commands import Cog
import config
logging.basicConfig(
format="%(asctime)s (%(levelname)s) %(message)s (Line %(lineno)d)",
level=logging.INFO,
@ -22,7 +23,7 @@ class LogFileReader(Cog):
async def download_file(self, log_url):
async with aiohttp.ClientSession() as session:
# Grabs first and last few bytes of log file to prevent abuse from large files
headers = {"Range": "bytes=0-35000, -6000"}
headers = {"Range": "bytes=0-60000, -6000"}
async with session.get(log_url, headers=headers) as response:
return await response.text("UTF-8")
@ -47,9 +48,11 @@ class LogFileReader(Cog):
},
"settings": {
"audio_backend": "Unknown",
"backend_threading": "Unknown",
"docked": "Unknown",
"expand_ram": "Unknown",
"fs_integrity": "Unknown",
"graphics_backend": "Unknown",
"ignore_missing_services": "Unknown",
"memory_manager": "Unknown",
"pptc": "Unknown",
@ -58,6 +61,7 @@ class LogFileReader(Cog):
"resolution_scale": "Unknown",
"anisotropic_filtering": "Unknown",
"aspect_ratio": "Unknown",
"texture_recompression": "Unknown",
},
}
attached_log = message.attachments[0]
@ -109,7 +113,6 @@ class LogFileReader(Cog):
continue
def get_ryujinx_info(log_file=log_file):
# try:
for setting in self.embed["emu_info"]:
try:
if setting == "ryu_version":
@ -154,17 +157,19 @@ class LogFileReader(Cog):
(
f"**Audio Backend:** `{self.embed['settings']['audio_backend']}`",
f"**Console Mode:** `{self.embed['settings']['docked']}`",
f"**PPTC cache:** `{self.embed['settings']['pptc']}`",
f"**Shader cache:** `{self.embed['settings']['shader_cache']}`",
f"**PPTC Cache:** `{self.embed['settings']['pptc']}`",
f"**Shader Cache:** `{self.embed['settings']['shader_cache']}`",
f"**V-Sync:** `{self.embed['settings']['vsync']}`",
)
)
graphics_settings_info = "\n".join(
(
f"**Graphics Backend:** `{self.embed['settings']['graphics_backend']}`",
f"**Resolution:** `{self.embed['settings']['resolution_scale']}`",
f"**Anisotropic Filtering:** `{self.embed['settings']['anisotropic_filtering']}`",
f"**Aspect Ratio:** `{self.embed['settings']['aspect_ratio']}`",
f"**Texture Recompression:** `{self.embed['settings']['texture_recompression']}`",
)
)
@ -199,11 +204,11 @@ class LogFileReader(Cog):
log_embed.add_field(
name="Empty Log",
value=f"""The log file appears to be empty. To get a proper log, follow these steps:
1) In Logging settings, ensure `Enable Logging to File` is checked.
2) Ensure the following default logs are enabled: `Info`, `Warning`, `Error`, `Guest` and `Stub`.
3) Start a game up.
4) Play until your issue occurs.
5) Upload the latest log file.""",
1) In Logging settings, ensure `Enable Logging to File` is checked.
2) Ensure the following default logs are enabled: `Info`, `Warning`, `Error`, `Guest` and `Stub`.
3) Start a game up.
4) Play until your issue occurs.
5) Upload the latest log file which is larger than 2KB.""",
inline=False,
)
if (
@ -218,11 +223,11 @@ class LogFileReader(Cog):
log_embed.add_field(
name="No Game Boot Detected",
value=f"""No game boot has been detected in log file. To get a proper log, follow these steps:
1) In Logging settings, ensure `Enable Logging to File` is checked.
2) Ensure the following default logs are enabled: `Info`, `Warning`, `Error`, `Guest` and `Stub`.
3) Start a game up.
4) Play until your issue occurs.
5) Upload the latest log file.""",
1) In Logging settings, ensure `Enable Logging to File` is checked.
2) Ensure the following default logs are enabled: `Info`, `Warning`, `Error`, `Guest` and `Stub`.
3) Start a game up.
4) Play until your issue occurs.
5) Upload the latest log file which is larger than 2KB.""",
inline=False,
)
else:
@ -235,15 +240,15 @@ class LogFileReader(Cog):
name="Mods", value=self.embed["game_info"]["mods"], inline=False
)
try:
notes_value = "\n".join(game_notes)
except TypeError:
notes_value = "Nothing to note"
log_embed.add_field(
name="Notes",
value=notes_value,
inline=False,
)
try:
notes_value = "\n".join(game_notes)
except TypeError:
notes_value = "Nothing to note"
log_embed.add_field(
name="Notes",
value=notes_value,
inline=False,
)
return log_embed
@ -296,6 +301,7 @@ class LogFileReader(Cog):
if name in [
"pptc",
"shader_cache",
"texture_recompression",
"vsync",
]:
setting[
@ -307,14 +313,17 @@ class LogFileReader(Cog):
"anisotropic_filtering": "MaxAnisotropy",
"aspect_ratio": "AspectRatio",
"audio_backend": "AudioBackend",
"backend_threading": "BackendThreading",
"docked": "EnableDockedMode",
"expand_ram": "ExpandRam",
"fs_integrity": "EnableFsIntegrityChecks",
"graphics_backend": "GraphicsBackend",
"ignore_missing_services": "IgnoreMissingServices",
"memory_manager": "MemoryManagerMode",
"pptc": "EnablePtc",
"resolution_scale": "ResScale",
"shader_cache": "EnableShaderCache",
"texture_recompression": "EnableTextureRecompression",
"vsync": "EnableVsync",
}
try:
@ -322,7 +331,7 @@ class LogFileReader(Cog):
setting_name, setting_map[setting_name], log_file=log_file
)
except (AttributeError, IndexError) as error:
print(
logging.info(
f"Settings exception: {setting_name}: {type(error).__name__}"
)
continue
@ -350,7 +359,7 @@ class LogFileReader(Cog):
return False
shader_cache_collision = error_search(["Cache collision found"])
dump_hash_warning = error_search(
dump_hash_error = error_search(
[
"ResultFsInvalidIvfcHash",
"ResultFsNonRealDataVerificationFailed",
@ -368,6 +377,12 @@ class LogFileReader(Cog):
["ResultFsPermissionDenied"]
)
file_not_found_error = error_search(["ResultFsTargetNotFound"])
missing_services_error = error_search(
["ServiceNotImplementedException"]
)
vulkan_out_of_memory_error = error_search(
["ErrorOutOfDeviceMemory"]
)
last_errors = "\n".join(
errors[-1][:2] if "|E|" in errors[-1][0] else ""
@ -377,23 +392,27 @@ class LogFileReader(Cog):
return (
last_errors,
shader_cache_collision,
dump_hash_warning,
dump_hash_error,
shader_cache_corruption,
update_keys_error,
file_permissions_error,
file_not_found_error,
missing_services_error,
vulkan_out_of_memory_error,
)
# Finds the lastest error denoted by |E| in the log and its first line
# Finds the latest error denoted by |E| in the log and its first line
# Also warns of common issues
(
last_error_snippet,
shader_cache_warn,
dump_hash_warning,
shader_cache_corruption_warn,
update_keys_error,
file_permissions_error,
file_not_found_error,
update_keys_warn,
file_permissions_warn,
file_not_found_warn,
missing_services_warn,
vulkan_out_of_memory_warn,
) = analyse_error_message()
if last_error_snippet:
self.embed["game_info"]["errors"] = f"```{last_error_snippet}```"
@ -427,24 +446,38 @@ class LogFileReader(Cog):
dump_hash_warning = f"⚠️ Dump error detected. Investigate possible bad game/firmware dump issues"
self.embed["game_info"]["notes"].append(dump_hash_warning)
if update_keys_error:
update_keys_error = (
if update_keys_warn:
update_keys_warn = (
f"⚠️ Keys or firmware out of date, consider updating them"
)
self.embed["game_info"]["notes"].append(update_keys_error)
self.embed["game_info"]["notes"].append(update_keys_warn)
if file_permissions_error:
file_permissions_error = f"⚠️ File permission error. Consider deleting save directory and allowing Ryujinx to make a new one"
self.embed["game_info"]["notes"].append(file_permissions_error)
if file_permissions_warn:
file_permissions_warn = f"⚠️ File permission error. Consider deleting save directory and allowing Ryujinx to make a new one"
self.embed["game_info"]["notes"].append(file_permissions_warn)
if file_not_found_error:
file_not_found_error = f"⚠️ Save not found error. Consider starting game without a save file or using a new save file"
self.embed["game_info"]["notes"].append(file_not_found_error)
if file_not_found_warn:
file_not_found_warn = f"⚠️ Save not found error. Consider starting game without a save file or using a new save file"
self.embed["game_info"]["notes"].append(file_not_found_warn)
if (
missing_services_warn
and self.embed["settings"]["ignore_missing_services"] == "False"
):
missing_services_warn = f"⚠️ Consider enabling `Ignore Missing Services` in Ryujinx settings"
self.embed["game_info"]["notes"].append(missing_services_warn)
if (
vulkan_out_of_memory_warn
and self.embed["settings"]["texture_recompression"] == "Disabled"
):
vulkan_out_of_memory_warn = f"⚠️ Consider enabling `Texture Recompression` in Ryujinx settings"
self.embed["game_info"]["notes"].append(vulkan_out_of_memory_warn)
timestamp_regex = re.compile(r"\d{2}:\d{2}:\d{2}\.\d{3}")
latest_timestamp = re.findall(timestamp_regex, log_file)[-1]
if latest_timestamp:
timestamp_message = f" Time elapsed in log: `{latest_timestamp}`"
timestamp_message = f" Time elapsed: `{latest_timestamp}`"
self.embed["game_info"]["notes"].append(timestamp_message)
def mods_information(log_file=log_file):
@ -458,6 +491,8 @@ class LogFileReader(Cog):
f" {i['mod']} ({'ExeFS' if i['status'] == '[E]' else 'RomFS'})"
for i in mods
]
# Remove duplicated mods from output
mods_status = list(dict.fromkeys(mods_status))
return mods_status
game_mods = mods_information()
@ -484,7 +519,9 @@ class LogFileReader(Cog):
self.embed["game_info"]["notes"].append(input_string)
try:
ram_available_regex = re.compile(r"Available\s(\d+)(?=\sMB)")
ram_available_regex = re.compile(
r"Application\sPrint:\sRAM:(?:.*Available\s)(\d+)"
)
ram_available = re.search(ram_available_regex, log_file)[1]
if int(ram_available) < 8000:
ram_warning = (
@ -498,13 +535,17 @@ class LogFileReader(Cog):
mac_os_warning = "**❌ macOS is currently unsupported**"
self.embed["game_info"]["notes"].append(mac_os_warning)
if "Intel" in self.embed["hardware_info"]["gpu"]:
if (
"Darwin" in self.embed["hardware_info"]["os"]
or "Windows" in self.embed["hardware_info"]["os"]
):
intel_gpu_warning = "**⚠️ Intel iGPUs are known to have driver issues, consider using a discrete GPU**"
if (
"Windows" in self.embed["hardware_info"]["os"]
and self.embed["settings"]["graphics_backend"] != "Vulkan"
):
if "Intel" in self.embed["hardware_info"]["gpu"]:
intel_gpu_warning = "**⚠️ Intel iGPU users should consider using Vulkan graphics backend**"
self.embed["game_info"]["notes"].append(intel_gpu_warning)
if "AMD" in self.embed["hardware_info"]["gpu"]:
amd_gpu_warning = "**⚠️ AMD GPU users should consider using Vulkan graphics backend**"
self.embed["game_info"]["notes"].append(amd_gpu_warning)
try:
default_logs = ["Info", "Warning", "Error", "Guest", "Stub"]
user_logs = (
@ -532,15 +573,6 @@ class LogFileReader(Cog):
firmware_warning = f"**❌ Nintendo Switch firmware not found**"
self.embed["game_info"]["notes"].append(firmware_warning)
if self.embed["settings"]["anisotropic_filtering"] not in [
"Auto",
"Unknown",
]:
anisotropic_filtering_warning = "⚠️ Anisotropic filtering not set to `Auto` can cause graphical issues"
self.embed["game_info"]["notes"].append(
anisotropic_filtering_warning
)
if self.embed["settings"]["audio_backend"] == "Dummy":
dummy_warning = (
f"⚠️ Dummy audio backend, consider changing to SDL2 or OpenAL"
@ -556,11 +588,11 @@ class LogFileReader(Cog):
self.embed["game_info"]["notes"].append(shader_warning)
if self.embed["settings"]["expand_ram"] == "True":
expand_ram_warning = f"⚠️ `Expand DRAM size to 6GB` should only be enabled for 4K mods"
expand_ram_warning = f"⚠️ `Use alternative memory layout` should only be enabled for 4K mods"
self.embed["game_info"]["notes"].append(expand_ram_warning)
if self.embed["settings"]["memory_manager"] == "SoftwarePageTable":
software_memory_manager_warning = "⚠️ `Software` setting in Memory Manager Mode will give slower performance than the default setting of `Host unchecked`"
software_memory_manager_warning = "🔴 **`Software` setting in Memory Manager Mode will give slower performance than the default setting of `Host unchecked`**"
self.embed["game_info"]["notes"].append(
software_memory_manager_warning
)
@ -579,10 +611,16 @@ class LogFileReader(Cog):
fs_integrity_warning = f"⚠️ Disabling file integrity checks may cause corrupted dumps to not be detected"
self.embed["game_info"]["notes"].append(fs_integrity_warning)
if self.embed["settings"]["backend_threading"] == "Off":
backend_threading_warning = (
f"🔴 **Graphics Backend Multithreading should be set to `Auto`**"
)
self.embed["game_info"]["notes"].append(backend_threading_warning)
mainline_version = re.compile(r"^\d\.\d\.\d+$")
old_mainline_version = re.compile(r"^\d\.\d\.(\d){4}$")
pr_version = re.compile(r"^\d\.\d\.\d\+([a-f]|\d){7}$")
ldn_version = re.compile(r"^\d\.\d\.\d\-ldn\d\.\d$")
ldn_version = re.compile(r"^\d\.\d\.\d\-ldn\d+\.\d+(?:\.\d+|$)")
if (
message.channel.id == config.bot_log_allowed_channels["support"]
@ -592,7 +630,7 @@ class LogFileReader(Cog):
== config.bot_log_allowed_channels["linux-master-race"]
):
if re.match(pr_version, self.embed["emu_info"]["ryu_version"]):
pr_version_warning = f"**⚠️ PR build logs should be posted in <#{config.bot_log_allowed_channels['pr-testing']}>**"
pr_version_warning = f"**⚠️ PR build logs should be posted in <#{config.bot_log_allowed_channels['pr-testing']}> if reporting bugs or tests**"
self.embed["game_info"]["notes"].append(pr_version_warning)
if re.match(
@ -652,6 +690,7 @@ class LogFileReader(Cog):
author_id = message.author.id
author_mention = message.author.mention
filename = message.attachments[0].filename
filesize = message.attachments[0].size
# Any message over 2000 chars is uploaded as message.txt, so this is accounted for
ryujinx_log_file_regex = re.compile(r"^Ryujinx_.*\.log|message\.txt$")
log_file = re.compile(r"^.*\.log|.*\.txt$")
@ -676,6 +715,7 @@ class LogFileReader(Cog):
self.uploaded_log_info.append(
{
"filename": filename,
"file_size": filesize,
"link": log_file_link,
"author": author_id,
}
@ -705,6 +745,7 @@ class LogFileReader(Cog):
elem
for elem in self.uploaded_log_info
if elem["filename"] == filename
and elem["file_size"] == filesize
and elem["author"] == author_id
),
None,