5151ELLIPSIS = "..."
5252
5353
54+ def _add_main_menu (output ,
55+ node_name = None ,
56+ enable_list_tensors = True ,
57+ enable_node_info = True ,
58+ enable_print_tensor = True ,
59+ enable_list_inputs = True ,
60+ enable_list_outputs = True ):
61+ """Generate main menu for the screen output from a command.
62+
63+ Args:
64+ output: (debugger_cli_common.RichTextLines) the output object to modify.
65+ node_name: (str or None) name of the node involved (if any). If None,
66+ the menu items node_info, list_inputs and list_outputs will be
67+ automatically disabled, overriding the values of arguments
68+ enable_node_info, enable_list_inputs and enable_list_outputs.
69+ enable_list_tensors: (bool) whether the list_tensor menu item will be
70+ enabled.
71+ enable_node_info: (bool) whether the node_info item will be enabled.
72+ enable_print_tensor: (bool) whether the print_tensor item will be enabled.
73+ enable_list_inputs: (bool) whether the item list_inputs will be enabled.
74+ enable_list_outputs: (bool) whether the item list_outputs will be enabled.
75+ """
76+
77+ menu = debugger_cli_common .Menu ()
78+
79+ menu .append (
80+ debugger_cli_common .MenuItem (
81+ "list_tensors" , "list_tensors" , enabled = enable_list_tensors ))
82+
83+ if node_name :
84+ menu .append (
85+ debugger_cli_common .MenuItem (
86+ "node_info" ,
87+ "node_info -a -d %s" % node_name ,
88+ enabled = enable_node_info ))
89+ menu .append (
90+ debugger_cli_common .MenuItem (
91+ "print_tensor" ,
92+ "print_tensor %s" % node_name ,
93+ enabled = enable_print_tensor ))
94+ menu .append (
95+ debugger_cli_common .MenuItem (
96+ "list_inputs" ,
97+ "list_inputs -c -r %s" % node_name ,
98+ enabled = enable_list_inputs ))
99+ menu .append (
100+ debugger_cli_common .MenuItem (
101+ "list_outputs" ,
102+ "list_outputs -c -r %s" % node_name ,
103+ enabled = enable_list_outputs ))
104+ else :
105+ menu .append (
106+ debugger_cli_common .MenuItem (
107+ "node_info" , None , enabled = False ))
108+ menu .append (
109+ debugger_cli_common .MenuItem ("print_tensor" , None , enabled = False ))
110+ menu .append (
111+ debugger_cli_common .MenuItem ("list_inputs" , None , enabled = False ))
112+ menu .append (
113+ debugger_cli_common .MenuItem ("list_outputs" , None , enabled = False ))
114+
115+ menu .append (
116+ debugger_cli_common .MenuItem ("run_info" , "run_info" ))
117+ menu .append (
118+ debugger_cli_common .MenuItem ("help" , "help" ))
119+
120+ output .annotations [debugger_cli_common .MAIN_MENU_KEY ] = menu
121+
122+
54123class DebugAnalyzer (object ):
55124 """Analyzer for debug data from dump directories."""
56125
@@ -301,6 +370,7 @@ def list_tensors(self, args, screen_info=None):
301370 parsed = self ._arg_parsers ["list_tensors" ].parse_args (args )
302371
303372 output = []
373+ font_attr_segs = {}
304374
305375 filter_strs = []
306376 if parsed .op_type_filter :
@@ -316,12 +386,16 @@ def list_tensors(self, args, screen_info=None):
316386 else :
317387 node_name_regex = None
318388
389+ filter_output = debugger_cli_common .RichTextLines (filter_strs )
390+
319391 if parsed .tensor_filter :
320392 try :
321393 filter_callable = self .get_tensor_filter (parsed .tensor_filter )
322394 except ValueError :
323- return cli_shared .error (
324- "There is no tensor filter named \" %s\" ." % parsed .tensor_filter )
395+ output = cli_shared .error ("There is no tensor filter named \" %s\" ." %
396+ parsed .tensor_filter )
397+ _add_main_menu (output , node_name = None , enable_list_tensors = False )
398+ return output
325399
326400 data_to_show = self ._debug_dump .find (filter_callable )
327401 else :
@@ -340,21 +414,28 @@ def list_tensors(self, args, screen_info=None):
340414 continue
341415
342416 rel_time = (dump .timestamp - self ._debug_dump .t0 ) / 1000.0
343- output .append ("[%.3f ms] %s:%d" % (rel_time , dump .node_name ,
344- dump .output_slot ))
417+ dumped_tensor_name = "%s:%d" % (dump .node_name , dump .output_slot )
418+ output .append ("[%.3f ms] %s" % (rel_time , dumped_tensor_name ))
419+ font_attr_segs [len (output ) - 1 ] = [(
420+ len (output [- 1 ]) - len (dumped_tensor_name ), len (output [- 1 ]),
421+ debugger_cli_common .MenuItem ("" , "pt %s" % dumped_tensor_name ))]
345422 dump_count += 1
346423
347- output .insert (0 , "" )
348-
349- output = filter_strs + output
424+ filter_output .append ("" )
425+ filter_output .extend (debugger_cli_common .RichTextLines (
426+ output , font_attr_segs = font_attr_segs ))
427+ output = filter_output
350428
351429 if parsed .tensor_filter :
352- output .insert (0 , "%d dumped tensor(s) passing filter \" %s\" :" %
353- (dump_count , parsed .tensor_filter ))
430+ output .prepend ([
431+ "%d dumped tensor(s) passing filter \" %s\" :" %
432+ (dump_count , parsed .tensor_filter )
433+ ])
354434 else :
355- output .insert ( 0 , "%d dumped tensor(s):" % dump_count )
435+ output .prepend ([ "%d dumped tensor(s):" % dump_count ] )
356436
357- return debugger_cli_common .RichTextLines (output )
437+ _add_main_menu (output , node_name = None , enable_list_tensors = False )
438+ return output
358439
359440 def node_info (self , args , screen_info = None ):
360441 """Command handler for node_info.
@@ -383,8 +464,16 @@ def node_info(self, args, screen_info=None):
383464 parsed .node_name )
384465
385466 if not self ._debug_dump .node_exists (node_name ):
386- return cli_shared .error (
467+ output = cli_shared .error (
387468 "There is no node named \" %s\" in the partition graphs" % node_name )
469+ _add_main_menu (
470+ output ,
471+ node_name = None ,
472+ enable_list_tensors = True ,
473+ enable_node_info = False ,
474+ enable_list_inputs = False ,
475+ enable_list_outputs = False )
476+ return output
388477
389478 # TODO(cais): Provide UI glossary feature to explain to users what the
390479 # term "partition graph" means and how it is related to TF graph objects
@@ -422,7 +511,9 @@ def node_info(self, args, screen_info=None):
422511 if parsed .dumps :
423512 lines .extend (self ._list_node_dumps (node_name ))
424513
425- return debugger_cli_common .RichTextLines (lines )
514+ output = debugger_cli_common .RichTextLines (lines )
515+ _add_main_menu (output , node_name = node_name , enable_node_info = False )
516+ return output
426517
427518 def list_inputs (self , args , screen_info = None ):
428519 """Command handler for inputs.
@@ -447,14 +538,19 @@ def list_inputs(self, args, screen_info=None):
447538
448539 parsed = self ._arg_parsers ["list_inputs" ].parse_args (args )
449540
450- return self ._list_inputs_or_outputs (
541+ output = self ._list_inputs_or_outputs (
451542 parsed .recursive ,
452543 parsed .node_name ,
453544 parsed .depth ,
454545 parsed .control ,
455546 parsed .op_type ,
456547 do_outputs = False )
457548
549+ node_name = debug_data .get_node_name (parsed .node_name )
550+ _add_main_menu (output , node_name = node_name , enable_list_inputs = False )
551+
552+ return output
553+
458554 def print_tensor (self , args , screen_info = None ):
459555 """Command handler for print_tensor.
460556
@@ -484,16 +580,44 @@ def print_tensor(self, args, screen_info=None):
484580 command_parser .parse_tensor_name_with_slicing (parsed .tensor_name ))
485581
486582 node_name , output_slot = debug_data .parse_node_or_tensor_name (tensor_name )
487- if output_slot is None :
488- return cli_shared .error ("\" %s\" is not a valid tensor name" %
489- parsed .tensor_name )
490-
491583 if (self ._debug_dump .loaded_partition_graphs () and
492584 not self ._debug_dump .node_exists (node_name )):
493- return cli_shared .error (
585+ output = cli_shared .error (
494586 "Node \" %s\" does not exist in partition graphs" % node_name )
587+ _add_main_menu (
588+ output ,
589+ node_name = None ,
590+ enable_list_tensors = True ,
591+ enable_print_tensor = False )
592+ return output
495593
496594 watch_keys = self ._debug_dump .debug_watch_keys (node_name )
595+ if output_slot is None :
596+ output_slots = set ()
597+ for watch_key in watch_keys :
598+ output_slots .add (int (watch_key .split (":" )[1 ]))
599+
600+ if len (output_slots ) == 1 :
601+ # There is only one dumped tensor from this node, so there is no
602+ # ambiguity. Proceed to show the only dumped tensor.
603+ output_slot = list (output_slots )[0 ]
604+ else :
605+ # There are more than one dumped tensors from this node. Indicate as
606+ # such.
607+ # TODO(cais): Provide an output screen with command links for
608+ # convenience.
609+ lines = [
610+ "Node \" %s\" generated debug dumps from %s output slots:" %
611+ (node_name , len (output_slots )),
612+ "Please specify the output slot: %s:x." % node_name
613+ ]
614+ output = debugger_cli_common .RichTextLines (lines )
615+ _add_main_menu (
616+ output ,
617+ node_name = node_name ,
618+ enable_list_tensors = True ,
619+ enable_print_tensor = False )
620+ return output
497621
498622 # Find debug dump data that match the tensor name (node name + output
499623 # slot).
@@ -506,22 +630,24 @@ def print_tensor(self, args, screen_info=None):
506630
507631 if not matching_data :
508632 # No dump for this tensor.
509- return cli_shared .error (
510- "Tensor \" %s \" did not generate any dumps." % parsed .tensor_name )
633+ output = cli_shared .error ("Tensor \" %s \" did not generate any dumps." %
634+ parsed .tensor_name )
511635 elif len (matching_data ) == 1 :
512636 # There is only one dump for this tensor.
513637 if parsed .number <= 0 :
514- return cli_shared .format_tensor (
638+ output = cli_shared .format_tensor (
515639 matching_data [0 ].get_tensor (),
516640 matching_data [0 ].watch_key ,
517641 np_printoptions ,
518642 print_all = parsed .print_all ,
519643 tensor_slicing = tensor_slicing ,
520644 highlight_options = highlight_options )
521645 else :
522- return cli_shared .error (
646+ output = cli_shared .error (
523647 "Invalid number (%d) for tensor %s, which generated one dump." %
524648 (parsed .number , parsed .tensor_name ))
649+
650+ _add_main_menu (output , node_name = node_name , enable_print_tensor = False )
525651 else :
526652 # There are more than one dumps for this tensor.
527653 if parsed .number < 0 :
@@ -540,21 +666,24 @@ def print_tensor(self, args, screen_info=None):
540666 lines .append ("For example:" )
541667 lines .append (" print_tensor %s -n 0" % parsed .tensor_name )
542668
543- return debugger_cli_common .RichTextLines (lines )
669+ output = debugger_cli_common .RichTextLines (lines )
544670 elif parsed .number >= len (matching_data ):
545- return cli_shared .error (
671+ output = cli_shared .error (
546672 "Specified number (%d) exceeds the number of available dumps "
547673 "(%d) for tensor %s" %
548674 (parsed .number , len (matching_data ), parsed .tensor_name ))
549675 else :
550- return cli_shared .format_tensor (
676+ output = cli_shared .format_tensor (
551677 matching_data [parsed .number ].get_tensor (),
552678 matching_data [parsed .number ].watch_key + " (dump #%d)" %
553679 parsed .number ,
554680 np_printoptions ,
555681 print_all = parsed .print_all ,
556682 tensor_slicing = tensor_slicing ,
557683 highlight_options = highlight_options )
684+ _add_main_menu (output , node_name = node_name , enable_print_tensor = False )
685+
686+ return output
558687
559688 def list_outputs (self , args , screen_info = None ):
560689 """Command handler for inputs.
@@ -579,14 +708,19 @@ def list_outputs(self, args, screen_info=None):
579708
580709 parsed = self ._arg_parsers ["list_outputs" ].parse_args (args )
581710
582- return self ._list_inputs_or_outputs (
711+ output = self ._list_inputs_or_outputs (
583712 parsed .recursive ,
584713 parsed .node_name ,
585714 parsed .depth ,
586715 parsed .control ,
587716 parsed .op_type ,
588717 do_outputs = True )
589718
719+ node_name = debug_data .get_node_name (parsed .node_name )
720+ _add_main_menu (output , node_name = node_name , enable_list_outputs = False )
721+
722+ return output
723+
590724 def _list_inputs_or_outputs (self ,
591725 recursive ,
592726 node_name ,
0 commit comments