| OLD | NEW |
| 1 #!/usr/bin/env python | 1 #!/usr/bin/env python |
| 2 # Copyright (c) 2017 The Chromium Authors. All rights reserved. | 2 # Copyright 2017 The Chromium Authors. All rights reserved. |
| 3 # Use of this source code is governed by a BSD-style license that can be | 3 # Use of this source code is governed by a BSD-style license that can be |
| 4 # found in the LICENSE file. | 4 # found in the LICENSE file. |
| 5 | 5 |
| 6 """This script is used to extract network traffic annotations from Chrome. | 6 """This script is used to extract network traffic annotations from Chrome. |
| 7 Please refer to README.md for running steps.""" | 7 Please refer to README.md for running steps.""" |
| 8 | 8 |
| 9 import argparse | 9 import argparse |
| 10 import datetime |
| 10 import os | 11 import os |
| 11 import subprocess | 12 import subprocess |
| 12 import sys | 13 import sys |
| 14 import tempfile |
| 15 |
| 16 from traffic_annotation_file_filter import TrafficAnnotationFileFilter |
| 17 |
| 13 | 18 |
| 14 # These two lines are required to import protobuf from third_party directory | 19 # These two lines are required to import protobuf from third_party directory |
| 15 # instead of the one installed with python. | 20 # instead of the one installed with python. |
| 16 from prepare_protobuf import PrepareProtobuf | 21 from prepare_protobuf import PrepareProtobuf |
| 17 PrepareProtobuf() | 22 PrepareProtobuf() |
| 18 | 23 |
| 19 from google.protobuf import text_format | 24 from google.protobuf import text_format |
| 20 import traffic_annotation_pb2 | 25 import traffic_annotation_pb2 |
| 21 | 26 |
| 22 | 27 |
| (...skipping 10 matching lines...) Expand all Loading... |
| 33 'net/traffic_annotation/network_traffic_annotation.h'. | 38 'net/traffic_annotation/network_traffic_annotation.h'. |
| 34 args: | 39 args: |
| 35 unique_id: str The string to be converted to hash code. | 40 unique_id: str The string to be converted to hash code. |
| 36 | 41 |
| 37 Returns: | 42 Returns: |
| 38 unsigned int Hash code of the input string | 43 unsigned int Hash code of the input string |
| 39 """ | 44 """ |
| 40 return _RecursiveHash(unique_id) if len(unique_id) else -1 | 45 return _RecursiveHash(unique_id) if len(unique_id) else -1 |
| 41 | 46 |
| 42 | 47 |
| 43 def _RunClangTool(src_dir, build_dir, path_filters): | 48 def _RunClangTool(src_dir, build_dir, path_filters, prefilter_files): |
| 44 """Executes the clang tool to extract annotations. | 49 """Executes the clang tool to extract annotations. |
| 45 Args: | 50 Args: |
| 46 src_dir: str Path to the src directory of Chrome. | 51 src_dir: str Path to the src directory of Chrome. |
| 47 build_dir: str Path to the build directory. | 52 build_dir: str Path to the build directory. |
| 48 path_filters: list of str List of paths to source directories for | 53 path_filters: list of str List of paths to source directories for |
| 49 extraction. | 54 extraction. |
| 55 prefilter_files: bool Flag stating if source files should be first filtered |
| 56 using annotation related keywords and then given to clang tool. |
| 50 | 57 |
| 51 Returns: | 58 Returns: |
| 52 raw_annotations: str Output of clang tool (extracted content and metadata of | 59 str Output of clang tool (extracted content and metadata of annotations). |
| 53 annotations). | |
| 54 """ | 60 """ |
| 55 raw_annotations = "" | 61 args = [ |
| 56 for path in path_filters: | |
| 57 args = [ | |
| 58 src_dir + "/tools/clang/scripts/run_tool.py", | 62 src_dir + "/tools/clang/scripts/run_tool.py", |
| 59 "--generate-compdb", | 63 "--generate-compdb", |
| 60 "--tool=traffic_annotation_extractor", | 64 "--tool=traffic_annotation_extractor", |
| 61 "-p", build_dir, | 65 "-p=" + build_dir] |
| 62 path] | 66 if sys.platform == "win32": |
| 63 if sys.platform == "win32": | |
| 64 args.insert(0, "python") | 67 args.insert(0, "python") |
| 65 command = subprocess.Popen(args, stdout=subprocess.PIPE, | 68 |
| 66 stderr=subprocess.PIPE) | 69 if prefilter_files: |
| 67 stdout_text, stderr_text = command.communicate() | 70 file_filter = TrafficAnnotationFileFilter(False) |
| 68 raw_annotations += stdout_text | 71 for path in path_filters: |
| 69 if stderr_text: | 72 args += file_filter.GetFilteredFilesList(path) |
| 70 print stderr_text | 73 else: |
| 71 return raw_annotations | 74 args += path_filters |
| 75 |
| 76 command = subprocess.Popen(args, stdout=subprocess.PIPE, |
| 77 stderr=subprocess.PIPE) |
| 78 stdout_text, stderr_text = command.communicate() |
| 79 if stderr_text: |
| 80 print stderr_text |
| 81 return stdout_text |
| 72 | 82 |
| 73 | 83 |
| 74 def _ParsRawAnnotations(raw_annotations): | 84 def _ParsRawAnnotations(raw_annotations): |
| 75 """Parses raw annotations texts which are received from the clang tool. | 85 """Parses raw annotations texts which are received from the clang tool. |
| 76 Args: | 86 Args: |
| 77 raw_annotations: str Serialization of annotations and metadata. Each | 87 raw_annotations: str Serialization of annotations and metadata. Each |
| 78 annotation should have either of the following lines: | 88 annotation should have either of the following lines: |
| 79 1- "==== NEW ANNOTATION ====" | 89 1- "==== NEW ANNOTATION ====" |
| 80 2- File path. | 90 2- File path. |
| 81 3- Name of the function including this position. | 91 3- Name of the function including this position. |
| (...skipping 34 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 116 raise Exception( | 126 raise Exception( |
| 117 "Not enough header lines at line %i." % current) | 127 "Not enough header lines at line %i." % current) |
| 118 | 128 |
| 119 # Extract header lines. | 129 # Extract header lines. |
| 120 source = traffic_annotation_pb2.NetworkTrafficAnnotation.TrafficSource() | 130 source = traffic_annotation_pb2.NetworkTrafficAnnotation.TrafficSource() |
| 121 source.file = lines[current + 1] | 131 source.file = lines[current + 1] |
| 122 source.function = lines[current + 2] | 132 source.function = lines[current + 2] |
| 123 source.line = int(lines[current + 3]) | 133 source.line = int(lines[current + 3]) |
| 124 unique_id = lines[current + 5] | 134 unique_id = lines[current + 5] |
| 125 | 135 |
| 126 new_metadata = {'function_type': lines[current + 4], | 136 new_metadata = {"function_type": lines[current + 4], |
| 127 'extra_id': lines[current + 6], | 137 "extra_id": lines[current + 6], |
| 128 'unique_id_hash': _ComputeStringHash(unique_id)} | 138 "unique_id_hash": _ComputeStringHash(unique_id)} |
| 129 # Extract serialized proto. | 139 # Extract serialized proto. |
| 130 current += 7 | 140 current += 7 |
| 131 annotation_text = "" | 141 annotation_text = "" |
| 132 | 142 |
| 133 while current < len(lines): | 143 while current < len(lines): |
| 134 if lines[current] == "==== ANNOTATION ENDS ====": | 144 if lines[current] == "==== ANNOTATION ENDS ====": |
| 135 break | 145 break |
| 136 else: | 146 else: |
| 137 annotation_text += lines[current] | 147 annotation_text += lines[current] |
| 138 current += 1 | 148 current += 1 |
| (...skipping 49 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 188 def _WriteSummaryFile(annotations, metadata, errors, file_path): | 198 def _WriteSummaryFile(annotations, metadata, errors, file_path): |
| 189 """Writes extracted annotations and errors into a simple text file. | 199 """Writes extracted annotations and errors into a simple text file. |
| 190 args: | 200 args: |
| 191 annotations: ExtractedNetworkTrafficAnnotation A protobuf including all | 201 annotations: ExtractedNetworkTrafficAnnotation A protobuf including all |
| 192 extracted annotations. | 202 extracted annotations. |
| 193 metadata: list of dict Metadata for annotations, as specified in the outputs | 203 metadata: list of dict Metadata for annotations, as specified in the outputs |
| 194 of _ParsRawAnnotations function. | 204 of _ParsRawAnnotations function. |
| 195 errors: list of str List of all extraction errors. | 205 errors: list of str List of all extraction errors. |
| 196 file_path: str File path to the brief summary file. | 206 file_path: str File path to the brief summary file. |
| 197 """ | 207 """ |
| 198 with open(file_path, 'w') as summary_file: | 208 with open(file_path, "w") as summary_file: |
| 199 if errors: | 209 if errors: |
| 200 summary_file.write("Errors:\n%s\n\n" % "\n".join(errors)) | 210 summary_file.write("Errors:\n%s\n\n" % "\n".join(errors)) |
| 201 if len(annotations.network_traffic_annotation): | 211 if len(annotations.network_traffic_annotation): |
| 202 summary_file.write("Annotations:\n") | 212 summary_file.write("Annotations:\n") |
| 203 for annotation, meta in zip(annotations.network_traffic_annotation, | 213 for annotation, meta in zip(annotations.network_traffic_annotation, |
| 204 metadata): | 214 metadata): |
| 205 summary_file.write( | 215 summary_file.write( |
| 206 "%s\n+MetaData:%s\n---\n" % (str(annotation), str(meta))) | 216 "%s\n+MetaData:%s\n---\n" % (str(annotation), str(meta))) |
| 207 | 217 |
| 208 | 218 |
| 209 def _WriteHashCodesFile(annotations, metadata, file_path): | 219 def _WriteHashCodesFile(annotations, metadata, file_path): |
| 210 """Writes unique ids and hash codes of annotations into a simple text file. | 220 """Writes unique ids and hash codes of annotations into a simple text file. |
| 211 args: | 221 args: |
| 212 annotations: ExtractedNetworkTrafficAnnotation A protobuf including all | 222 annotations: ExtractedNetworkTrafficAnnotation A protobuf including all |
| 213 extracted annotations. | 223 extracted annotations. |
| 214 metadata: list of dict Metadata for annotations, as specified in the outputs | 224 metadata: list of dict Metadata for annotations, as specified in the outputs |
| 215 of _ParsRawAnnotations function. | 225 of _ParsRawAnnotations function. |
| 216 file_path: str File path to the brief summary file. | 226 file_path: str File path to the brief summary file. |
| 217 """ | 227 """ |
| 218 with open(file_path, 'w') as summary_file: | 228 hash_list = [] |
| 219 for annotation, meta in zip(annotations.network_traffic_annotation, | 229 for annotation, meta in zip(annotations.network_traffic_annotation, metadata): |
| 220 metadata): | 230 hash_list += ["%s,%s" % (annotation.unique_id, meta["unique_id_hash"])] |
| 221 summary_file.write( | 231 for keyword in ("test", "test_partial", "undefined", "missing"): |
| 222 "%s,%s\n" % (annotation.unique_id, meta['unique_id_hash'])) | 232 hash_list += ["%s,%s" % (keyword, _ComputeStringHash(keyword))] |
| 223 for keyword in ("test", "test_partial", "undefined", "missing"): | 233 open(file_path, "w").write("\n".join(sorted(hash_list))) |
| 224 summary_file.write( | |
| 225 "%s,%s\n" % (keyword, _ComputeStringHash(keyword))) | |
| 226 | 234 |
| 227 | 235 |
| 228 def main(): | 236 def main(): |
| 229 parser = argparse.ArgumentParser(description='Traffic Annotation Auditor.') | 237 parser = argparse.ArgumentParser(description="Traffic Annotation Auditor.") |
| 230 parser.add_argument('--build-dir', | 238 parser.add_argument("--build-dir", |
| 231 help='Path to the build directory.') | 239 help="Path to the build directory.") |
| 232 parser.add_argument('--extractor-output', | 240 parser.add_argument("--extractor-output", |
| 233 help='Optional path to the temporary file that extracted ' | 241 help="Optional path to the temporary file that extracted " |
| 234 'annotations will be stored into.') | 242 "annotations will be stored into.") |
| 235 parser.add_argument('--extractor-input', | 243 parser.add_argument("--extractor-input", |
| 236 help='Optional path to the file that temporary extracted ' | 244 help="Optional path to the file that temporary extracted " |
| 237 'annotations are already stored in. If this is ' | 245 "annotations are already stored in. If this is " |
| 238 'provided, clang tool is not run and this is used ' | 246 "provided, clang tool is not run and this is used " |
| 239 'as input.') | 247 "as input.") |
| 240 parser.add_argument('--summary-file', | 248 parser.add_argument("--summary-file", |
| 241 help='Path to the output file with all annotations.') | 249 help="Path to the output file with all annotations.") |
| 242 parser.add_argument('--hash-codes-file', | 250 parser.add_argument("--hash-codes-file", |
| 243 help='Path to the output file with the list of unique ' | 251 help="Path to the output file with the list of unique " |
| 244 'ids and their hash codes.') | 252 "ids and their hash codes.") |
| 245 parser.add_argument('path_filters', | 253 parser.add_argument("path_filters", |
| 246 nargs='*', | 254 nargs="*", |
| 247 help='Optional paths to filter what files the tool is ' | 255 help="Optional paths to filter what files the tool is " |
| 248 'run on.') | 256 "run on.", |
| 257 default=[""]) |
| 258 parser.add_argument("--prefilter-files", action="store_true", |
| 259 help="Checks source files for patterns of annotations " |
| 260 "and network functions that may require annotation " |
| 261 "and limits running clang tool only on them.") |
| 249 args = parser.parse_args() | 262 args = parser.parse_args() |
| 250 | 263 |
| 251 if not args.summary_file and not args.hash_codes_file: | 264 if not args.summary_file and not args.hash_codes_file: |
| 252 print "Warning: Output file not specified." | 265 print "Warning: Output file not specified." |
| 253 | 266 |
| 254 # If a pre-extracted input file is provided, load it. | 267 # If a pre-extracted input file is provided, load it. |
| 255 if args.extractor_input: | 268 if args.extractor_input: |
| 256 with open(args.extractor_input, 'r') as raw_file: | 269 with open(args.extractor_input, "r") as raw_file: |
| 257 raw_annotations = raw_file.read() | 270 raw_annotations = raw_file.read() |
| 258 else: | 271 else: |
| 259 # Either extacted input file or build directory should be provided. | 272 # Either extacted input file or build directory should be provided. |
| 260 if not args.build_dir: | 273 if not args.build_dir: |
| 261 print "You must either specify the build directory to run the clang " \ | 274 print "You must either specify the build directory to run the clang " \ |
| 262 "tool and extract annotations, or specify the input directory " \ | 275 "tool and extract annotations, or specify the input directory " \ |
| 263 "where extracted annotation files already exist.\n" | 276 "where extracted annotation files already exist.\n" |
| 264 return 1 | 277 return 1 |
| 265 | 278 |
| 266 # Get Chrome source directory with relative path from this file. | 279 # Get Chrome source directory with relative path from this file. |
| 267 chrome_source = os.path.abspath(os.path.join(os.path.dirname( | 280 chrome_source = os.path.abspath(os.path.join(os.path.dirname( |
| 268 os.path.realpath(__file__)), "..", "..", "..")) | 281 os.path.realpath(__file__)), "..", "..", "..")) |
| 269 raw_annotations = _RunClangTool(chrome_source, args.build_dir, | 282 raw_annotations = _RunClangTool(chrome_source, args.build_dir, |
| 270 args.path_filters if args.path_filters else ["./"]) | 283 args.path_filters, args.prefilter_files) |
| 271 | 284 |
| 272 if args.extractor_output: | 285 if args.extractor_output: |
| 273 with open(args.extractor_output, 'w') as raw_file: | 286 with open(args.extractor_output, "w") as raw_file: |
| 274 raw_file.write(raw_annotations) | 287 raw_file.write(raw_annotations) |
| 275 | 288 |
| 276 annotations, metadata, errors = _ParsRawAnnotations(raw_annotations) | 289 annotations, metadata, errors = _ParsRawAnnotations(raw_annotations) |
| 277 | 290 |
| 278 if not annotations: | 291 if not annotations: |
| 279 print "Could not extract any annotation." | 292 print "Could not extract any annotation." |
| 280 if errors: | 293 if errors: |
| 281 print "Errors:\n%s" % "\n".join(errors) | 294 print "Errors:\n%s" % "\n".join(errors) |
| 282 return 1 | 295 return 1 |
| 283 | 296 |
| 284 if args.summary_file: | 297 if args.summary_file: |
| 285 _WriteSummaryFile(annotations, metadata, errors, args.summary_file) | 298 _WriteSummaryFile(annotations, metadata, errors, args.summary_file) |
| 286 | 299 |
| 287 if args.hash_codes_file: | 300 if args.hash_codes_file: |
| 288 _WriteHashCodesFile(annotations, metadata, args.hash_codes_file) | 301 _WriteHashCodesFile(annotations, metadata, args.hash_codes_file) |
| 289 | 302 |
| 290 return 0 | 303 return 0 |
| 291 | 304 |
| 292 | 305 |
| 293 if __name__ == '__main__': | 306 if __name__ == "__main__": |
| 294 sys.exit(main()) | 307 sys.exit(main()) |
| OLD | NEW |