Skip to content

Commit b8ead41

Browse files
Initial support for recording query keys in self-profiling data.
1 parent 996511a commit b8ead41

File tree

9 files changed

+354
-52
lines changed

9 files changed

+354
-52
lines changed

Cargo.lock

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1995,9 +1995,9 @@ dependencies = [
19951995

19961996
[[package]]
19971997
name = "measureme"
1998-
version = "0.6.0"
1998+
version = "0.7.0"
19991999
source = "registry+https://github.com/rust-lang/crates.io-index"
2000-
checksum = "36dcc09c1a633097649f7d48bde3d8a61d2a43c01ce75525e31fbbc82c0fccf4"
2000+
checksum = "ebefdcb02b2ddeee50178a218aeaf6d752d0777cd07914542f202cb7440e6e38"
20012001
dependencies = [
20022002
"byteorder",
20032003
"memmap",

src/librustc/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,6 @@ parking_lot = "0.9"
3636
byteorder = { version = "1.3" }
3737
chalk-engine = { version = "0.9.0", default-features=false }
3838
smallvec = { version = "1.0", features = ["union", "may_dangle"] }
39-
measureme = "0.6.0"
39+
measureme = "0.7.0"
4040
rustc_error_codes = { path = "../librustc_error_codes" }
4141
rustc_session = { path = "../librustc_session" }

src/librustc/dep_graph/graph.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ pub struct DepGraph {
3030

3131
/// This field is used for assigning DepNodeIndices when running in
3232
/// non-incremental mode. Even in non-incremental mode we make sure that
33-
/// each task as a `DepNodeIndex` that uniquely identifies it. This unique
33+
/// each task has a `DepNodeIndex` that uniquely identifies it. This unique
3434
/// ID is used for self-profiling.
3535
virtual_dep_node_index: Lrc<AtomicU32>,
3636
}

src/librustc/ty/query/mod.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,9 @@ pub(crate) use self::config::QueryDescription;
8181
mod on_disk_cache;
8282
pub use self::on_disk_cache::OnDiskCache;
8383

84+
mod profiling_support;
85+
pub use self::profiling_support::{IntoSelfProfilingString, QueryKeyStringBuilder};
86+
8487
// Each of these queries corresponds to a function pointer field in the
8588
// `Providers` struct for requesting a value of that type, and a method
8689
// on `tcx: TyCtxt` (and `tcx.at(span)`) for doing that request in a way

src/librustc/ty/query/plumbing.rs

Lines changed: 29 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -701,42 +701,6 @@ macro_rules! define_queries_inner {
701701
}
702702
}
703703

704-
/// All self-profiling events generated by the query engine use a
705-
/// virtual `StringId`s for their `event_id`. This method makes all
706-
/// those virtual `StringId`s point to actual strings.
707-
///
708-
/// If we are recording only summary data, the ids will point to
709-
/// just the query names. If we are recording query keys too, we
710-
/// allocate the corresponding strings here. (The latter is not yet
711-
/// implemented.)
712-
pub fn allocate_self_profile_query_strings(
713-
&self,
714-
profiler: &rustc_data_structures::profiling::SelfProfiler
715-
) {
716-
// Walk the entire query cache and allocate the appropriate
717-
// string representation. Each cache entry is uniquely
718-
// identified by its dep_node_index.
719-
$({
720-
let query_name_string_id =
721-
profiler.get_or_alloc_cached_string(stringify!($name));
722-
723-
let result_cache = self.$name.lock_shards();
724-
725-
for shard in result_cache.iter() {
726-
let query_invocation_ids = shard
727-
.results
728-
.values()
729-
.map(|v| v.index)
730-
.map(|dep_node_index| dep_node_index.into());
731-
732-
profiler.bulk_map_query_invocation_id_to_single_string(
733-
query_invocation_ids,
734-
query_name_string_id
735-
);
736-
}
737-
})*
738-
}
739-
740704
#[cfg(parallel_compiler)]
741705
pub fn collect_active_jobs(&self) -> Vec<Lrc<QueryJob<$tcx>>> {
742706
let mut jobs = Vec::new();
@@ -1040,6 +1004,35 @@ macro_rules! define_queries_inner {
10401004
pub fn $name(self, key: $K) -> $V {
10411005
self.at(DUMMY_SP).$name(key)
10421006
})*
1007+
1008+
/// All self-profiling events generated by the query engine use
1009+
/// virtual `StringId`s for their `event_id`. This method makes all
1010+
/// those virtual `StringId`s point to actual strings.
1011+
///
1012+
/// If we are recording only summary data, the ids will point to
1013+
/// just the query names. If we are recording query keys too, we
1014+
/// allocate the corresponding strings here.
1015+
pub fn alloc_self_profile_query_strings(self) {
1016+
use crate::ty::query::profiling_support::{
1017+
alloc_self_profile_query_strings_for_query_cache,
1018+
QueryKeyStringCache,
1019+
};
1020+
1021+
if !self.prof.enabled() {
1022+
return;
1023+
}
1024+
1025+
let mut string_cache = QueryKeyStringCache::new();
1026+
1027+
$({
1028+
alloc_self_profile_query_strings_for_query_cache(
1029+
self,
1030+
stringify!($name),
1031+
&self.queries.$name,
1032+
&mut string_cache,
1033+
);
1034+
})*
1035+
}
10431036
}
10441037

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

0 commit comments

Comments
 (0)