This section will introduce several debugging methods recommended by Dynamic Graph to Static Graph (hereafter called Dynamic-to-Staic).
> **NOTE:**
>
> Please ensure that the dynamic graph code before transformation can run successfully. It is recommended to call [paddle.jit.ProgramTranslator().enable(False)](../../api/dygraph/ProgramTranslator_en.html#enable) to disable Dynamic-to-Static, and run dynamic graph code as follows:
```python
importpaddle
importnumpyasnp
paddle.disable_static()
# Disable Dynamic-to-Static
paddle.jit.ProgramTranslator().enable(False)
@paddle.jit.to_static
deffunc(x):
x=paddle.to_tensor(x)
ifx>3:
x=x-1
returnx
func(np.ones([3,2]))
```
## Breakpoint Debugging
When using Dynamic-to-Static, you can use breakpoints to debug.
For example, call `pdb.set_trace()` in your code:
```Python
import pdb
@paddle.jit.to_static
def func(x):
x = paddle.to_tensor(x)
pdb.set_trace()
if x > 3:
x = x - 1
return x
```
Executing the following code will land the debugger in the transformed static graph code:
```Python
func(np.ones([3, 2]))
```
```bash
> /tmp/tmpR809hf.py(6)func()
-> def true_fn_0(x):
(Pdb) n
> /tmp/tmpR809hf.py(6)func()
-> def false_fn_0(x):
...
```
Calling [`paddle.jit.ProgramTranslator().enable(False)`](../../api/dygraph/ProgramTranslator_en.html#enable) before executing the code will land the debugger in the original dynamic graph code:
```python
paddle.jit.ProgramTranslator().enable(False)
func(np.ones([3,2]))
```
```bash
> <ipython-input-22-0bd4eab35cd5>(10)func()
-> if x > 3:
...
```
## Print Transformed Code
There are two ways to print the transformed static graph code:
1. Use the attribute `code` of the decorated function:
```Python
@paddle.jit.to_static
def func(x):
x = paddle.to_tensor(x)
if x > 3:
x = x - 1
return x
print(func.code)
```
```bash
def func(x):
x = fluid.layers.assign(x)
def true_fn_0(x):
x = x - 1
return x
def false_fn_0(x):
return x
x = fluid.dygraph.dygraph_to_static.convert_operators.convert_ifelse(x >
3, true_fn_0, false_fn_0, (x,), (x,), (x,))
return x
```
2. Call `set_code_level(level)` or set environment variable `TRANSLATOR_CODE_LEVEL=level`
You can view the transformed code in the log by calling `set_code_level` or set environment variable `TRANSLATOR_CODE_LEVEL`.
```python
@paddle.jit.to_static
def func(x):
x = paddle.to_tensor(x)
if x > 3:
x = x - 1
return x
paddle.jit.set_code_level() # the same effect to set os.environ["TRANSLATOR_CODE_LEVEL"] = '100'
func(np.ones([1]))
```
```bash
2020-XX-XX 00:00:00,980-INFO: After the level 100 ast transformer: 'All Transformers', the transformed code:
def func(x):
x = fluid.layers.assign(x)
def true_fn_0(x):
x = x - 1
return x
def false_fn_0(x):
return x
x = fluid.dygraph.dygraph_to_static.convert_operators.convert_ifelse(x >
3, true_fn_0, false_fn_0, (x,), (x,), (x,))
return x
```
`set_code_level` can set different levels to view the code transformed by different ast transformers. For details, please refer to [set_code_level]()<!--TODO:补充set_code_level文档链接-->。
## `print`
You can call `print` to view variables. `print` will be transformed when using Dynamic-to-Static. When only Paddle Tensor is printed, `print` will be transformed and call Paddle operator [Print](../../api/layers/Print.html) in runtime. Otherwise, call python `print`.
```python
@paddle.jit.to_static
deffunc(x):
x=paddle.to_tensor(x)
# x is a Paddle Tensor, so it will run Paddle Print(x) actually.
print(x)
# The string is not a Paddle Tensor, so it will run print as-is.
print("Here call print function.")
iflen(x)>3:
x=x-1
else:
x=paddle.ones(shape=[1])
returnx
func(np.ones([1]))
```
```bash
Variable: assign_0.tmp_0
- lod: {}
- place: CPUPlace
- shape: [1]
- layout: NCHW
- dtype: double
- data: [1]
Here call print function.
```
## Log Printing
ProgramTranslator can log additional debugging information to help you know whether the function was successfully transformed or not.
You can call `paddle.jit.set_verbosity(level)` or set environment variable `TRANSLATOR_VERBOSITY=level` to enable logging and view logs of different levels. The argument `level` varies from 0 to 3:
- 0: no logging
- 1: includes the information in Dynamic-to-Static tranformation process, such as the source code not transformed, the callable object to transform and so on
- 2: includes above and more detailed function transformation logs
- 3: includes above and extremely verbose logging
> **WARNING:**
>
> The logs includes information such as source code. Please make sure logs don't contain any sensitive information before sharing them.
You can call `paddle.jit.set_verbosity` to control the verbosity level of logs:
```python
paddle.jit.set_verbosity(3)
```
or use the environment variable `TRANSLATOR_VERBOSITY`:
```python
importos
os.environ["TRANSLATOR_VERBOSITY"]='3'
```
```bash
2020-XX-XX 00:00:00,123-Level 1: Source code:
@paddle.jit.to_static
def func(x):
x = paddle.to_tensor(x)
if len(x)> 3:
x = x - 1
else:
x = paddle.ones(shape=[1])
return x
2020-XX-XX 00:00:00,152-Level 1: Convert callable object: convert <built-in function len>.
WARNING: <function inner_func at 0x7fa9bcaacf50> doesn't have to be transformed to static function because it has been transformed before, it will be run as-is.
This section will introduce the error information when an exception occurs, so as to help you better understand the Dynamic-to-Static error information.
When running the transformed static graph code, the internal can be divided into two steps: the dynamic graph code is transformed into the static graph code, and the static graph code is run. We will introduce the error reporting in these two steps.
## Exceptions in Dynamic-to-Static Transformation
If ProgramTranslator cannot transform a function, it will display a warning message and try to run the function as-is.
In the following code, the function `inner_func` is transformed before calling. When calling `inner_func` in `x=inner_func(data)`, it is not allowed to transform repeatedly, and a warning message will be given:
```python
importpaddle
importnumpyasnp
paddle.disable_static()
@paddle.jit.to_static
deffunc():
definner_func(x):
x_tensor=paddle.to_tensor(x)
returnx_tensor
data=np.ones([3]).astype("int32")
x=inner_func(data)
returnx
func()
```
The warning message is as follows:
```bash
WARNING: <function inner_func at 0x7fa9bcaacf50> doesn't have to be transformed to static function because it has been transformed before, it will be run as-is.
```
## Exceptions in Running Transformed Code
When an exception occurs in the transformed code by ProgramTranslator, the exception is be catched and the error message is augmented. It maps the error line of the static graph code to the dynamic graph code un-transformed, and then re-raise the exception.
Among the features of the re-raised exception:
- Some useless call stacks of Dynamic-to-Static are hidden;
- A prompt will be given before the code un-transformed: "In User Code:";
- The error message includes references to the original dynamic graph code before transformation;
For example, if executing the following code, an exception is raised when the static graph is built, that is, at compile time:
```python
importpaddle
importnumpyasnp
paddle.disable_static()
@paddle.jit.to_static
deffunc(x):
x=paddle.to_tensor(x)
x=paddle.reshape(x,shape=[-1,-1])
returnx
func(np.ones([3,2]))
```
```bash
Traceback (most recent call last):
<ipython-input-13-f9c3ea702e3a> in <module>()
func(np.ones([3, 2]))
File "paddle/fluid/dygraph/dygraph_to_static/program_translator.py", line 332, in __call__
raise new_exception
AssertionError: In user code:
File "<ipython-input-13-f9c3ea702e3a>", line 7, in func
x = fluid.layers.reshape(x, shape=[-1, -1])
File "paddle/fluid/layers/nn.py", line 6193, in reshape
attrs["shape"]= get_attr_shape(shape)
File "paddle/fluid/layers/nn.py", line 6169, in get_attr_shape
"be -1. But received shape[%d] is also -1." % dim_idx)
AssertionError: Only one dimension value of 'shape'in reshape can be -1. But received shape[1] is also -1.
```
The above error information can be divided into three points:
1. In the error stack, the call stacks related to the code transformation process are hidden by default and not displayed, so as to avoid confusion.
2. In the error message processed by ProgramTranslator, a prompt "In user code:" will be included, which means that the following error stacks contains the original dynamic graph code, that is, the code written by the user:
```bash
AssertionError: In user code:
File "<ipython-input-13-f9c3ea702e3a>", line 7, in func
x = fluid.layers.reshape(x, shape=[-1, -1])
File "paddle/fluid/layers/nn.py", line 6193, in reshape
attrs["shape"] = get_attr_shape(shape)
File "paddle/fluid/layers/nn.py", line 6169, in get_attr_shape
"be -1. But received shape[%d] is also -1." % dim_idx)
```
`File "<ipython-input-13-f9c3ea702e3a>", line 7, in func` is the location information of un-transformed code, `x = fluid.layers.reshape(x, shape=[-1, -1])` is the code un-transformed.
3. The new exception contains the message that the exception originally reported, as follows:
```bash
AssertionError: Only one dimension value of 'shape'in reshape can be -1. But received shape[1] is also -1.
```
If executing the following code, an exception is raised when the static graph is executed, that is, at runtime:
```Python
@paddle.jit.to_static
def func(x):
x = paddle.to_tensor(x)
two = paddle.fill_constant(shape=[1], value=2, dtype="int32")
x = paddle.reshape(x, shape=[1, two])
return x
func(np.ones([3]).astype("int32"))
```
```bash
Traceback (most recent call last):
File "<ipython-input-57-c63d6a351262>", line 10, in <module>()
func(np.ones([3]).astype("int32"))
File "paddle/fluid/dygraph/dygraph_to_static/program_translator.py", line 332, in __call__
raise new_exception
EnforceNotMet: In user code:
File "<ipython-input-57-c63d6a351262>", line 7, in func
x = paddle.reshape(x, shape=[1, two])
File "paddle/tensor/manipulation.py", line 1347, in reshape
InvalidArgumentError: The 'shape'in ReshapeOp is invalid. The input tensor X'size must be equal to the capacity of 'shape'. But received X's shape =[3], X's size = 3, 'shape' is [1, 2], the capacity of 'shape' is 2.
[Hint: Expected capacity == in_size, but received capacity:2 != in_size:3.] (at /paddle/paddle/fluid/operators/reshape_op.cc:206)
In the above exception, in addition to hiding part of the error stack and locating the error to the dynamic graph code un-transformed, the error information includes the c++ error stack `C++ Traceback` and `Error Message Summary`, which are the exception from C++ and are displayed in Python exception after processing.