Adding stack traces to exception handling in unit tests
Signed-off-by: Esteban Papp <81431996+amznestebanpapp@users.noreply.github.com>
This commit is contained in:
@@ -40,6 +40,12 @@ namespace AZ
|
||||
static unsigned int Record(StackFrame* frames, unsigned int maxNumOfFrames, unsigned int suppressCount = 0, void* nativeThread = 0);
|
||||
};
|
||||
|
||||
class StackConverter
|
||||
{
|
||||
public:
|
||||
static unsigned int FromNative(StackFrame* frames, unsigned int maxNumOfFrames, void* nativeContext);
|
||||
};
|
||||
|
||||
class SymbolStorage
|
||||
{
|
||||
public:
|
||||
|
||||
@@ -31,6 +31,8 @@ namespace AZ
|
||||
{
|
||||
namespace Debug
|
||||
{
|
||||
struct StackFrame;
|
||||
|
||||
namespace Platform
|
||||
{
|
||||
#if defined(AZ_ENABLE_DEBUG_TOOLS)
|
||||
@@ -551,17 +553,19 @@ namespace AZ
|
||||
{
|
||||
StackFrame frames[25];
|
||||
|
||||
// Without StackFrame explicit alignment frames array is aligned to 4 bytes
|
||||
// which causes the stack tracing to fail.
|
||||
//size_t bla = AZStd::alignment_of<StackFrame>::value;
|
||||
//printf("Alignment value %d address 0x%08x : 0x%08x\n",bla,frames);
|
||||
SymbolStorage::StackLine lines[AZ_ARRAY_SIZE(frames)];
|
||||
unsigned int numFrames = 0;
|
||||
|
||||
if (!nativeContext)
|
||||
{
|
||||
suppressCount += 1; /// If we don't provide a context we will capture in the RecordFunction, so skip us (Trace::PrinCallstack).
|
||||
suppressCount += 1; /// If we don't provide a context we will capture in the RecordFunction, so skip us (Trace::PrintCallstack).
|
||||
numFrames = StackRecorder::Record(frames, AZ_ARRAY_SIZE(frames), suppressCount);
|
||||
}
|
||||
unsigned int numFrames = StackRecorder::Record(frames, AZ_ARRAY_SIZE(frames), suppressCount, nativeContext);
|
||||
else
|
||||
{
|
||||
numFrames = StackConverter::FromNative(frames, AZ_ARRAY_SIZE(frames), nativeContext);
|
||||
}
|
||||
|
||||
if (numFrames)
|
||||
{
|
||||
SymbolStorage::DecodeFrames(frames, numFrames, lines);
|
||||
|
||||
+5
@@ -17,6 +17,11 @@ namespace AZ
|
||||
return false;
|
||||
}
|
||||
|
||||
unsigned int StackConverter::FromNative(StackFrame*, unsigned int, void*)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
void SymbolStorage::LoadModuleData(const void*, unsigned int)
|
||||
{}
|
||||
|
||||
|
||||
@@ -78,6 +78,12 @@ StackRecorder::Record(StackFrame* frames, unsigned int maxNumOfFrames, unsigned
|
||||
return count;
|
||||
}
|
||||
|
||||
unsigned int StackConverter::FromNative([[maybe_unused]] StackFrame* frames, [[maybe_unused]] unsigned int maxNumOfFrames, [[maybe_unused]] void* nativeContext)
|
||||
{
|
||||
AZ_Assert(false, "StackConverter::FromNative() is not supported for UnixLike platform yet");
|
||||
return 0;
|
||||
}
|
||||
|
||||
void
|
||||
SymbolStorage::DecodeFrames(const StackFrame* frames, unsigned int numFrames, StackLine* textLines)
|
||||
{
|
||||
|
||||
@@ -1048,9 +1048,9 @@ cleanup:
|
||||
unsigned int
|
||||
StackRecorder::Record(StackFrame* frames, unsigned int maxNumOfFrames, unsigned int suppressCount, void* nativeThread)
|
||||
{
|
||||
#if defined(AZ_ENABLE_DEBUG_TOOLS)
|
||||
unsigned int numFrames = 0;
|
||||
|
||||
#if defined(AZ_ENABLE_DEBUG_TOOLS)
|
||||
if (nativeThread == NULL)
|
||||
{
|
||||
++suppressCount; // Skip current call
|
||||
@@ -1079,9 +1079,8 @@ cleanup:
|
||||
|
||||
STACKFRAME64 sf;
|
||||
memset(&sf, 0, sizeof(STACKFRAME64));
|
||||
DWORD imageType;
|
||||
DWORD imageType = IMAGE_FILE_MACHINE_AMD64;
|
||||
|
||||
imageType = IMAGE_FILE_MACHINE_AMD64;
|
||||
sf.AddrPC.Offset = context.Rip;
|
||||
sf.AddrPC.Mode = AddrModeFlat;
|
||||
sf.AddrFrame.Offset = context.Rsp;
|
||||
@@ -1090,8 +1089,7 @@ cleanup:
|
||||
sf.AddrStack.Mode = AddrModeFlat;
|
||||
|
||||
EnterCriticalSection(&g_csDbgHelpDll);
|
||||
s32 frame = -(s32)suppressCount;
|
||||
for (; frame < (s32)maxNumOfFrames; ++frame)
|
||||
for (s32 frame = -static_cast<s32>(suppressCount); frame < static_cast<s32>(maxNumOfFrames); ++frame)
|
||||
{
|
||||
if (!g_StackWalk64(imageType, g_currentProcess, hThread, &sf, &context, 0, g_SymFunctionTableAccess64, g_SymGetModuleBase64, 0))
|
||||
{
|
||||
@@ -1111,15 +1109,68 @@ cleanup:
|
||||
}
|
||||
|
||||
LeaveCriticalSection(&g_csDbgHelpDll);
|
||||
}
|
||||
return numFrames;
|
||||
}
|
||||
#else
|
||||
(void)frames;
|
||||
(void)maxNumOfFrames;
|
||||
(void)suppressCount;
|
||||
(void)nativeThread;
|
||||
return 0;
|
||||
AZ_UNUSED(frames);
|
||||
AZ_UNUSED(maxNumOfFrames);
|
||||
AZ_UNUSED(suppressCount);
|
||||
AZ_UNUSED(nativeThread);
|
||||
#endif // AZ_ENABLE_DEBUG_TOOLS
|
||||
|
||||
return numFrames;
|
||||
}
|
||||
|
||||
unsigned int StackConverter::FromNative(StackFrame* frames, unsigned int maxNumOfFrames, void* nativeContext)
|
||||
{
|
||||
unsigned int numFrames = 0;
|
||||
|
||||
#if defined(AZ_ENABLE_DEBUG_TOOLS)
|
||||
if (!g_dbgHelpLoaded)
|
||||
{
|
||||
LoadDbgHelp();
|
||||
}
|
||||
|
||||
HANDLE hThread;
|
||||
DuplicateHandle(GetCurrentProcess(), GetCurrentThread(), GetCurrentProcess(), &hThread, 0, false, DUPLICATE_SAME_ACCESS);
|
||||
|
||||
PCONTEXT nativeContextType = reinterpret_cast<PCONTEXT>(nativeContext);
|
||||
STACKFRAME64 sf;
|
||||
memset(&sf, 0, sizeof(STACKFRAME64));
|
||||
|
||||
DWORD imageType = IMAGE_FILE_MACHINE_AMD64;
|
||||
|
||||
sf.AddrPC.Offset = nativeContextType->Rip;
|
||||
sf.AddrPC.Mode = AddrModeFlat;
|
||||
sf.AddrFrame.Offset = nativeContextType->Rsp;
|
||||
sf.AddrFrame.Mode = AddrModeFlat;
|
||||
sf.AddrStack.Offset = nativeContextType->Rsp;
|
||||
sf.AddrStack.Mode = AddrModeFlat;
|
||||
|
||||
EnterCriticalSection(&g_csDbgHelpDll);
|
||||
for (unsigned int frame = 0; frame < maxNumOfFrames; ++frame)
|
||||
{
|
||||
if (!g_StackWalk64(imageType, g_currentProcess, hThread, &sf, nativeContext, 0, g_SymFunctionTableAccess64, g_SymGetModuleBase64, 0))
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
if (sf.AddrPC.Offset == sf.AddrReturn.Offset)
|
||||
{
|
||||
// "StackWalk64-Endless-Callstack!"
|
||||
break;
|
||||
}
|
||||
|
||||
frames[numFrames++].m_programCounter = sf.AddrPC.Offset;
|
||||
}
|
||||
|
||||
LeaveCriticalSection(&g_csDbgHelpDll);
|
||||
#else
|
||||
AZ_UNUSED(frames);
|
||||
AZ_UNUSED(maxNumOfFrames);
|
||||
AZ_UNUSED(nativeContext);
|
||||
#endif
|
||||
|
||||
return numFrames;
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
@@ -1413,7 +1413,7 @@ namespace UnitTest
|
||||
>;
|
||||
TYPED_TEST_CASE(HashedSetDifferentAllocatorFixture, SetTemplateConfigs);
|
||||
|
||||
#if GTEST_OS_SUPPORTS_DEATH_TEST
|
||||
#if GTEST_HAS_DEATH_TEST
|
||||
TYPED_TEST(HashedSetDifferentAllocatorFixture, InsertNodeHandleWithDifferentAllocatorsLogsTraceMessages)
|
||||
{
|
||||
using ContainerType = typename TypeParam::ContainerType;
|
||||
@@ -1435,7 +1435,7 @@ namespace UnitTest
|
||||
}
|
||||
}, ".*");
|
||||
}
|
||||
#endif // GTEST_OS_SUPPORTS_DEATH_TEST
|
||||
#endif // GTEST_HAS_DEATH_TEST
|
||||
|
||||
template<typename ContainerType>
|
||||
class HashedMapContainers
|
||||
@@ -1811,7 +1811,7 @@ namespace UnitTest
|
||||
>;
|
||||
TYPED_TEST_CASE(HashedMapDifferentAllocatorFixture, MapTemplateConfigs);
|
||||
|
||||
#if GTEST_OS_SUPPORTS_DEATH_TEST
|
||||
#if GTEST_HAS_DEATH_TEST
|
||||
TYPED_TEST(HashedMapDifferentAllocatorFixture, InsertNodeHandleWithDifferentAllocatorsLogsTraceMessages)
|
||||
{
|
||||
using ContainerType = typename TypeParam::ContainerType;
|
||||
@@ -1833,7 +1833,7 @@ namespace UnitTest
|
||||
}
|
||||
} , ".*");
|
||||
}
|
||||
#endif // GTEST_OS_SUPPORTS_DEATH_TEST
|
||||
#endif // GTEST_HAS_DEATH_TEST
|
||||
|
||||
namespace HashedContainerTransparentTestInternal
|
||||
{
|
||||
|
||||
@@ -1095,7 +1095,7 @@ namespace UnitTest
|
||||
>;
|
||||
TYPED_TEST_CASE(TreeSetDifferentAllocatorFixture, SetTemplateConfigs);
|
||||
|
||||
#if GTEST_OS_SUPPORTS_DEATH_TEST
|
||||
#if GTEST_HAS_DEATH_TEST
|
||||
TYPED_TEST(TreeSetDifferentAllocatorFixture, InsertNodeHandleWithDifferentAllocatorsLogsTraceMessages)
|
||||
{
|
||||
using ContainerType = typename TypeParam::ContainerType;
|
||||
@@ -1117,7 +1117,7 @@ namespace UnitTest
|
||||
}
|
||||
}, ".*");
|
||||
}
|
||||
#endif // GTEST_OS_SUPPORTS_DEATH_TEST
|
||||
#endif // GTEST_HAS_DEATH_TEST
|
||||
|
||||
TYPED_TEST(TreeSetDifferentAllocatorFixture, SwapMovesElementsWhenAllocatorsDiffer)
|
||||
{
|
||||
@@ -1516,7 +1516,7 @@ namespace UnitTest
|
||||
>;
|
||||
TYPED_TEST_CASE(TreeMapDifferentAllocatorFixture, MapTemplateConfigs);
|
||||
|
||||
#if GTEST_OS_SUPPORTS_DEATH_TEST
|
||||
#if GTEST_HAS_DEATH_TEST
|
||||
TYPED_TEST(TreeMapDifferentAllocatorFixture, InsertNodeHandleWithDifferentAllocatorsLogsTraceMessages)
|
||||
{
|
||||
using ContainerType = typename TypeParam::ContainerType;
|
||||
@@ -1538,7 +1538,7 @@ namespace UnitTest
|
||||
}
|
||||
}, ".*");
|
||||
}
|
||||
#endif // GTEST_OS_SUPPORTS_DEATH_TEST
|
||||
#endif // GTEST_HAS_DEATH_TEST
|
||||
|
||||
TYPED_TEST(TreeMapDifferentAllocatorFixture, SwapMovesElementsWhenAllocatorsDiffer)
|
||||
{
|
||||
|
||||
@@ -1595,7 +1595,7 @@ namespace UnitTest
|
||||
}
|
||||
};
|
||||
|
||||
#if GTEST_OS_SUPPORTS_DEATH_TEST
|
||||
#if GTEST_HAS_DEATH_TEST
|
||||
TEST_F(ThreadEventsDeathTest, UsingClientBus_AvoidsDeadlock)
|
||||
{
|
||||
EXPECT_EXIT(
|
||||
@@ -1608,5 +1608,5 @@ namespace UnitTest
|
||||
, ::testing::ExitedWithCode(0),".*");
|
||||
|
||||
}
|
||||
#endif // GTEST_OS_SUPPORTS_DEATH_TEST
|
||||
#endif // GTEST_HAS_DEATH_TEST
|
||||
}
|
||||
|
||||
@@ -0,0 +1,31 @@
|
||||
/*
|
||||
* Copyright (c) Contributors to the Open 3D Engine Project.
|
||||
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
*
|
||||
*/
|
||||
|
||||
#include <AzCore/UnitTest/TestTypes.h>
|
||||
|
||||
namespace UnitTest
|
||||
{
|
||||
class UnhandledExceptions
|
||||
: public ScopedAllocatorSetupFixture
|
||||
{
|
||||
|
||||
public:
|
||||
void causeAccessViolation()
|
||||
{
|
||||
int* someVariable = reinterpret_cast<int*>(0);
|
||||
*someVariable = 0;
|
||||
}
|
||||
};
|
||||
|
||||
#if GTEST_HAS_DEATH_TEST
|
||||
TEST_F(UnhandledExceptions, Handle)
|
||||
{
|
||||
EXPECT_DEATH(causeAccessViolation(), "");
|
||||
}
|
||||
#endif
|
||||
}
|
||||
@@ -144,14 +144,13 @@ namespace UnitTest
|
||||
}
|
||||
};
|
||||
|
||||
#if GTEST_OS_SUPPORTS_DEATH_TEST
|
||||
// SPEC-2669: Disabled since it is causing hangs on Linux
|
||||
#if GTEST_HAS_DEATH_TEST
|
||||
TEST_F(AllocatorsTestFixtureLeakDetectionDeathTest_SKIPCODECOVERAGE, AllocatorLeak)
|
||||
{
|
||||
// testing that the TraceBusHook will fail on cause the test to die
|
||||
EXPECT_DEATH(TestAllocatorLeak(), "");
|
||||
}
|
||||
#endif // GTEST_OS_SUPPORTS_DEATH_TEST
|
||||
#endif // GTEST_HAS_DEATH_TEST
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Testing ScopedAllocatorSetupFixture. Testing that detects leaks
|
||||
|
||||
@@ -327,7 +327,7 @@ namespace JsonSerializationTests
|
||||
SerializerWithOneType::Unreflect(m_jsonRegistrationContext.get());
|
||||
}
|
||||
|
||||
#if GTEST_OS_SUPPORTS_DEATH_TEST
|
||||
#if GTEST_HAS_DEATH_TEST
|
||||
using JsonSerializationDeathTests = JsonRegistrationContextTests;
|
||||
TEST_F(JsonSerializationDeathTests, DoubleUnregisterSerializer_Asserts)
|
||||
{
|
||||
@@ -338,5 +338,6 @@ namespace JsonSerializationTests
|
||||
}, ".*"
|
||||
);
|
||||
}
|
||||
#endif // GTEST_OS_SUPPORTS_DEATH_TEST
|
||||
#endif // GTEST_HAS_DEATH_TEST
|
||||
|
||||
} //namespace JsonSerializationTests
|
||||
|
||||
@@ -72,6 +72,7 @@ set(FILES
|
||||
Debug/AssetTracking.cpp
|
||||
Debug/LocalFileEventLoggerTests.cpp
|
||||
Debug/Trace.cpp
|
||||
Debug/UnhandledExceptions.cpp
|
||||
Name/NameJsonSerializerTests.cpp
|
||||
Name/NameTests.cpp
|
||||
RTTI/TypeSafeIntegralTests.cpp
|
||||
|
||||
@@ -92,10 +92,14 @@ namespace AZ
|
||||
|
||||
void ApplyGlobalParameters(int* argc, char** argv)
|
||||
{
|
||||
// this is a hook that can be used to apply any other global non-google parameters
|
||||
// that we use.
|
||||
// this is a hook that can be used to apply any other global parameters that we use.
|
||||
AZ_UNUSED(argc);
|
||||
AZ_UNUSED(argv);
|
||||
|
||||
// Disable gtest catching unhandled exceptions, instead, AzTestRunner will do it through:
|
||||
// AZ::Debug::Trace::HandleExceptions(true). This gives us a stack trace when the exception
|
||||
// is thrown (googletest does not).
|
||||
testing::FLAGS_gtest_catch_exceptions = false;
|
||||
}
|
||||
|
||||
//! Print out parameters that are not used by the framework
|
||||
|
||||
@@ -291,6 +291,9 @@ namespace AssetBundler
|
||||
|
||||
int main(int argc, char* argv[])
|
||||
{
|
||||
AZ::Debug::Trace::HandleExceptions(true);
|
||||
AZ::Test::ApplyGlobalParameters(&argc, argv);
|
||||
|
||||
INVOKE_AZ_UNIT_TEST_MAIN();
|
||||
|
||||
AZ::AllocatorInstance<AZ::SystemAllocator>::Create();
|
||||
|
||||
@@ -262,7 +262,7 @@ namespace UnitTests
|
||||
|
||||
auto result = m_data->m_reporter->ComputeDestination(entryContainer, m_data->m_platformConfig.GetScanFolderByPath(scanFolderEntry.m_scanFolder.c_str()), source, destination, destInfo);
|
||||
|
||||
ASSERT_EQ(result.IsSuccess(), expectSuccess) << result.GetError().c_str();
|
||||
ASSERT_EQ(result.IsSuccess(), expectSuccess) << (!result.IsSuccess() ? result.GetError().c_str() : "");
|
||||
|
||||
if (expectSuccess)
|
||||
{
|
||||
|
||||
@@ -29,6 +29,9 @@ int main(int argc, char* argv[])
|
||||
{
|
||||
qputenv("QT_MAC_DISABLE_FOREGROUND_APPLICATION_TRANSFORM", "1");
|
||||
|
||||
AZ::Debug::Trace::HandleExceptions(true);
|
||||
AZ::Test::ApplyGlobalParameters(&argc, argv);
|
||||
|
||||
// If "--unittest" is present on the command line, run unit testing
|
||||
// and return immediately. Otherwise, continue as normal.
|
||||
AZ::Test::addTestEnvironment(new BaseAssetProcessorTestEnvironment());
|
||||
|
||||
@@ -189,6 +189,8 @@ namespace AzTestRunner
|
||||
|
||||
int wrapped_main(int argc/*=0*/, char** argv/*=nullptr*/)
|
||||
{
|
||||
AZ::Debug::Trace::HandleExceptions(true);
|
||||
|
||||
if (argc>0 && argv!=nullptr)
|
||||
{
|
||||
return wrapped_command_arg_main(argc, argv);
|
||||
|
||||
@@ -18,6 +18,9 @@ int runDefaultRunner(int argc, char* argv[])
|
||||
|
||||
int main(int argc, char* argv[])
|
||||
{
|
||||
AZ::Debug::Trace::HandleExceptions(true);
|
||||
AZ::Test::ApplyGlobalParameters(&argc, argv);
|
||||
|
||||
if (argc == 1)
|
||||
{
|
||||
// if no parameters are provided, add the --unittests parameter
|
||||
|
||||
@@ -23,6 +23,9 @@ int runDefaultRunner(int argc, char* argv[])
|
||||
|
||||
int main(int argc, char* argv[])
|
||||
{
|
||||
AZ::Debug::Trace::HandleExceptions(true);
|
||||
AZ::Test::ApplyGlobalParameters(&argc, argv);
|
||||
|
||||
// ran with no parameters?
|
||||
if (argc == 1)
|
||||
{
|
||||
|
||||
@@ -48,10 +48,10 @@ namespace WhiteBox
|
||||
float time;
|
||||
AZ::Vector3 normal;
|
||||
const float rayLength = 1000.0f;
|
||||
const int intersected = AZ::Intersect::IntersectSegmentTriangleCCW(
|
||||
const bool intersected = AZ::Intersect::IntersectSegmentTriangleCCW(
|
||||
rayOrigin, rayOrigin + rayDirection * rayLength, p0, p1, p2, normal, time);
|
||||
|
||||
if (intersected != 0)
|
||||
if (intersected)
|
||||
{
|
||||
rayIntersectionDistance = time * rayLength;
|
||||
intersectedTriangleIndex = triangleIndex / 3;
|
||||
|
||||
Reference in New Issue
Block a user