Coverage for /Syzygy/instrument/instrument_app.cc

CoverageLines executed / instrumented / missingexe / inst / missLanguageGroup
96.1%1731800.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    :  // Defines the InstrumentApp class, which implements the command-line
  16    :  // "instrument" tool.
  17    :  
  18    :  #include "syzygy/instrument/instrument_app.h"
  19    :  
  20    :  #include <algorithm>
  21    :  #include <iostream>
  22    :  
  23    :  #include "base/string_util.h"
  24    :  #include "base/stringprintf.h"
  25    :  #include "syzygy/common/indexed_frequency_data.h"
  26    :  #include "syzygy/instrument/mutators/add_indexed_data_ranges_stream.h"
  27    :  #include "syzygy/instrument/transforms/asan_transform.h"
  28    :  #include "syzygy/instrument/transforms/basic_block_entry_hook_transform.h"
  29    :  #include "syzygy/instrument/transforms/coverage_transform.h"
  30    :  #include "syzygy/instrument/transforms/entry_thunk_transform.h"
  31    :  #include "syzygy/instrument/transforms/thunk_import_references_transform.h"
  32    :  #include "syzygy/pe/decomposer.h"
  33    :  #include "syzygy/pe/image_filter.h"
  34    :  #include "syzygy/pe/pe_relinker.h"
  35    :  
  36    :  namespace instrument {
  37    :  
  38    :  namespace {
  39    :  
  40    :  using block_graph::BlockGraph;
  41    :  using pe::Decomposer;
  42    :  using pe::PEFile;
  43    :  
  44    :  static const char kUsageFormatStr[] =
  45    :      "Usage: %ls [options]\n"
  46    :      "  Required arguments:\n"
  47    :      "    --input-image=<path> The input image to instrument.\n"
  48    :      "    --mode=asan|bbentry|calltrace|coverage|profile\n"
  49    :      "                            Specifies which instrumentation mode is to\n"
  50    :      "                            be used. If this is not specified it is\n"
  51    :      "                            equivalent to specifying --mode=calltrace\n"
  52    :      "                            (this default behaviour is DEPRECATED).\n"
  53    :      "    --output-image=<path>\n"
  54    :      "                            The instrumented output image.\n"
  55    :      "  DEPRECATED options:\n"
  56    :      "    --input-dll is aliased to --input-image.\n"
  57    :      "    --output-dll is aliased to --output-image.\n"
  58    :      "    --call-trace-client=RPC\n"
  59    :      "                            Equivalent to --mode=calltrace.\n"
  60    :      "    --call-trace-client=PROFILER\n"
  61    :      "                            Equivalent to --mode=profile.\n"
  62    :      "    --call-trace-client=<path>\n"
  63    :      "                            Equivalent to --mode=calltrace\n"
  64    :      "                                 --agent=<path>.\n"
  65    :      "  General options (applicable in all modes):\n"
  66    :      "    --agent=<path>          If specified indicates exactly which DLL to\n"
  67    :      "                            use when instrumenting the provided module.\n"
  68    :      "                            If not specified a default agent library\n"
  69    :      "                            will be used. This is ignored in ASAN mode.\n"
  70    :      "    --debug-friendly        Generate more debugger friendly output by\n"
  71    :      "                            making the thunks resolve to the original\n"
  72    :      "                            function's name. This is at the cost of the\n"
  73    :      "                            uniqueness of address->name resolution.\n"
  74    :      "    --inline-fast-path      Inline a fast path into the instrumented\n"
  75    :      "                            image.\n"
  76    :      "    --input-pdb=<path>      The PDB for the DLL to instrument. If not\n"
  77    :      "                            explicitly provided will be searched for.\n"
  78    :      "    --filter=<path>         The path of the filter to be used in\n"
  79    :      "                            applying the instrumentation. Ranges marked\n"
  80    :      "                            in the filter will not be instrumented.\n"
  81    :      "    --new-decomposer        Use the new decomposer.\n"
  82    :      "    --no-augment-pdb        Indicates that the relinker should not\n"
  83    :      "                            augment the output PDB with additional.\n"
  84    :      "                            metadata.\n"
  85    :      "    --no-parse-debug-info   Do not parse debug information. Harder to\n"
  86    :      "                            debug but much more memory and time\n"
  87    :      "                            efficient. Only affects the new decomposer.\n"
  88    :      "    --no-strip-strings      Indicates that the relinker should not strip\n"
  89    :      "                            the strings when augmenting the PDB. They\n"
  90    :      "                            are stripped by default to keep PDB sizes\n"
  91    :      "                            down.\n"
  92    :      "    --output-pdb=<path>     The PDB for the instrumented DLL. If not\n"
  93    :      "                            provided will attempt to generate one.\n"
  94    :      "    --overwrite             Allow output files to be overwritten.\n"
  95    :      "  asan mode options:\n"
  96    :      "    --use-liveness-analysis Use the information provided by the liveness\n"
  97    :      "                            analysis if possible.\n"
  98    :      "    --remove-redundant-checks\n"
  99    :      "                            Perform redundancy memory access analysis\n"
 100    :      "                            and remove useless redundant checks.\n"
 101    :      "  calltrace mode options:\n"
 102    :      "    --instrument-imports    Also instrument calls to imports.\n"
 103    :      "    --module-entry-only     If specified then the per-function entry\n"
 104    :      "                            hook will not be used and only module entry\n"
 105    :      "                            points will be hooked.\n"
 106    :      "    --no-unsafe-refs        Perform no instrumentation of references\n"
 107    :      "                            between code blocks that contain anything\n"
 108    :      "                            but C/C++.\n"
 109    :      "  profile mode options:\n"
 110    :      "    --instrument-imports    Also instrument calls to imports.\n"
 111    :      "\n";
 112    :  
 113    :  }  // namespace
 114    :  
 115    :  const char InstrumentApp::kAgentDllAsan[] = "asan_rtl.dll";
 116    :  const char InstrumentApp::kAgentDllBasicBlockEntry[] =
 117    :      "basic_block_entry_client.dll";
 118    :  const char InstrumentApp::kAgentDllCoverage[] = "coverage_client.dll";
 119    :  const char InstrumentApp::kAgentDllProfile[] = "profile_client.dll";
 120    :  const char InstrumentApp::kAgentDllRpc[] = "call_trace_client.dll";
 121    :  
 122  E :  pe::PERelinker& InstrumentApp::GetRelinker() {
 123  E :    if (relinker_.get() == NULL) {
 124  E :      relinker_.reset(new pe::PERelinker());
 125  E :      CHECK(relinker_.get() != NULL);
 126    :    }
 127  E :    return *(relinker_.get());
 128  E :  }
 129    :  
 130  E :  void InstrumentApp::ParseDeprecatedMode(const CommandLine* cmd_line) {
 131  E :    DCHECK(cmd_line != NULL);
 132    :  
 133  E :    std::string client = cmd_line->GetSwitchValueASCII("call-trace-client");
 134    :  
 135  E :    if (client.empty()) {
 136  E :      LOG(INFO) << "DEPRECATED: No mode specified, using --mode=calltrace.";
 137  E :      mode_ = kInstrumentCallTraceMode;
 138  E :      agent_dll_ = kAgentDllRpc;
 139  E :      return;
 140    :    }
 141    :  
 142  E :    if (LowerCaseEqualsASCII(client, "profiler")) {
 143  E :      LOG(INFO) << "DEPRECATED: Using --mode=profile.";
 144  E :      mode_ = kInstrumentProfileMode;
 145  E :      agent_dll_ = kAgentDllProfile;
 146  E :    } else if (LowerCaseEqualsASCII(client, "rpc")) {
 147  E :      LOG(INFO) << "DEPRECATED: Using --mode=calltrace.";
 148  E :      mode_ = kInstrumentCallTraceMode;
 149  E :      agent_dll_ = kAgentDllRpc;
 150  E :    } else {
 151  E :      LOG(INFO) << "DEPRECATED: Using --mode=calltrace --agent=" << client << ".";
 152  E :      mode_ = kInstrumentCallTraceMode;
 153  E :      agent_dll_ = client;
 154    :    }
 155  E :  }
 156    :  
 157  E :  bool InstrumentApp::ParseCommandLine(const CommandLine* cmd_line) {
 158  E :    DCHECK(cmd_line != NULL);
 159    :  
 160  E :    if (cmd_line->HasSwitch("help"))
 161  E :      return Usage(cmd_line, "");
 162    :  
 163    :    // TODO(chrisha): Simplify the input/output image parsing once external
 164    :    //     tools have been updated.
 165    :  
 166    :    // Parse the input image.
 167  E :    if (cmd_line->HasSwitch("input-dll")) {
 168  E :      LOG(WARNING) << "DEPRECATED: Using --input-dll.";
 169  E :      input_dll_path_ = AbsolutePath(cmd_line->GetSwitchValuePath("input-dll"));
 170  E :    } else {
 171  E :      input_dll_path_ = AbsolutePath(cmd_line->GetSwitchValuePath("input-image"));
 172    :    }
 173    :  
 174    :    // Parse the output image.
 175  E :    if (cmd_line->HasSwitch("output-dll")) {
 176  E :      LOG(WARNING) << "DEPRECATED: Using --output-dll.";
 177  E :      output_dll_path_ = AbsolutePath(cmd_line->GetSwitchValuePath("output-dll"));
 178  E :    } else {
 179    :      output_dll_path_ = AbsolutePath(cmd_line->GetSwitchValuePath(
 180  E :          "output-image"));
 181    :    }
 182    :  
 183    :    // Parse the filter path, if one was provided.
 184  E :    filter_path_ = cmd_line->GetSwitchValuePath("filter");
 185    :  
 186    :    // Ensure that both input and output have been specified.
 187  E :    if (input_dll_path_.empty() || output_dll_path_.empty())
 188  E :      return Usage(cmd_line, "You must provide input and output file names.");
 189    :  
 190    :    // Get the mode and the default client DLL.
 191  E :    if (!cmd_line->HasSwitch("mode")) {
 192    :      // TODO(chrisha): Remove this once build scripts and profiling tools have
 193    :      //     been updated.
 194  E :      ParseDeprecatedMode(cmd_line);
 195  E :    } else {
 196  E :      std::string mode = cmd_line->GetSwitchValueASCII("mode");
 197  E :      if (LowerCaseEqualsASCII(mode, "asan")) {
 198  E :        mode_ = kInstrumentAsanMode;
 199  E :        agent_dll_ = kAgentDllAsan;
 200  E :      } else if (LowerCaseEqualsASCII(mode, "bbentry")) {
 201  E :        mode_ = kInstrumentBasicBlockEntryMode;
 202  E :        agent_dll_ = kAgentDllBasicBlockEntry;
 203  E :      } else if (LowerCaseEqualsASCII(mode, "calltrace")) {
 204  E :        mode_ = kInstrumentCallTraceMode;
 205  E :        agent_dll_ = kAgentDllRpc;
 206  E :      } else if (LowerCaseEqualsASCII(mode, "coverage")) {
 207  E :        mode_ = kInstrumentCoverageMode;
 208  E :        agent_dll_ = kAgentDllCoverage;
 209  E :      } else if (LowerCaseEqualsASCII(mode, "profile")) {
 210  E :        mode_ = kInstrumentProfileMode;
 211  E :        agent_dll_ = kAgentDllProfile;
 212  E :      } else {
 213    :        return Usage(cmd_line,
 214    :                     base::StringPrintf("Unknown instrumentation mode: %s.",
 215  i :                                        mode.c_str()).c_str());
 216    :      }
 217    :  
 218  E :      if (!agent_dll_.empty()) {
 219  E :        LOG(INFO) << "Default agent DLL for " << mode << " mode is \""
 220    :                  << agent_dll_ << "\".";
 221    :      }
 222  E :    }
 223  E :    DCHECK_NE(kInstrumentInvalidMode, mode_);
 224    :  
 225    :    // Parse the custom agent if one is specified.
 226  E :    if (cmd_line->HasSwitch("agent")) {
 227  E :      std::string new_agent_dll = cmd_line->GetSwitchValueASCII("agent");
 228  E :      if (new_agent_dll != agent_dll_) {
 229  E :        agent_dll_ = new_agent_dll;
 230  E :        LOG(INFO) << "Using custom agent DLL \"" << agent_dll_ << "\".";
 231    :      }
 232  E :    }
 233    :  
 234    :    // Parse the remaining command line arguments. Not all of these are valid in
 235    :    // all modes, but we don't care too much about ignored arguments.
 236  E :    input_pdb_path_ = AbsolutePath(cmd_line->GetSwitchValuePath("input-pdb"));
 237  E :    output_pdb_path_ = AbsolutePath(cmd_line->GetSwitchValuePath("output-pdb"));
 238  E :    allow_overwrite_ = cmd_line->HasSwitch("overwrite");
 239  E :    new_decomposer_ = cmd_line->HasSwitch("new-decomposer");
 240  E :    no_augment_pdb_ = cmd_line->HasSwitch("no-augment-pdb");
 241  E :    no_parse_debug_info_ = cmd_line->HasSwitch("no-parse-debug-info");
 242  E :    no_strip_strings_ = cmd_line->HasSwitch("no-strip-strings");
 243  E :    debug_friendly_ = cmd_line->HasSwitch("debug-friendly");
 244  E :    thunk_imports_ = cmd_line->HasSwitch("instrument-imports");
 245  E :    use_liveness_analysis_ = cmd_line->HasSwitch("use-liveness-analysis");
 246  E :    remove_redundant_checks_ = cmd_line->HasSwitch("remove-redundant-checks");
 247  E :    instrument_unsafe_references_ = !cmd_line->HasSwitch("no-unsafe-refs");
 248  E :    module_entry_only_ = cmd_line->HasSwitch("module-entry-only");
 249  E :    inline_fast_path_ = cmd_line->HasSwitch("inline-fast-path");
 250    :  
 251    :    // Set per-mode overrides as necessary.
 252  E :    switch (mode_) {
 253    :      case kInstrumentBasicBlockEntryMode:
 254    :      case kInstrumentCoverageMode: {
 255  E :        thunk_imports_ = false;
 256  E :        instrument_unsafe_references_ = false;
 257  E :        module_entry_only_ = true;
 258  E :      } break;
 259    :  
 260    :      case kInstrumentProfileMode: {
 261  E :        instrument_unsafe_references_ = false;
 262  E :        module_entry_only_ = false;
 263    :      } break;
 264    :  
 265    :      default: break;
 266    :    }
 267    :  
 268  E :    return true;
 269  E :  }
 270    :  
 271  E :  int InstrumentApp::Run() {
 272  E :    DCHECK_NE(kInstrumentInvalidMode, mode_);
 273    :  
 274  E :    pe::PERelinker& relinker = GetRelinker();
 275  E :    relinker.set_input_path(input_dll_path_);
 276  E :    relinker.set_input_pdb_path(input_pdb_path_);
 277  E :    relinker.set_output_path(output_dll_path_);
 278  E :    relinker.set_output_pdb_path(output_pdb_path_);
 279  E :    relinker.set_allow_overwrite(allow_overwrite_);
 280  E :    relinker.set_augment_pdb(!no_augment_pdb_);
 281  E :    relinker.set_parse_debug_info(!no_parse_debug_info_);
 282  E :    relinker.set_use_new_decomposer(new_decomposer_);
 283  E :    relinker.set_strip_strings(!no_strip_strings_);
 284    :  
 285    :    // Parse the filter if one was provided.
 286  E :    scoped_ptr<pe::ImageFilter> filter;
 287  E :    bool filter_used = false;
 288  E :    if (!filter_path_.empty()) {
 289  E :      filter.reset(new pe::ImageFilter());
 290  E :      if (!filter->LoadFromJSON(filter_path_)) {
 291  i :        LOG(ERROR) << "Failed to parse filter file: " << filter_path_.value();
 292  i :        return 1;
 293    :      }
 294    :  
 295    :      // Ensure it is for the input module.
 296  E :      if (!filter->IsForModule(input_dll_path_)) {
 297  E :        LOG(ERROR) << "Filter does not match the input module.";
 298  E :        return 1;
 299    :      }
 300    :    }
 301    :  
 302    :    // Initialize the relinker. This does the decomposition, etc.
 303  E :    if (!relinker.Init()) {
 304  E :      LOG(ERROR) << "Failed to initialize relinker.";
 305  E :      return 1;
 306    :    }
 307    :  
 308    :    // A list of all possible transforms that we will need.
 309  E :    scoped_ptr<instrument::transforms::AsanTransform> asan_transform;
 310    :    scoped_ptr<instrument::transforms::BasicBlockEntryHookTransform>
 311  E :        basic_block_entry_transform;
 312  E :    scoped_ptr<instrument::transforms::EntryThunkTransform> entry_thunk_tx;
 313    :    scoped_ptr<instrument::transforms::ThunkImportReferencesTransform>
 314  E :        import_thunk_tx;
 315    :    scoped_ptr<instrument::transforms::CoverageInstrumentationTransform>
 316  E :        coverage_tx;
 317    :    scoped_ptr<instrument::mutators::AddIndexedDataRangesStreamPdbMutator>
 318  E :        add_bb_addr_stream_mutator;
 319    :  
 320    :    // We are instrumenting in ASAN mode.
 321  E :    if (mode_ == kInstrumentAsanMode) {
 322  E :      asan_transform.reset(new instrument::transforms::AsanTransform);
 323  E :      asan_transform->set_instrument_dll_name(agent_dll_);
 324  E :      asan_transform->set_use_liveness_analysis(use_liveness_analysis_);
 325  E :      asan_transform->set_remove_redundant_checks(remove_redundant_checks_);
 326    :  
 327    :      // Set up the filter if one was provided.
 328  E :      if (filter.get()) {
 329  E :        asan_transform->set_filter(&filter->filter);
 330  E :        filter_used = true;
 331    :      }
 332    :  
 333    :      // Set overwrite source range flag in the ASAN transform. The ASAN
 334    :      // transformation will overwrite the source range of created instructions to
 335    :      // the source range of corresponding instrumented instructions.
 336  E :      asan_transform->set_debug_friendly(debug_friendly_);
 337    :  
 338  E :      relinker.AppendTransform(asan_transform.get());
 339  E :    } else if (mode_ == kInstrumentBasicBlockEntryMode) {
 340    :      // If we're in basic-block-entry mode, we need to apply the basic block
 341    :      // entry hook transform (which adds basic-block frequency structures to
 342    :      // the image and thunks the entry points) and we need to augment the PDB
 343    :      // file with the basic block addresses.
 344    :      basic_block_entry_transform.reset(
 345  E :          new instrument::transforms::BasicBlockEntryHookTransform);
 346  E :      basic_block_entry_transform->set_instrument_dll_name(agent_dll_);
 347  E :      basic_block_entry_transform->set_src_ranges_for_thunks(debug_friendly_);
 348  E :      basic_block_entry_transform->set_inline_fast_path(inline_fast_path_);
 349  E :      relinker.AppendTransform(basic_block_entry_transform.get());
 350    :  
 351    :      add_bb_addr_stream_mutator.reset(
 352    :          new instrument::mutators::AddIndexedDataRangesStreamPdbMutator(
 353    :              basic_block_entry_transform->bb_ranges(),
 354  E :              common::kBasicBlockRangesStreamName));
 355  E :      relinker.AppendPdbMutator(add_bb_addr_stream_mutator.get());
 356  E :    } else if (mode_ == kInstrumentCoverageMode) {
 357    :      // If we're in coverage mode, we need to add coverage structures to
 358    :      // the image and we need to augment the PDB file with the basic block
 359    :      // addresses.
 360    :      coverage_tx.reset(
 361  E :          new instrument::transforms::CoverageInstrumentationTransform);
 362  E :      coverage_tx->set_instrument_dll_name(agent_dll_);
 363  E :      coverage_tx->set_src_ranges_for_thunks(debug_friendly_);
 364  E :      relinker.AppendTransform(coverage_tx.get());
 365    :  
 366    :      add_bb_addr_stream_mutator.reset(
 367    :          new instrument::mutators::AddIndexedDataRangesStreamPdbMutator(
 368    :              coverage_tx->bb_ranges(),
 369  E :              common::kBasicBlockRangesStreamName));
 370  E :      relinker.AppendPdbMutator(add_bb_addr_stream_mutator.get());
 371  E :    } else {
 372    :      // We're either in calltrace mode or profile mode. Each of these
 373    :      // use the entry_thunk_tx, so we handle them in the same manner.
 374    :      DCHECK(mode_ == kInstrumentCallTraceMode ||
 375  E :             mode_ == kInstrumentProfileMode);
 376    :  
 377    :      // Set up the entry thunk instrumenting transform and add it to the
 378    :      // relinker.
 379  E :      entry_thunk_tx.reset(new instrument::transforms::EntryThunkTransform);
 380  E :      entry_thunk_tx->set_instrument_dll_name(agent_dll_);
 381    :      entry_thunk_tx->set_instrument_unsafe_references(
 382  E :          instrument_unsafe_references_);
 383  E :      entry_thunk_tx->set_src_ranges_for_thunks(debug_friendly_);
 384  E :      entry_thunk_tx->set_only_instrument_module_entry(module_entry_only_);
 385  E :      relinker.AppendTransform(entry_thunk_tx.get());
 386    :  
 387    :      // If we are thunking imports then add the appropriate transform.
 388  E :      if (thunk_imports_) {
 389    :        import_thunk_tx.reset(
 390  i :            new instrument::transforms::ThunkImportReferencesTransform);
 391    :        // Use the selected client DLL.
 392  i :        import_thunk_tx->set_instrument_dll_name(agent_dll_);
 393  i :        relinker.AppendTransform(import_thunk_tx.get());
 394    :      }
 395    :    }
 396    :  
 397    :    // If a filter was provided but not used output a warning.
 398  E :    if (filter.get() && !filter_used)
 399  i :      LOG(WARNING) << "Not using provided filter.";
 400    :  
 401    :    // We let the PERelinker use the implicit OriginalOrderer.
 402  E :    if (!relinker.Relink()) {
 403  E :      LOG(ERROR) << "Unable to relink input image.";
 404  E :      return 1;
 405    :    }
 406    :  
 407  E :    return 0;
 408  E :  }
 409    :  
 410    :  bool InstrumentApp::Usage(const CommandLine* cmd_line,
 411  E :                            const base::StringPiece& message) const {
 412  E :    if (!message.empty()) {
 413  E :      ::fwrite(message.data(), 1, message.length(), err());
 414  E :      ::fprintf(err(), "\n\n");
 415    :    }
 416    :  
 417    :    ::fprintf(err(),
 418    :              kUsageFormatStr,
 419  E :              cmd_line->GetProgram().BaseName().value().c_str());
 420    :  
 421  E :    return false;
 422  E :  }
 423    :  
 424    :  }  // namespace instrument

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