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