@@ -472,6 +472,7 @@ \subsection{CMakeLists.txt}
472
472
473
473
474
474
\subsection {Class wrap files }
475
+ \label {subsec:ClassWrapFiles }
475
476
476
477
Wrapping specification for classes is written in the module's \code {*.wrap}
477
478
CMake script files. These files call wrapping CMake macros, and they specify
@@ -929,6 +930,300 @@ \subsubsection{Wrapping Tests}
929
930
\end {minted}
930
931
931
932
933
+ \subsection {Debugging Strategies}
934
+ \label {subsec:DebuggingStrategies}
935
+
936
+ ITK wrappings allow users to make use of ITK classes in other languages for various
937
+ purposes, such as relying on ITK Python wrappings to sidestep C++ compilation steps
938
+ for rapid prototyping. However, this often introduces additional complexity in the
939
+ process of identifying and localizing issues in C++ classes or even in the wrapping
940
+ process itself. Fortunately, language-specific tools are available to assist in
941
+ the debugging process. In this section we focus on strategies for investigating
942
+ ITK Python code generated with SWIG.
943
+
944
+ \subsubsection {Swig Python Architecture}
945
+ \label {subsubsec:SwigArchitecture}
946
+
947
+ ITK 5 .x uses SWIG (Simplified Wrapper and Interface Generator) to distill ITK C++
948
+ classes into Python modules. This largely takes place in four distinct stages:
949
+
950
+ \begin {itemize}
951
+
952
+ \item \code {.wrap} CMake files, included in the ITK source tree in the \code {Wrapping}
953
+ folder for each module to define class template instantiations to be wrapped.
954
+ These are discussed in Section~\ref {subsec:ClassWrapFiles}.
955
+
956
+ \item SWIG \code {.cpp} source files generated at compile time at \code {Wrapping/Modules}
957
+ under the ITK build tree. These C++ files explicitly implement the class and template
958
+ instantiations defined in the class \code {.wrap} files in the source tree.
959
+ Debug symbols will be generated for these files.
960
+
961
+ \item SWIG compiled code. For Python wrappings these are generated as Python
962
+ \code {.pyd} (Windows) or \code {.so} (Linux or macOS) binaries and Python \code {.py} modules at
963
+ \code {Wrapping/Generators/Python/itk} in the ITK build tree.
964
+
965
+ \item Additional Python configuration files are generated in the
966
+ \code {Wrapping/Generators/Python} directory and its subdirectories.
967
+ \code {WrapITK.pth} provides the path for a Python environment to find the ITK
968
+ module, while \code {\_\_ init\_\_ .py} allows the module under development to be loaded correctly at
969
+ runtime. \code {<module\_ name>Config.py} defines module dependencies and class
970
+ template definitions, while \code {<module\_ name>\_ snake\_ case.py} maps
971
+ C++-style filter pipeline executions to Pythonic snake case functions.
972
+
973
+ \end {itemize}
974
+
975
+ ITK Python pre-compiled wheels may be obtained from the PyPI package index and contain
976
+ pre-compiled binaries without debugging symbols. To debug the native binaries a local build
977
+ must be created as in Section~\ref {sec:UsingCMakeForConfiguringAndBuildingITK} with a
978
+ \code {Debug} or \code {RelWithDebInfo} CMake build configuration as described below.
979
+
980
+ \begin {minted}[baselinestretch=1 ,fontsize=\footnotesize ,linenos=false,bgcolor=ltgray]{bash}
981
+ python -m pip install itk
982
+ \end {minted}
983
+
984
+
985
+ \subsubsection {Python Runtime Tracing}
986
+ \label {subsubsec:PythonRuntimeTracing}
987
+
988
+ ITK Python contains glue behavior to make ITK classes behave in a Pythonic manner,
989
+ such as querying object attributes, joining class names to template instantiations, and more.
990
+ Python-specific behavior in ITK Python may be investigated with the
991
+ Python Debugger module, \code {pdb}.
992
+
993
+ Tracing can be performed on a function call with \code {pdb.run}, or by editing ITK Python
994
+ files to add a \code {pdb.set\_ trace()} statement inline. In the following code snippet
995
+ an image is loaded and the debugger is set up to trace through an image cast operation.
996
+
997
+ \begin {minted}[baselinestretch=1 ,fontsize=\footnotesize ,linenos=false,bgcolor=ltgray]{bash}
998
+ (venv-itk) > python
999
+ >>> import itk
1000
+ >>> import pdb
1001
+ >>> image = itk.imread(r'myimage.mha',pixel_type=itk.F)
1002
+ >>> pdb.runeval('itk.cast_image_filter(image,ttype=[type(image),itk.Image[itk.UC,2 ]])')
1003
+ > <string>(1 )<module>()
1004
+ (Pdb)
1005
+ \end {minted}
1006
+
1007
+ At this point the debugger can step into and through Python code for translating ITK
1008
+ type names and getting the correct template instantiations to run the cast operation. More
1009
+ information on Python debugger commands can be found at
1010
+ \href {https://docs.python.org/3/library/pdb.html}{https://docs.python.org/3/library/pdb.html}.
1011
+
1012
+ While the Python debugger is useful, it does not allow us to examine implementation
1013
+ details of ITK classes in the native binary. A deeper investigation may be necessary for localizing
1014
+ errors in ITK classes.
1015
+
1016
+
1017
+ \subsubsection {C++ Runtime Tracing}
1018
+ \label {subsubsec:CppRuntimeTracing}
1019
+
1020
+ As discussed in Section~\ref {subsubsec:SwigArchitecture}, SWIG wrapping generates
1021
+ C++ source files and manages the interface and ownership semantics between
1022
+ Python objects and C++ objects during ITK compilation.
1023
+ When binary debug symbols are available, a running Python process may be attached to step through
1024
+ ITK or ITK SWIG sources at runtime. Several steps are required to set up and execute debugging:
1025
+
1026
+ \begin {enumerate}
1027
+
1028
+ \item The ITK build must be configured so that debug symbols
1029
+ are generated. Python wrapping must also be enabled.
1030
+ This is accomplished by setting the CMake variables
1031
+
1032
+ \code {CMAKE\_ BUILD\_ TYPE:STRING="RelWithDebInfo"} and \code {ITK\_ WRAP\_ PYTHON:BOOL="On"}.
1033
+ Note that the "RelWithDebInfo" build type is strongly encouraged over a "Debug"
1034
+ build as the former will build against a standard Python distribution.
1035
+ See Section~\ref {sec:UsingCMakeForConfiguringAndBuildingITK} for a detailed
1036
+ explanation of how to build ITK locally with CMake.
1037
+
1038
+ \item A Python virtual environment must be appropriately configured with
1039
+ \code {WrapITK.pth} so that the ITK debug build can be \code {import}ed.
1040
+ See Section~\ref {sec:Wrapping} for further explanation.
1041
+
1042
+ \item The ITK modules to be debugged must be loaded in a new Python session initialized
1043
+ from the given virtual environment. Given that ITK Python uses lazy loading, it is
1044
+ pragmatic to use \code {itk.force\_ load()} to ensure that all possible debug symbols
1045
+ are made available. The \code {os} module can be used to identify the PID of the given session.
1046
+
1047
+ \begin {minted}[baselinestretch=1 ,fontsize=\footnotesize ,linenos=false,bgcolor=ltgray]{bash}
1048
+ (venv-itk) > python
1049
+ >>> import itk
1050
+ >>> itk.force_load()
1051
+ >>> import os
1052
+ >>> os.getpid()
1053
+ 99999
1054
+ \end {minted}
1055
+
1056
+ \item The Python session may be attached to a debugger using the returned PID.
1057
+
1058
+ \begin {itemize}
1059
+
1060
+ \item On a Windows operating system, Microsoft Visual Studio 2019 or a similar platform
1061
+ can be used for attaching to a running process for debugging. Select "Attach To Process"
1062
+ from the Debug menu, choose "Native" code, and then search for the process PID. If
1063
+ debug symbols were generated correctly then ITK modules will appear under the list of
1064
+ loaded modules.
1065
+
1066
+ In Visual Studio, debugging can be enabled right from the start if the Python script
1067
+ is loaded into Visual Studio as part of a "Python Project".
1068
+ Mixed mode debugging needs to be enabled, as per Visual Studio
1069
+ documentation\footnote {
1070
+ \url {https://docs.microsoft.com/en-us/visualstudio/python/debugging-mixed-mode-c-cpp-python-in-visual-studio}}.
1071
+ Starting a project like this is an order of magnitude slower, as many debug symbols need to be loaded and examined.
1072
+
1073
+ \item On a Linux operating system the GNU Project Debugger \code {gdb} can be used
1074
+ for attaching to a running Python process, reading symbol files, and setting breakpoints.
1075
+ The following example attaches to a running process and sets a breakpoint inside
1076
+ an \code {itk.Image} Python object. It may be necessary to elevate user permissions
1077
+ to allow \code {gdb} to attach to the running process.
1078
+
1079
+ \begin {minted}[baselinestretch=1 ,fontsize=\footnotesize ,linenos=false,bgcolor=ltgray]{bash}
1080
+ (venv-itk) > gdb
1081
+ (gdb) > attach 99999
1082
+ Reading symbols from
1083
+ /path/to/ITK-build/Wrapping/Generators/Python/itk/_ITKPyBasePython.so...
1084
+ Reading symbols from
1085
+ /path/to/ITK-build/Wrapping/Generators/Python/itk/_ITKCommonPython.so...
1086
+ (gdb) > break /path/to/ITK-build/Wrapping/Modules/ITKCommon/itkImagePython.cpp:<ln>
1087
+ Breakpoint 1 at ...:
1088
+ file /path/to/ITK-build/Wrapping/Modules/ITKCommon/itkImagePython.cpp, line <ln>.
1089
+ (gdb) > break /path/to/ITK-source/Core/Common/include/itkImage.hxx:<ln>
1090
+ Breakpoint 2 at ...:
1091
+ /path/to/ITK-source/Modules/Core/Common/include/itkImage.hxx:<ln>
1092
+ (gdb) > c
1093
+
1094
+ ... continue in Python session until breakpoint is hit ...
1095
+ \end {minted}
1096
+
1097
+ \item On a macOS operating system the LLDB debugger can be used for attaching to a
1098
+ Python process in much the same way as GDB on Linux, with a few extra security
1099
+ requirements and slightly different command syntax. The following example
1100
+ attaches to a running process and sets a breakpoint inside an \code {itk.Image}
1101
+ Python object.
1102
+
1103
+ \begin {minted}[baselinestretch=1 ,fontsize=\footnotesize ,linenos=false,bgcolor=ltgray]{bash}
1104
+ (venv-itk) > lldb
1105
+ (lldb) process attach -- pid 99999
1106
+ Process 99999 stopped
1107
+ * thread #1 , queue = 'com.apple.main-thread', stop reason = signal SIGSTOP
1108
+ frame #0 : 0 x00007 fff203 ec656 libsystem_kernel.dylib`__select + 10
1109
+ libsystem_kernel.dylib`__select:
1110
+ -> 0 x7 fff203 ec656 <+10 >: jae 0 x7 fff203 ec660 ; <+20 >
1111
+ 0 x7 fff203 ec658 <+12 >: movq % rax, %rdi
1112
+ 0 x7 fff203 ec65 b <+15 >: jmp 0 x7 fff203 e56 bd ; cerror
1113
+ 0 x7 fff203 ec660 <+20 >: retq
1114
+ Target 0 : (Python) stopped.
1115
+
1116
+ Executable module set to
1117
+ "/Library/Frameworks/Python.framework/Versions/3.9 /
1118
+ Resources/Python.app/Contents/MacOS/Python".
1119
+ Architecture set to: x86 _64 h-apple-macosx-.
1120
+
1121
+ (lldb) breakpoint set
1122
+ -f /path/to/ITK/Modules/Core/Common/include/itkImage.hxx
1123
+ --line <ln>
1124
+ Breakpoint 1 : 172 locations.
1125
+ (lldb) continue
1126
+
1127
+ ... continue in Python session until breakpoint is hit ...
1128
+ \end {minted}
1129
+
1130
+ LLDB command syntax is documented at \url {https://lldb.llvm.org/index.html}.
1131
+
1132
+ Developers may find it necessary to examine the follow security concepts
1133
+ and requirements in order to permit \code {lldb} to attach to Python:
1134
+
1135
+ \begin {itemize}
1136
+
1137
+ \item The Python process must be authorized for debugging. MacOS relies on the
1138
+ process of "hardened" runtimes to mitigate security concerns, which reduces
1139
+ the ability of debuggers such as \code {lldb} and other processes to attach
1140
+ to and intercept the functions of other programs. Python distributions
1141
+ are intentionally "hardened" in this way, but additional settings can be
1142
+ enabled to allow just-in-time debugging.
1143
+
1144
+ \item It may be necessary to enter developer mode to allow Python debugging.
1145
+ This is accomplished with the following command in the developer console:
1146
+
1147
+ \begin {minted}[baselinestretch=1 ,fontsize=\footnotesize ,linenos=false,bgcolor=ltgray]{bash}
1148
+ DevToolsSecurity -enable
1149
+ \end {minted}
1150
+
1151
+ \item It may be necessary to add entitlements to the Python executable so that
1152
+ \code {lldb} can attach to the hardened process. This may be accomplished by
1153
+ updating a \code {.plist} entitlements file and setting the executable entitlements.
1154
+
1155
+ \begin {minted}[baselinestretch=1 ,fontsize=\footnotesize ,linenos=false,bgcolor=ltgray]{bash}
1156
+ codesign -d --entitlements :-
1157
+ "/path/to/python" >> "/tmp/path/to/python_entitlements.plist"
1158
+ /usr/libexec/PlistBuddy -c
1159
+ "Add :com.apple.security.get-task-allow bool true"
1160
+ "/tmp/path/to/python_entitlements.plist"
1161
+ /usr/libexec/PlistBuddy -c
1162
+ "Add :com.apple.security.cs.allow-jit bool true"
1163
+ "/tmp/path/to/python_entitlements.plist"
1164
+ codesign --force --options runtime --sign -
1165
+ --entitlements "/tmp/path/to/python_entitlements.plist"
1166
+ "/path/to/python"
1167
+ \end {minted}
1168
+
1169
+ \item If attaching to the process continues to fail, log files can
1170
+ be dumped with \code {log collect} and opened in the Console application.
1171
+ \code {lldb} error messages will be listed as \code {debugserver} entries.
1172
+
1173
+ \end {itemize}
1174
+
1175
+ \end {itemize}
1176
+
1177
+ \end {enumerate}
1178
+
1179
+ With these steps completed the respective debugger can be used to set breakpoints
1180
+ and step through C++ source code for a respective ITK Python execution.
1181
+
1182
+ The debugger can also be used for examining runtime failures and crashes
1183
+ either at the time the error occurs or posthumously with a dump file.
1184
+
1185
+ \begin {itemize}
1186
+
1187
+ \item On Windows, Microsoft Visual Studio will catch process aborts
1188
+ to allow the attached process to be examined before exit. Stacks, threads,
1189
+ and variables are made available to the user for backtracing via the
1190
+ \code {Debug} toolbar menu. Dump files in the minidump format may also
1191
+ be manually saved and reloaded later for investigation\footnote {
1192
+ \url {https://docs.microsoft.com/en-us/visualstudio/debugger/using-dump-files?view=vs-2022}}.
1193
+
1194
+ \item On Linux, \code {gdb} will catch process aborts at runtime to allow
1195
+ a developer to examine the program state before it exits.
1196
+ If allowed, core dumps can also be generated on program failures
1197
+ to allow posthumous debugging. The following sample
1198
+ configures a Linux system to remove the default limit of 0 for allowable
1199
+ coredump size and to write out coredump files to the \code {/tmp/} directory.
1200
+
1201
+ \begin {minted}[baselinestretch=1 ,fontsize=\footnotesize ,linenos=false,bgcolor=ltgray]{bash}
1202
+ > ulimit -c unlimited
1203
+ > sudo bash -c 'echo "/tmp/coredump-% e.%p" > /proc/sys/kernel/core_pattern'
1204
+ \end {minted}
1205
+
1206
+ More information is available in Python documentation\footnote {
1207
+ \url {https://pythondev.readthedocs.io/debug_tools.html\#create-a-core-dump-file}}.
1208
+
1209
+ \item On MacOS, \code {lldb} will catch process aborts at runtime
1210
+ and may also be used to examine core dumps. The following sample
1211
+ configures a MacOS system to write out core dump files to the \code {/cores/}
1212
+ directory and then runs \code {lldb} to inspect a core dump.
1213
+
1214
+ \begin {minted}[baselinestretch=1 ,fontsize=\footnotesize ,linenos=false,bgcolor=ltgray]{bash}
1215
+ (venv-itk) > ulimit -c unlimited
1216
+
1217
+ ... Run process and generate a core dump ...
1218
+
1219
+ (venv-itk) > lldb
1220
+ (lldb) target create "python3 " --core "/cores/core.1007 "
1221
+ Core file '/cores/core.1007 ' (x86 _64 ) was loaded.
1222
+ \end {minted}
1223
+
1224
+ \end {itemize}
1225
+
1226
+
932
1227
\section {Third-Party Dependencies}
933
1228
\label {sec:ThirdParty}
934
1229
\index {module!third-party}
0 commit comments