Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(60)

Side by Side Diff: tools/traffic_annotation/auditor/traffic_annotation_auditor.cc

Issue 2448133006: Tool added to extract network traffic annotations. (Closed)
Patch Set: nits Created 3 years, 8 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(Empty)
1 // Copyright 2017 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 // See README.md for information and build instructions.
6
7 #include "base/command_line.h"
8 #include "base/files/file_enumerator.h"
9 #include "base/files/file_path.h"
10 #include "base/files/file_util.h"
11 #include "base/files/scoped_temp_dir.h"
12 #include "base/logging.h"
13 #include "base/process/launch.h"
14 #include "base/strings/string_number_conversions.h"
15 #include "base/strings/string_split.h"
16 #include "base/strings/stringprintf.h"
17 #include "base/strings/utf_string_conversions.h"
18 #include "net/traffic_annotation/network_traffic_annotation.h"
19 #include "third_party/protobuf/src/google/protobuf/text_format.h"
20
21 #include "tools/traffic_annotation/traffic_annotation.pb.h"
22
23 namespace {
24
25 // Holds an instance of network traffic annotation.
26 struct AnnotationInstance {
27 AnnotationInstance() : state(STATE_OK) {}
28
29 // Protobuf of the annotation.
30 traffic_annotation::NetworkTrafficAnnotation proto;
31 // Unique id of the annotation.
32 std::string unique_id;
33 // State of this annotation.
34 enum State { STATE_OK, STATE_DUPLICATE, STATE_UNDEFINED } state;
35 };
36
37 // Runs clang tool, returns true if all steps are successful. Returns annotation
38 // extractor clang tool's exit code in |clang_tool_exit_code|.
39 bool RunClangTool(const base::FilePath& src_dir,
40 const base::FilePath& build_dir,
41 const base::FilePath& output_dir,
42 const base::CommandLine::StringVector& path_filters,
43 int* clang_tool_exit_code) {
44 for (auto& path : path_filters) {
45 // Create commandline based on OS.
46 base::CommandLine::StringVector argv;
battre 2017/04/10 10:58:31 Would this work (for Windows+POSIX)? I haven't tri
Ramin Halavati 2017/04/10 13:05:57 Done.
47 #ifdef _WIN32
48 argv.push_back(L"python");
49 argv.push_back(base::ASCIIToUTF16(
50 src_dir.AppendASCII("tools/clang/scripts/run_tool.py").MaybeAsASCII()));
51 argv.push_back(L"--generate-compdb");
52 argv.push_back(L"traffic_annotation_extractor");
53 argv.push_back(base::ASCIIToUTF16(build_dir.MaybeAsASCII()));
54 argv.push_back(path);
55 argv.push_back(base::ASCIIToUTF16(
56 base::StringPrintf("--tool-args=-output-directory=%s",
57 output_dir.MaybeAsASCII().c_str())));
58 #else
59 argv.push_back(
60 src_dir.AppendASCII("tools/clang/scripts/run_tool.py").MaybeAsASCII());
61 argv.push_back("--generate-compdb");
62 argv.push_back("traffic_annotation_extractor");
63 argv.push_back(build_dir.MaybeAsASCII());
64 argv.push_back(path);
65 argv.push_back(base::StringPrintf("--tool-args=-output-directory=%s",
66 output_dir.MaybeAsASCII().c_str()));
67 #endif
68
69 base::Process process =
70 base::LaunchProcess(base::CommandLine(argv), base::LaunchOptions());
71 if (!process.IsValid() || !process.WaitForExit(clang_tool_exit_code)) {
72 LOG(ERROR) << "Executing clang tool failed.\n";
73 return false;
74 }
75 }
76 return true;
77 }
78
79 // Reads an extracted annotation file and returns it in the output variables.
80 // The file starts with four |header_lines| with the following meaning:
81 // 0- File path.
82 // 1- Name of the function including this position.
83 // 2- Line number.
84 // 3- Unique id of annotation.
85 // The rest of the file is the protobuf text (|msg_text|).
86 bool ReadFile(const base::FilePath& file_path,
87 std::vector<std::string>* header_lines,
88 std::string* msg_text) {
89 std::string file_content;
90 if (!base::ReadFileToString(file_path, &file_content))
91 return false;
92
93 header_lines->clear();
94 msg_text->clear();
95
96 std::vector<std::string> tokens = base::SplitString(
97 file_content, "\n", base::KEEP_WHITESPACE, base::SPLIT_WANT_ALL);
98
99 // If enough data is extracted, populate outputs, otherwise leave them blank.
100 if (tokens.size() > 4) {
101 for (int i = 0; i < 4; i++)
102 header_lines->push_back(tokens[i]);
103 for (size_t i = 4; i < tokens.size(); i++)
104 *msg_text += tokens[i] + "\n";
105 }
106
107 return true;
108 }
109
110 // Reads all extracted txt files from given input folder and populates instances
111 // and errors. Errors include not finding the file, incorrect content, or error
112 // passed from clang tool.
113 void ReadExtractedFiles(const base::FilePath& folder_name,
114 std::vector<AnnotationInstance>* instances,
115 std::vector<std::string>* errors) {
116 #ifdef _WIN32
117 base::FileEnumerator file_iter(folder_name, false,
118 base::FileEnumerator::FILES, L"*.txt");
119 #else
120 base::FileEnumerator file_iter(folder_name, false,
121 base::FileEnumerator::FILES, "*.txt");
122 #endif
123 while (!file_iter.Next().empty()) {
124 std::string file_name = file_iter.GetInfo().GetName().AsUTF8Unsafe();
125 LOG(INFO) << "Reading " << file_name.c_str() << "...\n";
126
127 std::vector<std::string> header_lines;
128 std::string msg_text;
129 if (!ReadFile(folder_name.Append(file_iter.GetInfo().GetName()),
130 &header_lines, &msg_text)) {
131 errors->push_back(
132 base::StringPrintf("Could not open file '%s'\n.", file_name.c_str()));
133 continue;
134 }
135 if (header_lines.size() < 4) {
136 errors->push_back(base::StringPrintf(
137 "Header lines are not complete for file '%s'\n.", file_name.c_str()));
138 continue;
139 }
140
141 AnnotationInstance new_instance;
142 if (header_lines[3] == "\"Undefined\"") {
143 new_instance.state = AnnotationInstance::STATE_UNDEFINED;
144 } else if (!google::protobuf::TextFormat::ParseFromString(
145 msg_text, (google::protobuf::Message*)&new_instance)) {
146 errors->push_back(base::StringPrintf(
147 "Could not parse protobuf for file '%s'\n.", file_name.c_str()));
148 continue;
149 }
150
151 // Add header data to new instance.
152 traffic_annotation::NetworkTrafficAnnotation_TrafficSource* src =
153 new_instance.proto.mutable_source();
154 src->set_file(header_lines[0]);
155 src->set_function(header_lines[1]);
156 int line;
157 base::StringToInt(header_lines[2], &line);
158 src->set_line(line);
159 new_instance.unique_id = header_lines[3];
160 instances->push_back(new_instance);
161 }
162 LOG(INFO) << instances->size() << " annotation instance(s) read.\n";
163 }
164
165 // Checks to see if unique ids are really unique and marks the ones which are
166 // not.
167 void MarkRepeatedUniqueIds(std::vector<AnnotationInstance>* instances) {
168 std::map<std::string, AnnotationInstance*> unique_ids;
169 for (auto& instance : *instances) {
170 if (instance.state == AnnotationInstance::STATE_OK) {
171 auto match = unique_ids.find(instance.unique_id);
172 if (match != unique_ids.end()) {
173 instance.state = match->second->state =
174 AnnotationInstance::STATE_DUPLICATE;
175 } else {
176 unique_ids.insert(
177 std::make_pair(std::string(instance.unique_id), &instance));
178 }
179 }
180 }
181 }
182
183 // Writes summary of annotation instances to a file. Returns true if successful.
184 bool WriteSummaryFile(int clang_tool_exit_code,
185 const std::vector<AnnotationInstance>& instances,
186 const std::vector<std::string>& errors,
187 const base::FilePath& file_path) {
188 std::string report = "";
189
190 if (errors.size() || clang_tool_exit_code) {
191 report = "[Errors]\n";
192
193 if (clang_tool_exit_code)
194 report += base::StringPrintf("Clang tool returned error: %i\n",
195 clang_tool_exit_code);
196
197 for (const auto& error : errors)
198 report += error + "\n";
199 }
200
201 report += "[Annotations]\n";
202
203 for (const auto& instance : instances) {
204 report +=
205 "------------------------------------------------------------"
206 "--------------------\n";
207 report += base::StringPrintf("Unique ID: %s\n", instance.unique_id.c_str());
208 if (instance.state == AnnotationInstance::STATE_UNDEFINED)
209 report += base::StringPrintf("WARNING: Undefined annotation.\n");
210 else if (instance.state == AnnotationInstance::STATE_DUPLICATE)
211 report += base::StringPrintf("WARNING: Duplicate unique id.\n");
212 std::string temp;
213 google::protobuf::TextFormat::PrintToString(instance.proto, &temp);
214 report += base::StringPrintf("Content:\n%s\n", temp.c_str());
215 }
216
217 if (base::WriteFile(file_path, report.c_str(), report.length()) == -1) {
218 LOG(INFO) << " Could not create output file: "
219 << file_path.MaybeAsASCII().c_str() << ".\n";
220 return false;
221 }
222
223 LOG(INFO) << "Output file " << file_path.MaybeAsASCII().c_str()
224 << " written for " << instances.size() << " instances.\n";
225 return true;
226 }
227
228 } // namespace
229
230 #ifdef _WIN32
231 int wmain(int argc, wchar_t* argv[]) {
232 #else
233 int main(int argc, char* argv[]) {
234 #endif
235 // Parse switches.
236 base::CommandLine command_line = base::CommandLine(argc, argv);
237 if (command_line.HasSwitch("help") || command_line.HasSwitch("h") ||
238 argc == 1) {
239 LOG(INFO)
240 << "Usage: traffic_annotation_auditor [OPTION]... [path_filter]...\n"
241 "Extracts network traffic annotations from source files. If path "
242 "filter(s) are specified, only those directories of the source "
243 "will be analyzed.\n"
244 "Options:\n"
245 " -h, --help Shows help.\n"
246 " --build-dir Path to the build directory from which "
247 "the\n"
248 " annotations will be extracted.\n"
249 " --extractor-output-dir Path to the directory that extracted\n"
250 " partial files will be written to. "
251 "Will\n"
252 " be automatically generated and "
253 "deleted\n"
254 " if not specified.\n"
255 " --extracted-input-dir Path to a directory where extracted\n"
256 " partial annotations are already "
257 "stored.\n"
258 " If specified, build directory will be\n"
259 " ignored.\n"
260 " --summary-file Path to an output file with summary of\n"
261 " extracted annotations.\n"
262 "Example:\n"
263 " traffic_annotation_auditor --build-dir=out/Debug\n"
264 " --summary-file=report.txt\n";
265 return 1;
266 }
267 base::FilePath extracted_files_dir =
268 command_line.GetSwitchValuePath("extracted-input-dir");
269 base::FilePath build_dir = command_line.GetSwitchValuePath("build-dir");
270 base::FilePath extractor_output_dir =
271 command_line.GetSwitchValuePath("extractor-output-dir");
272 base::FilePath summary_file = command_line.GetSwitchValuePath("summary-file");
273 base::ScopedTempDir temp_dir;
274
275 if (summary_file.empty())
276 LOG(ERROR) << "WARNING: Output file not specified.\n";
277
278 int clang_tool_exit_code = 0;
279
280 // Extract annotations.
281 if (extracted_files_dir.empty()) {
282 // Get build directory, if it is empty issue an error.
283 if (build_dir.empty()) {
284 LOG(ERROR)
285 << "You must either specify the build directory to run the clang "
286 "tool and extract annotations, or specify the input directory "
287 "where extracted annotation files already exist.\n";
288 return 1;
289 }
290 // If output directory is not provided, create a temporary one.
291 if (extractor_output_dir.empty()) {
292 if (!temp_dir.CreateUniqueTempDirUnderPath(build_dir)) {
293 LOG(ERROR) << "Could not create temporary directory in "
294 << build_dir.MaybeAsASCII().c_str() << ".\n";
295 return 1;
296 }
297 extractor_output_dir = temp_dir.GetPath();
298 } else {
299 // Ensure given directory is empty.
300 if (!base::FileEnumerator(extractor_output_dir, false,
301 base::FileEnumerator::FILES)
302 .Next()
303 .empty()) {
304 LOG(ERROR) << "Output directory "
305 << extractor_output_dir.MaybeAsASCII().c_str()
306 << "should be empty .\n";
307 return 1;
308 }
309 }
310
311 // Get path filters, if none is provided, just add all.
312 base::CommandLine::StringVector path_filters = command_line.GetArgs();
313 if (!path_filters.size()) {
314 base::FilePath temp;
315 path_filters.push_back(temp.AppendASCII("./").value().c_str());
316 }
317
318 // Eexcutable is usually in out/[Build Dir], so the path to source is
319 // extracted by moving two directories up.
320 if (!RunClangTool(command_line.GetProgram().DirName().AppendASCII("../.."),
321 build_dir, extractor_output_dir, path_filters,
322 &clang_tool_exit_code)) {
323 return 1;
324 }
325
326 extracted_files_dir = extractor_output_dir;
327 }
328
329 // Read all extracted files.
330 std::vector<AnnotationInstance> instances;
331 std::vector<std::string> errors;
332 ReadExtractedFiles(extracted_files_dir, &instances, &errors);
333
334 if (instances.empty()) {
335 LOG(ERROR) << "Could not read any file.\n";
336 return 1;
337 } else {
338 MarkRepeatedUniqueIds(&instances);
339 }
340
341 // Create Summary file if requested.
342 if (!summary_file.empty() &&
343 !WriteSummaryFile(clang_tool_exit_code, instances, errors, summary_file))
344 return 1;
345
346 return 0;
347 }
OLDNEW
« tools/traffic_annotation/auditor/README.md ('K') | « tools/traffic_annotation/auditor/README.md ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698