]> git.lizzy.rs Git - rust.git/blob - crates/ra_ide_db/src/change.rs
Add a command to compute memory usage statistics
[rust.git] / crates / ra_ide_db / src / change.rs
1 //! Defines a unit of change that can applied to a state of IDE to get the next
2 //! state. Changes are transactional.
3
4 use std::{fmt, sync::Arc, time};
5
6 use ra_db::{
7     salsa::{Database, Durability, SweepStrategy},
8     CrateGraph, FileId, RelativePathBuf, SourceDatabase, SourceDatabaseExt, SourceRoot,
9     SourceRootId,
10 };
11 use ra_prof::{memory_usage, profile, Bytes};
12 use rustc_hash::FxHashSet;
13
14 use crate::{symbol_index::SymbolsDatabase, RootDatabase};
15
16 #[derive(Default)]
17 pub struct AnalysisChange {
18     roots: Option<Vec<SourceRoot>>,
19     files_changed: Vec<(FileId, Option<Arc<String>>)>,
20     crate_graph: Option<CrateGraph>,
21 }
22
23 impl fmt::Debug for AnalysisChange {
24     fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
25         let mut d = fmt.debug_struct("AnalysisChange");
26         if let Some(roots) = &self.roots {
27             d.field("roots", roots);
28         }
29         if !self.files_changed.is_empty() {
30             d.field("files_changed", &self.files_changed.len());
31         }
32         if self.crate_graph.is_some() {
33             d.field("crate_graph", &self.crate_graph);
34         }
35         d.finish()
36     }
37 }
38
39 impl AnalysisChange {
40     pub fn new() -> AnalysisChange {
41         AnalysisChange::default()
42     }
43
44     pub fn set_roots(&mut self, roots: Vec<SourceRoot>) {
45         self.roots = Some(roots);
46     }
47
48     pub fn change_file(&mut self, file_id: FileId, new_text: Option<Arc<String>>) {
49         self.files_changed.push((file_id, new_text))
50     }
51
52     pub fn set_crate_graph(&mut self, graph: CrateGraph) {
53         self.crate_graph = Some(graph);
54     }
55 }
56
57 #[derive(Debug)]
58 struct AddFile {
59     file_id: FileId,
60     path: RelativePathBuf,
61     text: Arc<String>,
62 }
63
64 #[derive(Debug)]
65 struct RemoveFile {
66     file_id: FileId,
67     path: RelativePathBuf,
68 }
69
70 #[derive(Default)]
71 struct RootChange {
72     added: Vec<AddFile>,
73     removed: Vec<RemoveFile>,
74 }
75
76 impl fmt::Debug for RootChange {
77     fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
78         fmt.debug_struct("AnalysisChange")
79             .field("added", &self.added.len())
80             .field("removed", &self.removed.len())
81             .finish()
82     }
83 }
84
85 const GC_COOLDOWN: time::Duration = time::Duration::from_millis(100);
86
87 impl RootDatabase {
88     pub fn request_cancellation(&mut self) {
89         let _p = profile("RootDatabase::request_cancellation");
90         self.salsa_runtime_mut().synthetic_write(Durability::LOW);
91     }
92
93     pub fn apply_change(&mut self, change: AnalysisChange) {
94         let _p = profile("RootDatabase::apply_change");
95         self.request_cancellation();
96         log::info!("apply_change {:?}", change);
97         if let Some(roots) = change.roots {
98             let mut local_roots = FxHashSet::default();
99             let mut library_roots = FxHashSet::default();
100             for (idx, root) in roots.into_iter().enumerate() {
101                 let root_id = SourceRootId(idx as u32);
102                 let durability = durability(&root);
103                 if root.is_library {
104                     library_roots.insert(root_id);
105                 } else {
106                     local_roots.insert(root_id);
107                 }
108                 for file_id in root.iter() {
109                     self.set_file_source_root_with_durability(file_id, root_id, durability);
110                 }
111                 self.set_source_root_with_durability(root_id, Arc::new(root), durability);
112             }
113             self.set_local_roots_with_durability(Arc::new(local_roots), Durability::HIGH);
114             self.set_library_roots_with_durability(Arc::new(library_roots), Durability::HIGH);
115         }
116
117         for (file_id, text) in change.files_changed {
118             let source_root_id = self.file_source_root(file_id);
119             let source_root = self.source_root(source_root_id);
120             let durability = durability(&source_root);
121             // XXX: can't actually remove the file, just reset the text
122             let text = text.unwrap_or_default();
123             self.set_file_text_with_durability(file_id, text, durability)
124         }
125         if let Some(crate_graph) = change.crate_graph {
126             self.set_crate_graph_with_durability(Arc::new(crate_graph), Durability::HIGH)
127         }
128     }
129
130     pub fn maybe_collect_garbage(&mut self) {
131         if cfg!(feature = "wasm") {
132             return;
133         }
134
135         if self.last_gc_check.elapsed() > GC_COOLDOWN {
136             self.last_gc_check = crate::wasm_shims::Instant::now();
137         }
138     }
139
140     pub fn collect_garbage(&mut self) {
141         if cfg!(feature = "wasm") {
142             return;
143         }
144
145         let _p = profile("RootDatabase::collect_garbage");
146         self.last_gc = crate::wasm_shims::Instant::now();
147
148         let sweep = SweepStrategy::default().discard_values().sweep_all_revisions();
149
150         ra_db::ParseQuery.in_db(self).sweep(sweep);
151         hir::db::ParseMacroQuery.in_db(self).sweep(sweep);
152
153         // Macros do take significant space, but less then the syntax trees
154         // self.query(hir::db::MacroDefQuery).sweep(sweep);
155         // self.query(hir::db::MacroArgQuery).sweep(sweep);
156         // self.query(hir::db::MacroExpandQuery).sweep(sweep);
157
158         hir::db::AstIdMapQuery.in_db(self).sweep(sweep);
159
160         hir::db::BodyWithSourceMapQuery.in_db(self).sweep(sweep);
161
162         hir::db::ExprScopesQuery.in_db(self).sweep(sweep);
163         hir::db::InferQueryQuery.in_db(self).sweep(sweep);
164         hir::db::BodyQuery.in_db(self).sweep(sweep);
165     }
166
167     // Feature: Memory Usage
168     //
169     // Clears rust-analyzer's internal database and prints memory usage statistics.
170     //
171     // |===
172     // | Editor  | Action Name
173     //
174     // | VS Code | **Rust Analyzer: Memory Usage (Clears Database)**
175     // |===
176     pub fn per_query_memory_usage(&mut self) -> Vec<(String, Bytes)> {
177         let mut acc: Vec<(String, Bytes)> = vec![];
178         let sweep = SweepStrategy::default().discard_values().sweep_all_revisions();
179         macro_rules! sweep_each_query {
180             ($($q:path)*) => {$(
181                 let before = memory_usage().allocated;
182                 $q.in_db(self).sweep(sweep);
183                 let after = memory_usage().allocated;
184                 let q: $q = Default::default();
185                 let name = format!("{:?}", q);
186                 acc.push((name, before - after));
187
188                 let before = memory_usage().allocated;
189                 $q.in_db(self).sweep(sweep.discard_everything());
190                 let after = memory_usage().allocated;
191                 let q: $q = Default::default();
192                 let name = format!("{:?} (deps)", q);
193                 acc.push((name, before - after));
194             )*}
195         }
196         sweep_each_query![
197             // SourceDatabase
198             ra_db::ParseQuery
199             ra_db::SourceRootCratesQuery
200
201             // AstDatabase
202             hir::db::AstIdMapQuery
203             hir::db::MacroArgQuery
204             hir::db::MacroDefQuery
205             hir::db::ParseMacroQuery
206             hir::db::MacroExpandQuery
207
208             // DefDatabase
209             hir::db::ItemTreeQuery
210             hir::db::CrateDefMapQueryQuery
211             hir::db::StructDataQuery
212             hir::db::UnionDataQuery
213             hir::db::EnumDataQuery
214             hir::db::ImplDataQuery
215             hir::db::TraitDataQuery
216             hir::db::TypeAliasDataQuery
217             hir::db::FunctionDataQuery
218             hir::db::ConstDataQuery
219             hir::db::StaticDataQuery
220             hir::db::BodyWithSourceMapQuery
221             hir::db::BodyQuery
222             hir::db::ExprScopesQuery
223             hir::db::GenericParamsQuery
224             hir::db::AttrsQuery
225             hir::db::ModuleLangItemsQuery
226             hir::db::CrateLangItemsQuery
227             hir::db::LangItemQuery
228             hir::db::DocumentationQuery
229             hir::db::ImportMapQuery
230
231             // HirDatabase
232             hir::db::InferQueryQuery
233             hir::db::TyQuery
234             hir::db::ValueTyQuery
235             hir::db::ImplSelfTyQuery
236             hir::db::ImplTraitQuery
237             hir::db::FieldTypesQuery
238             hir::db::CallableItemSignatureQuery
239             hir::db::GenericPredicatesForParamQuery
240             hir::db::GenericPredicatesQuery
241             hir::db::GenericDefaultsQuery
242             hir::db::InherentImplsInCrateQuery
243             hir::db::TraitImplsInCrateQuery
244             hir::db::TraitImplsInDepsQuery
245             hir::db::AssociatedTyDataQuery
246             hir::db::TraitDatumQuery
247             hir::db::StructDatumQuery
248             hir::db::ImplDatumQuery
249             hir::db::AssociatedTyValueQuery
250             hir::db::TraitSolveQuery
251             hir::db::ReturnTypeImplTraitsQuery
252
253             // SymbolsDatabase
254             crate::symbol_index::FileSymbolsQuery
255
256             // LineIndexDatabase
257             crate::LineIndexQuery
258         ];
259
260         // To collect interned data, we need to bump the revision counter by performing a synthetic
261         // write.
262         // We do this after collecting the non-interned queries to correctly attribute memory used
263         // by interned data.
264         self.salsa_runtime_mut().synthetic_write(Durability::HIGH);
265
266         sweep_each_query![
267             // AstDatabase
268             hir::db::InternMacroQuery
269             hir::db::InternEagerExpansionQuery
270
271             // InternDatabase
272             hir::db::InternFunctionQuery
273             hir::db::InternStructQuery
274             hir::db::InternUnionQuery
275             hir::db::InternEnumQuery
276             hir::db::InternConstQuery
277             hir::db::InternStaticQuery
278             hir::db::InternTraitQuery
279             hir::db::InternTypeAliasQuery
280             hir::db::InternImplQuery
281
282             // HirDatabase
283             hir::db::InternTypeCtorQuery
284             hir::db::InternTypeParamIdQuery
285             hir::db::InternChalkImplQuery
286             hir::db::InternAssocTyValueQuery
287         ];
288
289         acc.sort_by_key(|it| std::cmp::Reverse(it.1));
290         acc
291     }
292 }
293
294 fn durability(source_root: &SourceRoot) -> Durability {
295     if source_root.is_library {
296         Durability::HIGH
297     } else {
298         Durability::LOW
299     }
300 }