dfeneyrou / palanteer

Visual Python and C++ nanosecond profiler, logger, tests enabler
Other
2.1k stars 88 forks source link

Question - Shared libraries #31

Open wh1t3lord opened 2 years ago

wh1t3lord commented 2 years ago

Has the library a design for working in separated projects but they can be connected with one static library? I mean is it possible to create one class with implementation where it is a wrapper for palanteer. Otherwise, I can't understand why the header file doesn't include code even if I declare preprocessor PL_IMPLEMENTATION clearly in .cpp file.

Or the purpose of this library is to test small codebases because I can't imagine how to use it if you have many separated projects that are connected with one main static library.

As the result, I have the error LNK2019: unresolved external symbol. Describe the correct usage of your header in the case when I want to use the implementation in .cpp file, but have declarations of functions (USE_PL) in a header file and share it between all projects. It's really sad if I can't do that at all.

dfeneyrou commented 2 years ago

I can't understand why the header file doesn't include code even if I declare preprocessor PL_IMPLEMENTATION clearly in .cpp file.

The #define PL_IMPLEMENTATION 1 shall be placed before the first palanteer.h (it may not be obvious if you include some headers of your own which include also palanteer.h, before the palanteer include in your cpp file). Can you double check that point? If you have unresolved symbol linked to the library implementation, that must be the problem.

Or the purpose of this library is to test small codebases because I can't imagine how to use it if you have many separated projects that are connected with one main static library.

There is no real limit on the project size. The limit is rather on the thread quantity (254 max) and how heavily you instrument your code with active logging (disabled logging at compile time do not count). Each process (what do you call "a project" exactly?) shall implement its own palanteer service, so that they can send through socket or store in a file the stream of events of the process.

Describe the correct usage of your header

As mention in the first paragraph, I guess that it is due to either:

And as you mention, USE_PL=1 shall also be set (in your Makefile/CMake/build system) so that the service is compiled. But as you have some unresolved external symbol, this is probably done correctly.

dfeneyrou commented 2 years ago

You also have an example program, just see the beginning of the file ./c++/testprogram/testProgram.cpp. The USE_PL=1 is set in the CMakeLists.txt in the same folder.

wh1t3lord commented 2 years ago

You don't understand, but I mean you have one library and want to share that library with other libraries.

So you want to have one instance of your profiler and you want to collect all data from different libraries that are linked to one executable.

Like your exe consists of:

  1. LibraryCore // Your profiler is here
  2. LibraryA <- LibraryCore
  3. LibraryB <- LibraryCore

Because I just can't compile your solution with that implementation or if you use it in header file as the result you will get many redefinitions and still get linking problems.

The question is can you use it with that type of solution where you split libraries into components?

@dfeneyrou

For the first time as I understand your implementation, you use #define USE_PL 1 in order to define your functions after in cpp file you declare those functions (#define PL_IMPLEMENTATION 1) and the problem is I get linking problems like implementations of those functions don't exist at all and that's why I asked you about this. Because the library where the profiler is located just compiles successfully, but when I want to link it to executable I got that link error.

wh1t3lord commented 2 years ago

You can try to create a real simple project with two static libraries and one executable. Where one static library contains the definitions and the declarations of your functions like plStart and plSetFilename and the second static library collects all function calls by plScope only. And you want to link those libraries to your executable.

dfeneyrou commented 2 years ago

The title of the issue mentions "shared libraries" but your initial description mentions several times "static library", that confused me.

So you are describing an executable with several dynamic/shared libraries that are also instrumented. As the implementation shall be done only once for the full process, shared library included, and as the library link chain requires the palanteer symbols to be defined before your dynamic libraries, the only option is to create a "palanteer dedicated shared library" that implements the palanteer service (so the only one with #define PL_IMPLEMENTATION 1), and all your other shared library and main executable will depend on it (this "palanteer library" shall be placed after them on the linker list of libraries)

Your library structure described above seems to be ok, it is however not clear where your "main" is... It should be something like (A <- B means here "A depends on library B")

Main executable (using plScope)   <--- Library A  (using plScope)    <---+
         ^                                                               |
         |                                                         Library Palanteer (implementing it)
         |                                                               | 
         +---------------------------- Library B  (using plScope)    <---+

That is the theory, at least. I will try it on my side indeed to ensure that there are no "surprise". Note that the main executable does not implement the palanteer service (but can use the plInitAndStart), it is implemented only in the "Library Palanteer"

you use #define USE_PL 1 in order to define your functions after in cpp file

The #define USE_PL 1 does not implement anything. It just makes the palanteer calls non empty (its main usage is to be able to remove fully palanteer at compile time, implementation + all logging calls). So in your described setup, as a first step, it shall be defined for all your libraries and main executable. If you do not want to use palanteer in a "release" version for instance, you can just not set USE_PL (or set it to 0)

wh1t3lord commented 2 years ago

@dfeneyrou I will appreciate it if you can implement the type of solution that I have described before. One library just uses plScope only and the second library contains an implementation of palanteer (PL_IMPLEMENTATION 1 in .cpp once respectively) and the executable just calls some functions from them in order to test it.

wh1t3lord commented 2 years ago

I don't think it was my problem with integrating your library...

Because I wasted a lot of time and thought I did all things right according to your documentation and examples.

wh1t3lord commented 2 years ago

Anyway, I don't think there's something wrong if you create another example with a "different architecture type" as the more advanced example of your integration.

dfeneyrou commented 2 years ago

I confirm that my above proposal of dynamic library structure works straight under Linux. But not under Windows, which is your use case.

Indeed, Windows requires explicit marking of shared APIs with __declspec(dllimport) and __declspec(dllexport). I am working on it, including some show-case examples that can be copy/pasted

wh1t3lord commented 2 years ago

@dfeneyrou how is it going?

dfeneyrou commented 2 years ago

Sorry for late answer. I had very few time recently to continue the work (I used to live in Kyiv...), I am now in better conditions to resume it. Keeping you informed.