[webkit-dev] Easing printf based debugging in WebKit with an helper.

Filip Pizlo fpizlo at apple.com
Thu Jul 19 15:09:48 PDT 2012


On Jul 19, 2012, at 2:58 PM, Balazs Kelemen <kbalazs at webkit.org> wrote:

> On 07/19/2012 11:27 PM, Maciej Stachowiak wrote:
>> On Jul 10, 2012, at 8:52 AM, Brady Eidson<beidson at apple.com>  wrote:
>> 
>>> On Jul 10, 2012, at 5:25 AM, Alexis Menard<alexis.menard at openbossa.org>  wrote:
>>> 
>>>> On Mon, Jul 9, 2012 at 6:53 PM, Brady Eidson<beidson at apple.com>  wrote:
>>>>> On Jul 9, 2012, at 2:43 PM, Alexis Menard<alexis.menard at openbossa.org>  wrote:
>>>>> 
>>>>>> Hi,
>>>>>> 
>>>>>> For those who "secretly" use printf debugging :). I know the
>>>>>> recommended way is to use a debugger and it's not the point of this
>>>>>> discussion.
>>>>> A lot of us do this, and sometimes it's necessary.  I agree with the gripe and support adding something easier.
>>>>> 
>>>>>> So I propose wtf() and its stream operator.
>>>>>> 
>>>>>> Usage :
>>>>>> 
>>>>>> wtf()<<"Hello"<<"World"<<3<<4.53322323; will output : Hello World 3 4.53322
>>>>> There is no reason to bring in stream operators - that are willfully absent from WebCore - just for debugging.
>>>>> 
>>>> But it's really nice for that purpose, and somehow match std::cout
>>> And we quite purposefully don't use std::cout in the project.
>>> 
>>>>> Overloading functions works just as well.
>>>> I'm not sure to understand what you mean here…
>>> I mean relying on C++'s overloading of functions for the different types you'd like to printf debug.
>>> 
>>> void debug(WebCore::String&);
>>> void debug(WebCore::Frame*);
>>> void debug(WebCore::Node*);
>>> 
>>> etc etc etc.
>>> 
>>> debug(someFrame);
>>> debug(someNode);
>>> debug(someString);
>>> 
>>> Especially that last one would help me from remembering how to type "printf("%s", someString.utf8().data())" which is all I've ever really wanted.
>> In principle, we could also have this support multiple arguments, so you could write:
>> 
>> debug("frame: ", someFrame, " node: ", someNode, " string", someString);
>> 
>> This would be no more verbose than the<<  style, but could compile to a single function call at the call site and therefore could be relatively compact. I would find this easier to deal with than a unary function or a printf-style format string. The way you'd do this is by defining template functions which call a unary overloaded function for each argument:
>> 
>> template<typename A, typename B>  debug(A a, B b)
>> {
>>     debug(a);
>>     debug(b);
>> }
>> 
>> template<typename A, typename B, typename C>  debug(A a, B b, C c)
>> {
>>     debug(a);
>>     debug(b);
>>     debug(c);
>> }
>> 
>> template<typename A, typename B, typename C, typename D>  debug(A a, B b, C c, D d)
>> {
>>     debug(a);
>>     debug(b);
>>     debug(c);
>>     debug(d);
>> }
>> 
>> ... and so on up to some reasonable number of arguments.
> 
> 
> But neither these compile to a single function call. Or we could define simple inline debug() overrides but we could also do that with the stream operator. And anyway, if the actual calls are not supposed to land than it doesn't matter how compact it is. For me the stream operator is a bit nicer.

This goal of not landing the actual calls is nice in theory, but we quickly found in JSC that it's impractical.  We did this for a while: each time someone needed to debug something, they'd add some printf's, find the bug, delete the printf's, land the change.  Then the next person to debug the code would add in the same printf's, find another bug, delete those printf's again, and land the change.  Rinse and repeat.  This got horrible very quickly.

So instead, we decided on the dataLog() API, and made the policy that for any sizeable set of dataLog() calls that were found to be useful for a particular debugging task, we would encapsulate them behind a dump() method on the relevant class and land that method along with the fixes.  The thing that we would _not_ land is the call to the dump() method.  The point being that it's easier to add back in the call to dump(), than it is to add back in all of the calls to dataLog().  And even in some cases, we did land the call to dump(), behind a check of some flag that can be flipped in one .cpp file or via an environment variable, or even via a command-line switch if you wanted to go so far.

We've found that this is nicer in the long term and has sped up debugging for a lot of common scenarios.

Thus, I tend to think that we want to come up with a debug printing API that produces sufficiently compact code that this is practical.  I kind of like Maciej's approach; I think it could work, though you risk template bloat.  A good compiler and linker will probably reduce that bloat, but such things are imperfect.

It's also possible that our obsession with ensuring that the debug code (i.e. the innards of the dump() methods) is compact is unfounded.  With the dataLog() API I found that it generally adds much less than 1% to the size of the JavaScriptCore library, so we declared victory.  But maybe we can give ourselves more latitude and use a nicer API.

In summary, I'm not arguing against having a nicer debug API than the current dataLog(); I'm arguing quite the opposite: that we should investigate such an API, and that the API should be robust enough that we land not just the API but also allow for the landing of dump() methods for classes that webkittens find themselves having to frequently add printf-style debugging to.

-F


> 
> -kbalazs
> _______________________________________________
> webkit-dev mailing list
> webkit-dev at lists.webkit.org
> http://lists.webkit.org/mailman/listinfo/webkit-dev



More information about the webkit-dev mailing list