Newer
Older
Simple-Multiplayer-Unity3D / Multiplayer Project / Library / PackageCache / com.unity.performance.profile-analyzer@1.2.2 / Editor / ProfileAnalyzer.cs
  1. using System.Collections.Generic;
  2. using UnityEngine;
  3. using UnityEditorInternal;
  4. using System.Text.RegularExpressions;
  5. using System;
  6. namespace UnityEditor.Performance.ProfileAnalyzer
  7. {
  8. internal class ProfileAnalyzer
  9. {
  10. public const int kDepthAll = -1;
  11. int m_Progress = 0;
  12. ProfilerFrameDataIterator m_frameData;
  13. List<string> m_threadNames = new List<string>();
  14. ProfileAnalysis m_analysis;
  15. public ProfileAnalyzer()
  16. {
  17. }
  18. public void QuickScan()
  19. {
  20. var frameData = new ProfilerFrameDataIterator();
  21. m_threadNames.Clear();
  22. int frameIndex = 0;
  23. int threadCount = frameData.GetThreadCount(0);
  24. frameData.SetRoot(frameIndex, 0);
  25. Dictionary<string, int> threadNameCount = new Dictionary<string, int>();
  26. for (int threadIndex = 0; threadIndex < threadCount; ++threadIndex)
  27. {
  28. frameData.SetRoot(frameIndex, threadIndex);
  29. var threadName = frameData.GetThreadName();
  30. var groupName = frameData.GetGroupName();
  31. threadName = ProfileData.GetThreadNameWithGroup(threadName, groupName);
  32. if (!threadNameCount.ContainsKey(threadName))
  33. threadNameCount.Add(threadName, 1);
  34. else
  35. threadNameCount[threadName] += 1;
  36. string threadNameWithIndex = ProfileData.ThreadNameWithIndex(threadNameCount[threadName], threadName);
  37. threadNameWithIndex = ProfileData.CorrectThreadName(threadNameWithIndex);
  38. m_threadNames.Add(threadNameWithIndex);
  39. }
  40. frameData.Dispose();
  41. }
  42. public List<string> GetThreadNames()
  43. {
  44. return m_threadNames;
  45. }
  46. void CalculateFrameTimeStats(ProfileData data, out float median, out float mean, out float standardDeviation)
  47. {
  48. List<float> frameTimes = new List<float>();
  49. for (int frameIndex = 0; frameIndex < data.GetFrameCount(); frameIndex++)
  50. {
  51. var frame = data.GetFrame(frameIndex);
  52. float msFrame = frame.msFrame;
  53. frameTimes.Add(msFrame);
  54. }
  55. frameTimes.Sort();
  56. median = frameTimes[frameTimes.Count / 2];
  57. double total = 0.0f;
  58. foreach (float msFrame in frameTimes)
  59. {
  60. total += msFrame;
  61. }
  62. mean = (float)(total / (double)frameTimes.Count);
  63. if (frameTimes.Count <= 1)
  64. {
  65. standardDeviation = 0f;
  66. }
  67. else
  68. {
  69. total = 0.0f;
  70. foreach (float msFrame in frameTimes)
  71. {
  72. float d = msFrame - mean;
  73. total += (d * d);
  74. }
  75. total /= (frameTimes.Count - 1);
  76. standardDeviation = (float)Math.Sqrt(total);
  77. }
  78. }
  79. int GetClampedOffsetToFrame(ProfileData profileData, int frameIndex)
  80. {
  81. int frameOffset = profileData.DisplayFrameToOffset(frameIndex);
  82. if (frameOffset < 0)
  83. {
  84. Debug.Log(string.Format("Frame index {0} offset {1} < 0, clamping", frameIndex, frameOffset));
  85. frameOffset = 0;
  86. }
  87. if (frameOffset >= profileData.GetFrameCount())
  88. {
  89. Debug.Log(string.Format("Frame index {0} offset {1} >= frame count {2}, clamping", frameIndex, frameOffset, profileData.GetFrameCount()));
  90. frameOffset = profileData.GetFrameCount() - 1;
  91. }
  92. return frameOffset;
  93. }
  94. public static bool MatchThreadFilter(string threadNameWithIndex, List<string> threadFilters)
  95. {
  96. if (threadFilters == null || threadFilters.Count == 0)
  97. return false;
  98. if (threadFilters.Contains(threadNameWithIndex))
  99. return true;
  100. return false;
  101. }
  102. public bool IsNullOrWhiteSpace(string s)
  103. {
  104. // return string.IsNullOrWhiteSpace(parentMarker);
  105. if (s == null || Regex.IsMatch(s, @"^[\s]*$"))
  106. return true;
  107. return false;
  108. }
  109. public void RemoveMarkerTimeFromParents(MarkerData[] markers, ProfileData profileData, ProfileThread threadData, int markerAt)
  110. {
  111. // Get the info for the marker we plan to remove (assume thats what we are at)
  112. ProfileMarker profileMarker = threadData.markers[markerAt];
  113. float markerTime = profileMarker.msMarkerTotal;
  114. // Traverse parents and remove time from them
  115. int currentDepth = profileMarker.depth;
  116. for (int parentMarkerAt = markerAt - 1; parentMarkerAt >= 0; parentMarkerAt--)
  117. {
  118. ProfileMarker parentMarkerData = threadData.markers[parentMarkerAt];
  119. if (parentMarkerData.depth == currentDepth - 1)
  120. {
  121. currentDepth--;
  122. if (parentMarkerData.nameIndex < markers.Length) // Had an issue where marker not yet processed(marker from another thread)
  123. {
  124. MarkerData parentMarker = markers[parentMarkerData.nameIndex];
  125. // If a depth slice is applied we may not have a parent marker stored
  126. if (parentMarker != null)
  127. {
  128. // Revise the duration of parent to remove time from there too
  129. // Note if the marker to remove is nested (i.e. parent of the same name, this could reduce the msTotal, more than we add to the timeIgnored)
  130. parentMarker.msTotal -= markerTime;
  131. // Reduce from the max marker time too
  132. // This could be incorrect when there are many instances that contribute the the total time
  133. if (parentMarker.msMaxIndividual > markerTime)
  134. {
  135. parentMarker.msMaxIndividual -= markerTime;
  136. }
  137. if (parentMarker.msMinIndividual > markerTime)
  138. {
  139. parentMarker.msMinIndividual -= markerTime;
  140. }
  141. // Revise stored frame time
  142. FrameTime frameTime = parentMarker.frames[parentMarker.frames.Count - 1];
  143. frameTime = new FrameTime(frameTime.frameIndex, frameTime.ms - markerTime, frameTime.count);
  144. parentMarker.frames[parentMarker.frames.Count - 1] = frameTime;
  145. // Note that we have modified the time
  146. parentMarker.timeRemoved += markerTime;
  147. // Note markerTime can be 0 in some cases.
  148. // Make sure timeRemoved is never left at 0.0
  149. // This makes sure we can test for non zero to indicate the marker has been removed
  150. if (parentMarker.timeRemoved == 0.0)
  151. parentMarker.timeRemoved = double.Epsilon;
  152. }
  153. }
  154. }
  155. }
  156. }
  157. public int RemoveMarker(ProfileThread threadData, int markerAt)
  158. {
  159. ProfileMarker profileMarker = threadData.markers[markerAt];
  160. int at = markerAt;
  161. // skip marker
  162. at++;
  163. // Skip children
  164. int currentDepth = profileMarker.depth;
  165. while (at < threadData.markers.Count)
  166. {
  167. profileMarker = threadData.markers[at];
  168. if (profileMarker.depth <= currentDepth)
  169. break;
  170. at++;
  171. }
  172. // Mark the following number to be ignored
  173. int markerAndChildCount = at - markerAt;
  174. return markerAndChildCount;
  175. }
  176. public ProfileAnalysis Analyze(ProfileData profileData, List<int> selectionIndices, List<string> threadFilters, int depthFilter, bool selfTimes = false, string parentMarker = null, float timeScaleMax = 0, string removeMarker = null)
  177. {
  178. m_Progress = 0;
  179. if (profileData == null)
  180. {
  181. return null;
  182. }
  183. if (profileData.GetFrameCount() <= 0)
  184. {
  185. return null;
  186. }
  187. int frameCount = selectionIndices.Count;
  188. if (frameCount < 0)
  189. {
  190. return null;
  191. }
  192. if (profileData.HasFrames && !profileData.HasThreads)
  193. {
  194. if (!ProfileData.Load(profileData.FilePath, out profileData))
  195. {
  196. return null;
  197. }
  198. }
  199. bool processMarkers = (threadFilters != null);
  200. ProfileAnalysis analysis = new ProfileAnalysis();
  201. if (selectionIndices.Count > 0)
  202. analysis.SetRange(selectionIndices[0], selectionIndices[selectionIndices.Count - 1]);
  203. else
  204. analysis.SetRange(0, 0);
  205. m_threadNames.Clear();
  206. int maxMarkerDepthFound = 0;
  207. var threads = new Dictionary<string, ThreadData>();
  208. var markers = new MarkerData[profileData.MarkerNameCount];
  209. var removedMarkers = new Dictionary<string, double>();
  210. var mainThreadIdentifier = new ThreadIdentifier("Main Thread", 1);
  211. int markerCount = 0;
  212. bool filteringByParentMarker = false;
  213. int parentMarkerIndex = -1;
  214. if (!IsNullOrWhiteSpace(parentMarker))
  215. {
  216. // Returns -1 if this marker doesn't exist in the data set
  217. parentMarkerIndex = profileData.GetMarkerIndex(parentMarker);
  218. filteringByParentMarker = true;
  219. }
  220. int at = 0;
  221. foreach (int frameIndex in selectionIndices)
  222. {
  223. int frameOffset = profileData.DisplayFrameToOffset(frameIndex);
  224. var frameData = profileData.GetFrame(frameOffset);
  225. if (frameData == null)
  226. continue;
  227. var msFrame = frameData.msFrame;
  228. if (processMarkers)
  229. {
  230. // get the file reader in case we need to rebuild the markers rather than opening
  231. // the file for every marker
  232. for (int threadIndex = 0; threadIndex < frameData.threads.Count; threadIndex++)
  233. {
  234. float msTimeOfMinDepthMarkers = 0.0f;
  235. float msIdleTimeOfMinDepthMarkers = 0.0f;
  236. var threadData = frameData.threads[threadIndex];
  237. var threadNameWithIndex = profileData.GetThreadName(threadData);
  238. ThreadData thread;
  239. if (!threads.ContainsKey(threadNameWithIndex))
  240. {
  241. m_threadNames.Add(threadNameWithIndex);
  242. thread = new ThreadData(threadNameWithIndex);
  243. analysis.AddThread(thread);
  244. threads[threadNameWithIndex] = thread;
  245. // Update threadsInGroup for all thread records of the same group name
  246. foreach (var threadAt in threads.Values)
  247. {
  248. if (threadAt == thread)
  249. continue;
  250. if (thread.threadGroupName == threadAt.threadGroupName)
  251. {
  252. threadAt.threadsInGroup += 1;
  253. thread.threadsInGroup += 1;
  254. }
  255. }
  256. }
  257. else
  258. {
  259. thread = threads[threadNameWithIndex];
  260. }
  261. bool include = MatchThreadFilter(threadNameWithIndex, threadFilters);
  262. int parentMarkerDepth = -1;
  263. if (threadData.markers.Count != threadData.markerCount)
  264. {
  265. if (!threadData.ReadMarkers(profileData.FilePath))
  266. {
  267. Debug.LogError("failed to read markers");
  268. }
  269. }
  270. int markerAndChildCount = 0;
  271. for (int markerAt = 0, n = threadData.markers.Count; markerAt < n; markerAt++)
  272. {
  273. var markerData = threadData.markers[markerAt];
  274. if (markerAndChildCount > 0)
  275. markerAndChildCount--;
  276. string markerName = null;
  277. float ms = markerData.msMarkerTotal - (selfTimes ? markerData.msChildren : 0);
  278. var markerDepth = markerData.depth;
  279. if (markerDepth > maxMarkerDepthFound)
  280. maxMarkerDepthFound = markerDepth;
  281. if (markerDepth == 1)
  282. {
  283. markerName = profileData.GetMarkerName(markerData);
  284. if (markerName.Equals("Idle", StringComparison.Ordinal))
  285. msIdleTimeOfMinDepthMarkers += ms;
  286. else
  287. msTimeOfMinDepthMarkers += ms;
  288. }
  289. if (removeMarker != null)
  290. {
  291. if (markerAndChildCount <= 0) // If we are already removing markers - don't focus on other occurances in the children
  292. {
  293. if (markerName == null)
  294. markerName = profileData.GetMarkerName(markerData);
  295. if (markerName == removeMarker)
  296. {
  297. float removeMarkerTime = markerData.msMarkerTotal;
  298. // Remove this markers time from frame time (if its on the main thread)
  299. if (thread.threadNameWithIndex == mainThreadIdentifier.threadNameWithIndex)
  300. {
  301. msFrame -= removeMarkerTime;
  302. }
  303. if (selfTimes == false) // (Self times would not need thread or parent adjustments)
  304. {
  305. // And from thread time
  306. if (markerName == "Idle")
  307. msIdleTimeOfMinDepthMarkers -= removeMarkerTime;
  308. else
  309. msTimeOfMinDepthMarkers -= removeMarkerTime;
  310. // And from parents
  311. RemoveMarkerTimeFromParents(markers, profileData, threadData, markerAt);
  312. }
  313. markerAndChildCount = RemoveMarker(threadData, markerAt);
  314. }
  315. }
  316. }
  317. if (!include)
  318. continue;
  319. // If only looking for markers below the parent
  320. if (filteringByParentMarker)
  321. {
  322. // If found the parent marker
  323. if (markerData.nameIndex == parentMarkerIndex)
  324. {
  325. // And we are not already below the parent higher in the depth tree
  326. if (parentMarkerDepth < 0)
  327. {
  328. // record the parent marker depth
  329. parentMarkerDepth = markerData.depth;
  330. }
  331. }
  332. else
  333. {
  334. // If we are now above or beside the parent marker then we are done for this level
  335. if (markerData.depth <= parentMarkerDepth)
  336. {
  337. parentMarkerDepth = -1;
  338. }
  339. }
  340. if (parentMarkerDepth < 0)
  341. continue;
  342. }
  343. if (depthFilter != kDepthAll && markerDepth != depthFilter)
  344. continue;
  345. MarkerData marker = markers[markerData.nameIndex];
  346. if (marker != null)
  347. {
  348. if (!marker.threads.Contains(threadNameWithIndex))
  349. marker.threads.Add(threadNameWithIndex);
  350. }
  351. else
  352. {
  353. if (markerName == null)
  354. markerName = profileData.GetMarkerName(markerData);
  355. marker = new MarkerData(markerName);
  356. marker.firstFrameIndex = frameIndex;
  357. marker.minDepth = markerDepth;
  358. marker.maxDepth = markerDepth;
  359. marker.threads.Add(threadNameWithIndex);
  360. analysis.AddMarker(marker);
  361. markers[markerData.nameIndex] = marker;
  362. markerCount += 1;
  363. }
  364. marker.count += 1;
  365. if (markerAndChildCount > 0)
  366. {
  367. marker.timeIgnored += ms;
  368. // Note ms can be 0 in some cases.
  369. // Make sure timeIgnored is never left at 0.0
  370. // This makes sure we can test for non zero to indicate the marker has been ignored
  371. if (marker.timeIgnored == 0.0)
  372. marker.timeIgnored = double.Epsilon;
  373. // zero out removed marker time
  374. // so we don't record in the individual marker times, marker frame times or min/max times
  375. // ('min/max times' is calculated later from marker frame times)
  376. ms = 0f;
  377. }
  378. marker.msTotal += ms;
  379. // Individual marker time (not total over frame)
  380. if (ms < marker.msMinIndividual)
  381. {
  382. marker.msMinIndividual = ms;
  383. marker.minIndividualFrameIndex = frameIndex;
  384. }
  385. if (ms > marker.msMaxIndividual)
  386. {
  387. marker.msMaxIndividual = ms;
  388. marker.maxIndividualFrameIndex = frameIndex;
  389. }
  390. // Record highest depth foun
  391. if (markerDepth < marker.minDepth)
  392. marker.minDepth = markerDepth;
  393. if (markerDepth > marker.maxDepth)
  394. marker.maxDepth = markerDepth;
  395. FrameTime frameTime;
  396. if (frameIndex != marker.lastFrame)
  397. {
  398. marker.presentOnFrameCount += 1;
  399. frameTime = new FrameTime(frameIndex, ms, 1);
  400. marker.frames.Add(frameTime);
  401. marker.lastFrame = frameIndex;
  402. }
  403. else
  404. {
  405. frameTime = marker.frames[marker.frames.Count - 1];
  406. frameTime = new FrameTime(frameTime.frameIndex, frameTime.ms + ms, frameTime.count + 1);
  407. marker.frames[marker.frames.Count - 1] = frameTime;
  408. }
  409. }
  410. if (include)
  411. thread.frames.Add(new ThreadFrameTime(frameIndex, msTimeOfMinDepthMarkers, msIdleTimeOfMinDepthMarkers));
  412. }
  413. }
  414. analysis.UpdateSummary(frameIndex, msFrame);
  415. at++;
  416. m_Progress = (100 * at) / frameCount;
  417. }
  418. analysis.GetFrameSummary().totalMarkers = profileData.MarkerNameCount;
  419. analysis.Finalise(timeScaleMax, maxMarkerDepthFound);
  420. /*
  421. foreach (int frameIndex in selectionIndices)
  422. {
  423. int frameOffset = profileData.DisplayFrameToOffset(frameIndex);
  424. var frameData = profileData.GetFrame(frameOffset);
  425. foreach (var threadData in frameData.threads)
  426. {
  427. var threadNameWithIndex = profileData.GetThreadName(threadData);
  428. if (filterThreads && threadFilter != threadNameWithIndex)
  429. continue;
  430. const bool enterChildren = true;
  431. foreach (var markerData in threadData.markers)
  432. {
  433. var markerName = markerData.name;
  434. var ms = markerData.msFrame;
  435. var markerDepth = markerData.depth;
  436. if (depthFilter != kDepthAll && markerDepth != depthFilter)
  437. continue;
  438. MarkerData marker = markers[markerName];
  439. bucketIndex = (range > 0) ? (int)(((marker.buckets.Length-1) * (ms - first)) / range) : 0;
  440. if (bucketIndex<0 || bucketIndex > (marker.buckets.Length - 1))
  441. {
  442. // This can happen if a single marker range is longer than the frame start end (which could occur if running on a separate thread)
  443. // Debug.Log(string.Format("Marker {0} : {1}ms exceeds range {2}-{3} on frame {4}", marker.name, ms, first, last, frameIndex));
  444. if (bucketIndex > (marker.buckets.Length - 1))
  445. bucketIndex = (marker.buckets.Length - 1);
  446. else
  447. bucketIndex = 0;
  448. }
  449. marker.individualBuckets[bucketIndex] += 1;
  450. }
  451. }
  452. }
  453. */
  454. m_Progress = 100;
  455. return analysis;
  456. }
  457. public int GetProgress()
  458. {
  459. return m_Progress;
  460. }
  461. }
  462. }