blob: cb6b25b83cb3e5a2e6cc08bd64888a95518594b0 [file]
//-------------------------------------------------------------------------------------------------------
// Copyright (C) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
//-------------------------------------------------------------------------------------------------------
#include "CommonExceptionsPch.h"
#ifndef USING_PAL_STDLIB
// === C Runtime Header Files ===
#pragma warning(push)
#pragma warning(disable: 4995) /* 'function': name was marked as #pragma deprecated */
#include <strsafe.h>
#pragma warning(pop)
#endif
#include "StackOverflowException.h"
#include "AsmJsParseException.h"
#include "OutOfMemoryException.h"
#include "NotImplementedException.h"
// Header files required before including ConfigFlagsTable.h
#include "EnumHelp.h"
#include "Common/MathUtil.h"
#include "Core/AllocSizeMath.h"
#include "Core/FaultInjection.h"
#include "Core/BasePtr.h"
#include "Core/AutoFile.h"
#include "Core/Output.h"
// Memory Management
namespace Memory {
class ArenaAllocator;
}
using namespace Memory;
#include "Memory/Allocator.h"
#include "Memory/HeapAllocator.h"
#include "Memory/RecyclerPointers.h"
// Data structure
#include "DataStructures/Comparer.h"
#include "DataStructures/SizePolicy.h"
#include "DataStructures/SList.h"
#include "DataStructures/KeyValuePair.h"
#include "DataStructures/DefaultContainerLockPolicy.h"
#include "DataStructures/BaseDictionary.h"
#include "Core/ConfigFlagsTable.h"
#include "Core/StackBackTrace.h"
#ifdef GENERATE_DUMP
// dbghelp.h is not clean with warning 4091
#pragma warning(push)
#pragma warning(disable: 4091) /* warning C4091: 'typedef ': ignored on left of '' when no variable is declared */
#include <dbghelp.h>
#pragma warning(pop)
#endif // GENERATE_DUMP
extern "C"{
BOOLEAN IsMessageBoxWPresent();
}
namespace Js {
#if defined(GENERATE_DUMP) && defined(STACK_BACK_TRACE)
THREAD_LOCAL StackBackTrace * Throw::stackBackTrace = nullptr;
#endif
void Throw::FatalInternalError()
{
int scenario = 2;
ReportFatalException(NULL, E_FAIL, Fatal_Internal_Error, scenario);
}
void Throw::FatalInternalErrorEx(int scenario)
{
ReportFatalException(NULL, E_FAIL, Fatal_Internal_Error, scenario);
}
void Throw::FatalProjectionError()
{
RaiseException((DWORD)DBG_TERMINATE_PROCESS, EXCEPTION_NONCONTINUABLE, 0, NULL);
}
#if ENABLE_JS_REENTRANCY_CHECK
void Throw::FatalJsReentrancyError()
{
AssertMsg(false, "Js reentrancy error!!");
ReportFatalException(NULL, E_UNEXPECTED, Fatal_JsReentrancy_Error, 0);
}
#endif
void Throw::InternalError()
{
AssertOrFailFastMsg(false, "Internal error!!");
}
void Throw::OutOfMemory()
{
#ifdef ENABLE_DEBUG_CONFIG_OPTIONS
if (CONFIG_FLAG(PrintSystemException))
{
Output::Print(_u("SystemException: OutOfMemory\n"));
Output::Flush();
}
#endif
if (JsUtil::ExternalApi::RaiseOutOfMemoryIfScriptActive())
{
AssertMsg(false, "We shouldn't be here");
}
throw OutOfMemoryException();
}
void Throw::CheckAndThrowOutOfMemory(BOOLEAN status)
{
if (!status)
{
OutOfMemory();
}
}
void Throw::StackOverflow(ScriptContext *scriptContext, PVOID returnAddress)
{
#ifdef ENABLE_DEBUG_CONFIG_OPTIONS
if (CONFIG_FLAG(PrintSystemException))
{
Output::Print(_u("SystemException: StackOverflow\n"));
Output::Flush();
}
#endif
if (JsUtil::ExternalApi::RaiseStackOverflowIfScriptActive(scriptContext, returnAddress))
{
AssertMsg(false, "We shouldn't be here");
}
throw StackOverflowException();
}
void Throw::NotImplemented()
{
AssertMsg(false, "This functionality is not yet implemented");
throw NotImplementedException();
}
#ifdef GENERATE_DUMP
CriticalSection Throw::csGenerateDump;
void Throw::GenerateDump(LPCWSTR filePath, bool terminate, bool needLock)
{
__try
{
if (terminate)
{
RaiseException((DWORD)DBG_TERMINATE_PROCESS, EXCEPTION_NONCONTINUABLE, 0, NULL);
}
else
{
RaiseException(0, 0, 0, NULL);
}
}
__except(Throw::GenerateDump(GetExceptionInformation(), filePath,
terminate? EXCEPTION_CONTINUE_SEARCH : EXCEPTION_EXECUTE_HANDLER), needLock)
{
// we don't do anything interesting in this handler
}
}
void Throw::GenerateDumpForAssert(LPCWSTR filePath)
{
__try
{
RaiseException(STATUS_ASSERTION_FAILURE, EXCEPTION_NONCONTINUABLE, 0, NULL);
}
__except (Throw::GenerateDump(GetExceptionInformation(), filePath, EXCEPTION_CONTINUE_SEARCH), false)
{
// no-op
}
}
int Throw::GenerateDump(PEXCEPTION_POINTERS exceptInfo, LPCWSTR filePath, int ret, bool needLock)
{
WCHAR tempFilePath[MAX_PATH];
WCHAR tempFileName[MAX_PATH];
HANDLE hTempFile;
DWORD retVal;
if (filePath == NULL)
{
retVal = GetTempPath(MAX_PATH, tempFilePath);
if (retVal > MAX_PATH || (retVal == 0))
{
return ret;
}
filePath = tempFilePath;
}
StringCchPrintf(tempFileName, _countof(tempFileName), _u("%s\\CH_%u_%u.dmp"), filePath, GetCurrentProcessId(), GetCurrentThreadId());
Output::Print(_u("dump filename %s \n"), tempFileName);
Output::Flush();
hTempFile = CreateFile(tempFileName, GENERIC_READ | GENERIC_WRITE, 0, NULL, CREATE_ALWAYS,
FILE_ATTRIBUTE_NORMAL, NULL);
if (hTempFile == INVALID_HANDLE_VALUE)
{
return GetLastError();
}
MINIDUMP_EXCEPTION_INFORMATION dumpExceptInfo;
dumpExceptInfo.ThreadId = GetCurrentThreadId();
dumpExceptInfo.ExceptionPointers = exceptInfo;
dumpExceptInfo.ClientPointers = FALSE;
{
MINIDUMP_TYPE dumpType = static_cast<MINIDUMP_TYPE>(MiniDumpWithDataSegs | MiniDumpWithPrivateReadWriteMemory);
// Generating full dump for the TE process (reason : it contains both managed and native memory)
if (CONFIG_FLAG(FullMemoryDump))
{
dumpType = static_cast<MINIDUMP_TYPE>(dumpType | MiniDumpWithFullMemory);
}
BOOL dumpGenerated = false;
if (needLock)
{
// the critical section might have been destructed at process shutdown time. At that time we don't need
// to lock.
AutoCriticalSection autocs(&csGenerateDump);
dumpGenerated = MiniDumpWriteDump(GetCurrentProcess(),
GetCurrentProcessId(),
hTempFile,
dumpType,
&dumpExceptInfo,
NULL,
NULL);
}
else
{
dumpGenerated = MiniDumpWriteDump(GetCurrentProcess(),
GetCurrentProcessId(),
hTempFile,
dumpType,
&dumpExceptInfo,
NULL,
NULL);
}
if (!dumpGenerated)
{
Output::Print(_u("Unable to write minidump (0x%08X)\n"), GetLastError());
Output::Flush();
}
}
FlushFileBuffers(hTempFile);
CloseHandle(hTempFile);
return ret;
}
#endif // GENERATE_DUMP
#if DBG
// After assert the program should terminate. Sometime we saw the program continue somehow
// log the existence of assert for debugging.
void Throw::LogAssert()
{
IsInAssert = true;
#if defined(GENERATE_DUMP) && defined(STACK_BACK_TRACE)
// This should be the last thing to happen in the process. Therefore, leaks are not an issue.
stackBackTrace = StackBackTrace::Capture(&NoCheckHeapAllocator::Instance, Throw::StackToSkip, Throw::StackTraceDepth);
#endif
}
#ifdef ENABLE_DEBUG_CONFIG_OPTIONS
#define CHAKRA_ASSERT_CAPTION _u("CHAKRA ASSERT")
#endif
bool Throw::ReportAssert(__in LPCSTR fileName, uint lineNumber, __in LPCSTR error, __in LPCSTR message)
{
#ifdef ENABLE_DEBUG_CONFIG_OPTIONS
if (Js::Configuration::Global.flags.IsEnabled(Js::AssertBreakFlag))
{
DebugBreak();
return false;
}
if (Js::Configuration::Global.flags.IsEnabled(Js::AssertIgnoreFlag))
{
return true;
}
#endif
if (AssertsToConsole)
{
fprintf(stderr, "ASSERTION %u: (%s, line %u) %s\n Failure: %s\n", GetCurrentProcessId(), fileName, lineNumber, message, error);
fflush(stderr);
#ifdef GENERATE_DUMP
// force dump if we have assert in jc.exe. check build only.
if (!Js::Configuration::Global.flags.IsEnabled(Js::DumpOnCrashFlag))
{
return false;
}
Throw::GenerateDumpForAssert(Js::Configuration::Global.flags.DumpOnCrash);
#else
return false;
#endif
}
// The following code is applicable only when we are hosted in an
// GUI environment
#if defined(ENABLE_DEBUG_CONFIG_OPTIONS) && defined(_WIN32)
// Then if DumpOncrashFlag is not specified it directly returns,
// otherwise if will raise a non-continuable exception, generate the dump and terminate the process.
// the popup message box might be useful when testing in IE
if (Js::Configuration::Global.flags.AssertPopUp && IsMessageBoxWPresent())
{
char16 buff[1024];
swprintf_s(buff, _countof(buff), _u("%S (%u)\n%S\n%S"), fileName, lineNumber, message, error);
buff[_countof(buff)-1] = 0;
int ret = MessageBox(nullptr, buff, CHAKRA_ASSERT_CAPTION, MB_ABORTRETRYIGNORE);
switch (ret)
{
case IDIGNORE:
return true;
case IDABORT:
Throw::FatalInternalError();
default:
return false;
}
}
#endif
return false;
}
#endif
} // namespace Js