private ProfileThread AddThread(int start) { Debug.Assert(Calls[start].ppid == Owner.Threadppid); int entryPoint = start + 1; int ownerId, depth; /* Don't crash if were a thread with no children at the end of the calls array */ if (entryPoint < Calls.Length && Calls[start].ppid == Owner.Threadppid) { ownerId = Calls[entryPoint].ppid; depth = Calls[entryPoint].Depth; entryPoint = -1; /* Try to find a better node to use for a thread name and entryPoint if we hit HeapAllocator::Free or HeapAllocator::Allocate as the first node */ for (int j = start + 1; j < Calls.Length; j++) { if (Calls[j].Depth < depth) { break; } else if (Calls[j].Depth != depth) { /* Skip nested nodes */ continue; } int id = Calls[j].ppid; if ((id == Owner.ServerGameUpdateId || id == Owner.ClientGameUpdateId)) { entryPoint = j; ownerId = id; break; } // Set a default incase we don't find ClientGame or ServerGame update root node if (entryPoint == -1 && id != Owner.HeapFreeId && id != Owner.HeapAllocateId) { entryPoint = j; ownerId = id; } } } else { depth = Calls[start].Depth + 1; ownerId = Owner.Threadppid; entryPoint = start; } var thread = new ProfileThread(this, ownerId, Owner.ppMap[ownerId]) { StartIndex = start, Time = Calls[start].Time, EntryPointIndex = entryPoint, }; if (ownerId == Owner.ServerGameUpdateId || ownerId == Owner.ClientGameUpdateId) { thread.Flags |= ProfileThreadFlag.MainThread; MainThread = thread; } Threads.Add(thread); return(thread); }
public void ComputeTime() { Threads = new List <ProfileThread>(); if (Calls == null) { Calls = Array.Empty <CallRecord>(); return; } int threadNodeId = Owner.Threadppid; int gcNodeId = Owner.ScriptGC_STEP; int prevDepth = 0, parent = 0; ProfileThread thread = null; var depthToParent = new int[MaxDepth + 1]; for (int i = 1; i < Calls.Length; i++) { int depth = Calls[i].Depth; var time = Calls[i].Time; if (Calls[i].ppid == threadNodeId) { // Set the node count for the previous thread if (thread != null) { thread.NodeCount = i - thread.StartIndex; } thread = AddThread(i); } else if (thread == null) { // Skip until we hit a thread because the profiler lost its current profile node context continue; } Calls[i].ExclusiveTime = time; if (depth > prevDepth) { // We've entered a child node record the previous node as the parent for this depth parent = Math.Max(i - 1, 0); Debug.Assert(Calls[parent].Depth == depth - 1); depthToParent[depth] = parent; } else if (depth < prevDepth) { parent = depthToParent[depth]; if (i > 0 && depth == 0) { parent = i; } Debug.Assert(depth == 0 || depthToParent[depth] < i && Calls[depthToParent[depth]].Depth < depth); } if (Owner.IdleNodes.Contains(Calls[i].ppid)) { if (thread != null) { thread.AddIdleTime(time, Calls[i].CallCount); } SubTime(i, depthToParent); } if (Calls[i].ppid == gcNodeId && thread.Name != "CollectGarbageJob::Run") { if (thread != null) { // Lift out the GC nodes so they don't distort actual CPU costs when analysing thread.AddGCTime(time, Calls[i].CallCount); } // Subtract time from all the parents of this Node SubTime(i, depthToParent); } /* Round*/ int newTime = (int)Calls[parent].ExclusiveTime - (int)time; if (newTime < 0) { /* * HeapAllocator::Free and HeapAllocator::Free * seem to have weird times */ //Debug.Assert(parent == 0 || newTime >= -1); newTime = 0; } Calls[parent].ExclusiveTime = (uint)newTime; Debug.Assert(parent == 0 || parent == thread.StartIndex || (Calls[parent].ExclusiveTime >= 0)); prevDepth = depth; } if (thread != null) { thread.NodeCount = Calls.Length - thread.StartIndex; } Debug.Assert(MainThread != null); }