blob: 0feea5f8ab522ed06d5c1df9f5bff5227b0cc702 [file] [log] [blame]
// Copyright 2012 Google Inc. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
// Defines the InstrumentApp class, which implements the command-line
// "instrument" tool.
#include "syzygy/instrument/instrument_app.h"
#include <algorithm>
#include <iostream>
#include <string>
#include "base/strings/string_util.h"
#include "base/strings/stringprintf.h"
#include "syzygy/instrument/instrumenters/afl_instrumenter.h"
#include "syzygy/instrument/instrumenters/archive_instrumenter.h"
#include "syzygy/instrument/instrumenters/asan_instrumenter.h"
#include "syzygy/instrument/instrumenters/bbentry_instrumenter.h"
#include "syzygy/instrument/instrumenters/branch_instrumenter.h"
#include "syzygy/instrument/instrumenters/coverage_instrumenter.h"
#include "syzygy/instrument/instrumenters/entry_call_instrumenter.h"
#include "syzygy/instrument/instrumenters/entry_thunk_instrumenter.h"
#include "syzygy/instrument/instrumenters/flummox_instrumenter.h"
namespace instrument {
namespace {
static const char kUsageFormatStr[] =
"Usage: %ls [options]\n"
" Required arguments:\n"
" --input-image=<path> The input image to instrument.\n"
" --mode=afl|asan|bbentry|branch|calltrace|coverage|flummox|profile\n"
" Specifies which instrumentation mode is to\n"
" be used. If this is not specified it is\n"
" equivalent to specifying --mode=calltrace\n"
" (this default behaviour is DEPRECATED).\n"
" --output-image=<path>\n"
" The instrumented output image.\n"
" DEPRECATED options:\n"
" --input-dll is aliased to --input-image.\n"
" --output-dll is aliased to --output-image.\n"
" --call-trace-client=RPC\n"
" Equivalent to --mode=calltrace.\n"
" --call-trace-client=PROFILER\n"
" Equivalent to --mode=profile.\n"
" --call-trace-client=<path>\n"
" Equivalent to --mode=calltrace\n"
" --agent=<path>.\n"
" General options (applicable in all modes):\n"
" --agent=<path> If specified indicates exactly which DLL to\n"
" use when instrumenting the provided module.\n"
" If not specified a default agent library\n"
" will be used. This is ignored in Asan mode.\n"
" --debug-friendly Generate more debugger friendly output by\n"
" making the thunks resolve to the original\n"
" function's name. This is at the cost of the\n"
" uniqueness of address->name resolution.\n"
" --inline-fast-path Inline a fast path into the instrumented\n"
" image.\n"
" --input-pdb=<path> The PDB for the DLL to instrument. If not\n"
" explicitly provided will be searched for.\n"
" --filter=<path> The path of the filter to be used in\n"
" applying the instrumentation. Ranges marked\n"
" in the filter will not be instrumented.\n"
" --no-augment-pdb Indicates that the relinker should not\n"
" augment the output PDB with additional.\n"
" metadata.\n"
" --no-strip-strings Indicates that the relinker should not strip\n"
" the strings when augmenting the PDB. They\n"
" are stripped by default to keep PDB sizes\n"
" down.\n"
" --output-pdb=<path> The PDB for the instrumented DLL. If not\n"
" provided will attempt to generate one.\n"
" --overwrite Allow output files to be overwritten.\n"
" afl options:\n"
" --config=<path> Specifies a JSON file describing, either\n"
" --cookie-check-hook Hooks __security_cookie_check.\n"
" --force-decompose Forces block decomposition.\n"
" --multithread Uses a thread-safe instrumentation.\n"
" a whitelist of functions to instrument or\n"
" a blacklist of functions to not instrument.\n"
" asan mode options:\n"
" --asan-rtl-options=OPTIONS\n"
" Allows specification of options that will\n"
" influence the Asan RTL that attaches to the\n"
" instrumented module. For descriptions of\n"
" these options see common/asan_parameters. If\n"
" not specified then the defaults of the RTL\n"
" will be used.\n"
" --hot-patching Use hot patching Asan instrumentation.\n"
" --instrumentation-rate=DOUBLE\n"
" Specifies the fraction of instructions to\n"
" be instrumented, as a value in the range\n"
" 0..1, inclusive. Defaults to 1.\n"
" --no-interceptors Disable the interception of the functions\n"
" like memset, memcpy, stcpy, ReadFile... to\n"
" check their parameters.\n"
" --no-liveness-analysis Disables register and flags liveness\n"
" analysis.\n"
" --no-redundancy-analysis\n"
" Disables redundant memory access analysis.\n"
" branch mode options:\n"
" --buffering Enable per-thread buffering of events.\n"
" --fs-slot=<slot> Specify which FS slot to use for thread\n"
" local storage.\n"
" calltrace mode options:\n"
" --instrument-imports Also instrument calls to imports.\n"
" --module-entry-only If specified then the per-function entry\n"
" hook will not be used and only module entry\n"
" points will be hooked.\n"
" --no-unsafe-refs Perform no instrumentation of references\n"
" between code blocks that contain anything\n"
" but C/C++.\n"
" profile mode options:\n"
" --instrument-imports Also instrument calls to imports.\n"
"\n";
// Currently only Asan supports COFF/LIB instrumentation. As other
// instrumenters add COFF support they need to be added with a similar
// mechanism.
InstrumenterInterface* AsanInstrumenterFactory() {
return new instrumenters::AsanInstrumenter();
}
} // namespace
void InstrumentApp::ParseDeprecatedMode(const base::CommandLine* cmd_line) {
DCHECK(cmd_line != NULL);
std::string client = cmd_line->GetSwitchValueASCII("call-trace-client");
if (client.empty()) {
LOG(INFO) << "DEPRECATED: No mode specified, using --mode=calltrace.";
instrumenter_.reset(new instrumenters::EntryThunkInstrumenter(
instrumenters::EntryThunkInstrumenter::CALL_TRACE));
return;
}
if (base::LowerCaseEqualsASCII(client, "profiler")) {
LOG(INFO) << "DEPRECATED: Using --mode=profile.";
instrumenter_.reset(new instrumenters::EntryThunkInstrumenter(
instrumenters::EntryThunkInstrumenter::PROFILE));
} else if (base::LowerCaseEqualsASCII(client, "rpc")) {
LOG(INFO) << "DEPRECATED: Using --mode=calltrace.";
instrumenter_.reset(new instrumenters::EntryThunkInstrumenter(
instrumenters::EntryThunkInstrumenter::CALL_TRACE));
} else {
LOG(INFO) << "DEPRECATED: Using --mode=calltrace --agent=" << client << ".";
instrumenter_.reset(new instrumenters::EntryThunkInstrumenter(
instrumenters::EntryThunkInstrumenter::CALL_TRACE));
}
}
bool InstrumentApp::ParseCommandLine(const base::CommandLine* cmd_line) {
DCHECK(cmd_line != NULL);
if (cmd_line->HasSwitch("help"))
return Usage(cmd_line, "");
// Get the mode and the default client DLL.
if (!cmd_line->HasSwitch("mode")) {
// TODO(chrisha): Remove this once build scripts and profiling tools have
// been updated.
ParseDeprecatedMode(cmd_line);
} else {
std::string mode = cmd_line->GetSwitchValueASCII("mode");
if (base::LowerCaseEqualsASCII(mode, "afl")) {
instrumenter_.reset(new instrumenters::AFLInstrumenter());
} else if (base::LowerCaseEqualsASCII(mode, "asan")) {
// We wrap the Asan instrumenter in an ArchiveInstrumenter adapter so
// that it can transparently handle .lib files.
instrumenter_.reset(new instrumenters::ArchiveInstrumenter(
&AsanInstrumenterFactory));
} else if (base::LowerCaseEqualsASCII(mode, "bbentry")) {
instrumenter_.reset(new instrumenters::BasicBlockEntryInstrumenter());
} else if (base::LowerCaseEqualsASCII(mode, "branch")) {
instrumenter_.reset(new instrumenters::BranchInstrumenter());
} else if (base::LowerCaseEqualsASCII(mode, "calltrace")) {
instrumenter_.reset(new instrumenters::EntryThunkInstrumenter(
instrumenters::EntryThunkInstrumenter::CALL_TRACE));
} else if (base::LowerCaseEqualsASCII(mode, "coverage")) {
instrumenter_.reset(new instrumenters::CoverageInstrumenter());
} else if (base::LowerCaseEqualsASCII(mode, "flummox")) {
instrumenter_.reset(new instrumenters::FlummoxInstrumenter());
} else if (base::LowerCaseEqualsASCII(mode, "profile")) {
instrumenter_.reset(new instrumenters::EntryCallInstrumenter());
} else {
return Usage(cmd_line,
base::StringPrintf("Unknown instrumentation mode: %s.",
mode.c_str()).c_str());
}
}
DCHECK(instrumenter_.get() != NULL);
return instrumenter_->ParseCommandLine(cmd_line);
}
int InstrumentApp::Run() {
DCHECK(instrumenter_.get() != NULL);
return instrumenter_->Instrument() ? 0 : 1;
}
bool InstrumentApp::Usage(const base::CommandLine* cmd_line,
const base::StringPiece& message) const {
if (!message.empty()) {
::fwrite(message.data(), 1, message.length(), err());
::fprintf(err(), "\n\n");
}
::fprintf(err(),
kUsageFormatStr,
cmd_line->GetProgram().BaseName().value().c_str());
return false;
}
} // namespace instrument