Skip to main content

rustc_codegen_llvm/coverageinfo/
mod.rs

1use std::cell::{OnceCell, RefCell};
2use std::ffi::{CStr, CString};
3
4use rustc_codegen_ssa::traits::{
5    ConstCodegenMethods, CoverageInfoBuilderMethods, MiscCodegenMethods,
6};
7use rustc_data_structures::fx::FxIndexMap;
8use rustc_middle::mir::coverage::CoverageKind;
9use rustc_middle::ty::Instance;
10use tracing::{debug, instrument};
11
12use crate::builder::Builder;
13use crate::common::CodegenCx;
14use crate::llvm;
15
16pub(crate) mod ffi;
17mod llvm_cov;
18mod mapgen;
19
20/// Extra per-CGU context/state needed for coverage instrumentation.
21pub(crate) struct CguCoverageContext<'ll, 'tcx> {
22    /// Associates function instances with an LLVM global that holds the
23    /// function's symbol name, as needed by LLVM coverage intrinsics.
24    ///
25    /// Instances in this map are also considered "used" for the purposes of
26    /// emitting covfun records. Every covfun record holds a hash of its
27    /// symbol name, and `llvm-cov` will exit fatally if it can't resolve that
28    /// hash back to an entry in the binary's `__llvm_prf_names` linker section.
29    pub(crate) pgo_func_name_var_map: RefCell<FxIndexMap<Instance<'tcx>, &'ll llvm::Value>>,
30
31    covfun_section_name: OnceCell<CString>,
32}
33
34impl<'ll, 'tcx> CguCoverageContext<'ll, 'tcx> {
35    pub(crate) fn new() -> Self {
36        Self { pgo_func_name_var_map: Default::default(), covfun_section_name: Default::default() }
37    }
38
39    /// Returns the list of instances considered "used" in this CGU, as
40    /// inferred from the keys of `pgo_func_name_var_map`.
41    pub(crate) fn instances_used(&self) -> Vec<Instance<'tcx>> {
42        // Collecting into a Vec is way easier than trying to juggle RefCell
43        // projections, and this should only run once per CGU anyway.
44        self.pgo_func_name_var_map.borrow().keys().copied().collect::<Vec<_>>()
45    }
46}
47
48impl<'ll, 'tcx> CodegenCx<'ll, 'tcx> {
49    pub(crate) fn coverageinfo_finalize(&mut self) {
50        mapgen::finalize(self)
51    }
52
53    /// Returns the section name to use when embedding per-function coverage information
54    /// in the object file, according to the target's object file format. LLVM's coverage
55    /// tools use information from this section when producing coverage reports.
56    ///
57    /// Typical values are:
58    /// - `__llvm_covfun` on Linux
59    /// - `__LLVM_COV,__llvm_covfun` on macOS (includes `__LLVM_COV,` segment prefix)
60    /// - `.lcovfun$M` on Windows (includes `$M` sorting suffix)
61    fn covfun_section_name(&self) -> &CStr {
62        self.coverage_cx()
63            .covfun_section_name
64            .get_or_init(|| llvm_cov::covfun_section_name(self.llmod))
65    }
66
67    /// For LLVM codegen, returns a function-specific `Value` for a global
68    /// string, to hold the function name passed to LLVM intrinsic
69    /// `instrprof.increment()`. The `Value` is only created once per instance.
70    /// Multiple invocations with the same instance return the same `Value`.
71    ///
72    /// This has the side-effect of causing coverage codegen to consider this
73    /// function "used", making it eligible to emit an associated covfun record.
74    fn ensure_pgo_func_name_var(&self, instance: Instance<'tcx>) -> &'ll llvm::Value {
75        {
    use ::tracing::__macro_support::Callsite as _;
    static __CALLSITE: ::tracing::callsite::DefaultCallsite =
        {
            static META: ::tracing::Metadata<'static> =
                {
                    ::tracing_core::metadata::Metadata::new("event compiler/rustc_codegen_llvm/src/coverageinfo/mod.rs:75",
                        "rustc_codegen_llvm::coverageinfo", ::tracing::Level::DEBUG,
                        ::tracing_core::__macro_support::Option::Some("compiler/rustc_codegen_llvm/src/coverageinfo/mod.rs"),
                        ::tracing_core::__macro_support::Option::Some(75u32),
                        ::tracing_core::__macro_support::Option::Some("rustc_codegen_llvm::coverageinfo"),
                        ::tracing_core::field::FieldSet::new(&["message"],
                            ::tracing_core::callsite::Identifier(&__CALLSITE)),
                        ::tracing::metadata::Kind::EVENT)
                };
            ::tracing::callsite::DefaultCallsite::new(&META)
        };
    let enabled =
        ::tracing::Level::DEBUG <= ::tracing::level_filters::STATIC_MAX_LEVEL
                &&
                ::tracing::Level::DEBUG <=
                    ::tracing::level_filters::LevelFilter::current() &&
            {
                let interest = __CALLSITE.interest();
                !interest.is_never() &&
                    ::tracing::__macro_support::__is_enabled(__CALLSITE.metadata(),
                        interest)
            };
    if enabled {
        (|value_set: ::tracing::field::ValueSet|
                    {
                        let meta = __CALLSITE.metadata();
                        ::tracing::Event::dispatch(meta, &value_set);
                        ;
                    })({
                #[allow(unused_imports)]
                use ::tracing::field::{debug, display, Value};
                let mut iter = __CALLSITE.metadata().fields().iter();
                __CALLSITE.metadata().fields().value_set(&[(&::tracing::__macro_support::Iterator::next(&mut iter).expect("FieldSet corrupted (this is a bug)"),
                                    ::tracing::__macro_support::Option::Some(&format_args!("getting pgo_func_name_var for instance={0:?}",
                                                    instance) as &dyn Value))])
            });
    } else { ; }
};debug!("getting pgo_func_name_var for instance={:?}", instance);
76        let mut pgo_func_name_var_map = self.coverage_cx().pgo_func_name_var_map.borrow_mut();
77        pgo_func_name_var_map.entry(instance).or_insert_with(|| {
78            let llfn = self.get_fn(instance);
79            let mangled_fn_name: &str = self.tcx.symbol_name(instance).name;
80            llvm_cov::create_pgo_func_name_var(llfn, mangled_fn_name)
81        })
82    }
83}
84
85impl<'tcx> CoverageInfoBuilderMethods<'tcx> for Builder<'_, '_, 'tcx> {
86    #[allow(clippy :: suspicious_else_formatting)]
{
    let __tracing_attr_span;
    let __tracing_attr_guard;
    if ::tracing::Level::DEBUG <= ::tracing::level_filters::STATIC_MAX_LEVEL
                &&
                ::tracing::Level::DEBUG <=
                    ::tracing::level_filters::LevelFilter::current() ||
            { false } {
        __tracing_attr_span =
            {
                use ::tracing::__macro_support::Callsite as _;
                static __CALLSITE: ::tracing::callsite::DefaultCallsite =
                    {
                        static META: ::tracing::Metadata<'static> =
                            {
                                ::tracing_core::metadata::Metadata::new("add_coverage",
                                    "rustc_codegen_llvm::coverageinfo", ::tracing::Level::DEBUG,
                                    ::tracing_core::__macro_support::Option::Some("compiler/rustc_codegen_llvm/src/coverageinfo/mod.rs"),
                                    ::tracing_core::__macro_support::Option::Some(86u32),
                                    ::tracing_core::__macro_support::Option::Some("rustc_codegen_llvm::coverageinfo"),
                                    ::tracing_core::field::FieldSet::new(&["instance", "kind"],
                                        ::tracing_core::callsite::Identifier(&__CALLSITE)),
                                    ::tracing::metadata::Kind::SPAN)
                            };
                        ::tracing::callsite::DefaultCallsite::new(&META)
                    };
                let mut interest = ::tracing::subscriber::Interest::never();
                if ::tracing::Level::DEBUG <=
                                    ::tracing::level_filters::STATIC_MAX_LEVEL &&
                                ::tracing::Level::DEBUG <=
                                    ::tracing::level_filters::LevelFilter::current() &&
                            { interest = __CALLSITE.interest(); !interest.is_never() }
                        &&
                        ::tracing::__macro_support::__is_enabled(__CALLSITE.metadata(),
                            interest) {
                    let meta = __CALLSITE.metadata();
                    ::tracing::Span::new(meta,
                        &{
                                #[allow(unused_imports)]
                                use ::tracing::field::{debug, display, Value};
                                let mut iter = meta.fields().iter();
                                meta.fields().value_set(&[(&::tracing::__macro_support::Iterator::next(&mut iter).expect("FieldSet corrupted (this is a bug)"),
                                                    ::tracing::__macro_support::Option::Some(&::tracing::field::debug(&instance)
                                                            as &dyn Value)),
                                                (&::tracing::__macro_support::Iterator::next(&mut iter).expect("FieldSet corrupted (this is a bug)"),
                                                    ::tracing::__macro_support::Option::Some(&::tracing::field::debug(&kind)
                                                            as &dyn Value))])
                            })
                } else {
                    let span =
                        ::tracing::__macro_support::__disabled_span(__CALLSITE.metadata());
                    {};
                    span
                }
            };
        __tracing_attr_guard = __tracing_attr_span.enter();
    }

    #[warn(clippy :: suspicious_else_formatting)]
    {

        #[allow(unknown_lints, unreachable_code, clippy ::
        diverging_sub_expression, clippy :: empty_loop, clippy ::
        let_unit_value, clippy :: let_with_type_underscore, clippy ::
        needless_return, clippy :: unreachable)]
        if false {
            let __tracing_attr_fake_return: () = loop {};
            return __tracing_attr_fake_return;
        }
        {
            let bx = self;
            let Some(_coverage_cx) = &bx.cx.coverage_cx else { return };
            let Some(function_coverage_info) =
                bx.tcx.instance_mir(instance.def).function_coverage_info.as_deref() else {
                    {
                        use ::tracing::__macro_support::Callsite as _;
                        static __CALLSITE: ::tracing::callsite::DefaultCallsite =
                            {
                                static META: ::tracing::Metadata<'static> =
                                    {
                                        ::tracing_core::metadata::Metadata::new("event compiler/rustc_codegen_llvm/src/coverageinfo/mod.rs:107",
                                            "rustc_codegen_llvm::coverageinfo", ::tracing::Level::DEBUG,
                                            ::tracing_core::__macro_support::Option::Some("compiler/rustc_codegen_llvm/src/coverageinfo/mod.rs"),
                                            ::tracing_core::__macro_support::Option::Some(107u32),
                                            ::tracing_core::__macro_support::Option::Some("rustc_codegen_llvm::coverageinfo"),
                                            ::tracing_core::field::FieldSet::new(&["message"],
                                                ::tracing_core::callsite::Identifier(&__CALLSITE)),
                                            ::tracing::metadata::Kind::EVENT)
                                    };
                                ::tracing::callsite::DefaultCallsite::new(&META)
                            };
                        let enabled =
                            ::tracing::Level::DEBUG <=
                                        ::tracing::level_filters::STATIC_MAX_LEVEL &&
                                    ::tracing::Level::DEBUG <=
                                        ::tracing::level_filters::LevelFilter::current() &&
                                {
                                    let interest = __CALLSITE.interest();
                                    !interest.is_never() &&
                                        ::tracing::__macro_support::__is_enabled(__CALLSITE.metadata(),
                                            interest)
                                };
                        if enabled {
                            (|value_set: ::tracing::field::ValueSet|
                                        {
                                            let meta = __CALLSITE.metadata();
                                            ::tracing::Event::dispatch(meta, &value_set);
                                            ;
                                        })({
                                    #[allow(unused_imports)]
                                    use ::tracing::field::{debug, display, Value};
                                    let mut iter = __CALLSITE.metadata().fields().iter();
                                    __CALLSITE.metadata().fields().value_set(&[(&::tracing::__macro_support::Iterator::next(&mut iter).expect("FieldSet corrupted (this is a bug)"),
                                                        ::tracing::__macro_support::Option::Some(&format_args!("function has a coverage statement but no coverage info")
                                                                as &dyn Value))])
                                });
                        } else { ; }
                    };
                    return;
                };
            let Some(ids_info) =
                bx.tcx.coverage_ids_info(instance.def) else {
                    {
                        use ::tracing::__macro_support::Callsite as _;
                        static __CALLSITE: ::tracing::callsite::DefaultCallsite =
                            {
                                static META: ::tracing::Metadata<'static> =
                                    {
                                        ::tracing_core::metadata::Metadata::new("event compiler/rustc_codegen_llvm/src/coverageinfo/mod.rs:111",
                                            "rustc_codegen_llvm::coverageinfo", ::tracing::Level::DEBUG,
                                            ::tracing_core::__macro_support::Option::Some("compiler/rustc_codegen_llvm/src/coverageinfo/mod.rs"),
                                            ::tracing_core::__macro_support::Option::Some(111u32),
                                            ::tracing_core::__macro_support::Option::Some("rustc_codegen_llvm::coverageinfo"),
                                            ::tracing_core::field::FieldSet::new(&["message"],
                                                ::tracing_core::callsite::Identifier(&__CALLSITE)),
                                            ::tracing::metadata::Kind::EVENT)
                                    };
                                ::tracing::callsite::DefaultCallsite::new(&META)
                            };
                        let enabled =
                            ::tracing::Level::DEBUG <=
                                        ::tracing::level_filters::STATIC_MAX_LEVEL &&
                                    ::tracing::Level::DEBUG <=
                                        ::tracing::level_filters::LevelFilter::current() &&
                                {
                                    let interest = __CALLSITE.interest();
                                    !interest.is_never() &&
                                        ::tracing::__macro_support::__is_enabled(__CALLSITE.metadata(),
                                            interest)
                                };
                        if enabled {
                            (|value_set: ::tracing::field::ValueSet|
                                        {
                                            let meta = __CALLSITE.metadata();
                                            ::tracing::Event::dispatch(meta, &value_set);
                                            ;
                                        })({
                                    #[allow(unused_imports)]
                                    use ::tracing::field::{debug, display, Value};
                                    let mut iter = __CALLSITE.metadata().fields().iter();
                                    __CALLSITE.metadata().fields().value_set(&[(&::tracing::__macro_support::Iterator::next(&mut iter).expect("FieldSet corrupted (this is a bug)"),
                                                        ::tracing::__macro_support::Option::Some(&format_args!("function has a coverage statement but no IDs info")
                                                                as &dyn Value))])
                                });
                        } else { ; }
                    };
                    return;
                };
            match *kind {
                CoverageKind::SpanMarker | CoverageKind::BlockMarker { .. } =>
                    {
                    ::core::panicking::panic_fmt(format_args!("internal error: entered unreachable code: {0}",
                            format_args!("marker statement {0:?} should have been removed by CleanupPostBorrowck",
                                kind)));
                }
                CoverageKind::VirtualCounter { bcb } if
                    let Some(&id) = ids_info.phys_counter_for_node.get(&bcb) =>
                    {
                    let fn_name = bx.ensure_pgo_func_name_var(instance);
                    let hash =
                        bx.const_u64(function_coverage_info.function_source_hash);
                    let num_counters = bx.const_u32(ids_info.num_counters);
                    let index = bx.const_u32(id.as_u32());
                    {
                        use ::tracing::__macro_support::Callsite as _;
                        static __CALLSITE: ::tracing::callsite::DefaultCallsite =
                            {
                                static META: ::tracing::Metadata<'static> =
                                    {
                                        ::tracing_core::metadata::Metadata::new("event compiler/rustc_codegen_llvm/src/coverageinfo/mod.rs:126",
                                            "rustc_codegen_llvm::coverageinfo", ::tracing::Level::DEBUG,
                                            ::tracing_core::__macro_support::Option::Some("compiler/rustc_codegen_llvm/src/coverageinfo/mod.rs"),
                                            ::tracing_core::__macro_support::Option::Some(126u32),
                                            ::tracing_core::__macro_support::Option::Some("rustc_codegen_llvm::coverageinfo"),
                                            ::tracing_core::field::FieldSet::new(&["message"],
                                                ::tracing_core::callsite::Identifier(&__CALLSITE)),
                                            ::tracing::metadata::Kind::EVENT)
                                    };
                                ::tracing::callsite::DefaultCallsite::new(&META)
                            };
                        let enabled =
                            ::tracing::Level::DEBUG <=
                                        ::tracing::level_filters::STATIC_MAX_LEVEL &&
                                    ::tracing::Level::DEBUG <=
                                        ::tracing::level_filters::LevelFilter::current() &&
                                {
                                    let interest = __CALLSITE.interest();
                                    !interest.is_never() &&
                                        ::tracing::__macro_support::__is_enabled(__CALLSITE.metadata(),
                                            interest)
                                };
                        if enabled {
                            (|value_set: ::tracing::field::ValueSet|
                                        {
                                            let meta = __CALLSITE.metadata();
                                            ::tracing::Event::dispatch(meta, &value_set);
                                            ;
                                        })({
                                    #[allow(unused_imports)]
                                    use ::tracing::field::{debug, display, Value};
                                    let mut iter = __CALLSITE.metadata().fields().iter();
                                    __CALLSITE.metadata().fields().value_set(&[(&::tracing::__macro_support::Iterator::next(&mut iter).expect("FieldSet corrupted (this is a bug)"),
                                                        ::tracing::__macro_support::Option::Some(&format_args!("codegen intrinsic instrprof.increment(fn_name={0:?}, hash={1:?}, num_counters={2:?}, index={3:?})",
                                                                        fn_name, hash, num_counters, index) as &dyn Value))])
                                });
                        } else { ; }
                    };
                    bx.instrprof_increment(fn_name, hash, num_counters, index);
                }
                CoverageKind::VirtualCounter { .. } => {}
            }
        }
    }
}#[instrument(level = "debug", skip(self))]
87    fn add_coverage(&mut self, instance: Instance<'tcx>, kind: &CoverageKind) {
88        // Our caller should have already taken care of inlining subtleties,
89        // so we can assume that counter/expression IDs in this coverage
90        // statement are meaningful for the given instance.
91        //
92        // (Either the statement was not inlined and directly belongs to this
93        // instance, or it was inlined *from* this instance.)
94
95        let bx = self;
96
97        // Due to LocalCopy instantiation or MIR inlining, coverage statements
98        // can end up in a crate that isn't doing coverage instrumentation.
99        // When that happens, we currently just discard those statements, so
100        // the corresponding code will be undercounted.
101        // FIXME(Zalathar): Find a better solution for mixed-coverage builds.
102        let Some(_coverage_cx) = &bx.cx.coverage_cx else { return };
103
104        let Some(function_coverage_info) =
105            bx.tcx.instance_mir(instance.def).function_coverage_info.as_deref()
106        else {
107            debug!("function has a coverage statement but no coverage info");
108            return;
109        };
110        let Some(ids_info) = bx.tcx.coverage_ids_info(instance.def) else {
111            debug!("function has a coverage statement but no IDs info");
112            return;
113        };
114
115        match *kind {
116            CoverageKind::SpanMarker | CoverageKind::BlockMarker { .. } => unreachable!(
117                "marker statement {kind:?} should have been removed by CleanupPostBorrowck"
118            ),
119            CoverageKind::VirtualCounter { bcb }
120                if let Some(&id) = ids_info.phys_counter_for_node.get(&bcb) =>
121            {
122                let fn_name = bx.ensure_pgo_func_name_var(instance);
123                let hash = bx.const_u64(function_coverage_info.function_source_hash);
124                let num_counters = bx.const_u32(ids_info.num_counters);
125                let index = bx.const_u32(id.as_u32());
126                debug!(
127                    "codegen intrinsic instrprof.increment(fn_name={:?}, hash={:?}, num_counters={:?}, index={:?})",
128                    fn_name, hash, num_counters, index,
129                );
130                bx.instrprof_increment(fn_name, hash, num_counters, index);
131            }
132            // If a BCB doesn't have an associated physical counter, there's nothing to codegen.
133            CoverageKind::VirtualCounter { .. } => {}
134        }
135    }
136}