OLD | NEW |
---|---|
(Empty) | |
1 // Copyright 2016 The Chromium Authors. All rights reserved. | |
msramek
2017/02/21 15:57:54
nit: 2017
Ramin Halavati
2017/02/23 12:09:50
Done.
| |
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.txt for information and build instructions. | |
msramek
2017/02/21 15:57:54
Also here :)
Ramin Halavati
2017/02/23 12:09:50
Done.
| |
6 | |
7 #include <cstdio> | |
8 #include <cstdlib> | |
9 #include <fstream> | |
10 #include <map> | |
11 #include <string> | |
12 | |
13 #include "base/command_line.h" | |
14 #include "base/files/file_enumerator.h" | |
15 #include "base/files/file_path.h" | |
16 #include "base/files/scoped_temp_dir.h" | |
17 #include "base/strings/stringprintf.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 | |
msramek
2017/02/21 15:57:53
Please use an anonymous namespace for helper class
Ramin Halavati
2017/02/23 12:09:49
Done.
| |
23 // Holds an instance of network traffic annotation | |
msramek
2017/02/21 15:57:54
nit: Period at the end of the sentence.
Ramin Halavati
2017/02/23 12:09:49
Done.
| |
24 struct Instance { | |
msramek
2017/02/21 15:57:54
This is very generic. How about AnnotationInstance
Ramin Halavati
2017/02/23 12:09:49
Done.
| |
25 Instance() : state(kOK) {} | |
26 | |
27 traffic_annotation::NetworkTrafficAnnotation proto; | |
msramek
2017/02/21 15:57:54
Please add documentation for public attributes.
Ramin Halavati
2017/02/23 12:09:50
Done.
| |
28 std::string unique_id; | |
29 std::string callee; | |
30 enum { kOK, kDuplicate, kUndefined } state; | |
msramek
2017/02/21 15:57:54
https://chromium.googlesource.com/chromium/src/+/m
Ramin Halavati
2017/02/23 12:09:49
Done.
| |
31 }; | |
32 | |
33 // Runs clang tool, returns true if all OK. | |
msramek
2017/02/21 15:57:54
nit: 'if successful' ?
Ramin Halavati
2017/02/23 12:09:50
Done.
| |
34 bool RunClangTool(const base::FilePath& src_dir, | |
35 const base::FilePath& build_dir, | |
36 const base::FilePath& output_dir, | |
37 const base::CommandLine::StringVector& path_filters) { | |
38 #ifdef _WIN32 | |
39 int result1 = system( | |
40 base::StringPrintf( | |
41 "python %s %s", | |
42 src_dir.AppendASCII("tools/clang/scripts/generate_win_compdb.py") | |
43 .MaybeAsASCII() | |
44 .c_str(), | |
45 build_dir.MaybeAsASCII().c_str()) | |
46 .c_str()); | |
47 if (result1) { | |
48 printf(" Executing generate_win_compdb returned code: %i.\n", result1); | |
49 return false; | |
50 } | |
51 #endif | |
52 for (auto& path : path_filters) { | |
53 #ifdef _WIN32 | |
54 std::string command( | |
55 "python %s traffic_annotation_extractor %s %S --tool-args %s "); | |
msramek
2017/02/21 15:57:53
Is the capital S intentional?
Ramin Halavati
2017/02/23 12:09:50
Yes, it's to convert |path| from ASCII to unicode.
| |
56 #else | |
57 std::string command( | |
58 "%s traffic_annotation_extractor --generate-compdb %s %s --tool-args " | |
59 "%s"); | |
60 #endif | |
61 int result2 = | |
msramek
2017/02/21 15:57:53
I would prefer more semantic names than |result1|
Ramin Halavati
2017/02/23 12:09:49
Done, variable is temporary and used just to avoid
| |
62 system(base::StringPrintf( | |
63 command.c_str(), | |
64 src_dir.AppendASCII("tools/clang/scripts/run_tool.py") | |
65 .MaybeAsASCII() | |
66 .c_str(), | |
67 build_dir.MaybeAsASCII().c_str(), path.c_str(), | |
68 output_dir.MaybeAsASCII().c_str()) | |
69 .c_str()); | |
70 if (result2) { | |
71 printf(" Executing clang tool returned code: %i.\n", result2); | |
72 return false; | |
73 } | |
74 } | |
75 return true; | |
76 } | |
77 | |
78 // Reads an extrated annotation file. The first 6 lines of the file include: | |
msramek
2017/02/21 15:57:54
typo: extracted
Ramin Halavati
2017/02/23 12:09:50
Done.
| |
79 // 0- File path. | |
80 // 1- Name of the function that is called using annotation. | |
81 // 2- Line number. | |
82 // 3- Name of the function including this position. | |
83 // 4- Error text associated with this annotation. | |
84 // 5- Unique id of annotation. | |
85 // The rest of the file is the protobuf text. | |
86 bool ReadFile(const base::FilePath& file_path, | |
87 std::vector<std::string>* header_lines, | |
msramek
2017/02/21 15:57:54
I assume that this is the first 6 lines and the be
Ramin Halavati
2017/02/23 12:09:49
Done.
| |
88 std::string* msg_text) { | |
89 std::ifstream input_file(file_path.value()); | |
msramek
2017/02/21 15:57:54
Should we use base::File instead?
Ramin Halavati
2017/02/23 12:09:49
Done.
| |
90 if (!input_file.is_open()) | |
91 return false; | |
92 | |
93 header_lines->clear(); | |
94 msg_text->clear(); | |
95 | |
96 while (!input_file.eof()) { | |
97 std::string line; | |
98 getline(input_file, line); | |
99 if (header_lines->size() < 6) | |
100 header_lines->push_back(line); | |
101 else | |
102 *msg_text = *msg_text + "\n" + line; | |
103 } | |
104 input_file.close(); | |
105 return true; | |
106 } | |
107 | |
108 // Reads all extracted txt files form given input folder and populates instances | |
msramek
2017/02/21 15:57:54
typo: from
Ramin Halavati
2017/02/23 12:09:49
Done.
| |
109 // and errors. | |
msramek
2017/02/21 15:57:54
What are errors in this case?
Ramin Halavati
2017/02/23 12:09:49
|errors| vector added, comment expanded.
| |
110 void ReadExtractedFiles(const base::FilePath& folder_name, | |
111 std::vector<Instance>* instances) { | |
112 #ifdef _WIN32 | |
113 base::FileEnumerator file_iter(folder_name, false, | |
114 base::FileEnumerator::FILES, L"*.txt"); | |
115 #else | |
116 base::FileEnumerator file_iter(folder_name, false, | |
117 base::FileEnumerator::FILES, "*.txt"); | |
118 #endif | |
119 while (!file_iter.Next().empty()) { | |
120 std::string file_name = file_iter.GetInfo().GetName().AsUTF8Unsafe(); | |
121 printf("\tReading %s...\n", file_name.c_str()); | |
122 | |
123 std::vector<std::string> header_lines; | |
124 std::string msg_text; | |
125 if (!ReadFile(folder_name.Append(file_iter.GetInfo().GetName()), | |
126 &header_lines, &msg_text)) { | |
127 fprintf(stderr, "Could not open file '%s'\n.", file_name.c_str()); | |
128 continue; | |
129 } | |
130 if (header_lines.size() < 6) { | |
131 fprintf(stderr, "Header lines are not complete for file '%s'\n.", | |
132 file_name.c_str()); | |
133 continue; | |
134 } | |
135 if (!header_lines[4].empty()) { | |
136 fprintf(stderr, "Error passed from extractor for file '%s': %s\n.", | |
137 file_name.c_str(), header_lines[4].c_str()); | |
138 continue; | |
139 } | |
140 | |
141 Instance new_instance; | |
142 if (header_lines[5] == "\"Undefined\"") { | |
143 new_instance.state = Instance::kUndefined; | |
144 } else if (!google::protobuf::TextFormat::ParseFromString( | |
145 msg_text, (google::protobuf::Message*)&new_instance)) { | |
146 fprintf(stderr, "Could not parse protobuf for file '%s'\n.", | |
147 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 src->set_line(atoi(header_lines[2].c_str())); | |
msramek
2017/02/21 15:57:53
base::StringToInt
Ramin Halavati
2017/02/23 12:09:50
Done.
| |
157 new_instance.callee = header_lines[3]; | |
158 new_instance.unique_id = header_lines[5]; | |
159 instances->push_back(new_instance); | |
160 } | |
161 printf("%i annotation instance(s) read.\n", (int)instances->size()); | |
msramek
2017/02/21 15:57:55
You can use %ud instead of the cast.
Ramin Halavati
2017/02/23 12:09:49
Done.
| |
162 } | |
163 | |
164 // Checks to see if unique ids are really unique and marks the ones which are | |
165 // not. | |
166 void MarkRepeatedUniqueIds(std::vector<Instance>* instances) { | |
167 std::map<std::string, Instance*> unique_ids; | |
168 for (auto& instance : *instances) { | |
169 if (instance.state == Instance::kOK) { | |
170 auto match = unique_ids.find(instance.unique_id); | |
171 if (match != unique_ids.end()) | |
172 instance.state = match->second->state = Instance::kDuplicate; | |
173 else | |
174 unique_ids.insert( | |
175 std::make_pair(std::string(instance.unique_id), &instance)); | |
176 } | |
177 } | |
178 } | |
179 | |
180 // Writes summary of annotation instances to a file. Returns true if successful. | |
181 bool WriteSummaryFile(const std::vector<Instance>& instances, | |
182 const base::FilePath& file_path) { | |
183 FILE* output_file = fopen(file_path.MaybeAsASCII().c_str(), "wt"); | |
msramek
2017/02/21 15:57:54
Ditto; should we use base::File?
Ramin Halavati
2017/02/23 12:09:49
Done.
| |
184 if (!output_file) { | |
185 printf(" Could not create output file: %s", | |
186 file_path.MaybeAsASCII().c_str()); | |
187 return false; | |
188 } | |
189 for (const auto& instance : instances) { | |
190 fprintf(output_file, "Unique ID: %s\n", instance.unique_id.c_str()); | |
191 if (instance.state == Instance::kUndefined) | |
192 fprintf(output_file, "WARNING: Undefined annotation.\n"); | |
193 else if (instance.state == Instance::kDuplicate) | |
194 fprintf(output_file, "WARNING: Duplicate unique id.\n"); | |
195 fprintf(output_file, "Calling Function: %s\n", instance.callee.c_str()); | |
196 std::string temp; | |
197 google::protobuf::TextFormat::PrintToString(instance.proto, &temp); | |
198 fprintf(output_file, "Content:\n%s\n", temp.c_str()); | |
199 fprintf(output_file, "------------------------------------------------\n"); | |
msramek
2017/02/21 15:57:54
How wide can the output above be? I think this sep
Ramin Halavati
2017/02/23 12:09:50
Done.
| |
200 } | |
201 fclose(output_file); | |
202 printf("Output file %s written for %i instances.\n", | |
203 file_path.MaybeAsASCII().c_str(), (int)instances.size()); | |
204 return true; | |
205 } | |
206 | |
207 #ifdef _WIN32 | |
208 int wmain(int argc, wchar_t* argv[]) { | |
209 #else | |
210 int main(int argc, char* argv[]) { | |
211 #endif | |
212 // Parse switches. | |
213 base::CommandLine command_line = base::CommandLine(argc, argv); | |
214 if (command_line.HasSwitch("help") || command_line.HasSwitch("h") || | |
215 argc == 1) { | |
216 printf( | |
217 "Usage: traffic_annotation_auditor [OPTION]... [path_filter]...\n" | |
218 "Extracts network traffic annotations from source files. If path " | |
219 "filter(s) are specified, only those directories of the source " | |
220 "will be analyzed.\n" | |
221 "Options:\n" | |
222 " -h, --help Shows help.\n" | |
223 " --build_dir Path to the build directory from which the\n" | |
224 " annotations will be extracted.\n" | |
225 " --extractor_output_dir Path to the directory that extracted\n" | |
226 " partial files will be written to. Will\n" | |
227 " be automatically generated and deleted\n" | |
228 " if not specified.\n" | |
229 " --extracted_input_dir Path to a directory where extracted\n" | |
230 " partial annotations are already stored.\n" | |
231 " If specified, build directory will be\n" | |
232 " ignored.\n" | |
233 " --summary_file Path to an output file with summary of\n" | |
234 " extracted annotations.\n" | |
235 "Example:\n" | |
236 " traffic_annotation_auditor --build_dir=out/Debug\n" | |
237 " --summary_file=report.txt\n"); | |
238 return 1; | |
239 } | |
240 base::FilePath extracted_files_dir = | |
241 command_line.GetSwitchValuePath("extracted_input_dir"); | |
242 base::FilePath build_dir = command_line.GetSwitchValuePath("build_dir"); | |
243 base::FilePath extractor_output_dir = | |
244 command_line.GetSwitchValuePath("extractor_output_dir"); | |
245 base::FilePath summary_file = command_line.GetSwitchValuePath("summary_file"); | |
246 base::ScopedTempDir temp_dir; | |
247 | |
248 if (summary_file.empty()) | |
249 fprintf(stderr, "WARNING: Output file not specified.\n"); | |
250 | |
251 // Extract annotations. | |
252 if (extracted_files_dir.empty()) { | |
253 // Get build directory, if it is empty issue an error. | |
254 if (build_dir.empty()) { | |
255 fprintf( | |
256 stderr, | |
257 "You must either specify build directory to run clang " | |
258 "tool and extract annotations, or specify extracted files's input " | |
259 "directory where already extracted files exist.\n"); | |
260 return 1; | |
261 } | |
262 // If output directory is not provided, create a temporary one. | |
263 if (extractor_output_dir.empty()) { | |
264 if (!temp_dir.CreateUniqueTempDirUnderPath(build_dir)) { | |
265 fprintf(stderr, "Could not create temporary directory in %s.\n", | |
266 build_dir.MaybeAsASCII().c_str()); | |
267 return 1; | |
268 } | |
269 extractor_output_dir = temp_dir.GetPath(); | |
270 } else { | |
271 // Ensure given directory is empty. | |
272 if (!base::FileEnumerator(extractor_output_dir, false, | |
273 base::FileEnumerator::FILES) | |
274 .Next() | |
275 .empty()) { | |
276 fprintf(stderr, "Output directory (%s) should be empty.\n", | |
277 extractor_output_dir.MaybeAsASCII().c_str()); | |
278 return 1; | |
279 } | |
280 } | |
281 | |
282 // Get path filters, if none is provided, just add all. | |
283 base::CommandLine::StringVector path_filters = command_line.GetArgs(); | |
284 if (!path_filters.size()) { | |
285 base::FilePath temp; | |
286 path_filters.push_back(temp.AppendASCII("./").value().c_str()); | |
287 } | |
288 | |
289 if (!RunClangTool(command_line.GetProgram().DirName().AppendASCII("../.."), | |
msramek
2017/02/21 15:57:55
Please add a comment to explain why two directorie
Ramin Halavati
2017/02/23 12:09:49
Done.
| |
290 build_dir, extractor_output_dir, path_filters)) { | |
291 return 1; | |
292 } | |
293 | |
294 extracted_files_dir = extractor_output_dir; | |
295 } | |
296 | |
297 // Read all extracted files. | |
298 std::vector<Instance> instances; | |
299 ReadExtractedFiles(extracted_files_dir, &instances); | |
300 | |
301 if (instances.empty()) { | |
302 fprintf(stderr, "Could not read any file.\n"); | |
303 return 1; | |
304 } else { | |
305 MarkRepeatedUniqueIds(&instances); | |
306 } | |
307 | |
308 // Create Summary file if requested. | |
309 if (!summary_file.empty() && !WriteSummaryFile(instances, summary_file)) | |
310 return 1; | |
311 | |
312 return 0; | |
313 } | |
OLD | NEW |