示例#1
0
文件: profiler.py 项目: slac-lcls/ami
class Profiler(QtCore.QObject):
    def __init__(self,
                 broker_addr="",
                 profile_addr="",
                 graph_name="graph",
                 loop=None):
        super().__init__()

        if loop is None:
            self.app = QtGui.QApplication([])
            loop = QEventLoop(self.app)

        asyncio.set_event_loop(loop)

        self.ctx = zmq.asyncio.Context()

        if broker_addr:
            self.broker = self.ctx.socket(zmq.SUB)
            self.broker.setsockopt_string(zmq.SUBSCRIBE, 'profiler')
            self.broker.connect(broker_addr)
        else:
            self.broker = None

        self.graph_name = graph_name
        self.profile_addr = profile_addr
        self.profile = self.ctx.socket(zmq.SUB)
        self.profile.setsockopt_string(zmq.SUBSCRIBE, self.graph_name)
        self.task = None

        self.deserializer = Deserializer()
        self.current_version = 0
        self.metadata = {}  # {version : metadata}
        self.parents = set()

        self.heartbeat_data = {}

        self.widget = QtWidgets.QWidget()
        self.layout = QtGui.QGridLayout(self.widget)
        self.widget.setLayout(self.layout)

        self.enabled_nodes = {}
        self.trace_layout = QtGui.QFormLayout(self.widget)
        hbox = QtWidgets.QHBoxLayout(self.widget)
        selectAll = QtWidgets.QPushButton("Select All", self.widget)
        selectAll.clicked.connect(self.selectAll)
        unselectAll = QtWidgets.QPushButton("Unselect All", self.widget)
        unselectAll.clicked.connect(self.unselectAll)
        hbox.addWidget(selectAll)
        hbox.addWidget(unselectAll)
        self.trace_layout.addRow(hbox)
        self.trace_group = WidgetGroup()
        self.trace_group.sigChanged.connect(self.state_changed)
        self.layout.addLayout(self.trace_layout, 0, 0, -1, 1)

        self.graphicsLayoutWidget = pg.GraphicsLayoutWidget()
        self.layout.addWidget(self.graphicsLayoutWidget, 0, 1, -1, -1)

        self.time_per_heartbeat = self.graphicsLayoutWidget.addPlot(row=0,
                                                                    col=0)
        self.time_per_heartbeat.showGrid(True, True)
        self.time_per_heartbeat.setLabel('bottom', "Heartbeat")
        self.time_per_heartbeat.setLabel('left', "Time (Sec)")
        self.time_per_heartbeat_data = collections.defaultdict(
            lambda: np.array([np.nan] * 100))
        self.time_per_heartbeat_traces = {}
        self.time_per_heartbeat_legend = self.time_per_heartbeat.addLegend()

        self.heartbeats_per_second = self.graphicsLayoutWidget.addPlot(row=0,
                                                                       col=1)
        self.heartbeats_per_second.showGrid(True, True)
        self.heartbeats_per_second.setLabel('bottom', "Heartbeat")
        self.heartbeats_per_second.setLabel('left', "Heartbeats/Second")
        self.heartbeats_per_second_data = np.array([np.nan] * 100)
        self.heartbeats_per_second_trace = None

        self.percent_per_heartbeat = self.graphicsLayoutWidget.addPlot(
            row=1, col=0, rowspan=1, colspan=2)
        self.percent_per_heartbeat.showGrid(True, True)
        self.percent_per_heartbeat_trace = None

        self.last_updated = pg.LabelItem(
            parent=self.time_per_heartbeat.getViewBox())
        self.total_heartbeat_time = pg.LabelItem(
            parent=self.percent_per_heartbeat.getViewBox())
        self.heartbeat_per_second = pg.LabelItem(
            parent=self.heartbeats_per_second.getViewBox())

        self.win = ProfilerWindow(self)
        self.win.setWindowTitle('Profiler')
        self.win.setCentralWidget(self.widget)
        self.win.show()

        with loop:
            loop.run_until_complete(
                asyncio.gather(self.process_broker_message(), self.monitor()))

    def selectAll(self, clicked):
        for name, btn in self.enabled_nodes.items():
            btn.setCheckState(QtCore.Qt.Checked)

    def unselectAll(self, clicked):
        for name, btn in self.enabled_nodes.items():
            btn.setCheckState(QtCore.Qt.Unchecked)

    def state_changed(self, *args, **kwargs):
        node, checked = args
        if node not in self.time_per_heartbeat_traces:
            return

        trace = self.time_per_heartbeat_traces[node]
        if checked:
            trace.show()
            self.time_per_heartbeat_legend.addItem(trace, node)
        else:
            trace.hide()
            self.time_per_heartbeat_legend.removeItem(trace)

    async def monitor(self):

        while self.task is None:
            await asyncio.sleep(0.1)

        try:
            await self.task
        except asyncio.CancelledError:
            pass

    async def process_broker_message(self):
        if self.broker is None:
            self.connect()
            return

        while True:
            await self.broker.recv_string()
            msg = await self.broker.recv_pyobj()
            self.graph_name = msg.name
            self.connect()
            self.win.show()

    def connect(self):
        if self.task is None:
            self.task = asyncio.ensure_future(self.process_profile_data())

    def cancel(self):
        self.task.cancel()
        self.task = None
        self.profile.disconnect(self.profile_addr)

    async def process_profile_data(self):
        self.profile.connect(self.profile_addr)

        while True:
            await self.profile.recv_string()
            name = await self.profile.recv_string()
            data_type = await self.profile.recv_string()
            data = await self.profile.recv_serialized(self.deserializer,
                                                      copy=False)

            if data_type == "profile":
                heartbeat = data['heartbeat']
                version = data['version']

                if heartbeat not in self.heartbeat_data:
                    if version not in self.metadata:
                        continue

                    metadata = self.metadata[version]
                    self.heartbeat_data[heartbeat] = HeartbeatData(
                        data['heartbeat'], metadata)
                heartbeat_data = self.heartbeat_data[heartbeat]

                if name.startswith('worker'):
                    heartbeat_data.add_worker_data(name, data['times'])
                elif name.startswith('localCollector'):
                    heartbeat_data.add_local_collector_data(
                        name, data['times'])
                elif name.startswith('globalCollector'):
                    heartbeat_data.add_global_collector_data(data['times'])

                    if version > self.current_version:
                        self.current_version = version

                        self.percent_per_heartbeat_data = collections.defaultdict(
                            lambda: 0)
                        if self.percent_per_heartbeat_trace:
                            self.percent_per_heartbeat.removeItem(
                                self.percent_per_heartbeat_trace)
                            self.percent_per_heartbeat_trace = None

                        parents = set()

                        for k, v in self.metadata[version].items():
                            parent = v['parent']
                            parents.add(parent)
                            if parent not in self.enabled_nodes:
                                widget = QtWidgets.QCheckBox(self.widget)
                                widget.node = parent
                                widget.setCheckState(QtCore.Qt.Checked)
                                self.enabled_nodes[parent] = widget
                                self.trace_layout.addRow(parent, widget)

                        deleted_nodes = self.parents.difference(parents)
                        for node in deleted_nodes:
                            self.trace_layout.removeRow(
                                self.enabled_nodes[node])
                            del self.enabled_nodes[node]
                            trace = self.time_per_heartbeat_traces[node]
                            self.time_per_heartbeat.removeItem(trace)
                            self.time_per_heartbeat_legend.removeItem(trace)
                            del self.time_per_heartbeat_traces[node]
                            del self.time_per_heartbeat_data[node]

                        self.parents = parents

                        self.trace_group.sigChanged.disconnect(
                            self.state_changed)
                        self.trace_group = WidgetGroup()
                        self.trace_group.sigChanged.connect(self.state_changed)
                        for node, ctrl in self.enabled_nodes.items():
                            self.trace_group.addWidget(ctrl, node)

                    self.time_per_heartbeat_data["heartbeat"][-1] = heartbeat
                    self.time_per_heartbeat_data["heartbeat"] = np.roll(
                        self.time_per_heartbeat_data["heartbeat"], -1)

                    total = 1
                    for node, time in heartbeat_data.total_time_per_heartbeat.items(
                    ):
                        self.time_per_heartbeat_data[node][-1] = time
                        self.time_per_heartbeat_data[node] = np.roll(
                            self.time_per_heartbeat_data[node], -1)
                        self.percent_per_heartbeat_data[
                            node] = time / heartbeat_data.total_heartbeat_time
                        total -= time / heartbeat_data.total_heartbeat_time

                    self.percent_per_heartbeat_data['Transfer'] = total

                    i = 0
                    for node, times in self.time_per_heartbeat_data.items():
                        if node == "heartbeat":
                            continue

                        if node not in self.time_per_heartbeat_traces:
                            symbol, color = symbols_colors[i]
                            self.time_per_heartbeat_traces[
                                node] = self.time_per_heartbeat.plot(
                                    x=self.
                                    time_per_heartbeat_data["heartbeat"],
                                    y=times,
                                    name=node,
                                    symbol=symbol,
                                    symbolBrush=color)
                        else:
                            self.time_per_heartbeat_traces[node].setData(
                                x=self.time_per_heartbeat_data["heartbeat"],
                                y=times)
                        i += 1

                    nodes, times = zip(
                        *self.percent_per_heartbeat_data.items())

                    if self.percent_per_heartbeat_trace is None:
                        x = np.arange(len(nodes))
                        self.percent_per_heartbeat_trace = pg.BarGraphItem(
                            x=x, height=times, width=1, brush='b')
                        self.percent_per_heartbeat.addItem(
                            self.percent_per_heartbeat_trace)
                        xticks = dict(zip(x, nodes))
                        ax = self.percent_per_heartbeat.getAxis('bottom')
                        ax.setTicks([xticks.items()])
                    else:
                        self.percent_per_heartbeat_trace.setOpts(height=times)

                    self.heartbeats_per_second_data[
                        -1] = 1 / heartbeat_data.total_heartbeat_time
                    self.heartbeats_per_second_data = np.roll(
                        self.heartbeats_per_second_data, -1)
                    if self.heartbeats_per_second_trace is None:
                        symbol, color = symbols_colors[0]
                        self.heartbeats_per_second_trace = self.heartbeats_per_second.plot(
                            x=self.time_per_heartbeat_data["heartbeat"],
                            y=self.heartbeats_per_second_data,
                            symbol=symbol,
                            symbolBrush=color)
                    else:
                        self.heartbeats_per_second_trace.setData(
                            x=self.time_per_heartbeat_data["heartbeat"],
                            y=self.heartbeats_per_second_data)

                    now = dt.datetime.now()
                    now = now.strftime("%H:%M:%S")
                    last_updated = f"Last Updated: {now}"
                    self.last_updated.setText(last_updated)
                    text = f"Seconds/Heartbeat: {heartbeat_data.total_heartbeat_time:.6f}<br/>Heartbeat: {heartbeat}"
                    self.total_heartbeat_time.setText(text)
                    text = f"Heartbeats/Second: {1/heartbeat_data.total_heartbeat_time:.0f}<br/>Heartbeat: {heartbeat}"
                    self.heartbeat_per_second.setText(text)

                    del self.heartbeat_data[heartbeat]

            elif data_type == "metadata":
                graph_name = data['graph']
                version = data['version']
                logger.info("Received metadata for %s v%d", graph_name,
                            version)

                self.metadata[version] = data['metadata']