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 |