#include "Base/Log.h"
#include "Base/LogFile.h"
#include "Common/Runtime.h"
#include "Exception/ExceptionCApi.h"
#ifdef __APPLE__
#include "Exception/CFException.h"
#endif
#include "ExceptionManager.inline.h"
#include "Mutator/Mutator.h"
#include "ObjectModel/MObject.h"
#include "UnwindStack/MangleNameHelper.h"
#include "Heap/Collector/CollectorResources.h"
#include "Heap/Collector/GcRequest.h"
#include "Inspector/CjHeapData.h"
namespace MapleRuntime {
std::mutex ExceptionManager::gUncaughtExceptionHandlerMtx;
#if defined(__OHOS__) && (__OHOS__ == 1)
std::mutex ExceptionManager::gEventMtx;
CJEventReportInfo ExceptionManager::eventReportHandler = {nullptr, nullptr};
ExceptionCallbackFunc ExceptionManager::exceptionCallback = nullptr;
void ExceptionManager::RegisterExceptionCallback(ExceptionCallbackFunc callback)
{
exceptionCallback = callback;
}
ExceptionCallbackFunc ExceptionManager::GetExceptionCallback()
{
return exceptionCallback;
}
#endif
void ExceptionManager::OutOfMemory()
{
ExceptionWrapper& eWrapper = Mutator::GetMutator()->GetExceptionWrapper();
if (!eWrapper.IsThrowingOOME()) {
eWrapper.SetThrowingOOME(true);
eWrapper.SetFatalException(false);
eWrapper.SetExceptionType(ExceptionWrapper::ExceptionType::OUT_OF_MEMORY);
const char* env = std::getenv("cjHeapDumpOnOOM");
CString s = CString(env).RemoveBlankSpace();
env = s.Str();
if (env && !strcmp(env, "on")) {
#if defined(__OHOS__) && (__OHOS__ == 1)
LOG(RTLOG_INFO, "Check cjHeapDumpLog environment variable to trigger oom file dump event");
const char* cjHeapDumpLogEnv = std::getenv("cjHeapDumpLog");
if (cjHeapDumpLogEnv == nullptr || strlen(cjHeapDumpLogEnv) == 0) {
LOG(RTLOG_INFO, "prepare to report OOM FILEDUMPTASK");
const char* domain = "FRAMEWORK";
const char* event = "ARK_STATS_DUMP";
std::map<std::string, std::string> params;
params["TYPE"] = "hidumper";
params["FILE_TYPE"] = "HEAP_DUMP";
params["DUMP_REASON"] = "OOM";
params["DUMP_LOG_PATH"] = "EMPTY";
TriggerEventReport(domain, event, HiSysEventType::FAULT, params);
LOG(RTLOG_ERROR, "finish reporting OOM FILEDUMPTASK");
LOG(RTLOG_INFO, "start sleep 5s to wait filedump event to heapdump");
std::this_thread::sleep_for(std::chrono::seconds(5));
} else {
LOG(RTLOG_INFO, "start child process for OOM heap dump");
pid_t childPid = CjHeapData::ForkAndDumpHeap(-1, true);
if (childPid < 0) {
LOG(RTLOG_ERROR, "Failed to start child process for OOM heap dump");
} else {
LOG(RTLOG_INFO, "start sleep 5s to wait child process finishing OOM heapdump");
std::this_thread::sleep_for(std::chrono::seconds(5));
}
}
#else
Heap::GetHeap().GetCollectorResources().RequestHeapDump(GCTask::TaskType::TASK_TYPE_DUMP_HEAP_OOM);
#endif
}
#if defined(__OHOS__) && (__OHOS__ == 1)
const char* domain = "FRAMEWORK";
const char* event = "CJ_OOM";
Heap& heap = Heap::GetHeap();
size_t activeMemory = heap.GetCurrentCapacity();
size_t limitSize = heap.GetMaxCapacity();
LOG(RTLOG_INFO, "OOM: activeMemory=%zu, limitSize=%zu", activeMemory, limitSize);
std::map<std::string, std::string> params;
params["LIMIT_SIZE"] = std::to_string(limitSize);
params["ACTIVE_MEMORY"] = std::to_string(activeMemory);
params["TYPE"] = "OOMDump";
params["EVENT_CONFIG"] = "event";
TriggerEventReport(domain, event, HiSysEventType::FAULT, params);
LOG(RTLOG_INFO, "OOM event reporting finished");
#endif
ThrowImplicitException(OOM);
} else {
std::vector<uint64_t>& liteFrameInfos = eWrapper.GetLiteFrameInfos();
liteFrameInfos.clear();
StackManager::RecordLiteFrameInfos(liteFrameInfos);
ThrowImplicitException(OOMR);
}
eWrapper.SetThrowingOOME(false);
}
MRT_OPTIONAL_DISABLE_TAIL_CALL
void ExceptionManager::StackOverflow(uint32_t adjustedSize __attribute__((unused)), void* ip)
{
Mutator* mutator = Mutator::GetMutator();
if (mutator == nullptr) {
return;
}
mutator->StackGuardExpand();
ExceptionWrapper& eWrapper = mutator->GetExceptionWrapper();
eWrapper.SetThrowingSOFFramePc(ip);
eWrapper.SetFatalException(true);
eWrapper.SetExceptionType(ExceptionWrapper::ExceptionType::STACK_OVERFLOW);
#if defined(__x86_64__)
if (CangjieRuntime::stackGrowConfig == StackGrowConfig::STACK_GROW_ON) {
eWrapper.SetAdjustedStackSize(adjustedSize);
} else {
eWrapper.SetAdjustedStackSize(COMPENSATE_SIZE);
}
#elif defined(__aarch64__) || defined(__arm__)
eWrapper.SetAdjustedStackSize(adjustedSize);
#endif
ThrowImplicitException(SOF);
MRT_ThrowPendingException();
}
MRT_OPTIONAL_DISABLE_TAIL_CALL void ExceptionManager::IncompatiblePackageExpection(CString msg)
{
ExceptionWrapper& eWrapper = Mutator::GetMutator()->GetExceptionWrapper();
eWrapper.SetExceptionType(ExceptionWrapper::ExceptionType::INCOMPATIBILE_PACKAGE);
eWrapper.SetExceptionMessage(msg.Str(), strlen(msg.Str()));
ExceptionRaiser func = Runtime::Current().GetExceptionManager().GetExceptionRaiser();
if (func == nullptr) {
LOG(RTLOG_FATAL, "Cangjie initialization failed, implicit exception is nullptr.");
}
uintptr_t threadData = MapleRuntime::MRT_GetThreadLocalData();
ExecuteCangjieStub(reinterpret_cast<void*>(static_cast<intptr_t>(INCOMP)), reinterpret_cast<void*>(msg.GetStr()),
0, reinterpret_cast<void*>(func), reinterpret_cast<void*>(threadData), 0);
MRT_ThrowPendingException();
}
#if defined(__OHOS__) && (__OHOS__ == 1)
void ExceptionManager::RegisterUncaughtExceptionHandler(const CJUncaughtExceptionInfo& handler)
{
std::lock_guard<std::mutex> lock(gUncaughtExceptionHandlerMtx);
uncaughtExceptionHandler = handler;
}
void ExceptionManager::RegisterEventHandler(const CJEventReportInfo& handler)
{
std::lock_guard<std::mutex> lock(gEventMtx);
eventReportHandler = handler;
}
void ExceptionManager::TriggerEventReport(const char* domain, const char* event, size_t hiSysEventType,
const std::map<std::string, std::string>& params)
{
std::lock_guard<std::mutex> lock(gEventMtx);
if (eventReportHandler.reportInfoTask != nullptr) {
eventReportHandler.reportInfoTask(domain, event, hiSysEventType, params);
}
}
#endif
#ifdef __IOS__
void ExceptionManager::DefaultUncaughtTask(const char* sunmary, const CJErrorObject errorObj)
{
(void)sunmary;
(void)errorObj;
abort();
}
#endif
#if defined(__ANDROID__)
void ExceptionManager::AndroidDefaultUncaughtTask(const char* summary, const CJErrorObject errorObj)
{
(void)summary;
(void)errorObj;
abort();
}
#endif
void ExceptionManager::DumpException()
{
ExceptionWrapper& eWrapper = Mutator::GetMutator()->GetExceptionWrapper();
std::vector<uint64_t>& liteFrameInfos = eWrapper.GetLiteFrameInfos();
LOG(RTLOG_ERROR, "An exception has occurred:\n");
MObject* exceptionObject = eWrapper.GetExceptionRef();
MangleNameHelper helper(exceptionObject->GetTypeInfo()->GetName());
CString clsName(helper.GetSimpleClassName());
std::vector<StackTraceElement> stackTrace;
StackManager::GetStackTraceByLiteFrameInfos(liteFrameInfos, stackTrace);
if (stackTrace.empty()) {
LOG(RTLOG_ERROR, "Stacetrace is empty.");
}
std::lock_guard<std::mutex> lock(gUncaughtExceptionHandlerMtx);
if (Runtime::Current().GetExceptionManager().GetUncaughtExceptionHandler().uncaughtTask) {
#if defined(__OHOS__) && (__OHOS__ == 1) || (__APPLE__) || (__ANDROID__)
const char* summary = "Uncaught exception was found.";
CString exceptionMsg(eWrapper.GetExceptionMessage());
#if defined(__APPLE__) && (__IOS__ == 1)
CFException::ReportBacktraceToIosIpsLog(eWrapper);
#endif
#if defined(__APPLE__) && (__IOS__ == 1) || (__ANDROID__)
LOG(RTLOG_ERROR, summary);
LOG(RTLOG_ERROR, exceptionMsg.Str());
#endif
CString exceptionStack;
const int strLen = 10;
char* str = static_cast<char*>(NativeAllocator::NativeAlloc(strLen * sizeof(char)));
if (str == nullptr) {
LOG(RTLOG_FATAL, "Alloca memory failed when Cangjie dump uncaught exception.");
}
for (const auto& ste : stackTrace) {
const int strLen = 10;
char* str = static_cast<char*>(malloc(strLen * sizeof(char)));
sprintf_s(str, strLen, "%ld", ste.lineNumber);
exceptionStack += "\t at ";
exceptionStack += ste.className.Str();
exceptionStack += (ste.className.Length() > 0 ? "." : "");
exceptionStack += ste.methodName.Str();
exceptionStack += "(";
exceptionStack += ste.fileName.Str();
exceptionStack += +":";
exceptionStack += str;
exceptionStack += ")\n";
#if defined(__APPLE__) || (__ANDROID__)
LOG(RTLOG_ERROR, exceptionStack.Str());
exceptionStack = "";
#endif
}
CJErrorObject errObj = {clsName.Str(), exceptionMsg.Str(), exceptionStack.Str()};
eWrapper.ClearInfo();
Runtime::Current().GetExceptionManager().GetUncaughtExceptionHandler().uncaughtTask(summary, errObj);
#endif
} else {
#ifdef __APPLE__
PRINT_ERROR("%s", clsName.Str());
#endif
LOG(RTLOG_ERROR, clsName.Str());
if (eWrapper.GetExceptionMessage() != nullptr && eWrapper.GetExceptionMessageLength() != 0) {
const char* linkStr = ": ";
#ifdef __APPLE__
PRINT_ERROR("%s", linkStr);
PRINT_ERROR("%s", eWrapper.GetExceptionMessage());
#endif
LOG(RTLOG_ERROR, linkStr);
LOG(RTLOG_ERROR, eWrapper.GetExceptionMessage());
}
#ifdef __APPLE__
PRINT_ERROR("\n");
#endif
LOG(RTLOG_ERROR, "\n");
constexpr int32_t frameInfoPairLen = 3;
SofStackFlag sofFoldedFlag = SofStackFlag::NOT_FOLDED;
if (liteFrameInfos.size() % frameInfoPairLen == 1) {
sofFoldedFlag = SofStackFlag(liteFrameInfos.back());
liteFrameInfos.pop_back();
}
if (sofFoldedFlag == SofStackFlag::TOP_FOLDED) {
#ifdef __APPLE__
PRINT_ERROR("\t ... Some frames are not displayed ...\n");
#endif
LOG(RTLOG_ERROR, "\t ... Some frames are not displayed ...\n");
}
for (const auto& ste : stackTrace) {
#ifdef __APPLE__
PRINT_ERROR("\t at %s%s%s(%s:%lld)\n", ste.className.Str(), ste.className.Length() > 0 ? "." : "",
ste.methodName.Str(), ste.fileName.Str(), ste.lineNumber);
#endif
LOG(RTLOG_ERROR, "\t at %s%s%s(%s:%ld)\n", ste.className.Str(), ste.className.Length() > 0 ? "." : "",
ste.methodName.Str(), ste.fileName.Str(), ste.lineNumber);
}
if (sofFoldedFlag == SofStackFlag::BOTTOM_FOLDED) {
#ifdef __APPLE__
PRINT_ERROR("\t ... Some frames are not displayed ...\n");
#endif
LOG(RTLOG_ERROR, "\t ... Some frames are not displayed ...\n");
}
#if defined(__OHOS__) && (__OHOS__ == 1)
LOG(RTLOG_FATAL, "Uncaught exception: %s. Terminate required.", clsName.Str());
#endif
}
}
void ExceptionManager::ThrowImplicitException(ImplicitExceptionType type)
{
MRT_SetStackGrow(false);
ExceptionWrapper& eWrapper = Mutator::GetMutator()->GetExceptionWrapper();
eWrapper.SetExceptionMessage(nullptr, 0);
ExceptionRaiser func = Runtime::Current().GetExceptionManager().GetExceptionRaiser();
if (func == nullptr) {
LOG(RTLOG_FATAL, "Cangjie initialization failed, implicit exception is nullptr.");
}
uintptr_t threadData = MapleRuntime::MRT_GetThreadLocalData();
ExecuteCangjieStub(reinterpret_cast<void*>(static_cast<intptr_t>(type)), 0, 0, reinterpret_cast<void*>(func),
reinterpret_cast<void*>(threadData), 0);
}
#ifndef _WIN64
static bool HandleAbortSignal(int sig, siginfo_t* info, void* context)
{
LOG(RTLOG_ERROR, "Two pending exceptions are thrown when a C function calls Cangjie function. Program will abort.");
return true;
}
static void InstallExceptionAbortHandler()
{
sigset_t mask;
CHECK_SIGNAL_CALL(sigemptyset, (&mask), "sigemptyset failed");
SignalAction sa;
sa.saSignalAction = HandleAbortSignal;
sa.scMask = mask;
sa.scFlags = SA_SIGINFO | SA_ONSTACK;
AddHandlerToSignalStack(SIGABRT, &sa);
}
#endif
void ExceptionManager::ThrowException(const ExceptionRef& exception)
{
ScopedEntryTrace trace("CJRT_THROW_EXCEPTION");
MRT_SetStackGrow(true);
ExceptionWrapper& mExceptionWrapper = Mutator::GetMutator()->GetExceptionWrapper();
#if defined(MRT_DEBUG) && (MRT_DEBUG == 1)
DLOG(EXCEPTION, "start throw exception");
DLOG(EXCEPTION, "Struct ExceptionWrapper");
DLOG(EXCEPTION, " ExceptionRef\t:\t%p", mExceptionWrapper.GetExceptionRef());
DLOG(EXCEPTION, " isCaught\t:\t%d", mExceptionWrapper.IsCaught());
DLOG(EXCEPTION, " typeIndex\t:\t%lu", mExceptionWrapper.GetTypeIndex());
DLOG(EXCEPTION, " throwingOOME\t:\t%d", mExceptionWrapper.IsThrowingOOME());
DLOG(EXCEPTION, " fatalException\t:\t%d", mExceptionWrapper.IsFatalException());
DLOG(EXCEPTION, " ExceptionType\t:\t%s", mExceptionWrapper.GetExceptionTypeName());
#endif
mExceptionWrapper.Reset();
if (mExceptionWrapper.GetExceptionRef() != nullptr) {
#ifndef _WIN64
InstallExceptionAbortHandler();
#endif
LOG(RTLOG_FATAL, "ThrowException fail, abort.");
}
mExceptionWrapper.SetExceptionRef(exception);
ExceptionHandling eh(mExceptionWrapper, nullptr);
}
void* ExceptionManager::BeginCatch(ExceptionWrapper* mExceptionWrapper __attribute__((unused)))
{
ScopedEntryTrace trace("CJRT_CATCH_EXCEPTION");
Mutator* mutator = Mutator::GetMutator();
ExceptionWrapper& eWrapper = mutator->GetExceptionWrapper();
if (eWrapper.IsThrowingSOFE()) {
mutator->StackGuardRecover();
eWrapper.SetThrowingSOFFramePc(nullptr);
eWrapper.SetAdjustedStackSize(0);
}
eWrapper.SetFatalException(false);
eWrapper.SetExceptionType(ExceptionWrapper::ExceptionType::UNKNOWN);
ExceptionRef thrown = eWrapper.GetExceptionRef();
eWrapper.SetExceptionRef(nullptr);
return thrown;
}
}