From aab24ee89f931095585669551d5e3aac0547a573 Mon Sep 17 00:00:00 2001 From: Alwin Esch Date: Fri, 11 Feb 2022 02:38:30 +0100 Subject: [PATCH] Update --- xbmc/addons/interface/Controller.cpp | 15 +- xbmc/addons/interface/InstanceHandler.cpp | 2 - xbmc/addons/interface/InstanceHandler.h | 1 + xbmc/addons/interface/RunningProcess.cpp | 11 +- xbmc/addons/interface/RunningProcess.h | 1 + xbmc/addons/interface/api/addon_base.cpp | 10 +- xbmc/addons/interface/api/addon_base.h | 2 +- .../interface/gui/GUIDialogCrashReporter.cpp | 30 +- .../interface/gui/GUIDialogCrashReporter.h | 18 +- xbmc/addons/interface/launcher/ILauncher.h | 2 + .../interface/launcher/LauncherPosix.cpp | 121 +- .../addons/interface/launcher/LauncherPosix.h | 11 +- xbmc/addons/kodi-dev-kit/cmake/Macros.cmake | 15 + .../kodi-dev-kit/include/kodi/AddonBase.h | 46 +- .../include/kodi/c-api/addon_base.h | 3 +- .../kodi-dev-kit/src/addon/CMakeLists.txt | 28 +- .../kodi-dev-kit/src/addon/api/addon_base.cpp | 30 +- .../kodi-dev-kit/src/addon/api/addon_base.h | 2 +- .../src/addon/core/addon_control.cpp | 2 + .../src/addon/core/addon_control.h | 5 + .../src/addon/third_party/CMakeLists.txt | 12 + .../backward-cpp/BackwardConfig.cmake | 266 + .../third_party/backward-cpp/CMakeLists.txt | 140 + .../third_party/backward-cpp/LICENSE.txt | 21 + .../addon/third_party/backward-cpp/README.md | 442 ++ .../third_party/backward-cpp/backward.cpp | 42 + .../third_party/backward-cpp/backward.hpp | 4471 +++++++++++++++++ .../addon/third_party/backward-cpp/builds.sh | 77 + .../third_party/backward-cpp/conanfile.py | 45 + .../backward-cpp/doc/CMakeLists.txt | 12 + .../third_party/backward-cpp/doc/nice.png | Bin 0 -> 40904 bytes .../third_party/backward-cpp/doc/pretty.png | Bin 0 -> 105498 bytes .../third_party/backward-cpp/doc/rude.png | Bin 0 -> 6183 bytes .../backward-cpp/test/CMakeLists.txt | 19 + .../backward-cpp/test/_test_main.cpp | 241 + .../backward-cpp/test/rectrace.cpp | 98 + .../backward-cpp/test/select_signals.cpp | 51 + .../backward-cpp/test/stacktrace.cpp | 57 + .../third_party/backward-cpp/test/suicide.cpp | 89 + .../third_party/backward-cpp/test/test.cpp | 63 + .../third_party/backward-cpp/test/test.hpp | 183 + .../backward-cpp/test_package/CMakeLists.txt | 13 + .../backward-cpp/test_package/conanfile.py | 14 + .../backward-cpp/test_package/main.cpp | 46 + .../src/addon_runner/CMakeLists.txt | 2 +- .../kodi-dev-kit/src/addon_runner/main.cpp | 23 + .../addon_runner/third_party/CMakeLists.txt | 12 + .../backward-cpp/BackwardConfig.cmake | 266 + .../third_party/backward-cpp/CMakeLists.txt | 140 + .../third_party/backward-cpp/LICENSE.txt | 21 + .../third_party/backward-cpp/README.md | 442 ++ .../third_party/backward-cpp/backward.cpp | 42 + .../third_party/backward-cpp/backward.hpp | 4471 +++++++++++++++++ .../third_party/backward-cpp/builds.sh | 77 + .../third_party/backward-cpp/conanfile.py | 45 + .../backward-cpp/doc/CMakeLists.txt | 12 + .../third_party/backward-cpp/doc/nice.png | Bin 0 -> 40904 bytes .../third_party/backward-cpp/doc/pretty.png | Bin 0 -> 105498 bytes .../third_party/backward-cpp/doc/rude.png | Bin 0 -> 6183 bytes .../backward-cpp/test/CMakeLists.txt | 19 + .../backward-cpp/test/_test_main.cpp | 241 + .../backward-cpp/test/rectrace.cpp | 98 + .../backward-cpp/test/select_signals.cpp | 51 + .../backward-cpp/test/stacktrace.cpp | 57 + .../third_party/backward-cpp/test/suicide.cpp | 89 + .../third_party/backward-cpp/test/test.cpp | 63 + .../third_party/backward-cpp/test/test.hpp | 183 + .../backward-cpp/test_package/CMakeLists.txt | 13 + .../backward-cpp/test_package/conanfile.py | 14 + .../backward-cpp/test_package/main.cpp | 46 + .../kodi-dev-kit/src/shared/api/addon_base.h | 11 +- ...MC_ADDONS_KODIDEVKIT_SRC_ADDON_allfiles.py | 10 + .../src/interface_code_generator.py | 20 +- xbmc/guilib/GUIWindowManager.cpp | 17 + xbmc/messaging/ApplicationMessenger.h | 3 +- xbmc/messaging/helpers/CMakeLists.txt | 6 +- .../messaging/helpers/DialogCrashReporter.cpp | 34 + xbmc/messaging/helpers/DialogCrashReporter.h | 37 + 78 files changed, 13187 insertions(+), 135 deletions(-) create mode 100644 xbmc/addons/kodi-dev-kit/src/addon/third_party/CMakeLists.txt create mode 100644 xbmc/addons/kodi-dev-kit/src/addon/third_party/backward-cpp/BackwardConfig.cmake create mode 100644 xbmc/addons/kodi-dev-kit/src/addon/third_party/backward-cpp/CMakeLists.txt create mode 100644 xbmc/addons/kodi-dev-kit/src/addon/third_party/backward-cpp/LICENSE.txt create mode 100644 xbmc/addons/kodi-dev-kit/src/addon/third_party/backward-cpp/README.md create mode 100644 xbmc/addons/kodi-dev-kit/src/addon/third_party/backward-cpp/backward.cpp create mode 100644 xbmc/addons/kodi-dev-kit/src/addon/third_party/backward-cpp/backward.hpp create mode 100755 xbmc/addons/kodi-dev-kit/src/addon/third_party/backward-cpp/builds.sh create mode 100644 xbmc/addons/kodi-dev-kit/src/addon/third_party/backward-cpp/conanfile.py create mode 100644 xbmc/addons/kodi-dev-kit/src/addon/third_party/backward-cpp/doc/CMakeLists.txt create mode 100644 xbmc/addons/kodi-dev-kit/src/addon/third_party/backward-cpp/doc/nice.png create mode 100644 xbmc/addons/kodi-dev-kit/src/addon/third_party/backward-cpp/doc/pretty.png create mode 100644 xbmc/addons/kodi-dev-kit/src/addon/third_party/backward-cpp/doc/rude.png create mode 100644 xbmc/addons/kodi-dev-kit/src/addon/third_party/backward-cpp/test/CMakeLists.txt create mode 100644 xbmc/addons/kodi-dev-kit/src/addon/third_party/backward-cpp/test/_test_main.cpp create mode 100644 xbmc/addons/kodi-dev-kit/src/addon/third_party/backward-cpp/test/rectrace.cpp create mode 100644 xbmc/addons/kodi-dev-kit/src/addon/third_party/backward-cpp/test/select_signals.cpp create mode 100644 xbmc/addons/kodi-dev-kit/src/addon/third_party/backward-cpp/test/stacktrace.cpp create mode 100644 xbmc/addons/kodi-dev-kit/src/addon/third_party/backward-cpp/test/suicide.cpp create mode 100644 xbmc/addons/kodi-dev-kit/src/addon/third_party/backward-cpp/test/test.cpp create mode 100644 xbmc/addons/kodi-dev-kit/src/addon/third_party/backward-cpp/test/test.hpp create mode 100644 xbmc/addons/kodi-dev-kit/src/addon/third_party/backward-cpp/test_package/CMakeLists.txt create mode 100644 xbmc/addons/kodi-dev-kit/src/addon/third_party/backward-cpp/test_package/conanfile.py create mode 100644 xbmc/addons/kodi-dev-kit/src/addon/third_party/backward-cpp/test_package/main.cpp create mode 100644 xbmc/addons/kodi-dev-kit/src/addon_runner/third_party/CMakeLists.txt create mode 100644 xbmc/addons/kodi-dev-kit/src/addon_runner/third_party/backward-cpp/BackwardConfig.cmake create mode 100644 xbmc/addons/kodi-dev-kit/src/addon_runner/third_party/backward-cpp/CMakeLists.txt create mode 100644 xbmc/addons/kodi-dev-kit/src/addon_runner/third_party/backward-cpp/LICENSE.txt create mode 100644 xbmc/addons/kodi-dev-kit/src/addon_runner/third_party/backward-cpp/README.md create mode 100644 xbmc/addons/kodi-dev-kit/src/addon_runner/third_party/backward-cpp/backward.cpp create mode 100644 xbmc/addons/kodi-dev-kit/src/addon_runner/third_party/backward-cpp/backward.hpp create mode 100755 xbmc/addons/kodi-dev-kit/src/addon_runner/third_party/backward-cpp/builds.sh create mode 100644 xbmc/addons/kodi-dev-kit/src/addon_runner/third_party/backward-cpp/conanfile.py create mode 100644 xbmc/addons/kodi-dev-kit/src/addon_runner/third_party/backward-cpp/doc/CMakeLists.txt create mode 100644 xbmc/addons/kodi-dev-kit/src/addon_runner/third_party/backward-cpp/doc/nice.png create mode 100644 xbmc/addons/kodi-dev-kit/src/addon_runner/third_party/backward-cpp/doc/pretty.png create mode 100644 xbmc/addons/kodi-dev-kit/src/addon_runner/third_party/backward-cpp/doc/rude.png create mode 100644 xbmc/addons/kodi-dev-kit/src/addon_runner/third_party/backward-cpp/test/CMakeLists.txt create mode 100644 xbmc/addons/kodi-dev-kit/src/addon_runner/third_party/backward-cpp/test/_test_main.cpp create mode 100644 xbmc/addons/kodi-dev-kit/src/addon_runner/third_party/backward-cpp/test/rectrace.cpp create mode 100644 xbmc/addons/kodi-dev-kit/src/addon_runner/third_party/backward-cpp/test/select_signals.cpp create mode 100644 xbmc/addons/kodi-dev-kit/src/addon_runner/third_party/backward-cpp/test/stacktrace.cpp create mode 100644 xbmc/addons/kodi-dev-kit/src/addon_runner/third_party/backward-cpp/test/suicide.cpp create mode 100644 xbmc/addons/kodi-dev-kit/src/addon_runner/third_party/backward-cpp/test/test.cpp create mode 100644 xbmc/addons/kodi-dev-kit/src/addon_runner/third_party/backward-cpp/test/test.hpp create mode 100644 xbmc/addons/kodi-dev-kit/src/addon_runner/third_party/backward-cpp/test_package/CMakeLists.txt create mode 100644 xbmc/addons/kodi-dev-kit/src/addon_runner/third_party/backward-cpp/test_package/conanfile.py create mode 100644 xbmc/addons/kodi-dev-kit/src/addon_runner/third_party/backward-cpp/test_package/main.cpp create mode 100644 xbmc/messaging/helpers/DialogCrashReporter.cpp create mode 100644 xbmc/messaging/helpers/DialogCrashReporter.h diff --git a/xbmc/addons/interface/Controller.cpp b/xbmc/addons/interface/Controller.cpp index 06cfa3fa5dcc3..14f9894e574ab 100644 --- a/xbmc/addons/interface/Controller.cpp +++ b/xbmc/addons/interface/Controller.cpp @@ -237,14 +237,16 @@ void CController::Process() const ChildStatus status = it->second->ProcessActive(); if (status != ChildStatus::Running) { - const auto addon = it->second->GetAddon(); - const auto uuid = it->second->GetUUID(); + const auto addonId = it->second->GetAddonID(); - fprintf(stderr, "--> Process Stopped %s - %i !!!\n", addon->ID().c_str(), status); CLog::Log(LOGFATAL, "CController::{}: Addon '{}' process was uncontrolled stopped!", - __func__, addon->ID()); + __func__, addonId); it->second->InformStopReport(status); it->second->Kill(); + + const auto uuid = it->second->GetUUID(); + const auto stacktrace = it->second->GetStackTrace(); + m_runningProcesses.erase(it++); /* @@ -252,7 +254,10 @@ void CController::Process() * and not start a GUI in this case! */ if (!m_onSystemStop) - CGUIDialogCrashReporter::ReportCrash(addon, uuid); + { + // Show Dialog with a non blocking call and to have this thread further available + MESSAGING::HELPERS::ShowReportCrashDialog(std::move(addonId), std::move(uuid), std::move(stacktrace)); + } continue; } diff --git a/xbmc/addons/interface/InstanceHandler.cpp b/xbmc/addons/interface/InstanceHandler.cpp index c1123056c73ff..013c8dd1de393 100644 --- a/xbmc/addons/interface/InstanceHandler.cpp +++ b/xbmc/addons/interface/InstanceHandler.cpp @@ -39,8 +39,6 @@ IInstanceHandler::IInstanceHandler(const KODI_ADDON_INSTANCE_BACKEND_HDL hdl, m_info.type = m_type; m_info.kodi = hdl; m_info.parent = m_parentInstance; - - // m_info.first_instance = m_addon && !m_addon->Initialized(); } IInstanceHandler::~IInstanceHandler() diff --git a/xbmc/addons/interface/InstanceHandler.h b/xbmc/addons/interface/InstanceHandler.h index 4a2f9d88dbb6b..a5ed0316a6d89 100644 --- a/xbmc/addons/interface/InstanceHandler.h +++ b/xbmc/addons/interface/InstanceHandler.h @@ -55,6 +55,7 @@ class IInstanceHandler const std::shared_ptr& Addon() const { return m_addon; } const std::shared_ptr& GetAddonInfo() const { return m_addonInfo; } const std::shared_ptr& GetProcess() const { return m_process; } + const KODI_ADDON_INSTANCE_INFO* GetCInfo() const { return &m_info; } virtual void StopReport(ChildStatus status) {} virtual void OnPreInstall() {} diff --git a/xbmc/addons/interface/RunningProcess.cpp b/xbmc/addons/interface/RunningProcess.cpp index e1920bf4351d7..689f6f3ff5ecd 100644 --- a/xbmc/addons/interface/RunningProcess.cpp +++ b/xbmc/addons/interface/RunningProcess.cpp @@ -385,11 +385,11 @@ void CRunningProcess::AddAddonInstance(IInstanceHandler* handler) return; } - if (m_activeAddonHandlers.empty()) - m_interface.kodi_addon_base_h->kodi_addon_create_v1(&m_hdl); - std::unique_lock lock(m_mutex); + if (m_activeAddonHandlers.empty()) + m_interface.kodi_addon_base_h->kodi_addon_create_v1(handler->GetCInfo(), &m_hdl); + // add the instance handler to the info to know used amount on addon m_activeAddonHandlers.insert(handler); } @@ -441,6 +441,11 @@ size_t CRunningProcess::UsedInstanceCount() const return m_activeAddonHandlers.size(); } +const std::string& CRunningProcess::GetStackTrace() const +{ + return m_launcher ? m_launcher->GetStackTrace() : StringUtils::Empty; +} + void CRunningProcess::SetEndtime(unsigned int milliseconds) { /* diff --git a/xbmc/addons/interface/RunningProcess.h b/xbmc/addons/interface/RunningProcess.h index b889f003a111b..98eebfa4b2902 100644 --- a/xbmc/addons/interface/RunningProcess.h +++ b/xbmc/addons/interface/RunningProcess.h @@ -65,6 +65,7 @@ class CRunningProcess : private CThread const std::shared_ptr& GetAddon() const { return m_addon; } std::string GetAddonID() const { return m_addon->ID(); } size_t UsedInstanceCount() const; + const std::string& GetStackTrace() const; CInterface& GetIfc() { return m_interface; } diff --git a/xbmc/addons/interface/api/addon_base.cpp b/xbmc/addons/interface/api/addon_base.cpp index 4847b1d0fb77f..944fdec760444 100644 --- a/xbmc/addons/interface/api/addon_base.cpp +++ b/xbmc/addons/interface/api/addon_base.cpp @@ -540,10 +540,10 @@ bool CHdl_kodi_addon_base_h::HandleMessage(int funcGroup, // Function calls from Kodi to addon enum ADDON_STATUS CHdl_kodi_addon_base_h::kodi_addon_create_v1( - const KODI_ADDON_INSTANCE_BACKEND_HDL first_instance, KODI_ADDON_HDL* hdl) + const struct KODI_ADDON_INSTANCE_INFO* first_instance, KODI_ADDON_HDL* hdl) { - // Original API call: typedef enum ADDON_STATUS(ATTR_APIENTRYP PFN_KODI_ADDON_CREATE_V1)(const KODI_ADDON_INSTANCE_BACKEND_HDL first_instance, KODI_ADDON_HDL* hdl); - // Tuple in: typedef std::tuple msgChild__IN_kodi_addon_create_v1; /* Autogenerated */ + // Original API call: typedef enum ADDON_STATUS(ATTR_APIENTRYP PFN_KODI_ADDON_CREATE_V1)(const struct KODI_ADDON_INSTANCE_INFO* first_instance, KODI_ADDON_HDL* hdl); + // Tuple in: typedef std::tuple msgChild__IN_kodi_addon_create_v1; /* Autogenerated */ // Tuple out: typedef std::tuple msgChild_OUT_kodi_addon_create_v1; /* Autogenerated */ #ifndef KODI_INHIBIT_SHARED @@ -551,8 +551,8 @@ enum ADDON_STATUS CHdl_kodi_addon_base_h::kodi_addon_create_v1( { msgpack::sbuffer in; msgpack::sbuffer out; - msgpack::pack(in, msgChild__IN_kodi_addon_create_v1( - PtrValue(first_instance) /* CheckAPIUse_WAY_12 (0005) */)); + msgpack::pack( + in, msgChild__IN_kodi_addon_create_v1(first_instance /* CheckAPIUse_WAY_16 (0005) */)); if (!m_process->SendMessage(funcGroup_addon_base_h, funcChild_kodi_addon_create_v1, in, out)) return ADDON_STATUS_OK; msgpack::unpacked ident = msgpack::unpack(out.data(), out.size()); diff --git a/xbmc/addons/interface/api/addon_base.h b/xbmc/addons/interface/api/addon_base.h index e9910525cd3a0..fc3764c105589 100644 --- a/xbmc/addons/interface/api/addon_base.h +++ b/xbmc/addons/interface/api/addon_base.h @@ -56,7 +56,7 @@ class CHdl_kodi_addon_base_h : public IMsgHdl // Function calls from Kodi to addon - enum ADDON_STATUS kodi_addon_create_v1(const KODI_ADDON_INSTANCE_BACKEND_HDL first_instance, + enum ADDON_STATUS kodi_addon_create_v1(const struct KODI_ADDON_INSTANCE_INFO* first_instance, KODI_ADDON_HDL* hdl); // Added with API 1 void kodi_addon_destroy_v1(KODI_ADDON_HDL hdl); // Added with API 1 diff --git a/xbmc/addons/interface/gui/GUIDialogCrashReporter.cpp b/xbmc/addons/interface/gui/GUIDialogCrashReporter.cpp index 895b170bd762a..b0970f97a5225 100644 --- a/xbmc/addons/interface/gui/GUIDialogCrashReporter.cpp +++ b/xbmc/addons/interface/gui/GUIDialogCrashReporter.cpp @@ -104,11 +104,22 @@ CGUIDialogCrashReporter::RESULT CGUIDialogCrashReporter::GetResult() const return RESULT::IgnoreCrash; } -void CGUIDialogCrashReporter::ReportCrash(const std::shared_ptr& addon, - const std::string& uuid) +void CGUIDialogCrashReporter::ReportCrash(const KODI::MESSAGING::HELPERS::DialogCrashReportMessage* options) +{ + ReportCrash(options->addon, options->uuid, options->stacktrace); + delete options; +} + +void CGUIDialogCrashReporter::ReportCrash(const std::string& addon, + const std::string& uuid, + const std::string& stacktrace) { using namespace ADDON; + AddonInfoPtr addonInfo = CServiceBroker::GetAddonMgr().GetAddonInfo(addon); + if (!addonInfo) + return; + const auto gui = CServiceBroker::GetGUI(); if (!gui) return; @@ -118,7 +129,7 @@ void CGUIDialogCrashReporter::ReportCrash(const std::shared_ptr& if (!dialog) return; - const std::string text = StringUtils::Format(g_localizeStrings.Get(2296), addon->Name()); + const std::string text = StringUtils::Format(g_localizeStrings.Get(2296), addonInfo->Name()); dialog->SetHeading(2295); dialog->SetText(text); dialog->m_canceled = false; @@ -135,11 +146,11 @@ void CGUIDialogCrashReporter::ReportCrash(const std::shared_ptr& case RESULT::IgnoreCrash: break; case RESULT::DisableAddonAndReport: - SendCrashReport(gui, addon, uuid); + SendCrashReport(gui, addonInfo, uuid, stacktrace); // fallthru is intended [[fallthrough]]; case RESULT::DisableAddon: - CServiceBroker::GetAddonMgr().DisableAddon(addon->ID(), + CServiceBroker::GetAddonMgr().DisableAddon(addonInfo->ID(), AddonDisabledReason::PERMANENT_FAILURE); break; @@ -149,8 +160,9 @@ void CGUIDialogCrashReporter::ReportCrash(const std::shared_ptr& } void CGUIDialogCrashReporter::SendCrashReport(CGUIComponent* const gui, - const std::shared_ptr& addon, - const std::string& uuid) + const ADDON::AddonInfoPtr& addonInfo, + const std::string& uuid, + const std::string& stacktrace) { using namespace ADDON; using namespace XFILE; @@ -163,7 +175,7 @@ void CGUIDialogCrashReporter::SendCrashReport(CGUIComponent* const gui, dialog->SetHeading(g_localizeStrings.Get(2294)); dialog->UseMonoFont(true); - if (addon->Type(addon->Type())->Language() == AddonLanguage::Java) + if (addonInfo->Type(addonInfo->MainType())->Language() == AddonLanguage::Java) { const std::string path = @@ -179,6 +191,8 @@ void CGUIDialogCrashReporter::SendCrashReport(CGUIComponent* const gui, } } } + else if (!stacktrace.empty()) + dialog->SetText(stacktrace); else { dialog->SetText("NEED TODO"); diff --git a/xbmc/addons/interface/gui/GUIDialogCrashReporter.h b/xbmc/addons/interface/gui/GUIDialogCrashReporter.h index 3f42a4fcba1dc..2c4ea83e457cf 100644 --- a/xbmc/addons/interface/gui/GUIDialogCrashReporter.h +++ b/xbmc/addons/interface/gui/GUIDialogCrashReporter.h @@ -10,6 +10,7 @@ #include "addons/IAddon.h" #include "dialogs/GUIDialogBoxBase.h" +#include "messaging/helpers/DialogCrashReporter.h" namespace KODI { @@ -28,7 +29,17 @@ class CGUIDialogCrashReporter : public CGUIDialogBoxBase bool OnAction(const CAction& action) override; bool OnBack(int actionID) override; - static void ReportCrash(const std::shared_ptr& addon, const std::string& uuid); + static void ReportCrash(const std::string& addon, const std::string& uuid, const std::string& stacktrace); + + /*! + \brief Open a add-on crash report dialog and wait for input + + \param[in] options a struct of type DialogCrashReportMessage containing + the options to set for this dialog. + + \sa KODI::MESSAGING::HELPERS::DialogCrashReportMessage + */ + void ReportCrash(const KODI::MESSAGING::HELPERS::DialogCrashReportMessage* options); private: void OnInitWindow() override; @@ -42,8 +53,9 @@ class CGUIDialogCrashReporter : public CGUIDialogBoxBase }; RESULT GetResult() const; static void SendCrashReport(CGUIComponent* const gui, - const std::shared_ptr& addon, - const std::string& uuid); + const ADDON::AddonInfoPtr& addonInfo, + const std::string& uuid, + const std::string& stacktrace); bool m_canceled; bool m_disableAddon; diff --git a/xbmc/addons/interface/launcher/ILauncher.h b/xbmc/addons/interface/launcher/ILauncher.h index 7326ce9624135..586ed97fec00c 100644 --- a/xbmc/addons/interface/launcher/ILauncher.h +++ b/xbmc/addons/interface/launcher/ILauncher.h @@ -54,6 +54,7 @@ class ILauncher int GetExitCode() const { return m_exitCode; } ChildStatus GetLastChildStatus() const { return m_lastStatus; } + const std::string& GetStackTrace() const { return m_stacktrace; } protected: const std::shared_ptr m_addonInfo; @@ -61,6 +62,7 @@ class ILauncher int m_exitCode = 0; std::atomic m_lastStatus{ChildStatus::NotStarted}; + std::string m_stacktrace; }; } /* namespace INTERFACE */ diff --git a/xbmc/addons/interface/launcher/LauncherPosix.cpp b/xbmc/addons/interface/launcher/LauncherPosix.cpp index 0e75c28a6d168..745f692f4d8b8 100644 --- a/xbmc/addons/interface/launcher/LauncherPosix.cpp +++ b/xbmc/addons/interface/launcher/LauncherPosix.cpp @@ -77,6 +77,17 @@ bool CLauncherPosix::Launch(const std::vector& argv, bool waitForExit) sigfillset(&full_sigset); const sigset_t orig_sigmask = SetSignalMask(full_sigset); + if (pipe(m_childToParent) < 0) + { + CLog::Log(LOGERROR, "CChildLauncherPosix::{}: Failed to create pipe for child process", __func__); + return false; + } + + int writeFD = m_childToParent[WRITE_FD]; + int readFD = m_childToParent[READ_FD]; + + fprintf(stderr, "--ddd-----------%s\n", __PRETTY_FUNCTION__); + const auto before_fork = high_resolution_clock::now(); pid_t pid = fork(); @@ -104,16 +115,31 @@ bool CLauncherPosix::Launch(const std::vector& argv, bool waitForExit) #if defined(TARGET_LINUX) if (prctl(PR_SET_PDEATHSIG, SIGKILL) != 0) { - fprintf(stderr, "prctl(PR_SET_PDEATHSIG) failed"); + fprintf(stderr, "prctl(PR_SET_PDEATHSIG) failed\n"); _exit(127); } #endif + fprintf(stderr, "-------------%s\n", __PRETTY_FUNCTION__); + + // crash_reporter writes output to stderr. Connect it to the status pipe fd. + if (dup2(writeFD, STDERR_FILENO) == -1) + { + fprintf(stderr, "Failed to init stderr handling\n"); + _exit(127); + } + + close(readFD); + + fprintf(stdout, "-------------%s\n", __PRETTY_FUNCTION__); + execvp(argv[0], argv.data()); _exit(0); } + close(writeFD); + CLog::Log( LOGINFO, "CChildLauncherPosix::{}: Started child process for webbrowser addon (pid {}) in wait {}", @@ -127,7 +153,6 @@ bool CLauncherPosix::Launch(const std::vector& argv, bool waitForExit) if (ret <= 0) { ProcessStatus(status); - m_pid = pid; } } @@ -136,8 +161,9 @@ bool CLauncherPosix::Launch(const std::vector& argv, bool waitForExit) bool CLauncherPosix::Kill(bool wait) { + pid_t pid = m_pid; fprintf(stderr, "KILL\n"); - if (m_pid) + if (pid > 0) { if (m_lastStatus == ChildStatus::Running) { @@ -148,15 +174,15 @@ bool CLauncherPosix::Kill(bool wait) if (ProcessActive() != ChildStatus::Running) { CLog::Log(LOGDEBUG, "CChildLauncherPosix::{}: Child correctly stopped itself (pid {})", - __func__, m_pid); + __func__, pid); return true; } } CLog::Log(LOGDEBUG, "CChildLauncherPosix::{}: Forcing stop of child (pid {})", __func__, - m_pid); + pid); - bool did_terminate = kill(m_pid, SIGTERM) == 0; + bool did_terminate = kill(pid, SIGTERM) == 0; if (wait && did_terminate) { @@ -165,15 +191,15 @@ bool CLauncherPosix::Kill(bool wait) while (ret_pid <= 0 && cnt-- > 0) { std::this_thread::sleep_for(std::chrono::milliseconds(50)); - ret_pid = HANDLE_EINTR(waitpid(m_pid, nullptr, WNOHANG)); + ret_pid = HANDLE_EINTR(waitpid(pid, nullptr, WNOHANG)); } if (ret_pid > 0) return true; - did_terminate = kill(m_pid, SIGKILL) == 0; + did_terminate = kill(pid, SIGKILL) == 0; if (did_terminate) { - ret_pid = HANDLE_EINTR(waitpid(m_pid, nullptr, 0)); + ret_pid = HANDLE_EINTR(waitpid(pid, nullptr, 0)); if (ret_pid > 0) return true; } @@ -182,10 +208,16 @@ bool CLauncherPosix::Kill(bool wait) if (!did_terminate) { CLog::Log(LOGERROR, "CChildLauncherPosix::{}: Unable to terminate process {}", __func__, - m_pid); + pid); return false; } } + + if (m_childToParent[READ_FD] > 0) + { + close(m_childToParent[READ_FD]); + m_childToParent[READ_FD] = -1; + } } return true; @@ -193,23 +225,60 @@ bool CLauncherPosix::Kill(bool wait) ChildStatus CLauncherPosix::ProcessActive() { - int status = 0; - pid_t ret = waitpid(m_pid, &status, WNOHANG | WUNTRACED | WCONTINUED); - if (ret == 0) - { - m_lastStatus = ChildStatus::Running; - } - else if (ret == -1) + pid_t pid = m_pid; + if (pid > 0) { - CLog::Log(LOGERROR, "CChildLauncherPosix::{}: Asked sandbox process pid {} no more present", - __func__, m_pid); - if (m_lastStatus == ChildStatus::Running) - m_lastStatus = ChildStatus::StoppedByUnknown; - return m_lastStatus; - } - else - { - ProcessStatus(status); + int status = 0; + pid_t ret = waitpid(pid, &status, WNOHANG | WUNTRACED | WCONTINUED); + if (ret == 0) + { + m_lastStatus = ChildStatus::Running; + } + else if (ret == -1) + { +// FILE* file = fdopen(m_childToParent[READ_FD], "r"); +// if (file) +// { +// constexpr int BUFFER_SIZE = 1024; +// char buffer[BUFFER_SIZE + 1] = {0}; +// std::string text; +// while (fread(buffer, 1, BUFFER_SIZE, file) != 0) +// { +// text += buffer; +// memset(buffer, 0, BUFFER_SIZE); +// } +// close(m_childToParent[READ_FD]); +// m_childToParent[READ_FD] = -1; +// fclose(file); +// +// fprintf(stderr, "--------->>>>>>>>>>>>>> %s\n", text.c_str()); +// } + + if (m_childToParent[READ_FD] > 0) + { + constexpr int BUFFER_SIZE = 1024; + char buffer[BUFFER_SIZE + 1] = {0}; + m_stacktrace.clear(); + while (read(m_childToParent[READ_FD], buffer, BUFFER_SIZE) > 0) + { + m_stacktrace += buffer; + memset(buffer, 0, BUFFER_SIZE); + } + fprintf(stderr, "--------->>>>>>>>>>>>>> %s\n", m_stacktrace.c_str()); + close(m_childToParent[READ_FD]); + m_childToParent[READ_FD] = -1; + } + + CLog::Log(LOGERROR, "CChildLauncherPosix::{}: Asked sandbox process pid {} no more present", + __func__, pid); + if (m_lastStatus == ChildStatus::Running) + m_lastStatus = ChildStatus::StoppedByUnknown; + return m_lastStatus; + } + else + { + ProcessStatus(status); + } } return m_lastStatus; diff --git a/xbmc/addons/interface/launcher/LauncherPosix.h b/xbmc/addons/interface/launcher/LauncherPosix.h index f931fad01e023..ddb4a8aebf603 100644 --- a/xbmc/addons/interface/launcher/LauncherPosix.h +++ b/xbmc/addons/interface/launcher/LauncherPosix.h @@ -12,6 +12,8 @@ #include "ILauncher.h" +#include + #define HANDLE_EINTR(x) \ ({ \ decltype(x) eintr_wrapper_result; \ @@ -48,9 +50,16 @@ class CLauncherPosix : public ILauncher ChildStatus ProcessActive() override; private: + enum PIPE_FILE_DESCRIPTERS + { + READ_FD = 0, + WRITE_FD = 1 + }; + void ProcessStatus(int status); - pid_t m_pid = -1; + std::atomic m_pid{-1}; + int m_childToParent[2]{-1}; }; } /* namespace INTERFACE */ diff --git a/xbmc/addons/kodi-dev-kit/cmake/Macros.cmake b/xbmc/addons/kodi-dev-kit/cmake/Macros.cmake index d7c40344dbb3c..03cb85673c2ad 100644 --- a/xbmc/addons/kodi-dev-kit/cmake/Macros.cmake +++ b/xbmc/addons/kodi-dev-kit/cmake/Macros.cmake @@ -8,3 +8,18 @@ macro(devkit_add_object name) POSITION_INDEPENDENT_CODE ON) set(DEVKIT_OBJECTS $ ${DEVKIT_OBJECTS} PARENT_SCOPE) endmacro() + +# +# Removes the specified compile flag from the specified target. +# _target - The target to remove the compile flag from +# _flag - The compile flag to remove +# +# Pre: apply_global_cxx_flags_to_all_targets() must be invoked. +# +macro(remove_flag_from_target _target _flag) + get_target_property(_target_cxx_flags ${_target} COMPILE_OPTIONS) + if(_target_cxx_flags) + list(REMOVE_ITEM _target_cxx_flags ${_flag}) + set_target_properties(${_target} PROPERTIES COMPILE_OPTIONS "${_target_cxx_flags}") + endif() +endmacro() diff --git a/xbmc/addons/kodi-dev-kit/include/kodi/AddonBase.h b/xbmc/addons/kodi-dev-kit/include/kodi/AddonBase.h index f249017306ccf..62b3e5f3d4545 100644 --- a/xbmc/addons/kodi-dev-kit/include/kodi/AddonBase.h +++ b/xbmc/addons/kodi-dev-kit/include/kodi/AddonBase.h @@ -286,16 +286,6 @@ class ATTR_DLL_LOCAL CSettingValue class ATTR_DLL_LOCAL IInstanceInfo { public: - explicit IInstanceInfo(const KODI_ADDON_INSTANCE_INFO* instance) - : m_type(instance->type), - m_instance_id(instance->instance_id), - m_unique_work_id(instance->unique_work_id), - m_kodi(instance->kodi), - m_parent(instance->parent), - m_first_instance(instance->first_instance) - { - } - /// @defgroup cpp_kodi_addon_addonbase_Defs_IInstanceInfo_Help Value Help /// @ingroup cpp_kodi_addon_addonbase_Defs_IInstanceInfo /// @@ -330,9 +320,6 @@ class ATTR_DLL_LOCAL IInstanceInfo KODI_ADDON_INSTANCE_BACKEND_HDL GetKodiHdl() const { return m_kodi; } KODI_ADDON_INSTANCE_HDL GetParentHdl() const { return m_parent; } - /// @brief Check this is first created instance by Kodi. - bool FirstInstance() const { return m_first_instance; } - ///@} private: @@ -341,6 +328,10 @@ class ATTR_DLL_LOCAL IInstanceInfo friend class kodi::dl; IInstanceInfo() = default; + explicit IInstanceInfo(const KODI_ADDON_INSTANCE_INFO* instance) + { + *this = instance; + } const IInstanceInfo& operator=(const KODI_ADDON_INSTANCE_INFO* right) { @@ -351,7 +342,6 @@ class ATTR_DLL_LOCAL IInstanceInfo m_unique_work_id = right->unique_work_id; m_kodi = right->kodi; m_parent = right->parent; - m_first_instance = right->first_instance; return *this; } @@ -361,7 +351,6 @@ class ATTR_DLL_LOCAL IInstanceInfo std::string m_unique_work_id; KODI_ADDON_INSTANCE_BACKEND_HDL m_kodi{nullptr}; KODI_ADDON_INSTANCE_HDL m_parent{nullptr}; - bool m_first_instance{false}; }; ///@} //------------------------------------------------------------------------------ @@ -432,6 +421,7 @@ class ATTR_DLL_LOCAL dl KODI_DLL_HDL dll; kodi::addon::IAddonInstance* globalSingleInstance; + kodi::addon::IInstanceInfo firstKodiInstance; kodi::addon::CAddonBase* addonBase; } static local; }; @@ -453,18 +443,10 @@ class ATTR_DLL_LOCAL IAddonInstance { public: IAddonInstance() + : m_instanceInfo(kodi::dl::local.firstKodiInstance), + m_kodi(kodi::dl::local.firstKodiInstance.GetKodiHdl()) { - KODI_ADDON_INSTANCE_INFO info{}; - if (kodi::dl::api.kodi_get_initial_instance_info(&info)) - { - m_instanceInfo = &info; - m_kodi = info.kodi; - kodi::dl::local.globalSingleInstance = this; - } - else - { - throw std::logic_error("kodi::addon::IAddonInstance: Called without available single instance!"); - } + kodi::dl::local.globalSingleInstance = this; } explicit IAddonInstance(const kodi::addon::IInstanceInfo& instance) @@ -865,15 +847,13 @@ class ATTR_DLL_LOCAL CAddonBase } private: - static ADDON_STATUS ADDONBASE_create(KODI_ADDON_HDL* hdl); + static ADDON_STATUS ADDONBASE_create(const struct KODI_ADDON_INSTANCE_INFO* first_instance, KODI_ADDON_HDL* hdl); static inline void ADDONBASE_destroy(const KODI_ADDON_HDL hdl) { - fprintf(stderr, "-----1----> %s\n", __PRETTY_FUNCTION__); delete static_cast(hdl); kodi::dl::local.globalSingleInstance = nullptr; kodi::dl::local.addonBase = nullptr; - fprintf(stderr, "-----2----> %s\n", __PRETTY_FUNCTION__); } static inline ADDON_STATUS ADDONBASE_create_instance(const KODI_ADDON_HDL hdl, @@ -902,7 +882,6 @@ class ATTR_DLL_LOCAL CAddonBase kodi::dl::local.globalSingleInstance->GetInfo().GetType() == info->type && kodi::dl::local.globalSingleInstance->GetInfo().GetKodiHdl() == info->kodi) { - fprintf(stderr, "-----1----> %s\n", __PRETTY_FUNCTION__); /* The handling here is intended for the case of the add-on only one * instance and this is integrated in the add-on base class. */ @@ -913,7 +892,6 @@ class ATTR_DLL_LOCAL CAddonBase } else { - fprintf(stderr, "----2-----> %s\n", __PRETTY_FUNCTION__); /* Here it should use the CreateInstance instance function to allow * creation of several on one addon. */ @@ -930,7 +908,6 @@ class ATTR_DLL_LOCAL CAddonBase { if (kodi::dl::local.globalSingleInstance != nullptr) { - fprintf(stderr, "---3------> %s\n", __PRETTY_FUNCTION__); kodi::dl::api.kodi_log(ADDON_LOG_FATAL, "kodi::addon::CAddonBase Creation of multiple together with " "single instance way is not allowed!"); @@ -979,11 +956,9 @@ class ATTR_DLL_LOCAL CAddonBase const KODI_ADDON_INSTANCE_HDL instance_hdl) { CAddonBase* base = static_cast(hdl); - fprintf(stderr, "---1------> %s\n", __PRETTY_FUNCTION__); if (kodi::dl::local.globalSingleInstance == nullptr && instance_hdl != base) { - fprintf(stderr, "---2------> %s\n", __PRETTY_FUNCTION__); IInstanceInfo instanceInfo(info); base->DestroyInstance(instanceInfo, instance_hdl); delete static_cast(instance_hdl); @@ -1915,9 +1890,10 @@ inline void ATTR_DLL_LOCAL Deinit() kodi::dl::LOCAL kodi::dl::local{}; #define ADDONCREATOR(AddonClass) \ KODI_STARTER \ - ADDON_STATUS kodi::addon::CAddonBase::ADDONBASE_create(KODI_ADDON_HDL* hdl) \ + ADDON_STATUS kodi::addon::CAddonBase::ADDONBASE_create(const struct KODI_ADDON_INSTANCE_INFO* first_instance, KODI_ADDON_HDL* hdl) \ { \ using namespace ::kodi::addon; \ + kodi::dl::local.firstKodiInstance = first_instance; \ kodi::dl::local.addonBase = new AddonClass; \ *hdl = kodi::dl::local.addonBase; \ return kodi::dl::local.addonBase->Create(); \ diff --git a/xbmc/addons/kodi-dev-kit/include/kodi/c-api/addon_base.h b/xbmc/addons/kodi-dev-kit/include/kodi/c-api/addon_base.h index 1f97aa66b433b..ad383961a12b4 100644 --- a/xbmc/addons/kodi-dev-kit/include/kodi/c-api/addon_base.h +++ b/xbmc/addons/kodi-dev-kit/include/kodi/c-api/addon_base.h @@ -196,7 +196,6 @@ extern "C" const char* unique_work_id; KODI_ADDON_INSTANCE_BACKEND_HDL kodi; KODI_ADDON_INSTANCE_HDL parent; - bool first_instance; }; /*---AUTO_GEN_PARSE---*/ @@ -221,7 +220,7 @@ extern "C" }; }; - typedef enum ADDON_STATUS(ATTR_APIENTRYP PFN_KODI_ADDON_CREATE_V1)(const KODI_ADDON_INSTANCE_BACKEND_HDL first_instance, KODI_ADDON_HDL* hdl); + typedef enum ADDON_STATUS(ATTR_APIENTRYP PFN_KODI_ADDON_CREATE_V1)(const struct KODI_ADDON_INSTANCE_INFO* first_instance, KODI_ADDON_HDL* hdl); typedef void(ATTR_APIENTRYP PFN_KODI_ADDON_DESTROY_V1)(KODI_ADDON_HDL hdl); typedef enum ADDON_STATUS(ATTR_APIENTRYP PFN_KODI_ADDON_SETTING_CHANGE_STRING_V1)( diff --git a/xbmc/addons/kodi-dev-kit/src/addon/CMakeLists.txt b/xbmc/addons/kodi-dev-kit/src/addon/CMakeLists.txt index 0e593d9990931..df050db6a3a53 100644 --- a/xbmc/addons/kodi-dev-kit/src/addon/CMakeLists.txt +++ b/xbmc/addons/kodi-dev-kit/src/addon/CMakeLists.txt @@ -17,14 +17,14 @@ message(STATUS "kodidevkit Version: ${__KDK_MAJOR__}.${__KDK_MINOR__}.${__KDK_BE if(APP_RENDER_SYSTEM STREQUAL "gl" OR NOT APP_RENDER_SYSTEM) find_package(OpenGl REQUIRED) find_package(EGL REQUIRED) - set(DEPLIBS ${OPENGL_LIBRARIES} ${EGL_LIBRARIES}) - set(INCLUDES ${OPENGL_INCLUDE_DIR} ${EGL_INCLUDE_DIR}) - add_definitions(${OPENGL_DEFINITIONS}) + set(${PROJECT_NAME}_DEPLIBS ${OPENGL_LIBRARIES} ${EGL_LIBRARIES}) + set(${PROJECT_NAME}_INCLUDES ${OPENGL_INCLUDE_DIR} ${EGL_INCLUDE_DIR}) + set(${PROJECT_NAME}_DEFINITIONS ${OPENGL_DEFINITIONS}) else() find_package(OpenGLES REQUIRED) - set(DEPLIBS ${OPENGLES_LIBRARIES}) - set(INCLUDES ${OPENGLES_INCLUDE_DIR}) - add_definitions(${OPENGLES_DEFINITIONS}) + set(${PROJECT_NAME}_DEPLIBS ${OPENGLES_LIBRARIES}) + set(${PROJECT_NAME}_INCLUDES ${OPENGLES_INCLUDE_DIR}) + set(${PROJECT_NAME}_DEFINITIONS ${OPENGLES_DEFINITIONS}) endif() #---AUTO_GEN_PARSE--- @@ -35,6 +35,7 @@ add_subdirectory(api/gui) add_subdirectory(api/gui/controls) add_subdirectory(api/gui/dialogs) add_subdirectory(core) +add_subdirectory(third_party/backward-cpp) #---AUTO_GEN_PARSE--- cmake_parse_arguments(arg "WRAPPED" "DEVKIT_OUTPUT_DIRECTORY" "" ${ARGN}) @@ -52,7 +53,12 @@ endif() set(DEVKIT_LIBRARY ${OUTPUT_NAME}${CMAKE_SHARED_LIBRARY_SUFFIX} CACHE STRING "Library used for dev-kit") -add_library(${PROJECT_NAME} SHARED ${DEV_KIT_HEADER} ${DEVKIT_OBJECTS} $) +add_library(${PROJECT_NAME} SHARED ${DEV_KIT_HEADER} + ${DEVKIT_OBJECTS} + $ + $) +add_backward(${PROJECT_NAME}) +target_compile_definitions(${PROJECT_NAME} PRIVATE ${BACKWARD_DEFINITIONS} ${${PROJECT_NAME}_DEFINITIONS}) set_target_properties(${PROJECT_NAME} PROPERTIES C_VISIBILITY_PRESET hidden CXX_VISIBILITY_PRESET hidden VISIBILITY_INLINES_HIDDEN ON @@ -60,7 +66,7 @@ set_target_properties(${PROJECT_NAME} PROPERTIES C_VISIBILITY_PRESET hidden LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/${DEVKIT_OUTPUT_DIRECTORY} RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/${DEVKIT_OUTPUT_DIRECTORY} OUTPUT_NAME ${OUTPUT_NAME} PREFIX "") -target_include_directories(${PROJECT_NAME} PRIVATE ${INCLUDES}) +target_include_directories(${PROJECT_NAME} PRIVATE ${${PROJECT_NAME}_INCLUDES}) foreach(OUTPUTCONFIG ${CMAKE_CONFIGURATION_TYPES}) string(TOUPPER ${OUTPUTCONFIG} OUTPUTCONFIG) set_target_properties(${name} PROPERTIES LIBRARY_OUTPUT_DIRECTORY_${OUTPUTCONFIG} ${CMAKE_BINARY_DIR}/${DEVKIT_OUTPUT_DIRECTORY} @@ -75,8 +81,12 @@ elseif(CORE_SYSTEM_NAME STREQUAL darwin_embedded) elseif(CORE_SYSTEM_NAME STREQUAL freebsd) message(FATAL_ERROR "OS currently not supported and in TODO!!!") elseif(CORE_SYSTEM_NAME STREQUAL linux) - target_link_libraries(${PROJECT_NAME} PRIVATE -static-libgcc -static-libstdc++ m c rt pthread ${DEPLIBS}) + target_link_libraries(${PROJECT_NAME} PRIVATE -static-libgcc -static-libstdc++ m c rt pthread ${${PROJECT_NAME}_DEPLIBS} dw) #target_link_libraries(${PROJECT_NAME} PUBLIC stdc++ gcc pthread m c rt ) + + # Remove as splitted dwarf not works currently in included "backward-cpp" and further should on + # user installed add-ons some debug infos available. + remove_flag_from_target(${PROJECT_NAME} -gsplit-dwarf) elseif(CORE_SYSTEM_NAME STREQUAL osx) message(FATAL_ERROR "OS currently not supported and in TODO!!!") elseif(CORE_SYSTEM_NAME STREQUAL windows) diff --git a/xbmc/addons/kodi-dev-kit/src/addon/api/addon_base.cpp b/xbmc/addons/kodi-dev-kit/src/addon/api/addon_base.cpp index 26ad9eb94d66c..291b1deb2d784 100644 --- a/xbmc/addons/kodi-dev-kit/src/addon/api/addon_base.cpp +++ b/xbmc/addons/kodi-dev-kit/src/addon/api/addon_base.cpp @@ -70,17 +70,16 @@ bool CHdl_kodi_addon_base_h::HandleMessage(int funcGroup, { case funcChild_kodi_addon_create_v1: { - // Original API call: typedef enum ADDON_STATUS(ATTR_APIENTRYP PFN_KODI_ADDON_CREATE_V1)(const KODI_ADDON_INSTANCE_BACKEND_HDL first_instance, KODI_ADDON_HDL* hdl); - // Tuple in: typedef std::tuple msgChild__IN_kodi_addon_create_v1; /* Autogenerated */ + // Original API call: typedef enum ADDON_STATUS(ATTR_APIENTRYP PFN_KODI_ADDON_CREATE_V1)(const struct KODI_ADDON_INSTANCE_INFO* first_instance, KODI_ADDON_HDL* hdl); + // Tuple in: typedef std::tuple msgChild__IN_kodi_addon_create_v1; /* Autogenerated */ // Tuple out: typedef std::tuple msgChild_OUT_kodi_addon_create_v1; /* Autogenerated */ msgChild__IN_kodi_addon_create_v1 t = in.get().as(); - const KODI_ADDON_INSTANCE_BACKEND_HDL first_instance = - reinterpret_cast( - std::get<0>(t)); /* CheckAPIUse(90)::PLACE_13_3(2) */ + KODI_ADDON_INSTANCE_INFO first_instance; /* CheckAPIUse_WAY_16 (0004) */ + std::get<0>(t).SetCStructure(&first_instance); /* CheckAPIUse_WAY_16 (0004) */ KODI_ADDON_HDL hdl = nullptr; /* CheckAPIUse(90)::PLACE_12(1) */ enum ADDON_STATUS auto_gen_ret = - kodi_addon_create_v1(this, first_instance /* CheckAPIUse(90)::PLACE_13_3(1) */, + kodi_addon_create_v1(this, &first_instance /* CheckAPIUse_WAY_16 (0004) */, &hdl /* CheckAPIUse(90)::PLACE_12(2) */); msgpack::pack( @@ -370,20 +369,27 @@ KODI::ADDONS::INTERFACE::DirectAddonIfc* kodi_check_direct_api(int argc, char* a enum ADDON_STATUS CHdl_kodi_addon_base_h::kodi_addon_create_v1( void* thisClassHdl, - const KODI_ADDON_INSTANCE_BACKEND_HDL first_instance, + const struct KODI_ADDON_INSTANCE_INFO* first_instance, KODI_ADDON_HDL* hdl) // Added with API 1 { auto thisClass = reinterpret_cast(thisClassHdl); if (thisClass == nullptr) return ADDON_STATUS_OK; - UnionAddonFunctions* union_data = reinterpret_cast(first_instance); - enum ADDON_STATUS auto_gen_ret = ADDON_STATUS_OK; - if (union_data->types.kodi_addon.v1.create) + /*---AUTO_GEN_PARSE---*/ + + ADDON_STATUS status; + if (thisClass->m_ifc->functions.types.kodi_addon.v1.create) { - auto_gen_ret = union_data->types.kodi_addon.v1.create(first_instance, hdl); + status = thisClass->m_ifc->functions.types.kodi_addon.v1.create(first_instance, hdl); + thisClass->m_ifc->functions.hdl = *hdl; } - return auto_gen_ret; + else + status = ADDON_STATUS_NOT_IMPLEMENTED; + *hdl = &thisClass->m_ifc->functions; + return status; + + /*---AUTO_GEN_PARSE---*/ } void CHdl_kodi_addon_base_h::kodi_addon_destroy_v1(void* thisClassHdl, diff --git a/xbmc/addons/kodi-dev-kit/src/addon/api/addon_base.h b/xbmc/addons/kodi-dev-kit/src/addon/api/addon_base.h index d5123d898f8b1..3453a6e372c22 100644 --- a/xbmc/addons/kodi-dev-kit/src/addon/api/addon_base.h +++ b/xbmc/addons/kodi-dev-kit/src/addon/api/addon_base.h @@ -57,7 +57,7 @@ class ATTR_DLL_LOCAL CHdl_kodi_addon_base_h : public IMsgHdl // Calls from Kodi to addon static enum ADDON_STATUS kodi_addon_create_v1( void* thisClassHdl, - const KODI_ADDON_INSTANCE_BACKEND_HDL first_instance, + const struct KODI_ADDON_INSTANCE_INFO* first_instance, KODI_ADDON_HDL* hdl); // Added with API 1 static void kodi_addon_destroy_v1(void* thisClassHdl, KODI_ADDON_HDL hdl); // Added with API 1 diff --git a/xbmc/addons/kodi-dev-kit/src/addon/core/addon_control.cpp b/xbmc/addons/kodi-dev-kit/src/addon/core/addon_control.cpp index eabdbf0127bcb..af93215d2345c 100644 --- a/xbmc/addons/kodi-dev-kit/src/addon/core/addon_control.cpp +++ b/xbmc/addons/kodi-dev-kit/src/addon/core/addon_control.cpp @@ -124,6 +124,8 @@ CAddonControl::~CAddonControl() bool CAddonControl::Create() { + m_stackTrace.load_here(32); + if (!m_mainThread->Create()) return false; diff --git a/xbmc/addons/kodi-dev-kit/src/addon/core/addon_control.h b/xbmc/addons/kodi-dev-kit/src/addon/core/addon_control.h index 3a8e6318c830f..d05f35f0d5ab8 100644 --- a/xbmc/addons/kodi-dev-kit/src/addon/core/addon_control.h +++ b/xbmc/addons/kodi-dev-kit/src/addon/core/addon_control.h @@ -11,6 +11,8 @@ #include "../../../include/kodi/c-api/addon_base.h" #include "../../shared/DirectData.h" #include "../../shared/Instances.h" +#define BACKWARD_HAS_DW 1 +#include "../third_party/backward-cpp/backward.hpp" #include "IMsgHdl.h" #include "addon_thread_control.h" @@ -84,6 +86,9 @@ class ATTR_DLL_LOCAL CAddonControl std::mutex m_mutex; static void kodi_kill_child(void* thisClassHdl, const char* uuid); + + backward::StackTrace m_stackTrace; + backward::SignalHandling m_signalHandling; }; } /* namespace INTERNAL */ diff --git a/xbmc/addons/kodi-dev-kit/src/addon/third_party/CMakeLists.txt b/xbmc/addons/kodi-dev-kit/src/addon/third_party/CMakeLists.txt new file mode 100644 index 0000000000000..9f61e09641281 --- /dev/null +++ b/xbmc/addons/kodi-dev-kit/src/addon/third_party/CMakeLists.txt @@ -0,0 +1,12 @@ +# Auto generated CMakeLists.txt. +# See xbmc/addons/kodi-dev-kit/tools/code-generator.py. + +set(SOURCES +) + +set(HEADERS +) + +if(SOURCES OR HEADERS) + devkit_add_object(devkit_third_party) +endif() diff --git a/xbmc/addons/kodi-dev-kit/src/addon/third_party/backward-cpp/BackwardConfig.cmake b/xbmc/addons/kodi-dev-kit/src/addon/third_party/backward-cpp/BackwardConfig.cmake new file mode 100644 index 0000000000000..867dab9c7f80c --- /dev/null +++ b/xbmc/addons/kodi-dev-kit/src/addon/third_party/backward-cpp/BackwardConfig.cmake @@ -0,0 +1,266 @@ +# +# BackwardMacros.cmake +# Copyright 2013 Google Inc. All Rights Reserved. +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. + +############################################################################### +# OPTIONS +############################################################################### + +set(STACK_WALKING_UNWIND TRUE) +set(STACK_WALKING_UNWIND TRUE CACHE BOOL + "Use compiler's unwind API") +set(STACK_WALKING_BACKTRACE FALSE CACHE BOOL + "Use backtrace from (e)glibc for stack walking") +set(STACK_WALKING_LIBUNWIND FALSE CACHE BOOL + "Use libunwind for stack walking") + +set(STACK_DETAILS_AUTO_DETECT TRUE CACHE BOOL + "Auto detect backward's stack details dependencies") + +set(STACK_DETAILS_BACKTRACE_SYMBOL FALSE CACHE BOOL + "Use backtrace from (e)glibc for symbols resolution") +set(STACK_DETAILS_DW FALSE CACHE BOOL + "Use libdw to read debug info") +set(STACK_DETAILS_BFD FALSE CACHE BOOL + "Use libbfd to read debug info") +set(STACK_DETAILS_DWARF FALSE CACHE BOOL + "Use libdwarf/libelf to read debug info") + +if(CMAKE_SOURCE_DIR STREQUAL CMAKE_CURRENT_SOURCE_DIR AND NOT DEFINED BACKWARD_TESTS) + # If this is a top level CMake project, we most lixely want the tests + set(BACKWARD_TESTS ON CACHE BOOL "Enable tests") +else() + set(BACKWARD_TESTS OFF CACHE BOOL "Enable tests") +endif() +############################################################################### +# CONFIGS +############################################################################### +include(FindPackageHandleStandardArgs) + +if (STACK_WALKING_LIBUNWIND) + # libunwind works on the macOS without having to add special include + # paths or libraries + if (NOT APPLE) + find_path(LIBUNWIND_INCLUDE_DIR NAMES "libunwind.h") + find_library(LIBUNWIND_LIBRARY unwind) + + if (LIBUNWIND_LIBRARY) + include(CheckSymbolExists) + check_symbol_exists(UNW_INIT_SIGNAL_FRAME libunwind.h HAVE_UNW_INIT_SIGNAL_FRAME) + if (NOT HAVE_UNW_INIT_SIGNAL_FRAME) + message(STATUS "libunwind does not support unwinding from signal handler frames") + endif() + endif() + + set(LIBUNWIND_INCLUDE_DIRS ${LIBUNWIND_INCLUDE_DIR}) + set(LIBDWARF_LIBRARIES ${LIBUNWIND_LIBRARY}) + find_package_handle_standard_args(libunwind DEFAULT_MSG + LIBUNWIND_LIBRARY LIBUNWIND_INCLUDE_DIR) + mark_as_advanced(LIBUNWIND_INCLUDE_DIR LIBUNWIND_LIBRARY) + list(APPEND _BACKWARD_LIBRARIES ${LIBUNWIND_LIBRARY}) + endif() + + # Disable other unwinders if libunwind is found + set(STACK_WALKING_UNWIND FALSE) + set(STACK_WALKING_BACKTRACE FALSE) +endif() + +if (${STACK_DETAILS_AUTO_DETECT}) + if(NOT CMAKE_VERSION VERSION_LESS 3.17) + set(_name_mismatched_arg NAME_MISMATCHED) + endif() + # find libdw + find_path(LIBDW_INCLUDE_DIR NAMES "elfutils/libdw.h" "elfutils/libdwfl.h") + find_library(LIBDW_LIBRARY dw) + # in case it's statically linked, look for all the possible dependencies + find_library(LIBELF_LIBRARY elf) + find_library(LIBPTHREAD_LIBRARY pthread) + find_library(LIBZ_LIBRARY z) + find_library(LIBBZ2_LIBRARY bz2) + find_library(LIBLZMA_LIBRARY lzma) + find_library(LIBZSTD_LIBRARY zstd) + set(LIBDW_INCLUDE_DIRS ${LIBDW_INCLUDE_DIR} ) + set(LIBDW_LIBRARIES ${LIBDW_LIBRARY} + $<$:${LIBELF_LIBRARY}> + $<$:${LIBPTHREAD_LIBRARY}> + $<$:${LIBZ_LIBRARY}> + $<$:${LIBBZ2_LIBRARY}> + $<$:${LIBLZMA_LIBRARY}> + $<$:${LIBZSTD_LIBRARY}>) + find_package_handle_standard_args(libdw ${_name_mismatched_arg} + REQUIRED_VARS LIBDW_LIBRARY LIBDW_INCLUDE_DIR) + mark_as_advanced(LIBDW_INCLUDE_DIR LIBDW_LIBRARY) + + # find libbfd + find_path(LIBBFD_INCLUDE_DIR NAMES "bfd.h") + find_path(LIBDL_INCLUDE_DIR NAMES "dlfcn.h") + find_library(LIBBFD_LIBRARY bfd) + find_library(LIBDL_LIBRARY dl) + set(LIBBFD_INCLUDE_DIRS ${LIBBFD_INCLUDE_DIR} ${LIBDL_INCLUDE_DIR}) + set(LIBBFD_LIBRARIES ${LIBBFD_LIBRARY} ${LIBDL_LIBRARY}) + find_package_handle_standard_args(libbfd ${_name_mismatched_arg} + REQUIRED_VARS LIBBFD_LIBRARY LIBBFD_INCLUDE_DIR + LIBDL_LIBRARY LIBDL_INCLUDE_DIR) + mark_as_advanced(LIBBFD_INCLUDE_DIR LIBBFD_LIBRARY + LIBDL_INCLUDE_DIR LIBDL_LIBRARY) + + # find libdwarf + find_path(LIBDWARF_INCLUDE_DIR NAMES "libdwarf.h" PATH_SUFFIXES libdwarf) + find_path(LIBELF_INCLUDE_DIR NAMES "libelf.h") + find_path(LIBDL_INCLUDE_DIR NAMES "dlfcn.h") + find_library(LIBDWARF_LIBRARY dwarf) + find_library(LIBELF_LIBRARY elf) + find_library(LIBDL_LIBRARY dl) + set(LIBDWARF_INCLUDE_DIRS ${LIBDWARF_INCLUDE_DIR} ${LIBELF_INCLUDE_DIR} ${LIBDL_INCLUDE_DIR}) + set(LIBDWARF_LIBRARIES ${LIBDWARF_LIBRARY} ${LIBELF_LIBRARY} ${LIBDL_LIBRARY}) + find_package_handle_standard_args(libdwarf ${_name_mismatched_arg} + REQUIRED_VARS LIBDWARF_LIBRARY LIBDWARF_INCLUDE_DIR + LIBELF_LIBRARY LIBELF_INCLUDE_DIR + LIBDL_LIBRARY LIBDL_INCLUDE_DIR) + mark_as_advanced(LIBDWARF_INCLUDE_DIR LIBDWARF_LIBRARY + LIBELF_INCLUDE_DIR LIBELF_LIBRARY + LIBDL_INCLUDE_DIR LIBDL_LIBRARY) + + if (LIBDW_FOUND) + LIST(APPEND _BACKWARD_INCLUDE_DIRS ${LIBDW_INCLUDE_DIRS}) + LIST(APPEND _BACKWARD_LIBRARIES ${LIBDW_LIBRARIES}) + set(STACK_DETAILS_DW TRUE) + set(STACK_DETAILS_BFD FALSE) + set(STACK_DETAILS_DWARF FALSE) + set(STACK_DETAILS_BACKTRACE_SYMBOL FALSE) + elseif(LIBBFD_FOUND) + LIST(APPEND _BACKWARD_INCLUDE_DIRS ${LIBBFD_INCLUDE_DIRS}) + LIST(APPEND _BACKWARD_LIBRARIES ${LIBBFD_LIBRARIES}) + + # If we attempt to link against static bfd, make sure to link its dependencies, too + get_filename_component(bfd_lib_ext "${LIBBFD_LIBRARY}" EXT) + if (bfd_lib_ext STREQUAL "${CMAKE_STATIC_LIBRARY_SUFFIX}") + list(APPEND _BACKWARD_LIBRARIES iberty z) + endif() + + set(STACK_DETAILS_DW FALSE) + set(STACK_DETAILS_BFD TRUE) + set(STACK_DETAILS_DWARF FALSE) + set(STACK_DETAILS_BACKTRACE_SYMBOL FALSE) + elseif(LIBDWARF_FOUND) + LIST(APPEND _BACKWARD_INCLUDE_DIRS ${LIBDWARF_INCLUDE_DIRS}) + LIST(APPEND _BACKWARD_LIBRARIES ${LIBDWARF_LIBRARIES}) + + set(STACK_DETAILS_DW FALSE) + set(STACK_DETAILS_BFD FALSE) + set(STACK_DETAILS_DWARF TRUE) + set(STACK_DETAILS_BACKTRACE_SYMBOL FALSE) + else() + set(STACK_DETAILS_DW FALSE) + set(STACK_DETAILS_BFD FALSE) + set(STACK_DETAILS_DWARF FALSE) + set(STACK_DETAILS_BACKTRACE_SYMBOL TRUE) + endif() +else() + if (STACK_DETAILS_DW) + LIST(APPEND _BACKWARD_LIBRARIES dw) + endif() + + if (STACK_DETAILS_BFD) + LIST(APPEND _BACKWARD_LIBRARIES bfd dl) + endif() + + if (STACK_DETAILS_DWARF) + LIST(APPEND _BACKWARD_LIBRARIES dwarf elf) + endif() +endif() + +macro(map_definitions var_prefix define_prefix) + foreach(def ${ARGN}) + if (${${var_prefix}${def}}) + LIST(APPEND _BACKWARD_DEFINITIONS "${define_prefix}${def}=1") + else() + LIST(APPEND _BACKWARD_DEFINITIONS "${define_prefix}${def}=0") + endif() + endforeach() +endmacro() + +if (NOT _BACKWARD_DEFINITIONS) + map_definitions("STACK_WALKING_" "BACKWARD_HAS_" UNWIND LIBUNWIND BACKTRACE) + map_definitions("STACK_DETAILS_" "BACKWARD_HAS_" BACKTRACE_SYMBOL DW BFD DWARF) +endif() + +if(WIN32) + list(APPEND _BACKWARD_LIBRARIES dbghelp psapi) + if(MINGW) + set(MINGW_MSVCR_LIBRARY "msvcr90$<$:d>" CACHE STRING "Mingw MSVC runtime import library") + list(APPEND _BACKWARD_LIBRARIES ${MINGW_MSVCR_LIBRARY}) + endif() +endif() + +set(BACKWARD_INCLUDE_DIR "${CMAKE_CURRENT_LIST_DIR}") + +set(BACKWARD_HAS_EXTERNAL_LIBRARIES FALSE) +set(FIND_PACKAGE_REQUIRED_VARS BACKWARD_INCLUDE_DIR) +if(DEFINED _BACKWARD_LIBRARIES) + set(BACKWARD_HAS_EXTERNAL_LIBRARIES TRUE) + list(APPEND FIND_PACKAGE_REQUIRED_VARS _BACKWARD_LIBRARIES) +endif() + +include(FindPackageHandleStandardArgs) +find_package_handle_standard_args(Backward + REQUIRED_VARS ${FIND_PACKAGE_REQUIRED_VARS} +) +list(APPEND _BACKWARD_INCLUDE_DIRS ${BACKWARD_INCLUDE_DIR}) + +# add_backward, optional bool argument; if passed and true, backward will be included as a system header +macro(add_backward target) + if ("${ARGN}") + target_include_directories(${target} SYSTEM PRIVATE ${BACKWARD_INCLUDE_DIRS}) + else() + target_include_directories(${target} PRIVATE ${BACKWARD_INCLUDE_DIRS}) + endif() + set_property(TARGET ${target} APPEND PROPERTY COMPILE_DEFINITIONS ${BACKWARD_DEFINITIONS}) + set_property(TARGET ${target} APPEND PROPERTY LINK_LIBRARIES ${BACKWARD_LIBRARIES}) +endmacro() + +set(BACKWARD_INCLUDE_DIRS ${_BACKWARD_INCLUDE_DIRS} CACHE INTERNAL "_BACKWARD_INCLUDE_DIRS") +set(BACKWARD_DEFINITIONS ${_BACKWARD_DEFINITIONS} CACHE INTERNAL "BACKWARD_DEFINITIONS") +set(BACKWARD_LIBRARIES ${_BACKWARD_LIBRARIES} CACHE INTERNAL "BACKWARD_LIBRARIES") +mark_as_advanced(BACKWARD_INCLUDE_DIRS BACKWARD_DEFINITIONS BACKWARD_LIBRARIES) + +# Expand each definition in BACKWARD_DEFINITIONS to its own cmake var and export +# to outer scope +foreach(var ${BACKWARD_DEFINITIONS}) + string(REPLACE "=" ";" var_as_list ${var}) + list(GET var_as_list 0 var_name) + list(GET var_as_list 1 var_value) + set(${var_name} ${var_value}) + mark_as_advanced(${var_name}) +endforeach() + +if (NOT TARGET Backward::Backward) + add_library(Backward::Backward INTERFACE IMPORTED) + set_target_properties(Backward::Backward PROPERTIES + INTERFACE_INCLUDE_DIRECTORIES "${BACKWARD_INCLUDE_DIRS}" + INTERFACE_COMPILE_DEFINITIONS "${BACKWARD_DEFINITIONS}" + ) + if(BACKWARD_HAS_EXTERNAL_LIBRARIES) + set_target_properties(Backward::Backward PROPERTIES + INTERFACE_LINK_LIBRARIES "${BACKWARD_LIBRARIES}" + ) + endif() +endif() diff --git a/xbmc/addons/kodi-dev-kit/src/addon/third_party/backward-cpp/CMakeLists.txt b/xbmc/addons/kodi-dev-kit/src/addon/third_party/backward-cpp/CMakeLists.txt new file mode 100644 index 0000000000000..84dd2af077016 --- /dev/null +++ b/xbmc/addons/kodi-dev-kit/src/addon/third_party/backward-cpp/CMakeLists.txt @@ -0,0 +1,140 @@ +# +# CMakeLists.txt +# Copyright 2013 Google Inc. All Rights Reserved. +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. + +cmake_minimum_required(VERSION 3.0) +project(backward CXX) + +# Introduce variables: +# * CMAKE_INSTALL_LIBDIR +# * CMAKE_INSTALL_BINDIR +# * CMAKE_INSTALL_INCLUDEDIR +include(GNUInstallDirs) + +include(BackwardConfig.cmake) + +# check if compiler is nvcc or nvcc_wrapper +set(COMPILER_IS_NVCC false) +get_filename_component(COMPILER_NAME ${CMAKE_CXX_COMPILER} NAME) +if (COMPILER_NAME MATCHES "^nvcc") + set(COMPILER_IS_NVCC true) +endif() + +if (DEFINED ENV{OMPI_CXX} OR DEFINED ENV{MPICH_CXX}) + if ( ($ENV{OMPI_CXX} MATCHES "nvcc") OR ($ENV{MPICH_CXX} MATCHES "nvcc") ) + set(COMPILER_IS_NVCC true) + endif() +endif() + +# set CXX standard +set(CMAKE_CXX_STANDARD_REQUIRED True) +set(CMAKE_CXX_STANDARD 17) # Kodi Note: Changed from 11 to 17 +if (${COMPILER_IS_NVCC}) + # GNU CXX extensions are not supported by nvcc + set(CMAKE_CXX_EXTENSIONS OFF) +endif() + +############################################################################### +# COMPILER FLAGS +############################################################################### + +if (CMAKE_CXX_COMPILER_ID STREQUAL "Clang" OR CMAKE_COMPILER_IS_GNUCXX) + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wextra") + if (NOT ${COMPILER_IS_NVCC}) + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -pedantic-errors") + endif() + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -g") +endif() + +############################################################################### +# BACKWARD OBJECT +############################################################################### + +add_library(backward_object OBJECT backward.cpp) +target_compile_definitions(backward_object PRIVATE ${BACKWARD_DEFINITIONS}) +target_include_directories(backward_object PRIVATE ${BACKWARD_INCLUDE_DIRS}) +set_target_properties(backward_object PROPERTIES POSITION_INDEPENDENT_CODE ON) +set(BACKWARD_ENABLE $ CACHE STRING + "Link with this object to setup backward automatically") + + +############################################################################### +# BACKWARD LIBRARY (Includes backward.cpp) +############################################################################### +option(BACKWARD_SHARED "Build dynamic backward-cpp shared lib" OFF) + +if(BACKWARD_SHARED) + set(libtype SHARED) +endif() +add_library(backward ${libtype} backward.cpp) +target_compile_definitions(backward PUBLIC ${BACKWARD_DEFINITIONS}) +target_include_directories(backward PUBLIC ${BACKWARD_INCLUDE_DIRS}) + +############################################################################### +# TESTS +############################################################################### + +if(BACKWARD_TESTS) + enable_testing() + + add_library(test_main OBJECT test/_test_main.cpp) + + macro(backward_add_test src) + get_filename_component(name ${src} NAME_WE) + set(test_name "test_${name}") + + add_executable(${test_name} ${src} ${ARGN} $) + + target_link_libraries(${test_name} PRIVATE Backward::Backward) + + add_test(NAME ${name} COMMAND ${test_name}) + endmacro() + + # Tests without backward.cpp + set(TESTS + test + stacktrace + rectrace + select_signals + ) + + foreach(test ${TESTS}) + backward_add_test(test/${test}.cpp) + endforeach() + + # Tests with backward.cpp + set(TESTS + suicide + ) + + foreach(test ${TESTS}) + backward_add_test(test/${test}.cpp ${BACKWARD_ENABLE}) + endforeach() +endif() + +install( + FILES "backward.hpp" + DESTINATION ${CMAKE_INSTALL_INCLUDEDIR} +) +install( + FILES "BackwardConfig.cmake" + DESTINATION ${CMAKE_INSTALL_LIBDIR}/backward +) diff --git a/xbmc/addons/kodi-dev-kit/src/addon/third_party/backward-cpp/LICENSE.txt b/xbmc/addons/kodi-dev-kit/src/addon/third_party/backward-cpp/LICENSE.txt new file mode 100644 index 0000000000000..269e8abbc0ffc --- /dev/null +++ b/xbmc/addons/kodi-dev-kit/src/addon/third_party/backward-cpp/LICENSE.txt @@ -0,0 +1,21 @@ +Copyright 2013 Google Inc. All Rights Reserved. + +The MIT License (MIT) + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/xbmc/addons/kodi-dev-kit/src/addon/third_party/backward-cpp/README.md b/xbmc/addons/kodi-dev-kit/src/addon/third_party/backward-cpp/README.md new file mode 100644 index 0000000000000..031716f3da9fe --- /dev/null +++ b/xbmc/addons/kodi-dev-kit/src/addon/third_party/backward-cpp/README.md @@ -0,0 +1,442 @@ +Backward-cpp [![badge](https://img.shields.io/badge/conan.io-backward%2F1.3.0-green.svg?logo=data:image/png;base64%2CiVBORw0KGgoAAAANSUhEUgAAAA4AAAAOCAMAAAAolt3jAAAA1VBMVEUAAABhlctjlstkl8tlmMtlmMxlmcxmmcxnmsxpnMxpnM1qnc1sn85voM91oM11oc1xotB2oc56pNF6pNJ2ptJ8ptJ8ptN9ptN8p9N5qNJ9p9N9p9R8qtOBqdSAqtOAqtR%2BrNSCrNJ/rdWDrNWCsNWCsNaJs9eLs9iRvNuVvdyVv9yXwd2Zwt6axN6dxt%2Bfx%2BChyeGiyuGjyuCjyuGly%2BGlzOKmzOGozuKoz%2BKqz%2BOq0OOv1OWw1OWw1eWx1eWy1uay1%2Baz1%2Baz1%2Bez2Oe02Oe12ee22ujUGwH3AAAAAXRSTlMAQObYZgAAAAFiS0dEAIgFHUgAAAAJcEhZcwAACxMAAAsTAQCanBgAAAAHdElNRQfgBQkREyOxFIh/AAAAiklEQVQI12NgAAMbOwY4sLZ2NtQ1coVKWNvoc/Eq8XDr2wB5Ig62ekza9vaOqpK2TpoMzOxaFtwqZua2Bm4makIM7OzMAjoaCqYuxooSUqJALjs7o4yVpbowvzSUy87KqSwmxQfnsrPISyFzWeWAXCkpMaBVIC4bmCsOdgiUKwh3JojLgAQ4ZCE0AMm2D29tZwe6AAAAAElFTkSuQmCC)](http://www.conan.io/source/backward/1.3.0/Manu343726/testing) +============ + +Backward is a beautiful stack trace pretty printer for C++. + +If you are bored to see this: + +![default trace](doc/rude.png) + +Backward will spice it up for you: + +![pretty stackstrace](doc/pretty.png) + +There is not much to say. Of course it will be able to display the code +snippets only if the source files are accessible (else see trace #4 in the +example). + +All "Source" lines and code snippet prefixed by a pipe "|" are frames inline +the next frame. +You can see that for the trace #1 in the example, the function +`you_shall_not_pass()` was inlined in the function `...read2::do_test()` by the +compiler. + +## Installation + +#### Install backward.hpp + +Backward is a header only library. So installing Backward is easy, simply drop +a copy of `backward.hpp` along with your other source files in your C++ project. +You can also use a git submodule or really any other way that best fits your +environment, as long as you can include `backward.hpp`. + +#### Install backward.cpp + +If you want Backward to automatically print a stack trace on most common fatal +errors (segfault, abort, un-handled exception...), simply add a copy of +`backward.cpp` to your project, and don't forget to tell your build system. + +The code in `backward.cpp` is trivial anyway, you can simply copy what it's +doing at your convenience. + +Note for [folly](https://github.com/facebook/folly) library users: must define `backward::SignalHandling sh;` after `folly::init(&argc, &argv);`. + +## Configuration & Dependencies + +### Integration with CMake + +If you are using CMake and want to use its configuration abilities to save +you the trouble, you can easily integrate Backward, depending on how you obtained +the library. + +#### As a subdirectory: + +In this case you have a subdirectory containing the whole repository of Backward +(eg.: using git-submodules), in this case you can do: + +``` +add_subdirectory(/path/to/backward-cpp) + +# This will add backward.cpp to your target +add_executable(mytarget mysource.cpp ${BACKWARD_ENABLE}) + +# This will add libraries, definitions and include directories needed by backward +# by setting each property on the target. +add_backward(mytarget) +``` + +#### Modifying CMAKE_MODULE_PATH + +In this case you can have Backward installed as a subdirectory: + +``` +list(APPEND CMAKE_MODULE_PATH /path/to/backward-cpp) +find_package(Backward) + +# This will add libraries, definitions and include directories needed by backward +# through an IMPORTED target. +target_link_libraries(mytarget PUBLIC Backward::Backward) +``` + +Notice that this is equivalent to using the the approach that uses `add_subdirectory()`, +however it uses cmake's [imported target](https://cmake.org/Wiki/CMake/Tutorials/Exporting_and_Importing_Targets) mechanism. + +#### Installation through a regular package manager + +In this case you have obtained Backward through a package manager. + +Packages currently available: +- [conda-forge](https://anaconda.org/conda-forge/backward-cpp) + +``` +find_package(Backward) + +# This will add libraries, definitions and include directories needed by backward +# through an IMPORTED target. +target_link_libraries(mytarget PUBLIC Backward::Backward) +``` +### Libraries to unwind the stack + +On Linux and macOS, backtrace can back-trace or "walk" the stack using the +following libraries: + +#### unwind + +Unwind comes from libgcc, but there is an equivalent inside clang itself. With +unwind, the stacktrace is as accurate as it can possibly be, since this is +used by the C++ runtine in gcc/clang for stack unwinding on exception. + +Normally libgcc is already linked to your program by default. + +#### libunwind from the [libunwind project](https://github.com/libunwind/libunwind) + + apt-get install binutils-dev (or equivalent) + +Libunwind provides, in some cases, a more accurate stacktrace as it knows +to decode signal handler frames and lets us edit the context registers when +unwinding, allowing stack traces over bad function references. + +For best results make sure you are using libunwind 1.3 or later, which added +`unw_init_local2` and support for handling signal frames. + +CMake will warn you when configuring if your libunwind version doesn't support +signal frames. + +On macOS clang provides a libunwind API compatible library as part of its +environment, so no third party libraries are necessary. + +### Compile with debug info + +You need to compile your project with generation of debug symbols enabled, +usually `-g` with clang++ and g++. + +Note that you can use `-g` with any level of optimization, with modern debug +information encoding like DWARF, it only takes space in the binary (it's not +loaded in memory until your debugger or Backward makes use of it, don't worry), +and it doesn't impact the code generation (at least on GNU/Linux x86\_64 for +what I know). + +If you are missing debug information, the stack trace will lack details about +your sources. + +### Libraries to read the debug info + +Backward supports pretty printed stack traces on GNU/Linux, macOS and Windows, +it will compile fine under other platforms but will not do anything. **Pull +requests are welcome :)** + +Also, by default you will get a really basic stack trace, based on the +`backtrace_symbols` API: + +![default trace](doc/nice.png) + +You will need to install some dependencies to get the ultimate stack trace. +Three libraries are currently supported, the only difference is which one is the +easiest for you to install, so pick your poison: + +#### libbfd from the [GNU/binutils](http://www.gnu.org/software/binutils/) + + apt-get install binutils-dev (or equivalent) + +And do not forget to link with the lib: `g++/clang++ -lbfd -ldl ...` + +This library requires dynamic loading. Which is provided by the library `dl`. +Hence why we also link with `-ldl`. + +Then define the following before every inclusion of `backward.hpp` (don't +forget to update `backward.cpp` as well): + + #define BACKWARD_HAS_BFD 1 + +#### libdw from the [elfutils](https://fedorahosted.org/elfutils/) + + apt-get install libdw-dev (or equivalent) + +And do not forget to link with the lib and inform Backward to use it: + + #define BACKWARD_HAS_DW 1 + +Of course you can simply add the define (`-DBACKWARD_HAS_...=1`) and the +linkage details in your build system and even auto-detect which library is +installed, it's up to you. + +#### [libdwarf](https://sourceforge.net/projects/libdwarf/) and [libelf](http://www.mr511.de/software/english.html) + + apt-get install libdwarf-dev (or equivalent) + +And do not forget to link with the lib and inform Backward to use it: + + #define BACKWARD_HAS_DWARF 1 + +There are several alternative implementations of libdwarf and libelf that +are API compatible so it's possible, although it hasn't been tested, to +replace the ones used when developing backward (in bold, below): + +* **_libelf_** by [Michael "Tired" Riepe](http://www.mr511.de/software/english.html) +* **_libdwarf_** by [David Anderson](https://www.prevanders.net/dwarf.html) +* libelf from [elfutils](https://fedorahosted.org/elfutils/) +* libelf and libdwarf from FreeBSD's [ELF Tool Chain](https://sourceforge.net/p/elftoolchain/wiki/Home/) project + + +Of course you can simply add the define (`-DBACKWARD_HAS_...=1`) and the +linkage details in your build system and even auto-detect which library is +installed, it's up to you. + +That's it, you are all set, you should be getting nice stack traces like the +one at the beginning of this document. + +## API + +If you don't want to limit yourself to the defaults offered by `backward.cpp`, +and you want to take some random stack traces for whatever reason and pretty +print them the way you love or you decide to send them all to your buddies over +the Internet, you will appreciate the simplicity of Backward's API. + +### Stacktrace + +The StackTrace class lets you take a "snapshot" of the current stack. +You can use it like this: + +```c++ +using namespace backward; +StackTrace st; st.load_here(32); +Printer p; p.print(st); +``` + +The public methods are: + +```c++ +class StackTrace { public: + // Take a snapshot of the current stack, with at most "trace_cnt_max" + // traces in it. The first trace is the most recent (ie the current + // frame). You can also provide a trace address to load_from() assuming + // the address is a valid stack frame (useful for signal handling traces). + // Both function return size(). + size_t load_here(size_t trace_cnt_max) + size_t load_from(void* address, size_t trace_cnt_max) + + // The number of traces loaded. This can be less than "trace_cnt_max". + size_t size() const + + // A unique id for the thread in which the trace was taken. The value + // 0 means the stack trace comes from the main thread. + size_t thread_id() const + + // Retrieve a trace by index. 0 is the most recent trace, size()-1 is + // the oldest one. + Trace operator[](size_t trace_idx) +}; +``` + +### TraceResolver + +The `TraceResolver` does the heavy lifting, and intends to transform a simple +`Trace` from its address into a fully detailed `ResolvedTrace` with the +filename of the source, line numbers, inlined functions and so on. + +You can use it like this: + +```c++ +using namespace backward; +StackTrace st; st.load_here(32); + +TraceResolver tr; tr.load_stacktrace(st); +for (size_t i = 0; i < st.size(); ++i) { + ResolvedTrace trace = tr.resolve(st[i]); + std::cout << "#" << i + << " " << trace.object_filename + << " " << trace.object_function + << " [" << trace.addr << "]" + << std::endl; +} +``` + +The public methods are: + +```c++ +class TraceResolver { public: + // Pre-load whatever is necessary from the stack trace. + template + void load_stacktrace(ST&) + + // Resolve a trace. It takes a ResolvedTrace, because a `Trace` is + // implicitly convertible to it. + ResolvedTrace resolve(ResolvedTrace t) +}; +``` + +### SnippetFactory + +The SnippetFactory is a simple helper class to automatically load and cache +source files in order to extract code snippets. + +```c++ +class SnippetFactory { public: + // A snippet is a list of line numbers and line contents. + typedef std::vector > lines_t; + + // Return a snippet starting at line_start with up to context_size lines. + lines_t get_snippet(const std::string& filename, + size_t line_start, size_t context_size) + + // Return a combined snippet from two different locations and combine them. + // context_size / 2 lines will be extracted from each location. + lines_t get_combined_snippet( + const std::string& filename_a, size_t line_a, + const std::string& filename_b, size_t line_b, + size_t context_size) + + // Tries to return a unified snippet if the two locations from the same + // file are close enough to fit inside one context_size, else returns + // the equivalent of get_combined_snippet(). + lines_t get_coalesced_snippet(const std::string& filename, + size_t line_a, size_t line_b, size_t context_size) +``` + +### Printer + +A simpler way to pretty print a stack trace to the terminal. It will +automatically resolve the traces for you: + +```c++ +using namespace backward; +StackTrace st; st.load_here(32); +Printer p; +p.object = true; +p.color_mode = ColorMode::always; +p.address = true; +p.print(st, stderr); +``` + +You can set a few options: + +```c++ +class Printer { public: + // Print a little snippet of code if possible. + bool snippet = true; + + // Colorize the trace + // - ColorMode::automatic: Activate colors if possible. For example, when using a TTY on linux. + // - ColorMode::always: Always use colors. + // - ColorMode::never: Never use colors. + bool color_mode = ColorMode::automatic; + + // Add the addresses of every source location to the trace. + bool address = false; + + // Even if there is a source location, also prints the object + // from where the trace came from. + bool object = false; + + // Resolve and print a stack trace to the given C FILE* object. + // On linux, if the FILE* object is attached to a TTY, + // color will be used if color_mode is set to automatic. + template + FILE* print(StackTrace& st, FILE* fp = stderr); + + // Resolve and print a stack trace to the given std::ostream object. + // Color will only be used if color_mode is set to always. + template + std::ostream& print(ST& st, std::ostream& os); +``` + + +### SignalHandling + +A simple helper class that registers for you the most common signals and other +callbacks to segfault, hardware exception, un-handled exception etc. + +`backward.cpp` simply uses it like that: + +```c++ +backward::SignalHandling sh; +``` + +Creating the object registers all the different signals and hooks. Destroying +this object doesn't do anything. It exposes only one method: + +```c++ +bool loaded() const // true if loaded with success +``` + +### Trace object + +To keep the memory footprint of a loaded `StackTrace` on the low-side, there a +hierarchy of trace object, from a minimal `Trace `to a `ResolvedTrace`. + +#### Simple trace + +```c++ +struct Trace { + void* addr; // address of the trace + size_t idx; // its index (0 == most recent) +}; +``` + +#### Resolved trace + +A `ResolvedTrace` should contains a maximum of details about the location of +the trace in the source code. Note that not all fields might be set. + +```c++ +struct ResolvedTrace: public Trace { + + struct SourceLoc { + std::string function; + std::string filename; + size_t line; + size_t col; + }; + + // In which binary object this trace is located. + std::string object_filename; + + // The function in the object that contains the trace. This is not the same + // as source.function which can be an function inlined in object_function. + std::string object_function; + + // The source location of this trace. It is possible for filename to be + // empty and for line/col to be invalid (value 0) if this information + // couldn't be deduced, for example if there is no debug information in the + // binary object. + SourceLoc source; + + // An optional list of "inliners". All of these sources locations where + // inlined in the source location of the trace (the attribute right above). + // This is especially useful when you compile with optimizations turned on. + typedef std::vector source_locs_t; + source_locs_t inliners; +}; +``` + +## Contact and copyright + +François-Xavier Bourlet + +Copyright 2013-2017 Google Inc. All Rights Reserved. +MIT License. + +### Disclaimer + +Although this project is owned by Google Inc. this is not a Google supported or +affiliated project. diff --git a/xbmc/addons/kodi-dev-kit/src/addon/third_party/backward-cpp/backward.cpp b/xbmc/addons/kodi-dev-kit/src/addon/third_party/backward-cpp/backward.cpp new file mode 100644 index 0000000000000..110441cba92c2 --- /dev/null +++ b/xbmc/addons/kodi-dev-kit/src/addon/third_party/backward-cpp/backward.cpp @@ -0,0 +1,42 @@ +// Pick your poison. +// +// On GNU/Linux, you have few choices to get the most out of your stack trace. +// +// By default you get: +// - object filename +// - function name +// +// In order to add: +// - source filename +// - line and column numbers +// - source code snippet (assuming the file is accessible) + +// Install one of the following libraries then uncomment one of the macro (or +// better, add the detection of the lib and the macro definition in your build +// system) + +// - apt-get install libdw-dev ... +// - g++/clang++ -ldw ... +// #define BACKWARD_HAS_DW 1 + +// - apt-get install binutils-dev ... +// - g++/clang++ -lbfd ... +// #define BACKWARD_HAS_BFD 1 + +// - apt-get install libdwarf-dev ... +// - g++/clang++ -ldwarf ... +// #define BACKWARD_HAS_DWARF 1 + +// Regardless of the library you choose to read the debug information, +// for potentially more detailed stack traces you can use libunwind +// - apt-get install libunwind-dev +// - g++/clang++ -lunwind +// #define BACKWARD_HAS_LIBUNWIND 1 + +#include "backward.hpp" + +namespace backward { + +backward::SignalHandling sh; + +} // namespace backward diff --git a/xbmc/addons/kodi-dev-kit/src/addon/third_party/backward-cpp/backward.hpp b/xbmc/addons/kodi-dev-kit/src/addon/third_party/backward-cpp/backward.hpp new file mode 100644 index 0000000000000..19d5ba31c4b36 --- /dev/null +++ b/xbmc/addons/kodi-dev-kit/src/addon/third_party/backward-cpp/backward.hpp @@ -0,0 +1,4471 @@ +/* + * backward.hpp + * Copyright 2013 Google Inc. All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifndef H_6B9572DA_A64B_49E6_B234_051480991C89 +#define H_6B9572DA_A64B_49E6_B234_051480991C89 + +#ifndef __cplusplus +#error "It's not going to compile without a C++ compiler..." +#endif + +#if defined(BACKWARD_CXX11) +#elif defined(BACKWARD_CXX98) +#else +#if __cplusplus >= 201103L || (defined(_MSC_VER) && _MSC_VER >= 1800) +#define BACKWARD_CXX11 +#define BACKWARD_ATLEAST_CXX11 +#define BACKWARD_ATLEAST_CXX98 +#if __cplusplus >= 201703L || (defined(_MSVC_LANG) && _MSVC_LANG >= 201703L) +#define BACKWARD_ATLEAST_CXX17 +#endif +#else +#define BACKWARD_CXX98 +#define BACKWARD_ATLEAST_CXX98 +#endif +#endif + +// You can define one of the following (or leave it to the auto-detection): +// +// #define BACKWARD_SYSTEM_LINUX +// - specialization for linux +// +// #define BACKWARD_SYSTEM_DARWIN +// - specialization for Mac OS X 10.5 and later. +// +// #define BACKWARD_SYSTEM_WINDOWS +// - specialization for Windows (Clang 9 and MSVC2017) +// +// #define BACKWARD_SYSTEM_UNKNOWN +// - placebo implementation, does nothing. +// +#if defined(BACKWARD_SYSTEM_LINUX) +#elif defined(BACKWARD_SYSTEM_DARWIN) +#elif defined(BACKWARD_SYSTEM_UNKNOWN) +#elif defined(BACKWARD_SYSTEM_WINDOWS) +#else +#if defined(__linux) || defined(__linux__) +#define BACKWARD_SYSTEM_LINUX +#elif defined(__APPLE__) +#define BACKWARD_SYSTEM_DARWIN +#elif defined(_WIN32) +#define BACKWARD_SYSTEM_WINDOWS +#else +#define BACKWARD_SYSTEM_UNKNOWN +#endif +#endif + +#define NOINLINE __attribute__((noinline)) + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#if defined(BACKWARD_SYSTEM_LINUX) + +// On linux, backtrace can back-trace or "walk" the stack using the following +// libraries: +// +// #define BACKWARD_HAS_UNWIND 1 +// - unwind comes from libgcc, but I saw an equivalent inside clang itself. +// - with unwind, the stacktrace is as accurate as it can possibly be, since +// this is used by the C++ runtine in gcc/clang for stack unwinding on +// exception. +// - normally libgcc is already linked to your program by default. +// +// #define BACKWARD_HAS_LIBUNWIND 1 +// - libunwind provides, in some cases, a more accurate stacktrace as it knows +// to decode signal handler frames and lets us edit the context registers when +// unwinding, allowing stack traces over bad function references. +// +// #define BACKWARD_HAS_BACKTRACE == 1 +// - backtrace seems to be a little bit more portable than libunwind, but on +// linux, it uses unwind anyway, but abstract away a tiny information that is +// sadly really important in order to get perfectly accurate stack traces. +// - backtrace is part of the (e)glib library. +// +// The default is: +// #define BACKWARD_HAS_UNWIND == 1 +// +// Note that only one of the define should be set to 1 at a time. +// +#if BACKWARD_HAS_UNWIND == 1 +#elif BACKWARD_HAS_LIBUNWIND == 1 +#elif BACKWARD_HAS_BACKTRACE == 1 +#else +#undef BACKWARD_HAS_UNWIND +#define BACKWARD_HAS_UNWIND 1 +#undef BACKWARD_HAS_LIBUNWIND +#define BACKWARD_HAS_LIBUNWIND 0 +#undef BACKWARD_HAS_BACKTRACE +#define BACKWARD_HAS_BACKTRACE 0 +#endif + +// On linux, backward can extract detailed information about a stack trace +// using one of the following libraries: +// +// #define BACKWARD_HAS_DW 1 +// - libdw gives you the most juicy details out of your stack traces: +// - object filename +// - function name +// - source filename +// - line and column numbers +// - source code snippet (assuming the file is accessible) +// - variable names (if not optimized out) +// - variable values (not supported by backward-cpp) +// - You need to link with the lib "dw": +// - apt-get install libdw-dev +// - g++/clang++ -ldw ... +// +// #define BACKWARD_HAS_BFD 1 +// - With libbfd, you get a fair amount of details: +// - object filename +// - function name +// - source filename +// - line numbers +// - source code snippet (assuming the file is accessible) +// - You need to link with the lib "bfd": +// - apt-get install binutils-dev +// - g++/clang++ -lbfd ... +// +// #define BACKWARD_HAS_DWARF 1 +// - libdwarf gives you the most juicy details out of your stack traces: +// - object filename +// - function name +// - source filename +// - line and column numbers +// - source code snippet (assuming the file is accessible) +// - variable names (if not optimized out) +// - variable values (not supported by backward-cpp) +// - You need to link with the lib "dwarf": +// - apt-get install libdwarf-dev +// - g++/clang++ -ldwarf ... +// +// #define BACKWARD_HAS_BACKTRACE_SYMBOL 1 +// - backtrace provides minimal details for a stack trace: +// - object filename +// - function name +// - backtrace is part of the (e)glib library. +// +// The default is: +// #define BACKWARD_HAS_BACKTRACE_SYMBOL == 1 +// +// Note that only one of the define should be set to 1 at a time. +// +#if BACKWARD_HAS_DW == 1 +#elif BACKWARD_HAS_BFD == 1 +#elif BACKWARD_HAS_DWARF == 1 +#elif BACKWARD_HAS_BACKTRACE_SYMBOL == 1 +#else +#undef BACKWARD_HAS_DW +#define BACKWARD_HAS_DW 0 +#undef BACKWARD_HAS_BFD +#define BACKWARD_HAS_BFD 0 +#undef BACKWARD_HAS_DWARF +#define BACKWARD_HAS_DWARF 0 +#undef BACKWARD_HAS_BACKTRACE_SYMBOL +#define BACKWARD_HAS_BACKTRACE_SYMBOL 1 +#endif + +#include +#include +#ifdef __ANDROID__ +// Old Android API levels define _Unwind_Ptr in both link.h and +// unwind.h Rename the one in link.h as we are not going to be using +// it +#define _Unwind_Ptr _Unwind_Ptr_Custom +#include +#undef _Unwind_Ptr +#else +#include +#endif +#if defined(__ppc__) || defined(__powerpc) || defined(__powerpc__) || \ + defined(__POWERPC__) +// Linux kernel header required for the struct pt_regs definition +// to access the NIP (Next Instruction Pointer) register value +#include +#endif +#include +#include +#include +#include + +#if BACKWARD_HAS_BFD == 1 +// NOTE: defining PACKAGE{,_VERSION} is required before including +// bfd.h on some platforms, see also: +// https://sourceware.org/bugzilla/show_bug.cgi?id=14243 +#ifndef PACKAGE +#define PACKAGE +#endif +#ifndef PACKAGE_VERSION +#define PACKAGE_VERSION +#endif +#include +#ifndef _GNU_SOURCE +#define _GNU_SOURCE +#include +#undef _GNU_SOURCE +#else +#include +#endif +#endif + +#if BACKWARD_HAS_DW == 1 +#include +#include +#include +#endif + +#if BACKWARD_HAS_DWARF == 1 +#include +#include +#include +#include +#include +#ifndef _GNU_SOURCE +#define _GNU_SOURCE +#include +#undef _GNU_SOURCE +#else +#include +#endif +#endif + +#if (BACKWARD_HAS_BACKTRACE == 1) || (BACKWARD_HAS_BACKTRACE_SYMBOL == 1) +// then we shall rely on backtrace +#include +#endif + +#endif // defined(BACKWARD_SYSTEM_LINUX) + +#if defined(BACKWARD_SYSTEM_DARWIN) +// On Darwin, backtrace can back-trace or "walk" the stack using the following +// libraries: +// +// #define BACKWARD_HAS_UNWIND 1 +// - unwind comes from libgcc, but I saw an equivalent inside clang itself. +// - with unwind, the stacktrace is as accurate as it can possibly be, since +// this is used by the C++ runtine in gcc/clang for stack unwinding on +// exception. +// - normally libgcc is already linked to your program by default. +// +// #define BACKWARD_HAS_LIBUNWIND 1 +// - libunwind comes from clang, which implements an API compatible version. +// - libunwind provides, in some cases, a more accurate stacktrace as it knows +// to decode signal handler frames and lets us edit the context registers when +// unwinding, allowing stack traces over bad function references. +// +// #define BACKWARD_HAS_BACKTRACE == 1 +// - backtrace is available by default, though it does not produce as much +// information as another library might. +// +// The default is: +// #define BACKWARD_HAS_UNWIND == 1 +// +// Note that only one of the define should be set to 1 at a time. +// +#if BACKWARD_HAS_UNWIND == 1 +#elif BACKWARD_HAS_BACKTRACE == 1 +#elif BACKWARD_HAS_LIBUNWIND == 1 +#else +#undef BACKWARD_HAS_UNWIND +#define BACKWARD_HAS_UNWIND 1 +#undef BACKWARD_HAS_BACKTRACE +#define BACKWARD_HAS_BACKTRACE 0 +#undef BACKWARD_HAS_LIBUNWIND +#define BACKWARD_HAS_LIBUNWIND 0 +#endif + +// On Darwin, backward can extract detailed information about a stack trace +// using one of the following libraries: +// +// #define BACKWARD_HAS_BACKTRACE_SYMBOL 1 +// - backtrace provides minimal details for a stack trace: +// - object filename +// - function name +// +// The default is: +// #define BACKWARD_HAS_BACKTRACE_SYMBOL == 1 +// +#if BACKWARD_HAS_BACKTRACE_SYMBOL == 1 +#else +#undef BACKWARD_HAS_BACKTRACE_SYMBOL +#define BACKWARD_HAS_BACKTRACE_SYMBOL 1 +#endif + +#include +#include +#include +#include +#include +#include + +#if (BACKWARD_HAS_BACKTRACE == 1) || (BACKWARD_HAS_BACKTRACE_SYMBOL == 1) +#include +#endif +#endif // defined(BACKWARD_SYSTEM_DARWIN) + +#if defined(BACKWARD_SYSTEM_WINDOWS) + +#include +#include +#include + +#include +typedef SSIZE_T ssize_t; + +#ifndef NOMINMAX +#define NOMINMAX +#endif +#include +#include + +#include +#include + +#ifndef __clang__ +#undef NOINLINE +#define NOINLINE __declspec(noinline) +#endif + +#ifdef _MSC_VER +#pragma comment(lib, "psapi.lib") +#pragma comment(lib, "dbghelp.lib") +#endif + +// Comment / packing is from stackoverflow: +// https://stackoverflow.com/questions/6205981/windows-c-stack-trace-from-a-running-app/28276227#28276227 +// Some versions of imagehlp.dll lack the proper packing directives themselves +// so we need to do it. +#pragma pack(push, before_imagehlp, 8) +#include +#pragma pack(pop, before_imagehlp) + +// TODO maybe these should be undefined somewhere else? +#undef BACKWARD_HAS_UNWIND +#undef BACKWARD_HAS_BACKTRACE +#if BACKWARD_HAS_PDB_SYMBOL == 1 +#else +#undef BACKWARD_HAS_PDB_SYMBOL +#define BACKWARD_HAS_PDB_SYMBOL 1 +#endif + +#endif + +#if BACKWARD_HAS_UNWIND == 1 + +#include +// while gcc's unwind.h defines something like that: +// extern _Unwind_Ptr _Unwind_GetIP (struct _Unwind_Context *); +// extern _Unwind_Ptr _Unwind_GetIPInfo (struct _Unwind_Context *, int *); +// +// clang's unwind.h defines something like this: +// uintptr_t _Unwind_GetIP(struct _Unwind_Context* __context); +// +// Even if the _Unwind_GetIPInfo can be linked to, it is not declared, worse we +// cannot just redeclare it because clang's unwind.h doesn't define _Unwind_Ptr +// anyway. +// +// Luckily we can play on the fact that the guard macros have a different name: +#ifdef __CLANG_UNWIND_H +// In fact, this function still comes from libgcc (on my different linux boxes, +// clang links against libgcc). +#include +extern "C" uintptr_t _Unwind_GetIPInfo(_Unwind_Context *, int *); +#endif + +#endif // BACKWARD_HAS_UNWIND == 1 + +#if BACKWARD_HAS_LIBUNWIND == 1 +#define UNW_LOCAL_ONLY +#include +#endif // BACKWARD_HAS_LIBUNWIND == 1 + +#ifdef BACKWARD_ATLEAST_CXX11 +#include +#include // for std::swap +namespace backward { +namespace details { +template struct hashtable { + typedef std::unordered_map type; +}; +using std::move; +} // namespace details +} // namespace backward +#else // NOT BACKWARD_ATLEAST_CXX11 +#define nullptr NULL +#define override +#include +namespace backward { +namespace details { +template struct hashtable { + typedef std::map type; +}; +template const T &move(const T &v) { return v; } +template T &move(T &v) { return v; } +} // namespace details +} // namespace backward +#endif // BACKWARD_ATLEAST_CXX11 + +namespace backward { +namespace details { +#if defined(BACKWARD_SYSTEM_WINDOWS) +const char kBackwardPathDelimiter[] = ";"; +#else +const char kBackwardPathDelimiter[] = ":"; +#endif +} // namespace details +} // namespace backward + +namespace backward { + +namespace system_tag { +struct linux_tag; // seems that I cannot call that "linux" because the name +// is already defined... so I am adding _tag everywhere. +struct darwin_tag; +struct windows_tag; +struct unknown_tag; + +#if defined(BACKWARD_SYSTEM_LINUX) +typedef linux_tag current_tag; +#elif defined(BACKWARD_SYSTEM_DARWIN) +typedef darwin_tag current_tag; +#elif defined(BACKWARD_SYSTEM_WINDOWS) +typedef windows_tag current_tag; +#elif defined(BACKWARD_SYSTEM_UNKNOWN) +typedef unknown_tag current_tag; +#else +#error "May I please get my system defines?" +#endif +} // namespace system_tag + +namespace trace_resolver_tag { +#if defined(BACKWARD_SYSTEM_LINUX) +struct libdw; +struct libbfd; +struct libdwarf; +struct backtrace_symbol; + +#if BACKWARD_HAS_DW == 1 +typedef libdw current; +#elif BACKWARD_HAS_BFD == 1 +typedef libbfd current; +#elif BACKWARD_HAS_DWARF == 1 +typedef libdwarf current; +#elif BACKWARD_HAS_BACKTRACE_SYMBOL == 1 +typedef backtrace_symbol current; +#else +#error "You shall not pass, until you know what you want." +#endif +#elif defined(BACKWARD_SYSTEM_DARWIN) +struct backtrace_symbol; + +#if BACKWARD_HAS_BACKTRACE_SYMBOL == 1 +typedef backtrace_symbol current; +#else +#error "You shall not pass, until you know what you want." +#endif +#elif defined(BACKWARD_SYSTEM_WINDOWS) +struct pdb_symbol; +#if BACKWARD_HAS_PDB_SYMBOL == 1 +typedef pdb_symbol current; +#else +#error "You shall not pass, until you know what you want." +#endif +#endif +} // namespace trace_resolver_tag + +namespace details { + +template struct rm_ptr { typedef T type; }; + +template struct rm_ptr { typedef T type; }; + +template struct rm_ptr { typedef const T type; }; + +template struct deleter { + template void operator()(U &ptr) const { (*F)(ptr); } +}; + +template struct default_delete { + void operator()(T &ptr) const { delete ptr; } +}; + +template > +class handle { + struct dummy; + T _val; + bool _empty; + +#ifdef BACKWARD_ATLEAST_CXX11 + handle(const handle &) = delete; + handle &operator=(const handle &) = delete; +#endif + +public: + ~handle() { + if (!_empty) { + Deleter()(_val); + } + } + + explicit handle() : _val(), _empty(true) {} + explicit handle(T val) : _val(val), _empty(false) { + if (!_val) + _empty = true; + } + +#ifdef BACKWARD_ATLEAST_CXX11 + handle(handle &&from) : _empty(true) { swap(from); } + handle &operator=(handle &&from) { + swap(from); + return *this; + } +#else + explicit handle(const handle &from) : _empty(true) { + // some sort of poor man's move semantic. + swap(const_cast(from)); + } + handle &operator=(const handle &from) { + // some sort of poor man's move semantic. + swap(const_cast(from)); + return *this; + } +#endif + + void reset(T new_val) { + handle tmp(new_val); + swap(tmp); + } + + void update(T new_val) { + _val = new_val; + _empty = !static_cast(new_val); + } + + operator const dummy *() const { + if (_empty) { + return nullptr; + } + return reinterpret_cast(_val); + } + T get() { return _val; } + T release() { + _empty = true; + return _val; + } + void swap(handle &b) { + using std::swap; + swap(b._val, _val); // can throw, we are safe here. + swap(b._empty, _empty); // should not throw: if you cannot swap two + // bools without throwing... It's a lost cause anyway! + } + + T &operator->() { return _val; } + const T &operator->() const { return _val; } + + typedef typename rm_ptr::type &ref_t; + typedef const typename rm_ptr::type &const_ref_t; + ref_t operator*() { return *_val; } + const_ref_t operator*() const { return *_val; } + ref_t operator[](size_t idx) { return _val[idx]; } + + // Watch out, we've got a badass over here + T *operator&() { + _empty = false; + return &_val; + } +}; + +// Default demangler implementation (do nothing). +template struct demangler_impl { + static std::string demangle(const char *funcname) { return funcname; } +}; + +#if defined(BACKWARD_SYSTEM_LINUX) || defined(BACKWARD_SYSTEM_DARWIN) + +template <> struct demangler_impl { + demangler_impl() : _demangle_buffer_length(0) {} + + std::string demangle(const char *funcname) { + using namespace details; + char *result = abi::__cxa_demangle(funcname, _demangle_buffer.get(), + &_demangle_buffer_length, nullptr); + if (result) { + _demangle_buffer.update(result); + return result; + } + return funcname; + } + +private: + details::handle _demangle_buffer; + size_t _demangle_buffer_length; +}; + +#endif // BACKWARD_SYSTEM_LINUX || BACKWARD_SYSTEM_DARWIN + +struct demangler : public demangler_impl {}; + +// Split a string on the platform's PATH delimiter. Example: if delimiter +// is ":" then: +// "" --> [] +// ":" --> ["",""] +// "::" --> ["","",""] +// "/a/b/c" --> ["/a/b/c"] +// "/a/b/c:/d/e/f" --> ["/a/b/c","/d/e/f"] +// etc. +inline std::vector split_source_prefixes(const std::string &s) { + std::vector out; + size_t last = 0; + size_t next = 0; + size_t delimiter_size = sizeof(kBackwardPathDelimiter) - 1; + while ((next = s.find(kBackwardPathDelimiter, last)) != std::string::npos) { + out.push_back(s.substr(last, next - last)); + last = next + delimiter_size; + } + if (last <= s.length()) { + out.push_back(s.substr(last)); + } + return out; +} + +} // namespace details + +/*************** A TRACE ***************/ + +struct Trace { + void *addr; + size_t idx; + + Trace() : addr(nullptr), idx(0) {} + + explicit Trace(void *_addr, size_t _idx) : addr(_addr), idx(_idx) {} +}; + +struct ResolvedTrace : public Trace { + + struct SourceLoc { + std::string function; + std::string filename; + unsigned line; + unsigned col; + + SourceLoc() : line(0), col(0) {} + + bool operator==(const SourceLoc &b) const { + return function == b.function && filename == b.filename && + line == b.line && col == b.col; + } + + bool operator!=(const SourceLoc &b) const { return !(*this == b); } + }; + + // In which binary object this trace is located. + std::string object_filename; + + // The function in the object that contain the trace. This is not the same + // as source.function which can be an function inlined in object_function. + std::string object_function; + + // The source location of this trace. It is possible for filename to be + // empty and for line/col to be invalid (value 0) if this information + // couldn't be deduced, for example if there is no debug information in the + // binary object. + SourceLoc source; + + // An optionals list of "inliners". All the successive sources location + // from where the source location of the trace (the attribute right above) + // is inlined. It is especially useful when you compiled with optimization. + typedef std::vector source_locs_t; + source_locs_t inliners; + + ResolvedTrace() : Trace() {} + ResolvedTrace(const Trace &mini_trace) : Trace(mini_trace) {} +}; + +/*************** STACK TRACE ***************/ + +// default implemention. +template class StackTraceImpl { +public: + size_t size() const { return 0; } + Trace operator[](size_t) const { return Trace(); } + size_t load_here(size_t = 0) { return 0; } + size_t load_from(void *, size_t = 0, void * = nullptr, void * = nullptr) { + return 0; + } + size_t thread_id() const { return 0; } + void skip_n_firsts(size_t) {} +}; + +class StackTraceImplBase { +public: + StackTraceImplBase() + : _thread_id(0), _skip(0), _context(nullptr), _error_addr(nullptr) {} + + size_t thread_id() const { return _thread_id; } + + void skip_n_firsts(size_t n) { _skip = n; } + +protected: + void load_thread_info() { +#ifdef BACKWARD_SYSTEM_LINUX +#ifndef __ANDROID__ + _thread_id = static_cast(syscall(SYS_gettid)); +#else + _thread_id = static_cast(gettid()); +#endif + if (_thread_id == static_cast(getpid())) { + // If the thread is the main one, let's hide that. + // I like to keep little secret sometimes. + _thread_id = 0; + } +#elif defined(BACKWARD_SYSTEM_DARWIN) + _thread_id = reinterpret_cast(pthread_self()); + if (pthread_main_np() == 1) { + // If the thread is the main one, let's hide that. + _thread_id = 0; + } +#endif + } + + void set_context(void *context) { _context = context; } + void *context() const { return _context; } + + void set_error_addr(void *error_addr) { _error_addr = error_addr; } + void *error_addr() const { return _error_addr; } + + size_t skip_n_firsts() const { return _skip; } + +private: + size_t _thread_id; + size_t _skip; + void *_context; + void *_error_addr; +}; + +class StackTraceImplHolder : public StackTraceImplBase { +public: + size_t size() const { + return (_stacktrace.size() >= skip_n_firsts()) + ? _stacktrace.size() - skip_n_firsts() + : 0; + } + Trace operator[](size_t idx) const { + if (idx >= size()) { + return Trace(); + } + return Trace(_stacktrace[idx + skip_n_firsts()], idx); + } + void *const *begin() const { + if (size()) { + return &_stacktrace[skip_n_firsts()]; + } + return nullptr; + } + +protected: + std::vector _stacktrace; +}; + +#if BACKWARD_HAS_UNWIND == 1 + +namespace details { + +template class Unwinder { +public: + size_t operator()(F &f, size_t depth) { + _f = &f; + _index = -1; + _depth = depth; + _Unwind_Backtrace(&this->backtrace_trampoline, this); + return static_cast(_index); + } + +private: + F *_f; + ssize_t _index; + size_t _depth; + + static _Unwind_Reason_Code backtrace_trampoline(_Unwind_Context *ctx, + void *self) { + return (static_cast(self))->backtrace(ctx); + } + + _Unwind_Reason_Code backtrace(_Unwind_Context *ctx) { + if (_index >= 0 && static_cast(_index) >= _depth) + return _URC_END_OF_STACK; + + int ip_before_instruction = 0; + uintptr_t ip = _Unwind_GetIPInfo(ctx, &ip_before_instruction); + + if (!ip_before_instruction) { + // calculating 0-1 for unsigned, looks like a possible bug to sanitiziers, + // so let's do it explicitly: + if (ip == 0) { + ip = std::numeric_limits::max(); // set it to 0xffff... (as + // from casting 0-1) + } else { + ip -= 1; // else just normally decrement it (no overflow/underflow will + // happen) + } + } + + if (_index >= 0) { // ignore first frame. + (*_f)(static_cast(_index), reinterpret_cast(ip)); + } + _index += 1; + return _URC_NO_REASON; + } +}; + +template size_t unwind(F f, size_t depth) { + Unwinder unwinder; + return unwinder(f, depth); +} + +} // namespace details + +template <> +class StackTraceImpl : public StackTraceImplHolder { +public: + NOINLINE + size_t load_here(size_t depth = 32, void *context = nullptr, + void *error_addr = nullptr) { + load_thread_info(); + set_context(context); + set_error_addr(error_addr); + if (depth == 0) { + return 0; + } + _stacktrace.resize(depth); + size_t trace_cnt = details::unwind(callback(*this), depth); + _stacktrace.resize(trace_cnt); + skip_n_firsts(0); + return size(); + } + size_t load_from(void *addr, size_t depth = 32, void *context = nullptr, + void *error_addr = nullptr) { + load_here(depth + 8, context, error_addr); + + for (size_t i = 0; i < _stacktrace.size(); ++i) { + if (_stacktrace[i] == addr) { + skip_n_firsts(i); + break; + } + } + + _stacktrace.resize(std::min(_stacktrace.size(), skip_n_firsts() + depth)); + return size(); + } + +private: + struct callback { + StackTraceImpl &self; + callback(StackTraceImpl &_self) : self(_self) {} + + void operator()(size_t idx, void *addr) { self._stacktrace[idx] = addr; } + }; +}; + +#elif BACKWARD_HAS_LIBUNWIND == 1 + +template <> +class StackTraceImpl : public StackTraceImplHolder { +public: + __attribute__((noinline)) size_t load_here(size_t depth = 32, + void *_context = nullptr, + void *_error_addr = nullptr) { + set_context(_context); + set_error_addr(_error_addr); + load_thread_info(); + if (depth == 0) { + return 0; + } + _stacktrace.resize(depth + 1); + + int result = 0; + + unw_context_t ctx; + size_t index = 0; + + // Add the tail call. If the Instruction Pointer is the crash address it + // means we got a bad function pointer dereference, so we "unwind" the + // bad pointer manually by using the return address pointed to by the + // Stack Pointer as the Instruction Pointer and letting libunwind do + // the rest + + if (context()) { + ucontext_t *uctx = reinterpret_cast(context()); +#ifdef REG_RIP // x86_64 + if (uctx->uc_mcontext.gregs[REG_RIP] == + reinterpret_cast(error_addr())) { + uctx->uc_mcontext.gregs[REG_RIP] = + *reinterpret_cast(uctx->uc_mcontext.gregs[REG_RSP]); + } + _stacktrace[index] = + reinterpret_cast(uctx->uc_mcontext.gregs[REG_RIP]); + ++index; + ctx = *reinterpret_cast(uctx); +#elif defined(REG_EIP) // x86_32 + if (uctx->uc_mcontext.gregs[REG_EIP] == + reinterpret_cast(error_addr())) { + uctx->uc_mcontext.gregs[REG_EIP] = + *reinterpret_cast(uctx->uc_mcontext.gregs[REG_ESP]); + } + _stacktrace[index] = + reinterpret_cast(uctx->uc_mcontext.gregs[REG_EIP]); + ++index; + ctx = *reinterpret_cast(uctx); +#elif defined(__arm__) + // libunwind uses its own context type for ARM unwinding. + // Copy the registers from the signal handler's context so we can + // unwind + unw_getcontext(&ctx); + ctx.regs[UNW_ARM_R0] = uctx->uc_mcontext.arm_r0; + ctx.regs[UNW_ARM_R1] = uctx->uc_mcontext.arm_r1; + ctx.regs[UNW_ARM_R2] = uctx->uc_mcontext.arm_r2; + ctx.regs[UNW_ARM_R3] = uctx->uc_mcontext.arm_r3; + ctx.regs[UNW_ARM_R4] = uctx->uc_mcontext.arm_r4; + ctx.regs[UNW_ARM_R5] = uctx->uc_mcontext.arm_r5; + ctx.regs[UNW_ARM_R6] = uctx->uc_mcontext.arm_r6; + ctx.regs[UNW_ARM_R7] = uctx->uc_mcontext.arm_r7; + ctx.regs[UNW_ARM_R8] = uctx->uc_mcontext.arm_r8; + ctx.regs[UNW_ARM_R9] = uctx->uc_mcontext.arm_r9; + ctx.regs[UNW_ARM_R10] = uctx->uc_mcontext.arm_r10; + ctx.regs[UNW_ARM_R11] = uctx->uc_mcontext.arm_fp; + ctx.regs[UNW_ARM_R12] = uctx->uc_mcontext.arm_ip; + ctx.regs[UNW_ARM_R13] = uctx->uc_mcontext.arm_sp; + ctx.regs[UNW_ARM_R14] = uctx->uc_mcontext.arm_lr; + ctx.regs[UNW_ARM_R15] = uctx->uc_mcontext.arm_pc; + + // If we have crashed in the PC use the LR instead, as this was + // a bad function dereference + if (reinterpret_cast(error_addr()) == + uctx->uc_mcontext.arm_pc) { + ctx.regs[UNW_ARM_R15] = + uctx->uc_mcontext.arm_lr - sizeof(unsigned long); + } + _stacktrace[index] = reinterpret_cast(ctx.regs[UNW_ARM_R15]); + ++index; +#elif defined(__APPLE__) && defined(__x86_64__) + unw_getcontext(&ctx); + // OS X's implementation of libunwind uses its own context object + // so we need to convert the passed context to libunwind's format + // (information about the data layout taken from unw_getcontext.s + // in Apple's libunwind source + ctx.data[0] = uctx->uc_mcontext->__ss.__rax; + ctx.data[1] = uctx->uc_mcontext->__ss.__rbx; + ctx.data[2] = uctx->uc_mcontext->__ss.__rcx; + ctx.data[3] = uctx->uc_mcontext->__ss.__rdx; + ctx.data[4] = uctx->uc_mcontext->__ss.__rdi; + ctx.data[5] = uctx->uc_mcontext->__ss.__rsi; + ctx.data[6] = uctx->uc_mcontext->__ss.__rbp; + ctx.data[7] = uctx->uc_mcontext->__ss.__rsp; + ctx.data[8] = uctx->uc_mcontext->__ss.__r8; + ctx.data[9] = uctx->uc_mcontext->__ss.__r9; + ctx.data[10] = uctx->uc_mcontext->__ss.__r10; + ctx.data[11] = uctx->uc_mcontext->__ss.__r11; + ctx.data[12] = uctx->uc_mcontext->__ss.__r12; + ctx.data[13] = uctx->uc_mcontext->__ss.__r13; + ctx.data[14] = uctx->uc_mcontext->__ss.__r14; + ctx.data[15] = uctx->uc_mcontext->__ss.__r15; + ctx.data[16] = uctx->uc_mcontext->__ss.__rip; + + // If the IP is the same as the crash address we have a bad function + // dereference The caller's address is pointed to by %rsp, so we + // dereference that value and set it to be the next frame's IP. + if (uctx->uc_mcontext->__ss.__rip == + reinterpret_cast<__uint64_t>(error_addr())) { + ctx.data[16] = + *reinterpret_cast<__uint64_t *>(uctx->uc_mcontext->__ss.__rsp); + } + _stacktrace[index] = reinterpret_cast(ctx.data[16]); + ++index; +#elif defined(__APPLE__) + unw_getcontext(&ctx) + // TODO: Convert the ucontext_t to libunwind's unw_context_t like + // we do in 64 bits + if (ctx.uc_mcontext->__ss.__eip == + reinterpret_cast(error_addr())) { + ctx.uc_mcontext->__ss.__eip = ctx.uc_mcontext->__ss.__esp; + } + _stacktrace[index] = + reinterpret_cast(ctx.uc_mcontext->__ss.__eip); + ++index; +#endif + } + + unw_cursor_t cursor; + if (context()) { +#if defined(UNW_INIT_SIGNAL_FRAME) + result = unw_init_local2(&cursor, &ctx, UNW_INIT_SIGNAL_FRAME); +#else + result = unw_init_local(&cursor, &ctx); +#endif + } else { + unw_getcontext(&ctx); + ; + result = unw_init_local(&cursor, &ctx); + } + + if (result != 0) + return 1; + + unw_word_t ip = 0; + + while (index <= depth && unw_step(&cursor) > 0) { + result = unw_get_reg(&cursor, UNW_REG_IP, &ip); + if (result == 0) { + _stacktrace[index] = reinterpret_cast(--ip); + ++index; + } + } + --index; + + _stacktrace.resize(index + 1); + skip_n_firsts(0); + return size(); + } + + size_t load_from(void *addr, size_t depth = 32, void *context = nullptr, + void *error_addr = nullptr) { + load_here(depth + 8, context, error_addr); + + for (size_t i = 0; i < _stacktrace.size(); ++i) { + if (_stacktrace[i] == addr) { + skip_n_firsts(i); + _stacktrace[i] = (void *)((uintptr_t)_stacktrace[i]); + break; + } + } + + _stacktrace.resize(std::min(_stacktrace.size(), skip_n_firsts() + depth)); + return size(); + } +}; + +#elif defined(BACKWARD_HAS_BACKTRACE) + +template <> +class StackTraceImpl : public StackTraceImplHolder { +public: + NOINLINE + size_t load_here(size_t depth = 32, void *context = nullptr, + void *error_addr = nullptr) { + set_context(context); + set_error_addr(error_addr); + load_thread_info(); + if (depth == 0) { + return 0; + } + _stacktrace.resize(depth + 1); + size_t trace_cnt = backtrace(&_stacktrace[0], _stacktrace.size()); + _stacktrace.resize(trace_cnt); + skip_n_firsts(1); + return size(); + } + + size_t load_from(void *addr, size_t depth = 32, void *context = nullptr, + void *error_addr = nullptr) { + load_here(depth + 8, context, error_addr); + + for (size_t i = 0; i < _stacktrace.size(); ++i) { + if (_stacktrace[i] == addr) { + skip_n_firsts(i); + _stacktrace[i] = (void *)((uintptr_t)_stacktrace[i] + 1); + break; + } + } + + _stacktrace.resize(std::min(_stacktrace.size(), skip_n_firsts() + depth)); + return size(); + } +}; + +#elif defined(BACKWARD_SYSTEM_WINDOWS) + +template <> +class StackTraceImpl : public StackTraceImplHolder { +public: + // We have to load the machine type from the image info + // So we first initialize the resolver, and it tells us this info + void set_machine_type(DWORD machine_type) { machine_type_ = machine_type; } + void set_context(CONTEXT *ctx) { ctx_ = ctx; } + void set_thread_handle(HANDLE handle) { thd_ = handle; } + + NOINLINE + size_t load_here(size_t depth = 32, void *context = nullptr, + void *error_addr = nullptr) { + set_context(static_cast(context)); + set_error_addr(error_addr); + CONTEXT localCtx; // used when no context is provided + + if (depth == 0) { + return 0; + } + + if (!ctx_) { + ctx_ = &localCtx; + RtlCaptureContext(ctx_); + } + + if (!thd_) { + thd_ = GetCurrentThread(); + } + + HANDLE process = GetCurrentProcess(); + + STACKFRAME64 s; + memset(&s, 0, sizeof(STACKFRAME64)); + + // TODO: 32 bit context capture + s.AddrStack.Mode = AddrModeFlat; + s.AddrFrame.Mode = AddrModeFlat; + s.AddrPC.Mode = AddrModeFlat; +#ifdef _M_X64 + s.AddrPC.Offset = ctx_->Rip; + s.AddrStack.Offset = ctx_->Rsp; + s.AddrFrame.Offset = ctx_->Rbp; +#else + s.AddrPC.Offset = ctx_->Eip; + s.AddrStack.Offset = ctx_->Esp; + s.AddrFrame.Offset = ctx_->Ebp; +#endif + + if (!machine_type_) { +#ifdef _M_X64 + machine_type_ = IMAGE_FILE_MACHINE_AMD64; +#else + machine_type_ = IMAGE_FILE_MACHINE_I386; +#endif + } + + for (;;) { + // NOTE: this only works if PDBs are already loaded! + SetLastError(0); + if (!StackWalk64(machine_type_, process, thd_, &s, ctx_, NULL, + SymFunctionTableAccess64, SymGetModuleBase64, NULL)) + break; + + if (s.AddrReturn.Offset == 0) + break; + + _stacktrace.push_back(reinterpret_cast(s.AddrPC.Offset)); + + if (size() >= depth) + break; + } + + return size(); + } + + size_t load_from(void *addr, size_t depth = 32, void *context = nullptr, + void *error_addr = nullptr) { + load_here(depth + 8, context, error_addr); + + for (size_t i = 0; i < _stacktrace.size(); ++i) { + if (_stacktrace[i] == addr) { + skip_n_firsts(i); + break; + } + } + + _stacktrace.resize(std::min(_stacktrace.size(), skip_n_firsts() + depth)); + return size(); + } + +private: + DWORD machine_type_ = 0; + HANDLE thd_ = 0; + CONTEXT *ctx_ = nullptr; +}; + +#endif + +class StackTrace : public StackTraceImpl {}; + +/*************** TRACE RESOLVER ***************/ + +class TraceResolverImplBase { +public: + virtual ~TraceResolverImplBase() {} + + virtual void load_addresses(void *const*addresses, int address_count) { + (void)addresses; + (void)address_count; + } + + template void load_stacktrace(ST &st) { + load_addresses(st.begin(), static_cast(st.size())); + } + + virtual ResolvedTrace resolve(ResolvedTrace t) { return t; } + +protected: + std::string demangle(const char *funcname) { + return _demangler.demangle(funcname); + } + +private: + details::demangler _demangler; +}; + +template class TraceResolverImpl; + +#ifdef BACKWARD_SYSTEM_UNKNOWN + +template <> class TraceResolverImpl + : public TraceResolverImplBase {}; + +#endif + +#ifdef BACKWARD_SYSTEM_LINUX + +class TraceResolverLinuxBase : public TraceResolverImplBase { +public: + TraceResolverLinuxBase() + : argv0_(get_argv0()), exec_path_(read_symlink("/proc/self/exe")) {} + std::string resolve_exec_path(Dl_info &symbol_info) const { + // mutates symbol_info.dli_fname to be filename to open and returns filename + // to display + if (symbol_info.dli_fname == argv0_) { + // dladdr returns argv[0] in dli_fname for symbols contained in + // the main executable, which is not a valid path if the + // executable was found by a search of the PATH environment + // variable; In that case, we actually open /proc/self/exe, which + // is always the actual executable (even if it was deleted/replaced!) + // but display the path that /proc/self/exe links to. + // However, this right away reduces probability of successful symbol + // resolution, because libbfd may try to find *.debug files in the + // same dir, in case symbols are stripped. As a result, it may try + // to find a file /proc/self/.debug, which obviously does + // not exist. /proc/self/exe is a last resort. First load attempt + // should go for the original executable file path. + symbol_info.dli_fname = "/proc/self/exe"; + return exec_path_; + } else { + return symbol_info.dli_fname; + } + } + +private: + std::string argv0_; + std::string exec_path_; + + static std::string get_argv0() { + std::string argv0; + std::ifstream ifs("/proc/self/cmdline"); + std::getline(ifs, argv0, '\0'); + return argv0; + } + + static std::string read_symlink(std::string const &symlink_path) { + std::string path; + path.resize(100); + + while (true) { + ssize_t len = + ::readlink(symlink_path.c_str(), &*path.begin(), path.size()); + if (len < 0) { + return ""; + } + if (static_cast(len) == path.size()) { + path.resize(path.size() * 2); + } else { + path.resize(static_cast(len)); + break; + } + } + + return path; + } +}; + +template class TraceResolverLinuxImpl; + +#if BACKWARD_HAS_BACKTRACE_SYMBOL == 1 + +template <> +class TraceResolverLinuxImpl + : public TraceResolverLinuxBase { +public: + void load_addresses(void *const*addresses, int address_count) override { + if (address_count == 0) { + return; + } + _symbols.reset(backtrace_symbols(addresses, address_count)); + } + + ResolvedTrace resolve(ResolvedTrace trace) override { + char *filename = _symbols[trace.idx]; + char *funcname = filename; + while (*funcname && *funcname != '(') { + funcname += 1; + } + trace.object_filename.assign(filename, + funcname); // ok even if funcname is the ending + // \0 (then we assign entire string) + + if (*funcname) { // if it's not end of string (e.g. from last frame ip==0) + funcname += 1; + char *funcname_end = funcname; + while (*funcname_end && *funcname_end != ')' && *funcname_end != '+') { + funcname_end += 1; + } + *funcname_end = '\0'; + trace.object_function = this->demangle(funcname); + trace.source.function = trace.object_function; // we cannot do better. + } + return trace; + } + +private: + details::handle _symbols; +}; + +#endif // BACKWARD_HAS_BACKTRACE_SYMBOL == 1 + +#if BACKWARD_HAS_BFD == 1 + +template <> +class TraceResolverLinuxImpl + : public TraceResolverLinuxBase { +public: + TraceResolverLinuxImpl() : _bfd_loaded(false) {} + + ResolvedTrace resolve(ResolvedTrace trace) override { + Dl_info symbol_info; + + // trace.addr is a virtual address in memory pointing to some code. + // Let's try to find from which loaded object it comes from. + // The loaded object can be yourself btw. + if (!dladdr(trace.addr, &symbol_info)) { + return trace; // dat broken trace... + } + + // Now we get in symbol_info: + // .dli_fname: + // pathname of the shared object that contains the address. + // .dli_fbase: + // where the object is loaded in memory. + // .dli_sname: + // the name of the nearest symbol to trace.addr, we expect a + // function name. + // .dli_saddr: + // the exact address corresponding to .dli_sname. + + if (symbol_info.dli_sname) { + trace.object_function = demangle(symbol_info.dli_sname); + } + + if (!symbol_info.dli_fname) { + return trace; + } + + trace.object_filename = resolve_exec_path(symbol_info); + bfd_fileobject *fobj; + // Before rushing to resolution need to ensure the executable + // file still can be used. For that compare inode numbers of + // what is stored by the executable's file path, and in the + // dli_fname, which not necessarily equals to the executable. + // It can be a shared library, or /proc/self/exe, and in the + // latter case has drawbacks. See the exec path resolution for + // details. In short - the dli object should be used only as + // the last resort. + // If inode numbers are equal, it is known dli_fname and the + // executable file are the same. This is guaranteed by Linux, + // because if the executable file is changed/deleted, it will + // be done in a new inode. The old file will be preserved in + // /proc/self/exe, and may even have inode 0. The latter can + // happen if the inode was actually reused, and the file was + // kept only in the main memory. + // + struct stat obj_stat; + struct stat dli_stat; + if (stat(trace.object_filename.c_str(), &obj_stat) == 0 && + stat(symbol_info.dli_fname, &dli_stat) == 0 && + obj_stat.st_ino == dli_stat.st_ino) { + // The executable file, and the shared object containing the + // address are the same file. Safe to use the original path. + // this is preferable. Libbfd will search for stripped debug + // symbols in the same directory. + fobj = load_object_with_bfd(trace.object_filename); + } else{ + // The original object file was *deleted*! The only hope is + // that the debug symbols are either inside the shared + // object file, or are in the same directory, and this is + // not /proc/self/exe. + fobj = nullptr; + } + if (fobj == nullptr || !fobj->handle) { + fobj = load_object_with_bfd(symbol_info.dli_fname); + if (!fobj->handle) { + return trace; + } + } + + find_sym_result *details_selected; // to be filled. + + // trace.addr is the next instruction to be executed after returning + // from the nested stack frame. In C++ this usually relate to the next + // statement right after the function call that leaded to a new stack + // frame. This is not usually what you want to see when printing out a + // stacktrace... + find_sym_result details_call_site = + find_symbol_details(fobj, trace.addr, symbol_info.dli_fbase); + details_selected = &details_call_site; + +#if BACKWARD_HAS_UNWIND == 0 + // ...this is why we also try to resolve the symbol that is right + // before the return address. If we are lucky enough, we will get the + // line of the function that was called. But if the code is optimized, + // we might get something absolutely not related since the compiler + // can reschedule the return address with inline functions and + // tail-call optimisation (among other things that I don't even know + // or cannot even dream about with my tiny limited brain). + find_sym_result details_adjusted_call_site = find_symbol_details( + fobj, (void *)(uintptr_t(trace.addr) - 1), symbol_info.dli_fbase); + + // In debug mode, we should always get the right thing(TM). + if (details_call_site.found && details_adjusted_call_site.found) { + // Ok, we assume that details_adjusted_call_site is a better estimation. + details_selected = &details_adjusted_call_site; + trace.addr = (void *)(uintptr_t(trace.addr) - 1); + } + + if (details_selected == &details_call_site && details_call_site.found) { + // we have to re-resolve the symbol in order to reset some + // internal state in BFD... so we can call backtrace_inliners + // thereafter... + details_call_site = + find_symbol_details(fobj, trace.addr, symbol_info.dli_fbase); + } +#endif // BACKWARD_HAS_UNWIND + + if (details_selected->found) { + if (details_selected->filename) { + trace.source.filename = details_selected->filename; + } + trace.source.line = details_selected->line; + + if (details_selected->funcname) { + // this time we get the name of the function where the code is + // located, instead of the function were the address is + // located. In short, if the code was inlined, we get the + // function correspoding to the code. Else we already got in + // trace.function. + trace.source.function = demangle(details_selected->funcname); + + if (!symbol_info.dli_sname) { + // for the case dladdr failed to find the symbol name of + // the function, we might as well try to put something + // here. + trace.object_function = trace.source.function; + } + } + + // Maybe the source of the trace got inlined inside the function + // (trace.source.function). Let's see if we can get all the inlined + // calls along the way up to the initial call site. + trace.inliners = backtrace_inliners(fobj, *details_selected); + +#if 0 + if (trace.inliners.size() == 0) { + // Maybe the trace was not inlined... or maybe it was and we + // are lacking the debug information. Let's try to make the + // world better and see if we can get the line number of the + // function (trace.source.function) now. + // + // We will get the location of where the function start (to be + // exact: the first instruction that really start the + // function), not where the name of the function is defined. + // This can be quite far away from the name of the function + // btw. + // + // If the source of the function is the same as the source of + // the trace, we cannot say if the trace was really inlined or + // not. However, if the filename of the source is different + // between the function and the trace... we can declare it as + // an inliner. This is not 100% accurate, but better than + // nothing. + + if (symbol_info.dli_saddr) { + find_sym_result details = find_symbol_details(fobj, + symbol_info.dli_saddr, + symbol_info.dli_fbase); + + if (details.found) { + ResolvedTrace::SourceLoc diy_inliner; + diy_inliner.line = details.line; + if (details.filename) { + diy_inliner.filename = details.filename; + } + if (details.funcname) { + diy_inliner.function = demangle(details.funcname); + } else { + diy_inliner.function = trace.source.function; + } + if (diy_inliner != trace.source) { + trace.inliners.push_back(diy_inliner); + } + } + } + } +#endif + } + + return trace; + } + +private: + bool _bfd_loaded; + + typedef details::handle > + bfd_handle_t; + + typedef details::handle bfd_symtab_t; + + struct bfd_fileobject { + bfd_handle_t handle; + bfd_vma base_addr; + bfd_symtab_t symtab; + bfd_symtab_t dynamic_symtab; + }; + + typedef details::hashtable::type fobj_bfd_map_t; + fobj_bfd_map_t _fobj_bfd_map; + + bfd_fileobject *load_object_with_bfd(const std::string &filename_object) { + using namespace details; + + if (!_bfd_loaded) { + using namespace details; + bfd_init(); + _bfd_loaded = true; + } + + fobj_bfd_map_t::iterator it = _fobj_bfd_map.find(filename_object); + if (it != _fobj_bfd_map.end()) { + return &it->second; + } + + // this new object is empty for now. + bfd_fileobject *r = &_fobj_bfd_map[filename_object]; + + // we do the work temporary in this one; + bfd_handle_t bfd_handle; + + int fd = open(filename_object.c_str(), O_RDONLY); + bfd_handle.reset(bfd_fdopenr(filename_object.c_str(), "default", fd)); + if (!bfd_handle) { + close(fd); + return r; + } + + if (!bfd_check_format(bfd_handle.get(), bfd_object)) { + return r; // not an object? You lose. + } + + if ((bfd_get_file_flags(bfd_handle.get()) & HAS_SYMS) == 0) { + return r; // that's what happen when you forget to compile in debug. + } + + ssize_t symtab_storage_size = bfd_get_symtab_upper_bound(bfd_handle.get()); + + ssize_t dyn_symtab_storage_size = + bfd_get_dynamic_symtab_upper_bound(bfd_handle.get()); + + if (symtab_storage_size <= 0 && dyn_symtab_storage_size <= 0) { + return r; // weird, is the file is corrupted? + } + + bfd_symtab_t symtab, dynamic_symtab; + ssize_t symcount = 0, dyn_symcount = 0; + + if (symtab_storage_size > 0) { + symtab.reset(static_cast( + malloc(static_cast(symtab_storage_size)))); + symcount = bfd_canonicalize_symtab(bfd_handle.get(), symtab.get()); + } + + if (dyn_symtab_storage_size > 0) { + dynamic_symtab.reset(static_cast( + malloc(static_cast(dyn_symtab_storage_size)))); + dyn_symcount = bfd_canonicalize_dynamic_symtab(bfd_handle.get(), + dynamic_symtab.get()); + } + + if (symcount <= 0 && dyn_symcount <= 0) { + return r; // damned, that's a stripped file that you got there! + } + + r->handle = move(bfd_handle); + r->symtab = move(symtab); + r->dynamic_symtab = move(dynamic_symtab); + return r; + } + + struct find_sym_result { + bool found; + const char *filename; + const char *funcname; + unsigned int line; + }; + + struct find_sym_context { + TraceResolverLinuxImpl *self; + bfd_fileobject *fobj; + void *addr; + void *base_addr; + find_sym_result result; + }; + + find_sym_result find_symbol_details(bfd_fileobject *fobj, void *addr, + void *base_addr) { + find_sym_context context; + context.self = this; + context.fobj = fobj; + context.addr = addr; + context.base_addr = base_addr; + context.result.found = false; + bfd_map_over_sections(fobj->handle.get(), &find_in_section_trampoline, + static_cast(&context)); + return context.result; + } + + static void find_in_section_trampoline(bfd *, asection *section, void *data) { + find_sym_context *context = static_cast(data); + context->self->find_in_section( + reinterpret_cast(context->addr), + reinterpret_cast(context->base_addr), context->fobj, section, + context->result); + } + + void find_in_section(bfd_vma addr, bfd_vma base_addr, bfd_fileobject *fobj, + asection *section, find_sym_result &result) { + if (result.found) + return; + +#ifdef bfd_get_section_flags + if ((bfd_get_section_flags(fobj->handle.get(), section) & SEC_ALLOC) == 0) +#else + if ((bfd_section_flags(section) & SEC_ALLOC) == 0) +#endif + return; // a debug section is never loaded automatically. + +#ifdef bfd_get_section_vma + bfd_vma sec_addr = bfd_get_section_vma(fobj->handle.get(), section); +#else + bfd_vma sec_addr = bfd_section_vma(section); +#endif +#ifdef bfd_get_section_size + bfd_size_type size = bfd_get_section_size(section); +#else + bfd_size_type size = bfd_section_size(section); +#endif + + // are we in the boundaries of the section? + if (addr < sec_addr || addr >= sec_addr + size) { + addr -= base_addr; // oups, a relocated object, lets try again... + if (addr < sec_addr || addr >= sec_addr + size) { + return; + } + } + +#if defined(__clang__) +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wzero-as-null-pointer-constant" +#endif + if (!result.found && fobj->symtab) { + result.found = bfd_find_nearest_line( + fobj->handle.get(), section, fobj->symtab.get(), addr - sec_addr, + &result.filename, &result.funcname, &result.line); + } + + if (!result.found && fobj->dynamic_symtab) { + result.found = bfd_find_nearest_line( + fobj->handle.get(), section, fobj->dynamic_symtab.get(), + addr - sec_addr, &result.filename, &result.funcname, &result.line); + } +#if defined(__clang__) +#pragma clang diagnostic pop +#endif + } + + ResolvedTrace::source_locs_t + backtrace_inliners(bfd_fileobject *fobj, find_sym_result previous_result) { + // This function can be called ONLY after a SUCCESSFUL call to + // find_symbol_details. The state is global to the bfd_handle. + ResolvedTrace::source_locs_t results; + while (previous_result.found) { + find_sym_result result; + result.found = bfd_find_inliner_info(fobj->handle.get(), &result.filename, + &result.funcname, &result.line); + + if (result + .found) /* and not ( + cstrings_eq(previous_result.filename, + result.filename) and + cstrings_eq(previous_result.funcname, result.funcname) + and result.line == previous_result.line + )) */ + { + ResolvedTrace::SourceLoc src_loc; + src_loc.line = result.line; + if (result.filename) { + src_loc.filename = result.filename; + } + if (result.funcname) { + src_loc.function = demangle(result.funcname); + } + results.push_back(src_loc); + } + previous_result = result; + } + return results; + } + + bool cstrings_eq(const char *a, const char *b) { + if (!a || !b) { + return false; + } + return strcmp(a, b) == 0; + } +}; +#endif // BACKWARD_HAS_BFD == 1 + +#if BACKWARD_HAS_DW == 1 + +template <> +class TraceResolverLinuxImpl + : public TraceResolverLinuxBase { +public: + TraceResolverLinuxImpl() : _dwfl_handle_initialized(false) {} + + ResolvedTrace resolve(ResolvedTrace trace) override { + using namespace details; + + Dwarf_Addr trace_addr = (Dwarf_Addr)trace.addr; + + if (!_dwfl_handle_initialized) { + // initialize dwfl... + _dwfl_cb.reset(new Dwfl_Callbacks); + _dwfl_cb->find_elf = &dwfl_linux_proc_find_elf; + _dwfl_cb->find_debuginfo = &dwfl_standard_find_debuginfo; + _dwfl_cb->debuginfo_path = 0; + + _dwfl_handle.reset(dwfl_begin(_dwfl_cb.get())); + _dwfl_handle_initialized = true; + + if (!_dwfl_handle) { + return trace; + } + + // ...from the current process. + dwfl_report_begin(_dwfl_handle.get()); + int r = dwfl_linux_proc_report(_dwfl_handle.get(), getpid()); + dwfl_report_end(_dwfl_handle.get(), NULL, NULL); + if (r < 0) { + return trace; + } + } + + if (!_dwfl_handle) { + return trace; + } + + // find the module (binary object) that contains the trace's address. + // This is not using any debug information, but the addresses ranges of + // all the currently loaded binary object. + Dwfl_Module *mod = dwfl_addrmodule(_dwfl_handle.get(), trace_addr); + if (mod) { + // now that we found it, lets get the name of it, this will be the + // full path to the running binary or one of the loaded library. + const char *module_name = dwfl_module_info(mod, 0, 0, 0, 0, 0, 0, 0); + if (module_name) { + trace.object_filename = module_name; + } + // We also look after the name of the symbol, equal or before this + // address. This is found by walking the symtab. We should get the + // symbol corresponding to the function (mangled) containing the + // address. If the code corresponding to the address was inlined, + // this is the name of the out-most inliner function. + const char *sym_name = dwfl_module_addrname(mod, trace_addr); + if (sym_name) { + trace.object_function = demangle(sym_name); + } + } + + // now let's get serious, and find out the source location (file and + // line number) of the address. + + // This function will look in .debug_aranges for the address and map it + // to the location of the compilation unit DIE in .debug_info and + // return it. + Dwarf_Addr mod_bias = 0; + Dwarf_Die *cudie = dwfl_module_addrdie(mod, trace_addr, &mod_bias); + +#if 1 + if (!cudie) { + // Sadly clang does not generate the section .debug_aranges, thus + // dwfl_module_addrdie will fail early. Clang doesn't either set + // the lowpc/highpc/range info for every compilation unit. + // + // So in order to save the world: + // for every compilation unit, we will iterate over every single + // DIEs. Normally functions should have a lowpc/highpc/range, which + // we will use to infer the compilation unit. + + // note that this is probably badly inefficient. + while ((cudie = dwfl_module_nextcu(mod, cudie, &mod_bias))) { + Dwarf_Die die_mem; + Dwarf_Die *fundie = + find_fundie_by_pc(cudie, trace_addr - mod_bias, &die_mem); + if (fundie) { + break; + } + } + } +#endif + +//#define BACKWARD_I_DO_NOT_RECOMMEND_TO_ENABLE_THIS_HORRIBLE_PIECE_OF_CODE +#ifdef BACKWARD_I_DO_NOT_RECOMMEND_TO_ENABLE_THIS_HORRIBLE_PIECE_OF_CODE + if (!cudie) { + // If it's still not enough, lets dive deeper in the shit, and try + // to save the world again: for every compilation unit, we will + // load the corresponding .debug_line section, and see if we can + // find our address in it. + + Dwarf_Addr cfi_bias; + Dwarf_CFI *cfi_cache = dwfl_module_eh_cfi(mod, &cfi_bias); + + Dwarf_Addr bias; + while ((cudie = dwfl_module_nextcu(mod, cudie, &bias))) { + if (dwarf_getsrc_die(cudie, trace_addr - bias)) { + + // ...but if we get a match, it might be a false positive + // because our (address - bias) might as well be valid in a + // different compilation unit. So we throw our last card on + // the table and lookup for the address into the .eh_frame + // section. + + handle frame; + dwarf_cfi_addrframe(cfi_cache, trace_addr - cfi_bias, &frame); + if (frame) { + break; + } + } + } + } +#endif + + if (!cudie) { + return trace; // this time we lost the game :/ + } + + // Now that we have a compilation unit DIE, this function will be able + // to load the corresponding section in .debug_line (if not already + // loaded) and hopefully find the source location mapped to our + // address. + Dwarf_Line *srcloc = dwarf_getsrc_die(cudie, trace_addr - mod_bias); + + if (srcloc) { + const char *srcfile = dwarf_linesrc(srcloc, 0, 0); + if (srcfile) { + trace.source.filename = srcfile; + } + int line = 0, col = 0; + dwarf_lineno(srcloc, &line); + dwarf_linecol(srcloc, &col); + trace.source.line = line; + trace.source.col = col; + } + + deep_first_search_by_pc(cudie, trace_addr - mod_bias, + inliners_search_cb(trace)); + if (trace.source.function.size() == 0) { + // fallback. + trace.source.function = trace.object_function; + } + + return trace; + } + +private: + typedef details::handle > + dwfl_handle_t; + details::handle > + _dwfl_cb; + dwfl_handle_t _dwfl_handle; + bool _dwfl_handle_initialized; + + // defined here because in C++98, template function cannot take locally + // defined types... grrr. + struct inliners_search_cb { + void operator()(Dwarf_Die *die) { + switch (dwarf_tag(die)) { + const char *name; + case DW_TAG_subprogram: + if ((name = dwarf_diename(die))) { + trace.source.function = name; + } + break; + + case DW_TAG_inlined_subroutine: + ResolvedTrace::SourceLoc sloc; + Dwarf_Attribute attr_mem; + + if ((name = dwarf_diename(die))) { + sloc.function = name; + } + if ((name = die_call_file(die))) { + sloc.filename = name; + } + + Dwarf_Word line = 0, col = 0; + dwarf_formudata(dwarf_attr(die, DW_AT_call_line, &attr_mem), &line); + dwarf_formudata(dwarf_attr(die, DW_AT_call_column, &attr_mem), &col); + sloc.line = (unsigned)line; + sloc.col = (unsigned)col; + + trace.inliners.push_back(sloc); + break; + }; + } + ResolvedTrace &trace; + inliners_search_cb(ResolvedTrace &t) : trace(t) {} + }; + + static bool die_has_pc(Dwarf_Die *die, Dwarf_Addr pc) { + Dwarf_Addr low, high; + + // continuous range + if (dwarf_hasattr(die, DW_AT_low_pc) && dwarf_hasattr(die, DW_AT_high_pc)) { + if (dwarf_lowpc(die, &low) != 0) { + return false; + } + if (dwarf_highpc(die, &high) != 0) { + Dwarf_Attribute attr_mem; + Dwarf_Attribute *attr = dwarf_attr(die, DW_AT_high_pc, &attr_mem); + Dwarf_Word value; + if (dwarf_formudata(attr, &value) != 0) { + return false; + } + high = low + value; + } + return pc >= low && pc < high; + } + + // non-continuous range. + Dwarf_Addr base; + ptrdiff_t offset = 0; + while ((offset = dwarf_ranges(die, offset, &base, &low, &high)) > 0) { + if (pc >= low && pc < high) { + return true; + } + } + return false; + } + + static Dwarf_Die *find_fundie_by_pc(Dwarf_Die *parent_die, Dwarf_Addr pc, + Dwarf_Die *result) { + if (dwarf_child(parent_die, result) != 0) { + return 0; + } + + Dwarf_Die *die = result; + do { + switch (dwarf_tag(die)) { + case DW_TAG_subprogram: + case DW_TAG_inlined_subroutine: + if (die_has_pc(die, pc)) { + return result; + } + }; + bool declaration = false; + Dwarf_Attribute attr_mem; + dwarf_formflag(dwarf_attr(die, DW_AT_declaration, &attr_mem), + &declaration); + if (!declaration) { + // let's be curious and look deeper in the tree, + // function are not necessarily at the first level, but + // might be nested inside a namespace, structure etc. + Dwarf_Die die_mem; + Dwarf_Die *indie = find_fundie_by_pc(die, pc, &die_mem); + if (indie) { + *result = die_mem; + return result; + } + } + } while (dwarf_siblingof(die, result) == 0); + return 0; + } + + template + static bool deep_first_search_by_pc(Dwarf_Die *parent_die, Dwarf_Addr pc, + CB cb) { + Dwarf_Die die_mem; + if (dwarf_child(parent_die, &die_mem) != 0) { + return false; + } + + bool branch_has_pc = false; + Dwarf_Die *die = &die_mem; + do { + bool declaration = false; + Dwarf_Attribute attr_mem; + dwarf_formflag(dwarf_attr(die, DW_AT_declaration, &attr_mem), + &declaration); + if (!declaration) { + // let's be curious and look deeper in the tree, function are + // not necessarily at the first level, but might be nested + // inside a namespace, structure, a function, an inlined + // function etc. + branch_has_pc = deep_first_search_by_pc(die, pc, cb); + } + if (!branch_has_pc) { + branch_has_pc = die_has_pc(die, pc); + } + if (branch_has_pc) { + cb(die); + } + } while (dwarf_siblingof(die, &die_mem) == 0); + return branch_has_pc; + } + + static const char *die_call_file(Dwarf_Die *die) { + Dwarf_Attribute attr_mem; + Dwarf_Word file_idx = 0; + + dwarf_formudata(dwarf_attr(die, DW_AT_call_file, &attr_mem), &file_idx); + + if (file_idx == 0) { + return 0; + } + + Dwarf_Die die_mem; + Dwarf_Die *cudie = dwarf_diecu(die, &die_mem, 0, 0); + if (!cudie) { + return 0; + } + + Dwarf_Files *files = 0; + size_t nfiles; + dwarf_getsrcfiles(cudie, &files, &nfiles); + if (!files) { + return 0; + } + + return dwarf_filesrc(files, file_idx, 0, 0); + } +}; +#endif // BACKWARD_HAS_DW == 1 + +#if BACKWARD_HAS_DWARF == 1 + +template <> +class TraceResolverLinuxImpl + : public TraceResolverLinuxBase { +public: + TraceResolverLinuxImpl() : _dwarf_loaded(false) {} + + ResolvedTrace resolve(ResolvedTrace trace) override { + // trace.addr is a virtual address in memory pointing to some code. + // Let's try to find from which loaded object it comes from. + // The loaded object can be yourself btw. + + Dl_info symbol_info; + int dladdr_result = 0; +#if defined(__GLIBC__) + link_map *link_map; + // We request the link map so we can get information about offsets + dladdr_result = + dladdr1(trace.addr, &symbol_info, reinterpret_cast(&link_map), + RTLD_DL_LINKMAP); +#else + // Android doesn't have dladdr1. Don't use the linker map. + dladdr_result = dladdr(trace.addr, &symbol_info); +#endif + if (!dladdr_result) { + return trace; // dat broken trace... + } + + // Now we get in symbol_info: + // .dli_fname: + // pathname of the shared object that contains the address. + // .dli_fbase: + // where the object is loaded in memory. + // .dli_sname: + // the name of the nearest symbol to trace.addr, we expect a + // function name. + // .dli_saddr: + // the exact address corresponding to .dli_sname. + // + // And in link_map: + // .l_addr: + // difference between the address in the ELF file and the address + // in memory + // l_name: + // absolute pathname where the object was found + + if (symbol_info.dli_sname) { + trace.object_function = demangle(symbol_info.dli_sname); + } + + if (!symbol_info.dli_fname) { + return trace; + } + + trace.object_filename = resolve_exec_path(symbol_info); + dwarf_fileobject &fobj = load_object_with_dwarf(symbol_info.dli_fname); + if (!fobj.dwarf_handle) { + return trace; // sad, we couldn't load the object :( + } + +#if defined(__GLIBC__) + // Convert the address to a module relative one by looking at + // the module's loading address in the link map + Dwarf_Addr address = reinterpret_cast(trace.addr) - + reinterpret_cast(link_map->l_addr); +#else + Dwarf_Addr address = reinterpret_cast(trace.addr); +#endif + + if (trace.object_function.empty()) { + symbol_cache_t::iterator it = fobj.symbol_cache.lower_bound(address); + + if (it != fobj.symbol_cache.end()) { + if (it->first != address) { + if (it != fobj.symbol_cache.begin()) { + --it; + } + } + trace.object_function = demangle(it->second.c_str()); + } + } + + // Get the Compilation Unit DIE for the address + Dwarf_Die die = find_die(fobj, address); + + if (!die) { + return trace; // this time we lost the game :/ + } + + // libdwarf doesn't give us direct access to its objects, it always + // allocates a copy for the caller. We keep that copy alive in a cache + // and we deallocate it later when it's no longer required. + die_cache_entry &die_object = get_die_cache(fobj, die); + if (die_object.isEmpty()) + return trace; // We have no line section for this DIE + + die_linemap_t::iterator it = die_object.line_section.lower_bound(address); + + if (it != die_object.line_section.end()) { + if (it->first != address) { + if (it == die_object.line_section.begin()) { + // If we are on the first item of the line section + // but the address does not match it means that + // the address is below the range of the DIE. Give up. + return trace; + } else { + --it; + } + } + } else { + return trace; // We didn't find the address. + } + + // Get the Dwarf_Line that the address points to and call libdwarf + // to get source file, line and column info. + Dwarf_Line line = die_object.line_buffer[it->second]; + Dwarf_Error error = DW_DLE_NE; + + char *filename; + if (dwarf_linesrc(line, &filename, &error) == DW_DLV_OK) { + trace.source.filename = std::string(filename); + dwarf_dealloc(fobj.dwarf_handle.get(), filename, DW_DLA_STRING); + } + + Dwarf_Unsigned number = 0; + if (dwarf_lineno(line, &number, &error) == DW_DLV_OK) { + trace.source.line = number; + } else { + trace.source.line = 0; + } + + if (dwarf_lineoff_b(line, &number, &error) == DW_DLV_OK) { + trace.source.col = number; + } else { + trace.source.col = 0; + } + + std::vector namespace_stack; + deep_first_search_by_pc(fobj, die, address, namespace_stack, + inliners_search_cb(trace, fobj, die)); + + dwarf_dealloc(fobj.dwarf_handle.get(), die, DW_DLA_DIE); + + return trace; + } + +public: + static int close_dwarf(Dwarf_Debug dwarf) { + return dwarf_finish(dwarf, NULL); + } + +private: + bool _dwarf_loaded; + + typedef details::handle > + dwarf_file_t; + + typedef details::handle > + dwarf_elf_t; + + typedef details::handle > + dwarf_handle_t; + + typedef std::map die_linemap_t; + + typedef std::map die_specmap_t; + + struct die_cache_entry { + die_specmap_t spec_section; + die_linemap_t line_section; + Dwarf_Line *line_buffer; + Dwarf_Signed line_count; + Dwarf_Line_Context line_context; + + inline bool isEmpty() { + return line_buffer == NULL || line_count == 0 || line_context == NULL || + line_section.empty(); + } + + die_cache_entry() : line_buffer(0), line_count(0), line_context(0) {} + + ~die_cache_entry() { + if (line_context) { + dwarf_srclines_dealloc_b(line_context); + } + } + }; + + typedef std::map die_cache_t; + + typedef std::map symbol_cache_t; + + struct dwarf_fileobject { + dwarf_file_t file_handle; + dwarf_elf_t elf_handle; + dwarf_handle_t dwarf_handle; + symbol_cache_t symbol_cache; + + // Die cache + die_cache_t die_cache; + die_cache_entry *current_cu; + }; + + typedef details::hashtable::type + fobj_dwarf_map_t; + fobj_dwarf_map_t _fobj_dwarf_map; + + static bool cstrings_eq(const char *a, const char *b) { + if (!a || !b) { + return false; + } + return strcmp(a, b) == 0; + } + + dwarf_fileobject &load_object_with_dwarf(const std::string &filename_object) { + + if (!_dwarf_loaded) { + // Set the ELF library operating version + // If that fails there's nothing we can do + _dwarf_loaded = elf_version(EV_CURRENT) != EV_NONE; + } + + fobj_dwarf_map_t::iterator it = _fobj_dwarf_map.find(filename_object); + if (it != _fobj_dwarf_map.end()) { + return it->second; + } + + // this new object is empty for now + dwarf_fileobject &r = _fobj_dwarf_map[filename_object]; + + dwarf_file_t file_handle; + file_handle.reset(open(filename_object.c_str(), O_RDONLY)); + if (file_handle.get() < 0) { + return r; + } + + // Try to get an ELF handle. We need to read the ELF sections + // because we want to see if there is a .gnu_debuglink section + // that points to a split debug file + dwarf_elf_t elf_handle; + elf_handle.reset(elf_begin(file_handle.get(), ELF_C_READ, NULL)); + if (!elf_handle) { + return r; + } + + const char *e_ident = elf_getident(elf_handle.get(), 0); + if (!e_ident) { + return r; + } + + // Get the number of sections + // We use the new APIs as elf_getshnum is deprecated + size_t shdrnum = 0; + if (elf_getshdrnum(elf_handle.get(), &shdrnum) == -1) { + return r; + } + + // Get the index to the string section + size_t shdrstrndx = 0; + if (elf_getshdrstrndx(elf_handle.get(), &shdrstrndx) == -1) { + return r; + } + + std::string debuglink; + // Iterate through the ELF sections to try to get a gnu_debuglink + // note and also to cache the symbol table. + // We go the preprocessor way to avoid having to create templated + // classes or using gelf (which might throw a compiler error if 64 bit + // is not supported +#define ELF_GET_DATA(ARCH) \ + Elf_Scn *elf_section = 0; \ + Elf_Data *elf_data = 0; \ + Elf##ARCH##_Shdr *section_header = 0; \ + Elf_Scn *symbol_section = 0; \ + size_t symbol_count = 0; \ + size_t symbol_strings = 0; \ + Elf##ARCH##_Sym *symbol = 0; \ + const char *section_name = 0; \ + \ + while ((elf_section = elf_nextscn(elf_handle.get(), elf_section)) != NULL) { \ + section_header = elf##ARCH##_getshdr(elf_section); \ + if (section_header == NULL) { \ + return r; \ + } \ + \ + if ((section_name = elf_strptr(elf_handle.get(), shdrstrndx, \ + section_header->sh_name)) == NULL) { \ + return r; \ + } \ + \ + if (cstrings_eq(section_name, ".gnu_debuglink")) { \ + elf_data = elf_getdata(elf_section, NULL); \ + if (elf_data && elf_data->d_size > 0) { \ + debuglink = \ + std::string(reinterpret_cast(elf_data->d_buf)); \ + } \ + } \ + \ + switch (section_header->sh_type) { \ + case SHT_SYMTAB: \ + symbol_section = elf_section; \ + symbol_count = section_header->sh_size / section_header->sh_entsize; \ + symbol_strings = section_header->sh_link; \ + break; \ + \ + /* We use .dynsyms as a last resort, we prefer .symtab */ \ + case SHT_DYNSYM: \ + if (!symbol_section) { \ + symbol_section = elf_section; \ + symbol_count = section_header->sh_size / section_header->sh_entsize; \ + symbol_strings = section_header->sh_link; \ + } \ + break; \ + } \ + } \ + \ + if (symbol_section && symbol_count && symbol_strings) { \ + elf_data = elf_getdata(symbol_section, NULL); \ + symbol = reinterpret_cast(elf_data->d_buf); \ + for (size_t i = 0; i < symbol_count; ++i) { \ + int type = ELF##ARCH##_ST_TYPE(symbol->st_info); \ + if (type == STT_FUNC && symbol->st_value > 0) { \ + r.symbol_cache[symbol->st_value] = std::string( \ + elf_strptr(elf_handle.get(), symbol_strings, symbol->st_name)); \ + } \ + ++symbol; \ + } \ + } + + if (e_ident[EI_CLASS] == ELFCLASS32) { + ELF_GET_DATA(32) + } else if (e_ident[EI_CLASS] == ELFCLASS64) { + // libelf might have been built without 64 bit support +#if __LIBELF64 + ELF_GET_DATA(64) +#endif + } + + if (!debuglink.empty()) { + // We have a debuglink section! Open an elf instance on that + // file instead. If we can't open the file, then return + // the elf handle we had already opened. + dwarf_file_t debuglink_file; + debuglink_file.reset(open(debuglink.c_str(), O_RDONLY)); + if (debuglink_file.get() > 0) { + dwarf_elf_t debuglink_elf; + debuglink_elf.reset(elf_begin(debuglink_file.get(), ELF_C_READ, NULL)); + + // If we have a valid elf handle, return the new elf handle + // and file handle and discard the original ones + if (debuglink_elf) { + elf_handle = move(debuglink_elf); + file_handle = move(debuglink_file); + } + } + } + + // Ok, we have a valid ELF handle, let's try to get debug symbols + Dwarf_Debug dwarf_debug; + Dwarf_Error error = DW_DLE_NE; + dwarf_handle_t dwarf_handle; + + int dwarf_result = dwarf_elf_init(elf_handle.get(), DW_DLC_READ, NULL, NULL, + &dwarf_debug, &error); + + // We don't do any special handling for DW_DLV_NO_ENTRY specially. + // If we get an error, or the file doesn't have debug information + // we just return. + if (dwarf_result != DW_DLV_OK) { + return r; + } + + dwarf_handle.reset(dwarf_debug); + + r.file_handle = move(file_handle); + r.elf_handle = move(elf_handle); + r.dwarf_handle = move(dwarf_handle); + + return r; + } + + die_cache_entry &get_die_cache(dwarf_fileobject &fobj, Dwarf_Die die) { + Dwarf_Error error = DW_DLE_NE; + + // Get the die offset, we use it as the cache key + Dwarf_Off die_offset; + if (dwarf_dieoffset(die, &die_offset, &error) != DW_DLV_OK) { + die_offset = 0; + } + + die_cache_t::iterator it = fobj.die_cache.find(die_offset); + + if (it != fobj.die_cache.end()) { + fobj.current_cu = &it->second; + return it->second; + } + + die_cache_entry &de = fobj.die_cache[die_offset]; + fobj.current_cu = &de; + + Dwarf_Addr line_addr; + Dwarf_Small table_count; + + // The addresses in the line section are not fully sorted (they might + // be sorted by block of code belonging to the same file), which makes + // it necessary to do so before searching is possible. + // + // As libdwarf allocates a copy of everything, let's get the contents + // of the line section and keep it around. We also create a map of + // program counter to line table indices so we can search by address + // and get the line buffer index. + // + // To make things more difficult, the same address can span more than + // one line, so we need to keep the index pointing to the first line + // by using insert instead of the map's [ operator. + + // Get the line context for the DIE + if (dwarf_srclines_b(die, 0, &table_count, &de.line_context, &error) == + DW_DLV_OK) { + // Get the source lines for this line context, to be deallocated + // later + if (dwarf_srclines_from_linecontext(de.line_context, &de.line_buffer, + &de.line_count, + &error) == DW_DLV_OK) { + + // Add all the addresses to our map + for (int i = 0; i < de.line_count; i++) { + if (dwarf_lineaddr(de.line_buffer[i], &line_addr, &error) != + DW_DLV_OK) { + line_addr = 0; + } + de.line_section.insert(std::pair(line_addr, i)); + } + } + } + + // For each CU, cache the function DIEs that contain the + // DW_AT_specification attribute. When building with -g3 the function + // DIEs are separated in declaration and specification, with the + // declaration containing only the name and parameters and the + // specification the low/high pc and other compiler attributes. + // + // We cache those specifications so we don't skip over the declarations, + // because they have no pc, and we can do namespace resolution for + // DWARF function names. + Dwarf_Debug dwarf = fobj.dwarf_handle.get(); + Dwarf_Die current_die = 0; + if (dwarf_child(die, ¤t_die, &error) == DW_DLV_OK) { + for (;;) { + Dwarf_Die sibling_die = 0; + + Dwarf_Half tag_value; + dwarf_tag(current_die, &tag_value, &error); + + if (tag_value == DW_TAG_subprogram || + tag_value == DW_TAG_inlined_subroutine) { + + Dwarf_Bool has_attr = 0; + if (dwarf_hasattr(current_die, DW_AT_specification, &has_attr, + &error) == DW_DLV_OK) { + if (has_attr) { + Dwarf_Attribute attr_mem; + if (dwarf_attr(current_die, DW_AT_specification, &attr_mem, + &error) == DW_DLV_OK) { + Dwarf_Off spec_offset = 0; + if (dwarf_formref(attr_mem, &spec_offset, &error) == + DW_DLV_OK) { + Dwarf_Off spec_die_offset; + if (dwarf_dieoffset(current_die, &spec_die_offset, &error) == + DW_DLV_OK) { + de.spec_section[spec_offset] = spec_die_offset; + } + } + } + dwarf_dealloc(dwarf, attr_mem, DW_DLA_ATTR); + } + } + } + + int result = dwarf_siblingof(dwarf, current_die, &sibling_die, &error); + if (result == DW_DLV_ERROR) { + break; + } else if (result == DW_DLV_NO_ENTRY) { + break; + } + + if (current_die != die) { + dwarf_dealloc(dwarf, current_die, DW_DLA_DIE); + current_die = 0; + } + + current_die = sibling_die; + } + } + return de; + } + + static Dwarf_Die get_referenced_die(Dwarf_Debug dwarf, Dwarf_Die die, + Dwarf_Half attr, bool global) { + Dwarf_Error error = DW_DLE_NE; + Dwarf_Attribute attr_mem; + + Dwarf_Die found_die = NULL; + if (dwarf_attr(die, attr, &attr_mem, &error) == DW_DLV_OK) { + Dwarf_Off offset; + int result = 0; + if (global) { + result = dwarf_global_formref(attr_mem, &offset, &error); + } else { + result = dwarf_formref(attr_mem, &offset, &error); + } + + if (result == DW_DLV_OK) { + if (dwarf_offdie(dwarf, offset, &found_die, &error) != DW_DLV_OK) { + found_die = NULL; + } + } + dwarf_dealloc(dwarf, attr_mem, DW_DLA_ATTR); + } + return found_die; + } + + static std::string get_referenced_die_name(Dwarf_Debug dwarf, Dwarf_Die die, + Dwarf_Half attr, bool global) { + Dwarf_Error error = DW_DLE_NE; + std::string value; + + Dwarf_Die found_die = get_referenced_die(dwarf, die, attr, global); + + if (found_die) { + char *name; + if (dwarf_diename(found_die, &name, &error) == DW_DLV_OK) { + if (name) { + value = std::string(name); + } + dwarf_dealloc(dwarf, name, DW_DLA_STRING); + } + dwarf_dealloc(dwarf, found_die, DW_DLA_DIE); + } + + return value; + } + + // Returns a spec DIE linked to the passed one. The caller should + // deallocate the DIE + static Dwarf_Die get_spec_die(dwarf_fileobject &fobj, Dwarf_Die die) { + Dwarf_Debug dwarf = fobj.dwarf_handle.get(); + Dwarf_Error error = DW_DLE_NE; + Dwarf_Off die_offset; + if (fobj.current_cu && + dwarf_die_CU_offset(die, &die_offset, &error) == DW_DLV_OK) { + die_specmap_t::iterator it = + fobj.current_cu->spec_section.find(die_offset); + + // If we have a DIE that completes the current one, check if + // that one has the pc we are looking for + if (it != fobj.current_cu->spec_section.end()) { + Dwarf_Die spec_die = 0; + if (dwarf_offdie(dwarf, it->second, &spec_die, &error) == DW_DLV_OK) { + return spec_die; + } + } + } + + // Maybe we have an abstract origin DIE with the function information? + return get_referenced_die(fobj.dwarf_handle.get(), die, + DW_AT_abstract_origin, true); + } + + static bool die_has_pc(dwarf_fileobject &fobj, Dwarf_Die die, Dwarf_Addr pc) { + Dwarf_Addr low_pc = 0, high_pc = 0; + Dwarf_Half high_pc_form = 0; + Dwarf_Form_Class return_class; + Dwarf_Error error = DW_DLE_NE; + Dwarf_Debug dwarf = fobj.dwarf_handle.get(); + bool has_lowpc = false; + bool has_highpc = false; + bool has_ranges = false; + + if (dwarf_lowpc(die, &low_pc, &error) == DW_DLV_OK) { + // If we have a low_pc check if there is a high pc. + // If we don't have a high pc this might mean we have a base + // address for the ranges list or just an address. + has_lowpc = true; + + if (dwarf_highpc_b(die, &high_pc, &high_pc_form, &return_class, &error) == + DW_DLV_OK) { + // We do have a high pc. In DWARF 4+ this is an offset from the + // low pc, but in earlier versions it's an absolute address. + + has_highpc = true; + // In DWARF 2/3 this would be a DW_FORM_CLASS_ADDRESS + if (return_class == DW_FORM_CLASS_CONSTANT) { + high_pc = low_pc + high_pc; + } + + // We have low and high pc, check if our address + // is in that range + return pc >= low_pc && pc < high_pc; + } + } else { + // Reset the low_pc, in case dwarf_lowpc failing set it to some + // undefined value. + low_pc = 0; + } + + // Check if DW_AT_ranges is present and search for the PC in the + // returned ranges list. We always add the low_pc, as it not set it will + // be 0, in case we had a DW_AT_low_pc and DW_AT_ranges pair + bool result = false; + + Dwarf_Attribute attr; + if (dwarf_attr(die, DW_AT_ranges, &attr, &error) == DW_DLV_OK) { + + Dwarf_Off offset; + if (dwarf_global_formref(attr, &offset, &error) == DW_DLV_OK) { + Dwarf_Ranges *ranges; + Dwarf_Signed ranges_count = 0; + Dwarf_Unsigned byte_count = 0; + + if (dwarf_get_ranges_a(dwarf, offset, die, &ranges, &ranges_count, + &byte_count, &error) == DW_DLV_OK) { + has_ranges = ranges_count != 0; + for (int i = 0; i < ranges_count; i++) { + if (ranges[i].dwr_addr1 != 0 && + pc >= ranges[i].dwr_addr1 + low_pc && + pc < ranges[i].dwr_addr2 + low_pc) { + result = true; + break; + } + } + dwarf_ranges_dealloc(dwarf, ranges, ranges_count); + } + } + } + + // Last attempt. We might have a single address set as low_pc. + if (!result && low_pc != 0 && pc == low_pc) { + result = true; + } + + // If we don't have lowpc, highpc and ranges maybe this DIE is a + // declaration that relies on a DW_AT_specification DIE that happens + // later. Use the specification cache we filled when we loaded this CU. + if (!result && (!has_lowpc && !has_highpc && !has_ranges)) { + Dwarf_Die spec_die = get_spec_die(fobj, die); + if (spec_die) { + result = die_has_pc(fobj, spec_die, pc); + dwarf_dealloc(dwarf, spec_die, DW_DLA_DIE); + } + } + + return result; + } + + static void get_type(Dwarf_Debug dwarf, Dwarf_Die die, std::string &type) { + Dwarf_Error error = DW_DLE_NE; + + Dwarf_Die child = 0; + if (dwarf_child(die, &child, &error) == DW_DLV_OK) { + get_type(dwarf, child, type); + } + + if (child) { + type.insert(0, "::"); + dwarf_dealloc(dwarf, child, DW_DLA_DIE); + } + + char *name; + if (dwarf_diename(die, &name, &error) == DW_DLV_OK) { + type.insert(0, std::string(name)); + dwarf_dealloc(dwarf, name, DW_DLA_STRING); + } else { + type.insert(0, ""); + } + } + + static std::string get_type_by_signature(Dwarf_Debug dwarf, Dwarf_Die die) { + Dwarf_Error error = DW_DLE_NE; + + Dwarf_Sig8 signature; + Dwarf_Bool has_attr = 0; + if (dwarf_hasattr(die, DW_AT_signature, &has_attr, &error) == DW_DLV_OK) { + if (has_attr) { + Dwarf_Attribute attr_mem; + if (dwarf_attr(die, DW_AT_signature, &attr_mem, &error) == DW_DLV_OK) { + if (dwarf_formsig8(attr_mem, &signature, &error) != DW_DLV_OK) { + return std::string(""); + } + } + dwarf_dealloc(dwarf, attr_mem, DW_DLA_ATTR); + } + } + + Dwarf_Unsigned next_cu_header; + Dwarf_Sig8 tu_signature; + std::string result; + bool found = false; + + while (dwarf_next_cu_header_d(dwarf, 0, 0, 0, 0, 0, 0, 0, &tu_signature, 0, + &next_cu_header, 0, &error) == DW_DLV_OK) { + + if (strncmp(signature.signature, tu_signature.signature, 8) == 0) { + Dwarf_Die type_cu_die = 0; + if (dwarf_siblingof_b(dwarf, 0, 0, &type_cu_die, &error) == DW_DLV_OK) { + Dwarf_Die child_die = 0; + if (dwarf_child(type_cu_die, &child_die, &error) == DW_DLV_OK) { + get_type(dwarf, child_die, result); + found = !result.empty(); + dwarf_dealloc(dwarf, child_die, DW_DLA_DIE); + } + dwarf_dealloc(dwarf, type_cu_die, DW_DLA_DIE); + } + } + } + + if (found) { + while (dwarf_next_cu_header_d(dwarf, 0, 0, 0, 0, 0, 0, 0, 0, 0, + &next_cu_header, 0, &error) == DW_DLV_OK) { + // Reset the cu header state. Unfortunately, libdwarf's + // next_cu_header API keeps its own iterator per Dwarf_Debug + // that can't be reset. We need to keep fetching elements until + // the end. + } + } else { + // If we couldn't resolve the type just print out the signature + std::ostringstream string_stream; + string_stream << "<0x" << std::hex << std::setfill('0'); + for (int i = 0; i < 8; ++i) { + string_stream << std::setw(2) << std::hex + << (int)(unsigned char)(signature.signature[i]); + } + string_stream << ">"; + result = string_stream.str(); + } + return result; + } + + struct type_context_t { + bool is_const; + bool is_typedef; + bool has_type; + bool has_name; + std::string text; + + type_context_t() + : is_const(false), is_typedef(false), has_type(false), has_name(false) { + } + }; + + // Types are resolved from right to left: we get the variable name first + // and then all specifiers (like const or pointer) in a chain of DW_AT_type + // DIEs. Call this function recursively until we get a complete type + // string. + static void set_parameter_string(dwarf_fileobject &fobj, Dwarf_Die die, + type_context_t &context) { + char *name; + Dwarf_Error error = DW_DLE_NE; + + // typedefs contain also the base type, so we skip it and only + // print the typedef name + if (!context.is_typedef) { + if (dwarf_diename(die, &name, &error) == DW_DLV_OK) { + if (!context.text.empty()) { + context.text.insert(0, " "); + } + context.text.insert(0, std::string(name)); + dwarf_dealloc(fobj.dwarf_handle.get(), name, DW_DLA_STRING); + } + } else { + context.is_typedef = false; + context.has_type = true; + if (context.is_const) { + context.text.insert(0, "const "); + context.is_const = false; + } + } + + bool next_type_is_const = false; + bool is_keyword = true; + + Dwarf_Half tag = 0; + Dwarf_Bool has_attr = 0; + if (dwarf_tag(die, &tag, &error) == DW_DLV_OK) { + switch (tag) { + case DW_TAG_structure_type: + case DW_TAG_union_type: + case DW_TAG_class_type: + case DW_TAG_enumeration_type: + context.has_type = true; + if (dwarf_hasattr(die, DW_AT_signature, &has_attr, &error) == + DW_DLV_OK) { + // If we have a signature it means the type is defined + // in .debug_types, so we need to load the DIE pointed + // at by the signature and resolve it + if (has_attr) { + std::string type = + get_type_by_signature(fobj.dwarf_handle.get(), die); + if (context.is_const) + type.insert(0, "const "); + + if (!context.text.empty()) + context.text.insert(0, " "); + context.text.insert(0, type); + } + + // Treat enums like typedefs, and skip printing its + // base type + context.is_typedef = (tag == DW_TAG_enumeration_type); + } + break; + case DW_TAG_const_type: + next_type_is_const = true; + break; + case DW_TAG_pointer_type: + context.text.insert(0, "*"); + break; + case DW_TAG_reference_type: + context.text.insert(0, "&"); + break; + case DW_TAG_restrict_type: + context.text.insert(0, "restrict "); + break; + case DW_TAG_rvalue_reference_type: + context.text.insert(0, "&&"); + break; + case DW_TAG_volatile_type: + context.text.insert(0, "volatile "); + break; + case DW_TAG_typedef: + // Propagate the const-ness to the next type + // as typedefs are linked to its base type + next_type_is_const = context.is_const; + context.is_typedef = true; + context.has_type = true; + break; + case DW_TAG_base_type: + context.has_type = true; + break; + case DW_TAG_formal_parameter: + context.has_name = true; + break; + default: + is_keyword = false; + break; + } + } + + if (!is_keyword && context.is_const) { + context.text.insert(0, "const "); + } + + context.is_const = next_type_is_const; + + Dwarf_Die ref = + get_referenced_die(fobj.dwarf_handle.get(), die, DW_AT_type, true); + if (ref) { + set_parameter_string(fobj, ref, context); + dwarf_dealloc(fobj.dwarf_handle.get(), ref, DW_DLA_DIE); + } + + if (!context.has_type && context.has_name) { + context.text.insert(0, "void "); + context.has_type = true; + } + } + + // Resolve the function return type and parameters + static void set_function_parameters(std::string &function_name, + std::vector &ns, + dwarf_fileobject &fobj, Dwarf_Die die) { + Dwarf_Debug dwarf = fobj.dwarf_handle.get(); + Dwarf_Error error = DW_DLE_NE; + Dwarf_Die current_die = 0; + std::string parameters; + bool has_spec = true; + // Check if we have a spec DIE. If we do we use it as it contains + // more information, like parameter names. + Dwarf_Die spec_die = get_spec_die(fobj, die); + if (!spec_die) { + has_spec = false; + spec_die = die; + } + + std::vector::const_iterator it = ns.begin(); + std::string ns_name; + for (it = ns.begin(); it < ns.end(); ++it) { + ns_name.append(*it).append("::"); + } + + if (!ns_name.empty()) { + function_name.insert(0, ns_name); + } + + // See if we have a function return type. It can be either on the + // current die or in its spec one (usually true for inlined functions) + std::string return_type = + get_referenced_die_name(dwarf, die, DW_AT_type, true); + if (return_type.empty()) { + return_type = get_referenced_die_name(dwarf, spec_die, DW_AT_type, true); + } + if (!return_type.empty()) { + return_type.append(" "); + function_name.insert(0, return_type); + } + + if (dwarf_child(spec_die, ¤t_die, &error) == DW_DLV_OK) { + for (;;) { + Dwarf_Die sibling_die = 0; + + Dwarf_Half tag_value; + dwarf_tag(current_die, &tag_value, &error); + + if (tag_value == DW_TAG_formal_parameter) { + // Ignore artificial (ie, compiler generated) parameters + bool is_artificial = false; + Dwarf_Attribute attr_mem; + if (dwarf_attr(current_die, DW_AT_artificial, &attr_mem, &error) == + DW_DLV_OK) { + Dwarf_Bool flag = 0; + if (dwarf_formflag(attr_mem, &flag, &error) == DW_DLV_OK) { + is_artificial = flag != 0; + } + dwarf_dealloc(dwarf, attr_mem, DW_DLA_ATTR); + } + + if (!is_artificial) { + type_context_t context; + set_parameter_string(fobj, current_die, context); + + if (parameters.empty()) { + parameters.append("("); + } else { + parameters.append(", "); + } + parameters.append(context.text); + } + } + + int result = dwarf_siblingof(dwarf, current_die, &sibling_die, &error); + if (result == DW_DLV_ERROR) { + break; + } else if (result == DW_DLV_NO_ENTRY) { + break; + } + + if (current_die != die) { + dwarf_dealloc(dwarf, current_die, DW_DLA_DIE); + current_die = 0; + } + + current_die = sibling_die; + } + } + if (parameters.empty()) + parameters = "("; + parameters.append(")"); + + // If we got a spec DIE we need to deallocate it + if (has_spec) + dwarf_dealloc(dwarf, spec_die, DW_DLA_DIE); + + function_name.append(parameters); + } + + // defined here because in C++98, template function cannot take locally + // defined types... grrr. + struct inliners_search_cb { + void operator()(Dwarf_Die die, std::vector &ns) { + Dwarf_Error error = DW_DLE_NE; + Dwarf_Half tag_value; + Dwarf_Attribute attr_mem; + Dwarf_Debug dwarf = fobj.dwarf_handle.get(); + + dwarf_tag(die, &tag_value, &error); + + switch (tag_value) { + char *name; + case DW_TAG_subprogram: + if (!trace.source.function.empty()) + break; + if (dwarf_diename(die, &name, &error) == DW_DLV_OK) { + trace.source.function = std::string(name); + dwarf_dealloc(dwarf, name, DW_DLA_STRING); + } else { + // We don't have a function name in this DIE. + // Check if there is a referenced non-defining + // declaration. + trace.source.function = + get_referenced_die_name(dwarf, die, DW_AT_abstract_origin, true); + if (trace.source.function.empty()) { + trace.source.function = + get_referenced_die_name(dwarf, die, DW_AT_specification, true); + } + } + + // Append the function parameters, if available + set_function_parameters(trace.source.function, ns, fobj, die); + + // If the object function name is empty, it's possible that + // there is no dynamic symbol table (maybe the executable + // was stripped or not built with -rdynamic). See if we have + // a DWARF linkage name to use instead. We try both + // linkage_name and MIPS_linkage_name because the MIPS tag + // was the unofficial one until it was adopted in DWARF4. + // Old gcc versions generate MIPS_linkage_name + if (trace.object_function.empty()) { + details::demangler demangler; + + if (dwarf_attr(die, DW_AT_linkage_name, &attr_mem, &error) != + DW_DLV_OK) { + if (dwarf_attr(die, DW_AT_MIPS_linkage_name, &attr_mem, &error) != + DW_DLV_OK) { + break; + } + } + + char *linkage; + if (dwarf_formstring(attr_mem, &linkage, &error) == DW_DLV_OK) { + trace.object_function = demangler.demangle(linkage); + dwarf_dealloc(dwarf, linkage, DW_DLA_STRING); + } + dwarf_dealloc(dwarf, attr_mem, DW_DLA_ATTR); + } + break; + + case DW_TAG_inlined_subroutine: + ResolvedTrace::SourceLoc sloc; + + if (dwarf_diename(die, &name, &error) == DW_DLV_OK) { + sloc.function = std::string(name); + dwarf_dealloc(dwarf, name, DW_DLA_STRING); + } else { + // We don't have a name for this inlined DIE, it could + // be that there is an abstract origin instead. + // Get the DW_AT_abstract_origin value, which is a + // reference to the source DIE and try to get its name + sloc.function = + get_referenced_die_name(dwarf, die, DW_AT_abstract_origin, true); + } + + set_function_parameters(sloc.function, ns, fobj, die); + + std::string file = die_call_file(dwarf, die, cu_die); + if (!file.empty()) + sloc.filename = file; + + Dwarf_Unsigned number = 0; + if (dwarf_attr(die, DW_AT_call_line, &attr_mem, &error) == DW_DLV_OK) { + if (dwarf_formudata(attr_mem, &number, &error) == DW_DLV_OK) { + sloc.line = number; + } + dwarf_dealloc(dwarf, attr_mem, DW_DLA_ATTR); + } + + if (dwarf_attr(die, DW_AT_call_column, &attr_mem, &error) == + DW_DLV_OK) { + if (dwarf_formudata(attr_mem, &number, &error) == DW_DLV_OK) { + sloc.col = number; + } + dwarf_dealloc(dwarf, attr_mem, DW_DLA_ATTR); + } + + trace.inliners.push_back(sloc); + break; + }; + } + ResolvedTrace &trace; + dwarf_fileobject &fobj; + Dwarf_Die cu_die; + inliners_search_cb(ResolvedTrace &t, dwarf_fileobject &f, Dwarf_Die c) + : trace(t), fobj(f), cu_die(c) {} + }; + + static Dwarf_Die find_fundie_by_pc(dwarf_fileobject &fobj, + Dwarf_Die parent_die, Dwarf_Addr pc, + Dwarf_Die result) { + Dwarf_Die current_die = 0; + Dwarf_Error error = DW_DLE_NE; + Dwarf_Debug dwarf = fobj.dwarf_handle.get(); + + if (dwarf_child(parent_die, ¤t_die, &error) != DW_DLV_OK) { + return NULL; + } + + for (;;) { + Dwarf_Die sibling_die = 0; + Dwarf_Half tag_value; + dwarf_tag(current_die, &tag_value, &error); + + switch (tag_value) { + case DW_TAG_subprogram: + case DW_TAG_inlined_subroutine: + if (die_has_pc(fobj, current_die, pc)) { + return current_die; + } + }; + bool declaration = false; + Dwarf_Attribute attr_mem; + if (dwarf_attr(current_die, DW_AT_declaration, &attr_mem, &error) == + DW_DLV_OK) { + Dwarf_Bool flag = 0; + if (dwarf_formflag(attr_mem, &flag, &error) == DW_DLV_OK) { + declaration = flag != 0; + } + dwarf_dealloc(dwarf, attr_mem, DW_DLA_ATTR); + } + + if (!declaration) { + // let's be curious and look deeper in the tree, functions are + // not necessarily at the first level, but might be nested + // inside a namespace, structure, a function, an inlined + // function etc. + Dwarf_Die die_mem = 0; + Dwarf_Die indie = find_fundie_by_pc(fobj, current_die, pc, die_mem); + if (indie) { + result = die_mem; + return result; + } + } + + int res = dwarf_siblingof(dwarf, current_die, &sibling_die, &error); + if (res == DW_DLV_ERROR) { + return NULL; + } else if (res == DW_DLV_NO_ENTRY) { + break; + } + + if (current_die != parent_die) { + dwarf_dealloc(dwarf, current_die, DW_DLA_DIE); + current_die = 0; + } + + current_die = sibling_die; + } + return NULL; + } + + template + static bool deep_first_search_by_pc(dwarf_fileobject &fobj, + Dwarf_Die parent_die, Dwarf_Addr pc, + std::vector &ns, CB cb) { + Dwarf_Die current_die = 0; + Dwarf_Debug dwarf = fobj.dwarf_handle.get(); + Dwarf_Error error = DW_DLE_NE; + + if (dwarf_child(parent_die, ¤t_die, &error) != DW_DLV_OK) { + return false; + } + + bool branch_has_pc = false; + bool has_namespace = false; + for (;;) { + Dwarf_Die sibling_die = 0; + + Dwarf_Half tag; + if (dwarf_tag(current_die, &tag, &error) == DW_DLV_OK) { + if (tag == DW_TAG_namespace || tag == DW_TAG_class_type) { + char *ns_name = NULL; + if (dwarf_diename(current_die, &ns_name, &error) == DW_DLV_OK) { + if (ns_name) { + ns.push_back(std::string(ns_name)); + } else { + ns.push_back(""); + } + dwarf_dealloc(dwarf, ns_name, DW_DLA_STRING); + } else { + ns.push_back(""); + } + has_namespace = true; + } + } + + bool declaration = false; + Dwarf_Attribute attr_mem; + if (tag != DW_TAG_class_type && + dwarf_attr(current_die, DW_AT_declaration, &attr_mem, &error) == + DW_DLV_OK) { + Dwarf_Bool flag = 0; + if (dwarf_formflag(attr_mem, &flag, &error) == DW_DLV_OK) { + declaration = flag != 0; + } + dwarf_dealloc(dwarf, attr_mem, DW_DLA_ATTR); + } + + if (!declaration) { + // let's be curious and look deeper in the tree, function are + // not necessarily at the first level, but might be nested + // inside a namespace, structure, a function, an inlined + // function etc. + branch_has_pc = deep_first_search_by_pc(fobj, current_die, pc, ns, cb); + } + + if (!branch_has_pc) { + branch_has_pc = die_has_pc(fobj, current_die, pc); + } + + if (branch_has_pc) { + cb(current_die, ns); + } + + int result = dwarf_siblingof(dwarf, current_die, &sibling_die, &error); + if (result == DW_DLV_ERROR) { + return false; + } else if (result == DW_DLV_NO_ENTRY) { + break; + } + + if (current_die != parent_die) { + dwarf_dealloc(dwarf, current_die, DW_DLA_DIE); + current_die = 0; + } + + if (has_namespace) { + has_namespace = false; + ns.pop_back(); + } + current_die = sibling_die; + } + + if (has_namespace) { + ns.pop_back(); + } + return branch_has_pc; + } + + static std::string die_call_file(Dwarf_Debug dwarf, Dwarf_Die die, + Dwarf_Die cu_die) { + Dwarf_Attribute attr_mem; + Dwarf_Error error = DW_DLE_NE; + Dwarf_Unsigned file_index; + + std::string file; + + if (dwarf_attr(die, DW_AT_call_file, &attr_mem, &error) == DW_DLV_OK) { + if (dwarf_formudata(attr_mem, &file_index, &error) != DW_DLV_OK) { + file_index = 0; + } + dwarf_dealloc(dwarf, attr_mem, DW_DLA_ATTR); + + if (file_index == 0) { + return file; + } + + char **srcfiles = 0; + Dwarf_Signed file_count = 0; + if (dwarf_srcfiles(cu_die, &srcfiles, &file_count, &error) == DW_DLV_OK) { + if (file_count > 0 && file_index <= static_cast(file_count)) { + file = std::string(srcfiles[file_index - 1]); + } + + // Deallocate all strings! + for (int i = 0; i < file_count; ++i) { + dwarf_dealloc(dwarf, srcfiles[i], DW_DLA_STRING); + } + dwarf_dealloc(dwarf, srcfiles, DW_DLA_LIST); + } + } + return file; + } + + Dwarf_Die find_die(dwarf_fileobject &fobj, Dwarf_Addr addr) { + // Let's get to work! First see if we have a debug_aranges section so + // we can speed up the search + + Dwarf_Debug dwarf = fobj.dwarf_handle.get(); + Dwarf_Error error = DW_DLE_NE; + Dwarf_Arange *aranges; + Dwarf_Signed arange_count; + + Dwarf_Die returnDie; + bool found = false; + if (dwarf_get_aranges(dwarf, &aranges, &arange_count, &error) != + DW_DLV_OK) { + aranges = NULL; + } + + if (aranges) { + // We have aranges. Get the one where our address is. + Dwarf_Arange arange; + if (dwarf_get_arange(aranges, arange_count, addr, &arange, &error) == + DW_DLV_OK) { + + // We found our address. Get the compilation-unit DIE offset + // represented by the given address range. + Dwarf_Off cu_die_offset; + if (dwarf_get_cu_die_offset(arange, &cu_die_offset, &error) == + DW_DLV_OK) { + // Get the DIE at the offset returned by the aranges search. + // We set is_info to 1 to specify that the offset is from + // the .debug_info section (and not .debug_types) + int dwarf_result = + dwarf_offdie_b(dwarf, cu_die_offset, 1, &returnDie, &error); + + found = dwarf_result == DW_DLV_OK; + } + dwarf_dealloc(dwarf, arange, DW_DLA_ARANGE); + } + } + + if (found) + return returnDie; // The caller is responsible for freeing the die + + // The search for aranges failed. Try to find our address by scanning + // all compilation units. + Dwarf_Unsigned next_cu_header; + Dwarf_Half tag = 0; + returnDie = 0; + + while (!found && + dwarf_next_cu_header_d(dwarf, 1, 0, 0, 0, 0, 0, 0, 0, 0, + &next_cu_header, 0, &error) == DW_DLV_OK) { + + if (returnDie) + dwarf_dealloc(dwarf, returnDie, DW_DLA_DIE); + + if (dwarf_siblingof(dwarf, 0, &returnDie, &error) == DW_DLV_OK) { + if ((dwarf_tag(returnDie, &tag, &error) == DW_DLV_OK) && + tag == DW_TAG_compile_unit) { + if (die_has_pc(fobj, returnDie, addr)) { + found = true; + } + } + } + } + + if (found) { + while (dwarf_next_cu_header_d(dwarf, 1, 0, 0, 0, 0, 0, 0, 0, 0, + &next_cu_header, 0, &error) == DW_DLV_OK) { + // Reset the cu header state. Libdwarf's next_cu_header API + // keeps its own iterator per Dwarf_Debug that can't be reset. + // We need to keep fetching elements until the end. + } + } + + if (found) + return returnDie; + + // We couldn't find any compilation units with ranges or a high/low pc. + // Try again by looking at all DIEs in all compilation units. + Dwarf_Die cudie; + while (dwarf_next_cu_header_d(dwarf, 1, 0, 0, 0, 0, 0, 0, 0, 0, + &next_cu_header, 0, &error) == DW_DLV_OK) { + if (dwarf_siblingof(dwarf, 0, &cudie, &error) == DW_DLV_OK) { + Dwarf_Die die_mem = 0; + Dwarf_Die resultDie = find_fundie_by_pc(fobj, cudie, addr, die_mem); + + if (resultDie) { + found = true; + break; + } + } + } + + if (found) { + while (dwarf_next_cu_header_d(dwarf, 1, 0, 0, 0, 0, 0, 0, 0, 0, + &next_cu_header, 0, &error) == DW_DLV_OK) { + // Reset the cu header state. Libdwarf's next_cu_header API + // keeps its own iterator per Dwarf_Debug that can't be reset. + // We need to keep fetching elements until the end. + } + } + + if (found) + return cudie; + + // We failed. + return NULL; + } +}; +#endif // BACKWARD_HAS_DWARF == 1 + +template <> +class TraceResolverImpl + : public TraceResolverLinuxImpl {}; + +#endif // BACKWARD_SYSTEM_LINUX + +#ifdef BACKWARD_SYSTEM_DARWIN + +template class TraceResolverDarwinImpl; + +template <> +class TraceResolverDarwinImpl + : public TraceResolverImplBase { +public: + void load_addresses(void *const*addresses, int address_count) override { + if (address_count == 0) { + return; + } + _symbols.reset(backtrace_symbols(addresses, address_count)); + } + + ResolvedTrace resolve(ResolvedTrace trace) override { + // parse: + // + + char *filename = _symbols[trace.idx]; + + // skip " " + while (*filename && *filename != ' ') + filename++; + while (*filename == ' ') + filename++; + + // find start of from end ( may contain a space) + char *p = filename + strlen(filename) - 1; + // skip to start of " + " + while (p > filename && *p != ' ') + p--; + while (p > filename && *p == ' ') + p--; + while (p > filename && *p != ' ') + p--; + while (p > filename && *p == ' ') + p--; + char *funcname_end = p + 1; + + // skip to start of "" + while (p > filename && *p != ' ') + p--; + char *funcname = p + 1; + + // skip to start of " " + while (p > filename && *p == ' ') + p--; + while (p > filename && *p != ' ') + p--; + while (p > filename && *p == ' ') + p--; + + // skip "", handling the case where it contains a + char *filename_end = p + 1; + if (p == filename) { + // something went wrong, give up + filename_end = filename + strlen(filename); + funcname = filename_end; + } + trace.object_filename.assign( + filename, filename_end); // ok even if filename_end is the ending \0 + // (then we assign entire string) + + if (*funcname) { // if it's not end of string + *funcname_end = '\0'; + + trace.object_function = this->demangle(funcname); + trace.object_function += " "; + trace.object_function += (funcname_end + 1); + trace.source.function = trace.object_function; // we cannot do better. + } + return trace; + } + +private: + details::handle _symbols; +}; + +template <> +class TraceResolverImpl + : public TraceResolverDarwinImpl {}; + +#endif // BACKWARD_SYSTEM_DARWIN + +#ifdef BACKWARD_SYSTEM_WINDOWS + +// Load all symbol info +// Based on: +// https://stackoverflow.com/questions/6205981/windows-c-stack-trace-from-a-running-app/28276227#28276227 + +struct module_data { + std::string image_name; + std::string module_name; + void *base_address; + DWORD load_size; +}; + +class get_mod_info { + HANDLE process; + static const int buffer_length = 4096; + +public: + get_mod_info(HANDLE h) : process(h) {} + + module_data operator()(HMODULE module) { + module_data ret; + char temp[buffer_length]; + MODULEINFO mi; + + GetModuleInformation(process, module, &mi, sizeof(mi)); + ret.base_address = mi.lpBaseOfDll; + ret.load_size = mi.SizeOfImage; + + GetModuleFileNameExA(process, module, temp, sizeof(temp)); + ret.image_name = temp; + GetModuleBaseNameA(process, module, temp, sizeof(temp)); + ret.module_name = temp; + std::vector img(ret.image_name.begin(), ret.image_name.end()); + std::vector mod(ret.module_name.begin(), ret.module_name.end()); + SymLoadModule64(process, 0, &img[0], &mod[0], (DWORD64)ret.base_address, + ret.load_size); + return ret; + } +}; + +template <> class TraceResolverImpl + : public TraceResolverImplBase { +public: + TraceResolverImpl() { + + HANDLE process = GetCurrentProcess(); + + std::vector modules; + DWORD cbNeeded; + std::vector module_handles(1); + SymInitialize(process, NULL, false); + DWORD symOptions = SymGetOptions(); + symOptions |= SYMOPT_LOAD_LINES | SYMOPT_UNDNAME; + SymSetOptions(symOptions); + EnumProcessModules(process, &module_handles[0], + module_handles.size() * sizeof(HMODULE), &cbNeeded); + module_handles.resize(cbNeeded / sizeof(HMODULE)); + EnumProcessModules(process, &module_handles[0], + module_handles.size() * sizeof(HMODULE), &cbNeeded); + std::transform(module_handles.begin(), module_handles.end(), + std::back_inserter(modules), get_mod_info(process)); + void *base = modules[0].base_address; + IMAGE_NT_HEADERS *h = ImageNtHeader(base); + image_type = h->FileHeader.Machine; + } + + static const int max_sym_len = 255; + struct symbol_t { + SYMBOL_INFO sym; + char buffer[max_sym_len]; + } sym; + + DWORD64 displacement; + + ResolvedTrace resolve(ResolvedTrace t) override { + HANDLE process = GetCurrentProcess(); + + char name[256]; + + memset(&sym, 0, sizeof(sym)); + sym.sym.SizeOfStruct = sizeof(SYMBOL_INFO); + sym.sym.MaxNameLen = max_sym_len; + + if (!SymFromAddr(process, (ULONG64)t.addr, &displacement, &sym.sym)) { + // TODO: error handling everywhere + char* lpMsgBuf; + DWORD dw = GetLastError(); + + if (FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER | + FORMAT_MESSAGE_FROM_SYSTEM | + FORMAT_MESSAGE_IGNORE_INSERTS, + NULL, dw, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), + (char*)&lpMsgBuf, 0, NULL)) { + std::fprintf(stderr, "%s\n", lpMsgBuf); + LocalFree(lpMsgBuf); + } + + // abort(); + } + UnDecorateSymbolName(sym.sym.Name, (PSTR)name, 256, UNDNAME_COMPLETE); + + DWORD offset = 0; + IMAGEHLP_LINE line; + if (SymGetLineFromAddr(process, (ULONG64)t.addr, &offset, &line)) { + t.object_filename = line.FileName; + t.source.filename = line.FileName; + t.source.line = line.LineNumber; + t.source.col = offset; + } + + t.source.function = name; + t.object_filename = ""; + t.object_function = name; + + return t; + } + + DWORD machine_type() const { return image_type; } + +private: + DWORD image_type; +}; + +#endif + +class TraceResolver : public TraceResolverImpl {}; + +/*************** CODE SNIPPET ***************/ + +class SourceFile { +public: + typedef std::vector > lines_t; + + SourceFile() {} + SourceFile(const std::string &path) { + // 1. If BACKWARD_CXX_SOURCE_PREFIXES is set then assume it contains + // a colon-separated list of path prefixes. Try prepending each + // to the given path until a valid file is found. + const std::vector &prefixes = get_paths_from_env_variable(); + for (size_t i = 0; i < prefixes.size(); ++i) { + // Double slashes (//) should not be a problem. + std::string new_path = prefixes[i] + '/' + path; + _file.reset(new std::ifstream(new_path.c_str())); + if (is_open()) + break; + } + // 2. If no valid file found then fallback to opening the path as-is. + if (!_file || !is_open()) { + _file.reset(new std::ifstream(path.c_str())); + } + } + bool is_open() const { return _file->is_open(); } + + lines_t &get_lines(unsigned line_start, unsigned line_count, lines_t &lines) { + using namespace std; + // This function make uses of the dumbest algo ever: + // 1) seek(0) + // 2) read lines one by one and discard until line_start + // 3) read line one by one until line_start + line_count + // + // If you are getting snippets many time from the same file, it is + // somewhat a waste of CPU, feel free to benchmark and propose a + // better solution ;) + + _file->clear(); + _file->seekg(0); + string line; + unsigned line_idx; + + for (line_idx = 1; line_idx < line_start; ++line_idx) { + std::getline(*_file, line); + if (!*_file) { + return lines; + } + } + + // think of it like a lambda in C++98 ;) + // but look, I will reuse it two times! + // What a good boy am I. + struct isspace { + bool operator()(char c) { return std::isspace(c); } + }; + + bool started = false; + for (; line_idx < line_start + line_count; ++line_idx) { + getline(*_file, line); + if (!*_file) { + return lines; + } + if (!started) { + if (std::find_if(line.begin(), line.end(), not_isspace()) == line.end()) + continue; + started = true; + } + lines.push_back(make_pair(line_idx, line)); + } + + lines.erase( + std::find_if(lines.rbegin(), lines.rend(), not_isempty()).base(), + lines.end()); + return lines; + } + + lines_t get_lines(unsigned line_start, unsigned line_count) { + lines_t lines; + return get_lines(line_start, line_count, lines); + } + + // there is no find_if_not in C++98, lets do something crappy to + // workaround. + struct not_isspace { + bool operator()(char c) { return !std::isspace(c); } + }; + // and define this one here because C++98 is not happy with local defined + // struct passed to template functions, fuuuu. + struct not_isempty { + bool operator()(const lines_t::value_type &p) { + return !(std::find_if(p.second.begin(), p.second.end(), not_isspace()) == + p.second.end()); + } + }; + + void swap(SourceFile &b) { _file.swap(b._file); } + +#ifdef BACKWARD_ATLEAST_CXX11 + SourceFile(SourceFile &&from) : _file(nullptr) { swap(from); } + SourceFile &operator=(SourceFile &&from) { + swap(from); + return *this; + } +#else + explicit SourceFile(const SourceFile &from) { + // some sort of poor man's move semantic. + swap(const_cast(from)); + } + SourceFile &operator=(const SourceFile &from) { + // some sort of poor man's move semantic. + swap(const_cast(from)); + return *this; + } +#endif + +private: + details::handle > + _file; + + std::vector get_paths_from_env_variable_impl() { + std::vector paths; + const char *prefixes_str = std::getenv("BACKWARD_CXX_SOURCE_PREFIXES"); + if (prefixes_str && prefixes_str[0]) { + paths = details::split_source_prefixes(prefixes_str); + } + return paths; + } + + const std::vector &get_paths_from_env_variable() { + static std::vector paths = get_paths_from_env_variable_impl(); + return paths; + } + +#ifdef BACKWARD_ATLEAST_CXX11 + SourceFile(const SourceFile &) = delete; + SourceFile &operator=(const SourceFile &) = delete; +#endif +}; + +class SnippetFactory { +public: + typedef SourceFile::lines_t lines_t; + + lines_t get_snippet(const std::string &filename, unsigned line_start, + unsigned context_size) { + + SourceFile &src_file = get_src_file(filename); + unsigned start = line_start - context_size / 2; + return src_file.get_lines(start, context_size); + } + + lines_t get_combined_snippet(const std::string &filename_a, unsigned line_a, + const std::string &filename_b, unsigned line_b, + unsigned context_size) { + SourceFile &src_file_a = get_src_file(filename_a); + SourceFile &src_file_b = get_src_file(filename_b); + + lines_t lines = + src_file_a.get_lines(line_a - context_size / 4, context_size / 2); + src_file_b.get_lines(line_b - context_size / 4, context_size / 2, lines); + return lines; + } + + lines_t get_coalesced_snippet(const std::string &filename, unsigned line_a, + unsigned line_b, unsigned context_size) { + SourceFile &src_file = get_src_file(filename); + + using std::max; + using std::min; + unsigned a = min(line_a, line_b); + unsigned b = max(line_a, line_b); + + if ((b - a) < (context_size / 3)) { + return src_file.get_lines((a + b - context_size + 1) / 2, context_size); + } + + lines_t lines = src_file.get_lines(a - context_size / 4, context_size / 2); + src_file.get_lines(b - context_size / 4, context_size / 2, lines); + return lines; + } + +private: + typedef details::hashtable::type src_files_t; + src_files_t _src_files; + + SourceFile &get_src_file(const std::string &filename) { + src_files_t::iterator it = _src_files.find(filename); + if (it != _src_files.end()) { + return it->second; + } + SourceFile &new_src_file = _src_files[filename]; + new_src_file = SourceFile(filename); + return new_src_file; + } +}; + +/*************** PRINTER ***************/ + +namespace ColorMode { +enum type { automatic, never, always }; +} + +class cfile_streambuf : public std::streambuf { +public: + cfile_streambuf(FILE *_sink) : sink(_sink) {} + int_type underflow() override { return traits_type::eof(); } + int_type overflow(int_type ch) override { + if (traits_type::not_eof(ch) && fputc(ch, sink) != EOF) { + return ch; + } + return traits_type::eof(); + } + + std::streamsize xsputn(const char_type *s, std::streamsize count) override { + return static_cast( + fwrite(s, sizeof *s, static_cast(count), sink)); + } + +#ifdef BACKWARD_ATLEAST_CXX11 +public: + cfile_streambuf(const cfile_streambuf &) = delete; + cfile_streambuf &operator=(const cfile_streambuf &) = delete; +#else +private: + cfile_streambuf(const cfile_streambuf &); + cfile_streambuf &operator=(const cfile_streambuf &); +#endif + +private: + FILE *sink; + std::vector buffer; +}; + +#ifdef BACKWARD_SYSTEM_LINUX + +namespace Color { +enum type { yellow = 33, purple = 35, reset = 39 }; +} // namespace Color + +class Colorize { +public: + Colorize(std::ostream &os) : _os(os), _reset(false), _enabled(false) {} + + void activate(ColorMode::type mode) { _enabled = mode == ColorMode::always; } + + void activate(ColorMode::type mode, FILE *fp) { activate(mode, fileno(fp)); } + + void set_color(Color::type ccode) { + if (!_enabled) + return; + + // I assume that the terminal can handle basic colors. Seriously I + // don't want to deal with all the termcap shit. + _os << "\033[" << static_cast(ccode) << "m"; + _reset = (ccode != Color::reset); + } + + ~Colorize() { + if (_reset) { + set_color(Color::reset); + } + } + +private: + void activate(ColorMode::type mode, int fd) { + activate(mode == ColorMode::automatic && isatty(fd) ? ColorMode::always + : mode); + } + + std::ostream &_os; + bool _reset; + bool _enabled; +}; + +#else // ndef BACKWARD_SYSTEM_LINUX + +namespace Color { +enum type { yellow = 0, purple = 0, reset = 0 }; +} // namespace Color + +class Colorize { +public: + Colorize(std::ostream &) {} + void activate(ColorMode::type) {} + void activate(ColorMode::type, FILE *) {} + void set_color(Color::type) {} +}; + +#endif // BACKWARD_SYSTEM_LINUX + +class Printer { +public: + bool snippet; + ColorMode::type color_mode; + bool address; + bool object; + int inliner_context_size; + int trace_context_size; + + Printer() + : snippet(true), color_mode(ColorMode::automatic), address(false), + object(false), inliner_context_size(5), trace_context_size(7) {} + + template FILE *print(ST &st, FILE *fp = stderr) { + cfile_streambuf obuf(fp); + std::ostream os(&obuf); + Colorize colorize(os); + colorize.activate(color_mode, fp); + print_stacktrace(st, os, colorize); + return fp; + } + + template std::ostream &print(ST &st, std::ostream &os) { + Colorize colorize(os); + colorize.activate(color_mode); + print_stacktrace(st, os, colorize); + return os; + } + + template + FILE *print(IT begin, IT end, FILE *fp = stderr, size_t thread_id = 0) { + cfile_streambuf obuf(fp); + std::ostream os(&obuf); + Colorize colorize(os); + colorize.activate(color_mode, fp); + print_stacktrace(begin, end, os, thread_id, colorize); + return fp; + } + + template + std::ostream &print(IT begin, IT end, std::ostream &os, + size_t thread_id = 0) { + Colorize colorize(os); + colorize.activate(color_mode); + print_stacktrace(begin, end, os, thread_id, colorize); + return os; + } + + TraceResolver const &resolver() const { return _resolver; } + +private: + TraceResolver _resolver; + SnippetFactory _snippets; + + template + void print_stacktrace(ST &st, std::ostream &os, Colorize &colorize) { + print_header(os, st.thread_id()); + _resolver.load_stacktrace(st); + for (size_t trace_idx = st.size(); trace_idx > 0; --trace_idx) { + print_trace(os, _resolver.resolve(st[trace_idx - 1]), colorize); + } + } + + template + void print_stacktrace(IT begin, IT end, std::ostream &os, size_t thread_id, + Colorize &colorize) { + print_header(os, thread_id); + for (; begin != end; ++begin) { + print_trace(os, *begin, colorize); + } + } + + void print_header(std::ostream &os, size_t thread_id) { + os << "Stack trace (most recent call last)"; + if (thread_id) { + os << " in thread " << thread_id; + } + os << ":\n"; + } + + void print_trace(std::ostream &os, const ResolvedTrace &trace, + Colorize &colorize) { + os << "#" << std::left << std::setw(2) << trace.idx << std::right; + bool already_indented = true; + + if (!trace.source.filename.size() || object) { + os << " Object \"" << trace.object_filename << "\", at " << trace.addr + << ", in " << trace.object_function << "\n"; + already_indented = false; + } + + for (size_t inliner_idx = trace.inliners.size(); inliner_idx > 0; + --inliner_idx) { + if (!already_indented) { + os << " "; + } + const ResolvedTrace::SourceLoc &inliner_loc = + trace.inliners[inliner_idx - 1]; + print_source_loc(os, " | ", inliner_loc); + if (snippet) { + print_snippet(os, " | ", inliner_loc, colorize, Color::purple, + inliner_context_size); + } + already_indented = false; + } + + if (trace.source.filename.size()) { + if (!already_indented) { + os << " "; + } + print_source_loc(os, " ", trace.source, trace.addr); + if (snippet) { + print_snippet(os, " ", trace.source, colorize, Color::yellow, + trace_context_size); + } + } + } + + void print_snippet(std::ostream &os, const char *indent, + const ResolvedTrace::SourceLoc &source_loc, + Colorize &colorize, Color::type color_code, + int context_size) { + using namespace std; + typedef SnippetFactory::lines_t lines_t; + + lines_t lines = _snippets.get_snippet(source_loc.filename, source_loc.line, + static_cast(context_size)); + + for (lines_t::const_iterator it = lines.begin(); it != lines.end(); ++it) { + if (it->first == source_loc.line) { + colorize.set_color(color_code); + os << indent << ">"; + } else { + os << indent << " "; + } + os << std::setw(4) << it->first << ": " << it->second << "\n"; + if (it->first == source_loc.line) { + colorize.set_color(Color::reset); + } + } + } + + void print_source_loc(std::ostream &os, const char *indent, + const ResolvedTrace::SourceLoc &source_loc, + void *addr = nullptr) { + os << indent << "Source \"" << source_loc.filename << "\", line " + << source_loc.line << ", in " << source_loc.function; + + if (address && addr != nullptr) { + os << " [" << addr << "]"; + } + os << "\n"; + } +}; + +/*************** SIGNALS HANDLING ***************/ + +#if defined(BACKWARD_SYSTEM_LINUX) || defined(BACKWARD_SYSTEM_DARWIN) + +class SignalHandling { +public: + static std::vector make_default_signals() { + const int posix_signals[] = { + // Signals for which the default action is "Core". + SIGABRT, // Abort signal from abort(3) + SIGBUS, // Bus error (bad memory access) + SIGFPE, // Floating point exception + SIGILL, // Illegal Instruction + SIGIOT, // IOT trap. A synonym for SIGABRT + SIGQUIT, // Quit from keyboard + SIGSEGV, // Invalid memory reference + SIGSYS, // Bad argument to routine (SVr4) + SIGTRAP, // Trace/breakpoint trap + SIGXCPU, // CPU time limit exceeded (4.2BSD) + SIGXFSZ, // File size limit exceeded (4.2BSD) +#if defined(BACKWARD_SYSTEM_DARWIN) + SIGEMT, // emulation instruction executed +#endif + }; + return std::vector(posix_signals, + posix_signals + + sizeof posix_signals / sizeof posix_signals[0]); + } + + SignalHandling(const std::vector &posix_signals = make_default_signals()) + : _loaded(false) { + bool success = true; + + const size_t stack_size = 1024 * 1024 * 8; + _stack_content.reset(static_cast(malloc(stack_size))); + if (_stack_content) { + stack_t ss; + ss.ss_sp = _stack_content.get(); + ss.ss_size = stack_size; + ss.ss_flags = 0; + if (sigaltstack(&ss, nullptr) < 0) { + success = false; + } + } else { + success = false; + } + + for (size_t i = 0; i < posix_signals.size(); ++i) { + struct sigaction action; + memset(&action, 0, sizeof action); + action.sa_flags = + static_cast(SA_SIGINFO | SA_ONSTACK | SA_NODEFER | SA_RESETHAND); + sigfillset(&action.sa_mask); + sigdelset(&action.sa_mask, posix_signals[i]); +#if defined(__clang__) +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdisabled-macro-expansion" +#endif + action.sa_sigaction = &sig_handler; +#if defined(__clang__) +#pragma clang diagnostic pop +#endif + + int r = sigaction(posix_signals[i], &action, nullptr); + if (r < 0) + success = false; + } + + _loaded = success; + } + + bool loaded() const { return _loaded; } + + static void handleSignal(int, siginfo_t *info, void *_ctx) { + ucontext_t *uctx = static_cast(_ctx); + + StackTrace st; + void *error_addr = nullptr; +#ifdef REG_RIP // x86_64 + error_addr = reinterpret_cast(uctx->uc_mcontext.gregs[REG_RIP]); +#elif defined(REG_EIP) // x86_32 + error_addr = reinterpret_cast(uctx->uc_mcontext.gregs[REG_EIP]); +#elif defined(__arm__) + error_addr = reinterpret_cast(uctx->uc_mcontext.arm_pc); +#elif defined(__aarch64__) + #if defined(__APPLE__) + error_addr = reinterpret_cast(uctx->uc_mcontext->__ss.__pc); + #else + error_addr = reinterpret_cast(uctx->uc_mcontext.pc); + #endif +#elif defined(__mips__) + error_addr = reinterpret_cast( + reinterpret_cast(&uctx->uc_mcontext)->sc_pc); +#elif defined(__ppc__) || defined(__powerpc) || defined(__powerpc__) || \ + defined(__POWERPC__) + error_addr = reinterpret_cast(uctx->uc_mcontext.regs->nip); +#elif defined(__riscv) + error_addr = reinterpret_cast(uctx->uc_mcontext.__gregs[REG_PC]); +#elif defined(__s390x__) + error_addr = reinterpret_cast(uctx->uc_mcontext.psw.addr); +#elif defined(__APPLE__) && defined(__x86_64__) + error_addr = reinterpret_cast(uctx->uc_mcontext->__ss.__rip); +#elif defined(__APPLE__) + error_addr = reinterpret_cast(uctx->uc_mcontext->__ss.__eip); +#else +#warning ":/ sorry, ain't know no nothing none not of your architecture!" +#endif + if (error_addr) { + st.load_from(error_addr, 32, reinterpret_cast(uctx), + info->si_addr); + } else { + st.load_here(32, reinterpret_cast(uctx), info->si_addr); + } + + Printer printer; + printer.address = true; + printer.print(st, stderr); + +#if (defined(_XOPEN_SOURCE) && _XOPEN_SOURCE >= 700) || \ + (defined(_POSIX_C_SOURCE) && _POSIX_C_SOURCE >= 200809L) + psiginfo(info, nullptr); +#else + (void)info; +#endif + } + +private: + details::handle _stack_content; + bool _loaded; + +#ifdef __GNUC__ + __attribute__((noreturn)) +#endif + static void + sig_handler(int signo, siginfo_t *info, void *_ctx) { + handleSignal(signo, info, _ctx); + + // try to forward the signal. + raise(info->si_signo); + + // terminate the process immediately. + puts("watf? exit"); + _exit(EXIT_FAILURE); + } +}; + +#endif // BACKWARD_SYSTEM_LINUX || BACKWARD_SYSTEM_DARWIN + +#ifdef BACKWARD_SYSTEM_WINDOWS + +class SignalHandling { +public: + SignalHandling(const std::vector & = std::vector()) + : reporter_thread_([]() { + /* We handle crashes in a utility thread: + backward structures and some Windows functions called here + need stack space, which we do not have when we encounter a + stack overflow. + To support reporting stack traces during a stack overflow, + we create a utility thread at startup, which waits until a + crash happens or the program exits normally. */ + + { + std::unique_lock lk(mtx()); + cv().wait(lk, [] { return crashed() != crash_status::running; }); + } + if (crashed() == crash_status::crashed) { + handle_stacktrace(skip_recs()); + } + { + std::unique_lock lk(mtx()); + crashed() = crash_status::ending; + } + cv().notify_one(); + }) { + SetUnhandledExceptionFilter(crash_handler); + + signal(SIGABRT, signal_handler); + _set_abort_behavior(0, _WRITE_ABORT_MSG | _CALL_REPORTFAULT); + + std::set_terminate(&terminator); +#ifndef BACKWARD_ATLEAST_CXX17 + std::set_unexpected(&terminator); +#endif + _set_purecall_handler(&terminator); + _set_invalid_parameter_handler(&invalid_parameter_handler); + } + bool loaded() const { return true; } + + ~SignalHandling() { + { + std::unique_lock lk(mtx()); + crashed() = crash_status::normal_exit; + } + + cv().notify_one(); + + reporter_thread_.join(); + } + +private: + static CONTEXT *ctx() { + static CONTEXT data; + return &data; + } + + enum class crash_status { running, crashed, normal_exit, ending }; + + static crash_status &crashed() { + static crash_status data; + return data; + } + + static std::mutex &mtx() { + static std::mutex data; + return data; + } + + static std::condition_variable &cv() { + static std::condition_variable data; + return data; + } + + static HANDLE &thread_handle() { + static HANDLE handle; + return handle; + } + + std::thread reporter_thread_; + + // TODO: how not to hardcode these? + static const constexpr int signal_skip_recs = +#ifdef __clang__ + // With clang, RtlCaptureContext also captures the stack frame of the + // current function Below that, there ar 3 internal Windows functions + 4 +#else + // With MSVC cl, RtlCaptureContext misses the stack frame of the current + // function The first entries during StackWalk are the 3 internal Windows + // functions + 3 +#endif + ; + + static int &skip_recs() { + static int data; + return data; + } + + static inline void terminator() { + crash_handler(signal_skip_recs); + abort(); + } + + static inline void signal_handler(int) { + crash_handler(signal_skip_recs); + abort(); + } + + static inline void __cdecl invalid_parameter_handler(const wchar_t *, + const wchar_t *, + const wchar_t *, + unsigned int, + uintptr_t) { + crash_handler(signal_skip_recs); + abort(); + } + + NOINLINE static LONG WINAPI crash_handler(EXCEPTION_POINTERS *info) { + // The exception info supplies a trace from exactly where the issue was, + // no need to skip records + crash_handler(0, info->ContextRecord); + return EXCEPTION_CONTINUE_SEARCH; + } + + NOINLINE static void crash_handler(int skip, CONTEXT *ct = nullptr) { + + if (ct == nullptr) { + RtlCaptureContext(ctx()); + } else { + memcpy(ctx(), ct, sizeof(CONTEXT)); + } + DuplicateHandle(GetCurrentProcess(), GetCurrentThread(), + GetCurrentProcess(), &thread_handle(), 0, FALSE, + DUPLICATE_SAME_ACCESS); + + skip_recs() = skip; + + { + std::unique_lock lk(mtx()); + crashed() = crash_status::crashed; + } + + cv().notify_one(); + + { + std::unique_lock lk(mtx()); + cv().wait(lk, [] { return crashed() != crash_status::crashed; }); + } + } + + static void handle_stacktrace(int skip_frames = 0) { + // printer creates the TraceResolver, which can supply us a machine type + // for stack walking. Without this, StackTrace can only guess using some + // macros. + // StackTrace also requires that the PDBs are already loaded, which is done + // in the constructor of TraceResolver + Printer printer; + + StackTrace st; + st.set_machine_type(printer.resolver().machine_type()); + st.set_thread_handle(thread_handle()); + st.load_here(32 + skip_frames, ctx()); + st.skip_n_firsts(skip_frames); + + printer.address = true; + printer.print(st, std::cerr); + } +}; + +#endif // BACKWARD_SYSTEM_WINDOWS + +#ifdef BACKWARD_SYSTEM_UNKNOWN + +class SignalHandling { +public: + SignalHandling(const std::vector & = std::vector()) {} + bool init() { return false; } + bool loaded() { return false; } +}; + +#endif // BACKWARD_SYSTEM_UNKNOWN + +} // namespace backward + +#endif /* H_GUARD */ diff --git a/xbmc/addons/kodi-dev-kit/src/addon/third_party/backward-cpp/builds.sh b/xbmc/addons/kodi-dev-kit/src/addon/third_party/backward-cpp/builds.sh new file mode 100755 index 0000000000000..6e1fb2074ed10 --- /dev/null +++ b/xbmc/addons/kodi-dev-kit/src/addon/third_party/backward-cpp/builds.sh @@ -0,0 +1,77 @@ +#!/bin/bash + +COMPILERS_CXX98=`cat</dev/null + ( + cd "$builddir" + cmake -DCMAKE_BUILD_TYPE=$buildtype -DBACKWARD_TESTS=ON .. + ) +} + +function build() { + local builddir=$1 + shift + make -C "$builddir" $@ +} + +function dotest() { + local builddir=$1 + shift + make -C "$builddir" test $@ + return 0 +} + +function do_action() { + local lang=$1 + local action=$2 + shift 2 + + for compiler in $COMPILERS; do + local builddir="build_${lang}_${compiler}" + + if [[ $action == "cmake" ]]; then + buildtype=$1 + mkbuild $compiler $lang "$buildtype" "$builddir" + [[ $? != 0 ]] && exit + elif [[ $action == "make" ]]; then + build "$builddir" $@ + [[ $? != 0 ]] && exit + elif [[ $action == "test" ]]; then + dotest "$builddir" $@ + [[ $? != 0 ]] && exit + elif [[ $action == "clean" ]]; then + rm -r "$builddir" + else + echo "usage: $0 cmake [debug|release|relwithdbg]|make|test|clean" + exit 255 + fi + done +} + +COMPILERS=$COMPILERS_CXX98 +do_action c++98 $@ +COMPILERS=$COMPILERS_CXX11 +do_action c++11 $@ diff --git a/xbmc/addons/kodi-dev-kit/src/addon/third_party/backward-cpp/conanfile.py b/xbmc/addons/kodi-dev-kit/src/addon/third_party/backward-cpp/conanfile.py new file mode 100644 index 0000000000000..d174b257b95db --- /dev/null +++ b/xbmc/addons/kodi-dev-kit/src/addon/third_party/backward-cpp/conanfile.py @@ -0,0 +1,45 @@ +from conans import ConanFile, CMake +import os + +class BackwardCpp(ConanFile): + settings = 'os', 'compiler', 'build_type', 'arch' + name = 'backward' + url = 'https://github.com/bombela/backward-cpp' + license = 'MIT' + version = '1.3.0' + options = { + 'stack_walking_unwind': [True, False], + 'stack_walking_backtrace': [True, False], + 'stack_details_auto_detect': [True, False], + 'stack_details_backtrace_symbol': [True, False], + 'stack_details_dw': [True, False], + 'stack_details_bfd': [True, False], + 'shared': [True, False] + } + default_options = ( + 'stack_walking_unwind=True', + 'stack_walking_backtrace=False', + 'stack_details_auto_detect=True', + 'stack_details_backtrace_symbol=False', + 'stack_details_dw=False', + 'stack_details_bfd=False', + 'shared=False' + ) + exports = 'backward.cpp', 'backward.hpp', 'test/*', 'CMakeLists.txt', 'BackwardConfig.cmake' + generators = 'cmake' + + def build(self): + cmake = CMake(self) + + cmake.configure(defs={'BACKWARD_' + name.upper(): value for name, value in self.options.values.as_list()}) + cmake.build() + + def package(self): + self.copy('backward.hpp', os.path.join('include', 'backward')) + self.copy('*.a', dst='lib') + self.copy('*.so', dst='lib') + self.copy('*.lib', dst='lib') + self.copy('*.dll', dst='lib') + + def package_info(self): + self.cpp_info.libs = ['backward'] diff --git a/xbmc/addons/kodi-dev-kit/src/addon/third_party/backward-cpp/doc/CMakeLists.txt b/xbmc/addons/kodi-dev-kit/src/addon/third_party/backward-cpp/doc/CMakeLists.txt new file mode 100644 index 0000000000000..81c5904eedbe6 --- /dev/null +++ b/xbmc/addons/kodi-dev-kit/src/addon/third_party/backward-cpp/doc/CMakeLists.txt @@ -0,0 +1,12 @@ +# Auto generated CMakeLists.txt. +# See xbmc/addons/kodi-dev-kit/tools/code-generator.py. + +set(SOURCES +) + +set(HEADERS +) + +if(SOURCES OR HEADERS) + devkit_add_object(devkit_third_party_backward-cpp_doc) +endif() diff --git a/xbmc/addons/kodi-dev-kit/src/addon/third_party/backward-cpp/doc/nice.png b/xbmc/addons/kodi-dev-kit/src/addon/third_party/backward-cpp/doc/nice.png new file mode 100644 index 0000000000000000000000000000000000000000..42147e5947b7fcfa852a571f47d9c36d9e31fcd5 GIT binary patch literal 40904 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 rm81GE!8#VGu$Pho_;sb6S@6FAF^e|a literal 0 HcmV?d00001 diff --git a/xbmc/addons/kodi-dev-kit/src/addon/third_party/backward-cpp/doc/pretty.png b/xbmc/addons/kodi-dev-kit/src/addon/third_party/backward-cpp/doc/pretty.png new file mode 100644 index 0000000000000000000000000000000000000000..f95d66ee0be9e821dcc7f97f05443ff5e889ee59 GIT binary patch literal 105498 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
I+M7TnYg*;R1m)mRSLg-afM&- zR!jsCSNb*PUXGej(G=_KUKy(}@jtYfv|6|j!WgW6k=)gJ_(UAI{-v{LZRS9yF~JUg z<>VH=UaRe}@~$|)+gSZ?T6%9Fj0mLQxW(z7Hz70MZ$A^p z?7upb`8>IXye+ddJ0_l95U`naWn^6c+fZvI>0}BDvPX4cimN;DnybsG^B#>!mRCEH z2$ZzX*-tw|Vy`O!cm9C^fGwE*PBNWG=xVuAg^ZAh^YdrsbAiY~u_w)fwpA}dg$vTy zpB`fmWFFmbl>+}*BX!u$|7l@1ly-4A2!vjlv7~5%HkpNODks8+as>v`wp*<_Sp3rY z)``A()&Ck~8cs@Lzf6`m%0t&28779d=c%G)TZ1EOFdqtfq6dafz}XVG1Z`AzU{FQQeBmWLNNGr@bRZx2FH`uWgmRx3?+WUzbmmOy+7{mG0&!>XQY( zSVG(#V?@i016XYv%3g9MH}npoE`N0i*^3dpU-@EYeB(IFfM~**Rd%B+V7jY5v(=+K z`Iug~Uhx%nUDR{?>AhxMsn(ottL!8+0rRy>deW;=w=WIflPq?Q*J9?r4Y4?3n*g8M z2CD6#lBI~6=8AEl{PjATQ30e=tLdY?*>UWz(;_tFj7N4!09mow$cGt!cL|$4qb71n zT=NqE3aZomi(4QRi(2Nzk&!PoQ9)8*4jh*5@KG(p9C|8uTLPH05pSL;Goyv5bxHwv zP`F0~Lo~}H7E`wCs^Xc?WhH9;$T%lNUX3rSrloo)4ZI8!5Zad+_ANdZCoSC>{y=L| zn$=+Fb3u}<(CRG?@_B?^DMr9|i(MbU2ts4hiAp!CcDKUk&Z*=OVg3t|1zRSL$(G{rza>5Xh@jw1gWK5hPieJjs_@{UIaOWfp;JS?y~5>>ouEk{>eu(7zsmN`Mo)_5lv1frVBcmvuCmGi zZ7XUQw|5~kBi~Q~1ghpzngg)`d1R!>R@xgz=o5~r7eNRH8m(0Ms=4H5Ock>S;hTwG z7}c_4!_M8ke;tqia~5u zojiyu-7VS$<|um%+5&;8_%Dc`Ee~Nxe0GW)Z@X5~ZLsEY(N?Y_329bMhAmbLZ>O%< z4|wgZ6~7vu%v&7&&&H>tQP&n;X_P#~u!TDJwm$amPnI(%kFvSqN$aP*x#q)bcTen) zh)O10hF#BQM2ju-fI*c1W!`^9ecD9?O_ghlm)pvFaLO+?3o40lLG8ZJn1z^K;F;?R ziyB!`U(+jTOKs`F^<$C8yPnANbG-BMLMWfH_LbjlQUKYFd$G3}M}rvP){#;@M)jSd^_kX z0?yt!iBMp~-Br$|xstfG!C_n$SuOYhLjJ+bQMykLLq#a6P+R7s%ndw)oA30WAn zdaCttItMZqnot8rIK6N$+hg8JD2;5dtq(v!93K1_4x z3Upk|DXBLaLOl!4VGwEFfwF%vT+%gXXfSI0-wKR1v&7XvTEPQMI*jvfN>1E|aZ-%Z zKfGmk7oa<)F%hm_V*-x6Db?m`R(jv@PVWU0Tk8H^=ijpR&>L_WO!&I)KHG~+Oymy; v(a?zH)V#iTUDE_pl>evVh+X#kdWOrkiKUwOU6pygijSwMs;5$~WE1^A1DQmn literal 0 HcmV?d00001 diff --git a/xbmc/addons/kodi-dev-kit/src/addon/third_party/backward-cpp/test/CMakeLists.txt b/xbmc/addons/kodi-dev-kit/src/addon/third_party/backward-cpp/test/CMakeLists.txt new file mode 100644 index 0000000000000..c5dad6896dde4 --- /dev/null +++ b/xbmc/addons/kodi-dev-kit/src/addon/third_party/backward-cpp/test/CMakeLists.txt @@ -0,0 +1,19 @@ +# Auto generated CMakeLists.txt. +# See xbmc/addons/kodi-dev-kit/tools/code-generator.py. + +set(SOURCES + _test_main.cpp + rectrace.cpp + select_signals.cpp + stacktrace.cpp + suicide.cpp + test.cpp +) + +set(HEADERS + test.hpp +) + +if(SOURCES OR HEADERS) + devkit_add_object(devkit_third_party_backward-cpp_test) +endif() diff --git a/xbmc/addons/kodi-dev-kit/src/addon/third_party/backward-cpp/test/_test_main.cpp b/xbmc/addons/kodi-dev-kit/src/addon/third_party/backward-cpp/test/_test_main.cpp new file mode 100644 index 0000000000000..68211e030fd29 --- /dev/null +++ b/xbmc/addons/kodi-dev-kit/src/addon/third_party/backward-cpp/test/_test_main.cpp @@ -0,0 +1,241 @@ +/* + * _test_main.cpp + * Copyright 2013 Google Inc. All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "test.hpp" +#include +#include + +#ifdef _WIN32 +#include +#define strcasecmp _stricmp +#else +#include +#include +#endif + +#if defined(__has_include) && __has_include() +#include +#else +#include + +#ifdef _WIN32 +char argv0[MAX_PATH]; +inline const char *getprogname() { + return GetModuleFileName(NULL, argv0, sizeof(argv0)) ? argv0 : NULL; +} +#elif !defined(__APPLE__) +// N.B. getprogname() is an Apple/BSD-ism. +// program_invocation_name is a GLIBC-ism, but it's also +// supported by libmusl. +#define getprogname() program_invocation_name +#endif + +void error(int status, int errnum, const char *format, ...) { + fflush(stdout); + fprintf(stderr, "%s: ", getprogname()); + + va_list args; + va_start(args, format); + vfprintf(stderr, format, args); + va_end(args); + + if (errnum != 0) { + fprintf(stderr, ": %s\n", strerror(errnum)); + } else { + fprintf(stderr, "\n"); + } + if (status != 0) { + exit(status); + } +} +#endif + +using namespace test; + +bool run_test(TestBase &test, bool use_child_process = true) { + if (!use_child_process) { + exit(static_cast(test.run())); + } + + printf("-- running test case: %s\n", test.name); + + fflush(stdout); + + test::TestStatus status = test::SUCCESS; + +#ifdef _WIN32 + char filename[256]; + GetModuleFileName(NULL, filename, 256); // TODO: check for error + std::string cmd_line = filename; + cmd_line += " --nofork "; + cmd_line += test.name; + + STARTUPINFO si; + PROCESS_INFORMATION pi; + ZeroMemory(&si, sizeof(si)); + si.cb = sizeof(si); + ZeroMemory(&pi, sizeof(pi)); + + if (!CreateProcessA(nullptr, const_cast(cmd_line.c_str()), nullptr, + nullptr, FALSE, 0, nullptr, nullptr, &si, &pi)) { + printf("unable to create process\n"); + exit(-1); + } + + WaitForSingleObject(pi.hProcess, INFINITE); + + DWORD exit_code; + GetExitCodeProcess(pi.hProcess, &exit_code); + switch (exit_code) { + case 3: + status = test::SIGNAL_ABORT; + break; + case 5: + status = test::EXCEPTION_UNCAUGHT; + break; + case EXCEPTION_ACCESS_VIOLATION: + status = test::SIGNAL_SEGFAULT; + break; + case EXCEPTION_STACK_OVERFLOW: + status = test::SIGNAL_SEGFAULT; + break; + case EXCEPTION_INT_DIVIDE_BY_ZERO: + status = test::SIGNAL_DIVZERO; + break; + } + printf("Exit code: %lu\n", exit_code); + + CloseHandle(pi.hProcess); + CloseHandle(pi.hThread); + + if (test.expected_status == test::ASSERT_FAIL) { + // assert calls abort on windows + return (status & test::SIGNAL_ABORT); + } + +#else + + pid_t child_pid = fork(); + if (child_pid == 0) { + exit(static_cast(test.run())); + } + if (child_pid == -1) { + error(EXIT_FAILURE, 0, "unable to fork"); + } + + int child_status = 0; + waitpid(child_pid, &child_status, 0); + + if (WIFEXITED(child_status)) { + int exit_status = WEXITSTATUS(child_status); + if (exit_status & ~test::STATUS_MASK) { + status = test::FAILED; + } else { + status = static_cast(exit_status); + } + } else if (WIFSIGNALED(child_status)) { + const int signum = WTERMSIG(child_status); + printf("!! signal (%d) %s\n", signum, strsignal(signum)); + switch (signum) { + case SIGABRT: + status = test::SIGNAL_ABORT; + break; + case SIGSEGV: + case SIGBUS: + status = test::SIGNAL_SEGFAULT; + break; + case SIGFPE: + status = test::SIGNAL_DIVZERO; + break; + default: + status = test::SIGNAL_UNCAUGHT; + } + } + +#endif + + if (test.expected_status == test::FAILED) { + return (status & test::FAILED); + } + + if (test.expected_status == test::SIGNAL_UNCAUGHT) { + return (status & test::SIGNAL_UNCAUGHT); + } + + return status == test.expected_status; +} + +int main(int argc, const char *const argv[]) { + +#ifdef _WIN32 + _set_abort_behavior(0, _WRITE_ABORT_MSG | _CALL_REPORTFAULT); +#endif + + if (argc == 3 && strcmp("--nofork", argv[1]) == 0) { + // Windows has no fork, so we simulate it + // we only execute one test, without forking + for (test_registry_t::iterator it = test_registry().begin(); + it != test_registry().end(); ++it) { + TestBase &test = **it; + if (strcasecmp(argv[2], test.name) == 0) { + run_test(test, false); + + return 0; + } + } + return -1; + } + + size_t success_cnt = 0; + size_t total_cnt = 0; + for (test_registry_t::iterator it = test_registry().begin(); + it != test_registry().end(); ++it) { + TestBase &test = **it; + + bool consider_test = (argc <= 1); + for (int i = 1; i < argc; ++i) { + if (strcasecmp(argv[i], test.name) == 0) { + consider_test = true; + break; + } + } + if (!consider_test) { + continue; + } + + total_cnt += 1; + if (run_test(test)) { + printf("-- test case success: %s\n", test.name); + success_cnt += 1; + } else { + printf("** test case FAILED : %s\n", test.name); + } + } + printf("-- tests passing: %zu/%zu", success_cnt, total_cnt); + if (total_cnt) { + printf(" (%zu%%)\n", success_cnt * 100 / total_cnt); + } else { + printf("\n"); + } + return (success_cnt == total_cnt) ? EXIT_SUCCESS : EXIT_FAILURE; +} diff --git a/xbmc/addons/kodi-dev-kit/src/addon/third_party/backward-cpp/test/rectrace.cpp b/xbmc/addons/kodi-dev-kit/src/addon/third_party/backward-cpp/test/rectrace.cpp new file mode 100644 index 0000000000000..10a38f475a029 --- /dev/null +++ b/xbmc/addons/kodi-dev-kit/src/addon/third_party/backward-cpp/test/rectrace.cpp @@ -0,0 +1,98 @@ +/* + * test/rectrace.cpp + * Copyright 2013 Google Inc. All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "backward.hpp" +#include "test/test.hpp" +#include + +using namespace backward; + +typedef StackTrace stacktrace_t; + +void end_of_our_journey(stacktrace_t &st) { + if (!st.size()) { + st.load_here(); + } +} + +int rec(stacktrace_t &st, int level) { + if (level <= 1) { + end_of_our_journey(st); + return 0; + } + return rec(st, level - 1); +} + +namespace toto { + +namespace titi { + +struct foo { + + union bar { + NOINLINE static int trampoline(stacktrace_t &st, int level) { + return rec(st, level); + } + }; +}; + +} // namespace titi + +} // namespace toto + +TEST(recursion) { + { // lexical scope. + stacktrace_t st; + const int input = 3; + int r = toto::titi::foo::bar::trampoline(st, input); + + std::cout << "rec(" << input << ") == " << r << std::endl; + + Printer printer; + // printer.address = true; + printer.object = true; + printer.print(st, stdout); + } +} + +int fib(StackTrace &st, int level) { + if (level == 2) { + return 1; + } + if (level <= 1) { + end_of_our_journey(st); + return 0; + } + return fib(st, level - 1) + fib(st, level - 2); +} + +TEST(fibrecursive) { + StackTrace st; + const int input = 6; + int r = fib(st, input); + + std::cout << "fib(" << input << ") == " << r << std::endl; + + Printer printer; + printer.print(st, stdout); +} diff --git a/xbmc/addons/kodi-dev-kit/src/addon/third_party/backward-cpp/test/select_signals.cpp b/xbmc/addons/kodi-dev-kit/src/addon/third_party/backward-cpp/test/select_signals.cpp new file mode 100644 index 0000000000000..57e6ebe0877c1 --- /dev/null +++ b/xbmc/addons/kodi-dev-kit/src/addon/third_party/backward-cpp/test/select_signals.cpp @@ -0,0 +1,51 @@ +/* + * test/segfault.cpp + * Copyright 2013 Google Inc. All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "backward.hpp" + +#include "test/test.hpp" +#include +#include + +using namespace backward; + +void badass_function() { + char *ptr = (char *)42; + *ptr = 42; +} + +TEST_SEGFAULT(pprint_sigsev) { + std::vector signals; + signals.push_back(SIGSEGV); + SignalHandling sh(signals); + std::cout << std::boolalpha << "sh.loaded() == " << sh.loaded() << std::endl; + badass_function(); +} + +TEST_SEGFAULT(wont_pprint) { + std::vector signals; + signals.push_back(SIGABRT); + SignalHandling sh(signals); + std::cout << std::boolalpha << "sh.loaded() == " << sh.loaded() << std::endl; + badass_function(); +} diff --git a/xbmc/addons/kodi-dev-kit/src/addon/third_party/backward-cpp/test/stacktrace.cpp b/xbmc/addons/kodi-dev-kit/src/addon/third_party/backward-cpp/test/stacktrace.cpp new file mode 100644 index 0000000000000..47084fe7da01f --- /dev/null +++ b/xbmc/addons/kodi-dev-kit/src/addon/third_party/backward-cpp/test/stacktrace.cpp @@ -0,0 +1,57 @@ +/* + * test/stacktrace.cpp + * Copyright 2013 Google Inc. All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "backward.hpp" +#include "test/test.hpp" +#include +#include + +using namespace backward; + +void collect_trace(StackTrace &st) { st.load_here(); } + +TEST(minitrace) { + Printer printer; + + StackTrace st; + collect_trace(st); + + printer.print(st, std::cout); +} + +void d(StackTrace &st) { st.load_here(); } + +void c(StackTrace &st) { return d(st); } + +void b(StackTrace &st) { return c(st); } + +NOINLINE void a(StackTrace &st) { return b(st); } + +TEST(smalltrace) { + Printer printer; + + StackTrace st; + a(st); + + printer.print(st, std::cout); +} diff --git a/xbmc/addons/kodi-dev-kit/src/addon/third_party/backward-cpp/test/suicide.cpp b/xbmc/addons/kodi-dev-kit/src/addon/third_party/backward-cpp/test/suicide.cpp new file mode 100644 index 0000000000000..e694c6fb46166 --- /dev/null +++ b/xbmc/addons/kodi-dev-kit/src/addon/third_party/backward-cpp/test/suicide.cpp @@ -0,0 +1,89 @@ +/* + * test/suicide.cpp + * Copyright 2013 Google Inc. All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "backward.hpp" + +#include "test/test.hpp" +#include + +#ifndef _WIN32 +#include +#endif + +using namespace backward; + +void badass_function() { + char *ptr = (char *)42; + *ptr = 42; +} + +TEST_SEGFAULT(invalid_write) { badass_function(); } + +int you_shall_not_pass() { + char *ptr = (char *)42; + int v = *ptr; + return v; +} + +TEST_SEGFAULT(invalid_read) { + int v = you_shall_not_pass(); + std::cout << "v=" << v << std::endl; +} + +void abort_abort_I_repeat_abort_abort() { + std::cout << "Jumping off the boat!" << std::endl; + abort(); +} + +TEST_ABORT(calling_abort) { abort_abort_I_repeat_abort_abort(); } + +// aarch64 and mips does not trap Division by zero +#if !defined(__aarch64__) && !defined(__mips__) +volatile int zero = 0; + +int divide_by_zero() { + std::cout << "And the wild black hole appears..." << std::endl; + int v = 42 / zero; + return v; +} + +TEST_DIVZERO(divide_by_zero) { + int v = divide_by_zero(); + std::cout << "v=" << v << std::endl; +} +#endif + +// Darwin does not allow RLIMIT_STACK to be reduced +#ifndef __APPLE__ +int bye_bye_stack(int i) { return bye_bye_stack(i + 1) + bye_bye_stack(i * 2); } + +TEST_SEGFAULT(stackoverflow) { +#ifndef _WIN32 + struct rlimit limit; + limit.rlim_max = 8096; + setrlimit(RLIMIT_STACK, &limit); +#endif + int r = bye_bye_stack(42); + std::cout << "r=" << r << std::endl; +} +#endif diff --git a/xbmc/addons/kodi-dev-kit/src/addon/third_party/backward-cpp/test/test.cpp b/xbmc/addons/kodi-dev-kit/src/addon/third_party/backward-cpp/test/test.cpp new file mode 100644 index 0000000000000..1a148f12cc2e2 --- /dev/null +++ b/xbmc/addons/kodi-dev-kit/src/addon/third_party/backward-cpp/test/test.cpp @@ -0,0 +1,63 @@ +/* + * test/test.cpp + * Copyright 2013 Google Inc. All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "test/test.hpp" +#include +#include +#include + +TEST(empty_test) {} + +TEST_FAIL_ASSERT(fail_assert) { ASSERT(1 == 2); } + +TEST_FAIL_ASSERT(fail_assert_ge) { ASSERT_GE(4, 5); } + +TEST_UNCAUGHT_EXCEPTION(uncaught_exception) { + throw std::runtime_error("some random runtime error"); +} + +TEST_UNCAUGHT_EXCEPTION(uncaught_exception_int) { throw 42; } + +TEST_SEGFAULT(segfault) { + char *a = 0; + char b = a[42]; + std::cout << "result: " << b << std::endl; +} + +TEST_ABORT(abort) { abort(); } + +TEST(catch_int) { + ASSERT_THROW({ throw 42; }, int); +} + +TEST_FAIL_ASSERT(fail_catch_int) { ASSERT_THROW({}, int); } + +TEST_FAIL_ASSERT(fail_no_throw) { + ASSERT_NO_THROW({ throw 42; }); +} + +TEST(any_throw) { + ASSERT_ANY_THROW({ throw 42; }); +} + +TEST_FAIL_ASSERT(fail_any_throw) { ASSERT_ANY_THROW({}); } diff --git a/xbmc/addons/kodi-dev-kit/src/addon/third_party/backward-cpp/test/test.hpp b/xbmc/addons/kodi-dev-kit/src/addon/third_party/backward-cpp/test/test.hpp new file mode 100644 index 0000000000000..dc77c627a16da --- /dev/null +++ b/xbmc/addons/kodi-dev-kit/src/addon/third_party/backward-cpp/test/test.hpp @@ -0,0 +1,183 @@ +/* + * test/test.hpp + * Copyright 2013 Google Inc. All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#pragma once +#ifndef H_54E531F7_9154_454B_BEB9_257408429470 +#define H_54E531F7_9154_454B_BEB9_257408429470 + +#include +#include +#include +#include +#include +#include + +namespace test { + +struct AssertFailedError : std::exception { + ~AssertFailedError() throw() {} + + AssertFailedError(const char *filename, int _line, const char *_errmsg) + : basename(_basename(filename)), line(_line), errmsg(_errmsg) {} + + const char *what() const throw() { + if (!_what.size()) { + std::ostringstream ss; + ss << "assertion failed (" << basename << ":" << line; + ss << ") " << errmsg; + _what = ss.str(); + } + return _what.c_str(); + } + + const char *basename; + int line; + const char *errmsg; + + mutable std::string _what; + + static const char *_basename(const char *filename) { + const char *basename = filename + strlen(filename); + while (basename != filename && *basename != '/') { + basename -= 1; + } + return basename + 1; + } +}; + +enum TestStatus { + SUCCESS = 0 << 0, + FAILED = 1 << 0, + + ASSERT_FAIL = FAILED | 1 << 1, + EXCEPTION_UNCAUGHT = FAILED | 2 << 1, + SIGNAL_UNCAUGHT = FAILED | 3 << 1, + SIGNAL_SEGFAULT = SIGNAL_UNCAUGHT | 1 << 3, + SIGNAL_ABORT = SIGNAL_UNCAUGHT | 2 << 3, + SIGNAL_DIVZERO = SIGNAL_UNCAUGHT | 2 << 3, + + STATUS_MASK = 0x1F +}; + +struct TestBase { + const char *name; + TestStatus expected_status; + + virtual ~TestBase() {} + TestBase(const char *, TestStatus); + virtual void do_test() = 0; + + TestStatus run() { + try { + do_test(); + return SUCCESS; + } catch (const AssertFailedError &e) { + printf("!! %s\n", e.what()); + return ASSERT_FAIL; + } catch (const std::exception &e) { + printf("!! exception: %s\n", e.what()); + return EXCEPTION_UNCAUGHT; + } catch (...) { + printf("!! unknown exception\n"); + return EXCEPTION_UNCAUGHT; + } + } +}; + +typedef std::vector test_registry_t; +inline test_registry_t &test_registry() { + static test_registry_t reg; + return reg; +} + +inline TestBase::TestBase(const char *n, TestStatus s) + : name(n), expected_status(s) { + test_registry().push_back(this); +} + +} // namespace test + +#define _TEST_STATUS(name, status) \ + struct TEST_##name : ::test::TestBase { \ + TEST_##name() : TestBase(#name, status) {} \ + void do_test(); \ + } TEST_##name; \ + void TEST_##name::do_test() + +#define TEST(name) _TEST_STATUS(name, ::test::SUCCESS) +#define TEST_FAIL(name) _TEST_STATUS(name, ::test::FAILED) +#define TEST_FAIL_ASSERT(name) _TEST_STATUS(name, ::test::ASSERT_FAIL) +#define TEST_UNCAUGHT_EXCEPTION(name) \ + _TEST_STATUS(name, ::test::EXCEPTION_UNCAUGHT) +#define TEST_UNCAUGHT_SIGNAL(name) _TEST_STATUS(name, ::test::SIGNAL_UNCAUGHT) +#define TEST_SEGFAULT(name) _TEST_STATUS(name, ::test::SIGNAL_SEGFAULT) +#define TEST_ABORT(name) _TEST_STATUS(name, ::test::SIGNAL_ABORT) +#define TEST_DIVZERO(name) _TEST_STATUS(name, ::test::SIGNAL_DIVZERO) + +#define ASSERT(expr) \ + (expr) ? static_cast(0) \ + : throw ::test::AssertFailedError(__FILE__, __LINE__, #expr) + +#define _ASSERT_BINOP(a, b, cmp) \ + (!(a cmp b)) ? static_cast(0) \ + : throw ::test::AssertFailedError( \ + __FILE__, __LINE__, "because " #a " " #cmp " " #b) + +#define ASSERT_EQ(a, b) _ASSERT_BINOP(a, b, !=) +#define ASSERT_NE(a, b) _ASSERT_BINOP(a, b, ==) +#define ASSERT_LT(a, b) _ASSERT_BINOP(a, b, >=) +#define ASSERT_LE(a, b) _ASSERT_BINOP(a, b, >) +#define ASSERT_GT(a, b) _ASSERT_BINOP(a, b, <=) +#define ASSERT_GE(a, b) _ASSERT_BINOP(a, b, <) + +#define ASSERT_THROW(expr, e_type) \ + do { \ + try { \ + expr \ + } catch (const e_type &) { \ + break; \ + } \ + throw ::test::AssertFailedError(__FILE__, __LINE__, \ + "expected exception " #e_type); \ + } while (0) + +#define ASSERT_ANY_THROW(expr) \ + do { \ + try { \ + expr \ + } catch (...) { \ + break; \ + } \ + throw ::test::AssertFailedError(__FILE__, __LINE__, \ + "expected any exception"); \ + } while (0) + +#define ASSERT_NO_THROW(expr) \ + try { \ + expr \ + } catch (...) { \ + throw ::test::AssertFailedError(__FILE__, __LINE__, \ + "no exception expected"); \ + } + +#endif /* H_GUARD */ diff --git a/xbmc/addons/kodi-dev-kit/src/addon/third_party/backward-cpp/test_package/CMakeLists.txt b/xbmc/addons/kodi-dev-kit/src/addon/third_party/backward-cpp/test_package/CMakeLists.txt new file mode 100644 index 0000000000000..56de8b2232c59 --- /dev/null +++ b/xbmc/addons/kodi-dev-kit/src/addon/third_party/backward-cpp/test_package/CMakeLists.txt @@ -0,0 +1,13 @@ +# Auto generated CMakeLists.txt. +# See xbmc/addons/kodi-dev-kit/tools/code-generator.py. + +set(SOURCES + main.cpp +) + +set(HEADERS +) + +if(SOURCES OR HEADERS) + devkit_add_object(devkit_third_party_backward-cpp_test_package) +endif() diff --git a/xbmc/addons/kodi-dev-kit/src/addon/third_party/backward-cpp/test_package/conanfile.py b/xbmc/addons/kodi-dev-kit/src/addon/third_party/backward-cpp/test_package/conanfile.py new file mode 100644 index 0000000000000..e509dd5a00c49 --- /dev/null +++ b/xbmc/addons/kodi-dev-kit/src/addon/third_party/backward-cpp/test_package/conanfile.py @@ -0,0 +1,14 @@ +from conans import ConanFile, CMake +import os + +class TestBackward(ConanFile): + settings = 'os', 'compiler', 'build_type', 'arch' + generators = 'cmake' + + def build(self): + cmake = CMake(self) + cmake.configure(defs={'CMAKE_VERBOSE_MAKEFILE': 'ON'}) + cmake.build() + + def test(self): + self.run(os.path.join('.', 'bin', 'example')) diff --git a/xbmc/addons/kodi-dev-kit/src/addon/third_party/backward-cpp/test_package/main.cpp b/xbmc/addons/kodi-dev-kit/src/addon/third_party/backward-cpp/test_package/main.cpp new file mode 100644 index 0000000000000..98ca96a43f287 --- /dev/null +++ b/xbmc/addons/kodi-dev-kit/src/addon/third_party/backward-cpp/test_package/main.cpp @@ -0,0 +1,46 @@ +#include +#include +#include +#include + +using namespace backward; + +class TracedException : public std::runtime_error { +public: + TracedException() : std::runtime_error(_get_trace()) {} + +private: + std::string _get_trace() { + std::ostringstream ss; + + StackTrace stackTrace; + TraceResolver resolver; + stackTrace.load_here(); + resolver.load_stacktrace(stackTrace); + + for (std::size_t i = 0; i < stackTrace.size(); ++i) { + const ResolvedTrace trace = resolver.resolve(stackTrace[i]); + + ss << "#" << i << " at " << trace.object_function << "\n"; + } + + return ss.str(); + } +}; + +void f(int i) { + if (i >= 42) { + throw TracedException(); + } else { + std::cout << "i=" << i << "\n"; + f(i + 1); + } +} + +int main() { + try { + f(0); + } catch (const TracedException &ex) { + std::cout << ex.what(); + } +} diff --git a/xbmc/addons/kodi-dev-kit/src/addon_runner/CMakeLists.txt b/xbmc/addons/kodi-dev-kit/src/addon_runner/CMakeLists.txt index 01d90a0fcedc8..207d7d37c4191 100644 --- a/xbmc/addons/kodi-dev-kit/src/addon_runner/CMakeLists.txt +++ b/xbmc/addons/kodi-dev-kit/src/addon_runner/CMakeLists.txt @@ -30,7 +30,7 @@ foreach(OUTPUTCONFIG ${CMAKE_CONFIGURATION_TYPES}) endforeach() target_link_libraries(${PROJECT_NAME} PUBLIC dl) -target_include_directories(${PROJECT_NAME} PRIVATE ../../include) +target_include_directories(${PROJECT_NAME} PRIVATE ${LIBDWARF_INCLUDE_DIR} ../../include) target_compile_definitions(${PROJECT_NAME} PRIVATE APP_NAME="${PROJECT_NAME}") set(LIBRARY_FILES ${LIBRARY_FILES} ${CMAKE_BINARY_DIR}/${DEVKIT_OUTPUT_DIRECTORY}/${PROJECT_NAME}${CMAKE_EXECUTABLE_SUFFIX} CACHE STRING "" FORCE) diff --git a/xbmc/addons/kodi-dev-kit/src/addon_runner/main.cpp b/xbmc/addons/kodi-dev-kit/src/addon_runner/main.cpp index e97c6fec61c92..c76bafe34d17a 100644 --- a/xbmc/addons/kodi-dev-kit/src/addon_runner/main.cpp +++ b/xbmc/addons/kodi-dev-kit/src/addon_runner/main.cpp @@ -8,6 +8,9 @@ #include "cxxopts.hpp" +#define BACKWARD_HAS_DW 1 +// #include "third_party/backward-cpp/backward.hpp" + #include #include @@ -131,6 +134,26 @@ ATTR_DLL_EXPORT int main(int argc, const char* argv[]) VALUES values; parse(argc, argv, values); +// backward::StackTrace m_stackTrace; +// backward::SignalHandling m_signalHandling; +// +// m_stackTrace.load_here(32); + +// using namespace backward; +// StackTrace st; st.load_here(32); +// +// std::vector signals; +// signals.push_back(SIGSEGV); +// SignalHandling sh(signals); + +/*Printer p; +p.object = true; +p.color_mode = ColorMode::always; +p.address = true; +p.print(st, stderr); +*/ + + if (values.m_debug) std::printf("Starting add-on library load to execute in Kodi: %s\n", values.m_addon_lib_path.c_str()); diff --git a/xbmc/addons/kodi-dev-kit/src/addon_runner/third_party/CMakeLists.txt b/xbmc/addons/kodi-dev-kit/src/addon_runner/third_party/CMakeLists.txt new file mode 100644 index 0000000000000..9f61e09641281 --- /dev/null +++ b/xbmc/addons/kodi-dev-kit/src/addon_runner/third_party/CMakeLists.txt @@ -0,0 +1,12 @@ +# Auto generated CMakeLists.txt. +# See xbmc/addons/kodi-dev-kit/tools/code-generator.py. + +set(SOURCES +) + +set(HEADERS +) + +if(SOURCES OR HEADERS) + devkit_add_object(devkit_third_party) +endif() diff --git a/xbmc/addons/kodi-dev-kit/src/addon_runner/third_party/backward-cpp/BackwardConfig.cmake b/xbmc/addons/kodi-dev-kit/src/addon_runner/third_party/backward-cpp/BackwardConfig.cmake new file mode 100644 index 0000000000000..867dab9c7f80c --- /dev/null +++ b/xbmc/addons/kodi-dev-kit/src/addon_runner/third_party/backward-cpp/BackwardConfig.cmake @@ -0,0 +1,266 @@ +# +# BackwardMacros.cmake +# Copyright 2013 Google Inc. All Rights Reserved. +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. + +############################################################################### +# OPTIONS +############################################################################### + +set(STACK_WALKING_UNWIND TRUE) +set(STACK_WALKING_UNWIND TRUE CACHE BOOL + "Use compiler's unwind API") +set(STACK_WALKING_BACKTRACE FALSE CACHE BOOL + "Use backtrace from (e)glibc for stack walking") +set(STACK_WALKING_LIBUNWIND FALSE CACHE BOOL + "Use libunwind for stack walking") + +set(STACK_DETAILS_AUTO_DETECT TRUE CACHE BOOL + "Auto detect backward's stack details dependencies") + +set(STACK_DETAILS_BACKTRACE_SYMBOL FALSE CACHE BOOL + "Use backtrace from (e)glibc for symbols resolution") +set(STACK_DETAILS_DW FALSE CACHE BOOL + "Use libdw to read debug info") +set(STACK_DETAILS_BFD FALSE CACHE BOOL + "Use libbfd to read debug info") +set(STACK_DETAILS_DWARF FALSE CACHE BOOL + "Use libdwarf/libelf to read debug info") + +if(CMAKE_SOURCE_DIR STREQUAL CMAKE_CURRENT_SOURCE_DIR AND NOT DEFINED BACKWARD_TESTS) + # If this is a top level CMake project, we most lixely want the tests + set(BACKWARD_TESTS ON CACHE BOOL "Enable tests") +else() + set(BACKWARD_TESTS OFF CACHE BOOL "Enable tests") +endif() +############################################################################### +# CONFIGS +############################################################################### +include(FindPackageHandleStandardArgs) + +if (STACK_WALKING_LIBUNWIND) + # libunwind works on the macOS without having to add special include + # paths or libraries + if (NOT APPLE) + find_path(LIBUNWIND_INCLUDE_DIR NAMES "libunwind.h") + find_library(LIBUNWIND_LIBRARY unwind) + + if (LIBUNWIND_LIBRARY) + include(CheckSymbolExists) + check_symbol_exists(UNW_INIT_SIGNAL_FRAME libunwind.h HAVE_UNW_INIT_SIGNAL_FRAME) + if (NOT HAVE_UNW_INIT_SIGNAL_FRAME) + message(STATUS "libunwind does not support unwinding from signal handler frames") + endif() + endif() + + set(LIBUNWIND_INCLUDE_DIRS ${LIBUNWIND_INCLUDE_DIR}) + set(LIBDWARF_LIBRARIES ${LIBUNWIND_LIBRARY}) + find_package_handle_standard_args(libunwind DEFAULT_MSG + LIBUNWIND_LIBRARY LIBUNWIND_INCLUDE_DIR) + mark_as_advanced(LIBUNWIND_INCLUDE_DIR LIBUNWIND_LIBRARY) + list(APPEND _BACKWARD_LIBRARIES ${LIBUNWIND_LIBRARY}) + endif() + + # Disable other unwinders if libunwind is found + set(STACK_WALKING_UNWIND FALSE) + set(STACK_WALKING_BACKTRACE FALSE) +endif() + +if (${STACK_DETAILS_AUTO_DETECT}) + if(NOT CMAKE_VERSION VERSION_LESS 3.17) + set(_name_mismatched_arg NAME_MISMATCHED) + endif() + # find libdw + find_path(LIBDW_INCLUDE_DIR NAMES "elfutils/libdw.h" "elfutils/libdwfl.h") + find_library(LIBDW_LIBRARY dw) + # in case it's statically linked, look for all the possible dependencies + find_library(LIBELF_LIBRARY elf) + find_library(LIBPTHREAD_LIBRARY pthread) + find_library(LIBZ_LIBRARY z) + find_library(LIBBZ2_LIBRARY bz2) + find_library(LIBLZMA_LIBRARY lzma) + find_library(LIBZSTD_LIBRARY zstd) + set(LIBDW_INCLUDE_DIRS ${LIBDW_INCLUDE_DIR} ) + set(LIBDW_LIBRARIES ${LIBDW_LIBRARY} + $<$:${LIBELF_LIBRARY}> + $<$:${LIBPTHREAD_LIBRARY}> + $<$:${LIBZ_LIBRARY}> + $<$:${LIBBZ2_LIBRARY}> + $<$:${LIBLZMA_LIBRARY}> + $<$:${LIBZSTD_LIBRARY}>) + find_package_handle_standard_args(libdw ${_name_mismatched_arg} + REQUIRED_VARS LIBDW_LIBRARY LIBDW_INCLUDE_DIR) + mark_as_advanced(LIBDW_INCLUDE_DIR LIBDW_LIBRARY) + + # find libbfd + find_path(LIBBFD_INCLUDE_DIR NAMES "bfd.h") + find_path(LIBDL_INCLUDE_DIR NAMES "dlfcn.h") + find_library(LIBBFD_LIBRARY bfd) + find_library(LIBDL_LIBRARY dl) + set(LIBBFD_INCLUDE_DIRS ${LIBBFD_INCLUDE_DIR} ${LIBDL_INCLUDE_DIR}) + set(LIBBFD_LIBRARIES ${LIBBFD_LIBRARY} ${LIBDL_LIBRARY}) + find_package_handle_standard_args(libbfd ${_name_mismatched_arg} + REQUIRED_VARS LIBBFD_LIBRARY LIBBFD_INCLUDE_DIR + LIBDL_LIBRARY LIBDL_INCLUDE_DIR) + mark_as_advanced(LIBBFD_INCLUDE_DIR LIBBFD_LIBRARY + LIBDL_INCLUDE_DIR LIBDL_LIBRARY) + + # find libdwarf + find_path(LIBDWARF_INCLUDE_DIR NAMES "libdwarf.h" PATH_SUFFIXES libdwarf) + find_path(LIBELF_INCLUDE_DIR NAMES "libelf.h") + find_path(LIBDL_INCLUDE_DIR NAMES "dlfcn.h") + find_library(LIBDWARF_LIBRARY dwarf) + find_library(LIBELF_LIBRARY elf) + find_library(LIBDL_LIBRARY dl) + set(LIBDWARF_INCLUDE_DIRS ${LIBDWARF_INCLUDE_DIR} ${LIBELF_INCLUDE_DIR} ${LIBDL_INCLUDE_DIR}) + set(LIBDWARF_LIBRARIES ${LIBDWARF_LIBRARY} ${LIBELF_LIBRARY} ${LIBDL_LIBRARY}) + find_package_handle_standard_args(libdwarf ${_name_mismatched_arg} + REQUIRED_VARS LIBDWARF_LIBRARY LIBDWARF_INCLUDE_DIR + LIBELF_LIBRARY LIBELF_INCLUDE_DIR + LIBDL_LIBRARY LIBDL_INCLUDE_DIR) + mark_as_advanced(LIBDWARF_INCLUDE_DIR LIBDWARF_LIBRARY + LIBELF_INCLUDE_DIR LIBELF_LIBRARY + LIBDL_INCLUDE_DIR LIBDL_LIBRARY) + + if (LIBDW_FOUND) + LIST(APPEND _BACKWARD_INCLUDE_DIRS ${LIBDW_INCLUDE_DIRS}) + LIST(APPEND _BACKWARD_LIBRARIES ${LIBDW_LIBRARIES}) + set(STACK_DETAILS_DW TRUE) + set(STACK_DETAILS_BFD FALSE) + set(STACK_DETAILS_DWARF FALSE) + set(STACK_DETAILS_BACKTRACE_SYMBOL FALSE) + elseif(LIBBFD_FOUND) + LIST(APPEND _BACKWARD_INCLUDE_DIRS ${LIBBFD_INCLUDE_DIRS}) + LIST(APPEND _BACKWARD_LIBRARIES ${LIBBFD_LIBRARIES}) + + # If we attempt to link against static bfd, make sure to link its dependencies, too + get_filename_component(bfd_lib_ext "${LIBBFD_LIBRARY}" EXT) + if (bfd_lib_ext STREQUAL "${CMAKE_STATIC_LIBRARY_SUFFIX}") + list(APPEND _BACKWARD_LIBRARIES iberty z) + endif() + + set(STACK_DETAILS_DW FALSE) + set(STACK_DETAILS_BFD TRUE) + set(STACK_DETAILS_DWARF FALSE) + set(STACK_DETAILS_BACKTRACE_SYMBOL FALSE) + elseif(LIBDWARF_FOUND) + LIST(APPEND _BACKWARD_INCLUDE_DIRS ${LIBDWARF_INCLUDE_DIRS}) + LIST(APPEND _BACKWARD_LIBRARIES ${LIBDWARF_LIBRARIES}) + + set(STACK_DETAILS_DW FALSE) + set(STACK_DETAILS_BFD FALSE) + set(STACK_DETAILS_DWARF TRUE) + set(STACK_DETAILS_BACKTRACE_SYMBOL FALSE) + else() + set(STACK_DETAILS_DW FALSE) + set(STACK_DETAILS_BFD FALSE) + set(STACK_DETAILS_DWARF FALSE) + set(STACK_DETAILS_BACKTRACE_SYMBOL TRUE) + endif() +else() + if (STACK_DETAILS_DW) + LIST(APPEND _BACKWARD_LIBRARIES dw) + endif() + + if (STACK_DETAILS_BFD) + LIST(APPEND _BACKWARD_LIBRARIES bfd dl) + endif() + + if (STACK_DETAILS_DWARF) + LIST(APPEND _BACKWARD_LIBRARIES dwarf elf) + endif() +endif() + +macro(map_definitions var_prefix define_prefix) + foreach(def ${ARGN}) + if (${${var_prefix}${def}}) + LIST(APPEND _BACKWARD_DEFINITIONS "${define_prefix}${def}=1") + else() + LIST(APPEND _BACKWARD_DEFINITIONS "${define_prefix}${def}=0") + endif() + endforeach() +endmacro() + +if (NOT _BACKWARD_DEFINITIONS) + map_definitions("STACK_WALKING_" "BACKWARD_HAS_" UNWIND LIBUNWIND BACKTRACE) + map_definitions("STACK_DETAILS_" "BACKWARD_HAS_" BACKTRACE_SYMBOL DW BFD DWARF) +endif() + +if(WIN32) + list(APPEND _BACKWARD_LIBRARIES dbghelp psapi) + if(MINGW) + set(MINGW_MSVCR_LIBRARY "msvcr90$<$:d>" CACHE STRING "Mingw MSVC runtime import library") + list(APPEND _BACKWARD_LIBRARIES ${MINGW_MSVCR_LIBRARY}) + endif() +endif() + +set(BACKWARD_INCLUDE_DIR "${CMAKE_CURRENT_LIST_DIR}") + +set(BACKWARD_HAS_EXTERNAL_LIBRARIES FALSE) +set(FIND_PACKAGE_REQUIRED_VARS BACKWARD_INCLUDE_DIR) +if(DEFINED _BACKWARD_LIBRARIES) + set(BACKWARD_HAS_EXTERNAL_LIBRARIES TRUE) + list(APPEND FIND_PACKAGE_REQUIRED_VARS _BACKWARD_LIBRARIES) +endif() + +include(FindPackageHandleStandardArgs) +find_package_handle_standard_args(Backward + REQUIRED_VARS ${FIND_PACKAGE_REQUIRED_VARS} +) +list(APPEND _BACKWARD_INCLUDE_DIRS ${BACKWARD_INCLUDE_DIR}) + +# add_backward, optional bool argument; if passed and true, backward will be included as a system header +macro(add_backward target) + if ("${ARGN}") + target_include_directories(${target} SYSTEM PRIVATE ${BACKWARD_INCLUDE_DIRS}) + else() + target_include_directories(${target} PRIVATE ${BACKWARD_INCLUDE_DIRS}) + endif() + set_property(TARGET ${target} APPEND PROPERTY COMPILE_DEFINITIONS ${BACKWARD_DEFINITIONS}) + set_property(TARGET ${target} APPEND PROPERTY LINK_LIBRARIES ${BACKWARD_LIBRARIES}) +endmacro() + +set(BACKWARD_INCLUDE_DIRS ${_BACKWARD_INCLUDE_DIRS} CACHE INTERNAL "_BACKWARD_INCLUDE_DIRS") +set(BACKWARD_DEFINITIONS ${_BACKWARD_DEFINITIONS} CACHE INTERNAL "BACKWARD_DEFINITIONS") +set(BACKWARD_LIBRARIES ${_BACKWARD_LIBRARIES} CACHE INTERNAL "BACKWARD_LIBRARIES") +mark_as_advanced(BACKWARD_INCLUDE_DIRS BACKWARD_DEFINITIONS BACKWARD_LIBRARIES) + +# Expand each definition in BACKWARD_DEFINITIONS to its own cmake var and export +# to outer scope +foreach(var ${BACKWARD_DEFINITIONS}) + string(REPLACE "=" ";" var_as_list ${var}) + list(GET var_as_list 0 var_name) + list(GET var_as_list 1 var_value) + set(${var_name} ${var_value}) + mark_as_advanced(${var_name}) +endforeach() + +if (NOT TARGET Backward::Backward) + add_library(Backward::Backward INTERFACE IMPORTED) + set_target_properties(Backward::Backward PROPERTIES + INTERFACE_INCLUDE_DIRECTORIES "${BACKWARD_INCLUDE_DIRS}" + INTERFACE_COMPILE_DEFINITIONS "${BACKWARD_DEFINITIONS}" + ) + if(BACKWARD_HAS_EXTERNAL_LIBRARIES) + set_target_properties(Backward::Backward PROPERTIES + INTERFACE_LINK_LIBRARIES "${BACKWARD_LIBRARIES}" + ) + endif() +endif() diff --git a/xbmc/addons/kodi-dev-kit/src/addon_runner/third_party/backward-cpp/CMakeLists.txt b/xbmc/addons/kodi-dev-kit/src/addon_runner/third_party/backward-cpp/CMakeLists.txt new file mode 100644 index 0000000000000..95544371cbbab --- /dev/null +++ b/xbmc/addons/kodi-dev-kit/src/addon_runner/third_party/backward-cpp/CMakeLists.txt @@ -0,0 +1,140 @@ +# +# CMakeLists.txt +# Copyright 2013 Google Inc. All Rights Reserved. +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. + +cmake_minimum_required(VERSION 3.0) +project(backward CXX) + +# Introduce variables: +# * CMAKE_INSTALL_LIBDIR +# * CMAKE_INSTALL_BINDIR +# * CMAKE_INSTALL_INCLUDEDIR +include(GNUInstallDirs) + +include(BackwardConfig.cmake) + +# check if compiler is nvcc or nvcc_wrapper +set(COMPILER_IS_NVCC false) +get_filename_component(COMPILER_NAME ${CMAKE_CXX_COMPILER} NAME) +if (COMPILER_NAME MATCHES "^nvcc") + set(COMPILER_IS_NVCC true) +endif() + +if (DEFINED ENV{OMPI_CXX} OR DEFINED ENV{MPICH_CXX}) + if ( ($ENV{OMPI_CXX} MATCHES "nvcc") OR ($ENV{MPICH_CXX} MATCHES "nvcc") ) + set(COMPILER_IS_NVCC true) + endif() +endif() + +# set CXX standard +set(CMAKE_CXX_STANDARD_REQUIRED True) +set(CMAKE_CXX_STANDARD 17) # Kodi Note: Changed from 11 to 17 +if (${COMPILER_IS_NVCC}) + # GNU CXX extensions are not supported by nvcc + set(CMAKE_CXX_EXTENSIONS OFF) +endif() + +############################################################################### +# COMPILER FLAGS +############################################################################### + +if (CMAKE_CXX_COMPILER_ID STREQUAL "Clang" OR CMAKE_COMPILER_IS_GNUCXX) + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wextra") + if (NOT ${COMPILER_IS_NVCC}) + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -pedantic-errors") + endif() + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -g") +endif() + +############################################################################### +# BACKWARD OBJECT +############################################################################### + +add_library(backward_object OBJECT backward.cpp) +target_compile_definitions(backward_object PRIVATE ${BACKWARD_DEFINITIONS}) +target_include_directories(backward_object PRIVATE ${BACKWARD_INCLUDE_DIRS}) +#set_target_properties(backward_object PROPERTIES POSITION_INDEPENDENT_CODE ON) +set(BACKWARD_ENABLE $ CACHE STRING + "Link with this object to setup backward automatically") + + +############################################################################### +# BACKWARD LIBRARY (Includes backward.cpp) +############################################################################### +option(BACKWARD_SHARED "Build dynamic backward-cpp shared lib" OFF) + +if(BACKWARD_SHARED) + set(libtype SHARED) +endif() +add_library(backward ${libtype} backward.cpp) +target_compile_definitions(backward PUBLIC ${BACKWARD_DEFINITIONS}) +target_include_directories(backward PUBLIC ${BACKWARD_INCLUDE_DIRS}) + +############################################################################### +# TESTS +############################################################################### + +if(BACKWARD_TESTS) + enable_testing() + + add_library(test_main OBJECT test/_test_main.cpp) + + macro(backward_add_test src) + get_filename_component(name ${src} NAME_WE) + set(test_name "test_${name}") + + add_executable(${test_name} ${src} ${ARGN} $) + + target_link_libraries(${test_name} PRIVATE Backward::Backward) + + add_test(NAME ${name} COMMAND ${test_name}) + endmacro() + + # Tests without backward.cpp + set(TESTS + test + stacktrace + rectrace + select_signals + ) + + foreach(test ${TESTS}) + backward_add_test(test/${test}.cpp) + endforeach() + + # Tests with backward.cpp + set(TESTS + suicide + ) + + foreach(test ${TESTS}) + backward_add_test(test/${test}.cpp ${BACKWARD_ENABLE}) + endforeach() +endif() + +install( + FILES "backward.hpp" + DESTINATION ${CMAKE_INSTALL_INCLUDEDIR} +) +install( + FILES "BackwardConfig.cmake" + DESTINATION ${CMAKE_INSTALL_LIBDIR}/backward +) diff --git a/xbmc/addons/kodi-dev-kit/src/addon_runner/third_party/backward-cpp/LICENSE.txt b/xbmc/addons/kodi-dev-kit/src/addon_runner/third_party/backward-cpp/LICENSE.txt new file mode 100644 index 0000000000000..269e8abbc0ffc --- /dev/null +++ b/xbmc/addons/kodi-dev-kit/src/addon_runner/third_party/backward-cpp/LICENSE.txt @@ -0,0 +1,21 @@ +Copyright 2013 Google Inc. All Rights Reserved. + +The MIT License (MIT) + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/xbmc/addons/kodi-dev-kit/src/addon_runner/third_party/backward-cpp/README.md b/xbmc/addons/kodi-dev-kit/src/addon_runner/third_party/backward-cpp/README.md new file mode 100644 index 0000000000000..031716f3da9fe --- /dev/null +++ b/xbmc/addons/kodi-dev-kit/src/addon_runner/third_party/backward-cpp/README.md @@ -0,0 +1,442 @@ +Backward-cpp [![badge](https://img.shields.io/badge/conan.io-backward%2F1.3.0-green.svg?logo=data:image/png;base64%2CiVBORw0KGgoAAAANSUhEUgAAAA4AAAAOCAMAAAAolt3jAAAA1VBMVEUAAABhlctjlstkl8tlmMtlmMxlmcxmmcxnmsxpnMxpnM1qnc1sn85voM91oM11oc1xotB2oc56pNF6pNJ2ptJ8ptJ8ptN9ptN8p9N5qNJ9p9N9p9R8qtOBqdSAqtOAqtR%2BrNSCrNJ/rdWDrNWCsNWCsNaJs9eLs9iRvNuVvdyVv9yXwd2Zwt6axN6dxt%2Bfx%2BChyeGiyuGjyuCjyuGly%2BGlzOKmzOGozuKoz%2BKqz%2BOq0OOv1OWw1OWw1eWx1eWy1uay1%2Baz1%2Baz1%2Bez2Oe02Oe12ee22ujUGwH3AAAAAXRSTlMAQObYZgAAAAFiS0dEAIgFHUgAAAAJcEhZcwAACxMAAAsTAQCanBgAAAAHdElNRQfgBQkREyOxFIh/AAAAiklEQVQI12NgAAMbOwY4sLZ2NtQ1coVKWNvoc/Eq8XDr2wB5Ig62ekza9vaOqpK2TpoMzOxaFtwqZua2Bm4makIM7OzMAjoaCqYuxooSUqJALjs7o4yVpbowvzSUy87KqSwmxQfnsrPISyFzWeWAXCkpMaBVIC4bmCsOdgiUKwh3JojLgAQ4ZCE0AMm2D29tZwe6AAAAAElFTkSuQmCC)](http://www.conan.io/source/backward/1.3.0/Manu343726/testing) +============ + +Backward is a beautiful stack trace pretty printer for C++. + +If you are bored to see this: + +![default trace](doc/rude.png) + +Backward will spice it up for you: + +![pretty stackstrace](doc/pretty.png) + +There is not much to say. Of course it will be able to display the code +snippets only if the source files are accessible (else see trace #4 in the +example). + +All "Source" lines and code snippet prefixed by a pipe "|" are frames inline +the next frame. +You can see that for the trace #1 in the example, the function +`you_shall_not_pass()` was inlined in the function `...read2::do_test()` by the +compiler. + +## Installation + +#### Install backward.hpp + +Backward is a header only library. So installing Backward is easy, simply drop +a copy of `backward.hpp` along with your other source files in your C++ project. +You can also use a git submodule or really any other way that best fits your +environment, as long as you can include `backward.hpp`. + +#### Install backward.cpp + +If you want Backward to automatically print a stack trace on most common fatal +errors (segfault, abort, un-handled exception...), simply add a copy of +`backward.cpp` to your project, and don't forget to tell your build system. + +The code in `backward.cpp` is trivial anyway, you can simply copy what it's +doing at your convenience. + +Note for [folly](https://github.com/facebook/folly) library users: must define `backward::SignalHandling sh;` after `folly::init(&argc, &argv);`. + +## Configuration & Dependencies + +### Integration with CMake + +If you are using CMake and want to use its configuration abilities to save +you the trouble, you can easily integrate Backward, depending on how you obtained +the library. + +#### As a subdirectory: + +In this case you have a subdirectory containing the whole repository of Backward +(eg.: using git-submodules), in this case you can do: + +``` +add_subdirectory(/path/to/backward-cpp) + +# This will add backward.cpp to your target +add_executable(mytarget mysource.cpp ${BACKWARD_ENABLE}) + +# This will add libraries, definitions and include directories needed by backward +# by setting each property on the target. +add_backward(mytarget) +``` + +#### Modifying CMAKE_MODULE_PATH + +In this case you can have Backward installed as a subdirectory: + +``` +list(APPEND CMAKE_MODULE_PATH /path/to/backward-cpp) +find_package(Backward) + +# This will add libraries, definitions and include directories needed by backward +# through an IMPORTED target. +target_link_libraries(mytarget PUBLIC Backward::Backward) +``` + +Notice that this is equivalent to using the the approach that uses `add_subdirectory()`, +however it uses cmake's [imported target](https://cmake.org/Wiki/CMake/Tutorials/Exporting_and_Importing_Targets) mechanism. + +#### Installation through a regular package manager + +In this case you have obtained Backward through a package manager. + +Packages currently available: +- [conda-forge](https://anaconda.org/conda-forge/backward-cpp) + +``` +find_package(Backward) + +# This will add libraries, definitions and include directories needed by backward +# through an IMPORTED target. +target_link_libraries(mytarget PUBLIC Backward::Backward) +``` +### Libraries to unwind the stack + +On Linux and macOS, backtrace can back-trace or "walk" the stack using the +following libraries: + +#### unwind + +Unwind comes from libgcc, but there is an equivalent inside clang itself. With +unwind, the stacktrace is as accurate as it can possibly be, since this is +used by the C++ runtine in gcc/clang for stack unwinding on exception. + +Normally libgcc is already linked to your program by default. + +#### libunwind from the [libunwind project](https://github.com/libunwind/libunwind) + + apt-get install binutils-dev (or equivalent) + +Libunwind provides, in some cases, a more accurate stacktrace as it knows +to decode signal handler frames and lets us edit the context registers when +unwinding, allowing stack traces over bad function references. + +For best results make sure you are using libunwind 1.3 or later, which added +`unw_init_local2` and support for handling signal frames. + +CMake will warn you when configuring if your libunwind version doesn't support +signal frames. + +On macOS clang provides a libunwind API compatible library as part of its +environment, so no third party libraries are necessary. + +### Compile with debug info + +You need to compile your project with generation of debug symbols enabled, +usually `-g` with clang++ and g++. + +Note that you can use `-g` with any level of optimization, with modern debug +information encoding like DWARF, it only takes space in the binary (it's not +loaded in memory until your debugger or Backward makes use of it, don't worry), +and it doesn't impact the code generation (at least on GNU/Linux x86\_64 for +what I know). + +If you are missing debug information, the stack trace will lack details about +your sources. + +### Libraries to read the debug info + +Backward supports pretty printed stack traces on GNU/Linux, macOS and Windows, +it will compile fine under other platforms but will not do anything. **Pull +requests are welcome :)** + +Also, by default you will get a really basic stack trace, based on the +`backtrace_symbols` API: + +![default trace](doc/nice.png) + +You will need to install some dependencies to get the ultimate stack trace. +Three libraries are currently supported, the only difference is which one is the +easiest for you to install, so pick your poison: + +#### libbfd from the [GNU/binutils](http://www.gnu.org/software/binutils/) + + apt-get install binutils-dev (or equivalent) + +And do not forget to link with the lib: `g++/clang++ -lbfd -ldl ...` + +This library requires dynamic loading. Which is provided by the library `dl`. +Hence why we also link with `-ldl`. + +Then define the following before every inclusion of `backward.hpp` (don't +forget to update `backward.cpp` as well): + + #define BACKWARD_HAS_BFD 1 + +#### libdw from the [elfutils](https://fedorahosted.org/elfutils/) + + apt-get install libdw-dev (or equivalent) + +And do not forget to link with the lib and inform Backward to use it: + + #define BACKWARD_HAS_DW 1 + +Of course you can simply add the define (`-DBACKWARD_HAS_...=1`) and the +linkage details in your build system and even auto-detect which library is +installed, it's up to you. + +#### [libdwarf](https://sourceforge.net/projects/libdwarf/) and [libelf](http://www.mr511.de/software/english.html) + + apt-get install libdwarf-dev (or equivalent) + +And do not forget to link with the lib and inform Backward to use it: + + #define BACKWARD_HAS_DWARF 1 + +There are several alternative implementations of libdwarf and libelf that +are API compatible so it's possible, although it hasn't been tested, to +replace the ones used when developing backward (in bold, below): + +* **_libelf_** by [Michael "Tired" Riepe](http://www.mr511.de/software/english.html) +* **_libdwarf_** by [David Anderson](https://www.prevanders.net/dwarf.html) +* libelf from [elfutils](https://fedorahosted.org/elfutils/) +* libelf and libdwarf from FreeBSD's [ELF Tool Chain](https://sourceforge.net/p/elftoolchain/wiki/Home/) project + + +Of course you can simply add the define (`-DBACKWARD_HAS_...=1`) and the +linkage details in your build system and even auto-detect which library is +installed, it's up to you. + +That's it, you are all set, you should be getting nice stack traces like the +one at the beginning of this document. + +## API + +If you don't want to limit yourself to the defaults offered by `backward.cpp`, +and you want to take some random stack traces for whatever reason and pretty +print them the way you love or you decide to send them all to your buddies over +the Internet, you will appreciate the simplicity of Backward's API. + +### Stacktrace + +The StackTrace class lets you take a "snapshot" of the current stack. +You can use it like this: + +```c++ +using namespace backward; +StackTrace st; st.load_here(32); +Printer p; p.print(st); +``` + +The public methods are: + +```c++ +class StackTrace { public: + // Take a snapshot of the current stack, with at most "trace_cnt_max" + // traces in it. The first trace is the most recent (ie the current + // frame). You can also provide a trace address to load_from() assuming + // the address is a valid stack frame (useful for signal handling traces). + // Both function return size(). + size_t load_here(size_t trace_cnt_max) + size_t load_from(void* address, size_t trace_cnt_max) + + // The number of traces loaded. This can be less than "trace_cnt_max". + size_t size() const + + // A unique id for the thread in which the trace was taken. The value + // 0 means the stack trace comes from the main thread. + size_t thread_id() const + + // Retrieve a trace by index. 0 is the most recent trace, size()-1 is + // the oldest one. + Trace operator[](size_t trace_idx) +}; +``` + +### TraceResolver + +The `TraceResolver` does the heavy lifting, and intends to transform a simple +`Trace` from its address into a fully detailed `ResolvedTrace` with the +filename of the source, line numbers, inlined functions and so on. + +You can use it like this: + +```c++ +using namespace backward; +StackTrace st; st.load_here(32); + +TraceResolver tr; tr.load_stacktrace(st); +for (size_t i = 0; i < st.size(); ++i) { + ResolvedTrace trace = tr.resolve(st[i]); + std::cout << "#" << i + << " " << trace.object_filename + << " " << trace.object_function + << " [" << trace.addr << "]" + << std::endl; +} +``` + +The public methods are: + +```c++ +class TraceResolver { public: + // Pre-load whatever is necessary from the stack trace. + template + void load_stacktrace(ST&) + + // Resolve a trace. It takes a ResolvedTrace, because a `Trace` is + // implicitly convertible to it. + ResolvedTrace resolve(ResolvedTrace t) +}; +``` + +### SnippetFactory + +The SnippetFactory is a simple helper class to automatically load and cache +source files in order to extract code snippets. + +```c++ +class SnippetFactory { public: + // A snippet is a list of line numbers and line contents. + typedef std::vector > lines_t; + + // Return a snippet starting at line_start with up to context_size lines. + lines_t get_snippet(const std::string& filename, + size_t line_start, size_t context_size) + + // Return a combined snippet from two different locations and combine them. + // context_size / 2 lines will be extracted from each location. + lines_t get_combined_snippet( + const std::string& filename_a, size_t line_a, + const std::string& filename_b, size_t line_b, + size_t context_size) + + // Tries to return a unified snippet if the two locations from the same + // file are close enough to fit inside one context_size, else returns + // the equivalent of get_combined_snippet(). + lines_t get_coalesced_snippet(const std::string& filename, + size_t line_a, size_t line_b, size_t context_size) +``` + +### Printer + +A simpler way to pretty print a stack trace to the terminal. It will +automatically resolve the traces for you: + +```c++ +using namespace backward; +StackTrace st; st.load_here(32); +Printer p; +p.object = true; +p.color_mode = ColorMode::always; +p.address = true; +p.print(st, stderr); +``` + +You can set a few options: + +```c++ +class Printer { public: + // Print a little snippet of code if possible. + bool snippet = true; + + // Colorize the trace + // - ColorMode::automatic: Activate colors if possible. For example, when using a TTY on linux. + // - ColorMode::always: Always use colors. + // - ColorMode::never: Never use colors. + bool color_mode = ColorMode::automatic; + + // Add the addresses of every source location to the trace. + bool address = false; + + // Even if there is a source location, also prints the object + // from where the trace came from. + bool object = false; + + // Resolve and print a stack trace to the given C FILE* object. + // On linux, if the FILE* object is attached to a TTY, + // color will be used if color_mode is set to automatic. + template + FILE* print(StackTrace& st, FILE* fp = stderr); + + // Resolve and print a stack trace to the given std::ostream object. + // Color will only be used if color_mode is set to always. + template + std::ostream& print(ST& st, std::ostream& os); +``` + + +### SignalHandling + +A simple helper class that registers for you the most common signals and other +callbacks to segfault, hardware exception, un-handled exception etc. + +`backward.cpp` simply uses it like that: + +```c++ +backward::SignalHandling sh; +``` + +Creating the object registers all the different signals and hooks. Destroying +this object doesn't do anything. It exposes only one method: + +```c++ +bool loaded() const // true if loaded with success +``` + +### Trace object + +To keep the memory footprint of a loaded `StackTrace` on the low-side, there a +hierarchy of trace object, from a minimal `Trace `to a `ResolvedTrace`. + +#### Simple trace + +```c++ +struct Trace { + void* addr; // address of the trace + size_t idx; // its index (0 == most recent) +}; +``` + +#### Resolved trace + +A `ResolvedTrace` should contains a maximum of details about the location of +the trace in the source code. Note that not all fields might be set. + +```c++ +struct ResolvedTrace: public Trace { + + struct SourceLoc { + std::string function; + std::string filename; + size_t line; + size_t col; + }; + + // In which binary object this trace is located. + std::string object_filename; + + // The function in the object that contains the trace. This is not the same + // as source.function which can be an function inlined in object_function. + std::string object_function; + + // The source location of this trace. It is possible for filename to be + // empty and for line/col to be invalid (value 0) if this information + // couldn't be deduced, for example if there is no debug information in the + // binary object. + SourceLoc source; + + // An optional list of "inliners". All of these sources locations where + // inlined in the source location of the trace (the attribute right above). + // This is especially useful when you compile with optimizations turned on. + typedef std::vector source_locs_t; + source_locs_t inliners; +}; +``` + +## Contact and copyright + +François-Xavier Bourlet + +Copyright 2013-2017 Google Inc. All Rights Reserved. +MIT License. + +### Disclaimer + +Although this project is owned by Google Inc. this is not a Google supported or +affiliated project. diff --git a/xbmc/addons/kodi-dev-kit/src/addon_runner/third_party/backward-cpp/backward.cpp b/xbmc/addons/kodi-dev-kit/src/addon_runner/third_party/backward-cpp/backward.cpp new file mode 100644 index 0000000000000..110441cba92c2 --- /dev/null +++ b/xbmc/addons/kodi-dev-kit/src/addon_runner/third_party/backward-cpp/backward.cpp @@ -0,0 +1,42 @@ +// Pick your poison. +// +// On GNU/Linux, you have few choices to get the most out of your stack trace. +// +// By default you get: +// - object filename +// - function name +// +// In order to add: +// - source filename +// - line and column numbers +// - source code snippet (assuming the file is accessible) + +// Install one of the following libraries then uncomment one of the macro (or +// better, add the detection of the lib and the macro definition in your build +// system) + +// - apt-get install libdw-dev ... +// - g++/clang++ -ldw ... +// #define BACKWARD_HAS_DW 1 + +// - apt-get install binutils-dev ... +// - g++/clang++ -lbfd ... +// #define BACKWARD_HAS_BFD 1 + +// - apt-get install libdwarf-dev ... +// - g++/clang++ -ldwarf ... +// #define BACKWARD_HAS_DWARF 1 + +// Regardless of the library you choose to read the debug information, +// for potentially more detailed stack traces you can use libunwind +// - apt-get install libunwind-dev +// - g++/clang++ -lunwind +// #define BACKWARD_HAS_LIBUNWIND 1 + +#include "backward.hpp" + +namespace backward { + +backward::SignalHandling sh; + +} // namespace backward diff --git a/xbmc/addons/kodi-dev-kit/src/addon_runner/third_party/backward-cpp/backward.hpp b/xbmc/addons/kodi-dev-kit/src/addon_runner/third_party/backward-cpp/backward.hpp new file mode 100644 index 0000000000000..19d5ba31c4b36 --- /dev/null +++ b/xbmc/addons/kodi-dev-kit/src/addon_runner/third_party/backward-cpp/backward.hpp @@ -0,0 +1,4471 @@ +/* + * backward.hpp + * Copyright 2013 Google Inc. All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifndef H_6B9572DA_A64B_49E6_B234_051480991C89 +#define H_6B9572DA_A64B_49E6_B234_051480991C89 + +#ifndef __cplusplus +#error "It's not going to compile without a C++ compiler..." +#endif + +#if defined(BACKWARD_CXX11) +#elif defined(BACKWARD_CXX98) +#else +#if __cplusplus >= 201103L || (defined(_MSC_VER) && _MSC_VER >= 1800) +#define BACKWARD_CXX11 +#define BACKWARD_ATLEAST_CXX11 +#define BACKWARD_ATLEAST_CXX98 +#if __cplusplus >= 201703L || (defined(_MSVC_LANG) && _MSVC_LANG >= 201703L) +#define BACKWARD_ATLEAST_CXX17 +#endif +#else +#define BACKWARD_CXX98 +#define BACKWARD_ATLEAST_CXX98 +#endif +#endif + +// You can define one of the following (or leave it to the auto-detection): +// +// #define BACKWARD_SYSTEM_LINUX +// - specialization for linux +// +// #define BACKWARD_SYSTEM_DARWIN +// - specialization for Mac OS X 10.5 and later. +// +// #define BACKWARD_SYSTEM_WINDOWS +// - specialization for Windows (Clang 9 and MSVC2017) +// +// #define BACKWARD_SYSTEM_UNKNOWN +// - placebo implementation, does nothing. +// +#if defined(BACKWARD_SYSTEM_LINUX) +#elif defined(BACKWARD_SYSTEM_DARWIN) +#elif defined(BACKWARD_SYSTEM_UNKNOWN) +#elif defined(BACKWARD_SYSTEM_WINDOWS) +#else +#if defined(__linux) || defined(__linux__) +#define BACKWARD_SYSTEM_LINUX +#elif defined(__APPLE__) +#define BACKWARD_SYSTEM_DARWIN +#elif defined(_WIN32) +#define BACKWARD_SYSTEM_WINDOWS +#else +#define BACKWARD_SYSTEM_UNKNOWN +#endif +#endif + +#define NOINLINE __attribute__((noinline)) + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#if defined(BACKWARD_SYSTEM_LINUX) + +// On linux, backtrace can back-trace or "walk" the stack using the following +// libraries: +// +// #define BACKWARD_HAS_UNWIND 1 +// - unwind comes from libgcc, but I saw an equivalent inside clang itself. +// - with unwind, the stacktrace is as accurate as it can possibly be, since +// this is used by the C++ runtine in gcc/clang for stack unwinding on +// exception. +// - normally libgcc is already linked to your program by default. +// +// #define BACKWARD_HAS_LIBUNWIND 1 +// - libunwind provides, in some cases, a more accurate stacktrace as it knows +// to decode signal handler frames and lets us edit the context registers when +// unwinding, allowing stack traces over bad function references. +// +// #define BACKWARD_HAS_BACKTRACE == 1 +// - backtrace seems to be a little bit more portable than libunwind, but on +// linux, it uses unwind anyway, but abstract away a tiny information that is +// sadly really important in order to get perfectly accurate stack traces. +// - backtrace is part of the (e)glib library. +// +// The default is: +// #define BACKWARD_HAS_UNWIND == 1 +// +// Note that only one of the define should be set to 1 at a time. +// +#if BACKWARD_HAS_UNWIND == 1 +#elif BACKWARD_HAS_LIBUNWIND == 1 +#elif BACKWARD_HAS_BACKTRACE == 1 +#else +#undef BACKWARD_HAS_UNWIND +#define BACKWARD_HAS_UNWIND 1 +#undef BACKWARD_HAS_LIBUNWIND +#define BACKWARD_HAS_LIBUNWIND 0 +#undef BACKWARD_HAS_BACKTRACE +#define BACKWARD_HAS_BACKTRACE 0 +#endif + +// On linux, backward can extract detailed information about a stack trace +// using one of the following libraries: +// +// #define BACKWARD_HAS_DW 1 +// - libdw gives you the most juicy details out of your stack traces: +// - object filename +// - function name +// - source filename +// - line and column numbers +// - source code snippet (assuming the file is accessible) +// - variable names (if not optimized out) +// - variable values (not supported by backward-cpp) +// - You need to link with the lib "dw": +// - apt-get install libdw-dev +// - g++/clang++ -ldw ... +// +// #define BACKWARD_HAS_BFD 1 +// - With libbfd, you get a fair amount of details: +// - object filename +// - function name +// - source filename +// - line numbers +// - source code snippet (assuming the file is accessible) +// - You need to link with the lib "bfd": +// - apt-get install binutils-dev +// - g++/clang++ -lbfd ... +// +// #define BACKWARD_HAS_DWARF 1 +// - libdwarf gives you the most juicy details out of your stack traces: +// - object filename +// - function name +// - source filename +// - line and column numbers +// - source code snippet (assuming the file is accessible) +// - variable names (if not optimized out) +// - variable values (not supported by backward-cpp) +// - You need to link with the lib "dwarf": +// - apt-get install libdwarf-dev +// - g++/clang++ -ldwarf ... +// +// #define BACKWARD_HAS_BACKTRACE_SYMBOL 1 +// - backtrace provides minimal details for a stack trace: +// - object filename +// - function name +// - backtrace is part of the (e)glib library. +// +// The default is: +// #define BACKWARD_HAS_BACKTRACE_SYMBOL == 1 +// +// Note that only one of the define should be set to 1 at a time. +// +#if BACKWARD_HAS_DW == 1 +#elif BACKWARD_HAS_BFD == 1 +#elif BACKWARD_HAS_DWARF == 1 +#elif BACKWARD_HAS_BACKTRACE_SYMBOL == 1 +#else +#undef BACKWARD_HAS_DW +#define BACKWARD_HAS_DW 0 +#undef BACKWARD_HAS_BFD +#define BACKWARD_HAS_BFD 0 +#undef BACKWARD_HAS_DWARF +#define BACKWARD_HAS_DWARF 0 +#undef BACKWARD_HAS_BACKTRACE_SYMBOL +#define BACKWARD_HAS_BACKTRACE_SYMBOL 1 +#endif + +#include +#include +#ifdef __ANDROID__ +// Old Android API levels define _Unwind_Ptr in both link.h and +// unwind.h Rename the one in link.h as we are not going to be using +// it +#define _Unwind_Ptr _Unwind_Ptr_Custom +#include +#undef _Unwind_Ptr +#else +#include +#endif +#if defined(__ppc__) || defined(__powerpc) || defined(__powerpc__) || \ + defined(__POWERPC__) +// Linux kernel header required for the struct pt_regs definition +// to access the NIP (Next Instruction Pointer) register value +#include +#endif +#include +#include +#include +#include + +#if BACKWARD_HAS_BFD == 1 +// NOTE: defining PACKAGE{,_VERSION} is required before including +// bfd.h on some platforms, see also: +// https://sourceware.org/bugzilla/show_bug.cgi?id=14243 +#ifndef PACKAGE +#define PACKAGE +#endif +#ifndef PACKAGE_VERSION +#define PACKAGE_VERSION +#endif +#include +#ifndef _GNU_SOURCE +#define _GNU_SOURCE +#include +#undef _GNU_SOURCE +#else +#include +#endif +#endif + +#if BACKWARD_HAS_DW == 1 +#include +#include +#include +#endif + +#if BACKWARD_HAS_DWARF == 1 +#include +#include +#include +#include +#include +#ifndef _GNU_SOURCE +#define _GNU_SOURCE +#include +#undef _GNU_SOURCE +#else +#include +#endif +#endif + +#if (BACKWARD_HAS_BACKTRACE == 1) || (BACKWARD_HAS_BACKTRACE_SYMBOL == 1) +// then we shall rely on backtrace +#include +#endif + +#endif // defined(BACKWARD_SYSTEM_LINUX) + +#if defined(BACKWARD_SYSTEM_DARWIN) +// On Darwin, backtrace can back-trace or "walk" the stack using the following +// libraries: +// +// #define BACKWARD_HAS_UNWIND 1 +// - unwind comes from libgcc, but I saw an equivalent inside clang itself. +// - with unwind, the stacktrace is as accurate as it can possibly be, since +// this is used by the C++ runtine in gcc/clang for stack unwinding on +// exception. +// - normally libgcc is already linked to your program by default. +// +// #define BACKWARD_HAS_LIBUNWIND 1 +// - libunwind comes from clang, which implements an API compatible version. +// - libunwind provides, in some cases, a more accurate stacktrace as it knows +// to decode signal handler frames and lets us edit the context registers when +// unwinding, allowing stack traces over bad function references. +// +// #define BACKWARD_HAS_BACKTRACE == 1 +// - backtrace is available by default, though it does not produce as much +// information as another library might. +// +// The default is: +// #define BACKWARD_HAS_UNWIND == 1 +// +// Note that only one of the define should be set to 1 at a time. +// +#if BACKWARD_HAS_UNWIND == 1 +#elif BACKWARD_HAS_BACKTRACE == 1 +#elif BACKWARD_HAS_LIBUNWIND == 1 +#else +#undef BACKWARD_HAS_UNWIND +#define BACKWARD_HAS_UNWIND 1 +#undef BACKWARD_HAS_BACKTRACE +#define BACKWARD_HAS_BACKTRACE 0 +#undef BACKWARD_HAS_LIBUNWIND +#define BACKWARD_HAS_LIBUNWIND 0 +#endif + +// On Darwin, backward can extract detailed information about a stack trace +// using one of the following libraries: +// +// #define BACKWARD_HAS_BACKTRACE_SYMBOL 1 +// - backtrace provides minimal details for a stack trace: +// - object filename +// - function name +// +// The default is: +// #define BACKWARD_HAS_BACKTRACE_SYMBOL == 1 +// +#if BACKWARD_HAS_BACKTRACE_SYMBOL == 1 +#else +#undef BACKWARD_HAS_BACKTRACE_SYMBOL +#define BACKWARD_HAS_BACKTRACE_SYMBOL 1 +#endif + +#include +#include +#include +#include +#include +#include + +#if (BACKWARD_HAS_BACKTRACE == 1) || (BACKWARD_HAS_BACKTRACE_SYMBOL == 1) +#include +#endif +#endif // defined(BACKWARD_SYSTEM_DARWIN) + +#if defined(BACKWARD_SYSTEM_WINDOWS) + +#include +#include +#include + +#include +typedef SSIZE_T ssize_t; + +#ifndef NOMINMAX +#define NOMINMAX +#endif +#include +#include + +#include +#include + +#ifndef __clang__ +#undef NOINLINE +#define NOINLINE __declspec(noinline) +#endif + +#ifdef _MSC_VER +#pragma comment(lib, "psapi.lib") +#pragma comment(lib, "dbghelp.lib") +#endif + +// Comment / packing is from stackoverflow: +// https://stackoverflow.com/questions/6205981/windows-c-stack-trace-from-a-running-app/28276227#28276227 +// Some versions of imagehlp.dll lack the proper packing directives themselves +// so we need to do it. +#pragma pack(push, before_imagehlp, 8) +#include +#pragma pack(pop, before_imagehlp) + +// TODO maybe these should be undefined somewhere else? +#undef BACKWARD_HAS_UNWIND +#undef BACKWARD_HAS_BACKTRACE +#if BACKWARD_HAS_PDB_SYMBOL == 1 +#else +#undef BACKWARD_HAS_PDB_SYMBOL +#define BACKWARD_HAS_PDB_SYMBOL 1 +#endif + +#endif + +#if BACKWARD_HAS_UNWIND == 1 + +#include +// while gcc's unwind.h defines something like that: +// extern _Unwind_Ptr _Unwind_GetIP (struct _Unwind_Context *); +// extern _Unwind_Ptr _Unwind_GetIPInfo (struct _Unwind_Context *, int *); +// +// clang's unwind.h defines something like this: +// uintptr_t _Unwind_GetIP(struct _Unwind_Context* __context); +// +// Even if the _Unwind_GetIPInfo can be linked to, it is not declared, worse we +// cannot just redeclare it because clang's unwind.h doesn't define _Unwind_Ptr +// anyway. +// +// Luckily we can play on the fact that the guard macros have a different name: +#ifdef __CLANG_UNWIND_H +// In fact, this function still comes from libgcc (on my different linux boxes, +// clang links against libgcc). +#include +extern "C" uintptr_t _Unwind_GetIPInfo(_Unwind_Context *, int *); +#endif + +#endif // BACKWARD_HAS_UNWIND == 1 + +#if BACKWARD_HAS_LIBUNWIND == 1 +#define UNW_LOCAL_ONLY +#include +#endif // BACKWARD_HAS_LIBUNWIND == 1 + +#ifdef BACKWARD_ATLEAST_CXX11 +#include +#include // for std::swap +namespace backward { +namespace details { +template struct hashtable { + typedef std::unordered_map type; +}; +using std::move; +} // namespace details +} // namespace backward +#else // NOT BACKWARD_ATLEAST_CXX11 +#define nullptr NULL +#define override +#include +namespace backward { +namespace details { +template struct hashtable { + typedef std::map type; +}; +template const T &move(const T &v) { return v; } +template T &move(T &v) { return v; } +} // namespace details +} // namespace backward +#endif // BACKWARD_ATLEAST_CXX11 + +namespace backward { +namespace details { +#if defined(BACKWARD_SYSTEM_WINDOWS) +const char kBackwardPathDelimiter[] = ";"; +#else +const char kBackwardPathDelimiter[] = ":"; +#endif +} // namespace details +} // namespace backward + +namespace backward { + +namespace system_tag { +struct linux_tag; // seems that I cannot call that "linux" because the name +// is already defined... so I am adding _tag everywhere. +struct darwin_tag; +struct windows_tag; +struct unknown_tag; + +#if defined(BACKWARD_SYSTEM_LINUX) +typedef linux_tag current_tag; +#elif defined(BACKWARD_SYSTEM_DARWIN) +typedef darwin_tag current_tag; +#elif defined(BACKWARD_SYSTEM_WINDOWS) +typedef windows_tag current_tag; +#elif defined(BACKWARD_SYSTEM_UNKNOWN) +typedef unknown_tag current_tag; +#else +#error "May I please get my system defines?" +#endif +} // namespace system_tag + +namespace trace_resolver_tag { +#if defined(BACKWARD_SYSTEM_LINUX) +struct libdw; +struct libbfd; +struct libdwarf; +struct backtrace_symbol; + +#if BACKWARD_HAS_DW == 1 +typedef libdw current; +#elif BACKWARD_HAS_BFD == 1 +typedef libbfd current; +#elif BACKWARD_HAS_DWARF == 1 +typedef libdwarf current; +#elif BACKWARD_HAS_BACKTRACE_SYMBOL == 1 +typedef backtrace_symbol current; +#else +#error "You shall not pass, until you know what you want." +#endif +#elif defined(BACKWARD_SYSTEM_DARWIN) +struct backtrace_symbol; + +#if BACKWARD_HAS_BACKTRACE_SYMBOL == 1 +typedef backtrace_symbol current; +#else +#error "You shall not pass, until you know what you want." +#endif +#elif defined(BACKWARD_SYSTEM_WINDOWS) +struct pdb_symbol; +#if BACKWARD_HAS_PDB_SYMBOL == 1 +typedef pdb_symbol current; +#else +#error "You shall not pass, until you know what you want." +#endif +#endif +} // namespace trace_resolver_tag + +namespace details { + +template struct rm_ptr { typedef T type; }; + +template struct rm_ptr { typedef T type; }; + +template struct rm_ptr { typedef const T type; }; + +template struct deleter { + template void operator()(U &ptr) const { (*F)(ptr); } +}; + +template struct default_delete { + void operator()(T &ptr) const { delete ptr; } +}; + +template > +class handle { + struct dummy; + T _val; + bool _empty; + +#ifdef BACKWARD_ATLEAST_CXX11 + handle(const handle &) = delete; + handle &operator=(const handle &) = delete; +#endif + +public: + ~handle() { + if (!_empty) { + Deleter()(_val); + } + } + + explicit handle() : _val(), _empty(true) {} + explicit handle(T val) : _val(val), _empty(false) { + if (!_val) + _empty = true; + } + +#ifdef BACKWARD_ATLEAST_CXX11 + handle(handle &&from) : _empty(true) { swap(from); } + handle &operator=(handle &&from) { + swap(from); + return *this; + } +#else + explicit handle(const handle &from) : _empty(true) { + // some sort of poor man's move semantic. + swap(const_cast(from)); + } + handle &operator=(const handle &from) { + // some sort of poor man's move semantic. + swap(const_cast(from)); + return *this; + } +#endif + + void reset(T new_val) { + handle tmp(new_val); + swap(tmp); + } + + void update(T new_val) { + _val = new_val; + _empty = !static_cast(new_val); + } + + operator const dummy *() const { + if (_empty) { + return nullptr; + } + return reinterpret_cast(_val); + } + T get() { return _val; } + T release() { + _empty = true; + return _val; + } + void swap(handle &b) { + using std::swap; + swap(b._val, _val); // can throw, we are safe here. + swap(b._empty, _empty); // should not throw: if you cannot swap two + // bools without throwing... It's a lost cause anyway! + } + + T &operator->() { return _val; } + const T &operator->() const { return _val; } + + typedef typename rm_ptr::type &ref_t; + typedef const typename rm_ptr::type &const_ref_t; + ref_t operator*() { return *_val; } + const_ref_t operator*() const { return *_val; } + ref_t operator[](size_t idx) { return _val[idx]; } + + // Watch out, we've got a badass over here + T *operator&() { + _empty = false; + return &_val; + } +}; + +// Default demangler implementation (do nothing). +template struct demangler_impl { + static std::string demangle(const char *funcname) { return funcname; } +}; + +#if defined(BACKWARD_SYSTEM_LINUX) || defined(BACKWARD_SYSTEM_DARWIN) + +template <> struct demangler_impl { + demangler_impl() : _demangle_buffer_length(0) {} + + std::string demangle(const char *funcname) { + using namespace details; + char *result = abi::__cxa_demangle(funcname, _demangle_buffer.get(), + &_demangle_buffer_length, nullptr); + if (result) { + _demangle_buffer.update(result); + return result; + } + return funcname; + } + +private: + details::handle _demangle_buffer; + size_t _demangle_buffer_length; +}; + +#endif // BACKWARD_SYSTEM_LINUX || BACKWARD_SYSTEM_DARWIN + +struct demangler : public demangler_impl {}; + +// Split a string on the platform's PATH delimiter. Example: if delimiter +// is ":" then: +// "" --> [] +// ":" --> ["",""] +// "::" --> ["","",""] +// "/a/b/c" --> ["/a/b/c"] +// "/a/b/c:/d/e/f" --> ["/a/b/c","/d/e/f"] +// etc. +inline std::vector split_source_prefixes(const std::string &s) { + std::vector out; + size_t last = 0; + size_t next = 0; + size_t delimiter_size = sizeof(kBackwardPathDelimiter) - 1; + while ((next = s.find(kBackwardPathDelimiter, last)) != std::string::npos) { + out.push_back(s.substr(last, next - last)); + last = next + delimiter_size; + } + if (last <= s.length()) { + out.push_back(s.substr(last)); + } + return out; +} + +} // namespace details + +/*************** A TRACE ***************/ + +struct Trace { + void *addr; + size_t idx; + + Trace() : addr(nullptr), idx(0) {} + + explicit Trace(void *_addr, size_t _idx) : addr(_addr), idx(_idx) {} +}; + +struct ResolvedTrace : public Trace { + + struct SourceLoc { + std::string function; + std::string filename; + unsigned line; + unsigned col; + + SourceLoc() : line(0), col(0) {} + + bool operator==(const SourceLoc &b) const { + return function == b.function && filename == b.filename && + line == b.line && col == b.col; + } + + bool operator!=(const SourceLoc &b) const { return !(*this == b); } + }; + + // In which binary object this trace is located. + std::string object_filename; + + // The function in the object that contain the trace. This is not the same + // as source.function which can be an function inlined in object_function. + std::string object_function; + + // The source location of this trace. It is possible for filename to be + // empty and for line/col to be invalid (value 0) if this information + // couldn't be deduced, for example if there is no debug information in the + // binary object. + SourceLoc source; + + // An optionals list of "inliners". All the successive sources location + // from where the source location of the trace (the attribute right above) + // is inlined. It is especially useful when you compiled with optimization. + typedef std::vector source_locs_t; + source_locs_t inliners; + + ResolvedTrace() : Trace() {} + ResolvedTrace(const Trace &mini_trace) : Trace(mini_trace) {} +}; + +/*************** STACK TRACE ***************/ + +// default implemention. +template class StackTraceImpl { +public: + size_t size() const { return 0; } + Trace operator[](size_t) const { return Trace(); } + size_t load_here(size_t = 0) { return 0; } + size_t load_from(void *, size_t = 0, void * = nullptr, void * = nullptr) { + return 0; + } + size_t thread_id() const { return 0; } + void skip_n_firsts(size_t) {} +}; + +class StackTraceImplBase { +public: + StackTraceImplBase() + : _thread_id(0), _skip(0), _context(nullptr), _error_addr(nullptr) {} + + size_t thread_id() const { return _thread_id; } + + void skip_n_firsts(size_t n) { _skip = n; } + +protected: + void load_thread_info() { +#ifdef BACKWARD_SYSTEM_LINUX +#ifndef __ANDROID__ + _thread_id = static_cast(syscall(SYS_gettid)); +#else + _thread_id = static_cast(gettid()); +#endif + if (_thread_id == static_cast(getpid())) { + // If the thread is the main one, let's hide that. + // I like to keep little secret sometimes. + _thread_id = 0; + } +#elif defined(BACKWARD_SYSTEM_DARWIN) + _thread_id = reinterpret_cast(pthread_self()); + if (pthread_main_np() == 1) { + // If the thread is the main one, let's hide that. + _thread_id = 0; + } +#endif + } + + void set_context(void *context) { _context = context; } + void *context() const { return _context; } + + void set_error_addr(void *error_addr) { _error_addr = error_addr; } + void *error_addr() const { return _error_addr; } + + size_t skip_n_firsts() const { return _skip; } + +private: + size_t _thread_id; + size_t _skip; + void *_context; + void *_error_addr; +}; + +class StackTraceImplHolder : public StackTraceImplBase { +public: + size_t size() const { + return (_stacktrace.size() >= skip_n_firsts()) + ? _stacktrace.size() - skip_n_firsts() + : 0; + } + Trace operator[](size_t idx) const { + if (idx >= size()) { + return Trace(); + } + return Trace(_stacktrace[idx + skip_n_firsts()], idx); + } + void *const *begin() const { + if (size()) { + return &_stacktrace[skip_n_firsts()]; + } + return nullptr; + } + +protected: + std::vector _stacktrace; +}; + +#if BACKWARD_HAS_UNWIND == 1 + +namespace details { + +template class Unwinder { +public: + size_t operator()(F &f, size_t depth) { + _f = &f; + _index = -1; + _depth = depth; + _Unwind_Backtrace(&this->backtrace_trampoline, this); + return static_cast(_index); + } + +private: + F *_f; + ssize_t _index; + size_t _depth; + + static _Unwind_Reason_Code backtrace_trampoline(_Unwind_Context *ctx, + void *self) { + return (static_cast(self))->backtrace(ctx); + } + + _Unwind_Reason_Code backtrace(_Unwind_Context *ctx) { + if (_index >= 0 && static_cast(_index) >= _depth) + return _URC_END_OF_STACK; + + int ip_before_instruction = 0; + uintptr_t ip = _Unwind_GetIPInfo(ctx, &ip_before_instruction); + + if (!ip_before_instruction) { + // calculating 0-1 for unsigned, looks like a possible bug to sanitiziers, + // so let's do it explicitly: + if (ip == 0) { + ip = std::numeric_limits::max(); // set it to 0xffff... (as + // from casting 0-1) + } else { + ip -= 1; // else just normally decrement it (no overflow/underflow will + // happen) + } + } + + if (_index >= 0) { // ignore first frame. + (*_f)(static_cast(_index), reinterpret_cast(ip)); + } + _index += 1; + return _URC_NO_REASON; + } +}; + +template size_t unwind(F f, size_t depth) { + Unwinder unwinder; + return unwinder(f, depth); +} + +} // namespace details + +template <> +class StackTraceImpl : public StackTraceImplHolder { +public: + NOINLINE + size_t load_here(size_t depth = 32, void *context = nullptr, + void *error_addr = nullptr) { + load_thread_info(); + set_context(context); + set_error_addr(error_addr); + if (depth == 0) { + return 0; + } + _stacktrace.resize(depth); + size_t trace_cnt = details::unwind(callback(*this), depth); + _stacktrace.resize(trace_cnt); + skip_n_firsts(0); + return size(); + } + size_t load_from(void *addr, size_t depth = 32, void *context = nullptr, + void *error_addr = nullptr) { + load_here(depth + 8, context, error_addr); + + for (size_t i = 0; i < _stacktrace.size(); ++i) { + if (_stacktrace[i] == addr) { + skip_n_firsts(i); + break; + } + } + + _stacktrace.resize(std::min(_stacktrace.size(), skip_n_firsts() + depth)); + return size(); + } + +private: + struct callback { + StackTraceImpl &self; + callback(StackTraceImpl &_self) : self(_self) {} + + void operator()(size_t idx, void *addr) { self._stacktrace[idx] = addr; } + }; +}; + +#elif BACKWARD_HAS_LIBUNWIND == 1 + +template <> +class StackTraceImpl : public StackTraceImplHolder { +public: + __attribute__((noinline)) size_t load_here(size_t depth = 32, + void *_context = nullptr, + void *_error_addr = nullptr) { + set_context(_context); + set_error_addr(_error_addr); + load_thread_info(); + if (depth == 0) { + return 0; + } + _stacktrace.resize(depth + 1); + + int result = 0; + + unw_context_t ctx; + size_t index = 0; + + // Add the tail call. If the Instruction Pointer is the crash address it + // means we got a bad function pointer dereference, so we "unwind" the + // bad pointer manually by using the return address pointed to by the + // Stack Pointer as the Instruction Pointer and letting libunwind do + // the rest + + if (context()) { + ucontext_t *uctx = reinterpret_cast(context()); +#ifdef REG_RIP // x86_64 + if (uctx->uc_mcontext.gregs[REG_RIP] == + reinterpret_cast(error_addr())) { + uctx->uc_mcontext.gregs[REG_RIP] = + *reinterpret_cast(uctx->uc_mcontext.gregs[REG_RSP]); + } + _stacktrace[index] = + reinterpret_cast(uctx->uc_mcontext.gregs[REG_RIP]); + ++index; + ctx = *reinterpret_cast(uctx); +#elif defined(REG_EIP) // x86_32 + if (uctx->uc_mcontext.gregs[REG_EIP] == + reinterpret_cast(error_addr())) { + uctx->uc_mcontext.gregs[REG_EIP] = + *reinterpret_cast(uctx->uc_mcontext.gregs[REG_ESP]); + } + _stacktrace[index] = + reinterpret_cast(uctx->uc_mcontext.gregs[REG_EIP]); + ++index; + ctx = *reinterpret_cast(uctx); +#elif defined(__arm__) + // libunwind uses its own context type for ARM unwinding. + // Copy the registers from the signal handler's context so we can + // unwind + unw_getcontext(&ctx); + ctx.regs[UNW_ARM_R0] = uctx->uc_mcontext.arm_r0; + ctx.regs[UNW_ARM_R1] = uctx->uc_mcontext.arm_r1; + ctx.regs[UNW_ARM_R2] = uctx->uc_mcontext.arm_r2; + ctx.regs[UNW_ARM_R3] = uctx->uc_mcontext.arm_r3; + ctx.regs[UNW_ARM_R4] = uctx->uc_mcontext.arm_r4; + ctx.regs[UNW_ARM_R5] = uctx->uc_mcontext.arm_r5; + ctx.regs[UNW_ARM_R6] = uctx->uc_mcontext.arm_r6; + ctx.regs[UNW_ARM_R7] = uctx->uc_mcontext.arm_r7; + ctx.regs[UNW_ARM_R8] = uctx->uc_mcontext.arm_r8; + ctx.regs[UNW_ARM_R9] = uctx->uc_mcontext.arm_r9; + ctx.regs[UNW_ARM_R10] = uctx->uc_mcontext.arm_r10; + ctx.regs[UNW_ARM_R11] = uctx->uc_mcontext.arm_fp; + ctx.regs[UNW_ARM_R12] = uctx->uc_mcontext.arm_ip; + ctx.regs[UNW_ARM_R13] = uctx->uc_mcontext.arm_sp; + ctx.regs[UNW_ARM_R14] = uctx->uc_mcontext.arm_lr; + ctx.regs[UNW_ARM_R15] = uctx->uc_mcontext.arm_pc; + + // If we have crashed in the PC use the LR instead, as this was + // a bad function dereference + if (reinterpret_cast(error_addr()) == + uctx->uc_mcontext.arm_pc) { + ctx.regs[UNW_ARM_R15] = + uctx->uc_mcontext.arm_lr - sizeof(unsigned long); + } + _stacktrace[index] = reinterpret_cast(ctx.regs[UNW_ARM_R15]); + ++index; +#elif defined(__APPLE__) && defined(__x86_64__) + unw_getcontext(&ctx); + // OS X's implementation of libunwind uses its own context object + // so we need to convert the passed context to libunwind's format + // (information about the data layout taken from unw_getcontext.s + // in Apple's libunwind source + ctx.data[0] = uctx->uc_mcontext->__ss.__rax; + ctx.data[1] = uctx->uc_mcontext->__ss.__rbx; + ctx.data[2] = uctx->uc_mcontext->__ss.__rcx; + ctx.data[3] = uctx->uc_mcontext->__ss.__rdx; + ctx.data[4] = uctx->uc_mcontext->__ss.__rdi; + ctx.data[5] = uctx->uc_mcontext->__ss.__rsi; + ctx.data[6] = uctx->uc_mcontext->__ss.__rbp; + ctx.data[7] = uctx->uc_mcontext->__ss.__rsp; + ctx.data[8] = uctx->uc_mcontext->__ss.__r8; + ctx.data[9] = uctx->uc_mcontext->__ss.__r9; + ctx.data[10] = uctx->uc_mcontext->__ss.__r10; + ctx.data[11] = uctx->uc_mcontext->__ss.__r11; + ctx.data[12] = uctx->uc_mcontext->__ss.__r12; + ctx.data[13] = uctx->uc_mcontext->__ss.__r13; + ctx.data[14] = uctx->uc_mcontext->__ss.__r14; + ctx.data[15] = uctx->uc_mcontext->__ss.__r15; + ctx.data[16] = uctx->uc_mcontext->__ss.__rip; + + // If the IP is the same as the crash address we have a bad function + // dereference The caller's address is pointed to by %rsp, so we + // dereference that value and set it to be the next frame's IP. + if (uctx->uc_mcontext->__ss.__rip == + reinterpret_cast<__uint64_t>(error_addr())) { + ctx.data[16] = + *reinterpret_cast<__uint64_t *>(uctx->uc_mcontext->__ss.__rsp); + } + _stacktrace[index] = reinterpret_cast(ctx.data[16]); + ++index; +#elif defined(__APPLE__) + unw_getcontext(&ctx) + // TODO: Convert the ucontext_t to libunwind's unw_context_t like + // we do in 64 bits + if (ctx.uc_mcontext->__ss.__eip == + reinterpret_cast(error_addr())) { + ctx.uc_mcontext->__ss.__eip = ctx.uc_mcontext->__ss.__esp; + } + _stacktrace[index] = + reinterpret_cast(ctx.uc_mcontext->__ss.__eip); + ++index; +#endif + } + + unw_cursor_t cursor; + if (context()) { +#if defined(UNW_INIT_SIGNAL_FRAME) + result = unw_init_local2(&cursor, &ctx, UNW_INIT_SIGNAL_FRAME); +#else + result = unw_init_local(&cursor, &ctx); +#endif + } else { + unw_getcontext(&ctx); + ; + result = unw_init_local(&cursor, &ctx); + } + + if (result != 0) + return 1; + + unw_word_t ip = 0; + + while (index <= depth && unw_step(&cursor) > 0) { + result = unw_get_reg(&cursor, UNW_REG_IP, &ip); + if (result == 0) { + _stacktrace[index] = reinterpret_cast(--ip); + ++index; + } + } + --index; + + _stacktrace.resize(index + 1); + skip_n_firsts(0); + return size(); + } + + size_t load_from(void *addr, size_t depth = 32, void *context = nullptr, + void *error_addr = nullptr) { + load_here(depth + 8, context, error_addr); + + for (size_t i = 0; i < _stacktrace.size(); ++i) { + if (_stacktrace[i] == addr) { + skip_n_firsts(i); + _stacktrace[i] = (void *)((uintptr_t)_stacktrace[i]); + break; + } + } + + _stacktrace.resize(std::min(_stacktrace.size(), skip_n_firsts() + depth)); + return size(); + } +}; + +#elif defined(BACKWARD_HAS_BACKTRACE) + +template <> +class StackTraceImpl : public StackTraceImplHolder { +public: + NOINLINE + size_t load_here(size_t depth = 32, void *context = nullptr, + void *error_addr = nullptr) { + set_context(context); + set_error_addr(error_addr); + load_thread_info(); + if (depth == 0) { + return 0; + } + _stacktrace.resize(depth + 1); + size_t trace_cnt = backtrace(&_stacktrace[0], _stacktrace.size()); + _stacktrace.resize(trace_cnt); + skip_n_firsts(1); + return size(); + } + + size_t load_from(void *addr, size_t depth = 32, void *context = nullptr, + void *error_addr = nullptr) { + load_here(depth + 8, context, error_addr); + + for (size_t i = 0; i < _stacktrace.size(); ++i) { + if (_stacktrace[i] == addr) { + skip_n_firsts(i); + _stacktrace[i] = (void *)((uintptr_t)_stacktrace[i] + 1); + break; + } + } + + _stacktrace.resize(std::min(_stacktrace.size(), skip_n_firsts() + depth)); + return size(); + } +}; + +#elif defined(BACKWARD_SYSTEM_WINDOWS) + +template <> +class StackTraceImpl : public StackTraceImplHolder { +public: + // We have to load the machine type from the image info + // So we first initialize the resolver, and it tells us this info + void set_machine_type(DWORD machine_type) { machine_type_ = machine_type; } + void set_context(CONTEXT *ctx) { ctx_ = ctx; } + void set_thread_handle(HANDLE handle) { thd_ = handle; } + + NOINLINE + size_t load_here(size_t depth = 32, void *context = nullptr, + void *error_addr = nullptr) { + set_context(static_cast(context)); + set_error_addr(error_addr); + CONTEXT localCtx; // used when no context is provided + + if (depth == 0) { + return 0; + } + + if (!ctx_) { + ctx_ = &localCtx; + RtlCaptureContext(ctx_); + } + + if (!thd_) { + thd_ = GetCurrentThread(); + } + + HANDLE process = GetCurrentProcess(); + + STACKFRAME64 s; + memset(&s, 0, sizeof(STACKFRAME64)); + + // TODO: 32 bit context capture + s.AddrStack.Mode = AddrModeFlat; + s.AddrFrame.Mode = AddrModeFlat; + s.AddrPC.Mode = AddrModeFlat; +#ifdef _M_X64 + s.AddrPC.Offset = ctx_->Rip; + s.AddrStack.Offset = ctx_->Rsp; + s.AddrFrame.Offset = ctx_->Rbp; +#else + s.AddrPC.Offset = ctx_->Eip; + s.AddrStack.Offset = ctx_->Esp; + s.AddrFrame.Offset = ctx_->Ebp; +#endif + + if (!machine_type_) { +#ifdef _M_X64 + machine_type_ = IMAGE_FILE_MACHINE_AMD64; +#else + machine_type_ = IMAGE_FILE_MACHINE_I386; +#endif + } + + for (;;) { + // NOTE: this only works if PDBs are already loaded! + SetLastError(0); + if (!StackWalk64(machine_type_, process, thd_, &s, ctx_, NULL, + SymFunctionTableAccess64, SymGetModuleBase64, NULL)) + break; + + if (s.AddrReturn.Offset == 0) + break; + + _stacktrace.push_back(reinterpret_cast(s.AddrPC.Offset)); + + if (size() >= depth) + break; + } + + return size(); + } + + size_t load_from(void *addr, size_t depth = 32, void *context = nullptr, + void *error_addr = nullptr) { + load_here(depth + 8, context, error_addr); + + for (size_t i = 0; i < _stacktrace.size(); ++i) { + if (_stacktrace[i] == addr) { + skip_n_firsts(i); + break; + } + } + + _stacktrace.resize(std::min(_stacktrace.size(), skip_n_firsts() + depth)); + return size(); + } + +private: + DWORD machine_type_ = 0; + HANDLE thd_ = 0; + CONTEXT *ctx_ = nullptr; +}; + +#endif + +class StackTrace : public StackTraceImpl {}; + +/*************** TRACE RESOLVER ***************/ + +class TraceResolverImplBase { +public: + virtual ~TraceResolverImplBase() {} + + virtual void load_addresses(void *const*addresses, int address_count) { + (void)addresses; + (void)address_count; + } + + template void load_stacktrace(ST &st) { + load_addresses(st.begin(), static_cast(st.size())); + } + + virtual ResolvedTrace resolve(ResolvedTrace t) { return t; } + +protected: + std::string demangle(const char *funcname) { + return _demangler.demangle(funcname); + } + +private: + details::demangler _demangler; +}; + +template class TraceResolverImpl; + +#ifdef BACKWARD_SYSTEM_UNKNOWN + +template <> class TraceResolverImpl + : public TraceResolverImplBase {}; + +#endif + +#ifdef BACKWARD_SYSTEM_LINUX + +class TraceResolverLinuxBase : public TraceResolverImplBase { +public: + TraceResolverLinuxBase() + : argv0_(get_argv0()), exec_path_(read_symlink("/proc/self/exe")) {} + std::string resolve_exec_path(Dl_info &symbol_info) const { + // mutates symbol_info.dli_fname to be filename to open and returns filename + // to display + if (symbol_info.dli_fname == argv0_) { + // dladdr returns argv[0] in dli_fname for symbols contained in + // the main executable, which is not a valid path if the + // executable was found by a search of the PATH environment + // variable; In that case, we actually open /proc/self/exe, which + // is always the actual executable (even if it was deleted/replaced!) + // but display the path that /proc/self/exe links to. + // However, this right away reduces probability of successful symbol + // resolution, because libbfd may try to find *.debug files in the + // same dir, in case symbols are stripped. As a result, it may try + // to find a file /proc/self/.debug, which obviously does + // not exist. /proc/self/exe is a last resort. First load attempt + // should go for the original executable file path. + symbol_info.dli_fname = "/proc/self/exe"; + return exec_path_; + } else { + return symbol_info.dli_fname; + } + } + +private: + std::string argv0_; + std::string exec_path_; + + static std::string get_argv0() { + std::string argv0; + std::ifstream ifs("/proc/self/cmdline"); + std::getline(ifs, argv0, '\0'); + return argv0; + } + + static std::string read_symlink(std::string const &symlink_path) { + std::string path; + path.resize(100); + + while (true) { + ssize_t len = + ::readlink(symlink_path.c_str(), &*path.begin(), path.size()); + if (len < 0) { + return ""; + } + if (static_cast(len) == path.size()) { + path.resize(path.size() * 2); + } else { + path.resize(static_cast(len)); + break; + } + } + + return path; + } +}; + +template class TraceResolverLinuxImpl; + +#if BACKWARD_HAS_BACKTRACE_SYMBOL == 1 + +template <> +class TraceResolverLinuxImpl + : public TraceResolverLinuxBase { +public: + void load_addresses(void *const*addresses, int address_count) override { + if (address_count == 0) { + return; + } + _symbols.reset(backtrace_symbols(addresses, address_count)); + } + + ResolvedTrace resolve(ResolvedTrace trace) override { + char *filename = _symbols[trace.idx]; + char *funcname = filename; + while (*funcname && *funcname != '(') { + funcname += 1; + } + trace.object_filename.assign(filename, + funcname); // ok even if funcname is the ending + // \0 (then we assign entire string) + + if (*funcname) { // if it's not end of string (e.g. from last frame ip==0) + funcname += 1; + char *funcname_end = funcname; + while (*funcname_end && *funcname_end != ')' && *funcname_end != '+') { + funcname_end += 1; + } + *funcname_end = '\0'; + trace.object_function = this->demangle(funcname); + trace.source.function = trace.object_function; // we cannot do better. + } + return trace; + } + +private: + details::handle _symbols; +}; + +#endif // BACKWARD_HAS_BACKTRACE_SYMBOL == 1 + +#if BACKWARD_HAS_BFD == 1 + +template <> +class TraceResolverLinuxImpl + : public TraceResolverLinuxBase { +public: + TraceResolverLinuxImpl() : _bfd_loaded(false) {} + + ResolvedTrace resolve(ResolvedTrace trace) override { + Dl_info symbol_info; + + // trace.addr is a virtual address in memory pointing to some code. + // Let's try to find from which loaded object it comes from. + // The loaded object can be yourself btw. + if (!dladdr(trace.addr, &symbol_info)) { + return trace; // dat broken trace... + } + + // Now we get in symbol_info: + // .dli_fname: + // pathname of the shared object that contains the address. + // .dli_fbase: + // where the object is loaded in memory. + // .dli_sname: + // the name of the nearest symbol to trace.addr, we expect a + // function name. + // .dli_saddr: + // the exact address corresponding to .dli_sname. + + if (symbol_info.dli_sname) { + trace.object_function = demangle(symbol_info.dli_sname); + } + + if (!symbol_info.dli_fname) { + return trace; + } + + trace.object_filename = resolve_exec_path(symbol_info); + bfd_fileobject *fobj; + // Before rushing to resolution need to ensure the executable + // file still can be used. For that compare inode numbers of + // what is stored by the executable's file path, and in the + // dli_fname, which not necessarily equals to the executable. + // It can be a shared library, or /proc/self/exe, and in the + // latter case has drawbacks. See the exec path resolution for + // details. In short - the dli object should be used only as + // the last resort. + // If inode numbers are equal, it is known dli_fname and the + // executable file are the same. This is guaranteed by Linux, + // because if the executable file is changed/deleted, it will + // be done in a new inode. The old file will be preserved in + // /proc/self/exe, and may even have inode 0. The latter can + // happen if the inode was actually reused, and the file was + // kept only in the main memory. + // + struct stat obj_stat; + struct stat dli_stat; + if (stat(trace.object_filename.c_str(), &obj_stat) == 0 && + stat(symbol_info.dli_fname, &dli_stat) == 0 && + obj_stat.st_ino == dli_stat.st_ino) { + // The executable file, and the shared object containing the + // address are the same file. Safe to use the original path. + // this is preferable. Libbfd will search for stripped debug + // symbols in the same directory. + fobj = load_object_with_bfd(trace.object_filename); + } else{ + // The original object file was *deleted*! The only hope is + // that the debug symbols are either inside the shared + // object file, or are in the same directory, and this is + // not /proc/self/exe. + fobj = nullptr; + } + if (fobj == nullptr || !fobj->handle) { + fobj = load_object_with_bfd(symbol_info.dli_fname); + if (!fobj->handle) { + return trace; + } + } + + find_sym_result *details_selected; // to be filled. + + // trace.addr is the next instruction to be executed after returning + // from the nested stack frame. In C++ this usually relate to the next + // statement right after the function call that leaded to a new stack + // frame. This is not usually what you want to see when printing out a + // stacktrace... + find_sym_result details_call_site = + find_symbol_details(fobj, trace.addr, symbol_info.dli_fbase); + details_selected = &details_call_site; + +#if BACKWARD_HAS_UNWIND == 0 + // ...this is why we also try to resolve the symbol that is right + // before the return address. If we are lucky enough, we will get the + // line of the function that was called. But if the code is optimized, + // we might get something absolutely not related since the compiler + // can reschedule the return address with inline functions and + // tail-call optimisation (among other things that I don't even know + // or cannot even dream about with my tiny limited brain). + find_sym_result details_adjusted_call_site = find_symbol_details( + fobj, (void *)(uintptr_t(trace.addr) - 1), symbol_info.dli_fbase); + + // In debug mode, we should always get the right thing(TM). + if (details_call_site.found && details_adjusted_call_site.found) { + // Ok, we assume that details_adjusted_call_site is a better estimation. + details_selected = &details_adjusted_call_site; + trace.addr = (void *)(uintptr_t(trace.addr) - 1); + } + + if (details_selected == &details_call_site && details_call_site.found) { + // we have to re-resolve the symbol in order to reset some + // internal state in BFD... so we can call backtrace_inliners + // thereafter... + details_call_site = + find_symbol_details(fobj, trace.addr, symbol_info.dli_fbase); + } +#endif // BACKWARD_HAS_UNWIND + + if (details_selected->found) { + if (details_selected->filename) { + trace.source.filename = details_selected->filename; + } + trace.source.line = details_selected->line; + + if (details_selected->funcname) { + // this time we get the name of the function where the code is + // located, instead of the function were the address is + // located. In short, if the code was inlined, we get the + // function correspoding to the code. Else we already got in + // trace.function. + trace.source.function = demangle(details_selected->funcname); + + if (!symbol_info.dli_sname) { + // for the case dladdr failed to find the symbol name of + // the function, we might as well try to put something + // here. + trace.object_function = trace.source.function; + } + } + + // Maybe the source of the trace got inlined inside the function + // (trace.source.function). Let's see if we can get all the inlined + // calls along the way up to the initial call site. + trace.inliners = backtrace_inliners(fobj, *details_selected); + +#if 0 + if (trace.inliners.size() == 0) { + // Maybe the trace was not inlined... or maybe it was and we + // are lacking the debug information. Let's try to make the + // world better and see if we can get the line number of the + // function (trace.source.function) now. + // + // We will get the location of where the function start (to be + // exact: the first instruction that really start the + // function), not where the name of the function is defined. + // This can be quite far away from the name of the function + // btw. + // + // If the source of the function is the same as the source of + // the trace, we cannot say if the trace was really inlined or + // not. However, if the filename of the source is different + // between the function and the trace... we can declare it as + // an inliner. This is not 100% accurate, but better than + // nothing. + + if (symbol_info.dli_saddr) { + find_sym_result details = find_symbol_details(fobj, + symbol_info.dli_saddr, + symbol_info.dli_fbase); + + if (details.found) { + ResolvedTrace::SourceLoc diy_inliner; + diy_inliner.line = details.line; + if (details.filename) { + diy_inliner.filename = details.filename; + } + if (details.funcname) { + diy_inliner.function = demangle(details.funcname); + } else { + diy_inliner.function = trace.source.function; + } + if (diy_inliner != trace.source) { + trace.inliners.push_back(diy_inliner); + } + } + } + } +#endif + } + + return trace; + } + +private: + bool _bfd_loaded; + + typedef details::handle > + bfd_handle_t; + + typedef details::handle bfd_symtab_t; + + struct bfd_fileobject { + bfd_handle_t handle; + bfd_vma base_addr; + bfd_symtab_t symtab; + bfd_symtab_t dynamic_symtab; + }; + + typedef details::hashtable::type fobj_bfd_map_t; + fobj_bfd_map_t _fobj_bfd_map; + + bfd_fileobject *load_object_with_bfd(const std::string &filename_object) { + using namespace details; + + if (!_bfd_loaded) { + using namespace details; + bfd_init(); + _bfd_loaded = true; + } + + fobj_bfd_map_t::iterator it = _fobj_bfd_map.find(filename_object); + if (it != _fobj_bfd_map.end()) { + return &it->second; + } + + // this new object is empty for now. + bfd_fileobject *r = &_fobj_bfd_map[filename_object]; + + // we do the work temporary in this one; + bfd_handle_t bfd_handle; + + int fd = open(filename_object.c_str(), O_RDONLY); + bfd_handle.reset(bfd_fdopenr(filename_object.c_str(), "default", fd)); + if (!bfd_handle) { + close(fd); + return r; + } + + if (!bfd_check_format(bfd_handle.get(), bfd_object)) { + return r; // not an object? You lose. + } + + if ((bfd_get_file_flags(bfd_handle.get()) & HAS_SYMS) == 0) { + return r; // that's what happen when you forget to compile in debug. + } + + ssize_t symtab_storage_size = bfd_get_symtab_upper_bound(bfd_handle.get()); + + ssize_t dyn_symtab_storage_size = + bfd_get_dynamic_symtab_upper_bound(bfd_handle.get()); + + if (symtab_storage_size <= 0 && dyn_symtab_storage_size <= 0) { + return r; // weird, is the file is corrupted? + } + + bfd_symtab_t symtab, dynamic_symtab; + ssize_t symcount = 0, dyn_symcount = 0; + + if (symtab_storage_size > 0) { + symtab.reset(static_cast( + malloc(static_cast(symtab_storage_size)))); + symcount = bfd_canonicalize_symtab(bfd_handle.get(), symtab.get()); + } + + if (dyn_symtab_storage_size > 0) { + dynamic_symtab.reset(static_cast( + malloc(static_cast(dyn_symtab_storage_size)))); + dyn_symcount = bfd_canonicalize_dynamic_symtab(bfd_handle.get(), + dynamic_symtab.get()); + } + + if (symcount <= 0 && dyn_symcount <= 0) { + return r; // damned, that's a stripped file that you got there! + } + + r->handle = move(bfd_handle); + r->symtab = move(symtab); + r->dynamic_symtab = move(dynamic_symtab); + return r; + } + + struct find_sym_result { + bool found; + const char *filename; + const char *funcname; + unsigned int line; + }; + + struct find_sym_context { + TraceResolverLinuxImpl *self; + bfd_fileobject *fobj; + void *addr; + void *base_addr; + find_sym_result result; + }; + + find_sym_result find_symbol_details(bfd_fileobject *fobj, void *addr, + void *base_addr) { + find_sym_context context; + context.self = this; + context.fobj = fobj; + context.addr = addr; + context.base_addr = base_addr; + context.result.found = false; + bfd_map_over_sections(fobj->handle.get(), &find_in_section_trampoline, + static_cast(&context)); + return context.result; + } + + static void find_in_section_trampoline(bfd *, asection *section, void *data) { + find_sym_context *context = static_cast(data); + context->self->find_in_section( + reinterpret_cast(context->addr), + reinterpret_cast(context->base_addr), context->fobj, section, + context->result); + } + + void find_in_section(bfd_vma addr, bfd_vma base_addr, bfd_fileobject *fobj, + asection *section, find_sym_result &result) { + if (result.found) + return; + +#ifdef bfd_get_section_flags + if ((bfd_get_section_flags(fobj->handle.get(), section) & SEC_ALLOC) == 0) +#else + if ((bfd_section_flags(section) & SEC_ALLOC) == 0) +#endif + return; // a debug section is never loaded automatically. + +#ifdef bfd_get_section_vma + bfd_vma sec_addr = bfd_get_section_vma(fobj->handle.get(), section); +#else + bfd_vma sec_addr = bfd_section_vma(section); +#endif +#ifdef bfd_get_section_size + bfd_size_type size = bfd_get_section_size(section); +#else + bfd_size_type size = bfd_section_size(section); +#endif + + // are we in the boundaries of the section? + if (addr < sec_addr || addr >= sec_addr + size) { + addr -= base_addr; // oups, a relocated object, lets try again... + if (addr < sec_addr || addr >= sec_addr + size) { + return; + } + } + +#if defined(__clang__) +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wzero-as-null-pointer-constant" +#endif + if (!result.found && fobj->symtab) { + result.found = bfd_find_nearest_line( + fobj->handle.get(), section, fobj->symtab.get(), addr - sec_addr, + &result.filename, &result.funcname, &result.line); + } + + if (!result.found && fobj->dynamic_symtab) { + result.found = bfd_find_nearest_line( + fobj->handle.get(), section, fobj->dynamic_symtab.get(), + addr - sec_addr, &result.filename, &result.funcname, &result.line); + } +#if defined(__clang__) +#pragma clang diagnostic pop +#endif + } + + ResolvedTrace::source_locs_t + backtrace_inliners(bfd_fileobject *fobj, find_sym_result previous_result) { + // This function can be called ONLY after a SUCCESSFUL call to + // find_symbol_details. The state is global to the bfd_handle. + ResolvedTrace::source_locs_t results; + while (previous_result.found) { + find_sym_result result; + result.found = bfd_find_inliner_info(fobj->handle.get(), &result.filename, + &result.funcname, &result.line); + + if (result + .found) /* and not ( + cstrings_eq(previous_result.filename, + result.filename) and + cstrings_eq(previous_result.funcname, result.funcname) + and result.line == previous_result.line + )) */ + { + ResolvedTrace::SourceLoc src_loc; + src_loc.line = result.line; + if (result.filename) { + src_loc.filename = result.filename; + } + if (result.funcname) { + src_loc.function = demangle(result.funcname); + } + results.push_back(src_loc); + } + previous_result = result; + } + return results; + } + + bool cstrings_eq(const char *a, const char *b) { + if (!a || !b) { + return false; + } + return strcmp(a, b) == 0; + } +}; +#endif // BACKWARD_HAS_BFD == 1 + +#if BACKWARD_HAS_DW == 1 + +template <> +class TraceResolverLinuxImpl + : public TraceResolverLinuxBase { +public: + TraceResolverLinuxImpl() : _dwfl_handle_initialized(false) {} + + ResolvedTrace resolve(ResolvedTrace trace) override { + using namespace details; + + Dwarf_Addr trace_addr = (Dwarf_Addr)trace.addr; + + if (!_dwfl_handle_initialized) { + // initialize dwfl... + _dwfl_cb.reset(new Dwfl_Callbacks); + _dwfl_cb->find_elf = &dwfl_linux_proc_find_elf; + _dwfl_cb->find_debuginfo = &dwfl_standard_find_debuginfo; + _dwfl_cb->debuginfo_path = 0; + + _dwfl_handle.reset(dwfl_begin(_dwfl_cb.get())); + _dwfl_handle_initialized = true; + + if (!_dwfl_handle) { + return trace; + } + + // ...from the current process. + dwfl_report_begin(_dwfl_handle.get()); + int r = dwfl_linux_proc_report(_dwfl_handle.get(), getpid()); + dwfl_report_end(_dwfl_handle.get(), NULL, NULL); + if (r < 0) { + return trace; + } + } + + if (!_dwfl_handle) { + return trace; + } + + // find the module (binary object) that contains the trace's address. + // This is not using any debug information, but the addresses ranges of + // all the currently loaded binary object. + Dwfl_Module *mod = dwfl_addrmodule(_dwfl_handle.get(), trace_addr); + if (mod) { + // now that we found it, lets get the name of it, this will be the + // full path to the running binary or one of the loaded library. + const char *module_name = dwfl_module_info(mod, 0, 0, 0, 0, 0, 0, 0); + if (module_name) { + trace.object_filename = module_name; + } + // We also look after the name of the symbol, equal or before this + // address. This is found by walking the symtab. We should get the + // symbol corresponding to the function (mangled) containing the + // address. If the code corresponding to the address was inlined, + // this is the name of the out-most inliner function. + const char *sym_name = dwfl_module_addrname(mod, trace_addr); + if (sym_name) { + trace.object_function = demangle(sym_name); + } + } + + // now let's get serious, and find out the source location (file and + // line number) of the address. + + // This function will look in .debug_aranges for the address and map it + // to the location of the compilation unit DIE in .debug_info and + // return it. + Dwarf_Addr mod_bias = 0; + Dwarf_Die *cudie = dwfl_module_addrdie(mod, trace_addr, &mod_bias); + +#if 1 + if (!cudie) { + // Sadly clang does not generate the section .debug_aranges, thus + // dwfl_module_addrdie will fail early. Clang doesn't either set + // the lowpc/highpc/range info for every compilation unit. + // + // So in order to save the world: + // for every compilation unit, we will iterate over every single + // DIEs. Normally functions should have a lowpc/highpc/range, which + // we will use to infer the compilation unit. + + // note that this is probably badly inefficient. + while ((cudie = dwfl_module_nextcu(mod, cudie, &mod_bias))) { + Dwarf_Die die_mem; + Dwarf_Die *fundie = + find_fundie_by_pc(cudie, trace_addr - mod_bias, &die_mem); + if (fundie) { + break; + } + } + } +#endif + +//#define BACKWARD_I_DO_NOT_RECOMMEND_TO_ENABLE_THIS_HORRIBLE_PIECE_OF_CODE +#ifdef BACKWARD_I_DO_NOT_RECOMMEND_TO_ENABLE_THIS_HORRIBLE_PIECE_OF_CODE + if (!cudie) { + // If it's still not enough, lets dive deeper in the shit, and try + // to save the world again: for every compilation unit, we will + // load the corresponding .debug_line section, and see if we can + // find our address in it. + + Dwarf_Addr cfi_bias; + Dwarf_CFI *cfi_cache = dwfl_module_eh_cfi(mod, &cfi_bias); + + Dwarf_Addr bias; + while ((cudie = dwfl_module_nextcu(mod, cudie, &bias))) { + if (dwarf_getsrc_die(cudie, trace_addr - bias)) { + + // ...but if we get a match, it might be a false positive + // because our (address - bias) might as well be valid in a + // different compilation unit. So we throw our last card on + // the table and lookup for the address into the .eh_frame + // section. + + handle frame; + dwarf_cfi_addrframe(cfi_cache, trace_addr - cfi_bias, &frame); + if (frame) { + break; + } + } + } + } +#endif + + if (!cudie) { + return trace; // this time we lost the game :/ + } + + // Now that we have a compilation unit DIE, this function will be able + // to load the corresponding section in .debug_line (if not already + // loaded) and hopefully find the source location mapped to our + // address. + Dwarf_Line *srcloc = dwarf_getsrc_die(cudie, trace_addr - mod_bias); + + if (srcloc) { + const char *srcfile = dwarf_linesrc(srcloc, 0, 0); + if (srcfile) { + trace.source.filename = srcfile; + } + int line = 0, col = 0; + dwarf_lineno(srcloc, &line); + dwarf_linecol(srcloc, &col); + trace.source.line = line; + trace.source.col = col; + } + + deep_first_search_by_pc(cudie, trace_addr - mod_bias, + inliners_search_cb(trace)); + if (trace.source.function.size() == 0) { + // fallback. + trace.source.function = trace.object_function; + } + + return trace; + } + +private: + typedef details::handle > + dwfl_handle_t; + details::handle > + _dwfl_cb; + dwfl_handle_t _dwfl_handle; + bool _dwfl_handle_initialized; + + // defined here because in C++98, template function cannot take locally + // defined types... grrr. + struct inliners_search_cb { + void operator()(Dwarf_Die *die) { + switch (dwarf_tag(die)) { + const char *name; + case DW_TAG_subprogram: + if ((name = dwarf_diename(die))) { + trace.source.function = name; + } + break; + + case DW_TAG_inlined_subroutine: + ResolvedTrace::SourceLoc sloc; + Dwarf_Attribute attr_mem; + + if ((name = dwarf_diename(die))) { + sloc.function = name; + } + if ((name = die_call_file(die))) { + sloc.filename = name; + } + + Dwarf_Word line = 0, col = 0; + dwarf_formudata(dwarf_attr(die, DW_AT_call_line, &attr_mem), &line); + dwarf_formudata(dwarf_attr(die, DW_AT_call_column, &attr_mem), &col); + sloc.line = (unsigned)line; + sloc.col = (unsigned)col; + + trace.inliners.push_back(sloc); + break; + }; + } + ResolvedTrace &trace; + inliners_search_cb(ResolvedTrace &t) : trace(t) {} + }; + + static bool die_has_pc(Dwarf_Die *die, Dwarf_Addr pc) { + Dwarf_Addr low, high; + + // continuous range + if (dwarf_hasattr(die, DW_AT_low_pc) && dwarf_hasattr(die, DW_AT_high_pc)) { + if (dwarf_lowpc(die, &low) != 0) { + return false; + } + if (dwarf_highpc(die, &high) != 0) { + Dwarf_Attribute attr_mem; + Dwarf_Attribute *attr = dwarf_attr(die, DW_AT_high_pc, &attr_mem); + Dwarf_Word value; + if (dwarf_formudata(attr, &value) != 0) { + return false; + } + high = low + value; + } + return pc >= low && pc < high; + } + + // non-continuous range. + Dwarf_Addr base; + ptrdiff_t offset = 0; + while ((offset = dwarf_ranges(die, offset, &base, &low, &high)) > 0) { + if (pc >= low && pc < high) { + return true; + } + } + return false; + } + + static Dwarf_Die *find_fundie_by_pc(Dwarf_Die *parent_die, Dwarf_Addr pc, + Dwarf_Die *result) { + if (dwarf_child(parent_die, result) != 0) { + return 0; + } + + Dwarf_Die *die = result; + do { + switch (dwarf_tag(die)) { + case DW_TAG_subprogram: + case DW_TAG_inlined_subroutine: + if (die_has_pc(die, pc)) { + return result; + } + }; + bool declaration = false; + Dwarf_Attribute attr_mem; + dwarf_formflag(dwarf_attr(die, DW_AT_declaration, &attr_mem), + &declaration); + if (!declaration) { + // let's be curious and look deeper in the tree, + // function are not necessarily at the first level, but + // might be nested inside a namespace, structure etc. + Dwarf_Die die_mem; + Dwarf_Die *indie = find_fundie_by_pc(die, pc, &die_mem); + if (indie) { + *result = die_mem; + return result; + } + } + } while (dwarf_siblingof(die, result) == 0); + return 0; + } + + template + static bool deep_first_search_by_pc(Dwarf_Die *parent_die, Dwarf_Addr pc, + CB cb) { + Dwarf_Die die_mem; + if (dwarf_child(parent_die, &die_mem) != 0) { + return false; + } + + bool branch_has_pc = false; + Dwarf_Die *die = &die_mem; + do { + bool declaration = false; + Dwarf_Attribute attr_mem; + dwarf_formflag(dwarf_attr(die, DW_AT_declaration, &attr_mem), + &declaration); + if (!declaration) { + // let's be curious and look deeper in the tree, function are + // not necessarily at the first level, but might be nested + // inside a namespace, structure, a function, an inlined + // function etc. + branch_has_pc = deep_first_search_by_pc(die, pc, cb); + } + if (!branch_has_pc) { + branch_has_pc = die_has_pc(die, pc); + } + if (branch_has_pc) { + cb(die); + } + } while (dwarf_siblingof(die, &die_mem) == 0); + return branch_has_pc; + } + + static const char *die_call_file(Dwarf_Die *die) { + Dwarf_Attribute attr_mem; + Dwarf_Word file_idx = 0; + + dwarf_formudata(dwarf_attr(die, DW_AT_call_file, &attr_mem), &file_idx); + + if (file_idx == 0) { + return 0; + } + + Dwarf_Die die_mem; + Dwarf_Die *cudie = dwarf_diecu(die, &die_mem, 0, 0); + if (!cudie) { + return 0; + } + + Dwarf_Files *files = 0; + size_t nfiles; + dwarf_getsrcfiles(cudie, &files, &nfiles); + if (!files) { + return 0; + } + + return dwarf_filesrc(files, file_idx, 0, 0); + } +}; +#endif // BACKWARD_HAS_DW == 1 + +#if BACKWARD_HAS_DWARF == 1 + +template <> +class TraceResolverLinuxImpl + : public TraceResolverLinuxBase { +public: + TraceResolverLinuxImpl() : _dwarf_loaded(false) {} + + ResolvedTrace resolve(ResolvedTrace trace) override { + // trace.addr is a virtual address in memory pointing to some code. + // Let's try to find from which loaded object it comes from. + // The loaded object can be yourself btw. + + Dl_info symbol_info; + int dladdr_result = 0; +#if defined(__GLIBC__) + link_map *link_map; + // We request the link map so we can get information about offsets + dladdr_result = + dladdr1(trace.addr, &symbol_info, reinterpret_cast(&link_map), + RTLD_DL_LINKMAP); +#else + // Android doesn't have dladdr1. Don't use the linker map. + dladdr_result = dladdr(trace.addr, &symbol_info); +#endif + if (!dladdr_result) { + return trace; // dat broken trace... + } + + // Now we get in symbol_info: + // .dli_fname: + // pathname of the shared object that contains the address. + // .dli_fbase: + // where the object is loaded in memory. + // .dli_sname: + // the name of the nearest symbol to trace.addr, we expect a + // function name. + // .dli_saddr: + // the exact address corresponding to .dli_sname. + // + // And in link_map: + // .l_addr: + // difference between the address in the ELF file and the address + // in memory + // l_name: + // absolute pathname where the object was found + + if (symbol_info.dli_sname) { + trace.object_function = demangle(symbol_info.dli_sname); + } + + if (!symbol_info.dli_fname) { + return trace; + } + + trace.object_filename = resolve_exec_path(symbol_info); + dwarf_fileobject &fobj = load_object_with_dwarf(symbol_info.dli_fname); + if (!fobj.dwarf_handle) { + return trace; // sad, we couldn't load the object :( + } + +#if defined(__GLIBC__) + // Convert the address to a module relative one by looking at + // the module's loading address in the link map + Dwarf_Addr address = reinterpret_cast(trace.addr) - + reinterpret_cast(link_map->l_addr); +#else + Dwarf_Addr address = reinterpret_cast(trace.addr); +#endif + + if (trace.object_function.empty()) { + symbol_cache_t::iterator it = fobj.symbol_cache.lower_bound(address); + + if (it != fobj.symbol_cache.end()) { + if (it->first != address) { + if (it != fobj.symbol_cache.begin()) { + --it; + } + } + trace.object_function = demangle(it->second.c_str()); + } + } + + // Get the Compilation Unit DIE for the address + Dwarf_Die die = find_die(fobj, address); + + if (!die) { + return trace; // this time we lost the game :/ + } + + // libdwarf doesn't give us direct access to its objects, it always + // allocates a copy for the caller. We keep that copy alive in a cache + // and we deallocate it later when it's no longer required. + die_cache_entry &die_object = get_die_cache(fobj, die); + if (die_object.isEmpty()) + return trace; // We have no line section for this DIE + + die_linemap_t::iterator it = die_object.line_section.lower_bound(address); + + if (it != die_object.line_section.end()) { + if (it->first != address) { + if (it == die_object.line_section.begin()) { + // If we are on the first item of the line section + // but the address does not match it means that + // the address is below the range of the DIE. Give up. + return trace; + } else { + --it; + } + } + } else { + return trace; // We didn't find the address. + } + + // Get the Dwarf_Line that the address points to and call libdwarf + // to get source file, line and column info. + Dwarf_Line line = die_object.line_buffer[it->second]; + Dwarf_Error error = DW_DLE_NE; + + char *filename; + if (dwarf_linesrc(line, &filename, &error) == DW_DLV_OK) { + trace.source.filename = std::string(filename); + dwarf_dealloc(fobj.dwarf_handle.get(), filename, DW_DLA_STRING); + } + + Dwarf_Unsigned number = 0; + if (dwarf_lineno(line, &number, &error) == DW_DLV_OK) { + trace.source.line = number; + } else { + trace.source.line = 0; + } + + if (dwarf_lineoff_b(line, &number, &error) == DW_DLV_OK) { + trace.source.col = number; + } else { + trace.source.col = 0; + } + + std::vector namespace_stack; + deep_first_search_by_pc(fobj, die, address, namespace_stack, + inliners_search_cb(trace, fobj, die)); + + dwarf_dealloc(fobj.dwarf_handle.get(), die, DW_DLA_DIE); + + return trace; + } + +public: + static int close_dwarf(Dwarf_Debug dwarf) { + return dwarf_finish(dwarf, NULL); + } + +private: + bool _dwarf_loaded; + + typedef details::handle > + dwarf_file_t; + + typedef details::handle > + dwarf_elf_t; + + typedef details::handle > + dwarf_handle_t; + + typedef std::map die_linemap_t; + + typedef std::map die_specmap_t; + + struct die_cache_entry { + die_specmap_t spec_section; + die_linemap_t line_section; + Dwarf_Line *line_buffer; + Dwarf_Signed line_count; + Dwarf_Line_Context line_context; + + inline bool isEmpty() { + return line_buffer == NULL || line_count == 0 || line_context == NULL || + line_section.empty(); + } + + die_cache_entry() : line_buffer(0), line_count(0), line_context(0) {} + + ~die_cache_entry() { + if (line_context) { + dwarf_srclines_dealloc_b(line_context); + } + } + }; + + typedef std::map die_cache_t; + + typedef std::map symbol_cache_t; + + struct dwarf_fileobject { + dwarf_file_t file_handle; + dwarf_elf_t elf_handle; + dwarf_handle_t dwarf_handle; + symbol_cache_t symbol_cache; + + // Die cache + die_cache_t die_cache; + die_cache_entry *current_cu; + }; + + typedef details::hashtable::type + fobj_dwarf_map_t; + fobj_dwarf_map_t _fobj_dwarf_map; + + static bool cstrings_eq(const char *a, const char *b) { + if (!a || !b) { + return false; + } + return strcmp(a, b) == 0; + } + + dwarf_fileobject &load_object_with_dwarf(const std::string &filename_object) { + + if (!_dwarf_loaded) { + // Set the ELF library operating version + // If that fails there's nothing we can do + _dwarf_loaded = elf_version(EV_CURRENT) != EV_NONE; + } + + fobj_dwarf_map_t::iterator it = _fobj_dwarf_map.find(filename_object); + if (it != _fobj_dwarf_map.end()) { + return it->second; + } + + // this new object is empty for now + dwarf_fileobject &r = _fobj_dwarf_map[filename_object]; + + dwarf_file_t file_handle; + file_handle.reset(open(filename_object.c_str(), O_RDONLY)); + if (file_handle.get() < 0) { + return r; + } + + // Try to get an ELF handle. We need to read the ELF sections + // because we want to see if there is a .gnu_debuglink section + // that points to a split debug file + dwarf_elf_t elf_handle; + elf_handle.reset(elf_begin(file_handle.get(), ELF_C_READ, NULL)); + if (!elf_handle) { + return r; + } + + const char *e_ident = elf_getident(elf_handle.get(), 0); + if (!e_ident) { + return r; + } + + // Get the number of sections + // We use the new APIs as elf_getshnum is deprecated + size_t shdrnum = 0; + if (elf_getshdrnum(elf_handle.get(), &shdrnum) == -1) { + return r; + } + + // Get the index to the string section + size_t shdrstrndx = 0; + if (elf_getshdrstrndx(elf_handle.get(), &shdrstrndx) == -1) { + return r; + } + + std::string debuglink; + // Iterate through the ELF sections to try to get a gnu_debuglink + // note and also to cache the symbol table. + // We go the preprocessor way to avoid having to create templated + // classes or using gelf (which might throw a compiler error if 64 bit + // is not supported +#define ELF_GET_DATA(ARCH) \ + Elf_Scn *elf_section = 0; \ + Elf_Data *elf_data = 0; \ + Elf##ARCH##_Shdr *section_header = 0; \ + Elf_Scn *symbol_section = 0; \ + size_t symbol_count = 0; \ + size_t symbol_strings = 0; \ + Elf##ARCH##_Sym *symbol = 0; \ + const char *section_name = 0; \ + \ + while ((elf_section = elf_nextscn(elf_handle.get(), elf_section)) != NULL) { \ + section_header = elf##ARCH##_getshdr(elf_section); \ + if (section_header == NULL) { \ + return r; \ + } \ + \ + if ((section_name = elf_strptr(elf_handle.get(), shdrstrndx, \ + section_header->sh_name)) == NULL) { \ + return r; \ + } \ + \ + if (cstrings_eq(section_name, ".gnu_debuglink")) { \ + elf_data = elf_getdata(elf_section, NULL); \ + if (elf_data && elf_data->d_size > 0) { \ + debuglink = \ + std::string(reinterpret_cast(elf_data->d_buf)); \ + } \ + } \ + \ + switch (section_header->sh_type) { \ + case SHT_SYMTAB: \ + symbol_section = elf_section; \ + symbol_count = section_header->sh_size / section_header->sh_entsize; \ + symbol_strings = section_header->sh_link; \ + break; \ + \ + /* We use .dynsyms as a last resort, we prefer .symtab */ \ + case SHT_DYNSYM: \ + if (!symbol_section) { \ + symbol_section = elf_section; \ + symbol_count = section_header->sh_size / section_header->sh_entsize; \ + symbol_strings = section_header->sh_link; \ + } \ + break; \ + } \ + } \ + \ + if (symbol_section && symbol_count && symbol_strings) { \ + elf_data = elf_getdata(symbol_section, NULL); \ + symbol = reinterpret_cast(elf_data->d_buf); \ + for (size_t i = 0; i < symbol_count; ++i) { \ + int type = ELF##ARCH##_ST_TYPE(symbol->st_info); \ + if (type == STT_FUNC && symbol->st_value > 0) { \ + r.symbol_cache[symbol->st_value] = std::string( \ + elf_strptr(elf_handle.get(), symbol_strings, symbol->st_name)); \ + } \ + ++symbol; \ + } \ + } + + if (e_ident[EI_CLASS] == ELFCLASS32) { + ELF_GET_DATA(32) + } else if (e_ident[EI_CLASS] == ELFCLASS64) { + // libelf might have been built without 64 bit support +#if __LIBELF64 + ELF_GET_DATA(64) +#endif + } + + if (!debuglink.empty()) { + // We have a debuglink section! Open an elf instance on that + // file instead. If we can't open the file, then return + // the elf handle we had already opened. + dwarf_file_t debuglink_file; + debuglink_file.reset(open(debuglink.c_str(), O_RDONLY)); + if (debuglink_file.get() > 0) { + dwarf_elf_t debuglink_elf; + debuglink_elf.reset(elf_begin(debuglink_file.get(), ELF_C_READ, NULL)); + + // If we have a valid elf handle, return the new elf handle + // and file handle and discard the original ones + if (debuglink_elf) { + elf_handle = move(debuglink_elf); + file_handle = move(debuglink_file); + } + } + } + + // Ok, we have a valid ELF handle, let's try to get debug symbols + Dwarf_Debug dwarf_debug; + Dwarf_Error error = DW_DLE_NE; + dwarf_handle_t dwarf_handle; + + int dwarf_result = dwarf_elf_init(elf_handle.get(), DW_DLC_READ, NULL, NULL, + &dwarf_debug, &error); + + // We don't do any special handling for DW_DLV_NO_ENTRY specially. + // If we get an error, or the file doesn't have debug information + // we just return. + if (dwarf_result != DW_DLV_OK) { + return r; + } + + dwarf_handle.reset(dwarf_debug); + + r.file_handle = move(file_handle); + r.elf_handle = move(elf_handle); + r.dwarf_handle = move(dwarf_handle); + + return r; + } + + die_cache_entry &get_die_cache(dwarf_fileobject &fobj, Dwarf_Die die) { + Dwarf_Error error = DW_DLE_NE; + + // Get the die offset, we use it as the cache key + Dwarf_Off die_offset; + if (dwarf_dieoffset(die, &die_offset, &error) != DW_DLV_OK) { + die_offset = 0; + } + + die_cache_t::iterator it = fobj.die_cache.find(die_offset); + + if (it != fobj.die_cache.end()) { + fobj.current_cu = &it->second; + return it->second; + } + + die_cache_entry &de = fobj.die_cache[die_offset]; + fobj.current_cu = &de; + + Dwarf_Addr line_addr; + Dwarf_Small table_count; + + // The addresses in the line section are not fully sorted (they might + // be sorted by block of code belonging to the same file), which makes + // it necessary to do so before searching is possible. + // + // As libdwarf allocates a copy of everything, let's get the contents + // of the line section and keep it around. We also create a map of + // program counter to line table indices so we can search by address + // and get the line buffer index. + // + // To make things more difficult, the same address can span more than + // one line, so we need to keep the index pointing to the first line + // by using insert instead of the map's [ operator. + + // Get the line context for the DIE + if (dwarf_srclines_b(die, 0, &table_count, &de.line_context, &error) == + DW_DLV_OK) { + // Get the source lines for this line context, to be deallocated + // later + if (dwarf_srclines_from_linecontext(de.line_context, &de.line_buffer, + &de.line_count, + &error) == DW_DLV_OK) { + + // Add all the addresses to our map + for (int i = 0; i < de.line_count; i++) { + if (dwarf_lineaddr(de.line_buffer[i], &line_addr, &error) != + DW_DLV_OK) { + line_addr = 0; + } + de.line_section.insert(std::pair(line_addr, i)); + } + } + } + + // For each CU, cache the function DIEs that contain the + // DW_AT_specification attribute. When building with -g3 the function + // DIEs are separated in declaration and specification, with the + // declaration containing only the name and parameters and the + // specification the low/high pc and other compiler attributes. + // + // We cache those specifications so we don't skip over the declarations, + // because they have no pc, and we can do namespace resolution for + // DWARF function names. + Dwarf_Debug dwarf = fobj.dwarf_handle.get(); + Dwarf_Die current_die = 0; + if (dwarf_child(die, ¤t_die, &error) == DW_DLV_OK) { + for (;;) { + Dwarf_Die sibling_die = 0; + + Dwarf_Half tag_value; + dwarf_tag(current_die, &tag_value, &error); + + if (tag_value == DW_TAG_subprogram || + tag_value == DW_TAG_inlined_subroutine) { + + Dwarf_Bool has_attr = 0; + if (dwarf_hasattr(current_die, DW_AT_specification, &has_attr, + &error) == DW_DLV_OK) { + if (has_attr) { + Dwarf_Attribute attr_mem; + if (dwarf_attr(current_die, DW_AT_specification, &attr_mem, + &error) == DW_DLV_OK) { + Dwarf_Off spec_offset = 0; + if (dwarf_formref(attr_mem, &spec_offset, &error) == + DW_DLV_OK) { + Dwarf_Off spec_die_offset; + if (dwarf_dieoffset(current_die, &spec_die_offset, &error) == + DW_DLV_OK) { + de.spec_section[spec_offset] = spec_die_offset; + } + } + } + dwarf_dealloc(dwarf, attr_mem, DW_DLA_ATTR); + } + } + } + + int result = dwarf_siblingof(dwarf, current_die, &sibling_die, &error); + if (result == DW_DLV_ERROR) { + break; + } else if (result == DW_DLV_NO_ENTRY) { + break; + } + + if (current_die != die) { + dwarf_dealloc(dwarf, current_die, DW_DLA_DIE); + current_die = 0; + } + + current_die = sibling_die; + } + } + return de; + } + + static Dwarf_Die get_referenced_die(Dwarf_Debug dwarf, Dwarf_Die die, + Dwarf_Half attr, bool global) { + Dwarf_Error error = DW_DLE_NE; + Dwarf_Attribute attr_mem; + + Dwarf_Die found_die = NULL; + if (dwarf_attr(die, attr, &attr_mem, &error) == DW_DLV_OK) { + Dwarf_Off offset; + int result = 0; + if (global) { + result = dwarf_global_formref(attr_mem, &offset, &error); + } else { + result = dwarf_formref(attr_mem, &offset, &error); + } + + if (result == DW_DLV_OK) { + if (dwarf_offdie(dwarf, offset, &found_die, &error) != DW_DLV_OK) { + found_die = NULL; + } + } + dwarf_dealloc(dwarf, attr_mem, DW_DLA_ATTR); + } + return found_die; + } + + static std::string get_referenced_die_name(Dwarf_Debug dwarf, Dwarf_Die die, + Dwarf_Half attr, bool global) { + Dwarf_Error error = DW_DLE_NE; + std::string value; + + Dwarf_Die found_die = get_referenced_die(dwarf, die, attr, global); + + if (found_die) { + char *name; + if (dwarf_diename(found_die, &name, &error) == DW_DLV_OK) { + if (name) { + value = std::string(name); + } + dwarf_dealloc(dwarf, name, DW_DLA_STRING); + } + dwarf_dealloc(dwarf, found_die, DW_DLA_DIE); + } + + return value; + } + + // Returns a spec DIE linked to the passed one. The caller should + // deallocate the DIE + static Dwarf_Die get_spec_die(dwarf_fileobject &fobj, Dwarf_Die die) { + Dwarf_Debug dwarf = fobj.dwarf_handle.get(); + Dwarf_Error error = DW_DLE_NE; + Dwarf_Off die_offset; + if (fobj.current_cu && + dwarf_die_CU_offset(die, &die_offset, &error) == DW_DLV_OK) { + die_specmap_t::iterator it = + fobj.current_cu->spec_section.find(die_offset); + + // If we have a DIE that completes the current one, check if + // that one has the pc we are looking for + if (it != fobj.current_cu->spec_section.end()) { + Dwarf_Die spec_die = 0; + if (dwarf_offdie(dwarf, it->second, &spec_die, &error) == DW_DLV_OK) { + return spec_die; + } + } + } + + // Maybe we have an abstract origin DIE with the function information? + return get_referenced_die(fobj.dwarf_handle.get(), die, + DW_AT_abstract_origin, true); + } + + static bool die_has_pc(dwarf_fileobject &fobj, Dwarf_Die die, Dwarf_Addr pc) { + Dwarf_Addr low_pc = 0, high_pc = 0; + Dwarf_Half high_pc_form = 0; + Dwarf_Form_Class return_class; + Dwarf_Error error = DW_DLE_NE; + Dwarf_Debug dwarf = fobj.dwarf_handle.get(); + bool has_lowpc = false; + bool has_highpc = false; + bool has_ranges = false; + + if (dwarf_lowpc(die, &low_pc, &error) == DW_DLV_OK) { + // If we have a low_pc check if there is a high pc. + // If we don't have a high pc this might mean we have a base + // address for the ranges list or just an address. + has_lowpc = true; + + if (dwarf_highpc_b(die, &high_pc, &high_pc_form, &return_class, &error) == + DW_DLV_OK) { + // We do have a high pc. In DWARF 4+ this is an offset from the + // low pc, but in earlier versions it's an absolute address. + + has_highpc = true; + // In DWARF 2/3 this would be a DW_FORM_CLASS_ADDRESS + if (return_class == DW_FORM_CLASS_CONSTANT) { + high_pc = low_pc + high_pc; + } + + // We have low and high pc, check if our address + // is in that range + return pc >= low_pc && pc < high_pc; + } + } else { + // Reset the low_pc, in case dwarf_lowpc failing set it to some + // undefined value. + low_pc = 0; + } + + // Check if DW_AT_ranges is present and search for the PC in the + // returned ranges list. We always add the low_pc, as it not set it will + // be 0, in case we had a DW_AT_low_pc and DW_AT_ranges pair + bool result = false; + + Dwarf_Attribute attr; + if (dwarf_attr(die, DW_AT_ranges, &attr, &error) == DW_DLV_OK) { + + Dwarf_Off offset; + if (dwarf_global_formref(attr, &offset, &error) == DW_DLV_OK) { + Dwarf_Ranges *ranges; + Dwarf_Signed ranges_count = 0; + Dwarf_Unsigned byte_count = 0; + + if (dwarf_get_ranges_a(dwarf, offset, die, &ranges, &ranges_count, + &byte_count, &error) == DW_DLV_OK) { + has_ranges = ranges_count != 0; + for (int i = 0; i < ranges_count; i++) { + if (ranges[i].dwr_addr1 != 0 && + pc >= ranges[i].dwr_addr1 + low_pc && + pc < ranges[i].dwr_addr2 + low_pc) { + result = true; + break; + } + } + dwarf_ranges_dealloc(dwarf, ranges, ranges_count); + } + } + } + + // Last attempt. We might have a single address set as low_pc. + if (!result && low_pc != 0 && pc == low_pc) { + result = true; + } + + // If we don't have lowpc, highpc and ranges maybe this DIE is a + // declaration that relies on a DW_AT_specification DIE that happens + // later. Use the specification cache we filled when we loaded this CU. + if (!result && (!has_lowpc && !has_highpc && !has_ranges)) { + Dwarf_Die spec_die = get_spec_die(fobj, die); + if (spec_die) { + result = die_has_pc(fobj, spec_die, pc); + dwarf_dealloc(dwarf, spec_die, DW_DLA_DIE); + } + } + + return result; + } + + static void get_type(Dwarf_Debug dwarf, Dwarf_Die die, std::string &type) { + Dwarf_Error error = DW_DLE_NE; + + Dwarf_Die child = 0; + if (dwarf_child(die, &child, &error) == DW_DLV_OK) { + get_type(dwarf, child, type); + } + + if (child) { + type.insert(0, "::"); + dwarf_dealloc(dwarf, child, DW_DLA_DIE); + } + + char *name; + if (dwarf_diename(die, &name, &error) == DW_DLV_OK) { + type.insert(0, std::string(name)); + dwarf_dealloc(dwarf, name, DW_DLA_STRING); + } else { + type.insert(0, ""); + } + } + + static std::string get_type_by_signature(Dwarf_Debug dwarf, Dwarf_Die die) { + Dwarf_Error error = DW_DLE_NE; + + Dwarf_Sig8 signature; + Dwarf_Bool has_attr = 0; + if (dwarf_hasattr(die, DW_AT_signature, &has_attr, &error) == DW_DLV_OK) { + if (has_attr) { + Dwarf_Attribute attr_mem; + if (dwarf_attr(die, DW_AT_signature, &attr_mem, &error) == DW_DLV_OK) { + if (dwarf_formsig8(attr_mem, &signature, &error) != DW_DLV_OK) { + return std::string(""); + } + } + dwarf_dealloc(dwarf, attr_mem, DW_DLA_ATTR); + } + } + + Dwarf_Unsigned next_cu_header; + Dwarf_Sig8 tu_signature; + std::string result; + bool found = false; + + while (dwarf_next_cu_header_d(dwarf, 0, 0, 0, 0, 0, 0, 0, &tu_signature, 0, + &next_cu_header, 0, &error) == DW_DLV_OK) { + + if (strncmp(signature.signature, tu_signature.signature, 8) == 0) { + Dwarf_Die type_cu_die = 0; + if (dwarf_siblingof_b(dwarf, 0, 0, &type_cu_die, &error) == DW_DLV_OK) { + Dwarf_Die child_die = 0; + if (dwarf_child(type_cu_die, &child_die, &error) == DW_DLV_OK) { + get_type(dwarf, child_die, result); + found = !result.empty(); + dwarf_dealloc(dwarf, child_die, DW_DLA_DIE); + } + dwarf_dealloc(dwarf, type_cu_die, DW_DLA_DIE); + } + } + } + + if (found) { + while (dwarf_next_cu_header_d(dwarf, 0, 0, 0, 0, 0, 0, 0, 0, 0, + &next_cu_header, 0, &error) == DW_DLV_OK) { + // Reset the cu header state. Unfortunately, libdwarf's + // next_cu_header API keeps its own iterator per Dwarf_Debug + // that can't be reset. We need to keep fetching elements until + // the end. + } + } else { + // If we couldn't resolve the type just print out the signature + std::ostringstream string_stream; + string_stream << "<0x" << std::hex << std::setfill('0'); + for (int i = 0; i < 8; ++i) { + string_stream << std::setw(2) << std::hex + << (int)(unsigned char)(signature.signature[i]); + } + string_stream << ">"; + result = string_stream.str(); + } + return result; + } + + struct type_context_t { + bool is_const; + bool is_typedef; + bool has_type; + bool has_name; + std::string text; + + type_context_t() + : is_const(false), is_typedef(false), has_type(false), has_name(false) { + } + }; + + // Types are resolved from right to left: we get the variable name first + // and then all specifiers (like const or pointer) in a chain of DW_AT_type + // DIEs. Call this function recursively until we get a complete type + // string. + static void set_parameter_string(dwarf_fileobject &fobj, Dwarf_Die die, + type_context_t &context) { + char *name; + Dwarf_Error error = DW_DLE_NE; + + // typedefs contain also the base type, so we skip it and only + // print the typedef name + if (!context.is_typedef) { + if (dwarf_diename(die, &name, &error) == DW_DLV_OK) { + if (!context.text.empty()) { + context.text.insert(0, " "); + } + context.text.insert(0, std::string(name)); + dwarf_dealloc(fobj.dwarf_handle.get(), name, DW_DLA_STRING); + } + } else { + context.is_typedef = false; + context.has_type = true; + if (context.is_const) { + context.text.insert(0, "const "); + context.is_const = false; + } + } + + bool next_type_is_const = false; + bool is_keyword = true; + + Dwarf_Half tag = 0; + Dwarf_Bool has_attr = 0; + if (dwarf_tag(die, &tag, &error) == DW_DLV_OK) { + switch (tag) { + case DW_TAG_structure_type: + case DW_TAG_union_type: + case DW_TAG_class_type: + case DW_TAG_enumeration_type: + context.has_type = true; + if (dwarf_hasattr(die, DW_AT_signature, &has_attr, &error) == + DW_DLV_OK) { + // If we have a signature it means the type is defined + // in .debug_types, so we need to load the DIE pointed + // at by the signature and resolve it + if (has_attr) { + std::string type = + get_type_by_signature(fobj.dwarf_handle.get(), die); + if (context.is_const) + type.insert(0, "const "); + + if (!context.text.empty()) + context.text.insert(0, " "); + context.text.insert(0, type); + } + + // Treat enums like typedefs, and skip printing its + // base type + context.is_typedef = (tag == DW_TAG_enumeration_type); + } + break; + case DW_TAG_const_type: + next_type_is_const = true; + break; + case DW_TAG_pointer_type: + context.text.insert(0, "*"); + break; + case DW_TAG_reference_type: + context.text.insert(0, "&"); + break; + case DW_TAG_restrict_type: + context.text.insert(0, "restrict "); + break; + case DW_TAG_rvalue_reference_type: + context.text.insert(0, "&&"); + break; + case DW_TAG_volatile_type: + context.text.insert(0, "volatile "); + break; + case DW_TAG_typedef: + // Propagate the const-ness to the next type + // as typedefs are linked to its base type + next_type_is_const = context.is_const; + context.is_typedef = true; + context.has_type = true; + break; + case DW_TAG_base_type: + context.has_type = true; + break; + case DW_TAG_formal_parameter: + context.has_name = true; + break; + default: + is_keyword = false; + break; + } + } + + if (!is_keyword && context.is_const) { + context.text.insert(0, "const "); + } + + context.is_const = next_type_is_const; + + Dwarf_Die ref = + get_referenced_die(fobj.dwarf_handle.get(), die, DW_AT_type, true); + if (ref) { + set_parameter_string(fobj, ref, context); + dwarf_dealloc(fobj.dwarf_handle.get(), ref, DW_DLA_DIE); + } + + if (!context.has_type && context.has_name) { + context.text.insert(0, "void "); + context.has_type = true; + } + } + + // Resolve the function return type and parameters + static void set_function_parameters(std::string &function_name, + std::vector &ns, + dwarf_fileobject &fobj, Dwarf_Die die) { + Dwarf_Debug dwarf = fobj.dwarf_handle.get(); + Dwarf_Error error = DW_DLE_NE; + Dwarf_Die current_die = 0; + std::string parameters; + bool has_spec = true; + // Check if we have a spec DIE. If we do we use it as it contains + // more information, like parameter names. + Dwarf_Die spec_die = get_spec_die(fobj, die); + if (!spec_die) { + has_spec = false; + spec_die = die; + } + + std::vector::const_iterator it = ns.begin(); + std::string ns_name; + for (it = ns.begin(); it < ns.end(); ++it) { + ns_name.append(*it).append("::"); + } + + if (!ns_name.empty()) { + function_name.insert(0, ns_name); + } + + // See if we have a function return type. It can be either on the + // current die or in its spec one (usually true for inlined functions) + std::string return_type = + get_referenced_die_name(dwarf, die, DW_AT_type, true); + if (return_type.empty()) { + return_type = get_referenced_die_name(dwarf, spec_die, DW_AT_type, true); + } + if (!return_type.empty()) { + return_type.append(" "); + function_name.insert(0, return_type); + } + + if (dwarf_child(spec_die, ¤t_die, &error) == DW_DLV_OK) { + for (;;) { + Dwarf_Die sibling_die = 0; + + Dwarf_Half tag_value; + dwarf_tag(current_die, &tag_value, &error); + + if (tag_value == DW_TAG_formal_parameter) { + // Ignore artificial (ie, compiler generated) parameters + bool is_artificial = false; + Dwarf_Attribute attr_mem; + if (dwarf_attr(current_die, DW_AT_artificial, &attr_mem, &error) == + DW_DLV_OK) { + Dwarf_Bool flag = 0; + if (dwarf_formflag(attr_mem, &flag, &error) == DW_DLV_OK) { + is_artificial = flag != 0; + } + dwarf_dealloc(dwarf, attr_mem, DW_DLA_ATTR); + } + + if (!is_artificial) { + type_context_t context; + set_parameter_string(fobj, current_die, context); + + if (parameters.empty()) { + parameters.append("("); + } else { + parameters.append(", "); + } + parameters.append(context.text); + } + } + + int result = dwarf_siblingof(dwarf, current_die, &sibling_die, &error); + if (result == DW_DLV_ERROR) { + break; + } else if (result == DW_DLV_NO_ENTRY) { + break; + } + + if (current_die != die) { + dwarf_dealloc(dwarf, current_die, DW_DLA_DIE); + current_die = 0; + } + + current_die = sibling_die; + } + } + if (parameters.empty()) + parameters = "("; + parameters.append(")"); + + // If we got a spec DIE we need to deallocate it + if (has_spec) + dwarf_dealloc(dwarf, spec_die, DW_DLA_DIE); + + function_name.append(parameters); + } + + // defined here because in C++98, template function cannot take locally + // defined types... grrr. + struct inliners_search_cb { + void operator()(Dwarf_Die die, std::vector &ns) { + Dwarf_Error error = DW_DLE_NE; + Dwarf_Half tag_value; + Dwarf_Attribute attr_mem; + Dwarf_Debug dwarf = fobj.dwarf_handle.get(); + + dwarf_tag(die, &tag_value, &error); + + switch (tag_value) { + char *name; + case DW_TAG_subprogram: + if (!trace.source.function.empty()) + break; + if (dwarf_diename(die, &name, &error) == DW_DLV_OK) { + trace.source.function = std::string(name); + dwarf_dealloc(dwarf, name, DW_DLA_STRING); + } else { + // We don't have a function name in this DIE. + // Check if there is a referenced non-defining + // declaration. + trace.source.function = + get_referenced_die_name(dwarf, die, DW_AT_abstract_origin, true); + if (trace.source.function.empty()) { + trace.source.function = + get_referenced_die_name(dwarf, die, DW_AT_specification, true); + } + } + + // Append the function parameters, if available + set_function_parameters(trace.source.function, ns, fobj, die); + + // If the object function name is empty, it's possible that + // there is no dynamic symbol table (maybe the executable + // was stripped or not built with -rdynamic). See if we have + // a DWARF linkage name to use instead. We try both + // linkage_name and MIPS_linkage_name because the MIPS tag + // was the unofficial one until it was adopted in DWARF4. + // Old gcc versions generate MIPS_linkage_name + if (trace.object_function.empty()) { + details::demangler demangler; + + if (dwarf_attr(die, DW_AT_linkage_name, &attr_mem, &error) != + DW_DLV_OK) { + if (dwarf_attr(die, DW_AT_MIPS_linkage_name, &attr_mem, &error) != + DW_DLV_OK) { + break; + } + } + + char *linkage; + if (dwarf_formstring(attr_mem, &linkage, &error) == DW_DLV_OK) { + trace.object_function = demangler.demangle(linkage); + dwarf_dealloc(dwarf, linkage, DW_DLA_STRING); + } + dwarf_dealloc(dwarf, attr_mem, DW_DLA_ATTR); + } + break; + + case DW_TAG_inlined_subroutine: + ResolvedTrace::SourceLoc sloc; + + if (dwarf_diename(die, &name, &error) == DW_DLV_OK) { + sloc.function = std::string(name); + dwarf_dealloc(dwarf, name, DW_DLA_STRING); + } else { + // We don't have a name for this inlined DIE, it could + // be that there is an abstract origin instead. + // Get the DW_AT_abstract_origin value, which is a + // reference to the source DIE and try to get its name + sloc.function = + get_referenced_die_name(dwarf, die, DW_AT_abstract_origin, true); + } + + set_function_parameters(sloc.function, ns, fobj, die); + + std::string file = die_call_file(dwarf, die, cu_die); + if (!file.empty()) + sloc.filename = file; + + Dwarf_Unsigned number = 0; + if (dwarf_attr(die, DW_AT_call_line, &attr_mem, &error) == DW_DLV_OK) { + if (dwarf_formudata(attr_mem, &number, &error) == DW_DLV_OK) { + sloc.line = number; + } + dwarf_dealloc(dwarf, attr_mem, DW_DLA_ATTR); + } + + if (dwarf_attr(die, DW_AT_call_column, &attr_mem, &error) == + DW_DLV_OK) { + if (dwarf_formudata(attr_mem, &number, &error) == DW_DLV_OK) { + sloc.col = number; + } + dwarf_dealloc(dwarf, attr_mem, DW_DLA_ATTR); + } + + trace.inliners.push_back(sloc); + break; + }; + } + ResolvedTrace &trace; + dwarf_fileobject &fobj; + Dwarf_Die cu_die; + inliners_search_cb(ResolvedTrace &t, dwarf_fileobject &f, Dwarf_Die c) + : trace(t), fobj(f), cu_die(c) {} + }; + + static Dwarf_Die find_fundie_by_pc(dwarf_fileobject &fobj, + Dwarf_Die parent_die, Dwarf_Addr pc, + Dwarf_Die result) { + Dwarf_Die current_die = 0; + Dwarf_Error error = DW_DLE_NE; + Dwarf_Debug dwarf = fobj.dwarf_handle.get(); + + if (dwarf_child(parent_die, ¤t_die, &error) != DW_DLV_OK) { + return NULL; + } + + for (;;) { + Dwarf_Die sibling_die = 0; + Dwarf_Half tag_value; + dwarf_tag(current_die, &tag_value, &error); + + switch (tag_value) { + case DW_TAG_subprogram: + case DW_TAG_inlined_subroutine: + if (die_has_pc(fobj, current_die, pc)) { + return current_die; + } + }; + bool declaration = false; + Dwarf_Attribute attr_mem; + if (dwarf_attr(current_die, DW_AT_declaration, &attr_mem, &error) == + DW_DLV_OK) { + Dwarf_Bool flag = 0; + if (dwarf_formflag(attr_mem, &flag, &error) == DW_DLV_OK) { + declaration = flag != 0; + } + dwarf_dealloc(dwarf, attr_mem, DW_DLA_ATTR); + } + + if (!declaration) { + // let's be curious and look deeper in the tree, functions are + // not necessarily at the first level, but might be nested + // inside a namespace, structure, a function, an inlined + // function etc. + Dwarf_Die die_mem = 0; + Dwarf_Die indie = find_fundie_by_pc(fobj, current_die, pc, die_mem); + if (indie) { + result = die_mem; + return result; + } + } + + int res = dwarf_siblingof(dwarf, current_die, &sibling_die, &error); + if (res == DW_DLV_ERROR) { + return NULL; + } else if (res == DW_DLV_NO_ENTRY) { + break; + } + + if (current_die != parent_die) { + dwarf_dealloc(dwarf, current_die, DW_DLA_DIE); + current_die = 0; + } + + current_die = sibling_die; + } + return NULL; + } + + template + static bool deep_first_search_by_pc(dwarf_fileobject &fobj, + Dwarf_Die parent_die, Dwarf_Addr pc, + std::vector &ns, CB cb) { + Dwarf_Die current_die = 0; + Dwarf_Debug dwarf = fobj.dwarf_handle.get(); + Dwarf_Error error = DW_DLE_NE; + + if (dwarf_child(parent_die, ¤t_die, &error) != DW_DLV_OK) { + return false; + } + + bool branch_has_pc = false; + bool has_namespace = false; + for (;;) { + Dwarf_Die sibling_die = 0; + + Dwarf_Half tag; + if (dwarf_tag(current_die, &tag, &error) == DW_DLV_OK) { + if (tag == DW_TAG_namespace || tag == DW_TAG_class_type) { + char *ns_name = NULL; + if (dwarf_diename(current_die, &ns_name, &error) == DW_DLV_OK) { + if (ns_name) { + ns.push_back(std::string(ns_name)); + } else { + ns.push_back(""); + } + dwarf_dealloc(dwarf, ns_name, DW_DLA_STRING); + } else { + ns.push_back(""); + } + has_namespace = true; + } + } + + bool declaration = false; + Dwarf_Attribute attr_mem; + if (tag != DW_TAG_class_type && + dwarf_attr(current_die, DW_AT_declaration, &attr_mem, &error) == + DW_DLV_OK) { + Dwarf_Bool flag = 0; + if (dwarf_formflag(attr_mem, &flag, &error) == DW_DLV_OK) { + declaration = flag != 0; + } + dwarf_dealloc(dwarf, attr_mem, DW_DLA_ATTR); + } + + if (!declaration) { + // let's be curious and look deeper in the tree, function are + // not necessarily at the first level, but might be nested + // inside a namespace, structure, a function, an inlined + // function etc. + branch_has_pc = deep_first_search_by_pc(fobj, current_die, pc, ns, cb); + } + + if (!branch_has_pc) { + branch_has_pc = die_has_pc(fobj, current_die, pc); + } + + if (branch_has_pc) { + cb(current_die, ns); + } + + int result = dwarf_siblingof(dwarf, current_die, &sibling_die, &error); + if (result == DW_DLV_ERROR) { + return false; + } else if (result == DW_DLV_NO_ENTRY) { + break; + } + + if (current_die != parent_die) { + dwarf_dealloc(dwarf, current_die, DW_DLA_DIE); + current_die = 0; + } + + if (has_namespace) { + has_namespace = false; + ns.pop_back(); + } + current_die = sibling_die; + } + + if (has_namespace) { + ns.pop_back(); + } + return branch_has_pc; + } + + static std::string die_call_file(Dwarf_Debug dwarf, Dwarf_Die die, + Dwarf_Die cu_die) { + Dwarf_Attribute attr_mem; + Dwarf_Error error = DW_DLE_NE; + Dwarf_Unsigned file_index; + + std::string file; + + if (dwarf_attr(die, DW_AT_call_file, &attr_mem, &error) == DW_DLV_OK) { + if (dwarf_formudata(attr_mem, &file_index, &error) != DW_DLV_OK) { + file_index = 0; + } + dwarf_dealloc(dwarf, attr_mem, DW_DLA_ATTR); + + if (file_index == 0) { + return file; + } + + char **srcfiles = 0; + Dwarf_Signed file_count = 0; + if (dwarf_srcfiles(cu_die, &srcfiles, &file_count, &error) == DW_DLV_OK) { + if (file_count > 0 && file_index <= static_cast(file_count)) { + file = std::string(srcfiles[file_index - 1]); + } + + // Deallocate all strings! + for (int i = 0; i < file_count; ++i) { + dwarf_dealloc(dwarf, srcfiles[i], DW_DLA_STRING); + } + dwarf_dealloc(dwarf, srcfiles, DW_DLA_LIST); + } + } + return file; + } + + Dwarf_Die find_die(dwarf_fileobject &fobj, Dwarf_Addr addr) { + // Let's get to work! First see if we have a debug_aranges section so + // we can speed up the search + + Dwarf_Debug dwarf = fobj.dwarf_handle.get(); + Dwarf_Error error = DW_DLE_NE; + Dwarf_Arange *aranges; + Dwarf_Signed arange_count; + + Dwarf_Die returnDie; + bool found = false; + if (dwarf_get_aranges(dwarf, &aranges, &arange_count, &error) != + DW_DLV_OK) { + aranges = NULL; + } + + if (aranges) { + // We have aranges. Get the one where our address is. + Dwarf_Arange arange; + if (dwarf_get_arange(aranges, arange_count, addr, &arange, &error) == + DW_DLV_OK) { + + // We found our address. Get the compilation-unit DIE offset + // represented by the given address range. + Dwarf_Off cu_die_offset; + if (dwarf_get_cu_die_offset(arange, &cu_die_offset, &error) == + DW_DLV_OK) { + // Get the DIE at the offset returned by the aranges search. + // We set is_info to 1 to specify that the offset is from + // the .debug_info section (and not .debug_types) + int dwarf_result = + dwarf_offdie_b(dwarf, cu_die_offset, 1, &returnDie, &error); + + found = dwarf_result == DW_DLV_OK; + } + dwarf_dealloc(dwarf, arange, DW_DLA_ARANGE); + } + } + + if (found) + return returnDie; // The caller is responsible for freeing the die + + // The search for aranges failed. Try to find our address by scanning + // all compilation units. + Dwarf_Unsigned next_cu_header; + Dwarf_Half tag = 0; + returnDie = 0; + + while (!found && + dwarf_next_cu_header_d(dwarf, 1, 0, 0, 0, 0, 0, 0, 0, 0, + &next_cu_header, 0, &error) == DW_DLV_OK) { + + if (returnDie) + dwarf_dealloc(dwarf, returnDie, DW_DLA_DIE); + + if (dwarf_siblingof(dwarf, 0, &returnDie, &error) == DW_DLV_OK) { + if ((dwarf_tag(returnDie, &tag, &error) == DW_DLV_OK) && + tag == DW_TAG_compile_unit) { + if (die_has_pc(fobj, returnDie, addr)) { + found = true; + } + } + } + } + + if (found) { + while (dwarf_next_cu_header_d(dwarf, 1, 0, 0, 0, 0, 0, 0, 0, 0, + &next_cu_header, 0, &error) == DW_DLV_OK) { + // Reset the cu header state. Libdwarf's next_cu_header API + // keeps its own iterator per Dwarf_Debug that can't be reset. + // We need to keep fetching elements until the end. + } + } + + if (found) + return returnDie; + + // We couldn't find any compilation units with ranges or a high/low pc. + // Try again by looking at all DIEs in all compilation units. + Dwarf_Die cudie; + while (dwarf_next_cu_header_d(dwarf, 1, 0, 0, 0, 0, 0, 0, 0, 0, + &next_cu_header, 0, &error) == DW_DLV_OK) { + if (dwarf_siblingof(dwarf, 0, &cudie, &error) == DW_DLV_OK) { + Dwarf_Die die_mem = 0; + Dwarf_Die resultDie = find_fundie_by_pc(fobj, cudie, addr, die_mem); + + if (resultDie) { + found = true; + break; + } + } + } + + if (found) { + while (dwarf_next_cu_header_d(dwarf, 1, 0, 0, 0, 0, 0, 0, 0, 0, + &next_cu_header, 0, &error) == DW_DLV_OK) { + // Reset the cu header state. Libdwarf's next_cu_header API + // keeps its own iterator per Dwarf_Debug that can't be reset. + // We need to keep fetching elements until the end. + } + } + + if (found) + return cudie; + + // We failed. + return NULL; + } +}; +#endif // BACKWARD_HAS_DWARF == 1 + +template <> +class TraceResolverImpl + : public TraceResolverLinuxImpl {}; + +#endif // BACKWARD_SYSTEM_LINUX + +#ifdef BACKWARD_SYSTEM_DARWIN + +template class TraceResolverDarwinImpl; + +template <> +class TraceResolverDarwinImpl + : public TraceResolverImplBase { +public: + void load_addresses(void *const*addresses, int address_count) override { + if (address_count == 0) { + return; + } + _symbols.reset(backtrace_symbols(addresses, address_count)); + } + + ResolvedTrace resolve(ResolvedTrace trace) override { + // parse: + // + + char *filename = _symbols[trace.idx]; + + // skip " " + while (*filename && *filename != ' ') + filename++; + while (*filename == ' ') + filename++; + + // find start of from end ( may contain a space) + char *p = filename + strlen(filename) - 1; + // skip to start of " + " + while (p > filename && *p != ' ') + p--; + while (p > filename && *p == ' ') + p--; + while (p > filename && *p != ' ') + p--; + while (p > filename && *p == ' ') + p--; + char *funcname_end = p + 1; + + // skip to start of "" + while (p > filename && *p != ' ') + p--; + char *funcname = p + 1; + + // skip to start of " " + while (p > filename && *p == ' ') + p--; + while (p > filename && *p != ' ') + p--; + while (p > filename && *p == ' ') + p--; + + // skip "", handling the case where it contains a + char *filename_end = p + 1; + if (p == filename) { + // something went wrong, give up + filename_end = filename + strlen(filename); + funcname = filename_end; + } + trace.object_filename.assign( + filename, filename_end); // ok even if filename_end is the ending \0 + // (then we assign entire string) + + if (*funcname) { // if it's not end of string + *funcname_end = '\0'; + + trace.object_function = this->demangle(funcname); + trace.object_function += " "; + trace.object_function += (funcname_end + 1); + trace.source.function = trace.object_function; // we cannot do better. + } + return trace; + } + +private: + details::handle _symbols; +}; + +template <> +class TraceResolverImpl + : public TraceResolverDarwinImpl {}; + +#endif // BACKWARD_SYSTEM_DARWIN + +#ifdef BACKWARD_SYSTEM_WINDOWS + +// Load all symbol info +// Based on: +// https://stackoverflow.com/questions/6205981/windows-c-stack-trace-from-a-running-app/28276227#28276227 + +struct module_data { + std::string image_name; + std::string module_name; + void *base_address; + DWORD load_size; +}; + +class get_mod_info { + HANDLE process; + static const int buffer_length = 4096; + +public: + get_mod_info(HANDLE h) : process(h) {} + + module_data operator()(HMODULE module) { + module_data ret; + char temp[buffer_length]; + MODULEINFO mi; + + GetModuleInformation(process, module, &mi, sizeof(mi)); + ret.base_address = mi.lpBaseOfDll; + ret.load_size = mi.SizeOfImage; + + GetModuleFileNameExA(process, module, temp, sizeof(temp)); + ret.image_name = temp; + GetModuleBaseNameA(process, module, temp, sizeof(temp)); + ret.module_name = temp; + std::vector img(ret.image_name.begin(), ret.image_name.end()); + std::vector mod(ret.module_name.begin(), ret.module_name.end()); + SymLoadModule64(process, 0, &img[0], &mod[0], (DWORD64)ret.base_address, + ret.load_size); + return ret; + } +}; + +template <> class TraceResolverImpl + : public TraceResolverImplBase { +public: + TraceResolverImpl() { + + HANDLE process = GetCurrentProcess(); + + std::vector modules; + DWORD cbNeeded; + std::vector module_handles(1); + SymInitialize(process, NULL, false); + DWORD symOptions = SymGetOptions(); + symOptions |= SYMOPT_LOAD_LINES | SYMOPT_UNDNAME; + SymSetOptions(symOptions); + EnumProcessModules(process, &module_handles[0], + module_handles.size() * sizeof(HMODULE), &cbNeeded); + module_handles.resize(cbNeeded / sizeof(HMODULE)); + EnumProcessModules(process, &module_handles[0], + module_handles.size() * sizeof(HMODULE), &cbNeeded); + std::transform(module_handles.begin(), module_handles.end(), + std::back_inserter(modules), get_mod_info(process)); + void *base = modules[0].base_address; + IMAGE_NT_HEADERS *h = ImageNtHeader(base); + image_type = h->FileHeader.Machine; + } + + static const int max_sym_len = 255; + struct symbol_t { + SYMBOL_INFO sym; + char buffer[max_sym_len]; + } sym; + + DWORD64 displacement; + + ResolvedTrace resolve(ResolvedTrace t) override { + HANDLE process = GetCurrentProcess(); + + char name[256]; + + memset(&sym, 0, sizeof(sym)); + sym.sym.SizeOfStruct = sizeof(SYMBOL_INFO); + sym.sym.MaxNameLen = max_sym_len; + + if (!SymFromAddr(process, (ULONG64)t.addr, &displacement, &sym.sym)) { + // TODO: error handling everywhere + char* lpMsgBuf; + DWORD dw = GetLastError(); + + if (FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER | + FORMAT_MESSAGE_FROM_SYSTEM | + FORMAT_MESSAGE_IGNORE_INSERTS, + NULL, dw, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), + (char*)&lpMsgBuf, 0, NULL)) { + std::fprintf(stderr, "%s\n", lpMsgBuf); + LocalFree(lpMsgBuf); + } + + // abort(); + } + UnDecorateSymbolName(sym.sym.Name, (PSTR)name, 256, UNDNAME_COMPLETE); + + DWORD offset = 0; + IMAGEHLP_LINE line; + if (SymGetLineFromAddr(process, (ULONG64)t.addr, &offset, &line)) { + t.object_filename = line.FileName; + t.source.filename = line.FileName; + t.source.line = line.LineNumber; + t.source.col = offset; + } + + t.source.function = name; + t.object_filename = ""; + t.object_function = name; + + return t; + } + + DWORD machine_type() const { return image_type; } + +private: + DWORD image_type; +}; + +#endif + +class TraceResolver : public TraceResolverImpl {}; + +/*************** CODE SNIPPET ***************/ + +class SourceFile { +public: + typedef std::vector > lines_t; + + SourceFile() {} + SourceFile(const std::string &path) { + // 1. If BACKWARD_CXX_SOURCE_PREFIXES is set then assume it contains + // a colon-separated list of path prefixes. Try prepending each + // to the given path until a valid file is found. + const std::vector &prefixes = get_paths_from_env_variable(); + for (size_t i = 0; i < prefixes.size(); ++i) { + // Double slashes (//) should not be a problem. + std::string new_path = prefixes[i] + '/' + path; + _file.reset(new std::ifstream(new_path.c_str())); + if (is_open()) + break; + } + // 2. If no valid file found then fallback to opening the path as-is. + if (!_file || !is_open()) { + _file.reset(new std::ifstream(path.c_str())); + } + } + bool is_open() const { return _file->is_open(); } + + lines_t &get_lines(unsigned line_start, unsigned line_count, lines_t &lines) { + using namespace std; + // This function make uses of the dumbest algo ever: + // 1) seek(0) + // 2) read lines one by one and discard until line_start + // 3) read line one by one until line_start + line_count + // + // If you are getting snippets many time from the same file, it is + // somewhat a waste of CPU, feel free to benchmark and propose a + // better solution ;) + + _file->clear(); + _file->seekg(0); + string line; + unsigned line_idx; + + for (line_idx = 1; line_idx < line_start; ++line_idx) { + std::getline(*_file, line); + if (!*_file) { + return lines; + } + } + + // think of it like a lambda in C++98 ;) + // but look, I will reuse it two times! + // What a good boy am I. + struct isspace { + bool operator()(char c) { return std::isspace(c); } + }; + + bool started = false; + for (; line_idx < line_start + line_count; ++line_idx) { + getline(*_file, line); + if (!*_file) { + return lines; + } + if (!started) { + if (std::find_if(line.begin(), line.end(), not_isspace()) == line.end()) + continue; + started = true; + } + lines.push_back(make_pair(line_idx, line)); + } + + lines.erase( + std::find_if(lines.rbegin(), lines.rend(), not_isempty()).base(), + lines.end()); + return lines; + } + + lines_t get_lines(unsigned line_start, unsigned line_count) { + lines_t lines; + return get_lines(line_start, line_count, lines); + } + + // there is no find_if_not in C++98, lets do something crappy to + // workaround. + struct not_isspace { + bool operator()(char c) { return !std::isspace(c); } + }; + // and define this one here because C++98 is not happy with local defined + // struct passed to template functions, fuuuu. + struct not_isempty { + bool operator()(const lines_t::value_type &p) { + return !(std::find_if(p.second.begin(), p.second.end(), not_isspace()) == + p.second.end()); + } + }; + + void swap(SourceFile &b) { _file.swap(b._file); } + +#ifdef BACKWARD_ATLEAST_CXX11 + SourceFile(SourceFile &&from) : _file(nullptr) { swap(from); } + SourceFile &operator=(SourceFile &&from) { + swap(from); + return *this; + } +#else + explicit SourceFile(const SourceFile &from) { + // some sort of poor man's move semantic. + swap(const_cast(from)); + } + SourceFile &operator=(const SourceFile &from) { + // some sort of poor man's move semantic. + swap(const_cast(from)); + return *this; + } +#endif + +private: + details::handle > + _file; + + std::vector get_paths_from_env_variable_impl() { + std::vector paths; + const char *prefixes_str = std::getenv("BACKWARD_CXX_SOURCE_PREFIXES"); + if (prefixes_str && prefixes_str[0]) { + paths = details::split_source_prefixes(prefixes_str); + } + return paths; + } + + const std::vector &get_paths_from_env_variable() { + static std::vector paths = get_paths_from_env_variable_impl(); + return paths; + } + +#ifdef BACKWARD_ATLEAST_CXX11 + SourceFile(const SourceFile &) = delete; + SourceFile &operator=(const SourceFile &) = delete; +#endif +}; + +class SnippetFactory { +public: + typedef SourceFile::lines_t lines_t; + + lines_t get_snippet(const std::string &filename, unsigned line_start, + unsigned context_size) { + + SourceFile &src_file = get_src_file(filename); + unsigned start = line_start - context_size / 2; + return src_file.get_lines(start, context_size); + } + + lines_t get_combined_snippet(const std::string &filename_a, unsigned line_a, + const std::string &filename_b, unsigned line_b, + unsigned context_size) { + SourceFile &src_file_a = get_src_file(filename_a); + SourceFile &src_file_b = get_src_file(filename_b); + + lines_t lines = + src_file_a.get_lines(line_a - context_size / 4, context_size / 2); + src_file_b.get_lines(line_b - context_size / 4, context_size / 2, lines); + return lines; + } + + lines_t get_coalesced_snippet(const std::string &filename, unsigned line_a, + unsigned line_b, unsigned context_size) { + SourceFile &src_file = get_src_file(filename); + + using std::max; + using std::min; + unsigned a = min(line_a, line_b); + unsigned b = max(line_a, line_b); + + if ((b - a) < (context_size / 3)) { + return src_file.get_lines((a + b - context_size + 1) / 2, context_size); + } + + lines_t lines = src_file.get_lines(a - context_size / 4, context_size / 2); + src_file.get_lines(b - context_size / 4, context_size / 2, lines); + return lines; + } + +private: + typedef details::hashtable::type src_files_t; + src_files_t _src_files; + + SourceFile &get_src_file(const std::string &filename) { + src_files_t::iterator it = _src_files.find(filename); + if (it != _src_files.end()) { + return it->second; + } + SourceFile &new_src_file = _src_files[filename]; + new_src_file = SourceFile(filename); + return new_src_file; + } +}; + +/*************** PRINTER ***************/ + +namespace ColorMode { +enum type { automatic, never, always }; +} + +class cfile_streambuf : public std::streambuf { +public: + cfile_streambuf(FILE *_sink) : sink(_sink) {} + int_type underflow() override { return traits_type::eof(); } + int_type overflow(int_type ch) override { + if (traits_type::not_eof(ch) && fputc(ch, sink) != EOF) { + return ch; + } + return traits_type::eof(); + } + + std::streamsize xsputn(const char_type *s, std::streamsize count) override { + return static_cast( + fwrite(s, sizeof *s, static_cast(count), sink)); + } + +#ifdef BACKWARD_ATLEAST_CXX11 +public: + cfile_streambuf(const cfile_streambuf &) = delete; + cfile_streambuf &operator=(const cfile_streambuf &) = delete; +#else +private: + cfile_streambuf(const cfile_streambuf &); + cfile_streambuf &operator=(const cfile_streambuf &); +#endif + +private: + FILE *sink; + std::vector buffer; +}; + +#ifdef BACKWARD_SYSTEM_LINUX + +namespace Color { +enum type { yellow = 33, purple = 35, reset = 39 }; +} // namespace Color + +class Colorize { +public: + Colorize(std::ostream &os) : _os(os), _reset(false), _enabled(false) {} + + void activate(ColorMode::type mode) { _enabled = mode == ColorMode::always; } + + void activate(ColorMode::type mode, FILE *fp) { activate(mode, fileno(fp)); } + + void set_color(Color::type ccode) { + if (!_enabled) + return; + + // I assume that the terminal can handle basic colors. Seriously I + // don't want to deal with all the termcap shit. + _os << "\033[" << static_cast(ccode) << "m"; + _reset = (ccode != Color::reset); + } + + ~Colorize() { + if (_reset) { + set_color(Color::reset); + } + } + +private: + void activate(ColorMode::type mode, int fd) { + activate(mode == ColorMode::automatic && isatty(fd) ? ColorMode::always + : mode); + } + + std::ostream &_os; + bool _reset; + bool _enabled; +}; + +#else // ndef BACKWARD_SYSTEM_LINUX + +namespace Color { +enum type { yellow = 0, purple = 0, reset = 0 }; +} // namespace Color + +class Colorize { +public: + Colorize(std::ostream &) {} + void activate(ColorMode::type) {} + void activate(ColorMode::type, FILE *) {} + void set_color(Color::type) {} +}; + +#endif // BACKWARD_SYSTEM_LINUX + +class Printer { +public: + bool snippet; + ColorMode::type color_mode; + bool address; + bool object; + int inliner_context_size; + int trace_context_size; + + Printer() + : snippet(true), color_mode(ColorMode::automatic), address(false), + object(false), inliner_context_size(5), trace_context_size(7) {} + + template FILE *print(ST &st, FILE *fp = stderr) { + cfile_streambuf obuf(fp); + std::ostream os(&obuf); + Colorize colorize(os); + colorize.activate(color_mode, fp); + print_stacktrace(st, os, colorize); + return fp; + } + + template std::ostream &print(ST &st, std::ostream &os) { + Colorize colorize(os); + colorize.activate(color_mode); + print_stacktrace(st, os, colorize); + return os; + } + + template + FILE *print(IT begin, IT end, FILE *fp = stderr, size_t thread_id = 0) { + cfile_streambuf obuf(fp); + std::ostream os(&obuf); + Colorize colorize(os); + colorize.activate(color_mode, fp); + print_stacktrace(begin, end, os, thread_id, colorize); + return fp; + } + + template + std::ostream &print(IT begin, IT end, std::ostream &os, + size_t thread_id = 0) { + Colorize colorize(os); + colorize.activate(color_mode); + print_stacktrace(begin, end, os, thread_id, colorize); + return os; + } + + TraceResolver const &resolver() const { return _resolver; } + +private: + TraceResolver _resolver; + SnippetFactory _snippets; + + template + void print_stacktrace(ST &st, std::ostream &os, Colorize &colorize) { + print_header(os, st.thread_id()); + _resolver.load_stacktrace(st); + for (size_t trace_idx = st.size(); trace_idx > 0; --trace_idx) { + print_trace(os, _resolver.resolve(st[trace_idx - 1]), colorize); + } + } + + template + void print_stacktrace(IT begin, IT end, std::ostream &os, size_t thread_id, + Colorize &colorize) { + print_header(os, thread_id); + for (; begin != end; ++begin) { + print_trace(os, *begin, colorize); + } + } + + void print_header(std::ostream &os, size_t thread_id) { + os << "Stack trace (most recent call last)"; + if (thread_id) { + os << " in thread " << thread_id; + } + os << ":\n"; + } + + void print_trace(std::ostream &os, const ResolvedTrace &trace, + Colorize &colorize) { + os << "#" << std::left << std::setw(2) << trace.idx << std::right; + bool already_indented = true; + + if (!trace.source.filename.size() || object) { + os << " Object \"" << trace.object_filename << "\", at " << trace.addr + << ", in " << trace.object_function << "\n"; + already_indented = false; + } + + for (size_t inliner_idx = trace.inliners.size(); inliner_idx > 0; + --inliner_idx) { + if (!already_indented) { + os << " "; + } + const ResolvedTrace::SourceLoc &inliner_loc = + trace.inliners[inliner_idx - 1]; + print_source_loc(os, " | ", inliner_loc); + if (snippet) { + print_snippet(os, " | ", inliner_loc, colorize, Color::purple, + inliner_context_size); + } + already_indented = false; + } + + if (trace.source.filename.size()) { + if (!already_indented) { + os << " "; + } + print_source_loc(os, " ", trace.source, trace.addr); + if (snippet) { + print_snippet(os, " ", trace.source, colorize, Color::yellow, + trace_context_size); + } + } + } + + void print_snippet(std::ostream &os, const char *indent, + const ResolvedTrace::SourceLoc &source_loc, + Colorize &colorize, Color::type color_code, + int context_size) { + using namespace std; + typedef SnippetFactory::lines_t lines_t; + + lines_t lines = _snippets.get_snippet(source_loc.filename, source_loc.line, + static_cast(context_size)); + + for (lines_t::const_iterator it = lines.begin(); it != lines.end(); ++it) { + if (it->first == source_loc.line) { + colorize.set_color(color_code); + os << indent << ">"; + } else { + os << indent << " "; + } + os << std::setw(4) << it->first << ": " << it->second << "\n"; + if (it->first == source_loc.line) { + colorize.set_color(Color::reset); + } + } + } + + void print_source_loc(std::ostream &os, const char *indent, + const ResolvedTrace::SourceLoc &source_loc, + void *addr = nullptr) { + os << indent << "Source \"" << source_loc.filename << "\", line " + << source_loc.line << ", in " << source_loc.function; + + if (address && addr != nullptr) { + os << " [" << addr << "]"; + } + os << "\n"; + } +}; + +/*************** SIGNALS HANDLING ***************/ + +#if defined(BACKWARD_SYSTEM_LINUX) || defined(BACKWARD_SYSTEM_DARWIN) + +class SignalHandling { +public: + static std::vector make_default_signals() { + const int posix_signals[] = { + // Signals for which the default action is "Core". + SIGABRT, // Abort signal from abort(3) + SIGBUS, // Bus error (bad memory access) + SIGFPE, // Floating point exception + SIGILL, // Illegal Instruction + SIGIOT, // IOT trap. A synonym for SIGABRT + SIGQUIT, // Quit from keyboard + SIGSEGV, // Invalid memory reference + SIGSYS, // Bad argument to routine (SVr4) + SIGTRAP, // Trace/breakpoint trap + SIGXCPU, // CPU time limit exceeded (4.2BSD) + SIGXFSZ, // File size limit exceeded (4.2BSD) +#if defined(BACKWARD_SYSTEM_DARWIN) + SIGEMT, // emulation instruction executed +#endif + }; + return std::vector(posix_signals, + posix_signals + + sizeof posix_signals / sizeof posix_signals[0]); + } + + SignalHandling(const std::vector &posix_signals = make_default_signals()) + : _loaded(false) { + bool success = true; + + const size_t stack_size = 1024 * 1024 * 8; + _stack_content.reset(static_cast(malloc(stack_size))); + if (_stack_content) { + stack_t ss; + ss.ss_sp = _stack_content.get(); + ss.ss_size = stack_size; + ss.ss_flags = 0; + if (sigaltstack(&ss, nullptr) < 0) { + success = false; + } + } else { + success = false; + } + + for (size_t i = 0; i < posix_signals.size(); ++i) { + struct sigaction action; + memset(&action, 0, sizeof action); + action.sa_flags = + static_cast(SA_SIGINFO | SA_ONSTACK | SA_NODEFER | SA_RESETHAND); + sigfillset(&action.sa_mask); + sigdelset(&action.sa_mask, posix_signals[i]); +#if defined(__clang__) +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdisabled-macro-expansion" +#endif + action.sa_sigaction = &sig_handler; +#if defined(__clang__) +#pragma clang diagnostic pop +#endif + + int r = sigaction(posix_signals[i], &action, nullptr); + if (r < 0) + success = false; + } + + _loaded = success; + } + + bool loaded() const { return _loaded; } + + static void handleSignal(int, siginfo_t *info, void *_ctx) { + ucontext_t *uctx = static_cast(_ctx); + + StackTrace st; + void *error_addr = nullptr; +#ifdef REG_RIP // x86_64 + error_addr = reinterpret_cast(uctx->uc_mcontext.gregs[REG_RIP]); +#elif defined(REG_EIP) // x86_32 + error_addr = reinterpret_cast(uctx->uc_mcontext.gregs[REG_EIP]); +#elif defined(__arm__) + error_addr = reinterpret_cast(uctx->uc_mcontext.arm_pc); +#elif defined(__aarch64__) + #if defined(__APPLE__) + error_addr = reinterpret_cast(uctx->uc_mcontext->__ss.__pc); + #else + error_addr = reinterpret_cast(uctx->uc_mcontext.pc); + #endif +#elif defined(__mips__) + error_addr = reinterpret_cast( + reinterpret_cast(&uctx->uc_mcontext)->sc_pc); +#elif defined(__ppc__) || defined(__powerpc) || defined(__powerpc__) || \ + defined(__POWERPC__) + error_addr = reinterpret_cast(uctx->uc_mcontext.regs->nip); +#elif defined(__riscv) + error_addr = reinterpret_cast(uctx->uc_mcontext.__gregs[REG_PC]); +#elif defined(__s390x__) + error_addr = reinterpret_cast(uctx->uc_mcontext.psw.addr); +#elif defined(__APPLE__) && defined(__x86_64__) + error_addr = reinterpret_cast(uctx->uc_mcontext->__ss.__rip); +#elif defined(__APPLE__) + error_addr = reinterpret_cast(uctx->uc_mcontext->__ss.__eip); +#else +#warning ":/ sorry, ain't know no nothing none not of your architecture!" +#endif + if (error_addr) { + st.load_from(error_addr, 32, reinterpret_cast(uctx), + info->si_addr); + } else { + st.load_here(32, reinterpret_cast(uctx), info->si_addr); + } + + Printer printer; + printer.address = true; + printer.print(st, stderr); + +#if (defined(_XOPEN_SOURCE) && _XOPEN_SOURCE >= 700) || \ + (defined(_POSIX_C_SOURCE) && _POSIX_C_SOURCE >= 200809L) + psiginfo(info, nullptr); +#else + (void)info; +#endif + } + +private: + details::handle _stack_content; + bool _loaded; + +#ifdef __GNUC__ + __attribute__((noreturn)) +#endif + static void + sig_handler(int signo, siginfo_t *info, void *_ctx) { + handleSignal(signo, info, _ctx); + + // try to forward the signal. + raise(info->si_signo); + + // terminate the process immediately. + puts("watf? exit"); + _exit(EXIT_FAILURE); + } +}; + +#endif // BACKWARD_SYSTEM_LINUX || BACKWARD_SYSTEM_DARWIN + +#ifdef BACKWARD_SYSTEM_WINDOWS + +class SignalHandling { +public: + SignalHandling(const std::vector & = std::vector()) + : reporter_thread_([]() { + /* We handle crashes in a utility thread: + backward structures and some Windows functions called here + need stack space, which we do not have when we encounter a + stack overflow. + To support reporting stack traces during a stack overflow, + we create a utility thread at startup, which waits until a + crash happens or the program exits normally. */ + + { + std::unique_lock lk(mtx()); + cv().wait(lk, [] { return crashed() != crash_status::running; }); + } + if (crashed() == crash_status::crashed) { + handle_stacktrace(skip_recs()); + } + { + std::unique_lock lk(mtx()); + crashed() = crash_status::ending; + } + cv().notify_one(); + }) { + SetUnhandledExceptionFilter(crash_handler); + + signal(SIGABRT, signal_handler); + _set_abort_behavior(0, _WRITE_ABORT_MSG | _CALL_REPORTFAULT); + + std::set_terminate(&terminator); +#ifndef BACKWARD_ATLEAST_CXX17 + std::set_unexpected(&terminator); +#endif + _set_purecall_handler(&terminator); + _set_invalid_parameter_handler(&invalid_parameter_handler); + } + bool loaded() const { return true; } + + ~SignalHandling() { + { + std::unique_lock lk(mtx()); + crashed() = crash_status::normal_exit; + } + + cv().notify_one(); + + reporter_thread_.join(); + } + +private: + static CONTEXT *ctx() { + static CONTEXT data; + return &data; + } + + enum class crash_status { running, crashed, normal_exit, ending }; + + static crash_status &crashed() { + static crash_status data; + return data; + } + + static std::mutex &mtx() { + static std::mutex data; + return data; + } + + static std::condition_variable &cv() { + static std::condition_variable data; + return data; + } + + static HANDLE &thread_handle() { + static HANDLE handle; + return handle; + } + + std::thread reporter_thread_; + + // TODO: how not to hardcode these? + static const constexpr int signal_skip_recs = +#ifdef __clang__ + // With clang, RtlCaptureContext also captures the stack frame of the + // current function Below that, there ar 3 internal Windows functions + 4 +#else + // With MSVC cl, RtlCaptureContext misses the stack frame of the current + // function The first entries during StackWalk are the 3 internal Windows + // functions + 3 +#endif + ; + + static int &skip_recs() { + static int data; + return data; + } + + static inline void terminator() { + crash_handler(signal_skip_recs); + abort(); + } + + static inline void signal_handler(int) { + crash_handler(signal_skip_recs); + abort(); + } + + static inline void __cdecl invalid_parameter_handler(const wchar_t *, + const wchar_t *, + const wchar_t *, + unsigned int, + uintptr_t) { + crash_handler(signal_skip_recs); + abort(); + } + + NOINLINE static LONG WINAPI crash_handler(EXCEPTION_POINTERS *info) { + // The exception info supplies a trace from exactly where the issue was, + // no need to skip records + crash_handler(0, info->ContextRecord); + return EXCEPTION_CONTINUE_SEARCH; + } + + NOINLINE static void crash_handler(int skip, CONTEXT *ct = nullptr) { + + if (ct == nullptr) { + RtlCaptureContext(ctx()); + } else { + memcpy(ctx(), ct, sizeof(CONTEXT)); + } + DuplicateHandle(GetCurrentProcess(), GetCurrentThread(), + GetCurrentProcess(), &thread_handle(), 0, FALSE, + DUPLICATE_SAME_ACCESS); + + skip_recs() = skip; + + { + std::unique_lock lk(mtx()); + crashed() = crash_status::crashed; + } + + cv().notify_one(); + + { + std::unique_lock lk(mtx()); + cv().wait(lk, [] { return crashed() != crash_status::crashed; }); + } + } + + static void handle_stacktrace(int skip_frames = 0) { + // printer creates the TraceResolver, which can supply us a machine type + // for stack walking. Without this, StackTrace can only guess using some + // macros. + // StackTrace also requires that the PDBs are already loaded, which is done + // in the constructor of TraceResolver + Printer printer; + + StackTrace st; + st.set_machine_type(printer.resolver().machine_type()); + st.set_thread_handle(thread_handle()); + st.load_here(32 + skip_frames, ctx()); + st.skip_n_firsts(skip_frames); + + printer.address = true; + printer.print(st, std::cerr); + } +}; + +#endif // BACKWARD_SYSTEM_WINDOWS + +#ifdef BACKWARD_SYSTEM_UNKNOWN + +class SignalHandling { +public: + SignalHandling(const std::vector & = std::vector()) {} + bool init() { return false; } + bool loaded() { return false; } +}; + +#endif // BACKWARD_SYSTEM_UNKNOWN + +} // namespace backward + +#endif /* H_GUARD */ diff --git a/xbmc/addons/kodi-dev-kit/src/addon_runner/third_party/backward-cpp/builds.sh b/xbmc/addons/kodi-dev-kit/src/addon_runner/third_party/backward-cpp/builds.sh new file mode 100755 index 0000000000000..6e1fb2074ed10 --- /dev/null +++ b/xbmc/addons/kodi-dev-kit/src/addon_runner/third_party/backward-cpp/builds.sh @@ -0,0 +1,77 @@ +#!/bin/bash + +COMPILERS_CXX98=`cat</dev/null + ( + cd "$builddir" + cmake -DCMAKE_BUILD_TYPE=$buildtype -DBACKWARD_TESTS=ON .. + ) +} + +function build() { + local builddir=$1 + shift + make -C "$builddir" $@ +} + +function dotest() { + local builddir=$1 + shift + make -C "$builddir" test $@ + return 0 +} + +function do_action() { + local lang=$1 + local action=$2 + shift 2 + + for compiler in $COMPILERS; do + local builddir="build_${lang}_${compiler}" + + if [[ $action == "cmake" ]]; then + buildtype=$1 + mkbuild $compiler $lang "$buildtype" "$builddir" + [[ $? != 0 ]] && exit + elif [[ $action == "make" ]]; then + build "$builddir" $@ + [[ $? != 0 ]] && exit + elif [[ $action == "test" ]]; then + dotest "$builddir" $@ + [[ $? != 0 ]] && exit + elif [[ $action == "clean" ]]; then + rm -r "$builddir" + else + echo "usage: $0 cmake [debug|release|relwithdbg]|make|test|clean" + exit 255 + fi + done +} + +COMPILERS=$COMPILERS_CXX98 +do_action c++98 $@ +COMPILERS=$COMPILERS_CXX11 +do_action c++11 $@ diff --git a/xbmc/addons/kodi-dev-kit/src/addon_runner/third_party/backward-cpp/conanfile.py b/xbmc/addons/kodi-dev-kit/src/addon_runner/third_party/backward-cpp/conanfile.py new file mode 100644 index 0000000000000..d174b257b95db --- /dev/null +++ b/xbmc/addons/kodi-dev-kit/src/addon_runner/third_party/backward-cpp/conanfile.py @@ -0,0 +1,45 @@ +from conans import ConanFile, CMake +import os + +class BackwardCpp(ConanFile): + settings = 'os', 'compiler', 'build_type', 'arch' + name = 'backward' + url = 'https://github.com/bombela/backward-cpp' + license = 'MIT' + version = '1.3.0' + options = { + 'stack_walking_unwind': [True, False], + 'stack_walking_backtrace': [True, False], + 'stack_details_auto_detect': [True, False], + 'stack_details_backtrace_symbol': [True, False], + 'stack_details_dw': [True, False], + 'stack_details_bfd': [True, False], + 'shared': [True, False] + } + default_options = ( + 'stack_walking_unwind=True', + 'stack_walking_backtrace=False', + 'stack_details_auto_detect=True', + 'stack_details_backtrace_symbol=False', + 'stack_details_dw=False', + 'stack_details_bfd=False', + 'shared=False' + ) + exports = 'backward.cpp', 'backward.hpp', 'test/*', 'CMakeLists.txt', 'BackwardConfig.cmake' + generators = 'cmake' + + def build(self): + cmake = CMake(self) + + cmake.configure(defs={'BACKWARD_' + name.upper(): value for name, value in self.options.values.as_list()}) + cmake.build() + + def package(self): + self.copy('backward.hpp', os.path.join('include', 'backward')) + self.copy('*.a', dst='lib') + self.copy('*.so', dst='lib') + self.copy('*.lib', dst='lib') + self.copy('*.dll', dst='lib') + + def package_info(self): + self.cpp_info.libs = ['backward'] diff --git a/xbmc/addons/kodi-dev-kit/src/addon_runner/third_party/backward-cpp/doc/CMakeLists.txt b/xbmc/addons/kodi-dev-kit/src/addon_runner/third_party/backward-cpp/doc/CMakeLists.txt new file mode 100644 index 0000000000000..81c5904eedbe6 --- /dev/null +++ b/xbmc/addons/kodi-dev-kit/src/addon_runner/third_party/backward-cpp/doc/CMakeLists.txt @@ -0,0 +1,12 @@ +# Auto generated CMakeLists.txt. +# See xbmc/addons/kodi-dev-kit/tools/code-generator.py. + +set(SOURCES +) + +set(HEADERS +) + +if(SOURCES OR HEADERS) + devkit_add_object(devkit_third_party_backward-cpp_doc) +endif() diff --git a/xbmc/addons/kodi-dev-kit/src/addon_runner/third_party/backward-cpp/doc/nice.png b/xbmc/addons/kodi-dev-kit/src/addon_runner/third_party/backward-cpp/doc/nice.png new file mode 100644 index 0000000000000000000000000000000000000000..42147e5947b7fcfa852a571f47d9c36d9e31fcd5 GIT binary patch literal 40904 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 rm81GE!8#VGu$Pho_;sb6S@6FAF^e|a literal 0 HcmV?d00001 diff --git a/xbmc/addons/kodi-dev-kit/src/addon_runner/third_party/backward-cpp/doc/pretty.png b/xbmc/addons/kodi-dev-kit/src/addon_runner/third_party/backward-cpp/doc/pretty.png new file mode 100644 index 0000000000000000000000000000000000000000..f95d66ee0be9e821dcc7f97f05443ff5e889ee59 GIT binary patch literal 105498 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
;qB|3^b*vb}h`?DC| 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
I+M7TnYg*;R1m)mRSLg-afM&- zR!jsCSNb*PUXGej(G=_KUKy(}@jtYfv|6|j!WgW6k=)gJ_(UAI{-v{LZRS9yF~JUg z<>VH=UaRe}@~$|)+gSZ?T6%9Fj0mLQxW(z7Hz70MZ$A^p z?7upb`8>IXye+ddJ0_l95U`naWn^6c+fZvI>0}BDvPX4cimN;DnybsG^B#>!mRCEH z2$ZzX*-tw|Vy`O!cm9C^fGwE*PBNWG=xVuAg^ZAh^YdrsbAiY~u_w)fwpA}dg$vTy zpB`fmWFFmbl>+}*BX!u$|7l@1ly-4A2!vjlv7~5%HkpNODks8+as>v`wp*<_Sp3rY z)``A()&Ck~8cs@Lzf6`m%0t&28779d=c%G)TZ1EOFdqtfq6dafz}XVG1Z`AzU{FQQeBmWLNNGr@bRZx2FH`uWgmRx3?+WUzbmmOy+7{mG0&!>XQY( zSVG(#V?@i016XYv%3g9MH}npoE`N0i*^3dpU-@EYeB(IFfM~**Rd%B+V7jY5v(=+K z`Iug~Uhx%nUDR{?>AhxMsn(ottL!8+0rRy>deW;=w=WIflPq?Q*J9?r4Y4?3n*g8M z2CD6#lBI~6=8AEl{PjATQ30e=tLdY?*>UWz(;_tFj7N4!09mow$cGt!cL|$4qb71n zT=NqE3aZomi(4QRi(2Nzk&!PoQ9)8*4jh*5@KG(p9C|8uTLPH05pSL;Goyv5bxHwv zP`F0~Lo~}H7E`wCs^Xc?WhH9;$T%lNUX3rSrloo)4ZI8!5Zad+_ANdZCoSC>{y=L| zn$=+Fb3u}<(CRG?@_B?^DMr9|i(MbU2ts4hiAp!CcDKUk&Z*=OVg3t|1zRSL$(G{rza>5Xh@jw1gWK5hPieJjs_@{UIaOWfp;JS?y~5>>ouEk{>eu(7zsmN`Mo)_5lv1frVBcmvuCmGi zZ7XUQw|5~kBi~Q~1ghpzngg)`d1R!>R@xgz=o5~r7eNRH8m(0Ms=4H5Ock>S;hTwG z7}c_4!_M8ke;tqia~5u zojiyu-7VS$<|um%+5&;8_%Dc`Ee~Nxe0GW)Z@X5~ZLsEY(N?Y_329bMhAmbLZ>O%< z4|wgZ6~7vu%v&7&&&H>tQP&n;X_P#~u!TDJwm$amPnI(%kFvSqN$aP*x#q)bcTen) zh)O10hF#BQM2ju-fI*c1W!`^9ecD9?O_ghlm)pvFaLO+?3o40lLG8ZJn1z^K;F;?R ziyB!`U(+jTOKs`F^<$C8yPnANbG-BMLMWfH_LbjlQUKYFd$G3}M}rvP){#;@M)jSd^_kX z0?yt!iBMp~-Br$|xstfG!C_n$SuOYhLjJ+bQMykLLq#a6P+R7s%ndw)oA30WAn zdaCttItMZqnot8rIK6N$+hg8JD2;5dtq(v!93K1_4x z3Upk|DXBLaLOl!4VGwEFfwF%vT+%gXXfSI0-wKR1v&7XvTEPQMI*jvfN>1E|aZ-%Z zKfGmk7oa<)F%hm_V*-x6Db?m`R(jv@PVWU0Tk8H^=ijpR&>L_WO!&I)KHG~+Oymy; v(a?zH)V#iTUDE_pl>evVh+X#kdWOrkiKUwOU6pygijSwMs;5$~WE1^A1DQmn literal 0 HcmV?d00001 diff --git a/xbmc/addons/kodi-dev-kit/src/addon_runner/third_party/backward-cpp/test/CMakeLists.txt b/xbmc/addons/kodi-dev-kit/src/addon_runner/third_party/backward-cpp/test/CMakeLists.txt new file mode 100644 index 0000000000000..c5dad6896dde4 --- /dev/null +++ b/xbmc/addons/kodi-dev-kit/src/addon_runner/third_party/backward-cpp/test/CMakeLists.txt @@ -0,0 +1,19 @@ +# Auto generated CMakeLists.txt. +# See xbmc/addons/kodi-dev-kit/tools/code-generator.py. + +set(SOURCES + _test_main.cpp + rectrace.cpp + select_signals.cpp + stacktrace.cpp + suicide.cpp + test.cpp +) + +set(HEADERS + test.hpp +) + +if(SOURCES OR HEADERS) + devkit_add_object(devkit_third_party_backward-cpp_test) +endif() diff --git a/xbmc/addons/kodi-dev-kit/src/addon_runner/third_party/backward-cpp/test/_test_main.cpp b/xbmc/addons/kodi-dev-kit/src/addon_runner/third_party/backward-cpp/test/_test_main.cpp new file mode 100644 index 0000000000000..68211e030fd29 --- /dev/null +++ b/xbmc/addons/kodi-dev-kit/src/addon_runner/third_party/backward-cpp/test/_test_main.cpp @@ -0,0 +1,241 @@ +/* + * _test_main.cpp + * Copyright 2013 Google Inc. All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "test.hpp" +#include +#include + +#ifdef _WIN32 +#include +#define strcasecmp _stricmp +#else +#include +#include +#endif + +#if defined(__has_include) && __has_include() +#include +#else +#include + +#ifdef _WIN32 +char argv0[MAX_PATH]; +inline const char *getprogname() { + return GetModuleFileName(NULL, argv0, sizeof(argv0)) ? argv0 : NULL; +} +#elif !defined(__APPLE__) +// N.B. getprogname() is an Apple/BSD-ism. +// program_invocation_name is a GLIBC-ism, but it's also +// supported by libmusl. +#define getprogname() program_invocation_name +#endif + +void error(int status, int errnum, const char *format, ...) { + fflush(stdout); + fprintf(stderr, "%s: ", getprogname()); + + va_list args; + va_start(args, format); + vfprintf(stderr, format, args); + va_end(args); + + if (errnum != 0) { + fprintf(stderr, ": %s\n", strerror(errnum)); + } else { + fprintf(stderr, "\n"); + } + if (status != 0) { + exit(status); + } +} +#endif + +using namespace test; + +bool run_test(TestBase &test, bool use_child_process = true) { + if (!use_child_process) { + exit(static_cast(test.run())); + } + + printf("-- running test case: %s\n", test.name); + + fflush(stdout); + + test::TestStatus status = test::SUCCESS; + +#ifdef _WIN32 + char filename[256]; + GetModuleFileName(NULL, filename, 256); // TODO: check for error + std::string cmd_line = filename; + cmd_line += " --nofork "; + cmd_line += test.name; + + STARTUPINFO si; + PROCESS_INFORMATION pi; + ZeroMemory(&si, sizeof(si)); + si.cb = sizeof(si); + ZeroMemory(&pi, sizeof(pi)); + + if (!CreateProcessA(nullptr, const_cast(cmd_line.c_str()), nullptr, + nullptr, FALSE, 0, nullptr, nullptr, &si, &pi)) { + printf("unable to create process\n"); + exit(-1); + } + + WaitForSingleObject(pi.hProcess, INFINITE); + + DWORD exit_code; + GetExitCodeProcess(pi.hProcess, &exit_code); + switch (exit_code) { + case 3: + status = test::SIGNAL_ABORT; + break; + case 5: + status = test::EXCEPTION_UNCAUGHT; + break; + case EXCEPTION_ACCESS_VIOLATION: + status = test::SIGNAL_SEGFAULT; + break; + case EXCEPTION_STACK_OVERFLOW: + status = test::SIGNAL_SEGFAULT; + break; + case EXCEPTION_INT_DIVIDE_BY_ZERO: + status = test::SIGNAL_DIVZERO; + break; + } + printf("Exit code: %lu\n", exit_code); + + CloseHandle(pi.hProcess); + CloseHandle(pi.hThread); + + if (test.expected_status == test::ASSERT_FAIL) { + // assert calls abort on windows + return (status & test::SIGNAL_ABORT); + } + +#else + + pid_t child_pid = fork(); + if (child_pid == 0) { + exit(static_cast(test.run())); + } + if (child_pid == -1) { + error(EXIT_FAILURE, 0, "unable to fork"); + } + + int child_status = 0; + waitpid(child_pid, &child_status, 0); + + if (WIFEXITED(child_status)) { + int exit_status = WEXITSTATUS(child_status); + if (exit_status & ~test::STATUS_MASK) { + status = test::FAILED; + } else { + status = static_cast(exit_status); + } + } else if (WIFSIGNALED(child_status)) { + const int signum = WTERMSIG(child_status); + printf("!! signal (%d) %s\n", signum, strsignal(signum)); + switch (signum) { + case SIGABRT: + status = test::SIGNAL_ABORT; + break; + case SIGSEGV: + case SIGBUS: + status = test::SIGNAL_SEGFAULT; + break; + case SIGFPE: + status = test::SIGNAL_DIVZERO; + break; + default: + status = test::SIGNAL_UNCAUGHT; + } + } + +#endif + + if (test.expected_status == test::FAILED) { + return (status & test::FAILED); + } + + if (test.expected_status == test::SIGNAL_UNCAUGHT) { + return (status & test::SIGNAL_UNCAUGHT); + } + + return status == test.expected_status; +} + +int main(int argc, const char *const argv[]) { + +#ifdef _WIN32 + _set_abort_behavior(0, _WRITE_ABORT_MSG | _CALL_REPORTFAULT); +#endif + + if (argc == 3 && strcmp("--nofork", argv[1]) == 0) { + // Windows has no fork, so we simulate it + // we only execute one test, without forking + for (test_registry_t::iterator it = test_registry().begin(); + it != test_registry().end(); ++it) { + TestBase &test = **it; + if (strcasecmp(argv[2], test.name) == 0) { + run_test(test, false); + + return 0; + } + } + return -1; + } + + size_t success_cnt = 0; + size_t total_cnt = 0; + for (test_registry_t::iterator it = test_registry().begin(); + it != test_registry().end(); ++it) { + TestBase &test = **it; + + bool consider_test = (argc <= 1); + for (int i = 1; i < argc; ++i) { + if (strcasecmp(argv[i], test.name) == 0) { + consider_test = true; + break; + } + } + if (!consider_test) { + continue; + } + + total_cnt += 1; + if (run_test(test)) { + printf("-- test case success: %s\n", test.name); + success_cnt += 1; + } else { + printf("** test case FAILED : %s\n", test.name); + } + } + printf("-- tests passing: %zu/%zu", success_cnt, total_cnt); + if (total_cnt) { + printf(" (%zu%%)\n", success_cnt * 100 / total_cnt); + } else { + printf("\n"); + } + return (success_cnt == total_cnt) ? EXIT_SUCCESS : EXIT_FAILURE; +} diff --git a/xbmc/addons/kodi-dev-kit/src/addon_runner/third_party/backward-cpp/test/rectrace.cpp b/xbmc/addons/kodi-dev-kit/src/addon_runner/third_party/backward-cpp/test/rectrace.cpp new file mode 100644 index 0000000000000..10a38f475a029 --- /dev/null +++ b/xbmc/addons/kodi-dev-kit/src/addon_runner/third_party/backward-cpp/test/rectrace.cpp @@ -0,0 +1,98 @@ +/* + * test/rectrace.cpp + * Copyright 2013 Google Inc. All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "backward.hpp" +#include "test/test.hpp" +#include + +using namespace backward; + +typedef StackTrace stacktrace_t; + +void end_of_our_journey(stacktrace_t &st) { + if (!st.size()) { + st.load_here(); + } +} + +int rec(stacktrace_t &st, int level) { + if (level <= 1) { + end_of_our_journey(st); + return 0; + } + return rec(st, level - 1); +} + +namespace toto { + +namespace titi { + +struct foo { + + union bar { + NOINLINE static int trampoline(stacktrace_t &st, int level) { + return rec(st, level); + } + }; +}; + +} // namespace titi + +} // namespace toto + +TEST(recursion) { + { // lexical scope. + stacktrace_t st; + const int input = 3; + int r = toto::titi::foo::bar::trampoline(st, input); + + std::cout << "rec(" << input << ") == " << r << std::endl; + + Printer printer; + // printer.address = true; + printer.object = true; + printer.print(st, stdout); + } +} + +int fib(StackTrace &st, int level) { + if (level == 2) { + return 1; + } + if (level <= 1) { + end_of_our_journey(st); + return 0; + } + return fib(st, level - 1) + fib(st, level - 2); +} + +TEST(fibrecursive) { + StackTrace st; + const int input = 6; + int r = fib(st, input); + + std::cout << "fib(" << input << ") == " << r << std::endl; + + Printer printer; + printer.print(st, stdout); +} diff --git a/xbmc/addons/kodi-dev-kit/src/addon_runner/third_party/backward-cpp/test/select_signals.cpp b/xbmc/addons/kodi-dev-kit/src/addon_runner/third_party/backward-cpp/test/select_signals.cpp new file mode 100644 index 0000000000000..57e6ebe0877c1 --- /dev/null +++ b/xbmc/addons/kodi-dev-kit/src/addon_runner/third_party/backward-cpp/test/select_signals.cpp @@ -0,0 +1,51 @@ +/* + * test/segfault.cpp + * Copyright 2013 Google Inc. All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "backward.hpp" + +#include "test/test.hpp" +#include +#include + +using namespace backward; + +void badass_function() { + char *ptr = (char *)42; + *ptr = 42; +} + +TEST_SEGFAULT(pprint_sigsev) { + std::vector signals; + signals.push_back(SIGSEGV); + SignalHandling sh(signals); + std::cout << std::boolalpha << "sh.loaded() == " << sh.loaded() << std::endl; + badass_function(); +} + +TEST_SEGFAULT(wont_pprint) { + std::vector signals; + signals.push_back(SIGABRT); + SignalHandling sh(signals); + std::cout << std::boolalpha << "sh.loaded() == " << sh.loaded() << std::endl; + badass_function(); +} diff --git a/xbmc/addons/kodi-dev-kit/src/addon_runner/third_party/backward-cpp/test/stacktrace.cpp b/xbmc/addons/kodi-dev-kit/src/addon_runner/third_party/backward-cpp/test/stacktrace.cpp new file mode 100644 index 0000000000000..47084fe7da01f --- /dev/null +++ b/xbmc/addons/kodi-dev-kit/src/addon_runner/third_party/backward-cpp/test/stacktrace.cpp @@ -0,0 +1,57 @@ +/* + * test/stacktrace.cpp + * Copyright 2013 Google Inc. All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "backward.hpp" +#include "test/test.hpp" +#include +#include + +using namespace backward; + +void collect_trace(StackTrace &st) { st.load_here(); } + +TEST(minitrace) { + Printer printer; + + StackTrace st; + collect_trace(st); + + printer.print(st, std::cout); +} + +void d(StackTrace &st) { st.load_here(); } + +void c(StackTrace &st) { return d(st); } + +void b(StackTrace &st) { return c(st); } + +NOINLINE void a(StackTrace &st) { return b(st); } + +TEST(smalltrace) { + Printer printer; + + StackTrace st; + a(st); + + printer.print(st, std::cout); +} diff --git a/xbmc/addons/kodi-dev-kit/src/addon_runner/third_party/backward-cpp/test/suicide.cpp b/xbmc/addons/kodi-dev-kit/src/addon_runner/third_party/backward-cpp/test/suicide.cpp new file mode 100644 index 0000000000000..e694c6fb46166 --- /dev/null +++ b/xbmc/addons/kodi-dev-kit/src/addon_runner/third_party/backward-cpp/test/suicide.cpp @@ -0,0 +1,89 @@ +/* + * test/suicide.cpp + * Copyright 2013 Google Inc. All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "backward.hpp" + +#include "test/test.hpp" +#include + +#ifndef _WIN32 +#include +#endif + +using namespace backward; + +void badass_function() { + char *ptr = (char *)42; + *ptr = 42; +} + +TEST_SEGFAULT(invalid_write) { badass_function(); } + +int you_shall_not_pass() { + char *ptr = (char *)42; + int v = *ptr; + return v; +} + +TEST_SEGFAULT(invalid_read) { + int v = you_shall_not_pass(); + std::cout << "v=" << v << std::endl; +} + +void abort_abort_I_repeat_abort_abort() { + std::cout << "Jumping off the boat!" << std::endl; + abort(); +} + +TEST_ABORT(calling_abort) { abort_abort_I_repeat_abort_abort(); } + +// aarch64 and mips does not trap Division by zero +#if !defined(__aarch64__) && !defined(__mips__) +volatile int zero = 0; + +int divide_by_zero() { + std::cout << "And the wild black hole appears..." << std::endl; + int v = 42 / zero; + return v; +} + +TEST_DIVZERO(divide_by_zero) { + int v = divide_by_zero(); + std::cout << "v=" << v << std::endl; +} +#endif + +// Darwin does not allow RLIMIT_STACK to be reduced +#ifndef __APPLE__ +int bye_bye_stack(int i) { return bye_bye_stack(i + 1) + bye_bye_stack(i * 2); } + +TEST_SEGFAULT(stackoverflow) { +#ifndef _WIN32 + struct rlimit limit; + limit.rlim_max = 8096; + setrlimit(RLIMIT_STACK, &limit); +#endif + int r = bye_bye_stack(42); + std::cout << "r=" << r << std::endl; +} +#endif diff --git a/xbmc/addons/kodi-dev-kit/src/addon_runner/third_party/backward-cpp/test/test.cpp b/xbmc/addons/kodi-dev-kit/src/addon_runner/third_party/backward-cpp/test/test.cpp new file mode 100644 index 0000000000000..1a148f12cc2e2 --- /dev/null +++ b/xbmc/addons/kodi-dev-kit/src/addon_runner/third_party/backward-cpp/test/test.cpp @@ -0,0 +1,63 @@ +/* + * test/test.cpp + * Copyright 2013 Google Inc. All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "test/test.hpp" +#include +#include +#include + +TEST(empty_test) {} + +TEST_FAIL_ASSERT(fail_assert) { ASSERT(1 == 2); } + +TEST_FAIL_ASSERT(fail_assert_ge) { ASSERT_GE(4, 5); } + +TEST_UNCAUGHT_EXCEPTION(uncaught_exception) { + throw std::runtime_error("some random runtime error"); +} + +TEST_UNCAUGHT_EXCEPTION(uncaught_exception_int) { throw 42; } + +TEST_SEGFAULT(segfault) { + char *a = 0; + char b = a[42]; + std::cout << "result: " << b << std::endl; +} + +TEST_ABORT(abort) { abort(); } + +TEST(catch_int) { + ASSERT_THROW({ throw 42; }, int); +} + +TEST_FAIL_ASSERT(fail_catch_int) { ASSERT_THROW({}, int); } + +TEST_FAIL_ASSERT(fail_no_throw) { + ASSERT_NO_THROW({ throw 42; }); +} + +TEST(any_throw) { + ASSERT_ANY_THROW({ throw 42; }); +} + +TEST_FAIL_ASSERT(fail_any_throw) { ASSERT_ANY_THROW({}); } diff --git a/xbmc/addons/kodi-dev-kit/src/addon_runner/third_party/backward-cpp/test/test.hpp b/xbmc/addons/kodi-dev-kit/src/addon_runner/third_party/backward-cpp/test/test.hpp new file mode 100644 index 0000000000000..dc77c627a16da --- /dev/null +++ b/xbmc/addons/kodi-dev-kit/src/addon_runner/third_party/backward-cpp/test/test.hpp @@ -0,0 +1,183 @@ +/* + * test/test.hpp + * Copyright 2013 Google Inc. All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#pragma once +#ifndef H_54E531F7_9154_454B_BEB9_257408429470 +#define H_54E531F7_9154_454B_BEB9_257408429470 + +#include +#include +#include +#include +#include +#include + +namespace test { + +struct AssertFailedError : std::exception { + ~AssertFailedError() throw() {} + + AssertFailedError(const char *filename, int _line, const char *_errmsg) + : basename(_basename(filename)), line(_line), errmsg(_errmsg) {} + + const char *what() const throw() { + if (!_what.size()) { + std::ostringstream ss; + ss << "assertion failed (" << basename << ":" << line; + ss << ") " << errmsg; + _what = ss.str(); + } + return _what.c_str(); + } + + const char *basename; + int line; + const char *errmsg; + + mutable std::string _what; + + static const char *_basename(const char *filename) { + const char *basename = filename + strlen(filename); + while (basename != filename && *basename != '/') { + basename -= 1; + } + return basename + 1; + } +}; + +enum TestStatus { + SUCCESS = 0 << 0, + FAILED = 1 << 0, + + ASSERT_FAIL = FAILED | 1 << 1, + EXCEPTION_UNCAUGHT = FAILED | 2 << 1, + SIGNAL_UNCAUGHT = FAILED | 3 << 1, + SIGNAL_SEGFAULT = SIGNAL_UNCAUGHT | 1 << 3, + SIGNAL_ABORT = SIGNAL_UNCAUGHT | 2 << 3, + SIGNAL_DIVZERO = SIGNAL_UNCAUGHT | 2 << 3, + + STATUS_MASK = 0x1F +}; + +struct TestBase { + const char *name; + TestStatus expected_status; + + virtual ~TestBase() {} + TestBase(const char *, TestStatus); + virtual void do_test() = 0; + + TestStatus run() { + try { + do_test(); + return SUCCESS; + } catch (const AssertFailedError &e) { + printf("!! %s\n", e.what()); + return ASSERT_FAIL; + } catch (const std::exception &e) { + printf("!! exception: %s\n", e.what()); + return EXCEPTION_UNCAUGHT; + } catch (...) { + printf("!! unknown exception\n"); + return EXCEPTION_UNCAUGHT; + } + } +}; + +typedef std::vector test_registry_t; +inline test_registry_t &test_registry() { + static test_registry_t reg; + return reg; +} + +inline TestBase::TestBase(const char *n, TestStatus s) + : name(n), expected_status(s) { + test_registry().push_back(this); +} + +} // namespace test + +#define _TEST_STATUS(name, status) \ + struct TEST_##name : ::test::TestBase { \ + TEST_##name() : TestBase(#name, status) {} \ + void do_test(); \ + } TEST_##name; \ + void TEST_##name::do_test() + +#define TEST(name) _TEST_STATUS(name, ::test::SUCCESS) +#define TEST_FAIL(name) _TEST_STATUS(name, ::test::FAILED) +#define TEST_FAIL_ASSERT(name) _TEST_STATUS(name, ::test::ASSERT_FAIL) +#define TEST_UNCAUGHT_EXCEPTION(name) \ + _TEST_STATUS(name, ::test::EXCEPTION_UNCAUGHT) +#define TEST_UNCAUGHT_SIGNAL(name) _TEST_STATUS(name, ::test::SIGNAL_UNCAUGHT) +#define TEST_SEGFAULT(name) _TEST_STATUS(name, ::test::SIGNAL_SEGFAULT) +#define TEST_ABORT(name) _TEST_STATUS(name, ::test::SIGNAL_ABORT) +#define TEST_DIVZERO(name) _TEST_STATUS(name, ::test::SIGNAL_DIVZERO) + +#define ASSERT(expr) \ + (expr) ? static_cast(0) \ + : throw ::test::AssertFailedError(__FILE__, __LINE__, #expr) + +#define _ASSERT_BINOP(a, b, cmp) \ + (!(a cmp b)) ? static_cast(0) \ + : throw ::test::AssertFailedError( \ + __FILE__, __LINE__, "because " #a " " #cmp " " #b) + +#define ASSERT_EQ(a, b) _ASSERT_BINOP(a, b, !=) +#define ASSERT_NE(a, b) _ASSERT_BINOP(a, b, ==) +#define ASSERT_LT(a, b) _ASSERT_BINOP(a, b, >=) +#define ASSERT_LE(a, b) _ASSERT_BINOP(a, b, >) +#define ASSERT_GT(a, b) _ASSERT_BINOP(a, b, <=) +#define ASSERT_GE(a, b) _ASSERT_BINOP(a, b, <) + +#define ASSERT_THROW(expr, e_type) \ + do { \ + try { \ + expr \ + } catch (const e_type &) { \ + break; \ + } \ + throw ::test::AssertFailedError(__FILE__, __LINE__, \ + "expected exception " #e_type); \ + } while (0) + +#define ASSERT_ANY_THROW(expr) \ + do { \ + try { \ + expr \ + } catch (...) { \ + break; \ + } \ + throw ::test::AssertFailedError(__FILE__, __LINE__, \ + "expected any exception"); \ + } while (0) + +#define ASSERT_NO_THROW(expr) \ + try { \ + expr \ + } catch (...) { \ + throw ::test::AssertFailedError(__FILE__, __LINE__, \ + "no exception expected"); \ + } + +#endif /* H_GUARD */ diff --git a/xbmc/addons/kodi-dev-kit/src/addon_runner/third_party/backward-cpp/test_package/CMakeLists.txt b/xbmc/addons/kodi-dev-kit/src/addon_runner/third_party/backward-cpp/test_package/CMakeLists.txt new file mode 100644 index 0000000000000..56de8b2232c59 --- /dev/null +++ b/xbmc/addons/kodi-dev-kit/src/addon_runner/third_party/backward-cpp/test_package/CMakeLists.txt @@ -0,0 +1,13 @@ +# Auto generated CMakeLists.txt. +# See xbmc/addons/kodi-dev-kit/tools/code-generator.py. + +set(SOURCES + main.cpp +) + +set(HEADERS +) + +if(SOURCES OR HEADERS) + devkit_add_object(devkit_third_party_backward-cpp_test_package) +endif() diff --git a/xbmc/addons/kodi-dev-kit/src/addon_runner/third_party/backward-cpp/test_package/conanfile.py b/xbmc/addons/kodi-dev-kit/src/addon_runner/third_party/backward-cpp/test_package/conanfile.py new file mode 100644 index 0000000000000..e509dd5a00c49 --- /dev/null +++ b/xbmc/addons/kodi-dev-kit/src/addon_runner/third_party/backward-cpp/test_package/conanfile.py @@ -0,0 +1,14 @@ +from conans import ConanFile, CMake +import os + +class TestBackward(ConanFile): + settings = 'os', 'compiler', 'build_type', 'arch' + generators = 'cmake' + + def build(self): + cmake = CMake(self) + cmake.configure(defs={'CMAKE_VERBOSE_MAKEFILE': 'ON'}) + cmake.build() + + def test(self): + self.run(os.path.join('.', 'bin', 'example')) diff --git a/xbmc/addons/kodi-dev-kit/src/addon_runner/third_party/backward-cpp/test_package/main.cpp b/xbmc/addons/kodi-dev-kit/src/addon_runner/third_party/backward-cpp/test_package/main.cpp new file mode 100644 index 0000000000000..98ca96a43f287 --- /dev/null +++ b/xbmc/addons/kodi-dev-kit/src/addon_runner/third_party/backward-cpp/test_package/main.cpp @@ -0,0 +1,46 @@ +#include +#include +#include +#include + +using namespace backward; + +class TracedException : public std::runtime_error { +public: + TracedException() : std::runtime_error(_get_trace()) {} + +private: + std::string _get_trace() { + std::ostringstream ss; + + StackTrace stackTrace; + TraceResolver resolver; + stackTrace.load_here(); + resolver.load_stacktrace(stackTrace); + + for (std::size_t i = 0; i < stackTrace.size(); ++i) { + const ResolvedTrace trace = resolver.resolve(stackTrace[i]); + + ss << "#" << i << " at " << trace.object_function << "\n"; + } + + return ss.str(); + } +}; + +void f(int i) { + if (i >= 42) { + throw TracedException(); + } else { + std::cout << "i=" << i << "\n"; + f(i + 1); + } +} + +int main() { + try { + f(0); + } catch (const TracedException &ex) { + std::cout << ex.what(); + } +} diff --git a/xbmc/addons/kodi-dev-kit/src/shared/api/addon_base.h b/xbmc/addons/kodi-dev-kit/src/shared/api/addon_base.h index cacaa456e2dde..3f046cb879dc7 100644 --- a/xbmc/addons/kodi-dev-kit/src/shared/api/addon_base.h +++ b/xbmc/addons/kodi-dev-kit/src/shared/api/addon_base.h @@ -174,7 +174,7 @@ typedef KODI_ADDON_INSTANCE_HDL(ATTR_INT_APIENTRYP PFN_INT_KODI_ADDON_INSTANCE_C typedef enum ADDON_STATUS(ATTR_INT_APIENTRYP PFN_INT_KODI_ADDON_CREATE_INSTANCE_V1)( void*, const KODI_ADDON_HDL, const struct KODI_ADDON_INSTANCE_INFO*, KODI_HANDLE*); typedef enum ADDON_STATUS(ATTR_INT_APIENTRYP PFN_INT_KODI_ADDON_CREATE_V1)( - void*, const KODI_ADDON_INSTANCE_BACKEND_HDL, KODI_ADDON_HDL*); + void*, const struct KODI_ADDON_INSTANCE_INFO*, KODI_ADDON_HDL*); typedef enum ADDON_STATUS(ATTR_INT_APIENTRYP PFN_INT_KODI_ADDON_INSTANCE_SETTING_CHANGE_BOOLEAN_V1)( void*, const KODI_ADDON_HDL, const KODI_ADDON_INSTANCE_HDL, const char*, bool); typedef enum ADDON_STATUS(ATTR_INT_APIENTRYP PFN_INT_KODI_ADDON_INSTANCE_SETTING_CHANGE_FLOAT_V1)( @@ -237,7 +237,6 @@ struct IFC_KODI_ADDON_INSTANCE_INFO unique_work_id = c_data->unique_work_id; kodi = PtrValue(c_data->kodi); parent = PtrValue(c_data->parent); - first_instance = c_data->first_instance; } void SetCStructure(KODI_ADDON_INSTANCE_INFO* c_data) @@ -250,7 +249,6 @@ struct IFC_KODI_ADDON_INSTANCE_INFO c_data->unique_work_id = unique_work_id.c_str(); c_data->kodi = reinterpret_cast(kodi); c_data->parent = reinterpret_cast(parent); - c_data->first_instance = first_instance; } enum KODI_ADDON_INSTANCE_TYPE type; @@ -258,9 +256,8 @@ struct IFC_KODI_ADDON_INSTANCE_INFO std::string unique_work_id; PtrValue kodi; PtrValue parent; - bool first_instance; - MSGPACK_DEFINE(type, instance_id, unique_work_id, kodi, parent, first_instance); + MSGPACK_DEFINE(type, instance_id, unique_work_id, kodi, parent); }; /*---AUTO_GEN_PARSE---*/ @@ -419,8 +416,8 @@ typedef enum funcChild_addon_base_h : int // clang-format off /*---AUTO_GEN_PARSE---*/ -// Original API call: typedef enum ADDON_STATUS(ATTR_APIENTRYP PFN_KODI_ADDON_CREATE_V1)(const KODI_ADDON_INSTANCE_BACKEND_HDL first_instance, KODI_ADDON_HDL* hdl); -typedef std::tuple msgChild__IN_kodi_addon_create_v1; /* Autogenerated */ +// Original API call: typedef enum ADDON_STATUS(ATTR_APIENTRYP PFN_KODI_ADDON_CREATE_V1)(const struct KODI_ADDON_INSTANCE_INFO* first_instance, KODI_ADDON_HDL* hdl); +typedef std::tuple msgChild__IN_kodi_addon_create_v1; /* Autogenerated */ typedef std::tuple msgChild_OUT_kodi_addon_create_v1; /* Autogenerated */ // Original API call: typedef void(ATTR_APIENTRYP PFN_KODI_ADDON_DESTROY_V1)(KODI_ADDON_HDL hdl); typedef std::tuple msgChild__IN_kodi_addon_destroy_v1; /* Autogenerated */ diff --git a/xbmc/addons/kodi-dev-kit/tools/code-generator/src/generateCMake__XBMC_ADDONS_KODIDEVKIT_SRC_ADDON_allfiles.py b/xbmc/addons/kodi-dev-kit/tools/code-generator/src/generateCMake__XBMC_ADDONS_KODIDEVKIT_SRC_ADDON_allfiles.py index c014985e905b9..f1eb8d5176392 100644 --- a/xbmc/addons/kodi-dev-kit/tools/code-generator/src/generateCMake__XBMC_ADDONS_KODIDEVKIT_SRC_ADDON_allfiles.py +++ b/xbmc/addons/kodi-dev-kit/tools/code-generator/src/generateCMake__XBMC_ADDONS_KODIDEVKIT_SRC_ADDON_allfiles.py @@ -56,6 +56,16 @@ def GenerateCMake__XBMC_ADDONS_KODIDEVKIT_SRC_ADDON_all_files(options): dirs = sorted(glob.glob(scan_dir, recursive=True)) for dir in dirs: source_dir = dir.replace(KODI_DIR, "") + + if "/third_party/" in source_dir: + if os.path.basename(os.path.dirname(os.path.dirname(dir))) == "third_party": + Log.PrintBegin(" - Check {}CMakeLists.txt ".format(source_dir)) + Log.PrintFollow("(Used by third party modules)") + cmake_dir = source_dir[len("{}/src/addon/".format(DEVKIT_DIR)) : -1] + base_cmake_subdirs += "add_subdirectory(" + cmake_dir + ")\n" + Log.PrintResult(Result.ALREADY_DONE) + continue + Log.PrintBegin(" - Check {}CMakeLists.txt".format(source_dir)) os_limits = [] diff --git a/xbmc/addons/kodi-dev-kit/tools/code-generator/src/interface_code_generator.py b/xbmc/addons/kodi-dev-kit/tools/code-generator/src/interface_code_generator.py index e05a48a66c6c2..69c1d41188532 100644 --- a/xbmc/addons/kodi-dev-kit/tools/code-generator/src/interface_code_generator.py +++ b/xbmc/addons/kodi-dev-kit/tools/code-generator/src/interface_code_generator.py @@ -5174,9 +5174,6 @@ def __init__(self, parent, cnt, value, value_list, ignored_count, file_text): self.done = True return - print("--> " + self.parent.funcname) - print("--> " + self.value_type) - if cnt == 0: if self.value_type.replace( "const ", "" @@ -5505,11 +5502,6 @@ def __init__(self, parent, cnt, value, value_list, ignored_count, file_text): self.done = True return - #print("--> " + self.parent.funcname) - #print("--> " + self.value_type) - #if "/*---AUTO_GEN_PARSE---*/" in file_text and self.value_type != "const KODI_ADDON_INSTANCE_BACKEND_HDL": - #exit() - # WARNING CONFIRMED_NO # CheckAPIUse_WAY_12b if (ContainsHdlVoidPointer(self.value_type.replace("*", "")) or self.value_type == "KODI_ADDON_HDL*") and self.value_type.count("*") == 1: @@ -6834,7 +6826,15 @@ def __init__(self, parent, cnt, value, value_list): values = [] retval = "" code_override = "" - if self.funcname == "kodi_addon_create_instance" or self.funcname == "kodi_addon_destroy_instance": + if self.funcname == "kodi_addon_create": + code = CodeGetField_AUTO_GEN_PARSE(target_file, "HAND_EDITED_FIELD={}_2".format(self.funcname_ifc.upper())) + if code != "": + code_override += ( + "/*---AUTO_GEN_PARSE---*/\n" + "{1}" + "/*---AUTO_GEN_PARSE---*/\n" + ).format(self.funcname_ifc.upper(), code) + elif self.funcname == "kodi_addon_create_instance" or self.funcname == "kodi_addon_destroy_instance": code = Generate_kodi_addon_instance_construct(self.options) code_override += "/*---AUTO_GEN_PARSE---*/\n".format(self.funcname_ifc.upper()) @@ -6864,8 +6864,6 @@ def __init__(self, parent, cnt, value, value_list): print('FATAL: Failed to scan "{}" on GenerateDevKitSourceCPPFunction'.format(file_text)) raise - print(scan_file) - print(file_text) default_ret = GetDefaultReturn(retval, self.function_complete, file_text, scan_file) if not code_override: diff --git a/xbmc/guilib/GUIWindowManager.cpp b/xbmc/guilib/GUIWindowManager.cpp index c3a142e5a1b68..2b3fe80190a01 100644 --- a/xbmc/guilib/GUIWindowManager.cpp +++ b/xbmc/guilib/GUIWindowManager.cpp @@ -23,6 +23,7 @@ #include "favourites/GUIDialogFavourites.h" #include "input/Key.h" #include "messaging/ApplicationMessenger.h" +#include "messaging/helpers/DialogCrashReporter.h" #include "messaging/helpers/DialogHelper.h" #include "music/dialogs/GUIDialogInfoProviderSettings.h" #include "music/dialogs/GUIDialogMusicInfo.h" @@ -1105,6 +1106,22 @@ void CGUIWindowManager::OnApplicationMessage(ThreadMessage* pMsg) pMsg->SetResult(static_cast(dialogOK->IsConfirmed())); } break; + + case TMSG_GUI_DIALOG_ADDON_CRASH_REPORT: + { + + if (!pMsg->lpVoid) + return; + + using namespace KODI::ADDONS::INTERFACE; + + auto dialogCrashReport = static_cast(GetWindow(WINDOW_DIALOG_ADDON_CRASH_REPORTER)); + if (!dialogCrashReport) + return; + + dialogCrashReport->ReportCrash(static_cast(pMsg->lpVoid)); + } + break; } } diff --git a/xbmc/messaging/ApplicationMessenger.h b/xbmc/messaging/ApplicationMessenger.h index 2024541f084de..700dd2b9ff6f4 100644 --- a/xbmc/messaging/ApplicationMessenger.h +++ b/xbmc/messaging/ApplicationMessenger.h @@ -121,6 +121,7 @@ */ #define TMSG_GUI_DIALOG_YESNO TMSG_MASK_WINDOWMANAGER + 8 #define TMSG_GUI_DIALOG_OK TMSG_MASK_WINDOWMANAGER + 9 +#define TMSG_GUI_DIALOG_ADDON_CRASH_REPORT TMSG_MASK_WINDOWMANAGER + 10 /*! \def TMSG_GUI_PREVIOUS_WINDOW @@ -129,7 +130,7 @@ This is an alternative to TMSG_GUI_ACTIVATE_WINDOW, but it keeps all configured parameters, like startup directory. */ -#define TMSG_GUI_PREVIOUS_WINDOW TMSG_MASK_WINDOWMANAGER + 10 +#define TMSG_GUI_PREVIOUS_WINDOW TMSG_MASK_WINDOWMANAGER + 11 #define TMSG_CALLBACK 800 diff --git a/xbmc/messaging/helpers/CMakeLists.txt b/xbmc/messaging/helpers/CMakeLists.txt index 288ba7cc3e00e..dc118dbb44bd3 100644 --- a/xbmc/messaging/helpers/CMakeLists.txt +++ b/xbmc/messaging/helpers/CMakeLists.txt @@ -1,7 +1,9 @@ -set(SOURCES DialogHelper.cpp +set(SOURCES DialogCrashReporter.cpp + DialogHelper.cpp DialogOKHelper.cpp) -set(HEADERS DialogHelper.h +set(HEADERS DialogCrashReporter.h + DialogHelper.h DialogOKHelper.h) core_add_library(messagingHelpers) diff --git a/xbmc/messaging/helpers/DialogCrashReporter.cpp b/xbmc/messaging/helpers/DialogCrashReporter.cpp new file mode 100644 index 0000000000000..0afb40adb63b7 --- /dev/null +++ b/xbmc/messaging/helpers/DialogCrashReporter.cpp @@ -0,0 +1,34 @@ +/* + * Copyright (C) 2005-2018 Team Kodi + * This file is part of Kodi - https://kodi.tv + * + * SPDX-License-Identifier: GPL-2.0-or-later + * See LICENSES/README.md for more information. + */ + +#include "DialogCrashReporter.h" + +#include "messaging/ApplicationMessenger.h" + +namespace KODI +{ +namespace MESSAGING +{ +namespace HELPERS +{ + +void ShowReportCrashDialog(std::string addon, + std::string uuid, + std::string stacktrace) +{ + DialogCrashReportMessage* options = new DialogCrashReportMessage; + options->addon = std::move(addon); + options->uuid = std::move(uuid); + options->stacktrace = std::move(stacktrace); + + CApplicationMessenger::GetInstance().PostMsg(TMSG_GUI_DIALOG_ADDON_CRASH_REPORT, -1, -1, static_cast(options)); +} + +} // namespace HELPERS +} // namespace MESSAGING +} // namespace KODI diff --git a/xbmc/messaging/helpers/DialogCrashReporter.h b/xbmc/messaging/helpers/DialogCrashReporter.h new file mode 100644 index 0000000000000..945bbb02cd830 --- /dev/null +++ b/xbmc/messaging/helpers/DialogCrashReporter.h @@ -0,0 +1,37 @@ +/* + * Copyright (C) 2005-2018 Team Kodi + * This file is part of Kodi - https://kodi.tv + * + * SPDX-License-Identifier: GPL-2.0-or-later + * See LICENSES/README.md for more information. + */ + +#pragma once + +#include "utils/Variant.h" + +#include +#include + +namespace KODI +{ +namespace MESSAGING +{ +namespace HELPERS +{ + +struct DialogCrashReportMessage +{ + std::string addon; + std::string uuid; + std::string stacktrace; +}; + +void ShowReportCrashDialog(std::string addon, + std::string uuid, + std::string stacktrace); + +} // namespace HELPERS +} // namespace MESSAGING +} // namespace KODI +