[PATCH] D52390: [analyzer] StackSizeChecker
Umann Kristóf via Phabricator via cfe-commits
cfe-commits at lists.llvm.org
Sat Sep 22 07:34:16 PDT 2018
Szelethus added subscribers: rnkovacs, baloghadamsoftware.
Szelethus added a comment.
I think the idea for a checker like this is great! I left some inline comments, but most of them are minor nits. I have some general remarks to make however:
- Your code lacks comments, especially a nice documentation describing this problem, how you approach it, what the general algorithm is etc. For example, you use `Usage` extensively, but I am still a little unsure what the difference is between `Empty` and `EmptyFlagged`. I'd encourage you to look at a couple nice examples:
1. @baloghadamsoftware's `IteratorChecker` <https://github.com/llvm-mirror/clang/blob/master/lib/StaticAnalyzer/Checkers/IteratorChecker.cpp> (A long code with fairly long comments to support it),
2. @rnkovacs's `DeleteWithNonVirtualDtorChecker` <https://github.com/llvm-mirror/clang/blob/master/lib/StaticAnalyzer/Checkers/DeleteWithNonVirtualDtorChecker.cpp> (A shorter code with shorter explanation),
3. My `UninitializedObjectChecker` <https://github.com/llvm-mirror/clang/tree/master/lib/StaticAnalyzer/Checkers/UninitializedObject> (Very similar to your checker in terms of quantity of files etc)
- Make sure you follow the LLVM Coding Standards <https://llvm.org/docs/CodingStandards.html>.
- As stated before, should move this to a new directory.
- For the amount of checker implementation you have, there aren't to many test cases. I can see that you handle `CXXTryStmt`, but there are no test cases for it.
- This revision is way too large for comfortable reading, you can help this by learning from my mistake and split it up into smaller patches. @NoQ summarized the reasons for this very nicely here: https://reviews.llvm.org/D45532#1083670
1. Post a checker class with an empty callback [1]. Even in this stage, some discussion could be had with the general direction the implementation should go. This avoids the potential of investing a lot of effort into a project that could be wrong from the get-go. Don't get me wrong, I'm not at all saying this is the case here!
2. Add implementation for checking a very easy case, such as a function with 10 array declarations, each having an absurd size.
3. Add each new functionality as a separate patch, and add dependencies with "Edit Related Revisions". For example, one for handling branches, one for loops, etc.
Let me emphasize that I'm a beginner myself, so I could be wrong on many of these ^-^
[1] This could be a nice first patch:
//=======- StackSizeChecker.cpp ----------------------------------*- C++ -*-==//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// This file defines a checker that checks for suspiciously high stack use.
// The checker produces a warning whenever the given stack limit is exceeded.
// The limit is defined by the "StackUsageLimit" analyzer parameter,
// or defaults to 100000 bytes.
//
//===----------------------------------------------------------------------===//
class StackSizeChecker
: public Checker<check::PreCall, check::PostCall, check::EndFunction> {
mutable std::unique_ptr<BugType> StackOverflowBugType;
public:
int StackUsageLimit;
void checkPreCall(const CallEvent &Call, CheckerContext &C) const {}
void checkPostCall(const CallEvent &Call, CheckerContext &C) const {}
void checkEndFunction(CheckerContext &C) const {}
};
void clang::ento::registerStackSizeChecker(CheckerManager &Mgr) {
StackSizeChecker *Check = Mgr.registerChecker<StackSizeChecker>();
Check->StackUsageLimit = Mgr.getAnalyzerOptions().getOptionAsInteger(
"StackUsageLimit", /*DefaultVal*/ 100000, Check);
}
================
Comment at: include/clang/StaticAnalyzer/Checkers/StackUsageMeasuringVisitor.h:30
+
+struct Usage {
+
----------------
To me, `Usage` seems to be a way too general name. Also, for a class being used this heavily, it has no comments explaining what it does. Is this a property of a variable? A function call?
================
Comment at: include/clang/StaticAnalyzer/Checkers/StackUsageMeasuringVisitor.h:46
+ // its children.
+ bool ExitFlag; // Tells the visitor to cease consuming the tree.
+
----------------
Which tree? I know you refer to the //statement// tree after reading the code for a while, but it would be more obvious if you put some comments for `Usage`.
================
Comment at: include/clang/StaticAnalyzer/Checkers/StackUsageMeasuringVisitor.h:94-131
+ void clear();
+ bool hasUsageStored(const Stmt *S) const {
+ return StmtSubtreeUsages.find(S) != StmtSubtreeUsages.end();
+ }
+ bool hasUsageStored(const Decl *D) const {
+ return DeclSubtreeUsages.find(D) != DeclSubtreeUsages.end();
+ }
----------------
Szelethus wrote:
> Are you sure this function belongs in a visitor?
Actually, it might be cleaner to move these into a new class, and have `StackUsageMeasuringVisitor` manage an object from that class.
================
Comment at: include/clang/StaticAnalyzer/Checkers/StackUsageMeasuringVisitor.h:102-111
+ bool hasEmptyFlaggedUsageStored(const Stmt *S) const {
+ auto iter = StmtSubtreeUsages.find(S);
+ return iter != StmtSubtreeUsages.end() &&
+ iter->second == Usage::EmptyFlagged;
+ }
+ bool hasEmptyFlaggedUsageStored(const Decl *D) const {
+ auto iter = DeclSubtreeUsages.find(D);
----------------
Would `isEmptyFlagged` be a better method name?
================
Comment at: include/clang/StaticAnalyzer/Checkers/StackUsageMeasuringVisitor.h:124
+ ? 0
+ : Context.getTypeSize(T) / Context.getCharWidth();
+ }
----------------
The function name states `sizeInBytes`, but
* `ASTContext::getTypeSizeInChars` returns the width in `CharUnits`,
* which you divide with `Context.getCharWidth()`, which is in bits (according to the doxygen comments),
* and we end up getting the amount of bytes `T` occupies?
I might make a fool out of myself, but wouldn't the formula look something like this: `(Context.getTypeSize(T) * Context.getCharWidth()) / 8`?
================
Comment at: include/clang/StaticAnalyzer/Checkers/StackUsageMeasuringVisitor.h:135-138
+ bool ContinueTraversing;
+ bool ExitFlag;
+ bool HardExit;
+ bool ForceTemporariesToConstant;
----------------
Needs explanation.
================
Comment at: lib/StaticAnalyzer/Checkers/StackSizeChecker.cpp:46
+public:
+ StackSizeChecker() : StackUsageLimit(0) {}
+ int StackUsageLimit;
----------------
Why initialize here if you're going to overwrite it when you register the checker?
================
Comment at: lib/StaticAnalyzer/Checkers/StackSizeChecker.cpp:54
+
+int countPredecessors(const CheckerContext &C) {
+ const auto *Frame = C.getStackFrame()->getParent();
----------------
I think this would be better as `static` with the definition being out-of-line on the bottom of the file (but not before registering the checker, as it's traditionally the last function in the checker's TU).
================
Comment at: lib/StaticAnalyzer/Checkers/StackSizeChecker.cpp:68-77
+int length(const llvm::ImmutableList<StackInt>& L) {
+ int N = 0;
+ auto I = L.begin();
+ auto E = L.end();
+ while(I != E){
+ ++N;
+ ++I;
----------------
Szelethus wrote:
> ```
> size_t length(const llvm::ImmutableList<StackInt> &L) {
> int Length = 0;
> for (auto It = L.begin(), E = L.end(); It != E; ++It)
> ++Length;
> return Length;
> }
Actually, same here, should be `static` and defined out of line, so the declarations and documentation can be placed on top of the file and implementation can be found on the bottom.
================
Comment at: lib/StaticAnalyzer/Checkers/StackSizeChecker.cpp:157-158
+ StackSizeChecker *Check = Mgr.registerChecker<StackSizeChecker>();
+ Check->StackUsageLimit = Mgr.getAnalyzerOptions().getOptionAsInteger(
+ "StackUsageLimit", 100000, Check);
+}
----------------
```
"StackUsageLimit", /*DefaultVal*/ 100000, Check);
================
Comment at: lib/StaticAnalyzer/Checkers/StackUsageMeasuringVisitor.cpp:22-23
+
+const Usage Usage::Empty = {0, 0, false, false};
+const Usage Usage::EmptyFlagged = {0, 0, false, true};
+
----------------
Szelethus wrote:
> Constant static data members should be in-class initialized. https://github.com/isocpp/CppCoreGuidelines/blob/master/CppCoreGuidelines.md#Rc-in-class-initializer
Actually, these shouldn't be static variables, but rather static functions (and probably within the class definition):
```
static Usage getEmptyUsage() { return Usage{0, 0, false, false}; }
static Usage getEmptyFlaggedUsage() { return Usage{0, 0, false, true}; }
================
Comment at: lib/StaticAnalyzer/Checkers/StackUsageMeasuringVisitor.cpp:37-39
+Usage withoutConstant(Usage U) {
+ return {0, U.Maximal, U.DynAlloc, U.ExitFlag};
+}
----------------
I think this would be more fitting as a static member function.
================
Comment at: lib/StaticAnalyzer/Checkers/StackUsageMeasuringVisitor.cpp:41
+
+bool countsAsZero(const Expr *Ex) {
+ auto Cls = Ex->getStmtClass();
----------------
I don't really understand what this function does.
================
Comment at: lib/StaticAnalyzer/Checkers/StackUsageMeasuringVisitor.cpp:109
+
+int StackUsageMeasuringVisitor::exprRetSize(const Expr *E) const {
+ auto StmtCls = E->getStmtClass();
----------------
`getReturnExpressionSize`?
================
Comment at: lib/StaticAnalyzer/Checkers/StackUsageMeasuringVisitor.cpp:153
+ } else if (D->getType()->isDependentType()) {
+ // A template declaration was found, signal the algorithm.
+ putUsage(D, Usage::EmptyFlagged);
----------------
Can you rephrase this? "signal the algorithm" doesn't sound too descriptive to me.
================
Comment at: test/Analysis/stack-size-callchain.cpp:1
+// RUN: %clang_cc1 -analyze -analyzer-checker=core,alpha.core.StackSize -analyzer-config alpha.core.StackSize:StackUsageLimit=200 -std=c++11 -triple x86_64-unknown-linux-gnu -verify %s
+
----------------
We only started doing this recently, but you can actually organize the run line like this:
```
// RUN: %clang_cc1 -analyze -analyzer-checker=core,alpha.core.StackSize \
// RUN: -analyzer-config alpha.core.StackSize:StackUsageLimit=200 \
// RUN: -std=c++11 -triple x86_64-unknown-linux-gnu -verify %s
Repository:
rC Clang
https://reviews.llvm.org/D52390
More information about the cfe-commits
mailing list