rustc_query_impl/
profiling_support.rs

1use std::fmt::Debug;
2use std::io::Write;
3
4use measureme::{StringComponent, StringId};
5use rustc_data_structures::fx::FxHashMap;
6use rustc_data_structures::profiling::SelfProfiler;
7use rustc_hir::def_id::{CrateNum, DefId, DefIndex, LOCAL_CRATE, LocalDefId};
8use rustc_hir::definitions::DefPathData;
9use rustc_middle::ty::TyCtxt;
10use rustc_query_system::query::QueryCache;
11
12pub(crate) struct QueryKeyStringCache {
13    def_id_cache: FxHashMap<DefId, StringId>,
14}
15
16impl QueryKeyStringCache {
17    fn new() -> QueryKeyStringCache {
18        QueryKeyStringCache { def_id_cache: Default::default() }
19    }
20}
21
22struct QueryKeyStringBuilder<'a, 'tcx> {
23    profiler: &'a SelfProfiler,
24    tcx: TyCtxt<'tcx>,
25    string_cache: &'a mut QueryKeyStringCache,
26}
27
28impl<'a, 'tcx> QueryKeyStringBuilder<'a, 'tcx> {
29    fn new(
30        profiler: &'a SelfProfiler,
31        tcx: TyCtxt<'tcx>,
32        string_cache: &'a mut QueryKeyStringCache,
33    ) -> QueryKeyStringBuilder<'a, 'tcx> {
34        QueryKeyStringBuilder { profiler, tcx, string_cache }
35    }
36
37    // The current implementation is rather crude. In the future it might be a
38    // good idea to base this on `ty::print` in order to get nicer and more
39    // efficient query keys.
40    fn def_id_to_string_id(&mut self, def_id: DefId) -> StringId {
41        if let Some(&string_id) = self.string_cache.def_id_cache.get(&def_id) {
42            return string_id;
43        }
44
45        let def_key = self.tcx.def_key(def_id);
46
47        let (parent_string_id, start_index) = match def_key.parent {
48            Some(parent_index) => {
49                let parent_def_id = DefId { index: parent_index, krate: def_id.krate };
50
51                (self.def_id_to_string_id(parent_def_id), 0)
52            }
53            None => (StringId::INVALID, 2),
54        };
55
56        let dis_buffer = &mut [0u8; 16];
57        let crate_name;
58        let other_name;
59        let name;
60        let dis;
61        let end_index;
62
63        match def_key.disambiguated_data.data {
64            DefPathData::CrateRoot => {
65                crate_name = self.tcx.crate_name(def_id.krate);
66                name = crate_name.as_str();
67                dis = "";
68                end_index = 3;
69            }
70            other => {
71                other_name = other.to_string();
72                name = other_name.as_str();
73                if def_key.disambiguated_data.disambiguator == 0 {
74                    dis = "";
75                    end_index = 3;
76                } else {
77                    write!(&mut dis_buffer[..], "[{}]", def_key.disambiguated_data.disambiguator)
78                        .unwrap();
79                    let end_of_dis = dis_buffer.iter().position(|&c| c == b']').unwrap();
80                    dis = std::str::from_utf8(&dis_buffer[..end_of_dis + 1]).unwrap();
81                    end_index = 4;
82                }
83            }
84        }
85
86        let components = [
87            StringComponent::Ref(parent_string_id),
88            StringComponent::Value("::"),
89            StringComponent::Value(name),
90            StringComponent::Value(dis),
91        ];
92
93        let string_id = self.profiler.alloc_string(&components[start_index..end_index]);
94
95        self.string_cache.def_id_cache.insert(def_id, string_id);
96
97        string_id
98    }
99}
100
101trait IntoSelfProfilingString {
102    fn to_self_profile_string(&self, builder: &mut QueryKeyStringBuilder<'_, '_>) -> StringId;
103}
104
105// The default implementation of `IntoSelfProfilingString` just uses `Debug`
106// which is slow and causes lots of duplication of string data.
107// The specialized impls below take care of making the `DefId` case more
108// efficient.
109impl<T: Debug> IntoSelfProfilingString for T {
110    default fn to_self_profile_string(
111        &self,
112        builder: &mut QueryKeyStringBuilder<'_, '_>,
113    ) -> StringId {
114        let s = format!("{self:?}");
115        builder.profiler.alloc_string(&s[..])
116    }
117}
118
119impl<T: SpecIntoSelfProfilingString> IntoSelfProfilingString for T {
120    fn to_self_profile_string(&self, builder: &mut QueryKeyStringBuilder<'_, '_>) -> StringId {
121        self.spec_to_self_profile_string(builder)
122    }
123}
124
125#[rustc_specialization_trait]
126trait SpecIntoSelfProfilingString: Debug {
127    fn spec_to_self_profile_string(&self, builder: &mut QueryKeyStringBuilder<'_, '_>) -> StringId;
128}
129
130impl SpecIntoSelfProfilingString for DefId {
131    fn spec_to_self_profile_string(&self, builder: &mut QueryKeyStringBuilder<'_, '_>) -> StringId {
132        builder.def_id_to_string_id(*self)
133    }
134}
135
136impl SpecIntoSelfProfilingString for CrateNum {
137    fn spec_to_self_profile_string(&self, builder: &mut QueryKeyStringBuilder<'_, '_>) -> StringId {
138        builder.def_id_to_string_id(self.as_def_id())
139    }
140}
141
142impl SpecIntoSelfProfilingString for DefIndex {
143    fn spec_to_self_profile_string(&self, builder: &mut QueryKeyStringBuilder<'_, '_>) -> StringId {
144        builder.def_id_to_string_id(DefId { krate: LOCAL_CRATE, index: *self })
145    }
146}
147
148impl SpecIntoSelfProfilingString for LocalDefId {
149    fn spec_to_self_profile_string(&self, builder: &mut QueryKeyStringBuilder<'_, '_>) -> StringId {
150        builder.def_id_to_string_id(DefId { krate: LOCAL_CRATE, index: self.local_def_index })
151    }
152}
153
154impl<T0, T1> SpecIntoSelfProfilingString for (T0, T1)
155where
156    T0: SpecIntoSelfProfilingString,
157    T1: SpecIntoSelfProfilingString,
158{
159    fn spec_to_self_profile_string(&self, builder: &mut QueryKeyStringBuilder<'_, '_>) -> StringId {
160        let val0 = self.0.to_self_profile_string(builder);
161        let val1 = self.1.to_self_profile_string(builder);
162
163        let components = &[
164            StringComponent::Value("("),
165            StringComponent::Ref(val0),
166            StringComponent::Value(","),
167            StringComponent::Ref(val1),
168            StringComponent::Value(")"),
169        ];
170
171        builder.profiler.alloc_string(components)
172    }
173}
174
175/// Allocate the self-profiling query strings for a single query cache. This
176/// method is called from `alloc_self_profile_query_strings` which knows all
177/// the queries via macro magic.
178pub(crate) fn alloc_self_profile_query_strings_for_query_cache<'tcx, C>(
179    tcx: TyCtxt<'tcx>,
180    query_name: &'static str,
181    query_cache: &C,
182    string_cache: &mut QueryKeyStringCache,
183) where
184    C: QueryCache,
185    C::Key: Debug + Clone,
186{
187    tcx.prof.with_profiler(|profiler| {
188        let event_id_builder = profiler.event_id_builder();
189
190        // Walk the entire query cache and allocate the appropriate
191        // string representations. Each cache entry is uniquely
192        // identified by its dep_node_index.
193        if profiler.query_key_recording_enabled() {
194            let mut query_string_builder = QueryKeyStringBuilder::new(profiler, tcx, string_cache);
195
196            let query_name = profiler.get_or_alloc_cached_string(query_name);
197
198            // Since building the string representation of query keys might
199            // need to invoke queries itself, we cannot keep the query caches
200            // locked while doing so. Instead we copy out the
201            // `(query_key, dep_node_index)` pairs and release the lock again.
202            let mut query_keys_and_indices = Vec::new();
203            query_cache.iter(&mut |k, _, i| query_keys_and_indices.push((*k, i)));
204
205            // Now actually allocate the strings. If allocating the strings
206            // generates new entries in the query cache, we'll miss them but
207            // we don't actually care.
208            for (query_key, dep_node_index) in query_keys_and_indices {
209                // Translate the DepNodeIndex into a QueryInvocationId
210                let query_invocation_id = dep_node_index.into();
211
212                // Create the string version of the query-key
213                let query_key = query_key.to_self_profile_string(&mut query_string_builder);
214                let event_id = event_id_builder.from_label_and_arg(query_name, query_key);
215
216                // Doing this in bulk might be a good idea:
217                profiler.map_query_invocation_id_to_string(
218                    query_invocation_id,
219                    event_id.to_string_id(),
220                );
221            }
222        } else {
223            // In this branch we don't allocate query keys
224            let query_name = profiler.get_or_alloc_cached_string(query_name);
225            let event_id = event_id_builder.from_label(query_name).to_string_id();
226
227            // FIXME(eddyb) make this O(1) by using a pre-cached query name `EventId`,
228            // instead of passing the `DepNodeIndex` to `finish_with_query_invocation_id`,
229            // when recording the event in the first place.
230            let mut query_invocation_ids = Vec::new();
231            query_cache.iter(&mut |_, _, i| {
232                query_invocation_ids.push(i.into());
233            });
234
235            profiler.bulk_map_query_invocation_id_to_single_string(
236                query_invocation_ids.into_iter(),
237                event_id,
238            );
239        }
240    });
241}
242
243/// All self-profiling events generated by the query engine use
244/// virtual `StringId`s for their `event_id`. This method makes all
245/// those virtual `StringId`s point to actual strings.
246///
247/// If we are recording only summary data, the ids will point to
248/// just the query names. If we are recording query keys too, we
249/// allocate the corresponding strings here.
250pub fn alloc_self_profile_query_strings(tcx: TyCtxt<'_>) {
251    if !tcx.prof.enabled() {
252        return;
253    }
254
255    let _prof_timer = tcx.sess.prof.generic_activity("self_profile_alloc_query_strings");
256
257    let mut string_cache = QueryKeyStringCache::new();
258
259    for alloc in super::ALLOC_SELF_PROFILE_QUERY_STRINGS.iter() {
260        alloc(tcx, &mut string_cache)
261    }
262}