~mcfletch/eric/update-to-4.5.13

1.2.3 by Gudjon I. Gudjonsson
Import upstream version 4.4.1
1
# -*- coding: utf-8 -*-
2
0.165.2 by Gudjon I. Gudjonsson
Import upstream version 4.5.0
3
# Copyright (c) 2009 - 2012 Detlev Offenbach <detlev@die-offenbachs.de>
1.2.3 by Gudjon I. Gudjonsson
Import upstream version 4.4.1
4
#
5
6
"""
7
Module implementing the debug base class.
8
"""
9
10
import sys
11
import traceback
12
import bdb
13
import os
14
import types
15
import atexit
16
import inspect
17
18
from DebugProtocol import *
19
20
gRecursionLimit = 64
21
22
def printerr(s):
23
    """
24
    Module function used for debugging the debug client.
25
    
26
    @param s data to be printed
27
    """
28
    import sys
29
    sys.__stderr__.write('{0!s}\n'.format(s))
30
    sys.__stderr__.flush()
31
32
def setRecursionLimit(limit):
33
    """
34
    Module function to set the recursion limit.
35
    
36
    @param limit recursion limit (integer)
37
    """
38
    global gRecursionLimit
39
    gRecursionLimit = limit
40
41
class DebugBase(bdb.Bdb):
42
    """
43
    Class implementing base class of the debugger.
44
45
    Provides simple wrapper methods around bdb for the 'owning' client to
46
    call to step etc.
47
    """
48
    def __init__(self, dbgClient):
49
        """
50
        Constructor
51
        
52
        @param dbgClient the owning client
53
        """
54
        bdb.Bdb.__init__(self)
55
56
        self._dbgClient = dbgClient
57
        self._mainThread = True
58
        
59
        self.breaks = self._dbgClient.breakpoints
60
        
61
        self.__event = ""
62
        self.__isBroken = ""
63
        self.cFrame = None
64
        
65
        # current frame we are at
66
        self.currentFrame = None
0.111.2 by Gudjon I. Gudjonsson
Import upstream version 4.4.15
67
        self.currentFrameLocals = None
1.2.3 by Gudjon I. Gudjonsson
Import upstream version 4.4.1
68
        
69
        # frame that we are stepping in, can be different than currentFrame
70
        self.stepFrame = None
71
        
72
        # provide a hook to perform a hard breakpoint
73
        # Use it like this:
74
        # if hasattr(sys, 'breakpoint): sys.breakpoint()
75
        sys.breakpoint = self.set_trace
76
        
77
        # initialize parent
78
        bdb.Bdb.reset(self)
79
        
80
        self.__recursionDepth = -1
81
        self.setRecursionDepth(inspect.currentframe())
82
    
83
    def getCurrentFrame(self):
84
        """
85
        Public method to return the current frame.
86
        
87
        @return the current frame
88
        """
89
        return self.currentFrame        
90
    
0.111.2 by Gudjon I. Gudjonsson
Import upstream version 4.4.15
91
    def getCurrentFrameLocals(self):
92
        """
93
        Public method to return the locals dictionary of the current frame.
94
        
95
        @return locals dictionary of the current frame
96
        """
97
        return self.currentFrameLocals        
98
    
1.2.3 by Gudjon I. Gudjonsson
Import upstream version 4.4.1
99
    def step(self, traceMode):
100
        """
101
        Public method to perform a step operation in this thread.
102
        
103
        @param traceMode If it is True, then the step is a step into,
104
              otherwise it is a step over.
105
        """
106
        self.stepFrame = self.currentFrame
107
        
108
        if traceMode:
109
            self.currentFrame = None
110
            self.set_step()
111
        else:
112
            self.set_next(self.currentFrame)
113
    
114
    def stepOut(self):
115
        """
116
        Public method to perform a step out of the current call.
117
        """
118
        self.stepFrame = self.currentFrame
119
        self.set_return(self.currentFrame)
120
    
121
    def go(self, special):
122
        """
123
        Public method to resume the thread.
124
125
        It resumes the thread stopping only at breakpoints or exceptions.
126
        
127
        @param special flag indicating a special continue operation
128
        """
129
        self.currentFrame = None
130
        self.set_continue(special)
131
    
132
    def setRecursionDepth(self, frame):
133
        """
134
        Public method to determine the current recursion depth.
135
        
136
        @param frame The current stack frame.
137
        """
138
        self.__recursionDepth = 0
139
        while frame is not None:
140
            self.__recursionDepth += 1
141
            frame = frame.f_back
142
    
143
    def profile(self, frame, event, arg):
144
        """
145
        Public method used to trace some stuff independant of the debugger 
146
        trace function.
147
        
148
        @param frame The current stack frame.
149
        @param event The trace event (string)
150
        @param arg The arguments
151
        """
152
        if event == 'return':  
153
            self.cFrame = frame.f_back
154
            self.__recursionDepth -= 1
155
        elif event == 'call':
156
            self.cFrame = frame
157
            self.__recursionDepth += 1
158
            if self.__recursionDepth > gRecursionLimit:
159
                raise RuntimeError('maximum recursion depth exceeded\n'
160
                    '(offending frame is two down the stack)')
161
    
162
    def trace_dispatch(self, frame, event, arg):
163
        """
164
        Reimplemented from bdb.py to do some special things.
165
        
166
        This specialty is to check the connection to the debug server
167
        for new events (i.e. new breakpoints) while we are going through
168
        the code.
169
        
170
        @param frame The current stack frame.
171
        @param event The trace event (string)
172
        @param arg The arguments
173
        @return local trace function
174
        """
175
        if self.quitting:
176
            return # None
177
        
178
        # give the client a chance to push through new break points.
179
        self._dbgClient.eventPoll()
180
        
181
        self.__event == event
182
        self.__isBroken = False
183
        
184
        if event == 'line':
185
            return self.dispatch_line(frame)
186
        if event == 'call':
187
            return self.dispatch_call(frame, arg)
188
        if event == 'return':
189
            return self.dispatch_return(frame, arg)
190
        if event == 'exception':
191
            return self.dispatch_exception(frame, arg)
192
        if event == 'c_call':
193
            return self.trace_dispatch
194
        if event == 'c_exception':
195
            return self.trace_dispatch
196
        if event == 'c_return':
197
            return self.trace_dispatch
198
        print('bdb.Bdb.dispatch: unknown debugging event: ', repr(event))
199
        return self.trace_dispatch
200
201
    def dispatch_line(self, frame):
202
        """
203
        Reimplemented from bdb.py to do some special things.
204
        
205
        This speciality is to check the connection to the debug server
206
        for new events (i.e. new breakpoints) while we are going through
207
        the code.
208
        
209
        @param frame The current stack frame.
210
        @return local trace function
211
        """
212
        if self.stop_here(frame) or self.break_here(frame):
213
            self.user_line(frame)
214
            if self.quitting: raise bdb.BdbQuit
215
        return self.trace_dispatch
216
217
    def dispatch_return(self, frame, arg):
218
        """
219
        Reimplemented from bdb.py to handle passive mode cleanly.
220
        
221
        @param frame The current stack frame.
222
        @param arg The arguments
223
        @return local trace function
224
        """
225
        if self.stop_here(frame) or frame == self.returnframe:
226
            self.user_return(frame, arg)
227
            if self.quitting and not self._dbgClient.passive:
228
                raise bdb.BdbQuit
229
        return self.trace_dispatch
230
231
    def dispatch_exception(self, frame, arg):
232
        """
233
        Reimplemented from bdb.py to always call user_exception.
234
        
235
        @param frame The current stack frame.
236
        @param arg The arguments
237
        @return local trace function
238
        """
239
        if not self.__skip_it(frame):
240
            self.user_exception(frame, arg)
241
            if self.quitting: raise bdb.BdbQuit
242
        return self.trace_dispatch
243
244
    def set_trace(self, frame = None):
245
        """
246
        Overridden method of bdb.py to do some special setup.
247
        
248
        @param frame frame to start debugging from
249
        """
250
        bdb.Bdb.set_trace(self, frame)
251
        sys.setprofile(self.profile)
252
    
253
    def set_continue(self, special):
254
        """
255
        Reimplemented from bdb.py to always get informed of exceptions.
256
        
257
        @param special flag indicating a special continue operation
258
        """
259
        # Modified version of the one found in bdb.py
260
        # Here we only set a new stop frame if it is a normal continue.
261
        if not special:
262
            self._set_stopinfo(self.botframe, None)
263
        else:
264
            self._set_stopinfo(self.stopframe, None)
265
266
    def set_quit(self):
267
        """
268
        Public method to quit. 
269
        
270
        It wraps call to bdb to clear the current frame properly.
271
        """
272
        self.currentFrame = None
273
        sys.setprofile(None)
274
        bdb.Bdb.set_quit(self)
275
    
276
    def fix_frame_filename(self, frame):
277
        """
278
        Public method used to fixup the filename for a given frame.
279
        
280
        The logic employed here is that if a module was loaded
281
        from a .pyc file, then the correct .py to operate with
282
        should be in the same path as the .pyc. The reason this
283
        logic is needed is that when a .pyc file is generated, the
284
        filename embedded and thus what is readable in the code object
285
        of the frame object is the fully qualified filepath when the
286
        pyc is generated. If files are moved from machine to machine
287
        this can break debugging as the .pyc will refer to the .py
288
        on the original machine. Another case might be sharing
289
        code over a network... This logic deals with that.
290
        
291
        @param frame the frame object
292
        """
293
        # get module name from __file__
294
        if '__file__' in frame.f_globals and \
295
           frame.f_globals['__file__'] and \
296
           frame.f_globals['__file__'] == frame.f_code.co_filename:
297
            root, ext = os.path.splitext(frame.f_globals['__file__'])
298
            if ext in ['.pyc', '.py', '.py3', '.pyo']:
299
                fixedName = root + '.py'
300
                if os.path.exists(fixedName):
301
                    return fixedName
302
                
303
                fixedName = root + '.py3'
304
                if os.path.exists(fixedName):
305
                    return fixedName
306
307
        return frame.f_code.co_filename
308
309
    def set_watch(self, cond, temporary = False):
310
        """
311
        Public method to set a watch expression.
312
        
313
        @param cond expression of the watch expression (string)
314
        @param temporary flag indicating a temporary watch expression (boolean)
315
        """
316
        bp = bdb.Breakpoint("Watch", 0, temporary, cond)
317
        if cond.endswith('??created??') or cond.endswith('??changed??'):
318
            bp.condition, bp.special = cond.split()
319
        else:
320
            bp.condition = cond
321
            bp.special = ""
322
        bp.values = {}
323
        if "Watch" not in self.breaks:
324
            self.breaks["Watch"] = 1
325
        else:
326
            self.breaks["Watch"] += 1
327
    
328
    def clear_watch(self, cond):
329
        """
330
        Public method to clear a watch expression.
331
        
332
        @param cond expression of the watch expression to be cleared (string)
333
        """
334
        try: 
335
            possibles = bdb.Breakpoint.bplist["Watch", 0]
336
            for i in range(0, len(possibles)):
337
                b = possibles[i]
338
                if b.cond == cond:
339
                    b.deleteMe()
340
                    self.breaks["Watch"] -= 1
341
                    if self.breaks["Watch"] == 0:
342
                        del self.breaks["Watch"]
343
                    break
344
        except KeyError:
345
            pass
346
    
347
    def get_watch(self, cond):
348
        """
349
        Public method to get a watch expression.
350
        
351
        @param cond expression of the watch expression to be cleared (string)
352
        """
353
        possibles = bdb.Breakpoint.bplist["Watch", 0]
354
        for i in range(0, len(possibles)):
355
            b = possibles[i]
356
            if b.cond == cond:
357
                return b
358
    
359
    def __do_clearWatch(self, cond):
360
        """
361
        Private method called to clear a temporary watch expression.
362
        
363
        @param cond expression of the watch expression to be cleared (string)
364
        """
365
        self.clear_watch(cond)
366
        self._dbgClient.write('{0}{1}\n'.format(ResponseClearWatch, cond))
367
368
    def __effective(self, frame):
369
        """
370
        Private method to determine, if a watch expression is effective.
371
        
372
        @param frame the current execution frame
373
        @return tuple of watch expression and a flag to indicate, that a temporary
374
            watch expression may be deleted (bdb.Breakpoint, boolean)
375
        """
376
        possibles = bdb.Breakpoint.bplist["Watch", 0]
377
        for i in range(0, len(possibles)):
378
            b = possibles[i]
379
            if not b.enabled:
380
                continue
381
            if not b.cond:
382
                # watch expression without expression shouldn't occur, just ignore it
383
                continue
384
            try:
385
                val = eval(b.condition, frame.f_globals, frame.f_locals)
386
                if b.special:
387
                    if b.special == '??created??':
388
                        if b.values[frame][0] == 0:
389
                            b.values[frame][0] = 1
390
                            b.values[frame][1] = val
391
                            return (b, True)
392
                        else:
393
                            continue
394
                    b.values[frame][0] = 1
395
                    if b.special == '??changed??':
396
                        if b.values[frame][1] != val:
397
                            b.values[frame][1] = val
398
                            if b.values[frame][2] > 0:
399
                                b.values[frame][2] -= 1
400
                                continue
401
                            else:
402
                                return (b, True)
403
                        else:
404
                            continue
405
                    continue
406
                if val:
407
                    if b.ignore > 0:
408
                        b.ignore -= 1
409
                        continue
410
                    else:
411
                        return (b, True)
412
            except:
413
                if b.special:
414
                    try:
415
                        b.values[frame][0] = 0
416
                    except KeyError:
417
                        b.values[frame] = [0, None, b.ignore]
418
                continue
419
        
420
        return (None, False)
421
    
422
    def break_here(self, frame):
423
        """
424
        Reimplemented from bdb.py to fix the filename from the frame. 
425
        
426
        See fix_frame_filename for more info.
427
        
428
        @param frame the frame object
429
        @return flag indicating the break status (boolean)
430
        """
431
        filename = self.canonic(self.fix_frame_filename(frame))
432
        if filename not in self.breaks and "Watch" not in self.breaks:
433
            return False
434
        
435
        if filename in self.breaks:
436
            lineno = frame.f_lineno
437
            if lineno not in self.breaks[filename]:
438
                # The line itself has no breakpoint, but maybe the line is the
439
                # first line of a function with breakpoint set by function name.
440
                lineno = frame.f_code.co_firstlineno
441
            if lineno in self.breaks[filename]:
442
                # flag says ok to delete temp. bp
443
                (bp, flag) = bdb.effective(filename, lineno, frame)
444
                if bp:
445
                    self.currentbp = bp.number
446
                    if (flag and bp.temporary):
447
                        self.__do_clear(filename, lineno)
448
                    return True
449
        
450
        if "Watch" in self.breaks:
451
            # flag says ok to delete temp. bp
452
            (bp, flag) = self.__effective(frame)
453
            if bp:
454
                self.currentbp = bp.number
455
                if (flag and bp.temporary):
456
                    self.__do_clearWatch(bp.cond)
457
                return True
458
        
459
        return False
460
461
    def break_anywhere(self, frame):
462
        """
463
        Reimplemented from bdb.py to do some special things.
464
        
465
        These speciality is to fix the filename from the frame
466
        (see fix_frame_filename for more info).
467
        
468
        @param frame the frame object
469
        @return flag indicating the break status (boolean)
470
        """
471
        return \
472
            self.canonic(self.fix_frame_filename(frame)) in self.breaks or \
473
            ("Watch" in self.breaks and self.breaks["Watch"])
474
475
    def get_break(self, filename, lineno):
476
        """
477
        Reimplemented from bdb.py to get the first breakpoint of a particular line.
478
        
479
        Because eric4 supports only one breakpoint per line, this overwritten
480
        method will return this one and only breakpoint.
481
        
482
        @param filename the filename of the bp to retrieve (string)
483
        @param lineno the linenumber of the bp to retrieve (integer)
484
        @return breakpoint or None, if there is no bp
485
        """
486
        filename = self.canonic(filename)
487
        return filename in self.breaks and \
488
            lineno in self.breaks[filename] and \
489
            bdb.Breakpoint.bplist[filename, lineno][0] or None
490
    
491
    def __do_clear(self, filename, lineno):
492
        """
493
        Private method called to clear a temporary breakpoint.
494
        
495
        @param filename name of the file the bp belongs to
496
        @param lineno linenumber of the bp
497
        """
498
        self.clear_break(filename, lineno)
499
        self._dbgClient.write('{0}{1},{2:d}\n'.format(
500
                              ResponseClearBreak, filename, lineno))
501
502
    def getStack(self):
503
        """
504
        Public method to get the stack.
505
        
506
        @return list of lists with file name (string), line number (integer)
507
            and function name (string)
508
        """
509
        fr = self.cFrame
510
        stack = []
511
        while fr is not None:
512
            fname = self._dbgClient.absPath(self.fix_frame_filename(fr))
513
            fline = fr.f_lineno
514
            ffunc = fr.f_code.co_name
515
            
516
            if ffunc == '?':
517
                ffunc = ''
518
            
519
            stack.append([fname, fline, ffunc])
520
            
521
            if fr == self._dbgClient.mainFrame:
522
                fr = None
523
            else:
524
                fr = fr.f_back
525
        
526
        return stack
527
    
528
    def user_line(self, frame):
529
        """
530
        Reimplemented to handle the program about to execute a particular line.
531
        
532
        @param frame the frame object
533
        """
534
        line = frame.f_lineno
535
536
        # We never stop on line 0.
537
        if line == 0:
538
            return
539
540
        fn = self._dbgClient.absPath(self.fix_frame_filename(frame))
541
542
        # See if we are skipping at the start of a newly loaded program.
543
        if self._dbgClient.mainFrame is None:
544
            if fn != self._dbgClient.getRunning():
545
                return
546
            self._dbgClient.mainFrame = frame
547
548
        self.currentFrame = frame
0.111.2 by Gudjon I. Gudjonsson
Import upstream version 4.4.15
549
        self.currentFrameLocals = frame.f_locals
550
        # remember the locals because it is reinitialized when accessed
0.165.2 by Gudjon I. Gudjonsson
Import upstream version 4.5.0
551
1.2.3 by Gudjon I. Gudjonsson
Import upstream version 4.4.1
552
        fr = frame
553
        stack = []
554
        while fr is not None:
555
            # Reset the trace function so we can be sure
556
            # to trace all functions up the stack... This gets around
557
            # problems where an exception/breakpoint has occurred
558
            # but we had disabled tracing along the way via a None
559
            # return from dispatch_call
560
            fr.f_trace = self.trace_dispatch
561
            fname = self._dbgClient.absPath(self.fix_frame_filename(fr))
562
            fline = fr.f_lineno
563
            ffunc = fr.f_code.co_name
564
            
565
            if ffunc == '?':
566
                ffunc = ''
567
            
568
            stack.append([fname, fline, ffunc])
569
            
570
            if fr == self._dbgClient.mainFrame:
571
                fr = None
572
            else:
573
                fr = fr.f_back
574
        
575
        self.__isBroken = True
576
        
577
        self._dbgClient.write('{0}{1}\n'.format(ResponseLine, str(stack)))
578
        self._dbgClient.eventLoop()
579
580
    def user_exception(self, frame, excinfo, unhandled = False):
581
        """
582
        Reimplemented to report an exception to the debug server.
583
        
584
        @param frame the frame object
585
        @param excinfo information about the exception
586
        @param unhandled flag indicating an uncaught exception
587
        """
588
        exctype, excval, exctb = excinfo
589
        if exctype in [SystemExit, bdb.BdbQuit]:
590
            atexit._run_exitfuncs()
0.165.2 by Gudjon I. Gudjonsson
Import upstream version 4.5.0
591
            if excval is None:
592
                excval = 0
593
            elif isinstance(excval, str):
594
                self._dbgClient.write(excval)
595
                excval = 1
596
            elif isinstance(excval, bytes):
597
                self._dbgClient.write(excval.decode())
598
                excval = 1
1.2.3 by Gudjon I. Gudjonsson
Import upstream version 4.4.1
599
            if isinstance(excval, int):
600
                self._dbgClient.progTerminated(excval)
601
            else:
602
                self._dbgClient.progTerminated(excval.code)
603
            return
604
        
605
        elif exctype in [SyntaxError, IndentationError]:
606
            try:
607
                message, (filename, linenr, charnr, text) = excval[0], excval[1]
608
            except ValueError:
609
                exclist = []
610
            else:
611
                exclist = [message, [filename, linenr, charnr]]
612
            
613
            self._dbgClient.write("{0}{1}\n".format(ResponseSyntax, str(exclist)))
614
        
615
        else:
616
            exctype = self.__extractExceptionName(exctype)
617
            
618
            if excval is None:
619
                excval = ''
620
            
621
            if unhandled:
622
                exctypetxt = "unhandled {0!s}".format(str(exctype))
623
            else:
624
                exctypetxt = str(exctype)
625
            try:
626
                exclist = [exctypetxt, 
627
                           str(excval).encode(
628
                                self._dbgClient.getCoding(), 'backslashreplace')]
629
            except TypeError:
630
                exclist = [exctypetxt, str(excval)]
631
            
632
            if exctb:
633
                frlist = self.__extract_stack(exctb)
634
                frlist.reverse()
635
                
636
                self.currentFrame = frlist[0]
637
                
638
                for fr in frlist:
639
                    filename = self._dbgClient.absPath(self.fix_frame_filename(fr))
640
                    linenr = fr.f_lineno
641
                    
642
                    if os.path.basename(filename).startswith("DebugClient") or \
643
                       os.path.basename(filename) == "bdb.py":
644
                        break
645
                    
646
                    exclist.append([filename, linenr])
647
            
648
            self._dbgClient.write("{0}{1}\n".format(ResponseException, str(exclist)))
649
            
650
            if exctb is None:
651
                return
652
            
653
        self._dbgClient.eventLoop()
654
    
655
    def __extractExceptionName(self, exctype):
656
        """
657
        Private method to extract the exception name given the exception
658
        type object.
659
        
660
        @param exctype type of the exception
661
        """
662
        return repr(exctype).replace("<class '", "").replace("'>", "")
663
    
664
    def __extract_stack(self, exctb):
665
        """
666
        Private member to return a list of stack frames.
667
        
668
        @param exctb exception traceback
669
        @return list of stack frames
670
        """
671
        tb = exctb
672
        stack = []
673
        while tb is not None:
674
            stack.append(tb.tb_frame)
675
            tb = tb.tb_next
676
        tb = None
677
        return stack
678
679
    def user_return(self, frame, retval):
680
        """
681
        Reimplemented to report program termination to the debug server.
682
        
683
        @param frame the frame object
684
        @param retval the return value of the program
685
        """
686
        # The program has finished if we have just left the first frame.
687
        if frame == self._dbgClient.mainFrame and \
688
            self._mainThread:
689
            atexit._run_exitfuncs()
690
            self._dbgClient.progTerminated(retval)
691
        elif frame is not self.stepFrame:
692
            self.stepFrame = None
693
            self.user_line(frame)
694
695
    def stop_here(self, frame):
696
        """
697
        Reimplemented to filter out debugger files.
698
        
699
        Tracing is turned off for files that are part of the
700
        debugger that are called from the application being debugged.
701
        
702
        @param frame the frame object
703
        @return flag indicating whether the debugger should stop here
704
        """
705
        if self.__skip_it(frame):
706
            return False
707
        
708
        return bdb.Bdb.stop_here(self,frame)
709
710
    def __skip_it(self, frame):
711
        """
712
        Private method to filter out debugger files.
713
        
714
        Tracing is turned off for files that are part of the
715
        debugger that are called from the application being debugged.
716
        
717
        @param frame the frame object
718
        @return flag indicating whether the debugger should skip this frame
719
        """
720
        fn = self.fix_frame_filename(frame)
721
722
        # Eliminate things like <string> and <stdin>.
723
        if fn[0] == '<':
724
            return 1
725
726
        #XXX - think of a better way to do this.  It's only a convience for
727
        #debugging the debugger - when the debugger code is in the current
728
        #directory.
729
        if os.path.basename(fn) in [\
730
            'AsyncFile.py', 'AsyncIO.py',
731
            'DebugConfig.py', 'DCTestResult.py',
732
            'DebugBase.py', 'DebugClientBase.py', 
733
            'DebugClientCapabilities.py', 'DebugClient.py', 
734
            'DebugClientThreads.py', 'DebugProtocol.py',
735
            'DebugThread.py', 'FlexCompleter.py',
736
            'PyProfile.py'] or \
737
           os.path.dirname(fn).endswith("coverage"):
738
            return True
739
740
        if self._dbgClient.shouldSkip(fn):
741
            return True
742
        
743
        return False
744
    
745
    def isBroken(self):
746
        """
747
        Public method to return the broken state of the debugger.
748
        
749
        @return flag indicating the broken state (boolean)
750
        """
751
        return self.__isBroken
752
    
753
    def getEvent(self):
754
        """
755
        Public method to return the last debugger event.
756
        
757
        @return last debugger event (string)
758
        """
759
        return self.__event