fn module_load_finished()

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(())
    }