You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
269 lines
7.8 KiB
C++
269 lines
7.8 KiB
C++
/*
|
|
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
|
|
* its licensors.
|
|
*
|
|
* For complete copyright and license terms please see the LICENSE at the root of this
|
|
* distribution (the "License"). All use of this software is governed by the License,
|
|
* or, if provided, by the license below or the license accompanying this file. Do not
|
|
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
*
|
|
*/
|
|
|
|
#include <TraceMessageHook.h>
|
|
#include <AzCore/std/smart_ptr/shared_ptr.h>
|
|
#include <AzFramework/StringFunc/StringFunc.h>
|
|
#include <AzToolsFramework/Debug/TraceContextLogFormatter.h>
|
|
#include <AzCore/Debug/Trace.h>
|
|
#include <AzCore/PlatformIncl.h>
|
|
|
|
namespace AssetBuilder
|
|
{
|
|
constexpr int MaxMessageLength = 4096;
|
|
|
|
TraceMessageHook::TraceMessageHook()
|
|
: m_stacks(nullptr)
|
|
, m_inDebugMode(false)
|
|
, m_skipErrorsCount(0)
|
|
, m_skipWarningsCount(0)
|
|
, m_skipPrintfsCount(0)
|
|
, m_totalWarningCount(0)
|
|
, m_totalErrorCount(0)
|
|
{
|
|
AssetBuilderSDK::AssetBuilderTraceBus::Handler::BusConnect();
|
|
AZ::Debug::TraceMessageBus::Handler::BusConnect();
|
|
}
|
|
|
|
TraceMessageHook::~TraceMessageHook()
|
|
{
|
|
AZ::Debug::TraceMessageBus::Handler::BusDisconnect();
|
|
AssetBuilderSDK::AssetBuilderTraceBus::Handler::BusDisconnect();
|
|
delete m_stacks;
|
|
m_stacks = nullptr;
|
|
}
|
|
|
|
void TraceMessageHook::EnableTraceContext(bool enable)
|
|
{
|
|
if (enable)
|
|
{
|
|
if (!m_stacks)
|
|
{
|
|
m_stacks = new AzToolsFramework::Debug::TraceContextMultiStackHandler();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
delete m_stacks;
|
|
m_stacks = nullptr;
|
|
}
|
|
}
|
|
|
|
void TraceMessageHook::EnableDebugMode(bool enable)
|
|
{
|
|
m_inDebugMode = enable;
|
|
}
|
|
|
|
bool TraceMessageHook::OnAssert(const char* message)
|
|
{
|
|
if (m_skipErrorsCount == 0)
|
|
{
|
|
CleanMessage(stderr, "E", message, true);
|
|
std::fflush(stderr);
|
|
++m_totalErrorCount;
|
|
}
|
|
else
|
|
{
|
|
--m_skipErrorsCount;
|
|
}
|
|
|
|
return !m_inDebugMode;
|
|
}
|
|
|
|
bool TraceMessageHook::OnPreError(const char* window, const char* fileName, int line, const char* func, const char* message)
|
|
{
|
|
if(m_skipErrorsCount == 0)
|
|
{
|
|
char header[MaxMessageLength];
|
|
|
|
azsnprintf(header, MaxMessageLength, "%s: Trace::Error\n>\t%s(%d): '%s'\n", window, fileName, line, func);
|
|
CleanMessage(stderr, "E", header, false);
|
|
|
|
CleanMessage(stderr, "E", message, true, ">\t");
|
|
|
|
++m_totalErrorCount;
|
|
}
|
|
else
|
|
{
|
|
--m_skipErrorsCount;
|
|
}
|
|
|
|
return !m_inDebugMode;
|
|
}
|
|
|
|
bool TraceMessageHook::OnPreWarning(const char* window, const char* fileName, int line, const char* func, const char* message)
|
|
{
|
|
if (m_skipWarningsCount == 0)
|
|
{
|
|
char header[MaxMessageLength];
|
|
|
|
azsnprintf(header, MaxMessageLength, "%s: Trace::Warning\n>\t%s(%d): '%s'\n", window, fileName, line, func);
|
|
CleanMessage(stdout, "W", header, false);
|
|
|
|
CleanMessage(stdout, "W", message, true, ">\t");
|
|
|
|
++m_totalWarningCount;
|
|
}
|
|
else
|
|
{
|
|
--m_skipWarningsCount;
|
|
}
|
|
|
|
return !m_inDebugMode;
|
|
}
|
|
|
|
bool TraceMessageHook::OnException(const char* message)
|
|
{
|
|
m_isInException = true;
|
|
CleanMessage(stderr, "E", message, true);
|
|
++m_totalErrorCount;
|
|
AZ::Debug::Trace::HandleExceptions(false);
|
|
AZ::Debug::Trace::PrintCallstack("", 3); // Skip all the Trace.cpp function calls
|
|
// note that the above call ultimately results in a whole bunch of TracePrint/Outputs, which will end up in OnOutput below.
|
|
|
|
std::fflush(stderr);
|
|
std::fflush(stdout);
|
|
|
|
// if we don't terminate here, the user may get a dialog box from the OS saying that the program crashed.
|
|
// we don't want this, because in this case, the program is one of potentially many, many background worker processes
|
|
// that are continuously starting/stopping and they'd get flooded by those message boxes.
|
|
AZ::Debug::Trace::Terminate(1);
|
|
|
|
return false;
|
|
}
|
|
|
|
bool TraceMessageHook::OnOutput(const char* /*window*/, const char* message)
|
|
{
|
|
if (m_isInException) // all messages that occur during an exception should be considered an error.
|
|
{
|
|
CleanMessage(stderr, "E", message, true);
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
bool TraceMessageHook::OnPrintf(const char* window, const char* message)
|
|
{
|
|
if (m_skipPrintfsCount == 0)
|
|
{
|
|
CleanMessage(stdout, window, message, false);
|
|
}
|
|
else
|
|
{
|
|
--m_skipPrintfsCount;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
void TraceMessageHook::IgnoreNextErrors(AZ::u32 count)
|
|
{
|
|
m_skipErrorsCount += count;
|
|
}
|
|
|
|
void TraceMessageHook::IgnoreNextWarning(AZ::u32 count)
|
|
{
|
|
m_skipWarningsCount += count;
|
|
}
|
|
|
|
void TraceMessageHook::IgnoreNextPrintf(AZ::u32 count)
|
|
{
|
|
m_skipPrintfsCount += count;
|
|
}
|
|
|
|
void TraceMessageHook::ResetWarningCount()
|
|
{
|
|
m_totalWarningCount = 0;
|
|
}
|
|
|
|
void TraceMessageHook::ResetErrorCount()
|
|
{
|
|
m_totalErrorCount = 0;
|
|
}
|
|
|
|
AZ::u32 TraceMessageHook::GetWarningCount()
|
|
{
|
|
return m_totalWarningCount;
|
|
}
|
|
|
|
AZ::u32 TraceMessageHook::GetErrorCount()
|
|
{
|
|
return m_totalErrorCount;
|
|
}
|
|
|
|
void TraceMessageHook::DumpTraceContext(FILE* stream) const
|
|
{
|
|
if (m_stacks)
|
|
{
|
|
AZStd::shared_ptr<const AzToolsFramework::Debug::TraceContextStack> stack = m_stacks->GetCurrentStack();
|
|
if (stack)
|
|
{
|
|
AZStd::string line;
|
|
size_t stackSize = stack->GetStackCount();
|
|
for (size_t i = 0; i < stackSize; ++i)
|
|
{
|
|
line.clear();
|
|
AzToolsFramework::Debug::TraceContextLogFormatter::PrintLine(line, *stack, i);
|
|
CleanMessage(stream, "C", line.c_str(), false, nullptr, false);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void TraceMessageHook::CleanMessage(FILE* stream, const char* prefix, const char* message, bool forceFlush, const char* extraPrefix, bool includeTraceContext) const
|
|
{
|
|
if (message && message[0])
|
|
{
|
|
AZStd::vector<AZStd::string> lines;
|
|
AzFramework::StringFunc::Tokenize(message, lines, '\n', true, true); // Make sure to keep empty lines because it could be intentional blank lines someone has added for formatting reasons
|
|
|
|
// If the message ended with a newline, remove it, we're adding newlines to each line already
|
|
if(lines.back().empty())
|
|
{
|
|
lines.pop_back();
|
|
}
|
|
|
|
for (const AZStd::string& line : lines)
|
|
{
|
|
if(includeTraceContext)
|
|
{
|
|
DumpTraceContext(stream);
|
|
}
|
|
|
|
if (prefix && prefix[0])
|
|
{
|
|
fprintf(stream, "%s: ", prefix);
|
|
}
|
|
|
|
if(extraPrefix && extraPrefix[0])
|
|
{
|
|
fprintf(stream, "%s", extraPrefix);
|
|
}
|
|
|
|
fprintf(stream, "%s\n", line.c_str());
|
|
}
|
|
|
|
// Make sure the message ends with a newline
|
|
if (message[AZStd::char_traits<char>::length(message) - 1] != '\n')
|
|
{
|
|
fprintf(stream, "\n");
|
|
}
|
|
|
|
if (forceFlush)
|
|
{
|
|
fflush(stream);
|
|
}
|
|
}
|
|
}
|
|
} // namespace AssetBuilder
|