├── .gitignore
├── CruncherSharp.Designer.cs
├── CruncherSharp.cs
├── CruncherSharp.csproj
├── CruncherSharp.csproj.user
├── CruncherSharp.resx
├── CruncherSharp.sln
├── CruncherSharpForm.Designer.cs
├── CruncherSharpForm.cs
├── CruncherSharpForm.resx
├── License.txt
├── Program.cs
├── Properties
├── AssemblyInfo.cs
├── Resources.Designer.cs
├── Resources.resx
├── Settings.Designer.cs
└── Settings.settings
├── README.md
├── Screenshot.png
├── SelectNamespace.Designer.cs
├── SelectNamespace.cs
├── SelectNamespaceForm.Designer.cs
├── SelectNamespaceForm.cs
├── SelectNamespaceForm.resx
├── SymbolAnalyzer.cs
├── SymbolFunctionInfo.cs
├── SymbolInfo.cs
├── SymbolMemberInfo.cs
├── app.config
└── crunchersharp.ico
/.gitignore:
--------------------------------------------------------------------------------
1 | bin/*
2 | obj/*
3 | .vs/*
4 | *.suo
5 |
--------------------------------------------------------------------------------
/CruncherSharp.Designer.cs:
--------------------------------------------------------------------------------
1 | namespace CruncherSharp
2 | {
3 | partial class CruncherSharp
4 | {
5 | ///
6 | /// Required designer variable.
7 | ///
8 | private System.ComponentModel.IContainer components = null;
9 |
10 | ///
11 | /// Clean up any resources being used.
12 | ///
13 | /// true if managed resources should be disposed; otherwise, false.
14 | protected override void Dispose(bool disposing)
15 | {
16 | if (disposing && (components != null))
17 | {
18 | components.Dispose();
19 | }
20 | base.Dispose(disposing);
21 | }
22 |
23 | #region Windows Form Designer generated code
24 |
25 | ///
26 | /// Required method for Designer support - do not modify
27 | /// the contents of this method with the code editor.
28 | ///
29 | private void InitializeComponent()
30 | {
31 | this.components = new System.ComponentModel.Container();
32 | System.Windows.Forms.DataGridViewCellStyle dataGridViewCellStyle1 = new System.Windows.Forms.DataGridViewCellStyle();
33 | this.mainMenu = new System.Windows.Forms.MenuStrip();
34 | this.fileToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
35 | this.loadPDBToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
36 | this.exitToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
37 | this.openPdbDialog = new System.Windows.Forms.OpenFileDialog();
38 | this.bindingSourceSymbols = new System.Windows.Forms.BindingSource(this.components);
39 | this.splitContainer2 = new System.Windows.Forms.SplitContainer();
40 | this.textBoxCache = new System.Windows.Forms.MaskedTextBox();
41 | this.label2 = new System.Windows.Forms.Label();
42 | this.label1 = new System.Windows.Forms.Label();
43 | this.textBoxFilter = new System.Windows.Forms.TextBox();
44 | this.splitContainer1 = new System.Windows.Forms.SplitContainer();
45 | this.dataGridSymbols = new System.Windows.Forms.DataGridView();
46 | this.dataGridViewSymbolInfo = new System.Windows.Forms.DataGridView();
47 | this.colField = new System.Windows.Forms.DataGridViewTextBoxColumn();
48 | this.colFieldOffset = new System.Windows.Forms.DataGridViewTextBoxColumn();
49 | this.colFieldSize = new System.Windows.Forms.DataGridViewTextBoxColumn();
50 | this.contextMenuStrip1 = new System.Windows.Forms.ContextMenuStrip(this.components);
51 | this.copyTypeLayoutToClipboardToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
52 | this.setPrefetchStartOffsetToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
53 | this.filterFeedbackLabel = new System.Windows.Forms.Label();
54 | this.mainMenu.SuspendLayout();
55 | ((System.ComponentModel.ISupportInitialize)(this.bindingSourceSymbols)).BeginInit();
56 | this.splitContainer2.Panel1.SuspendLayout();
57 | this.splitContainer2.Panel2.SuspendLayout();
58 | this.splitContainer2.SuspendLayout();
59 | this.splitContainer1.Panel1.SuspendLayout();
60 | this.splitContainer1.Panel2.SuspendLayout();
61 | this.splitContainer1.SuspendLayout();
62 | ((System.ComponentModel.ISupportInitialize)(this.dataGridSymbols)).BeginInit();
63 | ((System.ComponentModel.ISupportInitialize)(this.dataGridViewSymbolInfo)).BeginInit();
64 | this.contextMenuStrip1.SuspendLayout();
65 | this.SuspendLayout();
66 | //
67 | // mainMenu
68 | //
69 | this.mainMenu.Items.AddRange(new System.Windows.Forms.ToolStripItem[] {
70 | this.fileToolStripMenuItem});
71 | this.mainMenu.Location = new System.Drawing.Point(0, 0);
72 | this.mainMenu.Name = "mainMenu";
73 | this.mainMenu.Size = new System.Drawing.Size(1055, 24);
74 | this.mainMenu.TabIndex = 0;
75 | this.mainMenu.Text = "menuStrip1";
76 | //
77 | // fileToolStripMenuItem
78 | //
79 | this.fileToolStripMenuItem.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] {
80 | this.loadPDBToolStripMenuItem,
81 | this.exitToolStripMenuItem});
82 | this.fileToolStripMenuItem.Name = "fileToolStripMenuItem";
83 | this.fileToolStripMenuItem.Size = new System.Drawing.Size(37, 20);
84 | this.fileToolStripMenuItem.Text = "File";
85 | //
86 | // loadPDBToolStripMenuItem
87 | //
88 | this.loadPDBToolStripMenuItem.Name = "loadPDBToolStripMenuItem";
89 | this.loadPDBToolStripMenuItem.ShortcutKeys = ((System.Windows.Forms.Keys)((System.Windows.Forms.Keys.Control | System.Windows.Forms.Keys.O)));
90 | this.loadPDBToolStripMenuItem.Size = new System.Drawing.Size(177, 22);
91 | this.loadPDBToolStripMenuItem.Text = "Load PDB...";
92 | this.loadPDBToolStripMenuItem.Click += new System.EventHandler(this.loadPDBToolStripMenuItem_Click);
93 | //
94 | // exitToolStripMenuItem
95 | //
96 | this.exitToolStripMenuItem.Name = "exitToolStripMenuItem";
97 | this.exitToolStripMenuItem.Size = new System.Drawing.Size(177, 22);
98 | this.exitToolStripMenuItem.Text = "Exit";
99 | this.exitToolStripMenuItem.Click += new System.EventHandler(this.exitToolStripMenuItem_Click);
100 | //
101 | // openPdbDialog
102 | //
103 | this.openPdbDialog.Filter = "Symbol files|*.pdb|All files|*.*";
104 | //
105 | // splitContainer2
106 | //
107 | this.splitContainer2.Dock = System.Windows.Forms.DockStyle.Fill;
108 | this.splitContainer2.IsSplitterFixed = true;
109 | this.splitContainer2.Location = new System.Drawing.Point(0, 24);
110 | this.splitContainer2.Name = "splitContainer2";
111 | this.splitContainer2.Orientation = System.Windows.Forms.Orientation.Horizontal;
112 | //
113 | // splitContainer2.Panel1
114 | //
115 | this.splitContainer2.Panel1.Controls.Add(this.filterFeedbackLabel);
116 | this.splitContainer2.Panel1.Controls.Add(this.textBoxCache);
117 | this.splitContainer2.Panel1.Controls.Add(this.label2);
118 | this.splitContainer2.Panel1.Controls.Add(this.label1);
119 | this.splitContainer2.Panel1.Controls.Add(this.textBoxFilter);
120 | //
121 | // splitContainer2.Panel2
122 | //
123 | this.splitContainer2.Panel2.Controls.Add(this.splitContainer1);
124 | this.splitContainer2.Size = new System.Drawing.Size(1055, 340);
125 | this.splitContainer2.SplitterDistance = 25;
126 | this.splitContainer2.TabIndex = 1;
127 | //
128 | // textBoxCache
129 | //
130 | this.textBoxCache.Location = new System.Drawing.Point(363, 2);
131 | this.textBoxCache.Mask = "0000";
132 | this.textBoxCache.Name = "textBoxCache";
133 | this.textBoxCache.Size = new System.Drawing.Size(34, 20);
134 | this.textBoxCache.TabIndex = 4;
135 | this.textBoxCache.Text = "64";
136 | this.textBoxCache.KeyPress += new System.Windows.Forms.KeyPressEventHandler(this.textBoxCache_KeyPress);
137 | this.textBoxCache.Leave += new System.EventHandler(this.textBoxCache_Leave);
138 | //
139 | // label2
140 | //
141 | this.label2.AutoSize = true;
142 | this.label2.Location = new System.Drawing.Point(300, 5);
143 | this.label2.Name = "label2";
144 | this.label2.Size = new System.Drawing.Size(57, 13);
145 | this.label2.TabIndex = 3;
146 | this.label2.Text = "Cache line";
147 | //
148 | // label1
149 | //
150 | this.label1.AutoSize = true;
151 | this.label1.Location = new System.Drawing.Point(12, 5);
152 | this.label1.Name = "label1";
153 | this.label1.Size = new System.Drawing.Size(29, 13);
154 | this.label1.TabIndex = 1;
155 | this.label1.Text = "Filter";
156 | //
157 | // textBoxFilter
158 | //
159 | this.textBoxFilter.Location = new System.Drawing.Point(53, 2);
160 | this.textBoxFilter.Name = "textBoxFilter";
161 | this.textBoxFilter.Size = new System.Drawing.Size(179, 20);
162 | this.textBoxFilter.TabIndex = 0;
163 | this.textBoxFilter.TextChanged += new System.EventHandler(this.textBoxFilter_TextChanged);
164 | //
165 | // splitContainer1
166 | //
167 | this.splitContainer1.Dock = System.Windows.Forms.DockStyle.Fill;
168 | this.splitContainer1.Location = new System.Drawing.Point(0, 0);
169 | this.splitContainer1.Name = "splitContainer1";
170 | //
171 | // splitContainer1.Panel1
172 | //
173 | this.splitContainer1.Panel1.Controls.Add(this.dataGridSymbols);
174 | //
175 | // splitContainer1.Panel2
176 | //
177 | this.splitContainer1.Panel2.Controls.Add(this.dataGridViewSymbolInfo);
178 | this.splitContainer1.Size = new System.Drawing.Size(1055, 311);
179 | this.splitContainer1.SplitterDistance = 615;
180 | this.splitContainer1.TabIndex = 2;
181 | //
182 | // dataGridSymbols
183 | //
184 | this.dataGridSymbols.AllowUserToAddRows = false;
185 | this.dataGridSymbols.AllowUserToDeleteRows = false;
186 | this.dataGridSymbols.AllowUserToResizeRows = false;
187 | dataGridViewCellStyle1.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(224)))), ((int)(((byte)(224)))), ((int)(((byte)(224)))));
188 | this.dataGridSymbols.AlternatingRowsDefaultCellStyle = dataGridViewCellStyle1;
189 | this.dataGridSymbols.ColumnHeadersHeightSizeMode = System.Windows.Forms.DataGridViewColumnHeadersHeightSizeMode.AutoSize;
190 | this.dataGridSymbols.Dock = System.Windows.Forms.DockStyle.Fill;
191 | this.dataGridSymbols.Location = new System.Drawing.Point(0, 0);
192 | this.dataGridSymbols.MultiSelect = false;
193 | this.dataGridSymbols.Name = "dataGridSymbols";
194 | this.dataGridSymbols.ReadOnly = true;
195 | this.dataGridSymbols.RowHeadersVisible = false;
196 | this.dataGridSymbols.SelectionMode = System.Windows.Forms.DataGridViewSelectionMode.FullRowSelect;
197 | this.dataGridSymbols.Size = new System.Drawing.Size(615, 311);
198 | this.dataGridSymbols.TabIndex = 2;
199 | this.dataGridSymbols.SelectionChanged += new System.EventHandler(this.dataGridSymbols_SelectionChanged);
200 | this.dataGridSymbols.SortCompare += new System.Windows.Forms.DataGridViewSortCompareEventHandler(this.dataGridSymbols_SortCompare);
201 | //
202 | // dataGridViewSymbolInfo
203 | //
204 | this.dataGridViewSymbolInfo.AllowUserToAddRows = false;
205 | this.dataGridViewSymbolInfo.AllowUserToDeleteRows = false;
206 | this.dataGridViewSymbolInfo.AllowUserToResizeRows = false;
207 | this.dataGridViewSymbolInfo.ColumnHeadersHeightSizeMode = System.Windows.Forms.DataGridViewColumnHeadersHeightSizeMode.AutoSize;
208 | this.dataGridViewSymbolInfo.Columns.AddRange(new System.Windows.Forms.DataGridViewColumn[] {
209 | this.colField,
210 | this.colFieldOffset,
211 | this.colFieldSize});
212 | this.dataGridViewSymbolInfo.ContextMenuStrip = this.contextMenuStrip1;
213 | this.dataGridViewSymbolInfo.Dock = System.Windows.Forms.DockStyle.Fill;
214 | this.dataGridViewSymbolInfo.Location = new System.Drawing.Point(0, 0);
215 | this.dataGridViewSymbolInfo.MultiSelect = false;
216 | this.dataGridViewSymbolInfo.Name = "dataGridViewSymbolInfo";
217 | this.dataGridViewSymbolInfo.ReadOnly = true;
218 | this.dataGridViewSymbolInfo.RowHeadersVisible = false;
219 | this.dataGridViewSymbolInfo.SelectionMode = System.Windows.Forms.DataGridViewSelectionMode.FullRowSelect;
220 | this.dataGridViewSymbolInfo.Size = new System.Drawing.Size(436, 311);
221 | this.dataGridViewSymbolInfo.TabIndex = 0;
222 | this.dataGridViewSymbolInfo.CellMouseDoubleClick += new System.Windows.Forms.DataGridViewCellMouseEventHandler(this.dataGridViewSymbolInfo_CellMouseDoubleClick);
223 | this.dataGridViewSymbolInfo.CellPainting += new System.Windows.Forms.DataGridViewCellPaintingEventHandler(this.dataGridViewSymbolInfo_CellPainting);
224 | this.dataGridViewSymbolInfo.SortCompare += new System.Windows.Forms.DataGridViewSortCompareEventHandler(this.dataGridSymbols_SortCompare);
225 | //
226 | // colField
227 | //
228 | this.colField.HeaderText = "Field";
229 | this.colField.Name = "colField";
230 | this.colField.ReadOnly = true;
231 | this.colField.Width = 210;
232 | //
233 | // colFieldOffset
234 | //
235 | this.colFieldOffset.HeaderText = "Offset";
236 | this.colFieldOffset.Name = "colFieldOffset";
237 | this.colFieldOffset.ReadOnly = true;
238 | //
239 | // colFieldSize
240 | //
241 | this.colFieldSize.HeaderText = "Size";
242 | this.colFieldSize.Name = "colFieldSize";
243 | this.colFieldSize.ReadOnly = true;
244 | //
245 | // contextMenuStrip1
246 | //
247 | this.contextMenuStrip1.Items.AddRange(new System.Windows.Forms.ToolStripItem[] {
248 | this.copyTypeLayoutToClipboardToolStripMenuItem,
249 | this.setPrefetchStartOffsetToolStripMenuItem});
250 | this.contextMenuStrip1.Name = "contextMenuStrip1";
251 | this.contextMenuStrip1.Size = new System.Drawing.Size(243, 48);
252 | //
253 | // copyTypeLayoutToClipboardToolStripMenuItem
254 | //
255 | this.copyTypeLayoutToClipboardToolStripMenuItem.Name = "copyTypeLayoutToClipboardToolStripMenuItem";
256 | this.copyTypeLayoutToClipboardToolStripMenuItem.Size = new System.Drawing.Size(242, 22);
257 | this.copyTypeLayoutToClipboardToolStripMenuItem.Text = "Copy Type Layout To Clipboard";
258 | this.copyTypeLayoutToClipboardToolStripMenuItem.Click += new System.EventHandler(this.copyTypeLayoutToClipboardToolStripMenuItem_Click);
259 | //
260 | // setPrefetchStartOffsetToolStripMenuItem
261 | //
262 | this.setPrefetchStartOffsetToolStripMenuItem.Name = "setPrefetchStartOffsetToolStripMenuItem";
263 | this.setPrefetchStartOffsetToolStripMenuItem.Size = new System.Drawing.Size(242, 22);
264 | this.setPrefetchStartOffsetToolStripMenuItem.Text = "Set Prefetch Start Offset";
265 | this.setPrefetchStartOffsetToolStripMenuItem.Click += new System.EventHandler(this.setPrefetchStartOffsetToolStripMenuItem_Click);
266 | //
267 | // filterFeedbackLabel
268 | //
269 | this.filterFeedbackLabel.Location = new System.Drawing.Point(239, 5);
270 | this.filterFeedbackLabel.Name = "filterFeedbackLabel";
271 | this.filterFeedbackLabel.Size = new System.Drawing.Size(120, 13);
272 | this.filterFeedbackLabel.TabIndex = 5;
273 | //
274 | // CruncherSharp
275 | //
276 | this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
277 | this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
278 | this.ClientSize = new System.Drawing.Size(1055, 364);
279 | this.Controls.Add(this.splitContainer2);
280 | this.Controls.Add(this.mainMenu);
281 | this.MainMenuStrip = this.mainMenu;
282 | this.Name = "CruncherSharp";
283 | this.Text = "Cruncher #";
284 | this.mainMenu.ResumeLayout(false);
285 | this.mainMenu.PerformLayout();
286 | ((System.ComponentModel.ISupportInitialize)(this.bindingSourceSymbols)).EndInit();
287 | this.splitContainer2.Panel1.ResumeLayout(false);
288 | this.splitContainer2.Panel1.PerformLayout();
289 | this.splitContainer2.Panel2.ResumeLayout(false);
290 | this.splitContainer2.ResumeLayout(false);
291 | this.splitContainer1.Panel1.ResumeLayout(false);
292 | this.splitContainer1.Panel2.ResumeLayout(false);
293 | this.splitContainer1.ResumeLayout(false);
294 | ((System.ComponentModel.ISupportInitialize)(this.dataGridSymbols)).EndInit();
295 | ((System.ComponentModel.ISupportInitialize)(this.dataGridViewSymbolInfo)).EndInit();
296 | this.contextMenuStrip1.ResumeLayout(false);
297 | this.ResumeLayout(false);
298 | this.PerformLayout();
299 |
300 | }
301 |
302 | #endregion
303 |
304 | private System.Windows.Forms.MenuStrip mainMenu;
305 | private System.Windows.Forms.ToolStripMenuItem fileToolStripMenuItem;
306 | private System.Windows.Forms.ToolStripMenuItem loadPDBToolStripMenuItem;
307 | private System.Windows.Forms.ToolStripMenuItem exitToolStripMenuItem;
308 | private System.Windows.Forms.OpenFileDialog openPdbDialog;
309 | private System.Windows.Forms.BindingSource bindingSourceSymbols;
310 | private System.Windows.Forms.SplitContainer splitContainer2;
311 | private System.Windows.Forms.SplitContainer splitContainer1;
312 | private System.Windows.Forms.DataGridView dataGridSymbols;
313 | private System.Windows.Forms.DataGridView dataGridViewSymbolInfo;
314 | private System.Windows.Forms.DataGridViewTextBoxColumn colField;
315 | private System.Windows.Forms.DataGridViewTextBoxColumn colFieldOffset;
316 | private System.Windows.Forms.DataGridViewTextBoxColumn colFieldSize;
317 | private System.Windows.Forms.Label label1;
318 | private System.Windows.Forms.TextBox textBoxFilter;
319 | private System.Windows.Forms.ContextMenuStrip contextMenuStrip1;
320 | private System.Windows.Forms.ToolStripMenuItem copyTypeLayoutToClipboardToolStripMenuItem;
321 | private System.Windows.Forms.Label label2;
322 | private System.Windows.Forms.MaskedTextBox textBoxCache;
323 | private System.Windows.Forms.ToolStripMenuItem setPrefetchStartOffsetToolStripMenuItem;
324 | private System.Windows.Forms.Label filterFeedbackLabel;
325 | }
326 | }
327 |
328 |
--------------------------------------------------------------------------------
/CruncherSharp.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.ComponentModel;
4 | using System.Data;
5 | using System.Drawing;
6 | using System.Text;
7 | using System.Windows.Forms;
8 | using Dia2Lib;
9 |
10 | namespace CruncherSharp
11 | {
12 | public partial class CruncherSharp : Form
13 | {
14 | public CruncherSharp()
15 | {
16 | InitializeComponent();
17 | m_table = CreateDataTable();
18 | bindingSourceSymbols.DataSource = m_table;
19 | dataGridSymbols.DataSource = bindingSourceSymbols;
20 |
21 | dataGridSymbols.Columns[0].Width = 271;
22 | }
23 |
24 | private void exitToolStripMenuItem_Click(object sender, EventArgs e)
25 | {
26 | this.Close();
27 | }
28 |
29 | private void loadPDBToolStripMenuItem_Click(object sender, EventArgs e)
30 | {
31 | if (openPdbDialog.ShowDialog() == DialogResult.OK)
32 | {
33 | m_symbols.Clear();
34 |
35 | m_source = new DiaSourceClass();
36 | try
37 | {
38 | Cursor.Current = Cursors.WaitCursor;
39 | m_source.loadDataFromPdb(openPdbDialog.FileName);
40 | m_source.openSession(out m_session);
41 | Cursor.Current = Cursors.Default;
42 | }
43 | catch (System.Runtime.InteropServices.COMException exc)
44 | {
45 | MessageBox.Show(this, exc.ToString());
46 | return;
47 | }
48 |
49 | Cursor.Current = Cursors.WaitCursor;
50 | IDiaEnumSymbols allSymbols;
51 | m_session.findChildren(m_session.globalScope, SymTagEnum.SymTagUDT, null, 0, out allSymbols);
52 |
53 | // Temporarily clear the filter so, if current filter is invalid, we don't generate a ton of exceptions while populating the table
54 | var preExistingFilter = textBoxFilter.Text;
55 | textBoxFilter.Text = "";
56 |
57 | {
58 | PopulateDataTable(m_table, allSymbols);
59 | Cursor.Current = Cursors.Default;
60 |
61 | // Sort by name by default (ascending)
62 | dataGridSymbols.Sort(dataGridSymbols.Columns[0], ListSortDirection.Ascending);
63 | bindingSourceSymbols.Filter = null;// "Symbol LIKE '*rde*'";
64 | }
65 |
66 | // Restore the filter now that the table is populated
67 | textBoxFilter.Text = preExistingFilter;
68 |
69 | ShowSelectedSymbolInfo();
70 | }
71 | }
72 |
73 | DataTable CreateDataTable()
74 | {
75 | DataTable table = new DataTable("Symbols");
76 |
77 | DataColumn column = new DataColumn();
78 | column.ColumnName = "Symbol";
79 | column.ReadOnly = true;
80 | table.Columns.Add(column);
81 | column = new DataColumn();
82 | column.ColumnName = "Size";
83 | column.ReadOnly = true;
84 | column.DataType = System.Type.GetType("System.Int32");
85 | table.Columns.Add(column);
86 | column = new DataColumn();
87 | column.ColumnName = "Padding";
88 | column.ReadOnly = true;
89 | column.DataType = System.Type.GetType("System.Int32");
90 | table.Columns.Add(column);
91 | column = new DataColumn();
92 | column.ColumnName = "Padding/Size";
93 | column.ReadOnly = true;
94 | column.DataType = System.Type.GetType("System.Double");
95 | table.Columns.Add(column);
96 |
97 | return table;
98 | }
99 |
100 | void PopulateDataTable(DataTable table, IDiaEnumSymbols symbols)
101 | {
102 | ulong cacheLineSize = GetCacheLineSize();
103 | table.Rows.Clear();
104 |
105 | table.BeginLoadData();
106 | foreach (IDiaSymbol sym in symbols)
107 | {
108 | if (sym.length > 0 && !HasSymbol(sym.name))
109 | {
110 | SymbolInfo info = new SymbolInfo();
111 | info.Set(sym.name, "", sym.length, 0);
112 | info.ProcessChildren(sym);
113 |
114 | long totalPadding = info.CalcTotalPadding();
115 |
116 | DataRow row = table.NewRow();
117 | string symbolName = sym.name;
118 | row["Symbol"] = symbolName;
119 | row["Size"] = info.m_size;
120 | row["Padding"] = totalPadding;
121 | row["Padding/Size"] = (double)totalPadding / info.m_size;
122 | table.Rows.Add(row);
123 |
124 | m_symbols.Add(info.m_name, info);
125 | }
126 | }
127 | table.EndLoadData();
128 |
129 | }
130 | bool HasSymbol(string name)
131 | {
132 | return m_symbols.ContainsKey(name);
133 | }
134 | ulong GetCacheLineSize()
135 | {
136 | return Convert.ToUInt32(textBoxCache.Text);
137 | }
138 |
139 | class SymbolInfo
140 | {
141 | public void ProcessChildren(IDiaSymbol symbol)
142 | {
143 | IDiaEnumSymbols children;
144 | symbol.findChildren(SymTagEnum.SymTagNull, null, 0, out children);
145 | foreach (IDiaSymbol child in children)
146 | {
147 | SymbolInfo childInfo;
148 | if (ProcessChild(child, out childInfo))
149 | AddChild(childInfo);
150 | }
151 | // Sort children by offset, recalc padding.
152 | // Sorting is not needed normally (for data fields), but sometimes base class order is wrong.
153 | if (HasChildren())
154 | {
155 | m_children.Sort(CompareOffsets);
156 | for (int i = 0; i < m_children.Count; ++i)
157 | {
158 | SymbolInfo child = m_children[i];
159 | child.m_padding = CalcPadding(child.m_offset, i);
160 | }
161 | m_padding = CalcPadding((long)m_size, m_children.Count);
162 | }
163 | }
164 | bool ProcessChild(IDiaSymbol symbol, out SymbolInfo info)
165 | {
166 | info = new SymbolInfo();
167 | if (symbol.isStatic != 0 || (symbol.symTag != (uint)SymTagEnum.SymTagData && symbol.symTag != (uint)SymTagEnum.SymTagBaseClass))
168 | {
169 | return false;
170 | }
171 | // LocIsThisRel || LocIsNull || LocIsBitField
172 | if (symbol.locationType != 4 && symbol.locationType != 0 && symbol.locationType != 6)
173 | return false;
174 |
175 | ulong len = symbol.length;
176 | IDiaSymbol typeSymbol = symbol.type;
177 | if (typeSymbol != null)
178 | len = typeSymbol.length;
179 |
180 | string symbolName = symbol.name;
181 | if (symbol.symTag == (uint)SymTagEnum.SymTagBaseClass)
182 | symbolName = "Base: " + symbolName;
183 |
184 | info.Set(symbolName, (typeSymbol != null ? typeSymbol.name : ""), len, symbol.offset);
185 |
186 | return true;
187 | }
188 | long CalcPadding(long offset, int index)
189 | {
190 | long padding = 0;
191 | if (HasChildren() && index > 0)
192 | {
193 | SymbolInfo lastInfo = m_children[index - 1];
194 | padding = offset - (lastInfo.m_offset + (long)lastInfo.m_size);
195 | }
196 | return padding > 0 ? padding : 0;
197 | }
198 |
199 | public void Set(string name, string typeName, ulong size, long offset)
200 | {
201 | m_name = name;
202 | m_typeName = typeName;
203 | m_size = size;
204 | m_offset = offset;
205 | }
206 | public bool HasChildren() { return m_children != null; }
207 | public long CalcTotalPadding()
208 | {
209 | long totalPadding = m_padding;
210 | if (HasChildren())
211 | {
212 | foreach (SymbolInfo info in m_children)
213 | totalPadding += info.m_padding;
214 | }
215 | return totalPadding;
216 | }
217 | void AddChild(SymbolInfo child)
218 | {
219 | if (m_children == null)
220 | m_children = new List();
221 | m_children.Add(child);
222 | }
223 | public bool IsBase()
224 | {
225 | return m_name.IndexOf("Base: ") == 0;
226 | }
227 |
228 | private static int CompareOffsets(SymbolInfo x, SymbolInfo y)
229 | {
230 | // Base classes have to go first.
231 | if (x.IsBase() && !y.IsBase())
232 | return -1;
233 | if (!x.IsBase() && y.IsBase())
234 | return 1;
235 |
236 | if (x.m_offset == y.m_offset)
237 | {
238 | return (x.m_size == y.m_size ? 0 : (x.m_size < y.m_size) ? -1 : 1);
239 | }
240 | else return (x.m_offset < y.m_offset) ? -1 : 1;
241 | }
242 |
243 | public string m_name;
244 | public string m_typeName;
245 | public ulong m_size;
246 | public long m_offset;
247 | public long m_padding;
248 | public List m_children;
249 | };
250 |
251 | SymbolInfo FindSelectedSymbolInfo()
252 | {
253 | if (dataGridSymbols.SelectedRows.Count == 0)
254 | return null;
255 |
256 | DataGridViewRow selectedRow = dataGridSymbols.SelectedRows[0];
257 | string symbolName = selectedRow.Cells[0].Value.ToString();
258 |
259 | SymbolInfo info = FindSymbolInfo(symbolName);
260 | return info;
261 | }
262 | SymbolInfo FindSymbolInfo(string name)
263 | {
264 | SymbolInfo info;
265 | m_symbols.TryGetValue(name, out info);
266 | return info;
267 | }
268 |
269 | IDiaDataSource m_source;
270 | IDiaSession m_session;
271 | Dictionary m_symbols = new Dictionary();
272 | DataTable m_table = null;
273 | long m_prefetchStartOffset = 0;
274 |
275 | private void dataGridSymbols_SelectionChanged(object sender, EventArgs e)
276 | {
277 | m_prefetchStartOffset = 0;
278 | ShowSelectedSymbolInfo();
279 | }
280 |
281 | void ShowSelectedSymbolInfo()
282 | {
283 | dataGridViewSymbolInfo.Rows.Clear();
284 | SymbolInfo info = FindSelectedSymbolInfo();
285 | if (info != null)
286 | ShowSymbolInfo(info);
287 | }
288 |
289 | void ShowSymbolInfo(SymbolInfo info)
290 | {
291 | dataGridViewSymbolInfo.Rows.Clear();
292 | if (!info.HasChildren())
293 | return;
294 |
295 | long cacheLineSize = (long)GetCacheLineSize();
296 | long prevCacheBoundaryOffset = m_prefetchStartOffset;
297 |
298 | if (prevCacheBoundaryOffset > (long)info.m_size)
299 | prevCacheBoundaryOffset = (long)info.m_size;
300 |
301 | long numCacheLines = 0;
302 | foreach (SymbolInfo child in info.m_children)
303 | {
304 | if (child.m_padding > 0)
305 | {
306 | long paddingOffset = child.m_offset - child.m_padding;
307 | string[] paddingRow = { "Padding", paddingOffset.ToString(), child.m_padding.ToString() };
308 | dataGridViewSymbolInfo.Rows.Add(paddingRow);
309 | }
310 |
311 | // long childEndOffset = child.m_offset + (long)child.m_size;
312 | while (child.m_offset - prevCacheBoundaryOffset >= cacheLineSize)
313 | {
314 | numCacheLines = numCacheLines + 1;
315 | long cacheLineOffset = numCacheLines * cacheLineSize + m_prefetchStartOffset;
316 | string[] boundaryRow = { "Cacheline boundary", cacheLineOffset.ToString(), "" };
317 | dataGridViewSymbolInfo.Rows.Add(boundaryRow);
318 | prevCacheBoundaryOffset = cacheLineOffset;
319 | }
320 |
321 | string[] row = { child.m_name, child.m_offset.ToString(), child.m_size.ToString() };
322 | dataGridViewSymbolInfo.Rows.Add(row);
323 | dataGridViewSymbolInfo.Rows[dataGridViewSymbolInfo.Rows.Count - 1].Tag = child;
324 | if (child.m_typeName != null && child.m_typeName.Length != 0)
325 | {
326 | dataGridViewSymbolInfo.Rows[dataGridViewSymbolInfo.Rows.Count - 1].Cells[0].ToolTipText = child.m_typeName;
327 | }
328 | }
329 | // Final structure padding.
330 | if (info.m_padding > 0)
331 | {
332 | long paddingOffset = (long)info.m_size - info.m_padding;
333 | string[] paddingRow = { "Padding", paddingOffset.ToString(), info.m_padding.ToString() };
334 | dataGridViewSymbolInfo.Rows.Add(paddingRow);
335 | }
336 | }
337 |
338 | private void dataGridSymbols_SortCompare(object sender, DataGridViewSortCompareEventArgs e)
339 | {
340 | if (e.Column.Index > 0)
341 | {
342 | e.Handled = true;
343 | int val1;
344 | int val2;
345 | Int32.TryParse(e.CellValue1.ToString(), out val1);
346 | Int32.TryParse(e.CellValue2.ToString(), out val2);
347 | e.SortResult = val1 - val2;
348 | }
349 | }
350 |
351 | private void dataGridViewSymbolInfo_CellPainting(object sender, DataGridViewCellPaintingEventArgs e)
352 | {
353 | foreach (DataGridViewRow row in dataGridViewSymbolInfo.Rows)
354 | {
355 | DataGridViewCell cell = row.Cells[0];
356 | if (cell.Value.ToString() == "Padding")
357 | {
358 | cell.Style.BackColor = Color.LightPink;
359 | row.Cells[1].Style.BackColor = Color.LightPink;
360 | row.Cells[2].Style.BackColor = Color.LightPink;
361 | }
362 | else if (cell.Value.ToString().IndexOf("Base: ") == 0)
363 | {
364 | cell.Style.BackColor = Color.LightGreen;
365 | row.Cells[1].Style.BackColor = Color.LightGreen;
366 | row.Cells[2].Style.BackColor = Color.LightGreen;
367 | }
368 | else if (cell.Value.ToString() == "Cacheline boundary")
369 | {
370 | cell.Style.BackColor = Color.LightGray;
371 | row.Cells[1].Style.BackColor = Color.LightGray;
372 | row.Cells[2].Style.BackColor = Color.LightGray;
373 | }
374 | }
375 | }
376 |
377 | private void textBoxFilter_TextChanged(object sender, EventArgs e)
378 | {
379 | try
380 | {
381 | if (textBoxFilter.Text.Length == 0)
382 | bindingSourceSymbols.Filter = null;
383 | else
384 | bindingSourceSymbols.Filter = "Symbol LIKE '" + textBoxFilter.Text + "'";
385 |
386 | textBoxFilter.BackColor = Color.Empty;
387 | textBoxFilter.ForeColor = Color.Empty;
388 | filterFeedbackLabel.Text = "";
389 | }
390 | catch (System.Data.EvaluateException)
391 | {
392 | textBoxFilter.BackColor = Color.Red;
393 | textBoxFilter.ForeColor = Color.White;
394 | filterFeedbackLabel.Text = "Invalid filter";
395 | }
396 | }
397 |
398 | void dumpSymbolInfo(System.IO.TextWriter tw, SymbolInfo info)
399 | {
400 | tw.WriteLine("Symbol: " + info.m_name);
401 | tw.WriteLine("Size: " + info.m_size.ToString());
402 | tw.WriteLine("Total padding: " + info.CalcTotalPadding().ToString());
403 | tw.WriteLine("Members");
404 | tw.WriteLine("-------");
405 |
406 | foreach (SymbolInfo child in info.m_children)
407 | {
408 | if (child.m_padding > 0)
409 | {
410 | long paddingOffset = child.m_offset - child.m_padding;
411 | tw.WriteLine(String.Format("{0,-40} {1,5} {2,5}", "****Padding", paddingOffset, child.m_padding));
412 | }
413 |
414 | tw.WriteLine(String.Format("{0,-40} {1,5} {2,5}", child.m_name, child.m_offset, child.m_size));
415 | }
416 | // Final structure padding.
417 | if (info.m_padding > 0)
418 | {
419 | long paddingOffset = (long)info.m_size - info.m_padding;
420 | tw.WriteLine(String.Format("{0,-40} {1,5} {2,5}", "****Padding", paddingOffset, info.m_padding));
421 | }
422 | }
423 |
424 | private void copyTypeLayoutToClipboardToolStripMenuItem_Click(object sender, EventArgs e)
425 | {
426 | SymbolInfo info = FindSelectedSymbolInfo();
427 | if (info != null)
428 | {
429 | System.IO.StringWriter tw = new System.IO.StringWriter();
430 | dumpSymbolInfo(tw, info);
431 | Clipboard.SetText(tw.ToString());
432 | }
433 | }
434 |
435 | private void textBoxCache_KeyPress(object sender, KeyPressEventArgs e)
436 | {
437 | if (e.KeyChar == (char)Keys.Enter || e.KeyChar == (char)Keys.Escape)
438 | ShowSelectedSymbolInfo();
439 | base.OnKeyPress(e);
440 | }
441 |
442 | private void textBoxCache_Leave(object sender, EventArgs e)
443 | {
444 | ShowSelectedSymbolInfo();
445 | }
446 |
447 | private void setPrefetchStartOffsetToolStripMenuItem_Click(object sender, EventArgs e)
448 | {
449 | if (dataGridViewSymbolInfo.SelectedRows.Count != 0)
450 | {
451 | DataGridViewRow selectedRow = dataGridViewSymbolInfo.SelectedRows[0];
452 | long symbolOffset = Convert.ToUInt32(selectedRow.Cells[1].Value);
453 | m_prefetchStartOffset = symbolOffset;
454 | ShowSelectedSymbolInfo();
455 | }
456 | }
457 |
458 | private void dataGridViewSymbolInfo_CellMouseDoubleClick(object sender, DataGridViewCellMouseEventArgs e)
459 | {
460 | if (dataGridViewSymbolInfo.SelectedRows.Count != 0)
461 | {
462 | DataGridViewRow selectedRow = dataGridViewSymbolInfo.SelectedRows[0];
463 | SymbolInfo info = selectedRow.Tag as SymbolInfo;
464 | if (info != null)
465 | {
466 | SelectSymbol(info.m_typeName);
467 | }
468 | }
469 | }
470 |
471 | void SelectSymbol(string name)
472 | {
473 | foreach (DataGridViewRow dr in dataGridSymbols.Rows)
474 | {
475 | DataGridViewCell dc = dr.Cells[0]; // name
476 | if (dc.Value.ToString() == name)
477 | {
478 | dataGridSymbols.CurrentCell = dc;
479 | break;
480 | }
481 | }
482 | }
483 | }
484 | }
485 |
--------------------------------------------------------------------------------
/CruncherSharp.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Debug
5 | AnyCPU
6 | 9.0.30729
7 | 2.0
8 | {2F38B30C-7210-4699-8D54-7D9506AB6345}
9 | WinExe
10 | Properties
11 | CruncherSharp
12 | CruncherSharp
13 | v4.7.2
14 | 512
15 |
16 |
17 |
18 |
19 | 3.5
20 |
21 | false
22 | publish\
23 | true
24 | Disk
25 | false
26 | Foreground
27 | 7
28 | Days
29 | false
30 | false
31 | true
32 | 0
33 | 1.0.0.%2a
34 | false
35 | true
36 |
37 |
38 | true
39 | full
40 | false
41 | bin\Debug\
42 | DEBUG;TRACE
43 | prompt
44 | 4
45 | false
46 | true
47 |
48 |
49 | pdbonly
50 | true
51 | bin\Release\
52 | TRACE
53 | prompt
54 | 4
55 | false
56 | false
57 |
58 |
59 | crunchersharp.ico
60 |
61 |
62 | CruncherSharp.Program
63 |
64 |
65 |
66 |
67 | 3.5
68 |
69 |
70 | 3.5
71 |
72 |
73 | 3.5
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 | Form
84 |
85 |
86 | CruncherSharpForm.cs
87 |
88 |
89 |
90 |
91 | Form
92 |
93 |
94 | SelectNamespaceForm.cs
95 |
96 |
97 |
98 |
99 |
100 |
101 | CruncherSharpForm.cs
102 | Designer
103 |
104 |
105 | ResXFileCodeGenerator
106 | Resources.Designer.cs
107 | Designer
108 |
109 |
110 | True
111 | Resources.resx
112 | True
113 |
114 |
115 | SelectNamespaceForm.cs
116 |
117 |
118 |
119 | SettingsSingleFileGenerator
120 | Settings.Designer.cs
121 |
122 |
123 | True
124 | Settings.settings
125 | True
126 |
127 |
128 |
129 |
130 |
131 |
132 |
133 | {106173A0-0173-4E5C-84E7-E915422BE997}
134 | 2
135 | 0
136 | 0
137 | tlbimp
138 | False
139 |
140 |
141 |
142 |
143 |
144 |
145 |
146 | False
147 | Microsoft .NET Framework 4.6.2 %28x86 and x64%29
148 | true
149 |
150 |
151 | False
152 | .NET Framework 3.5 SP1
153 | false
154 |
155 |
156 |
157 |
158 | IF DEFINED GEARSTUDIO_CI regsvr32 /s \\ubisoft.org\projects\TechGroup\Public\Prism\GearStudio\GitLabBuild\extern\msdia\msdia140.dll
159 |
160 |
167 |
--------------------------------------------------------------------------------
/CruncherSharp.csproj.user:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | publish\
5 |
6 |
7 |
8 |
9 |
10 | en-US
11 | false
12 |
13 |
--------------------------------------------------------------------------------
/CruncherSharp.resx:
--------------------------------------------------------------------------------
1 |
2 |
3 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 |
107 |
108 |
109 | text/microsoft-resx
110 |
111 |
112 | 2.0
113 |
114 |
115 | System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
116 |
117 |
118 | System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
119 |
120 |
121 | 17, 17
122 |
123 |
124 | 121, 17
125 |
126 |
127 | 250, 17
128 |
129 |
130 | True
131 |
132 |
133 | True
134 |
135 |
136 | True
137 |
138 |
139 | 417, 17
140 |
141 |
--------------------------------------------------------------------------------
/CruncherSharp.sln:
--------------------------------------------------------------------------------
1 |
2 | Microsoft Visual Studio Solution File, Format Version 12.00
3 | # Visual Studio 15
4 | VisualStudioVersion = 15.0.27004.2002
5 | MinimumVisualStudioVersion = 10.0.40219.1
6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CruncherSharp", "CruncherSharp.csproj", "{2F38B30C-7210-4699-8D54-7D9506AB6345}"
7 | EndProject
8 | Global
9 | GlobalSection(SolutionConfigurationPlatforms) = preSolution
10 | Debug|Any CPU = Debug|Any CPU
11 | Release|Any CPU = Release|Any CPU
12 | EndGlobalSection
13 | GlobalSection(ProjectConfigurationPlatforms) = postSolution
14 | {2F38B30C-7210-4699-8D54-7D9506AB6345}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
15 | {2F38B30C-7210-4699-8D54-7D9506AB6345}.Debug|Any CPU.Build.0 = Debug|Any CPU
16 | {2F38B30C-7210-4699-8D54-7D9506AB6345}.Release|Any CPU.ActiveCfg = Release|Any CPU
17 | {2F38B30C-7210-4699-8D54-7D9506AB6345}.Release|Any CPU.Build.0 = Release|Any CPU
18 | EndGlobalSection
19 | GlobalSection(SolutionProperties) = preSolution
20 | HideSolutionNode = FALSE
21 | EndGlobalSection
22 | EndGlobal
23 |
--------------------------------------------------------------------------------
/CruncherSharpForm.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.ComponentModel;
4 | using System.Data;
5 | using System.Drawing;
6 | using System.IO;
7 | using System.Linq;
8 | using System.Windows.Forms;
9 | using static System.Int32;
10 |
11 | namespace CruncherSharp
12 | {
13 | public partial class CruncherSharpForm : Form
14 | {
15 | public enum SearchType
16 | {
17 | None,
18 | UnusedVTables,
19 | MSVCExtraPadding,
20 | MSVCEmptyBaseClass,
21 | UnusedInterfaces,
22 | UnusedVirtual,
23 | MaskingFunction,
24 | RemovedInline,
25 | }
26 |
27 | private readonly List _FunctionsToIgnore;
28 | private readonly Stack _NavigationStack;
29 | private readonly SymbolAnalyzer _SymbolAnalyzer;
30 | private readonly DataTable _Table;
31 | public bool _CloseRequested;
32 | private string _FileName;
33 | public bool _HasInstancesCount;
34 | public bool _HasSecondPDB;
35 | private ulong _PrefetchStartOffset;
36 | private SearchType _SearchCategory = SearchType.None;
37 |
38 | private SymbolInfo _SelectedSymbol;
39 |
40 | public CruncherSharpForm(SymbolAnalyzer symbolAnalyzer)
41 | {
42 | InitializeComponent();
43 |
44 | _SymbolAnalyzer = symbolAnalyzer;
45 | _Table = CreateDataTable();
46 | _Table.CaseSensitive = checkBoxMatchCase.Checked;
47 | _NavigationStack = new Stack();
48 | _FunctionsToIgnore = new List();
49 | _SelectedSymbol = null;
50 | _PrefetchStartOffset = 0;
51 |
52 | bindingSourceSymbols.DataSource = _Table;
53 | dataGridSymbols.DataSource = bindingSourceSymbols;
54 |
55 | dataGridSymbols.Columns[0].Width = 271;
56 |
57 | WindowState = FormWindowState.Maximized;
58 | }
59 |
60 | public SearchType SearchCategory
61 | {
62 | get => _SearchCategory;
63 | private set
64 | {
65 | _SearchCategory = value;
66 | UpdateCheckedOption();
67 | }
68 | }
69 |
70 | private void Reset()
71 | {
72 | checkedListBoxNamespaces.Items.Clear();
73 | _Table.Clear();
74 | dataGridViewSymbolInfo.Rows.Clear();
75 | dataGridViewFunctionsInfo.Rows.Clear();
76 | _SelectedSymbol = null;
77 | labelCurrentSymbol.Text = "";
78 | _SymbolAnalyzer.Reset();
79 | }
80 |
81 | private void exitToolStripMenuItem_Click(object sender, EventArgs e)
82 | {
83 | Close();
84 | }
85 |
86 | private void loadPDBToolStripMenuItem_Click(object sender, EventArgs e)
87 | {
88 | if (backgroundWorker.IsBusy)
89 | return;
90 |
91 | if (openPdbDialog.ShowDialog() != DialogResult.OK)
92 | return;
93 |
94 | _FileName = openPdbDialog.FileName;
95 | Text = "Cruncher# - " + _FileName;
96 | Reset();
97 | btnLoad.Enabled = true;
98 | btnReset.Enabled = true;
99 | textBoxFilter.Focus();
100 | }
101 |
102 | public void LoadPdb(string fileName, bool secondPDB)
103 | {
104 | _SelectedSymbol = null;
105 | toolStripProgressBar.Value = 0;
106 | toolStripStatusLabel.Text = "Loading PDB...";
107 | if (!secondPDB)
108 | {
109 | Text = "Cruncher# - " + fileName;
110 | if (textBoxFilter.Text.Length > 0)
111 | Text += " Filter: " + textBoxFilter.Text;
112 | }
113 |
114 | var task = new LoadPDBTask
115 | {
116 | FileName = fileName,
117 | SecondPDB = secondPDB,
118 | Filter = textBoxFilter.Text,
119 | MatchCase = checkBoxMatchCase.Checked,
120 | WholeExpression = checkBoxMatchWholeExpression.Checked,
121 | UseRegularExpression = checkBoxRegularExpressions.Checked
122 | };
123 | backgroundWorker.RunWorkerAsync(task);
124 | }
125 |
126 | private static DataTable CreateDataTable()
127 | {
128 | var table = new DataTable("Symbols");
129 | table.Columns.Add(new DataColumn {ColumnName = "Symbol", ReadOnly = true});
130 | table.Columns.Add(new DataColumn
131 | {
132 | ColumnName = "Size",
133 | ReadOnly = true,
134 | DataType = Type.GetType("System.UInt32")
135 | });
136 | table.Columns.Add(new DataColumn
137 | {
138 | ColumnName = "Padding",
139 | ReadOnly = true,
140 | DataType = Type.GetType("System.UInt32")
141 | });
142 | table.Columns.Add(new DataColumn
143 | {
144 | ColumnName = "Padding zones",
145 | ReadOnly = true,
146 | DataType = Type.GetType("System.UInt32")
147 | });
148 | table.Columns.Add(new DataColumn
149 | {
150 | ColumnName = "Total padding",
151 | ReadOnly = true,
152 | DataType = Type.GetType("System.UInt32")
153 | });
154 | table.Columns.Add(new DataColumn
155 | {
156 | ColumnName = "Padding %",
157 | ReadOnly = true,
158 | DataType = Type.GetType("System.UInt32")
159 | });
160 |
161 | return table;
162 | }
163 |
164 | private void AddInstancesCount()
165 | {
166 | if (_HasInstancesCount)
167 | return;
168 | _HasInstancesCount = true;
169 | if (_HasSecondPDB)
170 | _Table.Columns.Add(new DataColumn
171 | {
172 | ColumnName = "Total delta",
173 | ReadOnly = true,
174 | DataType = Type.GetType("System.Int32")
175 | });
176 | _Table.Columns.Add(new DataColumn
177 | {
178 | ColumnName = "Instances",
179 | ReadOnly = true,
180 | DataType = Type.GetType("System.UInt64")
181 | });
182 | _Table.Columns.Add(new DataColumn
183 | {
184 | ColumnName = "Total count",
185 | ReadOnly = true,
186 | DataType = Type.GetType("System.UInt64")
187 | });
188 | _Table.Columns.Add(new DataColumn
189 | {
190 | ColumnName = "Total size",
191 | ReadOnly = true,
192 | DataType = Type.GetType("System.UInt64")
193 | });
194 | _Table.Columns.Add(new DataColumn
195 | {
196 | ColumnName = "Total waste",
197 | ReadOnly = true,
198 | DataType = Type.GetType("System.UInt64")
199 | });
200 | }
201 |
202 | private void AddSecondPDB()
203 | {
204 | if (_HasSecondPDB)
205 | return;
206 | _HasSecondPDB = true;
207 | _Table.Columns.Add(new DataColumn
208 | {
209 | ColumnName = "New size",
210 | ReadOnly = true,
211 | DataType = Type.GetType("System.UInt32")
212 | });
213 | _Table.Columns.Add(new DataColumn
214 | {
215 | ColumnName = "Delta",
216 | ReadOnly = true,
217 | DataType = Type.GetType("System.Int32")
218 | });
219 | if (_HasInstancesCount)
220 | _Table.Columns.Add(new DataColumn
221 | {
222 | ColumnName = "Total delta",
223 | ReadOnly = true,
224 | DataType = Type.GetType("System.Int32")
225 | });
226 | }
227 |
228 | private void AddSymbolToTable(SymbolInfo symbolInfo)
229 | {
230 | var row = _Table.NewRow();
231 |
232 | row["Symbol"] = symbolInfo.Name;
233 | row["Size"] = symbolInfo.Size;
234 | row["Padding"] = symbolInfo.Padding;
235 | row["Padding zones"] = symbolInfo.PaddingZonesCount;
236 | row["Total padding"] = symbolInfo.TotalPadding.Value;
237 | row["Padding %"] = (uint) ((double) symbolInfo.TotalPadding.Value / symbolInfo.Size * 100);
238 | if (_HasInstancesCount)
239 | {
240 | row["Instances"] = symbolInfo.NumInstances;
241 | row["Total count"] = symbolInfo.TotalCount;
242 | row["Total size"] = symbolInfo.TotalCount * symbolInfo.Size;
243 | row["Total waste"] = symbolInfo.TotalCount * symbolInfo.Padding;
244 | }
245 |
246 | if (_HasSecondPDB)
247 | {
248 | row["New size"] = symbolInfo.NewSize;
249 | row["Delta"] = (long) symbolInfo.NewSize - (long) symbolInfo.Size;
250 | if (_HasInstancesCount && symbolInfo.NumInstances > 0)
251 | row["Total delta"] = ((long) symbolInfo.NewSize - (long) symbolInfo.Size) *
252 | (long) symbolInfo.NumInstances;
253 | }
254 |
255 | _Table.Rows.Add(row);
256 | }
257 |
258 |
259 | private ulong GetCacheLineSize()
260 | {
261 | return Convert.ToUInt32(textBoxCache.Text);
262 | }
263 |
264 | private SymbolInfo FindSelectedSymbolInfo()
265 | {
266 | if (dataGridSymbols.SelectedRows.Count == 0) return null;
267 |
268 | var selectedRow = dataGridSymbols.SelectedRows[0];
269 | var symbolName = selectedRow.Cells[0].Value.ToString();
270 | return _SymbolAnalyzer.FindSymbolInfo(symbolName);
271 | }
272 |
273 | private string GetFilterString()
274 | {
275 | var filters = new List();
276 | if (textBoxFilter.Text.Length > 0)
277 | if (checkBoxMatchWholeExpression.Checked)
278 | filters.Add($"Symbol = '{textBoxFilter.Text.Replace("'", string.Empty)}'");
279 | else if (checkBoxRegularExpressions.Checked)
280 | filters.Add($"Symbol LIKE '{textBoxFilter.Text.Replace("'", string.Empty)}'");
281 | else
282 | filters.Add($"Symbol LIKE '*{textBoxFilter.Text.Replace("'", string.Empty)}*'");
283 | if (!chkShowTemplates.Checked)
284 | filters.Add("Symbol NOT LIKE '*<*'");
285 | if (checkedListBoxNamespaces.CheckedItems.Count > 0)
286 | {
287 | var namespaceFilters = checkedListBoxNamespaces.CheckedItems.Cast()
288 | .Select(item => $"Symbol LIKE '*{item.Replace("'", string.Empty)}::*'");
289 | filters.Add($"({string.Join(" OR ", namespaceFilters)})");
290 | }
291 |
292 | return string.Join(" AND ", filters);
293 | }
294 |
295 | private void dataGridSymbols_SelectionChanged(object sender, EventArgs e)
296 | {
297 | _PrefetchStartOffset = 0;
298 | if (dataGridSymbols.SelectedRows.Count > 1)
299 | ShowSelectionSummary();
300 | else
301 | ShowSelectedSymbolInfo();
302 | }
303 |
304 | private void dataGridSymbols_ParentSelected(object sender, EventArgs e)
305 | {
306 | dataGridSymbols.ClearSelection();
307 | if (TrySelectSymbol(sender.ToString()) == false)
308 | {
309 | var symbolInfo = _SymbolAnalyzer.FindSymbolInfo(sender.ToString());
310 | if (symbolInfo != null)
311 | ShowSymbolInfo(symbolInfo, true);
312 | }
313 | }
314 |
315 | private void dataGridSymbols_CellMouseDoubleClick(object sender, DataGridViewCellMouseEventArgs e)
316 | {
317 | _PrefetchStartOffset = 0;
318 | ShowSelectedSymbolInfo();
319 | }
320 |
321 | private void ShowSelectionSummary()
322 | {
323 | dataGridViewSymbolInfo.Rows.Clear();
324 |
325 | ulong totalMemory = 0;
326 | ulong totalWaste = 0;
327 | long totalDiff = 0;
328 | foreach (DataGridViewRow selectedRow in dataGridSymbols.SelectedRows)
329 | {
330 | var symbolName = selectedRow.Cells[0].Value.ToString();
331 | var symbolInfo = _SymbolAnalyzer.FindSymbolInfo(symbolName);
332 |
333 | if (_HasInstancesCount)
334 | {
335 | totalMemory += symbolInfo.NumInstances * symbolInfo.Size;
336 | totalWaste += symbolInfo.NumInstances * symbolInfo.TotalPadding.Value;
337 | if (_HasSecondPDB)
338 | totalDiff += ((long) symbolInfo.NewSize - (long) symbolInfo.Size) *
339 | (long) symbolInfo.NumInstances;
340 | }
341 | else
342 | {
343 | totalMemory += symbolInfo.Size;
344 | totalWaste += symbolInfo.TotalPadding.Value;
345 | }
346 | }
347 |
348 | labelCurrentSymbol.Text = "Total memory: " + totalMemory + " total waste: " + totalWaste;
349 | if (_HasInstancesCount && _HasSecondPDB)
350 | labelCurrentSymbol.Text += " total diff: " + totalDiff;
351 | }
352 |
353 | private void ShowSelectedSymbolInfo()
354 | {
355 | dataGridViewSymbolInfo.Rows.Clear();
356 | var info = FindSelectedSymbolInfo();
357 | if (info != null) ShowSymbolInfo(info, false);
358 | }
359 |
360 | private bool TrySelectSymbol(string name)
361 | {
362 | foreach (DataGridViewRow dr in dataGridSymbols.Rows)
363 | {
364 | var dc = dr.Cells[0]; // name
365 | if (dc.Value.ToString() != name)
366 | continue;
367 | dataGridSymbols.CurrentCell = dc;
368 | return true;
369 | }
370 |
371 | return false;
372 | }
373 |
374 | private void ShowSymbolInfo(SymbolInfo info, bool addToStack)
375 | {
376 | labelCurrentSymbol.Text = info.Name;
377 |
378 | if (_SelectedSymbol != null && addToStack)
379 | _NavigationStack.Push(_SelectedSymbol);
380 | else if (!addToStack) _NavigationStack.Clear();
381 | _SelectedSymbol = info;
382 |
383 | UpdateSymbolGrid();
384 | }
385 |
386 | private void UpdateSymbolGrid()
387 | {
388 | dataGridViewSymbolInfo.Rows.Clear();
389 | dataGridViewFunctionsInfo.Rows.Clear();
390 |
391 | var prevCacheBoundaryOffset = _PrefetchStartOffset;
392 |
393 | if (prevCacheBoundaryOffset > _SelectedSymbol.Size) prevCacheBoundaryOffset = _SelectedSymbol.Size;
394 |
395 | ulong numCacheLines = 0;
396 | var increment = 0;
397 |
398 | AddSymbolToGrid(_SelectedSymbol, ref prevCacheBoundaryOffset, ref numCacheLines, 0, increment);
399 | }
400 |
401 | private void RefreshSymbolGrid(int rowIndex)
402 | {
403 | var firstRow = dataGridViewSymbolInfo.FirstDisplayedScrollingRowIndex;
404 | UpdateSymbolGrid();
405 | dataGridViewSymbolInfo.Rows[rowIndex].Selected = true;
406 | dataGridViewSymbolInfo.FirstDisplayedScrollingRowIndex = firstRow;
407 | }
408 |
409 | private void AddSymbolToGrid(SymbolInfo symbol, ref ulong prevCacheBoundaryOffset, ref ulong numCacheLines,
410 | ulong previousOffset, int increment)
411 | {
412 | var cacheLineSize = GetCacheLineSize();
413 |
414 | var incrementText = "";
415 | var incrementTextEmpty = "";
416 |
417 | var whitespaceIncrementText = "";
418 | for (var i = 0; i < increment; i++)
419 | {
420 | whitespaceIncrementText += " ";
421 | if (i == increment - 1)
422 | incrementText += " |__ ";
423 | else
424 | incrementText += " | ";
425 | incrementTextEmpty += " | ";
426 | }
427 |
428 | foreach (var member in symbol.Members)
429 | {
430 | var currentOffset = previousOffset + member.Offset;
431 | if (member.PaddingBefore > 0 && checkBoxPadding.Checked)
432 | {
433 | var paddingOffset = member.Offset - member.PaddingBefore;
434 | string[] paddingRow =
435 | {
436 | string.Empty,
437 | incrementTextEmpty,
438 | "Padding",
439 | paddingOffset.ToString(),
440 | string.Empty,
441 | member.PaddingBefore.ToString(),
442 | string.Empty
443 | };
444 | dataGridViewSymbolInfo.Rows.Add(paddingRow);
445 | }
446 |
447 | if (checkBoxCacheLines.Checked)
448 | {
449 | var cacheLines = new List();
450 | while (currentOffset >= cacheLineSize + prevCacheBoundaryOffset)
451 | {
452 | numCacheLines++;
453 | var cacheLineOffset = numCacheLines * cacheLineSize + _PrefetchStartOffset;
454 | cacheLines.Add(cacheLineOffset);
455 | prevCacheBoundaryOffset = cacheLineOffset;
456 | }
457 |
458 | if (cacheLines.Count > 3 && chkSmartCacheLines.Checked)
459 | {
460 | string[] firstBoundaryRow =
461 | {
462 | string.Empty, incrementTextEmpty, "Cacheline boundary", cacheLines[0].ToString(),
463 | string.Empty, string.Empty, string.Empty
464 | };
465 | dataGridViewSymbolInfo.Rows.Add(firstBoundaryRow);
466 | string[] middleBoundariesRow =
467 | {
468 | string.Empty, incrementTextEmpty, "Cacheline boundaries (...)", string.Empty,
469 | string.Empty, string.Empty, string.Empty
470 | };
471 | dataGridViewSymbolInfo.Rows.Add(middleBoundariesRow);
472 | string[] lastBoundaryRow =
473 | {
474 | string.Empty, incrementTextEmpty, "Cacheline boundary",
475 | cacheLines[cacheLines.Count - 1].ToString(), string.Empty, string.Empty, string.Empty
476 | };
477 | dataGridViewSymbolInfo.Rows.Add(lastBoundaryRow);
478 | }
479 | else
480 | {
481 | cacheLines.ForEach(offset =>
482 | {
483 | string[] boundaryRow =
484 | {
485 | string.Empty,
486 | incrementTextEmpty,
487 | "Cacheline boundary",
488 | offset.ToString(),
489 | string.Empty,
490 | string.Empty
491 | };
492 | dataGridViewSymbolInfo.Rows.Add(boundaryRow);
493 | });
494 | }
495 | }
496 |
497 | var baseInfo = _SymbolAnalyzer.FindSymbolInfo(member.TypeName);
498 | var expand = "";
499 | if (member.Expanded)
500 | expand = whitespaceIncrementText + "- ";
501 | else if (member.IsExapandable)
502 | expand = whitespaceIncrementText + "+ ";
503 |
504 | object[] row =
505 | {
506 | expand,
507 | incrementText + member.DisplayName,
508 | member.TypeName,
509 | currentOffset.ToString(),
510 | (member.BitField ? member.BitPosition.ToString() : string.Empty),
511 | member.BitField ? (member.BitSize.ToString() + (member.BitSize == 1 ? " bit" : " bits")) : (member.Size.ToString() + (member.Size == 1 ? " byte" : " bytes")),
512 | baseInfo?.TotalPadding.ToString() ?? string.Empty
513 | };
514 | dataGridViewSymbolInfo.Rows.Add(row);
515 | dataGridViewSymbolInfo.Rows[dataGridViewSymbolInfo.Rows.Count - 1].Tag = member;
516 |
517 | if (member.Expanded && baseInfo != null)
518 | AddSymbolToGrid(baseInfo, ref prevCacheBoundaryOffset, ref numCacheLines, currentOffset,
519 | increment + 1);
520 |
521 | if (member.BitField && member.BitPaddingAfter > 0 && checkBoxBitPadding.Checked)
522 | {
523 | var paddingOffset = member.BitPaddingAfter.ToString() + " bits";
524 | string[] paddingRow =
525 | {
526 | string.Empty,
527 | incrementTextEmpty,
528 | "Bitfield padding",
529 | currentOffset.ToString(),
530 | (member.BitPosition + member.BitSize).ToString(),
531 | paddingOffset,
532 | string.Empty
533 | };
534 | dataGridViewSymbolInfo.Rows.Add(paddingRow);
535 | }
536 | }
537 |
538 | // Final structure padding.
539 | if (symbol.EndPadding > 0 && checkBoxPadding.Checked)
540 | {
541 | var endPaddingOffset = symbol.Size - symbol.EndPadding;
542 | string[] paddingRow =
543 | {
544 | string.Empty, string.Empty, "Padding", endPaddingOffset.ToString(), symbol.EndPadding.ToString(),
545 | symbol.EndPadding.ToString()
546 | };
547 | dataGridViewSymbolInfo.Rows.Add(paddingRow);
548 | }
549 |
550 | foreach (var function in symbol.Functions)
551 | {
552 | object[] row =
553 | {
554 | function.DisplayName, function.Virtual, function.IsPure, function.IsOverride, function.IsOverloaded,
555 | function.IsMasking
556 | };
557 | dataGridViewFunctionsInfo.Rows.Add(row);
558 | dataGridViewFunctionsInfo.Rows[dataGridViewFunctionsInfo.Rows.Count - 1].Tag = function;
559 | }
560 | }
561 |
562 | private void dataGridSymbols_SortCompare(object sender, DataGridViewSortCompareEventArgs e)
563 | {
564 | if (e.Column.Index > 0)
565 | {
566 | e.Handled = true;
567 | TryParse(e.CellValue1.ToString(), out var val1);
568 | TryParse(e.CellValue2.ToString(), out var val2);
569 | e.SortResult = val1 - val2;
570 | }
571 | }
572 |
573 | private void dataGridViewSymbolInfo_CellFormatting(object sender, DataGridViewCellFormattingEventArgs e)
574 | {
575 | var row = dataGridViewSymbolInfo.Rows[e.RowIndex];
576 | var nameCell = row.Cells[2];
577 |
578 | if (row.Tag is SymbolMemberInfo info)
579 | {
580 | if (info.Category == SymbolMemberInfo.MemberCategory.VTable)
581 | e.CellStyle.BackColor = Color.LemonChiffon;
582 | else if (info.IsBase)
583 | e.CellStyle.BackColor = Color.LightGreen;
584 |
585 | else if (info.BitField)
586 | {
587 | if (e.ColumnIndex == 3 ) // Offset column
588 | {
589 | e.CellStyle.BackColor = Color.FromArgb((int)((info.Offset * 37) % 256), 192, 192); // random color not too dark
590 | row.Cells[e.ColumnIndex].ToolTipText = "Bitfield";
591 | }
592 | }
593 | else if (info.AlignWithPrevious)
594 | if (e.ColumnIndex == 3) // Offset column
595 | {
596 | e.CellStyle.BackColor = Color.Salmon;
597 | row.Cells[e.ColumnIndex].ToolTipText = "Aligned with previous";
598 | }
599 |
600 | if (info.Volatile)
601 | if (e.ColumnIndex == 0) // Name (Field) column
602 | {
603 | e.CellStyle.BackColor = Color.LightBlue;
604 | row.Cells[e.ColumnIndex].ToolTipText = "Volatile";
605 | }
606 | }
607 | else if (nameCell.Value.ToString() == "Padding")
608 | {
609 | e.CellStyle.BackColor = Color.LightPink;
610 | if (e.ColumnIndex == 2)
611 | e.CellStyle.Alignment = DataGridViewContentAlignment.MiddleCenter;
612 | }
613 | else if (nameCell.Value.ToString() == "Bitfield padding")
614 | {
615 | e.CellStyle.BackColor = Color.PaleVioletRed;
616 | if (e.ColumnIndex == 2)
617 | e.CellStyle.Alignment = DataGridViewContentAlignment.MiddleCenter;
618 | }
619 | else if (nameCell.Value.ToString().StartsWith("Cacheline"))
620 | {
621 | e.CellStyle.BackColor = Color.LightGray;
622 | if (e.ColumnIndex == 2)
623 | e.CellStyle.Alignment = DataGridViewContentAlignment.MiddleCenter;
624 | }
625 | }
626 |
627 | private void textBoxFilter_TextChanged(object sender, EventArgs e)
628 | {
629 | try
630 | {
631 | bindingSourceSymbols.Filter = GetFilterString();
632 | textBoxFilter.BackColor = Color.Empty;
633 | textBoxFilter.ForeColor = Color.Empty;
634 | }
635 | catch (EvaluateException)
636 | {
637 | textBoxFilter.BackColor = Color.Red;
638 | textBoxFilter.ForeColor = Color.White;
639 | }
640 | }
641 |
642 | private void textBoxFilter_KeyUp(object sender, KeyEventArgs e)
643 | {
644 | if (e.KeyCode == Keys.Return && !backgroundWorker.IsBusy)
645 | {
646 | LoadPdb(_FileName, false);
647 | btnLoad.Text = "Cancel";
648 | }
649 | }
650 |
651 | private void copyTypeLayoutToClipboardToolStripMenuItem_Click(object sender, EventArgs e)
652 | {
653 | var info = FindSelectedSymbolInfo();
654 | if (info != null) Clipboard.SetText(info.ToString());
655 | }
656 |
657 | private void textBoxCache_KeyPress(object sender, KeyPressEventArgs e)
658 | {
659 | if (e.KeyChar == (char) Keys.Enter || e.KeyChar == (char) Keys.Escape) ShowSelectedSymbolInfo();
660 | OnKeyPress(e);
661 | }
662 |
663 | private void textBoxCache_Leave(object sender, EventArgs e)
664 | {
665 | ShowSelectedSymbolInfo();
666 | }
667 |
668 | private void setPrefetchStartOffsetToolStripMenuItem_Click(object sender, EventArgs e)
669 | {
670 | if (dataGridViewSymbolInfo.SelectedRows.Count != 0)
671 | {
672 | var selectedRow = dataGridViewSymbolInfo.SelectedRows[0];
673 | ulong symbolOffset = Convert.ToUInt32(selectedRow.Cells[3].Value);
674 | _PrefetchStartOffset = symbolOffset % GetCacheLineSize();
675 | RefreshSymbolGrid(selectedRow.Index);
676 | }
677 | }
678 |
679 |
680 | private void checkBoxCacheLines_CheckedChanged(object sender, EventArgs e)
681 | {
682 | if (dataGridViewSymbolInfo.SelectedRows.Count != 0)
683 | {
684 | var selectedRow = dataGridViewSymbolInfo.SelectedRows[0];
685 | RefreshSymbolGrid(selectedRow.Index);
686 | }
687 | else
688 | {
689 | RefreshSymbolGrid(0);
690 | }
691 | }
692 |
693 | private void dataGridViewSymbolInfo_MouseClick(object sender, MouseEventArgs e)
694 | {
695 | if (e.Button == MouseButtons.Right)
696 | {
697 | var currentMouseOverRow = dataGridViewSymbolInfo.HitTest(e.X, e.Y).RowIndex;
698 | if (currentMouseOverRow >= 0)
699 | {
700 | dataGridViewSymbolInfo.Rows[currentMouseOverRow].Selected = true;
701 | contextMenuStripMembers.Show(dataGridViewSymbolInfo, new Point(e.X, e.Y));
702 | }
703 | }
704 | }
705 |
706 | private void dataGridViewSymbolInfo_CellMouseDoubleClick(object sender, DataGridViewCellMouseEventArgs e)
707 | {
708 | var selectedRow = dataGridViewSymbolInfo.Rows[e.RowIndex];
709 | var currentSymbolInfo = selectedRow.Tag as SymbolMemberInfo;
710 | if (currentSymbolInfo != null && currentSymbolInfo.Category != SymbolMemberInfo.MemberCategory.Member)
711 | {
712 | var typeName = currentSymbolInfo.TypeName;
713 | if (typeName.Contains("[")) typeName = typeName.Substring(0, typeName.IndexOf("["));
714 |
715 | if (currentSymbolInfo.Category == SymbolMemberInfo.MemberCategory.Pointer)
716 | {
717 | if (typeName.Contains("*")) typeName = typeName.Substring(0, typeName.IndexOf("*"));
718 | if (typeName.Contains("&")) typeName = typeName.Substring(0, typeName.IndexOf("&"));
719 | }
720 |
721 | var jumpToSymbolInfo = _SymbolAnalyzer.FindSymbolInfo(typeName, true);
722 | if (jumpToSymbolInfo != null)
723 | if (e.ColumnIndex == 0)
724 | {
725 | if (currentSymbolInfo.IsExapandable)
726 | {
727 | currentSymbolInfo.Expanded = !currentSymbolInfo.Expanded;
728 | RefreshSymbolGrid(e.RowIndex);
729 | }
730 | }
731 | else
732 | {
733 | ShowSymbolInfo(jumpToSymbolInfo, true);
734 | }
735 | }
736 | }
737 |
738 | private void chkShowTemplates_CheckedChanged(object sender, EventArgs e)
739 | {
740 | bindingSourceSymbols.Filter = GetFilterString();
741 | }
742 |
743 | private void chkSmartCacheLines_CheckedChanged(object sender, EventArgs e)
744 | {
745 | ShowSelectedSymbolInfo();
746 | }
747 |
748 | private void checkedListBoxNamespaces_ItemCheck(object sender, ItemCheckEventArgs e)
749 | {
750 | this.BeginInvoke(new Action(() =>
751 | {
752 | bindingSourceSymbols.Filter = GetFilterString();
753 | }));
754 | }
755 |
756 | private void dataGridViewSymbolInfo_KeyDown(object sender, KeyEventArgs e)
757 | {
758 | if (e.KeyCode == Keys.Back)
759 | if (_NavigationStack.Count > 0)
760 | {
761 | _SelectedSymbol = null;
762 | ShowSymbolInfo(_NavigationStack.Pop(), true);
763 | }
764 |
765 | OnKeyDown(e);
766 | }
767 |
768 | private void backgroundWorker_ProgressChanged(object sender, ProgressChangedEventArgs e)
769 | {
770 | toolStripProgressBar.Value = e.ProgressPercentage;
771 | toolStripStatusLabel.Text = e.UserState as String;
772 | }
773 |
774 | private void backgroundWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
775 | {
776 | btnLoad.Text = "Load";
777 |
778 | if (e.Cancelled)
779 | {
780 | if (_CloseRequested)
781 | {
782 | Close();
783 | }
784 | else
785 | {
786 | toolStripStatusLabel.Text = "";
787 | toolStripProgressBar.Value = 0;
788 | }
789 |
790 | return;
791 | }
792 |
793 | var loadPdbSuccess = (bool) e.Result;
794 |
795 | if (!loadPdbSuccess)
796 | {
797 | MessageBox.Show(this, _SymbolAnalyzer.LastError);
798 | toolStripStatusLabel.Text = "Failed to load PDB.";
799 | return;
800 | }
801 |
802 | OnPDBLoaded();
803 | }
804 |
805 | private void backgroundWorker_DoWork(object sender, DoWorkEventArgs e)
806 | {
807 | e.Result = _SymbolAnalyzer.LoadPdb(sender, e);
808 | }
809 |
810 | private void OnPDBLoaded()
811 | {
812 | compareWithPDBToolStripMenuItem.Enabled = true;
813 | loadInstanceCountToolStripMenuItem.Enabled = true;
814 | exportCsvToolStripMenuItem.Enabled = true;
815 | findUnusedVtablesToolStripMenuItem.Enabled = true;
816 | findMSVCExtraPaddingToolStripMenuItem.Enabled = true;
817 | findMSVCEmptyBaseClassToolStripMenuItem.Enabled = true;
818 | findUnusedInterfacesToolStripMenuItem.Enabled = true;
819 | findUnusedVirtualToolStripMenuItem.Enabled = true;
820 | findMaskingFunctionsToolStripMenuItem.Enabled = true;
821 | findRemovedInlineToolStripMenuItem.Enabled = true;
822 |
823 | // Temporarily clear the filter so, if current filter is invalid, we don't generate a ton of exceptions while populating the table
824 | var preExistingFilter = textBoxFilter.Text;
825 | textBoxFilter.Text = string.Empty;
826 |
827 | PopulateDataTable();
828 |
829 | checkedListBoxNamespaces.Items.Clear();
830 | foreach (var name in _SymbolAnalyzer.RootNamespaces) checkedListBoxNamespaces.Items.Add(name);
831 |
832 | // Sort by name by default (ascending)
833 | dataGridSymbols.Sort(dataGridSymbols.Columns[0], ListSortDirection.Ascending);
834 | bindingSourceSymbols.Filter = null; // "Symbol LIKE '*rde*'";
835 |
836 | // Restore the filter now that the table is populated
837 | textBoxFilter.Text = preExistingFilter;
838 | bindingSourceSymbols.Filter = GetFilterString();
839 |
840 | ShowSelectedSymbolInfo();
841 |
842 | _NavigationStack.Clear();
843 | }
844 |
845 | private void loadInstanceCountToolStripMenuItem_Click(object sender, EventArgs e)
846 | {
847 | if (backgroundWorker.IsBusy) return;
848 | if (openCsvDialog.ShowDialog() == DialogResult.OK) LoadCsv(openPdbDialog.FileName);
849 | }
850 |
851 | private void LoadCsv(string fileName)
852 | {
853 | Cursor.Current = Cursors.WaitCursor;
854 |
855 | if (_HasInstancesCount)
856 | foreach (var symbol in _SymbolAnalyzer.Symbols.Values)
857 | {
858 | symbol.NumInstances = 0;
859 | symbol.TotalCount = 0;
860 | }
861 |
862 | using (var sourceStream =
863 | File.Open(openCsvDialog.FileName, FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
864 | {
865 | using (var reader = new StreamReader(sourceStream))
866 | {
867 | var skipAllResolve = false;
868 | while (!reader.EndOfStream)
869 | {
870 | var line = reader.ReadLine();
871 | var values = line.Split(',');
872 | var count = ulong.Parse(values[1]);
873 | if (count == 0)
874 | continue;
875 |
876 | var info = _SymbolAnalyzer.FindSymbolInfo(values[0]);
877 | if (info == null)
878 | {
879 | var validNamespaces = new List();
880 | if (checkedListBoxNamespaces.CheckedItems.Count > 0)
881 | {
882 | foreach (var selected in checkedListBoxNamespaces.CheckedItems)
883 | {
884 | info = _SymbolAnalyzer.FindSymbolInfo(selected + "::" + values[0]);
885 | if (info != null)
886 | validNamespaces.Add(selected.ToString());
887 | }
888 |
889 | if (validNamespaces.Count == 0)
890 | foreach (var selected in _SymbolAnalyzer.Namespaces)
891 | {
892 | info = _SymbolAnalyzer.FindSymbolInfo(selected + "::" + values[0]);
893 | if (info != null)
894 | validNamespaces.Add(selected);
895 | }
896 | }
897 | else
898 | {
899 | foreach (var selected in _SymbolAnalyzer.Namespaces)
900 | {
901 | info = _SymbolAnalyzer.FindSymbolInfo(selected + "::" + values[0]);
902 | if (info != null)
903 | validNamespaces.Add(selected);
904 | }
905 | }
906 |
907 | if (validNamespaces.Count == 0) continue;
908 |
909 | if (validNamespaces.Count == 1)
910 | {
911 | info = _SymbolAnalyzer.FindSymbolInfo(validNamespaces[0] + "::" + values[0]);
912 | }
913 | else if (!skipAllResolve)
914 | {
915 | var namespaceForm = new SelectNamespaceForm();
916 | namespaceForm.SetName(values[0], count);
917 | namespaceForm.AddNamespaces(validNamespaces);
918 | namespaceForm.ShowDialog();
919 | skipAllResolve = namespaceForm.SkipAllNamespaces;
920 | info = _SymbolAnalyzer.FindSymbolInfo(namespaceForm.GetNamespace() + "::" + values[0]);
921 | }
922 | }
923 |
924 | if (info != null) info.TotalCount = info.NumInstances = count;
925 | }
926 | }
927 | }
928 |
929 | foreach (var symbol in _SymbolAnalyzer.Symbols.Values)
930 | if (symbol.NumInstances > 0)
931 | symbol.UpdateTotalCount(_SymbolAnalyzer, symbol.NumInstances);
932 |
933 | Cursor.Current = Cursors.Default;
934 | AddInstancesCount();
935 | PopulateDataTable();
936 | }
937 |
938 | private void compareWithPDBToolStripMenuItem_Click(object sender, EventArgs e)
939 | {
940 | if (backgroundWorker.IsBusy) return;
941 | if (openPdbDialog.ShowDialog() == DialogResult.OK)
942 | {
943 | AddSecondPDB();
944 | LoadPdb(openPdbDialog.FileName, true);
945 | }
946 | }
947 |
948 | private void exportCsvToolStripMenuItem_Click(object sender, EventArgs e)
949 | {
950 | if (backgroundWorker.IsBusy) return;
951 | if (saveCsvDialog.ShowDialog() == DialogResult.OK)
952 | using (var writer = new StreamWriter(saveCsvDialog.FileName))
953 | {
954 | writer.WriteLine(
955 | "Name,Size,Padding,Padding zones,Total padding,Num Instances,Total size,Total waste,");
956 |
957 | foreach (var symbolInfo in _SymbolAnalyzer.Symbols.Values)
958 | writer.WriteLine("{0},{1},{2},{3},{4},{5},{6},{7},", symbolInfo.Name, symbolInfo.Size,
959 | symbolInfo.Padding, symbolInfo.PaddingZonesCount, symbolInfo.TotalPadding.Value,
960 | symbolInfo.NumInstances, symbolInfo.NumInstances * symbolInfo.Size,
961 | symbolInfo.NumInstances * symbolInfo.Padding);
962 | }
963 | }
964 |
965 | private void CruncherSharpForm_FormClosing(object sender, FormClosingEventArgs e)
966 | {
967 | if (backgroundWorker.IsBusy)
968 | {
969 | e.Cancel = true; // Can't close form while background worker is busy!
970 | backgroundWorker.CancelAsync();
971 | _CloseRequested = true;
972 | }
973 | }
974 |
975 | private void findUnusedVtablesToolStripMenuItem_Click(object sender, EventArgs e)
976 | {
977 | SearchCategory = SearchType.UnusedVTables;
978 |
979 | PopulateDataTable();
980 | }
981 |
982 | private void Reset_Click(object sender, EventArgs e)
983 | {
984 | chkShowTemplates.Checked = true;
985 | chkSmartCacheLines.Checked = true;
986 | for (var i = 0; i < checkedListBoxNamespaces.Items.Count; i++)
987 | checkedListBoxNamespaces.SetItemChecked(i, false);
988 | textBoxFilter.Clear();
989 | SearchCategory = SearchType.None;
990 | PopulateDataTable();
991 | }
992 |
993 | private void findMSVCExtraPaddingToolStripMenuItem_Click(object sender, EventArgs e)
994 | {
995 | SearchCategory = SearchType.MSVCExtraPadding;
996 |
997 | PopulateDataTable();
998 | }
999 |
1000 | private void findMSVCEmptyBaseClassToolStripMenuItem_Click(object sender, EventArgs e)
1001 | {
1002 | SearchCategory = SearchType.MSVCEmptyBaseClass;
1003 |
1004 | PopulateDataTable();
1005 | }
1006 |
1007 | private void findUnusedInterfacesToolStripMenuItem_Click(object sender, EventArgs e)
1008 | {
1009 | SearchCategory = SearchType.UnusedInterfaces;
1010 |
1011 | PopulateDataTable();
1012 | }
1013 |
1014 | private void findUnusedVirtualToolStripMenuItem_Click(object sender, EventArgs e)
1015 | {
1016 | SearchCategory = SearchType.UnusedVirtual;
1017 |
1018 | PopulateDataTable();
1019 | }
1020 |
1021 | private void dataGridViewFunctionsInfo_CellFormatting(object sender, DataGridViewCellFormattingEventArgs e)
1022 | {
1023 | var row = dataGridViewFunctionsInfo.Rows[e.RowIndex];
1024 | var info = row.Tag as SymbolFunctionInfo;
1025 | if (_FunctionsToIgnore.Contains(info.Name))
1026 | e.CellStyle.BackColor = Color.LightGray;
1027 | else
1028 | switch (info.Category)
1029 | {
1030 | case SymbolFunctionInfo.FunctionCategory.Constructor:
1031 | e.CellStyle.BackColor = Color.LightBlue;
1032 | break;
1033 | case SymbolFunctionInfo.FunctionCategory.Destructor:
1034 | e.CellStyle.BackColor = Color.LightGreen;
1035 | break;
1036 | case SymbolFunctionInfo.FunctionCategory.StaticFunction:
1037 | e.CellStyle.BackColor = Color.LightPink;
1038 | break;
1039 | default:
1040 | switch (SearchCategory)
1041 | {
1042 | case SearchType.UnusedVirtual:
1043 | if (info.UnusedVirtual) e.CellStyle.BackColor = Color.IndianRed;
1044 |
1045 | break;
1046 | case SearchType.MaskingFunction:
1047 | if (info.IsMasking) e.CellStyle.BackColor = Color.IndianRed;
1048 |
1049 | break;
1050 | case SearchType.RemovedInline:
1051 | if (info.WasInlineRemoved) e.CellStyle.BackColor = Color.IndianRed;
1052 |
1053 | break;
1054 | }
1055 |
1056 | break;
1057 | }
1058 | }
1059 |
1060 | private void findMaskingFunctionsToolStripMenuItem_Click(object sender, EventArgs e)
1061 | {
1062 | SearchCategory = SearchType.MaskingFunction;
1063 |
1064 | PopulateDataTable();
1065 | }
1066 |
1067 | private void ignoreFunctionToolStripMenuItem_Click(object sender, EventArgs e)
1068 | {
1069 | if (dataGridViewFunctionsInfo.SelectedRows.Count != 0)
1070 | {
1071 | var selectedRow = dataGridViewFunctionsInfo.SelectedRows[0];
1072 | var info = selectedRow.Tag as SymbolFunctionInfo;
1073 | if (!_FunctionsToIgnore.Contains(info.Name))
1074 | {
1075 | _FunctionsToIgnore.Add(info.Name);
1076 | PopulateDataTable();
1077 | }
1078 | }
1079 | }
1080 |
1081 | private void dataGridViewFunctionsInfo_MouseClick(object sender, MouseEventArgs e)
1082 | {
1083 | if (e.Button == MouseButtons.Right)
1084 | {
1085 | var currentMouseOverRow = dataGridViewFunctionsInfo.HitTest(e.X, e.Y).RowIndex;
1086 | if (currentMouseOverRow >= 0)
1087 | {
1088 | dataGridViewFunctionsInfo.Rows[currentMouseOverRow].Selected = true;
1089 | contextMenuStripFunctions.Show(dataGridViewFunctionsInfo, new Point(e.X, e.Y));
1090 | }
1091 | }
1092 | }
1093 |
1094 | private void dataGridSymbols_MouseClick(object sender, MouseEventArgs e)
1095 | {
1096 | if (e.Button == MouseButtons.Right)
1097 | {
1098 | var currentMouseOverRow = dataGridSymbols.HitTest(e.X, e.Y).RowIndex;
1099 | if (currentMouseOverRow >= 0)
1100 | {
1101 | foreach (DataGridViewRow selectedRow in dataGridSymbols.SelectedRows)
1102 | selectedRow.Selected = false;
1103 | dataGridSymbols.Rows[currentMouseOverRow].Selected = true;
1104 |
1105 | toolStripMenuItemParentClasses.DropDownItems.Clear();
1106 | var info = FindSelectedSymbolInfo();
1107 | if (info != null)
1108 | {
1109 | if (info.DerivedClasses != null)
1110 | {
1111 | toolStripMenuItemParentClasses.Enabled = true;
1112 | foreach (var parent in info.DerivedClasses)
1113 | toolStripMenuItemParentClasses.DropDownItems.Add(parent.Name, null,
1114 | dataGridSymbols_ParentSelected);
1115 | }
1116 | else
1117 | {
1118 | toolStripMenuItemParentClasses.Enabled = false;
1119 | }
1120 |
1121 | ShowSymbolInfo(info, false);
1122 | }
1123 |
1124 | contextMenuStripClassInfo.Show(dataGridSymbols, new Point(e.X, e.Y));
1125 | }
1126 | }
1127 | }
1128 |
1129 | private void findRemovedInlineToolStripMenuItem_Click(object sender, EventArgs e)
1130 | {
1131 | SearchCategory = SearchType.RemovedInline;
1132 |
1133 | PopulateDataTable();
1134 | }
1135 |
1136 | private void UpdateCheckedOption()
1137 | {
1138 | findUnusedVtablesToolStripMenuItem.Checked = SearchCategory == SearchType.UnusedVTables;
1139 | findUnusedVirtualToolStripMenuItem.Checked = SearchCategory == SearchType.UnusedVirtual;
1140 | findMSVCExtraPaddingToolStripMenuItem.Checked = SearchCategory == SearchType.MSVCExtraPadding;
1141 | findMSVCEmptyBaseClassToolStripMenuItem.Checked = SearchCategory == SearchType.MSVCEmptyBaseClass;
1142 | findUnusedInterfacesToolStripMenuItem.Checked = SearchCategory == SearchType.UnusedInterfaces;
1143 | findMaskingFunctionsToolStripMenuItem.Checked = SearchCategory == SearchType.MaskingFunction;
1144 | findRemovedInlineToolStripMenuItem.Checked = SearchCategory == SearchType.RemovedInline;
1145 | }
1146 |
1147 | private void PopulateDataTable()
1148 | {
1149 | _Table.Rows.Clear();
1150 |
1151 | _Table.BeginLoadData();
1152 |
1153 | foreach (var symbolInfo in _SymbolAnalyzer.Symbols.Values)
1154 | switch (SearchCategory)
1155 | {
1156 | case SearchType.None:
1157 | AddSymbolToTable(symbolInfo);
1158 | break;
1159 | case SearchType.UnusedVTables:
1160 | if (symbolInfo.HasVtable && !symbolInfo.HasBaseClass && symbolInfo.DerivedClasses == null)
1161 | AddSymbolToTable(symbolInfo);
1162 | break;
1163 | case SearchType.MSVCExtraPadding:
1164 | if (symbolInfo.HasMSVCExtraPadding) AddSymbolToTable(symbolInfo);
1165 | break;
1166 | case SearchType.MSVCEmptyBaseClass:
1167 | if (symbolInfo.HasMSVCEmptyBaseClass) AddSymbolToTable(symbolInfo);
1168 | break;
1169 | case SearchType.UnusedInterfaces:
1170 | if (symbolInfo.IsAbstract && symbolInfo.DerivedClasses == null) AddSymbolToTable(symbolInfo);
1171 | break;
1172 | case SearchType.UnusedVirtual:
1173 | foreach (var function in symbolInfo.Functions)
1174 | if (function.UnusedVirtual)
1175 | if (!_FunctionsToIgnore.Contains(function.Name))
1176 | {
1177 | AddSymbolToTable(symbolInfo);
1178 | break;
1179 | }
1180 |
1181 | break;
1182 | case SearchType.MaskingFunction:
1183 | foreach (var function in symbolInfo.Functions)
1184 | if (function.IsMasking)
1185 | if (!_FunctionsToIgnore.Contains(function.Name))
1186 | {
1187 | AddSymbolToTable(symbolInfo);
1188 | break;
1189 | }
1190 |
1191 | break;
1192 | case SearchType.RemovedInline:
1193 | foreach (var function in symbolInfo.Functions)
1194 | if (function.WasInlineRemoved)
1195 | if (!_FunctionsToIgnore.Contains(function.Name))
1196 | {
1197 | AddSymbolToTable(symbolInfo);
1198 | break;
1199 | }
1200 |
1201 | break;
1202 | }
1203 |
1204 | _Table.EndLoadData();
1205 | }
1206 |
1207 | private void btnLoad_Click(object sender, EventArgs e)
1208 | {
1209 | if (backgroundWorker.IsBusy)
1210 | {
1211 | backgroundWorker.CancelAsync();
1212 | toolStripStatusLabel.Text = "Cancelling...";
1213 | }
1214 | else
1215 | {
1216 | LoadPdb(_FileName, false);
1217 | btnLoad.Text = "Cancel";
1218 | }
1219 | }
1220 |
1221 | private void checkBoxMatchCase_CheckedChanged(object sender, EventArgs e)
1222 | {
1223 | _Table.CaseSensitive = checkBoxMatchCase.Checked;
1224 | bindingSourceSymbols.Filter = GetFilterString();
1225 | }
1226 |
1227 | private void checkBoxMatchWholeExpression_CheckedChanged(object sender, EventArgs e)
1228 | {
1229 | bindingSourceSymbols.Filter = GetFilterString();
1230 | }
1231 |
1232 | private void checkBoxRegularExpressions_CheckedChanged(object sender, EventArgs e)
1233 | {
1234 | if (checkBoxRegularExpressions.Checked)
1235 | {
1236 | checkBoxMatchWholeExpression.Checked = false;
1237 | checkBoxMatchWholeExpression.Enabled = false;
1238 | }
1239 | else
1240 | {
1241 | checkBoxMatchWholeExpression.Enabled = true;
1242 | }
1243 |
1244 | bindingSourceSymbols.Filter = GetFilterString();
1245 | }
1246 |
1247 | private void checkBoxPadding_CheckedChanged(object sender, EventArgs e)
1248 | {
1249 | if (dataGridViewSymbolInfo.SelectedRows.Count != 0)
1250 | {
1251 | var selectedRow = dataGridViewSymbolInfo.SelectedRows[0];
1252 | RefreshSymbolGrid(selectedRow.Index);
1253 | }
1254 | else
1255 | {
1256 | RefreshSymbolGrid(0);
1257 | }
1258 | }
1259 |
1260 | private void checkBoxBitPadding_CheckedChanged(object sender, EventArgs e)
1261 | {
1262 | if (dataGridViewSymbolInfo.SelectedRows.Count != 0)
1263 | {
1264 | var selectedRow = dataGridViewSymbolInfo.SelectedRows[0];
1265 | RefreshSymbolGrid(selectedRow.Index);
1266 | }
1267 | else
1268 | {
1269 | RefreshSymbolGrid(0);
1270 | }
1271 | }
1272 | }
1273 | }
--------------------------------------------------------------------------------
/License.txt:
--------------------------------------------------------------------------------
1 | Copyright (c) <2009-2015> Maciej Sinilo
2 |
3 | This software is provided 'as-is', without any express or implied
4 | warranty. In no event will the authors be held liable for any damages
5 | arising from the use of this software.
6 |
7 | Permission is granted to anyone to use this software for any purpose,
8 | including commercial applications, and to alter it and redistribute it
9 | freely, subject to the following restrictions:
10 |
11 | 1. The origin of this software must not be misrepresented; you must not
12 | claim that you wrote the original software. If you use this software
13 | in a product, an acknowledgement in the product documentation would be
14 | appreciated but is not required.
15 | 2. Altered source versions must be plainly marked as such, and must not be
16 | misrepresented as being the original software.
17 | 3. This notice may not be removed or altered from any source distribution.
18 |
--------------------------------------------------------------------------------
/Program.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Windows.Forms;
3 |
4 | namespace CruncherSharp
5 | {
6 | static class Program
7 | {
8 | ///
9 | /// The main entry point for the application.
10 | ///
11 | [STAThread]
12 | static void Main(string[] args)
13 | {
14 | Application.EnableVisualStyles();
15 | Application.SetCompatibleTextRenderingDefault(false);
16 | var mainForm = new CruncherSharpForm(new SymbolAnalyzer());
17 | if (args.Length > 0)
18 | {
19 | // Let's assume our arg is indeed the full path of a pdb file...
20 | mainForm.LoadPdb(args[0], false);
21 | }
22 | Application.Run(mainForm);
23 | }
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/Properties/AssemblyInfo.cs:
--------------------------------------------------------------------------------
1 | using System.Reflection;
2 | using System.Runtime.CompilerServices;
3 | using System.Runtime.InteropServices;
4 |
5 | // General Information about an assembly is controlled through the following
6 | // set of attributes. Change these attribute values to modify the information
7 | // associated with an assembly.
8 | [assembly: AssemblyTitle("CruncherSharp")]
9 | [assembly: AssemblyDescription("")]
10 | [assembly: AssemblyConfiguration("")]
11 | [assembly: AssemblyCompany("")]
12 | [assembly: AssemblyProduct("CruncherSharp")]
13 | [assembly: AssemblyCopyright("Copyright © 2019")]
14 | [assembly: AssemblyTrademark("")]
15 | [assembly: AssemblyCulture("")]
16 |
17 | // Setting ComVisible to false makes the types in this assembly not visible
18 | // to COM components. If you need to access a type in this assembly from
19 | // COM, set the ComVisible attribute to true on that type.
20 | [assembly: ComVisible(false)]
21 |
22 | // The following GUID is for the ID of the typelib if this project is exposed to COM
23 | [assembly: Guid("6c1145d8-9bef-43e5-9629-f40de7d13e66")]
24 |
25 | // Version information for an assembly consists of the following four values:
26 | //
27 | // Major Version
28 | // Minor Version
29 | // Build Number
30 | // Revision
31 | //
32 | // You can specify all the values or you can default the Build and Revision Numbers
33 | // by using the '*' as shown below:
34 | // [assembly: AssemblyVersion("1.0.*")]
35 | [assembly: AssemblyVersion("1.0.0.0")]
36 | [assembly: AssemblyFileVersion("1.0.0.0")]
37 |
--------------------------------------------------------------------------------
/Properties/Resources.Designer.cs:
--------------------------------------------------------------------------------
1 | //------------------------------------------------------------------------------
2 | //
3 | // This code was generated by a tool.
4 | // Runtime Version:4.0.30319.42000
5 | //
6 | // Changes to this file may cause incorrect behavior and will be lost if
7 | // the code is regenerated.
8 | //
9 | //------------------------------------------------------------------------------
10 |
11 | namespace CruncherSharp.Properties {
12 | using System;
13 |
14 |
15 | ///
16 | /// A strongly-typed resource class, for looking up localized strings, etc.
17 | ///
18 | // This class was auto-generated by the StronglyTypedResourceBuilder
19 | // class via a tool like ResGen or Visual Studio.
20 | // To add or remove a member, edit your .ResX file then rerun ResGen
21 | // with the /str option, or rebuild your VS project.
22 | [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "15.0.0.0")]
23 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
24 | [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
25 | internal class Resources {
26 |
27 | private static global::System.Resources.ResourceManager resourceMan;
28 |
29 | private static global::System.Globalization.CultureInfo resourceCulture;
30 |
31 | [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
32 | internal Resources() {
33 | }
34 |
35 | ///
36 | /// Returns the cached ResourceManager instance used by this class.
37 | ///
38 | [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
39 | internal static global::System.Resources.ResourceManager ResourceManager {
40 | get {
41 | if (object.ReferenceEquals(resourceMan, null)) {
42 | global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("CruncherSharp.Properties.Resources", typeof(Resources).Assembly);
43 | resourceMan = temp;
44 | }
45 | return resourceMan;
46 | }
47 | }
48 |
49 | ///
50 | /// Overrides the current thread's CurrentUICulture property for all
51 | /// resource lookups using this strongly typed resource class.
52 | ///
53 | [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
54 | internal static global::System.Globalization.CultureInfo Culture {
55 | get {
56 | return resourceCulture;
57 | }
58 | set {
59 | resourceCulture = value;
60 | }
61 | }
62 | }
63 | }
64 |
--------------------------------------------------------------------------------
/Properties/Resources.resx:
--------------------------------------------------------------------------------
1 |
2 |
3 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 | text/microsoft-resx
107 |
108 |
109 | 2.0
110 |
111 |
112 | System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
113 |
114 |
115 | System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
116 |
117 |
--------------------------------------------------------------------------------
/Properties/Settings.Designer.cs:
--------------------------------------------------------------------------------
1 | //------------------------------------------------------------------------------
2 | //
3 | // This code was generated by a tool.
4 | // Runtime Version:4.0.30319.42000
5 | //
6 | // Changes to this file may cause incorrect behavior and will be lost if
7 | // the code is regenerated.
8 | //
9 | //------------------------------------------------------------------------------
10 |
11 | namespace CruncherSharp.Properties {
12 |
13 |
14 | [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
15 | [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "15.9.0.0")]
16 | internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase {
17 |
18 | private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings())));
19 |
20 | public static Settings Default {
21 | get {
22 | return defaultInstance;
23 | }
24 | }
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/Properties/Settings.settings:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # crunchersharp
2 | Program analyses debugger information file (PDB, so Microsoft Visual C++ only) and presents info about user defined structures (size, padding, cachelines, functions etc).
3 |
4 | - You can filter by namespace, search for a specific symbol
5 | - You can import a .csv with the instance count to get the total waste, the format should be "Class Name, Number of instances"
6 | - You can compare two PDBs
7 | - You search for useless vtables, useless virtual...
8 |
9 | Original blog post: http://msinilo.pl/blog/?p=425
10 |
11 | Note that you will need the `msdia` classes to be registered. To do this:
12 |
13 | 1) Find the msdia DLL corresponding to the version of the compiler you used to build the application.
14 | -- If you have Visual Studio installed, this DLL can be found in "C:\Program Files (x86)\Microsoft Visual Studio \Common7\IDE", where corresponds to your compiler version (e.g. "12.0" for Microsoft Visual Studio 2013)
15 | -- If you don't have the compiler installed, download the appropriate "Microsoft Visual C++ Redistributable Package" and install it.
16 |
17 | 2) Open an elevated (admin) command prompt in the directory containing msdia.dll.
18 |
19 | 3) Manually register the DLL by typing "regsvr32 msdia.dll" (e.g. "regsvr32 msdia12.dll" for Visual Studio 2013)
20 |
21 | 
22 |
--------------------------------------------------------------------------------
/Screenshot.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/msinilo/crunchersharp/9345e140bfd6f5250336f83a9843baf88116d1e6/Screenshot.png
--------------------------------------------------------------------------------
/SelectNamespace.Designer.cs:
--------------------------------------------------------------------------------
1 | namespace CruncherSharp
2 | {
3 | partial class SelectNamespace
4 | {
5 | ///
6 | /// Required designer variable.
7 | ///
8 | private System.ComponentModel.IContainer components = null;
9 |
10 | ///
11 | /// Clean up any resources being used.
12 | ///
13 | /// true if managed resources should be disposed; otherwise, false.
14 | protected override void Dispose(bool disposing)
15 | {
16 | if (disposing && (components != null))
17 | {
18 | components.Dispose();
19 | }
20 | base.Dispose(disposing);
21 | }
22 |
23 | #region Windows Form Designer generated code
24 |
25 | ///
26 | /// Required method for Designer support - do not modify
27 | /// the contents of this method with the code editor.
28 | ///
29 | private void InitializeComponent()
30 | {
31 | this.checkedListBoxNamespaces = new System.Windows.Forms.CheckedListBox();
32 | this.buttonOK = new System.Windows.Forms.Button();
33 | this.SuspendLayout();
34 | //
35 | // checkedListBoxNamespaces
36 | //
37 | this.checkedListBoxNamespaces.FormattingEnabled = true;
38 | this.checkedListBoxNamespaces.Location = new System.Drawing.Point(-1, 0);
39 | this.checkedListBoxNamespaces.Name = "checkedListBoxNamespaces";
40 | this.checkedListBoxNamespaces.Size = new System.Drawing.Size(344, 274);
41 | this.checkedListBoxNamespaces.TabIndex = 0;
42 | //
43 | // buttonOK
44 | //
45 | this.buttonOK.Location = new System.Drawing.Point(230, 292);
46 | this.buttonOK.Name = "buttonOK";
47 | this.buttonOK.Size = new System.Drawing.Size(75, 23);
48 | this.buttonOK.TabIndex = 1;
49 | this.buttonOK.Text = "OK";
50 | this.buttonOK.UseVisualStyleBackColor = true;
51 | //
52 | // SelectNamespace
53 | //
54 | this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
55 | this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
56 | this.ClientSize = new System.Drawing.Size(346, 327);
57 | this.Controls.Add(this.buttonOK);
58 | this.Controls.Add(this.checkedListBoxNamespaces);
59 | this.Name = "SelectNamespace";
60 | this.Text = "Select namespace";
61 | this.ResumeLayout(false);
62 |
63 | }
64 |
65 | #endregion
66 |
67 | private System.Windows.Forms.CheckedListBox checkedListBoxNamespaces;
68 | private System.Windows.Forms.Button buttonOK;
69 | }
70 | }
--------------------------------------------------------------------------------
/SelectNamespace.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.ComponentModel;
4 | using System.Data;
5 | using System.Drawing;
6 | using System.Linq;
7 | using System.Text;
8 | using System.Threading.Tasks;
9 | using System.Windows.Forms;
10 |
11 | namespace CruncherSharp
12 | {
13 | public partial class SelectNamespace : Form
14 | {
15 | public SelectNamespace()
16 | {
17 | InitializeComponent();
18 | }
19 |
20 | public void SetName(String className)
21 | {
22 | Name = "Select namespace for " + className;
23 | }
24 |
25 | public string GetNamespace()
26 | {
27 | if (checkedListBoxNamespaces.CheckedItems.Count == 0)
28 | return "";
29 | return checkedListBoxNamespaces.CheckedItems[0].ToString();
30 | }
31 |
32 | public void AddNamespaces(List namespaces)
33 | {
34 | foreach (var name in namespaces)
35 | checkedListBoxNamespaces.Items.Add(name);
36 | }
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/SelectNamespaceForm.Designer.cs:
--------------------------------------------------------------------------------
1 | namespace CruncherSharp
2 | {
3 | partial class SelectNamespaceForm
4 | {
5 | ///
6 | /// Required designer variable.
7 | ///
8 | private System.ComponentModel.IContainer components = null;
9 |
10 | ///
11 | /// Clean up any resources being used.
12 | ///
13 | /// true if managed resources should be disposed; otherwise, false.
14 | protected override void Dispose(bool disposing)
15 | {
16 | if (disposing && (components != null))
17 | {
18 | components.Dispose();
19 | }
20 | base.Dispose(disposing);
21 | }
22 |
23 | #region Windows Form Designer generated code
24 |
25 | ///
26 | /// Required method for Designer support - do not modify
27 | /// the contents of this method with the code editor.
28 | ///
29 | private void InitializeComponent()
30 | {
31 | this.checkedListBoxNamespaces = new System.Windows.Forms.CheckedListBox();
32 | this.buttonSkip = new System.Windows.Forms.Button();
33 | this.buttonSkipAll = new System.Windows.Forms.Button();
34 | this.labelSelectNamespace = new System.Windows.Forms.Label();
35 | this.SuspendLayout();
36 | //
37 | // checkedListBoxNamespaces
38 | //
39 | this.checkedListBoxNamespaces.FormattingEnabled = true;
40 | this.checkedListBoxNamespaces.Location = new System.Drawing.Point(11, 44);
41 | this.checkedListBoxNamespaces.Name = "checkedListBoxNamespaces";
42 | this.checkedListBoxNamespaces.Size = new System.Drawing.Size(430, 229);
43 | this.checkedListBoxNamespaces.TabIndex = 0;
44 | this.checkedListBoxNamespaces.ItemCheck += new System.Windows.Forms.ItemCheckEventHandler(this.checkedListBoxNamespaces_ItemCheck);
45 | //
46 | // buttonSkip
47 | //
48 | this.buttonSkip.Location = new System.Drawing.Point(285, 300);
49 | this.buttonSkip.Name = "buttonSkip";
50 | this.buttonSkip.Size = new System.Drawing.Size(75, 23);
51 | this.buttonSkip.TabIndex = 1;
52 | this.buttonSkip.Text = "Skip";
53 | this.buttonSkip.UseVisualStyleBackColor = true;
54 | this.buttonSkip.Click += new System.EventHandler(this.buttonSkip_Click);
55 | //
56 | // buttonSkipAll
57 | //
58 | this.buttonSkipAll.Location = new System.Drawing.Point(366, 300);
59 | this.buttonSkipAll.Name = "buttonSkipAll";
60 | this.buttonSkipAll.Size = new System.Drawing.Size(75, 23);
61 | this.buttonSkipAll.TabIndex = 2;
62 | this.buttonSkipAll.Text = "Skip all";
63 | this.buttonSkipAll.UseVisualStyleBackColor = true;
64 | this.buttonSkipAll.Click += new System.EventHandler(this.buttonSkipAll_Click);
65 | //
66 | // labelSelectNamespace
67 | //
68 | this.labelSelectNamespace.AutoSize = true;
69 | this.labelSelectNamespace.Location = new System.Drawing.Point(13, 13);
70 | this.labelSelectNamespace.Name = "labelSelectNamespace";
71 | this.labelSelectNamespace.Size = new System.Drawing.Size(95, 13);
72 | this.labelSelectNamespace.TabIndex = 3;
73 | this.labelSelectNamespace.Text = "Select namespace";
74 | //
75 | // SelectNamespaceForm
76 | //
77 | this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
78 | this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
79 | this.AutoSize = true;
80 | this.ClientSize = new System.Drawing.Size(453, 334);
81 | this.Controls.Add(this.labelSelectNamespace);
82 | this.Controls.Add(this.buttonSkipAll);
83 | this.Controls.Add(this.buttonSkip);
84 | this.Controls.Add(this.checkedListBoxNamespaces);
85 | this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedDialog;
86 | this.Name = "SelectNamespaceForm";
87 | this.StartPosition = System.Windows.Forms.FormStartPosition.CenterParent;
88 | this.Text = "Resolve namespace";
89 | this.ResumeLayout(false);
90 | this.PerformLayout();
91 |
92 | }
93 |
94 | #endregion
95 |
96 | private System.Windows.Forms.CheckedListBox checkedListBoxNamespaces;
97 | private System.Windows.Forms.Button buttonSkip;
98 | private System.Windows.Forms.Button buttonSkipAll;
99 | private System.Windows.Forms.Label labelSelectNamespace;
100 | }
101 | }
--------------------------------------------------------------------------------
/SelectNamespaceForm.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.ComponentModel;
4 | using System.Data;
5 | using System.Drawing;
6 | using System.Linq;
7 | using System.Text;
8 | using System.Threading.Tasks;
9 | using System.Windows.Forms;
10 |
11 | namespace CruncherSharp
12 | {
13 | public partial class SelectNamespaceForm : Form
14 | {
15 | public bool SkipAllNamespaces { get; set; }
16 |
17 | public SelectNamespaceForm()
18 | {
19 | InitializeComponent();
20 | SkipAllNamespaces = false;
21 | }
22 |
23 | public void SetName(String className, ulong count)
24 | {
25 | this.labelSelectNamespace.Text = "Select namespace for " + className + " (" + count + " instances)";
26 | }
27 |
28 | public string GetNamespace()
29 | {
30 | if (checkedListBoxNamespaces.CheckedItems.Count == 0)
31 | return "";
32 | return checkedListBoxNamespaces.CheckedItems[0].ToString();
33 | }
34 |
35 | public void AddNamespaces(List namespaces)
36 | {
37 | foreach (var name in namespaces)
38 | checkedListBoxNamespaces.Items.Add(name);
39 | }
40 |
41 | private void checkedListBoxNamespaces_ItemCheck(object sender, ItemCheckEventArgs e)
42 | {
43 | Close();
44 | }
45 |
46 | private void buttonSkip_Click(object sender, EventArgs e)
47 | {
48 | Close();
49 | }
50 |
51 | private void buttonSkipAll_Click(object sender, EventArgs e)
52 | {
53 | SkipAllNamespaces = true;
54 | Close();
55 | }
56 | }
57 | }
58 |
--------------------------------------------------------------------------------
/SelectNamespaceForm.resx:
--------------------------------------------------------------------------------
1 |
2 |
3 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 |
107 |
108 |
109 | text/microsoft-resx
110 |
111 |
112 | 2.0
113 |
114 |
115 | System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
116 |
117 |
118 | System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
119 |
120 |
--------------------------------------------------------------------------------
/SymbolAnalyzer.cs:
--------------------------------------------------------------------------------
1 | using Dia2Lib;
2 | using System;
3 | using System.Collections.Generic;
4 | using System.ComponentModel;
5 |
6 | namespace CruncherSharp
7 | {
8 | public class LoadPDBTask
9 | {
10 | public string FileName { get; set; }
11 | public string Filter { get; set; }
12 | public bool SecondPDB { get; set; }
13 | public bool MatchCase { get; set; }
14 | public bool WholeExpression { get; set; }
15 | public bool UseRegularExpression { get; set; }
16 | }
17 |
18 | public class SymbolAnalyzer
19 | {
20 | public Dictionary Symbols { get; }
21 | public SortedSet RootNamespaces { get; }
22 | public SortedSet Namespaces { get; }
23 | public string LastError { get; private set; }
24 | public string FileName { get; set; }
25 |
26 | public SymbolAnalyzer()
27 | {
28 | Symbols = new Dictionary();
29 | RootNamespaces = new SortedSet();
30 | Namespaces = new SortedSet();
31 | LastError = string.Empty;
32 | }
33 |
34 | public void Reset()
35 | {
36 | RootNamespaces.Clear();
37 | Namespaces.Clear();
38 | Symbols.Clear();
39 | LastError = string.Empty;
40 | FileName = null;
41 | }
42 |
43 | public bool LoadPdb(object sender, DoWorkEventArgs e)
44 | {
45 | try
46 | {
47 | var task = e.Argument as LoadPDBTask;
48 | FileName = task.FileName;
49 | if (!LoadPdb(sender, task))
50 | {
51 | e.Cancel = true;
52 | return false;
53 | }
54 | return true;
55 | }
56 | catch (System.Runtime.InteropServices.COMException exception)
57 | {
58 | LastError = exception.ToString();
59 | return false;
60 | }
61 | }
62 |
63 | private bool LoadPdb(object sender, LoadPDBTask task)
64 | {
65 | IDiaDataSource source = new DiaSourceClass();
66 | source.loadDataFromPdb(FileName);
67 | source.openSession(out IDiaSession session);
68 | if (!LoadSymbols(session, sender, task))
69 | return false;
70 | RunAnalysis();
71 | return true;
72 | }
73 |
74 | private bool LoadSymbols(IDiaSession session, object sender, LoadPDBTask task)
75 | {
76 | var worker = sender as BackgroundWorker;
77 | worker?.ReportProgress(0, "Finding symbols");
78 |
79 | IDiaEnumSymbols allSymbols;
80 |
81 | if (task.Filter.Length > 0)
82 | {
83 | uint compareFlags = 0;
84 | if (!task.WholeExpression || task.UseRegularExpression)
85 | compareFlags |= 0x8;
86 | if (!task.MatchCase)
87 | compareFlags |= 0x2;
88 | else
89 | compareFlags |= 0x2;
90 |
91 | if (task.UseRegularExpression)
92 | session.findChildren(session.globalScope, SymTagEnum.SymTagUDT, @task.Filter, compareFlags, out allSymbols);
93 | else if (task.WholeExpression)
94 | session.findChildren(session.globalScope, SymTagEnum.SymTagUDT, task.Filter, compareFlags, out allSymbols);
95 | else
96 | {
97 | string filter = '*' + task.Filter + '*';
98 | session.findChildren(session.globalScope, SymTagEnum.SymTagUDT, @filter, compareFlags, out allSymbols);
99 | }
100 | }
101 | else
102 | {
103 | session.findChildren(session.globalScope, SymTagEnum.SymTagUDT, null, 0, out allSymbols);
104 | }
105 |
106 | if (allSymbols == null)
107 | return false;
108 |
109 | worker?.ReportProgress(0, "Counting symbols");
110 |
111 | var allSymbolsCount = worker != null ? allSymbols.count : 0;
112 | var i = 0;
113 |
114 | worker?.ReportProgress(0, "Adding symbols");
115 |
116 | foreach (IDiaSymbol sym in allSymbols)
117 | {
118 | if (worker != null && worker.CancellationPending)
119 | {
120 | return false;
121 | }
122 |
123 | if ( task.SecondPDB)
124 | {
125 | SymbolInfo info = FindSymbolInfo(sym.name);
126 | if (info != null)
127 | {
128 | info.NewSize = sym.length;
129 | }
130 | }
131 | else
132 | {
133 | if (sym.length > 0 && !HasSymbolInfo(sym.name))
134 | {
135 | var symbolInfo = new SymbolInfo(sym.name, sym.GetType().Name, sym.length);
136 | symbolInfo.ProcessChildren(sym);
137 | Symbols.Add(symbolInfo.Name, symbolInfo);
138 |
139 | if (symbolInfo.Name.Contains("::") && !symbolInfo.Name.Contains("<"))
140 | {
141 | RootNamespaces.Add(symbolInfo.Name.Substring(0, symbolInfo.Name.IndexOf("::")));
142 | Namespaces.Add(symbolInfo.Name.Substring(0, symbolInfo.Name.LastIndexOf("::")));
143 | }
144 | }
145 | }
146 | var percentProgress = (int)Math.Round((double)(100 * i++) / allSymbolsCount);
147 | percentProgress = Math.Max(Math.Min(percentProgress, 99), 1);
148 | worker?.ReportProgress(percentProgress, String.Format("Adding symbol {0} on {1}", i, allSymbolsCount));
149 | }
150 |
151 |
152 | worker?.ReportProgress(100, String.Format("{0} symbols added", allSymbolsCount));
153 |
154 | return true;
155 | }
156 |
157 | private void RunAnalysis()
158 | {
159 | foreach (var symbol in Symbols.Values)
160 | {
161 | symbol.ComputeTotalPadding(this);
162 | symbol.UpdateBaseClass(this);
163 | }
164 |
165 | foreach (var symbol in Symbols.Values)
166 | {
167 | symbol.CheckOverride();
168 | symbol.CheckMasking();
169 | }
170 | }
171 |
172 |
173 | public bool HasSymbolInfo(string name)
174 | {
175 | return Symbols.ContainsKey(name);
176 | }
177 |
178 | public SymbolInfo FindSymbolInfo(string name, bool loadMissingSymbol = false)
179 | {
180 | if (!HasSymbolInfo(name) && name.Length > 0)
181 | {
182 | if (!loadMissingSymbol)
183 | return null;
184 | var task = new LoadPDBTask
185 | {
186 | FileName = FileName,
187 | SecondPDB = false,
188 | Filter = name,
189 | MatchCase = true,
190 | WholeExpression = true,
191 | UseRegularExpression = false
192 | };
193 |
194 | if (!LoadPdb(null, task))
195 | return null;
196 | }
197 |
198 | Symbols.TryGetValue(name, out var symbolInfo);
199 | return symbolInfo;
200 | }
201 | }
202 | }
203 |
--------------------------------------------------------------------------------
/SymbolFunctionInfo.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Text;
5 | using System.Threading.Tasks;
6 |
7 | namespace CruncherSharp
8 | {
9 | public class SymbolFunctionInfo
10 | {
11 | public enum FunctionCategory
12 | {
13 | Function,
14 | StaticFunction,
15 | Constructor,
16 | Destructor
17 | }
18 |
19 | public string Name { get; set; }
20 | public string DisplayName
21 | {
22 | get
23 | {
24 | switch (Category)
25 | {
26 | case FunctionCategory.StaticFunction:
27 | return $"static {Name}";
28 | default:
29 | return Name + (IsConst ? " const" : "");
30 | }
31 | }
32 | }
33 |
34 | public bool UnusedVirtual
35 | {
36 | get
37 | {
38 | return Category == FunctionCategory.Function && Virtual && !IsOverloaded && !IsOverride;
39 | }
40 | }
41 |
42 | public FunctionCategory Category { get; set; }
43 |
44 | public bool Virtual { get; set; }
45 | public bool IsPure { get; set; }
46 | public bool IsOverride { get; set; }
47 | public bool IsOverloaded { get; set; }
48 | public bool IsMasking { get; set; }
49 | public bool IsConst { get; set; }
50 | public bool WasInlineRemoved { get; set; }
51 |
52 | }
53 | }
54 |
--------------------------------------------------------------------------------
/SymbolInfo.cs:
--------------------------------------------------------------------------------
1 | using Dia2Lib;
2 | using System;
3 | using System.Collections.Generic;
4 | using System.IO;
5 | using System.Linq;
6 |
7 | namespace CruncherSharp
8 | {
9 | public class SymbolInfo
10 | {
11 | public string Name { get; private set; }
12 | public string TypeName { get; set; }
13 | public ulong Size { get; set; }
14 | public ulong NewSize { get; set; }
15 | public ulong EndPadding { get; set; }
16 | public ulong Padding => (ulong)((long)EndPadding + Members.Sum(info => (long)info.PaddingBefore)); // This is the local (intrinsic) padding
17 | public ulong PaddingZonesCount => (ulong)((EndPadding > 0 ? 1 : 0) + Members.Sum(info => info.PaddingBefore > 0 ? 1 : 0));
18 | public ulong? TotalPadding { get; set; } // Includes padding from base classes and members
19 | public ulong NumInstances { get; set; }
20 | public ulong TotalCount { get; set; }
21 | public bool IsAbstract { get; set; }
22 | public bool IsTemplate { get; set; }
23 | public List Members { get; set; }
24 | public List Functions { get; set; }
25 | public List DerivedClasses { get; set; }
26 |
27 | private const string PaddingMarker = "****Padding";
28 |
29 | public SymbolInfo(string name, string typeName, ulong size)
30 | {
31 | Name = name;
32 | TypeName = typeName;
33 | Size = size;
34 | EndPadding = 0;
35 | TotalPadding = null;
36 | Members = new List();
37 | Functions = new List();
38 | IsAbstract = false;
39 | if (Name.Contains("<") && Name.Contains(">"))
40 | IsTemplate = true;
41 | }
42 |
43 | private void AddMember(SymbolMemberInfo member)
44 | {
45 | Members.Add(member);
46 | }
47 |
48 | private void AddFunction(SymbolFunctionInfo function)
49 | {
50 | Functions.Add(function);
51 | }
52 |
53 | private bool ComputeOffsetCollision(int index)
54 | {
55 | return index > 0 && Members[index].Offset == Members[index - 1].Offset;
56 | }
57 |
58 | public bool HasVtable
59 | {
60 | get
61 | {
62 | foreach (var member in Members)
63 | {
64 | if (member.Category == SymbolMemberInfo.MemberCategory.VTable)
65 | {
66 | return true;
67 | }
68 | }
69 | return false;
70 | }
71 | }
72 |
73 | public bool HasBaseClass
74 | {
75 | get
76 | {
77 | foreach (var member in Members)
78 | {
79 | if (member.Category == SymbolMemberInfo.MemberCategory.Base)
80 | {
81 | return true;
82 | }
83 | }
84 | return false;
85 | }
86 | }
87 |
88 | public bool HasMSVCExtraPadding
89 | {
90 | get
91 | {
92 | if (HasBaseClass)
93 | return false;
94 | if (!HasVtable)
95 | return false;
96 | if (Members.Count < 2)
97 | return false;
98 | return Members[1].Size == 8 && Members[1].Offset == 16;
99 | }
100 | }
101 |
102 | public bool HasMSVCEmptyBaseClass
103 | {
104 | get
105 | {
106 | var n = 1;
107 | while (n + 2 < Members.Count)
108 | {
109 | if (Members[n - 1].IsBase && Members[n].IsBase && Members[n].Size == 1 && Members[n].Offset > Members[n - 1].Offset && Members[n + 1].Offset > Members[n].Offset)
110 | return true;
111 | n++;
112 | }
113 | return false;
114 |
115 | }
116 | }
117 |
118 | private ulong ComputePadding(int index)
119 | {
120 | if (index < 1 || index > Members.Count)
121 | {
122 | return 0;
123 | }
124 | if (index < Members.Count && Members[index].AlignWithPrevious)
125 | {
126 | return 0;
127 | }
128 | int previousIndex = index - 1;
129 | ulong biggestSize = Members[previousIndex].Size;
130 | while (Members[previousIndex].AlignWithPrevious && previousIndex > 0)
131 | {
132 | previousIndex--;
133 | if (biggestSize < Members[previousIndex].Size)
134 | biggestSize = Members[previousIndex].Size;
135 | }
136 |
137 | ulong currentOffset = index > Members.Count - 1 ? Size : Members[index].Offset;
138 | ulong previousEnd = Members[previousIndex].Offset + biggestSize;
139 | return currentOffset > previousEnd ? currentOffset - previousEnd : 0;
140 | }
141 |
142 | private ulong ComputeBitPadding(int index)
143 | {
144 | if (index > Members.Count)
145 | {
146 | return 0;
147 | }
148 | if (!Members[index].BitField)
149 | {
150 | return 0;
151 | }
152 | if (index + 1 < Members.Count)
153 | {
154 | if (Members[index + 1].BitField && Members[index + 1].Offset == Members[index].Offset)
155 | return 0;
156 | }
157 | return (8 * Members[index].Size) - (Members[index].BitPosition + Members[index].BitSize);
158 | }
159 |
160 | private ulong ComputeEndPadding()
161 | {
162 | return ComputePadding(Members.Count);
163 | }
164 |
165 | public ulong ComputeTotalPadding(SymbolAnalyzer symbolAnalyzer)
166 | {
167 | if (TotalPadding.HasValue)
168 | {
169 | return TotalPadding.Value;
170 | }
171 | TotalPadding = (ulong)((long)Padding + Members.Sum(info =>
172 | {
173 | if (info.AlignWithPrevious)
174 | return 0;
175 | if (info.Category == SymbolMemberInfo.MemberCategory.Member)
176 | return 0;
177 | if (info.TypeName == Name)
178 | return 0; // avoid infinite loops
179 | var referencedInfo = symbolAnalyzer.FindSymbolInfo(info.TypeName);
180 | if (referencedInfo == null)
181 | {
182 | return 0;
183 | }
184 | return (long)referencedInfo.ComputeTotalPadding(symbolAnalyzer);
185 | }));
186 | return TotalPadding.Value;
187 | }
188 |
189 | public void UpdateBaseClass(SymbolAnalyzer symbolAnalyzer)
190 | {
191 | foreach (var member in Members)
192 | {
193 | if (member.Category == SymbolMemberInfo.MemberCategory.Base)
194 | {
195 | var referencedInfo = symbolAnalyzer.FindSymbolInfo(member.Name);
196 | if (referencedInfo != null)
197 | {
198 | if (referencedInfo.DerivedClasses == null)
199 | referencedInfo.DerivedClasses = new List();
200 | referencedInfo.DerivedClasses.Add(this);
201 | }
202 | }
203 | }
204 | }
205 |
206 | public void CheckOverride()
207 | {
208 | foreach (var function in Functions)
209 | {
210 | if (function.Virtual && function.Category == SymbolFunctionInfo.FunctionCategory.Function && DerivedClasses != null)
211 | {
212 | foreach(var derivedClass in DerivedClasses)
213 | {
214 | if (derivedClass.IsOverloadingFunction(function))
215 | {
216 | function.IsOverloaded = true;
217 | break;
218 | }
219 | }
220 | }
221 | }
222 | }
223 |
224 | public void CheckMasking()
225 | {
226 | foreach (var function in Functions)
227 | {
228 | if (function.Virtual == false && function.Category == SymbolFunctionInfo.FunctionCategory.Function && DerivedClasses != null)
229 | {
230 | foreach (var derivedClass in DerivedClasses)
231 | {
232 | derivedClass.CheckMasking(function);
233 | }
234 | }
235 | }
236 | }
237 |
238 | private void CheckMasking(SymbolFunctionInfo func)
239 | {
240 | foreach (var function in Functions)
241 | {
242 | if (function.Virtual == false && function.Name == func.Name)
243 | {
244 | function.IsMasking = true;
245 |
246 | if (DerivedClasses != null)
247 | {
248 | foreach (var derivedClass in DerivedClasses)
249 | {
250 | derivedClass.CheckMasking(func);
251 | }
252 | }
253 | }
254 | }
255 | }
256 |
257 | private bool IsOverloadingFunction(SymbolFunctionInfo func)
258 | {
259 | foreach (var function in Functions)
260 | {
261 | if (function.Name == func.Name)
262 | return true;
263 | }
264 | if (DerivedClasses != null)
265 | {
266 | foreach (var derivedClass in DerivedClasses)
267 | {
268 | if (derivedClass.IsOverloadingFunction(func))
269 | return true;
270 | }
271 | }
272 | return false;
273 | }
274 |
275 | public void UpdateTotalCount(SymbolAnalyzer symbolAnalyzer, ulong count)
276 | {
277 | foreach (var member in Members)
278 | {
279 | if (member.Category != SymbolMemberInfo.MemberCategory.VTable)
280 | {
281 | var referencedInfo = symbolAnalyzer.FindSymbolInfo(member.TypeName);
282 | if (referencedInfo != null)
283 | {
284 | referencedInfo.TotalCount += count;
285 | referencedInfo.UpdateTotalCount(symbolAnalyzer,count);
286 | }
287 | }
288 | }
289 | }
290 |
291 | public void ProcessChildren(IDiaSymbol symbol)
292 | {
293 | symbol.findChildren(SymTagEnum.SymTagNull, null, 0, out var children);
294 | if (children == null)
295 | return;
296 | foreach (IDiaSymbol child in children)
297 | {
298 | if (child.symTag == (uint)SymTagEnum.SymTagFunction)
299 | {
300 | var functionInfo = ProcessFunction(child);
301 | if (functionInfo != null)
302 | {
303 | AddFunction(functionInfo);
304 | if (functionInfo.IsPure)
305 | IsAbstract = true;
306 | }
307 | }
308 | else
309 | {
310 | var memberInfo = ProcessMember(child);
311 | if (memberInfo != null)
312 | {
313 | AddMember(memberInfo);
314 | }
315 | }
316 | }
317 | // Sort members by offset, recompute padding.
318 | // Sorting is usually not needed (for data fields), but sometimes base class order is wrong.
319 | Members.Sort(SymbolMemberInfo.CompareOffsets);
320 | for (int i = 0; i < Members.Count; ++i)
321 | {
322 | var member = Members[i];
323 | member.AlignWithPrevious = ComputeOffsetCollision(i);
324 | member.PaddingBefore = ComputePadding(i);
325 | member.BitPaddingAfter = ComputeBitPadding(i);
326 | }
327 | EndPadding = ComputeEndPadding();
328 | }
329 |
330 | public SymbolMemberInfo ProcessMember(IDiaSymbol symbol)
331 | {
332 | if (symbol.symTag == (uint)SymTagEnum.SymTagVTable)
333 | {
334 | return new SymbolMemberInfo(SymbolMemberInfo.MemberCategory.VTable, string.Empty, string.Empty, 8, 0, (ulong)symbol.offset, symbol.bitPosition);
335 | }
336 |
337 | if (symbol.isStatic != 0 || (symbol.symTag != (uint)SymTagEnum.SymTagData && symbol.symTag != (uint)SymTagEnum.SymTagBaseClass))
338 | {
339 | return null;
340 | }
341 |
342 | // LocIsThisRel || LocIsNull || LocIsBitField
343 | if (symbol.locationType != 4 && symbol.locationType != 0 && symbol.locationType != 6)
344 | {
345 | return null;
346 | }
347 |
348 | var typeSymbol = symbol.type;
349 |
350 | var typeName = GetType(typeSymbol);
351 |
352 | var symbolName = symbol.name;
353 | var category = SymbolMemberInfo.MemberCategory.Member;
354 |
355 | if ((SymTagEnum)symbol.symTag == SymTagEnum.SymTagBaseClass)
356 | category = SymbolMemberInfo.MemberCategory.Base;
357 | else if ((SymTagEnum)typeSymbol.symTag == SymTagEnum.SymTagUDT)
358 | category = SymbolMemberInfo.MemberCategory.UDT;
359 | else if ((SymTagEnum)typeSymbol.symTag == SymTagEnum.SymTagPointerType)
360 | category = SymbolMemberInfo.MemberCategory.Pointer;
361 |
362 | var info = new SymbolMemberInfo(category, symbolName, typeName, typeSymbol.length, symbol.length, (ulong)symbol.offset, symbol.bitPosition);
363 |
364 | if (typeSymbol.volatileType == 1)
365 | info.Volatile = true;
366 | if (symbol.locationType == 6)
367 | info.BitField = true;
368 |
369 | return info;
370 | }
371 |
372 | public SymbolFunctionInfo ProcessFunction(IDiaSymbol symbol)
373 | {
374 | if (symbol.compilerGenerated > 0)
375 | return null;
376 |
377 | var info = new SymbolFunctionInfo();
378 | if (symbol.isStatic > 0)
379 | {
380 | info.Category = SymbolFunctionInfo.FunctionCategory.StaticFunction;
381 | }
382 | else if (symbol.classParent.name.EndsWith(symbol.name))
383 | {
384 | info.Category = SymbolFunctionInfo.FunctionCategory.Constructor;
385 | }
386 | else if (symbol.name.StartsWith("~"))
387 | {
388 | info.Category = SymbolFunctionInfo.FunctionCategory.Destructor;
389 | }
390 | else
391 | {
392 | info.Category = SymbolFunctionInfo.FunctionCategory.Function;
393 | }
394 |
395 | info.Virtual = (symbol.@virtual == 1);
396 | info.IsOverride = info.Virtual && (symbol.intro == 0);
397 | info.IsPure = info.Virtual && (symbol.pure != 0);
398 | info.IsConst = symbol.constType != 0;
399 | if (symbol.wasInlined == 0 && symbol.inlSpec != 0)
400 | info.WasInlineRemoved = true;
401 |
402 | info.Name = GetType(symbol.type.type) + " " + symbol.name;
403 |
404 | symbol.type.findChildren(SymTagEnum.SymTagFunctionArgType, null, 0, out var syms);
405 | if (syms.count == 0)
406 | {
407 | info.Name += "(void)";
408 | }
409 | else
410 | {
411 | var parameters = new List();
412 | foreach (IDiaSymbol argSym in syms)
413 | {
414 | parameters.Add(GetType(argSym.type));
415 | }
416 | info.Name += "(" + string.Join(",", parameters) + ")";
417 | }
418 | return info;
419 | }
420 |
421 | private string GetType(IDiaSymbol typeSymbol)
422 | {
423 | switch ((SymTagEnum)typeSymbol.symTag)
424 | {
425 | case SymTagEnum.SymTagFunctionType:
426 | var returnType = GetType(typeSymbol.type);
427 |
428 | typeSymbol.findChildren(SymTagEnum.SymTagFunctionArgType, null, 0, out var syms);
429 | if (syms.count == 0)
430 | {
431 | returnType += "(void)";
432 | }
433 | else
434 | {
435 | var parameters = new List();
436 | foreach (IDiaSymbol argSym in syms)
437 | {
438 | parameters.Add(GetType(argSym.type));
439 | }
440 | returnType += "(" + string.Join(",",parameters) + ")";
441 | }
442 | return returnType;
443 | case SymTagEnum.SymTagPointerType:
444 | return typeSymbol.reference != 0 ? $"{GetType(typeSymbol.type)}&" : $"{GetType(typeSymbol.type)}*";
445 | case SymTagEnum.SymTagBaseType:
446 | if (typeSymbol.constType != 0)
447 | return "const " + SymbolMemberInfo.GetBaseType(typeSymbol);
448 | return SymbolMemberInfo.GetBaseType(typeSymbol);
449 | case SymTagEnum.SymTagArrayType:
450 | // get array dimension:
451 | var dimension = typeSymbol.count.ToString();
452 | return $"{GetType(typeSymbol.type)}[{dimension}]";
453 | case SymTagEnum.SymTagUDT:
454 | return typeSymbol.name;
455 | case SymTagEnum.SymTagEnum:
456 | return $"enum {typeSymbol.name}";
457 | default:
458 | return string.Empty;
459 | }
460 | }
461 |
462 | public override string ToString()
463 | {
464 | var sw = new StringWriter();
465 |
466 | sw.WriteLine($"Symbol: {Name}");
467 | sw.WriteLine($"TypeName: {TypeName}");
468 | sw.WriteLine($"Size: {Size}");
469 | sw.WriteLine($"Padding: {Padding}");
470 | sw.WriteLine($"Total padding: {TotalPadding}");
471 | sw.WriteLine("Members:");
472 | sw.WriteLine("-------");
473 |
474 | foreach (var member in Members)
475 | {
476 | if (member.PaddingBefore > 0)
477 | {
478 | var paddingOffset = member.Offset - member.PaddingBefore;
479 | sw.WriteLine($"{PaddingMarker,-40} {paddingOffset,5} {member.PaddingBefore,5}");
480 | }
481 | sw.WriteLine($"{member.DisplayName,-40} {member.Offset,5} {member.Size,5}");
482 | }
483 |
484 | if (EndPadding > 0)
485 | {
486 | var endPaddingOffset = Size - EndPadding;
487 | sw.WriteLine($"{PaddingMarker,-40} {endPaddingOffset,5} {EndPadding,5}");
488 | }
489 |
490 | return sw.ToString();
491 | }
492 | }
493 | }
494 |
--------------------------------------------------------------------------------
/SymbolMemberInfo.cs:
--------------------------------------------------------------------------------
1 | using Dia2Lib;
2 | using System;
3 | using System.Collections.Generic;
4 | using System.IO;
5 | using System.Linq;
6 |
7 | namespace CruncherSharp
8 | {
9 | public class SymbolMemberInfo
10 | {
11 | public enum MemberCategory
12 | {
13 | VTable = 0,
14 | Base = 1,
15 | Member = 2,
16 | UDT = 3,
17 | Pointer = 4
18 | }
19 |
20 | public MemberCategory Category { get; set; }
21 | public string Name { get; set; }
22 | public string DisplayName
23 | {
24 | get
25 | {
26 | switch (Category)
27 | {
28 | case MemberCategory.VTable:
29 | return "vtable";
30 | case MemberCategory.Base:
31 | return $"Base: {Name}";
32 | default:
33 | return Name;
34 | }
35 | }
36 | }
37 | public string TypeName { get; set; }
38 | public ulong Size { get; set; }
39 | public ulong BitSize { get; set; }
40 | public ulong Offset { get; set; }
41 | public uint BitPosition { get; set; }
42 | public ulong PaddingBefore { get; set; }
43 | public ulong BitPaddingAfter { get; set; }
44 |
45 | public bool AlignWithPrevious { get; set; }
46 | public bool BitField { get; set; }
47 |
48 | public bool Volatile { get; set; }
49 | public bool Expanded { get; set; }
50 |
51 | public SymbolMemberInfo(MemberCategory category, string name, string typeName, ulong size, ulong bitSize, ulong offset, uint bitPosition)
52 | {
53 | Category = category;
54 | Name = name;
55 | TypeName = typeName;
56 | Size = size;
57 | BitSize = bitSize;
58 | Offset = offset;
59 | BitPosition = bitPosition;
60 | AlignWithPrevious = false;
61 | PaddingBefore = 0;
62 | BitPaddingAfter = 0;
63 | BitField = false;
64 | Volatile = false;
65 | Expanded = false;
66 | }
67 |
68 | public bool IsBase => Category == MemberCategory.Base;
69 |
70 | public bool IsExapandable => (Category == MemberCategory.Base || Category == MemberCategory.UDT);
71 |
72 | public static int CompareOffsets(SymbolMemberInfo a, SymbolMemberInfo b)
73 | {
74 | if (a.Offset != b.Offset)
75 | {
76 | return a.Offset < b.Offset ? -1 : 1;
77 | }
78 | if (a.IsBase != b.IsBase)
79 | {
80 | return a.IsBase ? -1 : 1;
81 | }
82 | if (a.BitPosition != b.BitPosition)
83 | {
84 | return a.BitPosition < b.BitPosition ? -1 : 1;
85 | }
86 | if (a.Size != b.Size)
87 | {
88 | return a.Size > b.Size ? -1 : 1;
89 | }
90 | return 0;
91 | }
92 |
93 | public static string GetBaseType(IDiaSymbol typeSymbol)
94 | {
95 | //cf. https://msdn.microsoft.com/en-us/library/4szdtzc3.aspx
96 | switch (typeSymbol.baseType)
97 | {
98 | case 0:
99 | return string.Empty;
100 | case 1:
101 | return "void";
102 | case 2:
103 | return "char";
104 | case 3:
105 | return "wchar";
106 | case 6:
107 | {
108 | switch (typeSymbol.length)
109 | {
110 | case 1:
111 | return "int8";
112 | case 2:
113 | return "int16";
114 | case 4:
115 | return "int32";
116 | case 8:
117 | return "int64";
118 | default:
119 | return "int";
120 | }
121 | }
122 | case 7:
123 | switch (typeSymbol.length)
124 | {
125 | case 1:
126 | return "uint8";
127 | case 2:
128 | return "uint16";
129 | case 4:
130 | return "uint32";
131 | case 8:
132 | return "uint64";
133 | default:
134 | return "uint";
135 | }
136 | case 8:
137 | return "float";
138 | case 9:
139 | return "BCS";
140 | case 10:
141 | return "bool";
142 | case 13:
143 | return "int32";
144 | case 14:
145 | return "uint32";
146 | case 29:
147 | return "bit";
148 | default:
149 | return $"Unhandled: {typeSymbol.baseType}";
150 | }
151 | }
152 | }
153 | }
--------------------------------------------------------------------------------
/app.config:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/crunchersharp.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/msinilo/crunchersharp/9345e140bfd6f5250336f83a9843baf88116d1e6/crunchersharp.ico
--------------------------------------------------------------------------------