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 <set>
20 : #include <unordered_map>
21 : #include <vector>
22 :
23 : #include "base/at_exit.h"
24 : #include "base/command_line.h"
25 : #include "base/macros.h"
26 : #include "base/files/file_path.h"
27 : #include "base/strings/string16.h"
28 : #include "base/strings/string_split.h"
29 : #include "base/strings/string_util.h"
30 : #include "base/strings/stringprintf.h"
31 : #include "syzygy/application/application.h"
32 : #include "syzygy/minidump/minidump.h"
33 : #include "syzygy/refinery/analyzers/analysis_runner.h"
34 : #include "syzygy/refinery/analyzers/analyzer_factory.h"
35 : #include "syzygy/refinery/analyzers/analyzer_util.h"
36 : #include "syzygy/refinery/process_state/process_state.h"
37 : #include "syzygy/refinery/symbols/dia_symbol_provider.h"
38 : #include "syzygy/refinery/symbols/symbol_provider.h"
39 :
40 m : namespace {
41 :
42 m : using AnalyzerName = std::string;
43 m : using AnalyzerNames = std::vector<std::string>;
44 m : using AnalyzerSet = std::set<AnalyzerName>;
45 m : using AnalyzerGraph = std::unordered_map<AnalyzerName, AnalyzerSet>;
46 m : using LayerNames = std::vector<std::string>;
47 :
48 m : class RunAnalyzerApplication : public application::AppImplBase {
49 m : public:
50 m : RunAnalyzerApplication();
51 :
52 m : bool ParseCommandLine(const base::CommandLine* command_line);
53 m : int Run();
54 :
55 m : private:
56 m : bool AddLayerPrerequisiteAnalyzers(const refinery::AnalyzerFactory& factory);
57 m : bool OrderAnalyzers(const refinery::AnalyzerFactory& factory);
58 :
59 m : template <typename LayerPtrType>
60 m : void PrintLayer(const char* layer_name,
61 m : refinery::ProcessState* process_state);
62 m : void PrintProcessState(refinery::ProcessState* process_state);
63 m : void PrintUsage(const base::FilePath& program,
64 m : const base::StringPiece& message);
65 m : bool AddAnalyzers(const refinery::AnalyzerFactory& factory,
66 m : refinery::AnalysisRunner* runner);
67 m : bool Analyze(const minidump::Minidump& minidump,
68 m : const refinery::AnalyzerFactory& factory,
69 m : const refinery::Analyzer::ProcessAnalysis& process_analysis);
70 :
71 m : std::vector<base::FilePath> mindump_paths_;
72 m : std::string analyzer_names_;
73 m : bool resolve_dependencies_;
74 m : std::string output_layers_;
75 :
76 m : DISALLOW_COPY_AND_ASSIGN(RunAnalyzerApplication);
77 m : };
78 :
79 : // A worker class that knows how to order analyzers topologically by their
80 : // layer dependencies.
81 m : class AnalyzerOrderer {
82 m : public:
83 m : explicit AnalyzerOrderer(const refinery::AnalyzerFactory& factory);
84 :
85 m : bool CreateGraph(const std::string& names);
86 m : std::string Order();
87 :
88 m : private:
89 m : void Visit(const AnalyzerName& name);
90 :
91 m : const refinery::AnalyzerFactory& factory_;
92 m : AnalyzerGraph graph_;
93 m : AnalyzerSet visited_;
94 m : AnalyzerSet used_;
95 m : AnalyzerNames ordering_;
96 m : };
97 :
98 m : std::vector<std::string> SplitStringList(const std::string& name_list) {
99 m : return base::SplitString(name_list, ",", base::TRIM_WHITESPACE,
100 m : base::SPLIT_WANT_NONEMPTY);
101 m : }
102 :
103 m : std::string JoinAnalyzerSet(const AnalyzerSet& analyzer_set) {
104 m : std::string ret;
105 m : for (const auto& analyzer_name : analyzer_set) {
106 m : if (!ret.empty())
107 m : ret.append(",");
108 :
109 m : ret.append(analyzer_name);
110 m : }
111 m : return ret;
112 m : }
113 :
114 m : const char kUsageFormatStr[] =
115 m : "Usage: %ls [options] <dump files or patterns>\n"
116 m : "\n"
117 m : " --analyzers=<comma-seperated list of analyzer names>\n"
118 m : " Configures the set of analyzers to run on each of the dump\n"
119 m : " files.\n"
120 m : " Default value: %s\n"
121 m : " --output-layers=<comma-seperated list of layer names>\n"
122 m : " The list of layers to output. If no list of analyzer is provided,\n"
123 m : " this option will configure all analyzers that output the requested\n"
124 m : " layer or layers.\n"
125 m : " Default value: %s\n"
126 m : " --no-dependencies\n"
127 m : " If provided, the layer dependencies of the requested analyzers\n"
128 m : " won't be used to supplement the analyzer list.\n";
129 :
130 m : const char kDefaultAnalyzers[] = "HeapAnalyzer,StackFrameAnalyzer,TebAnalyzer";
131 m : const char kDefaultOutputLayers[] = "TypedDataLayer";
132 :
133 m : bool RunAnalyzerApplication::AddLayerPrerequisiteAnalyzers(
134 m : const refinery::AnalyzerFactory& factory) {
135 : // Build the transitive closure of all the analyzers we need.
136 : // The analyzers we've yet to process, initialized to the entire list.
137 m : AnalyzerNames to_process = SplitStringList(analyzer_names_);
138 m : AnalyzerSet selected_analyzers;
139 m : for (const auto& name : to_process)
140 m : selected_analyzers.insert(name);
141 :
142 m : while (!to_process.empty()) {
143 : // Pop one analyzer name off the list to process.
144 m : std::string analyzer_name = to_process.back();
145 m : to_process.pop_back();
146 :
147 : // Get the input layers this analyzer depends on.
148 m : refinery::AnalyzerFactory::Layers input_layers;
149 m : if (!factory.GetInputLayers(analyzer_name, &input_layers))
150 m : return false;
151 :
152 : // Now retrieve all the analyzers that produce these layers, and see about
153 : // adding them to the mix.
154 m : for (const auto& input_layer : input_layers) {
155 m : refinery::AnalyzerFactory::AnalyzerNames outputting_names;
156 m : factory.GetAnalyzersOutputting(input_layer, &outputting_names);
157 :
158 m : for (const auto& outputting_name : outputting_names) {
159 m : bool inserted = selected_analyzers.insert(outputting_name).second;
160 m : if (inserted) {
161 : // This analyzer was not already in all names, add it to the queue
162 : // of names to process.
163 m : to_process.push_back(outputting_name);
164 m : }
165 m : }
166 m : }
167 m : }
168 :
169 m : analyzer_names_ = JoinAnalyzerSet(selected_analyzers);
170 :
171 m : return true;
172 m : }
173 :
174 m : bool RunAnalyzerApplication::OrderAnalyzers(
175 m : const refinery::AnalyzerFactory& factory) {
176 : // Topologically order the analyzers.
177 : // Start by building the graph of analyzer dependencies.
178 m : AnalyzerOrderer orderer(factory);
179 :
180 m : if (!orderer.CreateGraph(analyzer_names_))
181 m : return false;
182 :
183 m : analyzer_names_ = orderer.Order();
184 :
185 m : return true;
186 m : }
187 :
188 m : template <typename LayerPtrType>
189 m : void RunAnalyzerApplication::PrintLayer(const char* layer_name,
190 m : refinery::ProcessState* process_state) {
191 m : DCHECK(process_state);
192 :
193 m : LayerPtrType layer;
194 m : if (!process_state->FindLayer(&layer)) {
195 m : LOG(INFO) << "No " << layer_name << " layer";
196 m : return;
197 m : }
198 :
199 m : for (const auto& record : *layer) {
200 m : std::string str = record->data().DebugString();
201 :
202 m : ::fprintf(out(), "0x%08llX(0x%04X){\n%s}\n", record->range().start(),
203 m : record->range().size(), str.c_str());
204 m : }
205 m : }
206 :
207 m : void RunAnalyzerApplication::PrintProcessState(
208 m : refinery::ProcessState* process_state) {
209 m : DCHECK(process_state);
210 :
211 m : LayerNames layer_names = SplitStringList(output_layers_);
212 :
213 : #define PRINT_LAYER(layer_name) \
214 m : if (std::find(layer_names.begin(), layer_names.end(), \
215 m : #layer_name "Layer") != layer_names.end()) \
216 m : PrintLayer<refinery::layer_name##LayerPtr>(#layer_name, process_state);
217 m :
218 m : PROCESS_STATE_LAYERS(PRINT_LAYER)
219 m :
220 m : #undef PRINT_LAYER
221 m : }
222 m :
223 m : void RunAnalyzerApplication::PrintUsage(const base::FilePath& program,
224 m : const base::StringPiece& message) {
225 m : if (!message.empty()) {
226 m : ::fwrite(message.data(), 1, message.length(), out());
227 m : ::fprintf(out(), "\n\n");
228 m : }
229 m :
230 m : ::fprintf(out(), kUsageFormatStr, program.BaseName().value().c_str(),
231 m : kDefaultAnalyzers, kDefaultOutputLayers);
232 m : }
233 m :
234 m : RunAnalyzerApplication::RunAnalyzerApplication()
235 m : : AppImplBase("RunAnalyzerApplication"), resolve_dependencies_(true) {
236 m : }
237 m :
238 m : bool RunAnalyzerApplication::ParseCommandLine(
239 m : const base::CommandLine* cmd_line) {
240 m : if (cmd_line->HasSwitch("help")) {
241 m : PrintUsage(cmd_line->GetProgram(), "");
242 m : return false;
243 m : }
244 m :
245 m : if (cmd_line->HasSwitch("no-dependencies"))
246 m : resolve_dependencies_ = false;
247 m :
248 m : static const char kAnalyzers[] = "analyzers";
249 m : if (cmd_line->HasSwitch(kAnalyzers)) {
250 m : analyzer_names_ = cmd_line->GetSwitchValueASCII(kAnalyzers);
251 m :
252 m : if (analyzer_names_.empty()) {
253 m : PrintUsage(cmd_line->GetProgram(),
254 m : "Must provide a non-empty analyzer list with this flag.");
255 m : return false;
256 m : }
257 m : }
258 m :
259 m : static const char kOutputLayers[] = "output-layers";
260 m : if (cmd_line->HasSwitch(kOutputLayers)) {
261 m : output_layers_ = cmd_line->GetSwitchValueASCII(kOutputLayers);
262 m :
263 m : if (output_layers_.empty()) {
264 m : PrintUsage(cmd_line->GetProgram(),
265 m : "Must provide a non-empty output layer list with this flag.");
266 m : return false;
267 m : }
268 m : }
269 m :
270 m : for (const auto& arg : cmd_line->GetArgs()) {
271 m : if (!AppendMatchingPaths(base::FilePath(arg), &mindump_paths_)) {
272 m : PrintUsage(
273 m : cmd_line->GetProgram(),
274 m : base::StringPrintf("Can't find file or pattern \"%s\"", arg.c_str()));
275 m : return false;
276 m : }
277 m : }
278 m :
279 m : if (mindump_paths_.empty()) {
280 m : PrintUsage(cmd_line->GetProgram(),
281 m : "You must provide at least one dump file.");
282 m : return false;
283 m : }
284 m :
285 m : return true;
286 m : }
287 m :
288 m : int RunAnalyzerApplication::Run() {
289 m : // If no analyzers are specified, but output layers are, we pick analyzers
290 m : // from the requested layers.
291 m : refinery::StaticAnalyzerFactory analyzer_factory;
292 m : if (!output_layers_.empty() && analyzer_names_.empty()) {
293 m : AnalyzerSet selected_analyzers;
294 m : for (const auto& layer_name : SplitStringList(output_layers_)) {
295 m : refinery::ProcessState::LayerEnum layer =
296 m : refinery::ProcessState::LayerFromName(layer_name);
297 m : if (layer == refinery::ProcessState::UnknownLayer) {
298 m : LOG(ERROR) << "Unknown layer: " << layer_name;
299 m : return 1;
300 m : }
301 m :
302 m : AnalyzerNames analyzer_names;
303 m : analyzer_factory.GetAnalyzersOutputting(layer, &analyzer_names);
304 m : for (const auto& analyzer_name : analyzer_names)
305 m : selected_analyzers.insert(analyzer_name);
306 m : }
307 m :
308 m : analyzer_names_ = JoinAnalyzerSet(selected_analyzers);
309 m : }
310 m :
311 m : if (output_layers_.empty())
312 m : output_layers_ = kDefaultOutputLayers;
313 m : if (analyzer_names_.empty())
314 m : analyzer_names_ = kDefaultAnalyzers;
315 m :
316 m : if (resolve_dependencies_ &&
317 m : !AddLayerPrerequisiteAnalyzers(analyzer_factory)) {
318 m : LOG(ERROR) << "Unable to add dependent analyzers.";
319 m : return 1;
320 m : }
321 m :
322 m : if (!OrderAnalyzers(analyzer_factory)) {
323 m : LOG(ERROR) << "Unable to order analyzers.";
324 m : return 1;
325 m : } else {
326 m : LOG(INFO) << "Using analyzer list: " << analyzer_names_;
327 m : LOG(INFO) << "Outputting layers: " << output_layers_;
328 m : }
329 m :
330 m : scoped_refptr<refinery::SymbolProvider> symbol_provider(
331 m : new refinery::SymbolProvider());
332 m : scoped_refptr<refinery::DiaSymbolProvider> dia_symbol_provider(
333 m : new refinery::DiaSymbolProvider());
334 m :
335 m : for (const auto& minidump_path : mindump_paths_) {
336 m : ::fprintf(out(), "Processing \"%ls\"\n", minidump_path.value().c_str());
337 m :
338 m : minidump::FileMinidump minidump;
339 m : if (!minidump.Open(minidump_path)) {
340 m : LOG(ERROR) << "Unable to open dump file.";
341 m : return 1;
342 m : }
343 m :
344 m : refinery::ProcessState process_state;
345 m : refinery::SimpleProcessAnalysis analysis(
346 m : &process_state, dia_symbol_provider, symbol_provider);
347 m : if (Analyze(minidump, analyzer_factory, analysis)) {
348 m : PrintProcessState(&process_state);
349 m : } else {
350 m : LOG(ERROR) << "Failure processing minidump " << minidump_path.value();
351 m : }
352 m : }
353 m :
354 m : return 0;
355 m : }
356 m :
357 m : bool RunAnalyzerApplication::AddAnalyzers(
358 m : const refinery::AnalyzerFactory& factory,
359 m : refinery::AnalysisRunner* runner) {
360 m : AnalyzerNames analyzers = SplitStringList(analyzer_names_);
361 m : for (const auto& analyzer_name : analyzers) {
362 m : std::unique_ptr<refinery::Analyzer> analyzer(
363 m : factory.CreateAnalyzer(analyzer_name));
364 m : if (analyzer) {
365 m : runner->AddAnalyzer(std::move(analyzer));
366 m : } else {
367 m : LOG(ERROR) << "No such analyzer " << analyzer_name;
368 m : return false;
369 m : }
370 m : }
371 m :
372 m : return true;
373 m : }
374 m :
375 m : bool RunAnalyzerApplication::Analyze(
376 m : const minidump::Minidump& minidump,
377 m : const refinery::AnalyzerFactory& factory,
378 m : const refinery::Analyzer::ProcessAnalysis& process_analysis) {
379 m : DCHECK(process_analysis.process_state());
380 m :
381 m : minidump::Minidump::Stream sys_info_stream =
382 m : minidump.FindNextStream(nullptr, SystemInfoStream);
383 m :
384 m : MINIDUMP_SYSTEM_INFO system_info = {};
385 m : if (!sys_info_stream.ReadAndAdvanceElement(&system_info)) {
386 m : LOG(ERROR) << "Unable to read system info stream.";
387 m : return false;
388 m : }
389 m :
390 m : VLOG(1) << base::StringPrintf("Systeminformation");
391 m : VLOG(1) << base::StringPrintf(" ProcessorArchitecture 0x%04X",
392 m : system_info.ProcessorArchitecture);
393 m : VLOG(1) << base::StringPrintf(" ProcessorLevel 0x%04X",
394 m : system_info.ProcessorLevel);
395 m : VLOG(1) << base::StringPrintf(" ProcessorRevision 0x%04X",
396 m : system_info.ProcessorRevision);
397 m : VLOG(1) << base::StringPrintf(" NumberOfProcessors %d",
398 m : system_info.NumberOfProcessors);
399 m : VLOG(1) << base::StringPrintf(" ProductType %d", system_info.ProductType);
400 m : VLOG(1) << base::StringPrintf(" MajorVersion 0x%08X",
401 m : system_info.MajorVersion);
402 m : VLOG(1) << base::StringPrintf(" MinorVersion 0x%08X",
403 m : system_info.MinorVersion);
404 m : VLOG(1) << base::StringPrintf(" BuildNumber 0x%08X",
405 m : system_info.BuildNumber);
406 m : VLOG(1) << base::StringPrintf(" PlatformId 0x%08X", system_info.PlatformId);
407 m : VLOG(1) << base::StringPrintf(" CSDVersionRva 0x%08X",
408 m : system_info.CSDVersionRva);
409 m : VLOG(1) << base::StringPrintf(" SuiteMask 0x%04X", system_info.SuiteMask);
410 m :
411 m : VLOG(1) << " CPU information:";
412 m : VLOG(1) << base::StringPrintf(" VendorId 0x%08X:0x%08X:0x%08X",
413 m : system_info.Cpu.X86CpuInfo.VendorId[0],
414 m : system_info.Cpu.X86CpuInfo.VendorId[1],
415 m : system_info.Cpu.X86CpuInfo.VendorId[1]);
416 m :
417 m : VLOG(1) << base::StringPrintf(" VersionInformation 0x%08X",
418 m : system_info.Cpu.X86CpuInfo.VersionInformation);
419 m : VLOG(1) << base::StringPrintf(" FeatureInformation 0x%08X",
420 m : system_info.Cpu.X86CpuInfo.FeatureInformation);
421 m : VLOG(1) << base::StringPrintf(
422 m : " AMDExtendedCpuFeatures 0x%08X",
423 m : system_info.Cpu.X86CpuInfo.AMDExtendedCpuFeatures);
424 m :
425 m : refinery::AnalysisRunner runner;
426 m : if (!AddAnalyzers(factory, &runner))
427 m : return false;
428 m :
429 m : return runner.Analyze(minidump, process_analysis) ==
430 m : refinery::Analyzer::ANALYSIS_COMPLETE;
431 m : }
432 m :
433 m : AnalyzerOrderer::AnalyzerOrderer(const refinery::AnalyzerFactory& factory)
434 m : : factory_(factory) {
435 m : }
436 m :
437 m : bool AnalyzerOrderer::CreateGraph(const std::string& analyzer_names) {
438 m : AnalyzerNames analyzers = SplitStringList(analyzer_names);
439 m : AnalyzerSet all_analyzers;
440 m : for (const AnalyzerName& name : analyzers)
441 m : all_analyzers.insert(name);
442 m :
443 m : // For each requested analyser, find the layers it inputs. From each of those
444 m : // layers, find the analyzers that output those layers - intersected with
445 m : // the analyzers we care about.
446 m : for (const AnalyzerName& analyzer_name : all_analyzers) {
447 m : refinery::AnalyzerFactory::Layers input_layers;
448 m : if (!factory_.GetInputLayers(analyzer_name, &input_layers))
449 m : return false;
450 m :
451 m : AnalyzerSet& dependencies = graph_[analyzer_name];
452 m : for (auto input_layer : input_layers) {
453 m : refinery::AnalyzerFactory::AnalyzerNames outputting_names;
454 m : factory_.GetAnalyzersOutputting(input_layer, &outputting_names);
455 m : for (const AnalyzerName& outputting_name : outputting_names) {
456 m : if (all_analyzers.find(outputting_name) != all_analyzers.end()) {
457 m : // Note that the graph may be circular, and it's in particular
458 m : // acceptable for analyzers to consume and produce the same layer.
459 m : // This is the case for e.g. type propagation, which propagates
460 m : // the types of pointers.
461 m : dependencies.insert(outputting_name);
462 m : }
463 m : }
464 m : }
465 m : }
466 m :
467 m : return true;
468 m : }
469 m :
470 m : std::string AnalyzerOrderer::Order() {
471 m : DCHECK(visited_.empty());
472 m : DCHECK(used_.empty());
473 m : DCHECK(ordering_.empty());
474 m :
475 m : for (const auto& node : graph_)
476 m : Visit(node.first);
477 m :
478 m : DCHECK(visited_.empty());
479 m : DCHECK_EQ(graph_.size(), used_.size());
480 m : DCHECK_EQ(graph_.size(), ordering_.size());
481 m :
482 m : return base::JoinString(ordering_, ",");
483 m : }
484 m :
485 m : void AnalyzerOrderer::Visit(const AnalyzerName& name) {
486 m : DCHECK(graph_.find(name) != graph_.end());
487 m :
488 m : if (visited_.find(name) != visited_.end())
489 m : return;
490 m :
491 m : visited_.insert(name);
492 m : for (const AnalyzerName& dep : graph_[name])
493 m : Visit(dep);
494 m :
495 m : visited_.erase(name);
496 m : if (used_.find(name) == used_.end()) {
497 m : used_.insert(name);
498 m : ordering_.push_back(name);
499 m : }
500 m : }
501 m :
502 m : } // namespace
503 m :
504 m : int main(int argc, const char* const* argv) {
505 m : base::AtExitManager at_exit_manager;
506 m : base::CommandLine::Init(argc, argv);
507 m : return application::Application<RunAnalyzerApplication>().Run();
508 m : }
|