• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 use std::{fmt, marker::PhantomData};
2 
3 use hir::{
4     db::{AstIdMapQuery, AttrsQuery, BlockDefMapQuery, ParseMacroExpansionQuery},
5     Attr, Attrs, ExpandResult, MacroFile, Module,
6 };
7 use ide_db::{
8     base_db::{
9         salsa::{
10             debug::{DebugQueryTable, TableEntry},
11             Query, QueryTable,
12         },
13         CrateId, FileId, FileTextQuery, ParseQuery, SourceDatabase, SourceRootId,
14     },
15     symbol_index::ModuleSymbolsQuery,
16 };
17 use ide_db::{
18     symbol_index::{LibrarySymbolsQuery, SymbolIndex},
19     RootDatabase,
20 };
21 use itertools::Itertools;
22 use profile::{memory_usage, Bytes};
23 use std::env;
24 use stdx::format_to;
25 use syntax::{ast, Parse, SyntaxNode};
26 use triomphe::Arc;
27 
28 // Feature: Status
29 //
30 // Shows internal statistic about memory usage of rust-analyzer.
31 //
32 // |===
33 // | Editor  | Action Name
34 //
35 // | VS Code | **rust-analyzer: Status**
36 // |===
37 // image::https://user-images.githubusercontent.com/48062697/113065584-05f34500-91b1-11eb-98cc-5c196f76be7f.gif[]
status(db: &RootDatabase, file_id: Option<FileId>) -> String38 pub(crate) fn status(db: &RootDatabase, file_id: Option<FileId>) -> String {
39     let mut buf = String::new();
40 
41     format_to!(buf, "{}\n", collect_query(FileTextQuery.in_db(db)));
42     format_to!(buf, "{}\n", collect_query(ParseQuery.in_db(db)));
43     format_to!(buf, "{}\n", collect_query(ParseMacroExpansionQuery.in_db(db)));
44     format_to!(buf, "{}\n", collect_query(LibrarySymbolsQuery.in_db(db)));
45     format_to!(buf, "{}\n", collect_query(ModuleSymbolsQuery.in_db(db)));
46     format_to!(buf, "{} in total\n", memory_usage());
47     if env::var("RA_COUNT").is_ok() {
48         format_to!(buf, "\nCounts:\n{}", profile::countme::get_all());
49     }
50 
51     format_to!(buf, "\nDebug info:\n");
52     format_to!(buf, "{}\n", collect_query(AttrsQuery.in_db(db)));
53     format_to!(buf, "{} ast id maps\n", collect_query_count(AstIdMapQuery.in_db(db)));
54     format_to!(buf, "{} block def maps\n", collect_query_count(BlockDefMapQuery.in_db(db)));
55 
56     if let Some(file_id) = file_id {
57         format_to!(buf, "\nFile info:\n");
58         let crates = crate::parent_module::crates_for(db, file_id);
59         if crates.is_empty() {
60             format_to!(buf, "Does not belong to any crate");
61         }
62         let crate_graph = db.crate_graph();
63         for krate in crates {
64             let display_crate = |krate: CrateId| match &crate_graph[krate].display_name {
65                 Some(it) => format!("{it}({})", krate.into_raw()),
66                 None => format!("{}", krate.into_raw()),
67             };
68             format_to!(buf, "Crate: {}\n", display_crate(krate));
69             let deps = crate_graph[krate]
70                 .dependencies
71                 .iter()
72                 .map(|dep| format!("{}={:?}", dep.name, dep.crate_id))
73                 .format(", ");
74             format_to!(buf, "Dependencies: {}\n", deps);
75         }
76     }
77 
78     buf.trim().to_string()
79 }
80 
collect_query<'q, Q>(table: QueryTable<'q, Q>) -> <Q as QueryCollect>::Collector where QueryTable<'q, Q>: DebugQueryTable, Q: QueryCollect, <Q as Query>::Storage: 'q, <Q as QueryCollect>::Collector: StatCollect< <QueryTable<'q, Q> as DebugQueryTable>::Key, <QueryTable<'q, Q> as DebugQueryTable>::Value, >,81 fn collect_query<'q, Q>(table: QueryTable<'q, Q>) -> <Q as QueryCollect>::Collector
82 where
83     QueryTable<'q, Q>: DebugQueryTable,
84     Q: QueryCollect,
85     <Q as Query>::Storage: 'q,
86     <Q as QueryCollect>::Collector: StatCollect<
87         <QueryTable<'q, Q> as DebugQueryTable>::Key,
88         <QueryTable<'q, Q> as DebugQueryTable>::Value,
89     >,
90 {
91     struct StatCollectorWrapper<C>(C);
92     impl<C: StatCollect<K, V>, K, V> FromIterator<TableEntry<K, V>> for StatCollectorWrapper<C> {
93         fn from_iter<T>(iter: T) -> StatCollectorWrapper<C>
94         where
95             T: IntoIterator<Item = TableEntry<K, V>>,
96         {
97             let mut res = C::default();
98             for entry in iter {
99                 res.collect_entry(entry.key, entry.value);
100             }
101             StatCollectorWrapper(res)
102         }
103     }
104     table.entries::<StatCollectorWrapper<<Q as QueryCollect>::Collector>>().0
105 }
106 
collect_query_count<'q, Q>(table: QueryTable<'q, Q>) -> usize where QueryTable<'q, Q>: DebugQueryTable, Q: Query, <Q as Query>::Storage: 'q,107 fn collect_query_count<'q, Q>(table: QueryTable<'q, Q>) -> usize
108 where
109     QueryTable<'q, Q>: DebugQueryTable,
110     Q: Query,
111     <Q as Query>::Storage: 'q,
112 {
113     struct EntryCounter(usize);
114     impl<K, V> FromIterator<TableEntry<K, V>> for EntryCounter {
115         fn from_iter<T>(iter: T) -> EntryCounter
116         where
117             T: IntoIterator<Item = TableEntry<K, V>>,
118         {
119             EntryCounter(iter.into_iter().count())
120         }
121     }
122     table.entries::<EntryCounter>().0
123 }
124 
125 trait QueryCollect: Query {
126     type Collector;
127 }
128 
129 impl QueryCollect for LibrarySymbolsQuery {
130     type Collector = SymbolsStats<SourceRootId>;
131 }
132 
133 impl QueryCollect for ParseQuery {
134     type Collector = SyntaxTreeStats<false>;
135 }
136 
137 impl QueryCollect for ParseMacroExpansionQuery {
138     type Collector = SyntaxTreeStats<true>;
139 }
140 
141 impl QueryCollect for FileTextQuery {
142     type Collector = FilesStats;
143 }
144 
145 impl QueryCollect for ModuleSymbolsQuery {
146     type Collector = SymbolsStats<Module>;
147 }
148 
149 impl QueryCollect for AttrsQuery {
150     type Collector = AttrsStats;
151 }
152 
153 trait StatCollect<K, V>: Default {
collect_entry(&mut self, key: K, value: Option<V>)154     fn collect_entry(&mut self, key: K, value: Option<V>);
155 }
156 
157 #[derive(Default)]
158 struct FilesStats {
159     total: usize,
160     size: Bytes,
161 }
162 
163 impl fmt::Display for FilesStats {
fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result164     fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
165         write!(fmt, "{} of files", self.size)
166     }
167 }
168 
169 impl StatCollect<FileId, Arc<str>> for FilesStats {
collect_entry(&mut self, _: FileId, value: Option<Arc<str>>)170     fn collect_entry(&mut self, _: FileId, value: Option<Arc<str>>) {
171         self.total += 1;
172         self.size += value.unwrap().len();
173     }
174 }
175 
176 #[derive(Default)]
177 pub(crate) struct SyntaxTreeStats<const MACROS: bool> {
178     total: usize,
179     pub(crate) retained: usize,
180 }
181 
182 impl<const MACROS: bool> fmt::Display for SyntaxTreeStats<MACROS> {
fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result183     fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
184         write!(
185             fmt,
186             "{} trees, {} preserved{}",
187             self.total,
188             self.retained,
189             if MACROS { " (macros)" } else { "" }
190         )
191     }
192 }
193 
194 impl StatCollect<FileId, Parse<ast::SourceFile>> for SyntaxTreeStats<false> {
collect_entry(&mut self, _: FileId, value: Option<Parse<ast::SourceFile>>)195     fn collect_entry(&mut self, _: FileId, value: Option<Parse<ast::SourceFile>>) {
196         self.total += 1;
197         self.retained += value.is_some() as usize;
198     }
199 }
200 
201 impl<M> StatCollect<MacroFile, ExpandResult<(Parse<SyntaxNode>, M)>> for SyntaxTreeStats<true> {
collect_entry(&mut self, _: MacroFile, value: Option<ExpandResult<(Parse<SyntaxNode>, M)>>)202     fn collect_entry(&mut self, _: MacroFile, value: Option<ExpandResult<(Parse<SyntaxNode>, M)>>) {
203         self.total += 1;
204         self.retained += value.is_some() as usize;
205     }
206 }
207 
208 struct SymbolsStats<Key> {
209     total: usize,
210     size: Bytes,
211     phantom: PhantomData<Key>,
212 }
213 
214 impl<Key> Default for SymbolsStats<Key> {
default() -> Self215     fn default() -> Self {
216         Self { total: Default::default(), size: Default::default(), phantom: PhantomData }
217     }
218 }
219 
220 impl fmt::Display for SymbolsStats<Module> {
fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result221     fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
222         write!(fmt, "{} of module index symbols ({})", self.size, self.total)
223     }
224 }
225 impl fmt::Display for SymbolsStats<SourceRootId> {
fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result226     fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
227         write!(fmt, "{} of library index symbols ({})", self.size, self.total)
228     }
229 }
230 impl<Key> StatCollect<Key, Arc<SymbolIndex>> for SymbolsStats<Key> {
collect_entry(&mut self, _: Key, value: Option<Arc<SymbolIndex>>)231     fn collect_entry(&mut self, _: Key, value: Option<Arc<SymbolIndex>>) {
232         if let Some(symbols) = value {
233             self.total += symbols.len();
234             self.size += symbols.memory_size();
235         }
236     }
237 }
238 
239 #[derive(Default)]
240 struct AttrsStats {
241     entries: usize,
242     total: usize,
243 }
244 
245 impl fmt::Display for AttrsStats {
fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result246     fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
247         let size =
248             self.entries * std::mem::size_of::<Attrs>() + self.total * std::mem::size_of::<Attr>();
249         let size = Bytes::new(size as _);
250         write!(
251             fmt,
252             "{} attribute query entries, {} total attributes ({} for storing entries)",
253             self.entries, self.total, size
254         )
255     }
256 }
257 
258 impl<Key> StatCollect<Key, Attrs> for AttrsStats {
collect_entry(&mut self, _: Key, value: Option<Attrs>)259     fn collect_entry(&mut self, _: Key, value: Option<Attrs>) {
260         self.entries += 1;
261         self.total += value.map_or(0, |it| it.len());
262     }
263 }
264