tool for chasing elusive VclPtr<> crashers ...

Michael Meeks michael.meeks at
Wed May 13 04:00:29 PDT 2015

Hi guys,

	I've been chasing this bug:

	on and off for around a week. And it's quite an amazing one =)

	Before I go further, I'd like to hope this is quite a rare specimen -
the vast majority of VclPtr<> bugs are really not this bug; so this is
really a thing of last-resort:

	The stack trace shows something like this:

 0x4C28FAC: operator delete(void*) (in /usr/lib64/valgrind/
 0x3134D719: ScPivotLayoutTreeListLabel::~ScPivotLayoutTreeListLabel() (PivotLayoutTreeListLabel.cxx:33)

	Here we delete this guy - which sounds sensible; except that ...

 0xB7F3FC4: OutputDevice::release() const (outdev.hxx:284)
 0xB7F71FB: rtl::Reference<vcl::Window>::set(vcl::Window*) (ref.hxx:95)
 0xB7F5249: rtl::Reference<vcl::Window>::operator=(rtl::Reference<vcl::Window> const&) (ref.hxx:106)
 0xB7F4214: VclPtr<vcl::Window>::operator=(VclPtr<vcl::Window> const&) (vclptr.hxx:83)
 0xB82AFF4: vcl::Window::ImplRemoveWindow(bool) (stacking.cxx:148)
 0xB9A8E20: vcl::Window::dispose() (window.cxx:511)
 0xBA12D1D: Control::dispose() (ctrl.cxx:76)
 0x9B434A5: SvTreeListBox::dispose() (treelistbox.cxx:1584)
 0x313499FD: ScPivotLayoutTreeListBase::dispose() (PivotLayoutTreeListBase.cxx:38)
 0xBAEF813: OutputDevice::disposeOnce() (outdev.cxx:203)
 0xB85E3BF: VclPtr<vcl::Window>::disposeAndClear() (vclptr.hxx:209)

	In this frame we are holding a reference (to ensure that the object
lives until the end of the disposeAndClear)

 0xB83F000: VclBuilder::disposeBuilder() (builder.cxx:536)

	So - despite all the smart-ref-counting logic - somehow we're getting
the reference counting wrong: 

	So ... after about a week of chasing this on & off, I found the bug
which is here:

sal_Int8 SvTreeListBox::ExecuteDrop( const ExecuteDropEvent& rEvt, SvTreeListBox* pSourceView )
    SvLBoxDDInfo aDDInfo;
    memset( &aDDInfo, 0, sizeof(SvLBoxDDInfo) );

    TransferableDataHelper aData( rEvt.maDropEvent.Transferable );
    if( aData.HasFormat( SotClipboardFormatId::TREELISTBOX ))
        css::uno::Sequence<sal_Int8> aSeq = aData.GetSequence(SotClipboardFormatId::TREELISTBOX, OUString());
        if (sizeof(SvLBoxDDInfo) == aSeq.getLength())
            memcpy( &aDDInfo, aSeq.getConstArray(), sizeof(SvLBoxDDInfo) );

	This final memcpy writes over a VclPtr<> inside the aDDInfo struct:

struct SvLBoxDDInfo
    Application*    pApp;
    VclPtr<SvTreeListBox>         pSource;

	Lets hold our breath and gloss over the -horrible- details of passing
raw pointers through an UNO sequence as a byte array and then ... [ it
seems the D&D code - which is also threaded ] is some living nightmare
in this regard ;-)

	Anyhow - the question is: how to find this stuff ? the problem being
that when you track every one of the (2000+) acquire/releases on the
underlying ref-count they all appear to be correct: there are just more
releases than there should be. [ the ref-count is stored in the object
not the VclPtr which is clobbered ].

	Anyhow - here is how:

	a) run soffice.bin under gdb:

	gdb --args ./soffice.bin 2>&1 | tee /tmp/gdb-log.txt

	place a breakpoint in the 'makeFoo' function you're
	interested in - it is rather useful to get this function
	before the construction really gets going.

	(gdb) print &mnRefCnt
	$1 = (int *) 0x12345678
	(gdb) watch *$1

	This creates a hardware watch-point so whenever the ref-count
	changes you get a trace.

	(gdb) set pagination off
	(gdb) continue

	<it will break shortly afterwards>

	(gdb) commands
	backtrace 10
	(gdb) continue

	This will then produce a log of all stack frames that maniplate the
'mnRefCnt' - in my case this produced 2000+ of them or so ;-)

	It is probable that you want a dbgutil build - or at least one with
no-optimization too.

	When you hit your crash quit gdb.

	You should have a nice gdb-log.txt now.


	$ cat gdb-log.txt |
Problematic ref-count mis-matches on VclPtr instances with values:
this=0x7fffd7391148     -1
this=0x7fffffff6e70     -1
this=0x7fffffffc5b0     1
Search for the above pointer values in your log.

	Then look for the above ptr values in your log eg.

Old value = 22
New value = 21
0x00007ffff2af3647 in OutputDevice::release (this=0x2252e10) at /data/opt/libreoffice/master/include/vcl/outdev.hxx:284
284	        if (!--mnRefCnt)
#0  0x00007ffff2af3647 in OutputDevice::release (this=0x2252e10) at /data/opt/libreoffice/master/include/vcl/outdev.hxx:284

#1  0x00007ffff2baa6bb in rtl::Reference<SvTreeListBox>::~Reference (this=0x7fffd7391148, __in_chrg=<optimized out>) at /data/opt/libreoffice/master/include/rtl/ref.hxx:81
#2  0x00007ffff2ba9eca in VclPtr<SvTreeListBox>::~VclPtr (this=0x7fffd7391148, __in_chrg=<optimized out>) at /data/opt/libreoffice/master/include/vcl/vclptr.hxx:83
*******                                                        ^^^^^^^^^^^^^^ matching mis-counted VclPtr 'this' 
#3  0x00007ffff2bdf75e in SvLBoxDDInfo::~SvLBoxDDInfo (this=0x7fffd7391140, __in_chrg=<optimized out>) at /data/opt/libreoffice/master/include/svtools/treelistbox.hxx:820
#4  0x00007ffff2bd4fdd in SvTreeListBox::ExecuteDrop (this=0x222eda0, rEvt=..., pSourceView=0x2252e10) at /data/opt/libreoffice/master/svtools/source/contnr/treelistbox.cxx:1282

	And this of course took me straight to the SvLBoxDDInfo code.

	Phew ;-)

	I hope that's never useful for someone else =)



 michael.meeks at  <><, Pseudo Engineer, itinerant idiot
-------------- next part --------------
A non-text attachment was scrubbed...
Type: application/x-perl
Size: 1334 bytes
Desc: not available
URL: <>

More information about the LibreOffice mailing list