Coverage for /Syzygy/refinery/analyzers/run_analyzer_main.cc

CoverageLines executed / instrumented / missingexe / inst / missLanguageGroup
0.0%00422.C++source

Line-by-line coverage:

   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 :  }

Coverage information generated Fri Jul 29 11:00:21 2016.