Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright 2017 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 // This clang tool finds all instances of the following functions: | 5 // This clang tool finds all instances of the following functions: |
| 6 // - net::DefineNetworkTrafficAnnotation | 6 // - net::DefineNetworkTrafficAnnotation |
| 7 // - net::DefinePartialNetworkTrafficAnnotation | 7 // - net::DefinePartialNetworkTrafficAnnotation |
| 8 // - net::CompleteNetworkTrafficAnnotation | 8 // - net::CompleteNetworkTrafficAnnotation |
| 9 // - net::BranchedCompleteNetworkTrafficAnnotation | 9 // - net::BranchedCompleteNetworkTrafficAnnotation |
| 10 // It extracts the location info and content of annotation tags, and outputs | 10 // It extracts the location info and content of annotation tags, and outputs |
| 11 // them to llvm::outs. Please refer to README.md for build and usage | 11 // them to llvm::outs. It also extracts all calls the following network request |
| 12 // instructions. | 12 // creation functions and returns their source location and availability of a |
| 13 // net::[Partial]NetworkTrafficAnnotation parameter in them: | |
| 14 // - SSLClientSocket::SSLClientSocket | |
| 15 // - TCPClientSocket::TCPClientSocket | |
| 16 // - UDPClientSocket::UDPClientSocket | |
| 17 // - URLFetcher::Create | |
| 18 // - ClientSocketFactory::CreateDatagramClientSocket | |
| 19 // - ClientSocketFactory::CreateSSLClientSocket | |
| 20 // - ClientSocketFactory::CreateTransportClientSocket | |
| 21 // - URLRequestContext::CreateRequest | |
| 22 // Please refer to README.md for build and usage instructions. | |
| 13 | 23 |
| 14 #include <memory> | 24 #include <memory> |
| 15 #include <vector> | 25 #include <vector> |
| 16 | 26 |
| 17 #include "clang/ASTMatchers/ASTMatchFinder.h" | 27 #include "clang/ASTMatchers/ASTMatchFinder.h" |
| 18 #include "clang/ASTMatchers/ASTMatchers.h" | 28 #include "clang/ASTMatchers/ASTMatchers.h" |
| 19 #include "clang/Basic/SourceManager.h" | 29 #include "clang/Basic/SourceManager.h" |
| 20 #include "clang/Frontend/FrontendActions.h" | 30 #include "clang/Frontend/FrontendActions.h" |
| 21 #include "clang/Lex/Lexer.h" | 31 #include "clang/Lex/Lexer.h" |
| 22 #include "clang/Tooling/CommonOptionsParser.h" | 32 #include "clang/Tooling/CommonOptionsParser.h" |
| 23 #include "clang/Tooling/Refactoring.h" | 33 #include "clang/Tooling/Refactoring.h" |
| 24 #include "clang/Tooling/Tooling.h" | 34 #include "clang/Tooling/Tooling.h" |
| 25 #include "llvm/Support/CommandLine.h" | 35 #include "llvm/Support/CommandLine.h" |
| 26 | 36 |
| 27 using namespace clang::ast_matchers; | 37 using namespace clang::ast_matchers; |
| 28 | 38 |
| 29 namespace { | 39 namespace { |
| 30 | 40 |
| 41 // Information about location of a line of code. | |
| 42 struct Location { | |
| 43 std::string file_path; | |
| 44 int line_number = -1; | |
| 45 | |
| 46 // Name of the function including this line. E.g., in the following code, | |
| 47 // |function_name| will be 'foo' for all |line_number| values 101-103. | |
| 48 // | |
| 49 // 100 void foo() { | |
| 50 // 101 NetworkTrafficAnnotationTag baz = | |
| 51 // 102 net::DefineNetworkTrafficAnnotation(...); } | |
| 52 // 103 bar(baz); | |
| 53 // 104 } | |
| 54 // If no function is found, 'Global Namespace' will be returned. | |
| 55 std::string function_name; | |
| 56 }; | |
| 57 | |
| 31 // An instance of a call to either of the 4 network traffic annotation | 58 // An instance of a call to either of the 4 network traffic annotation |
| 32 // definition functions. | 59 // definition functions. |
| 33 struct NetworkAnnotationInstance { | 60 struct NetworkAnnotationInstance { |
| 34 // Information about where the call has happened. | |
| 35 struct Location { | |
| 36 std::string file_path; | |
| 37 int line_number = -1; | |
| 38 | |
| 39 // Name of the function including this line. E.g., in the following code, | |
| 40 // |function_name| will be 'foo' for all |line_number| values 101-103. | |
| 41 // | |
| 42 // 100 void foo() { | |
| 43 // 101 NetworkTrafficAnnotationTag baz = | |
| 44 // 102 net::DefineNetworkTrafficAnnotation(...); } | |
| 45 // 103 bar(baz); | |
| 46 // 104 } | |
| 47 // If no function is found, 'Global Namespace' will be returned. | |
| 48 std::string function_name; | |
| 49 }; | |
| 50 | |
| 51 // Annotation content. These are the arguments of the call to either of the 4 | 61 // Annotation content. These are the arguments of the call to either of the 4 |
| 52 // network traffic annotation definition functions. | 62 // network traffic annotation definition functions. |
| 53 struct Annotation { | 63 struct Annotation { |
| 54 std::string unique_id; | 64 std::string unique_id; |
| 55 std::string text; | 65 std::string text; |
| 56 | 66 |
| 57 // |extra_id| will have |completing_id| for | 67 // |extra_id| will have |completing_id| for |
| 58 // net::DefinePartialNetworkTrafficAnnotation and |group_id| for | 68 // net::DefinePartialNetworkTrafficAnnotation and |group_id| for |
| 59 // net::BranchedCompleteNetworkTrafficAnnotation. It will be empty in other | 69 // net::BranchedCompleteNetworkTrafficAnnotation. It will be empty in other |
| 60 // cases. | 70 // cases. |
| (...skipping 22 matching lines...) Expand all Loading... | |
| 83 case kCompleting: | 93 case kCompleting: |
| 84 return "Completing"; | 94 return "Completing"; |
| 85 case kBranchedCompleting: | 95 case kBranchedCompleting: |
| 86 return "BranchedCompleting"; | 96 return "BranchedCompleting"; |
| 87 } | 97 } |
| 88 assert(false); | 98 assert(false); |
| 89 return ""; | 99 return ""; |
| 90 } | 100 } |
| 91 }; | 101 }; |
| 92 | 102 |
| 93 using Collector = std::vector<NetworkAnnotationInstance>; | 103 // An instance of a call to one of the monitored function. |
| 104 struct CallInstance { | |
| 105 // Location of the call. | |
| 106 Location location; | |
| 107 | |
| 108 // Whether the function is annotated. | |
| 109 bool has_annotation = false; | |
| 110 | |
| 111 // Name of the called function. | |
| 112 std::string called_function_name; | |
| 113 }; | |
| 114 | |
| 115 // A structure to keep detected annotation and call instances. | |
| 116 struct Collector { | |
| 117 std::vector<NetworkAnnotationInstance> annotations; | |
| 118 std::vector<CallInstance> calls; | |
| 119 }; | |
| 94 | 120 |
| 95 // This class implements the call back functions for AST Matchers. The matchers | 121 // This class implements the call back functions for AST Matchers. The matchers |
| 96 // are defined in RunMatchers function. When a pattern is found there, | 122 // are defined in RunMatchers function. When a pattern is found there, |
| 97 // the run function in this class is called back with information on the matched | 123 // the run function in this class is called back with information on the matched |
| 98 // location and description of the matched pattern. | 124 // location and description of the matched pattern. |
| 99 class NetworkAnnotationTagCallback : public MatchFinder::MatchCallback { | 125 class NetworkAnnotationTagCallback : public MatchFinder::MatchCallback { |
| 100 public: | 126 public: |
| 101 explicit NetworkAnnotationTagCallback(Collector* collector) | 127 explicit NetworkAnnotationTagCallback(Collector* collector) |
| 102 : collector_(collector) {} | 128 : collector_(collector) {} |
| 103 ~NetworkAnnotationTagCallback() override = default; | 129 ~NetworkAnnotationTagCallback() override = default; |
| 104 | 130 |
| 105 // Is called on any pattern found by ASTMathers that are defined in RunMathers | 131 // Is called on any pattern found by ASTMathers that are defined in RunMathers |
| 106 // function. | 132 // function. |
| 107 virtual void run(const MatchFinder::MatchResult& result) override { | 133 virtual void run(const MatchFinder::MatchResult& result) override { |
| 134 if (const clang::CallExpr* call_expr = | |
| 135 result.Nodes.getNodeAs<clang::CallExpr>("monitored_function")) { | |
| 136 AddFunction(call_expr, result); | |
| 137 } else { | |
| 138 AddAnnotation(result); | |
| 139 } | |
| 140 } | |
| 141 | |
| 142 void GetInstanceLocation(const MatchFinder::MatchResult& result, | |
| 143 const clang::CallExpr* call_expr, | |
| 144 const clang::FunctionDecl* ancestor, | |
| 145 Location* instance_location) { | |
| 146 clang::SourceLocation source_location = call_expr->getLocStart(); | |
| 147 if (source_location.isMacroID()) { | |
| 148 source_location = | |
| 149 result.SourceManager->getImmediateMacroCallerLoc(source_location); | |
| 150 } | |
| 151 instance_location->file_path = | |
| 152 result.SourceManager->getFilename(source_location); | |
| 153 instance_location->line_number = | |
| 154 result.SourceManager->getSpellingLineNumber(source_location); | |
| 155 if (ancestor) | |
| 156 instance_location->function_name = ancestor->getQualifiedNameAsString(); | |
| 157 else | |
| 158 instance_location->function_name = "Global Namespace"; | |
| 159 | |
| 160 std::replace(instance_location->file_path.begin(), | |
| 161 instance_location->file_path.end(), '\\', '/'); | |
| 162 | |
| 163 // Trim leading "../"s from file path. | |
| 164 while (instance_location->file_path.length() > 3 && | |
| 165 instance_location->file_path.substr(0, 3) == "../") { | |
| 166 instance_location->file_path = instance_location->file_path.substr( | |
| 167 3, instance_location->file_path.length() - 3); | |
| 168 } | |
| 169 } | |
| 170 | |
| 171 // Stores a function call that should be monitored. | |
| 172 void AddFunction(const clang::CallExpr* call_expr, | |
| 173 const MatchFinder::MatchResult& result) { | |
| 174 CallInstance instance; | |
| 175 | |
| 176 const clang::FunctionDecl* ancestor = | |
| 177 result.Nodes.getNodeAs<clang::FunctionDecl>("function_context"); | |
| 178 GetInstanceLocation(result, call_expr, ancestor, &instance.location); | |
| 179 instance.called_function_name = | |
| 180 call_expr->getDirectCallee()->getQualifiedNameAsString(); | |
| 181 instance.has_annotation = | |
| 182 (result.Nodes.getNodeAs<clang::RecordDecl>("annotation") != NULL); | |
|
dcheng
2017/05/29 08:28:00
Nit: nullptr instead of NULL
Ramin Halavati
2017/05/29 09:14:02
Done.
| |
| 183 collector_->calls.push_back(instance); | |
| 184 } | |
| 185 | |
| 186 // Stores an annotation. | |
| 187 void AddAnnotation(const MatchFinder::MatchResult& result) { | |
| 108 NetworkAnnotationInstance instance; | 188 NetworkAnnotationInstance instance; |
| 109 | 189 |
| 110 const clang::StringLiteral* unique_id = | 190 const clang::StringLiteral* unique_id = |
| 111 result.Nodes.getNodeAs<clang::StringLiteral>("unique_id"); | 191 result.Nodes.getNodeAs<clang::StringLiteral>("unique_id"); |
| 112 const clang::StringLiteral* annotation_text = | 192 const clang::StringLiteral* annotation_text = |
| 113 result.Nodes.getNodeAs<clang::StringLiteral>("annotation_text"); | 193 result.Nodes.getNodeAs<clang::StringLiteral>("annotation_text"); |
| 114 const clang::FunctionDecl* ancestor = | 194 const clang::FunctionDecl* ancestor = |
| 115 result.Nodes.getNodeAs<clang::FunctionDecl>("function_context"); | 195 result.Nodes.getNodeAs<clang::FunctionDecl>("function_context"); |
| 116 const clang::StringLiteral* group_id = | 196 const clang::StringLiteral* group_id = |
| 117 result.Nodes.getNodeAs<clang::StringLiteral>("group_id"); | 197 result.Nodes.getNodeAs<clang::StringLiteral>("group_id"); |
| (...skipping 18 matching lines...) Expand all Loading... | |
| 136 assert(group_id); | 216 assert(group_id); |
| 137 instance.annotation.extra_id = group_id->getString(); | 217 instance.annotation.extra_id = group_id->getString(); |
| 138 } else { | 218 } else { |
| 139 assert(false); | 219 assert(false); |
| 140 } | 220 } |
| 141 | 221 |
| 142 assert(unique_id && annotation_text); | 222 assert(unique_id && annotation_text); |
| 143 instance.annotation.unique_id = unique_id->getString(); | 223 instance.annotation.unique_id = unique_id->getString(); |
| 144 instance.annotation.text = annotation_text->getString(); | 224 instance.annotation.text = annotation_text->getString(); |
| 145 | 225 |
| 146 // Get annotation location. | 226 GetInstanceLocation(result, call_expr, ancestor, &instance.location); |
| 147 clang::SourceLocation source_location = call_expr->getLocStart(); | |
| 148 if (source_location.isMacroID()) { | |
| 149 source_location = | |
| 150 result.SourceManager->getImmediateMacroCallerLoc(source_location); | |
| 151 } | |
| 152 instance.location.file_path = | |
| 153 result.SourceManager->getFilename(source_location); | |
| 154 instance.location.line_number = | |
| 155 result.SourceManager->getSpellingLineNumber(source_location); | |
| 156 if (ancestor) | |
| 157 instance.location.function_name = ancestor->getQualifiedNameAsString(); | |
| 158 else | |
| 159 instance.location.function_name = "Global Namespace"; | |
| 160 | 227 |
| 161 // Trim leading "../"s from file path. | 228 collector_->annotations.push_back(instance); |
| 162 std::replace(instance.location.file_path.begin(), | |
| 163 instance.location.file_path.end(), '\\', '/'); | |
| 164 while (instance.location.file_path.length() > 3 && | |
| 165 instance.location.file_path.substr(0, 3) == "../") { | |
| 166 instance.location.file_path = instance.location.file_path.substr( | |
| 167 3, instance.location.file_path.length() - 3); | |
| 168 } | |
| 169 | |
| 170 collector_->push_back(instance); | |
| 171 } | 229 } |
| 172 | 230 |
| 173 private: | 231 private: |
| 174 Collector* collector_; | 232 Collector* collector_; |
| 175 }; | 233 }; |
| 176 | 234 |
| 177 // Sets up an ASTMatcher and runs clang tool to populate collector. Returns the | 235 // Sets up an ASTMatcher and runs clang tool to populate collector. Returns the |
| 178 // result of running the clang tool. | 236 // result of running the clang tool. |
| 179 int RunMatchers(clang::tooling::ClangTool* clang_tool, Collector* collector) { | 237 int RunMatchers(clang::tooling::ClangTool* clang_tool, Collector* collector) { |
| 180 NetworkAnnotationTagCallback callback(collector); | 238 NetworkAnnotationTagCallback callback(collector); |
| (...skipping 36 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 217 callExpr(hasDeclaration(functionDecl(anyOf( | 275 callExpr(hasDeclaration(functionDecl(anyOf( |
| 218 hasName("BranchedCompleteNetworkTrafficAnnotation"), | 276 hasName("BranchedCompleteNetworkTrafficAnnotation"), |
| 219 hasName("net::BranchedCompleteNetworkTrafficAnnotation")))), | 277 hasName("net::BranchedCompleteNetworkTrafficAnnotation")))), |
| 220 hasArgument(0, stringLiteral().bind("unique_id")), | 278 hasArgument(0, stringLiteral().bind("unique_id")), |
| 221 hasArgument(1, stringLiteral().bind("group_id")), | 279 hasArgument(1, stringLiteral().bind("group_id")), |
| 222 hasArgument(3, stringLiteral().bind("annotation_text")), | 280 hasArgument(3, stringLiteral().bind("annotation_text")), |
| 223 anyOf(hasAncestor(functionDecl().bind("function_context")), | 281 anyOf(hasAncestor(functionDecl().bind("function_context")), |
| 224 unless(hasAncestor(functionDecl())))) | 282 unless(hasAncestor(functionDecl())))) |
| 225 .bind("branched_completing_function"), | 283 .bind("branched_completing_function"), |
| 226 &callback); | 284 &callback); |
| 285 | |
| 286 // Setup patterns to find functions that should be monitored. | |
| 287 match_finder.addMatcher( | |
| 288 callExpr( | |
| 289 hasDeclaration(functionDecl( | |
| 290 anyOf(hasName("SSLClientSocket::SSLClientSocket"), | |
| 291 hasName("TCPClientSocket::TCPClientSocket"), | |
| 292 hasName("UDPClientSocket::UDPClientSocket"), | |
| 293 hasName("URLFetcher::Create"), | |
| 294 hasName("ClientSocketFactory::CreateDatagramClientSocket"), | |
| 295 hasName("ClientSocketFactory::CreateSSLClientSocket"), | |
| 296 hasName("ClientSocketFactory::CreateTransportClientSocket"), | |
| 297 hasName("URLRequestContext::CreateRequest")), | |
| 298 anyOf( | |
| 299 hasAnyParameter(hasType( | |
| 300 recordDecl( | |
| 301 anyOf(hasName("net::NetworkTrafficAnnotationTag"), | |
| 302 hasName( | |
| 303 "net::PartialNetworkTrafficAnnotationTag"))) | |
| 304 .bind("annotation"))), | |
| 305 unless(hasAnyParameter(hasType(recordDecl(anyOf( | |
| 306 hasName("net::NetworkTrafficAnnotationTag"), | |
| 307 hasName( | |
| 308 "net::PartialNetworkTrafficAnnotationTag"))))))))), | |
| 309 anyOf(hasAncestor(functionDecl().bind("function_context")), | |
| 310 unless(hasAncestor(functionDecl())))) | |
|
dcheng
2017/05/29 08:28:00
One thing to consider is to move some of these exp
Ramin Halavati
2017/05/29 09:14:02
Thank you very much, Done.
| |
| 311 .bind("monitored_function"), | |
| 312 &callback); | |
| 313 | |
| 227 std::unique_ptr<clang::tooling::FrontendActionFactory> frontend_factory = | 314 std::unique_ptr<clang::tooling::FrontendActionFactory> frontend_factory = |
| 228 clang::tooling::newFrontendActionFactory(&match_finder); | 315 clang::tooling::newFrontendActionFactory(&match_finder); |
| 229 return clang_tool->run(frontend_factory.get()); | 316 return clang_tool->run(frontend_factory.get()); |
| 230 } | 317 } |
| 231 | 318 |
| 232 } // namespace | 319 } // namespace |
| 233 | 320 |
| 234 static llvm::cl::OptionCategory ToolCategory( | 321 static llvm::cl::OptionCategory ToolCategory( |
| 235 "traffic_annotation_extractor: Extract traffic annotation texts"); | 322 "traffic_annotation_extractor: Extract traffic annotation texts"); |
| 236 static llvm::cl::extrahelp CommonHelp( | 323 static llvm::cl::extrahelp CommonHelp( |
| 237 clang::tooling::CommonOptionsParser::HelpMessage); | 324 clang::tooling::CommonOptionsParser::HelpMessage); |
| 238 | 325 |
| 239 int main(int argc, const char* argv[]) { | 326 int main(int argc, const char* argv[]) { |
| 240 clang::tooling::CommonOptionsParser options(argc, argv, ToolCategory); | 327 clang::tooling::CommonOptionsParser options(argc, argv, ToolCategory); |
| 241 clang::tooling::ClangTool tool(options.getCompilations(), | 328 clang::tooling::ClangTool tool(options.getCompilations(), |
| 242 options.getSourcePathList()); | 329 options.getSourcePathList()); |
| 243 Collector collector; | 330 Collector collector; |
| 244 | 331 |
| 245 int result = RunMatchers(&tool, &collector); | 332 int result = RunMatchers(&tool, &collector); |
| 246 | 333 |
| 247 if (result != 0) | 334 if (result != 0) |
| 248 return result; | 335 return result; |
| 249 | 336 |
| 250 // For each call to any of the functions that define a network traffic | 337 // For each call to any of the functions that define a network traffic |
| 251 // annotation, write annotation text and relevant meta data into llvm::outs(). | 338 // annotation, write annotation text and relevant meta data into llvm::outs(). |
| 252 for (const NetworkAnnotationInstance& call : collector) { | 339 for (const NetworkAnnotationInstance& instance : collector.annotations) { |
| 253 llvm::outs() << "==== NEW ANNOTATION ====\n"; | 340 llvm::outs() << "==== NEW ANNOTATION ====\n"; |
| 254 llvm::outs() << call.location.file_path << "\n"; | 341 llvm::outs() << instance.location.file_path << "\n"; |
| 255 llvm::outs() << call.location.function_name << "\n"; | 342 llvm::outs() << instance.location.function_name << "\n"; |
| 256 llvm::outs() << call.location.line_number << "\n"; | 343 llvm::outs() << instance.location.line_number << "\n"; |
| 257 llvm::outs() << call.GetTypeName() << "\n"; | 344 llvm::outs() << instance.GetTypeName() << "\n"; |
| 258 llvm::outs() << call.annotation.unique_id << "\n"; | 345 llvm::outs() << instance.annotation.unique_id << "\n"; |
| 259 llvm::outs() << call.annotation.extra_id << "\n"; | 346 llvm::outs() << instance.annotation.extra_id << "\n"; |
| 260 llvm::outs() << call.annotation.text << "\n"; | 347 llvm::outs() << instance.annotation.text << "\n"; |
| 261 llvm::outs() << "==== ANNOTATION ENDS ====\n"; | 348 llvm::outs() << "==== ANNOTATION ENDS ====\n"; |
| 262 } | 349 } |
| 263 | 350 |
| 351 // For each call, write annotation text and relevant meta data. | |
| 352 for (const CallInstance& instance : collector.calls) { | |
| 353 llvm::outs() << "==== NEW CALL ====\n"; | |
| 354 llvm::outs() << instance.location.file_path << "\n"; | |
| 355 llvm::outs() << instance.location.function_name << "\n"; | |
| 356 llvm::outs() << instance.location.line_number << "\n"; | |
| 357 llvm::outs() << instance.called_function_name << "\n"; | |
| 358 llvm::outs() << instance.has_annotation << "\n"; | |
| 359 llvm::outs() << "==== CALL ENDS ====\n"; | |
| 360 } | |
| 361 | |
| 264 return 0; | 362 return 0; |
| 265 } | 363 } |
| OLD | NEW |