PageRenderTime 57ms CodeModel.GetById 28ms RepoModel.GetById 1ms app.codeStats 0ms

/7.0/Spikes/MemoryTest-Communications/MemoryTest-Communications/MemoryDiagnostics/MemoryDiagnosticsHelper.cs

#
C# | 402 lines | 204 code | 53 blank | 145 comment | 16 complexity | 9682c0d59396ae23a25d8419ab3d67c9 MD5 | raw file
  1. namespace MemoryTest_Communications.MemoryDiagnostics
  2. {
  3. using System;
  4. using System.Collections.Generic;
  5. using System.Diagnostics;
  6. using System.Windows;
  7. using System.Windows.Controls;
  8. using System.Windows.Controls.Primitives;
  9. using System.Windows.Media;
  10. using System.Windows.Threading;
  11. using Microsoft.Phone.Info;
  12. /// <summary>
  13. /// Helper class for showing current memory usage
  14. /// </summary>
  15. public static class MemoryDiagnosticsHelper
  16. {
  17. #region Constants and Fields
  18. /// <summary>
  19. /// The ma x_ checkpoints.
  20. /// </summary>
  21. private const long MAX_CHECKPOINTS = 10; // adjust as needed
  22. /// <summary>
  23. /// The ma x_ memory.
  24. /// </summary>
  25. private const long MAX_MEMORY = 90 * 1024 * 1024; // 90MB, per marketplace
  26. /// <summary>
  27. /// The already failed peak.
  28. /// </summary>
  29. private static bool alreadyFailedPeak; // to avoid endless Asserts
  30. /// <summary>
  31. /// The current memory block.
  32. /// </summary>
  33. private static TextBlock currentMemoryBlock;
  34. /// <summary>
  35. /// The force gc.
  36. /// </summary>
  37. private static bool forceGc;
  38. /// <summary>
  39. /// The last safety band.
  40. /// </summary>
  41. private static int lastSafetyBand = -1; // to avoid needless changes of colour
  42. /// <summary>
  43. /// The peak memory block.
  44. /// </summary>
  45. private static TextBlock peakMemoryBlock;
  46. /// <summary>
  47. /// The popup.
  48. /// </summary>
  49. private static Popup popup;
  50. /// <summary>
  51. /// The recent checkpoints.
  52. /// </summary>
  53. private static Queue<MemoryCheckpoint> recentCheckpoints;
  54. /// <summary>
  55. /// The timer.
  56. /// </summary>
  57. private static DispatcherTimer timer;
  58. #endregion
  59. #region Properties
  60. /// <summary>
  61. /// Recent checkpoints stored by the app; will always be empty in retail mode
  62. /// </summary>
  63. public static IEnumerable<MemoryCheckpoint> RecentCheckpoints
  64. {
  65. get
  66. {
  67. if (recentCheckpoints == null)
  68. {
  69. yield break;
  70. }
  71. foreach (MemoryCheckpoint checkpoint in recentCheckpoints)
  72. {
  73. yield return checkpoint;
  74. }
  75. }
  76. }
  77. #endregion
  78. #region Public Methods
  79. /// <summary>
  80. /// Add a checkpoint to the system to help diagnose failures. Ignored in retail mode
  81. /// </summary>
  82. /// <param name="text">
  83. /// Text to describe the most recent thing that happened
  84. /// </param>
  85. //[Conditional("DEBUG")]
  86. public static void Checkpoint(string text)
  87. {
  88. if (recentCheckpoints == null)
  89. {
  90. return;
  91. }
  92. if (recentCheckpoints.Count >= MAX_CHECKPOINTS - 1)
  93. {
  94. recentCheckpoints.Dequeue();
  95. }
  96. recentCheckpoints.Enqueue(new MemoryCheckpoint(text, GetCurrentMemoryUsage()));
  97. }
  98. /// <summary>
  99. /// Gets the current memory usage, in bytes. Returns zero in non-debug mode
  100. /// </summary>
  101. /// <returns>
  102. /// Current usage
  103. /// </returns>
  104. public static long GetCurrentMemoryUsage()
  105. {
  106. //#if DEBUG
  107. // don't use DeviceExtendedProperties for release builds (requires a capability)
  108. return (long)DeviceExtendedProperties.GetValue("ApplicationCurrentMemoryUsage");
  109. //#else
  110. // return 0;
  111. //#endif
  112. }
  113. /// <summary>
  114. /// Gets the peak memory usage, in bytes. Returns zero in non-debug mode
  115. /// </summary>
  116. /// <returns>
  117. /// Peak memory usage
  118. /// </returns>
  119. public static long GetPeakMemoryUsage()
  120. {
  121. //#if DEBUG
  122. // don't use DeviceExtendedProperties for release builds (requires a capability)
  123. return (long)DeviceExtendedProperties.GetValue("ApplicationPeakMemoryUsage");
  124. //#else
  125. // return 0;
  126. //#endif
  127. }
  128. /// <summary>
  129. /// Starts the memory diagnostic timer and shows the counter
  130. /// </summary>
  131. /// <param name="timespan">
  132. /// The timespan between counter updates
  133. /// </param>
  134. /// <param name="forceGc">
  135. /// Whether or not to force a GC before collecting memory stats
  136. /// </param>
  137. //[Conditional("DEBUG")]
  138. public static void Start(TimeSpan timespan, bool forceGc)
  139. {
  140. if (timer != null)
  141. {
  142. throw new InvalidOperationException("Diagnostics already running");
  143. }
  144. MemoryDiagnosticsHelper.forceGc = forceGc;
  145. recentCheckpoints = new Queue<MemoryCheckpoint>();
  146. StartTimer(timespan);
  147. ShowPopup();
  148. }
  149. /// <summary>
  150. /// Stops the timer and hides the counter
  151. /// </summary>
  152. //[Conditional("DEBUG")]
  153. public static void Stop()
  154. {
  155. HidePopup();
  156. StopTimer();
  157. recentCheckpoints = null;
  158. }
  159. #endregion
  160. #region Methods
  161. /// <summary>
  162. /// The get brush for safety band.
  163. /// </summary>
  164. /// <param name="safetyBand">
  165. /// The safety band.
  166. /// </param>
  167. /// <returns>
  168. /// </returns>
  169. private static Brush GetBrushForSafetyBand(int safetyBand)
  170. {
  171. switch (safetyBand)
  172. {
  173. case 0:
  174. return new SolidColorBrush(Colors.Green);
  175. case 1:
  176. return new SolidColorBrush(Colors.Orange);
  177. default:
  178. return new SolidColorBrush(Colors.Red);
  179. }
  180. }
  181. /// <summary>
  182. /// The get safety band.
  183. /// </summary>
  184. /// <param name="mem">
  185. /// The mem.
  186. /// </param>
  187. /// <returns>
  188. /// The get safety band.
  189. /// </returns>
  190. private static int GetSafetyBand(long mem)
  191. {
  192. double percent = mem / (double)MAX_MEMORY;
  193. if (percent <= 0.75)
  194. {
  195. return 0;
  196. }
  197. if (percent <= 0.90)
  198. {
  199. return 1;
  200. }
  201. return 2;
  202. }
  203. /// <summary>
  204. /// The hide popup.
  205. /// </summary>
  206. private static void HidePopup()
  207. {
  208. popup.IsOpen = false;
  209. popup = null;
  210. }
  211. /// <summary>
  212. /// The show popup.
  213. /// </summary>
  214. private static void ShowPopup()
  215. {
  216. popup = new Popup();
  217. double fontSize = (double)Application.Current.Resources["PhoneFontSizeSmall"] - 2;
  218. var foreground = (Brush)Application.Current.Resources["PhoneForegroundBrush"];
  219. var sp = new StackPanel
  220. {
  221. Orientation = Orientation.Horizontal,
  222. Background = (Brush)Application.Current.Resources["PhoneSemitransparentBrush"]
  223. };
  224. currentMemoryBlock = new TextBlock { Text = "---", FontSize = fontSize, Foreground = foreground };
  225. peakMemoryBlock = new TextBlock
  226. {
  227. Text = string.Empty, FontSize = fontSize, Foreground = foreground, Margin = new Thickness(5, 0, 0, 0)
  228. };
  229. sp.Children.Add(currentMemoryBlock);
  230. sp.Children.Add(new TextBlock { Text = " kb", FontSize = fontSize, Foreground = foreground });
  231. sp.Children.Add(peakMemoryBlock);
  232. sp.RenderTransform = new CompositeTransform
  233. {
  234. Rotation = 90, TranslateX = 480, TranslateY = 425, CenterX = 0, CenterY = 0
  235. };
  236. popup.Child = sp;
  237. popup.IsOpen = true;
  238. }
  239. /// <summary>
  240. /// The start timer.
  241. /// </summary>
  242. /// <param name="timespan">
  243. /// The timespan.
  244. /// </param>
  245. private static void StartTimer(TimeSpan timespan)
  246. {
  247. timer = new DispatcherTimer();
  248. timer.Interval = timespan;
  249. timer.Tick += timer_Tick;
  250. timer.Start();
  251. }
  252. /// <summary>
  253. /// The stop timer.
  254. /// </summary>
  255. private static void StopTimer()
  256. {
  257. timer.Stop();
  258. timer = null;
  259. }
  260. /// <summary>
  261. /// The update current memory usage.
  262. /// </summary>
  263. private static void UpdateCurrentMemoryUsage()
  264. {
  265. long mem = GetCurrentMemoryUsage();
  266. currentMemoryBlock.Text = string.Format("{0:N}", mem / 1024);
  267. int safetyBand = GetSafetyBand(mem);
  268. if (safetyBand != lastSafetyBand)
  269. {
  270. currentMemoryBlock.Foreground = GetBrushForSafetyBand(safetyBand);
  271. lastSafetyBand = safetyBand;
  272. }
  273. }
  274. /// <summary>
  275. /// The update peak memory usage.
  276. /// </summary>
  277. private static void UpdatePeakMemoryUsage()
  278. {
  279. if (alreadyFailedPeak)
  280. {
  281. return;
  282. }
  283. long peak = GetPeakMemoryUsage();
  284. if (peak >= MAX_MEMORY)
  285. {
  286. alreadyFailedPeak = true;
  287. Checkpoint("*MEMORY USAGE FAIL*");
  288. peakMemoryBlock.Text = "FAIL!";
  289. peakMemoryBlock.Foreground = new SolidColorBrush(Colors.Red);
  290. if (Debugger.IsAttached)
  291. {
  292. Debug.Assert(false, "Peak memory condition violated");
  293. }
  294. }
  295. }
  296. /// <summary>
  297. /// The timer_ tick.
  298. /// </summary>
  299. /// <param name="sender">
  300. /// The sender.
  301. /// </param>
  302. /// <param name="e">
  303. /// The e.
  304. /// </param>
  305. private static void timer_Tick(object sender, EventArgs e)
  306. {
  307. if (forceGc)
  308. {
  309. GC.Collect();
  310. }
  311. UpdateCurrentMemoryUsage();
  312. UpdatePeakMemoryUsage();
  313. }
  314. #endregion
  315. }
  316. /// <summary>
  317. /// Holds checkpoint information for diagnosing memory usage
  318. /// </summary>
  319. public class MemoryCheckpoint
  320. {
  321. #region Constructors and Destructors
  322. /// <summary>
  323. /// Initializes a new instance of the <see cref="MemoryCheckpoint"/> class.
  324. /// Creates a new instance
  325. /// </summary>
  326. /// <param name="text">
  327. /// Text for the checkpoint
  328. /// </param>
  329. /// <param name="memoryUsage">
  330. /// Memory usage at the time of the checkpoint
  331. /// </param>
  332. internal MemoryCheckpoint(string text, long memoryUsage)
  333. {
  334. this.Text = text;
  335. this.MemoryUsage = memoryUsage;
  336. }
  337. #endregion
  338. #region Properties
  339. /// <summary>
  340. /// The memory usage at the time of the checkpoint
  341. /// </summary>
  342. public long MemoryUsage { get; private set; }
  343. /// <summary>
  344. /// The text associated with this checkpoint
  345. /// </summary>
  346. public string Text { get; private set; }
  347. #endregion
  348. }
  349. }