StartupTraceHandler.cpp 6.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194
  1. /*
  2. * Copyright (c) Contributors to the Open 3D Engine Project.
  3. * For complete copyright and license terms please see the LICENSE at the root of this distribution.
  4. *
  5. * SPDX-License-Identifier: Apache-2.0 OR MIT
  6. *
  7. */
  8. #include "EditorDefs.h"
  9. #include "StartupTraceHandler.h"
  10. // Qt
  11. #include <QMessageBox>
  12. // AzCore
  13. #include <AzCore/Component/TickBus.h>
  14. // AzToolsFramework
  15. #include <AzToolsFramework/API/ToolsApplicationAPI.h>
  16. // Editor
  17. #include "ErrorDialog.h"
  18. namespace SandboxEditor
  19. {
  20. StartupTraceHandler::StartupTraceHandler()
  21. {
  22. ConnectToMessageBus();
  23. }
  24. StartupTraceHandler::~StartupTraceHandler()
  25. {
  26. EndCollectionAndShowCollectedErrors();
  27. DisconnectFromMessageBus();
  28. }
  29. bool StartupTraceHandler::OnPreAssert(const char* fileName, int line, const char* func, const char* message)
  30. {
  31. AZ_UNUSED(fileName);
  32. AZ_UNUSED(line);
  33. AZ_UNUSED(func);
  34. AZ_UNUSED(message);
  35. // Return false so other listeners can handle this. The StartupTraceHandler won't report messages
  36. // will probably crash before that occurs, because this is an assert.
  37. return false;
  38. }
  39. bool StartupTraceHandler::OnException(const char* message)
  40. {
  41. // Exceptions are more fatal than errors, and need to be displayed right away.
  42. // After the exception occurs, nothing else may be functional enough to collect and display messages.
  43. OnMessage(message, nullptr, MessageDisplayBehavior::AlwaysShow);
  44. // Return false so other listeners can handle this. The StartupTraceHandler won't report messages
  45. // until the next time the main thread updates the system tick bus function queue. The editor
  46. // will probably crash before that occurs, because this is an exception.
  47. return false;
  48. }
  49. bool StartupTraceHandler::OnPreError(const char* /*window*/, const char* /*fileName*/, int /*line*/, const char* /*func*/, const char* message)
  50. {
  51. // If a collection is not active, then show this error. Otherwise, collect it
  52. // and show it with other occurs that occured in the operation.
  53. return OnMessage(message, &m_errors, MessageDisplayBehavior::ShowWhenNotCollecting);
  54. }
  55. bool StartupTraceHandler::OnPreWarning(const char* /*window*/, const char* /*fileName*/, int /*line*/, const char* /*func*/, const char* message)
  56. {
  57. // Only track warnings if messages are being collected.
  58. return OnMessage(message, &m_warnings, MessageDisplayBehavior::OnlyCollect);
  59. }
  60. bool StartupTraceHandler::OnMessage(
  61. const char *message,
  62. AZStd::list<QString>* messageList,
  63. MessageDisplayBehavior messageDisplayBehavior)
  64. {
  65. if (m_isCollecting && messageList)
  66. {
  67. AZStd::lock_guard<AZStd::recursive_mutex> lock(m_mutex);
  68. messageList->push_back(QString(message));
  69. return true;
  70. }
  71. if ((!m_isCollecting && messageDisplayBehavior == MessageDisplayBehavior::ShowWhenNotCollecting) ||
  72. messageDisplayBehavior == MessageDisplayBehavior::AlwaysShow)
  73. {
  74. ShowMessageBox(message);
  75. return true;
  76. }
  77. return false;
  78. }
  79. void StartupTraceHandler::StartCollection()
  80. {
  81. ConnectToMessageBus();
  82. if (m_isCollecting)
  83. {
  84. EndCollectionAndShowCollectedErrors();
  85. }
  86. m_isCollecting = true;
  87. }
  88. void StartupTraceHandler::EndCollectionAndShowCollectedErrors()
  89. {
  90. AZStd::list<QString> cachedWarnings;
  91. AZStd::list<QString> cachedErrors;
  92. {
  93. AZStd::lock_guard<AZStd::recursive_mutex> lock(m_mutex);
  94. m_isCollecting = false;
  95. if (m_warnings.size() == 0 && m_errors.size() == 0)
  96. {
  97. return;
  98. }
  99. if (m_warnings.size() > 0)
  100. {
  101. cachedWarnings.splice(cachedWarnings.end(), m_warnings);
  102. }
  103. if (m_errors.size() > 0)
  104. {
  105. cachedErrors.splice(cachedErrors.end(), m_errors);
  106. }
  107. }
  108. if (m_showWindow)
  109. {
  110. AZ::SystemTickBus::QueueFunction([cachedWarnings, cachedErrors]()
  111. {
  112. // Parent to the main window, so that the error dialog doesn't
  113. // show up as a separate window when alt-tabbing.
  114. QWidget* mainWindow = nullptr;
  115. AzToolsFramework::EditorRequests::Bus::BroadcastResult(
  116. mainWindow,
  117. &AzToolsFramework::EditorRequests::Bus::Events::GetMainWindow);
  118. ErrorDialog* errorDialog = new ErrorDialog(mainWindow);
  119. errorDialog->AddMessages(
  120. SandboxEditor::ErrorDialog::MessageType::Warning,
  121. cachedWarnings);
  122. errorDialog->AddMessages(
  123. SandboxEditor::ErrorDialog::MessageType::Error,
  124. cachedErrors);
  125. // Use open() instead of exec() here so that we still achieve the modal dialog functionality without
  126. // blocking the event queue
  127. errorDialog->setAttribute(Qt::WA_DeleteOnClose, true);
  128. errorDialog->open();
  129. });
  130. }
  131. }
  132. void StartupTraceHandler::ShowMessageBox(const QString& message)
  133. {
  134. AZ::SystemTickBus::QueueFunction([message]()
  135. {
  136. // Parent to the main window, so that the error dialog doesn't
  137. // show up as a separate window when alt-tabbing.
  138. QWidget* mainWindow = nullptr;
  139. AzToolsFramework::EditorRequests::Bus::BroadcastResult(
  140. mainWindow,
  141. &AzToolsFramework::EditorRequests::Bus::Events::GetMainWindow);
  142. QMessageBox* msg = new QMessageBox(
  143. QMessageBox::Critical,
  144. QObject::tr("Error"),
  145. message,
  146. QMessageBox::Ok,
  147. mainWindow);
  148. msg->setTextInteractionFlags(Qt::TextSelectableByMouse);
  149. // Use open() instead of exec() here so that we still achieve the modal dialog functionality without
  150. // blocking the event queue
  151. msg->setAttribute(Qt::WA_DeleteOnClose, true);
  152. msg->open();
  153. });
  154. }
  155. void StartupTraceHandler::ConnectToMessageBus()
  156. {
  157. AZ::Debug::TraceMessageBus::Handler::BusConnect();
  158. }
  159. void StartupTraceHandler::DisconnectFromMessageBus()
  160. {
  161. AZ::Debug::TraceMessageBus::Handler::BusDisconnect();
  162. }
  163. bool StartupTraceHandler::IsConnectedToMessageBus() const
  164. {
  165. return AZ::Debug::TraceMessageBus::Handler::BusIsConnected();
  166. }
  167. }