485 lines
17 KiB
C#
485 lines
17 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.ComponentModel;
|
|
using System.Data;
|
|
using System.Drawing;
|
|
using System.Text;
|
|
using System.Windows.Forms;
|
|
using Dia2Lib;
|
|
|
|
namespace CruncherSharp
|
|
{
|
|
public partial class CruncherSharp : Form
|
|
{
|
|
public CruncherSharp()
|
|
{
|
|
InitializeComponent();
|
|
m_table = CreateDataTable();
|
|
bindingSourceSymbols.DataSource = m_table;
|
|
dataGridSymbols.DataSource = bindingSourceSymbols;
|
|
|
|
dataGridSymbols.Columns[0].Width = 271;
|
|
}
|
|
|
|
private void exitToolStripMenuItem_Click(object sender, EventArgs e)
|
|
{
|
|
this.Close();
|
|
}
|
|
|
|
private void loadPDBToolStripMenuItem_Click(object sender, EventArgs e)
|
|
{
|
|
if (openPdbDialog.ShowDialog() == DialogResult.OK)
|
|
{
|
|
m_symbols.Clear();
|
|
|
|
m_source = new DiaSourceClass();
|
|
try
|
|
{
|
|
Cursor.Current = Cursors.WaitCursor;
|
|
m_source.loadDataFromPdb(openPdbDialog.FileName);
|
|
m_source.openSession(out m_session);
|
|
Cursor.Current = Cursors.Default;
|
|
}
|
|
catch (System.Runtime.InteropServices.COMException exc)
|
|
{
|
|
MessageBox.Show(this, exc.ToString());
|
|
return;
|
|
}
|
|
|
|
Cursor.Current = Cursors.WaitCursor;
|
|
IDiaEnumSymbols allSymbols;
|
|
m_session.findChildren(m_session.globalScope, SymTagEnum.SymTagUDT, null, 0, out allSymbols);
|
|
|
|
// Temporarily clear the filter so, if current filter is invalid, we don't generate a ton of exceptions while populating the table
|
|
var preExistingFilter = textBoxFilter.Text;
|
|
textBoxFilter.Text = "";
|
|
|
|
{
|
|
PopulateDataTable(m_table, allSymbols);
|
|
Cursor.Current = Cursors.Default;
|
|
|
|
// Sort by name by default (ascending)
|
|
dataGridSymbols.Sort(dataGridSymbols.Columns[0], ListSortDirection.Ascending);
|
|
bindingSourceSymbols.Filter = null;// "Symbol LIKE '*rde*'";
|
|
}
|
|
|
|
// Restore the filter now that the table is populated
|
|
textBoxFilter.Text = preExistingFilter;
|
|
|
|
ShowSelectedSymbolInfo();
|
|
}
|
|
}
|
|
|
|
DataTable CreateDataTable()
|
|
{
|
|
DataTable table = new DataTable("Symbols");
|
|
|
|
DataColumn column = new DataColumn();
|
|
column.ColumnName = "Symbol";
|
|
column.ReadOnly = true;
|
|
table.Columns.Add(column);
|
|
column = new DataColumn();
|
|
column.ColumnName = "Size";
|
|
column.ReadOnly = true;
|
|
column.DataType = System.Type.GetType("System.Int32");
|
|
table.Columns.Add(column);
|
|
column = new DataColumn();
|
|
column.ColumnName = "Padding";
|
|
column.ReadOnly = true;
|
|
column.DataType = System.Type.GetType("System.Int32");
|
|
table.Columns.Add(column);
|
|
column = new DataColumn();
|
|
column.ColumnName = "Padding/Size";
|
|
column.ReadOnly = true;
|
|
column.DataType = System.Type.GetType("System.Double");
|
|
table.Columns.Add(column);
|
|
|
|
return table;
|
|
}
|
|
|
|
void PopulateDataTable(DataTable table, IDiaEnumSymbols symbols)
|
|
{
|
|
ulong cacheLineSize = GetCacheLineSize();
|
|
table.Rows.Clear();
|
|
|
|
table.BeginLoadData();
|
|
foreach (IDiaSymbol sym in symbols)
|
|
{
|
|
if (sym.length > 0 && !HasSymbol(sym.name))
|
|
{
|
|
SymbolInfo info = new SymbolInfo();
|
|
info.Set(sym.name, "", sym.length, 0);
|
|
info.ProcessChildren(sym);
|
|
|
|
long totalPadding = info.CalcTotalPadding();
|
|
|
|
DataRow row = table.NewRow();
|
|
string symbolName = sym.name;
|
|
row["Symbol"] = symbolName;
|
|
row["Size"] = info.m_size;
|
|
row["Padding"] = totalPadding;
|
|
row["Padding/Size"] = (double)totalPadding / info.m_size;
|
|
table.Rows.Add(row);
|
|
|
|
m_symbols.Add(info.m_name, info);
|
|
}
|
|
}
|
|
table.EndLoadData();
|
|
|
|
}
|
|
bool HasSymbol(string name)
|
|
{
|
|
return m_symbols.ContainsKey(name);
|
|
}
|
|
ulong GetCacheLineSize()
|
|
{
|
|
return Convert.ToUInt32(textBoxCache.Text);
|
|
}
|
|
|
|
class SymbolInfo
|
|
{
|
|
public void ProcessChildren(IDiaSymbol symbol)
|
|
{
|
|
IDiaEnumSymbols children;
|
|
symbol.findChildren(SymTagEnum.SymTagNull, null, 0, out children);
|
|
foreach (IDiaSymbol child in children)
|
|
{
|
|
SymbolInfo childInfo;
|
|
if (ProcessChild(child, out childInfo))
|
|
AddChild(childInfo);
|
|
}
|
|
// Sort children by offset, recalc padding.
|
|
// Sorting is not needed normally (for data fields), but sometimes base class order is wrong.
|
|
if (HasChildren())
|
|
{
|
|
m_children.Sort(CompareOffsets);
|
|
for (int i = 0; i < m_children.Count; ++i)
|
|
{
|
|
SymbolInfo child = m_children[i];
|
|
child.m_padding = CalcPadding(child.m_offset, i);
|
|
}
|
|
m_padding = CalcPadding((long)m_size, m_children.Count);
|
|
}
|
|
}
|
|
bool ProcessChild(IDiaSymbol symbol, out SymbolInfo info)
|
|
{
|
|
info = new SymbolInfo();
|
|
if (symbol.isStatic != 0 || (symbol.symTag != (uint)SymTagEnum.SymTagData && symbol.symTag != (uint)SymTagEnum.SymTagBaseClass))
|
|
{
|
|
return false;
|
|
}
|
|
// LocIsThisRel || LocIsNull || LocIsBitField
|
|
if (symbol.locationType != 4 && symbol.locationType != 0 && symbol.locationType != 6)
|
|
return false;
|
|
|
|
ulong len = symbol.length;
|
|
IDiaSymbol typeSymbol = symbol.type;
|
|
if (typeSymbol != null)
|
|
len = typeSymbol.length;
|
|
|
|
string symbolName = symbol.name;
|
|
if (symbol.symTag == (uint)SymTagEnum.SymTagBaseClass)
|
|
symbolName = "Base: " + symbolName;
|
|
|
|
info.Set(symbolName, (typeSymbol != null ? typeSymbol.name : ""), len, symbol.offset);
|
|
|
|
return true;
|
|
}
|
|
long CalcPadding(long offset, int index)
|
|
{
|
|
long padding = 0;
|
|
if (HasChildren() && index > 0)
|
|
{
|
|
SymbolInfo lastInfo = m_children[index - 1];
|
|
padding = offset - (lastInfo.m_offset + (long)lastInfo.m_size);
|
|
}
|
|
return padding > 0 ? padding : 0;
|
|
}
|
|
|
|
public void Set(string name, string typeName, ulong size, long offset)
|
|
{
|
|
m_name = name;
|
|
m_typeName = typeName;
|
|
m_size = size;
|
|
m_offset = offset;
|
|
}
|
|
public bool HasChildren() { return m_children != null; }
|
|
public long CalcTotalPadding()
|
|
{
|
|
long totalPadding = m_padding;
|
|
if (HasChildren())
|
|
{
|
|
foreach (SymbolInfo info in m_children)
|
|
totalPadding += info.m_padding;
|
|
}
|
|
return totalPadding;
|
|
}
|
|
void AddChild(SymbolInfo child)
|
|
{
|
|
if (m_children == null)
|
|
m_children = new List<SymbolInfo>();
|
|
m_children.Add(child);
|
|
}
|
|
public bool IsBase()
|
|
{
|
|
return m_name.IndexOf("Base: ") == 0;
|
|
}
|
|
|
|
private static int CompareOffsets(SymbolInfo x, SymbolInfo y)
|
|
{
|
|
// Base classes have to go first.
|
|
if (x.IsBase() && !y.IsBase())
|
|
return -1;
|
|
if (!x.IsBase() && y.IsBase())
|
|
return 1;
|
|
|
|
if (x.m_offset == y.m_offset)
|
|
{
|
|
return (x.m_size == y.m_size ? 0 : (x.m_size < y.m_size) ? -1 : 1);
|
|
}
|
|
else return (x.m_offset < y.m_offset) ? -1 : 1;
|
|
}
|
|
|
|
public string m_name;
|
|
public string m_typeName;
|
|
public ulong m_size;
|
|
public long m_offset;
|
|
public long m_padding;
|
|
public List<SymbolInfo> m_children;
|
|
};
|
|
|
|
SymbolInfo FindSelectedSymbolInfo()
|
|
{
|
|
if (dataGridSymbols.SelectedRows.Count == 0)
|
|
return null;
|
|
|
|
DataGridViewRow selectedRow = dataGridSymbols.SelectedRows[0];
|
|
string symbolName = selectedRow.Cells[0].Value.ToString();
|
|
|
|
SymbolInfo info = FindSymbolInfo(symbolName);
|
|
return info;
|
|
}
|
|
SymbolInfo FindSymbolInfo(string name)
|
|
{
|
|
SymbolInfo info;
|
|
m_symbols.TryGetValue(name, out info);
|
|
return info;
|
|
}
|
|
|
|
IDiaDataSource m_source;
|
|
IDiaSession m_session;
|
|
Dictionary<string, SymbolInfo> m_symbols = new Dictionary<string, SymbolInfo>();
|
|
DataTable m_table = null;
|
|
long m_prefetchStartOffset = 0;
|
|
|
|
private void dataGridSymbols_SelectionChanged(object sender, EventArgs e)
|
|
{
|
|
m_prefetchStartOffset = 0;
|
|
ShowSelectedSymbolInfo();
|
|
}
|
|
|
|
void ShowSelectedSymbolInfo()
|
|
{
|
|
dataGridViewSymbolInfo.Rows.Clear();
|
|
SymbolInfo info = FindSelectedSymbolInfo();
|
|
if (info != null)
|
|
ShowSymbolInfo(info);
|
|
}
|
|
|
|
void ShowSymbolInfo(SymbolInfo info)
|
|
{
|
|
dataGridViewSymbolInfo.Rows.Clear();
|
|
if (!info.HasChildren())
|
|
return;
|
|
|
|
long cacheLineSize = (long)GetCacheLineSize();
|
|
long prevCacheBoundaryOffset = m_prefetchStartOffset;
|
|
|
|
if (prevCacheBoundaryOffset > (long)info.m_size)
|
|
prevCacheBoundaryOffset = (long)info.m_size;
|
|
|
|
long numCacheLines = 0;
|
|
foreach (SymbolInfo child in info.m_children)
|
|
{
|
|
if (child.m_padding > 0)
|
|
{
|
|
long paddingOffset = child.m_offset - child.m_padding;
|
|
string[] paddingRow = { "Padding", paddingOffset.ToString(), child.m_padding.ToString() };
|
|
dataGridViewSymbolInfo.Rows.Add(paddingRow);
|
|
}
|
|
|
|
// long childEndOffset = child.m_offset + (long)child.m_size;
|
|
while (child.m_offset - prevCacheBoundaryOffset >= cacheLineSize)
|
|
{
|
|
numCacheLines = numCacheLines + 1;
|
|
long cacheLineOffset = numCacheLines * cacheLineSize + m_prefetchStartOffset;
|
|
string[] boundaryRow = { "Cacheline boundary", cacheLineOffset.ToString(), "" };
|
|
dataGridViewSymbolInfo.Rows.Add(boundaryRow);
|
|
prevCacheBoundaryOffset = cacheLineOffset;
|
|
}
|
|
|
|
string[] row = { child.m_name, child.m_offset.ToString(), child.m_size.ToString() };
|
|
dataGridViewSymbolInfo.Rows.Add(row);
|
|
dataGridViewSymbolInfo.Rows[dataGridViewSymbolInfo.Rows.Count - 1].Tag = child;
|
|
if (child.m_typeName != null && child.m_typeName.Length != 0)
|
|
{
|
|
dataGridViewSymbolInfo.Rows[dataGridViewSymbolInfo.Rows.Count - 1].Cells[0].ToolTipText = child.m_typeName;
|
|
}
|
|
}
|
|
// Final structure padding.
|
|
if (info.m_padding > 0)
|
|
{
|
|
long paddingOffset = (long)info.m_size - info.m_padding;
|
|
string[] paddingRow = { "Padding", paddingOffset.ToString(), info.m_padding.ToString() };
|
|
dataGridViewSymbolInfo.Rows.Add(paddingRow);
|
|
}
|
|
}
|
|
|
|
private void dataGridSymbols_SortCompare(object sender, DataGridViewSortCompareEventArgs e)
|
|
{
|
|
if (e.Column.Index > 0)
|
|
{
|
|
e.Handled = true;
|
|
int val1;
|
|
int val2;
|
|
Int32.TryParse(e.CellValue1.ToString(), out val1);
|
|
Int32.TryParse(e.CellValue2.ToString(), out val2);
|
|
e.SortResult = val1 - val2;
|
|
}
|
|
}
|
|
|
|
private void dataGridViewSymbolInfo_CellPainting(object sender, DataGridViewCellPaintingEventArgs e)
|
|
{
|
|
foreach (DataGridViewRow row in dataGridViewSymbolInfo.Rows)
|
|
{
|
|
DataGridViewCell cell = row.Cells[0];
|
|
if (cell.Value.ToString() == "Padding")
|
|
{
|
|
cell.Style.BackColor = Color.LightPink;
|
|
row.Cells[1].Style.BackColor = Color.LightPink;
|
|
row.Cells[2].Style.BackColor = Color.LightPink;
|
|
}
|
|
else if (cell.Value.ToString().IndexOf("Base: ") == 0)
|
|
{
|
|
cell.Style.BackColor = Color.LightGreen;
|
|
row.Cells[1].Style.BackColor = Color.LightGreen;
|
|
row.Cells[2].Style.BackColor = Color.LightGreen;
|
|
}
|
|
else if (cell.Value.ToString() == "Cacheline boundary")
|
|
{
|
|
cell.Style.BackColor = Color.LightGray;
|
|
row.Cells[1].Style.BackColor = Color.LightGray;
|
|
row.Cells[2].Style.BackColor = Color.LightGray;
|
|
}
|
|
}
|
|
}
|
|
|
|
private void textBoxFilter_TextChanged(object sender, EventArgs e)
|
|
{
|
|
try
|
|
{
|
|
if (textBoxFilter.Text.Length == 0)
|
|
bindingSourceSymbols.Filter = null;
|
|
else
|
|
bindingSourceSymbols.Filter = "Symbol LIKE '" + textBoxFilter.Text + "'";
|
|
|
|
textBoxFilter.BackColor = Color.Empty;
|
|
textBoxFilter.ForeColor = Color.Empty;
|
|
filterFeedbackLabel.Text = "";
|
|
}
|
|
catch (System.Data.EvaluateException)
|
|
{
|
|
textBoxFilter.BackColor = Color.Red;
|
|
textBoxFilter.ForeColor = Color.White;
|
|
filterFeedbackLabel.Text = "Invalid filter";
|
|
}
|
|
}
|
|
|
|
void dumpSymbolInfo(System.IO.TextWriter tw, SymbolInfo info)
|
|
{
|
|
tw.WriteLine("Symbol: " + info.m_name);
|
|
tw.WriteLine("Size: " + info.m_size.ToString());
|
|
tw.WriteLine("Total padding: " + info.CalcTotalPadding().ToString());
|
|
tw.WriteLine("Members");
|
|
tw.WriteLine("-------");
|
|
|
|
foreach (SymbolInfo child in info.m_children)
|
|
{
|
|
if (child.m_padding > 0)
|
|
{
|
|
long paddingOffset = child.m_offset - child.m_padding;
|
|
tw.WriteLine(String.Format("{0,-40} {1,5} {2,5}", "****Padding", paddingOffset, child.m_padding));
|
|
}
|
|
|
|
tw.WriteLine(String.Format("{0,-40} {1,5} {2,5}", child.m_name, child.m_offset, child.m_size));
|
|
}
|
|
// Final structure padding.
|
|
if (info.m_padding > 0)
|
|
{
|
|
long paddingOffset = (long)info.m_size - info.m_padding;
|
|
tw.WriteLine(String.Format("{0,-40} {1,5} {2,5}", "****Padding", paddingOffset, info.m_padding));
|
|
}
|
|
}
|
|
|
|
private void copyTypeLayoutToClipboardToolStripMenuItem_Click(object sender, EventArgs e)
|
|
{
|
|
SymbolInfo info = FindSelectedSymbolInfo();
|
|
if (info != null)
|
|
{
|
|
System.IO.StringWriter tw = new System.IO.StringWriter();
|
|
dumpSymbolInfo(tw, info);
|
|
Clipboard.SetText(tw.ToString());
|
|
}
|
|
}
|
|
|
|
private void textBoxCache_KeyPress(object sender, KeyPressEventArgs e)
|
|
{
|
|
if (e.KeyChar == (char)Keys.Enter || e.KeyChar == (char)Keys.Escape)
|
|
ShowSelectedSymbolInfo();
|
|
base.OnKeyPress(e);
|
|
}
|
|
|
|
private void textBoxCache_Leave(object sender, EventArgs e)
|
|
{
|
|
ShowSelectedSymbolInfo();
|
|
}
|
|
|
|
private void setPrefetchStartOffsetToolStripMenuItem_Click(object sender, EventArgs e)
|
|
{
|
|
if (dataGridViewSymbolInfo.SelectedRows.Count != 0)
|
|
{
|
|
DataGridViewRow selectedRow = dataGridViewSymbolInfo.SelectedRows[0];
|
|
long symbolOffset = Convert.ToUInt32(selectedRow.Cells[1].Value);
|
|
m_prefetchStartOffset = symbolOffset;
|
|
ShowSelectedSymbolInfo();
|
|
}
|
|
}
|
|
|
|
private void dataGridViewSymbolInfo_CellMouseDoubleClick(object sender, DataGridViewCellMouseEventArgs e)
|
|
{
|
|
if (dataGridViewSymbolInfo.SelectedRows.Count != 0)
|
|
{
|
|
DataGridViewRow selectedRow = dataGridViewSymbolInfo.SelectedRows[0];
|
|
SymbolInfo info = selectedRow.Tag as SymbolInfo;
|
|
if (info != null)
|
|
{
|
|
SelectSymbol(info.m_typeName);
|
|
}
|
|
}
|
|
}
|
|
|
|
void SelectSymbol(string name)
|
|
{
|
|
foreach (DataGridViewRow dr in dataGridSymbols.Rows)
|
|
{
|
|
DataGridViewCell dc = dr.Cells[0]; // name
|
|
if (dc.Value.ToString() == name)
|
|
{
|
|
dataGridSymbols.CurrentCell = dc;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|