fatal.cpp 6.8 KB

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