123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074 |
- --
- -- Collect memory reference info.
- -- https://github.com/yaukeywang/LuaMemorySnapshotDump
- --
- -- @filename MemoryReferenceInfo.lua
- -- @author WangYaoqi
- -- @date 2016-02-03
- -- The global config of the mri.
- local cConfig =
- {
- m_bAllMemoryRefFileAddTime = true,
- m_bSingleMemoryRefFileAddTime = true,
- m_bComparedMemoryRefFileAddTime = true
- }
- -- Get the format string of date time.
- local function FormatDateTimeNow()
- local cDateTime = os.date("*t")
- local strDateTime = string.format("%04d%02d%02d-%02d%02d%02d", tostring(cDateTime.year), tostring(cDateTime.month), tostring(cDateTime.day),
- tostring(cDateTime.hour), tostring(cDateTime.min), tostring(cDateTime.sec))
- return strDateTime
- end
- -- Get the string result without overrided __tostring.
- local function GetOriginalToStringResult(cObject)
- if not cObject then
- return ""
- end
- local cMt = getmetatable(cObject)
- if not cMt then
- return tostring(cObject)
- end
- -- Check tostring override.
- local strName = ""
- local cToString = rawget(cMt, "__tostring")
- if cToString then
- rawset(cMt, "__tostring", nil)
- strName = tostring(cObject)
- rawset(cMt, "__tostring", cToString)
- else
- strName = tostring(cObject)
- end
- return strName
- end
- -- Create a container to collect the mem ref info results.
- local function CreateObjectReferenceInfoContainer()
- -- Create new container.
- local cContainer = {}
- -- Contain [table/function] - [reference count] info.
- local cObjectReferenceCount = {}
- setmetatable(cObjectReferenceCount, {__mode = "k"})
- -- Contain [table/function] - [name] info.
- local cObjectAddressToName = {}
- setmetatable(cObjectAddressToName, {__mode = "k"})
- -- Set members.
- cContainer.m_cObjectReferenceCount = cObjectReferenceCount
- cContainer.m_cObjectAddressToName = cObjectAddressToName
- -- For stack info.
- cContainer.m_nStackLevel = -1
- cContainer.m_strShortSrc = "None"
- cContainer.m_nCurrentLine = -1
- return cContainer
- end
- -- Create a container to collect the mem ref info results from a dumped file.
- -- strFilePath - The file path.
- local function CreateObjectReferenceInfoContainerFromFile(strFilePath)
- -- Create a empty container.
- local cContainer = CreateObjectReferenceInfoContainer()
- cContainer.m_strShortSrc = strFilePath
- -- Cache ref info.
- local cRefInfo = cContainer.m_cObjectReferenceCount
- local cNameInfo = cContainer.m_cObjectAddressToName
- -- Read each line from file.
- local cFile = assert(io.open(strFilePath, "rb"))
- for strLine in cFile:lines() do
- local strHeader = string.sub(strLine, 1, 2)
- if "--" ~= strHeader then
- local _, _, strAddr, strName, strRefCount= string.find(strLine, "(.+)\t(.*)\t(%d+)")
- if strAddr then
- cRefInfo[strAddr] = strRefCount
- cNameInfo[strAddr] = strName
- end
- end
- end
- -- Close and clear file handler.
- io.close(cFile)
- cFile = nil
- return cContainer
- end
- -- Create a container to collect the mem ref info results from a dumped file.
- -- strObjectName - The object name you need to collect info.
- -- cObject - The object you need to collect info.
- local function CreateSingleObjectReferenceInfoContainer(strObjectName, cObject)
- -- Create new container.
- local cContainer = {}
- -- Contain [address] - [true] info.
- local cObjectExistTag = {}
- setmetatable(cObjectExistTag, {__mode = "k"})
- -- Contain [name] - [true] info.
- local cObjectAliasName = {}
- -- Contain [access] - [true] info.
- local cObjectAccessTag = {}
- setmetatable(cObjectAccessTag, {__mode = "k"})
- -- Set members.
- cContainer.m_cObjectExistTag = cObjectExistTag
- cContainer.m_cObjectAliasName = cObjectAliasName
- cContainer.m_cObjectAccessTag = cObjectAccessTag
- -- For stack info.
- cContainer.m_nStackLevel = -1
- cContainer.m_strShortSrc = "None"
- cContainer.m_nCurrentLine = -1
- -- Init with object values.
- cContainer.m_strObjectName = strObjectName
- cContainer.m_strAddressName = (("string" == type(cObject)) and ("\"" .. tostring(cObject) .. "\"")) or GetOriginalToStringResult(cObject)
- cContainer.m_cObjectExistTag[cObject] = true
- return cContainer
- end
- -- Collect memory reference info from a root table or function.
- -- strName - The root object name that start to search, default is "_G" if leave this to nil.
- -- cObject - The root object that start to search, default is _G if leave this to nil.
- -- cDumpInfoContainer - The container of the dump result info.
- local function CollectObjectReferenceInMemory(strName, cObject, cDumpInfoContainer)
- if not cObject then
- return
- end
- if not strName then
- strName = ""
- end
- -- Check container.
- if (not cDumpInfoContainer) then
- cDumpInfoContainer = CreateObjectReferenceInfoContainer()
- end
- -- Check stack.
- if cDumpInfoContainer.m_nStackLevel > 0 then
- local cStackInfo = debug.getinfo(cDumpInfoContainer.m_nStackLevel, "Sl")
- if cStackInfo then
- cDumpInfoContainer.m_strShortSrc = cStackInfo.short_src
- cDumpInfoContainer.m_nCurrentLine = cStackInfo.currentline
- end
- cDumpInfoContainer.m_nStackLevel = -1
- end
- -- Get ref and name info.
- local cRefInfoContainer = cDumpInfoContainer.m_cObjectReferenceCount
- local cNameInfoContainer = cDumpInfoContainer.m_cObjectAddressToName
- local strType = type(cObject)
- if "table" == strType then
- -- Check table with class name.
- if rawget(cObject, "__cname") then
- if "string" == type(cObject.__cname) then
- strName = strName .. "[class:" .. cObject.__cname .. "]"
- end
- elseif rawget(cObject, "class") then
- if "string" == type(cObject.class) then
- strName = strName .. "[class:" .. cObject.class .. "]"
- end
- elseif rawget(cObject, "_className") then
- if "string" == type(cObject._className) then
- strName = strName .. "[class:" .. cObject._className .. "]"
- end
- end
- -- Check if table is _G.
- if cObject == _G then
- strName = strName .. "[_G]"
- end
- -- Get metatable.
- local bWeakK = false
- local bWeakV = false
- local cMt = getmetatable(cObject)
- if cMt then
- -- Check mode.
- local strMode = rawget(cMt, "__mode")
- if strMode then
- if "k" == strMode then
- bWeakK = true
- elseif "v" == strMode then
- bWeakV = true
- elseif "kv" == strMode then
- bWeakK = true
- bWeakV = true
- end
- end
- end
- -- Add reference and name.
- cRefInfoContainer[cObject] = (cRefInfoContainer[cObject] and (cRefInfoContainer[cObject] + 1)) or 1
- if cNameInfoContainer[cObject] then
- return
- end
- -- Set name.
- cNameInfoContainer[cObject] = strName
- -- Dump table key and value.
- for k, v in pairs(cObject) do
- -- Check key type.
- local strKeyType = type(k)
- if "table" == strKeyType then
- if not bWeakK then
- CollectObjectReferenceInMemory(strName .. ".[table:key.table]", k, cDumpInfoContainer)
- end
- if not bWeakV then
- CollectObjectReferenceInMemory(strName .. ".[table:value]", v, cDumpInfoContainer)
- end
- elseif "function" == strKeyType then
- if not bWeakK then
- CollectObjectReferenceInMemory(strName .. ".[table:key.function]", k, cDumpInfoContainer)
- end
- if not bWeakV then
- CollectObjectReferenceInMemory(strName .. ".[table:value]", v, cDumpInfoContainer)
- end
- elseif "thread" == strKeyType then
- if not bWeakK then
- CollectObjectReferenceInMemory(strName .. ".[table:key.thread]", k, cDumpInfoContainer)
- end
- if not bWeakV then
- CollectObjectReferenceInMemory(strName .. ".[table:value]", v, cDumpInfoContainer)
- end
- elseif "userdata" == strKeyType then
- if not bWeakK then
- CollectObjectReferenceInMemory(strName .. ".[table:key.userdata]", k, cDumpInfoContainer)
- end
- if not bWeakV then
- CollectObjectReferenceInMemory(strName .. ".[table:value]", v, cDumpInfoContainer)
- end
- else
- CollectObjectReferenceInMemory(strName .. "." .. k, v, cDumpInfoContainer)
- end
- end
- -- Dump metatable.
- if cMt then
- CollectObjectReferenceInMemory(strName ..".[metatable]", cMt, cDumpInfoContainer)
- end
- elseif "function" == strType then
- -- Get function info.
- local cDInfo = debug.getinfo(cObject, "Su")
- -- Write this info.
- cRefInfoContainer[cObject] = (cRefInfoContainer[cObject] and (cRefInfoContainer[cObject] + 1)) or 1
- if cNameInfoContainer[cObject] then
- return
- end
- -- Set name.
- cNameInfoContainer[cObject] = strName .. "[line:" .. tostring(cDInfo.linedefined) .. "@file:" .. cDInfo.short_src .. "]"
- -- Get upvalues.
- local nUpsNum = cDInfo.nups
- for i = 1, nUpsNum do
- local strUpName, cUpValue = debug.getupvalue(cObject, i)
- local strUpValueType = type(cUpValue)
- --print(strUpName, cUpValue)
- if "table" == strUpValueType then
- CollectObjectReferenceInMemory(strName .. ".[ups:table:" .. strUpName .. "]", cUpValue, cDumpInfoContainer)
- elseif "function" == strUpValueType then
- CollectObjectReferenceInMemory(strName .. ".[ups:function:" .. strUpName .. "]", cUpValue, cDumpInfoContainer)
- elseif "thread" == strUpValueType then
- CollectObjectReferenceInMemory(strName .. ".[ups:thread:" .. strUpName .. "]", cUpValue, cDumpInfoContainer)
- elseif "userdata" == strUpValueType then
- CollectObjectReferenceInMemory(strName .. ".[ups:userdata:" .. strUpName .. "]", cUpValue, cDumpInfoContainer)
- end
- end
- -- Dump environment table.
- local getfenv = debug.getfenv
- if getfenv then
- local cEnv = getfenv(cObject)
- if cEnv then
- CollectObjectReferenceInMemory(strName ..".[function:environment]", cEnv, cDumpInfoContainer)
- end
- end
- elseif "thread" == strType then
- -- Add reference and name.
- cRefInfoContainer[cObject] = (cRefInfoContainer[cObject] and (cRefInfoContainer[cObject] + 1)) or 1
- if cNameInfoContainer[cObject] then
- return
- end
- -- Set name.
- cNameInfoContainer[cObject] = strName
- -- Dump environment table.
- local getfenv = debug.getfenv
- if getfenv then
- local cEnv = getfenv(cObject)
- if cEnv then
- CollectObjectReferenceInMemory(strName ..".[thread:environment]", cEnv, cDumpInfoContainer)
- end
- end
- -- Dump metatable.
- local cMt = getmetatable(cObject)
- if cMt then
- CollectObjectReferenceInMemory(strName ..".[thread:metatable]", cMt, cDumpInfoContainer)
- end
- elseif "userdata" == strType then
- -- Add reference and name.
- cRefInfoContainer[cObject] = (cRefInfoContainer[cObject] and (cRefInfoContainer[cObject] + 1)) or 1
- if cNameInfoContainer[cObject] then
- return
- end
- -- Set name.
- cNameInfoContainer[cObject] = strName
- -- Dump environment table.
- local getfenv = debug.getfenv
- if getfenv then
- local cEnv = getfenv(cObject)
- if cEnv then
- CollectObjectReferenceInMemory(strName ..".[userdata:environment]", cEnv, cDumpInfoContainer)
- end
- end
- -- Dump metatable.
- local cMt = getmetatable(cObject)
- if cMt then
- CollectObjectReferenceInMemory(strName ..".[userdata:metatable]", cMt, cDumpInfoContainer)
- end
- elseif "string" == strType then
- -- Add reference and name.
- cRefInfoContainer[cObject] = (cRefInfoContainer[cObject] and (cRefInfoContainer[cObject] + 1)) or 1
- if cNameInfoContainer[cObject] then
- return
- end
- -- Set name.
- cNameInfoContainer[cObject] = strName .. "[" .. strType .. "]"
- else
- -- For "number" and "boolean". (If you want to dump them, uncomment the followed lines.)
- -- -- Add reference and name.
- -- cRefInfoContainer[cObject] = (cRefInfoContainer[cObject] and (cRefInfoContainer[cObject] + 1)) or 1
- -- if cNameInfoContainer[cObject] then
- -- return
- -- end
- -- -- Set name.
- -- cNameInfoContainer[cObject] = strName .. "[" .. strType .. ":" .. tostring(cObject) .. "]"
- end
- end
- -- Collect memory reference info of a single object from a root table or function.
- -- strName - The root object name that start to search, can not be nil.
- -- cObject - The root object that start to search, can not be nil.
- -- cDumpInfoContainer - The container of the dump result info.
- local function CollectSingleObjectReferenceInMemory(strName, cObject, cDumpInfoContainer)
- if not cObject then
- return
- end
- if not strName then
- strName = ""
- end
- -- Check container.
- if (not cDumpInfoContainer) then
- cDumpInfoContainer = CreateObjectReferenceInfoContainer()
- end
- -- Check stack.
- if cDumpInfoContainer.m_nStackLevel > 0 then
- local cStackInfo = debug.getinfo(cDumpInfoContainer.m_nStackLevel, "Sl")
- if cStackInfo then
- cDumpInfoContainer.m_strShortSrc = cStackInfo.short_src
- cDumpInfoContainer.m_nCurrentLine = cStackInfo.currentline
- end
- cDumpInfoContainer.m_nStackLevel = -1
- end
- local cExistTag = cDumpInfoContainer.m_cObjectExistTag
- local cNameAllAlias = cDumpInfoContainer.m_cObjectAliasName
- local cAccessTag = cDumpInfoContainer.m_cObjectAccessTag
- local strType = type(cObject)
- if "table" == strType then
- -- Check table with class name.
- if rawget(cObject, "__cname") then
- if "string" == type(cObject.__cname) then
- strName = strName .. "[class:" .. cObject.__cname .. "]"
- end
- elseif rawget(cObject, "class") then
- if "string" == type(cObject.class) then
- strName = strName .. "[class:" .. cObject.class .. "]"
- end
- elseif rawget(cObject, "_className") then
- if "string" == type(cObject._className) then
- strName = strName .. "[class:" .. cObject._className .. "]"
- end
- end
- -- Check if table is _G.
- if cObject == _G then
- strName = strName .. "[_G]"
- end
- -- Get metatable.
- local bWeakK = false
- local bWeakV = false
- local cMt = getmetatable(cObject)
- if cMt then
- -- Check mode.
- local strMode = rawget(cMt, "__mode")
- if strMode then
- if "k" == strMode then
- bWeakK = true
- elseif "v" == strMode then
- bWeakV = true
- elseif "kv" == strMode then
- bWeakK = true
- bWeakV = true
- end
- end
- end
- -- Check if the specified object.
- if cExistTag[cObject] and (not cNameAllAlias[strName]) then
- cNameAllAlias[strName] = true
- end
- -- Add reference and name.
- if cAccessTag[cObject] then
- return
- end
- -- Get this name.
- cAccessTag[cObject] = true
- -- Dump table key and value.
- for k, v in pairs(cObject) do
- -- Check key type.
- local strKeyType = type(k)
- if "table" == strKeyType then
- if not bWeakK then
- CollectSingleObjectReferenceInMemory(strName .. ".[table:key.table]", k, cDumpInfoContainer)
- end
- if not bWeakV then
- CollectSingleObjectReferenceInMemory(strName .. ".[table:value]", v, cDumpInfoContainer)
- end
- elseif "function" == strKeyType then
- if not bWeakK then
- CollectSingleObjectReferenceInMemory(strName .. ".[table:key.function]", k, cDumpInfoContainer)
- end
- if not bWeakV then
- CollectSingleObjectReferenceInMemory(strName .. ".[table:value]", v, cDumpInfoContainer)
- end
- elseif "thread" == strKeyType then
- if not bWeakK then
- CollectSingleObjectReferenceInMemory(strName .. ".[table:key.thread]", k, cDumpInfoContainer)
- end
- if not bWeakV then
- CollectSingleObjectReferenceInMemory(strName .. ".[table:value]", v, cDumpInfoContainer)
- end
- elseif "userdata" == strKeyType then
- if not bWeakK then
- CollectSingleObjectReferenceInMemory(strName .. ".[table:key.userdata]", k, cDumpInfoContainer)
- end
- if not bWeakV then
- CollectSingleObjectReferenceInMemory(strName .. ".[table:value]", v, cDumpInfoContainer)
- end
- else
- CollectSingleObjectReferenceInMemory(strName .. "." .. k, v, cDumpInfoContainer)
- end
- end
- -- Dump metatable.
- if cMt then
- CollectSingleObjectReferenceInMemory(strName ..".[metatable]", cMt, cDumpInfoContainer)
- end
- elseif "function" == strType then
- -- Get function info.
- local cDInfo = debug.getinfo(cObject, "Su")
- local cCombinedName = strName .. "[line:" .. tostring(cDInfo.linedefined) .. "@file:" .. cDInfo.short_src .. "]"
- -- Check if the specified object.
- if cExistTag[cObject] and (not cNameAllAlias[cCombinedName]) then
- cNameAllAlias[cCombinedName] = true
- end
- -- Write this info.
- if cAccessTag[cObject] then
- return
- end
- -- Set name.
- cAccessTag[cObject] = true
- -- Get upvalues.
- local nUpsNum = cDInfo.nups
- for i = 1, nUpsNum do
- local strUpName, cUpValue = debug.getupvalue(cObject, i)
- local strUpValueType = type(cUpValue)
- --print(strUpName, cUpValue)
- if "table" == strUpValueType then
- CollectSingleObjectReferenceInMemory(strName .. ".[ups:table:" .. strUpName .. "]", cUpValue, cDumpInfoContainer)
- elseif "function" == strUpValueType then
- CollectSingleObjectReferenceInMemory(strName .. ".[ups:function:" .. strUpName .. "]", cUpValue, cDumpInfoContainer)
- elseif "thread" == strUpValueType then
- CollectSingleObjectReferenceInMemory(strName .. ".[ups:thread:" .. strUpName .. "]", cUpValue, cDumpInfoContainer)
- elseif "userdata" == strUpValueType then
- CollectSingleObjectReferenceInMemory(strName .. ".[ups:userdata:" .. strUpName .. "]", cUpValue, cDumpInfoContainer)
- end
- end
- -- Dump environment table.
- local getfenv = debug.getfenv
- if getfenv then
- local cEnv = getfenv(cObject)
- if cEnv then
- CollectSingleObjectReferenceInMemory(strName ..".[function:environment]", cEnv, cDumpInfoContainer)
- end
- end
- elseif "thread" == strType then
- -- Check if the specified object.
- if cExistTag[cObject] and (not cNameAllAlias[strName]) then
- cNameAllAlias[strName] = true
- end
- -- Add reference and name.
- if cAccessTag[cObject] then
- return
- end
- -- Get this name.
- cAccessTag[cObject] = true
- -- Dump environment table.
- local getfenv = debug.getfenv
- if getfenv then
- local cEnv = getfenv(cObject)
- if cEnv then
- CollectSingleObjectReferenceInMemory(strName ..".[thread:environment]", cEnv, cDumpInfoContainer)
- end
- end
- -- Dump metatable.
- local cMt = getmetatable(cObject)
- if cMt then
- CollectSingleObjectReferenceInMemory(strName ..".[thread:metatable]", cMt, cDumpInfoContainer)
- end
- elseif "userdata" == strType then
- -- Check if the specified object.
- if cExistTag[cObject] and (not cNameAllAlias[strName]) then
- cNameAllAlias[strName] = true
- end
- -- Add reference and name.
- if cAccessTag[cObject] then
- return
- end
- -- Get this name.
- cAccessTag[cObject] = true
- -- Dump environment table.
- local getfenv = debug.getfenv
- if getfenv then
- local cEnv = getfenv(cObject)
- if cEnv then
- CollectSingleObjectReferenceInMemory(strName ..".[userdata:environment]", cEnv, cDumpInfoContainer)
- end
- end
- -- Dump metatable.
- local cMt = getmetatable(cObject)
- if cMt then
- CollectSingleObjectReferenceInMemory(strName ..".[userdata:metatable]", cMt, cDumpInfoContainer)
- end
- elseif "string" == strType then
- -- Check if the specified object.
- if cExistTag[cObject] and (not cNameAllAlias[strName]) then
- cNameAllAlias[strName] = true
- end
- -- Add reference and name.
- if cAccessTag[cObject] then
- return
- end
- -- Get this name.
- cAccessTag[cObject] = true
- else
- -- For "number" and "boolean" type, they are not object type, skip.
- end
- end
- -- The base method to dump a mem ref info result into a file.
- -- strSavePath - The save path of the file to store the result, must be a directory path, If nil or "" then the result will output to console as print does.
- -- strExtraFileName - If you want to add extra info append to the end of the result file, give a string, nothing will do if set to nil or "".
- -- nMaxRescords - How many rescords of the results in limit to save in the file or output to the console, -1 will give all the result.
- -- strRootObjectName - The header info to show the root object name, can be nil.
- -- cRootObject - The header info to show the root object address, can be nil.
- -- cDumpInfoResultsBase - The base dumped mem info result, nil means no compare and only output cDumpInfoResults, otherwise to compare with cDumpInfoResults.
- -- cDumpInfoResults - The compared dumped mem info result, dump itself only if cDumpInfoResultsBase is nil, otherwise dump compared results with cDumpInfoResultsBase.
- local function OutputMemorySnapshot(strSavePath, strExtraFileName, nMaxRescords, strRootObjectName, cRootObject, cDumpInfoResultsBase, cDumpInfoResults)
- -- Check results.
- if not cDumpInfoResults then
- return
- end
- -- Get time format string.
- local strDateTime = FormatDateTimeNow()
- -- Collect memory info.
- local cRefInfoBase = (cDumpInfoResultsBase and cDumpInfoResultsBase.m_cObjectReferenceCount) or nil
- local cNameInfoBase = (cDumpInfoResultsBase and cDumpInfoResultsBase.m_cObjectAddressToName) or nil
- local cRefInfo = cDumpInfoResults.m_cObjectReferenceCount
- local cNameInfo = cDumpInfoResults.m_cObjectAddressToName
- -- Create a cache result to sort by ref count.
- local cRes = {}
- local nIdx = 0
- for k in pairs(cRefInfo) do
- nIdx = nIdx + 1
- cRes[nIdx] = k
- end
- -- Sort result.
- table.sort(cRes, function (l, r)
- return cRefInfo[l] > cRefInfo[r]
- end)
- -- Save result to file.
- local bOutputFile = strSavePath and (string.len(strSavePath) > 0)
- local cOutputHandle = nil
- local cOutputEntry = print
- if bOutputFile then
- -- Check save path affix.
- local strAffix = string.sub(strSavePath, -1)
- if ("/" ~= strAffix) and ("\\" ~= strAffix) then
- strSavePath = strSavePath .. "/"
- end
- -- Combine file name.
- local strFileName = strSavePath .. "LuaMemRefInfo-All"
- if (not strExtraFileName) or (0 == string.len(strExtraFileName)) then
- if cDumpInfoResultsBase then
- if cConfig.m_bComparedMemoryRefFileAddTime then
- strFileName = strFileName .. "-[" .. strDateTime .. "].txt"
- else
- strFileName = strFileName .. ".txt"
- end
- else
- if cConfig.m_bAllMemoryRefFileAddTime then
- strFileName = strFileName .. "-[" .. strDateTime .. "].txt"
- else
- strFileName = strFileName .. ".txt"
- end
- end
- else
- if cDumpInfoResultsBase then
- if cConfig.m_bComparedMemoryRefFileAddTime then
- strFileName = strFileName .. "-[" .. strDateTime .. "]-[" .. strExtraFileName .. "].txt"
- else
- strFileName = strFileName .. "-[" .. strExtraFileName .. "].txt"
- end
- else
- if cConfig.m_bAllMemoryRefFileAddTime then
- strFileName = strFileName .. "-[" .. strDateTime .. "]-[" .. strExtraFileName .. "].txt"
- else
- strFileName = strFileName .. "-[" .. strExtraFileName .. "].txt"
- end
- end
- end
- local cFile = assert(io.open(strFileName, "w"))
- cOutputHandle = cFile
- cOutputEntry = cFile.write
- end
- local cOutputer = function (strContent)
- if cOutputHandle then
- cOutputEntry(cOutputHandle, strContent)
- else
- cOutputEntry(strContent)
- end
- end
- -- Write table header.
- if cDumpInfoResultsBase then
- cOutputer("--------------------------------------------------------\n")
- cOutputer("-- This is compared memory information.\n")
- cOutputer("--------------------------------------------------------\n")
- cOutputer("-- Collect base memory reference at line:" .. tostring(cDumpInfoResultsBase.m_nCurrentLine) .. "@file:" .. cDumpInfoResultsBase.m_strShortSrc .. "\n")
- cOutputer("-- Collect compared memory reference at line:" .. tostring(cDumpInfoResults.m_nCurrentLine) .. "@file:" .. cDumpInfoResults.m_strShortSrc .. "\n")
- else
- cOutputer("--------------------------------------------------------\n")
- cOutputer("-- Collect memory reference at line:" .. tostring(cDumpInfoResults.m_nCurrentLine) .. "@file:" .. cDumpInfoResults.m_strShortSrc .. "\n")
- end
- cOutputer("--------------------------------------------------------\n")
- cOutputer("-- [Table/Function/String Address/Name]\t[Reference Path]\t[Reference Count]\n")
- cOutputer("--------------------------------------------------------\n")
- if strRootObjectName and cRootObject then
- if "string" == type(cRootObject) then
- cOutputer("-- From Root Object: \"" .. tostring(cRootObject) .. "\" (" .. strRootObjectName .. ")\n")
- else
- cOutputer("-- From Root Object: " .. GetOriginalToStringResult(cRootObject) .. " (" .. strRootObjectName .. ")\n")
- end
- end
- -- Save each info.
- for i, v in ipairs(cRes) do
- if (not cDumpInfoResultsBase) or (not cRefInfoBase[v]) then
- if (nMaxRescords > 0) then
- if (i <= nMaxRescords) then
- if "string" == type(v) then
- local strOrgString = tostring(v)
- local nPattenBegin, nPattenEnd = string.find(strOrgString, "string: \".*\"")
- if ((not cDumpInfoResultsBase) and ((nil == nPattenBegin) or (nil == nPattenEnd))) then
- local strRepString = string.gsub(strOrgString, "([\n\r])", "\\n")
- cOutputer("string: \"" .. strRepString .. "\"\t" .. cNameInfo[v] .. "\t" .. tostring(cRefInfo[v]) .. "\n")
- else
- cOutputer(tostring(v) .. "\t" .. cNameInfo[v] .. "\t" .. tostring(cRefInfo[v]) .. "\n")
- end
- else
- cOutputer(GetOriginalToStringResult(v) .. "\t" .. cNameInfo[v] .. "\t" .. tostring(cRefInfo[v]) .. "\n")
- end
- end
- else
- if "string" == type(v) then
- local strOrgString = tostring(v)
- local nPattenBegin, nPattenEnd = string.find(strOrgString, "string: \".*\"")
- if ((not cDumpInfoResultsBase) and ((nil == nPattenBegin) or (nil == nPattenEnd))) then
- local strRepString = string.gsub(strOrgString, "([\n\r])", "\\n")
- cOutputer("string: \"" .. strRepString .. "\"\t" .. cNameInfo[v] .. "\t" .. tostring(cRefInfo[v]) .. "\n")
- else
- cOutputer(tostring(v) .. "\t" .. cNameInfo[v] .. "\t" .. tostring(cRefInfo[v]) .. "\n")
- end
- else
- cOutputer(GetOriginalToStringResult(v) .. "\t" .. cNameInfo[v] .. "\t" .. tostring(cRefInfo[v]) .. "\n")
- end
- end
- end
- end
- if bOutputFile then
- io.close(cOutputHandle)
- cOutputHandle = nil
- end
- end
- -- The base method to dump a mem ref info result of a single object into a file.
- -- strSavePath - The save path of the file to store the result, must be a directory path, If nil or "" then the result will output to console as print does.
- -- strExtraFileName - If you want to add extra info append to the end of the result file, give a string, nothing will do if set to nil or "".
- -- nMaxRescords - How many rescords of the results in limit to save in the file or output to the console, -1 will give all the result.
- -- cDumpInfoResults - The dumped results.
- local function OutputMemorySnapshotSingleObject(strSavePath, strExtraFileName, nMaxRescords, cDumpInfoResults)
- -- Check results.
- if not cDumpInfoResults then
- return
- end
- -- Get time format string.
- local strDateTime = FormatDateTimeNow()
- -- Collect memory info.
- local cObjectAliasName = cDumpInfoResults.m_cObjectAliasName
- -- Save result to file.
- local bOutputFile = strSavePath and (string.len(strSavePath) > 0)
- local cOutputHandle = nil
- local cOutputEntry = print
- if bOutputFile then
- -- Check save path affix.
- local strAffix = string.sub(strSavePath, -1)
- if ("/" ~= strAffix) and ("\\" ~= strAffix) then
- strSavePath = strSavePath .. "/"
- end
- -- Combine file name.
- local strFileName = strSavePath .. "LuaMemRefInfo-Single"
- if (not strExtraFileName) or (0 == string.len(strExtraFileName)) then
- if cConfig.m_bSingleMemoryRefFileAddTime then
- strFileName = strFileName .. "-[" .. strDateTime .. "].txt"
- else
- strFileName = strFileName .. ".txt"
- end
- else
- if cConfig.m_bSingleMemoryRefFileAddTime then
- strFileName = strFileName .. "-[" .. strDateTime .. "]-[" .. strExtraFileName .. "].txt"
- else
- strFileName = strFileName .. "-[" .. strExtraFileName .. "].txt"
- end
- end
- local cFile = assert(io.open(strFileName, "w"))
- cOutputHandle = cFile
- cOutputEntry = cFile.write
- end
- local cOutputer = function (strContent)
- if cOutputHandle then
- cOutputEntry(cOutputHandle, strContent)
- else
- cOutputEntry(strContent)
- end
- end
- -- Write table header.
- cOutputer("--------------------------------------------------------\n")
- cOutputer("-- Collect single object memory reference at line:" .. tostring(cDumpInfoResults.m_nCurrentLine) .. "@file:" .. cDumpInfoResults.m_strShortSrc .. "\n")
- cOutputer("--------------------------------------------------------\n")
- -- Calculate reference count.
- local nCount = 0
- for k in pairs(cObjectAliasName) do
- nCount = nCount + 1
- end
- -- Output reference count.
- cOutputer("-- For Object: " .. cDumpInfoResults.m_strAddressName .. " (" .. cDumpInfoResults.m_strObjectName .. "), have " .. tostring(nCount) .. " reference in total.\n")
- cOutputer("--------------------------------------------------------\n")
- -- Save each info.
- for k in pairs(cObjectAliasName) do
- if (nMaxRescords > 0) then
- if (i <= nMaxRescords) then
- cOutputer(k .. "\n")
- end
- else
- cOutputer(k .. "\n")
- end
- end
- if bOutputFile then
- io.close(cOutputHandle)
- cOutputHandle = nil
- end
- end
- -- Fileter an existing result file and output it.
- -- strFilePath - The existing result file.
- -- strFilter - The filter string.
- -- bIncludeFilter - Include(true) or exclude(false) the filter.
- -- bOutputFile - Output to file(true) or console(false).
- local function OutputFilteredResult(strFilePath, strFilter, bIncludeFilter, bOutputFile)
- if (not strFilePath) or (0 == string.len(strFilePath)) then
- print("You need to specify a file path.")
- return
- end
- if (not strFilter) or (0 == string.len(strFilter)) then
- print("You need to specify a filter string.")
- return
- end
- -- Read file.
- local cFilteredResult = {}
- local cReadFile = assert(io.open(strFilePath, "rb"))
- for strLine in cReadFile:lines() do
- local nBegin, nEnd = string.find(strLine, strFilter)
- if nBegin and nEnd then
- if bIncludeFilter then
- nBegin, nEnd = string.find(strLine, "[\r\n]")
- if nBegin and nEnd and (string.len(strLine) == nEnd) then
- table.insert(cFilteredResult, string.sub(strLine, 1, nBegin - 1))
- else
- table.insert(cFilteredResult, strLine)
- end
- end
- else
- if not bIncludeFilter then
- nBegin, nEnd = string.find(strLine, "[\r\n]")
- if nBegin and nEnd and (string.len(strLine) == nEnd) then
- table.insert(cFilteredResult, string.sub(strLine, 1, nBegin - 1))
- else
- table.insert(cFilteredResult, strLine)
- end
- end
- end
- end
- -- Close and clear read file handle.
- io.close(cReadFile)
- cReadFile = nil
- -- Write filtered result.
- local cOutputHandle = nil
- local cOutputEntry = print
- if bOutputFile then
- -- Combine file name.
- local _, _, strResFileName = string.find(strFilePath, "(.*)%.txt")
- strResFileName = strResFileName .. "-Filter-" .. ((bIncludeFilter and "I") or "E") .. "-[" .. strFilter .. "].txt"
- local cFile = assert(io.open(strResFileName, "w"))
- cOutputHandle = cFile
- cOutputEntry = cFile.write
- end
- local cOutputer = function (strContent)
- if cOutputHandle then
- cOutputEntry(cOutputHandle, strContent)
- else
- cOutputEntry(strContent)
- end
- end
- -- Output result.
- for i, v in ipairs(cFilteredResult) do
- cOutputer(v .. "\n")
- end
- if bOutputFile then
- io.close(cOutputHandle)
- cOutputHandle = nil
- end
- end
- -- Dump memory reference at current time.
- -- strSavePath - The save path of the file to store the result, must be a directory path, If nil or "" then the result will output to console as print does.
- -- strExtraFileName - If you want to add extra info append to the end of the result file, give a string, nothing will do if set to nil or "".
- -- nMaxRescords - How many rescords of the results in limit to save in the file or output to the console, -1 will give all the result.
- -- strRootObjectName - The root object name that start to search, default is "_G" if leave this to nil.
- -- cRootObject - The root object that start to search, default is _G if leave this to nil.
- local function DumpMemorySnapshot(strSavePath, strExtraFileName, nMaxRescords, strRootObjectName, cRootObject)
- -- Get time format string.
- local strDateTime = FormatDateTimeNow()
- -- Check root object.
- if cRootObject then
- if (not strRootObjectName) or (0 == string.len(strRootObjectName)) then
- strRootObjectName = tostring(cRootObject)
- end
- else
- cRootObject = debug.getregistry()
- strRootObjectName = "registry"
- end
- -- Create container.
- local cDumpInfoContainer = CreateObjectReferenceInfoContainer()
- local cStackInfo = debug.getinfo(2, "Sl")
- if cStackInfo then
- cDumpInfoContainer.m_strShortSrc = cStackInfo.short_src
- cDumpInfoContainer.m_nCurrentLine = cStackInfo.currentline
- end
- -- Collect memory info.
- CollectObjectReferenceInMemory(strRootObjectName, cRootObject, cDumpInfoContainer)
- -- Dump the result.
- OutputMemorySnapshot(strSavePath, strExtraFileName, nMaxRescords, strRootObjectName, cRootObject, nil, cDumpInfoContainer)
- end
- -- Dump compared memory reference results generated by DumpMemorySnapshot.
- -- strSavePath - The save path of the file to store the result, must be a directory path, If nil or "" then the result will output to console as print does.
- -- strExtraFileName - If you want to add extra info append to the end of the result file, give a string, nothing will do if set to nil or "".
- -- nMaxRescords - How many rescords of the results in limit to save in the file or output to the console, -1 will give all the result.
- -- cResultBefore - The base dumped results.
- -- cResultAfter - The compared dumped results.
- local function DumpMemorySnapshotCompared(strSavePath, strExtraFileName, nMaxRescords, cResultBefore, cResultAfter)
- -- Dump the result.
- OutputMemorySnapshot(strSavePath, strExtraFileName, nMaxRescords, nil, nil, cResultBefore, cResultAfter)
- end
- -- Dump compared memory reference file results generated by DumpMemorySnapshot.
- -- strSavePath - The save path of the file to store the result, must be a directory path, If nil or "" then the result will output to console as print does.
- -- strExtraFileName - If you want to add extra info append to the end of the result file, give a string, nothing will do if set to nil or "".
- -- nMaxRescords - How many rescords of the results in limit to save in the file or output to the console, -1 will give all the result.
- -- strResultFilePathBefore - The base dumped results file.
- -- strResultFilePathAfter - The compared dumped results file.
- local function DumpMemorySnapshotComparedFile(strSavePath, strExtraFileName, nMaxRescords, strResultFilePathBefore, strResultFilePathAfter)
- -- Read results from file.
- local cResultBefore = CreateObjectReferenceInfoContainerFromFile(strResultFilePathBefore)
- local cResultAfter = CreateObjectReferenceInfoContainerFromFile(strResultFilePathAfter)
- -- Dump the result.
- OutputMemorySnapshot(strSavePath, strExtraFileName, nMaxRescords, nil, nil, cResultBefore, cResultAfter)
- end
- -- Dump memory reference of a single object at current time.
- -- strSavePath - The save path of the file to store the result, must be a directory path, If nil or "" then the result will output to console as print does.
- -- strExtraFileName - If you want to add extra info append to the end of the result file, give a string, nothing will do if set to nil or "".
- -- nMaxRescords - How many rescords of the results in limit to save in the file or output to the console, -1 will give all the result.
- -- strObjectName - The object name reference you want to dump.
- -- cObject - The object reference you want to dump.
- local function DumpMemorySnapshotSingleObject(strSavePath, strExtraFileName, nMaxRescords, strObjectName, cObject)
- -- Check object.
- if not cObject then
- return
- end
- if (not strObjectName) or (0 == string.len(strObjectName)) then
- strObjectName = GetOriginalToStringResult(cObject)
- end
- -- Get time format string.
- local strDateTime = FormatDateTimeNow()
- -- Create container.
- local cDumpInfoContainer = CreateSingleObjectReferenceInfoContainer(strObjectName, cObject)
- local cStackInfo = debug.getinfo(2, "Sl")
- if cStackInfo then
- cDumpInfoContainer.m_strShortSrc = cStackInfo.short_src
- cDumpInfoContainer.m_nCurrentLine = cStackInfo.currentline
- end
- -- Collect memory info.
- CollectSingleObjectReferenceInMemory("registry", debug.getregistry(), cDumpInfoContainer)
- -- Dump the result.
- OutputMemorySnapshotSingleObject(strSavePath, strExtraFileName, nMaxRescords, cDumpInfoContainer)
- end
- -- Return methods.
- local cPublications = {m_cConfig = nil, m_cMethods = {}, m_cHelpers = {}, m_cBases = {}}
- cPublications.m_cConfig = cConfig
- cPublications.m_cMethods.DumpMemorySnapshot = DumpMemorySnapshot
- cPublications.m_cMethods.DumpMemorySnapshotCompared = DumpMemorySnapshotCompared
- cPublications.m_cMethods.DumpMemorySnapshotComparedFile = DumpMemorySnapshotComparedFile
- cPublications.m_cMethods.DumpMemorySnapshotSingleObject = DumpMemorySnapshotSingleObject
- cPublications.m_cHelpers.FormatDateTimeNow = FormatDateTimeNow
- cPublications.m_cHelpers.GetOriginalToStringResult = GetOriginalToStringResult
- cPublications.m_cBases.CreateObjectReferenceInfoContainer = CreateObjectReferenceInfoContainer
- cPublications.m_cBases.CreateObjectReferenceInfoContainerFromFile = CreateObjectReferenceInfoContainerFromFile
- cPublications.m_cBases.CreateSingleObjectReferenceInfoContainer = CreateSingleObjectReferenceInfoContainer
- cPublications.m_cBases.CollectObjectReferenceInMemory = CollectObjectReferenceInMemory
- cPublications.m_cBases.CollectSingleObjectReferenceInMemory = CollectSingleObjectReferenceInMemory
- cPublications.m_cBases.OutputMemorySnapshot = OutputMemorySnapshot
- cPublications.m_cBases.OutputMemorySnapshotSingleObject = OutputMemorySnapshotSingleObject
- cPublications.m_cBases.OutputFilteredResult = OutputFilteredResult
- return cPublications
|