jchanvfx / NodeGraphQt

Node graph framework that can be re-implemented into applications that supports PySide2
http://chantonic.com/NodeGraphQt/
MIT License
1.26k stars 256 forks source link

Advice on debugging SegFault 11 on all exit? #177

Closed piiq closed 3 years ago

piiq commented 3 years ago

Hi! First of all thank you all for the wonderful tool. It's really nice and looks like it can open lots of opportunities for me to dive into Qt development.

I would like to ask for advice on debug the following behaviour:

When I run the examples (math nodes and auto nodes) upon exit I see a SegFault 11 or a Bus error 10.

└─▪ python example_math_nodes.py
Cannot import "Qt.py" module falling back on "NodeGraphQt.vendor.Qt (1.2.0)"
Segmentation fault: 11

I suppose this relates to how the app quits, but I after some googling I still don't understand how to debug it. I would appreciate any advice regarding the debugging process.

P.S. I'm running python 3.8 on macOS Catalina and I'm new to Qt.

piiq commented 3 years ago

A small update on debugging this issue. It appears that the minimum action required to make the segfault go away is pressing "tab" in the node editor to call the node library menu. image

The issue may be related to the node registration or the menu itself. When I ran the app through gdb the segfault is accompanied with the following message

Thread 1 "python3" received signal SIGILL, Illegal instruction.
0x00007f3af6e30370 in QListData::shared_null ()
   from /usr/local/lib/python3.6/dist-packages/PySide2/Qt/lib/libQt5Core.so.5
piiq commented 3 years ago

So the steps required to reproduce the SegFault are:

  1. Launch math nodes or auto nodes example
  2. Quit without pressing the tab key
  3. Segmentation fault 11

Tested on macOS Catalina and Ubuntu 18


After further debugging it seems that in order for the app to exit normally the build_menu_tree() function in TabSearchMenuWidget is required to be called after the nodes are registered in the system on startup.

Since build_menu_tree is called from set_nodes() that ends up with a self._show() if I try to call everything that calls set_nodes() during startup I end up with the nodes menu on the screen before the viewer is displayed.

My workaround is to:

  1. In tab_search.py create a function without the _show for loading the nodes

    def set_nodes(self, node_dict=None):
        self.load_nodes()
        self._show()
    
    def load_nodes(self, node_dict=None):
        if not self._node_dict or self.rebuild:
            self._node_dict.clear()
            self._clear_actions()
            self._set_menu_visible(False)
            [self.removeAction(menu.menuAction()) for menu in self._menus.values()]
            self._actions.clear()
            self._menus.clear()
            for name, node_types in node_dict.items():
                if len(node_types) == 1:
                    self._node_dict[name] = node_types[0]
                    continue
                for node_id in node_types:
                    self._node_dict['{} ({})'.format(name, node_id)] = node_id
            self.build_menu_tree()
            self.rebuild = False
  2. In viewer.py create a function that calls preparation of the tab_search_widget

    def tab_search_load_nodes(self, nodes: dict = {}):
        self._search_widget.load_nodes(nodes)
    
    def tab_search_set_nodes(self, nodes):
        self._search_widget.set_nodes(nodes)
  3. And during the initialization of the NodeGraph, after the registration of the nodes I call

    graph._viewer.tab_search_load_nodes(graph._node_factory.names)

    to trigger all the events that lead to the successful execution of build_menu_tree()

aboellinger commented 3 years ago

Might be related to this PR I just submitted: #178

piiq commented 3 years ago

Hey, thanks @aboellinger ! It worked for me just fine