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