1 : // Copyright 2016 Google Inc. All Rights Reserved.
2 : //
3 : // Licensed under the Apache License, Version 2.0 (the "License");
4 : // you may not use this file except in compliance with the License.
5 : // You may obtain a copy of the License at
6 : //
7 : // http://www.apache.org/licenses/LICENSE-2.0
8 : //
9 : // Unless required by applicable law or agreed to in writing, software
10 : // distributed under the License is distributed on an "AS IS" BASIS,
11 : // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 : // See the License for the specific language governing permissions and
13 : // limitations under the License.
14 :
15 : // Runs a particular analyzer over a minidump or set of minidumps.
16 : #include <windows.h> // NOLINT
17 : #include <dbghelp.h>
18 :
19 : #include <vector>
20 :
21 : #include "base/at_exit.h"
22 : #include "base/command_line.h"
23 : #include "base/macros.h"
24 : #include "base/files/file_path.h"
25 : #include "base/strings/string16.h"
26 : #include "base/strings/string_split.h"
27 : #include "base/strings/stringprintf.h"
28 : #include "syzygy/application/application.h"
29 : #include "syzygy/minidump/minidump.h"
30 : #include "syzygy/refinery/analyzers/analysis_runner.h"
31 : #include "syzygy/refinery/analyzers/heap_analyzer.h"
32 : #include "syzygy/refinery/analyzers/memory_analyzer.h"
33 : #include "syzygy/refinery/analyzers/module_analyzer.h"
34 : #include "syzygy/refinery/analyzers/stack_analyzer.h"
35 : #include "syzygy/refinery/process_state/process_state.h"
36 : #include "syzygy/refinery/symbols/dia_symbol_provider.h"
37 : #include "syzygy/refinery/symbols/symbol_provider.h"
38 :
39 m : namespace {
40 :
41 m : class RunAnalyzerApplication : public application::AppImplBase {
42 m : public:
43 m : RunAnalyzerApplication();
44 :
45 m : bool ParseCommandLine(const base::CommandLine* command_line);
46 m : int Run();
47 :
48 m : private:
49 m : void PrintUsage(const base::FilePath& program,
50 m : const base::StringPiece& message);
51 m : bool AddAnalyzers(
52 m : scoped_refptr<refinery::SymbolProvider> symbol_provider,
53 m : scoped_refptr<refinery::DiaSymbolProvider> dia_symbol_provider,
54 m : refinery::AnalysisRunner* runner);
55 m : bool Analyze(const minidump::Minidump& minidump,
56 m : scoped_refptr<refinery::SymbolProvider> symbol_provider,
57 m : scoped_refptr<refinery::DiaSymbolProvider> dia_symbol_provider,
58 m : refinery::ProcessState* process_state);
59 :
60 m : std::vector<base::FilePath> mindump_paths_;
61 m : std::vector<std::string> analyzer_names_;
62 :
63 m : DISALLOW_COPY_AND_ASSIGN(RunAnalyzerApplication);
64 m : };
65 :
66 m : const char kUsageFormatStr[] =
67 m : "Usage: %ls [options] <dump files or patterns>\n"
68 m : "\n"
69 m : " --analyzers=<comma-seperated list of analyzer names>\n"
70 m : " Configures the set of analyzers to run on each of the dump\n"
71 m : " files.\n";
72 :
73 m : const char kDefaultAnalyzers[] = "MemoryAnalyzer,ModuleAnalyser,HeapAnalyzer";
74 :
75 m : void RunAnalyzerApplication::PrintUsage(const base::FilePath& program,
76 m : const base::StringPiece& message) {
77 m : if (!message.empty()) {
78 m : ::fwrite(message.data(), 1, message.length(), out());
79 m : ::fprintf(out(), "\n\n");
80 m : }
81 :
82 m : ::fprintf(out(), kUsageFormatStr, program.BaseName().value().c_str());
83 m : }
84 :
85 m : RunAnalyzerApplication::RunAnalyzerApplication()
86 m : : AppImplBase("RunAnalyzerApplication") {
87 m : }
88 :
89 m : bool RunAnalyzerApplication::ParseCommandLine(
90 m : const base::CommandLine* cmd_line) {
91 m : if (cmd_line->HasSwitch("help")) {
92 m : PrintUsage(cmd_line->GetProgram(), "");
93 m : return false;
94 m : }
95 :
96 m : std::string analyzers = cmd_line->GetSwitchValueASCII("analyzers");
97 m : if (analyzers.empty())
98 m : analyzers = kDefaultAnalyzers;
99 :
100 m : analyzer_names_ = base::SplitString(analyzers, ",", base::TRIM_WHITESPACE,
101 m : base::SPLIT_WANT_NONEMPTY);
102 m : if (analyzer_names_.empty()) {
103 m : PrintUsage(cmd_line->GetProgram(),
104 m : "Must provide a non-empty analyzer list.");
105 m : return false;
106 m : }
107 :
108 m : for (const auto& arg : cmd_line->GetArgs()) {
109 m : if (!AppendMatchingPaths(base::FilePath(arg), &mindump_paths_)) {
110 m : PrintUsage(
111 m : cmd_line->GetProgram(),
112 m : base::StringPrintf("Can't find file or pattern \"%s\"", arg.c_str()));
113 m : return false;
114 m : }
115 m : }
116 :
117 m : if (mindump_paths_.empty()) {
118 m : PrintUsage(cmd_line->GetProgram(),
119 m : "You must provide at least one dump file.");
120 m : return false;
121 m : }
122 :
123 m : return true;
124 m : }
125 :
126 m : int RunAnalyzerApplication::Run() {
127 m : scoped_refptr<refinery::SymbolProvider> symbol_provider(
128 m : new refinery::SymbolProvider());
129 m : scoped_refptr<refinery::DiaSymbolProvider> dia_symbol_provider(
130 m : new refinery::DiaSymbolProvider());
131 :
132 m : for (const auto& minidump_path : mindump_paths_) {
133 m : ::fprintf(out(), "Processing \"%ls\"\n", minidump_path.value().c_str());
134 :
135 m : minidump::Minidump minidump;
136 m : if (!minidump.Open(minidump_path)) {
137 m : LOG(ERROR) << "Unable to open dump file.";
138 m : return 1;
139 m : }
140 :
141 m : refinery::ProcessState process_state;
142 m : if (!Analyze(minidump, symbol_provider, dia_symbol_provider,
143 m : &process_state)) {
144 m : LOG(ERROR) << "Failure processing minidump " << minidump_path.value();
145 m : }
146 m : }
147 :
148 m : return 0;
149 m : }
150 :
151 m : bool RunAnalyzerApplication::AddAnalyzers(
152 m : scoped_refptr<refinery::SymbolProvider> symbol_provider,
153 m : scoped_refptr<refinery::DiaSymbolProvider> dia_symbol_provider,
154 m : refinery::AnalysisRunner* runner) {
155 m : scoped_ptr<refinery::Analyzer> analyzer;
156 : // TODO(siggi): Figure a better way to do this.
157 m : for (const auto& analyzer_name : analyzer_names_) {
158 m : if (analyzer_name == "MemoryAnalyzer") {
159 m : analyzer.reset(new refinery::MemoryAnalyzer());
160 m : runner->AddAnalyzer(analyzer.Pass());
161 m : } else if (analyzer_name == "ModuleAnalyzer") {
162 m : analyzer.reset(new refinery::ModuleAnalyzer());
163 m : runner->AddAnalyzer(analyzer.Pass());
164 m : } else if (analyzer_name == "HeapAnalyzer") {
165 m : analyzer.reset(new refinery::HeapAnalyzer(symbol_provider));
166 m : runner->AddAnalyzer(analyzer.Pass());
167 m : } else if (analyzer_name == "StackAnalyzer") {
168 m : analyzer.reset(new refinery::StackAnalyzer(dia_symbol_provider));
169 m : runner->AddAnalyzer(analyzer.Pass());
170 m : } else {
171 m : return false;
172 m : }
173 m : }
174 :
175 m : return true;
176 m : }
177 :
178 m : bool RunAnalyzerApplication::Analyze(
179 m : const minidump::Minidump& minidump,
180 m : scoped_refptr<refinery::SymbolProvider> symbol_provider,
181 m : scoped_refptr<refinery::DiaSymbolProvider> dia_symbol_provider,
182 m : refinery::ProcessState* process_state) {
183 m : DCHECK(symbol_provider);
184 m : DCHECK(dia_symbol_provider);
185 m : DCHECK(process_state);
186 :
187 m : minidump::Minidump::Stream sys_info_stream =
188 m : minidump.GetStream(SystemInfoStream);
189 :
190 m : MINIDUMP_SYSTEM_INFO system_info = {};
191 m : if (!sys_info_stream.ReadElement(&system_info)) {
192 m : LOG(ERROR) << "Unable to read system info stream.";
193 m : return false;
194 m : }
195 :
196 m : VLOG(1) << base::StringPrintf("Systeminformation");
197 m : VLOG(1) << base::StringPrintf(" ProcessorArchitecture 0x%04X",
198 m : system_info.ProcessorArchitecture);
199 m : VLOG(1) << base::StringPrintf(" ProcessorLevel 0x%04X",
200 m : system_info.ProcessorLevel);
201 m : VLOG(1) << base::StringPrintf(" ProcessorRevision 0x%04X",
202 m : system_info.ProcessorRevision);
203 m : VLOG(1) << base::StringPrintf(" NumberOfProcessors %d",
204 m : system_info.NumberOfProcessors);
205 m : VLOG(1) << base::StringPrintf(" ProductType %d", system_info.ProductType);
206 m : VLOG(1) << base::StringPrintf(" MajorVersion 0x%08X",
207 m : system_info.MajorVersion);
208 m : VLOG(1) << base::StringPrintf(" MinorVersion 0x%08X",
209 m : system_info.MinorVersion);
210 m : VLOG(1) << base::StringPrintf(" BuildNumber 0x%08X",
211 m : system_info.BuildNumber);
212 m : VLOG(1) << base::StringPrintf(" PlatformId 0x%08X", system_info.PlatformId);
213 m : VLOG(1) << base::StringPrintf(" CSDVersionRva 0x%08X",
214 m : system_info.CSDVersionRva);
215 m : VLOG(1) << base::StringPrintf(" SuiteMask 0x%04X", system_info.SuiteMask);
216 :
217 m : VLOG(1) << " CPU information:";
218 m : VLOG(1) << base::StringPrintf(" VendorId 0x%08X:0x%08X:0x%08X",
219 m : system_info.Cpu.X86CpuInfo.VendorId[0],
220 m : system_info.Cpu.X86CpuInfo.VendorId[1],
221 m : system_info.Cpu.X86CpuInfo.VendorId[1]);
222 :
223 m : VLOG(1) << base::StringPrintf(" VersionInformation 0x%08X",
224 m : system_info.Cpu.X86CpuInfo.VersionInformation);
225 m : VLOG(1) << base::StringPrintf(" FeatureInformation 0x%08X",
226 m : system_info.Cpu.X86CpuInfo.FeatureInformation);
227 m : VLOG(1) << base::StringPrintf(
228 m : " AMDExtendedCpuFeatures 0x%08X",
229 m : system_info.Cpu.X86CpuInfo.AMDExtendedCpuFeatures);
230 :
231 m : refinery::AnalysisRunner runner;
232 m : if (!AddAnalyzers(symbol_provider, dia_symbol_provider, &runner))
233 m : return false;
234 :
235 m : return runner.Analyze(minidump, process_state) ==
236 m : refinery::Analyzer::ANALYSIS_COMPLETE;
237 m : }
238 :
239 m : } // namespace
240 :
241 m : int main(int argc, const char* const* argv) {
242 m : base::AtExitManager at_exit_manager;
243 m : base::CommandLine::Init(argc, argv);
244 m : return application::Application<RunAnalyzerApplication>().Run();
245 m : }
|