Coverage for /Syzygy/reorder/reorder_app.cc

CoverageLines executed / instrumented / missingexe / inst / missLanguageGroup
82.8%1111340.C++source

Line-by-line coverage:

   1    :  // Copyright 2012 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    :  // Parses a module and ETW trace files, generating an ordering of the
  16    :  // blocks in the decomposed image.
  17    :  
  18    :  #include "syzygy/reorder/reorder_app.h"
  19    :  
  20    :  #include <objbase.h>
  21    :  #include <iostream>
  22    :  
  23    :  #include "base/string_number_conversions.h"
  24    :  #include "base/string_split.h"
  25    :  #include "base/stringprintf.h"
  26    :  #include "syzygy/grinder/basic_block_entry_count_serializer.h"
  27    :  #include "syzygy/grinder/basic_block_util.h"
  28    :  #include "syzygy/pe/find.h"
  29    :  #include "syzygy/reorder/basic_block_optimizer.h"
  30    :  #include "syzygy/reorder/dead_code_finder.h"
  31    :  #include "syzygy/reorder/linear_order_generator.h"
  32    :  #include "syzygy/reorder/random_order_generator.h"
  33    :  
  34    :  namespace reorder {
  35    :  
  36    :  namespace {
  37    :  
  38    :  using grinder::basic_block_util::EntryCountMap;
  39    :  using grinder::basic_block_util::ModuleEntryCountMap;
  40    :  using grinder::basic_block_util::LoadBasicBlockRanges;
  41    :  using grinder::basic_block_util::FindEntryCountMap;
  42    :  using grinder::basic_block_util::RelativeAddressRangeVector;
  43    :  using grinder::BasicBlockEntryCountSerializer;
  44    :  
  45    :  static const char kUsageFormatStr[] =
  46    :      "Usage: %s [options] [log files ...]\n"
  47    :      "  Required Options:\n"
  48    :      "    --instrumented-image=<path> the path to the instrumented image file.\n"
  49    :      "    --output-file=<path> the output file.\n"
  50    :      "  Optional Options:\n"
  51    :      "    --input-image=<path> the input image file to reorder. If this is not\n"
  52    :      "        specified it will be inferred from the instrumented image's\n"
  53    :      "        metadata.\n"
  54    :      "    --basic-block-entry-counts=PATH the path to the JSON file containing\n"
  55    :      "        the summary basic-block entry counts for the image. If this is\n"
  56    :      "        given then the input image is also required.\n"
  57    :      "    --seed=INT generates a random ordering; don't specify ETW log files.\n"
  58    :      "    --list-dead-code instead of an ordering, output the set of functions\n"
  59    :      "        not visited during the trace.\n"
  60    :      "    --pretty-print enables pretty printing of the JSON output file.\n"
  61    :      "    --reorderer-flags=<comma separated reorderer flags>\n"
  62    :      "  Reorderer Flags:\n"
  63    :      "    no-code: Do not reorder code sections.\n"
  64    :      "    no-data: Do not reorder data sections.\n"
  65    :      "  Deprecated Options:\n"
  66    :      "    --instrumented-dll=<path> aliases to --instrumented-image.\n"
  67    :      "    --input-dll=<path> aliases to --input-image.\n";
  68    :  
  69    :  // Parses reorderer flags. Returns true on success, false otherwise. On
  70    :  // failure, also outputs Usage with an error message.
  71  E :  bool ParseFlags(const std::string& flags_str, Reorderer::Flags* flags) {
  72  E :    DCHECK(flags != NULL);
  73    :  
  74    :    // Set the default flags value.
  75    :    Reorderer::Flags out_flags =
  76  E :        Reorderer::kFlagReorderData | Reorderer::kFlagReorderCode;
  77    :  
  78    :    // If there is a string to process then extract its flags.
  79  E :    if (!flags_str.empty()) {
  80    :      typedef std::vector<std::string> StringVector;
  81  E :      StringVector text_flags;
  82  E :      base::SplitString(flags_str, ',', &text_flags);
  83  E :      StringVector::const_iterator flag_iter = text_flags.begin();
  84  E :      for (; flag_iter != text_flags.end(); ++flag_iter) {
  85  E :        if (*flag_iter == "no-data") {
  86  E :          out_flags &= ~Reorderer::kFlagReorderData;
  87  E :        } else if (*flag_iter == "no-code") {
  88  E :          out_flags &= ~Reorderer::kFlagReorderCode;
  89  E :        } else if (!flag_iter->empty()) {
  90  E :          LOG(ERROR) << "Unknown reorderer flag: " << *flag_iter << ".";
  91  E :          return false;
  92    :        }
  93  E :      }
  94  E :    }
  95    :  
  96    :    // Set the return value.
  97  E :    *flags = out_flags;
  98    :  
  99  E :    return true;
 100  E :  }
 101    :  
 102    :  }  // namespace
 103    :  
 104    :  const char ReorderApp::kInstrumentedImage[] = "instrumented-image";
 105    :  const char ReorderApp::kOutputFile[] = "output-file";
 106    :  const char ReorderApp::kInputImage[] = "input-image";
 107    :  const char ReorderApp::kBasicBlockEntryCounts[] = "basic-block-entry-counts";
 108    :  const char ReorderApp::kSeed[] = "seed";
 109    :  const char ReorderApp::kListDeadCode[] = "list-dead-code";
 110    :  const char ReorderApp::kPrettyPrint[] = "pretty-print";
 111    :  const char ReorderApp::kReordererFlags[] = "reorderer-flags";
 112    :  const char ReorderApp::kInstrumentedDll[] = "instrumented-dll";
 113    :  const char ReorderApp::kInputDll[] = "input-dll";
 114    :  
 115    :  ReorderApp::ReorderApp()
 116    :      : AppImplBase("Reorder"),
 117    :        mode_(kInvalidMode),
 118    :        seed_(0),
 119    :        pretty_print_(false),
 120  E :        flags_(0) {
 121  E :  }
 122    :  
 123  E :  bool ReorderApp::ParseCommandLine(const CommandLine* command_line) {
 124  E :    DCHECK(command_line != NULL);
 125  E :    DCHECK_EQ(kInvalidMode, mode_);
 126    :  
 127    :    // Parse the instrumented image path.
 128    :    if (!GetDeprecatedSwitch(command_line,
 129    :                             kInstrumentedImage,
 130    :                             kInstrumentedDll,
 131    :                             &CommandLine::GetSwitchValuePath,
 132    :                             &instrumented_image_path_) ||
 133  E :        instrumented_image_path_.empty()) {
 134  E :      return Usage(command_line, "Invalid or missing instrumented image path.");
 135    :    }
 136    :  
 137    :    // Parse the output file path.
 138  E :    output_file_path_ = command_line->GetSwitchValuePath(kOutputFile);
 139  E :    if (output_file_path_.empty()) {
 140  i :      return Usage(command_line, "Invalid or missing output file path.");
 141    :    }
 142    :  
 143    :    // Parse the (optional) input image path.
 144    :    if (!GetDeprecatedSwitch(command_line,
 145    :                             kInputImage,
 146    :                             kInputDll,
 147    :                             &CommandLine::GetSwitchValuePath,
 148  E :                             &input_image_path_)) {
 149  i :      return Usage(command_line, "Invalid input image path.");
 150    :    }
 151    :  
 152    :    bb_entry_count_file_path_ =
 153  E :        command_line->GetSwitchValuePath(kBasicBlockEntryCounts);
 154    :  
 155    :    // Parse the reorderer flags.
 156  E :    std::string flags_str(command_line->GetSwitchValueASCII(kReordererFlags));
 157  E :    if (!ParseFlags(flags_str, &flags_)) {
 158  E :      return Usage(command_line, "Invalid reorderer flags");
 159    :    }
 160    :  
 161    :    // Parse the pretty-print switch.
 162  E :    pretty_print_ = command_line->HasSwitch(kPrettyPrint);
 163    :  
 164    :    // Make all of the input paths absolute.
 165  E :    input_image_path_ = AbsolutePath(input_image_path_);
 166  E :    instrumented_image_path_ = AbsolutePath(instrumented_image_path_);
 167  E :    output_file_path_ = AbsolutePath(output_file_path_);
 168  E :    bb_entry_count_file_path_ = AbsolutePath(bb_entry_count_file_path_);
 169    :  
 170    :    // Capture the (possibly empty) set of trace files to read.
 171  E :    for (size_t i = 0; i < command_line->GetArgs().size(); ++i) {
 172  E :      const FilePath pattern(command_line->GetArgs()[i]);
 173  E :      if (!AppendMatchingPaths(pattern, &trace_file_paths_)) {
 174  i :        LOG(ERROR) << "Found no files matching '" << pattern.value() << "'.";
 175  i :        return Usage(command_line, "");
 176    :      }
 177  E :    }
 178    :  
 179    :    // Check if we are in random order mode. Look for and parse --seed.
 180  E :    if (command_line->HasSwitch(kSeed)) {
 181  E :      if (!trace_file_paths_.empty()) {
 182    :        return Usage(command_line,
 183  i :                     "Trace files are not accepted in random order mode.");
 184    :      }
 185  E :      std::string seed_str(command_line->GetSwitchValueASCII(kSeed));
 186  E :      int tmp_seed = 0;
 187  E :      if (seed_str.empty() || !base::StringToInt(seed_str, &tmp_seed)) {
 188  E :        return Usage(command_line, "Invalid seed value.");
 189    :      }
 190  E :      seed_ = tmp_seed;
 191  E :      mode_ = kRandomOrderMode;
 192  E :    }
 193    :  
 194    :    // Parse the list-dead-code switch.
 195  E :    if (command_line->HasSwitch(kListDeadCode)) {
 196  E :      if (mode_ != kInvalidMode) {
 197  E :        LOG(ERROR) << "--" << kListDeadCode << " and --" << kSeed << "=N are "
 198    :                   << "mutually exclusive.";
 199  E :        return false;
 200    :      }
 201  E :      mode_ = kDeadCodeFinderMode;
 202    :    }
 203    :  
 204    :    // If we haven't found anything to over-ride the default mode (linear order),
 205    :    // then the default it is.
 206  E :    if (mode_ == kInvalidMode)
 207  E :      mode_ = kLinearOrderMode;
 208    :  
 209    :    // We do not accept trace file paths in random order mode.
 210  E :    if (mode_ != kRandomOrderMode && trace_file_paths_.empty()) {
 211    :      return Usage(command_line,
 212    :                   "At least one trace file is required when not in random "
 213  E :                   "order mode.");
 214    :    }
 215    :  
 216    :    // We only accept a basic-block entry count file in linear order mode, and
 217    :    // we require the input image path when we do so.
 218  E :    if (!bb_entry_count_file_path_.empty()) {
 219  E :      if (mode_ != kLinearOrderMode) {
 220    :        return Usage(command_line,
 221    :                     "A basic-block entry counts file is only accepted in linear "
 222  i :                     "order mode.");
 223    :      }
 224  E :      if (input_image_path_.empty()) {
 225    :        return Usage(command_line,
 226    :                     "The input image is required for basic-block level "
 227  i :                     "optimization.");
 228    :      }
 229    :    }
 230    :  
 231    :    // If we get here then the command-line switches were valid.
 232  E :    return true;
 233  E :  }
 234    :  
 235  E :  bool ReorderApp::SetUp() {
 236  E :    switch (mode_) {
 237    :      case kLinearOrderMode:
 238  E :        order_generator_.reset(new LinearOrderGenerator());
 239  E :        return true;
 240    :  
 241    :      case kRandomOrderMode:
 242  E :        order_generator_.reset(new RandomOrderGenerator(seed_));
 243  E :        return true;
 244    :  
 245    :      case kDeadCodeFinderMode:
 246  E :        order_generator_.reset(new DeadCodeFinder());
 247  E :        return true;
 248    :    }
 249    :  
 250  i :    NOTREACHED();
 251  i :    return false;
 252  E :  }
 253    :  
 254  E :  int ReorderApp::Run() {
 255  E :    pe::PEFile input_image;
 256  E :    block_graph::BlockGraph block_graph;
 257  E :    pe::ImageLayout image_layout(&block_graph);
 258  E :    Reorderer::Order order;
 259    :    Reorderer reorderer(input_image_path_,
 260    :                        instrumented_image_path_,
 261    :                        trace_file_paths_,
 262  E :                        flags_);
 263    :  
 264    :    // Generate a block-level ordering.
 265    :    if (!reorderer.Reorder(order_generator_.get(),
 266    :                           &order,
 267    :                           &input_image,
 268  E :                           &image_layout)) {
 269  i :      LOG(ERROR) << "Reorder failed.";
 270  i :      return 1;
 271    :    }
 272    :  
 273    :    // Basic-block optimize the resulting order if there is an entry count file.
 274  E :    if (mode_ == kLinearOrderMode && !bb_entry_count_file_path_.empty()) {
 275  E :      pe::PEFile::Signature signature;
 276  E :      input_image.GetSignature(&signature);
 277  E :      if (!OptimizeBasicBlocks(signature, image_layout, &order)) {
 278  i :        LOG(ERROR) << "Basic-block optimization failed.";
 279  i :        return 1;
 280    :      }
 281  E :    }
 282    :  
 283    :    // Serialize the order to JSON.
 284  E :    if (!order.SerializeToJSON(input_image, output_file_path_, pretty_print_)) {
 285  i :      LOG(ERROR) << "Unable to output order.";
 286  i :      return 1;
 287    :    }
 288    :  
 289    :    // We were successful.
 290  E :    return 0;
 291  E :  }
 292    :  
 293    :  bool ReorderApp::Usage(const CommandLine* cmd_line,
 294  E :                         const base::StringPiece& message) const {
 295  E :    if (!message.empty()) {
 296  E :      ::fwrite(message.data(), 1, message.length(), err());
 297  E :      ::fprintf(err(), "\n\n");
 298    :    }
 299    :  
 300    :    ::fprintf(err(),
 301    :              kUsageFormatStr,
 302  E :              cmd_line->GetProgram().BaseName().value().c_str());
 303    :  
 304  E :    return false;
 305  E :  }
 306    :  
 307    :  bool ReorderApp::OptimizeBasicBlocks(const pe::PEFile::Signature& signature,
 308    :                                       const pe::ImageLayout& image_layout,
 309  E :                                       Reorderer::Order* order) {
 310  E :    DCHECK(order != NULL);
 311    :  
 312  E :    LOG(INFO) << "Performing basic block ordering.";
 313    :  
 314    :    // Load the basic-block entry count data.
 315  E :    ModuleEntryCountMap module_entry_count_map;
 316  E :    BasicBlockEntryCountSerializer serializer;
 317    :    if (!serializer.LoadFromJson(bb_entry_count_file_path_,
 318  E :                                 &module_entry_count_map)) {
 319  i :      LOG(ERROR) << "Failed to load basic-block entry count data";
 320  i :      return false;
 321    :    }
 322    :  
 323  E :    const EntryCountMap* entry_counts = NULL;
 324  E :    if (!FindEntryCountMap(signature, module_entry_count_map, &entry_counts)) {
 325  i :      LOG(ERROR) << "Failed to find entry count vector for '"
 326    :                 << signature.path << "'.";
 327  i :      return false;
 328    :    }
 329    :  
 330    :    // Find the PDB file for the module.
 331  E :    FilePath pdb_path;
 332    :    if (!pe::FindPdbForModule(instrumented_image_path_, &pdb_path) ||
 333  E :        pdb_path.empty()) {
 334  i :      LOG(ERROR) << "Failed to find PDB for instrumented image: "
 335    :                 << instrumented_image_path_.value();
 336  i :      return false;
 337    :    }
 338    :  
 339    :    // Optimize the ordering for at the basic-block level.
 340  E :    BasicBlockOptimizer optimizer;
 341  E :    if (!optimizer.Optimize(image_layout, *entry_counts, order)) {
 342  i :      LOG(ERROR) << "Failed to optimize basic-block ordering.";
 343  i :      return false;
 344    :    }
 345    :  
 346  E :    return true;
 347  E :  }
 348    :  
 349    :  }  // namespace reorder

Coverage information generated Thu Mar 14 11:53:36 2013.