Module analyser
[hide private]
[frames] | no frames]

Source Code for Module analyser

  1  #!/usr/bin/env python 
  2   
  3  ### BITPIM 
  4  ### 
  5  ### Copyright (C) 2003-2004 Roger Binns <rogerb@rogerbinns.com> 
  6  ### 
  7  ### This program is free software; you can redistribute it and/or modify 
  8  ### it under the terms of the BitPim license as detailed in the LICENSE file. 
  9  ### 
 10  ### $Id: analyser.py 3037 2006-04-03 00:30:28Z rogerb $ 
 11   
 12  """Graphical view of protocol data and a decode of it""" 
 13   
 14  import sys 
 15  import re 
 16  import traceback 
 17  import wx 
 18  import StringIO 
 19  import struct 
 20   
 21  import common 
 22  import prototypes 
 23   
 24  import hexeditor 
 25   
26 -class Eventlist(wx.ListCtrl):
27 "List control showing the various events" 28
29 - def __init__(self, parent, id=-1, events=[]):
30 self.events=events 31 wx.ListCtrl.__init__(self, parent, id, style=wx.LC_REPORT|wx.LC_VIRTUAL) 32 self.InsertColumn(0, "Time") 33 self.InsertColumn(1, "Size") 34 self.InsertColumn(2, "Class") 35 self.InsertColumn(3, "Description") 36 37 self.SetColumnWidth(0, 100) 38 self.SetColumnWidth(1, 50) 39 self.SetColumnWidth(2, 200) 40 self.SetColumnWidth(3, 1000) 41 42 self.SetItemCount(len(events))
43
44 - def newdata(self, events):
45 self.DeleteAllItems() 46 self.events=events 47 self.SetItemCount(len(events))
48
49 - def OnGetItemText(self, index, col):
50 curtime, curdesc, curclass, curdata=self.events[index] 51 if col==0: 52 return curtime 53 if col==1: 54 if len(curdata): 55 return "%5d" % (len(curdata),) 56 return "" 57 if col==2: 58 return curclass 59 if col==3: 60 return curdesc 61 assert False
62
63 - def OnGetItemImage(self, item):
64 return -1
65 66 67
68 -class Analyser(wx.Frame):
69 """A top level frame for analysing protocol data""" 70 _pane_widths=[-2, -3, -4] 71 _pos_pane_index=0 72 _sel_pane_index=1 73 _val_pane_index=2 74
75 - def __init__(self, parent=None, id=-1, title="BitPim Protocol Analyser", data=None):
76 """Start the show 77 78 @param data: data to show. If None, then it will be obtained from the clipboard 79 """ 80 wx.Frame.__init__(self, parent, id, title, size=(800,750), 81 style=wx.DEFAULT_FRAME_STYLE) 82 # add a status bar to display various status items 83 self.CreateStatusBar(len(self._pane_widths)) 84 self.SetStatusWidths(self._pane_widths) 85 86 topsplit=wx.SplitterWindow(self, -1, style=wx.SP_3D|wx.SP_LIVE_UPDATE) 87 88 self.list=Eventlist(topsplit, 12) 89 90 botsplit=wx.SplitterWindow(topsplit, -1, style=wx.SP_3D|wx.SP_LIVE_UPDATE) 91 topsplit.SplitHorizontally(self.list, botsplit, 300) 92 93 self.tree=wx.TreeCtrl(botsplit, 23, style=wx.TR_DEFAULT_STYLE) 94 self.hex=hexeditor.HexEditor(botsplit, 95 _set_pos=self.set_pos, 96 _set_sel=self.set_sel, 97 _set_val=self.set_val) 98 botsplit.SplitHorizontally(self.tree, self.hex, 200) 99 100 if data is None: 101 data=self.getclipboarddata() 102 103 self.newdata(data) 104 105 wx.EVT_LIST_ITEM_SELECTED(self, self.list.GetId(), self.OnListBoxItem) 106 wx.EVT_LIST_ITEM_ACTIVATED(self, self.list.GetId(), self.OnListBoxItem) 107 108 wx.EVT_TREE_SEL_CHANGED(self, self.tree.GetId(), self.OnTreeSelection) 109 110 self.Show()
111
112 - def newdata(self, data):
113 "We have new data - the old data is tossed" 114 self.parsedata(data) 115 self.list.newdata(self.packets)
116
117 - def OnListBoxItem(self,evt):
118 "The user selected an event in the listbox" 119 index=evt.m_itemIndex 120 curtime, curdesc, curclass, curdata=self.packets[index] 121 self.errorinfo="" 122 self.hex.SetData("") 123 self.hex.highlightrange(-1,-1) 124 if len(curdata): 125 self.hex.SetData(curdata) 126 # self.hex.ShowPosition(self.hex.XYToPosition(0,0)) 127 else: 128 self.hex.SetData(curdesc) 129 # self.hex.ShowPosition(self.hex.XYToPosition(0,0)) 130 131 self.tree.DeleteAllItems() 132 if len(curclass): 133 b=prototypes.buffer(curdata) 134 try: 135 klass=common.getfullname(curclass) 136 except Exception,e: 137 self.errorme("Finding class",e) 138 wx.TipWindow(self.tree,self.errorinfo) 139 return 140 try: 141 obj=klass() 142 except Exception,e: 143 self.errorme("Instantiating object",e) 144 wx.TipWindow(self.tree,self.errorinfo) 145 return 146 147 try: 148 obj.readfrombuffer(b, autolog=False) 149 except Exception,e: 150 self.errorme("Reading from buffer",e) 151 # no return, we persevere 152 153 root=self.tree.AddRoot(curclass) 154 try: 155 self.tree.SetPyData(root, obj.packetspan()) 156 except: 157 self.errorme("Object did not construct correctly") 158 # no return, we persevere 159 self.addtreeitems(obj, root) 160 if len(self.errorinfo): 161 wx.TipWindow(self.tree,self.errorinfo)
162
163 - def addtreeitems(self, obj, parent):
164 "Add fields from obj to parent node" 165 try: 166 for name,field,desc in obj.containerelements(): 167 if desc is None: 168 desc="" 169 else: 170 desc=" - "+desc 171 iscontainer=False 172 try: 173 iscontainer=field.iscontainer() 174 except: 175 pass 176 # Add ourselves 177 s=field.__class__.__name__+" "+name 178 if iscontainer: 179 c=field.__class__ 180 s+=": <%s.%s>" % (c.__module__, c.__name__) 181 else: 182 try: 183 v=field.getvalue() 184 except Exception,e: 185 v="<Exception: "+e.__str__()+">" 186 s+=": " 187 if isinstance(v, int) and not isinstance(v, type(True)): 188 s+="%d 0x%x" % (v,v) 189 else: 190 s+=`v` 191 if len(desc): 192 s+=desc 193 node=self.tree.AppendItem(parent, s) 194 try: 195 self.tree.SetPyData(node, field.packetspan()) 196 except: 197 pass 198 if iscontainer: 199 self.addtreeitems(field, node) 200 except Exception,e: 201 str="<Exception: "+e.__str__()+">" 202 self.tree.AppendItem(parent,str)
203
204 - def OnTreeSelection(self, evt):
205 "User selected an item in the tree" 206 item=evt.GetItem() 207 try: 208 start,end=self.tree.GetPyData(item) 209 except: 210 self.hex.highlightrange(-1,-1) 211 return 212 self.hex.highlightrange(start,end)
213 # self.hex.ShowPosition(begin) 214
215 - def errorme(self, desc, exception=None):
216 "Put exception information into the hex pane and output traceback to console" 217 if exception is not None: 218 x=StringIO.StringIO() 219 print >>x,exception.__str__(), 220 self.errorinfo+=x.getvalue()+" : " 221 print >>sys.stderr, common.formatexception() 222 self.errorinfo+=desc+"\n"
223
224 - def getclipboarddata(self):
225 """Gets text data on clipboard""" 226 do=wx.TextDataObject() 227 wx.TheClipboard.Open() 228 success=wx.TheClipboard.GetData(do) 229 wx.TheClipboard.Close() 230 if not success: 231 wx.MessageBox("Whatever is in the clipboard isn't text", "No way Dude") 232 return "" 233 return do.GetText()
234 235 patevent=re.compile(r"^(\d?\d:\d\d:\d\d\.\d\d\d)(.*)") 236 patdataevent=re.compile(r"^(\d?\d:\d\d:\d\d\.\d\d\d)(.*)(Data - \d+ bytes.*)") 237 patdatarow=re.compile(r"^([0-9A-Fa-f]{8})(.*)") 238 patclass=re.compile(r"^<#!\s+(.*)\s+!#>") 239
240 - def parsedata(self, data):
241 """Fills in our internal data structures based on contents of data""" 242 243 # santise all the data by doing the eol nonsense 244 data=data.replace("\r", "\n") 245 lastlen=0 246 while lastlen!=len(data): 247 lastlen=len(data) 248 data=data.replace("\n\n", "\n") 249 250 self.packets=[] 251 252 curtime=curdesc=curclass=curdata="" 253 254 indata=False 255 256 for line in data.split('\n'): 257 # ignore blank lines 258 if len(line.strip())==0: 259 continue 260 mo=self.patclass.match(line) 261 if mo is not None: 262 # found a class description 263 curclass=mo.group(1) 264 indata=True 265 continue 266 # if indata, try for some more 267 if indata: 268 mo=self.patdatarow.match(line) 269 if mo is not None: 270 # found another data row 271 pos=int(mo.group(1), 16) 272 assert pos==len(curdata) 273 for i in range(9, min(len(line), 9+16*3), 3): # at most 16 bytes 274 s=line[i:i+2] 275 if len(s)!=2 or s==" ": 276 # last line with trailing spaces 277 continue 278 b=int(s,16) 279 curdata+=chr(b) 280 continue 281 # end of data, save it 282 indata=False 283 self.packets.append( (curtime, curdesc, curclass, curdata) ) 284 curtime=curdesc=curclass=curdata="" 285 # and move on 286 # data event? 287 mo=self.patdataevent.match(line) 288 if mo is not None: 289 self.packets.append( (curtime, curdesc, curclass, curdata) ) 290 curtime=curdesc=curclass=curdata="" 291 curtime=mo.group(1) 292 curdesc=mo.group(2)+mo.group(3) 293 indata=True 294 continue 295 # ordinary event? 296 mo=self.patevent.match(line) 297 if mo is not None: 298 self.packets.append( (curtime, curdesc, curclass, curdata) ) 299 curtime=curdesc=curclass=curdata="" 300 curtime=mo.group(1) 301 curdesc=mo.group(2) 302 indata=True 303 continue 304 # No idea what it is, just add on end of desc 305 if len(curdesc): 306 curdesc+="\n" 307 curdesc+=line 308 309 # Add whatever is in variables at end 310 self.packets.append( (curtime, curdesc, curclass, curdata) ) 311 312 # remove all blank lines 313 # filter, reduce, map and lambda all in one go! 314 self.packets=filter(lambda item: reduce(lambda x,y: x+y, map(len, item)), self.packets)
315
316 - def set_pos(self, pos):
317 """Display the current buffer offset in the format of 318 Pos: 0x12=18 319 """ 320 if pos is None: 321 s='' 322 else: 323 s='Pos: 0x%X=%d'%(pos, pos) 324 self.SetStatusText(s, self._pos_pane_index)
325 - def set_sel(self, sel_start, sel_end):
326 if sel_start is None or sel_start==-1 or\ 327 sel_end is None or sel_end==-1: 328 s='' 329 else: 330 sel_len=sel_end-sel_start 331 sel_end-=1 332 s='Sel: 0x%X=%d to 0x%X=%d (0x%X=%d bytes)'%( 333 sel_start, sel_start, sel_end, sel_end, 334 sel_len, sel_len) 335 self.SetStatusText(s, self._sel_pane_index)
336 - def set_val(self, v):
337 if v: 338 # char 339 s='Val: 0x%02X=%d'%(ord(v[0]), ord(v[0])) 340 if len(v)>1: 341 # short 342 u_s=struct.unpack('<H', v[:struct.calcsize('<H')])[0] 343 s+=' 0x%04X=%d'%(u_s, u_s) 344 if len(v)>3: 345 # int/long 346 u_i=struct.unpack('<I', v[:struct.calcsize('<I')])[0] 347 s+=' 0x%08X=%d'%(u_i, u_i) 348 else: 349 s='' 350 self.SetStatusText(s, self._val_pane_index)
351 352 if __name__=='__main__': 353 app=wx.PySimpleApp() 354 # Find the data source 355 data=None 356 if len(sys.argv)==2: 357 # From a file 358 data=common.opentextfile(sys.argv[1]).read() 359 frame=Analyser(data=data) 360 app.MainLoop() 361