12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076 |
- --
- -- 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
- print('tostring overridden:', tostring(cObject))
- --? rawset(cMt, "__tostring", nil)
- --? strName = tostring(cObject)
- --? rawset(cMt, "__tostring", cToString)
- --? else
- --? strName = tostring(cObject)
- end
- strName = tostring(cObject)
- 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 .. "." .. tostring(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
|