log access violation on windows tinderboxen

where we currently have no good feedback when something
goes wrong.

Credit: Exception printing code written by
  msdn-whiteknight
as seen at
  https://stackoverflow.com/questions/22467604/how-can-you-use-capturestackbacktrace-to-capture-the-exception-stack-not-the-ca

Change-Id: I44c65b10c0b0151c3236521d62a824570d4cdccf
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/119561
Tested-by: Jenkins
Reviewed-by: Noel Grandin <noel.grandin@collabora.co.uk>
This commit is contained in:
Noel Grandin 2021-07-27 15:15:21 +02:00 committed by Noel Grandin
parent 67faef71f9
commit 24b06b9c6b
2 changed files with 213 additions and 81 deletions

View file

@ -28,6 +28,12 @@ $(eval $(call gb_Executable_add_exception_objects,cppunittester,\
sal/cppunittester/cppunittester \
))
ifneq ($(strip $(debug)$(DEBUG)),)
$(eval $(call gb_Executable_use_system_win32_libs,cppunittester,\
dbghelp \
))
endif
ifeq ($(COM),MSC)
$(eval $(call gb_Executable_add_ldflags,cppunittester,\

View file

@ -23,6 +23,9 @@
#endif
#include <windows.h>
#endif
#if defined(_WIN32) && defined(_DEBUG)
#include "dbghelp.h"
#endif
#ifdef UNX
#include <sys/time.h>
@ -378,109 +381,232 @@ void reportResourceUsage([[maybe_unused]] const OUString& /*rPath*/)
}
SAL_IMPLEMENT_MAIN()
static bool main2()
{
bool ok = false;
OUString path;
try
{
#ifdef _WIN32
//Disable Dr-Watson in order to crash simply without popup dialogs under
//windows
DWORD dwMode = SetErrorMode(SEM_NOGPFAULTERRORBOX);
SetErrorMode(SEM_NOGPFAULTERRORBOX|dwMode);
//Disable Dr-Watson in order to crash simply without popup dialogs under
//windows
DWORD dwMode = SetErrorMode(SEM_NOGPFAULTERRORBOX);
SetErrorMode(SEM_NOGPFAULTERRORBOX|dwMode);
#ifdef _DEBUG // These functions are present only in the debugging runtime
_CrtSetReportMode(_CRT_WARN, _CRTDBG_MODE_DEBUG|_CRTDBG_MODE_FILE);
_CrtSetReportFile(_CRT_WARN, _CRTDBG_FILE_STDERR);
_CrtSetReportMode(_CRT_ERROR, _CRTDBG_MODE_DEBUG|_CRTDBG_MODE_FILE);
_CrtSetReportFile(_CRT_ERROR, _CRTDBG_FILE_STDERR);
_CrtSetReportMode(_CRT_ASSERT, _CRTDBG_MODE_DEBUG|_CRTDBG_MODE_FILE);
_CrtSetReportFile(_CRT_ASSERT, _CRTDBG_FILE_STDERR);
_CrtSetReportMode(_CRT_WARN, _CRTDBG_MODE_DEBUG|_CRTDBG_MODE_FILE);
_CrtSetReportFile(_CRT_WARN, _CRTDBG_FILE_STDERR);
_CrtSetReportMode(_CRT_ERROR, _CRTDBG_MODE_DEBUG|_CRTDBG_MODE_FILE);
_CrtSetReportFile(_CRT_ERROR, _CRTDBG_FILE_STDERR);
_CrtSetReportMode(_CRT_ASSERT, _CRTDBG_MODE_DEBUG|_CRTDBG_MODE_FILE);
_CrtSetReportFile(_CRT_ASSERT, _CRTDBG_FILE_STDERR);
#endif
#endif
std::vector<CppUnit::Protector *> protectors;
CppUnit::TestResult result;
std::string args;
std::string testlib;
sal_uInt32 index = 0;
while (index < osl_getCommandArgCount())
std::vector<CppUnit::Protector *> protectors;
CppUnit::TestResult result;
std::string args;
std::string testlib;
sal_uInt32 index = 0;
while (index < osl_getCommandArgCount())
{
OUString arg = getArgument(index);
if (arg.startsWith("-env:CPPUNITTESTTARGET=", &path))
{
OUString arg = getArgument(index);
if (arg.startsWith("-env:CPPUNITTESTTARGET=", &path))
++index;
continue;
}
if (arg.startsWith("-env:"))
{
++index;
continue; // ignore it here - will be read later
}
if ( arg != "--protector" )
{
if (testlib.empty())
{
++index;
continue;
testlib = OUStringToOString(arg, osl_getThreadTextEncoding()).getStr();
args += testlib;
}
if (arg.startsWith("-env:"))
{
++index;
continue; // ignore it here - will be read later
}
if ( arg != "--protector" )
{
if (testlib.empty())
{
testlib = OUStringToOString(arg, osl_getThreadTextEncoding()).getStr();
args += testlib;
}
else
{
args += ' ';
args += OUStringToOString(arg, osl_getThreadTextEncoding()).getStr();
}
++index;
continue;
}
if (osl_getCommandArgCount() - index < 3) {
usageFailure();
}
OUString lib(getArgument(index + 1));
OUString sym(getArgument(index + 2));
#ifndef DISABLE_DYNLOADING
osl::Module mod(lib, SAL_LOADMODULE_GLOBAL);
oslGenericFunction fn = mod.getFunctionSymbol(sym);
mod.release();
#else
oslGenericFunction fn = 0;
if (sym == "unoexceptionprotector")
fn = (oslGenericFunction) unoexceptionprotector;
else if (sym == "unobootstrapprotector")
fn = (oslGenericFunction) unobootstrapprotector;
else if (sym == "vclbootstrapprotector")
fn = (oslGenericFunction) vclbootstrapprotector;
else
{
std::cerr
<< "Only unoexceptionprotector or unobootstrapprotector protectors allowed"
<< std::endl;
std::exit(EXIT_FAILURE);
args += ' ';
args += OUStringToOString(arg, osl_getThreadTextEncoding()).getStr();
}
#endif
CppUnit::Protector *protector = fn == nullptr
? nullptr
: (*reinterpret_cast< cppunittester::ProtectorFactory * >(fn))();
if (protector == nullptr) {
std::cerr
<< "Failure instantiating protector \"" << convertLazy(lib)
<< "\", \"" << convertLazy(sym) << '"' << std::endl;
std::exit(EXIT_FAILURE);
}
protectors.push_back(protector);
index+=3;
++index;
continue;
}
if (osl_getCommandArgCount() - index < 3) {
usageFailure();
}
OUString lib(getArgument(index + 1));
OUString sym(getArgument(index + 2));
#ifndef DISABLE_DYNLOADING
osl::Module mod(lib, SAL_LOADMODULE_GLOBAL);
oslGenericFunction fn = mod.getFunctionSymbol(sym);
mod.release();
#else
oslGenericFunction fn = 0;
if (sym == "unoexceptionprotector")
fn = (oslGenericFunction) unoexceptionprotector;
else if (sym == "unobootstrapprotector")
fn = (oslGenericFunction) unobootstrapprotector;
else if (sym == "vclbootstrapprotector")
fn = (oslGenericFunction) vclbootstrapprotector;
else
{
std::cerr
<< "Only unoexceptionprotector or unobootstrapprotector protectors allowed"
<< std::endl;
std::exit(EXIT_FAILURE);
}
#endif
CppUnit::Protector *protector = fn == nullptr
? nullptr
: (*reinterpret_cast< cppunittester::ProtectorFactory * >(fn))();
if (protector == nullptr) {
std::cerr
<< "Failure instantiating protector \"" << convertLazy(lib)
<< "\", \"" << convertLazy(sym) << '"' << std::endl;
std::exit(EXIT_FAILURE);
}
protectors.push_back(protector);
index+=3;
}
ProtectedFixtureFunctor tests(testlib, args, protectors, result);
ok = tests.run();
ProtectedFixtureFunctor tests(testlib, args, protectors, result);
ok = tests.run();
reportResourceUsage(path);
return ok;
}
#if defined(_WIN32) && defined(_DEBUG)
//Prints stack trace based on exception context record
void printStack( CONTEXT* ctx )
{
constexpr int MaxNameLen = 256;
BOOL result;
HANDLE process;
HANDLE thread;
HMODULE hModule;
STACKFRAME64 stack;
ULONG frame;
DWORD64 displacement;
DWORD disp;
char buffer[sizeof(SYMBOL_INFO) + MAX_SYM_NAME * sizeof(TCHAR)];
char module[MaxNameLen];
PSYMBOL_INFO pSymbol = (PSYMBOL_INFO)buffer;
memset( &stack, 0, sizeof( STACKFRAME64 ) );
process = GetCurrentProcess();
thread = GetCurrentThread();
displacement = 0;
#if !defined(_M_AMD64)
stack.AddrPC.Offset = (*ctx).Eip;
stack.AddrPC.Mode = AddrModeFlat;
stack.AddrStack.Offset = (*ctx).Esp;
stack.AddrStack.Mode = AddrModeFlat;
stack.AddrFrame.Offset = (*ctx).Ebp;
stack.AddrFrame.Mode = AddrModeFlat;
#endif
SymInitialize( process, NULL, TRUE ); //load symbols
std::unique_ptr<IMAGEHLP_LINE64> line(new IMAGEHLP_LINE64);
line->SizeOfStruct = sizeof(IMAGEHLP_LINE64);
for( frame = 0; ; frame++ )
{
//get next call from stack
result = StackWalk64
(
#if defined(_M_AMD64)
IMAGE_FILE_MACHINE_AMD64,
#else
IMAGE_FILE_MACHINE_I386,
#endif
process,
thread,
&stack,
ctx,
NULL,
SymFunctionTableAccess64,
SymGetModuleBase64,
NULL
);
if( !result )
break;
//get symbol name for address
pSymbol->SizeOfStruct = sizeof(SYMBOL_INFO);
pSymbol->MaxNameLen = MAX_SYM_NAME;
SymFromAddr(process, ( ULONG64 )stack.AddrPC.Offset, &displacement, pSymbol);
//try to get line
if (SymGetLineFromAddr64(process, stack.AddrPC.Offset, &disp, line.get()))
{
printf("\tat %s in %s: line: %lu: address: 0x%0X\n", pSymbol->Name, line->FileName, line->LineNumber, pSymbol->Address);
}
else
{
//failed to get line
printf("\tat %s, address 0x%0X.\n", pSymbol->Name, pSymbol->Address);
hModule = NULL;
lstrcpyA(module,"");
GetModuleHandleEx(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS | GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT,
(LPCTSTR)(stack.AddrPC.Offset), &hModule);
//at least print module name
if(hModule != NULL)GetModuleFileNameA(hModule,module,MaxNameLen);
printf ("in %s\n",module);
}
}
}
// The exception filter function:
LONG WINAPI ExpFilter(EXCEPTION_POINTERS* ex)
{
// we only want this active on the Jenkins tinderboxen
printf("*** Exception 0x%x occured ***\n\n",ex->ExceptionRecord->ExceptionCode);
printStack(ex->ContextRecord);
return EXCEPTION_EXECUTE_HANDLER;
}
SAL_IMPLEMENT_MAIN()
{
bool ok = false;
// This magic kind of Windows-specific exception handling has to be in it's own function
// because it cannot be in a function that has objects with destructors.
__try
{
ok = main2();
}
__except (ExpFilter(GetExceptionInformation()))
{
}
return ok ? EXIT_SUCCESS : EXIT_FAILURE;
}
#else
SAL_IMPLEMENT_MAIN()
{
bool ok = false;
try
{
ok = main2();
}
catch (const std::exception& e)
{
SAL_WARN("vcl.app", "Fatal exception: " << e.what());
}
reportResourceUsage(path);
return ok ? EXIT_SUCCESS : EXIT_FAILURE;
}
#endif
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */