Coverage for /Syzygy/reorder/reorder_app.cc

CoverageLines executed / instrumented / missingexe / inst / missLanguageGroup
83.8%1091300.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/stringprintf.h"
  25    :  #include "base/strings/string_split.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: %ls [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 base::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  E :                     "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  i :                   "Trace files are not accepted in random order mode.");
 213    :    }
 214    :  
 215    :    // We only accept a basic-block entry count file in linear order mode, and
 216    :    // we require the input image path when we do so.
 217  E :    if (!bb_entry_count_file_path_.empty()) {
 218  E :      if (mode_ != kLinearOrderMode) {
 219    :        return Usage(command_line,
 220    :                     "A basic-block entry counts file is only accepted in linear "
 221  i :                     "order mode.");
 222    :      }
 223  E :      if (input_image_path_.empty()) {
 224    :        return Usage(command_line,
 225    :                     "The input image is required for basic-block level "
 226  i :                     "optimization.");
 227    :      }
 228    :    }
 229    :  
 230    :    // If we get here then the command-line switches were valid.
 231  E :    return true;
 232  E :  }
 233    :  
 234  E :  bool ReorderApp::SetUp() {
 235  E :    switch (mode_) {
 236    :      case kLinearOrderMode:
 237  E :        order_generator_.reset(new LinearOrderGenerator());
 238  E :        return true;
 239    :  
 240    :      case kRandomOrderMode:
 241  E :        order_generator_.reset(new RandomOrderGenerator(seed_));
 242  E :        return true;
 243    :  
 244    :      case kDeadCodeFinderMode:
 245  E :        order_generator_.reset(new DeadCodeFinder());
 246  E :        return true;
 247    :    }
 248    :  
 249  i :    NOTREACHED();
 250  i :    return false;
 251  E :  }
 252    :  
 253  E :  int ReorderApp::Run() {
 254  E :    pe::PEFile input_image;
 255  E :    block_graph::BlockGraph block_graph;
 256  E :    pe::ImageLayout image_layout(&block_graph);
 257  E :    Reorderer::Order order;
 258    :    Reorderer reorderer(input_image_path_,
 259    :                        instrumented_image_path_,
 260    :                        trace_file_paths_,
 261  E :                        flags_);
 262    :  
 263    :    // Generate a block-level ordering.
 264    :    if (!reorderer.Reorder(order_generator_.get(),
 265    :                           &order,
 266    :                           &input_image,
 267  E :                           &image_layout)) {
 268  i :      LOG(ERROR) << "Reorder failed.";
 269  i :      return 1;
 270    :    }
 271    :  
 272    :    // Basic-block optimize the resulting order if there is an entry count file.
 273  E :    if (mode_ == kLinearOrderMode && !bb_entry_count_file_path_.empty()) {
 274  E :      pe::PEFile::Signature signature;
 275  E :      input_image.GetSignature(&signature);
 276  E :      if (!OptimizeBasicBlocks(signature, image_layout, &order)) {
 277  i :        LOG(ERROR) << "Basic-block optimization failed.";
 278  i :        return 1;
 279    :      }
 280  E :    }
 281    :  
 282    :    // Serialize the order to JSON.
 283  E :    if (!order.SerializeToJSON(input_image, output_file_path_, pretty_print_)) {
 284  i :      LOG(ERROR) << "Unable to output order.";
 285  i :      return 1;
 286    :    }
 287    :  
 288    :    // We were successful.
 289  E :    return 0;
 290  E :  }
 291    :  
 292    :  bool ReorderApp::Usage(const CommandLine* cmd_line,
 293  E :                         const base::StringPiece& message) const {
 294  E :    if (!message.empty()) {
 295  E :      ::fwrite(message.data(), 1, message.length(), err());
 296  E :      ::fprintf(err(), "\n\n");
 297    :    }
 298    :  
 299    :    ::fprintf(err(),
 300    :              kUsageFormatStr,
 301  E :              cmd_line->GetProgram().BaseName().value().c_str());
 302    :  
 303  E :    return false;
 304  E :  }
 305    :  
 306    :  bool ReorderApp::OptimizeBasicBlocks(const pe::PEFile::Signature& signature,
 307    :                                       const pe::ImageLayout& image_layout,
 308  E :                                       Reorderer::Order* order) {
 309  E :    DCHECK(order != NULL);
 310    :  
 311  E :    LOG(INFO) << "Performing basic block ordering.";
 312    :  
 313    :    // Load the basic-block entry count data.
 314  E :    ModuleEntryCountMap module_entry_count_map;
 315  E :    BasicBlockEntryCountSerializer serializer;
 316    :    if (!serializer.LoadFromJson(bb_entry_count_file_path_,
 317  E :                                 &module_entry_count_map)) {
 318  i :      LOG(ERROR) << "Failed to load basic-block entry count data";
 319  i :      return false;
 320    :    }
 321    :  
 322  E :    const EntryCountMap* entry_counts = NULL;
 323  E :    if (!FindEntryCountMap(signature, module_entry_count_map, &entry_counts)) {
 324  i :      LOG(ERROR) << "Failed to find entry count vector for '"
 325    :                 << signature.path << "'.";
 326  i :      return false;
 327    :    }
 328    :  
 329    :    // Optimize the ordering at the basic-block level.
 330  E :    BasicBlockOptimizer optimizer;
 331  E :    if (!optimizer.Optimize(image_layout, *entry_counts, order)) {
 332  i :      LOG(ERROR) << "Failed to optimize basic-block ordering.";
 333  i :      return false;
 334    :    }
 335    :  
 336  E :    return true;
 337  E :  }
 338    :  
 339    :  }  // namespace reorder

Coverage information generated Thu Jul 04 09:34:53 2013.