Skip to content

Commit 46e78d2

Browse files
yushangdiChao1Han
authored andcommitted
Fix dynamo stack trace (pytorch#165930)
Fixes pytorch#165911 - Add message to Attribute error so we see ` Developer debug context: raised exception AttributeError(["'Linear' object has no attribute 'w'"])` instead of just `Developer debug context: raised exception AttributeError([])` - Add stack trace in `ObservedException` so we display the inner most error stack trace back to user code Output: ``` /data/users/shangdiy/pytorch/torch/__init__.py:2641: UserWarning: You are calling torch.compile inside torch.export region. To capture an useful graph, we will implicitly switch to torch.compile(backend=eager) warnings.warn( Traceback (most recent call last): File "/data/users/shangdiy/pytorch/torch/_dynamo/variables/user_defined.py", line 1385, in var_getattr subobj = self._getattr_static(name) ^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/data/users/shangdiy/pytorch/torch/_dynamo/variables/user_defined.py", line 1256, in _getattr_static subobj = type(self.value).__getattribute__(self.value, name) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ AttributeError: 'Linear' object has no attribute 'w' During handling of the above exception, another exception occurred: torch._dynamo.exc.ObservedAttributeError: 'Linear' object has no attribute 'w' The above exception was the direct cause of the following exception: Traceback (most recent call last): File "/data/users/shangdiy/pytorch/test.py", line 34, in <module> mod = torch._dynamo.functional_export._dynamo_graph_capture_for_export(Model())(x) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/data/users/shangdiy/pytorch/torch/_dynamo/functional_export.py", line 481, in inner out = fullgraph_capture( ^^^^^^^^^^^^^^^^^^ File "/data/users/shangdiy/pytorch/torch/_dynamo/convert_frame.py", line 1053, in fullgraph_capture return _fullgraph_capture_frame( ^^^^^^^^^^^^^^^^^^^^^^^^^ File "/data/users/shangdiy/pytorch/torch/_dynamo/convert_frame.py", line 1115, in _fullgraph_capture_frame raise e.with_traceback(None) from e.__cause__ # User compiler error ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ torch._dynamo.exc.Unsupported: Observed exception Explanation: Dynamo found no exception handler at the top-level compiled function when encountering an exception. Exception will propagate outside the compiled region. Hint: Dynamo has detected that tracing the code will result in an error when running in eager. Please double check that your code doesn't contain a similar error when actually running eager/uncompiled. Hint: It may be possible to write Dynamo tracing rules for this code. Please report an issue to PyTorch if you encounter this graph break often and it is causing performance issues. Developer debug context: raised exception AttributeError(["'Linear' object has no attribute 'w'"]) For more details about this graph break, please visit: https://meta-pytorch.github.io/compile-graph-break-site/gb/gb0088.html from user code: File "/data/users/shangdiy/pytorch/torch/_dynamo/functional_export.py", line 171, in forward res = self._export_root(*args, **kwargs) File "/data/users/shangdiy/pytorch/test.py", line 31, in forward weight = self.linear.w Set TORCHDYNAMO_VERBOSE=1 for the internal stack trace (please do this especially if you're reporting a bug to PyTorch). For even more developer context, set TORCH_LOGS="+dynamo" ``` Pull Request resolved: pytorch#165930 Approved by: https://github.com/anijain2305
1 parent 117f9c6 commit 46e78d2

File tree

5 files changed

+69
-10
lines changed

5 files changed

+69
-10
lines changed

test/dynamo/test_exceptions.py

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -942,6 +942,22 @@ def fn():
942942

943943
self.assertRaises(Unsupported, fn)
944944

945+
def test_stack_trace_from_observed_exception(self):
946+
class Model(torch.nn.Module):
947+
def __init__(self):
948+
super().__init__()
949+
self.linear = torch.nn.Linear(16, 16)
950+
951+
def forward(self, x):
952+
# no attribute w on self.linear
953+
weight = self.linear.w
954+
return torch.nn.functional.linear(x, weight)
955+
956+
x = (torch.randn(4, 16, requires_grad=True),)
957+
958+
with self.assertRaisesRegex(Exception, "weight = self.linear.w"):
959+
torch._dynamo.functional_export._dynamo_graph_capture_for_export(Model())(x)
960+
945961

946962
instantiate_parametrized_tests(ExceptionTests)
947963

torch/_dynamo/exc.py

Lines changed: 26 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -163,9 +163,17 @@ def __init__(
163163

164164

165165
class Unsupported(TorchDynamoException):
166-
def __init__(self, msg: str, *, case_name: Optional[str] = None) -> None:
166+
def __init__(
167+
self,
168+
msg: str,
169+
*,
170+
case_name: Optional[str] = None,
171+
real_stack: None | StackSummary = None,
172+
) -> None:
167173
super().__init__(msg)
168-
self.real_stack = torch._guards.TracingContext.extract_stack()
174+
if not real_stack:
175+
real_stack = torch._guards.TracingContext.extract_stack()
176+
self.real_stack = real_stack
169177
self.msg = msg
170178
self.category: Optional[str] = None
171179
self.add_to_stats()
@@ -300,7 +308,9 @@ class PackageError(TorchDynamoException):
300308

301309
class ObservedException(TorchDynamoException):
302310
# An exception observed during the tracing. This exception is used by Dynamo to handle exceptions.
303-
pass
311+
def __init__(self, *args: Any, **kwargs: Any) -> None:
312+
super().__init__(*args, **kwargs)
313+
self.real_stack: StackSummary = torch._guards.TracingContext.extract_stack()
304314

305315

306316
class ObservedUserStopIteration(ObservedException):
@@ -384,14 +394,22 @@ def raise_observed_exception(
384394
*,
385395
args: Optional[list[Any]] = None,
386396
kwargs: Optional[dict[str, Any]] = None,
397+
msg: Optional[str] = None,
387398
) -> NoReturn:
388399
from .variables import BuiltinVariable
389400

390401
# CPython here raises an exception. Since there is no python code, we have to manually setup the exception
391402
# stack and raise the exception.
403+
# If a message is provided but no args, use the message as the first argument
404+
if msg is not None and (args is None or len(args) == 0):
405+
args = [msg]
392406
exception_vt = BuiltinVariable(exc_type).call_function(tx, args or [], kwargs or {}) # type: ignore[arg-type]
393407
tx.exn_vt_stack.set_current_exception(exception_vt) # type: ignore[arg-type]
394-
raise get_dynamo_observed_exception(exc_type)
408+
raised_exc = get_dynamo_observed_exception(exc_type)
409+
# Store the original exception arguments for better error messages
410+
if args:
411+
raise raised_exc(*args)
412+
raise raised_exc
395413

396414

397415
def handle_observed_exception(tx: Any) -> None:
@@ -598,7 +616,10 @@ def unimplemented_v2(
598616
if log_warning:
599617
log.warning(msg)
600618
if from_exc is not _NOTHING:
601-
raise Unsupported(msg) from from_exc
619+
past_real_stack = None
620+
if hasattr(from_exc, "real_stack"):
621+
past_real_stack = from_exc.real_stack
622+
raise Unsupported(msg, real_stack=past_real_stack) from from_exc
602623
raise Unsupported(msg)
603624

604625

torch/_dynamo/symbolic_convert.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2204,6 +2204,7 @@ def bubble_exception_to_interpreter() -> None:
22042204
*graph_break_hints.USER_ERROR,
22052205
*graph_break_hints.SUPPORTABLE,
22062206
],
2207+
from_exc=raised_exception,
22072208
)
22082209

22092210
if sys.version_info >= (3, 11):

torch/_dynamo/variables/nn_module.py

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -104,10 +104,14 @@ def convert_to_fake(x):
104104
fake_kwargs = {k: convert_to_fake(v) for k, v in proxy_kwargs.items()}
105105
try:
106106
mod._infer_parameters(mod, fake_args, fake_kwargs)
107-
except AttributeError:
107+
except AttributeError as e:
108+
# Re-raise with the original error message from the AttributeError
108109
raise_observed_exception(
109110
AttributeError,
110111
tx,
112+
msg=str(e)
113+
if str(e)
114+
else "AttributeError during lazy module initialization",
111115
)
112116

113117

@@ -370,6 +374,7 @@ def var_getattr(self, tx: "InstructionTranslator", name):
370374
raise_observed_exception(
371375
AttributeError,
372376
tx,
377+
msg=f"'{type(base).__name__}' object has no attribute '{name}'",
373378
)
374379

375380
if name == "forward":
@@ -1199,7 +1204,11 @@ def manually_trace_nn_module_getattr(self, tx: "InstructionTranslator", name):
11991204
if out is None:
12001205
out = self.getattr_helper(tx, "_buffers", name_vt)
12011206
if out is None:
1202-
raise_observed_exception(AttributeError, tx)
1207+
raise_observed_exception(
1208+
AttributeError,
1209+
tx,
1210+
msg=f"'{type(self.value).__name__}' object has no attribute '{name}'",
1211+
)
12031212
return out
12041213

12051214

torch/_dynamo/variables/user_defined.py

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -277,7 +277,11 @@ def var_getattr(self, tx: "InstructionTranslator", name: str) -> "VariableTracke
277277
obj = inspect.getattr_static(self.value, name)
278278
except AttributeError:
279279
if type(self.value) is type:
280-
raise_observed_exception(AttributeError, tx)
280+
raise_observed_exception(
281+
AttributeError,
282+
tx,
283+
msg=f"type object '{self.value.__name__}' has no attribute '{name}'",
284+
)
281285
else:
282286
# Cannot reason about classes with a custom metaclass
283287
# See: test_functions::test_getattr_metaclass
@@ -1365,7 +1369,11 @@ def var_getattr(self, tx: "InstructionTranslator", name):
13651369
if tx.output.side_effects.has_pending_mutation_of_attr(self, name):
13661370
result = tx.output.side_effects.load_attr(self, name, deleted_ok=True)
13671371
if isinstance(result, variables.DeletedVariable):
1368-
raise_observed_exception(AttributeError, tx)
1372+
raise_observed_exception(
1373+
AttributeError,
1374+
tx,
1375+
msg=f"'{type(self.value).__name__}' object has no attribute '{name}'",
1376+
)
13691377
return result
13701378

13711379
if name == "__dict__":
@@ -1637,7 +1645,11 @@ def var_getattr(self, tx: "InstructionTranslator", name):
16371645
return VariableTracker.build(tx, subobj)
16381646

16391647
# Earlier we were returning GetAttrVariable but its incorrect. In absence of attr, Python raises AttributeError.
1640-
raise_observed_exception(AttributeError, tx)
1648+
raise_observed_exception(
1649+
AttributeError,
1650+
tx,
1651+
msg=f"'{type(self.value).__name__}' object has no attribute '{name}'",
1652+
)
16411653

16421654
def call_obj_hasattr(
16431655
self, tx: "InstructionTranslator", name: str

0 commit comments

Comments
 (0)