getsentry / sentry-native

Sentry SDK for C, C++ and native applications.
MIT License
393 stars 165 forks source link

MSVC static lib build produces PDB that's not recognized by Sentry CLI #895

Closed vaind closed 10 months ago

vaind commented 11 months ago

Description

When I build a static library, a PDB file is produced but this PDB file doesn't get picked up by sentry-cli during symbol upload. Although this may be a CLI issue, it feels more like a build setting issue. Maybe related to this: https://stackoverflow.com/questions/7575298/static-library-debug-symbols

When does the problem happen

Environment

Steps To Reproduce

  1. Build with the following command
    cmake -B build -S . ^
        -D SENTRY_SDK_NAME=sentry.native.dotnet ^
        -D SENTRY_BUILD_SHARED_LIBS=0 ^
        -D SENTRY_BUILD_RUNTIMESTATIC=1 ^
        -D SENTRY_BACKEND=none ^
        -D SENTRY_TRANSPORT=none
    cmake --build build --target sentry --config RelWithDebInfo --parallel
  2. Check the output with sentry-cli (tested with v2.21.2)
    sentry-cli-Windows-x86_64.exe debug-files check build\RelWithDebInfo\sentry.pdb
  3. Observe the output:
    Debug Info File Check
    Type: pdb
    Contained debug identifiers:
    Contained debug information:
    > none
    Usable: no (missing debug identifier, likely stripped)

    Log output

-- Building for: Visual Studio 17 2022
  -- Selecting Windows SDK version 10.0.22000.0 to target Windows 10.0.22621.
  -- The C compiler identification is MSVC 19.37.32824.0
  -- The CXX compiler identification is MSVC 19.37.32824.0
  -- The ASM compiler identification is MSVC
  -- Found assembler: .../VC/Tools/MSVC/14.37.32822/bin/Hostx64/x64/cl.exe
  -- Detecting C compiler ABI info
  -- Detecting C compiler ABI info - done
  -- Check for working C compiler: .../VC/Tools/MSVC/14.37.32822/bin/Hostx64/x64/cl.exe - skipped
  -- Detecting C compile features
  -- Detecting C compile features - done
  -- Detecting CXX compiler ABI info
  -- Detecting CXX compiler ABI info - done
  -- Check for working CXX compiler: .../VC/Tools/MSVC/14.37.32822/bin/Hostx64/x64/cl.exe - skipped
  -- Detecting CXX compile features
  -- Detecting CXX compile features - done
  -- Performing Test CMAKE_HAVE_LIBC_PTHREAD
  -- Performing Test CMAKE_HAVE_LIBC_PTHREAD - Failed
  -- Looking for pthread_create in pthreads
  -- Looking for pthread_create in pthreads - not found
  -- Looking for pthread_create in pthread
  -- Looking for pthread_create in pthread - not found
  -- Found Threads: TRUE
  -- SENTRY_TRANSPORT=none
  -- SENTRY_BACKEND=none
  -- SENTRY_LIBRARY_TYPE=STATIC
  -- SENTRY_SDK_NAME=sentry.native.dotnet
  -- Looking for sys/types.h
  -- Looking for sys/types.h - found
  -- Looking for stdint.h
  -- Looking for stdint.h - found
  -- Looking for stddef.h
  -- Looking for stddef.h - found
  -- Check size of long
  -- Check size of long - done
  -- Configuring done (6.2s)
  -- Generating done (0.1s)
  -- Build files have been written to: C:/dev/dotnet/modules/sentry-native/build
  MSBuild version 17.7.2+d6990bcfa for .NET Framework

    1>Checking Build System
    Building Custom Rule C:/dev/dotnet/modules/sentry-native/CMakeLists.txt
    mpack.c
    sentry_alloc.c
    sentry_backend.c
    sentry_core.c
    sentry_database.c
    sentry_envelope.c
    sentry_info.c
    sentry_json.c
    sentry_logger.c
    sentry_options.c
    sentry_os.c
    sentry_random.c
    sentry_ratelimiter.c
    sentry_scope.c
    sentry_session.c
    sentry_slice.c
    sentry_string.c
    sentry_sync.c
    sentry_transport.c
    sentry_utils.c
    sentry_uuid.c
    Generating Code...
    Compiling...
    sentry_value.c
    sentry_tracing.c
    sentry_path.c
    sentry_disk_transport.c
    sentry_function_transport.c
    sentry_unwinder.c
    sentry_windows_dbghelp.c
    sentry_path_windows.c
    sentry_symbolizer_windows.c
    sentry_modulefinder_windows.c
    sentry_transport_none.c
    sentry_backend_none.c
    sentry_unwinder_dbghelp.c
    Generating Code...
    sentry.vcxproj -> C:\dev\dotnet\modules\sentry-native\build\RelWithDebInfo\sentry.lib
supervacuus commented 11 months ago

Hi, @vaind!

The full PDB is a product of the linker. The linker is not involved when building a static library, so no full PDB is generated. The PDB you're seeing is the last of the compiler's partial PDBs for each translation unit (= object file).

We can use the /Z7 flag for static libraries to embed the debug information in the lib, but I am unsure if sentry-cli supports that format. Of course, if you link your static library with an executable or shared library, the generated PDB for that build artifact will contain all the debug information, and you should be able to upload it.

What is the scenario? There may be a better way to package the artifact.

vaind commented 11 months ago

if you link your static library with an executable or shared library, the generated PDB for that build artifact will contain all the debug information, and you should be able to upload it.

Would that be the case when we use the /z7 flag or already with how the static lib is built now?

What is the scenario? There may be a better way to package the artifact.

This is for .net SDK native AOT support

supervacuus commented 11 months ago

if you link your static library with an executable or shared library, the generated PDB for that build artifact will contain all the debug information, and you should be able to upload it.

Would that be the case when we use the /z7 flag or already with how the static lib is built now?

If you need to distribute the static library, then we'd need to use /Z7. If you build the follow-up artifact on the same machine then the way it is built now should be enough.

This is for .net SDK native AOT support

Okay, but which requirements of this use case lead you to build it in this specific way (why static vs dynamic, why static runtime, etc.)?

vaind commented 11 months ago

If you need to distribute the static library, then we'd need to use /Z7.

Yes, the library is zipped up to a .nuget package and then used by users. I guess it should be possible to add this flag when calling cmake to configure the project, right? Probably no need to change this in sentry-native (and wait for a release). Unless it's useful for others in the future.

Okay, but which requirements of this use case lead you to build it in this specific way (why static vs dynamic, why static runtime, etc.)?

With .NET Native AOT (which is a new thing), the final app that integrates Sentry is compiled as a single standalone native executable (for example for server-side symbolication, I got to use native debug images, not what we originally got from .NET). The idea is that providing a static library, it gets compiled into the final executable - this actually all works fine, I'm just figuring out what to do with the generated PDB and was scratching my head for a bit why it didn't get picked up by sentry-cli during upload (you know it's a new code on the .net side so at first I thought I was doing something wrong...).

supervacuus commented 11 months ago

Yes, the library is zipped up to a .nuget package and then used by users. I guess it should be possible to add this flag when calling cmake to configure the project, right? Probably no need to change this in sentry-native (and wait for a release). Unless it's useful for others in the future.

I have no problem adding a CMake variable/option to let users choose the debug format. If you want to validate this first, you can do so by modifying the initial C/C++ flags for RelWithDebInfo by supplying a preload-script to populate the CMake cache before project() loads the toolchain-specific flags:

  1. create a file named relwithdebinfo_init_flags.cmake (or something; the actual name/path is insignificant). This is your initial cache file.
  2. enter the following lines in that file (these are the MSVC defaults with /Z7 instead of /Zi):
    set(CMAKE_C_FLAGS_RELWITHDEBINFO "/Z7 /O2 /Ob1 /DNDEBUG" CACHE STRING "C Flags for RelWithDebInfo" FORCE)
    set(CMAKE_CXX_FLAGS_RELWITHDEBINFO "/Z7 /O2 /Ob1 /DNDEBUG" CACHE STRING "CXX Flags for RelWithDebInfo" FORCE)
  3. adapt your configure invocation to:
    cmake -B build -S . ^
        -D SENTRY_SDK_NAME=sentry.native.dotnet ^
        -D SENTRY_BUILD_SHARED_LIBS=0 ^
        -D SENTRY_BUILD_RUNTIMESTATIC=1 ^
        -D SENTRY_BACKEND=none ^
        -D SENTRY_TRANSPORT=none ^
        -C .\relwithdebinfo_init_flags.cmake
  4. build like you already did (if you use --verbose, you will see /Z7 used during compilation). The output will no longer contain a PDB, and the resulting static library will grow accordingly.
  5. if you link this library to an executable or shared library, you will get a PDB that contains the debug information in your static library (given that the build config produces a PDB).

With .NET Native AOT (which is a new thing), the final app that integrates Sentry is compiled as a single standalone native executable (for example for server-side symbolication, I got to use native debug images, not what we originally got from .NET). The idea is that providing a static library, it gets compiled into the final executable - this actually all works fine, I'm just figuring out what to do with the generated PDB and was scratching my head for a bit why it didn't get picked up by sentry-cli during upload (you know it's a new code on the .net side so at first I thought I was doing something wrong...).

Okay, thanks for the insight. Sounds like a static library makes the most sense as a first step.

vaind commented 11 months ago

Thanks @supervacuus I'll give it a try in the dotnet repo first

vaind commented 10 months ago

Building as suggested did change what's created although I can't really tell whether there are debug symbols in the static lib or not. https://github.com/getsentry/sentry-dotnet/pull/2765

supervacuus commented 10 months ago

I can't really tell whether there are debug symbols in the static lib or not. getsentry/sentry-dotnet#2765

Yes, you will only be able to verify this when building an AOT target, and it produces a PDB. The upside of creating a DLL instead of static library - even for the AOT use-case - would be to have a PDB before that build (which is also relevant when you want to provide the debug-info for all users once instead of letting them do the upload as part of their project).