summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/HomeSettingsFragment.kt5
-rw-r--r--src/android/app/src/main/res/layout/card_home_option.xml3
-rw-r--r--src/common/fs/fs_paths.h1
-rw-r--r--src/common/fs/path_util.cpp1
-rw-r--r--src/common/fs/path_util.h1
-rw-r--r--src/common/nvidia_flags.cpp1
-rw-r--r--src/common/settings.h1
-rw-r--r--src/core/hle/service/am/am.cpp8
-rw-r--r--src/core/hle/service/am/applets/applet_cabinet.cpp4
-rw-r--r--src/core/hle/service/hid/controllers/palma.cpp4
-rw-r--r--src/core/hle/service/hid/hid.cpp4
-rw-r--r--src/core/hle/service/hid/hidbus/hidbus_base.cpp5
-rw-r--r--src/core/hle/service/pctl/pctl_module.cpp6
-rw-r--r--src/video_core/renderer_vulkan/vk_state_tracker.cpp2
-rw-r--r--src/yuzu/CMakeLists.txt10
-rw-r--r--src/yuzu/breakpad.cpp77
-rw-r--r--src/yuzu/breakpad.h10
-rw-r--r--src/yuzu/configuration/configure_debug.cpp18
-rw-r--r--src/yuzu/configuration/configure_debug.ui7
-rw-r--r--src/yuzu/main.cpp19
-rw-r--r--src/yuzu/mini_dump.cpp202
-rw-r--r--src/yuzu/mini_dump.h19
22 files changed, 134 insertions, 274 deletions
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/HomeSettingsFragment.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/HomeSettingsFragment.kt
index fd9785075..f273c880a 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/HomeSettingsFragment.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/HomeSettingsFragment.kt
@@ -26,7 +26,7 @@ import androidx.fragment.app.Fragment
26import androidx.fragment.app.activityViewModels 26import androidx.fragment.app.activityViewModels
27import androidx.navigation.findNavController 27import androidx.navigation.findNavController
28import androidx.navigation.fragment.findNavController 28import androidx.navigation.fragment.findNavController
29import androidx.recyclerview.widget.LinearLayoutManager 29import androidx.recyclerview.widget.GridLayoutManager
30import com.google.android.material.transition.MaterialSharedAxis 30import com.google.android.material.transition.MaterialSharedAxis
31import org.yuzu.yuzu_emu.BuildConfig 31import org.yuzu.yuzu_emu.BuildConfig
32import org.yuzu.yuzu_emu.HomeNavigationDirections 32import org.yuzu.yuzu_emu.HomeNavigationDirections
@@ -186,7 +186,8 @@ class HomeSettingsFragment : Fragment() {
186 } 186 }
187 187
188 binding.homeSettingsList.apply { 188 binding.homeSettingsList.apply {
189 layoutManager = LinearLayoutManager(requireContext()) 189 layoutManager =
190 GridLayoutManager(requireContext(), resources.getInteger(R.integer.grid_columns))
190 adapter = HomeSettingAdapter( 191 adapter = HomeSettingAdapter(
191 requireActivity() as AppCompatActivity, 192 requireActivity() as AppCompatActivity,
192 viewLifecycleOwner, 193 viewLifecycleOwner,
diff --git a/src/android/app/src/main/res/layout/card_home_option.xml b/src/android/app/src/main/res/layout/card_home_option.xml
index f9f1d89fb..6e8a232f9 100644
--- a/src/android/app/src/main/res/layout/card_home_option.xml
+++ b/src/android/app/src/main/res/layout/card_home_option.xml
@@ -16,7 +16,8 @@
16 <LinearLayout 16 <LinearLayout
17 android:id="@+id/option_layout" 17 android:id="@+id/option_layout"
18 android:layout_width="match_parent" 18 android:layout_width="match_parent"
19 android:layout_height="wrap_content"> 19 android:layout_height="wrap_content"
20 android:layout_gravity="center_vertical">
20 21
21 <ImageView 22 <ImageView
22 android:id="@+id/option_icon" 23 android:id="@+id/option_icon"
diff --git a/src/common/fs/fs_paths.h b/src/common/fs/fs_paths.h
index 441c8af97..bcf447089 100644
--- a/src/common/fs/fs_paths.h
+++ b/src/common/fs/fs_paths.h
@@ -13,6 +13,7 @@
13#define AMIIBO_DIR "amiibo" 13#define AMIIBO_DIR "amiibo"
14#define CACHE_DIR "cache" 14#define CACHE_DIR "cache"
15#define CONFIG_DIR "config" 15#define CONFIG_DIR "config"
16#define CRASH_DUMPS_DIR "crash_dumps"
16#define DUMP_DIR "dump" 17#define DUMP_DIR "dump"
17#define KEYS_DIR "keys" 18#define KEYS_DIR "keys"
18#define LOAD_DIR "load" 19#define LOAD_DIR "load"
diff --git a/src/common/fs/path_util.cpp b/src/common/fs/path_util.cpp
index 0abd81a45..0c4c88cde 100644
--- a/src/common/fs/path_util.cpp
+++ b/src/common/fs/path_util.cpp
@@ -119,6 +119,7 @@ public:
119 GenerateYuzuPath(YuzuPath::AmiiboDir, yuzu_path / AMIIBO_DIR); 119 GenerateYuzuPath(YuzuPath::AmiiboDir, yuzu_path / AMIIBO_DIR);
120 GenerateYuzuPath(YuzuPath::CacheDir, yuzu_path_cache); 120 GenerateYuzuPath(YuzuPath::CacheDir, yuzu_path_cache);
121 GenerateYuzuPath(YuzuPath::ConfigDir, yuzu_path_config); 121 GenerateYuzuPath(YuzuPath::ConfigDir, yuzu_path_config);
122 GenerateYuzuPath(YuzuPath::CrashDumpsDir, yuzu_path / CRASH_DUMPS_DIR);
122 GenerateYuzuPath(YuzuPath::DumpDir, yuzu_path / DUMP_DIR); 123 GenerateYuzuPath(YuzuPath::DumpDir, yuzu_path / DUMP_DIR);
123 GenerateYuzuPath(YuzuPath::KeysDir, yuzu_path / KEYS_DIR); 124 GenerateYuzuPath(YuzuPath::KeysDir, yuzu_path / KEYS_DIR);
124 GenerateYuzuPath(YuzuPath::LoadDir, yuzu_path / LOAD_DIR); 125 GenerateYuzuPath(YuzuPath::LoadDir, yuzu_path / LOAD_DIR);
diff --git a/src/common/fs/path_util.h b/src/common/fs/path_util.h
index 63801c924..2874ea738 100644
--- a/src/common/fs/path_util.h
+++ b/src/common/fs/path_util.h
@@ -15,6 +15,7 @@ enum class YuzuPath {
15 AmiiboDir, // Where Amiibo backups are stored. 15 AmiiboDir, // Where Amiibo backups are stored.
16 CacheDir, // Where cached filesystem data is stored. 16 CacheDir, // Where cached filesystem data is stored.
17 ConfigDir, // Where config files are stored. 17 ConfigDir, // Where config files are stored.
18 CrashDumpsDir, // Where crash dumps are stored.
18 DumpDir, // Where dumped data is stored. 19 DumpDir, // Where dumped data is stored.
19 KeysDir, // Where key files are stored. 20 KeysDir, // Where key files are stored.
20 LoadDir, // Where cheat/mod files are stored. 21 LoadDir, // Where cheat/mod files are stored.
diff --git a/src/common/nvidia_flags.cpp b/src/common/nvidia_flags.cpp
index 7ed7690ee..fa3747782 100644
--- a/src/common/nvidia_flags.cpp
+++ b/src/common/nvidia_flags.cpp
@@ -25,6 +25,7 @@ void ConfigureNvidiaEnvironmentFlags() {
25 25
26 void(_putenv(fmt::format("__GL_SHADER_DISK_CACHE_PATH={}", windows_path_string).c_str())); 26 void(_putenv(fmt::format("__GL_SHADER_DISK_CACHE_PATH={}", windows_path_string).c_str()));
27 void(_putenv("__GL_SHADER_DISK_CACHE_SKIP_CLEANUP=1")); 27 void(_putenv("__GL_SHADER_DISK_CACHE_SKIP_CLEANUP=1"));
28 void(_putenv("__GL_THREADED_OPTIMIZATIONS=1"));
28#endif 29#endif
29} 30}
30 31
diff --git a/src/common/settings.h b/src/common/settings.h
index 236e33bee..9317075f7 100644
--- a/src/common/settings.h
+++ b/src/common/settings.h
@@ -505,7 +505,6 @@ struct Values {
505 linkage, false, "use_auto_stub", Category::Debugging, Specialization::Default, false}; 505 linkage, false, "use_auto_stub", Category::Debugging, Specialization::Default, false};
506 Setting<bool> enable_all_controllers{linkage, false, "enable_all_controllers", 506 Setting<bool> enable_all_controllers{linkage, false, "enable_all_controllers",
507 Category::Debugging}; 507 Category::Debugging};
508 Setting<bool> create_crash_dumps{linkage, false, "create_crash_dumps", Category::Debugging};
509 Setting<bool> perform_vulkan_check{linkage, true, "perform_vulkan_check", Category::Debugging}; 508 Setting<bool> perform_vulkan_check{linkage, true, "perform_vulkan_check", Category::Debugging};
510 509
511 // Miscellaneous 510 // Miscellaneous
diff --git a/src/core/hle/service/am/am.cpp b/src/core/hle/service/am/am.cpp
index 98765b81a..ff067c8d9 100644
--- a/src/core/hle/service/am/am.cpp
+++ b/src/core/hle/service/am/am.cpp
@@ -796,7 +796,9 @@ ILockAccessor::ILockAccessor(Core::System& system_)
796 lock_event = service_context.CreateEvent("ILockAccessor::LockEvent"); 796 lock_event = service_context.CreateEvent("ILockAccessor::LockEvent");
797} 797}
798 798
799ILockAccessor::~ILockAccessor() = default; 799ILockAccessor::~ILockAccessor() {
800 service_context.CloseEvent(lock_event);
801};
800 802
801void ILockAccessor::TryLock(HLERequestContext& ctx) { 803void ILockAccessor::TryLock(HLERequestContext& ctx) {
802 IPC::RequestParser rp{ctx}; 804 IPC::RequestParser rp{ctx};
@@ -909,7 +911,9 @@ ICommonStateGetter::ICommonStateGetter(Core::System& system_,
909 msg_queue->PushMessage(AppletMessageQueue::AppletMessage::ChangeIntoForeground); 911 msg_queue->PushMessage(AppletMessageQueue::AppletMessage::ChangeIntoForeground);
910} 912}
911 913
912ICommonStateGetter::~ICommonStateGetter() = default; 914ICommonStateGetter::~ICommonStateGetter() {
915 service_context.CloseEvent(sleep_lock_event);
916};
913 917
914void ICommonStateGetter::GetBootMode(HLERequestContext& ctx) { 918void ICommonStateGetter::GetBootMode(HLERequestContext& ctx) {
915 LOG_DEBUG(Service_AM, "called"); 919 LOG_DEBUG(Service_AM, "called");
diff --git a/src/core/hle/service/am/applets/applet_cabinet.cpp b/src/core/hle/service/am/applets/applet_cabinet.cpp
index 19ed184e8..b379dadeb 100644
--- a/src/core/hle/service/am/applets/applet_cabinet.cpp
+++ b/src/core/hle/service/am/applets/applet_cabinet.cpp
@@ -25,7 +25,9 @@ Cabinet::Cabinet(Core::System& system_, LibraryAppletMode applet_mode_,
25 service_context.CreateEvent("CabinetApplet:AvailabilityChangeEvent"); 25 service_context.CreateEvent("CabinetApplet:AvailabilityChangeEvent");
26} 26}
27 27
28Cabinet::~Cabinet() = default; 28Cabinet::~Cabinet() {
29 service_context.CloseEvent(availability_change_event);
30};
29 31
30void Cabinet::Initialize() { 32void Cabinet::Initialize() {
31 Applet::Initialize(); 33 Applet::Initialize();
diff --git a/src/core/hle/service/hid/controllers/palma.cpp b/src/core/hle/service/hid/controllers/palma.cpp
index 14c67e454..73a2a2b91 100644
--- a/src/core/hle/service/hid/controllers/palma.cpp
+++ b/src/core/hle/service/hid/controllers/palma.cpp
@@ -19,7 +19,9 @@ Controller_Palma::Controller_Palma(Core::HID::HIDCore& hid_core_, u8* raw_shared
19 operation_complete_event = service_context.CreateEvent("hid:PalmaOperationCompleteEvent"); 19 operation_complete_event = service_context.CreateEvent("hid:PalmaOperationCompleteEvent");
20} 20}
21 21
22Controller_Palma::~Controller_Palma() = default; 22Controller_Palma::~Controller_Palma() {
23 service_context.CloseEvent(operation_complete_event);
24};
23 25
24void Controller_Palma::OnInit() {} 26void Controller_Palma::OnInit() {}
25 27
diff --git a/src/core/hle/service/hid/hid.cpp b/src/core/hle/service/hid/hid.cpp
index 4d70006c1..929dd5f03 100644
--- a/src/core/hle/service/hid/hid.cpp
+++ b/src/core/hle/service/hid/hid.cpp
@@ -2757,6 +2757,10 @@ public:
2757 joy_detach_event = service_context.CreateEvent("HidSys::JoyDetachEvent"); 2757 joy_detach_event = service_context.CreateEvent("HidSys::JoyDetachEvent");
2758 } 2758 }
2759 2759
2760 ~HidSys() {
2761 service_context.CloseEvent(joy_detach_event);
2762 };
2763
2760private: 2764private:
2761 void ApplyNpadSystemCommonPolicy(HLERequestContext& ctx) { 2765 void ApplyNpadSystemCommonPolicy(HLERequestContext& ctx) {
2762 LOG_WARNING(Service_HID, "called"); 2766 LOG_WARNING(Service_HID, "called");
diff --git a/src/core/hle/service/hid/hidbus/hidbus_base.cpp b/src/core/hle/service/hid/hidbus/hidbus_base.cpp
index ee522c36e..8c44f93e8 100644
--- a/src/core/hle/service/hid/hidbus/hidbus_base.cpp
+++ b/src/core/hle/service/hid/hidbus/hidbus_base.cpp
@@ -13,7 +13,10 @@ HidbusBase::HidbusBase(Core::System& system_, KernelHelpers::ServiceContext& ser
13 : system(system_), service_context(service_context_) { 13 : system(system_), service_context(service_context_) {
14 send_command_async_event = service_context.CreateEvent("hidbus:SendCommandAsyncEvent"); 14 send_command_async_event = service_context.CreateEvent("hidbus:SendCommandAsyncEvent");
15} 15}
16HidbusBase::~HidbusBase() = default; 16
17HidbusBase::~HidbusBase() {
18 service_context.CloseEvent(send_command_async_event);
19};
17 20
18void HidbusBase::ActivateDevice() { 21void HidbusBase::ActivateDevice() {
19 if (is_activated) { 22 if (is_activated) {
diff --git a/src/core/hle/service/pctl/pctl_module.cpp b/src/core/hle/service/pctl/pctl_module.cpp
index 938330dd0..6a7fd72bc 100644
--- a/src/core/hle/service/pctl/pctl_module.cpp
+++ b/src/core/hle/service/pctl/pctl_module.cpp
@@ -141,6 +141,12 @@ public:
141 service_context.CreateEvent("IParentalControlService::RequestSuspensionEvent"); 141 service_context.CreateEvent("IParentalControlService::RequestSuspensionEvent");
142 } 142 }
143 143
144 ~IParentalControlService() {
145 service_context.CloseEvent(synchronization_event);
146 service_context.CloseEvent(unlinked_event);
147 service_context.CloseEvent(request_suspension_event);
148 };
149
144private: 150private:
145 bool CheckFreeCommunicationPermissionImpl() const { 151 bool CheckFreeCommunicationPermissionImpl() const {
146 if (states.temporary_unlocked) { 152 if (states.temporary_unlocked) {
diff --git a/src/video_core/renderer_vulkan/vk_state_tracker.cpp b/src/video_core/renderer_vulkan/vk_state_tracker.cpp
index d56558a83..daaea2979 100644
--- a/src/video_core/renderer_vulkan/vk_state_tracker.cpp
+++ b/src/video_core/renderer_vulkan/vk_state_tracker.cpp
@@ -190,7 +190,7 @@ void SetupDirtySpecialOps(Tables& tables) {
190void SetupDirtyViewportSwizzles(Tables& tables) { 190void SetupDirtyViewportSwizzles(Tables& tables) {
191 static constexpr size_t swizzle_offset = 6; 191 static constexpr size_t swizzle_offset = 6;
192 for (size_t index = 0; index < Regs::NumViewports; ++index) { 192 for (size_t index = 0; index < Regs::NumViewports; ++index) {
193 tables[0][OFF(viewport_transform) + index * NUM(viewport_transform[0]) + swizzle_offset] = 193 tables[1][OFF(viewport_transform) + index * NUM(viewport_transform[0]) + swizzle_offset] =
194 ViewportSwizzles; 194 ViewportSwizzles;
195 } 195 }
196} 196}
diff --git a/src/yuzu/CMakeLists.txt b/src/yuzu/CMakeLists.txt
index 34208ed74..33e1fb663 100644
--- a/src/yuzu/CMakeLists.txt
+++ b/src/yuzu/CMakeLists.txt
@@ -227,14 +227,14 @@ add_executable(yuzu
227 yuzu.rc 227 yuzu.rc
228) 228)
229 229
230if (WIN32 AND YUZU_CRASH_DUMPS) 230if (YUZU_CRASH_DUMPS)
231 target_sources(yuzu PRIVATE 231 target_sources(yuzu PRIVATE
232 mini_dump.cpp 232 breakpad.cpp
233 mini_dump.h 233 breakpad.h
234 ) 234 )
235 235
236 target_link_libraries(yuzu PRIVATE ${DBGHELP_LIBRARY}) 236 target_link_libraries(yuzu PRIVATE libbreakpad_client)
237 target_compile_definitions(yuzu PRIVATE -DYUZU_DBGHELP) 237 target_compile_definitions(yuzu PRIVATE YUZU_CRASH_DUMPS)
238endif() 238endif()
239 239
240if (CMAKE_CXX_COMPILER_ID STREQUAL "Clang") 240if (CMAKE_CXX_COMPILER_ID STREQUAL "Clang")
diff --git a/src/yuzu/breakpad.cpp b/src/yuzu/breakpad.cpp
new file mode 100644
index 000000000..0f6a71ab0
--- /dev/null
+++ b/src/yuzu/breakpad.cpp
@@ -0,0 +1,77 @@
1// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include <algorithm>
5#include <ranges>
6
7#if defined(_WIN32)
8#include <client/windows/handler/exception_handler.h>
9#elif defined(__linux__)
10#include <client/linux/handler/exception_handler.h>
11#else
12#error Minidump creation not supported on this platform
13#endif
14
15#include "common/fs/fs_paths.h"
16#include "common/fs/path_util.h"
17#include "yuzu/breakpad.h"
18
19namespace Breakpad {
20
21static void PruneDumpDirectory(const std::filesystem::path& dump_path) {
22 // Code in this function should be exception-safe.
23 struct Entry {
24 std::filesystem::path path;
25 std::filesystem::file_time_type last_write_time;
26 };
27 std::vector<Entry> existing_dumps;
28
29 // Get existing entries.
30 std::error_code ec;
31 std::filesystem::directory_iterator dir(dump_path, ec);
32 for (auto& entry : dir) {
33 if (entry.is_regular_file()) {
34 existing_dumps.push_back(Entry{
35 .path = entry.path(),
36 .last_write_time = entry.last_write_time(ec),
37 });
38 }
39 }
40
41 // Sort descending by creation date.
42 std::ranges::stable_sort(existing_dumps, [](const auto& a, const auto& b) {
43 return a.last_write_time > b.last_write_time;
44 });
45
46 // Delete older dumps.
47 for (size_t i = 5; i < existing_dumps.size(); i++) {
48 std::filesystem::remove(existing_dumps[i].path, ec);
49 }
50}
51
52#if defined(__linux__)
53[[noreturn]] bool DumpCallback(const google_breakpad::MinidumpDescriptor& descriptor, void* context,
54 bool succeeded) {
55 // Prevent time- and space-consuming core dumps from being generated, as we have
56 // already generated a minidump and a core file will not be useful anyway.
57 _exit(1);
58}
59#endif
60
61void InstallCrashHandler() {
62 // Write crash dumps to profile directory.
63 const auto dump_path = GetYuzuPath(Common::FS::YuzuPath::CrashDumpsDir);
64 PruneDumpDirectory(dump_path);
65
66#if defined(_WIN32)
67 // TODO: If we switch to MinGW builds for Windows, this needs to be wrapped in a C API.
68 static google_breakpad::ExceptionHandler eh{dump_path, nullptr, nullptr, nullptr,
69 google_breakpad::ExceptionHandler::HANDLER_ALL};
70#elif defined(__linux__)
71 static google_breakpad::MinidumpDescriptor descriptor{dump_path};
72 static google_breakpad::ExceptionHandler eh{descriptor, nullptr, DumpCallback,
73 nullptr, true, -1};
74#endif
75}
76
77} // namespace Breakpad
diff --git a/src/yuzu/breakpad.h b/src/yuzu/breakpad.h
new file mode 100644
index 000000000..0f911aa9c
--- /dev/null
+++ b/src/yuzu/breakpad.h
@@ -0,0 +1,10 @@
1// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6namespace Breakpad {
7
8void InstallCrashHandler();
9
10}
diff --git a/src/yuzu/configuration/configure_debug.cpp b/src/yuzu/configuration/configure_debug.cpp
index b22fda746..ef421c754 100644
--- a/src/yuzu/configuration/configure_debug.cpp
+++ b/src/yuzu/configuration/configure_debug.cpp
@@ -27,16 +27,6 @@ ConfigureDebug::ConfigureDebug(const Core::System& system_, QWidget* parent)
27 27
28 connect(ui->toggle_gdbstub, &QCheckBox::toggled, 28 connect(ui->toggle_gdbstub, &QCheckBox::toggled,
29 [&]() { ui->gdbport_spinbox->setEnabled(ui->toggle_gdbstub->isChecked()); }); 29 [&]() { ui->gdbport_spinbox->setEnabled(ui->toggle_gdbstub->isChecked()); });
30
31 connect(ui->create_crash_dumps, &QCheckBox::stateChanged, [&](int) {
32 if (crash_dump_warning_shown) {
33 return;
34 }
35 QMessageBox::warning(this, tr("Restart Required"),
36 tr("yuzu is required to restart in order to apply this setting."),
37 QMessageBox::Ok, QMessageBox::Ok);
38 crash_dump_warning_shown = true;
39 });
40} 30}
41 31
42ConfigureDebug::~ConfigureDebug() = default; 32ConfigureDebug::~ConfigureDebug() = default;
@@ -89,13 +79,6 @@ void ConfigureDebug::SetConfiguration() {
89 ui->disable_web_applet->setEnabled(false); 79 ui->disable_web_applet->setEnabled(false);
90 ui->disable_web_applet->setText(tr("Web applet not compiled")); 80 ui->disable_web_applet->setText(tr("Web applet not compiled"));
91#endif 81#endif
92
93#ifdef YUZU_DBGHELP
94 ui->create_crash_dumps->setChecked(Settings::values.create_crash_dumps.GetValue());
95#else
96 ui->create_crash_dumps->setEnabled(false);
97 ui->create_crash_dumps->setText(tr("MiniDump creation not compiled"));
98#endif
99} 82}
100 83
101void ConfigureDebug::ApplyConfiguration() { 84void ConfigureDebug::ApplyConfiguration() {
@@ -107,7 +90,6 @@ void ConfigureDebug::ApplyConfiguration() {
107 Settings::values.enable_fs_access_log = ui->fs_access_log->isChecked(); 90 Settings::values.enable_fs_access_log = ui->fs_access_log->isChecked();
108 Settings::values.reporting_services = ui->reporting_services->isChecked(); 91 Settings::values.reporting_services = ui->reporting_services->isChecked();
109 Settings::values.dump_audio_commands = ui->dump_audio_commands->isChecked(); 92 Settings::values.dump_audio_commands = ui->dump_audio_commands->isChecked();
110 Settings::values.create_crash_dumps = ui->create_crash_dumps->isChecked();
111 Settings::values.quest_flag = ui->quest_flag->isChecked(); 93 Settings::values.quest_flag = ui->quest_flag->isChecked();
112 Settings::values.use_debug_asserts = ui->use_debug_asserts->isChecked(); 94 Settings::values.use_debug_asserts = ui->use_debug_asserts->isChecked();
113 Settings::values.use_auto_stub = ui->use_auto_stub->isChecked(); 95 Settings::values.use_auto_stub = ui->use_auto_stub->isChecked();
diff --git a/src/yuzu/configuration/configure_debug.ui b/src/yuzu/configuration/configure_debug.ui
index 66b8b7459..76fe98924 100644
--- a/src/yuzu/configuration/configure_debug.ui
+++ b/src/yuzu/configuration/configure_debug.ui
@@ -471,13 +471,6 @@
471 </property> 471 </property>
472 </widget> 472 </widget>
473 </item> 473 </item>
474 <item row="4" column="0">
475 <widget class="QCheckBox" name="create_crash_dumps">
476 <property name="text">
477 <string>Create Minidump After Crash</string>
478 </property>
479 </widget>
480 </item>
481 <item row="3" column="0"> 474 <item row="3" column="0">
482 <widget class="QCheckBox" name="dump_audio_commands"> 475 <widget class="QCheckBox" name="dump_audio_commands">
483 <property name="toolTip"> 476 <property name="toolTip">
diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp
index 816d804c4..7cc11ae3b 100644
--- a/src/yuzu/main.cpp
+++ b/src/yuzu/main.cpp
@@ -159,8 +159,8 @@ static FileSys::VirtualFile VfsDirectoryCreateFileWrapper(const FileSys::Virtual
159#include "yuzu/util/clickable_label.h" 159#include "yuzu/util/clickable_label.h"
160#include "yuzu/vk_device_info.h" 160#include "yuzu/vk_device_info.h"
161 161
162#ifdef YUZU_DBGHELP 162#ifdef YUZU_CRASH_DUMPS
163#include "yuzu/mini_dump.h" 163#include "yuzu/breakpad.h"
164#endif 164#endif
165 165
166using namespace Common::Literals; 166using namespace Common::Literals;
@@ -5187,22 +5187,15 @@ int main(int argc, char* argv[]) {
5187 return 0; 5187 return 0;
5188 } 5188 }
5189 5189
5190#ifdef YUZU_DBGHELP
5191 PROCESS_INFORMATION pi;
5192 if (!is_child && Settings::values.create_crash_dumps.GetValue() &&
5193 MiniDump::SpawnDebuggee(argv[0], pi)) {
5194 // Delete the config object so that it doesn't save when the program exits
5195 config.reset(nullptr);
5196 MiniDump::DebugDebuggee(pi);
5197 return 0;
5198 }
5199#endif
5200
5201 if (StartupChecks(argv[0], &has_broken_vulkan, 5190 if (StartupChecks(argv[0], &has_broken_vulkan,
5202 Settings::values.perform_vulkan_check.GetValue())) { 5191 Settings::values.perform_vulkan_check.GetValue())) {
5203 return 0; 5192 return 0;
5204 } 5193 }
5205 5194
5195#ifdef YUZU_CRASH_DUMPS
5196 Breakpad::InstallCrashHandler();
5197#endif
5198
5206 Common::DetachedTasks detached_tasks; 5199 Common::DetachedTasks detached_tasks;
5207 MicroProfileOnThreadCreate("Frontend"); 5200 MicroProfileOnThreadCreate("Frontend");
5208 SCOPE_EXIT({ MicroProfileShutdown(); }); 5201 SCOPE_EXIT({ MicroProfileShutdown(); });
diff --git a/src/yuzu/mini_dump.cpp b/src/yuzu/mini_dump.cpp
deleted file mode 100644
index a34dc6a9c..000000000
--- a/src/yuzu/mini_dump.cpp
+++ /dev/null
@@ -1,202 +0,0 @@
1// SPDX-FileCopyrightText: 2022 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include <cstdio>
5#include <cstring>
6#include <ctime>
7#include <filesystem>
8#include <fmt/format.h>
9#include <windows.h>
10#include "yuzu/mini_dump.h"
11#include "yuzu/startup_checks.h"
12
13// dbghelp.h must be included after windows.h
14#include <dbghelp.h>
15
16namespace MiniDump {
17
18void CreateMiniDump(HANDLE process_handle, DWORD process_id, MINIDUMP_EXCEPTION_INFORMATION* info,
19 EXCEPTION_POINTERS* pep) {
20 char file_name[255];
21 const std::time_t the_time = std::time(nullptr);
22 std::strftime(file_name, 255, "yuzu-crash-%Y%m%d%H%M%S.dmp", std::localtime(&the_time));
23
24 // Open the file
25 HANDLE file_handle = CreateFileA(file_name, GENERIC_READ | GENERIC_WRITE, 0, nullptr,
26 CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, nullptr);
27
28 if (file_handle == nullptr || file_handle == INVALID_HANDLE_VALUE) {
29 fmt::print(stderr, "CreateFileA failed. Error: {}", GetLastError());
30 return;
31 }
32
33 // Create the minidump
34 const MINIDUMP_TYPE dump_type = MiniDumpNormal;
35
36 const bool write_dump_status = MiniDumpWriteDump(process_handle, process_id, file_handle,
37 dump_type, (pep != 0) ? info : 0, 0, 0);
38
39 if (write_dump_status) {
40 fmt::print(stderr, "MiniDump created: {}", file_name);
41 } else {
42 fmt::print(stderr, "MiniDumpWriteDump failed. Error: {}", GetLastError());
43 }
44
45 // Close the file
46 CloseHandle(file_handle);
47}
48
49void DumpFromDebugEvent(DEBUG_EVENT& deb_ev, PROCESS_INFORMATION& pi) {
50 EXCEPTION_RECORD& record = deb_ev.u.Exception.ExceptionRecord;
51
52 HANDLE thread_handle = OpenThread(THREAD_GET_CONTEXT, false, deb_ev.dwThreadId);
53 if (thread_handle == nullptr) {
54 fmt::print(stderr, "OpenThread failed ({})", GetLastError());
55 return;
56 }
57
58 // Get child process context
59 CONTEXT context = {};
60 context.ContextFlags = CONTEXT_ALL;
61 if (!GetThreadContext(thread_handle, &context)) {
62 fmt::print(stderr, "GetThreadContext failed ({})", GetLastError());
63 return;
64 }
65
66 // Create exception pointers for minidump
67 EXCEPTION_POINTERS ep;
68 ep.ExceptionRecord = &record;
69 ep.ContextRecord = &context;
70
71 MINIDUMP_EXCEPTION_INFORMATION info;
72 info.ThreadId = deb_ev.dwThreadId;
73 info.ExceptionPointers = &ep;
74 info.ClientPointers = false;
75
76 CreateMiniDump(pi.hProcess, pi.dwProcessId, &info, &ep);
77
78 if (CloseHandle(thread_handle) == 0) {
79 fmt::print(stderr, "error: CloseHandle(thread_handle) failed ({})", GetLastError());
80 }
81}
82
83bool SpawnDebuggee(const char* arg0, PROCESS_INFORMATION& pi) {
84 std::memset(&pi, 0, sizeof(pi));
85
86 // Don't debug if we are already being debugged
87 if (IsDebuggerPresent()) {
88 return false;
89 }
90
91 if (!SpawnChild(arg0, &pi, 0)) {
92 fmt::print(stderr, "warning: continuing without crash dumps");
93 return false;
94 }
95
96 const bool can_debug = DebugActiveProcess(pi.dwProcessId);
97 if (!can_debug) {
98 fmt::print(stderr,
99 "warning: DebugActiveProcess failed ({}), continuing without crash dumps",
100 GetLastError());
101 return false;
102 }
103
104 return true;
105}
106
107static const char* ExceptionName(DWORD exception) {
108 switch (exception) {
109 case EXCEPTION_ACCESS_VIOLATION:
110 return "EXCEPTION_ACCESS_VIOLATION";
111 case EXCEPTION_DATATYPE_MISALIGNMENT:
112 return "EXCEPTION_DATATYPE_MISALIGNMENT";
113 case EXCEPTION_BREAKPOINT:
114 return "EXCEPTION_BREAKPOINT";
115 case EXCEPTION_SINGLE_STEP:
116 return "EXCEPTION_SINGLE_STEP";
117 case EXCEPTION_ARRAY_BOUNDS_EXCEEDED:
118 return "EXCEPTION_ARRAY_BOUNDS_EXCEEDED";
119 case EXCEPTION_FLT_DENORMAL_OPERAND:
120 return "EXCEPTION_FLT_DENORMAL_OPERAND";
121 case EXCEPTION_FLT_DIVIDE_BY_ZERO:
122 return "EXCEPTION_FLT_DIVIDE_BY_ZERO";
123 case EXCEPTION_FLT_INEXACT_RESULT:
124 return "EXCEPTION_FLT_INEXACT_RESULT";
125 case EXCEPTION_FLT_INVALID_OPERATION:
126 return "EXCEPTION_FLT_INVALID_OPERATION";
127 case EXCEPTION_FLT_OVERFLOW:
128 return "EXCEPTION_FLT_OVERFLOW";
129 case EXCEPTION_FLT_STACK_CHECK:
130 return "EXCEPTION_FLT_STACK_CHECK";
131 case EXCEPTION_FLT_UNDERFLOW:
132 return "EXCEPTION_FLT_UNDERFLOW";
133 case EXCEPTION_INT_DIVIDE_BY_ZERO:
134 return "EXCEPTION_INT_DIVIDE_BY_ZERO";
135 case EXCEPTION_INT_OVERFLOW:
136 return "EXCEPTION_INT_OVERFLOW";
137 case EXCEPTION_PRIV_INSTRUCTION:
138 return "EXCEPTION_PRIV_INSTRUCTION";
139 case EXCEPTION_IN_PAGE_ERROR:
140 return "EXCEPTION_IN_PAGE_ERROR";
141 case EXCEPTION_ILLEGAL_INSTRUCTION:
142 return "EXCEPTION_ILLEGAL_INSTRUCTION";
143 case EXCEPTION_NONCONTINUABLE_EXCEPTION:
144 return "EXCEPTION_NONCONTINUABLE_EXCEPTION";
145 case EXCEPTION_STACK_OVERFLOW:
146 return "EXCEPTION_STACK_OVERFLOW";
147 case EXCEPTION_INVALID_DISPOSITION:
148 return "EXCEPTION_INVALID_DISPOSITION";
149 case EXCEPTION_GUARD_PAGE:
150 return "EXCEPTION_GUARD_PAGE";
151 case EXCEPTION_INVALID_HANDLE:
152 return "EXCEPTION_INVALID_HANDLE";
153 default:
154 return "unknown exception type";
155 }
156}
157
158void DebugDebuggee(PROCESS_INFORMATION& pi) {
159 DEBUG_EVENT deb_ev = {};
160
161 while (deb_ev.dwDebugEventCode != EXIT_PROCESS_DEBUG_EVENT) {
162 const bool wait_success = WaitForDebugEvent(&deb_ev, INFINITE);
163 if (!wait_success) {
164 fmt::print(stderr, "error: WaitForDebugEvent failed ({})", GetLastError());
165 return;
166 }
167
168 switch (deb_ev.dwDebugEventCode) {
169 case OUTPUT_DEBUG_STRING_EVENT:
170 case CREATE_PROCESS_DEBUG_EVENT:
171 case CREATE_THREAD_DEBUG_EVENT:
172 case EXIT_PROCESS_DEBUG_EVENT:
173 case EXIT_THREAD_DEBUG_EVENT:
174 case LOAD_DLL_DEBUG_EVENT:
175 case RIP_EVENT:
176 case UNLOAD_DLL_DEBUG_EVENT:
177 // Continue on all other debug events
178 ContinueDebugEvent(deb_ev.dwProcessId, deb_ev.dwThreadId, DBG_CONTINUE);
179 break;
180 case EXCEPTION_DEBUG_EVENT:
181 EXCEPTION_RECORD& record = deb_ev.u.Exception.ExceptionRecord;
182
183 // We want to generate a crash dump if we are seeing the same exception again.
184 if (!deb_ev.u.Exception.dwFirstChance) {
185 fmt::print(stderr, "Creating MiniDump on ExceptionCode: 0x{:08x} {}\n",
186 record.ExceptionCode, ExceptionName(record.ExceptionCode));
187 DumpFromDebugEvent(deb_ev, pi);
188 }
189
190 // Continue without handling the exception.
191 // Lets the debuggee use its own exception handler.
192 // - If one does not exist, we will see the exception once more where we make a minidump
193 // for. Then when it reaches here again, yuzu will probably crash.
194 // - DBG_CONTINUE on an exception that the debuggee does not handle can set us up for an
195 // infinite loop of exceptions.
196 ContinueDebugEvent(deb_ev.dwProcessId, deb_ev.dwThreadId, DBG_EXCEPTION_NOT_HANDLED);
197 break;
198 }
199 }
200}
201
202} // namespace MiniDump
diff --git a/src/yuzu/mini_dump.h b/src/yuzu/mini_dump.h
deleted file mode 100644
index d6b6cca84..000000000
--- a/src/yuzu/mini_dump.h
+++ /dev/null
@@ -1,19 +0,0 @@
1// SPDX-FileCopyrightText: 2022 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include <windows.h>
7
8#include <dbghelp.h>
9
10namespace MiniDump {
11
12void CreateMiniDump(HANDLE process_handle, DWORD process_id, MINIDUMP_EXCEPTION_INFORMATION* info,
13 EXCEPTION_POINTERS* pep);
14
15void DumpFromDebugEvent(DEBUG_EVENT& deb_ev, PROCESS_INFORMATION& pi);
16bool SpawnDebuggee(const char* arg0, PROCESS_INFORMATION& pi);
17void DebugDebuggee(PROCESS_INFORMATION& pi);
18
19} // namespace MiniDump