diff --git a/libraries/libstratosphere/include/stratosphere/pm/pm_shell_api.hpp b/libraries/libstratosphere/include/stratosphere/pm/pm_shell_api.hpp index 4dd79409c..c38452710 100644 --- a/libraries/libstratosphere/include/stratosphere/pm/pm_shell_api.hpp +++ b/libraries/libstratosphere/include/stratosphere/pm/pm_shell_api.hpp @@ -25,6 +25,8 @@ namespace ams::pm::shell { /* Shell API. */ Result LaunchProgram(os::ProcessId *out, const ncm::ProgramLocation &loc, u32 launch_flags); Result TerminateProcess(os::ProcessId process_id); + Result GetProcessEventEvent(os::SystemEvent *out); + Result GetProcessEventInfo(ProcessEventInfo *out); Result GetApplicationProcessIdForShell(os::ProcessId *out); Result BoostSystemMemoryResourceLimit(u64 size); Result EnableApplicationExtraThread(); diff --git a/libraries/libstratosphere/include/stratosphere/pm/pm_types.hpp b/libraries/libstratosphere/include/stratosphere/pm/pm_types.hpp index fbd4931fe..f433bddb4 100644 --- a/libraries/libstratosphere/include/stratosphere/pm/pm_types.hpp +++ b/libraries/libstratosphere/include/stratosphere/pm/pm_types.hpp @@ -54,7 +54,7 @@ namespace ams::pm { constexpr inline u32 LaunchFlagsMask = (1 << 6) - 1; - enum class ProcessEvent { + enum class ProcessEvent : u32 { None = 0, Exited = 1, Started = 2, @@ -63,7 +63,7 @@ namespace ams::pm { DebugBreak = 5, }; - enum class ProcessEventDeprecated { + enum class ProcessEventDeprecated : u32 { None = 0, Exception = 1, Exited = 2, diff --git a/libraries/libstratosphere/include/stratosphere/settings.hpp b/libraries/libstratosphere/include/stratosphere/settings.hpp index 8c95dc127..a509fe93a 100644 --- a/libraries/libstratosphere/include/stratosphere/settings.hpp +++ b/libraries/libstratosphere/include/stratosphere/settings.hpp @@ -19,6 +19,7 @@ #include "settings/settings_types.hpp" #include "settings/settings_fwdbg_types.hpp" #include "settings/settings_fwdbg_api.hpp" +#include "settings/system/settings_error_report.hpp" #include "settings/system/settings_firmware_version.hpp" #include "settings/system/settings_product_model.hpp" #include "settings/system/settings_region.hpp" diff --git a/libraries/libstratosphere/include/stratosphere/settings/system/settings_error_report.hpp b/libraries/libstratosphere/include/stratosphere/settings/system/settings_error_report.hpp new file mode 100644 index 000000000..c0c67d5b8 --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/settings/system/settings_error_report.hpp @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#pragma once +#include +#include + +namespace ams::settings::system { + + enum ErrorReportSharePermission { + ErrorReportSharePermission_NotConfirmed = 0, + ErrorReportSharePermission_Granted = 1, + ErrorReportSharePermission_Denied = 2, + }; + + ErrorReportSharePermission GetErrorReportSharePermission(); + +} diff --git a/libraries/libstratosphere/source/lr/lr_remote_location_resolver_impl.hpp b/libraries/libstratosphere/source/lr/lr_remote_location_resolver_impl.hpp index 5ac2b47e4..59093e7d8 100644 --- a/libraries/libstratosphere/source/lr/lr_remote_location_resolver_impl.hpp +++ b/libraries/libstratosphere/source/lr/lr_remote_location_resolver_impl.hpp @@ -28,19 +28,19 @@ namespace ams::lr { public: /* Actual commands. */ virtual Result ResolveProgramPath(sf::Out out, ncm::ProgramId id) override { - return lrLrResolveProgramPath(std::addressof(this->srv), static_cast(id), out->str); + return lrLrResolveProgramPath(std::addressof(this->srv), id.value, out->str); } virtual Result RedirectProgramPath(const Path &path, ncm::ProgramId id) override { - return lrLrRedirectProgramPath(std::addressof(this->srv), static_cast(id), path.str); + return lrLrRedirectProgramPath(std::addressof(this->srv), id.value, path.str); } virtual Result ResolveApplicationControlPath(sf::Out out, ncm::ProgramId id) override { - return lrLrResolveApplicationControlPath(std::addressof(this->srv), static_cast(id), out->str); + return lrLrResolveApplicationControlPath(std::addressof(this->srv), id.value, out->str); } virtual Result ResolveApplicationHtmlDocumentPath(sf::Out out, ncm::ProgramId id) override { - return lrLrResolveApplicationHtmlDocumentPath(std::addressof(this->srv), static_cast(id), out->str); + return lrLrResolveApplicationHtmlDocumentPath(std::addressof(this->srv), id.value, out->str); } virtual Result ResolveDataPath(sf::Out out, ncm::DataId id) override { @@ -48,31 +48,31 @@ namespace ams::lr { } virtual Result RedirectApplicationControlPathDeprecated(const Path &path, ncm::ProgramId id) override { - return lrLrRedirectApplicationControlPath(std::addressof(this->srv), static_cast(id), 0, path.str); + return lrLrRedirectApplicationControlPath(std::addressof(this->srv), id.value, 0, path.str); } virtual Result RedirectApplicationControlPath(const Path &path, ncm::ProgramId id, ncm::ProgramId owner_id) override { - return lrLrRedirectApplicationControlPath(std::addressof(this->srv), static_cast(id), static_cast(owner_id), path.str); + return lrLrRedirectApplicationControlPath(std::addressof(this->srv), id.value, owner_id.value, path.str); } virtual Result RedirectApplicationHtmlDocumentPathDeprecated(const Path &path, ncm::ProgramId id) override { - return lrLrRedirectApplicationHtmlDocumentPath(std::addressof(this->srv), static_cast(id), 0, path.str); + return lrLrRedirectApplicationHtmlDocumentPath(std::addressof(this->srv), id.value, 0, path.str); } virtual Result RedirectApplicationHtmlDocumentPath(const Path &path, ncm::ProgramId id, ncm::ProgramId owner_id) override { - return lrLrRedirectApplicationHtmlDocumentPath(std::addressof(this->srv), static_cast(id), static_cast(owner_id), path.str); + return lrLrRedirectApplicationHtmlDocumentPath(std::addressof(this->srv), id.value, owner_id.value, path.str); } virtual Result ResolveApplicationLegalInformationPath(sf::Out out, ncm::ProgramId id) override { - return lrLrResolveApplicationLegalInformationPath(std::addressof(this->srv), static_cast(id), out->str); + return lrLrResolveApplicationLegalInformationPath(std::addressof(this->srv), id.value, out->str); } virtual Result RedirectApplicationLegalInformationPathDeprecated(const Path &path, ncm::ProgramId id) override { - return lrLrRedirectApplicationLegalInformationPath(std::addressof(this->srv), static_cast(id), 0, path.str); + return lrLrRedirectApplicationLegalInformationPath(std::addressof(this->srv), id.value, 0, path.str); } virtual Result RedirectApplicationLegalInformationPath(const Path &path, ncm::ProgramId id, ncm::ProgramId owner_id) override { - return lrLrRedirectApplicationLegalInformationPath(std::addressof(this->srv), static_cast(id), static_cast(owner_id), path.str); + return lrLrRedirectApplicationLegalInformationPath(std::addressof(this->srv), id.value, owner_id.value, path.str); } virtual Result Refresh() override { @@ -100,8 +100,7 @@ namespace ams::lr { } virtual Result EraseProgramRedirection(ncm::ProgramId id) override { - /* TODO: libnx bindings */ - AMS_ABORT(); + return lrLrEraseProgramRedirection(std::addressof(this->srv), id.value); } virtual Result EraseApplicationControlRedirection(ncm::ProgramId id) override { diff --git a/libraries/libstratosphere/source/pgl/srv/pgl_srv_shell.cpp b/libraries/libstratosphere/source/pgl/srv/pgl_srv_shell.cpp index 5ae538539..37537bd4b 100644 --- a/libraries/libstratosphere/source/pgl/srv/pgl_srv_shell.cpp +++ b/libraries/libstratosphere/source/pgl/srv/pgl_srv_shell.cpp @@ -140,8 +140,182 @@ namespace ams::pgl::srv { return pm::shell::LaunchProgram(std::addressof(dummy_process_id), ncm::ProgramLocation::Make(ncm::SystemDebugAppletId::SnapShotDumper, ncm::StorageId::BuiltInSystem), pm::LaunchFlags_None); } + bool ShouldSnapShotAutoDump() { + bool dump; + const size_t sz = settings::fwdbg::GetSettingsItemValue(std::addressof(dump), sizeof(dump), "snap_shot_dump", "auto_dump"); + return sz == sizeof(dump) && dump; + } + + bool ShouldSnapShotFullDump() { + bool dump; + const size_t sz = settings::fwdbg::GetSettingsItemValue(std::addressof(dump), sizeof(dump), "snap_shot_dump", "full_dump"); + return sz == sizeof(dump) && dump; + } + + SnapShotDumpType GetSnapShotDumpType() { + if (ShouldSnapShotAutoDump()) { + if (ShouldSnapShotFullDump()) { + return SnapShotDumpType::Full; + } else { + return SnapShotDumpType::Auto; + } + } else { + return SnapShotDumpType::None; + } + } + + void TriggerSnapShotDumper(os::ProcessId process_id) { + TriggerSnapShotDumper(process_id, GetSnapShotDumpType(), nullptr); + } + + s32 GetCrashReportDetailedArgument(u32 data_flags) { + if (((data_flags & ProcessDataFlag_DetailedCrashReportAllowed) != 0) && ((data_flags & ProcessDataFlag_DetailedCrashReportEnabled) != 0)) { + return 1; + } else { + return 0; + } + } + + s32 GetCrashReportScreenShotArgument(u32 data_flags) { + if (settings::system::GetErrorReportSharePermission() == settings::system::ErrorReportSharePermission_Granted) { + return ((data_flags & ProcessDataFlag_EnableCrashReportScreenShot) != 0) ? 1 : 0; + } else { + return 0; + } + } + + void TriggerCrashReport(os::ProcessId process_id) { + static os::ProcessId s_crashed_process_id = os::InvalidProcessId; + static os::ProcessId s_creport_process_id = os::InvalidProcessId; + + /* If the program that crashed is creport, we should just terminate both processes and return. */ + if (process_id == s_creport_process_id) { + TerminateProcess(s_crashed_process_id); + TerminateProcess(s_creport_process_id); + s_crashed_process_id = os::InvalidProcessId; + s_creport_process_id = os::InvalidProcessId; + return; + } + + /* Get the data flags for the process. */ + u32 data_flags; + { + std::scoped_lock lk(g_process_data_mutex); + if (auto *data = FindProcessData(process_id); data != nullptr) { + data_flags = data->flags; + } else { + data_flags = ProcessDataFlag_None; + } + } + + /* Generate arguments. */ + char arguments[0x40]; + const size_t len = std::snprintf(arguments, sizeof(arguments), "%ld %d %d", static_cast(static_cast(process_id)), GetCrashReportDetailedArgument(data_flags), GetCrashReportScreenShotArgument(data_flags)); + if (R_FAILED(ldr::SetProgramArgument(ncm::SystemProgramId::Creport, arguments, len + 1))) { + return; + } + + /* Launch creport. */ + os::ProcessId creport_process_id; + if (R_FAILED(pm::shell::LaunchProgram(std::addressof(creport_process_id), ncm::ProgramLocation::Make(ncm::SystemProgramId::Creport, ncm::StorageId::BuiltInSystem), pm::LaunchFlags_None))) { + return; + } + + /* Set the globals. */ + s_crashed_process_id = process_id; + s_creport_process_id = creport_process_id; + } + + void HandleException(os::ProcessId process_id) { + if (g_enable_jit_debug) { + /* If jit debug is enabled, we want to try to launch snap shot dumper. */ + ProcessData *data = nullptr; + { + std::scoped_lock lk(g_process_data_mutex); + data = FindProcessData(process_id); + } + + /* If we're tracking the process, we can launch dumper. Otherwise we should just terminate. */ + if (data != nullptr) { + TriggerSnapShotDumper(process_id); + } else { + TerminateProcess(process_id); + } + } else { + /* Otherwise, we want to launch creport. */ + TriggerCrashReport(process_id); + } + } + + void HandleExit(os::ProcessId process_id) { + std::scoped_lock lk(g_process_data_mutex); + if (auto *data = FindProcessData(process_id); data != nullptr) { + data->process_id = os::InvalidProcessId; + } + } + + void OnProcessEvent(const pm::ProcessEventInfo &event_info) { + /* Determine if we're tracking the process. */ + ProcessData *data = nullptr; + { + std::scoped_lock lk(g_process_data_mutex); + data = FindProcessData(event_info.process_id); + } + + /* If we are, we're going to want to notify our listeners. */ + if (data != nullptr) { + /* If we closed the process, note that. */ + if (static_cast(event_info.event) == pm::ProcessEvent::Exited) { + HandleExit(event_info.process_id); + } + + /* Notify all observers. */ + std::scoped_lock lk(g_observer_list_mutex); + for (auto &observer : g_observer_list) { + observer.Notify(event_info); + } + } + + /* If the process crashed, handle that. */ + if (static_cast(event_info.event) == pm::ProcessEvent::Exception) { + HandleException(event_info.process_id); + } + } + void ProcessControlTask(void *) { - /* TODO */ + /* Get the process event event from pm. */ + os::SystemEvent process_event; + R_ABORT_UNLESS(pm::shell::GetProcessEventEvent(std::addressof(process_event))); + + while (true) { + /* Wait for an event to come in, and clear our signal. */ + process_event.Wait(); + process_event.Signal(); + + bool continue_getting_event = true; + while (continue_getting_event) { + /* Try to get an event info. */ + pm::ProcessEventInfo event_info; + if (R_FAILED(pm::shell::GetProcessEventInfo(std::addressof(event_info)))) { + break; + } + + /* Process the event. */ + switch (static_cast(event_info.event)) { + case pm::ProcessEvent::None: + continue_getting_event = false; + break; + case pm::ProcessEvent::Exited: + case pm::ProcessEvent::Started: + case pm::ProcessEvent::Exception: + case pm::ProcessEvent::DebugRunning: + case pm::ProcessEvent::DebugBreak: + OnProcessEvent(event_info); + break; + AMS_UNREACHABLE_DEFAULT_CASE(); + } + } + } } } diff --git a/libraries/libstratosphere/source/pm/pm_shell_api.cpp b/libraries/libstratosphere/source/pm/pm_shell_api.cpp index cee379611..dd3155edd 100644 --- a/libraries/libstratosphere/source/pm/pm_shell_api.cpp +++ b/libraries/libstratosphere/source/pm/pm_shell_api.cpp @@ -28,6 +28,18 @@ namespace ams::pm::shell { return ::pmshellTerminateProcess(static_cast(process_id)); } + Result GetProcessEventEvent(os::SystemEvent *out) { + ::Event evt; + R_TRY(::pmshellGetProcessEventHandle(std::addressof(evt))); + out->Attach(evt.revent, true, svc::InvalidHandle, false, os::EventClearMode_ManualClear); + return ResultSuccess(); + } + + Result GetProcessEventInfo(ProcessEventInfo *out) { + static_assert(sizeof(*out) == sizeof(::PmProcessEventInfo)); + return ::pmshellGetProcessEventInfo(reinterpret_cast<::PmProcessEventInfo *>(out)); + } + Result GetApplicationProcessIdForShell(os::ProcessId *out) { static_assert(sizeof(*out) == sizeof(u64)); return ::pmshellGetApplicationProcessIdForShell(reinterpret_cast(out)); diff --git a/libraries/libstratosphere/source/settings/impl/settings_error_report.cpp b/libraries/libstratosphere/source/settings/impl/settings_error_report.cpp new file mode 100644 index 000000000..26e8ce588 --- /dev/null +++ b/libraries/libstratosphere/source/settings/impl/settings_error_report.cpp @@ -0,0 +1,26 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#include +#include "settings_error_report_impl.hpp" + +namespace ams::settings::impl { + + Result GetErrorReportSharePermission(s32 *out) { + static_assert(sizeof(*out) == sizeof(::SetSysErrorReportSharePermission)); + return ::setsysGetErrorReportSharePermission(reinterpret_cast<::SetSysErrorReportSharePermission *>(out)); + } + +} diff --git a/libraries/libstratosphere/source/settings/impl/settings_error_report_impl.hpp b/libraries/libstratosphere/source/settings/impl/settings_error_report_impl.hpp new file mode 100644 index 000000000..6f791b17d --- /dev/null +++ b/libraries/libstratosphere/source/settings/impl/settings_error_report_impl.hpp @@ -0,0 +1,23 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once +#include + +namespace ams::settings::impl { + + Result GetErrorReportSharePermission(s32 *out); + +} diff --git a/libraries/libstratosphere/source/settings/settings_error_report.cpp b/libraries/libstratosphere/source/settings/settings_error_report.cpp new file mode 100644 index 000000000..f1b8ff528 --- /dev/null +++ b/libraries/libstratosphere/source/settings/settings_error_report.cpp @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#include +#include "impl/settings_error_report_impl.hpp" + +namespace ams::settings::system { + + ErrorReportSharePermission GetErrorReportSharePermission() { + s32 perm = 0; + R_ABORT_UNLESS(settings::impl::GetErrorReportSharePermission(std::addressof(perm))); + return static_cast(model); + } + +}