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