diff options
Diffstat (limited to '')
| -rw-r--r-- | src/core/hle/service/fatal/fatal.cpp | 141 | ||||
| -rw-r--r-- | src/core/hle/service/fatal/fatal.h | 1 | ||||
| -rw-r--r-- | src/core/hle/service/fatal/fatal_u.cpp | 2 |
3 files changed, 140 insertions, 4 deletions
diff --git a/src/core/hle/service/fatal/fatal.cpp b/src/core/hle/service/fatal/fatal.cpp index b436ce4e6..6de7edf9e 100644 --- a/src/core/hle/service/fatal/fatal.cpp +++ b/src/core/hle/service/fatal/fatal.cpp | |||
| @@ -2,8 +2,17 @@ | |||
| 2 | // Licensed under GPLv2 or any later version | 2 | // Licensed under GPLv2 or any later version |
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
| 5 | #include <array> | ||
| 6 | #include <cstring> | ||
| 7 | #include <ctime> | ||
| 8 | #include <fmt/time.h> | ||
| 9 | #include "common/common_paths.h" | ||
| 10 | #include "common/file_util.h" | ||
| 5 | #include "common/logging/log.h" | 11 | #include "common/logging/log.h" |
| 12 | #include "common/scm_rev.h" | ||
| 13 | #include "common/swap.h" | ||
| 6 | #include "core/hle/ipc_helpers.h" | 14 | #include "core/hle/ipc_helpers.h" |
| 15 | #include "core/hle/kernel/process.h" | ||
| 7 | #include "core/hle/service/fatal/fatal.h" | 16 | #include "core/hle/service/fatal/fatal.h" |
| 8 | #include "core/hle/service/fatal/fatal_p.h" | 17 | #include "core/hle/service/fatal/fatal_p.h" |
| 9 | #include "core/hle/service/fatal/fatal_u.h" | 18 | #include "core/hle/service/fatal/fatal_u.h" |
| @@ -15,16 +24,142 @@ Module::Interface::Interface(std::shared_ptr<Module> module, const char* name) | |||
| 15 | 24 | ||
| 16 | Module::Interface::~Interface() = default; | 25 | Module::Interface::~Interface() = default; |
| 17 | 26 | ||
| 27 | struct FatalInfo { | ||
| 28 | std::array<u64_le, 31> registers{}; // TODO(ogniK): See if this actually is registers or | ||
| 29 | // not(find a game which has non zero valeus) | ||
| 30 | u64_le unk0{}; | ||
| 31 | u64_le unk1{}; | ||
| 32 | u64_le unk2{}; | ||
| 33 | u64_le unk3{}; | ||
| 34 | u64_le unk4{}; | ||
| 35 | u64_le unk5{}; | ||
| 36 | u64_le unk6{}; | ||
| 37 | |||
| 38 | std::array<u64_le, 32> backtrace{}; | ||
| 39 | u64_le unk7{}; | ||
| 40 | u64_le unk8{}; | ||
| 41 | u32_le backtrace_size{}; | ||
| 42 | u32_le unk9{}; | ||
| 43 | u32_le unk10{}; // TODO(ogniK): Is this even used or is it just padding? | ||
| 44 | }; | ||
| 45 | static_assert(sizeof(FatalInfo) == 0x250, "FatalInfo is an invalid size"); | ||
| 46 | |||
| 47 | enum class FatalType : u32 { | ||
| 48 | ErrorReportAndScreen = 0, | ||
| 49 | ErrorReport = 1, | ||
| 50 | ErrorScreen = 2, | ||
| 51 | }; | ||
| 52 | |||
| 53 | static void GenerateErrorReport(ResultCode error_code, const FatalInfo& info) { | ||
| 54 | const auto title_id = Core::CurrentProcess()->program_id; | ||
| 55 | std::string crash_report = | ||
| 56 | fmt::format("Yuzu {}-{} crash report\n" | ||
| 57 | "Title ID: {:016x}\n" | ||
| 58 | "Result: 0x{:X} ({:04}-{:04d})\n" | ||
| 59 | "\n", | ||
| 60 | Common::g_scm_branch, Common::g_scm_desc, title_id, error_code.raw, | ||
| 61 | 2000 + static_cast<u32>(error_code.module.Value()), | ||
| 62 | static_cast<u32>(error_code.description.Value()), info.unk8, info.unk7); | ||
| 63 | if (info.backtrace_size != 0x0) { | ||
| 64 | crash_report += "Registers:\n"; | ||
| 65 | // TODO(ogniK): This is just a guess, find a game which actually has non zero values | ||
| 66 | for (size_t i = 0; i < info.registers.size(); i++) { | ||
| 67 | crash_report += | ||
| 68 | fmt::format(" X[{:02d}]: {:016x}\n", i, info.registers[i]); | ||
| 69 | } | ||
| 70 | crash_report += fmt::format(" Unknown 0: {:016x}\n", info.unk0); | ||
| 71 | crash_report += fmt::format(" Unknown 1: {:016x}\n", info.unk1); | ||
| 72 | crash_report += fmt::format(" Unknown 2: {:016x}\n", info.unk2); | ||
| 73 | crash_report += fmt::format(" Unknown 3: {:016x}\n", info.unk3); | ||
| 74 | crash_report += fmt::format(" Unknown 4: {:016x}\n", info.unk4); | ||
| 75 | crash_report += fmt::format(" Unknown 5: {:016x}\n", info.unk5); | ||
| 76 | crash_report += fmt::format(" Unknown 6: {:016x}\n", info.unk6); | ||
| 77 | crash_report += "\nBacktrace:\n"; | ||
| 78 | for (size_t i = 0; i < info.backtrace_size; i++) { | ||
| 79 | crash_report += | ||
| 80 | fmt::format(" Backtrace[{:02d}]: {:016x}\n", i, info.backtrace[i]); | ||
| 81 | } | ||
| 82 | crash_report += fmt::format("\nUnknown 7: 0x{:016x}\n", info.unk7); | ||
| 83 | crash_report += fmt::format("Unknown 8: 0x{:016x}\n", info.unk8); | ||
| 84 | crash_report += fmt::format("Unknown 9: 0x{:016x}\n", info.unk9); | ||
| 85 | crash_report += fmt::format("Unknown 10: 0x{:016x}\n", info.unk10); | ||
| 86 | } | ||
| 87 | |||
| 88 | LOG_ERROR(Service_Fatal, "{}", crash_report); | ||
| 89 | |||
| 90 | const std::string crashreport_dir = | ||
| 91 | FileUtil::GetUserPath(FileUtil::UserPath::LogDir) + "crash_logs"; | ||
| 92 | |||
| 93 | if (!FileUtil::CreateFullPath(crashreport_dir)) { | ||
| 94 | LOG_ERROR( | ||
| 95 | Service_Fatal, | ||
| 96 | "Unable to create crash report directory. Possible log directory permissions issue."); | ||
| 97 | return; | ||
| 98 | } | ||
| 99 | |||
| 100 | const std::time_t t = std::time(nullptr); | ||
| 101 | const std::string crashreport_filename = | ||
| 102 | fmt::format("{}/{:016x}-{:%F-%H%M%S}.log", crashreport_dir, title_id, *std::localtime(&t)); | ||
| 103 | |||
| 104 | auto file = FileUtil::IOFile(crashreport_filename, "wb"); | ||
| 105 | if (file.IsOpen()) { | ||
| 106 | file.WriteString(crash_report); | ||
| 107 | LOG_ERROR(Service_Fatal, "Saving error report to {}", crashreport_filename); | ||
| 108 | } else { | ||
| 109 | LOG_ERROR(Service_Fatal, "Failed to save error report to {}", crashreport_filename); | ||
| 110 | } | ||
| 111 | } | ||
| 112 | |||
| 113 | static void ThrowFatalError(ResultCode error_code, FatalType fatal_type, const FatalInfo& info) { | ||
| 114 | LOG_ERROR(Service_Fatal, "Threw fatal error type {}", static_cast<u32>(fatal_type)); | ||
| 115 | switch (fatal_type) { | ||
| 116 | case FatalType::ErrorReportAndScreen: | ||
| 117 | GenerateErrorReport(error_code, info); | ||
| 118 | [[fallthrough]]; | ||
| 119 | case FatalType::ErrorScreen: | ||
| 120 | // Since we have no fatal:u error screen. We should just kill execution instead | ||
| 121 | ASSERT(false); | ||
| 122 | break; | ||
| 123 | // Should not throw a fatal screen but should generate an error report | ||
| 124 | case FatalType::ErrorReport: | ||
| 125 | GenerateErrorReport(error_code, info); | ||
| 126 | break; | ||
| 127 | }; | ||
| 128 | } | ||
| 129 | |||
| 130 | void Module::Interface::ThrowFatal(Kernel::HLERequestContext& ctx) { | ||
| 131 | LOG_ERROR(Service_Fatal, "called"); | ||
| 132 | IPC::RequestParser rp{ctx}; | ||
| 133 | auto error_code = rp.Pop<ResultCode>(); | ||
| 134 | |||
| 135 | ThrowFatalError(error_code, FatalType::ErrorScreen, {}); | ||
| 136 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 137 | rb.Push(RESULT_SUCCESS); | ||
| 138 | } | ||
| 139 | |||
| 18 | void Module::Interface::ThrowFatalWithPolicy(Kernel::HLERequestContext& ctx) { | 140 | void Module::Interface::ThrowFatalWithPolicy(Kernel::HLERequestContext& ctx) { |
| 141 | LOG_ERROR(Service_Fatal, "called"); | ||
| 19 | IPC::RequestParser rp(ctx); | 142 | IPC::RequestParser rp(ctx); |
| 20 | u32 error_code = rp.Pop<u32>(); | 143 | auto error_code = rp.Pop<ResultCode>(); |
| 21 | LOG_WARNING(Service_Fatal, "(STUBBED) called, error_code=0x{:X}", error_code); | 144 | auto fatal_type = rp.PopEnum<FatalType>(); |
| 145 | |||
| 146 | ThrowFatalError(error_code, fatal_type, {}); // No info is passed with ThrowFatalWithPolicy | ||
| 22 | IPC::ResponseBuilder rb{ctx, 2}; | 147 | IPC::ResponseBuilder rb{ctx, 2}; |
| 23 | rb.Push(RESULT_SUCCESS); | 148 | rb.Push(RESULT_SUCCESS); |
| 24 | } | 149 | } |
| 25 | 150 | ||
| 26 | void Module::Interface::ThrowFatalWithCpuContext(Kernel::HLERequestContext& ctx) { | 151 | void Module::Interface::ThrowFatalWithCpuContext(Kernel::HLERequestContext& ctx) { |
| 27 | LOG_WARNING(Service_Fatal, "(STUBBED) called"); | 152 | LOG_ERROR(Service_Fatal, "called"); |
| 153 | IPC::RequestParser rp(ctx); | ||
| 154 | auto error_code = rp.Pop<ResultCode>(); | ||
| 155 | auto fatal_type = rp.PopEnum<FatalType>(); | ||
| 156 | auto fatal_info = ctx.ReadBuffer(); | ||
| 157 | FatalInfo info{}; | ||
| 158 | |||
| 159 | ASSERT_MSG(fatal_info.size() == sizeof(FatalInfo), "Invalid fatal info buffer size!"); | ||
| 160 | std::memcpy(&info, fatal_info.data(), sizeof(FatalInfo)); | ||
| 161 | |||
| 162 | ThrowFatalError(error_code, fatal_type, info); | ||
| 28 | IPC::ResponseBuilder rb{ctx, 2}; | 163 | IPC::ResponseBuilder rb{ctx, 2}; |
| 29 | rb.Push(RESULT_SUCCESS); | 164 | rb.Push(RESULT_SUCCESS); |
| 30 | } | 165 | } |
diff --git a/src/core/hle/service/fatal/fatal.h b/src/core/hle/service/fatal/fatal.h index 4d9a5be52..09371ff7f 100644 --- a/src/core/hle/service/fatal/fatal.h +++ b/src/core/hle/service/fatal/fatal.h | |||
| @@ -15,6 +15,7 @@ public: | |||
| 15 | explicit Interface(std::shared_ptr<Module> module, const char* name); | 15 | explicit Interface(std::shared_ptr<Module> module, const char* name); |
| 16 | ~Interface() override; | 16 | ~Interface() override; |
| 17 | 17 | ||
| 18 | void ThrowFatal(Kernel::HLERequestContext& ctx); | ||
| 18 | void ThrowFatalWithPolicy(Kernel::HLERequestContext& ctx); | 19 | void ThrowFatalWithPolicy(Kernel::HLERequestContext& ctx); |
| 19 | void ThrowFatalWithCpuContext(Kernel::HLERequestContext& ctx); | 20 | void ThrowFatalWithCpuContext(Kernel::HLERequestContext& ctx); |
| 20 | 21 | ||
diff --git a/src/core/hle/service/fatal/fatal_u.cpp b/src/core/hle/service/fatal/fatal_u.cpp index befc307cf..1572a2051 100644 --- a/src/core/hle/service/fatal/fatal_u.cpp +++ b/src/core/hle/service/fatal/fatal_u.cpp | |||
| @@ -8,7 +8,7 @@ namespace Service::Fatal { | |||
| 8 | 8 | ||
| 9 | Fatal_U::Fatal_U(std::shared_ptr<Module> module) : Module::Interface(std::move(module), "fatal:u") { | 9 | Fatal_U::Fatal_U(std::shared_ptr<Module> module) : Module::Interface(std::move(module), "fatal:u") { |
| 10 | static const FunctionInfo functions[] = { | 10 | static const FunctionInfo functions[] = { |
| 11 | {0, nullptr, "ThrowFatal"}, | 11 | {0, &Fatal_U::ThrowFatal, "ThrowFatal"}, |
| 12 | {1, &Fatal_U::ThrowFatalWithPolicy, "ThrowFatalWithPolicy"}, | 12 | {1, &Fatal_U::ThrowFatalWithPolicy, "ThrowFatalWithPolicy"}, |
| 13 | {2, &Fatal_U::ThrowFatalWithCpuContext, "ThrowFatalWithCpuContext"}, | 13 | {2, &Fatal_U::ThrowFatalWithCpuContext, "ThrowFatalWithCpuContext"}, |
| 14 | }; | 14 | }; |