| 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. | 
|  | 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 } | 
| OLD | NEW | 
|---|