/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
- namespace MemoryTest_Communications.MemoryDiagnostics
- {
- using System;
- using System.Collections.Generic;
- using System.Diagnostics;
- using System.Windows;
- using System.Windows.Controls;
- using System.Windows.Controls.Primitives;
- using System.Windows.Media;
- using System.Windows.Threading;
- using Microsoft.Phone.Info;
-
- /// <summary>
- /// Helper class for showing current memory usage
- /// </summary>
- public static class MemoryDiagnosticsHelper
- {
- #region Constants and Fields
-
- /// <summary>
- /// The ma x_ checkpoints.
- /// </summary>
- private const long MAX_CHECKPOINTS = 10; // adjust as needed
-
- /// <summary>
- /// The ma x_ memory.
- /// </summary>
- private const long MAX_MEMORY = 90 * 1024 * 1024; // 90MB, per marketplace
-
- /// <summary>
- /// The already failed peak.
- /// </summary>
- private static bool alreadyFailedPeak; // to avoid endless Asserts
-
- /// <summary>
- /// The current memory block.
- /// </summary>
- private static TextBlock currentMemoryBlock;
-
- /// <summary>
- /// The force gc.
- /// </summary>
- private static bool forceGc;
-
- /// <summary>
- /// The last safety band.
- /// </summary>
- private static int lastSafetyBand = -1; // to avoid needless changes of colour
-
- /// <summary>
- /// The peak memory block.
- /// </summary>
- private static TextBlock peakMemoryBlock;
-
- /// <summary>
- /// The popup.
- /// </summary>
- private static Popup popup;
-
- /// <summary>
- /// The recent checkpoints.
- /// </summary>
- private static Queue<MemoryCheckpoint> recentCheckpoints;
-
- /// <summary>
- /// The timer.
- /// </summary>
- private static DispatcherTimer timer;
-
- #endregion
-
- #region Properties
-
- /// <summary>
- /// Recent checkpoints stored by the app; will always be empty in retail mode
- /// </summary>
- public static IEnumerable<MemoryCheckpoint> RecentCheckpoints
- {
- get
- {
- if (recentCheckpoints == null)
- {
- yield break;
- }
-
- foreach (MemoryCheckpoint checkpoint in recentCheckpoints)
- {
- yield return checkpoint;
- }
- }
- }
-
- #endregion
-
- #region Public Methods
-
- /// <summary>
- /// Add a checkpoint to the system to help diagnose failures. Ignored in retail mode
- /// </summary>
- /// <param name="text">
- /// Text to describe the most recent thing that happened
- /// </param>
- //[Conditional("DEBUG")]
- public static void Checkpoint(string text)
- {
- if (recentCheckpoints == null)
- {
- return;
- }
-
- if (recentCheckpoints.Count >= MAX_CHECKPOINTS - 1)
- {
- recentCheckpoints.Dequeue();
- }
-
- recentCheckpoints.Enqueue(new MemoryCheckpoint(text, GetCurrentMemoryUsage()));
- }
-
- /// <summary>
- /// Gets the current memory usage, in bytes. Returns zero in non-debug mode
- /// </summary>
- /// <returns>
- /// Current usage
- /// </returns>
- public static long GetCurrentMemoryUsage()
- {
- //#if DEBUG
-
- // don't use DeviceExtendedProperties for release builds (requires a capability)
- return (long)DeviceExtendedProperties.GetValue("ApplicationCurrentMemoryUsage");
- //#else
- // return 0;
- //#endif
- }
-
- /// <summary>
- /// Gets the peak memory usage, in bytes. Returns zero in non-debug mode
- /// </summary>
- /// <returns>
- /// Peak memory usage
- /// </returns>
- public static long GetPeakMemoryUsage()
- {
- //#if DEBUG
-
- // don't use DeviceExtendedProperties for release builds (requires a capability)
- return (long)DeviceExtendedProperties.GetValue("ApplicationPeakMemoryUsage");
- //#else
- // return 0;
- //#endif
- }
-
- /// <summary>
- /// Starts the memory diagnostic timer and shows the counter
- /// </summary>
- /// <param name="timespan">
- /// The timespan between counter updates
- /// </param>
- /// <param name="forceGc">
- /// Whether or not to force a GC before collecting memory stats
- /// </param>
- //[Conditional("DEBUG")]
- public static void Start(TimeSpan timespan, bool forceGc)
- {
- if (timer != null)
- {
- throw new InvalidOperationException("Diagnostics already running");
- }
-
- MemoryDiagnosticsHelper.forceGc = forceGc;
- recentCheckpoints = new Queue<MemoryCheckpoint>();
-
- StartTimer(timespan);
- ShowPopup();
- }
-
- /// <summary>
- /// Stops the timer and hides the counter
- /// </summary>
- //[Conditional("DEBUG")]
- public static void Stop()
- {
- HidePopup();
- StopTimer();
- recentCheckpoints = null;
- }
-
- #endregion
-
- #region Methods
-
- /// <summary>
- /// The get brush for safety band.
- /// </summary>
- /// <param name="safetyBand">
- /// The safety band.
- /// </param>
- /// <returns>
- /// </returns>
- private static Brush GetBrushForSafetyBand(int safetyBand)
- {
- switch (safetyBand)
- {
- case 0:
- return new SolidColorBrush(Colors.Green);
-
- case 1:
- return new SolidColorBrush(Colors.Orange);
-
- default:
- return new SolidColorBrush(Colors.Red);
- }
- }
-
- /// <summary>
- /// The get safety band.
- /// </summary>
- /// <param name="mem">
- /// The mem.
- /// </param>
- /// <returns>
- /// The get safety band.
- /// </returns>
- private static int GetSafetyBand(long mem)
- {
- double percent = mem / (double)MAX_MEMORY;
- if (percent <= 0.75)
- {
- return 0;
- }
-
- if (percent <= 0.90)
- {
- return 1;
- }
-
- return 2;
- }
-
- /// <summary>
- /// The hide popup.
- /// </summary>
- private static void HidePopup()
- {
- popup.IsOpen = false;
- popup = null;
- }
-
- /// <summary>
- /// The show popup.
- /// </summary>
- private static void ShowPopup()
- {
- popup = new Popup();
- double fontSize = (double)Application.Current.Resources["PhoneFontSizeSmall"] - 2;
- var foreground = (Brush)Application.Current.Resources["PhoneForegroundBrush"];
- var sp = new StackPanel
- {
- Orientation = Orientation.Horizontal,
- Background = (Brush)Application.Current.Resources["PhoneSemitransparentBrush"]
- };
- currentMemoryBlock = new TextBlock { Text = "---", FontSize = fontSize, Foreground = foreground };
- peakMemoryBlock = new TextBlock
- {
- Text = string.Empty, FontSize = fontSize, Foreground = foreground, Margin = new Thickness(5, 0, 0, 0)
- };
- sp.Children.Add(currentMemoryBlock);
- sp.Children.Add(new TextBlock { Text = " kb", FontSize = fontSize, Foreground = foreground });
- sp.Children.Add(peakMemoryBlock);
- sp.RenderTransform = new CompositeTransform
- {
- Rotation = 90, TranslateX = 480, TranslateY = 425, CenterX = 0, CenterY = 0
- };
- popup.Child = sp;
- popup.IsOpen = true;
- }
-
- /// <summary>
- /// The start timer.
- /// </summary>
- /// <param name="timespan">
- /// The timespan.
- /// </param>
- private static void StartTimer(TimeSpan timespan)
- {
- timer = new DispatcherTimer();
- timer.Interval = timespan;
- timer.Tick += timer_Tick;
- timer.Start();
- }
-
- /// <summary>
- /// The stop timer.
- /// </summary>
- private static void StopTimer()
- {
- timer.Stop();
- timer = null;
- }
-
- /// <summary>
- /// The update current memory usage.
- /// </summary>
- private static void UpdateCurrentMemoryUsage()
- {
- long mem = GetCurrentMemoryUsage();
- currentMemoryBlock.Text = string.Format("{0:N}", mem / 1024);
- int safetyBand = GetSafetyBand(mem);
- if (safetyBand != lastSafetyBand)
- {
- currentMemoryBlock.Foreground = GetBrushForSafetyBand(safetyBand);
- lastSafetyBand = safetyBand;
- }
- }
-
- /// <summary>
- /// The update peak memory usage.
- /// </summary>
- private static void UpdatePeakMemoryUsage()
- {
- if (alreadyFailedPeak)
- {
- return;
- }
-
- long peak = GetPeakMemoryUsage();
- if (peak >= MAX_MEMORY)
- {
- alreadyFailedPeak = true;
- Checkpoint("*MEMORY USAGE FAIL*");
- peakMemoryBlock.Text = "FAIL!";
- peakMemoryBlock.Foreground = new SolidColorBrush(Colors.Red);
- if (Debugger.IsAttached)
- {
- Debug.Assert(false, "Peak memory condition violated");
- }
- }
- }
-
- /// <summary>
- /// The timer_ tick.
- /// </summary>
- /// <param name="sender">
- /// The sender.
- /// </param>
- /// <param name="e">
- /// The e.
- /// </param>
- private static void timer_Tick(object sender, EventArgs e)
- {
- if (forceGc)
- {
- GC.Collect();
- }
-
- UpdateCurrentMemoryUsage();
- UpdatePeakMemoryUsage();
- }
-
- #endregion
- }
-
- /// <summary>
- /// Holds checkpoint information for diagnosing memory usage
- /// </summary>
- public class MemoryCheckpoint
- {
- #region Constructors and Destructors
-
- /// <summary>
- /// Initializes a new instance of the <see cref="MemoryCheckpoint"/> class.
- /// Creates a new instance
- /// </summary>
- /// <param name="text">
- /// Text for the checkpoint
- /// </param>
- /// <param name="memoryUsage">
- /// Memory usage at the time of the checkpoint
- /// </param>
- internal MemoryCheckpoint(string text, long memoryUsage)
- {
- this.Text = text;
- this.MemoryUsage = memoryUsage;
- }
-
- #endregion
-
- #region Properties
-
- /// <summary>
- /// The memory usage at the time of the checkpoint
- /// </summary>
- public long MemoryUsage { get; private set; }
-
- /// <summary>
- /// The text associated with this checkpoint
- /// </summary>
- public string Text { get; private set; }
-
- #endregion
- }
- }