@@ -78,7 +78,7 @@ def filter_var_dict(d):
7878
7979class PGLogger (bdb .Bdb ):
8080
81- def __init__ (self , finalizer_func ):
81+ def __init__ (self , finalizer_func , cumulative_display = False ):
8282 bdb .Bdb .__init__ (self )
8383 self .mainpyfile = ''
8484 self ._wait_for_mainpyfile = 0
@@ -87,6 +87,12 @@ def __init__(self, finalizer_func):
8787 # processes it
8888 self .finalizer_func = finalizer_func
8989
90+ # if True, then displays ALL stack frames that have ever existed
91+ # rather than only those currently on the stack (and their
92+ # lexical parents)
93+ self .cumulative_display = cumulative_display
94+ self .cumulative_display = True
95+
9096 # each entry contains a dict with the information for a single
9197 # executed line
9298 self .trace = []
@@ -98,10 +104,16 @@ def __init__(self, finalizer_func):
98104 # Value: parent frame
99105 self .closures = {}
100106
101- # List of frames to KEEP AROUND after the function exits
102- # because nested functions were defined within those frames.
103- # (ORDER matters for aesthetics)
104- self .zombie_parent_frames = []
107+ # Key: id() of frame object
108+ # Value: monotonically increasing small ID, based on call order
109+ self .frame_ordered_ids = {}
110+ self .cur_frame_id = 1
111+
112+ # List of frames to KEEP AROUND after the function exits.
113+ # If cumulative_display is True, then keep ALL frames in
114+ # zombie_frames; otherwise keep only frames where
115+ # nested functions were defined within them.
116+ self .zombie_frames = []
105117
106118 # all globals that ever appeared in the program, in the order in
107119 # which they appeared. note that this might be a superset of all
@@ -116,6 +128,10 @@ def __init__(self, finalizer_func):
116128 self .executed_script = None # Python script to be executed!
117129
118130
131+ def get_frame_id (self , cur_frame ):
132+ return self .frame_ordered_ids [id (cur_frame )]
133+
134+
119135 # Returns the (lexical) parent frame of the function that was called
120136 # to create the stack frame 'frame'.
121137 #
@@ -148,19 +164,12 @@ def get_parent_frame(self, frame):
148164 return None
149165
150166
151- def get_zombie_parent_frame_id (self , f ):
152- # should be None unless this is a zombie frame
153- try :
154- # make the frame id's one-indexed for clarity
155- # (and to prevent possible confusion with None)
156- return self .zombie_parent_frames .index (f ) + 1
157- except ValueError :
158- pass
159- return None
160-
161- def lookup_zombie_frame_by_id (self , idx ):
162- # remember this is one-indexed
163- return self .zombie_parent_frames [idx - 1 ]
167+ def lookup_zombie_frame_by_id (self , frame_id ):
168+ # TODO: kinda inefficient
169+ for e in self .zombie_frames :
170+ if self .get_frame_id (e ) == frame_id :
171+ return e
172+ assert False # should never get here
164173
165174
166175 # unused ...
@@ -227,10 +236,19 @@ def interaction(self, frame, traceback, event_type):
227236 self .encoder .reset_heap () # VERY VERY VERY IMPORTANT,
228237 # or else we won't properly capture heap object mutations in the trace!
229238
239+ if event_type == 'call' :
240+ tfid = id (top_frame )
241+ assert tfid not in self .frame_ordered_ids
242+ self .frame_ordered_ids [tfid ] = self .cur_frame_id
243+ self .cur_frame_id += 1
244+
245+ if self .cumulative_display :
246+ self .zombie_frames .append (top_frame )
247+
230248
231249 # only render zombie frames that are NO LONGER on the stack
232250 cur_stack_frames = [e [0 ] for e in self .stack ]
233- zombie_frames_to_render = [e for e in self .zombie_parent_frames if e not in cur_stack_frames ]
251+ zombie_frames_to_render = [e for e in self .zombie_frames if e not in cur_stack_frames ]
234252
235253
236254 # each element is a pair of (function name, ENCODED locals dict)
@@ -242,15 +260,13 @@ def create_encoded_stack_entry(cur_frame):
242260 ret = {}
243261
244262
245- # your immediate parent frame ID is parent_frame_id_list[0]
246- # and all other members are your further ancestors
247263 parent_frame_id_list = []
248264
249265 f = cur_frame
250266 while True :
251267 p = self .get_parent_frame (f )
252268 if p :
253- pid = self .get_zombie_parent_frame_id (p )
269+ pid = self .get_frame_id (p )
254270 assert pid
255271 parent_frame_id_list .append (pid )
256272 f = p
@@ -299,7 +315,7 @@ def create_encoded_stack_entry(cur_frame):
299315 if type (v ) in (types .FunctionType , types .MethodType ):
300316 try :
301317 enclosing_frame = self .closures [v ]
302- enclosing_frame_id = self .get_zombie_parent_frame_id (enclosing_frame )
318+ enclosing_frame_id = self .get_frame_id (enclosing_frame )
303319 self .encoder .set_function_parent_frame_ID (encoded_val , enclosing_frame_id )
304320 except KeyError :
305321 pass
@@ -340,7 +356,8 @@ def create_encoded_stack_entry(cur_frame):
340356 assert e in encoded_locals
341357
342358 return dict (func_name = cur_name ,
343- frame_id = self .get_zombie_parent_frame_id (cur_frame ),
359+ frame_id = self .get_frame_id (cur_frame ),
360+ # TODO: fixme
344361 parent_frame_id_list = parent_frame_id_list ,
345362 encoded_locals = encoded_locals ,
346363 ordered_varnames = ordered_varnames )
@@ -355,8 +372,8 @@ def create_encoded_stack_entry(cur_frame):
355372 if (type (v ) in (types .FunctionType , types .MethodType ) and \
356373 v not in self .closures ):
357374 self .closures [v ] = top_frame
358- if not top_frame in self .zombie_parent_frames :
359- self .zombie_parent_frames .append (top_frame )
375+ if not top_frame in self .zombie_frames :
376+ self .zombie_frames .append (top_frame )
360377
361378
362379 # climb up until you find '<module>', which is (hopefully) the global scope
@@ -382,7 +399,7 @@ def create_encoded_stack_entry(cur_frame):
382399 if type (v ) in (types .FunctionType , types .MethodType ):
383400 try :
384401 enclosing_frame = self .closures [v ]
385- enclosing_frame_id = self .get_zombie_parent_frame_id (enclosing_frame )
402+ enclosing_frame_id = self .get_frame_id (enclosing_frame )
386403 self .encoder .set_function_parent_frame_ID (encoded_val , enclosing_frame_id )
387404 except KeyError :
388405 pass
@@ -402,40 +419,38 @@ def create_encoded_stack_entry(cur_frame):
402419 # making it look aesthetically pretty
403420 stack_to_render = [];
404421
405- # first push all regular stack entries BACKWARDS
422+ # first push all regular stack entries
406423 if encoded_stack_locals :
407- stack_to_render = encoded_stack_locals [::- 1 ]
408- for e in stack_to_render :
424+ for e in encoded_stack_locals :
409425 e ['is_zombie' ] = False
410426 e ['is_highlighted' ] = False
427+ stack_to_render .append (e )
411428
412- stack_to_render [- 1 ]['is_highlighted' ] = True
429+ # highlight the top-most active stack entry
430+ stack_to_render [0 ]['is_highlighted' ] = True
413431
414432
415- # zombie_encoded_stack_locals consists of exited functions that have returned
416- # nested functions. Push zombie stack entries at the BEGINNING of stack_to_render,
417- # EXCEPT put zombie entries BEHIND regular entries that are their parents
418- for e in zombie_encoded_stack_locals [::- 1 ]:
433+ # now push all zombie stack entries
434+ for e in zombie_encoded_stack_locals :
419435 # don't display return value for zombie frames
436+ # TODO: reconsider ...
437+ '''
420438 try:
421439 e['ordered_varnames'].remove('__return__')
422440 except ValueError:
423441 pass
442+ '''
424443
425444 e ['is_zombie' ] = True
426445 e ['is_highlighted' ] = False # never highlight zombie entries
427446
428- # j should be 0 most of the time, so we're always inserting new
429- # elements to the front of stack_to_render (which is why we are
430- # iterating backwards over zombie_stack_locals).
431- j = 0
432- while j < len (stack_to_render ):
433- if stack_to_render [j ]['frame_id' ] in e ['parent_frame_id_list' ]:
434- j += 1
435- continue
436- break
447+ stack_to_render .append (e )
448+
449+ # now sort by frame_id since that sorts frames in "chronological
450+ # order" based on the order they were invoked
451+ stack_to_render .sort (key = lambda e : e ['frame_id' ])
452+
437453
438- stack_to_render .insert (j , e )
439454
440455 # create a unique hash for this stack entry, so that the
441456 # frontend can uniquely identify it when doing incremental
@@ -445,8 +460,7 @@ def create_encoded_stack_entry(cur_frame):
445460 # disambiguating recursion!)
446461 for (stack_index , e ) in enumerate (stack_to_render ):
447462 hash_str = e ['func_name' ]
448- if e ['frame_id' ]:
449- hash_str += '_f' + str (e ['frame_id' ])
463+ hash_str += '_f' + str (e ['frame_id' ])
450464 if e ['parent_frame_id_list' ]:
451465 hash_str += '_p' + '_' .join ([str (i ) for i in e ['parent_frame_id_list' ]])
452466 if e ['is_zombie' ]:
0 commit comments