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