/* compute the tree and all the counters and write all that to the backing store */ bool BuildTreeAndComputeStats() { /* id of the previous thread */ int prevThreadId = -1; /* information about the current thread (to speed up lookup) */ int prevDepth = 0; ArrayList stack = null; SortedList functions = null; ArrayList queuedNodes = null; Stream s = null; ProgressForm progressForm = null; const int prevStackInitialSize = 100; int prevStackMaxSize = prevStackInitialSize; int[] prevStackTrace = new int[prevStackInitialSize]; int prevStackLen = 0; StacktraceTable stacktraceTable = logResult.callstackHistogram.readNewLog.stacktraceTable; // Preprocessing for function filters int nIncludeCallFilters = 0; int nIncludeAllocFilters = 0; for (int j=0; j < filterInclude.Length; j++) { if (filterInclude[j].functionId != -1) { if (filterInclude[j].nodetype == TreeNode.NodeType.Call) nIncludeCallFilters++; else if (filterInclude[j].nodetype == TreeNode.NodeType.Allocation) nIncludeAllocFilters++; } } if (firstNewStack != -1) { stacktraceTable.FreeEntries( firstNewStack ); } firstNewStack = -1; try { /* log parser code (straight from the ReadNewLog.cs) */ s = new FileStream(logFileName, FileMode.Open, FileAccess.Read, FileShare.ReadWrite); r = new StreamReader(s); progressForm = new ProgressForm(); progressForm.Text = "Preparing call tree view"; progressForm.Visible = true; progressForm.setProgress(0); progressForm.TopMost = false; int maxProgress = (int)(r.BaseStream.Length/1024); progressForm.setMaximum(maxProgress); buffer = new byte[4096]; bufPos = 0; bufLevel = 0; line = 1; StringBuilder sb = new StringBuilder(); c = ReadChar(); bool found; string assemblyName = null; int threadid = 0, stackid = 0; TreeNode.NodeType nodetype = TreeNode.NodeType.Call; while (c != -1) { found = false; if ((line % 1024) == 0) { int currentProgress = (int)(pos/1024); if (currentProgress <= maxProgress) { progressForm.setProgress(currentProgress); Application.DoEvents(); } } lastLineStartPos = pos-1; /* only parse calls and allocations */ switch (c) { case -1: break; // 'A' with thread identifier case '!': { found = true; c = ReadChar(); threadid = ReadInt(); ReadInt(); stackid = ReadInt(); nodetype = TreeNode.NodeType.Allocation; if (c == -1) {found = false;} break; } case 'C': case 'c': { found = true; c = ReadChar(); nodetype = TreeNode.NodeType.Call; threadid = ReadInt(); stackid = ReadInt(); if (c == -1) {found = false;} break; } case 'y': case 'Y': { found = true; c = ReadChar(); nodetype = TreeNode.NodeType.AssemblyLoad; threadid = ReadInt(); /* int assemblyId = */ ReadInt(); while (c == ' ' || c == '\t') { c = ReadChar(); } sb.Length = 0; while (c > ' ') { sb.Append((char)c); c = ReadChar(); } assemblyName = sb.ToString(); break; } default: { // just ignore the unknown while(c != '\n' && c != '\r') { c = ReadChar(); } break; } } while (c == ' ' || c == '\t') c = ReadChar(); if (c == '\r') c = ReadChar(); if (c == '\n') { c = ReadChar(); line++; } if(!found) { continue; } int[] stacktrace = IndexToStacktrace(stackid); bool fFoundCallInclude = true; bool fFoundCallExclude = false; bool fFoundAllocInclude = true; bool fFoundAllocExclude = false; int includeMatches; // Apply filters to current node if (filterInclude.Length != 0 || filterExclude.Length != 0) { if (nodetype == TreeNode.NodeType.Call || nodetype == TreeNode.NodeType.Allocation) { int i = 0; int includesFound = 0; fFoundCallInclude = false; if (nodetype == TreeNode.NodeType.Allocation) { i = 2; } for (; i < stacktrace.Length; i++) { includeMatches = 0; // See if the stack contain the required include functions for (int j=0; j < filterInclude.Length; j++) { if (filterInclude[j].nodetype == TreeNode.NodeType.Call && filterInclude[j].functionId != -1) { if (stacktrace[i] == filterInclude[j].functionId ) includeMatches++; } } if (includeMatches > 0) includesFound++; // Now see if the stack contains any exclude functions for (int j=0; j < filterExclude.Length; j++) { if (filterExclude[j].nodetype == TreeNode.NodeType.Call && stacktrace[i] == filterExclude[j].functionId ) { // Any exclusion match gets this node bounced fFoundCallExclude = true; break; } } } // This node can pass the filter only if all include filters are found if (includesFound == nIncludeCallFilters) fFoundCallInclude = true; } else { fFoundCallInclude = false; } if (nodetype == TreeNode.NodeType.Allocation) { includeMatches = 0; fFoundAllocInclude = false; // See if the stack contain the required include allocations for (int j=0; j < filterInclude.Length; j++) if (filterInclude[j].nodetype == TreeNode.NodeType.Allocation) { if (stacktrace[0] == filterInclude[j].functionId || filterInclude[j].functionId == -1) includeMatches++; } if (includeMatches > 0 || nIncludeAllocFilters == 0) fFoundAllocInclude = true; // Now see if the stack contains any exclude allocations for (int j=0; j < filterExclude.Length; j++) { if (filterExclude[j].nodetype == TreeNode.NodeType.Allocation && stacktrace[0] == filterExclude[j].functionId) { fFoundAllocExclude = true; break; } } } } // Proceed to process this node only if the trace has all functions // and no exclude functions. if ( !fFoundCallInclude || fFoundCallExclude || !fFoundAllocInclude || fFoundAllocExclude) { // Skip this node continue; } /* if thread changed, have to retrieve information about it. * info about the last thread is cached to speed up the process */ if(threadid != prevThreadId) { if(prevThreadId != -1) { /* store everything about the previous thread */ ThreadState prevState = threads[prevThreadId]; prevState.prevStackLen = prevStackLen; prevState.prevStackTrace = prevStackTrace; prevState.functions = functions; prevState.prevDepth = prevDepth; prevState.stack = stack; prevState.queuedNodes = queuedNodes; } else { /* this is the first call ever, mark the * thread where it occured as the main thread */ firstThread = threadid; } /* get the information about the current (new) thread */ ThreadState state; if (!threads.TryGetValue(threadid, out state)) { /* create if necessary */ state = new ThreadState(); state.prevStackLen = 0; state.prevStackTrace = new int[prevStackInitialSize]; state.prevDepth = 0; state.stack = new ArrayList(); state.functions = new SortedList(); state.queuedNodes = new ArrayList(); TreeNode threadRoot = new TreeNode(TreeNode.NodeType.Call, 0); state.stack.Add(threadRoot); threads[threadid] = state; } prevStackLen = state.prevStackLen; prevStackTrace = state.prevStackTrace; prevStackMaxSize = prevStackTrace.Length; prevDepth = state.prevDepth; stack = state.stack; functions = state.functions; queuedNodes = state.queuedNodes; prevThreadId = threadid; } /* if we're here, `iscall`, `stackid`, and `threadid` are set correctly */ int depth = 0; if ( nodetype == TreeNode.NodeType.Allocation ) { // To build a call tree from the allocation log // Need to recreate the stacks and Call nodes that got us here. // Ignore first 2 ints in the stack trace. They are allocation information. int curStackLen = stacktrace.Length - 2; int i; bool fNewStack; // Find out how much of the callstack we need to construct fNewStack = curStackLen != prevStackLen; for (i = 0; i < curStackLen && i < prevStackLen; i++) { if (prevStackTrace[i] != stacktrace[i+2]) { fNewStack = true; break; } } int nextStackIndex = stacktraceTable.Length; for ( ; i < curStackLen; i++) { // We blindly add a new entry to the stack table, even though this stack may already // exist. Searching for a match would be expensive, so for now just do a new allocation. // If this becomes hugely expensive, it will be worth making the stack trace searchable. stacktraceTable.Add( nextStackIndex, stacktrace, 2, i+1, false); TreeNode callnode = new TreeNode(TreeNode.NodeType.Call, nextStackIndex); callnode.nodeOffset = lastLineStartPos; queuedNodes.Add( callnode ); if (firstNewStack == -1) { // Remember which stacks we created firstNewStack = nextStackIndex; } nextStackIndex++; } if (fNewStack) { // Reallocate prev stack if neccessary if (curStackLen > prevStackMaxSize) { prevStackMaxSize += prevStackInitialSize; prevStackTrace = new int[prevStackMaxSize]; } // Save a copy of the current stack for (i = 0; i < curStackLen; i++) { prevStackTrace[i] = stacktrace[i+2]; } prevStackLen = curStackLen; } } else if ( nodetype == TreeNode.NodeType.Call ) { // Reallocate prev stack if neccessary if (stacktrace.Length > prevStackMaxSize) { prevStackMaxSize += prevStackInitialSize; prevStackTrace = new int[prevStackMaxSize]; } prevStackLen = stacktrace.Length; stacktrace.CopyTo( prevStackTrace, 0 ); } TreeNode node = new TreeNode(nodetype, stackid); int functionId = (nodetype != TreeNode.NodeType.AssemblyLoad ? stacktrace[stacktrace.Length - 1] : 0); switch(nodetype) { case TreeNode.NodeType.Allocation: node.data.bytesAllocated = stacktrace[1]; break; case TreeNode.NodeType.Call: if(functionId == 0) { node.isunmanaged = true; } break; case TreeNode.NodeType.AssemblyLoad: if(!assemblyNameToIdMap.Contains(assemblyName)) { assemblyNameToIdMap.Add(assemblyName, null); node.nameId = assemblyNames.Add(assemblyName); queuedNodes.Add(node); } continue; } queuedNodes.Add(node); for(int i = 0; i < queuedNodes.Count; i++) { node = (TreeNode)queuedNodes[i]; nodetype = node.nodetype; stacktrace = IndexToStacktrace(node.stackid); int stackLength = stacktrace.Length; if(nodetype == TreeNode.NodeType.Allocation) { // Skip first 2 entries in the stack. They are type-id and bytes-allocated. // Add 1 to depth so allocation looks like a call to allocation function. depth = stackLength - 2 + 1; } else if(nodetype == TreeNode.NodeType.Call) { depth = stackLength; } else if(nodetype == TreeNode.NodeType.AssemblyLoad) { depth = stackLength; } if(depth <= 0) { continue; } if(depth > prevDepth) { /* kids go to the stack */ if(depth - prevDepth > 1) { for(int idx = 1; idx < depth; idx++) { TreeNode n = new TreeNode(TreeNode.NodeType.Call, -idx); n.nodeOffset = lastLineStartPos; stack.Add(n); } } stack.Add(node); } else { /* moving up or sideways, have to adjust the stats * and dump some of the branches to the backing store */ for(int j = 1 + prevDepth; j-- > depth + 1;) { if(((TreeNode)stack[j]).nodetype == TreeNode.NodeType.Call) { /* record functions left */ LeaveFunction(functions, GetFunctionIdFromStackId(((TreeNode)stack[j]).stackid)); } UpdateStats(stack[j - 1], stack[j]); ((TreeNode)stack[j - 1]).kidOffset = Dump(stack[j]); } if(((TreeNode)stack[depth]).nodetype == TreeNode.NodeType.Call) { LeaveFunction(functions, GetFunctionIdFromStackId(((TreeNode)stack[depth]).stackid)); } UpdateStats(stack[depth - 1], stack[depth]); node.prevOffset = Dump(stack[depth]); stack[depth] = node; stack.RemoveRange(1 + depth, stack.Count - depth - 1); } /* adjust the global statistics */ if(nodetype == TreeNode.NodeType.Call) { functionId = stacktrace[stacktrace.Length - 1]; globalCallStats[functionId].timesCalled++; foreach(int fid in functions.Keys) { globalCallStats[fid].totalFunctionsCalled++; } if(!globalCallStats[functionId].calledAlready) { globalCallStats[functionId].calledAlready = true; node.data.firstTimeBroughtIn = true; foreach(TreeNode n in stack) { n.data.numberOfNewFunctionsBroughtIn++; } /* `node` (the new function itself) is on the * stack too, so we have to reverse its counter */ node.data.numberOfNewFunctionsBroughtIn--; foreach(int fid in functions.Keys) { globalCallStats[fid].totalNewFunctionsBroughtIn++; } } /* record entering the function */ EnterFunction(functions, functionId); } else if(nodetype == TreeNode.NodeType.Allocation) { foreach(int fid in functions.Keys) { globalCallStats[fid].totalBytesAllocated += stacktrace[1]; } globalAllocStats[stacktrace[0]].timesAllocated++; globalAllocStats[stacktrace[0]].totalBytesAllocated += stacktrace[1]; } prevDepth = depth; } queuedNodes.Clear(); } } catch { /* exceptions are no good */ MessageBox.Show(this, "Error creating backing store file, check if there is\nenough space on drive that holds your TEMP directory", "Failure"); return false; // throw new Exception(e.Message + "\n" + e.StackTrace); } finally { /* get rid of the progress form and close the log file */ if(progressForm != null) { progressForm.Visible = false; progressForm.Dispose(); } if(s != null) { s.Close(); } } /* dump the root and the remains of the tree * that are still in memory to the backing store */ foreach(ThreadState state in threads.Values) { stack = state.stack; for(int j = stack.Count; j-- > 1;) { LeaveFunction(functions, GetFunctionIdFromStackId(((TreeNode)stack[j]).stackid)); UpdateStats(stack[j - 1], stack[j]); ((TreeNode)stack[j - 1]).kidOffset = Dump(stack[j]); } ((TreeNode)stack[0]).HasKids = true; stack.RemoveRange(1, stack.Count - 1); } /* remove spurious threads from the thread array. don't think * it's an issue anymore but the code doesn't do anybody no harm */ List<int> nulls = new List<int>(); foreach(int key in threads.Keys) { if (threads[key] == null) { nulls.Add(key); } } foreach(int key in nulls) { threads.Remove(key); } writer.Flush(); return true; }
internal void ReadFile(long startFileOffset, long endFileOffset, ReadLogResult readLogResult) { ProgressForm progressForm = new ProgressForm(); progressForm.Text = string.Format("Progress loading {0}", fileName); progressForm.Visible = progressFormVisible; progressForm.setProgress(0); if (stacktraceTable == null) stacktraceTable = new StacktraceTable(); if (timePos == null) timePos = new TimePos[1000]; AddTypeName(0, "Free Space"); try { Stream s = new FileStream(fileName, FileMode.Open, FileAccess.Read, FileShare.ReadWrite); r = new StreamReader(s); for (timePosIndex = timePosCount; timePosIndex > 0; timePosIndex--) if (timePos[timePosIndex-1].pos <= startFileOffset) break; // start at the beginning if no later start point available or asked for info that can only // be constructed by reading the whole file. if (timePosIndex <= 1 || readLogResult.relocatedHistogram != null || readLogResult.finalizerHistogram != null || readLogResult.criticalFinalizerHistogram != null || readLogResult.liveObjectTable != null) { pos = 0; timePosIndex = 1; } else { timePosIndex--; pos = timePos[timePosIndex].pos; } if (timePosCount == 0) { timePos[0] = new TimePos(0.0, 0); timePosCount = timePosIndex = 1; } s.Position = pos; buffer = new byte[4096]; bufPos = 0; bufLevel = 0; int maxProgress = (int)(r.BaseStream.Length/1024); progressForm.setMaximum(maxProgress); line = 1; StringBuilder sb = new StringBuilder(); ulong[] ulongStack = new ulong[1000]; int[] intStack = new int[1000]; int stackPtr = 0; c = ReadChar(); bool thisIsR = false, previousWasR; bool extendedRootInfoSeen = false; int lastTickIndex = 0; bool newGcEvent = false; while (c != -1) { if (pos > endFileOffset) break; if ((line % 1024) == 0) { int currentProgress = (int)(pos/1024); if (currentProgress <= maxProgress) { progressForm.setProgress(currentProgress); Application.DoEvents(); if (progressForm.DialogResult == DialogResult.Cancel) break; } } lastLineStartPos = pos-1; previousWasR = thisIsR; thisIsR = false; switch (c) { case -1: break; case 'F': case 'f': { c = ReadChar(); int funcIndex = ReadInt(); while (c == ' ' || c == '\t') c = ReadChar(); sb.Length = 0; // Name may contain spaces if they are in angle brackets. // Example: <Module>::std_less<unsigned void>.() // The name may be truncated at 255 chars by profilerOBJ.dll int angleBracketsScope = 0; while (c > ' ' || angleBracketsScope != 0 && sb.Length < 255) { if (c == '<') angleBracketsScope++; sb.Append((char)c); c = ReadChar(); if (c == '>' && angleBracketsScope > 0) angleBracketsScope--; } string name = sb.ToString(); while (c == ' ' || c == '\t') c = ReadChar(); sb.Length = 0; while (c > '\r') { sb.Append((char)c); if (c == ')') { c = ReadChar(); break; } c = ReadChar(); } string signature = sb.ToString(); ulong addr = ReadULong(); uint size = ReadUInt(); int modIndex = ReadInt(); int stackIndex = ReadInt(); if (c != -1) { EnsureStringCapacity(funcIndex, ref funcName); funcName[funcIndex] = name; EnsureStringCapacity(funcIndex, ref funcSignature); funcSignature[funcIndex] = signature; EnsureIntCapacity(funcIndex, ref funcModule); funcModule[funcIndex] = modIndex; string nameAndSignature = name; if (signature != null) nameAndSignature = name + ' '+signature; if (stackIndex >= 0 && readLogResult.functionList != null) { funcSignatureIdHash[nameAndSignature] = funcIndex; readLogResult.functionList.Add(funcIndex, stackIndex, size, modIndex); } } break; } case 'T': case 't': { c = ReadChar(); int typeIndex = ReadInt(); while (c == ' ' || c == '\t') c = ReadChar(); if (c != -1 && Char.IsDigit((char)c)) { if (ReadInt() != 0) { finalizableTypes[typeIndex] = true; } } while (c == ' ' || c == '\t') c = ReadChar(); sb.Length = 0; while (c > '\r') { sb.Append((char)c); c = ReadChar(); } string typeName = sb.ToString(); if (c != -1) { AddTypeName(typeIndex, typeName); } break; } // 'A' with thread identifier case '!': { c = ReadChar(); int threadId = ReadInt(); ulong id = ReadULong(); int typeSizeStackTraceIndex = ReadInt(); typeSizeStackTraceIndex = stacktraceTable.MapTypeSizeStacktraceId(typeSizeStackTraceIndex); if (c != -1) { if (readLogResult.liveObjectTable != null) readLogResult.liveObjectTable.InsertObject(id, typeSizeStackTraceIndex, lastTickIndex, lastTickIndex, true, readLogResult.sampleObjectTable); if (pos >= startFileOffset && pos < endFileOffset && readLogResult.allocatedHistogram != null) { // readLogResult.calls.Add(new CallOrAlloc(false, threadId, typeSizeStackTraceIndex)); readLogResult.allocatedHistogram.AddObject(typeSizeStackTraceIndex, 1); } List<string> prev; if (assembliesJustLoaded.TryGetValue(threadId, out prev) && prev.Count != 0) { foreach(string assemblyName in prev) { assemblies[assemblyName] = -typeSizeStackTraceIndex; } prev.Clear(); } } readLogResult.hadAllocInfo = true; readLogResult.hadCallInfo = true; break; } case 'A': case 'a': { c = ReadChar(); ulong id = ReadULong(); int typeSizeStackTraceIndex = ReadInt(); typeSizeStackTraceIndex = stacktraceTable.MapTypeSizeStacktraceId(typeSizeStackTraceIndex); if (c != -1) { if (readLogResult.liveObjectTable != null) readLogResult.liveObjectTable.InsertObject(id, typeSizeStackTraceIndex, lastTickIndex, lastTickIndex, true, readLogResult.sampleObjectTable); if (pos >= startFileOffset && pos < endFileOffset && readLogResult.allocatedHistogram != null) { // readLogResult.calls.Add(new CallOrAlloc(false, typeSizeStackTraceIndex)); readLogResult.allocatedHistogram.AddObject(typeSizeStackTraceIndex, 1); } } readLogResult.hadAllocInfo = true; readLogResult.hadCallInfo = true; break; } case 'C': case 'c': { c = ReadChar(); if (pos < startFileOffset || pos >= endFileOffset) { while (c >= ' ') c = ReadChar(); break; } int threadIndex = ReadInt(); int stackTraceIndex = ReadInt(); stackTraceIndex = stacktraceTable.MapTypeSizeStacktraceId(stackTraceIndex); if (c != -1) { if (readLogResult.callstackHistogram != null) { readLogResult.callstackHistogram.AddObject(stackTraceIndex, 1); } List<string> prev; if (assembliesJustLoaded.TryGetValue(threadIndex, out prev) && prev.Count != 0) { foreach(string assemblyName in prev) { assemblies[assemblyName] = stackTraceIndex; } prev.Clear(); } } readLogResult.hadCallInfo = true; break; } case 'E': case 'e': { c = ReadChar(); extendedRootInfoSeen = true; thisIsR = true; if (pos < startFileOffset || pos >= endFileOffset) { while (c >= ' ') c = ReadChar(); break; } if (!previousWasR) { heapDumpEventList.AddEvent(lastTickIndex, null); if (readLogResult.objectGraph != null && !readLogResult.objectGraph.empty) { readLogResult.objectGraph.BuildTypeGraph(new FilterForm()); readLogResult.objectGraph.Neuter(); } readLogResult.objectGraph = new ObjectGraph(this, lastTickIndex); Histogram[] h = readLogResult.heapDumpHistograms; if (h != null) { readLogResult.heapDumpHistograms = new Histogram[h.Length+1]; for (int i = 0; i < h.Length; i++) readLogResult.heapDumpHistograms[i] = h[i]; readLogResult.heapDumpHistograms[h.Length] = new Histogram(this); } } ulong objectID = ReadULong(); GcRootKind rootKind = (GcRootKind)ReadInt(); GcRootFlags rootFlags = (GcRootFlags)ReadInt(); ulong rootID = ReadULong(); ObjectGraph objectGraph = readLogResult.objectGraph; if (c != -1 && objectID > 0 && objectGraph != null && (rootFlags & GcRootFlags.WeakRef) == 0) { string rootName; switch (rootKind) { case GcRootKind.Stack: rootName = "Stack"; break; case GcRootKind.Finalizer: rootName = "Finalizer"; break; case GcRootKind.Handle: rootName = "Handle"; break; default: rootName = "Other"; break; } if ((rootFlags & GcRootFlags.Pinning) != 0) rootName += ", Pinning"; if ((rootFlags & GcRootFlags.WeakRef) != 0) rootName += ", WeakRef"; if ((rootFlags & GcRootFlags.Interior) != 0) rootName += ", Interior"; if ((rootFlags & GcRootFlags.Refcounted) != 0) rootName += ", RefCounted"; int rootTypeId = objectGraph.GetOrCreateGcType(rootName); ulongStack[0] = objectID; ObjectGraph.GcObject rootObject = objectGraph.CreateObject(rootTypeId, 1, ulongStack); objectGraph.AddRootObject(rootObject, rootID); } break; } case 'R': case 'r': { c = ReadChar(); thisIsR = true; if (extendedRootInfoSeen || pos < startFileOffset || pos >= endFileOffset) { while (c >= ' ') c = ReadChar(); break; } if (!previousWasR) { heapDumpEventList.AddEvent(lastTickIndex, null); if (readLogResult.objectGraph != null && !readLogResult.objectGraph.empty) { readLogResult.objectGraph.BuildTypeGraph(new FilterForm()); readLogResult.objectGraph.Neuter(); } readLogResult.objectGraph = new ObjectGraph(this, lastTickIndex); Histogram[] h = readLogResult.heapDumpHistograms; if (h != null) { readLogResult.heapDumpHistograms = new Histogram[h.Length+1]; for (int i = 0; i < h.Length; i++) readLogResult.heapDumpHistograms[i] = h[i]; readLogResult.heapDumpHistograms[h.Length] = new Histogram(this); } } stackPtr = 0; ulong objectID; while ((objectID = ReadULong()) != ulong.MaxValue) { if (objectID > 0) { ulongStack[stackPtr] = objectID; stackPtr++; if (stackPtr >= ulongStack.Length) ulongStack = GrowULongVector(ulongStack); } } if (c != -1) { if (readLogResult.objectGraph != null) readLogResult.objectGraph.AddRoots(stackPtr, ulongStack); } break; } case 'O': case 'o': { c = ReadChar(); if (pos < startFileOffset || pos >= endFileOffset || readLogResult.objectGraph == null) { while (c >= ' ') c = ReadChar(); break; } ulong objectId = ReadULong(); int typeIndex = ReadInt(); uint size = ReadUInt(); stackPtr = 0; ulong objectID; while ((objectID = ReadULong()) != ulong.MaxValue) { if (objectID > 0) { ulongStack[stackPtr] = objectID; stackPtr++; if (stackPtr >= ulongStack.Length) ulongStack = GrowULongVector(ulongStack); } } if (c != -1) { ObjectGraph objectGraph = readLogResult.objectGraph; objectGraph.GetOrCreateGcType(typeIndex); int typeSizeStackTraceId = -1; int allocTickIndex = 0; // try to find the allocation stack trace and allocation time // from the live object table if (readLogResult.liveObjectTable != null) { LiveObjectTable.LiveObject liveObject; readLogResult.liveObjectTable.GetNextObject(objectId, objectId, out liveObject); if (liveObject.id == objectId) { typeSizeStackTraceId = liveObject.typeSizeStacktraceIndex; allocTickIndex = liveObject.allocTickIndex; Histogram[] h = readLogResult.heapDumpHistograms; if (h != null) h[h.Length-1].AddObject(liveObject.typeSizeStacktraceIndex, 1); } } if (typeSizeStackTraceId == -1) typeSizeStackTraceId = stacktraceTable.GetOrCreateTypeSizeId(typeIndex, (int)size); ObjectGraph.GcObject gcObject = objectGraph.CreateAndEnterObject(objectId, typeSizeStackTraceId, stackPtr, ulongStack); gcObject.AllocTickIndex = allocTickIndex; } break; } case 'M': case 'm': { c = ReadChar(); int modIndex = ReadInt(); sb.Length = 0; while (c > '\r') { sb.Append((char)c); c = ReadChar(); } if (c != -1) { string lineString = sb.ToString(); int addrPos = lineString.LastIndexOf(" 0x"); if (addrPos <= 0) addrPos = lineString.Length; int backSlashPos = lineString.LastIndexOf(@"\"); if (backSlashPos <= 0) backSlashPos = -1; string basicName = lineString.Substring(backSlashPos + 1, addrPos - backSlashPos - 1); string fullName = lineString.Substring(0, addrPos); EnsureStringCapacity(modIndex, ref modBasicName); modBasicName[modIndex] = basicName; EnsureStringCapacity(modIndex, ref modFullName); modFullName[modIndex] = fullName; } break; } case 'U': case 'u': { c = ReadChar(); ulong oldId = ReadULong(); ulong newId = ReadULong(); uint length = ReadUInt(); Histogram reloHist = null; if (pos >= startFileOffset && pos < endFileOffset) reloHist = readLogResult.relocatedHistogram; if (readLogResult.liveObjectTable != null) readLogResult.liveObjectTable.UpdateObjects(reloHist, oldId, newId, length, lastTickIndex, readLogResult.sampleObjectTable); break; } case 'V': case 'v': { c = ReadChar(); ulong startId = ReadULong(); uint length = ReadUInt(); Histogram reloHist = null; if (pos >= startFileOffset && pos < endFileOffset) reloHist = readLogResult.relocatedHistogram; if (readLogResult.liveObjectTable != null) readLogResult.liveObjectTable.UpdateObjects(reloHist, startId, startId, length, lastTickIndex, readLogResult.sampleObjectTable); break; } case 'B': case 'b': c = ReadChar(); int startGC = ReadInt(); int induced = ReadInt(); int condemnedGeneration = ReadInt(); if (startGC != 0) newGcEvent = gcEventList.AddEvent(lastTickIndex, null); if (newGcEvent) { if (startGC != 0) { if (induced != 0) { for (int gen = 0; gen <= condemnedGeneration; gen++) inducedGcCount[gen]++; } } else { int condemnedLimit = condemnedGeneration; if (condemnedLimit == 2) condemnedLimit = 3; for (int gen = 0; gen <= condemnedLimit; gen++) { cumulativeGenerationSize[gen] += generationSize[gen]; gcCount[gen]++; } } } for (int gen = 0; gen <= 3; gen++) generationSize[gen] = 0; while (c >= ' ') { ulong rangeStart = ReadULong(); ulong rangeLength = ReadULong(); ulong rangeLengthReserved = ReadULong(); int rangeGeneration = ReadInt(); if (c == -1 || rangeGeneration < 0) break; if (readLogResult.liveObjectTable != null) { if (startGC != 0) { if (rangeGeneration > condemnedGeneration && condemnedGeneration != 2) readLogResult.liveObjectTable.Preserve(rangeStart, rangeLength, lastTickIndex); } else { readLogResult.liveObjectTable.GenerationInterval(rangeStart, rangeLength, rangeGeneration, lastTickIndex); } } generationSize[rangeGeneration] += rangeLength; } if (startGC == 0 && readLogResult.liveObjectTable != null) { readLogResult.liveObjectTable.RecordGc(lastTickIndex, condemnedGeneration, readLogResult.sampleObjectTable, false); } break; case 'L': case 'l': { c = ReadChar(); int isCritical = ReadInt(); ulong objectId = ReadULong(); if (pos >= startFileOffset && pos < endFileOffset && readLogResult.liveObjectTable != null) { // try to find the allocation stack trace and allocation time // from the live object table LiveObjectTable.LiveObject liveObject; readLogResult.liveObjectTable.GetNextObject(objectId, objectId, out liveObject); if (liveObject.id == objectId) { if (isCritical != 0 && readLogResult.criticalFinalizerHistogram != null) readLogResult.criticalFinalizerHistogram.AddObject(liveObject.typeSizeStacktraceIndex, 1); if (readLogResult.finalizerHistogram != null) readLogResult.finalizerHistogram.AddObject(liveObject.typeSizeStacktraceIndex, 1); } } break; } case 'I': case 'i': c = ReadChar(); int tickCount = ReadInt(); if (c != -1) { lastTickIndex = AddTimePos(tickCount, lastLineStartPos); if (maxTickIndex < lastTickIndex) maxTickIndex = lastTickIndex; } break; case 'G': case 'g': c = ReadChar(); int gcGen0Count = ReadInt(); int gcGen1Count = ReadInt(); int gcGen2Count = ReadInt(); // if the newer 'b' lines occur, disregard the 'g' lines. if (gcCount[0] == 0 && readLogResult.liveObjectTable != null) { if (c == -1 || gcGen0Count < 0) readLogResult.liveObjectTable.RecordGc(lastTickIndex, 0, readLogResult.sampleObjectTable, gcGen0Count < 0); else readLogResult.liveObjectTable.RecordGc(lastTickIndex, gcGen0Count, gcGen1Count, gcGen2Count, readLogResult.sampleObjectTable); } break; case 'N': case 'n': { c = ReadChar(); int funcIndex; int stackTraceIndex = ReadInt(); stackPtr = 0; int flag = ReadInt(); int matched = flag / 4; int hadTypeId = (flag & 2); bool hasTypeId = (flag & 1) == 1; if (hasTypeId) { intStack[stackPtr++] = ReadInt(); intStack[stackPtr++] = ReadInt(); } if (matched > 0 && c != -1) { /* use some other stack trace as a reference */ int otherStackTraceId = ReadInt(); otherStackTraceId = stacktraceTable.MapTypeSizeStacktraceId(otherStackTraceId); int[] stacktrace = stacktraceTable.IndexToStacktrace(otherStackTraceId); if (matched > stacktrace.Length - hadTypeId) matched = stacktrace.Length - hadTypeId; for(int i = 0; i < matched; i++) { int funcId = stacktrace[i + hadTypeId]; Debug.Assert(funcId < funcName.Length && funcName[funcId] != null); intStack[stackPtr++] = funcId; if (stackPtr >= intStack.Length) { intStack = GrowIntVector(intStack); } } } while ((funcIndex = ReadInt()) >= 0) { intStack[stackPtr] = funcIndex; stackPtr++; if (stackPtr >= intStack.Length) intStack = GrowIntVector(intStack); } if (c != -1) { stacktraceTable.Add(stackTraceIndex, intStack, stackPtr, hasTypeId); } break; } case 'y': case 'Y': { c = ReadChar(); int threadid = ReadInt(); if(!assembliesJustLoaded.ContainsKey(threadid)) { assembliesJustLoaded[threadid] = new List<string>(); } /* int assemblyId = */ ReadInt(); while (c == ' ' || c == '\t') { c = ReadChar(); } sb.Length = 0; while (c > '\r') { sb.Append((char)c); c = ReadChar(); } string assemblyName = sb.ToString(); assembliesJustLoaded[threadid].Add(assemblyName); break; } case 'S': case 's': { c = ReadChar(); int stackTraceIndex = ReadInt(); int funcIndex; stackPtr = 0; while ((funcIndex = ReadInt()) >= 0) { intStack[stackPtr] = funcIndex; stackPtr++; if (stackPtr >= intStack.Length) intStack = GrowIntVector(intStack); } if (c != -1) { stacktraceTable.Add(stackTraceIndex, intStack, stackPtr, false); } break; } case 'Z': case 'z': { sb.Length = 0; c = ReadChar(); while (c == ' ' || c == '\t') c = ReadChar(); while (c > '\r') { sb.Append((char)c); c = ReadChar(); } if (c != -1) { lastTickIndex = AddTimePos(lastLineStartPos); if (maxTickIndex < lastTickIndex) maxTickIndex = lastTickIndex; commentEventList.AddEvent(lastTickIndex, sb.ToString()); } break; } case 'H': case 'h': { c = ReadChar(); int threadId = ReadInt(); ulong handleId = ReadULong(); ulong initialObjectId = ReadULong(); int stacktraceId = ReadInt(); if (c != -1) { if (readLogResult.handleHash != null) readLogResult.handleHash[handleId] = new HandleInfo(threadId, handleId, initialObjectId, lastTickIndex, stacktraceId); if (readLogResult.createdHandlesHistogram != null) readLogResult.createdHandlesHistogram.AddObject(stacktraceId, 1); } break; } case 'J': case 'j': { c = ReadChar(); int threadId = ReadInt(); ulong handleId = ReadULong(); int stacktraceId = ReadInt(); if (c != -1) { if (readLogResult.handleHash != null) { if (readLogResult.handleHash.ContainsKey(handleId)) readLogResult.handleHash.Remove(handleId); else { // Console.WriteLine("Non-existent handle {0:x} destroyed in line {1}", handleId, line); int[] stacktrace = stacktraceTable.IndexToStacktrace(stacktraceId); for (int i = stacktrace.Length; --i >= 0; ) { Console.WriteLine(" {0}", funcName[stacktrace[i]]); } } } if (readLogResult.destroyedHandlesHistogram != null) readLogResult.destroyedHandlesHistogram.AddObject(stacktraceId, 1); } break; } default: { // just ignore the unknown while(c != '\n' && c != '\r') { c = ReadChar(); } break; } } while (c == ' ' || c == '\t') c = ReadChar(); if (c == '\r') c = ReadChar(); if (c == '\n') { c = ReadChar(); line++; } } // readLogResult.functionList.ReportCallCountSizes(readLogResult.callstackHistogram); } // catch (Exception) // { // throw new Exception(string.Format("Bad format in log file {0} line {1}", fileName, line)); // throw; // } finally { progressForm.Visible = false; progressForm.Dispose(); if (r != null) r.Close(); } }
private void ReadFile(CallTreeForm callTrace, string fileName, Hashtable FuncExcl, Hashtable TypeExcl) { Hashtable funcCalled = new Hashtable(); Hashtable TypeAlloc = new Hashtable(); Stream s = null; ProgressForm progressForm = null; try { s = new FileStream(fileName, FileMode.Open, FileAccess.Read, FileShare.ReadWrite); r = new StreamReader(s); progressForm = new ProgressForm(); progressForm.Text = "Preparing call tree view"; progressForm.Visible = true; progressForm.setProgress(0); progressForm.TopMost = false; int maxProgress = (int)(r.BaseStream.Length/1024); progressForm.setMaximum(maxProgress); buffer = new byte[4096]; bufPos = 0; bufLevel = 0; line = 1; StringBuilder sb = new StringBuilder(); c = ReadChar(); bool found; string assemblyName = null; int threadid = 0, stackid = 0; TreeNode.NodeType nodetype = TreeNode.NodeType.Call; while (c != -1) { found = false; if ((line % 1024) == 0) { int currentProgress = (int)(pos/1024); if (currentProgress <= maxProgress) { progressForm.setProgress(currentProgress); } } lastLineStartPos = pos-1; switch (c) { case -1: break; // 'A' with thread identifier case '!': { found = true; c = ReadChar(); threadid = ReadInt(); ReadInt(); stackid = ReadInt(); nodetype = TreeNode.NodeType.Allocation; if (c == -1) {found = false;} break; } case 'C': case 'c': { found = true; c = ReadChar(); nodetype = TreeNode.NodeType.Call; threadid = ReadInt(); stackid = ReadInt(); if (c == -1) {found = false;} break; } case 'y': case 'Y': { found = true; c = ReadChar(); nodetype = TreeNode.NodeType.AssemblyLoad; threadid = ReadInt(); ReadInt(); while (c == ' ' || c == '\t') { c = ReadChar(); } sb.Length = 0; while (c > ' ') { sb.Append((char)c); c = ReadChar(); } assemblyName = sb.ToString(); break; } default: { // just ignore the unknown while(c != '\n' && c != '\r') { c = ReadChar(); } break; } } while (c == ' ' || c == '\t') c = ReadChar(); if (c == '\r') c = ReadChar(); if (c == '\n') { c = ReadChar(); line++; } if(!found) { continue; } string name = null; string typename = null; int[] stacktrace = callTrace.IndexToStacktrace(stackid); int functionId = (nodetype != TreeNode.NodeType.AssemblyLoad ? stacktrace[stacktrace.Length - 1] : 0); switch(nodetype) { case TreeNode.NodeType.Allocation: string key = null; if( (functionId < callTrace.LogResult.callstackHistogram.readNewLog.funcName.Length )&& ((name = callTrace.LogResult.callstackHistogram.readNewLog.funcName[functionId]) != null)) { if( callTrace.LogResult.callstackHistogram.readNewLog.funcSignature[functionId] != null) { name += ' ' + callTrace.LogResult.callstackHistogram.readNewLog.funcSignature[functionId]; } } else { name = "NATIVE FUNCTION ( UNKNOWN ARGUMENTS )"; } // function Excl if(FuncExcl.ContainsKey(name)) { int alloc = (int)FuncExcl[(string)name]; alloc += stacktrace[1]; FuncExcl[name] = alloc; } else { FuncExcl.Add(name, stacktrace[1]); } // Type Excl if( stacktrace[0]>=0 && stacktrace[0] < callTrace.LogResult.callstackHistogram.readNewLog.typeName.Length) { typename = callTrace.LogResult.callstackHistogram.readNewLog.typeName[stacktrace[0]]; } if(typename == null) typename = "NATIVE FUNCTION ( UNKNOWN ARGUMENTS )"; if(TypeExcl.ContainsKey(typename)) { int alloc = (int)TypeExcl[(string)typename]; alloc += stacktrace[1]; TypeExcl[typename] = alloc; } else { TypeExcl.Add(typename, stacktrace[1]); } // Type Allocated by Excl if(name != "NATIVE FUNCTION ( UNKNOWN ARGUMENTS )") key = typename + '|' + functionId; else key = typename + '|' + 0; if( TypeAlloc.ContainsKey(key)) { int alloc = (int)TypeAlloc[key]; alloc += stacktrace[1]; TypeAlloc[key] = alloc; } else { TypeAlloc.Add(key, stacktrace[1]); } break; case TreeNode.NodeType.Call: if(funcCalled.ContainsKey(functionId)) { int calls = (int)funcCalled[functionId] + 1;; funcCalled[functionId]= calls; } else { funcCalled.Add(functionId,1); } break; } } } catch (Exception) { throw new Exception(string.Format("Bad format in log file {0} line {1}", fileName, line)); } finally { progressForm.Visible = false; progressForm.Dispose(); if (r != null) r.Close(); } }