diff --git a/Engine/source/T3D/assets/SoundAsset.h b/Engine/source/T3D/assets/SoundAsset.h index f867e0e4f..8692e96f4 100644 --- a/Engine/source/T3D/assets/SoundAsset.h +++ b/Engine/source/T3D/assets/SoundAsset.h @@ -516,7 +516,12 @@ if (m##name##AssetId[index] != StringTable->EmptyString())\ else Con::warnf("Warning: %s::LOAD_SOUNDASSET_ARRAY(%s[%i])-%s", mClassName, m##name##AssetId[index], index, ImageAsset::getAssetErrstrn(assetState).c_str());\ } -#define assetEnumNameConcat(x,suff)(new std::string( x + std::string(#suff)))->c_str() +#define assetEnumNameConcat(x, suff) ([](const char* base) { \ + String result = String(base) + #suff; \ + char* ret = Con::getReturnBuffer(result.length() + 1); \ + dStrcpy(ret, result.c_str(), result.length() + 1); \ + return ret; \ + })(x) #define INITPERSISTFIELD_SOUNDASSET_ENUMED(name, enumType, maxValue, consoleClass, docs) \ for (U32 i = 0; i < maxValue; i++)\ diff --git a/Engine/source/app/mainLoop.cpp b/Engine/source/app/mainLoop.cpp index 2acb64845..0d3c77c41 100644 --- a/Engine/source/app/mainLoop.cpp +++ b/Engine/source/app/mainLoop.cpp @@ -114,29 +114,6 @@ namespace engineAPI } - -// The following are some tricks to make the memory leak checker run after global -// dtors have executed by placing some code in the termination segments. - -#if defined( TORQUE_DEBUG ) && !defined( TORQUE_DISABLE_MEMORY_MANAGER ) - - #ifdef TORQUE_COMPILER_VISUALC - # pragma data_seg( ".CRT$XTU" ) - - static void* sCheckMemBeforeTermination = &Memory::ensureAllFreed; - - # pragma data_seg() - #elif defined( TORQUE_COMPILER_GCC ) - - __attribute__ ( ( destructor ) ) static void _ensureAllFreed() - { - Memory::ensureAllFreed(); - } - - #endif - -#endif - // Process a time event and update all sub-processes void processTimeEvent(S32 elapsedTime) { @@ -216,10 +193,6 @@ void StandardMainLoop::init() gStartupTimer = PlatformTimer::create(); #endif - #ifdef TORQUE_DEBUG_GUARD - Memory::flagCurrentAllocs( Memory::FLAG_Global ); - #endif - Platform::setMathControlStateKnown(); // Asserts should be created FIRST @@ -327,10 +300,6 @@ void StandardMainLoop::init() // Hook in for UDP notification Net::getPacketReceiveEvent().notify(GNet, &NetInterface::processPacketReceiveEvent); - - #ifdef TORQUE_DEBUG_GUARD - Memory::flagCurrentAllocs( Memory::FLAG_Static ); - #endif } void StandardMainLoop::shutdown() @@ -378,9 +347,6 @@ void StandardMainLoop::shutdown() // asserts should be destroyed LAST PlatformAssert::destroy(); -#if defined( TORQUE_DEBUG ) && !defined( TORQUE_DISABLE_MEMORY_MANAGER ) - Memory::validate(); -#endif } void StandardMainLoop::preShutdown() diff --git a/Engine/source/assets/assetManager.cpp b/Engine/source/assets/assetManager.cpp index 0b166a238..eef113ae5 100644 --- a/Engine/source/assets/assetManager.cpp +++ b/Engine/source/assets/assetManager.cpp @@ -119,6 +119,18 @@ void AssetManager::onRemove() mAssetTagsManifest->deleteObject(); } + purgeAssets(); + + for (auto itr = mDeclaredAssets.begin(); itr != mDeclaredAssets.end(); ++itr) + { + delete itr->value; + } + mDeclaredAssets.clear(); + + // Clear dependency graphs + mAssetDependsOn.clear(); + mAssetIsDependedOn.clear(); + // Call parent. Parent::onRemove(); } diff --git a/Engine/source/console/console.cpp b/Engine/source/console/console.cpp index 863626b62..015ee7540 100644 --- a/Engine/source/console/console.cpp +++ b/Engine/source/console/console.cpp @@ -71,7 +71,7 @@ char* ConsoleValue::convertToBuffer() const const char* ConsoleValue::getConsoleData() const { - return Con::getData(ct->consoleType, ct->dataPtr, 0, ct->enumTable); + return Con::getData(type, dataPtr, 0, enumTable); } ConsoleDocFragment* ConsoleDocFragment::smFirst; @@ -427,20 +427,6 @@ void init() smConsoleInput.notify(postConsoleInput); } -//-------------------------------------- - -void shutdown() -{ - AssertFatal(active == true, "Con::shutdown should only be called once."); - active = false; - - smConsoleInput.remove(postConsoleInput); - - consoleLogFile.close(); - Namespace::shutdown(); - AbstractClassRep::shutdown(); - Compiler::freeConsoleParserList(); -} bool isActive() { @@ -1201,10 +1187,10 @@ ConsoleValue _internalExecute(S32 argc, ConsoleValue argv[]) warnf(ConsoleLogEntry::Script, "%s: Unknown command.", funcName); STR.clearFunctionOffset(); - return std::move(ConsoleValue()); + return (ConsoleValue()); } - return std::move(ent->execute(argc, argv, NULL)); + return (ent->execute(argc, argv, NULL)); } ConsoleValue execute(S32 argc, ConsoleValue argv[]) @@ -1234,7 +1220,7 @@ ConsoleValue execute(S32 argc, const char *argv[]) ConsoleStackFrameSaver stackSaver; stackSaver.save(); StringArrayToConsoleValueWrapper args(argc, argv); - return std::move(execute(args.count(), args)); + return (execute(args.count(), args)); } //------------------------------------------------------------------------------ @@ -1243,12 +1229,12 @@ ConsoleValue execute(S32 argc, const char *argv[]) static ConsoleValue _internalExecute(SimObject *object, S32 argc, ConsoleValue argv[], bool thisCallOnly) { if (object == NULL) - return std::move(ConsoleValue()); + return (ConsoleValue()); if(argc < 2) { STR.clearFunctionOffset(); - return std::move(ConsoleValue()); + return (ConsoleValue()); } // [neo, 10/05/2007 - #3010] @@ -1276,7 +1262,7 @@ static ConsoleValue _internalExecute(SimObject *object, S32 argc, ConsoleValue a { //warnf(ConsoleLogEntry::Script, "%s: undefined for object '%s' - id %d", funcName, object->getName(), object->getId()); STR.clearFunctionOffset(); - return std::move(ConsoleValue()); + return (ConsoleValue()); } const char* oldIdent = dStrdup(argv[1].getString()); @@ -1284,7 +1270,7 @@ static ConsoleValue _internalExecute(SimObject *object, S32 argc, ConsoleValue a // Twiddle %this argument argv[1].setInt(ident); - ConsoleValue ret = std::move(ent->execute(argc, argv, object)); + ConsoleValue ret = (ent->execute(argc, argv, object)); // Twiddle it back argv[1].setString(oldIdent); @@ -1295,7 +1281,7 @@ static ConsoleValue _internalExecute(SimObject *object, S32 argc, ConsoleValue a warnf(ConsoleLogEntry::Script, "Con::execute - %d has no namespace: %s", object->getId(), funcName); STR.clearFunctionOffset(); - return std::move(ConsoleValue()); + return (ConsoleValue()); } ConsoleValue execute(SimObject *object, S32 argc, ConsoleValue argv[], bool thisCallOnly) @@ -1303,7 +1289,7 @@ ConsoleValue execute(SimObject *object, S32 argc, ConsoleValue argv[], bool this if(argc < 2) { STR.clearFunctionOffset(); - return std::move(ConsoleValue()); + return (ConsoleValue()); } ConsoleStackFrameSaver stackSaver; @@ -1313,7 +1299,7 @@ ConsoleValue execute(SimObject *object, S32 argc, ConsoleValue argv[], bool this { if (isMainThread()) { - return std::move(_internalExecute(object, argc, argv, thisCallOnly)); + return (_internalExecute(object, argc, argv, thisCallOnly)); } else { @@ -1325,7 +1311,7 @@ ConsoleValue execute(SimObject *object, S32 argc, ConsoleValue argv[], bool this warnf(ConsoleLogEntry::Script, "Con::execute - %d has no namespace: %s", object->getId(), argv[0].getString()); STR.clearFunctionOffset(); - return std::move(ConsoleValue()); + return (ConsoleValue()); } ConsoleValue execute(SimObject *object, S32 argc, const char *argv[], bool thisCallOnly) @@ -1333,7 +1319,7 @@ ConsoleValue execute(SimObject *object, S32 argc, const char *argv[], bool thisC ConsoleStackFrameSaver stackSaver; stackSaver.save(); StringArrayToConsoleValueWrapper args(argc, argv); - return std::move(execute(object, args.count(), args, thisCallOnly)); + return (execute(object, args.count(), args, thisCallOnly)); } inline ConsoleValue _executef(SimObject *obj, S32 checkArgc, S32 argc, ConsoleValue *argv) @@ -1341,7 +1327,7 @@ inline ConsoleValue _executef(SimObject *obj, S32 checkArgc, S32 argc, ConsoleVa const U32 maxArg = 12; AssertFatal(checkArgc == argc, "Incorrect arg count passed to Con::executef(SimObject*)"); AssertFatal(argc <= maxArg - 1, "Too many args passed to Con::_executef(SimObject*). Please update the function to handle more."); - return std::move(execute(obj, argc, argv)); + return (execute(obj, argc, argv)); } //------------------------------------------------------------------------------ @@ -1350,7 +1336,7 @@ inline ConsoleValue _executef(S32 checkArgc, S32 argc, ConsoleValue *argv) const U32 maxArg = 10; AssertFatal(checkArgc == argc, "Incorrect arg count passed to Con::executef()"); AssertFatal(argc <= maxArg, "Too many args passed to Con::_executef(). Please update the function to handle more."); - return std::move(execute(argc, argv)); + return (execute(argc, argv)); } //------------------------------------------------------------------------------ @@ -2110,6 +2096,23 @@ void ensureTrailingSlash(char* pDstPath, const char* pSrcPath, S32 dstSize) pDstPath[trailIndex] = 0; } +//-------------------------------------- + +void shutdown() +{ + AssertFatal(active == true, "Con::shutdown should only be called once."); + active = false; + + smConsoleInput.remove(postConsoleInput); + + consoleLogFile.close(); + Namespace::shutdown(); + AbstractClassRep::shutdown(); + Compiler::freeConsoleParserList(); + gGlobalVars.reset(); + PathExpandos.clear(); +} + //----------------------------------------------------------------------------- StringTableEntry getDSOPath(const char *scriptPath) @@ -2309,10 +2312,10 @@ ConsoleValue _BaseEngineConsoleCallbackHelper::_exec() STR.clearFunctionOffset(); mArgc = mInitialArgc; // reset - return std::move(ConsoleValue()); + return (ConsoleValue()); } - ConsoleValue returnValue = std::move(Con::_internalExecute( mArgc, mArgv )); + ConsoleValue returnValue = (Con::_internalExecute( mArgc, mArgv )); mArgc = mInitialArgc; // reset args return returnValue; } @@ -2321,7 +2324,7 @@ ConsoleValue _BaseEngineConsoleCallbackHelper::_execLater(SimConsoleThreadExecEv { mArgc = mInitialArgc; // reset args Sim::postEvent((SimObject*)Sim::getRootGroup(), evt, Sim::getCurrentTime()); - return std::move(evt->getCB().waitForResult()); + return (evt->getCB().waitForResult()); } //------------------------------------------------------------------------------ diff --git a/Engine/source/console/console.h b/Engine/source/console/console.h index b3163e5c5..beec7ccc8 100644 --- a/Engine/source/console/console.h +++ b/Engine/source/console/console.h @@ -37,6 +37,7 @@ #include "core/util/str.h" #include "core/util/journal/journaledSignal.h" #include "core/stringTable.h" +#include class SimObject; class Namespace; @@ -119,6 +120,7 @@ typedef const char *StringTableEntry; enum ConsoleValueType { + cvNULL = -5, cvInteger = -4, cvFloat = -3, cvString = -2, @@ -126,93 +128,109 @@ enum ConsoleValueType cvConsoleValueType = 0 }; -struct ConsoleValueConsoleType -{ - S32 consoleType; - void* dataPtr; - EnumTable* enumTable; -}; - class ConsoleValue { +public: +#pragma warning( push ) +#pragma warning( disable : 4201 ) // warning C4201: nonstandard extension used : nameless struct/union union { - F64 f; - S64 i; - char* s; - void* data; - ConsoleValueConsoleType* ct; + struct + { + F64 f; + S64 i; + char* s; + }; + + struct + { + void* dataPtr; + EnumTable* enumTable; + }; }; S32 type; + U32 bufferLen; static DataChunker sConversionAllocator; char* convertToBuffer() const; - TORQUE_FORCEINLINE bool hasAllocatedData() const - { - return (type == ConsoleValueType::cvString || isConsoleType()) && data != NULL; - } - const char* getConsoleData() const; TORQUE_FORCEINLINE void cleanupData() { - if (hasAllocatedData()) + if (type <= cvString && bufferLen > 0) { - dFree(data); - data = NULL; - } - } - - TORQUE_FORCEINLINE void _move(ConsoleValue&& ref) noexcept - { - type = ref.type; - - switch (ref.type) - { - case cvInteger: - i = ref.i; - break; - case cvFloat: - f = ref.f; - break; - case cvSTEntry: - TORQUE_CASE_FALLTHROUGH; - case cvString: - s = ref.s; - break; - default: - data = ref.data; - break; + dFree(s); + bufferLen = 0; } - ref.data = NULL; - ref.setEmptyString(); + s = const_cast(StringTable->EmptyString()); + type = ConsoleValueType::cvNULL; } - -public: ConsoleValue() { type = ConsoleValueType::cvSTEntry; s = const_cast(StringTable->EmptyString()); + bufferLen = 0; } - ConsoleValue(ConsoleValue&& ref) noexcept + ConsoleValue(const ConsoleValue& ref) { - _move(std::move(ref)); + type = ConsoleValueType::cvSTEntry; + s = const_cast(StringTable->EmptyString()); + bufferLen = 0; + + switch (ref.type) + { + case cvNULL: + std::cout << "Ref already cleared!"; + break; + case cvInteger: + setInt(ref.i); + break; + case cvFloat: + setFloat(ref.f); + break; + case cvSTEntry: + setStringTableEntry(ref.s); + break; + case cvString: + setString(ref.s); + break; + default: + setConsoleData(ref.type, ref.dataPtr, ref.enumTable); + break; + } } - TORQUE_FORCEINLINE ConsoleValue& operator=(ConsoleValue&& ref) noexcept + ConsoleValue& operator=(const ConsoleValue& ref) { - _move(std::move(ref)); + switch (ref.type) + { + case cvNULL: + std::cout << "Ref already cleared!"; + break; + case cvInteger: + setInt(ref.i); + break; + case cvFloat: + setFloat(ref.f); + break; + case cvSTEntry: + setStringTableEntry(ref.s); + break; + case cvString: + setString(ref.s); + break; + default: + setConsoleData(ref.type, ref.dataPtr, ref.enumTable); + break; + } return *this; } - ConsoleValue(const ConsoleValue&) = delete; - ConsoleValue& operator=(const ConsoleValue&) = delete; - TORQUE_FORCEINLINE ~ConsoleValue() { cleanupData(); @@ -308,16 +326,19 @@ public: type = ConsoleValueType::cvString; - s = (char*)dMalloc(static_cast(len) + 1); + s = (char*)dMalloc(len + 1); + + bufferLen = len + 1; s[len] = '\0'; - dStrcpy(s, val, static_cast(len) + 1); + dStrcpy(s, val, len + 1); } TORQUE_FORCEINLINE void setStringRef(const char* ref, S32 len) { cleanupData(); type = ConsoleValueType::cvString; - s = const_cast(ref); + s = (char*)std::move(ref); + bufferLen = len; } TORQUE_FORCEINLINE void setBool(const bool val) @@ -331,7 +352,8 @@ public: { cleanupData(); type = ConsoleValueType::cvSTEntry; - s = const_cast(val); + s = (char*)std::move(val); + bufferLen = 0; } TORQUE_FORCEINLINE void setEmptyString() @@ -339,12 +361,13 @@ public: setStringTableEntry(StringTable->EmptyString()); } - TORQUE_FORCEINLINE void setConsoleData(S32 consoleType, void* dataPtr, const EnumTable* enumTable) + TORQUE_FORCEINLINE void setConsoleData(S32 inConsoleType, void* inDataPtr, const EnumTable* inEnumTable) { cleanupData(); - type = ConsoleValueType::cvConsoleValueType; - ct = new ConsoleValueConsoleType{ consoleType, dataPtr, const_cast(enumTable) }; - } + type = inConsoleType; + dataPtr = inDataPtr; + enumTable = const_cast(inEnumTable); + }; TORQUE_FORCEINLINE S32 getType() const { @@ -366,11 +389,11 @@ public: return type >= ConsoleValueType::cvConsoleValueType; } - TORQUE_FORCEINLINE ConsoleValueConsoleType* getConsoleType() const + TORQUE_FORCEINLINE S32 getConsoleType() const { if(type >= ConsoleValueType::cvConsoleValueType) { - return ct; + return type; } else { @@ -1022,7 +1045,7 @@ namespace Con ConsoleValue executef(R r, ArgTs ...argTs) { _EngineConsoleExecCallbackHelper callback(r); - return std::move(callback.template call(argTs...)); + return (callback.template call(argTs...)); } /// } }; diff --git a/Engine/source/console/consoleInternal.cpp b/Engine/source/console/consoleInternal.cpp index 8c04ee830..454af91ec 100644 --- a/Engine/source/console/consoleInternal.cpp +++ b/Engine/source/console/consoleInternal.cpp @@ -924,6 +924,12 @@ void Namespace::shutdown() for (Namespace *walk = mNamespaceList; walk; walk = walk->mNext) walk->~Namespace(); + + gNamespaceCache.clear(); + + mNamespaceList = nullptr; + mGlobalNamespace = nullptr; + mAllocator.freeBlocks(); } void Namespace::trashCache() @@ -1153,11 +1159,11 @@ ConsoleValue Namespace::Entry::execute(S32 argc, ConsoleValue *argv, SimObject * { if (mFunctionOffset) { - return std::move(mModule->exec(mFunctionOffset, argv[0].getString(), mNamespace, argc, argv, false, mPackage).value); + return (mModule->exec(mFunctionOffset, argv[0].getString(), mNamespace, argc, argv, false, mPackage).value); } else { - return std::move(ConsoleValue()); + return (ConsoleValue()); } } @@ -1167,7 +1173,7 @@ ConsoleValue Namespace::Entry::execute(S32 argc, ConsoleValue *argv, SimObject * if (mToolOnly && !Con::isCurrentScriptToolScript()) { Con::errorf(ConsoleLogEntry::Script, "%s::%s - attempting to call tools only function from outside of tools", mNamespace->mName, mFunctionName); - return std::move(ConsoleValue()); + return (ConsoleValue()); } #endif @@ -1175,7 +1181,7 @@ ConsoleValue Namespace::Entry::execute(S32 argc, ConsoleValue *argv, SimObject * { Con::warnf(ConsoleLogEntry::Script, "%s::%s - wrong number of arguments. got %d, expected %d to %d", mNamespace->mName, mFunctionName, argc, mMinArgs, mMaxArgs); Con::warnf(ConsoleLogEntry::Script, "usage: %s", mUsage); - return std::move(ConsoleValue()); + return (ConsoleValue()); } ConsoleValue result; diff --git a/Engine/source/console/consoleInternal.h b/Engine/source/console/consoleInternal.h index 6c0c91828..beffb2e2c 100644 --- a/Engine/source/console/consoleInternal.h +++ b/Engine/source/console/consoleInternal.h @@ -321,7 +321,7 @@ public: void reset(); - inline ConsoleValue getValue() { return std::move(value); } + inline ConsoleValue getValue() { return (value); } inline U32 getIntValue() { @@ -349,8 +349,7 @@ public: if (value.isConsoleType()) { const char* dptr = Con::getData(TypeS32, &val, 0); - ConsoleValueConsoleType* cvt = value.getConsoleType(); - Con::setData(cvt->consoleType, cvt->dataPtr, 0, 1, &dptr, cvt->enumTable); + Con::setData(value.type, value.dataPtr, 0, 1, &dptr, value.enumTable); } else { @@ -373,8 +372,7 @@ public: if (value.isConsoleType()) { const char* dptr = Con::getData(TypeF32, &val, 0); - ConsoleValueConsoleType* cvt = value.getConsoleType(); - Con::setData(cvt->consoleType, cvt->dataPtr, 0, 1, &dptr, cvt->enumTable); + Con::setData(value.type, value.dataPtr, 0, 1, &dptr, value.enumTable); } else { @@ -397,8 +395,7 @@ public: if (value.isConsoleType()) { - ConsoleValueConsoleType* cvt = value.getConsoleType(); - Con::setData(cvt->consoleType, cvt->dataPtr, 0, 1, &val, cvt->enumTable); + Con::setData(value.type, value.dataPtr, 0, 1, &val, value.enumTable); } else { diff --git a/Engine/source/console/consoleObject.cpp b/Engine/source/console/consoleObject.cpp index 9156c1da7..eed85cbfa 100644 --- a/Engine/source/console/consoleObject.cpp +++ b/Engine/source/console/consoleObject.cpp @@ -286,6 +286,12 @@ void AbstractClassRep::shutdown() // Release storage allocated to the class table. + for (auto walk = classLinkList; walk; walk = walk->nextClass) + { + walk->mFieldList.clear(); + walk->mFieldList.compact(); // Important: frees the internal buffer + } + for (U32 group = 0; group < NetClassGroupsCount; group++) for(U32 type = 0; type < NetClassTypesCount; type++) if( classTable[ group ][ type ] ) diff --git a/Engine/source/console/consoleObject.h b/Engine/source/console/consoleObject.h index b4daf6613..9995dc2aa 100644 --- a/Engine/source/console/consoleObject.h +++ b/Engine/source/console/consoleObject.h @@ -705,7 +705,9 @@ public: smPropertyTable = _smPropertyTable; const_cast(mTypeInfo)->mPropertyTable = &_smPropertyTable; - + + // After we hand it off, immediately delete if safe: + delete[] props; // Let the base finish up. AbstractClassRep::init(); } diff --git a/Engine/source/console/consoleValueStack.h b/Engine/source/console/consoleValueStack.h index 8b44a652c..529f5f1a9 100644 --- a/Engine/source/console/consoleValueStack.h +++ b/Engine/source/console/consoleValueStack.h @@ -89,10 +89,10 @@ public: stack.pop_back(); } - TORQUE_FORCEINLINE void push(ConsoleValue&& val) + TORQUE_FORCEINLINE void push(ConsoleValue val) { Frame& frame = stack.last(); - frame.values[frame.internalCounter++] = std::move(val); + frame.values[frame.internalCounter++] = (val); } TORQUE_FORCEINLINE void argvc(StringTableEntry fn, S32& argc, ConsoleValue** argv) diff --git a/Engine/source/console/engineDoc.cpp b/Engine/source/console/engineDoc.cpp index baffb12e6..6dde4ab7c 100644 --- a/Engine/source/console/engineDoc.cpp +++ b/Engine/source/console/engineDoc.cpp @@ -148,7 +148,7 @@ static void dumpVariable( Stream& stream, // Skip variables for which we can't decipher their type. - ConsoleBaseType* type = ConsoleBaseType::getType( entry->value.getConsoleType()->consoleType ); + ConsoleBaseType* type = ConsoleBaseType::getType( entry->value.type ); if( !type ) { Con::errorf( "Can't find type for variable '%s'", entry->name ); diff --git a/Engine/source/console/runtime.h b/Engine/source/console/runtime.h index d10df543c..cb2a32805 100644 --- a/Engine/source/console/runtime.h +++ b/Engine/source/console/runtime.h @@ -14,10 +14,10 @@ namespace Con public: EvalResult() {} - EvalResult(ConsoleValue&& pValue) + EvalResult(ConsoleValue pValue) { valid = true; - value = (ConsoleValue&&)pValue; + value = (pValue); } EvalResult(String errorMessage) diff --git a/Engine/source/console/script.h b/Engine/source/console/script.h index 55653bff2..d6dca7c24 100644 --- a/Engine/source/console/script.h +++ b/Engine/source/console/script.h @@ -16,7 +16,7 @@ namespace Con gLastEvalResult.value.setString(pLastEvalResult.value.getString()); return pLastEvalResult; } - inline EvalResult getLastEvalResult() { return setLastEvalResult(std::move(gLastEvalResult)); }; + inline EvalResult getLastEvalResult() { return setLastEvalResult((gLastEvalResult)); }; bool runStream(Stream* byteCode, const char* fileName); diff --git a/Engine/source/console/simEvents.cpp b/Engine/source/console/simEvents.cpp index c27728580..47924082e 100644 --- a/Engine/source/console/simEvents.cpp +++ b/Engine/source/console/simEvents.cpp @@ -116,7 +116,7 @@ ConsoleValue SimConsoleThreadExecCallback::waitForResult() { if(sem->acquire(true)) { - return std::move(retVal); + return (retVal); } return ConsoleValue(); @@ -134,9 +134,9 @@ void SimConsoleThreadExecEvent::process(SimObject* object) if (cb) { if (mOnObject) - cb->handleCallback(std::move(Con::execute(object, mArgc, mArgv))); + cb->handleCallback(Con::execute(object, mArgc, mArgv)); else - cb->handleCallback(std::move(Con::execute(mArgc, mArgv))); + cb->handleCallback(Con::execute(mArgc, mArgv)); } else { diff --git a/Engine/source/console/simObject.cpp b/Engine/source/console/simObject.cpp index 6039393ef..cabafe466 100644 --- a/Engine/source/console/simObject.cpp +++ b/Engine/source/console/simObject.cpp @@ -3536,15 +3536,13 @@ DefineEngineMethod( SimObject, getDebugInfo, ArrayObject*, (),, array->push_back( "Flag|CanSave", object->getCanSave() ? "true" : "false" ); #ifndef TORQUE_DISABLE_MEMORY_MANAGER - Memory::Info memInfo; + Memory::MemInfo memInfo; Memory::getMemoryInfo( object, memInfo ); - array->push_back( "Memory|AllocNumber", String::ToString( memInfo.mAllocNumber ) ); - array->push_back( "Memory|AllocSize", String::ToString( memInfo.mAllocSize ) ); - array->push_back( "Memory|AllocFile", memInfo.mFileName ); - array->push_back( "Memory|AllocLine", String::ToString( memInfo.mLineNumber ) ); - array->push_back( "Memory|IsGlobal", memInfo.mIsGlobal ? "true" : "false" ); - array->push_back( "Memory|IsStatic", memInfo.mIsStatic ? "true" : "false" ); + array->push_back( "Memory|AllocNumber", String::ToString( memInfo.allocId) ); + array->push_back( "Memory|AllocSize", String::ToString( (U32)memInfo.size) ); + array->push_back( "Memory|AllocFile", memInfo.file); + array->push_back( "Memory|AllocLine", String::ToString( memInfo.line) ); #endif return array; diff --git a/Engine/source/console/torquescript/cmdgram.cpp b/Engine/source/console/torquescript/cmdgram.cpp index d6d877da2..ab15ffc73 100644 --- a/Engine/source/console/torquescript/cmdgram.cpp +++ b/Engine/source/console/torquescript/cmdgram.cpp @@ -3363,7 +3363,7 @@ yyreport_syntax_error (const yypcontext_t *ctx) output += String::ToString("%5s | %*s", "", loc->first_column, "^"); } - yyerror("%s",output.c_str()); + yyerror("%s", output.c_str()); return ret; } diff --git a/Engine/source/console/torquescript/codeBlock.cpp b/Engine/source/console/torquescript/codeBlock.cpp index f7534cc54..5a4bdb30a 100644 --- a/Engine/source/console/torquescript/codeBlock.cpp +++ b/Engine/source/console/torquescript/codeBlock.cpp @@ -668,7 +668,7 @@ Con::EvalResult CodeBlock::compileExec(StringTableEntry fileName, const char *in Con::warnf(ConsoleLogEntry::General, "precompile size mismatch, precompile: %d compile: %d", codeSize, lastIp); // repurpose argc as local register counter for global state - return std::move(exec(0, fileName, NULL, localRegisterCount, 0, noCalls, NULL, setFrame)); + return (exec(0, fileName, NULL, localRegisterCount, 0, noCalls, NULL, setFrame)); } //------------------------------------------------------------------------- diff --git a/Engine/source/console/torquescript/compiledEval.cpp b/Engine/source/console/torquescript/compiledEval.cpp index 94ddcab80..b9ada8750 100644 --- a/Engine/source/console/torquescript/compiledEval.cpp +++ b/Engine/source/console/torquescript/compiledEval.cpp @@ -612,7 +612,7 @@ Con::EvalResult CodeBlock::exec(U32 ip, const char* functionName, Namespace* thi { S32 reg = code[ip + (2 + 6 + 1 + 1) + i]; ConsoleValue& value = argv[i + 1]; - Script::gEvalState.moveConsoleValue(reg, std::move(value)); + Script::gEvalState.moveConsoleValue(reg, (value)); } ip = ip + fnArgc + (2 + 6 + 1 + 1); curFloatTable = functionFloats; @@ -1214,7 +1214,7 @@ Con::EvalResult CodeBlock::exec(U32 ip, const char* functionName, Namespace* thi case OP_RETURN: { - returnValue = std::move(stack[_STK]); + returnValue = (stack[_STK]); _STK--; // Clear iterator state. @@ -1905,7 +1905,7 @@ Con::EvalResult CodeBlock::exec(U32 ip, const char* functionName, Namespace* thi if (nsEntry->mFunctionOffset) { ConsoleValue returnFromFn = nsEntry->mModule->exec(nsEntry->mFunctionOffset, fnName, nsEntry->mNamespace, callArgc, callArgv, false, nsEntry->mPackage).value; - stack[_STK + 1] = std::move(returnFromFn); + stack[_STK + 1] = (returnFromFn); } else // no body stack[_STK + 1].setEmptyString(); @@ -2040,7 +2040,7 @@ Con::EvalResult CodeBlock::exec(U32 ip, const char* functionName, Namespace* thi break; case OP_PUSH: - gCallStack.push(std::move(stack[_STK--])); + gCallStack.push((stack[_STK--])); break; case OP_PUSH_FRAME: @@ -2303,7 +2303,7 @@ execFinished: AssertFatal(!(_STK < stackStart), "String stack popped too much in script exec"); #endif - return Con::EvalResult(std::move(returnValue)); + return Con::EvalResult((returnValue)); } //------------------------------------------------------------ diff --git a/Engine/source/console/torquescript/compiler.cpp b/Engine/source/console/torquescript/compiler.cpp index 817b181c3..890f47e98 100644 --- a/Engine/source/console/torquescript/compiler.cpp +++ b/Engine/source/console/torquescript/compiler.cpp @@ -269,8 +269,10 @@ U32 CompilerStringTable::addFloatString(F64 value) void CompilerStringTable::reset() { - list = NULL; + // Reset list and associated variables + list = nullptr; totalLen = 0; + hashTable.clear(); // Clear the lookup table too } char *CompilerStringTable::build() diff --git a/Engine/source/console/torquescript/evalState.h b/Engine/source/console/torquescript/evalState.h index cd0d34a13..05c3be335 100644 --- a/Engine/source/console/torquescript/evalState.h +++ b/Engine/source/console/torquescript/evalState.h @@ -1,4 +1,4 @@ -#ifndef _EVALSTATE_H +#ifndef _EVALSTATE_H #define _EVALSTATE_H #include "console/consoleInternal.h" @@ -83,7 +83,7 @@ public: TORQUE_FORCEINLINE void moveConsoleValue(S32 reg, ConsoleValue val) { - currentRegisterArray->values[reg] = std::move(val); + currentRegisterArray->values[reg] = (val); } void pushFrame(StringTableEntry frameName, Namespace *ns, S32 regCount); diff --git a/Engine/source/console/torquescript/runtime.cpp b/Engine/source/console/torquescript/runtime.cpp index be2d4af91..ddb063699 100644 --- a/Engine/source/console/torquescript/runtime.cpp +++ b/Engine/source/console/torquescript/runtime.cpp @@ -39,7 +39,7 @@ namespace TorqueScript fileName = StringTable->insert(fileName); CodeBlock* newCodeBlock = new CodeBlock(); - return std::move(newCodeBlock->compileExec(fileName, string, false, fileName ? -1 : 0)); + return (newCodeBlock->compileExec(fileName, string, false, fileName ? -1 : 0)); } Con::EvalResult TorqueScriptRuntime::evaluate(const char* script, S32 frame, bool echo, const char* fileName) diff --git a/Engine/source/core/strings/unicode.cpp b/Engine/source/core/strings/unicode.cpp index ad43ad3e1..28fb25323 100644 --- a/Engine/source/core/strings/unicode.cpp +++ b/Engine/source/core/strings/unicode.cpp @@ -289,6 +289,11 @@ UTF8* createUTF8string( const UTF16* unistring) return ret; } +void UTF16ClearCache() +{ + sgUTF16Cache.clear(); +} + //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- diff --git a/Engine/source/core/strings/unicode.h b/Engine/source/core/strings/unicode.h index ec26d1c91..009489630 100644 --- a/Engine/source/core/strings/unicode.h +++ b/Engine/source/core/strings/unicode.h @@ -66,6 +66,8 @@ UTF16* createUTF16string( const UTF8 *unistring); UTF8* createUTF8string( const UTF16 *unistring); +void UTF16ClearCache(); + //----------------------------------------------------------------------------- /// Functions that convert buffers of unicode code points, into a provided buffer. /// - These functions are useful for working on existing buffers. @@ -138,4 +140,4 @@ const UTF8* getNthCodepoint(const UTF8 *unistring, const U32 n); bool chompUTF8BOM( const char *inString, char **outStringPtr ); bool isValidUTF8BOM( U8 bom[4] ); -#endif // _UNICODE_H_ \ No newline at end of file +#endif // _UNICODE_H_ diff --git a/Engine/source/core/util/tDictionary.h b/Engine/source/core/util/tDictionary.h index 4af0d6037..6c8c7d21f 100644 --- a/Engine/source/core/util/tDictionary.h +++ b/Engine/source/core/util/tDictionary.h @@ -193,7 +193,7 @@ private: { Node* mNext; Pair mPair; - Node(): mNext(0) {} + Node(): mNext(nullptr) {} Node(Pair p,Node* n) : mNext(n), mPair(p) @@ -226,8 +226,8 @@ public: _Iterator() { - mHashTable = 0; - mLink = 0; + mHashTable = nullptr; + mLink = nullptr; } _Iterator(M* table,E* ptr) @@ -320,7 +320,7 @@ public: template HashTable::HashTable() : mNodeAllocator(512) { mTableSize = 0; - mTable = 0; + mTable = nullptr; mSize = 0; } @@ -328,7 +328,7 @@ template HashTable::HashTable(const Has { mSize = 0; mTableSize = 0; - mTable = 0; + mTable = nullptr; *this = p; } @@ -357,7 +357,7 @@ typename HashTable::Node* HashTable::_next(U32 index) cons for (; index < mTableSize; index++) if (Node* node = mTable[index]) return node; - return 0; + return nullptr; } template @@ -402,7 +402,7 @@ void HashTable::_destroy() mNodeAllocator.freeBlocks(); delete[] mTable; - mTable = NULL; + mTable = nullptr; } @@ -509,7 +509,7 @@ typename HashTable::Iterator HashTable::insertEqual(const template void HashTable::erase(const Key& key) { - if (mTable==NULL) + if (mTable == nullptr) return; Node** prev = &mTable[_index(key)]; for (Node* itr = *prev; itr; prev = &itr->mNext, itr = itr->mNext) @@ -529,7 +529,7 @@ void HashTable::erase(const Key& key) template void HashTable::erase(Iterator node) { - if (mTable==NULL) + if (mTable == nullptr) return; Node** prev = &mTable[_index(node->key)]; for (Node* itr = *prev; itr; prev = &itr->mNext, itr = itr->mNext) @@ -547,7 +547,7 @@ void HashTable::erase(Iterator node) template void HashTable::erase(const Key & key, const Value & value) { - if (mTable==NULL) + if (mTable == nullptr) return; Node** prev = &mTable[_index(key)]; for (Node* itr = *prev; itr; prev = &itr->mNext, itr = itr->mNext) @@ -591,7 +591,7 @@ typename HashTable::Iterator HashTable::find(const Key& ke for (Node* itr = mTable[_index(key)]; itr; itr = itr->mNext) if ( KeyCmp::equals( itr->mPair.key, key ) ) return Iterator(this,itr); - return Iterator(this,0); + return Iterator(this, nullptr); } template @@ -605,7 +605,7 @@ typename HashTable::ConstIterator HashTable::find(const Ke return ConstIterator(this,itr); } } - return ConstIterator(this,0); + return ConstIterator(this, nullptr); } template @@ -659,13 +659,13 @@ inline typename HashTable::ConstIterator HashTable::begin( template inline typename HashTable::Iterator HashTable::end() { - return Iterator(this,0); + return Iterator(this, nullptr); } template inline typename HashTable::ConstIterator HashTable::end() const { - return ConstIterator(this,0); + return ConstIterator(this, nullptr); } diff --git a/Engine/source/main/main.cpp b/Engine/source/main/main.cpp index dc2e19c19..c0699b9a1 100644 --- a/Engine/source/main/main.cpp +++ b/Engine/source/main/main.cpp @@ -205,6 +205,7 @@ int main(int argc, const char **argv) #include "platform/platform.h" #include "app/mainLoop.h" #include "T3D/gameFunctions.h" +#include "platform/platformMemory.h" #if defined(WIN32) || defined(_WIN32) //tell switchable graphics supported systems that they need to use the beefier GPU @@ -230,8 +231,9 @@ S32 TorqueMain(S32 argc, const char **argv) // argv = argvFake; // } - // Memory::enableLogging("testMem.log"); - // Memory::setBreakAlloc(104717); +#if defined( TORQUE_DEBUG ) && !defined(TORQUE_DISABLE_MEMORY_MANAGER) + Memory::init(); +#endif // Initialize the subsystems. StandardMainLoop::init(); @@ -254,6 +256,12 @@ S32 TorqueMain(S32 argc, const char **argv) if( StandardMainLoop::requiresRestart() ) Platform::restartInstance(); + + +#if defined( TORQUE_DEBUG ) && !defined( TORQUE_DISABLE_MEMORY_MANAGER ) + Memory::shutdown(); +#endif + // Return. return StandardMainLoop::getReturnStatus(); } diff --git a/Engine/source/platform/platformMemory.cpp b/Engine/source/platform/platformMemory.cpp index f10113308..14fd4d02f 100644 --- a/Engine/source/platform/platformMemory.cpp +++ b/Engine/source/platform/platformMemory.cpp @@ -30,6 +30,16 @@ #include "platform/threads/mutex.h" #include "core/module.h" +#ifdef _WIN32 +#include +#include +#pragma comment(lib, "Dbghelp.lib") +#else +#include +#endif +#include +#include + // If profile paths are enabled, disable profiling of the // memory manager as that would cause a cyclic dependency // through the string table's allocation stuff used by the @@ -45,7 +55,7 @@ #endif #ifdef TORQUE_MULTITHREAD -void * gMemMutex = NULL; +void* gMemMutex = NULL; #endif //-------------------------------------- Make sure we don't have the define set @@ -53,1663 +63,309 @@ void * gMemMutex = NULL; #undef new #endif -enum MemConstants : U32 -{ - Allocated = BIT(0), - Array = BIT(1), - DebugFlag = BIT(2), - Reallocated = BIT(3), /// This flag is set if the memory has been allocated, then 'realloc' is called - GlobalFlag = BIT(4), - StaticFlag = BIT(5), - AllocatedGuard = 0xCEDEFEDE, - FreeGuard = 0x5555FFFF, - MaxAllocationAmount = 0xFFFFFFFF, - TreeNodeAllocCount = 2048, -}; - -inline U32 flagToBit( Memory::EFlag flag ) -{ - using namespace Memory; - - U32 bit = 0; - switch( flag ) - { - case FLAG_Debug: bit = DebugFlag; break; - case FLAG_Global: bit = GlobalFlag; break; - case FLAG_Static: bit = StaticFlag; break; - } - return bit; -} - -enum RedBlackTokens { - Red = 0, - Black = 1 -}; - -static U32 MinPageSize = 8 * 1024 * 1024; - -#if !defined(TORQUE_SHIPPING) && defined(TORQUE_DEBUG_GUARD) -#define LOG_PAGE_ALLOCS -#endif - -U32 gNewNewTotal = 0; -U32 gImageAlloc = 0; - //--------------------------------------------------------------------------- namespace Memory { +#if !defined(TORQUE_DISABLE_MEMORY_MANAGER) -ConsoleFunctionGroupBegin( Memory, "Memory manager utility functions."); -struct FreeHeader; + static const U32 MaxAllocs = 10240; + static MemInfo allocList[MaxAllocs]; + static U32 allocCount = 0; + static U32 currentAllocId = 0; + static bool initialized = false; + char gLogFilename[256] = { 0 }; + bool gStackTrace = true; + bool gFromScript = false; -/// Red/Black Tree Node - used to store queues of free blocks -struct TreeNode -{ - U32 size; - TreeNode *parent; - TreeNode *left; - TreeNode *right; - U32 color; - FreeHeader *queueHead; - FreeHeader *queueTail; - U32 unused; -}; - -struct Header -{ - // doubly linked list of allocated and free blocks - - // contiguous in memory. -#ifdef TORQUE_DEBUG_GUARD - U32 preguard[4]; -#endif - Header *next; - Header *prev; - dsize_t size; - U32 flags; -#ifdef TORQUE_DEBUG_GUARD - #ifdef TORQUE_ENABLE_PROFILE_PATH - U32 unused[5]; - #else - U32 unused[4]; - #endif - U32 postguard[4]; -#endif -}; - -struct AllocatedHeader -{ -#ifdef TORQUE_DEBUG_GUARD - U32 preguard[4]; -#endif - Header *next; - Header *prev; - dsize_t size; - U32 flags; - -#ifdef TORQUE_DEBUG_GUARD - // an allocated header will only have this stuff if TORQUE_DEBUG_GUARD - U32 line; - U32 allocNum; - const char *fileName; - #ifdef TORQUE_ENABLE_PROFILE_PATH - const char * profilePath; - #endif - U32 realSize; - U32 postguard[4]; -#endif - - void* getUserPtr() + struct memReport { - return ( this + 1 ); + std::string report; + bool skip; + U32 count = 1; + U32 total = 0; + } memLog[MaxAllocs]; + + void init() + { + if (initialized) return; + std::memset(allocList, 0, sizeof(allocList)); + allocCount = 0; + currentAllocId = 0; + initialized = true; + + // Generate timestamped log filename + std::time_t now = std::time(nullptr); + std::tm* localTime = std::localtime(&now); + std::strftime(gLogFilename, sizeof(gLogFilename), "memlog_%Y-%m-%d_%H-%M-%S.txt", localTime); + + //std::atexit(shutdown); } -}; -struct FreeHeader -{ -#ifdef TORQUE_DEBUG_GUARD - U32 preguard[4]; -#endif - Header *next; - Header *prev; - dsize_t size; - U32 flags; - -// since a free header has at least one cache line (16 bytes) -// we can tag some more stuff on: - - FreeHeader *nextQueue; // of the same size - FreeHeader *prevQueue; // doubly linked - TreeNode *treeNode; // which tree node we're coming off of. - U32 guard; -#ifdef TORQUE_DEBUG_GUARD - #ifdef TORQUE_ENABLE_PROFILE_PATH - U32 unused; - #endif - U32 postguard[4]; -#endif -}; - -struct PageRecord -{ - dsize_t allocSize; - PageRecord *prevPage; - Header *headerList; // if headerList is NULL, this is a treeNode page - void *basePtr; - U32 unused[4]; // even out the record to 32 bytes... - // so if we're on a 32-byte cache-line comp, the tree nodes - // will cache better -}; - -PageRecord *gPageList = NULL; -TreeNode nil; -TreeNode *NIL = &nil; -TreeNode *gFreeTreeRoot = &nil; -TreeNode *gTreeFreeList = NULL; - -U32 gInsertCount = 0; -U32 gRemoveCount = 0; -U32 gBreakAlloc = 0xFFFFFFFF; -U32 gCurrAlloc = 0; -char gLogFilename[256] = "memlog.txt"; -bool gEnableLogging = false; -bool gNeverLogLeaks = 0; -bool gAlwaysLogLeaks = 0; -U32 gBytesAllocated = 0; -U32 gBlocksAllocated = 0; -U32 gPageBytesAllocated = 0; - -struct HeapIterator -{ - PageRecord* mCurrentPage; - Header* mCurrentHeader; - bool mAllocatedOnly; - - HeapIterator( bool allocatedOnly = true ) - : mCurrentPage( gPageList ), - mCurrentHeader( NULL ), - mAllocatedOnly( allocatedOnly ) + void shutdown() { - if( mCurrentPage ) + if (!initialized) return; + + FILE* log = std::fopen(gLogFilename, "w"); + if (!log) + return; + + std::fprintf(log, "\n--- Memory Leak Report ---\n"); + + U32 start = 0; + U32 stop = allocCount; + if (gFromScript) //filter out the bits from console { - mCurrentHeader = mCurrentPage->headerList; - while( !mCurrentHeader && mCurrentPage ) + start = 6; + stop = allocCount - 8; + } + for (U32 curRep = start; curRep < stop; ++curRep) + { + if (allocList[curRep].ptr != nullptr) { - mCurrentPage = mCurrentPage->prevPage; - mCurrentHeader = mCurrentPage->headerList; + char entry[512] = ""; + std::sprintf(entry, "from %s:%u\n", allocList[curRep].file ? allocList[curRep].file : "(null)", allocList[curRep].line); + + memLog[curRep].skip = false; + std::string report = entry; + + if (gStackTrace) + { + char stack[512] = ""; +#ifdef _WIN32 + SYMBOL_INFO* symbol = (SYMBOL_INFO*)malloc(sizeof(SYMBOL_INFO) + 256); + symbol->MaxNameLen = 255; + symbol->SizeOfStruct = sizeof(SYMBOL_INFO); + + HANDLE process = GetCurrentProcess(); + SymInitialize(process, NULL, TRUE); + + for (int curStack = 0; curStack < allocList[curRep].backtraceSize; ++curStack) + { + DWORD64 addr = (DWORD64)(allocList[curRep].backtracePtrs[curStack]); + DWORD displacement = 0; + IMAGEHLP_LINE64 line; + std::memset(&line, 0, sizeof(IMAGEHLP_LINE64)); + line.SizeOfStruct = sizeof(IMAGEHLP_LINE64); + + if (SymFromAddr(process, addr, 0, symbol)) { + if (SymGetLineFromAddr64(process, addr, &displacement, &line)) { + std::sprintf(stack, " [%d] %s - %s:%lu (0x%0llX)\n", + curStack, symbol->Name, line.FileName, line.LineNumber, symbol->Address); + } + else { + std::sprintf(stack, " [%d] %s - ???:??? (0x%0llX)\n", + curStack, symbol->Name, symbol->Address); + } + } + else { + std::sprintf(stack, " [%d] ??? - 0x%0llX\n", curStack, addr); + } + report += stack; + } + + std::free(symbol); +#else + char** symbols = backtrace_symbols(allocList[curRep].backtracePtrs, allocList[i].backtraceSize); + for (int curStack = 0; curStack < allocList[curRep].backtraceSize; ++curStack) + { + std::sprintf(stack, " [%d] %s\n", curStack, symbols[curStack]); + report += stack; + } + std::free(symbols); +#endif + } + + //if (report.find("getDocsLink") != std::string::npos) + //{ + // //known issue. one off allocation + // memLog[curRep].skip = true; + //} + + for (U32 oldRep = start; oldRep < curRep; ++oldRep) + { + if (!memLog[oldRep].skip && (memLog[oldRep].report.find(report) != std::string::npos)) + { + //inc origional + memLog[oldRep].count++; + memLog[oldRep].total += allocList[curRep].size; + //skip dupe report + memLog[curRep].skip = true; + } + } + if (!memLog[curRep].skip) + { + memLog[curRep].report = report; + memLog[curRep].count = 1; + memLog[curRep].total = allocList[curRep].size; + } } - - if( mCurrentHeader && mAllocatedOnly && !( mCurrentHeader->flags & Allocated ) ) - ++ ( *this ); // Advance to first allocated record. } - } - bool isValid() const - { - return ( mCurrentHeader != NULL ); - } - HeapIterator& operator ++() - { - do + for (U32 ntry = start; ntry < stop; ++ntry) { - if( !mCurrentHeader ) + if (!memLog[ntry].skip) { - if( mCurrentPage ) - mCurrentPage = mCurrentPage->prevPage; - - if( !mCurrentPage ) - break; - mCurrentHeader = mCurrentPage->headerList; + std::fprintf(log, "Leak-count[%i]total[%i]:%s", memLog[ntry].count, memLog[ntry].total, memLog[ntry].report.c_str()); + memLog[ntry].report.clear(); } - else - mCurrentHeader = mCurrentHeader->next; - } - while( !mCurrentHeader || ( mAllocatedOnly && !( mCurrentHeader->flags & Allocated ) ) ); - - return *this; - } - operator Header*() const - { - return mCurrentHeader; - } - Header* operator *() const - { - return mCurrentHeader; - } - Header* operator ->() const - { - return mCurrentHeader; - } -}; - -#ifdef TORQUE_DEBUG_GUARD - -static bool checkGuard(Header *header, bool alloc) -{ - U32 guardVal = alloc ? AllocatedGuard : FreeGuard; - for(U32 i = 0; i < 4; i++) - if(header->preguard[i] != guardVal || header->postguard[i] != guardVal) - { - Platform::debugBreak(); - return false; } - return true; -} + std::fclose(log); -#if !defined(TORQUE_DISABLE_MEMORY_MANAGER) -static void setGuard(Header *header, bool alloc) -{ - U32 guardVal = alloc ? AllocatedGuard : FreeGuard; - for(U32 i = 0; i < 4; i++) - header->preguard[i] = header->postguard[i] = guardVal; -} -#endif // !defined(TORQUE_DISABLE_MEMORY_MANAGER) - -#endif // TORQUE_DEBUG_GUARD - -static void memoryError() -{ - // free all the pages - PageRecord *walk = gPageList; - while(walk) { - PageRecord *prev = walk->prevPage; - dRealFree(walk); - walk = prev; + std::memset(allocList, 0, sizeof(allocList)); + allocCount = 0; + currentAllocId = 0; + initialized = false; } - AssertFatal(false, "Error allocating memory! Shutting down."); - Platform::AlertOK("Torque Memory Error", "Error allocating memory. Shutting down.\n"); - Platform::forceShutdown(-1); -} -PageRecord *allocPage(dsize_t pageSize) -{ - pageSize += sizeof(PageRecord); - void* base = dRealMalloc(pageSize); - if (base == NULL) - memoryError(); - - PageRecord *rec = (PageRecord *) base; - rec->basePtr = (void *) (rec + 1); - rec->allocSize = pageSize; - rec->prevPage = gPageList; - gPageList = rec; - rec->headerList = NULL; - return rec; -} - -TreeNode *allocTreeNode() -{ - if(!gTreeFreeList) + void checkPtr(void* ptr) { - PageRecord *newPage = allocPage(TreeNodeAllocCount * sizeof(TreeNode)); - TreeNode *walk = (TreeNode *) newPage->basePtr; - U32 i; - gTreeFreeList = walk; - for(i = 0; i < TreeNodeAllocCount - 1; i++, walk++) - walk->parent = walk + 1; - walk->parent = NULL; - } - TreeNode *ret = gTreeFreeList; - gTreeFreeList = ret->parent; - return ret; -} + for (U32 i = 0; i < allocCount; ++i) + if (allocList[i].ptr == ptr) + return; -void freeTreeNode(TreeNode *tn) -{ - tn->parent = gTreeFreeList; - gTreeFreeList = tn; -} - - -static U32 validateTreeRecurse(TreeNode *tree) -{ - if(tree == NIL) - return 1; - // check my left tree - S32 lcount, rcount, nc = 0; - - if(tree->color == Red) - { - if(tree->left->color == Red || tree->right->color == Red) - Platform::debugBreak(); - } - else - nc = 1; - - FreeHeader *walk = tree->queueHead; - if(!walk) Platform::debugBreak(); - - FreeHeader *prev = NULL; - while(walk) - { - if(walk->prevQueue != prev) - Platform::debugBreak(); - if(walk->treeNode != tree) - Platform::debugBreak(); - if(walk->size != tree->size) - Platform::debugBreak(); - if(!walk->nextQueue && walk != tree->queueTail) - Platform::debugBreak(); - prev = walk; - walk = walk->nextQueue; } - lcount = validateTreeRecurse(tree->left); - rcount = validateTreeRecurse(tree->right); - if(lcount != rcount) - Platform::debugBreak(); - return lcount + nc; -} - -static void validateParentageRecurse(TreeNode *tree) -{ - if(tree->left != NIL) + static void* alloc(dsize_t size, bool array, const char* fileName, U32 line) { - if(tree->left->parent != tree) - Platform::debugBreak(); + if (size == 0) + return nullptr; - if(tree->left->size > tree->size) - Platform::debugBreak(); - validateParentageRecurse(tree->left); - } - if(tree->right != NIL) - { - if(tree->right->parent != tree) - Platform::debugBreak(); + void* ptr = std::malloc(size); + if (!ptr) + return nullptr; - if(tree->right->size < tree->size) - Platform::debugBreak(); - validateParentageRecurse(tree->right); - } -} + if (!initialized || allocCount >= MaxAllocs) + return ptr; -static void validateTree() -{ - if(gFreeTreeRoot == NIL) - return; - validateParentageRecurse(gFreeTreeRoot); - validateTreeRecurse(gFreeTreeRoot); -} + MemInfo& info = allocList[allocCount++]; + info.ptr = ptr; + info.size = size; + info.file = fileName ? fileName : "unknown"; + info.line = line; + info.allocId = currentAllocId++; + info.flagged = false; -void validate() -{ -#ifdef TORQUE_MULTITHREAD - if(!gMemMutex) - gMemMutex = Mutex::createMutex(); - - Mutex::lockMutex(gMemMutex); -#endif - - - // first validate the free tree: - validateTree(); - // now validate all blocks: - for(PageRecord *list = gPageList; list; list = list->prevPage) - { - Header *prev = NULL; - for(Header *walk = list->headerList; walk; walk = walk->next) + if (gStackTrace) { -#ifdef TORQUE_DEBUG_GUARD - checkGuard(walk, walk->flags & Allocated); +#ifdef _WIN32 + info.backtraceSize = CaptureStackBackTrace(0, 16, info.backtracePtrs, nullptr); +#else + info.backtraceSize = backtrace(info.backtracePtrs, MaxBacktraceDepth); #endif - if(walk->prev != prev) - Platform::debugBreak(); - prev = walk; - if(walk->next && ((const char *)(walk->next) != (const char *)(walk) + sizeof(Header) + walk->size)) - Platform::debugBreak(); } + + return ptr; } -#ifdef TORQUE_MULTITHREAD - Mutex::unlockMutex(gMemMutex); -#endif -} - -#if !defined(TORQUE_DISABLE_MEMORY_MANAGER) -static void rotateLeft(TreeNode *hdr) -{ - TreeNode *temp = hdr->right; - hdr->right = temp->left; - if(temp->left != NIL) - temp->left->parent = hdr; - temp->parent = hdr->parent; - if(temp->parent == NIL) - gFreeTreeRoot = temp; - else if(hdr == hdr->parent->left) - hdr->parent->left = temp; - else - hdr->parent->right = temp; - temp->left = hdr; - hdr->parent = temp; -} -#endif - -#if !defined(TORQUE_DISABLE_MEMORY_MANAGER) -static void rotateRight(TreeNode *hdr) -{ - TreeNode *temp = hdr->left; - hdr->left = temp->right; - if(temp->right != NIL) - temp->right->parent = hdr; - temp->parent = hdr->parent; - if(temp->parent == NIL) - gFreeTreeRoot = temp; - else if(hdr == hdr->parent->left) - hdr->parent->left = temp; - else - hdr->parent->right = temp; - temp->right = hdr; - hdr->parent = temp; -} -#endif - -#if !defined(TORQUE_DISABLE_MEMORY_MANAGER) -static void treeInsert(FreeHeader *fhdr) -{ -#ifdef TORQUE_DEBUG_GUARD - checkGuard((Header *) fhdr, true); // check to see that it's got allocated guards - setGuard((Header *) fhdr, false); -#endif - //gInsertCount++; - - TreeNode *newParent = NIL; - TreeNode *walk = gFreeTreeRoot; - while(walk != NIL) + static void free(void* ptr, bool array) { - newParent = walk; - if(fhdr->size < walk->size) - walk = walk->left; - else if(fhdr->size > walk->size) - walk = walk->right; - else // tag it on the end of the queue... + if (!ptr) + return; + + if (!initialized) { - // insert it on this header... - walk->queueTail->nextQueue = fhdr; - fhdr->prevQueue = walk->queueTail; - walk->queueTail = fhdr; - fhdr->nextQueue = NULL; - fhdr->treeNode = walk; + std::free(ptr); return; } - } - TreeNode *hdr = allocTreeNode(); - hdr->size = fhdr->size; - hdr->queueHead = hdr->queueTail = fhdr; - fhdr->nextQueue = fhdr->prevQueue = NULL; - fhdr->treeNode = hdr; - hdr->left = NIL; - hdr->right = NIL; - - hdr->parent = newParent; - - if(newParent == NIL) - gFreeTreeRoot = hdr; - else if(hdr->size < newParent->size) - newParent->left = hdr; - else - newParent->right = hdr; - - // do red/black rotations - hdr->color = Red; - while(hdr != gFreeTreeRoot && (hdr->parent->color == Red)) - { - TreeNode *parent = hdr->parent; - TreeNode *pparent = hdr->parent->parent; - - if(parent == pparent->left) + for (U32 i = 0; i < allocCount; ++i) { - TreeNode *temp = pparent->right; - if(temp->color == Red) + if (allocList[i].ptr == ptr) { - parent->color = Black; - temp->color = Black; - pparent->color = Red; - hdr = pparent; - } - else - { - if(hdr == parent->right) - { - hdr = parent; - rotateLeft(hdr); - parent = hdr->parent; - pparent = hdr->parent->parent; - } - parent->color = Black; - pparent->color = Red; - rotateRight(pparent); - } - } - else - { - TreeNode *temp = pparent->left; - if(temp->color == Red) - { - parent->color = Black; - temp->color = Black; - pparent->color = Red; - hdr = pparent; - } - else - { - if(hdr == parent->left) - { - hdr = parent; - rotateRight(hdr); - parent = hdr->parent; - pparent = hdr->parent->parent; - } - parent->color = Black; - pparent->color = Red; - rotateLeft(pparent); - } - } - } - gFreeTreeRoot->color = Black; - //validateTree(); -} -#endif - -#if !defined(TORQUE_DISABLE_MEMORY_MANAGER) -static void treeRemove(FreeHeader *hdr) -{ -#ifdef TORQUE_DEBUG_GUARD - checkGuard((Header *) hdr, false); - setGuard((Header *) hdr, true); -#endif - //validateTree(); - //gRemoveCount++; - - FreeHeader *prev = hdr->prevQueue; - FreeHeader *next = hdr->nextQueue; - - if(prev) - prev->nextQueue = next; - else - hdr->treeNode->queueHead = next; - - if(next) - next->prevQueue = prev; - else - hdr->treeNode->queueTail = prev; - - if(prev || next) - return; - - TreeNode *z = hdr->treeNode; - - nil.color = Black; - - TreeNode *y, *x; - if(z->left == NIL || z->right == NIL) - y = z; - else - { - y = z->right; - while(y->left != NIL) - y = y->left; - } - if(y->left != NIL) - x = y->left; - else - x = y->right; - - x->parent = y->parent; - if(y->parent == NIL) - gFreeTreeRoot = x; - else if(y == y->parent->left) - y->parent->left = x; - else - y->parent->right = x; - - U32 yColor = y->color; - if(y != z) - { - // copy y's important fields into z (since we're going to free y) - if(z->parent->left == z) - z->parent->left = y; - else if(z->parent->right == z) - z->parent->right = y; - y->left = z->left; - y->right = z->right; - if(y->left != NIL) - y->left->parent = y; - if(y->right != NIL) - y->right->parent = y; - y->parent = z->parent; - if(z->parent == NIL) - gFreeTreeRoot = y; - y->color = z->color; - if(x->parent == z) - x->parent = y; - //validateTree(); - } - freeTreeNode(z); - - if(yColor == Black) - { - while(x != gFreeTreeRoot && x->color == Black) - { - TreeNode *w; - if(x == x->parent->left) - { - w = x->parent->right; - if(w->color == Red) - { - w->color = Black; - x->parent->color = Red; - rotateLeft(x->parent); - w = x->parent->right; - } - if(w->left->color == Black && w->right->color == Black) - { - w->color = Red; - x = x->parent; - } - else - { - if(w->right->color == Black) - { - w->left->color = Black; - rotateRight(w); - w = x->parent->right; - } - w->color = x->parent->color; - x->parent->color = Black; - w->right->color = Black; - rotateLeft(x->parent); - x = gFreeTreeRoot; - } - } - else - { - w = x->parent->left; - if(w->color == Red) - { - w->color = Black; - x->parent->color = Red; - rotateRight(x->parent); - w = x->parent->left; - } - if(w->left->color == Black && w->right->color == Black) - { - w->color = Red; - x = x->parent; - } - else - { - if(w->left->color == Black) - { - w->right->color = Black; - rotateLeft(w); - w = x->parent->left; - } - w->color = x->parent->color; - x->parent->color = Black; - w->left->color = Black; - rotateRight(x->parent); - x = gFreeTreeRoot; - } - } - } - x->color = Black; - } - //validateTree(); -} -#endif - -#if !defined(TORQUE_DISABLE_MEMORY_MANAGER) -static FreeHeader *treeFindSmallestGreaterThan(dsize_t size) -{ - TreeNode *bestMatch = NIL; - TreeNode *walk = gFreeTreeRoot; - while(walk != NIL) - { - if(size == walk->size) - return walk->queueHead; - else if(size > walk->size) - walk = walk->right; - else // size < walk->size - { - bestMatch = walk; - walk = walk->left; - } - } - //validateTree(); - if(bestMatch != NIL) - return bestMatch->queueHead; - - return NULL; -} -#endif - -/// Trigger a breakpoint if ptr is not a valid heap pointer or if its memory guards -/// have been destroyed (only if TORQUE_DEBUG_GUARD is enabled). -/// -/// @note This function does not allow interior pointers! - -void checkPtr( void* ptr ) -{ - for( HeapIterator iter; iter.isValid(); ++ iter ) - { - AllocatedHeader* header = ( AllocatedHeader* ) *iter; - if( header->getUserPtr() == ptr ) - { -#ifdef TORQUE_DEBUG_GUARD - char buffer[ 1024 ]; - if( !checkGuard( *iter, true ) ) - { - dSprintf( buffer, sizeof( buffer ), "0x%x is a valid heap pointer but has its guards corrupted", ptr ); - Platform::outputDebugString( buffer ); + std::free(ptr); + allocList[i] = allocList[allocCount - 1]; + allocList[--allocCount] = {}; return; } - //dSprintf( buffer, sizeof( buffer ), "0x%x is a valid heap pointer", ptr ); - //Platform::outputDebugString( buffer ); -#endif + } + + // Unknown pointer, still free it. + std::free(ptr); + } + + void getMemoryInfo(void* ptr, MemInfo& info) + { + if (!ptr || !initialized) return; - } - } - char buffer[ 1024 ]; - dSprintf( buffer, sizeof( buffer ), "0x%x is not a valid heap pointer", ptr ); - Platform::outputDebugString( buffer ); - - Platform::debugBreak(); -} - -/// Dump info on all memory blocks that are still allocated. -/// @note Only works if TORQUE_DISABLE_MEMORY_MANAGER is not defined; otherwise this is a NOP. - -void ensureAllFreed() -{ -#ifndef TORQUE_DISABLE_MEMORY_MANAGER - - U32 numLeaks = 0; - U32 bytesLeaked = 0; - - for( HeapIterator iter; iter.isValid(); ++ iter ) - { - AllocatedHeader* header = ( AllocatedHeader* ) *iter; - if( !( header->flags & GlobalFlag ) ) + for (U32 i = 0; i < allocCount; ++i) { - // Note: can't spill profile paths here since they by - // now are all invalid (they're on the now freed string table) - -#ifdef TORQUE_DEBUG_GUARD - Platform::outputDebugString( "MEMORY LEAKED: 0x%x %i %s %s:%i = %i (%i)", - header->getUserPtr(), - header->allocNum, - ( header->flags & StaticFlag ? "(static)" : "" ), - header->fileName, header->line, header->realSize, header->size ); - numLeaks ++; - bytesLeaked += header->size; -#endif + if (allocList[i].ptr == ptr) + { + info = allocList[i]; + return; + } } } - if( numLeaks ) - Platform::outputDebugString( "NUM LEAKS: %i (%i bytes)", numLeaks, bytesLeaked ); -#endif -} - -struct MemDumpLog -{ - U32 size; - U32 count; - U32 depthTotal; - U32 maxDepth; - U32 minDepth; -}; - -void logDumpTraverse(MemDumpLog *sizes, TreeNode *header, U32 depth) -{ - if(header == NIL) - return; - MemDumpLog *mySize = sizes; - while(mySize->size < header->size) - mySize++; - - U32 cnt = 0; - for(FreeHeader *walk = header->queueHead; walk; walk = walk->nextQueue) - cnt++; - mySize->count += cnt; - mySize->depthTotal += depth * cnt; - mySize->maxDepth = depth > mySize->maxDepth ? depth : mySize->maxDepth; - mySize->minDepth = depth < mySize->minDepth ? depth : mySize->minDepth; - logDumpTraverse(sizes, header->left, depth + 1); - logDumpTraverse(sizes, header->right, depth + 1); -} - -#ifdef TORQUE_DEBUG -DefineEngineFunction( validateMemory, void, ( ),, - "@brief Used to validate memory space for the game.\n\n" - "@ingroup Debugging" ) -{ - validate(); -} -#endif - -DefineEngineFunction( freeMemoryDump, void, ( ),, - "@brief Dumps some useful statistics regarding free memory.\n\n" - "Dumps an analysis of \'free chunks\' of memory. " - "Does not print how much memory is free.\n\n" - "@ingroup Debugging" ) -{ - U32 startSize = 16; - MemDumpLog memSizes[20]; - U32 i; - for(i = 0; i < 20; i++) + static void* realloc(void* oldPtr, dsize_t newSize, const char* fileName, U32 line) { - memSizes[i].size = startSize << i; - memSizes[i].count = 0; - memSizes[i].depthTotal = 0; - memSizes[i].maxDepth = 0; - memSizes[i].minDepth = 1000; - } - memSizes[19].size = MaxAllocationAmount; - logDumpTraverse(memSizes, gFreeTreeRoot, 1); - MemDumpLog fullMem; - fullMem.count = 0; - fullMem.depthTotal = 0; - fullMem.maxDepth = 0; - fullMem.minDepth = 1000; + if (!initialized) + return std::realloc(oldPtr, newSize); // fallback if not tracking - for(i = 0; i < 20; i++) - { - if(memSizes[i].count) - Con::printf("Size: %d - Free blocks: %d Max Depth: %d Min Depth: %d Average Depth: %g", - memSizes[i].size, memSizes[i].count, memSizes[i].maxDepth, memSizes[i].minDepth, - F32(memSizes[i].depthTotal) / F32(memSizes[i].count)); - - fullMem.count += memSizes[i].count; - fullMem.depthTotal += memSizes[i].depthTotal; - fullMem.maxDepth = memSizes[i].maxDepth > fullMem.maxDepth ? memSizes[i].maxDepth : fullMem.maxDepth; - fullMem.minDepth = memSizes[i].minDepth < fullMem.minDepth ? memSizes[i].minDepth : fullMem.minDepth; - } - Con::printf("Total free blocks: %d Max Depth: %d Min Depth: %d Average Depth: %g", - fullMem.count, fullMem.maxDepth, fullMem.minDepth, F32(fullMem.depthTotal) / F32(fullMem.count)); -} - -#ifdef TORQUE_DEBUG_GUARD - -void flagCurrentAllocs( EFlag flag ) -{ -#ifdef TORQUE_ENABLE_PROFILE_PATH - if (gProfiler && !gProfiler->isEnabled()) - { - gProfiler->enable(true); - // warm it up - //gProfiler->dumpToConsole(); - } -#endif - - U32 bit = flagToBit( flag ); - for( HeapIterator iter; iter.isValid(); ++ iter ) - iter->flags |= bit; -} - -DefineEngineFunction(flagCurrentAllocs, void, (),, - "@brief Flags all current memory allocations.\n\n" - "Flags all current memory allocations for exclusion in subsequent calls to dumpUnflaggedAllocs(). " - "Helpful in detecting memory leaks and analyzing memory usage.\n\n" - "@ingroup Debugging" ) -{ - flagCurrentAllocs(); -} - -void dumpUnflaggedAllocs(const char *file, EFlag flag) -{ - countUnflaggedAllocs(file, NULL, flag); -} - -S32 countUnflaggedAllocs(const char * filename, S32 *outUnflaggedRealloc, EFlag flag) -{ - S32 unflaggedAllocCount = 0; - S32 unflaggedReAllocCount = 0; - - FileStream fws; - bool useFile = filename && *filename; - if (useFile) - useFile = fws.open(filename, Torque::FS::File::Write); - char buffer[1024]; - - U32 bit = flagToBit( flag ); - - PageRecord* walk; - for (walk = gPageList; walk; walk = walk->prevPage) - { - for(Header *probe = walk->headerList; probe; probe = probe->next) + if (newSize == 0) { - if (probe->flags & Allocated) + free(oldPtr, false); + return nullptr; + } + + if (oldPtr == nullptr) + return alloc(newSize, false, fileName, line); + + + void* newPtr = std::realloc(oldPtr, newSize); + if (!newPtr) + return nullptr; + + + // Update existing record + for (U32 i = 0; i < allocCount; ++i) + { + if (allocList[i].ptr == oldPtr) { - AllocatedHeader* pah = (AllocatedHeader*)probe; - if (!(pah->flags & bit)) - { - // If you want to extract further information from an unflagged - // memory allocation, do the following: - // U8 *foo = (U8 *)pah; - // foo += sizeof(Header); - // FooObject *obj = (FooObject *)foo; - dSprintf(buffer, 1023, "%s%s\t%d\t%d\t%d\r\n", - pah->flags & Reallocated ? "[R] " : "", - pah->fileName != NULL ? pah->fileName : "Undetermined", - pah->line, pah->realSize, pah->allocNum); - - if( pah->flags & Reallocated ) - unflaggedReAllocCount++; - else - unflaggedAllocCount++; - - if (useFile) - { - fws.write(dStrlen(buffer), buffer); - fws.write(2, "\r\n"); - } - else - { - if( pah->flags & Reallocated ) - Con::warnf(buffer); - else - Con::errorf(buffer); - } - -#ifdef TORQUE_ENABLE_PROFILE_PATH - static char line[4096]; - dSprintf(line, sizeof(line), " %s\r\nreal size=%d", - pah->profilePath ? pah->profilePath : "unknown", - pah->realSize); - - if (useFile) - { - fws.write(dStrlen(line), line); - fws.write(2, "\r\n"); - } - else - { - if( pah->flags & Reallocated ) - Con::warnf(line); - else - Con::errorf(line); - } -#endif - - } + allocList[i].ptr = newPtr; + allocList[i].size = newSize; + allocList[i].file = fileName; + allocList[i].line = line; + allocList[i].allocId = currentAllocId++; + return newPtr; } } - } - if (useFile) - fws.close(); - - if( outUnflaggedRealloc != NULL ) - *outUnflaggedRealloc = unflaggedReAllocCount; - - return unflaggedAllocCount; -} - -DefineEngineFunction(dumpUnflaggedAllocs, void, ( const char* fileName ), ( "" ), - "@brief Dumps all unflagged memory allocations.\n\n" - "Dumps all memory allocations that were made after a call to flagCurrentAllocs(). " - "Helpful when used with flagCurrentAllocs() for detecting memory leaks and analyzing general memory usage.\n\n" - "@param fileName Optional file path and location to dump all memory allocations not flagged by flagCurrentAllocs(). " - "If left blank, data will be dumped to the console.\n\n" - "@tsexample\n" - "dumpMemSnapshot(); // dumps info to console\n" - "dumpMemSnapshot( \"C:/Torque/profilerlog1.txt\" ); // dumps info to file\n" - "@endtsexample\n\n" - "@note Available in debug builds only. " - "In torqueConfig.h, TORQUE_DISABLE_MEMORY_MANAGER must be undefined to use this function.\n\n" - "@ingroup Debugging" ) -{ - dumpUnflaggedAllocs(fileName); -} - -static void initLog() -{ - static const char* sInitString = " --- INIT MEMORY LOG (ACTION): (FILE) (LINE) (SIZE) (ALLOCNUMBER) ---\r\n"; - - FileStream fws; - fws.open(gLogFilename, Torque::FS::File::Write); - fws.write(dStrlen(sInitString), sInitString); - fws.close(); -} - -#if !defined(TORQUE_DISABLE_MEMORY_MANAGER) -static void logAlloc(const AllocatedHeader* hdr, S32 memSize) -{ - FileStream fws; - fws.open(gLogFilename, Torque::FS::File::ReadWrite); - fws.setPosition(fws.getStreamSize()); - - char buffer[1024]; - dSprintf(buffer, 1023, "alloc: %s %d %d %d\r\n", - hdr->fileName != NULL ? hdr->fileName : "Undetermined", - hdr->line, memSize, hdr->allocNum); - fws.write(dStrlen(buffer), buffer); - fws.close(); -} -#endif - -#if !defined(TORQUE_DISABLE_MEMORY_MANAGER) -static void logRealloc(const AllocatedHeader* hdr, S32 memSize) -{ - FileStream fws; - fws.open(gLogFilename, Torque::FS::File::ReadWrite); - fws.setPosition(fws.getStreamSize()); - - char buffer[1024]; - dSprintf(buffer, 1023, "realloc: %s %d %d %d\r\n", - hdr->fileName != NULL ? hdr->fileName : "Undetermined", - hdr->line, memSize, hdr->allocNum); - fws.write(dStrlen(buffer), buffer); - fws.close(); -} -#endif - -#if !defined(TORQUE_DISABLE_MEMORY_MANAGER) -static void logFree(const AllocatedHeader* hdr) -{ - FileStream fws; - fws.open(gLogFilename, Torque::FS::File::ReadWrite); - fws.setPosition(fws.getStreamSize()); - - char buffer[1024]; - dSprintf(buffer, 1023, "free: %s %d %d\r\n", - hdr->fileName != NULL ? hdr->fileName : "Undetermined", - hdr->line, hdr->allocNum); - fws.write(dStrlen(buffer), buffer); - fws.close(); -} -#endif // !defined(TORQUE_DISABLE_MEMORY_MANAGER) - -#endif - -void enableLogging(const char* fileName) -{ - dStrcpy(gLogFilename, fileName, 256); - if (!gEnableLogging) - { - gEnableLogging = true; -#ifdef TORQUE_DEBUG_GUARD - initLog(); -#endif - } -} - -void disableLogging() -{ - gLogFilename[0] = '\0'; - gEnableLogging = false; -} - -// CodeReview - this is never called so commented out to save warning. -// Do we want to re-enable it? Might be nice to get leak tracking on -// exit...or maybe that is just a problematical feature we shouldn't -// worry about. -//static void shutdown() -//{ -//#ifdef TORQUE_MULTITHREAD -// Mutex::destroyMutex(gMemMutex); -// gMemMutex = NULL; -//#endif -// -//#ifdef TORQUE_DEBUG_GUARD -// -// // write out leaks and such -// const U32 maxNumLeaks = 1024; -// U32 numLeaks = 0; -// -// AllocatedHeader* pLeaks[maxNumLeaks]; -// for (PageRecord * walk = gPageList; walk; walk = walk->prevPage) -// for(Header *probe = walk->headerList; probe; probe = probe->next) -// if ((probe->flags & Allocated) && ((AllocatedHeader *)probe)->fileName != NULL) -// pLeaks[numLeaks++] = (AllocatedHeader *) probe; -// -// if (numLeaks && !gNeverLogLeaks) -// { -// if (gAlwaysLogLeaks || Platform::AlertOKCancel("Memory Status", "Memory leaks detected. Write to memoryLeaks.log?") == true) -// { -// char buffer[1024]; -// FileStream logFile; -// logFile.open("memoryLeaks.log", Torque::FS::File::Write); -// -// for (U32 i = 0; i < numLeaks; i++) -// { -// dSprintf(buffer, 1023, "Leak in %s: %d (%d)\r\n", pLeaks[i]->fileName, pLeaks[i]->line, pLeaks[i]->allocNum); -// logFile.write(dStrlen(buffer), buffer); -// } -// logFile.close(); -// } -// } -//#endif -// -// // then free all the memory pages -// for (PageRecord * walk = gPageList; walk; ) -// { -// PageRecord *prev = walk->prevPage; -// dRealFree(walk); -// walk = prev; -// } -//} - -#if !defined(TORQUE_DISABLE_MEMORY_MANAGER) -static Header *allocMemPage(dsize_t pageSize) -{ - pageSize += sizeof(Header); - if(pageSize < MinPageSize) - pageSize = MinPageSize; - PageRecord *base = allocPage(pageSize); - - Header* rec = (Header*)base->basePtr; - base->headerList = rec; - - rec->size = pageSize - sizeof(Header); - rec->next = NULL; - rec->prev = NULL; - rec->flags = 0; -#ifdef TORQUE_DEBUG_GUARD - setGuard(rec, true); -#endif - -#ifdef LOG_PAGE_ALLOCS - gPageBytesAllocated += pageSize; - // total bytes allocated so far will be 0 when TORQUE_DEBUG_GUARD is disabled, so convert that into more meaningful string - const U32 StrSize = 256; - char strBytesAllocated[StrSize]; - if (gBytesAllocated > 0) - dSprintf(strBytesAllocated, sizeof(strBytesAllocated), "%i", gBytesAllocated); - else - dStrncpy(strBytesAllocated,"unknown - enable TORQUE_DEBUG_GUARD", StrSize); - -#ifndef TORQUE_MULTITHREAD // May deadlock. - // NOTE: This code may be called within Con::_printf, and if that is the case - // this will infinitly recurse. This is the reason for the code in Con::_printf - // that sets Con::active to false. -patw - if (Con::isActive()) - Con::errorf("PlatformMemory: allocating new page, total bytes allocated so far: %s (total bytes in all pages=%i)",strBytesAllocated,gPageBytesAllocated); -#endif -#endif - return rec; -} -#endif - -#if !defined(TORQUE_DISABLE_MEMORY_MANAGER) -static void checkUnusedAlloc(FreeHeader *header, U32 size) -{ - //validate(); - if(header->size >= size + sizeof(Header) + 16) - { - U8 *basePtr = (U8 *) header; - basePtr += sizeof(Header); - FreeHeader *newHeader = (FreeHeader *) (basePtr + size); - newHeader->next = header->next; - newHeader->prev = (Header *) header; - header->next = (Header *) newHeader; - if(newHeader->next) - newHeader->next->prev = (Header *) newHeader; - newHeader->flags = 0; - newHeader->size = header->size - size - sizeof(Header); - header->size = size; -#ifdef TORQUE_DEBUG_GUARD - setGuard((Header *) newHeader, true); -#endif - treeInsert(newHeader); - } -} -#endif - -#if defined(TORQUE_MULTITHREAD) && !defined(TORQUE_DISABLE_MEMORY_MANAGER) -static bool gReentrantGuard = false; -#endif - -#if !defined(TORQUE_DISABLE_MEMORY_MANAGER) -static void* alloc(dsize_t size, bool array, const char* fileName, const U32 line) -{ - AssertFatal(size < MaxAllocationAmount, "Memory::alloc - tried to allocate > MaxAllocationAmount!"); - -#ifdef TORQUE_MULTITHREAD - if(!gMemMutex && !gReentrantGuard) - { - gReentrantGuard = true; - gMemMutex = Mutex::createMutex(); - gReentrantGuard = false; - } - - if(!gReentrantGuard) - Mutex::lockMutex(gMemMutex); - -#endif - - AssertFatal(size < MaxAllocationAmount, "Size error."); - //validate(); - if (size == 0) - { -#ifdef TORQUE_MULTITHREAD - if(!gReentrantGuard) - Mutex::unlockMutex(gMemMutex); -#endif - return NULL; - } - -#ifndef TORQUE_ENABLE_PROFILE_PATH - // Note: will cause crash if profile path is on - PROFILE_START(MemoryAlloc); -#endif - -#ifdef TORQUE_DEBUG_GUARD - // if we're guarding, round up to the nearest DWORD - size = ((size + 3) & ~0x3); -#else - // round up size to nearest 16 byte boundary (cache lines and all...) - size = ((size + 15) & ~0xF); -#endif - - FreeHeader *header = treeFindSmallestGreaterThan(size); - if(header) - treeRemove(header); - else - header = (FreeHeader *) allocMemPage(size); - - // ok, see if there's enough room in the block to make another block - // for this to happen it has to have enough room for a header - // and 16 more bytes. - - U8 *basePtr = (U8 *) header; - basePtr += sizeof(Header); - - checkUnusedAlloc(header, size); - - AllocatedHeader *retHeader = (AllocatedHeader *) header; - retHeader->flags = array ? (Allocated | Array) : Allocated; - -#ifdef TORQUE_DEBUG_GUARD - retHeader->line = line; - retHeader->fileName = fileName; - retHeader->allocNum = gCurrAlloc; - retHeader->realSize = size; -#ifdef TORQUE_ENABLE_PROFILE_PATH - retHeader->profilePath = gProfiler ? gProfiler->getProfilePath() : "pre"; -#endif - gBytesAllocated += size; - gBlocksAllocated ++; - //static U32 skip = 0; - //if ((++skip % 1000) == 0) - // Con::printf("new=%i, newnew=%i, imagenew=%i",gBytesAllocated,gNewNewTotal,gImageAlloc); - if (gEnableLogging) - logAlloc(retHeader, size); -#endif - if(gCurrAlloc == gBreakAlloc && gBreakAlloc != 0xFFFFFFFF) - Platform::debugBreak(); - gCurrAlloc++; -#ifndef TORQUE_ENABLE_PROFILE_PATH - PROFILE_END(); -#endif - //validate(); - -#ifdef TORQUE_DEBUG - // fill the block with the fill value. although this is done in free(), that won't fill - // newly allocated MM memory (which hasn't been freed yet). We use a different fill value - // to diffentiate filled freed memory from filled new memory; this may aid debugging. - #ifndef TORQUE_ENABLE_PROFILE_PATH - PROFILE_START(stompMem1); - #endif - dMemset(basePtr, 0xCF, size); - #ifndef TORQUE_ENABLE_PROFILE_PATH - PROFILE_END(); - #endif -#endif - - if(gCurrAlloc == gBreakAlloc && gBreakAlloc != 0xFFFFFFFF) - Platform::debugBreak(); - - gCurrAlloc++; - -#ifdef TORQUE_MULTITHREAD - if(!gReentrantGuard) - Mutex::unlockMutex(gMemMutex); -#endif - - return basePtr; -} -#endif - -#if !defined(TORQUE_DISABLE_MEMORY_MANAGER) -static void free(void* mem, bool array) -{ - // validate(); - - if (!mem) - return; - -#ifdef TORQUE_MULTITHREAD - if(!gMemMutex) - gMemMutex = Mutex::createMutex(); - - if( mem != gMemMutex ) - Mutex::lockMutex(gMemMutex); - else - gMemMutex = NULL; -#endif - - PROFILE_START(MemoryFree); - AllocatedHeader *hdr = ((AllocatedHeader *)mem) - 1; - - AssertFatal(hdr->flags & Allocated, avar("Not an allocated block!")); - AssertFatal(((bool)((hdr->flags & Array)==Array))==array, avar("Array alloc mismatch. ")); - - gBlocksAllocated --; -#ifdef TORQUE_DEBUG_GUARD - gBytesAllocated -= hdr->realSize; - if (gEnableLogging) - logFree(hdr); -#endif - - hdr->flags = 0; - - // fill the block with the fill value - -#ifdef TORQUE_DEBUG - #ifndef TORQUE_ENABLE_PROFILE_PATH - PROFILE_START(stompMem2); - #endif - dMemset(mem, 0xCE, hdr->size); - #ifndef TORQUE_ENABLE_PROFILE_PATH - PROFILE_END(); - #endif -#endif - - // see if we can merge hdr with the block after it. - - Header* next = hdr->next; - if (next && next->flags == 0) - { - treeRemove((FreeHeader *) next); - hdr->size += next->size + sizeof(Header); - hdr->next = next->next; - if(next->next) - next->next->prev = (Header *) hdr; - } - - // see if we can merge hdr with the block before it. - Header* prev = hdr->prev; - - if (prev && prev->flags == 0) - { - treeRemove((FreeHeader *) prev); - prev->size += hdr->size + sizeof(Header); - prev->next = hdr->next; - if (hdr->next) - hdr->next->prev = prev; - - hdr = (AllocatedHeader *) prev; - } - - // throw this puppy into the tree! - treeInsert((FreeHeader *) hdr); - PROFILE_END(); - -// validate(); - -#ifdef TORQUE_MULTITHREAD - Mutex::unlockMutex(gMemMutex); -#endif -} -#endif - -#if !defined(TORQUE_DISABLE_MEMORY_MANAGER) -static void* realloc(void* mem, dsize_t size, const char* fileName, const U32 line) -{ - //validate(); - if (!size) { - free(mem, false); - return NULL; - } - if(!mem) - return alloc(size, false, fileName, line); - -#ifdef TORQUE_MULTITHREAD - if(!gMemMutex) - gMemMutex = Mutex::createMutex(); - - Mutex::lockMutex(gMemMutex); -#endif - - AllocatedHeader* hdr = ((AllocatedHeader *)mem) - 1; -#ifdef TORQUE_DEBUG_GUARD - checkGuard( ( Header* ) hdr, true ); -#endif - - AssertFatal((hdr->flags & Allocated) == Allocated, "Bad block flags."); - - size = (size + 0xF) & ~0xF; - - U32 oldSize = hdr->size; - if(oldSize == size) - { -#ifdef TORQUE_MULTITHREAD - Mutex::unlockMutex(gMemMutex); -#endif - return mem; - } - PROFILE_START(MemoryRealloc); - - FreeHeader *next = (FreeHeader *) hdr->next; - -#ifdef TORQUE_DEBUG_GUARD - // adjust header size and allocated bytes size - hdr->realSize += size - oldSize; - gBytesAllocated += size - oldSize; - if (gEnableLogging) - logRealloc(hdr, size); - - // Add reallocated flag, note header changes will not persist if the realloc - // decides tofree, and then perform a fresh allocation for the memory. The flag will - // be manually set again after this takes place, down at the bottom of this fxn. - hdr->flags |= Reallocated; - - // Note on Above ^ - // A more useful/robust implementation can be accomplished by storing an additional - // AllocatedHeader* in DEBUG_GUARD builds inside the AllocatedHeader structure - // itself to create a sort of reallocation history. This will be, essentially, - // a allocation header stack for each allocation. Each time the memory is reallocated - // it should use dRealMalloc (IMPORTANT!!) to allocate a AllocatedHeader* and chain - // it to the reallocation history chain, and the dump output changed to display - // reallocation history. It is also important to clean up this chain during 'free' - // using dRealFree (Since memory for the chain was allocated via dRealMalloc). - // - // See patw for details. -#endif - if (next && !(next->flags & Allocated) && next->size + hdr->size + sizeof(Header) >= size) - { - // we can merge with the next dude. - treeRemove(next); - hdr->size += sizeof(Header) + next->size; - hdr->next = next->next; - if(next->next) - next->next->prev = (Header *) hdr; - - checkUnusedAlloc((FreeHeader *) hdr, size); - //validate(); - PROFILE_END(); -#ifdef TORQUE_MULTITHREAD - Mutex::unlockMutex(gMemMutex); -#endif - return mem; - } - else if(size < oldSize) - { - checkUnusedAlloc((FreeHeader *) hdr, size); - PROFILE_END(); -#ifdef TORQUE_MULTITHREAD - Mutex::unlockMutex(gMemMutex); -#endif - return mem; - } -#ifdef TORQUE_DEBUG_GUARD - // undo above adjustment because we're going though alloc instead - hdr->realSize -= size - oldSize; - gBytesAllocated -= size - oldSize; -#endif - void* ret = alloc(size, false, fileName, line); - dMemcpy(ret, mem, oldSize); - free(mem, false); - PROFILE_END(); - - // Re-enable the 'Reallocated' flag so that this allocation can be ignored by - // a non-strict run of the flag/dumpunflagged. - hdr = ((AllocatedHeader *)ret) - 1; - hdr->flags |= Reallocated; - -#ifdef TORQUE_MULTITHREAD - Mutex::unlockMutex(gMemMutex); -#endif - return ret; -} -#endif - -dsize_t getMemoryUsed() -{ - U32 size = 0; - - PageRecord* walk; - for (walk = gPageList; walk; walk = walk->prevPage) { - for(Header *probe = walk->headerList; probe; probe = probe->next) - if (probe->flags & Allocated) { - size += probe->size; - } - } - - return size; -} - -#ifdef TORQUE_DEBUG_GUARD -DefineEngineFunction( dumpAlloc, void, ( S32 allocNum ),, - "@brief Dumps information about the given allocated memory block.\n\n" - "@param allocNum Memory block to dump information about." - "@note Available in debug builds only. " - "In torqueConfig.h, TORQUE_DISABLE_MEMORY_MANAGER must be undefined to use this function.\n\n" - "@ingroup Debugging") -{ - PageRecord* walk; - for( walk = gPageList; walk; walk = walk->prevPage ) - for( Header* probe = walk->headerList; probe; probe = probe->next ) - if( probe->flags & Allocated ) + // Not found — see if newPtr is already being tracked + for (U32 i = 0; i < allocCount; ++i) + { + if (allocList[i].ptr == newPtr) { - AllocatedHeader* pah = ( AllocatedHeader* ) probe; - if( pah->allocNum == allocNum ) - { - Con::printf( "file: %s\n" - "line: %i\n" - "size: %i\n" - "allocNum: %i\n" - "reallocated: %s", - pah->fileName != NULL ? pah->fileName : "Undetermined", - pah->line, - pah->realSize, - pah->allocNum, - pah->flags & Reallocated ? "yes" : "no" - ); - - // Dump the profile path, if we have one. - - #ifdef TORQUE_ENABLE_PROFILE_PATH - if( pah->profilePath && pah->profilePath[ 0 ] ) - Con::printf( "profilepath: %s", pah->profilePath ); - #endif - } - } -} + allocList[i].size = newSize; + allocList[i].file = fileName; + allocList[i].line = line; + allocList[i].allocId = currentAllocId++; -DefineEngineFunction( dumpMemSnapshot, void, ( const char* fileName ),, - "@brief Dumps a snapshot of current memory to a file.\n\n" - "The total memory used will also be output to the console.\n" - "This function will attempt to create the file if it does not already exist.\n" - "@param fileName Name and path of file to save profiling stats to. Must use forward slashes (/)\n" - "@tsexample\n" - "dumpMemSnapshot( \"C:/Torque/ProfilerLogs/profilerlog1.txt\" );\n" - "@endtsexample\n\n" - "@note Available in debug builds only. " - "In torqueConfig.h, TORQUE_DISABLE_MEMORY_MANAGER must be undefined to use this function.\n\n" - "@ingroup Debugging") -{ - FileStream fws; - fws.open(fileName, Torque::FS::File::Write); - char buffer[ 2048 ]; - - PageRecord* walk; - for (walk = gPageList; walk; walk = walk->prevPage) { - for(Header *probe = walk->headerList; probe; probe = probe->next) - if (probe->flags & Allocated) { - AllocatedHeader* pah = (AllocatedHeader*)probe; - - dSprintf( buffer, sizeof( buffer ), "%s%s\t%d\t%d\t%d\r\n", - pah->flags & Reallocated ? "[R] " : "", - pah->fileName != NULL ? pah->fileName : "Undetermined", - pah->line, pah->realSize, pah->allocNum); - fws.write(dStrlen(buffer), buffer); - - // Dump the profile path, if we have one. - - #ifdef TORQUE_ENABLE_PROFILE_PATH - if( pah->profilePath ) - { - dSprintf( buffer, sizeof( buffer ), "%s\r\n\r\n", pah->profilePath ); - fws.write( dStrlen( buffer ), buffer ); - } - #endif + return newPtr; } + } + + // Still not found — treat as a new allocation + if (allocCount < MaxAllocs) + { + MemInfo& info = allocList[allocCount++]; + info = {}; + info.ptr = newPtr; + info.size = newSize; + info.file = fileName; + info.line = line; + info.allocId = currentAllocId++; + } + return newPtr; } - Con::errorf("total memory used: %d",getMemoryUsed()); - fws.close(); -} #endif - -dsize_t getMemoryAllocated() -{ - return 0; } - -void getMemoryInfo( void* ptr, Info& info ) -{ - #ifndef TORQUE_DISABLE_MEMORY_MANAGER - - AllocatedHeader* header = ( ( AllocatedHeader* ) ptr ) - 1; - - info.mAllocSize = header->size; - #ifdef TORQUE_DEBUG_GUARD - info.mAllocNumber = header->allocNum; - info.mLineNumber = header->line; - info.mFileName = header->fileName; - #endif - info.mIsArray = header->flags & Array; - info.mIsGlobal = header->flags & GlobalFlag; - info.mIsStatic = header->flags & StaticFlag; - - #endif -} - -void setBreakAlloc(U32 breakAlloc) -{ - gBreakAlloc = breakAlloc; -} - -ConsoleFunctionGroupEnd( Memory ); - -} // namespace Memory - -void setMinimumAllocUnit(U32 allocUnit) -{ - AssertFatal(isPow2(allocUnit) && allocUnit > (2 << 20), - "Error, allocunit must be a power of two, and greater than 2 megs"); - - MinPageSize = allocUnit; -} - //--------------------------------------------------------------------------- //--------------------------------------------------------------------------- @@ -1763,14 +419,12 @@ void* dRealloc_r(void* in_pResize, dsize_t in_size, const char* fileName, const return Memory::realloc(in_pResize, in_size, fileName, line); } -AFTER_MODULE_INIT( Sim ) +DefineEngineFunction(LeakTrace, void, (bool start, bool stackTrace), (true, true), "start/stop tracing leaks") { - Con::addVariable( "$Memory::numBlocksAllocated", TypeS32, &Memory::gBlocksAllocated, - "Total number of memory blocks currently allocated.\n\n" - "@ingroup Debugging" ); - Con::addVariable( "$Memory::numBytesAllocated", TypeS32, &Memory::gBytesAllocated, - "Total number of bytes currently allocated.\n\n" - "@ingroup Debugging" ); + if (Memory::initialized) Memory::shutdown(); + if (start) Memory::init(); + Memory::gFromScript = true; + Memory::gStackTrace = stackTrace; } #else diff --git a/Engine/source/platform/platformMemory.h b/Engine/source/platform/platformMemory.h index 96db06584..c9b74c750 100644 --- a/Engine/source/platform/platformMemory.h +++ b/Engine/source/platform/platformMemory.h @@ -27,33 +27,25 @@ namespace Memory { - enum EFlag +#if !defined(TORQUE_DISABLE_MEMORY_MANAGER) + struct MemInfo { - FLAG_Debug, - FLAG_Global, - FLAG_Static + void* ptr; + dsize_t size; + const char* file; + U32 line; + U32 allocId; + bool flagged; + + void* backtracePtrs[16]; + int backtraceSize; }; - struct Info - { - U32 mAllocNumber; - U32 mAllocSize; - const char* mFileName; - U32 mLineNumber; - bool mIsArray; - bool mIsGlobal; - bool mIsStatic; - }; - - void checkPtr( void* ptr ); - void flagCurrentAllocs( EFlag flag = FLAG_Debug ); - void ensureAllFreed(); - void dumpUnflaggedAllocs(const char *file, EFlag flag = FLAG_Debug ); - S32 countUnflaggedAllocs(const char *file, S32 *outUnflaggedRealloc = NULL, EFlag flag = FLAG_Debug ); - dsize_t getMemoryUsed(); - dsize_t getMemoryAllocated(); - void getMemoryInfo( void* ptr, Info& info ); - void validate(); + void init(); + void shutdown(); + void getMemoryInfo(void* ptr, MemInfo& info); + void checkPtr(void* ptr); +#endif } #endif // _TORQUE_PLATFORM_PLATFORMMEMORY_H_ diff --git a/Engine/source/platformWin32/winWindow.cpp b/Engine/source/platformWin32/winWindow.cpp index 011884ce4..4661140d1 100644 --- a/Engine/source/platformWin32/winWindow.cpp +++ b/Engine/source/platformWin32/winWindow.cpp @@ -317,6 +317,8 @@ void Platform::shutdown() GFXDevice::destroy(); WinConsole::destroy(); + + UTF16ClearCache(); } extern bool LinkConsoleFunctions; diff --git a/Engine/source/scene/zones/sceneZoneSpaceManager.cpp b/Engine/source/scene/zones/sceneZoneSpaceManager.cpp index 84f733d03..b610d1724 100644 --- a/Engine/source/scene/zones/sceneZoneSpaceManager.cpp +++ b/Engine/source/scene/zones/sceneZoneSpaceManager.cpp @@ -922,7 +922,7 @@ void SceneZoneSpaceManager::verifyState() AssertFatal( mObjectZoneLists.containsBinItem(object->mZoneListHandle, zoneId), "SceneZoneSpaceManager::verifyState - Object doesn't have zone in list!"); #ifndef TORQUE_DISABLE_MEMORY_MANAGER - Memory::checkPtr( ref->object ); + Memory::checkPtr( object ); #endif } } diff --git a/Engine/source/testing/ScriptTest.cpp b/Engine/source/testing/ScriptTest.cpp index d13d2b6f2..7b99613d3 100644 --- a/Engine/source/testing/ScriptTest.cpp +++ b/Engine/source/testing/ScriptTest.cpp @@ -32,7 +32,8 @@ inline ConsoleValue RunScript(const char* str) { - return std::move(Con::evaluate(str, false, NULL).value); + auto conRes = Con::evaluate(str, false, NULL); + return conRes.value; } using ::testing::Matcher; diff --git a/Engine/source/ts/assimp/assimpAppMaterial.cpp b/Engine/source/ts/assimp/assimpAppMaterial.cpp index d761f1a18..fce979dfc 100644 --- a/Engine/source/ts/assimp/assimpAppMaterial.cpp +++ b/Engine/source/ts/assimp/assimpAppMaterial.cpp @@ -29,12 +29,23 @@ #include "ts/tsMaterialList.h" #include "core/stream/fileStream.h" +#if !defined(TORQUE_DISABLE_MEMORY_MANAGER) +#ifdef new +#undef new +#endif +#endif + // assimp include files. #include #include #include #include +#if !defined(TORQUE_DISABLE_MEMORY_MANAGER) +# define _new new(__FILE__, __LINE__) +# define new _new +#endif + U32 AssimpAppMaterial::sDefaultMatNumber = 0; String AppMaterial::cleanString(const String& str) diff --git a/Engine/source/ts/assimp/assimpAppMaterial.h b/Engine/source/ts/assimp/assimpAppMaterial.h index 0e973aaeb..4b80b5430 100644 --- a/Engine/source/ts/assimp/assimpAppMaterial.h +++ b/Engine/source/ts/assimp/assimpAppMaterial.h @@ -26,8 +26,20 @@ #ifndef _APPMATERIAL_H_ #include "ts/loader/appMaterial.h" #endif + +#if !defined(TORQUE_DISABLE_MEMORY_MANAGER) +#ifdef new +#undef new +#endif +#endif + #include +#if !defined(TORQUE_DISABLE_MEMORY_MANAGER) +# define _new new(__FILE__, __LINE__) +# define new _new +#endif + class Material; class AssimpAppMaterial : public AppMaterial diff --git a/Engine/source/ts/assimp/assimpAppMesh.cpp b/Engine/source/ts/assimp/assimpAppMesh.cpp index bd116869a..0eb015473 100644 --- a/Engine/source/ts/assimp/assimpAppMesh.cpp +++ b/Engine/source/ts/assimp/assimpAppMesh.cpp @@ -24,12 +24,24 @@ #include "ts/collada/colladaExtensions.h" #include "ts/assimp/assimpAppMesh.h" + +#if !defined(TORQUE_DISABLE_MEMORY_MANAGER) +#ifdef new +#undef new +#endif +#endif + // assimp include files. #include #include #include #include +#if !defined(TORQUE_DISABLE_MEMORY_MANAGER) +# define _new new(__FILE__, __LINE__) +# define new _new +#endif + bool AssimpAppMesh::fixedSizeEnabled = false; S32 AssimpAppMesh::fixedSize = 2; diff --git a/Engine/source/ts/assimp/assimpAppNode.cpp b/Engine/source/ts/assimp/assimpAppNode.cpp index ac1802468..62d1ad5a3 100644 --- a/Engine/source/ts/assimp/assimpAppNode.cpp +++ b/Engine/source/ts/assimp/assimpAppNode.cpp @@ -25,12 +25,25 @@ #include "ts/assimp/assimpAppNode.h" #include "ts/assimp/assimpAppMesh.h" + +#if !defined(TORQUE_DISABLE_MEMORY_MANAGER) +#ifdef new +#undef new +#endif +#endif + // assimp include files. #include #include #include #include +#if !defined(TORQUE_DISABLE_MEMORY_MANAGER) +# define _new new(__FILE__, __LINE__) +# define new _new +#endif + + aiAnimation* AssimpAppNode::sActiveSequence = NULL; F32 AssimpAppNode::sTimeMultiplier = 1.0f; diff --git a/Engine/source/ts/assimp/assimpAppNode.h b/Engine/source/ts/assimp/assimpAppNode.h index db5e18a81..311b42d93 100644 --- a/Engine/source/ts/assimp/assimpAppNode.h +++ b/Engine/source/ts/assimp/assimpAppNode.h @@ -33,11 +33,23 @@ #include "ts/collada/colladaExtensions.h" #endif + +#if !defined(TORQUE_DISABLE_MEMORY_MANAGER) +#ifdef new +#undef new +#endif +#endif + #ifndef AI_TYPES_H_INC #include #endif #include +#if !defined(TORQUE_DISABLE_MEMORY_MANAGER) +# define _new new(__FILE__, __LINE__) +# define new _new +#endif + class AssimpAppMesh; class AssimpAppNode : public AppNode diff --git a/Engine/source/ts/assimp/assimpAppSequence.h b/Engine/source/ts/assimp/assimpAppSequence.h index 1c4ac16dd..5468e1ce0 100644 --- a/Engine/source/ts/assimp/assimpAppSequence.h +++ b/Engine/source/ts/assimp/assimpAppSequence.h @@ -18,8 +18,20 @@ #include "ts/loader/appSequence.h" #endif +#if !defined(TORQUE_DISABLE_MEMORY_MANAGER) +#ifdef new +#undef new +#endif +#endif + #include +#if !defined(TORQUE_DISABLE_MEMORY_MANAGER) +# define _new new(__FILE__, __LINE__) +# define new _new +#endif + + class AssimpAppSequence : public AppSequence { String mSequenceName; diff --git a/Engine/source/ts/assimp/assimpShapeLoader.cpp b/Engine/source/ts/assimp/assimpShapeLoader.cpp index 19162ac82..c261f11cd 100644 --- a/Engine/source/ts/assimp/assimpShapeLoader.cpp +++ b/Engine/source/ts/assimp/assimpShapeLoader.cpp @@ -51,6 +51,11 @@ #include "gfx/bitmap/gBitmap.h" #include "gui/controls/guiTreeViewCtrl.h" +#if !defined(TORQUE_DISABLE_MEMORY_MANAGER) +#ifdef new +#undef new +#endif +#endif // assimp include files. #include #include @@ -59,6 +64,12 @@ #include #include +#if !defined(TORQUE_DISABLE_MEMORY_MANAGER) +# define _new new(__FILE__, __LINE__) +# define new _new +#endif + + MODULE_BEGIN( AssimpShapeLoader ) MODULE_INIT_AFTER( ShapeLoader ) MODULE_INIT diff --git a/Tools/CMake/torqueConfig.h.in b/Tools/CMake/torqueConfig.h.in index 717c05eb1..11eba5fbe 100644 --- a/Tools/CMake/torqueConfig.h.in +++ b/Tools/CMake/torqueConfig.h.in @@ -55,6 +55,9 @@ /// Define me if you want to disable Torque memory manager. #cmakedefine TORQUE_DISABLE_MEMORY_MANAGER +#ifndef TORQUE_ENABLE_ASSERTS // disable memory manager when in release build. +#define TORQUE_DISABLE_MEMORY_MANAGER +#endif /// Define me if you want to disable the virtual mount system. #cmakedefine TORQUE_DISABLE_VIRTUAL_MOUNT_SYSTEM