[tfdbg2] Fix num_outputs properties when execution or graph op has no output

The previous assumption that `self._output_tensor_ids` is always a tuple
is incorrect. It can be `None` for ops without output tensors.

Also in this CL: Clarify when properties are `None` and when they are
`tuple`s in doc strings of the data objects.

PiperOrigin-RevId: 315261796
Change-Id: I398192ba4451e9d8087a2b9e934dff3f561b651a
This commit is contained in:
Shanqing Cai 2020-06-08 06:21:22 -07:00 committed by TensorFlower Gardener
parent 251a474096
commit 94ce307aa0
2 changed files with 45 additions and 6 deletions

View File

@ -308,14 +308,17 @@ class Execution(ExecutionDigest):
graph_id: ID of the executed FuncGraph (applicable only the execution of a
tf.function). `None` for the eager execution of an individual op.
input_tensor_ids: IDs of the input (eager) tensor(s) for this execution, if
any.
any. If the eager execution has no input tensor, this is `None`. Else,
this is a `tuple` of `int`s.
output_tensor_ids: IDs of the output (eager) tensor(s) from this execution,
if any.
if any. If the eager execution produces no output tensor, this is `None`.
Else, this is a `tuple` of `int`s.
debug_tensor_values: Values of the debug tensor(s), applicable only to
non-FULL_TENSOR tensor debug mode. A tuple of list of numbers. Each
element of the tuple corresponds to an output tensor of the execution.
See documentation of the various TensorDebugModes for the semantics of the
numbers.
numbers. If the eager execution produces no output tensor, this is
`None`. Else, this is a `tuple` of `list`s.
"""
def __init__(self,
@ -362,7 +365,7 @@ class Execution(ExecutionDigest):
@property
def num_outputs(self):
return len(self._output_tensor_ids)
return len(self._output_tensor_ids) if self._output_tensor_ids else 0
@property
def output_tensor_ids(self):
@ -542,6 +545,8 @@ class GraphOpCreationDigest(BaseDigest):
op_type: Type name of the op (e.g., "MatMul").
op_name: Name of the op (e.g., "dense_1/MatMul").
output_tensor_ids: Debugger-generated IDs for the output(s) of the op.
If the op produces no output tensor, this is `None`. Else, this is a
`tuple` of `int`s.
input_names: Names of the input tensors to the op.
device_name: The name of the device that the op is placed on (if available).
host_name: Name of the host on which the op is created.
@ -588,7 +593,7 @@ class GraphOpCreationDigest(BaseDigest):
@property
def num_outputs(self):
return len(self._output_tensor_ids)
return len(self._output_tensor_ids) if self.output_tensor_ids else 0
@property
def input_names(self):

View File

@ -583,7 +583,7 @@ class DebugEventsWriterTest(dumping_callback_test_lib.DumpingCallbackTestBase,
self.assertEqual(traces[-1].op_name, "Op_%d" % (expected_end - 1))
class DataObjectsTest(test_util.TensorFlowTestCase):
class DataObjectsTest(test_util.TensorFlowTestCase, parameterized.TestCase):
def jsonRoundTripCheck(self, obj):
self.assertEqual(
@ -660,6 +660,22 @@ class DataObjectsTest(test_util.TensorFlowTestCase):
self.assertIsNone(json["output_tensor_ids"])
self.assertIsNone(json["debug_tensor_values"])
@parameterized.named_parameters(
("EmptyList", []),
("None", None),
)
def testExecutionWithNoOutputTensorsReturnsZeroForNumOutputs(
self, output_tensor_ids):
execution = debug_events_reader.Execution(
debug_events_reader.ExecutionDigest(1234, 5678, "FooOp"),
"localhost", ("a1", "b2"),
debug_event_pb2.TensorDebugMode.FULL_HEALTH,
graph_id="abcd",
input_tensor_ids=[13, 37],
output_tensor_ids=output_tensor_ids,
debug_tensor_values=None)
self.assertEqual(execution.num_outputs, 0)
def testDebuggedDeviceToJons(self):
debugged_device = debug_events_reader.DebuggedDevice("/TPU:3", 4)
self.assertEqual(debugged_device.to_json(), {
@ -697,6 +713,24 @@ class DataObjectsTest(test_util.TensorFlowTestCase):
"inner_graph_ids": ["c2d3", "c2d3e4"],
})
@parameterized.named_parameters(
("EmptyList", []),
("None", None),
)
def testGraphOpDigestWithNoOutpusReturnsNumOutputsZero(
self, output_tensor_ids):
op_creation_digest = debug_events_reader.GraphOpCreationDigest(
1234,
5678,
"deadbeef",
"FooOp",
"Model_1/Foo_2",
output_tensor_ids,
"machine.cluster", ("a1", "a2"),
input_names=None,
device_name=None)
self.assertEqual(op_creation_digest.num_outputs, 0)
def testGraphOpCreationDigestNoInputNoDeviceNameToJson(self):
op_creation_digest = debug_events_reader.GraphOpCreationDigest(
1234,