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

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

Powered by Google App Engine
This is Rietveld 408576698