[cfe-dev] Checking if a ParmVarDecl is null in a Checker

Artem Dergachev via cfe-dev cfe-dev at lists.llvm.org
Fri Apr 27 19:53:16 PDT 2018


The analyzer constructs only one exploded graph for every top-level 
function. It's the same graph used by all checkers. This is why we have 
dozens of checkers enabled simultaneously without significant 
performance regressions - we are designed to scale really well in this 
sense. But it also means that different checkers cannot have different 
ideas of what functions they need to inline.

You really do want to inline as much as possible. Even if nothing 
interesting happens within the function with respect to your checker, 
the analyzer will be able to model everything else correctly, and the 
ultimate goal of that would be to make sure that the execution path on 
which a bug is found by your checker is indeed a valid path through the 
program. For instance, if the function does not ever return null on any 
path, the analyzer will not proceed to explore the path on which the 
return value of that function is assumed to be null; if it did explore 
such path, any bugs found on such path would immediately be classified 
as false positives.

Still, the analyzer cannot inline everything, because sometimes the 
source code of the function is not available, and in other cases it 
needs to make difficult choices on whether it's worth it to inline a 
huge function and make the user wait forever for the analysis to 
complete, or skip the function and risk producing false positives. And 
the *lack* of inlining often *does* indeed requiring a special care on 
the checker side to carefully assume that the inlined function is indeed 
doing something sane (that's why checkers are subscribing for "pointer 
escapes" and/or "invalidations").

That said, i don't think i understand your question. Controlling 
inlining by the checker is definitely not possible, but what makes you 
think it's necessary? Because for now no checkers have ever needed that. 
Just in case - you might be thinking about a situation like this:

   // calls the block and exits
   void call_block(^(*block)());

   void foo() {
     call_block(^{});
   }

In this case if call_block isn't inlined, there's no way for the 
analyzer to understand that the block will be called within 
call_block(). Letting the checker model such call_block() call by 
explaining to the analyzer core that this function does indeed call the 
block is currently an open problem for the analyzer. It should be 
possible, but nobody had it implemented. Some such functions are modeled 
with the "body farm" technique - eg. dispatch_once(), but there's no way 
of doing it in the checker.


On 4/27/18 5:21 PM, Timothy J. Wood wrote:
>
>
>> On Apr 27, 2018, at 4:03 PM, Artem Dergachev <noqnoqneo at gmail.com 
>> <mailto:noqnoqneo at gmail.com>> wrote:
>>
>> Yep, indeed, this interface could be improved dramatically with a bit 
>> of work.
>>
>> I'm usually getting away pretty well by searching the graph in clever 
>> manners. If your dot viewer doesn't support text search across the 
>> graph (most don't), you might want to convert it to .svg (eg. dot 
>> -Tsvg graph.dot -o graph.svg) and open the svg in a web browser and 
>> then use the find-on-page feature.
>>
>
> Thanks for the hints!
>
> I started printing the state pointers in my log output and that’s 
> helping match up my log messages with where I am in the graph.
>
> One other thing that occurred to me is that some of the large graphs 
> I’m seeing are due to inlining across ObjC methods:
>
> - (BOOL)inner:(NSError **)outError; {…}
> - (BOOL)outer:(NSError **)outError; {
> return [self inner: outError];
> }
>
> Here I’ll get something like:
>
>     begin function
>     begin call
>     begin function
>>     end function
>     end call
>>     end function
>
> but I don’t need this sort of inlining for my checker. When processing 
> -outer: I can assume that the call to -inner: does the right thing 
> (writes to outError if its result is null, and leaves it undefined if 
> it is non-null), and then -inner: can be independently checked.
>
> But, if a method calls a block literal, I do need to let the inner 
> function be processed. I haven’t yet arrived at a way using 
> generateSink() that doesn’t just end the whole checking pass — is 
> there a way to conditionally stop inline checking that I can use here?
>
> void NSErrorWriteChecker::checkBeginFunction(CheckerContext &C) const {
>
>   if (!C.inTopFrame()) {
>     const LocationContext *LocCtxt = C.getLocationContext();
>     if (dyn_cast<BlockDecl>(LocCtxt->getDecl()) == NULL) {
>       // ???
>       return;
>     }
>   }
>
>   …
> }
>
> Thanks!
>
> -Tim
>

-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.llvm.org/pipermail/cfe-dev/attachments/20180427/e72878e1/attachment-0001.html>


More information about the cfe-dev mailing list