diff --git a/src/controllers/debugcontroller.py b/src/controllers/debugcontroller.py index 982782e..2fbf69b 100644 --- a/src/controllers/debugcontroller.py +++ b/src/controllers/debugcontroller.py @@ -28,6 +28,7 @@ from helpers.gdboutput import GdbOutput import logging from helpers.configstore import ConfigSet, ConfigItem +from helpers.gdbinit import GDBInit class DebugConfig(ConfigSet): @@ -47,13 +48,22 @@ def __init__(self, distributedObjects): self.distributedObjects = distributedObjects self.connector = self.distributedObjects.gdb_connector + self.gdbinit = self.distributedObjects.gdb_init self.signalProxy = self.distributedObjects.signalProxy self.executableName = None self.lastCmdWasStep = False self.ptyhandler.start() + + GDBInit.writeFile(self.gdbinit.getPath(),self.gdbinit.getFileName()) + self.connector.start() + + self.connector.initPrettyPrinter(self.gdbinit.getPath() + self.gdbinit.getFileName()) + self.connector.startPrettyPrinting() + + self.toggleBeautify = True self.connector.reader.asyncRecordReceived.connect(self.handleAsyncRecord, Qt.QueuedConnection) @@ -131,7 +141,18 @@ def finish(self): def until(self, file_, line): self.connector.until(file_, line) self.lastCmdWasStep = False - + + def beautify(self): + if self.toggleBeautify: + self.connector.disablePrettyPrinter() + else: + self.connector.enablePrettyPrinter() + + self.toggleBeautify = not self.toggleBeautify + self.distributedObjects.localsController.reloadLocals() + self.distributedObjects.watchController.clearModel() + self.distributedObjects.datagraphController.clearDataGraph() + def evaluateExpression(self, exp): if exp == "": return None diff --git a/src/controllers/localscontroller.py b/src/controllers/localscontroller.py index 82145af..a843828 100644 --- a/src/controllers/localscontroller.py +++ b/src/controllers/localscontroller.py @@ -38,3 +38,10 @@ def getLocals(self): for vw in self.variableList.list: self.add(vw) + + def reloadLocals(self): + self.clear() + self.variableList.reloadLocals() + + for vw in self.variableList.list: + self.add(vw) diff --git a/src/controllers/tooltipcontroller.py b/src/controllers/tooltipcontroller.py index e59872b..59129a7 100644 --- a/src/controllers/tooltipcontroller.py +++ b/src/controllers/tooltipcontroller.py @@ -46,3 +46,4 @@ def showToolTip(self, exp, pos, parent): def hideToolTip(self): self.view.hideLater() + \ No newline at end of file diff --git a/src/controllers/treeitemcontroller.py b/src/controllers/treeitemcontroller.py index 4d60326..e38e3de 100644 --- a/src/controllers/treeitemcontroller.py +++ b/src/controllers/treeitemcontroller.py @@ -90,12 +90,14 @@ def getChildren(self, factory): Get Children from VariableList for StructVariable @param factory derived from VarWrapperFactory, factory to look in VariableList for children """ - if len(self.childItems) == 0: + if (len(self.childItems) == 0) or (self._v.numChild != len(self.childItems)): + self.removeChildren() for child in self._v.childs: vwChild = child.makeWrapper(factory) vwChild.parent = self - vwChild.dataChanged.connect(vwChild.hasChanged) + vwChild.dataChanged.connect(vwChild.hasChanged) self.addChild(vwChild) + self._v.numChild = len(self.childItems) return self.childItems diff --git a/src/controllers/watchcontroller.py b/src/controllers/watchcontroller.py index 4ff518a..da5c846 100644 --- a/src/controllers/watchcontroller.py +++ b/src/controllers/watchcontroller.py @@ -80,4 +80,8 @@ def loadSession(self, xmlHandler): childnodes = watchParent.childNodes() for i in range(childnodes.size()): attr = xmlHandler.getAttributes(childnodes.at(i)) - self.addWatch(attr["exp"]) + self.addWatch(attr["exp"]) + + def clearModel(self): + self.model.clear() + \ No newline at end of file diff --git a/src/datagraph/datagraphcontroller.py b/src/datagraph/datagraphcontroller.py index 89eaade..df928aa 100644 --- a/src/datagraph/datagraphcontroller.py +++ b/src/datagraph/datagraphcontroller.py @@ -67,6 +67,7 @@ def __init__(self, distributedObjects): ## @var vwFactory # datagraph.datagraphvwfactory.DataGraphVWFactory, private, self-created DataGraphVWFactory self.vwFactory = DataGraphVWFactory(self.distributedObjects) + ## @var variableList # variables.variablelist.VariableList, private, self-created VariableList self.variableList = VariableList(self.vwFactory, self.distributedObjects) @@ -101,23 +102,27 @@ def addVar(self, varWrapper, xPos=0, yPos=0, addVarToList=True): @param yPos Integer, the Y-Coordinate of the Position where to add the VariableWrapper @param addVarToList Boolean, tells if varWrapper should be added to the VariableList too """ - varWrapper.createView() + self.addGraph(varWrapper, xPos, yPos) + if addVarToList: + self.variableList.addVar(varWrapper) + + def addGraph(self, wrapper, xPos=0, yPos=0): + wrapper.createView() try: - varWrapper.getView().render() + wrapper.getView().render() except: from mako import exceptions logging.error("Caught exception while rendering template: %s", exceptions.text_error_template().render()) - varWrapper.setXPos(xPos) - varWrapper.setYPos(yPos) - self.data_graph_view.addItem(varWrapper.getView()) - if addVarToList: - self.variableList.addVar(varWrapper) + wrapper.setXPos(xPos) + wrapper.setYPos(yPos) + self.data_graph_view.addItem(wrapper.getView()) def removeVar(self, varWrapper): """ removes the given varWrapper from the DataGraphView and the PointerList @param varWrapper variables.variablewrapper.VariableWrapper, the VariableWrapper to remove """ - self.variableList.removeVar(varWrapper) + if varWrapper in self.variableList: + self.variableList.removeVar(varWrapper) self.data_graph_view.removeItem(varWrapper.getView()) def addPointer(self, fromView, toView): diff --git a/src/datagraph/datagraphvw.py b/src/datagraph/datagraphvw.py index 9f56d74..227bb8a 100644 --- a/src/datagraph/datagraphvw.py +++ b/src/datagraph/datagraphvw.py @@ -127,6 +127,7 @@ def prepareContextMenu(self, menu): action.setChecked(self.vertical) def render(self, role, **kwargs): + self.varWrapper.getChildren() return HtmlTemplateHandler.render(self, role, vertical=self.vertical, **kwargs) @@ -204,7 +205,7 @@ def render(self, role, **kwargs): def setFilter(self, f): VariableWrapper.setFilter(self, f) self.setDirty(True) - + class ComplexDataGraphVW(DataGraphVW): def __init__(self, variable, distributedObjects, vwFactory, templateHandler): @@ -221,14 +222,13 @@ def __init__(self, variable, distributedObjects, vwFactory, templateHandler): def setOpen(self, open_): self.isOpen = open_ self.setDirty(True) - + def getChildren(self): """ returns list of children as DataGraphVWs; creates the wrappers if they haven't yet been @return list of datagraph.datagraphvw.DataGraphVW """ - if not self.childrenWrapper: - self.childrenWrapper = [] - for childVar in self.childs: - wrapper = childVar.makeWrapper(self.vwFactory) - wrapper.setExistingView(self.getView(), self) - self.childrenWrapper.append(wrapper) - return self.childrenWrapper + self.childrenWrapper = [] + for childVar in self.childs: + wrapper = childVar.makeWrapper(self.vwFactory) + wrapper.setExistingView(self.getView(), self) + self.childrenWrapper.append(wrapper) + return self.childrenWrapper \ No newline at end of file diff --git a/src/datagraph/svgimage.py b/src/datagraph/svgimage.py new file mode 100644 index 0000000..d9e7728 --- /dev/null +++ b/src/datagraph/svgimage.py @@ -0,0 +1,46 @@ +# ricodebug - A GDB frontend which focuses on visually supported +# debugging using data structure graphs and SystemC features. +# +# Copyright (C) 2011 The ricodebug project team at the +# Upper Austrian University Of Applied Sciences Hagenberg, +# Department Embedded Systems Design +# +# This file is part of ricodebug. +# +# ricodebug is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . +# +# For further information see . + +from PyQt4.QtCore import QObject, pyqtSignal + + +class SVGImage(QObject): + changed = pyqtSignal(str) + + def __init__(self, name, fileObject): + QObject.__init__(self) + self.name = name + self.fileObject = fileObject + self.imageContent = fileObject.read() + self.inScope = True + + def refresh(self): + self.fileObject.seek(0) + self.imageContent = self.fileObject.read() + + def die(self): + pass + + def __str__(self): + return self.name diff --git a/src/datagraph/svgvw.py b/src/datagraph/svgvw.py new file mode 100644 index 0000000..655c1f5 --- /dev/null +++ b/src/datagraph/svgvw.py @@ -0,0 +1,55 @@ +# ricodebug - A GDB frontend which focuses on visually supported +# debugging using data structure graphs and SystemC features. +# +# Copyright (C) 2011 The ricodebug project team at the +# Upper Austrian University Of Applied Sciences Hagenberg, +# Department Embedded Systems Design +# +# This file is part of ricodebug. +# +# ricodebug is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . +# +# For further information see . + +from .datagraphvw import HtmlTemplateHandler, DataGraphVW +from .htmlvariableview import HtmlVariableView + + +class SVGDataGraphVW(DataGraphVW): + """ Wrapper for SVG Images """ + + def __init__(self, image, distributedObjects): + """ Constructor + @param image SVG image to wrap with the new DataGraphVW + @param distributedObjects the DistributedObjects-Instance + """ + DataGraphVW.__init__(self, image, distributedObjects) + self.image = image + self.templateHandler = HtmlTemplateHandler(self, + self.distributedObjects, + "svgview.mako") + + def showContent(self): + self.image.refresh() + return self.image.imageContent + + def showName(self): + return str(self.image) + + def render(self, role, **kwargs): + return self.templateHandler.render(role) + + def createView(self): + self._view = HtmlVariableView(self, self.distributedObjects) + self.parentWrapper = self._view diff --git a/src/datagraph/templates/svgview.mako b/src/datagraph/templates/svgview.mako new file mode 100644 index 0000000..0f4de27 --- /dev/null +++ b/src/datagraph/templates/svgview.mako @@ -0,0 +1,2 @@ +${varWrapper.showName()} +${varWrapper.showContent()} diff --git a/src/helpers/actions.py b/src/helpers/actions.py index cc67518..03da96f 100644 --- a/src/helpers/actions.py +++ b/src/helpers/actions.py @@ -129,6 +129,12 @@ def __init__(self): "Del var from Watch", "+", "Remove selected variable from watchview-window") + ############################################### + ## miscellaneous + ############################################### + self.Beautify = self.__createAction(":/icons/images/beautify.png", + "Beautify", None, "Pretty Print of Objects") + def getAddToWatchAction(self, name, slot): a = self.createEx(name) a.setText("Add '%s' to watch window" % name) @@ -155,6 +161,15 @@ def getAddToTracepointAction(self, varname, tpname, slot): a.triggeredEx.connect(slot) return a + def getAddSVGToDatagraphAction(self, name, slot): + a = self.createEx(name) + a.setText(str(name)) + a.setIcon(QtGui.QIcon(":/icons/images/insert.png")) + a.setIconVisibleInMenu(True) + a.triggeredEx.connect(slot) + return a + + def createEx(self, parameter): return self.ActionEx(parameter, self) diff --git a/src/helpers/distributedobjects.py b/src/helpers/distributedobjects.py index 38b9452..168da01 100644 --- a/src/helpers/distributedobjects.py +++ b/src/helpers/distributedobjects.py @@ -53,7 +53,7 @@ from views.threadview import ThreadView from models.threadmodel import ThreadModel from views.mitraceview import MiTraceView - +from helpers.gdbinit import GDBInit class DistributedObjects: def __init__(self, mainwindow): @@ -61,19 +61,17 @@ def __init__(self, mainwindow): self.settings = QSettings("fh-hagenberg", "ricodebug") self.configStore = ConfigStore(self.settings) self.gdb_connector = GdbConnector() + self.gdb_init = GDBInit() self.actions = Actions() self.signalProxy = SignalProxy(self) self.sessionManager = SessionManager(self) - self.breakpointModel, _ = self.buildModelAndView(BreakpointModel, BreakpointView, "Breakpoints") - self.debugController = DebugController(self) self.variablePool = VariablePool(self) self.editorController = EditorController(self) self.toolTipController = ToolTipController(self, ToolTipView(self, self.editorController.editor_view)) self.filelistController = FileListController(self) self.stackController = StackController(self) - self.threadModel, _ = self.buildModelAndView(ThreadModel, ThreadView, "Threads") self.watchView = WatchView() diff --git a/src/helpers/gdbconnector.py b/src/helpers/gdbconnector.py index 8ffa454..708ce4d 100644 --- a/src/helpers/gdbconnector.py +++ b/src/helpers/gdbconnector.py @@ -25,6 +25,7 @@ import subprocess import signal import logging +import os from .gdbreader import GdbReader from .gdboutput import GdbOutput from PyQt4.QtCore import QObject, pyqtSignal @@ -41,7 +42,7 @@ def __init__(self): def start(self): try: - self.gdb = subprocess.Popen(['gdb', '-i', 'mi', '-q'], + self.gdb = subprocess.Popen(['gdb', '-i', 'mi', '-q', '-nx'], \ shell=False, stdin=subprocess.PIPE, stdout=subprocess.PIPE) except OSError as e: logging.critical("Could not start gdb. Error message: %s", e) @@ -79,6 +80,10 @@ def openFile(self, filename): self.executeAndRaiseIfFailed("-file-exec-and-symbols " + filename, "Could not open file!") + def startPrettyPrinting(self): + self.execute("-enable-pretty-printing", \ + "Enable MI PrettyPrint") + def getSources(self): res = self.executeAndRaiseIfFailed("-file-list-exec-source-files", "Could not get files.") @@ -268,8 +273,8 @@ def var_assign(self, exp, value): return self.execute("-var-assign \"" + exp + "\" " + value) def var_list_children(self, exp): - return self.execute("-var-list-children --all-values \"" + - str(exp) + "\"") + return self.execute("-var-list-children --all-values \"" + \ + str(exp) + "\" 0 100") def var_update(self, exp): return self.execute("-var-update --all-values \"" + exp + "\"") @@ -289,3 +294,13 @@ def threadInfo(self): def selectThread(self, id_): return self.executeAndRaiseIfFailed("-thread-select %s" % id_) + + def initPrettyPrinter(self,path): + command = "source" + path + self.executeAndRaiseIfFailed(command,"Can not load pretty printer module!") + + def enablePrettyPrinter(self): + return self.executeAndRaiseIfFailed("enable pretty-printer", "Enables Initialized Printers") + + def disablePrettyPrinter(self): + return self.executeAndRaiseIfFailed("disable pretty-printer", "Disables Initialized Printers") diff --git a/src/helpers/gdbinit.py b/src/helpers/gdbinit.py new file mode 100644 index 0000000..e2b03f7 --- /dev/null +++ b/src/helpers/gdbinit.py @@ -0,0 +1,62 @@ +# ricodebug - A GDB frontend which focuses on visually supported +# debugging using data structure graphs and SystemC features. +# +# Copyright (C) 2011 The ricodebug project team at the +# Upper Austrian University Of Applied Sciences Hagenberg, +# Department Embedded Systems Design +# +# This file is part of ricodebug. +# +# ricodebug is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . +# +# For further information see . + +from PyQt4.QtCore import QObject +import os + + +class GDBInit(QObject): + def __init__(self): + self.fileName = "load_pretty_printer" + filePath = repr(__file__) + self.path = filePath[1:filePath.find("helpers/gdbinit.py")] + "third_party/" + + @staticmethod + def writeFile(path,fileName): + init = path + fileName + + file_content = [] + file_content.append("python\n") + file_content.append("import sys\n") + file_content.append("path=\"" + path + "pretty_printer\"\n") + file_content.append("sys.path.insert(0, path)\n") + file_content.append("from libstdcxx.v6.printers import register_libstdcxx_printers\n") + file_content.append("register_libstdcxx_printers (None)\n") + file_content.append("end\n") + + if os.path.exists(init): + os.remove(init) + + init_script = open(init,'w') + + for i in file_content: + init_script.write(i) + + init_script.close() + + def getPath(self): + return self.path + + def getFileName(self): + return self.fileName \ No newline at end of file diff --git a/src/images/beautify.png b/src/images/beautify.png new file mode 100644 index 0000000..cb4a7f6 Binary files /dev/null and b/src/images/beautify.png differ diff --git a/src/plugins/SysCDiagram/SysCDiagramPlugin.py b/src/plugins/SysCDiagram/SysCDiagramPlugin.py new file mode 100644 index 0000000..edeb7c2 --- /dev/null +++ b/src/plugins/SysCDiagram/SysCDiagramPlugin.py @@ -0,0 +1,215 @@ +# ricodebug - A GDB frontend which focuses on visually supported +# debugging using data structure graphes and SystemC features. +# +# Copyright (C) 2011 The ricodebug project team at the +# Upper Austrian University Of Applied Sciences Hagenberg, +# Department Embedded Systems Design +# +# This file is part of ricodebug. +# +# ricodebug is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . +# +# For further information see . + +from datagraph.svgvw import SVGDataGraphVW +from datagraph.svgimage import SVGImage +from StringIO import StringIO +from pydot import Dot, Cluster, Node + +from helpers.tools import cpp2py + +from variables.varwrapperfactory import VarWrapperFactory +from variables.variablelist import VariableList + + +class SysCDiagramPlugin(): + + def initPlugin(self, signalproxy): + """Initialise the systemc block diagram plugin""" + + self.signalproxy = signalproxy + self.do = signalproxy.distributedObjects + + # systemc stuff and and pointer type strings needed for casting in gdb + # because systemc objects can be systemc modules, systemc ports, etc. + self.ctx = None + self.ctx_pointer = None + self.ctx_func = "sc_get_curr_simcontext()" + self.ctx_found = False + + self.ctx_type = "(sc_core::sc_simcontext*)" + self.port_type = "(sc_core::sc_port_base*)" + self.module_type = "(sc_core::sc_module*)" + self.object_type = "(sc_core::sc_object*)" + self.prim_channel_type = "(sc_core::sc_prim_channel*)" + + # dict with a string that represents the pointer as key and + # a nested dict with parent, children, etc as key-values + # dst_dict[ptr] = {"wrapper": vw, + # "name": None, + # "parent_ptr": None, + # "children": {}} + self.sysc_modules = {} + self.sysc_objects = {} + self.sysc_ports = {} + self.sysc_prim_channels = {} + + # because of how we built the interface for the tracepoints on the + # datagraph we first need to create an file obj. We choose StringIO + # because it uses a string as buffer and does not acces the filesystem + self._file_obj = StringIO() + self.image = SVGImage("SystemC Block Diagram", self._file_obj) + self.image_wrapper = SVGDataGraphVW(self.image, self.do) + + self.signalproxy.inferiorStoppedNormally.connect(self.update) + + # hook Datagraph variable wrapper into the datagraph controller + # after self.action.commit is called the image will be displayed at the + # datagraph + self.action = self.do.actions.\ + getAddSVGToDatagraphAction(self.image_wrapper, + self.do. + datagraphController.addVar) + + # pydot graph visualization library + self.block_diagram = Dot(graph_type='digraph') + self.block_diagram.set_graph_defaults(compound='true', + splines='ortho', + rankdir='LR') + self.block_diagram.set_node_defaults(shape='box') + + # Needed for creating the right variables and variable wrappers from + # the systemc pointers + self.vwFactory = VarWrapperFactory() + self.variableList = VariableList(self.vwFactory, self.do) + + def deInitPlugin(self): + pass + + def evaluateExp(self, exp): + return self.signalproxy.gdbEvaluateExpression(exp) + + def showDiagram(self): + self.action.commit() + + def update(self): + if not self.ctx_found and self.ctx is None: + self.__findSimContext() + if self.ctx is None: + return + else: + # don't try to analyze if elaboration is not done + if not cpp2py(self.ctx["m_elaboration_done"].value): + return + + # prepare for easy information collection + object_vec = self.ctx["m_child_objects"] + + # find all relevant information + self.__findSysCObjects(object_vec, self.object_type, self.sysc_objects) + + # if there are no objects to draw than skip the drawing part + # this might happen if you set the breakpoint before any objects are + # created. This is actually catched above, but there might also be a + # design with no objects. + if len(self.sysc_objects.keys()) == 0: + return + + clusters = {} + nodes = {} + + # build pydot hierachy and add all subgraphs and nodes to the main + # graph + self.__buildHierachy(self.sysc_objects, clusters, nodes) + for sptr in clusters: + self.block_diagram.add_subgraph(clusters[sptr]) + for sptr in nodes: + self.block_diagram.add_node(nodes[sptr]) + + self._file_obj.write(self.block_diagram.create_svg()) + self.signalproxy.inferiorStoppedNormally.disconnect(self.update) + self.showDiagram() + + def __buildHierachy(self, obj_dict, clusters, nodes): + """ Build Cluster and Node hierachy for pydot + """ + for ptr in obj_dict: + obj = obj_dict[ptr] + if ptr in (self.sysc_ports.keys()+self.sysc_prim_channels.keys()): + continue + + if len(obj["children"].keys()) == 0: + node = Node(obj["name"]) + nodes[ptr] = node + else: + clust = Cluster(obj["name"].replace(".", "_"), + color='red', + label=obj["name"]) + c_clusters = {} + c_nodes = {} + self.__buildHierachy(obj["children"], c_clusters, c_nodes) + for sptr in c_clusters: + clust.add_subgraph(c_clusters[sptr]) + for sptr in c_nodes: + clust.add_node(c_nodes[sptr]) + clusters[ptr] = clust + + def __findSysCObjects(self, obj_vec, obj_type, dst_dict): + """ Find sc_object from module, port and prim channel registry + """ + for i in obj_vec.childs: + ptr = i.value + var = "(*{}{})".format(obj_type, ptr) + vw = self.variableList.addVarByName(var) + dst_dict[ptr] = {"wrapper": vw, + "name": None, + "parent_ptr": None, + "children": {}} + + for member in vw.childs: + if member.exp == "m_name": + dst_dict[ptr]["name"] = member.value.strip('"') + + elif member.exp == "m_child_objects": + children = {} + self.__findSysCObjects(vw["m_child_objects"], + obj_type, + children) + dst_dict[ptr]["children"] = children + + elif member.exp == "m_parent": + dst_dict[ptr]["parent_ptr"] = member.value + + def __findSimContext(self): + """ Find systemc simulation context + """ + self.ctx_pointer = self.evaluateExp(self.ctx_func) + if self.ctx is None: + frame = 0 + depth = self.signalproxy.gdbGetStackDepth() + while (self.ctx_pointer is None) and frame <= depth: + frame += 1 + self.signalproxy.gdbSelectStackFrame(frame) + self.ctx_pointer = self.evaluateExp(self.ctx_func) + else: + if self.ctx_pointer is None: + self.ctx_found = False + return + else: + self.ctx_found = True + self.ctx = self.do.variablePool.getVar(self.ctx_func)["*"] + else: + self.ctx_found = True + self.ctx = self.do.variablePool.getVar(self.ctx_func)["*"] + diff --git a/src/plugins/SysCDiagram/__init__.py b/src/plugins/SysCDiagram/__init__.py new file mode 100644 index 0000000..bdc7acf --- /dev/null +++ b/src/plugins/SysCDiagram/__init__.py @@ -0,0 +1,27 @@ +# ricodebug - A GDB frontend which focuses on visually supported +# debugging using data structure graphes and SystemC features. +# +# Copyright (C) 2011 The ricodebug project team at the +# Upper Austrian University Of Applied Sciences Hagenberg, +# Department Embedded Systems Design +# +# This file is part of ricodebug. +# +# ricodebug is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . +# +# For further information see . + +#optional: define name of plugin, will be added to menu Help -> Plugins +#Pluginloader will call plugin like file if PluginName is not defined +PluginName = "SystemC Block Diagram" diff --git a/src/plugins/SysCSimCtx/SysCSimCtxPlugin.py b/src/plugins/SysCSimCtx/SysCSimCtxPlugin.py index 5a6984e..d0c6598 100644 --- a/src/plugins/SysCSimCtx/SysCSimCtxPlugin.py +++ b/src/plugins/SysCSimCtx/SysCSimCtxPlugin.py @@ -145,7 +145,8 @@ def _findSimVariables(self): self._setDeltaCycle(self._currDeltaCycles.value) if self._simContext["m_curr_proc_info"]["process_handle"]["*"] is not None: - self._currProcess = self._simContext["m_curr_proc_info"]["process_handle"]["*"]["m_name"]["_M_dataplus"]["_M_p"] + self._currProcess = self._simContext["m_curr_proc_info"]\ + ["process_handle"]["*"]["m_name"] self._currProcess.changed.connect(self._setProcess) self._setProcess(self._currProcess.value) else: @@ -201,17 +202,17 @@ def _changedProcessHandle(self, v): self._currProcess.changed.disconnect() self._currProcess.die() self._currProcess = self.__sp.distributedObjects.variablePool.\ - getVar("(sc_core::sc_process_b *)" + str(v))["*"]["m_name"]["_M_dataplus"]["_M_p"] + getVar("(sc_core::sc_process_b *)" + str(v))["*"]["m_name"] self._currProcess.changed.connect(self._setProcess) self._setProcess(self._currProcess.value) def _setProcessKind(self, v): t = { - "sc_core::SC_NO_PROC_": "No Process", - "sc_core::SC_METHOD_PROC_": "Method", - "sc_core::SC_THREAD_PROC_": "Thread", - "sc_core::SC_CTHREAD_PROC_": "CThread", - } + "sc_core::SC_NO_PROC_": "No Process", + "sc_core::SC_METHOD_PROC_": "Method", + "sc_core::SC_THREAD_PROC_": "Thread", + "sc_core::SC_CTHREAD_PROC_": "CThread", + } self.ui.processKindEdit.setText(t[str(v)]) @@ -228,7 +229,7 @@ def _setSimTime(self): "us": 6, "ms": 9, "s": 12 - } + } time /= 10 ** div[unit] diff --git a/src/resources.qrc b/src/resources.qrc index 65304f3..d301ecf 100644 --- a/src/resources.qrc +++ b/src/resources.qrc @@ -62,6 +62,7 @@ images/save-html.png images/minus.png images/arrow-right.png + images/beautify.png images/markers/exec_pos.png diff --git a/src/third_party/pretty_printer/libstdcxx/__init__.py b/src/third_party/pretty_printer/libstdcxx/__init__.py new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/src/third_party/pretty_printer/libstdcxx/__init__.py @@ -0,0 +1 @@ + diff --git a/src/third_party/pretty_printer/libstdcxx/v6/__init__.py b/src/third_party/pretty_printer/libstdcxx/v6/__init__.py new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/src/third_party/pretty_printer/libstdcxx/v6/__init__.py @@ -0,0 +1 @@ + diff --git a/src/third_party/pretty_printer/libstdcxx/v6/printers.py b/src/third_party/pretty_printer/libstdcxx/v6/printers.py new file mode 100644 index 0000000..0eac413 --- /dev/null +++ b/src/third_party/pretty_printer/libstdcxx/v6/printers.py @@ -0,0 +1,930 @@ +# Pretty-printers for libstc++. + +# Copyright (C) 2008, 2009, 2010, 2011, 2012 Free Software Foundation, Inc. + +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +import gdb +import itertools +import re + +# Try to use the new-style pretty-printing if available. +_use_gdb_pp = True +try: + import gdb.printing +except ImportError: + _use_gdb_pp = False + +# Starting with the type ORIG, search for the member type NAME. This +# handles searching upward through superclasses. This is needed to +# work around http://sourceware.org/bugzilla/show_bug.cgi?id=13615. +def find_type(orig, name): + typ = orig.strip_typedefs() + while True: + search = str(typ) + '::' + name + try: + return gdb.lookup_type(search) + except RuntimeError: + pass + # The type was not found, so try the superclass. We only need + # to check the first superclass, so we don't bother with + # anything fancier here. + field = typ.fields()[0] + if not field.is_base_class: + raise ValueError, "Cannot find type %s::%s" % (str(orig), name) + typ = field.type + +class SharedPointerPrinter: + "Print a shared_ptr or weak_ptr" + + def __init__ (self, typename, val): + self.typename = typename + self.val = val + + def to_string (self): + state = 'empty' + refcounts = self.val['_M_refcount']['_M_pi'] + if refcounts != 0: + usecount = refcounts['_M_use_count'] + weakcount = refcounts['_M_weak_count'] + if usecount == 0: + state = 'expired, weak %d' % weakcount + else: + state = 'count %d, weak %d' % (usecount, weakcount - 1) + return '%s (%s) %s' % (self.typename, state, self.val['_M_ptr']) + +class UniquePointerPrinter: + "Print a unique_ptr" + + def __init__ (self, typename, val): + self.val = val + + def to_string (self): + v = self.val['_M_t']['_M_head_impl'] + return ('std::unique_ptr<%s> containing %s' % (str(v.type.target()), + str(v))) + +class StdListPrinter: + "Print a std::list" + + class _iterator: + def __init__(self, nodetype, head): + self.nodetype = nodetype + self.base = head['_M_next'] + self.head = head.address + self.count = 0 + + def __iter__(self): + return self + + def next(self): + if self.base == self.head: + raise StopIteration + elt = self.base.cast(self.nodetype).dereference() + self.base = elt['_M_next'] + count = self.count + self.count = self.count + 1 + return ('[%d]' % count, elt['_M_data']) + + def __init__(self, typename, val): + self.typename = typename + self.val = val + + def children(self): + nodetype = find_type(self.val.type, '_Node') + nodetype = nodetype.strip_typedefs().pointer() + return self._iterator(nodetype, self.val['_M_impl']['_M_node']) + + def to_string(self): + if self.val['_M_impl']['_M_node'].address == self.val['_M_impl']['_M_node']['_M_next']: + return 'empty %s' % (self.typename) + return '%s' % (self.typename) + +class StdListIteratorPrinter: + "Print std::list::iterator" + + def __init__(self, typename, val): + self.val = val + self.typename = typename + + def to_string(self): + nodetype = find_type(self.val.type, '_Node') + nodetype = nodetype.strip_typedefs().pointer() + return self.val['_M_node'].cast(nodetype).dereference()['_M_data'] + +class StdSlistPrinter: + "Print a __gnu_cxx::slist" + + class _iterator: + def __init__(self, nodetype, head): + self.nodetype = nodetype + self.base = head['_M_head']['_M_next'] + self.count = 0 + + def __iter__(self): + return self + + def next(self): + if self.base == 0: + raise StopIteration + elt = self.base.cast(self.nodetype).dereference() + self.base = elt['_M_next'] + count = self.count + self.count = self.count + 1 + return ('[%d]' % count, elt['_M_data']) + + def __init__(self, typename, val): + self.val = val + + def children(self): + nodetype = find_type(self.val.type, '_Node') + nodetype = nodetype.strip_typedefs().pointer() + return self._iterator(nodetype, self.val) + + def to_string(self): + if self.val['_M_head']['_M_next'] == 0: + return 'empty __gnu_cxx::slist' + return '__gnu_cxx::slist' + +class StdSlistIteratorPrinter: + "Print __gnu_cxx::slist::iterator" + + def __init__(self, typename, val): + self.val = val + + def to_string(self): + nodetype = find_type(self.val.type, '_Node') + nodetype = nodetype.strip_typedefs().pointer() + return self.val['_M_node'].cast(nodetype).dereference()['_M_data'] + +class StdVectorPrinter: + "Print a std::vector" + + class _iterator: + def __init__ (self, start, finish, bitvec): + self.bitvec = bitvec + if bitvec: + self.item = start['_M_p'] + self.so = start['_M_offset'] + self.finish = finish['_M_p'] + self.fo = finish['_M_offset'] + itype = self.item.dereference().type + self.isize = 8 * itype.sizeof + else: + self.item = start + self.finish = finish + self.count = 0 + + def __iter__(self): + return self + + def next(self): + count = self.count + self.count = self.count + 1 + if self.bitvec: + if self.item == self.finish and self.so >= self.fo: + raise StopIteration + elt = self.item.dereference() + if elt & (1 << self.so): + obit = 1 + else: + obit = 0 + self.so = self.so + 1 + if self.so >= self.isize: + self.item = self.item + 1 + self.so = 0 + return ('[%d]' % count, obit) + else: + if self.item == self.finish: + raise StopIteration + elt = self.item.dereference() + self.item = self.item + 1 + return ('[%d]' % count, elt) + + def __init__(self, typename, val): + self.typename = typename + self.val = val + self.is_bool = val.type.template_argument(0).code == gdb.TYPE_CODE_BOOL + + def children(self): + return self._iterator(self.val['_M_impl']['_M_start'], + self.val['_M_impl']['_M_finish'], + self.is_bool) + + def to_string(self): + start = self.val['_M_impl']['_M_start'] + finish = self.val['_M_impl']['_M_finish'] + end = self.val['_M_impl']['_M_end_of_storage'] + if self.is_bool: + start = self.val['_M_impl']['_M_start']['_M_p'] + so = self.val['_M_impl']['_M_start']['_M_offset'] + finish = self.val['_M_impl']['_M_finish']['_M_p'] + fo = self.val['_M_impl']['_M_finish']['_M_offset'] + itype = start.dereference().type + bl = 8 * itype.sizeof + length = (bl - so) + bl * ((finish - start) - 1) + fo + capacity = bl * (end - start) + return ('%s of length %d, capacity %d' + % (self.typename, int (length), int (capacity))) + else: + return ('%s of length %d, capacity %d' + % (self.typename, int (finish - start), int (end - start))) + + def display_hint(self): + return 'array' + +class StdVectorIteratorPrinter: + "Print std::vector::iterator" + + def __init__(self, typename, val): + self.val = val + + def to_string(self): + return self.val['_M_current'].dereference() + +class StdTuplePrinter: + "Print a std::tuple" + + class _iterator: + def __init__ (self, head): + self.head = head + + # Set the base class as the initial head of the + # tuple. + nodes = self.head.type.fields () + if len (nodes) == 1: + # Set the actual head to the first pair. + self.head = self.head.cast (nodes[0].type) + elif len (nodes) != 0: + raise ValueError, "Top of tuple tree does not consist of a single node." + self.count = 0 + + def __iter__ (self): + return self + + def next (self): + nodes = self.head.type.fields () + # Check for further recursions in the inheritance tree. + if len (nodes) == 0: + raise StopIteration + # Check that this iteration has an expected structure. + if len (nodes) != 2: + raise ValueError, "Cannot parse more than 2 nodes in a tuple tree." + + # - Left node is the next recursion parent. + # - Right node is the actual class contained in the tuple. + + # Process right node. + impl = self.head.cast (nodes[1].type) + + # Process left node and set it as head. + self.head = self.head.cast (nodes[0].type) + self.count = self.count + 1 + + # Finally, check the implementation. If it is + # wrapped in _M_head_impl return that, otherwise return + # the value "as is". + fields = impl.type.fields () + if len (fields) < 1 or fields[0].name != "_M_head_impl": + return ('[%d]' % self.count, impl) + else: + return ('[%d]' % self.count, impl['_M_head_impl']) + + def __init__ (self, typename, val): + self.typename = typename + self.val = val; + + def children (self): + return self._iterator (self.val) + + def to_string (self): + if len (self.val.type.fields ()) == 0: + return 'empty %s' % (self.typename) + return '%s containing' % (self.typename) + +class StdStackOrQueuePrinter: + "Print a std::stack or std::queue" + + def __init__ (self, typename, val): + self.typename = typename + self.visualizer = gdb.default_visualizer(val['c']) + + def children (self): + return self.visualizer.children() + + def to_string (self): + return '%s wrapping: %s' % (self.typename, + self.visualizer.to_string()) + + def display_hint (self): + if hasattr (self.visualizer, 'display_hint'): + return self.visualizer.display_hint () + return None + +class RbtreeIterator: + def __init__(self, rbtree): + self.size = rbtree['_M_t']['_M_impl']['_M_node_count'] + self.node = rbtree['_M_t']['_M_impl']['_M_header']['_M_left'] + self.count = 0 + + def __iter__(self): + return self + + def __len__(self): + return int (self.size) + + def next(self): + if self.count == self.size: + raise StopIteration + result = self.node + self.count = self.count + 1 + if self.count < self.size: + # Compute the next node. + node = self.node + if node.dereference()['_M_right']: + node = node.dereference()['_M_right'] + while node.dereference()['_M_left']: + node = node.dereference()['_M_left'] + else: + parent = node.dereference()['_M_parent'] + while node == parent.dereference()['_M_right']: + node = parent + parent = parent.dereference()['_M_parent'] + if node.dereference()['_M_right'] != parent: + node = parent + self.node = node + return result + +# This is a pretty printer for std::_Rb_tree_iterator (which is +# std::map::iterator), and has nothing to do with the RbtreeIterator +# class above. +class StdRbtreeIteratorPrinter: + "Print std::map::iterator" + + def __init__ (self, typename, val): + self.val = val + + def to_string (self): + typename = str(self.val.type.strip_typedefs()) + '::_Link_type' + nodetype = gdb.lookup_type(typename).strip_typedefs() + return self.val.cast(nodetype).dereference()['_M_value_field'] + +class StdDebugIteratorPrinter: + "Print a debug enabled version of an iterator" + + def __init__ (self, typename, val): + self.val = val + + # Just strip away the encapsulating __gnu_debug::_Safe_iterator + # and return the wrapped iterator value. + def to_string (self): + itype = self.val.type.template_argument(0) + return self.val['_M_current'].cast(itype) + +class StdMapPrinter: + "Print a std::map or std::multimap" + + # Turn an RbtreeIterator into a pretty-print iterator. + class _iter: + def __init__(self, rbiter, type): + self.rbiter = rbiter + self.count = 0 + self.type = type + + def __iter__(self): + return self + + def next(self): + if self.count % 2 == 0: + n = self.rbiter.next() + n = n.cast(self.type).dereference()['_M_value_field'] + self.pair = n + item = n['first'] + else: + item = self.pair['second'] + result = ('[%d]' % self.count, item) + self.count = self.count + 1 + return result + + def __init__ (self, typename, val): + self.typename = typename + self.val = val + + def to_string (self): + return '%s with %d elements' % (self.typename, + len (RbtreeIterator (self.val))) + + def children (self): + rep_type = find_type(self.val.type, '_Rep_type') + node = find_type(rep_type, '_Link_type') + node = node.strip_typedefs() + return self._iter (RbtreeIterator (self.val), node) + + def display_hint (self): + return 'map' + +class StdSetPrinter: + "Print a std::set or std::multiset" + + # Turn an RbtreeIterator into a pretty-print iterator. + class _iter: + def __init__(self, rbiter, type): + self.rbiter = rbiter + self.count = 0 + self.type = type + + def __iter__(self): + return self + + def next(self): + item = self.rbiter.next() + item = item.cast(self.type).dereference()['_M_value_field'] + # FIXME: this is weird ... what to do? + # Maybe a 'set' display hint? + result = ('[%d]' % self.count, item) + self.count = self.count + 1 + return result + + def __init__ (self, typename, val): + self.typename = typename + self.val = val + + def to_string (self): + return '%s with %d elements' % (self.typename, + len (RbtreeIterator (self.val))) + + def children (self): + rep_type = find_type(self.val.type, '_Rep_type') + node = find_type(rep_type, '_Link_type') + node = node.strip_typedefs() + return self._iter (RbtreeIterator (self.val), node) + +class StdBitsetPrinter: + "Print a std::bitset" + + def __init__(self, typename, val): + self.typename = typename + self.val = val + + def to_string (self): + # If template_argument handled values, we could print the + # size. Or we could use a regexp on the type. + return '%s' % (self.typename) + + def children (self): + words = self.val['_M_w'] + wtype = words.type + + # The _M_w member can be either an unsigned long, or an + # array. This depends on the template specialization used. + # If it is a single long, convert to a single element list. + if wtype.code == gdb.TYPE_CODE_ARRAY: + tsize = wtype.target ().sizeof + else: + words = [words] + tsize = wtype.sizeof + + nwords = wtype.sizeof / tsize + result = [] + byte = 0 + while byte < nwords: + w = words[byte] + bit = 0 + while w != 0: + if (w & 1) != 0: + # Another spot where we could use 'set'? + result.append(('[%d]' % (byte * tsize * 8 + bit), 1)) + bit = bit + 1 + w = w >> 1 + byte = byte + 1 + return result + +class StdDequePrinter: + "Print a std::deque" + + class _iter: + def __init__(self, node, start, end, last, buffer_size): + self.node = node + self.p = start + self.end = end + self.last = last + self.buffer_size = buffer_size + self.count = 0 + + def __iter__(self): + return self + + def next(self): + if self.p == self.last: + raise StopIteration + + result = ('[%d]' % self.count, self.p.dereference()) + self.count = self.count + 1 + + # Advance the 'cur' pointer. + self.p = self.p + 1 + if self.p == self.end: + # If we got to the end of this bucket, move to the + # next bucket. + self.node = self.node + 1 + self.p = self.node[0] + self.end = self.p + self.buffer_size + + return result + + def __init__(self, typename, val): + self.typename = typename + self.val = val + self.elttype = val.type.template_argument(0) + size = self.elttype.sizeof + if size < 512: + self.buffer_size = int (512 / size) + else: + self.buffer_size = 1 + + def to_string(self): + start = self.val['_M_impl']['_M_start'] + end = self.val['_M_impl']['_M_finish'] + + delta_n = end['_M_node'] - start['_M_node'] - 1 + delta_s = start['_M_last'] - start['_M_cur'] + delta_e = end['_M_cur'] - end['_M_first'] + + size = self.buffer_size * delta_n + delta_s + delta_e + + return '%s with %d elements' % (self.typename, long (size)) + + def children(self): + start = self.val['_M_impl']['_M_start'] + end = self.val['_M_impl']['_M_finish'] + return self._iter(start['_M_node'], start['_M_cur'], start['_M_last'], + end['_M_cur'], self.buffer_size) + + def display_hint (self): + return 'array' + +class StdDequeIteratorPrinter: + "Print std::deque::iterator" + + def __init__(self, typename, val): + self.val = val + + def to_string(self): + return self.val['_M_cur'].dereference() + +class StdStringPrinter: + "Print a std::basic_string of some kind" + + def __init__(self, typename, val): + self.val = val + + def to_string(self): + # Make sure &string works, too. + type = self.val.type + if type.code == gdb.TYPE_CODE_REF: + type = type.target () + + # Calculate the length of the string so that to_string returns + # the string according to length, not according to first null + # encountered. + ptr = self.val ['_M_dataplus']['_M_p'] + realtype = type.unqualified ().strip_typedefs () + reptype = gdb.lookup_type (str (realtype) + '::_Rep').pointer () + header = ptr.cast(reptype) - 1 + len = header.dereference ()['_M_length'] + if hasattr(ptr, "lazy_string"): + return ptr.lazy_string (length = len) + return ptr.string (length = len) + + def display_hint (self): + return 'string' + +class Tr1HashtableIterator: + def __init__ (self, hash): + self.node = hash['_M_before_begin']['_M_nxt'] + self.node_type = find_type(hash.type, '__node_type').pointer() + + def __iter__ (self): + return self + + def next (self): + if self.node == 0: + raise StopIteration + node = self.node.cast(self.node_type) + result = node.dereference()['_M_v'] + self.node = node.dereference()['_M_nxt'] + return result + +class Tr1UnorderedSetPrinter: + "Print a tr1::unordered_set" + + def __init__ (self, typename, val): + self.typename = typename + self.val = val + + def to_string (self): + return '%s with %d elements' % (self.typename, self.val['_M_element_count']) + + @staticmethod + def format_count (i): + return '[%d]' % i + + def children (self): + counter = itertools.imap (self.format_count, itertools.count()) + return itertools.izip (counter, Tr1HashtableIterator (self.val)) + +class Tr1UnorderedMapPrinter: + "Print a tr1::unordered_map" + + def __init__ (self, typename, val): + self.typename = typename + self.val = val + + def to_string (self): + return '%s with %d elements' % (self.typename, self.val['_M_element_count']) + + @staticmethod + def flatten (list): + for elt in list: + for i in elt: + yield i + + @staticmethod + def format_one (elt): + return (elt['first'], elt['second']) + + @staticmethod + def format_count (i): + return '[%d]' % i + + def children (self): + counter = itertools.imap (self.format_count, itertools.count()) + # Map over the hash table and flatten the result. + data = self.flatten (itertools.imap (self.format_one, Tr1HashtableIterator (self.val))) + # Zip the two iterators together. + return itertools.izip (counter, data) + + def display_hint (self): + return 'map' + +class StdForwardListPrinter: + "Print a std::forward_list" + + class _iterator: + def __init__(self, nodetype, head): + self.nodetype = nodetype + self.base = head['_M_next'] + self.count = 0 + + def __iter__(self): + return self + + def next(self): + if self.base == 0: + raise StopIteration + elt = self.base.cast(self.nodetype).dereference() + self.base = elt['_M_next'] + count = self.count + self.count = self.count + 1 + return ('[%d]' % count, elt['_M_value']) + + def __init__(self, typename, val): + self.val = val + self.typename = typename + + def children(self): + nodetype = find_type(self.val.type, '_Node') + nodetype = nodetype.strip_typedefs().pointer() + return self._iterator(nodetype, self.val['_M_impl']['_M_head']) + + def to_string(self): + if self.val['_M_impl']['_M_head']['_M_next'] == 0: + return 'empty %s' % (self.typename) + return '%s' % (self.typename) + + +# A "regular expression" printer which conforms to the +# "SubPrettyPrinter" protocol from gdb.printing. +class RxPrinter(object): + def __init__(self, name, function): + super(RxPrinter, self).__init__() + self.name = name + self.function = function + self.enabled = True + + def invoke(self, value): + if not self.enabled: + return None + return self.function(self.name, value) + +# A pretty-printer that conforms to the "PrettyPrinter" protocol from +# gdb.printing. It can also be used directly as an old-style printer. +class Printer(object): + def __init__(self, name): + super(Printer, self).__init__() + self.name = name + self.subprinters = [] + self.lookup = {} + self.enabled = True + self.compiled_rx = re.compile('^([a-zA-Z0-9_:]+)<.*>$') + + def add(self, name, function): + # A small sanity check. + # FIXME + if not self.compiled_rx.match(name + '<>'): + raise ValueError, 'libstdc++ programming error: "%s" does not match' % name + printer = RxPrinter(name, function) + self.subprinters.append(printer) + self.lookup[name] = printer + + # Add a name using _GLIBCXX_BEGIN_NAMESPACE_VERSION. + def add_version(self, base, name, function): + self.add(base + name, function) + self.add(base + '__7::' + name, function) + + # Add a name using _GLIBCXX_BEGIN_NAMESPACE_CONTAINER. + def add_container(self, base, name, function): + self.add_version(base, name, function) + self.add_version(base + '__cxx1998::', name, function) + + @staticmethod + def get_basic_type(type): + # If it points to a reference, get the reference. + if type.code == gdb.TYPE_CODE_REF: + type = type.target () + + # Get the unqualified type, stripped of typedefs. + type = type.unqualified ().strip_typedefs () + + return type.tag + + def __call__(self, val): + typename = self.get_basic_type(val.type) + if not typename: + return None + + # All the types we match are template types, so we can use a + # dictionary. + match = self.compiled_rx.match(typename) + if not match: + return None + + basename = match.group(1) + if basename in self.lookup: + return self.lookup[basename].invoke(val) + + # Cannot find a pretty printer. Return None. + return None + +libstdcxx_printer = None + +def register_libstdcxx_printers (obj): + "Register libstdc++ pretty-printers with objfile Obj." + + global _use_gdb_pp + global libstdcxx_printer + + if _use_gdb_pp: + gdb.printing.register_pretty_printer(obj, libstdcxx_printer) + else: + if obj is None: + obj = gdb + obj.pretty_printers.append(libstdcxx_printer) + +def build_libstdcxx_dictionary (): + global libstdcxx_printer + + libstdcxx_printer = Printer("libstdc++-v6") + + # For _GLIBCXX_BEGIN_NAMESPACE_VERSION. + vers = '(__7::)?' + # For _GLIBCXX_BEGIN_NAMESPACE_CONTAINER. + container = '(__cxx1998::' + vers + ')?' + + # libstdc++ objects requiring pretty-printing. + # In order from: + # http://gcc.gnu.org/onlinedocs/libstdc++/latest-doxygen/a01847.html + libstdcxx_printer.add_version('std::', 'basic_string', StdStringPrinter) + libstdcxx_printer.add_container('std::', 'bitset', StdBitsetPrinter) + libstdcxx_printer.add_container('std::', 'deque', StdDequePrinter) + libstdcxx_printer.add_container('std::', 'list', StdListPrinter) + libstdcxx_printer.add_container('std::', 'map', StdMapPrinter) + libstdcxx_printer.add_container('std::', 'multimap', StdMapPrinter) + libstdcxx_printer.add_container('std::', 'multiset', StdSetPrinter) + libstdcxx_printer.add_version('std::', 'priority_queue', + StdStackOrQueuePrinter) + libstdcxx_printer.add_version('std::', 'queue', StdStackOrQueuePrinter) + libstdcxx_printer.add_version('std::', 'tuple', StdTuplePrinter) + libstdcxx_printer.add_container('std::', 'set', StdSetPrinter) + libstdcxx_printer.add_version('std::', 'stack', StdStackOrQueuePrinter) + libstdcxx_printer.add_version('std::', 'unique_ptr', UniquePointerPrinter) + libstdcxx_printer.add_container('std::', 'vector', StdVectorPrinter) + # vector + + # Printer registrations for classes compiled with -D_GLIBCXX_DEBUG. + libstdcxx_printer.add('std::__debug::bitset', StdBitsetPrinter) + libstdcxx_printer.add('std::__debug::deque', StdDequePrinter) + libstdcxx_printer.add('std::__debug::list', StdListPrinter) + libstdcxx_printer.add('std::__debug::map', StdMapPrinter) + libstdcxx_printer.add('std::__debug::multimap', StdMapPrinter) + libstdcxx_printer.add('std::__debug::multiset', StdSetPrinter) + libstdcxx_printer.add('std::__debug::priority_queue', + StdStackOrQueuePrinter) + libstdcxx_printer.add('std::__debug::queue', StdStackOrQueuePrinter) + libstdcxx_printer.add('std::__debug::set', StdSetPrinter) + libstdcxx_printer.add('std::__debug::stack', StdStackOrQueuePrinter) + libstdcxx_printer.add('std::__debug::unique_ptr', UniquePointerPrinter) + libstdcxx_printer.add('std::__debug::vector', StdVectorPrinter) + + # These are the TR1 and C++0x printers. + # For array - the default GDB pretty-printer seems reasonable. + libstdcxx_printer.add_version('std::', 'shared_ptr', SharedPointerPrinter) + libstdcxx_printer.add_version('std::', 'weak_ptr', SharedPointerPrinter) + libstdcxx_printer.add_container('std::', 'unordered_map', + Tr1UnorderedMapPrinter) + libstdcxx_printer.add_container('std::', 'unordered_set', + Tr1UnorderedSetPrinter) + libstdcxx_printer.add_container('std::', 'unordered_multimap', + Tr1UnorderedMapPrinter) + libstdcxx_printer.add_container('std::', 'unordered_multiset', + Tr1UnorderedSetPrinter) + libstdcxx_printer.add_container('std::', 'forward_list', + StdForwardListPrinter) + + libstdcxx_printer.add_version('std::tr1::', 'shared_ptr', SharedPointerPrinter) + libstdcxx_printer.add_version('std::tr1::', 'weak_ptr', SharedPointerPrinter) + libstdcxx_printer.add_version('std::tr1::', 'unordered_map', + Tr1UnorderedMapPrinter) + libstdcxx_printer.add_version('std::tr1::', 'unordered_set', + Tr1UnorderedSetPrinter) + libstdcxx_printer.add_version('std::tr1::', 'unordered_multimap', + Tr1UnorderedMapPrinter) + libstdcxx_printer.add_version('std::tr1::', 'unordered_multiset', + Tr1UnorderedSetPrinter) + + # These are the C++0x printer registrations for -D_GLIBCXX_DEBUG cases. + # The tr1 namespace printers do not seem to have any debug + # equivalents, so do no register them. + libstdcxx_printer.add('std::__debug::unordered_map', + Tr1UnorderedMapPrinter) + libstdcxx_printer.add('std::__debug::unordered_set', + Tr1UnorderedSetPrinter) + libstdcxx_printer.add('std::__debug::unordered_multimap', + Tr1UnorderedMapPrinter) + libstdcxx_printer.add('std::__debug::unordered_multiset', + Tr1UnorderedSetPrinter) + libstdcxx_printer.add('std::__debug::forward_list', + StdForwardListPrinter) + + + # Extensions. + libstdcxx_printer.add_version('__gnu_cxx::', 'slist', StdSlistPrinter) + + if True: + # These shouldn't be necessary, if GDB "print *i" worked. + # But it often doesn't, so here they are. + libstdcxx_printer.add_container('std::', '_List_iterator', + StdListIteratorPrinter) + libstdcxx_printer.add_container('std::', '_List_const_iterator', + StdListIteratorPrinter) + libstdcxx_printer.add_version('std::', '_Rb_tree_iterator', + StdRbtreeIteratorPrinter) + libstdcxx_printer.add_version('std::', '_Rb_tree_const_iterator', + StdRbtreeIteratorPrinter) + libstdcxx_printer.add_container('std::', '_Deque_iterator', + StdDequeIteratorPrinter) + libstdcxx_printer.add_container('std::', '_Deque_const_iterator', + StdDequeIteratorPrinter) + libstdcxx_printer.add_version('__gnu_cxx::', '__normal_iterator', + StdVectorIteratorPrinter) + libstdcxx_printer.add_version('__gnu_cxx::', '_Slist_iterator', + StdSlistIteratorPrinter) + + # Debug (compiled with -D_GLIBCXX_DEBUG) printer + # registrations. The Rb_tree debug iterator when unwrapped + # from the encapsulating __gnu_debug::_Safe_iterator does not + # have the __norm namespace. Just use the existing printer + # registration for that. + libstdcxx_printer.add('__gnu_debug::_Safe_iterator', + StdDebugIteratorPrinter) + libstdcxx_printer.add('std::__norm::_List_iterator', + StdListIteratorPrinter) + libstdcxx_printer.add('std::__norm::_List_const_iterator', + StdListIteratorPrinter) + libstdcxx_printer.add('std::__norm::_Deque_const_iterator', + StdDequeIteratorPrinter) + libstdcxx_printer.add('std::__norm::_Deque_iterator', + StdDequeIteratorPrinter) + +build_libstdcxx_dictionary () diff --git a/src/variables/variable.py b/src/variables/variable.py index 2ad1e06..c85542b 100644 --- a/src/variables/variable.py +++ b/src/variables/variable.py @@ -39,7 +39,7 @@ class Variable(QObject): def __init__(self, variablepool, exp, gdbName, uniqueName, type_, value, inScope, - hasChildren, access): + numChild, access): QObject.__init__(self) self.exp = exp self.type = type_ @@ -47,11 +47,13 @@ def __init__(self, variablepool, exp, gdbName, self.access = access self.uniqueName = uniqueName self.value = value - self.hasChildren = hasChildren + self.numChild = numChild self._vp = variablepool self._gdbName = gdbName self._childs = [] + + self.hasChildren = property(lambda self: self.numChild > 0) def _getChildrenFromGdb(self): """Load the children from GDB, if there are any.""" @@ -99,6 +101,11 @@ def emitChanged(self): def makeWrapper(self, factory): return factory.makeWrapper(self) + + def removeChildren(self): + for child in self._childs: + child.removeChildren() + del self._childs[:] def die(self): self._vp.removeVar(self) diff --git a/src/variables/variablelist.py b/src/variables/variablelist.py index 886ae22..8c603eb 100644 --- a/src/variables/variablelist.py +++ b/src/variables/variablelist.py @@ -52,17 +52,24 @@ def addVar(self, varWrapper): """ adds VariableWrapper varWrapper to the list @param varWrapper variables.variablewrapper.VariableWrapper, VariableWrapper to add to the list """ self.list.append(varWrapper) - + + def reloadLocals(self): + self.clear() + for var in self.varPool.reloadLocals(): + vw = var.makeWrapper(self.factory) + self.list.append(vw) + def addLocals(self): for var in self.varPool.addLocals(): vw = var.makeWrapper(self.factory) self.list.append(vw) - + def removeVar(self, varWrapper): """ removes VariableWrapper varWrapper from the list @param varWrapper variables.variablewrapper.VariableWrapper, VariableWrapper to remove from the list """ - self.list.remove(varWrapper) - varWrapper.die() + if varWrapper in self.list: + self.list.remove(varWrapper) + varWrapper.die() def clear(self): """ Clears the whole VariableList. """ diff --git a/src/variables/variablepool.py b/src/variables/variablepool.py index 600626d..aa9ad60 100644 --- a/src/variables/variablepool.py +++ b/src/variables/variablepool.py @@ -60,7 +60,21 @@ def clearVars(self): """ self.variables = {} - + + def reloadLocals(self): + """ function deletes all variables created within gdb and stored within ricodebug + after that the locals are read again + function is important for beautify (en/disable) + reason :: calling 'disable pretty-printer' disables access to vars with a saved + format like var4.4 -> after disable -> var24.private._M_dataplus + """ + for var in self.variables: + self.connector.var_delete(var) + + self.clearVars() + ret = self.addLocals() + return ret + def justUpdateValues(self): """ just update variables for tracepoints, dont signal changes to connected views this function is connected to the signal SignalProxy::tracepointOccured() @@ -92,6 +106,10 @@ def __updateVars(self, isTracePoint=None): for changed in res: var = self.variables[changed.name] var.inScope = (changed.in_scope == "true") + if hasattr(changed, "new_num_children"): + var.removeChildren() + var.numChild = changed.new_num_children + self.getChildren(var._gdbName, var._childs, var.access, var.uniqueName, var._childFormat) if hasattr(changed, "value"): var.value = changed.value if not isTracePoint: @@ -136,9 +154,10 @@ def removeVar(self, variable): """ remove variable from variable pool @param variable Variable type instance """ - self.variables.pop(variable._gdbName) - self.connector.var_delete(variable._gdbName) - del variable + if variable._gdbName in self.variables: + self.variables.pop(variable._gdbName) + self.connector.var_delete(variable._gdbName) + del variable def getChildren(self, name, childList, access, parentName, childformat): """ @@ -200,7 +219,7 @@ def __createVariable(self, gdbVar, parentName=None, exp=None, access=None, child type_ = gdbVar.type value = gdbVar.value inScope = True - haschildren = (int(gdbVar.numchild) > 0) + numChild = int(gdbVar.numchild) access = access # We use some heuristics to find out whether a type is a pointer, an @@ -211,18 +230,18 @@ def __createVariable(self, gdbVar, parentName=None, exp=None, access=None, child # Therefore, if the value looks like a size, treat it as an array. # * Everything else with children is a structure. # * Again, everything else is a normal variable. - if gdbVar.value.startswith('0x'): + if value.startswith('0x'): logging.debug("Creating a pointer variable for '%s'", exp) - varReturn = PtrVariable(self, exp, gdbName, uniqueName, type_, value, inScope, haschildren, access) - elif re.match("\[\d+\]", gdbVar.value) and int(gdbVar.numchild) >= 1: - logging.debug("Creating a array variable for '%s'", exp) - varReturn = ArrayVariable(self, exp, gdbName, uniqueName, type_, value, inScope, haschildren, access) - elif haschildren: + varReturn = PtrVariable(self, exp, gdbName, uniqueName, type_, value, inScope, numChild, access) + elif numChild > 0: logging.debug("Creating a struct variable for '%s'", exp) - varReturn = StructVariable(self, exp, gdbName, uniqueName, type_, value, inScope, haschildren, access) + varReturn = StructVariable(self, exp, gdbName, uniqueName, type_, value, inScope, numChild, access) + elif re.match("{...}", value): + logging.debug("Creating an array variable for '%s'", exp) + varReturn = ArrayVariable(self, exp, gdbName, uniqueName, type_, value, inScope, numChild, access) else: logging.debug("Creating a normal variable for '%s'", exp) - varReturn = StdVariable(self, exp, gdbName, uniqueName, type_, value, inScope, haschildren, access) + varReturn = StdVariable(self, exp, gdbName, uniqueName, type_, value, inScope, numChild, access) return varReturn diff --git a/src/views/mainwindow.py b/src/views/mainwindow.py index 9ab07bb..cc40ffa 100644 --- a/src/views/mainwindow.py +++ b/src/views/mainwindow.py @@ -130,6 +130,8 @@ def __initActions(self): self.act.ReverseNext.setEnabled(False) self.act.ReverseStep.setEnabled(False) self.act.SaveFile.setEnabled(False) + self.act.Beautify.setCheckable(True) + self.act.Beautify.setChecked(True) # debug actions self.ui.menuDebug.addAction(self.act.Run) self.ui.menuDebug.addAction(self.act.Continue) @@ -141,6 +143,7 @@ def __initActions(self): self.ui.menuDebug.addAction(self.act.Record) self.ui.menuDebug.addAction(self.act.ReverseNext) self.ui.menuDebug.addAction(self.act.ReverseStep) + self.ui.menuDebug.addAction(self.act.Beautify) # file actions self.ui.menuFile.insertAction(self.ui.actionSaveSession, @@ -168,6 +171,7 @@ def __initActions(self): self.ui.Main.addAction(self.act.ReverseStep) self.ui.Main.addAction(self.act.Finish) self.ui.Main.addAction(self.act.RunToCursor) + self.ui.Main.addAction(self.act.Beautify) self.ui.Main.addSeparator() self.ui.Main.addAction(self.act.Exit) @@ -190,7 +194,8 @@ def __connectActions(self): self.act.Record.triggered.connect(self.toggleRecord) self.act.ReverseStep.triggered.connect(self.debugController.reverse_step) self.act.ReverseNext.triggered.connect(self.debugController.reverse_next) - + self.act.Beautify.triggered.connect(self.__askBeautify) + self.act.Interrupt.triggered.connect(self.debugController.interrupt) self.act.Finish.triggered.connect(self.debugController.finish) self.act.RunToCursor.triggered.connect(self.debugController.inferiorUntil) @@ -301,6 +306,7 @@ def enableButtons(self): self.act.Finish.setEnabled(True) self.act.RunToCursor.setEnabled(True) self.act.Record.setEnabled(True) + self.act.Beautify.setEnabled(True) def disableButtons(self): self.act.Continue.setEnabled(False) @@ -311,6 +317,7 @@ def disableButtons(self): self.act.RunToCursor.setEnabled(False) self.act.Record.setChecked(False) self.act.Record.setEnabled(False) + self.act.Beautify.setEnabled(False) def __observeWorkingBinary(self, filename): """ Private Method to Observe Debugged Binary """ @@ -328,3 +335,12 @@ def __binaryChanged(self): else: self.fileWatcher.removePath(self.binaryName) self.fileWatcher.addPath(self.binaryName) + + def __askBeautify(self): + """ Warn user before Beautify - Using QtMessagebox for interaction""" + box = QtGui.QMessageBox() + if box.question(self, "Warning!", "This deletes the objects in watch and datagraphview.", + QtGui.QMessageBox.Ok, QtGui.QMessageBox.Cancel) == QtGui.QMessageBox.Ok: + self.debugController.beautify() + else: + self.act.Beautify.toggle() \ No newline at end of file