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

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

Issue 2448133006: Tool added to extract network traffic annotations. (Closed)
Patch Set: nits Created 4 years 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.
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.
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
23 // Holds an instance of network traffic annotation
24 struct Instance {
25 Instance() : hash_code(0), state(kOK) {}
26
27 traffic_annotation::NetworkTrafficAnnotation proto;
28 std::string unique_id;
29 std::string callee;
30 unsigned int hash_code;
31 enum { kOK, kDuplicate, kUndefined } state;
32 };
33
34 // Runs clang tool, returns true if all OK.
35 bool RunClangTool(const base::FilePath& src_dir,
36 const base::FilePath& build_dir,
37 const base::FilePath& output_dir,
38 const base::CommandLine::StringVector& path_filters) {
39 #ifdef _WIN32
40 int result1 = system(
41 base::StringPrintf(
42 "python %s %s",
43 src_dir.AppendASCII("tools/clang/scripts/generate_win_compdb.py")
44 .MaybeAsASCII()
45 .c_str(),
46 build_dir.MaybeAsASCII().c_str())
47 .c_str());
48 if (result1) {
49 printf(" Executing generate_win_compdb returned code: %i.\n", result1);
50 return false;
51 }
52 #endif
53 for (auto& path : path_filters) {
54 #ifdef _WIN32
55 std::string command(
56 "python %s traffic_annotation_extractor %s %S --tool-params "
57 "\"output_dir=%s\"");
58 #else
59 std::string command(
60 "%s traffic_annotation_extractor --generate-compdb %s %s --tool-params "
61 "'output_dir=%s'");
62 #endif
63 int result2 =
64 system(base::StringPrintf(
65 command.c_str(),
66 src_dir.AppendASCII("tools/clang/scripts/run_tool.py")
67 .MaybeAsASCII()
68 .c_str(),
69 build_dir.MaybeAsASCII().c_str(), path.c_str(),
70 output_dir.MaybeAsASCII().c_str())
71 .c_str());
72 if (result2) {
73 printf(" Executing clang tool returned code: %i.\n", result2);
74 return false;
75 }
76 }
77 return true;
78 }
79
80 // Reads an extrated annotation file. The first 6 lines of the file include:
81 // 0- File path.
82 // 1- Name of the function that is called using annotation.
83 // 2- Line number.
84 // 3- Name of the function including this position.
85 // 4- Error text associated with this annotation.
86 // 5- Unique id of annotation.
87 // The rest of the file is the protobuf text.
88 bool ReadFile(const base::FilePath& file_path,
89 std::vector<std::string>* header_lines,
90 std::string* msg_text) {
91 std::ifstream input_file(file_path.value());
92 if (!input_file.is_open())
93 return false;
94
95 header_lines->clear();
96 msg_text->clear();
97
98 while (!input_file.eof()) {
99 std::string line;
100 getline(input_file, line);
101 if (header_lines->size() < 6)
102 header_lines->push_back(line);
103 else
104 *msg_text = *msg_text + "\n" + line;
105 }
106 input_file.close();
107 return true;
108 }
109
110 // Reads all extracted txt files form given input folder and populates instances
111 // and errors.
112 void ReadExtractedFiles(const base::FilePath& folder_name,
113 std::vector<Instance>* instances) {
114 #ifdef _WIN32
115 base::FileEnumerator file_iter(folder_name, false,
116 base::FileEnumerator::FILES, L"*.txt");
117 #else
118 base::FileEnumerator file_iter(folder_name, false,
119 base::FileEnumerator::FILES, "*.txt");
120 #endif
121 while (!file_iter.Next().empty()) {
122 std::string file_name = file_iter.GetInfo().GetName().AsUTF8Unsafe();
123 printf("\tReading %s...\n", file_name.c_str());
124
125 std::vector<std::string> header_lines;
126 std::string msg_text;
127 if (!ReadFile(folder_name.Append(file_iter.GetInfo().GetName()),
128 &header_lines, &msg_text)) {
129 fprintf(stderr, "Could not open file '%s'\n.", file_name.c_str());
130 continue;
131 }
132 if (header_lines.size() < 6) {
133 fprintf(stderr, "Header lines are not complete for file '%s'\n.",
134 file_name.c_str());
135 continue;
136 }
137 if (!header_lines[4].empty()) {
138 fprintf(stderr, "Error passed from extractor for file '%s': %s\n.",
139 file_name.c_str(), header_lines[4].c_str());
140 continue;
141 }
142
143 Instance new_instance;
144 if (header_lines[5] == "\"Undefined\"") {
145 new_instance.state = Instance::kUndefined;
146 } else if (!google::protobuf::TextFormat::ParseFromString(
147 msg_text, (google::protobuf::Message*)&new_instance)) {
148 fprintf(stderr, "Could not parse protobuf for file '%s'\n.",
149 file_name.c_str());
150 continue;
151 }
152
153 // Add header data to new instance.
154 traffic_annotation::NetworkTrafficAnnotation_TrafficSource* src =
155 new_instance.proto.mutable_source();
156 src->set_file(header_lines[0]);
157 src->set_function(header_lines[1]);
158 src->set_line(atoi(header_lines[2].c_str()));
159 new_instance.callee = header_lines[3];
160 new_instance.unique_id = header_lines[5];
161 new_instance.hash_code =
162 COMPUTE_STRING_HASH(new_instance.unique_id.c_str());
163 instances->push_back(new_instance);
164 }
165 printf("%i annotation instance(s) read.\n", (int)instances->size());
166 }
167
168 // Checks to see if unique ids have similar hash codes and marks them if so.
169 void MarkHashCollisions(std::vector<Instance>* instances) {
170 std::map<unsigned int, Instance*> hashed_ids;
171 for (auto& instance : *instances) {
172 if (instance.state == Instance::kOK) {
173 auto match = hashed_ids.find(instance.hash_code);
174 if (match != hashed_ids.end())
175 instance.state = match->second->state = Instance::kDuplicate;
176 else
177 hashed_ids.insert(std::make_pair(instance.hash_code, &instance));
178 }
179 }
180 }
181
182 // Writes summary of annotation instances to a file. Returns true if successful.
183 bool WriteSummaryFile(const std::vector<Instance>& instances,
184 const base::FilePath& file_path) {
185 FILE* output_file = fopen(file_path.MaybeAsASCII().c_str(), "wt");
186 if (!output_file) {
187 printf(" Could not create output file: %s",
188 file_path.MaybeAsASCII().c_str());
189 return false;
190 }
191 for (const auto& instance : instances) {
192 fprintf(output_file, "Unique ID: %s\tHash code:%u\n",
193 instance.unique_id.c_str(), instance.hash_code);
194 if (instance.state == Instance::kUndefined)
195 fprintf(output_file, "WARNING: Undefined annotation.\n");
196 else if (instance.state == Instance::kDuplicate)
197 fprintf(output_file, "WARNING: Duplicate hash code for unique id.\n");
198 fprintf(output_file, "Calling Function: %s\n", instance.callee.c_str());
199 std::string temp;
200 google::protobuf::TextFormat::PrintToString(instance.proto, &temp);
201 fprintf(output_file, "Content:\n%s\n", temp.c_str());
202 fprintf(output_file, "------------------------------------------------\n");
203 }
204 fclose(output_file);
205 printf("Output file %s written for %i instances.\n",
206 file_path.MaybeAsASCII().c_str(), (int)instances.size());
207 return true;
208 }
209
210 #ifdef _WIN32
211 int wmain(int argc, wchar_t* argv[]) {
212 #else
213 int main(int argc, char* argv[]) {
214 #endif
215 // Parse switches.
216 base::CommandLine command_line = base::CommandLine(argc, argv);
217 if (command_line.HasSwitch("help") || command_line.HasSwitch("h") ||
218 argc == 1) {
219 printf(
220 "Usage: traffic_annotation_auditor [OPTION]... [path_filter]...\n"
221 "Extracts network traffic annotations from source files. If path "
222 "filter(s) are specified, only those directories of the source "
223 "will be analyzed.\n"
224 "Options:\n"
225 " -h, --help Shows help.\n"
226 " --build_dir Path to the build directory from which the\n"
227 " annotations will be extracted.\n"
228 " --extractor_output_dir Path to the directory that extracted\n"
229 " partial files will be written to. Will\n"
230 " be automatically generated and deleted\n"
231 " if not specified.\n"
232 " --extracted_input_dir Path to a directory where extracted\n"
233 " partial annotations are already stored.\n"
234 " If specified, build directory will be\n"
235 " ignored.\n"
236 " --summary_file Path to an output file with summary of\n"
237 " extracted annotations.\n"
238 "Example:\n"
239 " traffic_annotation_auditor --build_dir=out/Debug\n"
240 " --summary_file=report.txt\n");
241 return 1;
242 }
243 base::FilePath extracted_files_dir =
244 command_line.GetSwitchValuePath("extracted_input_dir");
245 base::FilePath build_dir = command_line.GetSwitchValuePath("build_dir");
246 base::FilePath extractor_output_dir =
247 command_line.GetSwitchValuePath("extractor_output_dir");
248 base::FilePath summary_file = command_line.GetSwitchValuePath("summary_file");
249 base::ScopedTempDir temp_dir;
250
251 if (summary_file.empty())
252 fprintf(stderr, "WARNING: Output file not specified.\n");
253
254 // Extract annotations.
255 if (extracted_files_dir.empty()) {
256 // Get build directory, if it is empty issue an error.
257 if (build_dir.empty()) {
258 fprintf(
259 stderr,
260 "You must either specify build directory to run clang "
261 "tool and extract annotations, or specify extracted files's input "
262 "directory where already extracted files exist.\n");
263 return 1;
264 }
265 // If output directory is not provided, create a temporary one.
266 if (extractor_output_dir.empty()) {
267 if (!temp_dir.CreateUniqueTempDirUnderPath(build_dir)) {
268 fprintf(stderr, "Could not create temporary directory in %s.\n",
269 build_dir.MaybeAsASCII().c_str());
270 return 1;
271 }
272 extractor_output_dir = temp_dir.GetPath();
273 } else {
274 // Ensure given directory is empty.
275 if (!base::FileEnumerator(extractor_output_dir, false,
276 base::FileEnumerator::FILES)
277 .Next()
278 .empty()) {
279 fprintf(stderr, "Output directory (%s) should be empty.\n",
280 extractor_output_dir.MaybeAsASCII().c_str());
281 return 1;
282 }
283 }
284
285 // Get path filters, if none is provided, just add all.
286 base::CommandLine::StringVector path_filters = command_line.GetArgs();
287 if (!path_filters.size()) {
288 base::FilePath temp;
289 path_filters.push_back(temp.AppendASCII("./").value().c_str());
290 }
291
292 if (!RunClangTool(command_line.GetProgram().DirName().AppendASCII("../.."),
293 build_dir, extractor_output_dir, path_filters)) {
294 return 1;
295 }
296
297 extracted_files_dir = extractor_output_dir;
298 }
299
300 // Read all extracted files.
301 std::vector<Instance> instances;
302 ReadExtractedFiles(extracted_files_dir, &instances);
303
304 if (instances.empty()) {
305 fprintf(stderr, "Could not read any file.\n");
306 return 1;
307 } else {
308 MarkHashCollisions(&instances);
309 }
310
311 // Create Summary file if requested.
312 if (!summary_file.empty() && !WriteSummaryFile(instances, summary_file))
313 return 1;
314
315 return 0;
316 }
OLDNEW
« tools/traffic_annotation/README.txt ('K') | « tools/traffic_annotation/README.txt ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698