-
Notifications
You must be signed in to change notification settings - Fork 3.9k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Trace external pointers through maps #1285
Trace external pointers through maps #1285
Conversation
294a0e9
to
91ccf40
Compare
Looks like a good improvement. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for fixing this. Definitely positive for user programs. A few comments are inlined with the codes.
} | ||
} | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I do not fully understand why we need to track map.update or map.insert here?
It is totally possible that map.update and map.insert not in the same compilation unit, e.g., populated by user space or shared with another bpf program. Then we will not able to trace map value pointers below?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Where would you track map.update
and map.insert
if not here?
Yes, if the updates/inserts are not in the same compilation unit, we can't trace map value pointers. I don't think there's much we can do in that case, at least without hints from developers.
return true; | ||
|
||
if (memb_name == "lookup" || memb_name == "lookup_or_init") { | ||
if (m_.find(Ref->getDecl()) != m_.end()) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Again not sure whether we need this m_ check or not. What if we do not check?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The m_ check makes sure the map has an external pointer as value. AFAIK, the only way to know that is to track them from the function arguments (what's already implemented) to the map.inserts/updates, remember that, and use it when we meet the map.lookups (what this pull request implements).
@@ -773,7 +852,8 @@ void BFrontendAction::EndSourceFileAction() { | |||
unique_ptr<ASTConsumer> BFrontendAction::CreateASTConsumer(CompilerInstance &Compiler, llvm::StringRef InFile) { | |||
rewriter_->setSourceMgr(Compiler.getSourceManager(), Compiler.getLangOpts()); | |||
vector<unique_ptr<ASTConsumer>> consumers; | |||
consumers.push_back(unique_ptr<ASTConsumer>(new ProbeConsumer(Compiler.getASTContext(), *rewriter_))); | |||
consumers.push_back(unique_ptr<ASTConsumer>(new MapConsumer(m_))); | |||
consumers.push_back(unique_ptr<ASTConsumer>(new ProbeConsumer(Compiler.getASTContext(), *rewriter_, m_))); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If we use handleTranslationUnit to post process ProbeConsumer and BTypeConsumer, I am wondering whether we need ProbeConsumer and BTypeConsumer registered as the BFrontendAction consumer. We can do post process at MapConsumer handleTranslationUnit?
Note that this implementation changes how each construct is processed by ProbeConsumer and BTypeConsumer. The old implementation will call the processing function for each consumer for each construct at a time before moving to the next construct. The new implementation will process all constructs for one consumer before moving to the next consumer.
Look at the ProbeConsumer and BTypeConsumer code, they are largely independent so it should be okay.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If we use handleTranslationUnit to post process ProbeConsumer and BTypeConsumer, I am wondering whether we need ProbeConsumer and BTypeConsumer registered as the BFrontendAction consumer. We can do post process at MapConsumer handleTranslationUnit?
So we'd have a single Consumer
for all visitors? Sure 👍
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done in the last commit.
@yonghong-song Thanks for the review and sorry for the delay to answer. |
91ccf40
to
114a5ee
Compare
114a5ee
to
3c75763
Compare
Looks great. Probably needs rebase before merge? |
The bcc rewriter currently traces external pointers using ProbeVisitor in order to replace dereferences with calls to bpf_probe_read. It is, however, unable to trace them through maps. This commit remedy this for simple (yet common) cases. Through a first traversal of the Clang AST, MapVisitor looks for calls to update (and insert) to find maps with an external pointer as value. When ProbeVisitor runs, in a second time, it looks for calls to lookup (and lookup_or_init). If the map was registered as having an external pointer as value, the l-value of the lookup assignment is marked as being an external pointer. Two traversals of the Clang AST are needed because the update of a map may happen after the lookup in the AST. Therefore, the first traversal makes sure we inspect all updates before acting on lookups. To implement this two-stage traversal without parsing the AST twice, ProbeConsumer and BTypeConsumer now implement HandleTranslationUnit, which is called after a whole translation unit has been parsed.
3c75763
to
55cf837
Compare
@4ast Done (with a new commit to address @yonghong-song's comment above). |
55cf837
to
e67cb56
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM
The bcc rewriter currently traces external pointers using
ProbeVisitor
in order to replace dereferences with calls tobpf_probe_read
. It is, however, unable to trace them through maps.This pull request fixes this for simple (yet common) cases. Through a
first traversal of the Clang AST,
MapVisitor
looks for calls toupdate (and insert) to find maps with an external pointer as value.
When
ProbeVisitor
runs, in a second time, it looks for calls tolookup (and lookup_or_init). If the map was registered as having an
external pointer as value, the l-value of the lookup assignment is
marked as being an external pointer.
Two traversals of the Clang AST are needed because the update of a
map may happen after the lookup in the AST. Therefore, the first
traversal makes sure we inspect all updates before acting on lookups.
To implement this two-stage traversal without parsing the AST twice,
ProbeConsumer
andBTypeConsumer
now implementHandleTranslationUnit
, which is called after a whole translationunit has been parsed.
This change should probably be considered a breaking API change. In
bcc, it breaks a few tools that were relying on the rewriter not
being able to replace dereferences. For instance, the
bpf_probe_read(&saddr, sizeof(saddr), &skp->__sk_common.skc_rcv_saddr)
expression in
examples/tracing/tcpv4connect.py
now returns an error.Fixes #253.
/cc @drzaeus77