#4315 Crash in GLTF uploader

Properly handle importer's crashes in general
master
Andrey Kleshchev 2025-07-01 23:14:24 +03:00 committed by Andrey Kleshchev
parent 230a8caa5d
commit 76dd9385f9
7 changed files with 110 additions and 4 deletions

View File

@ -149,7 +149,42 @@ LLModelLoader::~LLModelLoader()
void LLModelLoader::run()
{
mWarningsArray.clear();
doLoadModel();
try
{
doLoadModel();
}
// Model loader isn't mission critical, so we just log all exceptions
catch (const LLException& e)
{
LL_WARNS("THREAD") << "LLException in model loader: " << e.what() << "" << LL_ENDL;
LLSD args;
args["Message"] = "UnknownException";
args["FILENAME"] = mFilename;
args["EXCEPTION"] = e.what();
mWarningsArray.append(args);
setLoadState(ERROR_PARSING);
}
catch (const std::exception& e)
{
LL_WARNS() << "Exception in LLModelLoader::run: " << e.what() << LL_ENDL;
LLSD args;
args["Message"] = "UnknownException";
args["FILENAME"] = mFilename;
args["EXCEPTION"] = e.what();
mWarningsArray.append(args);
setLoadState(ERROR_PARSING);
}
catch (...)
{
LOG_UNHANDLED_EXCEPTION("LLModelLoader");
LLSD args;
args["Message"] = "UnknownException";
args["FILENAME"] = mFilename;
args["EXCEPTION"] = "Unknown exception";
mWarningsArray.append(args);
setLoadState(ERROR_PARSING);
}
// todo: we are inside of a thread, push this into main thread worker,
// not into doOnIdleOneTime that laks tread safety
doOnIdleOneTime(boost::bind(&LLModelLoader::loadModelCallback,this));

View File

@ -124,7 +124,32 @@ bool LLGLTFLoader::OpenFile(const std::string &filename)
std::string filename_lc(filename);
LLStringUtil::toLower(filename_lc);
mGltfLoaded = mGLTFAsset.load(filename, false);
try
{
mGltfLoaded = mGLTFAsset.load(filename, false);
}
catch (const std::exception& e)
{
LL_WARNS() << "Exception in LLModelLoader::run: " << e.what() << LL_ENDL;
LLSD args;
args["Message"] = "ParsingErrorException";
args["FILENAME"] = filename;
args["EXCEPTION"] = e.what();
mWarningsArray.append(args);
setLoadState(ERROR_PARSING);
return false;
}
catch (...)
{
LOG_UNHANDLED_EXCEPTION("LLGLTFLoader");
LLSD args;
args["Message"] = "ParsingErrorException";
args["FILENAME"] = filename;
args["EXCEPTION"] = "Unknown exception";
mWarningsArray.append(args);
setLoadState(ERROR_PARSING);
return false;
}
if (!mGltfLoaded)
{

View File

@ -5679,6 +5679,27 @@ void LLAppViewer::forceErrorThreadCrash()
thread->start();
}
void LLAppViewer::forceExceptionThreadCrash()
{
class LLCrashTestThread : public LLThread
{
public:
LLCrashTestThread() : LLThread("Crash logging test thread")
{
}
void run()
{
throw std::exception();
}
};
LL_WARNS() << "This is a deliberate exception in a thread" << LL_ENDL;
LLCrashTestThread* thread = new LLCrashTestThread();
thread->start();
}
void LLAppViewer::initMainloopTimeout(std::string_view state, F32 secs)
{
if (!mMainloopTimeout)

View File

@ -175,6 +175,7 @@ public:
virtual void forceErrorCoroprocedureCrash();
virtual void forceErrorWorkQueueCrash();
virtual void forceErrorThreadCrash();
virtual void forceExceptionThreadCrash();
// The list is found in app_settings/settings_files.xml
// but since they are used explicitly in code,

View File

@ -289,6 +289,7 @@ void force_error_coroutine_crash();
void force_error_coroprocedure_crash();
void force_error_work_queue_crash();
void force_error_thread_crash();
void force_exception_thread_crash();
void handle_force_delete();
void print_object_info();
@ -2663,6 +2664,15 @@ class LLAdvancedForceErrorThreadCrash : public view_listener_t
}
};
class LLAdvancedForceExceptionThreadCrash : public view_listener_t
{
bool handleEvent(const LLSD& userdata)
{
force_exception_thread_crash();
return true;
}
};
class LLAdvancedForceErrorDisconnectViewer : public view_listener_t
{
bool handleEvent(const LLSD& userdata)
@ -8696,6 +8706,11 @@ void force_error_thread_crash()
LLAppViewer::instance()->forceErrorThreadCrash();
}
void force_exception_thread_crash()
{
LLAppViewer::instance()->forceExceptionThreadCrash();
}
class LLToolsUseSelectionForGrid : public view_listener_t
{
bool handleEvent(const LLSD& userdata)
@ -9898,6 +9913,7 @@ void initialize_menus()
view_listener_t::addMenu(new LLAdvancedForceErrorCoroprocedureCrash(), "Advanced.ForceErrorCoroprocedureCrash");
view_listener_t::addMenu(new LLAdvancedForceErrorWorkQueueCrash(), "Advanced.ForceErrorWorkQueueCrash");
view_listener_t::addMenu(new LLAdvancedForceErrorThreadCrash(), "Advanced.ForceErrorThreadCrash");
view_listener_t::addMenu(new LLAdvancedForceExceptionThreadCrash(), "Advanced.ForceExceptionThreadCrash");
view_listener_t::addMenu(new LLAdvancedForceErrorDisconnectViewer(), "Advanced.ForceErrorDisconnectViewer");
// Advanced (toplevel)

View File

@ -61,6 +61,7 @@
<string name="ParsingErrorNoRoot">Document has no root</string>
<string name="ParsingErrorNoScene">Document has no visual_scene</string>
<string name="ParsingErrorPositionInvalidModel">Unable to process mesh without position data. Invalid model.</string>
<string name="UnknownException">Importer crashed while processing [FILENAME], if you encounter this and file is valid, please report the issue to Second Life Support. Exception: [EXCEPTION].</string>
<!-- GLTF specific messages -->
<string name="NoScenesFound">No scenes defined in GLTF file</string>
@ -80,6 +81,7 @@
<string name="ModelTooManySubmodels">Model [MODEL_NAME] contains [SUBMODEL_COUNT] generated mesh parts, parts were trimmed to [SUBMODEL_LIMIT]</string>
<string name="ParsingErrorMissingBuffer">Buffer is either missing or empty [BUFFER_NAME].</string>
<string name="ParsingErrorMissingBufferBin">Buffer is either missing or empty. Check presence of [BUFFER_URI] file.</string>
<string name="ParsingErrorException">Parser failed to process [FILENAME], file might be corrupt, incomplete or protected from reading. Exception: [EXCEPTION].</string>
<panel
follows="top|left"

View File

@ -2808,11 +2808,17 @@ function="World.EnvPreset"
function="Advanced.ForceErrorWorkQueueCrash" />
</menu_item_call>
<menu_item_call
label="Force a Crash in a Thread"
name="Force a Crash in a Thread">
label="Force an LLError Crash in a Thread"
name="Force an LLError Crash in a Thread">
<menu_item_call.on_click
function="Advanced.ForceErrorThreadCrash" />
</menu_item_call>
<menu_item_call
label="Force an Exception Crash in a Thread"
name="Force an Exception Crash in a Thread">
<menu_item_call.on_click
function="Advanced.ForceExceptionThreadCrash" />
</menu_item_call>
<menu_item_call
label="Force Disconnect Viewer"
name="Force Disconnect Viewer">