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

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, 10 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 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 }
OLDNEW
« tools/traffic_annotation/auditor/README.txt ('K') | « tools/traffic_annotation/auditor/README.txt ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698