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 : #include <string>
23 :
24 : #include "base/strings/string_util.h"
25 : #include "base/strings/stringprintf.h"
26 : #include "syzygy/instrument/instrumenters/archive_instrumenter.h"
27 : #include "syzygy/instrument/instrumenters/asan_instrumenter.h"
28 : #include "syzygy/instrument/instrumenters/bbentry_instrumenter.h"
29 : #include "syzygy/instrument/instrumenters/branch_instrumenter.h"
30 : #include "syzygy/instrument/instrumenters/coverage_instrumenter.h"
31 : #include "syzygy/instrument/instrumenters/entry_call_instrumenter.h"
32 : #include "syzygy/instrument/instrumenters/entry_thunk_instrumenter.h"
33 : #include "syzygy/instrument/instrumenters/flummox_instrumenter.h"
34 :
35 : namespace instrument {
36 :
37 : namespace {
38 :
39 : static const char kUsageFormatStr[] =
40 : "Usage: %ls [options]\n"
41 : " Required arguments:\n"
42 : " --input-image=<path> The input image to instrument.\n"
43 : " --mode=asan|bbentry|branch|calltrace|coverage|flummox|profile\n"
44 : " Specifies which instrumentation mode is to\n"
45 : " be used. If this is not specified it is\n"
46 : " equivalent to specifying --mode=calltrace\n"
47 : " (this default behaviour is DEPRECATED).\n"
48 : " --output-image=<path>\n"
49 : " The instrumented output image.\n"
50 : " DEPRECATED options:\n"
51 : " --input-dll is aliased to --input-image.\n"
52 : " --output-dll is aliased to --output-image.\n"
53 : " --call-trace-client=RPC\n"
54 : " Equivalent to --mode=calltrace.\n"
55 : " --call-trace-client=PROFILER\n"
56 : " Equivalent to --mode=profile.\n"
57 : " --call-trace-client=<path>\n"
58 : " Equivalent to --mode=calltrace\n"
59 : " --agent=<path>.\n"
60 : " General options (applicable in all modes):\n"
61 : " --agent=<path> If specified indicates exactly which DLL to\n"
62 : " use when instrumenting the provided module.\n"
63 : " If not specified a default agent library\n"
64 : " will be used. This is ignored in Asan mode.\n"
65 : " --debug-friendly Generate more debugger friendly output by\n"
66 : " making the thunks resolve to the original\n"
67 : " function's name. This is at the cost of the\n"
68 : " uniqueness of address->name resolution.\n"
69 : " --inline-fast-path Inline a fast path into the instrumented\n"
70 : " image.\n"
71 : " --input-pdb=<path> The PDB for the DLL to instrument. If not\n"
72 : " explicitly provided will be searched for.\n"
73 : " --filter=<path> The path of the filter to be used in\n"
74 : " applying the instrumentation. Ranges marked\n"
75 : " in the filter will not be instrumented.\n"
76 : " --no-augment-pdb Indicates that the relinker should not\n"
77 : " augment the output PDB with additional.\n"
78 : " metadata.\n"
79 : " --no-strip-strings Indicates that the relinker should not strip\n"
80 : " the strings when augmenting the PDB. They\n"
81 : " are stripped by default to keep PDB sizes\n"
82 : " down.\n"
83 : " --output-pdb=<path> The PDB for the instrumented DLL. If not\n"
84 : " provided will attempt to generate one.\n"
85 : " --overwrite Allow output files to be overwritten.\n"
86 : " asan mode options:\n"
87 : " --asan-rtl-options=OPTIONS\n"
88 : " Allows specification of options that will\n"
89 : " influence the Asan RTL that attaches to the\n"
90 : " instrumented module. For descriptions of\n"
91 : " these options see common/asan_parameters. If\n"
92 : " not specified then the defaults of the RTL\n"
93 : " will be used.\n"
94 : " --hot-patching Use hot patching Asan instrumentation.\n"
95 : " --instrumentation-rate=DOUBLE\n"
96 : " Specifies the fraction of instructions to\n"
97 : " be instrumented, as a value in the range\n"
98 : " 0..1, inclusive. Defaults to 1.\n"
99 : " --no-interceptors Disable the interception of the functions\n"
100 : " like memset, memcpy, stcpy, ReadFile... to\n"
101 : " check their parameters.\n"
102 : " --no-liveness-analysis Disables register and flags liveness\n"
103 : " analysis.\n"
104 : " --no-redundancy-analysis\n"
105 : " Disables redundant memory access analysis.\n"
106 : " branch mode options:\n"
107 : " --buffering Enable per-thread buffering of events.\n"
108 : " --fs-slot=<slot> Specify which FS slot to use for thread\n"
109 : " local storage.\n"
110 : " calltrace mode options:\n"
111 : " --instrument-imports Also instrument calls to imports.\n"
112 : " --module-entry-only If specified then the per-function entry\n"
113 : " hook will not be used and only module entry\n"
114 : " points will be hooked.\n"
115 : " --no-unsafe-refs Perform no instrumentation of references\n"
116 : " between code blocks that contain anything\n"
117 : " but C/C++.\n"
118 : " profile mode options:\n"
119 : " --instrument-imports Also instrument calls to imports.\n"
120 : "\n";
121 :
122 : // Currently only Asan supports COFF/LIB instrumentation. As other
123 : // instrumenters add COFF support they need to be added with a similar
124 : // mechanism.
125 E : InstrumenterInterface* AsanInstrumenterFactory() {
126 E : return new instrumenters::AsanInstrumenter();
127 E : }
128 :
129 : } // namespace
130 :
131 E : void InstrumentApp::ParseDeprecatedMode(const base::CommandLine* cmd_line) {
132 E : DCHECK(cmd_line != NULL);
133 :
134 E : std::string client = cmd_line->GetSwitchValueASCII("call-trace-client");
135 :
136 E : if (client.empty()) {
137 E : LOG(INFO) << "DEPRECATED: No mode specified, using --mode=calltrace.";
138 : instrumenter_.reset(new instrumenters::EntryThunkInstrumenter(
139 E : instrumenters::EntryThunkInstrumenter::CALL_TRACE));
140 E : return;
141 : }
142 :
143 E : if (base::LowerCaseEqualsASCII(client, "profiler")) {
144 E : LOG(INFO) << "DEPRECATED: Using --mode=profile.";
145 : instrumenter_.reset(new instrumenters::EntryThunkInstrumenter(
146 E : instrumenters::EntryThunkInstrumenter::PROFILE));
147 E : } else if (base::LowerCaseEqualsASCII(client, "rpc")) {
148 E : LOG(INFO) << "DEPRECATED: Using --mode=calltrace.";
149 : instrumenter_.reset(new instrumenters::EntryThunkInstrumenter(
150 E : instrumenters::EntryThunkInstrumenter::CALL_TRACE));
151 E : } else {
152 i : LOG(INFO) << "DEPRECATED: Using --mode=calltrace --agent=" << client << ".";
153 : instrumenter_.reset(new instrumenters::EntryThunkInstrumenter(
154 i : instrumenters::EntryThunkInstrumenter::CALL_TRACE));
155 : }
156 E : }
157 :
158 E : bool InstrumentApp::ParseCommandLine(const base::CommandLine* cmd_line) {
159 E : DCHECK(cmd_line != NULL);
160 :
161 E : if (cmd_line->HasSwitch("help"))
162 E : return Usage(cmd_line, "");
163 :
164 : // Get the mode and the default client DLL.
165 E : if (!cmd_line->HasSwitch("mode")) {
166 : // TODO(chrisha): Remove this once build scripts and profiling tools have
167 : // been updated.
168 E : ParseDeprecatedMode(cmd_line);
169 E : } else {
170 E : std::string mode = cmd_line->GetSwitchValueASCII("mode");
171 E : if (base::LowerCaseEqualsASCII(mode, "asan")) {
172 : // We wrap the Asan instrumenter in an ArchiveInstrumenter adapter so
173 : // that it can transparently handle .lib files.
174 : instrumenter_.reset(new instrumenters::ArchiveInstrumenter(
175 E : &AsanInstrumenterFactory));
176 E : } else if (base::LowerCaseEqualsASCII(mode, "bbentry")) {
177 E : instrumenter_.reset(new instrumenters::BasicBlockEntryInstrumenter());
178 E : } else if (base::LowerCaseEqualsASCII(mode, "branch")) {
179 E : instrumenter_.reset(new instrumenters::BranchInstrumenter());
180 E : } else if (base::LowerCaseEqualsASCII(mode, "calltrace")) {
181 : instrumenter_.reset(new instrumenters::EntryThunkInstrumenter(
182 E : instrumenters::EntryThunkInstrumenter::CALL_TRACE));
183 E : } else if (base::LowerCaseEqualsASCII(mode, "coverage")) {
184 E : instrumenter_.reset(new instrumenters::CoverageInstrumenter());
185 E : } else if (base::LowerCaseEqualsASCII(mode, "flummox")) {
186 i : instrumenter_.reset(new instrumenters::FlummoxInstrumenter());
187 E : } else if (base::LowerCaseEqualsASCII(mode, "profile")) {
188 E : instrumenter_.reset(new instrumenters::EntryCallInstrumenter());
189 E : } else {
190 : return Usage(cmd_line,
191 : base::StringPrintf("Unknown instrumentation mode: %s.",
192 i : mode.c_str()).c_str());
193 : }
194 E : }
195 E : DCHECK(instrumenter_.get() != NULL);
196 :
197 E : return instrumenter_->ParseCommandLine(cmd_line);
198 E : }
199 :
200 E : int InstrumentApp::Run() {
201 E : DCHECK(instrumenter_.get() != NULL);
202 :
203 E : return instrumenter_->Instrument() ? 0 : 1;
204 E : }
205 :
206 : bool InstrumentApp::Usage(const base::CommandLine* cmd_line,
207 E : const base::StringPiece& message) const {
208 E : if (!message.empty()) {
209 i : ::fwrite(message.data(), 1, message.length(), err());
210 i : ::fprintf(err(), "\n\n");
211 : }
212 :
213 : ::fprintf(err(),
214 : kUsageFormatStr,
215 E : cmd_line->GetProgram().BaseName().value().c_str());
216 :
217 E : return false;
218 E : }
219 :
220 : } // namespace instrument
|