|
| 1 | +.. _examples_dev_flow_debug_visual: |
| 2 | + |
| 3 | + |
| 4 | +Debugging shared libraries with Visual Studio |
| 5 | +============================================= |
| 6 | + |
| 7 | +In the previous example we discussed how to debug dependencies in Visual Studio, but when using Conan dependencies in a project it is |
| 8 | +possible that the original build folder and build files don't exist. Conan packages don't contain the necessary |
| 9 | +information for debugging libraries with Visual Studio by default, this information is stored in PDBs that are |
| 10 | +generated during the compilation of the libraries. When using Conan these PDBs are generated in the build folder, |
| 11 | +which is only needed during the building of the libraries. For that reason it's a common operation to clean the Conan |
| 12 | +cache with ``conan cache clean`` to remove the build folder and save disk space. |
| 13 | + |
| 14 | +For these cases where the build folder is not present we created a hook that copies the PDBs generated in the build folder to the |
| 15 | +package folder. This behavior can't be forced by default because PDB files are usually larger than the whole package, |
| 16 | +and it would greatly increase the package sizes. |
| 17 | + |
| 18 | +This section will follow some examples on how to debug a project in different cases to show how users can make use of |
| 19 | +the PDB hook. |
| 20 | + |
| 21 | + |
| 22 | +Creating a project and debugging as usual |
| 23 | +----------------------------------------- |
| 24 | + |
| 25 | +First we will debug our project as usual, as it is explained in more detail in the :ref:`previous example <examples_dev_flow_debug_step_into>` |
| 26 | +We can start building our dependencies from sources as in the previous section, only this time we will build them as |
| 27 | +shared. To begin with, clone the sources needed for the example from the `examples2 repository <https://github.com/conan-io/examples2>`_ |
| 28 | +in GitHub and create the project. |
| 29 | + |
| 30 | +.. code-block:: bash |
| 31 | +
|
| 32 | + $ git clone https://github.com/conan-io/examples2.git |
| 33 | + $ cd examples2/tutorial/consuming_packages/simple_cmake_project |
| 34 | + $ conan install . -o="*:shared=True" -s build_type=Debug --build="zlib/*" |
| 35 | + ... |
| 36 | + Install finished successfully |
| 37 | +
|
| 38 | + # CMake presets require CMake>=3.23 |
| 39 | + $ cmake --preset=conan-default |
| 40 | +
|
| 41 | +.. note:: |
| 42 | + |
| 43 | + We will only cover the case where the dependencies are built as shared because the PDBs and how they are linked |
| 44 | + to the libraries works differently for static libraries. |
| 45 | + |
| 46 | +We can now open the solution ``compressor.sln`` to open our project in Visual Studio and debug it as explained in the |
| 47 | +previous example. Setting a breakpoint in line 22, running the debugger and using the step into will allow us to debug |
| 48 | +inside our dependency file ``deflate.c``. |
| 49 | + |
| 50 | +.. image:: ../../../images/examples/dev_flow/debug_with_build_files.png |
| 51 | + :alt: Debugging with build files in cache |
| 52 | + |
| 53 | +In this case the original build files were all present so the debugger worked as usual. Next we will see how the |
| 54 | +debugger works after removing the build files from the Conan cache. |
| 55 | + |
| 56 | + |
| 57 | +Removing build files from the Conan cache |
| 58 | +----------------------------------------- |
| 59 | + |
| 60 | +There are multiple reasons that can cause the build files to not be present after the dependencies are compiled. We |
| 61 | +will clean our build files from the cache to simulate one of those cases using ``conan cache clean``. The ``--build`` |
| 62 | +flag makes sure that we only remove the build files, as we will need our source files for this example. |
| 63 | + |
| 64 | +.. code-block:: bash |
| 65 | +
|
| 66 | + $ conan list zlib/1.2.11:* |
| 67 | + $ conan cache path --folder build zlib/1.2.11:17b26a16efb893750e4481f98a154db2934ead88 |
| 68 | + $ conan cache clean zlib/1.2.11 --build |
| 69 | + $ conan cache path --folder build zlib/1.2.11:17b26a16efb893750e4481f98a154db2934ead88 |
| 70 | +
|
| 71 | +After closing and reopening our solution in Visual Studio, we can try to debug again. If you try to step into the |
| 72 | +dependency, with the breakpoint on line 22, you will notice it will directly skip over to the next line as Visual Studio |
| 73 | +doesn't have any information on the dependencies to debug. |
| 74 | + |
| 75 | + |
| 76 | +Installing a hook to copy the PDBs to the package folder |
| 77 | +-------------------------------------------------------- |
| 78 | + |
| 79 | +To solve the issue of not having the PDBs in the package folder, we created a hook that copies the PDBs from the build |
| 80 | +folder to the package folder. |
| 81 | +The hook is available in the `conan-extensions repository <https://github.com/conan-io/conan-extensions>`_. |
| 82 | +Installing the whole repository will work, but we recommend to only install the hooks folder from the |
| 83 | +``conan-extensions`` repository with: |
| 84 | + |
| 85 | +.. code-block:: text |
| 86 | +
|
| 87 | + $ conan config install https://github.com/conan-io/conan-extensions.git -sf=extensions/hooks -tf=extensions/hooks |
| 88 | +
|
| 89 | +The hook is made so it won't run by default, as it can increase the package size significantly. As explained in the |
| 90 | +:ref:`hooks documentation <reference_extensions_hooks>`, we need to change the name of our hook to start with ``hook_``. |
| 91 | +To locate the path where the hook was placed, run the command ``conan config home`` to find |
| 92 | +your local cache path and go to the ``extensions/hooks`` folder to rename the ``_hook_copy_pdbs_to_package.py`` file. |
| 93 | +Be aware that this hook will run everytime a ``package()`` method is run, to disable the hook just rename the hook back |
| 94 | +to start with ``_hook_``. |
| 95 | + |
| 96 | +The hook is implemented as a post-package hook, which means that it will execute after the package is created through the |
| 97 | +``package()`` method of a recipe. This avoids any potential issue, as the order will be as follows: |
| 98 | + |
| 99 | +- The ``build()`` method of the recipe is executed, generating the DLLs and PDBs |
| 100 | +- The ``package()`` method of the recipe is executed, copying the necessary files to the package folder (in this case the DLLs but not the PDBs) |
| 101 | +- The hook is executed copying the PDBs from the build folder next to its DLL for every DLL in the package |
| 102 | + |
| 103 | +The hook makes use of the ``dumpbin`` tool which is included in the Visual Studio installation. This tool allows us |
| 104 | +to get information of a DLL, in this case the path where its associated PDB is located. It will be used for every DLL |
| 105 | +in the package to locate its PDB to copy it to the package folder. |
| 106 | + |
| 107 | +For more information on how PDBs work with Visual and how we used it to create the hook can be found in the |
| 108 | +`hook readme <https://github.com/conan-io/conan-extensions/blob/main/hooks/README.md>`_. |
| 109 | + |
| 110 | +Debugging without build files |
| 111 | +----------------------------- |
| 112 | + |
| 113 | +After installing the hook we will create again the project from sources so the hook can now copy the PDBs to the package |
| 114 | +folder alongside the package DLLs so they can be found by the debugger. |
| 115 | + |
| 116 | +.. code-block:: bash |
| 117 | +
|
| 118 | + $ conan install . -o="*:shared=True" -s build_type=Debug --build="zlib/*" |
| 119 | + ... |
| 120 | + zlib/1.2.11: Calling package() |
| 121 | + ... |
| 122 | + [HOOK - hook_copy_pdbs_to_package.py] post_package(): PDBs post package hook running |
| 123 | + ... |
| 124 | + Install finished successfully |
| 125 | +
|
| 126 | + # CMake presets require CMake>=3.23 |
| 127 | + $ cmake --preset=conan-default |
| 128 | +
|
| 129 | +Notice that when running the conan install now you will see the outputs of the hook running after the call to ``package()``. |
| 130 | +To test the hook we can clean the cache again to remove the build files, this includes the sources used to build the |
| 131 | +library and the PDBs that were originally generated. |
| 132 | + |
| 133 | +.. code-block:: bash |
| 134 | +
|
| 135 | + $ conan cache clean zlib/1.2.11 --build |
| 136 | +
|
| 137 | +Open the solution in Visual Studio again and start the debugger. When you try to step into the dependency in line 22, an error |
| 138 | +message will pop up telling us the file was not found and it will ask where the file is located. We can close this window |
| 139 | +and it will give the option to view the disassembly which can be debugged thanks to the PDB. The PDB only contains the |
| 140 | +debugging information but Visual Studio is missing the source files, so it won't be able to debug over those as it did |
| 141 | +initially. |
| 142 | + |
| 143 | +.. image:: ../../../images/examples/dev_flow/source_file_not_found.png |
| 144 | + :alt: Debugging without build files in cache |
| 145 | + |
| 146 | + |
| 147 | +Locating the sources path for the debugger |
| 148 | +------------------------------------------ |
| 149 | + |
| 150 | +Visual Studio won't be able to find te source files by itself after deleting the original build files. To be able to |
| 151 | +debug over the source files, there's an option to manually set the source folder path so that it's possible to debug over the source files. This |
| 152 | +requires that the source files for the dependency exist. In our case we can get the location of this source files |
| 153 | +by running a ```conan cache path``. |
| 154 | + |
| 155 | +.. code-block:: |
| 156 | +
|
| 157 | + $ conan cache path --folder source zlib/1.2.11 |
| 158 | +
|
| 159 | +In case this source path is not present we can use a config to download the sources again. |
| 160 | + |
| 161 | +.. code-block:: |
| 162 | +
|
| 163 | + $ conan install . -o="*:shared=True" -s build_type=Debug -c:a="tools.build:download_source=True" |
| 164 | +
|
| 165 | +Once we have the source path we can set it in Visual Studio so the debugger can find the source files. Right click on |
| 166 | +the solution in the Solution Explorer and select Properties. Go to Debug Source Files in the Common Properties section |
| 167 | +and add our source path. |
| 168 | + |
| 169 | +.. image:: ../../../images/examples/dev_flow/add_path_to_debug_source_files.png |
| 170 | + :alt: Setting source path |
| 171 | + |
| 172 | +Starting the debugger again will allow to step into the code of the dependency as in the first example we did. |
| 173 | + |
| 174 | +.. note:: |
| 175 | + |
| 176 | + If there are patches to the source files we won't be able to debug over the modified files, as we are using the |
| 177 | + files from the source folder and the patches are applied in a later step right before being compiled in the build folder. |
| 178 | + |
| 179 | + Any modification to the source files will not allow debugging over them, as Visual Studio does a checksum check, so |
| 180 | + they need to be the exact same files as when the libraries were compiled. |
| 181 | + |
| 182 | + |
0 commit comments