Skip to content

Conversation

@Rhynier
Copy link

@Rhynier Rhynier commented Oct 16, 2025

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.md is updated.
  • Documentation is updated.
  • New features are covered by tests.

@linux-foundation-easycla
Copy link

linux-foundation-easycla bot commented Oct 16, 2025

CLA Signed

The committers listed above are authorized under a signed CLA.

}

// clang-format off
// This method will generate new type __DDLoaderFixup__ in target module.
Copy link
Contributor

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?

Copy link
Author

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.

Copy link
Contributor

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
Copy link
Contributor

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?

Copy link
Author

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?

Copy link
Contributor

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)
Copy link
Contributor

@iskiselev iskiselev Oct 16, 2025

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.

Copy link
Contributor

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 :)

Copy link
Author

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.

Copy link
Contributor

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)
Copy link
Contributor

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");
Copy link
Contributor

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)
Copy link
Contributor

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.

Copy link
Author

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");
Copy link
Contributor

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?

Copy link
Author

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)
Copy link
Contributor

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?

Copy link
Author

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.

Copy link
Contributor

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.");
Copy link
Contributor

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.

Copy link
Author

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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Make setting DOTNET_STARTUP_HOOKS optional

3 participants