jeremy-rifkin / cpptrace

Simple, portable, and self-contained stacktrace library for C++11 and newer
MIT License
621 stars 64 forks source link

Problems on MSDEV 2019 #116

Closed LouisClt closed 4 months ago

LouisClt commented 4 months ago

Hello, first of all thanks for your library, which is really useful.

I tried using it on windows and I found different problems (I gather them here because, it is mainly minor bypassable problems, sorry for this) :

  1. The 0.5.2 version that we can find here or in vcpkg does not compile (problem with format<> (I use visual studio 2019). Problem not present in the main version that I took)
  2. Cmake by default builds the library in static mode (producing only a cpptrace.lib file). However, When I tried to use it in my program, I got unresolved symbols along these lines :
    1>Util.obj : error LNK2001: unresolved external symbol "__declspec(dllimport) struct cpptrace::stacktrace __cdecl cpptrace::generate_trace(unsigned __int64)" (__imp_?generate_trace@cpptrace@@YA?AUstacktrace@1@_K@Z)
    1>Util.obj : error LNK2001:  unresolved external symbol "__declspec(dllimport) public: class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > __cdecl cpptrace::stacktrace::to_string(bool)const " (__imp_?to_string@stacktrace@cpptrace@@QEBA?AV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@_N@Z)
    1>Util.obj : error LNK2001:  unresolved external symbol "__declspec(dllimport) public: __cdecl cpptrace::stacktrace::~stacktrace(void)" (__imp_??1stacktrace@cpptrace@@QEAA@XZ)

    I managed to bypass this issue by setting CPPTRACE_STATIC_DEFINE before including CPPTrace. Maybe it will be good to document this

  3. cpptrace was not working at first because there was a problem of initialization with the "SymInitialize " of dbghelp. I think that was maybe because I there was another component used that was calling this method beforehand (which should only be called once). Now I call directly the generate_trace method at the startup and it works better.(furthermore, this method should not be called in multi thread so it is better to make sure this is called directly once (which is not done by default in cpptrace)). I now also call SymRefreshModuleList before generate_trace because I load dlls dynamically.
  4. I am generating a string with the "to_string" method (on a "stack_trace"). But there was 0 (nulls) in the middle of the string produced (which was badly interpreted when given to my tracing method as it usually means end of string). I replaced all the nulls with ' ' and now it works fine.
jeremy-rifkin commented 4 months ago

Hi, thanks for taking the time to write these out. I'll try to go in order:

  1. I haven't seen an issue with format, so I'll take a look
  2. Yeah, sorry about that. CPPTRACE_STATIC_DEFINE is mentioned in the README, however perhaps not prominently enough. It's hard to figure out where to place it where people will actually see it.
  3. Correct, SymInitialize must be called only once so if other components are calling it that's a problem. Unfortunately I don't know of a way to detect that it has already been called, other than maybe adding a method to cpptrace to let the user indicate it's already been done. Cpptrace's invocation of SymInitialize does use a lock in case of multithreading, however it's not possible to protect against non-cpptrace codepaths as far as I know.
  4. This sounds like the same issue as #113 which is resolved and will be part of the next release
jeremy-rifkin commented 4 months ago

Oh, and SymRefreshModuleList I'm not familiar with. What benefit does it bring?

jeremy-rifkin commented 4 months ago

What issue with format<> did you find? I'm not able to reproduce anything right off hand.

LouisClt commented 4 months ago

Hi, thanks for your answers.

1- Here is the error I got (but, again, it seems to be fixed on the main branch) :

cl : Command line warning D9025 : overriding '/W3' with '/W4'
\cpptrace\src\v0.5.2-dcbc54c7ef.clean\src\utils\microfmt.hpp(316): error C2664: 'std::string microfmt::detail::format<0>(const char *,const char *,std::array<microfmt::detail::format_value,0>)': cannot convert argument 3 from 'initializer list' to 'std::array<microfmt::detail::format_value,0>'
\vcpkg-master\buildtrees\cpptrace\src\v0.5.2-dcbc54c7ef.clean\src\utils\microfmt.hpp(316): note: Invalid aggregate initialization
\vcpkg-master\buildtrees\cpptrace\src\v0.5.2-dcbc54c7ef.clean\src\utils\microfmt.hpp(215): note: see declaration of 'microfmt::detail::format'
\vcpkg-master\buildtrees\cpptrace\src\v0.5.2-dcbc54c7ef.clean\src\utils\error.hpp(25): note: see reference to function template instantiation 'std::string microfmt::format<>(const char *)' being compiled
\vcpkg-master\buildtrees\cpptrace\src\v0.5.2-dcbc54c7ef.clean\src\unwind\../utils/utils.hpp(455): note: see reference to function template instantiation 'cpptrace::detail::internal_error::internal_error<>(const char *)' being compiled

2- Indeed, now that you say it, I am able to find CPPTRACE_STATIC_DEFINE in the ReadMe. But it is true I did not see this before.

3- I understand the problem with SymInitialize, I do not know a method to know if it has already been called either. As for SymRefreshModuleList , it allows to refresh the list of modules. If you call SymInitialize and then load a dll dynamically (with AfxLoadLibrary for instance) it will not be symbolized before you call SymRefreshModuleList . But it is maybe a bit out of the scope of your tool.

4- Glad to know it is already fixed.

jeremy-rifkin commented 4 months ago

Thanks for confirming! I've added additional notes about CPPTRACE_STATIC_DEFINE to the readme to help prevent them from being missed and v0.5.3 fixing the other issues is now released and available through package managers. I'll go ahead and close this for now, please let me know if there's anything else you run into!

LouisClt commented 4 months ago

Thanks !