Important: Please read the Qt Code of Conduct - https://forum.qt.io/topic/113070/qt-code-of-conduct

How to debug PySide2 silent crashes?



  • Hello,

    My specifications are:

    Windows 10
    Python==3.6.8
    PySide2==5.15.2

    I have a pretty big closed source application, so it's really hard to create a minimal example. But the general concept is a nodeeditor. I'm trying at the moment to pinpoint the real issue. My application uses QGraphicsView, QGraphicsScene, QGraphicsItem, QGraphicsProxyWidget etc. Crash occasionally happens when i move the mouse around.

    To pinpoint the issue, I have wrapped every function in my application for tracing, used "faultHandler", used sys.settrace and crash seems to happens inside event loop. Only error i get is:

    Windows fatal exception: access violation
    

    The error above is thrown on the line below:

    sys.exit(app.exec_())
    

    I've been reading through the internet for two weeks, tried many things, could not find a solution/practice. I know what i provide is near to nothing but Is there a better way to debug my application?



  • @deahran
    Does your Python debugger's stack trace show anything useful for where in sys.exit(app.exec_()) it crashed?



  • @JonB , thank you for responding. I'm using Windows Command Prompt with the command below, since most IDE's monkey patch sys.traceback:

    python -m -q -X faulthandler main.py
    

    Sometimes application crashes with a traceback with same Windows Fatal Exception, but these are pretty random and inconsistent, sometimes happens inside native Python modules, sometimes in requests.send etc. I believe they are caused by interruption when main thread dies.



  • @deahran
    I saw from some other post of yours that you seem to be using PyCharm. I would have run your program in that debugger and examined the stack trace when it "crashes" to see if there is anything useful.



  • @deahran It's kind of tricky to track errors when the program is called main.py.

    Do you have something like this to start your code?:

    class MainWindow(QMainWindow):
        def __init__(self, parent=None):
        QMainWindow.__init__(self, parent)
    .
    .
    .
    
    if __name__ == '__main__':
        app = QApplication(sys.argv)
        mainApp = MainWindow()
        mainApp.show()
                    
        sys.exit(app.exec_())
    


  • @JonB

    Sadly, if i run the application in PyCharm debug mode and the application freezes/crashes, somehow PyCharm can't reach any frame due to internal C errors, which is only shown by faulthandler module, The traceback from faulthandler always and only pinpoints to sys.exit(app.exec_()), where the event loop starts.

    I have been expecting a memory leak, invalid shiboken2 wrappers inside events, infinite looping and could not find any.

    The one issue i was able to pinpoint was if i were to use QWidget.setStyleSheet("...") frequently in a mouseMoveEvent for a QWidget inside QGraphicsScene, it also causes a a lot of silent crashes. Removing that line improved the issue a lot.

    @Shawn-Driscoll

    Thank you for responding. I would love to provide a minimal example. But i can not since the application is closed source and pretty big. I'm preparing a one right now but will take quite some time.



  • @deahran I usually add temporary print functions to my code so that I can see in a terminal console where my program is going and how often. Something like:

    def calc_bitcoin(amount_sold):
        print('Before.....')
        value = amount_sold / 0.0
        print('After!!! Did I make it to this point or not??????')
    


  • @Shawn-Driscoll i like to hodl :). Jokes aside, i've tried that approach too. Crash seems to happen right after QGraphicsScene.drawBackGround() , QGraphicsView.mouseMoveEvent() or other keyboard-mouse events finish returning. I also examined the outer frame with the function below. When these paint or event methods are called, the outer frame points to my main.py.

    import sys
    from itertools import count
    
    def stack_size2a(size=2):
        """Get stack size for caller's frame.
        """
        frame = sys._getframe(size)
    
        for size in count(size):
            frame = frame.f_back
            if not frame:
                return size
    

    Also my drawBackGround function , i always see the function return part before crash:

    class MyScene(QGraphicsScene):
    .
    .
    .
      def drawBackground(self, painter, rect):
           if DEBUG: print("graphics_scene.paint -> entering")
           super().drawBackground(painter, rect)
    
           if DEBUG: print("graphics_scene.paint -> called super")
           # here we create our grid
           left = int(math.floor(rect.left()))
           right = int(math.ceil(rect.right()))
           top = int(math.floor(rect.top()))
           bottom = int(math.ceil(rect.bottom()))
    
           first_left = left - (left % self.grid_size)
           first_top = top - (top % self.grid_size)
    
           # all lines to be drawn
           lines_light, lines_dark = [], []
           for x in range(first_left, right, self.grid_size):
               if (x % (self.grid_size*self.gridSquares) != 0): lines_light.append(QLine(x, top, x, bottom))
               else: lines_dark.append(QLine(x, top, x, bottom))
    
           for y in range(first_top, bottom, self.grid_size):
               if (y % (self.grid_size*self.gridSquares) != 0): lines_light.append(QLine(left, y, right, y))
               else: lines_dark.append(QLine(left, y, right, y))
    
           if DEBUG: print("graphics_scene.paint -> lines light:", lines_light)
           if DEBUG: print("graphics_scene.paint -> lines dark:", lines_dark)
           
           # draw the lines
           painter.setPen(self._pen_yellow) #  self._pen_yellow = QPen("yellow")
           painter.drawLines(lines_light)
    
           painter.setPen(self._pen_black) #  self._pen_black = QPen("black")
           painter.drawLines(lines_dark)
           if DEBUG: print("graphics_scene.paint -> lines drawn, function return.")
    


  • @deahran
    If you think something in your drawBackground() code is causing the crash, keep commenting out lines till it goes away! Or not, if you get down to nothing but calling the base implementation. In which case it's something else!

    I know it's laborious/not easy, but really if you are getting a crash and debugger/print statements aren't helping that's what to do....

    How does:

    def drawBackground(self, painter, rect):
        pass
    

    behave?!

    What about with only super().drawBackground(painter, rect) in it? What about if you comment out the # draw the lines section at the bottom? What about if you remove your whole def drawBackground() override?



  • First of all, even though even though i'm not providing a source code or anything, i do really appreciate the help and welcome here!

    @JonB I'll try those (and for other methods also), i'll post an update if i come across anything.



  • I would like to provide update on my situation. Problem seems to be resolved. I'm not %100 percent sure since it's hard to reproduce the crash.

    I used a JIT debugger (IDA) to see what happens during crash, which showed me errors were happening because of a segmentation error (SIGSEGV) inside Qt5Widgets.dll->...->QGraphicsItem::topLevelItem->... . Which implies Qt is trying to read some value by pointer from memory, but can not. The changes i made:

    • There were some QGrahpicsItems with paint() method drawing outside of boundingRect(). Made sure all painting events stays inside boundingRect().
    • Made sure before changes to boundingRect(), prepareGeometryChange() was called (QGraphicsItem, QGraphicsPathItem)

    Special thanks to all who replied.


Log in to reply