-
Notifications
You must be signed in to change notification settings - Fork 123
Inject startup hook assembly if DOTNET_STARTUP_HOOKS not configured #4529
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Conversation
| } | ||
|
|
||
| // clang-format off | ||
| // This method will generate new type __DDLoaderFixup__ in target module. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should we use new prefixm not DD for generated code?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I thought __DD was some kind of convention, not arbitrarily chosen. If the idea is to have a random pair of letters as prefix I can change it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
DD=DataDog
| return EmptyWStr; | ||
| } | ||
|
|
||
| #ifdef _WIN32 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Are we linking old stdlib on linux that doesn't have path?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't know. Will have to look into this. Are you saying that older stdlib versions returned null or that they returned the module name without path?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It was a question, why can't we use std:path on Linux also?
| return EmptyWStr; | ||
| } | ||
|
|
||
| inline WSTRING GetModuleFilePath(const WSTRING& moduleName) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As I understand, there are at least 3 possible layouts:
ZIP:
OTEL_HOME/<RID>/<native_profler_lib>
OTEL_HOME/net/OpenTelemetry.AutoInstrumentation.StartupHook.dll
NuGet, platform dependent deployment:
<APP_FOLDER>/<native_profler_lib>
<APP_FOLDER>/OpenTelemetry.AutoInstrumentation.StartupHook.dll
NuGet, platform independent deployment: (I suspect this is not validated correctly in IsStartupHookValid)
<APP_FOLDER>/runtimes/<RID>/<native_profler_lib>
<APP_FOLDER>/OpenTelemetry.AutoInstrumentation.StartupHook.dll
I'm also not sure, what is behaviour if OTEL_HOME envirinment variable not set, especially in case of zip deployment.
In any case, once we calculated the path - we should probe that file exists.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Probably, we should apply manual add of profiler hook only if OpenTelemetry.AutoInstrumentation.StartupHook.dll is not present in environment variable at all. If it is there, but points to unexpected location, we should just log it.
I really hope that noone will rename the startup hook file :)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This functionality is definitely just a placeholder for now in order to test that the overall idea works. I will update it to be more robust and take different layouts, etc. into consideration.
Regarding the second question, that is already done in the PR as it is right now. The startup_fix_required class member variable was added for that purpose.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I've seen it. Right now startup_fix_required is based on IsStartupHookValid. We probably need it to be even more flexible.
There are situations, that we may emit warning, as we have not identified hook path in environment varaible, but don't want to apply patch, as we still not sure - like we have dll name in env, but in unexpected path. Keep in mind, on windows check should be case insensitive.
|
|
||
| mdAssemblyRef corlib_ref = mdTokenNil; | ||
| // We need assemblyRef only when we generate type outside of mscorlib | ||
| if (module_info.assembly.name != mscorlib_assemblyName) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You never need corlib_ref, as that modification should be done inside corlib, as I understand - similat to ModifyAppDomainCreate.
| &path_separator_token); | ||
| if (FAILED(hr)) | ||
| { | ||
| Logger::Warn("GenerateHookFixup: DefineUserString ; failed"); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ptobably typo "; "
|
|
||
| // Generate IL for the method body | ||
| // C# equivalent: | ||
| // if (startupHooks == null) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It may be helpfull to add IL code you are genereting. When I used same body, I see a little bit different code:
IL_0000 | ldarg.0 |
IL_0001 | ldind.ref |
IL_0002 | brtrue.s | IL_000C
IL_0004 | ldarg.0 |
IL_0005 | ldstr | "<startup_hook_dll_name>"
IL_000A | stind.ref |
IL_000B | ret |
IL_000C | ldarg.0 |
IL_000D | ldarg.0 |
IL_000E | ldind.ref |
IL_000F | ldstr | "<path_separator><startup_hook_dll_name>"
IL_0014 | call | String.Concat (String, String)
IL_0019 | stind.ref |
IL_001A | ret
It may be eisier to verify if you target IL would be also visible.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'll will add the comment, making sure it matches the generated code.
| #ifdef _WIN32 | ||
| const WSTRING opentelemetry_autoinstrumentation_startuphook_filepath = WStr("net\\OpenTelemetry.AutoInstrumentation.StartupHook.dll"); | ||
| #else | ||
| const WSTRING opentelemetry_autoinstrumentation_startuphook_filepath = WStr("net/OpenTelemetry.AutoInstrumentation.StartupHook.dll"); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should we use that constant in IsStartupHookValid if we define it?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good point. I will update the code to use the same constants.
| ILInstr* pFirstInstr = rewriter.GetILList()->m_pNext; | ||
| ILInstr* pNewInstr = NULL; | ||
|
|
||
| // ldarga.s 0 ; Load the address of the first argument (startupHooks string) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Would it be easier to do all checks/modifications here, instead of creating additional class / method?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In theory, might we not have issues with method body resizing if we add too much code? I know there are things to be aware of but don't know the ins and outs as well as you do.
Other than that it is a good idea. I had originally planned to have more logic in the generated method but then realized I can resolve the assembly name in C++ and just hardcode it in the IL.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not aware of any issues with changing method body size.
| if (!IsStartupHookValid(startup_hooks, home_path)) | ||
| { | ||
| FailProfiler(Error, "The required StartupHook was not configured correctly. No telemetry will be captured.") | ||
| Logger::Info("The StartupHook was not configured. Will patch ProcessStartupHooks."); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is it too late to inject process level env in this point?
Just looking all that code that is necessary if there are simpler workarounds.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Unfortunately the CLR loads environment variables and caches them before it initializes the profiler. Injection is the only way we can overcome that.
Resolves #4491
Why
To use the auto instrumentation customers currently have to set both the profiler environment variables as well as the DOTNET_STARTUP_HOOKS environment variable. This really does not add any additional value to setup and is just a redundant step. The profiler could just ensure that the auto instrumentation initialization occurs.
Tests
Checklist
CHANGELOG.mdis updated.