neuronsimulator / nrn

NEURON Simulator
http://nrn.readthedocs.io
Other
376 stars 113 forks source link

Convert C++ exception to Python exceptions. #2923

Closed 1uc closed 2 weeks ago

1uc commented 2 weeks ago

This PR introduces a wrapper of all C function that are registered with Python. The wrapper catches all exceptions, and converts them to Python exceptions, then it returns a value signaling an error.

The reason we want to convert C++ exceptions to Python errors, is that when a C++ exception occurs, it terminates the program in a manner that makes it very hard to determine which Python code triggered the exception. For example PDB will crash before being able to print a backtrace. Similarly the regular Python exception mechanism will not run; and not print the usual helpful backtraces. Using GDB for example reveals that PyEval_EvalCode calls getattro for rng_StochKv3, but it doesn't help understand what happened in the Python layer, i.e. what lead to calling getattro.

With the wrapper we get the following output for #2783:

Traceback (most recent call last):
  File "/repro_neuron9_bug/nrn-repro.py", line 20, in <module>
    s.psection()
  File "/build-debug/install/lib/python/neuron/psection.py", line 66, in psection
    pvals.append(getattr(seg, mechname))
                 ^^^^^^^^^^^^^^^^^^^^^^
RuntimeError: generic_data_handle{raw=0x57a598c7f120 type=void*} cannot be converted to data_handle<double>

It contains the C++ error message generic_data_handle bla bla bla but also the Python backtrace. We get the same behaviour in the more complex reproducer, which allowed us to instantly spot that the bug was indeed in NRN not in the user library. We can now use PDB for debugging:

> /build-debug/install/lib/python/neuron/psection.py(66)psection()
-> pvals.append(getattr(seg, mechname))
(Pdb) l
 61                    mechname = name  # + '_' + mech
 62                    for seg in sec:
 63                        if n > 1:
 64                            pvals.append([getattr(seg, mechname)[i] for i in range(n)])
 65                        else:
 66  ->                        pvals.append(getattr(seg, mechname))
 67                my_results[
 68                    name[: -(len(mech) + 1)] if name.endswith("_" + mech) else name
 69                ] = pvals
 70            # TODO: should be a better way of testing if an ion
 71            if mech.endswith("_ion"):

If we want to debug the C++ layer we use GDB as before on x86_64/special.

azure-pipelines[bot] commented 2 weeks ago

✔️ febdf6d570c8821ba5eb1e0069810328f561990e -> Azure artifacts URL

bbpbuildbot commented 2 weeks ago

Logfiles from GitLab pipeline #217749 (:no_entry:) have been uploaded here!

Status and direct links:

azure-pipelines[bot] commented 2 weeks ago

✔️ f8eb6ef6d8c9b2017eb47ec749582209cbaca667 -> Azure artifacts URL

sonarcloud[bot] commented 2 weeks ago

Quality Gate Passed Quality Gate passed

Issues
15 New issues
0 Accepted issues

Measures
0 Security Hotspots
0.0% Coverage on New Code
0.0% Duplication on New Code

See analysis details on SonarCloud

azure-pipelines[bot] commented 2 weeks ago

✔️ 4f3b5ab91e08b833616a56b188134f25f8caf3a8 -> Azure artifacts URL

bbpbuildbot commented 2 weeks ago

Logfiles from GitLab pipeline #217821 (:no_entry:) have been uploaded here!

Status and direct links: