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