]>
Commit | Line | Data |
---|---|---|
064997fb FG |
1 | use std::{fmt, iter::FromIterator, sync::Arc}; |
2 | ||
3 | use hir::{ExpandResult, MacroFile}; | |
4 | use ide_db::base_db::{ | |
5 | salsa::debug::{DebugQueryTable, TableEntry}, | |
6 | CrateId, FileId, FileTextQuery, SourceDatabase, SourceRootId, | |
7 | }; | |
8 | use ide_db::{ | |
9 | symbol_index::{LibrarySymbolsQuery, SymbolIndex}, | |
10 | RootDatabase, | |
11 | }; | |
12 | use itertools::Itertools; | |
13 | use profile::{memory_usage, Bytes}; | |
14 | use std::env; | |
15 | use stdx::format_to; | |
16 | use syntax::{ast, Parse, SyntaxNode}; | |
17 | ||
18 | fn syntax_tree_stats(db: &RootDatabase) -> SyntaxTreeStats { | |
19 | ide_db::base_db::ParseQuery.in_db(db).entries::<SyntaxTreeStats>() | |
20 | } | |
21 | fn macro_syntax_tree_stats(db: &RootDatabase) -> SyntaxTreeStats { | |
22 | hir::db::ParseMacroExpansionQuery.in_db(db).entries::<SyntaxTreeStats>() | |
23 | } | |
24 | ||
25 | // Feature: Status | |
26 | // | |
27 | // Shows internal statistic about memory usage of rust-analyzer. | |
28 | // | |
29 | // |=== | |
30 | // | Editor | Action Name | |
31 | // | |
32 | // | VS Code | **Rust Analyzer: Status** | |
33 | // |=== | |
34 | // image::https://user-images.githubusercontent.com/48062697/113065584-05f34500-91b1-11eb-98cc-5c196f76be7f.gif[] | |
35 | pub(crate) fn status(db: &RootDatabase, file_id: Option<FileId>) -> String { | |
36 | let mut buf = String::new(); | |
37 | format_to!(buf, "{}\n", FileTextQuery.in_db(db).entries::<FilesStats>()); | |
38 | format_to!(buf, "{}\n", LibrarySymbolsQuery.in_db(db).entries::<LibrarySymbolsStats>()); | |
39 | format_to!(buf, "{}\n", syntax_tree_stats(db)); | |
40 | format_to!(buf, "{} (Macros)\n", macro_syntax_tree_stats(db)); | |
41 | format_to!(buf, "{} in total\n", memory_usage()); | |
42 | if env::var("RA_COUNT").is_ok() { | |
43 | format_to!(buf, "\nCounts:\n{}", profile::countme::get_all()); | |
44 | } | |
45 | ||
46 | if let Some(file_id) = file_id { | |
47 | format_to!(buf, "\nFile info:\n"); | |
48 | let crates = crate::parent_module::crate_for(db, file_id); | |
49 | if crates.is_empty() { | |
50 | format_to!(buf, "Does not belong to any crate"); | |
51 | } | |
52 | let crate_graph = db.crate_graph(); | |
53 | for krate in crates { | |
54 | let display_crate = |krate: CrateId| match &crate_graph[krate].display_name { | |
55 | Some(it) => format!("{}({:?})", it, krate), | |
56 | None => format!("{:?}", krate), | |
57 | }; | |
58 | format_to!(buf, "Crate: {}\n", display_crate(krate)); | |
59 | let deps = crate_graph[krate] | |
60 | .dependencies | |
61 | .iter() | |
62 | .map(|dep| format!("{}={:?}", dep.name, dep.crate_id)) | |
63 | .format(", "); | |
64 | format_to!(buf, "Dependencies: {}\n", deps); | |
65 | } | |
66 | } | |
67 | ||
68 | buf.trim().to_string() | |
69 | } | |
70 | ||
71 | #[derive(Default)] | |
72 | struct FilesStats { | |
73 | total: usize, | |
74 | size: Bytes, | |
75 | } | |
76 | ||
77 | impl fmt::Display for FilesStats { | |
78 | fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { | |
79 | write!(fmt, "{} of files", self.size) | |
80 | } | |
81 | } | |
82 | ||
83 | impl FromIterator<TableEntry<FileId, Arc<String>>> for FilesStats { | |
84 | fn from_iter<T>(iter: T) -> FilesStats | |
85 | where | |
86 | T: IntoIterator<Item = TableEntry<FileId, Arc<String>>>, | |
87 | { | |
88 | let mut res = FilesStats::default(); | |
89 | for entry in iter { | |
90 | res.total += 1; | |
91 | res.size += entry.value.unwrap().len(); | |
92 | } | |
93 | res | |
94 | } | |
95 | } | |
96 | ||
97 | #[derive(Default)] | |
98 | pub(crate) struct SyntaxTreeStats { | |
99 | total: usize, | |
100 | pub(crate) retained: usize, | |
101 | } | |
102 | ||
103 | impl fmt::Display for SyntaxTreeStats { | |
104 | fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { | |
105 | write!(fmt, "{} trees, {} preserved", self.total, self.retained) | |
106 | } | |
107 | } | |
108 | ||
109 | impl FromIterator<TableEntry<FileId, Parse<ast::SourceFile>>> for SyntaxTreeStats { | |
110 | fn from_iter<T>(iter: T) -> SyntaxTreeStats | |
111 | where | |
112 | T: IntoIterator<Item = TableEntry<FileId, Parse<ast::SourceFile>>>, | |
113 | { | |
114 | let mut res = SyntaxTreeStats::default(); | |
115 | for entry in iter { | |
116 | res.total += 1; | |
117 | res.retained += entry.value.is_some() as usize; | |
118 | } | |
119 | res | |
120 | } | |
121 | } | |
122 | ||
123 | impl<M> FromIterator<TableEntry<MacroFile, ExpandResult<Option<(Parse<SyntaxNode>, M)>>>> | |
124 | for SyntaxTreeStats | |
125 | { | |
126 | fn from_iter<T>(iter: T) -> SyntaxTreeStats | |
127 | where | |
128 | T: IntoIterator<Item = TableEntry<MacroFile, ExpandResult<Option<(Parse<SyntaxNode>, M)>>>>, | |
129 | { | |
130 | let mut res = SyntaxTreeStats::default(); | |
131 | for entry in iter { | |
132 | res.total += 1; | |
133 | res.retained += entry.value.is_some() as usize; | |
134 | } | |
135 | res | |
136 | } | |
137 | } | |
138 | ||
139 | #[derive(Default)] | |
140 | struct LibrarySymbolsStats { | |
141 | total: usize, | |
142 | size: Bytes, | |
143 | } | |
144 | ||
145 | impl fmt::Display for LibrarySymbolsStats { | |
146 | fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { | |
147 | write!(fmt, "{} of index symbols ({})", self.size, self.total) | |
148 | } | |
149 | } | |
150 | ||
151 | impl FromIterator<TableEntry<SourceRootId, Arc<SymbolIndex>>> for LibrarySymbolsStats { | |
152 | fn from_iter<T>(iter: T) -> LibrarySymbolsStats | |
153 | where | |
154 | T: IntoIterator<Item = TableEntry<SourceRootId, Arc<SymbolIndex>>>, | |
155 | { | |
156 | let mut res = LibrarySymbolsStats::default(); | |
157 | for entry in iter { | |
158 | let symbols = entry.value.unwrap(); | |
159 | res.total += symbols.len(); | |
160 | res.size += symbols.memory_size(); | |
161 | } | |
162 | res | |
163 | } | |
164 | } |