Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(244)

Side by Side Diff: tools/win/ShowGlobals/ShowGlobals.cc

Issue 2580833003: ShowGlobals tool to dump large/repeated Win32 globals (Closed)
Patch Set: More code review tweaks Created 4 years ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « no previous file | tools/win/ShowGlobals/ShowGlobals.sln » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
1 // Copyright (c) 2016 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 // This tool scans a PDB file and prints out information about 'interesting'
6 // global variables. This includes duplicates and large globals. This is often
7 // helpful inunderstanding code bloat or finding inefficient globals.
8 //
9 // Duplicate global variables often happen when constructs like this are placed
10 // in a header file:
11 //
12 // const double sqrt_two = sqrt(2.0);
13 //
14 // Many (although usually not all) of the translation units that include this
15 // header file will get a copy of sqrt_two, possibly including an initializer.
16 // Because 'const' implies 'static' there are no warnings or errors from the
17 // linker. This duplication can happen with float/double, structs and classes,
18 // and arrays - any non-integral type.
19 //
20 // Global variables are not necessarily a problem but it is useful to understand
21 // them, and monitoring their changes can be instructive.
22
23 #include <atlbase.h>
24 #include <dia2.h>
25 #include <stdio.h>
26
27 #include <algorithm>
28 #include <vector>
29
30 // Helper function for comparing strings - returns a strcmp/wcscmp compatible
31 // value.
32 int StringCompare(const std::wstring& lhs, const std::wstring& rhs) {
33 return wcscmp(lhs.c_str(), rhs.c_str());
34 }
35
36 // Use this struct to record data about symbols for sorting and analysis.
37 struct SymbolData {
38 SymbolData(ULONGLONG size, DWORD section, const wchar_t* name)
39 : size(size), section(section), name(name) {}
40
41 ULONGLONG size;
42 DWORD section;
43 std::wstring name;
44 };
45
46 // Comparison function for when sorting symbol data by name, in order to allow
47 // looking for duplicate symbols. It uses the symbol size as a tiebreaker. This
48 // is necessary because sometimes there are symbols with matching names but
49 // different sizes in which case they aren't actually duplicates. These false
50 // positives happen because namespaces are omitted from the symbol names that
51 // DIA2 returns.
52 bool NameCompare(const SymbolData& lhs, const SymbolData& rhs) {
53 int nameCompare = StringCompare(lhs.name, rhs.name);
54 if (nameCompare == 0)
55 return lhs.size < rhs.size;
56 return nameCompare < 0;
57 }
58
59 // Comparison function for when sorting symbols by size, in order to allow
60 // finding the largest global variables. Use the symbol names as a tiebreaker
61 // in order to get consistent ordering.
62 bool SizeCompare(const SymbolData& lhs, const SymbolData& rhs) {
63 if (lhs.size == rhs.size)
64 return StringCompare(lhs.name, rhs.name) < 0;
65 return lhs.size < rhs.size;
66 }
67
68 // Use this struct to store data about repeated globals, for later sorting.
69 struct RepeatData {
70 RepeatData(ULONGLONG repeat_count,
71 ULONGLONG bytes_wasted,
72 const std::wstring& name)
73 : repeat_count(repeat_count), bytes_wasted(bytes_wasted), name(name) {}
74 bool operator<(const RepeatData& rhs) {
75 return bytes_wasted < rhs.bytes_wasted;
76 }
77
78 ULONGLONG repeat_count;
79 ULONGLONG bytes_wasted;
80 std::wstring name;
81 };
82
83 bool DumpInterestingGlobals(IDiaSymbol* global, const wchar_t* filename) {
84 wprintf(L"#Dups\tDupSize\t Size\tSection\tSymbol name\tPDB name\n");
85
86 // How many bytes must be wasted on repeats before being listed.
87 const int kWastageThreshold = 100;
88 // How big must an individual symbol be before being listed.
89 const int kBigSizeThreshold = 500;
90
91 std::vector<SymbolData> symbols;
92 std::vector<RepeatData> repeats;
93
94 CComPtr<IDiaEnumSymbols> enum_symbols;
95 HRESULT result =
96 global->findChildren(SymTagData, NULL, nsNone, &enum_symbols);
97 if (FAILED(result)) {
98 wprintf(L"ERROR - DumpInterestingGlobals() returned no symbols.\n");
99 return false;
100 }
101
102 CComPtr<IDiaSymbol> symbol;
103 // Must call symbol.Release() at end of loop to prepare for reuse of symbol
104 // smart pointer, because DIA2 is not smart-pointer aware.
105 for (ULONG celt = 0;
106 SUCCEEDED(enum_symbols->Next(1, &symbol, &celt)) && (celt == 1);
107 symbol.Release()) {
108 // If we call get_length on symbol it works for functions but not for
109 // data. For some reason for data we have to call get_type() to get
110 // another IDiaSymbol object which we can query for length.
111 CComPtr<IDiaSymbol> type_symbol;
112 if (FAILED(symbol->get_type(&type_symbol))) {
113 wprintf(L"Get_type failed.\n");
114 continue;
115 }
116
117 // Errors in the remainder of this loop can be ignored silently.
118 ULONGLONG size = 0;
119 type_symbol->get_length(&size);
120
121 // Use -1 and -2 as canary values to indicate various failures.
122 DWORD section = static_cast<DWORD>(-1);
123 if (symbol->get_addressSection(&section) != S_OK)
124 section = static_cast<DWORD>(-2);
125
126 CComBSTR name;
127 if (symbol->get_name(&name) == S_OK) {
128 symbols.push_back(SymbolData(size, section, name));
129 }
130 }
131
132 // Sort the symbols by name/size so that we can print a report about duplicate
133 // variables.
134 std::sort(symbols.begin(), symbols.end(), NameCompare);
135 for (auto p = symbols.begin(); p != symbols.end(); /**/) {
136 auto pScan = p;
137 // Scan the data looking for symbols that have the same name
138 // and size.
139 while (pScan != symbols.end() && p->size == pScan->size &&
140 StringCompare(p->name, pScan->name) == 0)
141 ++pScan;
142
143 // Calculate how many times the symbol name/size appears in this PDB.
144 size_t repeat_count = pScan - p;
145 if (repeat_count > 1) {
146 // Change the count from how many instances of this variable there are to
147 // how many *excess* instances there are.
148 --repeat_count;
149 ULONGLONG bytes_wasted = repeat_count * p->size;
150 if (bytes_wasted > kWastageThreshold) {
151 repeats.push_back(RepeatData(repeat_count, bytes_wasted, p->name));
152 }
153 }
154
155 p = pScan;
156 }
157
158 // Print a summary of duplicated variables, sorted to put the worst offenders
159 // first.
160 std::sort(repeats.begin(), repeats.end());
161 std::reverse(repeats.begin(), repeats.end());
162 for (const auto& repeat : repeats) {
163 // The empty field contain a zero so that Excel/sheets will more easily
164 // create the pivot tables that I want.
165 wprintf(L"%llu\t%llu\t%6u\t%u\t%s\t%s\n", repeat.repeat_count,
166 repeat.bytes_wasted, 0, 0, repeat.name.c_str(), filename);
167 }
168 wprintf(L"\n");
169
170 // Print a summary of the largest global variables
171 std::sort(symbols.begin(), symbols.end(), SizeCompare);
172 std::reverse(symbols.begin(), symbols.end());
173 for (const auto& s : symbols) {
174 if (s.size < kBigSizeThreshold)
175 break;
176 // The empty fields contain a zero so that the columns line up which can
177 // be important when pasting the data into a spreadsheet.
178 wprintf(L"%u\t%u\t%6llu\t%u\t%s\t%s\n", 0, 0, s.size, s.section,
179 s.name.c_str(), filename);
180 }
181
182 return true;
183 }
184
185 bool Initialize(const wchar_t* filename,
186 CComPtr<IDiaDataSource>& source,
187 CComPtr<IDiaSession>& session,
188 CComPtr<IDiaSymbol>& global) {
189 // Initialize DIA2
190 HRESULT hr = CoCreateInstance(__uuidof(DiaSource), NULL, CLSCTX_INPROC_SERVER,
191 __uuidof(IDiaDataSource), (void**)&source);
192 if (FAILED(hr)) {
193 wprintf(L"Failed to initialized DIA2 - %08X.\n", hr);
194 return false;
195 }
196
197 // Open the PDB
198 hr = source->loadDataFromPdb(filename);
199 if (FAILED(hr)) {
200 wprintf(L"LoadDataFromPdb failed - %08X.\n", hr);
201 return false;
202 }
203
204 hr = source->openSession(&session);
205 if (FAILED(hr)) {
206 wprintf(L"OpenSession failed - %08X.\n", hr);
207 return false;
208 }
209
210 // Retrieve a reference to the global scope
211 hr = session->get_globalScope(&global);
212 if (hr != S_OK) {
213 wprintf(L"Get_globalScope failed - %08X.\n", hr);
214 return false;
215 }
216
217 return true;
218 }
219
220 int wmain(int argc, wchar_t* argv[]) {
221 if (argc < 2) {
222 wprintf(L"Usage: ShowGlobals file.pdb");
223 return -1;
224 }
225
226 const wchar_t* filename = argv[1];
227
228 HRESULT hr = CoInitialize(NULL);
229 if (FAILED(hr)) {
230 wprintf(L"CoInitialize failed - %08X.", hr);
231 return false;
232 }
233
234 // Extra scope so that we can call CoUninitialize after we destroy our local
235 // variables.
236 {
237 CComPtr<IDiaDataSource> source;
238 CComPtr<IDiaSession> session;
239 CComPtr<IDiaSymbol> global;
240 if (!(Initialize(filename, source, session, global)))
241 return -1;
242
243 DumpInterestingGlobals(global, filename);
244 }
245
246 CoUninitialize();
247 }
OLDNEW
« no previous file with comments | « no previous file | tools/win/ShowGlobals/ShowGlobals.sln » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698