app.py

Go to the documentation of this file.
00001 #
00002 # Copyright (c) 2006, 2007 Cristian L. Vlasceanu.
00003 #
00004 # Permission is hereby granted, free of charge, to any person
00005 # obtaining a copy of this software and associated documentation
00006 # files (the "Software"), to deal in the Software without
00007 # restriction, including without limitation the rights to use,
00008 # copy, modify, merge, publish, distribute, sublicense, and/or
00009 # sell copies of the Software, and to permit persons to whom
00010 # the Software is furnished to do so, subject to the following
00011 # conditions:
00012 # 
00013 # The above copyright notice and this permission notice shall be
00014 # included in all copies or substantial portions of the Software.
00015 # 
00016 # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
00017 # KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
00018 # WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE
00019 # AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
00020 # HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
00021 # WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
00022 # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE
00023 # OR OTHER DEALINGS IN THE SOFTWARE.
00024 # 
00025 # NOTE: The above applies solely to Python source code provided 
00026 # herein, and it DOES NOT COVER the Zero Debugger Engine and plug-ins.
00027 #
00028 import os
00029 try:
00030         topPath=os.environ["ZTOP"]
00031 except KeyError:
00032         topPath="/home/cristiv/workspace/zero/plugin/python/zui"
00033         os.environ["ZTOP"] = topPath
00034 
00035 gladefile = topPath + "/zui.glade"
00036 
00037 import gobject
00038 import gtk
00039 import gtk.glade
00040 import pygtk
00041 import sys
00042 sys.path.append(topPath)
00043 import dlg
00044 import ffilter
00045 import view
00046 import tool
00047 import zero
00048 
00049 
00050 def update_state(parent, active):
00051         for w in parent.get_children():
00052                 name = w.get_name()
00053                 if name[0:5] == "tool_":
00054                         #hackish workaround for ToolButton bug
00055                         # see: http://bugzilla.gnome.org/show_bug.cgi?id=56070
00056                         w.hide() 
00057                         w.show() 
00058                         if name == "tool_stop":
00059                                 w.set_sensitive(not active)
00060                         else:
00061                                 w.set_sensitive(active)
00062 
00063 
00064 #################################################################
00065 #
00066 #
00067 class ZApp(view.App):
00068         """
00069         The main Debugger ZApplication class
00070         """
00071         def __init__(self):
00072                 view.Composite.__init__(self)
00073 
00074                 #Schedule connecting the event pipe to event pump. The reason for
00075                 # doing it this way rather than directly calling gobject.io_add_watch
00076                 # is to let the UI paint itself before starting a (possibly) lengthy
00077                 # debugger operation. (The PythonGate plugin blocks the main thread 
00078                 # until zero.event_pipe() is called).
00079                 gobject.idle_add(self.__connect_event_pipe)
00080 
00081                 self.__corefile = None
00082                 self.__debugger = zero.debugger()
00083                 self.__process = None
00084                 self.__thread = None
00085                 self.__widgets = gtk.glade.XML(gladefile)
00086                 self.__window = self.__widgets.get_widget("main")
00087                 self.__window.connect("destroy", self.__on_destroy)
00088 
00089                 #The listings view shows C++ code and assembly code
00090                 self.__listings = view.Listings(self.__widgets.get_widget("listings"))
00091                 self.__init_bottom_book()
00092                 self.__init_right_book()
00093                 self.add(self.__listings)
00094                 self.__init_menu()
00095                 self.__init_toolbar(self.__widgets.get_widget("toolbar"))
00096                 self.__init_font_button()
00097 
00098         def bypass_builtin_command_interpreter(self, value):
00099                 zero.bypass_builtin_command_interpreter(value)
00100 
00101         # Event pump
00102         def __dispatch(self, source, condition):
00103                 zero.event_iteration()
00104                 return True
00105 
00106 
00107         # Connect event pipe to event pump above
00108         def __connect_event_pipe(self):
00109                 gobject.io_add_watch(zero.event_pipe(), gobject.IO_IN, self.__dispatch)
00110                 return False
00111 
00112 
00113 
00114 
00115         ############################################################
00116         #
00117         # helpers called by __init__
00118         #
00119         def __init_toolbar(self, toolbar):
00120                 self.__init_file_open(toolbar)
00121                 btns = [
00122                         ("stop", "gtk-media-pause", "Break", "Break execution", 0),
00123                         ("continue", "gtk-media-play", "Continue", "Resume execution", 1),
00124                         ("next", "gtk-media-forward", "Next", "Step over source line", 0),
00125                         ("step", "gtk-media-next", "Step", "Step into function", 1),
00126                         ("ret", "gtk-undo", "Return", "Run until function returns", 1)
00127                 ]
00128                 tb = tool.Bar(toolbar)                  
00129                 tb.add_space()
00130                 for b in btns:
00131                         tb.add_stock(b[0], b[1], b[2], b[3], b[4], self.__on_tool_clicked)
00132                 
00133 
00134         def __init_file_open(self, toolbar):
00135                 try:
00136                         self.__fileDialog = gtk.FileChooserDialog()
00137                         self.__fileDialog.connect("close", self.__on_filechooser_close)
00138                         self.__fileDialog.connect("unmap-event", self.__on_unmap_event)
00139 
00140                         self.__fileButton = gtk.FileChooserButton(self.__fileDialog)
00141                         self.__fileButton.set_width_chars(6)
00142 
00143                         #connect the widget with the class that implements the logic
00144                         self.__fileOpen = dlg.FileOpen(
00145                                 self.__fileDialog, 
00146                                 [ffilter.ELF(self, gtk.FileFilter())])
00147 
00148                         #wrap FileChooserButton in a ToolItem
00149                         item = gtk.ToolItem()
00150                         item.add(self.__fileButton)
00151                         item.show_all()
00152                         toolbar.insert(item, 0)
00153 
00154                 except:
00155                         #FileChooser is available n Pygtk 2.4 and newer;
00156                         # fallback to FileSelection if not available
00157                         self.__fileButton = None
00158                         self.__fileDialog = gtk.FileSelection()
00159                         self.__fileDialog.hide_fileop_buttons()
00160                         #connect the widget with the class that implements the logic
00161                         self.__fileOpen = dlg.FileOpen(
00162                                         self.__fileDialog, [ffilter.ELF(self, None)])
00163 
00164 
00165         def __init_font_button(self):
00166                 button = self.__widgets.get_widget("fontbutton")
00167                 if button:
00168                         button.connect("font-set", self.__on_font_set)
00169                         button.set_show_style(True)
00170 
00171         def __on_detach(self, item):
00172                 #todo: ask user to confirm
00173                 self.__debugger.detach()        
00174 
00175         def __on_open(self, item):
00176                 self.__fileDialog.set_transient_for(self.__window)
00177                 self.__fileDialog.run()
00178 
00179         def __on_quit(self, item):
00180                 self.__window.destroy()
00181 
00182 
00183         def __init_menu(self):
00184                 quit = self.__widgets.get_widget("quit")
00185                 quit.connect("activate", self.__on_quit)
00186                 menuItem = self.__widgets.get_widget("open")
00187                 menuItem.connect("activate", self.__on_open)
00188                 menuItem = self.__widgets.get_widget("detach")
00189                 menuItem.connect("activate", self.__on_detach)
00190 
00191                 programMenuItems = [
00192                         "tool_continue", 
00193                         "tool_next", 
00194                         "tool_step", 
00195                         "tool_stop"]
00196 
00197                 for name in programMenuItems:
00198                         menuItem = self.__widgets.get_widget(name)
00199                         menuItem.connect("activate", self.__on_tool_clicked)
00200 
00201 
00202         def __init_bottom_book(self):
00203                 stackWidget = self.__widgets.get_widget("stack")
00204                 stackView = view.Stack(stackWidget)
00205                 stackView.connect("selection-changed", self.__listings.show_frame)
00206                 stackView.connect("selection-changed", self.__on_stack_selection)
00207                 self.add(stackView)
00208                 localsWidget = self.__widgets.get_widget("locals")
00209                 self.__locals = view.Data(localsWidget)
00210         
00211                 try:
00212                         bottom = self.__widgets.get_widget("notebook1")
00213                         bottom.set_group_id(1)
00214                         bottom.set_tab_detachable(localsWidget, True)
00215                         bottom.set_tab_detachable(stackWidget, True)
00216                 except:
00217                         pass
00218 
00219         
00220         def __init_right_book(self):
00221                 threadsWidget = self.__widgets.get_widget("threads")
00222                 regsWidget = self.__widgets.get_widget("registers")
00223                 self.add(view.Threads(threadsWidget))
00224                 self.add(view.Registers(regsWidget))
00225                 try:
00226                         right = self.__widgets.get_widget("notebook2")
00227                         right.set_group_id(1)
00228                         right.set_tab_detachable(threadsWidget, True)
00229                         right.set_tab_detachable(regsWidget, True)
00230                 except:
00231                         pass
00232 
00233 
00234         def clear(self):
00235                 super(ZApp, self).clear()
00236                 self.__process = None
00237 
00238 
00239         def update_debuggee_name(self, process):
00240                 name = process.name()
00241                 self.__process = process
00242                 if process.origin() <> zero.Process.Origin.Core:
00243                         self.__corefile = None
00244                 self.__window.set_title("Zero: " + name)
00245                 self.__set_filename()
00246 
00247 
00248         def update(self, event):
00249                 super(ZApp, self).update(event)
00250                 thread = event.thread()
00251                 self.__locals.show(thread.variables(zero.Scope.Local))
00252                 self.__thread = (thread.lwpid(), thread.id())
00253                 self.__debugger = thread.debugger()
00254 
00255 
00256         def load_exec(self, command):
00257                 if self.__debugger:
00258                         self.__debugger.load_exec(command)
00259 
00260 
00261         def load_core(self, corefile, program):
00262                 if self.__debugger:
00263                         self.__corefile = corefile
00264                         self.__debugger.load_core(corefile, program)
00265 
00266 
00267         def __on_filechooser_close(self, dialog):
00268                 dialog.hide()
00269 
00270 
00271         def __set_filename(self):
00272                 process = self.__process
00273                 if process and self.__fileButton:
00274                         if self.__corefile:
00275                                 self.__fileDialog.set_filename(self.__corefile)
00276                         else:
00277                                 self.__fileDialog.set_filename(process.name())
00278                         dlg.set_filename(self.__fileButton, process)
00279 
00280 
00281         #Prevent the resetting of the filename and image in
00282         # the filechooser button when the dialog is unmapped.
00283         def __on_unmap_event(self, w, event):
00284                 self.__set_filename()
00285 
00286 
00287         def __on_destroy(self, window):
00288                 if self.__debugger:
00289                         self.__debugger.quit()
00290                 self.bypass_builtin_command_interpreter(False)
00291                 gtk.main_quit()
00292 
00293 
00294         def __on_font_set(self, button):
00295                 print button.get_font_name()
00296                 # TODO: set font
00297 
00298 
00299         def __on_stack_selection(self, stackView, thread, frame):
00300                 self.__locals.show(thread.variables(zero.Scope.Local))
00301 
00302 
00303         def __on_tool_clicked(self, button):
00304                 tid = self.__thread
00305                 if tid is None:
00306                         return
00307                 thread = self.__debugger.get_thread(tid[0], tid[1])
00308                 if thread:
00309                         name = button.get_name()
00310                         if name[0:5] == "tool_":
00311                                 self.__debugger.command(name[5:], thread)
00312                         else:
00313                                 self.__debugger.command(name, thread)
00314 
00315 
00316         def on_state(self, active):
00317                 #
00318                 # set toolbar buttons sensitivity
00319                 #
00320                 toolbar = self.__widgets.get_widget("toolbar")
00321                 update_state(toolbar, active)
00322                 menu = self.__widgets.get_widget("menu_program").get_submenu()
00323                 update_state(menu, active)
00324 
00325 #end ZApp
00326 
00327 
00328 if __name__ == "__main__":
00329         app = ZApp()
00330 
00331         #update the ZApp view to reflect the state of the debuggee after the event
00332         def on_event(event):
00333                 if event.thread() is None:
00334                         return True
00335                 return app.update(event)
00336 
00337 
00338         def on_process(process, thread):
00339                 app.bypass_builtin_command_interpreter(True)
00340                 app.update_debuggee_name(process)
00341 
00342 
00343         def on_thread(thread):
00344                 #print '----- on_thread:', thread.lwpid(), '-----'
00345                 app.bypass_builtin_command_interpreter(True)
00346 
00347 
00348         def on_process_detach(process):
00349                 print '----- process detached -----'
00350                 app.clear()
00351                 app.bypass_builtin_command_interpreter(False)
00352 
00353 
00354         def on_command_loop_state(active):
00355                 app.on_state(active)
00356 
00357 
00358         def on_error(msg):
00359                 print msg
00360 
00361 
00362         gtk.main()