Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(222)

Side by Side Diff: tools/clang/traffic_annotation_extractor/traffic_annotation_extractor.cpp

Issue 2448133006: Tool added to extract network traffic annotations. (Closed)
Patch Set: Created 4 years, 1 month ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(Empty)
1 // Copyright 2016 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include <memory>
6 #include <stdio.h>
7
8 #include "clang/ASTMatchers/ASTMatchFinder.h"
9 #include "clang/ASTMatchers/ASTMatchers.h"
10 #include "clang/Basic/SourceManager.h"
11 #include "clang/Frontend/FrontendActions.h"
12 #include "clang/Lex/Lexer.h"
13 #include "clang/Tooling/CommonOptionsParser.h"
14 #include "clang/Tooling/Refactoring.h"
15 #include "clang/Tooling/Tooling.h"
16 #include "llvm/Support/CommandLine.h"
17 //--> TODO: #include "../../traffic_annotation/traffic_annotation.pb.h"
battre 2016/10/26 14:29:18 remove?
Ramin Halavati 2016/10/27 09:40:08 I think if we want to add protobuf to clang tool i
18
19 using namespace clang::ast_matchers;
20 using clang::tooling::CommonOptionsParser;
21
22 namespace {
23
24 // Structure to collect instances.
battre 2016/10/26 14:29:19 instances of what? Can you add more comments to th
Ramin Halavati 2016/10/27 09:40:08 Done.
25 typedef struct Collector {
battre 2016/10/26 14:29:18 I think that in C++ we don't need to "typedef stru
Ramin Halavati 2016/10/27 09:40:10 Done.
26 typedef struct Instance {
27 Instance() {
battre 2016/10/26 14:29:18 Can you use an initializer list? Instance() : is_
Ramin Halavati 2016/10/27 09:40:08 Done.
28 is_direct_call = false;
29 transitive_parameter = false;
30 variable_reference = NULL;
battre 2016/10/26 14:29:19 nullptr
Ramin Halavati 2016/10/27 09:40:09 Done.
31 }
32
33 // Information about where this annotation or call has happened.
34 struct Location {
35 std::string file_name;
36 int line_number;
battre 2016/10/26 14:29:19 line number is not default initialized.
Ramin Halavati 2016/10/27 09:40:09 Done.
37 // Name of the function including this instance.
38 std::string function_name;
39 // Name of the variable that contains annotation or the function called
40 // with annotation.
41 std::string object_name;
42 };
43
44 // Annotation content
battre 2016/10/26 14:29:18 Comments should end with period. (please also chec
Ramin Halavati 2016/10/27 09:40:09 Done.
45 struct Annotation {
46 std::string unique_id;
47 std::string text;
48 };
49
50 Location location;
51 Annotation annotation;
52
53 // Possible error (empty if no error)
54 std::string error;
55 // A reference to the variable containing annotation. Null if not available.
56 const clang::NamedDecl* variable_reference;
57 // Flag stating that parameter is directly passed to annotate function here
58 // or it's through a variable.
59 bool is_direct_call;
60 // Flag stating that a variable is a parameter received by upper level
61 // function.
62 bool transitive_parameter;
63 } Instance;
64
65 std::vector<Instance> variable_definitions, calls;
battre 2016/10/26 14:29:19 one variable definition per line.
Ramin Halavati 2016/10/27 09:40:09 Done.
66 } Collector;
67
68 // Returns the function that includes the given clang::Decl.
69 std::string GetCoveringFunction(const clang::Decl* token,
70 const MatchFinder::MatchResult& result);
71
72 // Checks if a token matches a name, with or without net:: namespace
73 bool net_match(const std::string& token, const std::string& name) {
74 return token == name || token == (std::string("net::") + name);
75 }
76
77 // Returns the text of a given statement or subclass.
78 std::string GetStmtText(const clang::Stmt* token,
79 const clang::SourceManager& source_manager) {
80 clang::LangOptions lopt;
81 // Get text range
82 clang::SourceLocation start = token->getLocStart();
83 clang::SourceLocation end = token->getLocEnd();
84
85 // If it's a macro, go to definition.
86 if (start.isMacroID())
87 start = source_manager.getSpellingLoc(start);
88 if (end.isMacroID())
89 end = source_manager.getSpellingLoc(end);
90
91 // Get the real end of the token.
92 end = clang::Lexer::getLocForEndOfToken(end, 0, source_manager, lopt);
93
94 // Extract text.
95 std::string output = std::string(source_manager.getCharacterData(start),
96 source_manager.getCharacterData(end) -
97 source_manager.getCharacterData(start));
battre 2016/10/26 14:29:19 can you also write std::string output(source_manag
Ramin Halavati 2016/10/27 09:40:09 Done.
98
99 // Raw string?
100 if (output == "R")
battre 2016/10/26 14:29:19 {} around multiline blocks. Same in the next line.
Ramin Halavati 2016/10/27 09:40:09 Done.
101 if (auto* c1 = clang::dyn_cast<clang::ImplicitCastExpr>(token))
102 if (const clang::StringLiteral* c2 =
103 clang::dyn_cast<clang::StringLiteral>(
104 c1->getSubExprAsWritten())) {
105 output = c2->getString();
106 }
107
108 return output;
109 }
110
111 // Returns annotation text of a call to "DefineNetworkTrafficAnnotation"
112 // function.
113 void GetAnnotationText(const clang::CallExpr* call_expr,
114 const clang::SourceManager& source_manager,
115 Collector::Instance* instance) {
116 if (net_match(GetStmtText(call_expr->getCallee(), source_manager),
117 "DefineNetworkTrafficAnnotation") &&
118 call_expr->getNumArgs() == 2) {
119 instance->annotation.unique_id =
120 GetStmtText(call_expr->getArgs()[0], source_manager);
121 instance->annotation.text =
122 GetStmtText(call_expr->getArgs()[1], source_manager);
123 instance->error = "";
124 } else {
125 instance->annotation.unique_id = "";
126 instance->annotation.text = "";
127 instance->error = "Unexpected function.";
128 }
129 }
130
131 // Returns the function that includes the given clang::Stmt.
132 std::string GetCoveringFunction(const clang::Stmt* token,
133 const MatchFinder::MatchResult& result) {
134 auto parents = result.Context->getParents(*token);
135 if (parents.size() == 1) {
136 if (const clang::Stmt* s = parents[0].get<clang::Stmt>())
137 return GetCoveringFunction(s, result);
138 else if (const clang::Decl* d = parents[0].get<clang::Decl>())
139 return GetCoveringFunction(d, result);
140 }
141 return "Unknown";
142 }
143
144 // Returns the function that includes the given clang::Decl.
145 std::string GetCoveringFunction(const clang::Decl* token,
146 const MatchFinder::MatchResult& result) {
147 if (auto f = clang::dyn_cast<clang::FunctionDecl>(token))
148 return f->getQualifiedNameAsString();
149
150 auto parents = result.Context->getParents(*token);
151 if (parents.size() == 1) {
battre 2016/10/26 14:29:18 Add a comment what having a single parent means?
Ramin Halavati 2016/10/27 09:40:09 I did not find a conclusive description on this an
152 if (const clang::Stmt* s = parents[0].get<clang::Stmt>())
153 return GetCoveringFunction(s, result);
154 else if (const clang::Decl* d = parents[0].get<clang::Decl>())
155 return GetCoveringFunction(d, result);
156 }
157 return "Unknown";
158 }
159
160 // Returns the file name and line number of the given token.
161 template <class T>
162 void GetOccuranceLocation(const T* token,
battre 2016/10/26 14:29:19 typo: occurr*e*nce What's the difference between
Ramin Halavati 2016/10/27 09:40:09 Renamed it to GetLocation as it can be called gene
163 const MatchFinder::MatchResult& result,
164 Collector::Instance::Location* location) {
165 clang::SourceLocation source_location = token->getLocStart();
166 location->file_name = result.SourceManager->getFilename(source_location);
167 location->line_number =
168 result.SourceManager->getSpellingLineNumber(source_location);
169 }
170
171 class TheCallback : public MatchFinder::MatchCallback {
battre 2016/10/26 14:29:19 Description of the responsibility of this class?
Ramin Halavati 2016/10/27 09:40:08 Done.
172 public:
173 explicit TheCallback(Collector* collector) : collector_(collector) {}
battre 2016/10/26 14:29:18 ~TheCallback() override = default;
Ramin Halavati 2016/10/27 09:40:09 Done.
174
175 virtual void run(const MatchFinder::MatchResult& result) override {
176 if (const clang::VarDecl* var_decl =
177 result.Nodes.getNodeAs<clang::VarDecl>("annotation_variable"))
battre 2016/10/26 14:29:19 also {} if the is statement spans more than one li
Ramin Halavati 2016/10/27 09:40:09 Done.
178 AddVariable(var_decl, result);
179 else if (const clang::CallExpr* call_expr =
180 result.Nodes.getNodeAs<clang::CallExpr>("user_function"))
181 AddFunction(call_expr, result);
182 }
183
184 // Stores an annotation variable defintion in the Collector.
185 void AddVariable(const clang::VarDecl* var_decl,
186 const MatchFinder::MatchResult& result) {
187 Collector::Instance instance;
188
189 GetOccuranceLocation(var_decl, result, &instance.location);
190 instance.location.object_name = var_decl->getQualifiedNameAsString();
191 instance.variable_reference = clang::dyn_cast<clang::NamedDecl>(var_decl);
192
193 // Get annotation text.
194 // If it doesn't have initialization, but it's a parameter, store it.
195 if (!var_decl->hasInit() && var_decl->isLocalVarDeclOrParm() &&
196 !var_decl->isLocalVarDecl()) {
197 instance.transitive_parameter = true;
198 } else if (auto* init_expr = var_decl->getInit()) {
199 if (auto* call_expr = clang::dyn_cast<clang::CallExpr>(init_expr))
200 GetAnnotationText(call_expr, *result.SourceManager, &instance);
201 }
202 // If nothing is set, issue an error.
203 if (!instance.transitive_parameter &&
204 instance.annotation.unique_id.empty() && instance.error.empty())
battre 2016/10/26 14:29:19 {}
Ramin Halavati 2016/10/27 09:40:09 Done.
205 instance.error = "Could not resolve variable initialization.";
206
207 collector_->variable_definitions.push_back(instance);
208 }
209
210 // Stores a function call that should be monitored.
211 void AddFunction(const clang::CallExpr* call_expr,
212 const MatchFinder::MatchResult& result) {
213 Collector::Instance instance;
214
215 GetOccuranceLocation(call_expr, result, &instance.location);
216 instance.location.function_name =
217 GetCoveringFunction(clang::dyn_cast<clang::Stmt>(call_expr), result);
218 instance.location.object_name =
219 call_expr->getDirectCallee()->getQualifiedNameAsString();
220
221 // Get annotation text.
222 const clang::FunctionDecl* function_decl = call_expr->getDirectCallee();
223 unsigned params_count = function_decl->getNumParams();
224 unsigned args_count = call_expr->getNumArgs();
225
226 for (unsigned i = 0; i < params_count; i++) {
227 if (net_match(clang::QualType::getAsString(
228 function_decl->getParamDecl(i)->getType().split()),
229 "NetworkTrafficAnnotationTag")) {
230 if (i >= args_count) {
231 instance.error = "Function missing annotation argument.";
232 } else {
233 // Get the argument.
234 const clang::Expr* arg = call_expr->getArgs()[i];
235
236 // Is it a call to annotate function?
237 if (auto* inner_call_expr = clang::dyn_cast<clang::CallExpr>(arg)) {
238 instance.is_direct_call = true;
239 GetAnnotationText(inner_call_expr, *result.SourceManager,
240 &instance);
241 instance.error = "";
242 } else {
243 // Then it's a variable.
244 instance.is_direct_call = false;
245 if (auto* pure_arg =
246 clang::dyn_cast<clang::DeclRefExpr>(arg->IgnoreCasts())) {
247 instance.variable_reference = pure_arg->getFoundDecl();
248 instance.error = "";
249 } else {
250 instance.error = "Unknwon parameter type.";
251 }
252 }
253 }
254 collector_->calls.push_back(instance);
255 }
256 }
257 }
258
259 private:
260 // TODO store pointer
battre 2016/10/26 14:29:19 Delete the todo?
Ramin Halavati 2016/10/27 09:40:09 Done.
261 Collector* collector_;
262 };
263
264 // NRA class keeps the call back function and sets the matchers.
battre 2016/10/26 14:29:18 Please don't use abbreviations.
Ramin Halavati 2016/10/27 09:40:08 Done. The class is totally removed and replaced by
265 class NetworkRequestAuditor {
battre 2016/10/26 14:29:19 Rename this to NetworkAnnotationTagExtractor? Then
Ramin Halavati 2016/10/27 09:40:09 The class is totally removed and replaced by a sin
266 public:
267 explicit NetworkRequestAuditor(Collector* collector)
268 : the_callback_(collector) {}
269
270 void SetupMatchers(MatchFinder* match_finder) {
271 // Find variables defined as Annotation
battre 2016/10/26 14:29:19 . What does "Find variables defined as Annotation
Ramin Halavati 2016/10/27 09:40:08 Comments changed.
272 match_finder->addMatcher(
273 varDecl(anyOf(hasType(asString("NetworkTrafficAnnotationTag")),
274 hasType(asString("net::NetworkTrafficAnnotationTag"))))
275 .bind("annotation_variable"),
276 &the_callback_);
277
278 // Find instances of functions that have a parameter of type Annotate.
279 match_finder->addMatcher(
280 callExpr(hasDeclaration(functionDecl(hasAnyParameter(anyOf(
281 hasType(asString("NetworkTrafficAnnotationTag")),
282 hasType(asString("net::NetworkTrafficAnnotationTag")))))))
283 .bind("user_function"),
284 &the_callback_);
285 }
286
287 private:
288 TheCallback the_callback_;
289 };
290
291 } // namespace
292
293 int main(int argc, const char* argv[]) {
294 llvm::cl::OptionCategory category("Network Request Audit Extractor Tool");
295 CommonOptionsParser options(argc, argv, category);
296 clang::tooling::ClangTool tool(options.getCompilations(),
297 options.getSourcePathList());
298
299 Collector collector;
300 NetworkRequestAuditor auditor(&collector);
301 MatchFinder match_finder;
302 auditor.SetupMatchers(&match_finder);
303
304 // Find output folder
305 const std::string kOutputSpecifier("output_dir=");
306 std::string output_dir;
307 for (int i = 0; i < argc; i++) {
308 if (!strncmp(argv[i], kOutputSpecifier.c_str(),
309 kOutputSpecifier.length())) {
battre 2016/10/26 14:29:19 if (argv[i] == kOutputSpecifier) does the same thi
Ramin Halavati 2016/10/27 09:40:08 argv[i] parameter includes the specifier and the p
310 output_dir = argv[i] + kOutputSpecifier.length();
311 break;
312 }
313 }
314
315 if (output_dir == "") {
316 llvm::errs() << "Temporary files directory is not specified.";
317 return 1;
318 }
319
320 std::unique_ptr<clang::tooling::FrontendActionFactory> frontend_factory =
321 clang::tooling::newFrontendActionFactory(&match_finder);
322 int result = tool.run(frontend_factory.get());
323
324 if (result != 0)
325 return result;
326
327 llvm::outs() << "==== BEGIN EDITS ====\n";
328 llvm::outs() << "==== END EDITS ====\n";
329
330 // For each call, if the parameter is not generated by a direct call to
331 // "DefineNetworkTrafficAnnotation", find the variable that holds the value.
332 for (auto& c : collector.calls) {
battre 2016/10/26 14:29:19 const auto& c?
Ramin Halavati 2016/10/27 09:40:09 the annotation field of iterators may be changed 5
333 if (!c.is_direct_call) {
334 // Find the variable.
335 for (auto& v : collector.variable_definitions)
battre 2016/10/26 14:29:19 {} const auto& ?
Ramin Halavati 2016/10/27 09:40:09 Done.
336 if (v.variable_reference == c.variable_reference) {
337 c.annotation = v.annotation;
338 c.transitive_parameter = v.transitive_parameter;
339 c.error = c.error + (c.error.length() ? "\n+" : "") + v.error;
340 break;
341 }
342 if (!c.annotation.unique_id.length())
343 c.error = "Variable not found.";
344 }
345
346 // If the function just receives the variable and passes it to another
347 // function, ignore it, otherwise write it to file.
348 if (!c.transitive_parameter) {
349 std::string s = c.location.file_name;
350 std::replace(s.begin(), s.end(), '/', '_');
351 std::replace(s.begin(), s.end(), '.', '_');
352 char file_name[1000];
353
354 snprintf(file_name, sizeof(file_name), "%s/%s(%i).txt",
355 output_dir.c_str(), s.c_str(), c.location.line_number);
battre 2016/10/26 14:29:18 std::string filename = output_dir + "/" + s + "("
Ramin Halavati 2016/10/27 09:40:09 Done.
356
357 FILE* file = fopen(file_name, "wt");
battre 2016/10/26 14:29:18 how about staying C++ here and using std::ofstream
Ramin Halavati 2016/10/27 09:40:08 Done.
358 if (file) {
359 fprintf(file, "%s\n", c.location.file_name.c_str());
360 fprintf(file, "%s\n", c.location.function_name.c_str());
361 fprintf(file, "%i\n", c.location.line_number);
362 fprintf(file, "%s\n", c.location.object_name.c_str());
363 fprintf(file, "%s\n", c.error.c_str());
364 fprintf(file, "%s\n", c.annotation.unique_id.c_str());
365 fprintf(file, "%s", c.annotation.text.c_str());
366 fclose(file);
367 } else {
368 llvm::errs() << "Could not write to file: " << file_name << " because "
369 << strerror(errno) << "\n";
370 return 1;
371 }
372 }
373 }
374
375 return 0;
376 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698