Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -16,13 +16,18 @@ namespace environment
const WSTRING internal_instrument_all_lines_enabled = WStr("DD_INTERNAL_DEBUGGER_INSTRUMENT_ALL_LINES");
const WSTRING internal_instrument_all_lines_path = WStr("DD_INTERNAL_DEBUGGER_INSTRUMENT_ALL_LINES_PATH");

// Determines if the Dynamic Instrumentation (aka live debugger) is enabled.
// Determines if the Dynamic Instrumentation (aka live debugger) product is enabled.
const WSTRING dynamic_instrumentation_enabled = WStr("DD_DYNAMIC_INSTRUMENTATION_ENABLED");

// Determines if the Exception Replay product is enabled.
const WSTRING exception_debugging_enabled = WStr("DD_EXCEPTION_DEBUGGING_ENABLED"); // Old name
const WSTRING exception_replay_enabled = WStr("DD_EXCEPTION_REPLAY_ENABLED");

// Determines if Dynamic Instrumentation should be controlled by managed activation
const WSTRING dynamic_instrumentation_managed_activation_enabled = WStr("DD_DYNAMIC_INSTRUMENTATION_MANAGED_ACTIVATION_ENABLED");

// Determines if Exception Replay should be controlled by managed activation
const WSTRING exception_replay_managed_activation_enabled = WStr("DD_EXCEPTION_REPLAY_MANAGED_ACTIVATION_ENABLED");

} // namespace environment
} // namespace debugger

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,15 +11,17 @@ bool IsDynamicInstrumentationEnabled()

bool IsExceptionReplayEnabled()
{
static int sValue = -1;
if (sValue == -1)
{
const auto old_exception_replay_flag = GetEnvironmentValue(environment::exception_debugging_enabled);
const auto new_exception_replay_flag = GetEnvironmentValue(environment::exception_replay_enabled);
sValue = (IsTrue(old_exception_replay_flag) || IsTrue(new_exception_replay_flag)) ? 1 : 0;
}

return sValue == 1;
CheckIfTrue(GetEnvironmentValue(environment::exception_replay_enabled));
}

bool IsDynamicInstrumentationManagedActivationDisabled()
{
CheckIfFalse(GetEnvironmentValue(environment::dynamic_instrumentation_managed_activation_enabled));
}

bool IsExceptionReplayManagedActivationDisabled()
{
CheckIfFalse(GetEnvironmentValue(environment::exception_replay_managed_activation_enabled));
}

bool IsDebuggerInstrumentAllEnabled()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ namespace debugger

bool IsDynamicInstrumentationEnabled();
bool IsExceptionReplayEnabled();
bool IsDynamicInstrumentationManagedActivationDisabled();
bool IsExceptionReplayManagedActivationDisabled();
bool IsDebuggerInstrumentAllEnabled();
bool IsDebuggerInstrumentAllLinesEnabled();

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -330,19 +330,36 @@ DebuggerProbesInstrumentationRequester::DebuggerProbesInstrumentationRequester(
m_work_offloader(work_offloader),
m_fault_tolerant_method_duplicator(fault_tolerant_method_duplicator)
{
auto diEnabled = IsDynamicInstrumentationEnabled();
auto diManagedActivationDisabled = IsDynamicInstrumentationManagedActivationDisabled();
if (diManagedActivationDisabled)
{
Logger::Info("Dynamic Instrumentation Stable Config is explicitly disabled");
}

auto diEnabled = !diManagedActivationDisabled || IsDynamicInstrumentationEnabled();
if (diEnabled == false)
{
Logger::Debug("Dynamic Instrumentation is disabled");
Logger::Info("Dynamic Instrumentation hot standby is disabled");
}

auto erManagedActivationDisabled = IsExceptionReplayManagedActivationDisabled();
if (erManagedActivationDisabled)
{
Logger::Info("Exception Replay Stable Config is explicitly disabled");
}

auto erEnabled = IsExceptionReplayEnabled();
auto erEnabled = !erManagedActivationDisabled || IsExceptionReplayEnabled();
if (erEnabled == false)
{
Logger::Debug("Exception Replay is explicitly disabled");
Logger::Info("Exception Replay hot standby is disabled");
}

is_debugger_or_exception_debugging_enabled = diEnabled || erEnabled;
is_debugger_or_exception_replay_hot_standby = diEnabled || erEnabled;

if (is_debugger_or_exception_replay_hot_standby)
{
Logger::Info("Dynamic Instrumentation/Exception Replay Hot Standby is on");
}
}

void DebuggerProbesInstrumentationRequester::RemoveProbes(debugger::DebuggerRemoveProbesDefinition* removeProbes,
Expand Down Expand Up @@ -870,6 +887,53 @@ void DebuggerProbesInstrumentationRequester::RequestRejitForLoadedModule(const M
Logger::Debug("[Debugger] Total number of ReJIT Requested: ", numReJITs);
}

// There's a bug in .NET Framework, where if the app uses a different (older/newer) version
// of Datadog.Trace through manual instrumentation than the automatic instrumentation Datadog.Trace,
// and we try to inject our state machine field inside one if it's StateMachine classes,
// then managed loader fails to load Datadog.Trace with `FileLoadException`.
// To mitigate that, we actively refuse to inject anything in if the app use manual instrumentation
// with Datadog.Trace that does not match the Automatic Instrumentation.
bool DebuggerProbesInstrumentationRequester::ShouldSkipInjection(const ComPtr<IMetaDataAssemblyImport>& assemblyImport)
{
const auto& expected_assembly_reference = trace::AssemblyReference(managed_profiler_full_assembly_version);

// Enumerate the assemblyref of the module
auto assemblyRefEnum = EnumAssemblyRefs(assemblyImport);
auto assemblyRefIterator = assemblyRefEnum.begin();
for (; assemblyRefIterator != assemblyRefEnum.end(); assemblyRefIterator = ++assemblyRefIterator)
{
auto assemblyRef = *assemblyRefIterator;
const void* pvPublicKeyOrToken;
ULONG cbPublicKeyOrToken;
WCHAR wszName[512];
ULONG cchNameReturned;
ASSEMBLYMETADATA asmMetaData;
ZeroMemory(&asmMetaData, sizeof(asmMetaData));
const void* pbHashValue;
ULONG cbHashValue;
DWORD asmRefFlags;

auto hr = assemblyImport->GetAssemblyRefProps(assemblyRef, &pvPublicKeyOrToken, &cbPublicKeyOrToken, wszName,
512, &cchNameReturned, &asmMetaData, &pbHashValue, &cbHashValue,
&asmRefFlags);

if (FAILED(hr))
{
continue;
}

const auto& version = Version(asmMetaData.usMajorVersion, asmMetaData.usMinorVersion, asmMetaData.usBuildNumber, asmMetaData.usRevisionNumber);

if (wszName == expected_assembly_reference.name && version != expected_assembly_reference.version)
{
Logger::Info("ShouldSkipInjection: ", expected_assembly_reference.name, " was found with version:", version.str() ,", that does not match our expected version:", expected_assembly_reference.version.str());
return true;
}
}

return false;
}

void DebuggerProbesInstrumentationRequester::ModuleLoadFinished_AddMetadataToModule(const ModuleID moduleId)
{
auto corProfilerInfo = m_rejit_handler->GetCorProfilerInfo();
Expand All @@ -888,17 +952,17 @@ void DebuggerProbesInstrumentationRequester::ModuleLoadFinished_AddMetadataToMod
return;
}

Logger::Debug("Requesting Rejit for Module: ", moduleInfo.assembly.name);
Logger::Debug("DebuggerProbesInstrumentationRequester::ModuleLoadFinished_AddMetadataToModule: Requesting Rejit for Module: ", moduleInfo.assembly.name);

ComPtr<IUnknown> metadataInterfaces;

Logger::Debug(" Loading Assembly Metadata...");
Logger::Debug("DebuggerProbesInstrumentationRequester::ModuleLoadFinished_AddMetadataToModule: Loading Assembly Metadata...");
auto hr = corProfilerInfo->GetModuleMetaData(moduleInfo.id, ofRead | ofWrite, IID_IMetaDataImport2,
metadataInterfaces.GetAddressOf());
if (hr != S_OK)
{
Logger::Warn(
"DebuggerProbesInstrumentationRequester::sAddMetadataToModule failed to get metadata interface for ",
"DebuggerProbesInstrumentationRequester::ModuleLoadFinished_AddMetadataToModule: failed to get metadata interface for ",
moduleInfo.id, " ", moduleInfo.assembly.name);
return;
}
Expand All @@ -911,11 +975,18 @@ void DebuggerProbesInstrumentationRequester::ModuleLoadFinished_AddMetadataToMod
metadataInterfaces.As<IMetaDataAssemblyEmit>(IID_IMetaDataAssemblyEmit);
std::unique_ptr<AssemblyMetadata> assemblyMetadata =
std::make_unique<AssemblyMetadata>(GetAssemblyImportMetadata(assemblyImport));
Logger::Debug(" Assembly Metadata loaded for: ", assemblyMetadata->name, "(", assemblyMetadata->version.str(),
Logger::Debug("DebuggerProbesInstrumentationRequester::ModuleLoadFinished_AddMetadataToModule: Assembly Metadata loaded for: ", assemblyMetadata->name, "(", assemblyMetadata->version.str(),
").");

m_fault_tolerant_method_duplicator->DuplicateAll(moduleId, moduleInfo, metadataImport, metadataEmit);

if (ShouldSkipInjection(assemblyImport))
{
Logger::Info("Skipped processing of Assembly Metadata: ", assemblyMetadata->name, "(", assemblyMetadata->version.str(),
").");
return;
}

// Enumerate the types of the module
auto typeDefEnum = EnumTypeDefs(metadataImport);
auto typeDefIterator = typeDefEnum.begin();
Expand Down Expand Up @@ -1020,20 +1091,20 @@ void DebuggerProbesInstrumentationRequester::ModuleLoadFinished_AddMetadataToMod
"_isFirstEntry failed");
}

Logger::Debug("Added IsFirstEntry field [ModuleId=", moduleInfo.id, ", Assembly=", moduleInfo.assembly.name,
Logger::Debug("DebuggerProbesInstrumentationRequester::ModuleLoadFinished_AddMetadataToModule: Added IsFirstEntry field [ModuleId=", moduleInfo.id, ", Assembly=", moduleInfo.assembly.name,
", Type=", typeInfo.name, ", IsValueType=", typeInfo.valueType, "]");
}
}

HRESULT STDMETHODCALLTYPE DebuggerProbesInstrumentationRequester::ModuleLoadFinished(const ModuleID moduleId)
{
if (!is_debugger_or_exception_debugging_enabled)
if (!is_debugger_or_exception_replay_hot_standby)
{
return S_OK;
return S_OK;
}

// IMPORTANT: The call to `ModuleLoadFinished_AddMetadataToModule` must be in `ModuleLoadFinished` as mutating the
// layout of types is only feasible prior the type is loaded.s
// layout of types is only feasible prior the type is loaded.
ModuleLoadFinished_AddMetadataToModule(moduleId);
RequestRejitForLoadedModule(moduleId);
return S_OK;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ class DebuggerProbesInstrumentationRequester
std::shared_ptr<RejitHandler> m_rejit_handler = nullptr;
std::shared_ptr<RejitWorkOffloader> m_work_offloader = nullptr;
std::shared_ptr<fault_tolerant::FaultTolerantMethodDuplicator> m_fault_tolerant_method_duplicator = nullptr;
bool is_debugger_or_exception_debugging_enabled = false;
bool is_debugger_or_exception_replay_hot_standby = false;
std::unordered_map<std::pair<std::string, mdToken>, std::vector<int>, pair_hash> explorationLineProbes;
std::once_flag explorationLinesInitFlag;

Expand Down Expand Up @@ -68,6 +68,7 @@ class DebuggerProbesInstrumentationRequester
debugger::DebuggerMethodSpanProbeDefinition* spanProbes, int spanProbesLength,
debugger::DebuggerRemoveProbesDefinition* removeProbes, int removeProbesLength);
static int GetProbesStatuses(WCHAR** probeIds, int probeIdsLength, debugger::DebuggerProbeStatus* probeStatuses);
static bool ShouldSkipInjection(const ComPtr<IMetaDataAssemblyImport>& assemblyImport);
void PerformInstrumentAllIfNeeded(const ModuleID& module_id, mdToken& function_token);
void InitializeExplorationTestLineProbes(const WSTRING& filename);
auto& GetExplorationTestLineProbes(const WSTRING& filename);
Expand Down
Loading