PDA

View Full Version : Occasional crash related to QGraphicsItem::parentItem



mickybadia
10th August 2015, 08:39
Hello experts,

I have registered here for help because I am having a SEGFAULT crash problem that is difficult for me to track down as I cannot reproduce it on demand, is included in quite a large project and takes normally over two hours only to happen randomly. For the record, I am using PyQt5 but I do not think it actually makes a difference. The only clue I have is from the wrapped C++:


Program received signal SIGSEGV, Segmentation fault. 0x00007ffff5f65554 in QGraphicsItem::parentItem
(this=this@entry=0x7ffff79fe5d0 <type_call>) at graphicsview/qgraphicsitem.cpp:1550
1550 return d_ptr->parent;

The full backtrace is here (http://mickybadia.free.fr/misc/files/Qt-backTrace).The top few lines are:


#0 0x00007ffff5f65554 in QGraphicsItem::parentItem (this=this@entry=0x7ffff79fe5d0 <type_call>)
at graphicsview/qgraphicsitem.cpp:1550
#1 0x00007ffff5f6557b in QGraphicsItem::topLevelItem (this=this@entry=0x555555e06350) at graphicsview/qgraphicsitem.cpp:1563
#2 0x00007ffff5fb44ee in QGraphicsSceneFindItemBspTreeVisitor::visit (this=0x555557d853e0, items=0x555557f72f48)
at graphicsview/qgraphicsscene_bsp.cpp:72

My first thought was that I was looking up a parentItem while it had already been deleted, but can an item deletion really trigger while painting some of its children?!

I have thought of reworking the whole code never to use that method (not that it is inherently buggy but I would learn something if the problem happens again... or be frustratingly happy if it does not), but my analysis of this today---which I find puzzling---is not really that the parentItem is NULL, but that an internal pointer in the parentItem method's implemention is NULL and dereferenced, and I have no clue where that can come from.

Would somebody have something helpful in mind?
Thank you for your time.

d_stranz
11th August 2015, 00:51
A SEGFAULT doesn't mean a NULL pointer (and I don't see anything that indicates that in your dump), but can occur any time a pointer points to an invalid memory location. This almost invariably occurs for one of several reasons:


The pointer has not been initialized and points into limbo
The pointer has been initialized, but the value has become corrupted
The pointer has been deleted


The second case is almost always the result of a memory overrun, where something gets written into a data structure that is too big for whatever location it is being written to and ends up writing garbage into whatever memory follows. This can corrupt the heap, or for stack-allocated memory, the stack itself. Usually the place indicated by the SEGFAULT is a red herring; it just happens to be the place where corruption finally caused something bad to happen.

mickybadia
13th August 2015, 13:09
Hmm. Thank you for your expert answer. But the crash happens inside a parentItem, when dereferencing a pointer which I did not create so it is difficult to know what could be wrong. I thought of a deleted parentItem but can I not assume that graphicsitem comes with deletion of its children, hence no risk of parentItem failing when called from a child?

mickybadia
13th August 2015, 23:08
Sorry: "can I not assume that graphicsitem DELETION comes with deletion of its children...?"

d_stranz
13th August 2015, 23:09
Like I said, the crash you are seeing is a red herring, i.e. it is the immediate cause of the crash, but it isn't what sets in motion the events leading up to the crash. Something else has almost certainly corrupted the heap or stack prior to where the program crashes. You can sometimes verify this by rearranging your code to put variables, etc. in a different order and observe if the crash occurs in the same place. If it still occurs in the same place, then look for a dangling reference to a pointer - something that is referring to a pointer, and tries to access it after the object has been deleted. If the location of the crash moves as you rearrange, then look for something that is corrupting the heap or stack - typically a memory overrun.

I don't know what your code does, whether it is all Python or a mix of Python and C++. If it is the latter, then look to the C++ first as the source of the bug.

As to your deletion question:

QGraphicsItem::~QGraphicsItem()


QGraphicsItem::~QGraphicsItem()
Destroys the QGraphicsItem and all its children. If this item is currently associated with a scene, the item will be removed from the scene before it is deleted.

mickybadia
16th November 2015, 23:13
Hello,
After a while hunting, I am still having this crash. I have been using "logging" to trace execution of the program, and now feel like asking you a question.

The basic behaviour of the program is:
- a regular timer signal triggers a function updating Python objects (Radar.scan method updating instances of Aircraft class)
- a QGraphicsScene has a few QGraphicsItems drawn, each sourcing geometry and text info from a contained Aircraft
- drawn items sometimes appear or disappear
- a wheel event allows to zoom the scene in/out

Tracing the start and end of every paint method of every item, every zoom signal and associated slot function, as well as every thread, I can positively say the crash ALWAYS occurs OUTSIDE of any of those. So on first thought it seems to be happening between the programmed actions... when none of my code is being read! In other words, when the execution is left to the Qt system to do whatever it needs to do.

Besides, the backtraces (mentioned earlier in this thread) always locates the crash inside a QGraphicsItem's internal parentItem method, and it happens a lot more often on zooming (after all my functions have returned and before others start), i.e. when supposedly redrawing would be needed. So before accusing Qt itself, I am thinking the problem is when Qt is computing stuff between my lines for the QGraphicsScene, and I should focus the boundingRect methods, which I have not yet traced. Could it be that bad values there would lead to a crash?! Reading this (http://www.qtcentre.org/archive/index.php/t-33730.html) it seems so (poster reports on "dereferencing a pointer to a deleted item"), but the doc clearly says boundingRect should return an estimate of the area painted, which makes it sound unlikely that bad boundingRects could lead to fatal crashes.

Of course I will be trying, but it only happens after long sessions and no known scenario reproduces it, so if you say no I would rather invest my debugging inspiration on other things.

Thank you for reading.

mickybadia
26th November 2015, 11:38
Well, the issue seems to be solved so in the end I think that WAS it. No bad memory management, just missing calls to QGraphicsItem.prepareGeometryChange. Good to know and to tell people like who would not have expected such a behaviour, but this is a CAUSE OF CRASH so it is important to get it right.

My dicipline now: never make boundingRect dependant on values that can change, unless they are changed by another method of the item that calls self.prepareGeometryChange.

d_stranz
27th November 2015, 20:57
Glad you found it. Most commonly QGraphicsItem instances are created with a fixed size that doesn't change during execution, so boundingRect() always returns the same size (in the object's own internal coordinate system, by the way). But, from QGraphicsItem::boundingRect() docs:


If you want to change the item's bounding rectangle, you must first call prepareGeometryChange(). This notifies the scene of the imminent change, so that it can update its item geometry index; otherwise, the scene will be unaware of the item's new geometry, and the results are undefined (typically, rendering artifacts are left within the view).

or, as you have found, the program crashes.