1 | //===-- LVReader.cpp ------------------------------------------------------===// |
2 | // |
3 | // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
4 | // See https://llvm.org/LICENSE.txt for license information. |
5 | // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
6 | // |
7 | //===----------------------------------------------------------------------===// |
8 | // |
9 | // This implements the LVReader class. |
10 | // |
11 | //===----------------------------------------------------------------------===// |
12 | |
13 | #include "llvm/DebugInfo/LogicalView/Core/LVReader.h" |
14 | #include "llvm/DebugInfo/LogicalView/Core/LVLine.h" |
15 | #include "llvm/DebugInfo/LogicalView/Core/LVScope.h" |
16 | #include "llvm/Support/FileSystem.h" |
17 | #include "llvm/Support/FormatAdapters.h" |
18 | #include "llvm/Support/FormatVariadic.h" |
19 | #include <tuple> |
20 | |
21 | using namespace llvm; |
22 | using namespace llvm::logicalview; |
23 | |
24 | #define DEBUG_TYPE "Reader" |
25 | |
26 | // Detect elements that are inserted more than once at different scopes, |
27 | // causing a crash on the reader destruction, as the element is already |
28 | // deleted from other scope. Helper for CodeView reader. |
29 | bool checkIntegrityScopesTree(LVScope *Root) { |
30 | using LVDuplicateEntry = std::tuple<LVElement *, LVScope *, LVScope *>; |
31 | using LVDuplicate = std::vector<LVDuplicateEntry>; |
32 | LVDuplicate Duplicate; |
33 | |
34 | using LVIntegrity = std::map<LVElement *, LVScope *>; |
35 | LVIntegrity Integrity; |
36 | |
37 | // Add the given element to the integrity map. |
38 | auto AddElement = [&](LVElement *Element, LVScope *Scope) { |
39 | LVIntegrity::iterator Iter = Integrity.find(x: Element); |
40 | if (Iter == Integrity.end()) |
41 | Integrity.emplace(args&: Element, args&: Scope); |
42 | else |
43 | // We found a duplicate. |
44 | Duplicate.emplace_back(args&: Element, args&: Scope, args&: Iter->second); |
45 | }; |
46 | |
47 | // Recursively add all the elements in the scope. |
48 | std::function<void(LVScope * Parent)> TraverseScope = [&](LVScope *Parent) { |
49 | auto Traverse = [&](const auto *Set) { |
50 | if (Set) |
51 | for (const auto &Entry : *Set) |
52 | AddElement(Entry, Parent); |
53 | }; |
54 | if (const LVScopes *Scopes = Parent->getScopes()) { |
55 | for (LVScope *Scope : *Scopes) { |
56 | AddElement(Scope, Parent); |
57 | TraverseScope(Scope); |
58 | } |
59 | } |
60 | Traverse(Parent->getSymbols()); |
61 | Traverse(Parent->getTypes()); |
62 | Traverse(Parent->getLines()); |
63 | }; |
64 | |
65 | // Start traversing the scopes root and print any duplicates. |
66 | TraverseScope(Root); |
67 | bool PassIntegrity = true; |
68 | if (Duplicate.size()) { |
69 | std::stable_sort(first: begin(cont&: Duplicate), last: end(cont&: Duplicate), |
70 | comp: [](const auto &l, const auto &r) { |
71 | return std::get<0>(l)->getID() < std::get<0>(r)->getID(); |
72 | }); |
73 | |
74 | auto PrintIndex = [](unsigned Index) { |
75 | if (Index) |
76 | dbgs() << format(Fmt: "%8d: " , Vals: Index); |
77 | else |
78 | dbgs() << format(Fmt: "%8c: " , Vals: ' '); |
79 | }; |
80 | auto PrintElement = [&](LVElement *Element, unsigned Index = 0) { |
81 | PrintIndex(Index); |
82 | std::string ElementName(Element->getName()); |
83 | dbgs() << format(Fmt: "%15s ID=0x%08x '%s'\n" , Vals: Element->kind(), |
84 | Vals: Element->getID(), Vals: ElementName.c_str()); |
85 | }; |
86 | |
87 | std::string RootName(Root->getName()); |
88 | dbgs() << formatv(Fmt: "{0}\n" , Vals: fmt_repeat(Item: '=', Count: 72)); |
89 | dbgs() << format(Fmt: "Root: '%s'\nDuplicated elements: %d\n" , Vals: RootName.c_str(), |
90 | Vals: Duplicate.size()); |
91 | dbgs() << formatv(Fmt: "{0}\n" , Vals: fmt_repeat(Item: '=', Count: 72)); |
92 | |
93 | unsigned Index = 0; |
94 | for (const LVDuplicateEntry &Entry : Duplicate) { |
95 | LVElement *Element; |
96 | LVScope *First; |
97 | LVScope *Second; |
98 | std::tie(args&: Element, args&: First, args&: Second) = Entry; |
99 | dbgs() << formatv(Fmt: "\n{0}\n" , Vals: fmt_repeat(Item: '-', Count: 72)); |
100 | PrintElement(Element, ++Index); |
101 | PrintElement(First); |
102 | PrintElement(Second); |
103 | dbgs() << formatv(Fmt: "{0}\n" , Vals: fmt_repeat(Item: '-', Count: 72)); |
104 | } |
105 | PassIntegrity = false; |
106 | } |
107 | return PassIntegrity; |
108 | } |
109 | |
110 | //===----------------------------------------------------------------------===// |
111 | // Class to represent a split context. |
112 | //===----------------------------------------------------------------------===// |
113 | Error LVSplitContext::createSplitFolder(StringRef Where) { |
114 | // The 'location' will represent the root directory for the output created |
115 | // by the context. It will contain the different CUs files, that will be |
116 | // extracted from a single ELF. |
117 | Location = std::string(Where); |
118 | |
119 | // Add a trailing slash, if there is none. |
120 | size_t Pos = Location.find_last_of(c: '/'); |
121 | if (Location.length() != Pos + 1) |
122 | Location.append(s: "/" ); |
123 | |
124 | // Make sure the new directory exists, creating it if necessary. |
125 | if (std::error_code EC = llvm::sys::fs::create_directories(path: Location)) |
126 | return createStringError(EC, Fmt: "Error: could not create directory %s" , |
127 | Vals: Location.c_str()); |
128 | |
129 | return Error::success(); |
130 | } |
131 | |
132 | std::error_code LVSplitContext::open(std::string ContextName, |
133 | std::string Extension, raw_ostream &OS) { |
134 | assert(OutputFile == nullptr && "OutputFile already set." ); |
135 | |
136 | // Transforms '/', '\', '.', ':' into '_'. |
137 | std::string Name(flattenedFilePath(Path: ContextName)); |
138 | Name.append(str: Extension); |
139 | // Add the split context location folder name. |
140 | if (!Location.empty()) |
141 | Name.insert(pos1: 0, str: Location); |
142 | |
143 | std::error_code EC; |
144 | OutputFile = std::make_unique<ToolOutputFile>(args&: Name, args&: EC, args: sys::fs::OF_None); |
145 | if (EC) |
146 | return EC; |
147 | |
148 | // Don't remove output file. |
149 | OutputFile->keep(); |
150 | return std::error_code(); |
151 | } |
152 | |
153 | LVReader *CurrentReader = nullptr; |
154 | LVReader &LVReader::getInstance() { |
155 | if (CurrentReader) |
156 | return *CurrentReader; |
157 | outs() << "Invalid instance reader.\n" ; |
158 | llvm_unreachable("Invalid instance reader." ); |
159 | } |
160 | void LVReader::setInstance(LVReader *Reader) { CurrentReader = Reader; } |
161 | |
162 | Error LVReader::createSplitFolder() { |
163 | if (OutputSplit) { |
164 | // If the '--output=split' was specified, but no '--split-folder' |
165 | // option, use the input file as base for the split location. |
166 | if (options().getOutputFolder().empty()) |
167 | options().setOutputFolder(getFilename().str() + "_cus" ); |
168 | |
169 | SmallString<128> SplitFolder; |
170 | SplitFolder = options().getOutputFolder(); |
171 | sys::fs::make_absolute(path&: SplitFolder); |
172 | |
173 | // Return error if unable to create a split context location. |
174 | if (Error Err = SplitContext.createSplitFolder(Where: SplitFolder)) |
175 | return Err; |
176 | |
177 | OS << "\nSplit View Location: '" << SplitContext.getLocation() << "'\n" ; |
178 | } |
179 | |
180 | return Error::success(); |
181 | } |
182 | |
183 | // Get the filename for given object. |
184 | StringRef LVReader::getFilename(LVObject *Object, size_t Index) const { |
185 | // TODO: The current CodeView Reader implementation does not have support |
186 | // for multiple compile units. Until we have a proper offset calculation, |
187 | // check only in the current compile unit. |
188 | if (CompileUnits.size()) { |
189 | // Get Compile Unit for the given object. |
190 | LVCompileUnits::const_iterator Iter = |
191 | std::prev(x: CompileUnits.lower_bound(x: Object->getOffset())); |
192 | if (Iter != CompileUnits.end()) |
193 | return Iter->second->getFilename(Index); |
194 | } |
195 | |
196 | return CompileUnit ? CompileUnit->getFilename(Index) : StringRef(); |
197 | } |
198 | |
199 | // The Reader is the module that creates the logical view using the debug |
200 | // information contained in the binary file specified in the command line. |
201 | // This is the main entry point for the Reader and performs the following |
202 | // steps: |
203 | // - Process any patterns collected from the '--select' options. |
204 | // - For each compile unit in the debug information: |
205 | // * Create the logical elements (scopes, symbols, types, lines). |
206 | // * Collect debug ranges and debug locations. |
207 | // * Move the collected logical lines to their associated scopes. |
208 | // - Once all the compile units have been processed, traverse the scopes |
209 | // tree in order to: |
210 | // * Calculate symbol coverage. |
211 | // * Detect invalid ranges and locations. |
212 | // * "resolve" the logical elements. During this pass, the names and |
213 | // file information are updated, to reflect any dependency with other |
214 | // logical elements. |
215 | Error LVReader::doLoad() { |
216 | // Set current Reader instance. |
217 | setInstance(this); |
218 | |
219 | // Before any scopes creation, process any pattern specified by the |
220 | // --select and --select-offsets options. |
221 | patterns().addGenericPatterns(Patterns&: options().Select.Generic); |
222 | patterns().addOffsetPatterns(Patterns: options().Select.Offsets); |
223 | |
224 | // Add any specific element printing requests based on the element kind. |
225 | patterns().addRequest(Selection&: options().Select.Elements); |
226 | patterns().addRequest(Selection&: options().Select.Lines); |
227 | patterns().addRequest(Selection&: options().Select.Scopes); |
228 | patterns().addRequest(Selection&: options().Select.Symbols); |
229 | patterns().addRequest(Selection&: options().Select.Types); |
230 | |
231 | // Once we have processed the requests for any particular kind of elements, |
232 | // we need to update the report options, in order to have a default value. |
233 | patterns().updateReportOptions(); |
234 | |
235 | // Delegate the scope tree creation to the specific reader. |
236 | if (Error Err = createScopes()) |
237 | return Err; |
238 | |
239 | if (options().getInternalIntegrity() && !checkIntegrityScopesTree(Root)) |
240 | return llvm::make_error<StringError>(Args: "Duplicated elements in Scopes Tree" , |
241 | Args: inconvertibleErrorCode()); |
242 | |
243 | // Calculate symbol coverage and detect invalid debug locations and ranges. |
244 | Root->processRangeInformation(); |
245 | |
246 | // As the elements can depend on elements from a different compile unit, |
247 | // information such as name and file/line source information needs to be |
248 | // updated. |
249 | Root->resolveElements(); |
250 | |
251 | sortScopes(); |
252 | return Error::success(); |
253 | } |
254 | |
255 | // Default handler for a generic reader. |
256 | Error LVReader::doPrint() { |
257 | // Set current Reader instance. |
258 | setInstance(this); |
259 | |
260 | // Check for any '--report' request. |
261 | if (options().getReportExecute()) { |
262 | // Requested details. |
263 | if (options().getReportList()) |
264 | if (Error Err = printMatchedElements(/*UseMatchedElements=*/true)) |
265 | return Err; |
266 | // Requested only children. |
267 | if (options().getReportChildren() && !options().getReportParents()) |
268 | if (Error Err = printMatchedElements(/*UseMatchedElements=*/false)) |
269 | return Err; |
270 | // Requested (parents) or (parents and children). |
271 | if (options().getReportParents() || options().getReportView()) |
272 | if (Error Err = printScopes()) |
273 | return Err; |
274 | |
275 | return Error::success(); |
276 | } |
277 | |
278 | return printScopes(); |
279 | } |
280 | |
281 | Error LVReader::printScopes() { |
282 | if (bool DoPrint = |
283 | (options().getPrintExecute() || options().getComparePrint())) { |
284 | if (Error Err = createSplitFolder()) |
285 | return Err; |
286 | |
287 | // Start printing from the root. |
288 | bool DoMatch = options().getSelectGenericPattern() || |
289 | options().getSelectGenericKind() || |
290 | options().getSelectOffsetPattern(); |
291 | return Root->doPrint(Split: OutputSplit, Match: DoMatch, Print: DoPrint, OS); |
292 | } |
293 | |
294 | return Error::success(); |
295 | } |
296 | |
297 | Error LVReader::printMatchedElements(bool UseMatchedElements) { |
298 | if (Error Err = createSplitFolder()) |
299 | return Err; |
300 | |
301 | return Root->doPrintMatches(Split: OutputSplit, OS, UseMatchedElements); |
302 | } |
303 | |
304 | void LVReader::print(raw_ostream &OS) const { |
305 | OS << "LVReader\n" ; |
306 | LLVM_DEBUG(dbgs() << "PrintReader\n" ); |
307 | } |
308 | |