[cfe-dev] Clang Static Analyzer - tracking object value

Gábor Kozár kozargabor at gmail.com
Tue Apr 30 06:27:13 PDT 2013


Update: sorry, please ignore my previous e-mail - it turns out that the
issue was in my code.


2013/4/30 Gábor Kozár <kozargabor at gmail.com>

> Update: apparently if I explicitly state in the TransactionWrapper ctor
> that I want the MyTransaction object by rvalue reference, then it moves. I
> was under the impression it would move anyway.
>
> Also, it might be obvious, but the last MyTransaction object never
> actually dies - the analyzer hangs instead. If I move the MyTransaction
> object, then it doesn't die either (and no copy is created obviously).
>
> None of my checker's callbacks are called while the analyzer is hanging,
> so I suspect it's an internal issue? Note that I'm using Clang 3.2, so it
> might be something that has been fixed since then.
>
>
> 2013/4/30 Gábor Kozár <kozargabor at gmail.com>
>
>> Hi Jordan,
>>
>> Thank you very much for your help! I have no idea how I missed that. That
>> was a totally derp question, sorry.
>>
>>
>> > *...a lot of C++ objects are value objects, which means they get
>> copied from place to place, and currently it's up to checker writers to
>> determine when that copy happens. Anna actually started sketching out a C++
>> iterator checker and realized that this problem was nontrivial (though by
>> no means insurmountable).
>>
>> *
>> I have thought that's what checkBind() is for - but I haven't really
>> looked into what is it that it exactly does. Although I imagine that
>> wrapping an object inside another object would cause complications -
>> because in that case you have to check how the copy ctor behaves in
>> relation to the wrapped object (i.e. is it copied or is a new one created,
>> or is it passed by reference/pointer?), and then track it accordingly.
>> Hmm... yeah, I think can see why this is a non-trivial matter. :) I do find
>> it interesting though - might be something I'll look into during the summer
>> holiday.
>>
>>
>> > *Aside: if the region is more important than the symbol, why did we
>> choose to attach our stream information to the symbol in
>> SimpleStreamChecker?*
>>
>> It makes sense: fopen() and the like are not ctors, so - as far as I know
>> - the analyzer can't really reason about their return value, as it doesn't
>> do any inter-procedural analysis. That means that it can't do better than
>> just a create a symbol for their return value. My scenario is different,
>> because in my case, transaction objects are created by their ctors, and so
>> the analyzer has more information.
>>
>>
>> > *This "usually" is another problem for you—if we're constructing an
>> anonymous temporary object like 'MyHandler(this)', the expression may
>> result in an rvalue, which means the value represents a collection of
>> struct field bindings instead of an actual region in memory.
>>
>> *
>> I did a quick test on this rvalue case, and discovered that the analyzer
>> (when invoked with my checker) seems to hang indefinitely on this code:
>>
>> class MyTransaction {};
>>
>> class TransactionWrapper
>> {
>> public:
>>     TransactionWrapper(MyTransaction tr) : m_tr(tr) {}
>>
>> private:
>>     MyTransaction m_tr;
>> };
>>
>> int main(int argc, char* argv[])
>> {
>>     auto trw = TransactionWrapper(MyTransaction());
>>     return 0;
>> }
>>
>> It reports the creation of the MyTransaction object (apparently
>> represented by CXXTempObjectRegion in this case), then it also shows the
>> copy (why isn't it getting moved?), and also shows that the original
>> MyTransaction objects dies (at the end of the statement I imagine) - but
>> then it just hangs. What could cause such behavior?
>>
>> Thanks again!
>>
>> Gabor
>>
>>
>>
>> 2013/4/30 Jordan Rose <jordan_rose at apple.com>
>>
>>> Aack, no, this is not how it works. :-)
>>>
>>> From a checker-writer's perspective, conjureSymbolVal() always creates a
>>> *new* symbol. Now, that symbol isn't *really* unique, because it's
>>> cheaper if we reuse symbols along multiple paths, but you really can't use
>>> it to represent the same symbol that the analyzer core will create.
>>>
>>> Instead, what you want is a *post-*call check, where you can then use
>>> getSVal on the constructor expression to get the return value (which is
>>> *usually* the region that was just constructed*). Unfortunately...
>>>
>>> Next, my problem is with detecting when members of the transaction
>>> object is called. I was trying to get the SymbolRef of the object whose
>>> member is called:
>>>
>>> const CXXInstanceCall* instanceCall =
>>> llvm::dyn_cast<CXXInstanceCall>(&call);
>>> ...
>>> SymbolRef transactionObjectSymbol =
>>> instanceCall->getCXXThisVal().getAsSymbol();
>>>
>>> But I find that this SymbolRef is always NULL. Why is that?
>>>
>>>
>>> ...not every C++ object has an associated symbol. As we describe in "Building
>>> a Checker in 24 Hours <http://llvm.org/devmtg/2012-11/>", a symbol is
>>> only used to represent a *value* that the analyzer can't reason about.
>>> What you're interested in here is the *region,* which represents the
>>> memory backing the abstract C++ object. Unfortunately...
>>>
>>> ...a lot of C++ objects are value objects, which means they get copied
>>> from place to place, and currently it's up to checker writers to determine
>>> when that copy happens. Anna actually started sketching out a C++ iterator
>>> checker and realized that this problem was nontrivial (though by no means
>>> insurmountable).
>>>
>>> (Aside: if the region is more important than the symbol, why did we
>>> choose to attach our stream information to the symbol in
>>> SimpleStreamChecker? Well, the type of a POSIX stream is FILE*, but the
>>> pointer-ness of it is essentially an implementation detail. It's more
>>> important to associate the state with the return *value* of
>>> fopen(). Indeed, the same logic in SimpleStreamChecker could be used for
>>> the raw file descriptors used by open(), write(), and close().)
>>>
>>> That's sort of where we stand today. I don't have a good answer for you
>>> on how to deal with C++ value objects. Sorry!
>>>
>>> Jordan
>>>
>>> * This *"usually"* is another problem for you—if we're constructing an
>>> anonymous temporary object like 'MyHandler(this)', the expression may
>>> result in an rvalue, which means the value represents a collection of
>>> struct field bindings instead of an actual region in memory. I would not
>>> worry about this case so much right now, though.
>>>
>>
>>
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.llvm.org/pipermail/cfe-dev/attachments/20130430/7e7692ae/attachment.html>


More information about the cfe-dev mailing list