fremag/MemoScope.Net

View on GitHub
MemoScope/Modules/DumpDiff/DumpDiffModule.cs

Summary

Maintainability
A
1 hr
Test Coverage
using MemoScope.Core;
using MemoScope.Core.Helpers;
using System.Collections.Generic;
using WinFwk.UIModules;
using BrightIdeasSoftware;
using System.Drawing;
using MemoScope.Core.Data;
using MemoScope.Modules.Instances;
using System.Windows.Forms;
using System.Collections;
using System;
using System.Linq;

namespace MemoScope.Modules.DumpDiff
{
    
    public partial class DumpDiffModule : UIModule
    {
        public enum SortMode { Value, AbsValue }
        private List<ClrDump> ClrDumps { get; set; }
        HashSet<string> typeNames = new HashSet<string>();

        public DumpDiffModule()
        {
            InitializeComponent();
            cbSortMode.Items.Add(SortMode.Value);
            cbSortMode.Items.Add(SortMode.AbsValue);
            cbSortMode.SelectedItem = SortMode.AbsValue;
        }

        public void Setup(List<ClrDump> clrDumps)
        {
            ClrDumps = clrDumps;
            Icon = Properties.Resources.subtotal_small;
            Name = $"Dump diff";
            dlvDumpDiff.SetUpTypeColumn(colType);
            colType.Text = "Type";
            colType.AspectGetter = o => (string)o;
            ClrDump prevClrDump = null;
            foreach(var clrDump in ClrDumps.OrderBy( dump => dump.Id))
            {
                var stats = clrDump.GetTypeStats();
                DiffColumn diffCol = new DiffColumn(clrDump, stats, prevClrDump?.GetTypeStats());
                dlvDumpDiff.AllColumns.Add(diffCol);
                prevClrDump = clrDump;
                dlvDumpDiff.RegisterDataProvider(() => SelectedTypeInstancesAddressList(clrDump), this, $"#{clrDump.Id}");
            }
            dlvDumpDiff.RebuildColumns();
            dlvDumpDiff.UseCellFormatEvents = true;
            dlvDumpDiff.FormatCell += OnFormatCell;
            dlvDumpDiff.CellClick += OnCellClick;
            dlvDumpDiff.CustomSorter = DumpDiffSort;
            
            dlvDumpDiff.SetRegexFilter(regexFilterControl, o => (string)o);
        }

        private void DumpDiffSort(OLVColumn column, SortOrder sortOrder)
        {
            dlvDumpDiff.ListViewItemSorter = new DumpDiffComparer(column, sortOrder, (SortMode)cbSortMode.SelectedItem);
        }

        private void OnCellClick(object sender, CellClickEventArgs e)
        {
            if(e.ClickCount != 2)
            {
                return;
            }
            var col = e.Column as DiffColumn;
            if( col == null)
            {
                return;
            }
            var clrDumpType = SelectedTypeInstancesAddressList(col.ClrDump);
            TypeInstancesModule.Create(clrDumpType, this, mod => RequestDockModule(mod));
        }

        private ClrDumpType SelectedTypeInstancesAddressList(ClrDump clrDump)
        {
            var selectedType = dlvDumpDiff.SelectedObject<string>();
            return new ClrDumpType(clrDump, selectedType);
        }

        private void OnFormatCell(object sender, FormatCellEventArgs e)
        {
            if (e.Column == colType || e.ColumnIndex <= 1 || e.CellValue == null || ! (e.CellValue is long))
            {
                return;
            }

            var value = (long)e.CellValue;
            if (value > 0)
            {
                e.SubItem.BackColor = Color.LightGreen;
            }
            else if (value < 0)
            {
                e.SubItem.BackColor = Color.LightPink;
            }
            else
            {
                e.SubItem.BackColor = Color.LightGray;
            }
        }

        public override void Init()
        {
            foreach (var clrDump in ClrDumps)
            {
                var stats = clrDump.GetTypeStats();
                foreach (var stat in stats)
                {
                    typeNames.Add(stat.Type.Name);
                }
            }
        }

        public override void PostInit()
        {
            base.PostInit();
            Summary = $"{ClrDumps.Count} dumps, {typeNames.Count} types";

            dlvDumpDiff.Objects= typeNames;
            dlvDumpDiff.Sort(dlvDumpDiff.AllColumns[2], SortOrder.Descending);
        }

        private void cbSortMode_SelectedIndexChanged(object sender, EventArgs e)
        {
            dlvDumpDiff.Sort();
        }
    }

    public class DumpDiffComparer : IComparer
    {
        OLVColumn column;
        SortOrder sortOrder;
        DumpDiffModule.SortMode sortMode;
        public DumpDiffComparer(OLVColumn column, SortOrder sortOrder, DumpDiffModule.SortMode sortMode)
        {
            this.column = column;
            this.sortOrder = sortOrder;
            this.sortMode = sortMode;
        }

        public int Compare(object x, object y)
        {
            if( sortOrder == SortOrder.None)
            {
                return 0;
            }

            OLVListItem itemX = x as OLVListItem;
            OLVListItem itemY = y as OLVListItem;
            var objValueX = itemX.GetSubItem(column.Index).ModelValue;
            var objValueY = itemY.GetSubItem(column.Index).ModelValue;

            long res = 0;
            if (objValueX is string || objValueY is string)
            {
                res = string.Compare((string)objValueX, (string)objValueY);
            }
            if (objValueX is long || objValueY is long)
            {
                long valueX = objValueX != null ? (long)objValueX : 0;
                long valueY = objValueY != null ? (long)objValueY : 0;
                
                if(sortMode == DumpDiffModule.SortMode.AbsValue)
                {
                    res = Math.Abs(valueX) - Math.Abs(valueY);
                }
                else
                {
                    res = valueX - valueY;
                }
            }

            return sortOrder== SortOrder.Ascending ? (int)res : -(int)res;
        }
    }
}