in src/profiler/elastic_apm_profiler/src/profiler/mod.rs [773:1077]
fn module_load_finished(&self, module_id: ModuleID, hr_status: HRESULT) -> Result<(), HRESULT> {
if FAILED(hr_status) {
log::error!(
"ModuleLoadFinished: hr status is {} for module id {}. skipping",
hr_status,
module_id
);
return Ok(());
}
if !IS_ATTACHED.load(Ordering::SeqCst) {
log::trace!("ModuleLoadFinished: profiler not attached");
return Ok(());
}
let mut modules = self.modules.lock().unwrap();
if !IS_ATTACHED.load(Ordering::SeqCst) {
log::trace!("ModuleLoadFinished: profiler not attached");
return Ok(());
}
if let Some(module_info) = self.get_module_info(module_id) {
let app_domain_id = module_info.assembly.app_domain_id;
let assembly_name = &module_info.assembly.name;
log::debug!(
"ModuleLoadFinished: {} {} app domain {} {}",
module_id,
assembly_name,
app_domain_id,
&module_info.assembly.app_domain_name
);
if !self.cor_lib_module_loaded.load(Ordering::SeqCst)
&& (assembly_name == "mscorlib" || assembly_name == "System.Private.CoreLib")
{
self.cor_lib_module_loaded.store(true, Ordering::SeqCst);
self.cor_app_domain_id
.store(app_domain_id, Ordering::SeqCst);
let profiler_borrow = self.profiler_info.borrow();
let profiler_info = profiler_borrow.as_ref().unwrap();
let metadata_assembly_import = profiler_info
.get_module_metadata::<IMetaDataAssemblyImport>(
module_id,
CorOpenFlags::ofRead | CorOpenFlags::ofWrite,
)?;
let mut assembly_metadata = metadata_assembly_import.get_assembly_metadata()?;
assembly_metadata.name = assembly_name.to_string();
log::info!(
"ModuleLoadFinished: Cor library {} {}",
&assembly_metadata.name,
&assembly_metadata.version
);
self.cor_assembly_property.replace(Some(assembly_metadata));
return Ok(());
}
// if this is the loader module, track the app domain it's been loaded into
if assembly_name == MANAGED_PROFILER_ASSEMBLY_LOADER {
log::info!(
"ModuleLoadFinished: {} loaded into AppDomain {} {}",
MANAGED_PROFILER_ASSEMBLY_LOADER,
app_domain_id,
&module_info.assembly.app_domain_name
);
self.first_jit_compilation_app_domains
.write()
.unwrap()
.insert(app_domain_id);
return Ok(());
}
// if this is a windows runtime module, skip it
if module_info.is_windows_runtime() {
log::debug!(
"ModuleLoadFinished: skipping windows metadata module {} {}",
module_id,
&assembly_name
);
return Ok(());
}
for pattern in SKIP_ASSEMBLY_PREFIXES {
if assembly_name.starts_with(pattern) {
log::debug!(
"ModuleLoadFinished: skipping module {} {} because it matches skip pattern {}",
module_id,
assembly_name,
pattern
);
return Ok(());
}
}
for skip in SKIP_ASSEMBLIES {
if assembly_name == skip {
log::debug!(
"ModuleLoadFinished: skipping module {} {} because it matches skip {}",
module_id,
assembly_name,
skip
);
return Ok(());
}
}
let call_target_enabled = *env::ELASTIC_APM_PROFILER_CALLTARGET_ENABLED;
// TODO: Avoid cloning integration methods. Should be possible to make all filtered_integrations a collection of references
let mut filtered_integrations = if call_target_enabled {
self.integration_methods.read().unwrap().to_vec()
} else {
self.integration_methods
.read()
.unwrap()
.iter()
.filter(|m| {
if let Some(caller) = m.method_replacement.caller() {
caller.assembly.is_empty() || &caller.assembly == assembly_name
} else {
true
}
})
.cloned()
.collect()
};
if filtered_integrations.is_empty() {
log::debug!(
"ModuleLoadFinished: skipping module {} {} because filtered by caller",
module_id,
assembly_name
);
return Ok(());
}
// get the metadata interfaces for the module
let profiler_borrow = self.profiler_info.borrow();
let profiler_info = profiler_borrow.as_ref().unwrap();
let metadata_import = profiler_info
.get_module_metadata::<IMetaDataImport2>(
module_id,
CorOpenFlags::ofRead | CorOpenFlags::ofWrite,
)
.map_err(|e| {
log::warn!(
"ModuleLoadFinished: unable to get IMetaDataEmit2 for module id {}",
module_id
);
e
})?;
let metadata_emit = metadata_import
.query_interface::<IMetaDataEmit2>()
.ok_or_else(|| {
log::warn!(
"ModuleLoadFinished: unable to get IMetaDataEmit2 for module id {}",
module_id
);
E_FAIL
})?;
let assembly_import = metadata_import
.query_interface::<IMetaDataAssemblyImport>().ok_or_else(|| {
log::warn!("ModuleLoadFinished: unable to get IMetaDataAssemblyImport for module id {}", module_id);
E_FAIL
})?;
let assembly_emit = metadata_import
.query_interface::<IMetaDataAssemblyEmit>()
.ok_or_else(|| {
log::warn!(
"ModuleLoadFinished: unable to get IMetaDataAssemblyEmit for module id {}",
module_id
);
E_FAIL
})?;
// don't skip Microsoft.AspNetCore.Hosting so we can run the startup hook and
// subscribe to DiagnosticSource events.
// don't skip Dapper: it makes ADO.NET calls even though it doesn't reference
// System.Data or System.Data.Common
if assembly_name != "Microsoft.AspNetCore.Hosting"
&& assembly_name != "Dapper"
&& !call_target_enabled
{
let assembly_metadata = assembly_import.get_assembly_metadata().map_err(|e| {
log::warn!(
"ModuleLoadFinished: unable to get assembly metadata for {}",
assembly_name
);
e
})?;
let assembly_refs = assembly_import.enum_assembly_refs()?;
let assembly_ref_metadata: Result<Vec<AssemblyMetaData>, HRESULT> = assembly_refs
.into_iter()
.map(|r| assembly_import.get_referenced_assembly_metadata(r))
.collect();
if assembly_ref_metadata.is_err() {
log::warn!("ModuleLoadFinished: unable to get referenced assembly metadata");
return Err(assembly_ref_metadata.err().unwrap());
}
fn meets_requirements(
assembly_metadata: &AssemblyMetaData,
method_replacement: &MethodReplacement,
) -> bool {
match method_replacement.target() {
Some(target)
if target.is_valid_for_assembly(
&assembly_metadata.name,
&assembly_metadata.version,
) =>
{
true
}
_ => false,
}
}
let assembly_ref_metadata = assembly_ref_metadata.unwrap();
filtered_integrations.retain(|i| {
meets_requirements(&assembly_metadata, &i.method_replacement)
|| assembly_ref_metadata
.iter()
.any(|m| meets_requirements(m, &i.method_replacement))
});
if filtered_integrations.is_empty() {
log::debug!(
"ModuleLoadFinished: skipping module {} {} because filtered by target",
module_id,
assembly_name
);
return Ok(());
}
}
let module_version_id = metadata_import.get_module_version_id().map_err(|e| {
log::warn!("ModuleLoadFinished: unable to get module version id for {} {} app domain {} {}",
module_id,
assembly_name,
app_domain_id,
&module_info.assembly.app_domain_name);
e
})?;
let cor_assembly_property = {
let cor_assembly_property_borrow = self.cor_assembly_property.borrow();
cor_assembly_property_borrow.as_ref().unwrap().clone()
};
let module_metadata = ModuleMetadata::new(
metadata_import,
metadata_emit,
assembly_import,
assembly_emit,
assembly_name.to_string(),
app_domain_id,
module_version_id,
filtered_integrations,
cor_assembly_property,
);
modules.insert(module_id, module_metadata);
let module_metadata = modules.get(&module_id).unwrap();
{
let module_wrapper_tokens = self
.module_wrapper_tokens
.lock()
.unwrap()
.insert(module_id, ModuleWrapperTokens::new());
}
log::debug!(
"ModuleLoadFinished: stored metadata for {} {} app domain {} {}",
module_id,
assembly_name,
app_domain_id,
&module_info.assembly.app_domain_name
);
log::trace!("ModuleLoadFinished: tracking {} module(s)", modules.len());
if call_target_enabled {
let rejit_count =
self.calltarget_request_rejit_for_module(module_id, module_metadata)?;
if rejit_count > 0 {
log::trace!(
"ModuleLoadFinished: requested rejit of {} methods",
rejit_count
);
}
}
}
Ok(())
}