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 |
msramek
2017/05/29 21:03:36
typo: calls of
Ramin Halavati
2017/05/30 04:40:36
Done.
| |
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") != nullptr); | |
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); |
181 MatchFinder match_finder; | 239 MatchFinder match_finder; |
182 | 240 |
183 // Set up patterns to find network traffic annotation definition functions, | 241 // Set up patterns to find network traffic annotation definition functions, |
184 // their arguments, and their ancestor function (when possible). | 242 // their arguments, and their ancestor function (when possible). |
243 auto bind_function_context_if_present = | |
244 anyOf(hasAncestor(functionDecl().bind("function_context")), | |
245 unless(hasAncestor(functionDecl()))); | |
246 auto has_annotation_parameter = anyOf( | |
247 hasAnyParameter(hasType( | |
248 recordDecl(anyOf(hasName("net::NetworkTrafficAnnotationTag"), | |
249 hasName("net::PartialNetworkTrafficAnnotationTag"))) | |
250 .bind("annotation"))), | |
251 unless(hasAnyParameter(hasType(recordDecl( | |
252 anyOf(hasName("net::NetworkTrafficAnnotationTag"), | |
253 hasName("net::PartialNetworkTrafficAnnotationTag"))))))); | |
185 match_finder.addMatcher( | 254 match_finder.addMatcher( |
186 callExpr(hasDeclaration(functionDecl( | 255 callExpr(hasDeclaration(functionDecl( |
187 anyOf(hasName("DefineNetworkTrafficAnnotation"), | 256 anyOf(hasName("DefineNetworkTrafficAnnotation"), |
188 hasName("net::DefineNetworkTrafficAnnotation")))), | 257 hasName("net::DefineNetworkTrafficAnnotation")))), |
189 hasArgument(0, stringLiteral().bind("unique_id")), | 258 hasArgument(0, stringLiteral().bind("unique_id")), |
190 hasArgument(1, stringLiteral().bind("annotation_text")), | 259 hasArgument(1, stringLiteral().bind("annotation_text")), |
191 anyOf(hasAncestor(functionDecl().bind("function_context")), | 260 bind_function_context_if_present) |
192 unless(hasAncestor(functionDecl())))) | |
193 .bind("definition_function"), | 261 .bind("definition_function"), |
194 &callback); | 262 &callback); |
195 match_finder.addMatcher( | 263 match_finder.addMatcher( |
196 callExpr(hasDeclaration(functionDecl(anyOf( | 264 callExpr(hasDeclaration(functionDecl(anyOf( |
197 hasName("DefinePartialNetworkTrafficAnnotation"), | 265 hasName("DefinePartialNetworkTrafficAnnotation"), |
198 hasName("net::DefinePartialNetworkTrafficAnnotation")))), | 266 hasName("net::DefinePartialNetworkTrafficAnnotation")))), |
199 hasArgument(0, stringLiteral().bind("unique_id")), | 267 hasArgument(0, stringLiteral().bind("unique_id")), |
200 hasArgument(1, stringLiteral().bind("completing_id")), | 268 hasArgument(1, stringLiteral().bind("completing_id")), |
201 hasArgument(2, stringLiteral().bind("annotation_text")), | 269 hasArgument(2, stringLiteral().bind("annotation_text")), |
202 anyOf(hasAncestor(functionDecl().bind("function_context")), | 270 bind_function_context_if_present) |
203 unless(hasAncestor(functionDecl())))) | |
204 .bind("partial_function"), | 271 .bind("partial_function"), |
205 &callback); | 272 &callback); |
206 match_finder.addMatcher( | 273 match_finder.addMatcher( |
207 callExpr(hasDeclaration(functionDecl( | 274 callExpr(hasDeclaration(functionDecl( |
208 anyOf(hasName("CompleteNetworkTrafficAnnotation"), | 275 anyOf(hasName("CompleteNetworkTrafficAnnotation"), |
209 hasName("net::CompleteNetworkTrafficAnnotation")))), | 276 hasName("net::CompleteNetworkTrafficAnnotation")))), |
210 hasArgument(0, stringLiteral().bind("unique_id")), | 277 hasArgument(0, stringLiteral().bind("unique_id")), |
211 hasArgument(2, stringLiteral().bind("annotation_text")), | 278 hasArgument(2, stringLiteral().bind("annotation_text")), |
212 anyOf(hasAncestor(functionDecl().bind("function_context")), | 279 bind_function_context_if_present) |
213 unless(hasAncestor(functionDecl())))) | |
214 .bind("completing_function"), | 280 .bind("completing_function"), |
215 &callback); | 281 &callback); |
216 match_finder.addMatcher( | 282 match_finder.addMatcher( |
217 callExpr(hasDeclaration(functionDecl(anyOf( | 283 callExpr(hasDeclaration(functionDecl(anyOf( |
218 hasName("BranchedCompleteNetworkTrafficAnnotation"), | 284 hasName("BranchedCompleteNetworkTrafficAnnotation"), |
219 hasName("net::BranchedCompleteNetworkTrafficAnnotation")))), | 285 hasName("net::BranchedCompleteNetworkTrafficAnnotation")))), |
220 hasArgument(0, stringLiteral().bind("unique_id")), | 286 hasArgument(0, stringLiteral().bind("unique_id")), |
221 hasArgument(1, stringLiteral().bind("group_id")), | 287 hasArgument(1, stringLiteral().bind("group_id")), |
222 hasArgument(3, stringLiteral().bind("annotation_text")), | 288 hasArgument(3, stringLiteral().bind("annotation_text")), |
223 anyOf(hasAncestor(functionDecl().bind("function_context")), | 289 bind_function_context_if_present) |
224 unless(hasAncestor(functionDecl())))) | |
225 .bind("branched_completing_function"), | 290 .bind("branched_completing_function"), |
226 &callback); | 291 &callback); |
292 | |
293 // Setup patterns to find functions that should be monitored. | |
294 match_finder.addMatcher( | |
295 callExpr( | |
296 hasDeclaration(functionDecl( | |
297 anyOf(hasName("SSLClientSocket::SSLClientSocket"), | |
298 hasName("TCPClientSocket::TCPClientSocket"), | |
299 hasName("UDPClientSocket::UDPClientSocket"), | |
300 hasName("URLFetcher::Create"), | |
301 hasName("ClientSocketFactory::CreateDatagramClientSocket"), | |
302 hasName("ClientSocketFactory::CreateSSLClientSocket"), | |
303 hasName("ClientSocketFactory::CreateTransportClientSocket"), | |
304 hasName("URLRequestContext::CreateRequest")), | |
305 has_annotation_parameter)), | |
306 bind_function_context_if_present) | |
307 .bind("monitored_function"), | |
308 &callback); | |
309 | |
227 std::unique_ptr<clang::tooling::FrontendActionFactory> frontend_factory = | 310 std::unique_ptr<clang::tooling::FrontendActionFactory> frontend_factory = |
228 clang::tooling::newFrontendActionFactory(&match_finder); | 311 clang::tooling::newFrontendActionFactory(&match_finder); |
229 return clang_tool->run(frontend_factory.get()); | 312 return clang_tool->run(frontend_factory.get()); |
230 } | 313 } |
231 | 314 |
232 } // namespace | 315 } // namespace |
233 | 316 |
234 static llvm::cl::OptionCategory ToolCategory( | 317 static llvm::cl::OptionCategory ToolCategory( |
235 "traffic_annotation_extractor: Extract traffic annotation texts"); | 318 "traffic_annotation_extractor: Extract traffic annotation texts"); |
236 static llvm::cl::extrahelp CommonHelp( | 319 static llvm::cl::extrahelp CommonHelp( |
237 clang::tooling::CommonOptionsParser::HelpMessage); | 320 clang::tooling::CommonOptionsParser::HelpMessage); |
238 | 321 |
239 int main(int argc, const char* argv[]) { | 322 int main(int argc, const char* argv[]) { |
240 clang::tooling::CommonOptionsParser options(argc, argv, ToolCategory); | 323 clang::tooling::CommonOptionsParser options(argc, argv, ToolCategory); |
241 clang::tooling::ClangTool tool(options.getCompilations(), | 324 clang::tooling::ClangTool tool(options.getCompilations(), |
242 options.getSourcePathList()); | 325 options.getSourcePathList()); |
243 Collector collector; | 326 Collector collector; |
244 | 327 |
245 int result = RunMatchers(&tool, &collector); | 328 int result = RunMatchers(&tool, &collector); |
246 | 329 |
247 if (result != 0) | 330 if (result != 0) |
248 return result; | 331 return result; |
249 | 332 |
250 // For each call to any of the functions that define a network traffic | 333 // 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(). | 334 // annotation, write annotation text and relevant meta data into llvm::outs(). |
252 for (const NetworkAnnotationInstance& call : collector) { | 335 for (const NetworkAnnotationInstance& instance : collector.annotations) { |
253 llvm::outs() << "==== NEW ANNOTATION ====\n"; | 336 llvm::outs() << "==== NEW ANNOTATION ====\n"; |
254 llvm::outs() << call.location.file_path << "\n"; | 337 llvm::outs() << instance.location.file_path << "\n"; |
255 llvm::outs() << call.location.function_name << "\n"; | 338 llvm::outs() << instance.location.function_name << "\n"; |
256 llvm::outs() << call.location.line_number << "\n"; | 339 llvm::outs() << instance.location.line_number << "\n"; |
257 llvm::outs() << call.GetTypeName() << "\n"; | 340 llvm::outs() << instance.GetTypeName() << "\n"; |
258 llvm::outs() << call.annotation.unique_id << "\n"; | 341 llvm::outs() << instance.annotation.unique_id << "\n"; |
259 llvm::outs() << call.annotation.extra_id << "\n"; | 342 llvm::outs() << instance.annotation.extra_id << "\n"; |
260 llvm::outs() << call.annotation.text << "\n"; | 343 llvm::outs() << instance.annotation.text << "\n"; |
261 llvm::outs() << "==== ANNOTATION ENDS ====\n"; | 344 llvm::outs() << "==== ANNOTATION ENDS ====\n"; |
262 } | 345 } |
263 | 346 |
347 // For each call, write annotation text and relevant meta data. | |
348 for (const CallInstance& instance : collector.calls) { | |
349 llvm::outs() << "==== NEW CALL ====\n"; | |
350 llvm::outs() << instance.location.file_path << "\n"; | |
351 llvm::outs() << instance.location.function_name << "\n"; | |
352 llvm::outs() << instance.location.line_number << "\n"; | |
353 llvm::outs() << instance.called_function_name << "\n"; | |
354 llvm::outs() << instance.has_annotation << "\n"; | |
355 llvm::outs() << "==== CALL ENDS ====\n"; | |
356 } | |
357 | |
264 return 0; | 358 return 0; |
265 } | 359 } |
OLD | NEW |