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 : #include "syzygy/grinder/grinder_app.h"
16 :
17 : #include "base/logging.h"
18 : #include "base/files/file_util.h"
19 : #include "base/strings/string_util.h"
20 : #include "base/strings/stringprintf.h"
21 : #include "syzygy/grinder/grinders/coverage_grinder.h"
22 : #include "syzygy/grinder/grinders/indexed_frequency_data_grinder.h"
23 : #include "syzygy/grinder/grinders/mem_replay_grinder.h"
24 : #include "syzygy/grinder/grinders/profile_grinder.h"
25 : #include "syzygy/grinder/grinders/sample_grinder.h"
26 :
27 : namespace grinder {
28 :
29 : namespace {
30 :
31 : const char kUsageFormatStr[] =
32 : "Usage: %ls <trace files> [options]\n"
33 : "\n"
34 : " A tool that parses trace files and produces summary output.\n"
35 : "\n"
36 : " In 'bbentry' mode it processes BB entry trace files and produces\n"
37 : " summarized BB entry counts for use with the BB optimizer.\n"
38 : "\n"
39 : " In 'branch' mode it processes BB arc trace files and produces\n"
40 : " summarized BB arc counts for use with the BB optimizer.\n"
41 : "\n"
42 : " In 'coverage' mode it outputs GCOV/LCOV-compatible or\n"
43 : " KCacheGrind-compatible output files for further processing with code\n"
44 : " coverage or line profiler visualization tools.\n"
45 : "\n"
46 : " In 'memreplay' mode it processes memory profiler information and\n"
47 : " outputs a story for replay with the bard utility."
48 : "\n"
49 : " In 'profile' mode it outputs KCacheGrind-compatible output files for\n"
50 : " visualization.\n"
51 : "\n"
52 : " In 'sample' mode it processes sampling profiler data and outputs heat\n"
53 : " per basic-block/function/compiland in CSV format.\n"
54 : "\n"
55 : "Required parameters\n"
56 : " --mode=<mode>\n"
57 : " The processing mode. Must be one of 'bbentry', 'branch',\n"
58 : " 'coverage', 'memreplay', 'profile' or 'sample'.\n"
59 : "\n"
60 : "Optional parameters\n"
61 : " --output-file=<output file>\n"
62 : " The location of output file. If not specified, output is to stdout.\n"
63 : "coverage mode optional parameters\n"
64 : " --output-format=<output format>\n"
65 : " Output format must be one of 'lcov' or 'cachegrind'. Defaults to\n"
66 : " 'lcov' if not explicitly specified.\n"
67 : "profile mode optional parameters\n"
68 : " --thread-parts\n"
69 : " Aggregate and output separate parts for each thread seen in the\n"
70 : " trace files.\n"
71 : "sample mode optional parameters\n"
72 : " --aggregation-level=<level>\n"
73 : " The level of aggregation. Must be one of 'basic-block', 'function',\n"
74 : " 'compiland' or 'line'. Output is in CSV format, except for 'line'\n"
75 : " aggregation, which outputs to KCacheGrind format. Defaults to\n"
76 : " 'basic-block'.\n"
77 : " --image=<path>\n"
78 : " The path to the image for which sampling information is to be\n"
79 : " processed. If this is not specified then aggregate information\n"
80 : " will be reported for all modules encountered in the trace files.\n"
81 : " This must be specified for 'basic-block' aggregation modes, as\n"
82 : " only one module may be processed at a time in this mode.\n"
83 : "\n";
84 :
85 : } // namespace
86 :
87 : GrinderApp::GrinderApp()
88 E : : application::AppImplBase("Grinder"), mode_() {
89 E : }
90 :
91 : void GrinderApp::PrintUsage(const base::FilePath& program,
92 E : const base::StringPiece& message) {
93 E : if (!message.empty()) {
94 E : ::fwrite(message.data(), 1, message.length(), out());
95 E : ::fprintf(out(), "\n\n");
96 : }
97 :
98 E : ::fprintf(out(), kUsageFormatStr, program.BaseName().value().c_str());
99 E : }
100 :
101 E : bool GrinderApp::ParseCommandLine(const base::CommandLine* command_line) {
102 E : DCHECK(command_line != NULL);
103 :
104 E : base::CommandLine::StringVector args = command_line->GetArgs();
105 E : if (args.empty()) {
106 : PrintUsage(command_line->GetProgram(),
107 E : "You must provide at least one trace file.");
108 E : return false;
109 : }
110 :
111 E : if (!command_line->HasSwitch("mode")) {
112 : PrintUsage(command_line->GetProgram(),
113 E : "You must specify the processing mode.");
114 E : return false;
115 : }
116 :
117 E : for (size_t i = 0; i < args.size(); ++i) {
118 E : if (!AppendMatchingPaths(base::FilePath(args[i]), &trace_files_)) {
119 : PrintUsage(command_line->GetProgram(),
120 i : base::StringPrintf("No such file '%ws'.", args[i].c_str()));
121 i : return false;
122 : }
123 E : }
124 :
125 : // Parse the processing mode.
126 E : std::string mode = command_line->GetSwitchValueASCII("mode");
127 E : if (base::LowerCaseEqualsASCII(mode, "bbentry")) {
128 E : mode_ = kBasicBlockEntry;
129 E : grinder_.reset(new grinders::IndexedFrequencyDataGrinder());
130 E : } else if (base::LowerCaseEqualsASCII(mode, "branch")) {
131 i : mode_ = kIndexedFrequencyData;
132 i : grinder_.reset(new grinders::IndexedFrequencyDataGrinder());
133 E : } else if (base::LowerCaseEqualsASCII(mode, "coverage")) {
134 E : mode_ = kCoverage;
135 E : grinder_.reset(new grinders::CoverageGrinder());
136 E : } else if (base::LowerCaseEqualsASCII(mode, "memreplay")) {
137 E : mode = kMemReplay;
138 E : grinder_.reset(new grinders::MemReplayGrinder());
139 E : } else if (base::LowerCaseEqualsASCII(mode, "profile")) {
140 E : mode_ = kProfile;
141 E : grinder_.reset(new grinders::ProfileGrinder());
142 E : } else if (base::LowerCaseEqualsASCII(mode, "sample")) {
143 E : mode_ = kSample;
144 E : grinder_.reset(new grinders::SampleGrinder());
145 E : } else {
146 : PrintUsage(command_line->GetProgram(),
147 i : base::StringPrintf("Unknown mode: %s.", mode.c_str()));
148 i : return false;
149 : }
150 E : DCHECK(grinder_.get() != NULL);
151 :
152 : // Parse the command-line for the grinder.
153 E : if (!grinder_->ParseCommandLine(command_line)) {
154 : PrintUsage(command_line->GetProgram(),
155 : base::StringPrintf("Failed to parse %s parameters.",
156 i : mode.c_str()));
157 i : return false;
158 : }
159 :
160 E : output_file_ = command_line->GetSwitchValuePath("output-file");
161 :
162 E : return true;
163 E : }
164 :
165 E : int GrinderApp::Run() {
166 E : DCHECK(grinder_.get() != NULL);
167 :
168 E : trace::parser::Parser parser;
169 E : grinder_->SetParser(&parser);
170 E : if (!parser.Init(grinder_.get()))
171 i : return 1;
172 :
173 : // Open the input files.
174 E : for (size_t i = 0; i < trace_files_.size(); ++i) {
175 E : if (!parser.OpenTraceFile(trace_files_[i])) {
176 i : LOG(ERROR) << "Unable to open trace file \'"
177 : << trace_files_[i].value() << "'";
178 i : return 1;
179 : }
180 E : }
181 :
182 : // Open the output file. We do this early so as to fail before processing
183 : // the logs if the output is not able to be opened.
184 E : FILE* output = out();
185 E : base::ScopedFILE auto_close;
186 E : if (!output_file_.empty()) {
187 E : output = base::OpenFile(output_file_, "w");
188 E : if (output == NULL) {
189 i : LOG(ERROR) << "Unable to create output file \'"
190 : << output_file_.value() << "'";
191 i : return 1;
192 : }
193 :
194 E : auto_close.reset(output);
195 : }
196 :
197 E : LOG(INFO) << "Parsing trace files.";
198 E : if (!parser.Consume()) {
199 i : LOG(ERROR) << "Error parsing trace files.";
200 i : return 1;
201 : }
202 :
203 E : LOG(INFO) << "Aggregating data.";
204 E : if (!grinder_->Grind()) {
205 i : LOG(ERROR) << "Failed to grind data.";
206 i : return 1;
207 : }
208 :
209 E : std::wstring output_name(L"stdout");
210 E : if (!output_file_.empty())
211 E : output_name = base::StringPrintf(L"\"%ls\"", output_file_.value().c_str());
212 E : LOG(INFO) << "Writing output to " << output_name << ".";
213 E : DCHECK(output != NULL);
214 E : if (!grinder_->OutputData(output)) {
215 i : LOG(ERROR) << "Failed to output data.";
216 i : return 1;
217 : }
218 :
219 E : return 0;
220 E : }
221 :
222 E : void GrinderApp::TearDown() {
223 : // Release the grinder so it has a chance to clean up before COM goes away.
224 E : grinder_.reset();
225 E : }
226 :
227 : } // namespace grinder
|