Chromium Code Reviews| OLD | NEW |
|---|---|
| (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. | |
|
battre
2016/10/26 14:29:20
Please add this.
Ramin Halavati
2016/10/27 09:40:10
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/file_util.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() { | |
|
battre
2016/10/26 14:29:19
use initializer list?
Ramin Halavati
2016/10/27 09:40:10
Done.
| |
| 26 state = ok; | |
| 27 hash_code = 0; | |
| 28 } | |
| 29 traffic_annotation::NetworkTrafficAnnotation proto; | |
| 30 std::string unique_id; | |
| 31 std::string callee; | |
| 32 uint hash_code; | |
|
battre
2016/10/26 14:29:20
I think that C++ makes no definition of the size o
Ramin Halavati
2016/10/27 09:40:10
In the other CL "unsigned" type is returned by sev
| |
| 33 enum { ok, duplicate, undefined } state; | |
|
battre
2016/10/26 14:29:19
See Style guide on the naming of enums.
Ramin Halavati
2016/10/27 09:40:10
Done.
| |
| 34 }; | |
| 35 | |
| 36 // Runs clang tool, returns true if all OK. | |
| 37 bool RunClangTool(const base::FilePath& src_dir, | |
| 38 const base::FilePath& build_dir, | |
| 39 const base::FilePath& output_dir, | |
| 40 const base::CommandLine::StringVector& path_filters) { | |
| 41 for (auto& path : path_filters) { | |
| 42 int result = system( | |
| 43 base::StringPrintf( | |
| 44 "%s traffic_annotation_extractor" | |
| 45 " --generate-compdb %s %s --tool-params 'output_dir=%s'", | |
| 46 src_dir.Append("tools/clang/scripts/run_tool.py").value().c_str(), | |
| 47 build_dir.value().c_str(), path.c_str(), output_dir.value().c_str()) | |
| 48 .c_str()); | |
| 49 if (result) { | |
| 50 printf(" Executing clang tool returned code: %i.\n", result); | |
| 51 return false; | |
| 52 } | |
| 53 } | |
| 54 return true; | |
| 55 } | |
| 56 | |
| 57 // Reads an extrated annotation file. The first 6 lines of the file include: | |
| 58 // 0- File path and file name. | |
| 59 // 1- Name of the function that is called using annotation. | |
| 60 // 2- Line number. | |
| 61 // 3- Name of the function including this position. | |
| 62 // 4- Error text associated with this annotation. | |
| 63 // 5- Unique id of annotation. | |
| 64 // The rest of the file is the protobuf text. | |
| 65 bool ReadFile(const base::FilePath& file_path, | |
| 66 std::vector<std::string>* header_lines, | |
| 67 std::string* msg_text) { | |
| 68 std::ifstream input_file(file_path.value()); | |
| 69 if (!input_file.is_open()) | |
| 70 return false; | |
| 71 | |
|
battre
2016/10/26 14:29:19
msg_text->clear();
Ramin Halavati
2016/10/27 09:40:10
Done.
| |
| 72 while (!input_file.eof()) { | |
| 73 std::string temp; | |
|
battre
2016/10/26 14:29:20
std::string line;?
Ramin Halavati
2016/10/27 09:40:10
Done.
| |
| 74 getline(input_file, temp); | |
| 75 if (header_lines->size() < 6) | |
| 76 header_lines->push_back(temp); | |
| 77 else | |
| 78 *msg_text = *msg_text + "\n" + temp; | |
| 79 } | |
| 80 input_file.close(); | |
| 81 return true; | |
| 82 } | |
| 83 | |
| 84 // Reads all extracted txt files form given input folder and populates instances | |
| 85 // and errors. | |
| 86 void ReadExtractedFiles(const base::FilePath& folder_name, | |
| 87 std::vector<Instance>* instances) { | |
| 88 base::FileEnumerator file_iter(folder_name, false, | |
| 89 base::FileEnumerator::FILES, "*.txt"); | |
| 90 while (!file_iter.Next().empty()) { | |
| 91 std::string file_name = file_iter.GetInfo().GetName().AsUTF8Unsafe(); | |
| 92 printf("\tReading %s...\n", file_name.c_str()); | |
| 93 | |
| 94 std::vector<std::string> header_lines; | |
| 95 std::string msg_text; | |
| 96 if (!ReadFile(folder_name.Append(file_iter.GetInfo().GetName()), | |
| 97 &header_lines, &msg_text)) { | |
| 98 fprintf(stderr, "Could not open file '%s'.", file_name.c_str()); | |
| 99 continue; | |
| 100 } | |
| 101 if (header_lines.size() < 6) { | |
| 102 fprintf(stderr, "Header lines are not complete for file '%s'.", | |
| 103 file_name.c_str()); | |
| 104 continue; | |
| 105 } | |
| 106 if (!header_lines[4].empty()) { | |
| 107 fprintf(stderr, "Error passed from extractor for file '%s': %s.", | |
| 108 file_name.c_str(), header_lines[4].c_str()); | |
| 109 continue; | |
| 110 } | |
| 111 | |
| 112 Instance new_instance; | |
| 113 if (header_lines[5] == "\"Undefined\"") { | |
| 114 new_instance.state = Instance::undefined; | |
| 115 } else if (!google::protobuf::TextFormat::ParseFromString( | |
| 116 msg_text, (google::protobuf::Message*)&new_instance)) { | |
| 117 fprintf(stderr, "Could not parse protobuf for file '%s'.", | |
| 118 file_name.c_str()); | |
| 119 continue; | |
| 120 } | |
| 121 | |
| 122 // Add header data to new instance. | |
| 123 traffic_annotation::NetworkTrafficAnnotation_TrafficSource* src = | |
| 124 new_instance.proto.mutable_source(); | |
| 125 src->set_file(header_lines[0]); | |
| 126 src->set_function(header_lines[1]); | |
| 127 src->set_line(atoi(header_lines[2].c_str())); | |
| 128 new_instance.callee = header_lines[3]; | |
| 129 new_instance.unique_id = header_lines[5]; | |
| 130 new_instance.hash_code = | |
| 131 COMPUTE_STRING_HASH(new_instance.unique_id.c_str()); | |
| 132 instances->push_back(new_instance); | |
| 133 } | |
| 134 printf("%i annotation instance(s) read.\n", (int)instances->size()); | |
| 135 } | |
| 136 | |
| 137 // Checks to see if two unique ids have similar hash codes. | |
|
battre
2016/10/26 14:29:20
This function mutates the instances. That should b
Ramin Halavati
2016/10/27 09:40:10
Done.
| |
| 138 void CheckDuplicateHashCodes(std::vector<Instance>* instances) { | |
| 139 std::map<uint, Instance*> hashed_ids; | |
| 140 for (auto& instance : *instances) | |
|
battre
2016/10/26 14:29:20
{}
Ramin Halavati
2016/10/27 09:40:10
Done.
| |
| 141 if (instance.state == Instance::ok) { | |
| 142 auto p = hashed_ids.find(instance.hash_code); | |
|
battre
2016/10/26 14:29:20
what is p? --> Too short variable name. Maybe "ite
Ramin Halavati
2016/10/27 09:40:10
Renamed to match.
| |
| 143 if (p != hashed_ids.end()) | |
| 144 instance.state = p->second->state = Instance::duplicate; | |
| 145 else | |
| 146 hashed_ids.insert(std::make_pair(instance.hash_code, &instance)); | |
| 147 } | |
| 148 } | |
| 149 | |
| 150 // Writes summary of annotation instances to a file. Returns true if successful. | |
| 151 bool WriteSummaryFile(const std::vector<Instance>& instances, | |
| 152 const std::string& file_name) { | |
| 153 FILE* output_file = fopen(file_name.c_str(), "wt"); | |
| 154 if (!output_file) { | |
| 155 printf(" Could not create output file: %s", file_name.c_str()); | |
| 156 return false; | |
| 157 } | |
| 158 for (auto& instance : instances) { | |
|
battre
2016/10/26 14:29:19
const
Ramin Halavati
2016/10/27 09:40:10
Done.
| |
| 159 fprintf(output_file, "Unique ID: %s\tHash code:%u\n", | |
| 160 instance.unique_id.c_str(), instance.hash_code); | |
| 161 if (instance.state == Instance::undefined) | |
| 162 fprintf(output_file, "WARNING: Undefined annotation.\n"); | |
| 163 else if (instance.state == Instance::duplicate) | |
| 164 fprintf(output_file, "WARNING: Duplicate hash code for unique id.\n"); | |
| 165 fprintf(output_file, "Calling Function: %s\n", instance.callee.c_str()); | |
| 166 std::string temp; | |
| 167 google::protobuf::TextFormat::PrintToString(instance.proto, &temp); | |
| 168 fprintf(output_file, "Content:\n%s\n", temp.c_str()); | |
| 169 fprintf(output_file, "------------------------------------------------\n"); | |
| 170 } | |
| 171 fclose(output_file); | |
| 172 printf("Output file %s written for %i instances.\n", file_name.c_str(), | |
| 173 (int)instances.size()); | |
| 174 return true; | |
| 175 } | |
| 176 | |
| 177 int main(int argc, char* argv[]) { | |
| 178 // Parse switches. | |
| 179 base::CommandLine command_line = base::CommandLine(argc, argv); | |
| 180 if (command_line.HasSwitch("help") || command_line.HasSwitch("h") || | |
| 181 argc == 1) { | |
| 182 printf( | |
| 183 "Usage: traffic_annotation_auditor [OPTION]... [path_filter]...\n" | |
| 184 "Extracts network traffic annotations from source files. If path " | |
| 185 "filter(s) are specified, only those directories of the source " | |
| 186 "will be analyzed.\n" | |
| 187 "Options:\n" | |
| 188 " -h, --help Shows this.\n" | |
| 189 " --build_dir Path to the build directory from which the\n" | |
| 190 " annotations will be extracted.\n" | |
| 191 " --extractor_output_dir Path to the directory that extracted\n" | |
| 192 " partial files will be written to. Will\n" | |
| 193 " be automatically generated and deleted\n" | |
| 194 " if not specified.\n" | |
| 195 " --extracted_input_dir Path to a directory where extracted\n" | |
| 196 " partial annotations are already stored.\n" | |
| 197 " If specified, build directory will be\n" | |
| 198 " ignored.\n" | |
| 199 " --summary_file Path to an ouput file with summary of\n" | |
| 200 " extracted annotations.\n" | |
| 201 "Example:\n" | |
| 202 " traffic_annotation_auditor --build_dir=out/Debug\n" | |
| 203 " --summery_file=report.txt\n"); | |
| 204 return 1; | |
| 205 } | |
| 206 base::FilePath extracted_files_dir = | |
| 207 command_line.GetSwitchValuePath("extracted_input_dir"); | |
| 208 base::FilePath build_dir = command_line.GetSwitchValuePath("build_dir"); | |
| 209 base::FilePath extractor_output_dir = | |
| 210 command_line.GetSwitchValuePath("extractor_output_dir"); | |
| 211 base::FilePath summary_file = command_line.GetSwitchValuePath("summary_file"); | |
| 212 | |
| 213 // Extract annotations. | |
| 214 if (extracted_files_dir.empty()) { | |
| 215 // Get build directory, if it is empty issue an error. | |
| 216 if (build_dir.empty()) { | |
| 217 fprintf( | |
| 218 stderr, | |
| 219 "You must either specify build directory to run clang " | |
| 220 "tool and extract annotations, or specify extracted files's input " | |
| 221 "directory where already extracted files exist.\n"); | |
| 222 return 1; | |
| 223 } | |
| 224 // Get output directory, if it is not provided, create a temporary one. | |
| 225 if (extracted_files_dir.empty()) { | |
| 226 if (!base::CreateTemporaryDirInDir(build_dir, "", &extracted_files_dir)) { | |
| 227 fprintf(stderr, "Could not create temporary directory in %s.", | |
| 228 build_dir.value().c_str()); | |
|
battre
2016/10/26 14:29:20
Unless you create a scoped temp directory, the dir
Ramin Halavati
2016/10/27 09:40:10
Done.
| |
| 229 return 1; | |
| 230 } | |
| 231 } else { | |
| 232 // Ensure given directory is empty. | |
| 233 if (!base::FileEnumerator(extracted_files_dir, false, | |
| 234 base::FileEnumerator::FILES, "*.txt") | |
| 235 .Next() | |
| 236 .empty()) { | |
| 237 fprintf(stderr, "Ouput directory (%s) should be empty.", | |
| 238 extracted_files_dir.value().c_str()); | |
| 239 return 1; | |
| 240 } | |
| 241 } | |
| 242 | |
| 243 // Get path filters, if none is provided, just add all. | |
| 244 base::CommandLine::StringVector path_filters = command_line.GetArgs(); | |
| 245 if (!path_filters.size()) | |
| 246 path_filters.push_back("./"); | |
| 247 | |
| 248 if (!RunClangTool(command_line.GetProgram().DirName().Append("../.."), | |
| 249 build_dir, extracted_files_dir, path_filters)) | |
|
battre
2016/10/26 14:29:19
{}
Ramin Halavati
2016/10/27 09:40:10
Done.
| |
| 250 return 1; | |
| 251 } | |
| 252 | |
| 253 // Read all extracted files. | |
| 254 std::vector<Instance> instances; | |
| 255 ReadExtractedFiles(extracted_files_dir, &instances); | |
| 256 | |
| 257 if (instances.size()) { | |
|
battre
2016/10/26 14:29:20
if (!instances.empty()) {
Ramin Halavati
2016/10/27 09:40:10
Done.
| |
| 258 CheckDuplicateHashCodes(&instances); | |
| 259 } else { | |
| 260 printf("Could not read any file.\n"); | |
| 261 return 1; | |
| 262 } | |
| 263 | |
| 264 // Create Summary file. | |
| 265 if (!summary_file.empty()) | |
|
battre
2016/10/26 14:29:19
{}
Ramin Halavati
2016/10/27 09:40:10
Done.
| |
| 266 if (!WriteSummaryFile(instances, | |
| 267 command_line.GetSwitchValueASCII("summary_file"))) | |
|
battre
2016/10/26 14:29:20
{}
Ramin Halavati
2016/10/27 09:40:10
Done.
| |
| 268 return 1; | |
| 269 | |
| 270 return 0; | |
| 271 } | |
| OLD | NEW |