Skip to content

Commit 1c5b5e8

Browse files
caisqtensorflower-gardener
authored andcommitted
tfdbg: Add ability to inspect Python source against TF graphs
Change: 148926382
1 parent 4111527 commit 1c5b5e8

9 files changed

Lines changed: 618 additions & 9 deletions

File tree

tensorflow/docs_src/programmers_guide/debugger.md

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -128,6 +128,9 @@ Try the following commands at the `tfdbg>` prompt (referencing the code at
128128
| `lo -r hidden/Relu:0` | List the recipients of the output of the node `hidden/Relu`, recursively—i.e., the output recipient tree. |
129129
| `lt -n softmax.*` | List all dumped tensors whose names match the regular-expression pattern `softmax.*`. |
130130
| `lt -t MatMul` | List all dumped tensors whose node type is `MatMul`. |
131+
| `ps /path/to/source.py` | Print the Python source file source.py, with the lines annotated with the ops created at each of them, respectively. |
132+
| `ps -t /path/to/source.py` | Same as the command above, but perform annotation using dumped Tensors, instead of ops. |
133+
| `ps -b 30 /path/to/source.py` | Annotate source.py beginning at line 30. |
131134
| `run_info` or `ri` | Display information about the current run, including fetches and feeds. |
132135
| `help` | Print general help information listing all available **tfdbg** commands and their flags. |
133136
| `help lt` | Print the help information for the `lt` command. |
@@ -238,13 +241,22 @@ to show the traceback of the node's construction:
238241
tfdbg> ni -t cross_entropy/Log
239242
```
240243

244+
The `-t` flag is used by default, if you use the clickable "node_info" menu item
245+
at the top of the screen.
246+
241247
From the traceback, you can see that the op is constructed at line 109 of
242248
[`debug_mnist.py`](https://www.tensorflow.org/code/tensorflow/python/debug/examples/debug_mnist.py):
243249

244250
```python
245251
diff = y_ * tf.log(y)
246252
```
247253

254+
TIP: tfdbg lets you view a Python source file with its lines annotated with
255+
the ops or Tensors created by them. To use this feature,
256+
simply click the underlined line numbers in the stack trace output of the
257+
`ni -t <op_name>` commands, or use the `ps` (or `print_source`) command such as:
258+
`ps /path/to/source.py`
259+
248260
Apply a value clipping on the input to @{tf.log}
249261
to resolve this problem:
250262

tensorflow/python/debug/BUILD

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,12 @@ py_library(
4646
srcs_version = "PY2AND3",
4747
)
4848

49+
py_library(
50+
name = "source_utils",
51+
srcs = ["lib/source_utils.py"],
52+
srcs_version = "PY2AND3",
53+
)
54+
4955
py_library(
5056
name = "stepper",
5157
srcs = ["lib/stepper.py"],
@@ -121,7 +127,9 @@ py_library(
121127
":command_parser",
122128
":debug_data",
123129
":debugger_cli_common",
130+
":source_utils",
124131
":ui_factory",
132+
"//third_party/py/numpy",
125133
"@six_archive//:six",
126134
],
127135
)
@@ -323,6 +331,25 @@ py_test(
323331
],
324332
)
325333

334+
py_test(
335+
name = "source_utils_test",
336+
size = "small",
337+
srcs = [
338+
"lib/source_utils_test.py",
339+
],
340+
srcs_version = "PY2AND3",
341+
deps = [
342+
":debug_data",
343+
":debug_utils",
344+
":source_utils",
345+
"//tensorflow/python:client",
346+
"//tensorflow/python:framework_test_lib",
347+
"//tensorflow/python:math_ops",
348+
"//tensorflow/python:platform_test",
349+
"//tensorflow/python:variables",
350+
],
351+
)
352+
326353
cuda_py_test(
327354
name = "stepper_test",
328355
size = "small",

tensorflow/python/debug/cli/analyzer_cli.py

Lines changed: 106 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -27,14 +27,17 @@
2727
import copy
2828
import re
2929

30+
import numpy as np
3031
from six.moves import xrange # pylint: disable=redefined-builtin
3132

3233
from tensorflow.python.debug.cli import cli_shared
3334
from tensorflow.python.debug.cli import command_parser
3435
from tensorflow.python.debug.cli import debugger_cli_common
3536
from tensorflow.python.debug.cli import ui_factory
3637
from tensorflow.python.debug.lib import debug_data
38+
from tensorflow.python.debug.lib import source_utils
3739

40+
RL = debugger_cli_common.RichLine
3841

3942
# String constants for the depth-dependent hanging indent at the beginning
4043
# of each line.
@@ -89,7 +92,7 @@ def _add_main_menu(output,
8992
menu.append(
9093
debugger_cli_common.MenuItem(
9194
"node_info",
92-
"node_info -a -d %s" % node_name,
95+
"node_info -a -d -t %s" % node_name,
9396
enabled=enable_node_info))
9497
menu.append(
9598
debugger_cli_common.MenuItem(
@@ -303,7 +306,6 @@ def __init__(self, debug_dump):
303306
help="Numerical ranges to highlight tensor elements in. "
304307
"Examples: -r 0,1e-8, -r [-0.1,0.1], "
305308
"-r \"[[-inf, -0.1], [0.1, inf]]\"")
306-
307309
ap.add_argument(
308310
"-a",
309311
"--all",
@@ -312,6 +314,37 @@ def __init__(self, debug_dump):
312314
help="Print the tensor in its entirety, i.e., do not use ellipses.")
313315
self._arg_parsers["print_tensor"] = ap
314316

317+
# Parser for print_source.
318+
ap = argparse.ArgumentParser(
319+
description="Print a Python source file with overlaid debug "
320+
"information, including the nodes (ops) or Tensors created at the "
321+
"source lines.",
322+
usage=argparse.SUPPRESS)
323+
ap.add_argument(
324+
"source_file_path",
325+
type=str,
326+
help="Path to the source file.")
327+
ap.add_argument(
328+
"-t",
329+
"--tensors",
330+
dest="tensors",
331+
action="store_true",
332+
help="Label lines with dumped Tensors, instead of ops.")
333+
ap.add_argument(
334+
"-m",
335+
"--max_elements_per_line",
336+
type=int,
337+
default=10,
338+
help="Maximum number of elements (ops or Tensors) to show per source "
339+
"line.")
340+
ap.add_argument(
341+
"-b",
342+
"--line_begin",
343+
type=int,
344+
default=1,
345+
help="Print source beginning at line number (1-based.)")
346+
self._arg_parsers["print_source"] = ap
347+
315348
# TODO(cais): Implement list_nodes.
316349

317350
def add_tensor_filter(self, filter_name, filter_callable):
@@ -709,15 +742,20 @@ def _render_node_traceback(self, node_name):
709742
construction.
710743
"""
711744

712-
lines = ["", "", "Traceback of node construction:"]
713-
font_attr_segs = {len(lines) - 1: [(0, len(lines[-1]), "bold")]}
745+
lines = [RL(""), RL(""), RL("Traceback of node construction:", "bold")]
714746

715747
try:
716748
node_stack = self._debug_dump.node_traceback(node_name)
717749
for depth, (file_path, line, function_name, text) in enumerate(
718750
node_stack):
719751
lines.append("%d: %s" % (depth, file_path))
720-
lines.append(" Line: %d" % line)
752+
753+
attribute = debugger_cli_common.MenuItem(
754+
"", "ps %s -b %d" % (file_path, line)) if text else None
755+
line_number_line = RL(" ")
756+
line_number_line += RL("Line: %d" % line, attribute)
757+
lines.append(line_number_line)
758+
721759
lines.append(" Function: %s" % function_name)
722760
lines.append(" Text: " + (("\"%s\"" % text) if text else "None"))
723761
lines.append("")
@@ -726,8 +764,7 @@ def _render_node_traceback(self, node_name):
726764
except LookupError:
727765
lines.append("(Unavailable because no Python graph has been loaded)")
728766

729-
return debugger_cli_common.RichTextLines(lines,
730-
font_attr_segs=font_attr_segs)
767+
return debugger_cli_common.rich_text_lines_from_rich_line_list(lines)
731768

732769
def list_inputs(self, args, screen_info=None):
733770
"""Command handler for inputs.
@@ -942,6 +979,63 @@ def list_outputs(self, args, screen_info=None):
942979

943980
return output
944981

982+
def print_source(self, args, screen_info=None):
983+
"""Print the content of a source file."""
984+
del screen_info # Unused.
985+
986+
parsed = self._arg_parsers["print_source"].parse_args(args)
987+
988+
source_annotation = source_utils.annotate_source(
989+
self._debug_dump,
990+
parsed.source_file_path,
991+
do_dumped_tensors=parsed.tensors,
992+
min_line=parsed.line_begin)
993+
994+
with open(parsed.source_file_path, "rU") as f:
995+
source_text = f.read()
996+
997+
source_lines = source_text.split("\n")
998+
num_lines = len(source_lines)
999+
line_num_width = int(np.ceil(np.log10(num_lines))) + 3
1000+
1001+
labeled_source_lines = []
1002+
if parsed.line_begin > 1:
1003+
labeled_source_lines.append(
1004+
RL("(... Omitted %d source lines ...)" % (parsed.line_begin - 1),
1005+
"bold"))
1006+
1007+
for i, line in enumerate(source_lines[parsed.line_begin - 1:]):
1008+
annotated_line = RL("L%d" % (i + parsed.line_begin), "yellow")
1009+
annotated_line += " " * (line_num_width - len(annotated_line))
1010+
annotated_line += line
1011+
labeled_source_lines.append(annotated_line)
1012+
1013+
if i + parsed.line_begin in source_annotation:
1014+
sorted_elements = sorted(source_annotation[i + parsed.line_begin])
1015+
for k, element in enumerate(sorted_elements):
1016+
if k >= parsed.max_elements_per_line:
1017+
labeled_source_lines.append(
1018+
" (... Omitted %d of %d %s ...)" % (
1019+
len(sorted_elements) - parsed.max_elements_per_line,
1020+
len(sorted_elements),
1021+
"tensor(s)" if parsed.tensors else "op(s)"))
1022+
break
1023+
1024+
label = RL(" " * 4)
1025+
if self._debug_dump.debug_watch_keys(
1026+
debug_data.get_node_name(element)):
1027+
attribute = debugger_cli_common.MenuItem("", "pt %s" % element)
1028+
else:
1029+
attribute = "blue"
1030+
1031+
label += RL(element, attribute)
1032+
labeled_source_lines.append(label)
1033+
1034+
output = debugger_cli_common.rich_text_lines_from_rich_line_list(
1035+
labeled_source_lines)
1036+
_add_main_menu(output, node_name=None)
1037+
return output
1038+
9451039
def _list_inputs_or_outputs(self,
9461040
recursive,
9471041
node_name,
@@ -1292,6 +1386,11 @@ def create_analyzer_ui(debug_dump, tensor_filters=None, ui_type="curses"):
12921386
analyzer.print_tensor,
12931387
analyzer.get_help("print_tensor"),
12941388
prefix_aliases=["pt"])
1389+
cli.register_command_handler(
1390+
"print_source",
1391+
analyzer.print_source,
1392+
analyzer.get_help("print_source"),
1393+
prefix_aliases=["ps"])
12951394

12961395
dumped_tensor_names = []
12971396
for datum in debug_dump.dumped_tensor_data:

0 commit comments

Comments
 (0)