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 CallInstance() : has_annotation(false) {} | |
|
dcheng
2017/05/27 08:27:46
Or just use an in-class initializer to be consiste
Ramin Halavati
2017/05/29 08:13:28
Done.
| |
| 106 | |
| 107 // Location of the call. | |
| 108 Location location; | |
| 109 | |
| 110 // Whether the function is annotated. | |
| 111 bool has_annotation; | |
| 112 | |
| 113 // Name of the called function. | |
| 114 std::string called_function_name; | |
| 115 }; | |
| 116 | |
| 117 // A structure to keep detected annotation and call instances. | |
| 118 struct Collector { | |
| 119 std::vector<NetworkAnnotationInstance> annotations; | |
| 120 std::vector<CallInstance> calls; | |
| 121 }; | |
| 94 | 122 |
| 95 // This class implements the call back functions for AST Matchers. The matchers | 123 // This class implements the call back functions for AST Matchers. The matchers |
| 96 // are defined in RunMatchers function. When a pattern is found there, | 124 // 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 | 125 // the run function in this class is called back with information on the matched |
| 98 // location and description of the matched pattern. | 126 // location and description of the matched pattern. |
| 99 class NetworkAnnotationTagCallback : public MatchFinder::MatchCallback { | 127 class NetworkAnnotationTagCallback : public MatchFinder::MatchCallback { |
| 100 public: | 128 public: |
| 101 explicit NetworkAnnotationTagCallback(Collector* collector) | 129 explicit NetworkAnnotationTagCallback(Collector* collector) |
| 102 : collector_(collector) {} | 130 : collector_(collector) {} |
| 103 ~NetworkAnnotationTagCallback() override = default; | 131 ~NetworkAnnotationTagCallback() override = default; |
| 104 | 132 |
| 105 // Is called on any pattern found by ASTMathers that are defined in RunMathers | 133 // Is called on any pattern found by ASTMathers that are defined in RunMathers |
| 106 // function. | 134 // function. |
| 107 virtual void run(const MatchFinder::MatchResult& result) override { | 135 virtual void run(const MatchFinder::MatchResult& result) override { |
| 136 if (const clang::CallExpr* call_expr = | |
| 137 result.Nodes.getNodeAs<clang::CallExpr>("monitored_function")) { | |
| 138 AddFunction(call_expr, result); | |
| 139 } else { | |
| 140 AddAnnotation(result); | |
| 141 } | |
| 142 } | |
| 143 | |
| 144 void GetInstanceLocation(const MatchFinder::MatchResult& result, | |
| 145 const clang::CallExpr* call_expr, | |
| 146 const clang::FunctionDecl* ancestor, | |
| 147 Location* instance_location) { | |
| 148 clang::SourceLocation source_location = call_expr->getLocStart(); | |
| 149 if (source_location.isMacroID()) { | |
| 150 source_location = | |
| 151 result.SourceManager->getImmediateMacroCallerLoc(source_location); | |
| 152 } | |
| 153 instance_location->file_path = | |
| 154 result.SourceManager->getFilename(source_location); | |
| 155 instance_location->line_number = | |
| 156 result.SourceManager->getSpellingLineNumber(source_location); | |
| 157 if (ancestor) | |
| 158 instance_location->function_name = ancestor->getQualifiedNameAsString(); | |
| 159 else | |
| 160 instance_location->function_name = "Global Namespace"; | |
| 161 | |
| 162 // Trim leading "../"s from file path. | |
|
dcheng
2017/05/27 08:27:46
Nit: this comment probably before line 165, or it
Ramin Halavati
2017/05/29 08:13:28
Done.
| |
| 163 std::replace(instance_location->file_path.begin(), | |
| 164 instance_location->file_path.end(), '\\', '/'); | |
| 165 while (instance_location->file_path.length() > 3 && | |
| 166 instance_location->file_path.substr(0, 3) == "../") { | |
| 167 instance_location->file_path = instance_location->file_path.substr( | |
| 168 3, instance_location->file_path.length() - 3); | |
| 169 } | |
| 170 } | |
| 171 | |
| 172 // Stores a function call that should be monitored. | |
| 173 void AddFunction(const clang::CallExpr* call_expr, | |
| 174 const MatchFinder::MatchResult& result) { | |
| 175 CallInstance instance; | |
| 176 | |
| 177 const clang::FunctionDecl* ancestor = | |
| 178 result.Nodes.getNodeAs<clang::FunctionDecl>("function_context"); | |
| 179 | |
| 180 GetInstanceLocation(result, call_expr, ancestor, &instance.location); | |
| 181 | |
| 182 instance.called_function_name = | |
| 183 call_expr->getDirectCallee()->getQualifiedNameAsString(); | |
| 184 | |
| 185 // Check if it is annotated. | |
| 186 // TODO: Daniel, any sugestion on how to change this into an ASTMatcher? I | |
| 187 // couldn't do it. | |
|
dcheng
2017/05/27 08:27:46
hasAnyParameter(hasType(recordDecl(anyOf(hasName("
Ramin Halavati
2017/05/29 08:13:28
Thank you very much.
| |
| 188 const clang::FunctionDecl* function_decl = call_expr->getDirectCallee(); | |
| 189 unsigned params_count = function_decl->getNumParams(); | |
| 190 | |
| 191 for (unsigned i = 0; i < params_count; i++) { | |
| 192 std::string arg_type = clang::QualType::getAsString( | |
| 193 function_decl->getParamDecl(i)->getType().split()); | |
| 194 if (arg_type == "struct net::NetworkTrafficAnnotationTag" || | |
| 195 arg_type == "struct net::PartialNetworkTrafficAnnotationTag") { | |
| 196 instance.has_annotation = true; | |
| 197 break; | |
| 198 } | |
| 199 } | |
| 200 collector_->calls.push_back(instance); | |
| 201 } | |
| 202 | |
| 203 // Stores an annotation. | |
| 204 void AddAnnotation(const MatchFinder::MatchResult& result) { | |
| 108 NetworkAnnotationInstance instance; | 205 NetworkAnnotationInstance instance; |
| 109 | 206 |
| 110 const clang::StringLiteral* unique_id = | 207 const clang::StringLiteral* unique_id = |
| 111 result.Nodes.getNodeAs<clang::StringLiteral>("unique_id"); | 208 result.Nodes.getNodeAs<clang::StringLiteral>("unique_id"); |
| 112 const clang::StringLiteral* annotation_text = | 209 const clang::StringLiteral* annotation_text = |
| 113 result.Nodes.getNodeAs<clang::StringLiteral>("annotation_text"); | 210 result.Nodes.getNodeAs<clang::StringLiteral>("annotation_text"); |
| 114 const clang::FunctionDecl* ancestor = | 211 const clang::FunctionDecl* ancestor = |
| 115 result.Nodes.getNodeAs<clang::FunctionDecl>("function_context"); | 212 result.Nodes.getNodeAs<clang::FunctionDecl>("function_context"); |
| 116 const clang::StringLiteral* group_id = | 213 const clang::StringLiteral* group_id = |
| 117 result.Nodes.getNodeAs<clang::StringLiteral>("group_id"); | 214 result.Nodes.getNodeAs<clang::StringLiteral>("group_id"); |
| (...skipping 18 matching lines...) Expand all Loading... | |
| 136 assert(group_id); | 233 assert(group_id); |
| 137 instance.annotation.extra_id = group_id->getString(); | 234 instance.annotation.extra_id = group_id->getString(); |
| 138 } else { | 235 } else { |
| 139 assert(false); | 236 assert(false); |
| 140 } | 237 } |
| 141 | 238 |
| 142 assert(unique_id && annotation_text); | 239 assert(unique_id && annotation_text); |
| 143 instance.annotation.unique_id = unique_id->getString(); | 240 instance.annotation.unique_id = unique_id->getString(); |
| 144 instance.annotation.text = annotation_text->getString(); | 241 instance.annotation.text = annotation_text->getString(); |
| 145 | 242 |
| 146 // Get annotation location. | 243 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 | 244 |
| 161 // Trim leading "../"s from file path. | 245 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 } | 246 } |
| 172 | 247 |
| 173 private: | 248 private: |
| 174 Collector* collector_; | 249 Collector* collector_; |
| 175 }; | 250 }; |
| 176 | 251 |
| 177 // Sets up an ASTMatcher and runs clang tool to populate collector. Returns the | 252 // Sets up an ASTMatcher and runs clang tool to populate collector. Returns the |
| 178 // result of running the clang tool. | 253 // result of running the clang tool. |
| 179 int RunMatchers(clang::tooling::ClangTool* clang_tool, Collector* collector) { | 254 int RunMatchers(clang::tooling::ClangTool* clang_tool, Collector* collector) { |
| 180 NetworkAnnotationTagCallback callback(collector); | 255 NetworkAnnotationTagCallback callback(collector); |
| (...skipping 36 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 217 callExpr(hasDeclaration(functionDecl(anyOf( | 292 callExpr(hasDeclaration(functionDecl(anyOf( |
| 218 hasName("BranchedCompleteNetworkTrafficAnnotation"), | 293 hasName("BranchedCompleteNetworkTrafficAnnotation"), |
| 219 hasName("net::BranchedCompleteNetworkTrafficAnnotation")))), | 294 hasName("net::BranchedCompleteNetworkTrafficAnnotation")))), |
| 220 hasArgument(0, stringLiteral().bind("unique_id")), | 295 hasArgument(0, stringLiteral().bind("unique_id")), |
| 221 hasArgument(1, stringLiteral().bind("group_id")), | 296 hasArgument(1, stringLiteral().bind("group_id")), |
| 222 hasArgument(3, stringLiteral().bind("annotation_text")), | 297 hasArgument(3, stringLiteral().bind("annotation_text")), |
| 223 anyOf(hasAncestor(functionDecl().bind("function_context")), | 298 anyOf(hasAncestor(functionDecl().bind("function_context")), |
| 224 unless(hasAncestor(functionDecl())))) | 299 unless(hasAncestor(functionDecl())))) |
| 225 .bind("branched_completing_function"), | 300 .bind("branched_completing_function"), |
| 226 &callback); | 301 &callback); |
| 302 | |
| 303 // Setup patterns to find functions that should be monitored. | |
| 304 match_finder.addMatcher( | |
| 305 callExpr(hasDeclaration(functionDecl(anyOf( | |
| 306 hasName("SSLClientSocket::SSLClientSocket"), | |
| 307 hasName("TCPClientSocket::TCPClientSocket"), | |
| 308 hasName("UDPClientSocket::UDPClientSocket"), | |
| 309 hasName("URLFetcher::Create"), | |
| 310 hasName("ClientSocketFactory::CreateDatagramClientSocket"), | |
| 311 hasName("ClientSocketFactory::CreateSSLClientSocket"), | |
| 312 hasName("ClientSocketFactory::CreateTransportClientSocket"), | |
| 313 hasName("URLRequestContext::CreateRequest")))), | |
| 314 anyOf(hasAncestor(functionDecl().bind("function_context")), | |
| 315 unless(hasAncestor(functionDecl())))) | |
| 316 .bind("monitored_function"), | |
| 317 &callback); | |
| 318 | |
| 227 std::unique_ptr<clang::tooling::FrontendActionFactory> frontend_factory = | 319 std::unique_ptr<clang::tooling::FrontendActionFactory> frontend_factory = |
| 228 clang::tooling::newFrontendActionFactory(&match_finder); | 320 clang::tooling::newFrontendActionFactory(&match_finder); |
| 229 return clang_tool->run(frontend_factory.get()); | 321 return clang_tool->run(frontend_factory.get()); |
| 230 } | 322 } |
| 231 | 323 |
| 232 } // namespace | 324 } // namespace |
| 233 | 325 |
| 234 static llvm::cl::OptionCategory ToolCategory( | 326 static llvm::cl::OptionCategory ToolCategory( |
| 235 "traffic_annotation_extractor: Extract traffic annotation texts"); | 327 "traffic_annotation_extractor: Extract traffic annotation texts"); |
| 236 static llvm::cl::extrahelp CommonHelp( | 328 static llvm::cl::extrahelp CommonHelp( |
| 237 clang::tooling::CommonOptionsParser::HelpMessage); | 329 clang::tooling::CommonOptionsParser::HelpMessage); |
| 238 | 330 |
| 239 int main(int argc, const char* argv[]) { | 331 int main(int argc, const char* argv[]) { |
| 240 clang::tooling::CommonOptionsParser options(argc, argv, ToolCategory); | 332 clang::tooling::CommonOptionsParser options(argc, argv, ToolCategory); |
| 241 clang::tooling::ClangTool tool(options.getCompilations(), | 333 clang::tooling::ClangTool tool(options.getCompilations(), |
| 242 options.getSourcePathList()); | 334 options.getSourcePathList()); |
| 243 Collector collector; | 335 Collector collector; |
| 244 | 336 |
| 245 int result = RunMatchers(&tool, &collector); | 337 int result = RunMatchers(&tool, &collector); |
| 246 | 338 |
| 247 if (result != 0) | 339 if (result != 0) |
| 248 return result; | 340 return result; |
| 249 | 341 |
| 250 // For each call to any of the functions that define a network traffic | 342 // 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(). | 343 // annotation, write annotation text and relevant meta data into llvm::outs(). |
| 252 for (const NetworkAnnotationInstance& call : collector) { | 344 for (const NetworkAnnotationInstance& instance : collector.annotations) { |
| 253 llvm::outs() << "==== NEW ANNOTATION ====\n"; | 345 llvm::outs() << "==== NEW ANNOTATION ====\n"; |
| 254 llvm::outs() << call.location.file_path << "\n"; | 346 llvm::outs() << instance.location.file_path << "\n"; |
| 255 llvm::outs() << call.location.function_name << "\n"; | 347 llvm::outs() << instance.location.function_name << "\n"; |
| 256 llvm::outs() << call.location.line_number << "\n"; | 348 llvm::outs() << instance.location.line_number << "\n"; |
| 257 llvm::outs() << call.GetTypeName() << "\n"; | 349 llvm::outs() << instance.GetTypeName() << "\n"; |
| 258 llvm::outs() << call.annotation.unique_id << "\n"; | 350 llvm::outs() << instance.annotation.unique_id << "\n"; |
| 259 llvm::outs() << call.annotation.extra_id << "\n"; | 351 llvm::outs() << instance.annotation.extra_id << "\n"; |
| 260 llvm::outs() << call.annotation.text << "\n"; | 352 llvm::outs() << instance.annotation.text << "\n"; |
| 261 llvm::outs() << "==== ANNOTATION ENDS ====\n"; | 353 llvm::outs() << "==== ANNOTATION ENDS ====\n"; |
| 262 } | 354 } |
| 263 | 355 |
| 356 // For each call, write annotation text and relevant meta data. | |
| 357 for (const CallInstance& instance : collector.calls) { | |
| 358 llvm::outs() << "==== NEW CALL ====\n"; | |
| 359 llvm::outs() << instance.location.file_path << "\n"; | |
| 360 llvm::outs() << instance.location.function_name << "\n"; | |
| 361 llvm::outs() << instance.location.line_number << "\n"; | |
| 362 llvm::outs() << instance.called_function_name << "\n"; | |
| 363 llvm::outs() << instance.has_annotation << "\n"; | |
| 364 llvm::outs() << "==== CALL ENDS ====\n"; | |
| 365 } | |
| 366 | |
| 264 return 0; | 367 return 0; |
| 265 } | 368 } |
| OLD | NEW |