public void RemoveItemTest() { GrowableArray <int> tester = new GrowableArray <int>(); Assert.Fail(); }
static void Main() { int expectedNumberOfNodes = 1000; MemoryGraph memoryGraph = new MemoryGraph(expectedNumberOfNodes); GrowableArray <NodeIndex> tempForChildren = new GrowableArray <NodeIndex>(); // We can make a new Node index NodeIndex newNodeIdx = memoryGraph.CreateNode(); NodeIndex childIdx = memoryGraph.CreateNode(); // NodeTypeIndex newNodeType = memoryGraph.CreateType("MyChild"); memoryGraph.SetNode(childIdx, newType, 100, tempForChildren); memoryGraph.AllowReading(); // Serialize to a file memoryGraph.WriteAsBinaryFile("file.gcHeap"); // Can unserialize easily. // var readBackIn = MemoryGraph.ReadFromBinaryFile("file.gcHeap"); }
/// <summary> /// TraceProcesses represents the entire ETL moduleFile log. At the node level it is organized by threads. /// /// The TraceProcesses also is where we put various caches that are independent of the process involved. /// These include a cache for TraceModuleFile that represent native images that can be loaded into a /// process, as well as the process lookup tables and a cache that remembers the last calls to /// GetNameForAddress(). /// </summary> internal TraceProcesses(TraceLog log, TraceEventDispatcher source) { this.log = log; this.source = source; processes = new GrowableArray <TraceProcess>(64); processesByPID = new GrowableArray <TraceProcess>(64); }
public PrimeSieve2(long upTo) { primes = new GrowableArray<bool>(upTo); primes.Set(0, true); primes.Set(1, true); PopulateSieveFrom(primes); }
public GCSimulation(ClrProfilerParser profiler) { m_clrProfiler = profiler; m_relocs = new GrowableArray <Relocation>(256); Allocs = new GrowableArray <AllocInfo>(100000); m_clrProfiler.GCStart += new ClrProfilerParser.GCEventHandler(this.GCStart); m_clrProfiler.GCEnd += new ClrProfilerParser.GCEventHandler(this.GCEnd); m_clrProfiler.ObjectRangeLive += new ClrProfilerParser.LiveObjectRangeHandler(this.ObjectRangeLive); m_clrProfiler.ObjectRangeRelocation += new ClrProfilerParser.RelocationEventHandler(this.ObjectRangeRelocation); // TODO expose the thread information AllocInfo info = new AllocInfo(); m_clrProfiler.Allocation += delegate(ProfilerAllocID allocId, Address objectAddress, uint threadId) { Debug.Assert(allocId != ProfilerAllocID.Null); info.Size = (int)m_clrProfiler.GetAllocSize(allocId); info.AllocId = allocId; var stackId = m_clrProfiler.GetAllocStack(allocId); info.MsecFromStart = CurrentTimeMSec; info.ObjectAddress = objectAddress; Allocs.Add(info); m_numAllocs++; }; m_clrProfiler.Tick += delegate(int milliSecondsSinceStart) { CurrentTimeMSec = milliSecondsSinceStart; }; }
static void main2() { GrowableArray <double> ga = new GrowableArray <double>(); int i = 7; ga.AddElement(i); }
public GrowableArrayGenericUser() { GrowableArray <int> ga = new GrowableArray <int>(); int num = 10; ga.AddElement(num); //no boxing operation here int newNum = (int)ga.GetElement(0); //type safety and no unboxing operation // string str = (string)ga.GetElement(0); //would not compile }
public void AddTest() { GrowableArray <int> tester = new GrowableArray <int> { 5 }; Assert.Equals(tester.list, new int[] { 5 }); }
public GrowableArrayUser() { GrowableArray ga = new GrowableArray(); int num = 10; ga.AddElement(num); //boxing operation here int newNum = (int)ga.GetElement(0); //no type safety and unboxing operation string str = (string)ga.GetElement(0); //runtime error here }
private void GetChildrenForAddresses(ref GrowableArray <NodeIndex> children, string to) { // TODO inefficient foreach (var numStr in to.Split(' ')) { int num; if (int.TryParse(numStr, NumberStyles.HexNumber, null, out num)) { children.Add(GetNodeIndex((Address)num)); } } }
public static MemoryGraph Create(string dllPath, SymbolReader symbolReader) { var ret = new MemoryGraph(1000); string pdbPath = symbolReader.FindSymbolFilePathForModule(dllPath); symbolReader.Log.WriteLine("Got PDB path {0}", pdbPath); NativeSymbolModule module = symbolReader.OpenNativeSymbolFile(pdbPath); List <Symbol> symbols = new List <Symbol>(); AddAllChildren(symbols, module.GlobalSymbol); symbols.Sort(); /****** Make a graph out of the symbols ******/ // Put all nodes under this root. var rootChildren = new GrowableArray <NodeIndex>(1000); // Create a node for each symbol uint lastRVA = 0; string lastName = "Header"; var empty = new GrowableArray <NodeIndex>(); foreach (var symbol in symbols) { var symRVA = symbol.RVA; int lastSize = (int)symRVA - (int)lastRVA; NodeTypeIndex typeIdx = ret.CreateType(lastName, null, lastSize); NodeIndex nodeIdx = ret.CreateNode(); ret.SetNode(nodeIdx, typeIdx, lastSize, empty); rootChildren.Add(nodeIdx); lastName = symbol.Name; lastRVA = symRVA; } // TODO FIX NOW dropping the last symbol. // Create the root node. NodeIndex rootIdx = ret.CreateNode(); NodeTypeIndex rootTypeIdx = ret.CreateType("METHODS"); ret.SetNode(rootIdx, rootTypeIdx, 0, rootChildren); ret.RootIndex = rootIdx; ret.AllowReading(); return(ret); }
public void TestArrayAccess() { int sz = 100; GrowableArray<int> ga = new GrowableArray<int>(sz); for(int i = 0; i < 2 * sz; ++i) { ga.Set(i, 13*i); Assert.AreEqual(i < sz ? sz : 2 * sz, ga.LongLength); } for(int i = 0; i < 2 * sz; ++i) { Assert.AreEqual(i*13, ga.Get(i)); } }
private static void PopulateSieveFrom(GrowableArray<bool> sieve, long from = 2) { long upTo = (long)Math.Sqrt(sieve.LongLength) + 1; for (long i = 2; i < upTo; i++) { if (!sieve.Get(i)) { var k = (i < from) ? FirstK(i, from) : 2 * i; while (k < sieve.LongLength) { sieve.Set(k,true); k += i; } } } }
void IFastSerializable.FromStream(Deserializer deserializer) { base.FromStream(deserializer); // Read in the Memory addresses of each object int addressCount = deserializer.ReadInt(); m_nodeAddresses = new GrowableArray <Address>(addressCount); for (int i = 0; i < addressCount; i++) { m_nodeAddresses.Add((Address)deserializer.ReadInt64()); } bool is64bit = false; deserializer.TryReadTagged(ref is64bit); Is64Bit = is64bit; }
public ClrProfilerMethodSizeStackSource(string clrProfilerFileName) { m_fileName = clrProfilerFileName; m_clrProfiler = new ClrProfilerParser(); m_calls = new GrowableArray <int>(1000000); m_clrProfiler.Call += delegate(ProfilerStackTraceID stackId, uint threadId) { m_calls.Add((int)stackId); var method = m_clrProfiler.Method(stackId); var stats = (MethodStats)method.UserData; if (stats == null) { m_totalMethodSize += (int)method.size; m_totalMethodCount++; method.UserData = stats = new MethodStats(); // Debug.WriteLine(string.Format("METHOD Size {1,6}: {0}", method.name, method.size)); } stats.count++; }; m_clrProfiler.ReadFile(m_fileName); // Debug.WriteLine(string.Format("MethodSize {0} MethodCount {1} callCount {2}", m_totalMethodSize, m_totalMethodCount, m_calls.Count)); }
public ClrProfilerMemoryGraph(string profilerFile) : base(10000) { // Needed for the callback in ReadFile m_tempChildren = new GrowableArray <NodeIndex>(1000); ClearWorker(); m_clrProfilerParser = new ClrProfilerParser(); m_clrProfilerParser.ObjectDescription += OnObjectDescription; m_clrProfilerParser.GCRoot += OnGCRoot; m_clrProfilerParser.HeapDump += OnHeapDump; m_clrProfilerParser.StaticVar += OnStaticVar; m_clrProfilerParser.LocalVar += OnLocalVar; m_clrProfilerParser.ReadFile(profilerFile); // set the module names on every type if present. var nodeTypeStorage = AllocTypeNodeStorage(); for (var profilerTypeId = 0; profilerTypeId < (int)m_clrProfilerParser.TypeIdLimit; profilerTypeId++) { var profilerType = m_clrProfilerParser.GetTypeById((ProfilerTypeID)profilerTypeId); if (profilerType == null) { continue; } var module = profilerType.Module; if (module != null && profilerTypeId < m_profilerTypeToNodeType.Count) { var nodeTypeId = m_profilerTypeToNodeType[profilerTypeId]; if (nodeTypeId != NodeTypeIndex.Invalid) { var nodeType = GetType((NodeTypeIndex)nodeTypeId, nodeTypeStorage); nodeType.ModuleName = profilerType.Module.name; } } } // Now we have module information, process the defer local and static processing foreach (var deferedRoot in m_deferedRoots) { ProfilerType profilerType = m_clrProfilerParser.GetTypeById(deferedRoot.typeID); var appDomainNode = m_rootNode.FindOrCreateChild("[appdomain " + deferedRoot.appDomainName + "]"); var varKindNode = appDomainNode.FindOrCreateChild("[" + deferedRoot.prefix + " vars]"); var moduleName = System.IO.Path.GetFileNameWithoutExtension(profilerType.ModuleName); var moduleNode = varKindNode.FindOrCreateChild("[" + deferedRoot.prefix + " vars " + moduleName + "]"); var typeNode = moduleNode.FindOrCreateChild("[" + deferedRoot.prefix + " vars " + profilerType.name + "]"); var node = typeNode.FindOrCreateChild( profilerType.name + "+" + deferedRoot.name + " [" + deferedRoot.prefix + " var]", profilerType.ModuleName); node.AddChild(deferedRoot.nodeIndex); } // finish off the root nodes. RootIndex = m_rootNode.Build(); AllowReading(); // These are only needed for the callbacks in 'ReadFile' save space by clearing them out. m_clrProfilerParser = null; m_tempChildren = new GrowableArray <NodeIndex>(); // Clear the array m_profilerTypeToNodeType = new GrowableArray <NodeTypeIndex>(); // Clear the array m_rootNode = null; m_rootNodeForUnknownRoot = null; m_addressToNodeIndex = null; m_deferedRoots = null; }
public GCHeapGraph(GCHeap heap) : base((int)heap.NumberOfObjects) { // TODO is basically nodes that have not had 'SetNode' done to them. Thus the node index has been // allocated, but we have not processed the defintion of the node. // This list should NEVER contain duplicates (you should test if you already processed the node before // adding to this queue. Queue <NodeIndex> toDo = new Queue <NodeIndex>(); // Create the root node. var root = new MemoryNodeBuilder(this, "[root]"); // Create categories for the roots. var nodeForKind = new MemoryNodeBuilder[((int)GCRootKind.Max) + 1]; var otherRoots = root.FindOrCreateChild("[other roots]"); nodeForKind[(int)GCRootKind.LocalVar] = otherRoots.FindOrCreateChild("[unknown local vars]"); for (int i = 0; i < nodeForKind.Length; i++) { if (nodeForKind[i] == null) { nodeForKind[i] = otherRoots.FindOrCreateChild("[" + ((GCRootKind)i).ToString().ToLower() + " handles]"); } } var unreachable = root.FindOrCreateChild("[unreachable but not yet collected]"); foreach (var gcRoot in heap.Roots) { if (gcRoot.HeapReference == 0) { continue; } // TODO FIX NOW condition should not be needed, should be able to assert it. if (!heap.IsInHeap(gcRoot.HeapReference)) { continue; } Debug.Assert(heap.IsInHeap(gcRoot.HeapReference)); // Make a node for it, and notice if the root is already in the heap. var lastLimit = NodeIndexLimit; var newNodeIdx = GetNodeIndex(gcRoot.HeapReference); var nodeForGcRoot = nodeForKind[(int)gcRoot.Kind]; if (gcRoot.Type != null) { var prefix = "other"; if (gcRoot.Kind == GCRootKind.StaticVar) { prefix = "static"; } else if (gcRoot.Kind == GCRootKind.LocalVar) { prefix = "local"; } var appDomainNode = root.FindOrCreateChild("[appdomain " + gcRoot.AppDomainName + "]"); var varKindNode = appDomainNode.FindOrCreateChild("[" + prefix + " vars]"); var moduleName = Path.GetFileNameWithoutExtension(gcRoot.Type.ModuleFilePath); var moduleNode = varKindNode.FindOrCreateChild("[" + prefix + " vars " + moduleName + "]"); var typeNode = moduleNode.FindOrCreateChild("[" + prefix + " vars " + gcRoot.Type.Name + "]"); var name = gcRoot.Type.Name + "+" + gcRoot.Name + " [" + prefix + " var]"; nodeForGcRoot = typeNode.FindOrCreateChild(name, gcRoot.Type.ModuleFilePath); // TODO REMOVE // if (nodeForGcRoot.Size == 0) // nodeForGcRoot.Size = 4; } // Give the user a bit more information about runtime internal handles if (gcRoot.Kind == GCRootKind.Pinning) { var pinnedType = heap.GetObjectType(gcRoot.HeapReference); if (pinnedType.Name == "System.Object []") { // Traverse pinned object arrays a bit 'manually' here so we tell the users they are likely handles. var handleArray = new MemoryNodeBuilder( this, "[likely runtime object array handle table]", pinnedType.ModuleFilePath, newNodeIdx); handleArray.Size = pinnedType.GetSize(gcRoot.HeapReference); nodeForGcRoot.AddChild(handleArray); pinnedType.EnumerateRefsOfObjectCarefully(gcRoot.HeapReference, delegate(Address childRef, int childFieldOffset) { // Poor man's visited bit. If we did not need to add a node, then we have visited it. var childLastLimit = NodeIndexLimit; var childNodeIdx = GetNodeIndex(childRef); if (childNodeIdx < childLastLimit) // Already visited, simply add the child and move one { handleArray.AddChild(childNodeIdx); return; } var childType = heap.GetObjectType(childRef); if (childType.Name == "System.String") { // TODO FIX NOW: Only want to morph the name if something else does not point at it. var literal = new MemoryNodeBuilder( this, "[likely string literal]", childType.ModuleFilePath, childNodeIdx); literal.Size = childType.GetSize(childRef); handleArray.AddChild(literal); } else { handleArray.AddChild(childNodeIdx); toDo.Enqueue(childNodeIdx); } }); continue; // we are done processing this node. } } nodeForGcRoot.AddChild(newNodeIdx); if (newNodeIdx >= lastLimit) // have not been visited. { toDo.Enqueue(newNodeIdx); } } root.AllocateTypeIndexes(); // Create the necessary types. int memoryTypesStart = (int)NodeTypeIndexLimit; foreach (var type in heap.Types) { NodeTypeIndex nodeType = CreateType(type.Name, type.ModuleFilePath); Debug.Assert((int)nodeType == (int)type.Index + memoryTypesStart); } var children = new GrowableArray <NodeIndex>(); while (toDo.Count > 0) { var nodeIndex = toDo.Dequeue(); var objRef = GetAddress(nodeIndex); children.Clear(); GCHeapType objType = heap.GetObjectType(objRef); objType.EnumerateRefsOfObjectCarefully(objRef, delegate(Address childRef, int childFieldOffset) { Debug.Assert(heap.IsInHeap(childRef)); var lastLimit = NodeIndexLimit; var childNodeIdx = GetNodeIndex(childRef); children.Add(childNodeIdx); // Poor man's visited bit. If the index we just asked for is a new node, put it in the work queue if (childNodeIdx >= lastLimit) { toDo.Enqueue(childNodeIdx); } }); int objSize = objType.GetSize(objRef); Debug.Assert(objSize > 0); SetNode(nodeIndex, (NodeTypeIndex)((int)objType.Index + memoryTypesStart), objSize, children); } long unreachableSize = (heap.TotalSize - TotalSize); unreachable.Size = (int)unreachableSize; if (unreachable.Size != unreachableSize) { unreachable.Size = int.MaxValue; // TODO not correct on overflow } // We are done! RootIndex = root.Build(); AllowReading(); }
public PdbScopeMemoryGraph(string pdbScopeFile) : base(10000) { var children = new GrowableArray <NodeIndex>(1000); Dictionary <string, NodeTypeIndex> knownTypes = new Dictionary <string, NodeTypeIndex>(1000); XmlReaderSettings settings = new XmlReaderSettings() { IgnoreWhitespace = true, IgnoreComments = true }; using (XmlReader reader = XmlReader.Create(pdbScopeFile, settings)) { int foundBestRoot = int.MaxValue; // it is zero when when we find the best root. Address imageBase = 0; uint sizeOfImageHeader = 0; Address lastAddress = 0; int badValues = 0; Queue <Section> sections = new Queue <Section>(); Address prevAddr = 0; Address expectedAddr = 0; RootIndex = NodeIndex.Invalid; NodeIndex firstNodeIndex = NodeIndex.Invalid; while (reader.Read()) { if (reader.NodeType == XmlNodeType.Element) { switch (reader.Name) { case "Section": { Section section = new Section(); section.Start = Address.Parse(reader.GetAttribute("Start")); section.Size = uint.Parse(reader.GetAttribute("Size")); section.Name = reader.GetAttribute("Name"); sections.Enqueue(section); lastAddress = Math.Max(lastAddress, section.EndRoundedUpToPage); } break; case "Module": if (imageBase == 0) { imageBase = Address.Parse(reader.GetAttribute("Base")); sizeOfImageHeader = 1024; // We are using the file size number NodeIndex nodeIndex = GetNodeIndex(imageBase); NodeTypeIndex typeIndex = CreateType("Image Header"); children.Clear(); SetNode(nodeIndex, typeIndex, (int)sizeOfImageHeader, children); expectedAddr = imageBase + 0x1000; DebugWriteLine("Loading Module Map table used to decode $N symbol prefixes."); string dllFilePath = reader.GetAttribute("FilePath"); if (dllFilePath != null) { LoadModuleMap(dllFilePath, pdbScopeFile); } else { DebugWriteLine("Could not find path to original DLL being analyzed."); } if (m_moduleMap != null) { DebugWriteLine("Loaded Module Map of " + m_moduleMap.Count + " Project N style IL modules to unmangled $N_ prefixes."); } else { DebugWriteLine("Warning: No Module Map Found: $N_ prefixes will not be unmangled."); } } break; case "ObjectTypes": case "Type": case "Dicectory": case "Sections": case "PdbscopeReport": case "Symbols": case "SourceFiles": case "File": break; case "Symbol": string addrStr = reader.GetAttribute("addr"); Address addr; if (addrStr != null && Address.TryParse(addrStr, NumberStyles.AllowHexSpecifier, null, out addr)) { if (addr < lastAddress) { // Get Size string sizeStr = reader.GetAttribute("size"); uint size = 0; if (sizeStr != null) { uint.TryParse(sizeStr, out size); } // Get Children children.Clear(); string to = reader.GetAttribute("to"); if (to != null) { GetChildrenForAddresses(ref children, to); } // Get Name, make a type out of it string name; NodeTypeIndex typeIndex = GetTypeForXmlElement(knownTypes, reader, size, out name); // Currently PdbScope files have extra information lines where it shows the different generic instantiations associated // with a given symbol. These ways have the same address as the previous entry and have no size (size will be 0) so // we filter these lines out with the following condition. if (prevAddr != addr || size != 0) { prevAddr = addr; if (addr < expectedAddr) { DebugWriteLine(string.Format("Got Address {0:x} which is less than the expected address {1:x}. Discarding {2}", addr, expectedAddr, name)); badValues++; if (50 < badValues) { throw new ApplicationException("Too many cases where the addresses were not ascending in the file"); } continue; // discard } /*** We want to make sure we account for all bytes, so log when we see gaps ***/ // If we don't match see if it is because of section boundary. if (addr != expectedAddr) { EmitNodesForGaps(sections, expectedAddr, addr); } expectedAddr = addr + size; NodeIndex nodeIndex = GetNodeIndex((Address)addr); SetNode(nodeIndex, typeIndex, (int)size, children); // See if this is a good root if (foundBestRoot != 0 && name != null) { if (name == "RHBinder__ShimExeMain") { RootIndex = nodeIndex; foundBestRoot = 0; } else if (0 < foundBestRoot && name.Contains("ILT$Main")) { RootIndex = nodeIndex; foundBestRoot = 1; } else if (1 < foundBestRoot & name.Contains("DllMainCRTStartup")) { RootIndex = nodeIndex; foundBestRoot = 1; } else if (2 < foundBestRoot & name.Contains("Main")) { RootIndex = nodeIndex; foundBestRoot = 2; } } // Remember first node. if (firstNodeIndex == NodeIndex.Invalid) { firstNodeIndex = nodeIndex; } } } else { DebugWriteLine(string.Format("Warning Discarding Symbol node {0:x} outside the last address in the image {1:x}", addr, lastAddress)); } } else { DebugWriteLine("Error: symbol without addr"); } break; default: DebugWriteLine(string.Format("Skipping unknown element {0}", reader.Name)); break; } } } EmitNodesForGaps(sections, expectedAddr, lastAddress); if (RootIndex == NodeIndex.Invalid) { RootIndex = firstNodeIndex; } DebugWriteLine(string.Format("Image Base {0:x} LastAddress {1:x}", imageBase, lastAddress)); DebugWriteLine(string.Format("Total Virtual Size {0} ({0:x})", lastAddress - imageBase)); DebugWriteLine(string.Format("Total File Size {0} ({0:x})", TotalSize)); } AllowReading(); }
/// <summary> /// This demo shows you how to create a a graph of memory and turn it into a stackSource with MemoryGraphStackSource. /// </summary> public void DemoMemoryGraph() { // Make a custom stack source that was created out of nothing. InternStackSouce is your friend here. MemoryGraph myGraph = new MemoryGraph(1000); // Create a memory graph out of 'nothing'. In the example below we create a graph where the root which points at // 'base' which has a bunch of children, each of which have a child that points back to the base node (thus it has lots of cycles) var baseIdx = myGraph.GetNodeIndex(0); // Define the NAME (index) for the of the graph (but we have not defined what is in it) GrowableArray <NodeIndex> children = new GrowableArray <NodeIndex>(1); // This array is reused again and again for each child. GrowableArray <NodeIndex> rootChildren = new GrowableArray <NodeIndex>(100); // This is used to create the children of the root; //Here I make up a graph of memory addresses at made up locations for (Address objAddr = 0x100000; objAddr < 0x200000; objAddr += 0x10000) { NodeIndex nodeIdx = myGraph.GetNodeIndex(objAddr); // Create the name (node index) for the child // Make a type for the child. In this case we make a new type for each node, normally you keep these in a interning table so that // every distinct type name has exactly one type index. Interning is not STRICTLLY needed, but the representation assumes that // there are many more nodes than there are types. NodeTypeIndex nodeTypeIdx = myGraph.CreateType("MyType_" + objAddr.ToString(), "MyModule"); // Create a list of children for this node in this case, each node has exactly one child, which is the root node. children.Clear(); children.Add(baseIdx); // Actually define the node with the given name (nodeIdx), type, size (100 in our case) and children; myGraph.SetNode(nodeIdx, nodeTypeIdx, 100, children); rootChildren.Add(nodeIdx); // Remember this node name as belonging to the things that the root points at. } // At this point we have everything we need to define the base node, do it here. myGraph.SetNode(baseIdx, myGraph.CreateType("[Base]"), 0, rootChildren); // Create a root node that points at the base node. myGraph.RootIndex = myGraph.GetNodeIndex(1); children.Clear(); children.Add(baseIdx); myGraph.SetNode(myGraph.RootIndex, myGraph.CreateType("[ROOT]"), 0, children); // Note that the raw graph APIs force you to know all the children of a particular node before you can define // the node. There is a class call MemoryNodeBuilder which makes this a bit nicer in that you can incrementally // add children to nodes and then 'finalize' them all at once at the end. Ideally, however you don't have many // nodes that need MemoryNodeBuilder as they are more expensive to build. // So far, the graph is in 'write mode' where only creation APIs are allowed. Change to 'Read Mode' which no writes // are allowed by read APIs are allowed. (We may lift this restriction at some point). myGraph.AllowReading(); // I can dump it as XML to look at it. using (var writer = File.CreateText("MyGraph.dump.xml")) { myGraph.WriteXml(writer); } // I can write the graph out as a file and read it back in later. // myGraph.WriteAsBinaryFile("myGraph.gcGraph"); // var roundTrip = MemoryGraph.ReadFromBinaryFile("myGraph.gcGraph"); // OK we have a graph, turn it into a stack source so that I can view it. MemoryGraphStackSource myStackSource = new MemoryGraphStackSource(myGraph, LogFile); // Create a Stacks class (which remembers all the Filters, Scaling policy and other things the viewer wants. Stacks stacks = new Stacks(myStackSource); // If you wanted to, you can change the filtering ... stacks.Filter.GroupRegExs = ""; stacks.Filter.MinInclusiveTimePercent = ""; // And view it. OpenStackViewer(stacks); }
void Read(TextReader reader) { // TODO this is relatively inefficient. var regEx = new Regex(@"^\s*(\d+)\s*(\d+)\s*\[\s*(\d+)\s*\]\s*(\S*?)!?(.*)"); var stack = new GrowableArray <WTStackElem>(); WTStackElem elem = new WTStackElem(); long time = 0; var sample = new StackSourceSample(this); for (; ;) { var line = reader.ReadLine(); if (line == null) { break; } var match = regEx.Match(line); if (match.Success) { // Parse the line. int excInstrSoFar = int.Parse(match.Groups[1].Value); int depth = int.Parse(match.Groups[3].Value); string module = match.Groups[4].Value; string method = match.Groups[5].Value; // Form the name for this line var moduleIndex = Interner.ModuleIntern(module); var frameIndex = Interner.FrameIntern(method, moduleIndex); // Get the parent stack for this line var parent = StackSourceCallStackIndex.Invalid; if (depth > 0) { parent = stack[depth - 1].FirstCallStackIndex; // TODO handle out of range } // Form the stack for this entry var callStackIndex = Interner.CallStackIntern(frameIndex, parent); int exclInstr; // Number of instructions executed on this line int extra = stack.Count - depth; // The number of frames we need to pop off (including me) if (extra > 0) { // We returned from one or more methods OR we have not left the current method // elem = stack[depth]; // We expect to return to the same method we were at at this depth. if (callStackIndex == elem.CallStackIndex) { exclInstr = excInstrSoFar - elem.ExclInstrSoFar; // We are continuing the function } else { // We are tail-calling to another routine. exclInstr = excInstrSoFar; elem.CallStackIndex = callStackIndex; } // Pop off all the frames we returned from Debug.Assert(exclInstr >= 0); stack.RemoveRange(depth, extra); } else { // Means we are adding a new frame (we called someone) Debug.Assert(extra == 0); // We always add only one more frame (e.g. we never go from depth 2 to 4) elem.CallStackIndex = callStackIndex; elem.FirstCallStackIndex = callStackIndex; exclInstr = excInstrSoFar; } elem.ExclInstrSoFar = excInstrSoFar; stack.Add(elem); time += exclInstr; sample.Metric = exclInstr; sample.TimeRelativeMSec = time - exclInstr; sample.StackIndex = elem.FirstCallStackIndex; AddSample(sample); } } Interner.DoneInterning(); }
void Read(TextReader reader) { // TODO this is relatively inefficient. var regEx = new Regex(@"^\s*(\d+)\s*(\d+)\s*\[\s*(\d+)\s*\]\s*(\S*?)!?(.*)"); var stack = new GrowableArray <WTStackElem>(); WTStackElem elem = new WTStackElem(); long time = 0; for (; ;) { var line = reader.ReadLine(); if (line == null) { break; } var match = regEx.Match(line); if (match.Success) { int excInstrSoFar = int.Parse(match.Groups[1].Value); int depth = int.Parse(match.Groups[3].Value); string module = match.Groups[4].Value; string method = match.Groups[5].Value; var moduleIndex = ModuleIntern(module); var frameIndex = FrameIntern(method, moduleIndex); var parent = StackSourceCallStackIndex.Invalid; if (depth > 0) { parent = stack[depth - 1].FirstCallStackIndex; // TODO handle out of range } var callStackIndex = CallStackIntern(frameIndex, parent); int extra = stack.Count - depth; int exclInstr; if (extra > 0) { elem = stack[depth]; if (callStackIndex == elem.CallStackIndex) { exclInstr = excInstrSoFar - elem.ExclInstrSoFar; } else { exclInstr = excInstrSoFar; elem.CallStackIndex = callStackIndex; } Debug.Assert(exclInstr >= 0); stack.RemoveRange(depth, extra); } else { elem.CallStackIndex = callStackIndex; elem.FirstCallStackIndex = callStackIndex; Debug.Assert(extra == 0); exclInstr = excInstrSoFar; } elem.ExclInstrSoFar = excInstrSoFar; stack.Add(elem); time += exclInstr; var sample = new StackSourceSample(this); sample.SampleIndex = (StackSourceSampleIndex)m_samples.Count; sample.Metric = exclInstr; sample.TimeRelMSec = time - exclInstr; sample.StackIndex = elem.FirstCallStackIndex; // Break long sequences of instructions into individual samples. This // makes timeline work well. // TODO this bloats the data, not clear if this is the right tradeoff .... const int maxSize = 20; while (sample.Metric > maxSize) { var subSample = new StackSourceSample(sample); subSample.Metric = maxSize; sample.Metric -= maxSize; sample.TimeRelMSec += maxSize; m_samples.Add(subSample); sample.SampleIndex = (StackSourceSampleIndex)m_samples.Count; } m_samples.Add(sample); #if DEBUG var sampleStr = this.ToString(sample); Debug.WriteLine(sampleStr); #endif } } m_sampleTimeRelMSecLimit = time; CompletedReading(); }
void Read(TextReader reader) { var framePattern = new Regex(@"\b(\w+?)\!(\S\(?[\S\s]*\)?)"); var stackStart = new Regex(@"Call Site"); // the call stack from the debugger kc command looksl like this //Call Site //coreclr!JIT_MonEnterWorker_Portable //System_Windows_ni!MS.Internal.ManagedPeerTable.TryGetManagedPeer(IntPtr, Boolean, System.Object ByRef) //System_Windows_ni!MS.Internal.ManagedPeerTable.EnsureManagedPeer(IntPtr, Int32, System.Type, Boolean) //System_Windows_ni!MS.Internal.FrameworkCallbacks.CheckPeerType(IntPtr, System.String, Boolean) //System_Windows_ni!DomainBoundILStubClass.IL_STUB_ReversePInvoke(Int32, IntPtr, Int32) //coreclr!UM2MThunk_WrapperHelper //coreclr!UMThunkStubWorker //coreclr!UMThunkStub //agcore!CParser::StartObjectElement //agcore!CParser::Attribute //agcore!CParser::LoadXaml var stack = new GrowableArray <DebuggerCallStackFrame>(); bool newCallStackFound = false; var sample = new StackSourceSample(this); float time = 0; for (; ;) { var line = reader.ReadLine(); if (line == null) { break; } var match = framePattern.Match(line); if (match.Success && newCallStackFound) { var module = match.Groups[1].Value; var methodName = match.Groups[2].Value; // trim the methodName if it has file name info (if the trace is collected with kv instead of kc) int index = methodName.LastIndexOf(")+"); if (index != -1) { methodName = methodName.Substring(0, index + 1); } var moduleIndex = Interner.ModuleIntern(module); var frameIndex = Interner.FrameIntern(methodName, moduleIndex); DebuggerCallStackFrame frame = new DebuggerCallStackFrame(); frame.frame = frameIndex; stack.Add(frame); } else { var stackStartMatch = stackStart.Match(line); if (stackStartMatch.Success) { // start a new sample. // add the previous sample // clear the stack if (stack.Count != 0) { StackSourceCallStackIndex parent = StackSourceCallStackIndex.Invalid; for (int i = stack.Count - 1; i >= 0; --i) { parent = Interner.CallStackIntern(stack[i].frame, parent); } stack.Clear(); sample.StackIndex = parent; sample.TimeRelativeMSec = time; time++; AddSample(sample); } newCallStackFound = true; } } } Interner.DoneInterning(); }
private void Read(Stream rawStream) { XmlReaderSettings settings = new XmlReaderSettings() { IgnoreWhitespace = true, IgnoreComments = true }; XmlReader reader = XmlTextReader.Create(rawStream, settings); var stack = new GrowableArray <StackSourceSample>(); bool metricsInclusive = false; // If true, we need to convert them to exclusive as part of processing while (reader.Read()) { if (reader.NodeType == XmlNodeType.Element) { if (reader.Name == "node") { var sample = new StackSourceSample(this); string callTree = reader.GetAttribute("call_tree"); // Case for allocation stacks string sizeStr = reader.GetAttribute("size"); if (sizeStr != null) { metricsInclusive = true; // allocation numbers are inclusive int size = 0; int.TryParse(sizeStr, out size); sample.Metric = size; string recoredObectsStr = reader.GetAttribute("recorded_objects"); int recoredObects = 0; if (recoredObectsStr != null) { int.TryParse(recoredObectsStr, out recoredObects); } sample.Count = recoredObects; } else { Debug.Assert(metricsInclusive == false); // CPU time is exclusive. // For CPU string own_time_msStr = reader.GetAttribute("own_time_ms"); if (own_time_msStr != null) { int own_time_ms; int.TryParse(own_time_msStr, out own_time_ms); sample.Metric = own_time_ms; string countStr = reader.GetAttribute("count"); int count = 0; if (countStr != null) { int.TryParse(countStr, out count); } sample.Count = count; } } // Get the parent stack for this line var parentStackIndex = StackSourceCallStackIndex.Invalid; int depth = stack.Count; if (depth > 0) { StackSourceSample parent = stack[depth - 1]; parentStackIndex = parent.StackIndex; if (metricsInclusive) { // The values are inclusive, but StackSoruceSamples are the exclusive amounts, so remove children. parent.Count -= sample.Count; parent.Metric -= sample.Metric; } } if (callTree != null) { var frameIndex = Interner.FrameIntern(callTree); sample.StackIndex = Interner.CallStackIntern(frameIndex, parentStackIndex); } stack.Add(sample); } } if (reader.NodeType == XmlNodeType.EndElement || reader.IsEmptyElement) { if (reader.Name == "node") { StackSourceSample sample = stack.Pop(); if ((sample.Count > 0 || sample.Metric > 0) && sample.StackIndex != StackSourceCallStackIndex.Invalid) { AddSample(sample); } } } } Debug.Assert(stack.Count == 0); Interner.DoneInterning(); }
void Read(TextReader reader) { var stack = new GrowableArray <StackSourceCallStackIndex>(); var line = reader.ReadLine(); // Skip the first line, which is column headers. var sample = new StackSourceSample(this); for (; ;) { line = reader.ReadLine(); if (line == null) { break; } // 0 1 2 3 4 5 6 7 8 // Order, # of Calls, % Incl Time, % Excl Time, Depth, Function, Module, Incl Time, Excl Time,% Sw. Out, Incl Switched Out, Type, Comments Min Avg Max Excl Switched Out int idx = 0; int depth = 0; string method = null; string module = null; int intVal; long longVal; for (int col = 0; col <= 8; col++) { var newIdx = line.IndexOf('\t', idx); Debug.Assert(0 < newIdx); if (newIdx < 0) { goto SKIP; } switch (col) { case 1: int.TryParse(line.Substring(idx, newIdx - idx), System.Globalization.NumberStyles.Number, null, out intVal); sample.Count = intVal; break; case 4: int.TryParse(line.Substring(idx, newIdx - idx), System.Globalization.NumberStyles.Number, null, out depth); break; case 5: while (idx < newIdx) { if (line[idx] != ' ') { break; } idx++; } method = line.Substring(idx, newIdx - idx); method = method.Replace((char)0xFFFD, '@'); // They used this character to separate the method name from signature. break; case 6: module = ""; if (depth != 0) { module = line.Substring(idx, newIdx - idx); } break; case 8: long.TryParse(line.Substring(idx, newIdx - idx), System.Globalization.NumberStyles.Number, null, out longVal); sample.Metric = longVal / 1000000; // TODO what is the metric? break; } idx = newIdx + 1; } var moduleIdx = Interner.ModuleIntern(module); var frameIdx = Interner.FrameIntern(method, moduleIdx); var prevFrame = StackSourceCallStackIndex.Invalid; if (0 < depth && depth <= stack.Count) { prevFrame = stack[depth - 1]; } var callStackIdx = Interner.CallStackIntern(frameIdx, prevFrame); if (depth < stack.Count) { stack.Count = depth; } stack.Add(callStackIdx); sample.StackIndex = callStackIdx; AddSample(sample); SKIP :; } Interner.DoneInterning(); }
/// <summary> /// Reads the Nodes Element /// </summary> private static void ReadNodesFromXml(XmlReader reader, MemoryGraph graph) { Debug.Assert(reader.NodeType == XmlNodeType.Element); var inputDepth = reader.Depth; reader.Read(); // Advance to children var children = new GrowableArray <NodeIndex>(1000); var typeStorage = graph.AllocTypeNodeStorage(); while (inputDepth < reader.Depth) { if (reader.NodeType == XmlNodeType.Element) { switch (reader.Name) { case "Node": { NodeIndex readNodeIndex = (NodeIndex)FetchInt(reader, "Index", -1); NodeTypeIndex typeIndex = (NodeTypeIndex)FetchInt(reader, "TypeIndex", -1); int size = FetchInt(reader, "Size"); if (readNodeIndex == NodeIndex.Invalid) { throw new ApplicationException("Node element does not have a Index attribute."); } if (typeIndex == NodeTypeIndex.Invalid) { throw new ApplicationException("Node element does not have a TypeIndex attribute."); } // TODO FIX NOW very inefficient. Use ReadValueChunk and FastStream to make more efficient. children.Clear(); var body = reader.ReadElementContentAsString(); foreach (var num in Regex.Split(body, @"\s+")) { if (num.Length > 0) { children.Add((NodeIndex)int.Parse(num)); } } if (size == 0) { size = graph.GetType(typeIndex, typeStorage).Size; } // TODO should probably just reserve node index 0 to be an undefined object? NodeIndex nodeIndex = 0; if (readNodeIndex != 0) { nodeIndex = graph.CreateNode(); } if (readNodeIndex != nodeIndex) { throw new ApplicationException("Node Indexes do not start at 0 or 1 and increase consecutively."); } graph.SetNode(nodeIndex, typeIndex, size, children); } break; default: Debug.WriteLine("Skipping unknown element {0}", reader.Name); reader.Skip(); break; } } else if (!reader.Read()) { break; } } }
public void MethodOne(GrowableArray <double> gad) { ; }
public void MethodTwo(GrowableArray <object> gad) { ; }
static void main() { GrowableArray <int> ga = new GrowableArray <int>(); //MethodOne(ga); //wil NOT compile //MethodTwo(ga); //wil NOT compile }
public MemoryGraph(int expectedSize) : base(expectedSize) { m_addressToNodeIndex = new Dictionary <Address, NodeIndex>(expectedSize); m_nodeAddresses = new GrowableArray <Address>(expectedSize); }
/// <summary> /// After reading the events the graph is not actually created, you need to post process the information we gathered /// from the events. This is where that happens. Thus 'SetupCallbacks, Process(), ConvertHeapDataToGraph()' is how /// you dump a heap. /// </summary> internal unsafe void ConvertHeapDataToGraph() { int maxNodeCount = 10_000_000; if (m_converted) { return; } m_converted = true; if (!m_seenStart) { if (m_processName != null) { throw new ApplicationException("ETL file did not include a Heap Dump for process " + m_processName); } throw new ApplicationException("ETL file did not include a Heap Dump for process ID " + m_processId); } if (!m_ignoreEvents) { throw new ApplicationException("ETL file shows the start of a heap dump but not its completion."); } m_log.WriteLine("Processing Heap Data, BulkTypeEventCount:{0} BulkNodeEventCount:{1} BulkEdgeEventCount:{2}", m_typeBlocks.Count, m_nodeBlocks.Count, m_edgeBlocks.Count); // Process the type information (we can't do it on the fly because we need the module information, which may be // at the end of the trace. while (m_typeBlocks.Count > 0) { GCBulkTypeTraceData data = m_typeBlocks.Dequeue(); for (int i = 0; i < data.Count; i++) { GCBulkTypeValues typeData = data.Values(i); var typeName = typeData.TypeName; if (IsProjectN) { // For project N we only log the type ID and module base address. Debug.Assert(typeName.Length == 0); Debug.Assert((typeData.Flags & TypeFlags.ModuleBaseAddress) != 0); var moduleBaseAddress = typeData.TypeID - (ulong)typeData.TypeNameID; // Tricky way of getting the image base. Debug.Assert((moduleBaseAddress & 0xFFFF) == 0); // Image loads should be on 64K boundaries. Module module = GetModuleForImageBase(moduleBaseAddress); if (module.Path == null) { m_log.WriteLine("Error: Could not find DLL name for imageBase 0x{0:x} looking up typeID 0x{1:x} with TypeNameID {2:x}", moduleBaseAddress, typeData.TypeID, typeData.TypeNameID); } m_typeID2TypeIndex[typeData.TypeID] = m_graph.CreateType(typeData.TypeNameID, module); } else { if (typeName.Length == 0) { if ((typeData.Flags & TypeFlags.Array) != 0) { typeName = "ArrayType(0x" + typeData.TypeNameID.ToString("x") + ")"; } else { typeName = "Type(0x" + typeData.TypeNameID.ToString("x") + ")"; } } // TODO FIX NOW these are kind of hacks typeName = Regex.Replace(typeName, @"`\d+", ""); typeName = typeName.Replace("[", "<"); typeName = typeName.Replace("]", ">"); typeName = typeName.Replace("<>", "[]"); string moduleName; if (!m_moduleID2Name.TryGetValue(typeData.ModuleID, out moduleName)) { moduleName = "Module(0x" + typeData.ModuleID.ToString("x") + ")"; m_moduleID2Name[typeData.ModuleID] = moduleName; } // Is this type a an RCW? If so mark the type name that way. if ((typeData.Flags & TypeFlags.ExternallyImplementedCOMObject) != 0) { typeName = "[RCW " + typeName + "]"; } m_typeID2TypeIndex[typeData.TypeID] = CreateType(typeName, moduleName); // Trace.WriteLine(string.Format("Type 0x{0:x} = {1}", typeData.TypeID, typeName)); } } } // Process all the ccw root information (which also need the type information complete) var ccwRoot = m_root.FindOrCreateChild("[COM/WinRT Objects]"); while (m_ccwBlocks.Count > 0) { GCBulkRootCCWTraceData data = m_ccwBlocks.Dequeue(); GrowableArray <NodeIndex> ccwChildren = new GrowableArray <NodeIndex>(1); for (int i = 0; i < data.Count; i++) { unsafe { GCBulkRootCCWValues ccwInfo = data.Values(i); // TODO Debug.Assert(ccwInfo.IUnknown != 0); if (ccwInfo.IUnknown == 0) { // TODO currently there are times when a CCWs IUnknown pointer is not set (it is set lazily). // m_log.WriteLine("Warning seen a CCW with IUnknown == 0"); continue; } // Create a CCW node that represents the COM object that has one child that points at the managed object. var ccwNode = m_graph.GetNodeIndex(ccwInfo.IUnknown); var ccwTypeIndex = GetTypeIndex(ccwInfo.TypeID, 200); var ccwType = m_graph.GetType(ccwTypeIndex, m_typeStorage); var typeName = "[CCW 0x" + ccwInfo.IUnknown.ToString("x") + " for type " + ccwType.Name + "]"; ccwTypeIndex = CreateType(typeName); ccwChildren.Clear(); ccwChildren.Add(m_graph.GetNodeIndex(ccwInfo.ObjectID)); m_graph.SetNode(ccwNode, ccwTypeIndex, 200, ccwChildren); ccwRoot.AddChild(ccwNode); } } } // Process all the static variable root information (which also need the module information complete var staticVarsRoot = m_root.FindOrCreateChild("[static vars]"); while (m_staticVarBlocks.Count > 0) { GCBulkRootStaticVarTraceData data = m_staticVarBlocks.Dequeue(); for (int i = 0; i < data.Count; i++) { GCBulkRootStaticVarValues staticVarData = data.Values(i); var rootToAddTo = staticVarsRoot; if ((staticVarData.Flags & GCRootStaticVarFlags.ThreadLocal) != 0) { rootToAddTo = m_root.FindOrCreateChild("[thread static vars]"); } // Get the type name. NodeTypeIndex typeIdx; string typeName; if (m_typeID2TypeIndex.TryGetValue(staticVarData.TypeID, out typeIdx)) { var type = m_graph.GetType(typeIdx, m_typeStorage); typeName = type.Name; } else { typeName = "Type(0x" + staticVarData.TypeID.ToString("x") + ")"; } string fullFieldName = typeName + "." + staticVarData.FieldName; rootToAddTo = rootToAddTo.FindOrCreateChild("[static var " + fullFieldName + "]"); var nodeIdx = m_graph.GetNodeIndex(staticVarData.ObjectID); rootToAddTo.AddChild(nodeIdx); } } // var typeStorage = m_graph.AllocTypeNodeStorage(); GCBulkNodeUnsafeNodes nodeStorage = new GCBulkNodeUnsafeNodes(); // Process all the node and edge nodes we have collected. bool doCompletionCheck = true; for (; ;) { GCBulkNodeUnsafeNodes *node = GetNextNode(&nodeStorage); if (node == null) { break; } // Get the node index var nodeIdx = m_graph.GetNodeIndex((Address)node->Address); var objSize = (int)node->Size; Debug.Assert(node->Size < 0x1000000000); var typeIdx = GetTypeIndex(node->TypeID, objSize); // TODO FIX NOW REMOVE // var type = m_graph.GetType(typeIdx, typeStorage); // Trace.WriteLine(string.Format("Got Object 0x{0:x} Type {1} Size {2} #children {3} nodeIdx {4}", (Address)node->Address, type.Name, objSize, node->EdgeCount, nodeIdx)); // Process the edges (which can add children) m_children.Clear(); for (int i = 0; i < node->EdgeCount; i++) { Address edge = GetNextEdge(); var childIdx = m_graph.GetNodeIndex(edge); m_children.Add(childIdx); // Trace.WriteLine(string.Format(" Child 0x{0:x}", edge)); } // TODO we can use the nodes type to see if this is an RCW before doing this lookup which may be a bit more efficient. RCWInfo info; if (m_objectToRCW.TryGetValue((Address)node->Address, out info)) { // Add the COM object this RCW points at as a child of this node. m_children.Add(m_graph.GetNodeIndex(info.IUnknown)); // We add 1000 to account for the overhead of the RCW that is NOT on the GC heap. objSize += 1000; } Debug.Assert(!m_graph.IsDefined(nodeIdx)); m_graph.SetNode(nodeIdx, typeIdx, objSize, m_children); if (m_graph.NodeCount >= maxNodeCount) { doCompletionCheck = false; var userMessage = string.Format("Exceeded max node count {0}", maxNodeCount); m_log.WriteLine("[WARNING: ]", userMessage); break; } } if (doCompletionCheck && m_curEdgeBlock != null && m_curEdgeBlock.Count != m_curEdgeIdx) { throw new ApplicationException("Error: extra edge data. Giving up on heap dump."); } m_root.Build(); m_graph.RootIndex = m_root.Index; }
private void Read(XmlReader reader) { Stack <string> frameStack = null; // We use the invarient culture, otherwise if we encode in france and decode // in english we get parse errors (this happened!); var invariantCulture = CultureInfo.InvariantCulture; var inputDepth = reader.Depth; var depthForSamples = 0; while (reader.Read()) { PROCESS_NODE: switch (reader.NodeType) { case XmlNodeType.Element: if (reader.Name == "Sample") { var sample = new StackSourceSample(this); sample.Metric = 1; if (reader.MoveToFirstAttribute()) { do { if (reader.Name == "Time") { sample.TimeRelativeMSec = double.Parse(reader.ReadContentAsString(), invariantCulture); } else if (reader.Name == "StackID") { sample.StackIndex = (StackSourceCallStackIndex)reader.ReadContentAsInt(); } else if (reader.Name == "Metric") { sample.Metric = float.Parse(reader.ReadContentAsString(), invariantCulture); } } while (reader.MoveToNextAttribute()); } sample.SampleIndex = (StackSourceSampleIndex)m_curSample; m_samples.Set(m_curSample++, sample); if (sample.TimeRelativeMSec > m_maxTime) { m_maxTime = sample.TimeRelativeMSec; } // See if there is a literal stack present as the body of if (!reader.Read()) { break; } if (reader.NodeType != XmlNodeType.Text) { goto PROCESS_NODE; } string rawStack = reader.Value.Trim(); if (0 < rawStack.Length) { InitInterner(); StackSourceCallStackIndex stackIdx = StackSourceCallStackIndex.Invalid; string[] frames = rawStack.Split('\n'); for (int i = frames.Length - 1; 0 <= i; --i) { var frameIdx = m_interner.FrameIntern(frames[i].Trim()); stackIdx = m_interner.CallStackIntern(frameIdx, stackIdx); } sample.StackIndex = stackIdx; } } else if (reader.Name == "Stack") { int stackID = -1; int callerID = -1; int frameID = -1; if (reader.MoveToFirstAttribute()) { do { if (reader.Name == "ID") { stackID = reader.ReadContentAsInt(); } else if (reader.Name == "FrameID") { frameID = reader.ReadContentAsInt(); } else if (reader.Name == "CallerID") { callerID = reader.ReadContentAsInt(); } } while (reader.MoveToNextAttribute()); if (0 <= stackID) { m_stacks.Set(stackID, new Frame(frameID, callerID)); } } } else if (reader.Name == "Frame") { var frameID = -1; if (reader.MoveToFirstAttribute()) { do { if (reader.Name == "ID") { frameID = reader.ReadContentAsInt(); } } while (reader.MoveToNextAttribute()); } reader.Read(); // Move on to body of the element var frameName = reader.ReadContentAsString(); m_frames.Set(frameID, frameName); } else if (reader.Name == "Frames") { var count = reader.GetAttribute("Count"); if (count != null && m_frames.Count == 0) { m_frames = new GrowableArray <string>(int.Parse(count)); } } else if (reader.Name == "Stacks") { var count = reader.GetAttribute("Count"); if (count != null && m_stacks.Count == 0) { m_stacks = new GrowableArray <Frame>(int.Parse(count)); } #if DEBUG for (int i = 0; i < m_stacks.Count; i++) { m_stacks[i] = new Frame(int.MinValue, int.MinValue); } #endif } else if (reader.Name == "Samples") { var count = reader.GetAttribute("Count"); if (count != null && m_samples.Count == 0) { m_samples = new GrowableArray <StackSourceSample>(int.Parse(count)); } depthForSamples = reader.Depth; } // This is the logic for the JSON case. These are the anonymous object representing a sample. else if (reader.Name == "item") { // THis is an item which is an element of the 'Samples' array. if (reader.Depth == depthForSamples + 1) { var sample = new StackSourceSample(this); sample.Metric = 1; InitInterner(); int depthForSample = reader.Depth; if (frameStack == null) { frameStack = new Stack <string>(); } frameStack.Clear(); while (reader.Read()) { PROCESS_NODE_SAMPLE: if (reader.Depth <= depthForSample) { break; } if (reader.NodeType == XmlNodeType.Element) { if (reader.Name == "Time") { sample.TimeRelativeMSec = reader.ReadElementContentAsDouble(); goto PROCESS_NODE_SAMPLE; } else if (reader.Name == "Metric") { sample.Metric = (float)reader.ReadElementContentAsDouble(); goto PROCESS_NODE_SAMPLE; } else if (reader.Name == "item") { // Item is a string under stack under the sample. if (reader.Depth == depthForSample + 2) { frameStack.Push(reader.ReadElementContentAsString()); goto PROCESS_NODE_SAMPLE; } } } } // Reverse the order of the frames in the stack. sample.StackIndex = StackSourceCallStackIndex.Invalid; while (0 < frameStack.Count) { var frameIdx = m_interner.FrameIntern(frameStack.Pop()); sample.StackIndex = m_interner.CallStackIntern(frameIdx, sample.StackIndex); } if (sample.TimeRelativeMSec > m_maxTime) { m_maxTime = sample.TimeRelativeMSec; } sample.SampleIndex = (StackSourceSampleIndex)m_curSample; m_samples.Set(m_curSample++, sample); } } break; case XmlNodeType.EndElement: if (reader.Depth <= inputDepth) { reader.Read(); goto Done; } break; case XmlNodeType.Text: default: break; } } Done :; #if DEBUG for (int i = 0; i < m_samples.Count; i++) { Debug.Assert(m_samples[i] != null); } for (int i = 0; i < m_frames.Count; i++) { Debug.Assert(m_frames[i] != null); } for (int i = 0; i < m_stacks.Count; i++) { Debug.Assert(m_stacks[i].frameID >= 0); Debug.Assert(m_stacks[i].callerID >= -1); } #endif }
internal unsafe void ConvertHeapDataToGraph() { if (m_converted) { return; } m_converted = true; if (!m_seenStart) { throw new ApplicationException("ETL file did not include a Start Heap Dump Event"); } if (!m_ignoreEvents) { throw new ApplicationException("ETL file did not include a Stop Heap Dump Event"); } // Since we may have multiple roots, I create a pseudo-node to act as its parent. var root = new MemoryNodeBuilder(m_graph, "[JS Roots]"); var nodeNames = new GrowableArray <string>(1000); for (; ;) { BulkNodeValues node; if (!GetNextNode(out node)) { break; } // Get the node index var nodeIdx = m_graph.GetNodeIndex(node.Address); m_children.Clear(); // Get the basic type name var typeName = "?"; if (0 <= node.TypeNameId) { typeName = m_stringTable[node.TypeNameId]; if (typeName.Length > 6 && typeName.EndsWith("Object")) { typeName = "JS" + typeName.Substring(0, typeName.Length - 6); } } var relationships = ""; // Process the edges (which can add children) for (int i = 0; i < node.EdgeCount; i++) { BulkEdgeValues edge; if (!GetNextEdge(out edge)) { throw new ApplicationException("Missing Edge Nodes in ETW data"); } // Is this an edge to another object? (externals count) if (edge.TargetType == EdgeTargetType.Object || edge.TargetType == EdgeTargetType.External) { var childIdx = m_graph.GetNodeIndex((Address)edge.Value); // Get the property name if it has one string childPropertyName = null; if (edge.RelationshipType == EdgeRelationshipType.NamedProperty || edge.RelationshipType == EdgeRelationshipType.Event) { childPropertyName = m_stringTable[edge.NameId]; } else if (edge.RelationshipType == EdgeRelationshipType.IndexedProperty) { // The edge is an element of an array and the NameID is the index in the array. // We want to treat all index properties as a single field childPropertyName = "[]"; } else if (edge.RelationshipType == EdgeRelationshipType.InternalProperty) { childPropertyName = "InternalProperty"; } if (childPropertyName != null) { // Remember the property so that when we display the target object, we show that too. if ((int)childIdx >= nodeNames.Count) { nodeNames.Count = (int)childIdx + 100; // expand by at least 100. } nodeNames[(int)childIdx] = childPropertyName; } m_children.Add(childIdx); } else if (edge.TargetType == EdgeTargetType.BSTR) { if (edge.RelationshipType == EdgeRelationshipType.RelationShip) { // This is extra information (typically the tag or a class of a HTML DOM object). Add it to the type name. var relationshipName = m_stringTable[edge.NameId]; var relationshipValue = m_stringTable[(int)edge.Value]; if (relationships.Length > 0) { relationships += " "; } relationships += relationshipName + ":" + relationshipValue; } } } // Get the property name we saved from things that refer to this object. string propertyName = null; if ((int)nodeIdx < nodeNames.Count) { propertyName = nodeNames[(int)nodeIdx]; if (propertyName != null) { nodeNames[(int)nodeIdx] = null; } } // Process the attributes. We can get a good function name as well as some more children from the attributes. int objSize = node.Size; for (int i = 0; i < node.AttributeCount; i++) { BulkAttributeValues attribute; if (!GetNextAttribute(out attribute)) { throw new ApplicationException("Missing Attribute Nodes in ETW data"); } // TODO FIX NOW Currently I include the prototype link. is this a good idea? if (attribute.Type == AttributeType.Prototype) { m_children.Add(m_graph.GetNodeIndex(attribute.Value)); } else if (attribute.Type == AttributeType.Scope) { // TODO FIX NOW: it seems that Value is truncated to 32 bits and we have to restore it. // Feels like a hack (and not clear if it is correct) var target = attribute.Value; if ((target >> 32) == 0 && (node.Address >> 32) != 0) { target += node.Address & 0xFFFFFFFF00000000; } m_children.Add(m_graph.GetNodeIndex(target)); } // WPA does this, I don't really understand it. if (attribute.Type == AttributeType.TextChildrenSize) { objSize += (int)attribute.Value; } else if (attribute.Type == AttributeType.FunctionName) { propertyName = m_stringTable[(int)attribute.Value]; } } if (relationships.Length > 0) { typeName += " <|" + relationships + "|>"; } // Create the complete type name if ((node.Flags & ObjectFlags.WINRT) != 0) { typeName = "(WinRT " + " " + typeName + ")"; } else { typeName = "(Type " + " " + typeName + ")"; } if (propertyName != null) { typeName = propertyName + " " + typeName; } // typeName += " [0x" + node.Address.ToString("x") + "]"; var typeIdx = GetTypeIndex(typeName, node.Size); if ((node.Flags & ObjectFlags.IS_ROOT) != 0) { root.AddChild(nodeIdx); } if (!m_graph.IsDefined(nodeIdx)) { m_graph.SetNode(nodeIdx, typeIdx, objSize, m_children); } else { // Only external objects might be listed twice. Debug.Assert((node.Flags & (ObjectFlags.EXTERNAL | ObjectFlags.EXTERNAL_UNKNOWN | ObjectFlags.EXTERNAL_DISPATCH | ObjectFlags.WINRT_DELEGATE | ObjectFlags.WINRT_INSTANCE | ObjectFlags.WINRT_NAMESPACE | ObjectFlags.WINRT_RUNTIMECLASS)) != 0); } } root.Build(); m_graph.RootIndex = root.Index; }