1 | //===- llvm-cxxmap.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 | // llvm-cxxmap computes a correspondence between old symbol names and new |
10 | // symbol names based on a symbol equivalence file. |
11 | // |
12 | //===----------------------------------------------------------------------===// |
13 | |
14 | #include "llvm/ADT/DenseMap.h" |
15 | #include "llvm/ADT/DenseSet.h" |
16 | #include "llvm/ADT/StringRef.h" |
17 | #include "llvm/ProfileData/SymbolRemappingReader.h" |
18 | #include "llvm/Support/CommandLine.h" |
19 | #include "llvm/Support/FileSystem.h" |
20 | #include "llvm/Support/InitLLVM.h" |
21 | #include "llvm/Support/LineIterator.h" |
22 | #include "llvm/Support/MemoryBuffer.h" |
23 | #include "llvm/Support/WithColor.h" |
24 | #include "llvm/Support/raw_ostream.h" |
25 | |
26 | using namespace llvm; |
27 | |
28 | cl::OptionCategory CXXMapCategory("CXX Map Options" ); |
29 | |
30 | cl::opt<std::string> OldSymbolFile(cl::Positional, cl::Required, |
31 | cl::desc("<symbol-file>" ), |
32 | cl::cat(CXXMapCategory)); |
33 | cl::opt<std::string> NewSymbolFile(cl::Positional, cl::Required, |
34 | cl::desc("<symbol-file>" ), |
35 | cl::cat(CXXMapCategory)); |
36 | cl::opt<std::string> RemappingFile("remapping-file" , cl::Required, |
37 | cl::desc("Remapping file" ), |
38 | cl::cat(CXXMapCategory)); |
39 | cl::alias RemappingFileA("r" , cl::aliasopt(RemappingFile), |
40 | cl::cat(CXXMapCategory)); |
41 | cl::opt<std::string> OutputFilename("output" , cl::value_desc("output" ), |
42 | cl::init(Val: "-" ), cl::desc("Output file" ), |
43 | cl::cat(CXXMapCategory)); |
44 | cl::alias OutputFilenameA("o" , cl::aliasopt(OutputFilename), |
45 | cl::cat(CXXMapCategory)); |
46 | |
47 | cl::opt<bool> WarnAmbiguous( |
48 | "Wambiguous" , |
49 | cl::desc("Warn on equivalent symbols in the output symbol list" ), |
50 | cl::cat(CXXMapCategory)); |
51 | cl::opt<bool> WarnIncomplete( |
52 | "Wincomplete" , |
53 | cl::desc("Warn on input symbols missing from output symbol list" ), |
54 | cl::cat(CXXMapCategory)); |
55 | |
56 | static void warn(Twine Message, Twine Whence = "" , |
57 | std::string Hint = "" ) { |
58 | WithColor::warning(); |
59 | std::string WhenceStr = Whence.str(); |
60 | if (!WhenceStr.empty()) |
61 | errs() << WhenceStr << ": " ; |
62 | errs() << Message << "\n" ; |
63 | if (!Hint.empty()) |
64 | WithColor::note() << Hint << "\n" ; |
65 | } |
66 | |
67 | static void exitWithError(Twine Message, Twine Whence = "" , |
68 | std::string Hint = "" ) { |
69 | WithColor::error(); |
70 | std::string WhenceStr = Whence.str(); |
71 | if (!WhenceStr.empty()) |
72 | errs() << WhenceStr << ": " ; |
73 | errs() << Message << "\n" ; |
74 | if (!Hint.empty()) |
75 | WithColor::note() << Hint << "\n" ; |
76 | ::exit(status: 1); |
77 | } |
78 | |
79 | static void exitWithError(Error E, StringRef Whence = "" ) { |
80 | exitWithError(Message: toString(E: std::move(E)), Whence); |
81 | } |
82 | |
83 | static void exitWithErrorCode(std::error_code EC, StringRef Whence = "" ) { |
84 | exitWithError(Message: EC.message(), Whence); |
85 | } |
86 | |
87 | static void remapSymbols(MemoryBuffer &OldSymbolFile, |
88 | MemoryBuffer &NewSymbolFile, |
89 | MemoryBuffer &RemappingFile, |
90 | raw_ostream &Out) { |
91 | // Load the remapping file and prepare to canonicalize symbols. |
92 | SymbolRemappingReader Reader; |
93 | if (Error E = Reader.read(B&: RemappingFile)) |
94 | exitWithError(E: std::move(E)); |
95 | |
96 | // Canonicalize the new symbols. |
97 | DenseMap<SymbolRemappingReader::Key, StringRef> MappedNames; |
98 | DenseSet<StringRef> UnparseableSymbols; |
99 | for (line_iterator LineIt(NewSymbolFile, /*SkipBlanks=*/true, '#'); |
100 | !LineIt.is_at_eof(); ++LineIt) { |
101 | StringRef Symbol = *LineIt; |
102 | |
103 | auto K = Reader.insert(FunctionName: Symbol); |
104 | if (!K) { |
105 | UnparseableSymbols.insert(V: Symbol); |
106 | continue; |
107 | } |
108 | |
109 | auto ItAndIsNew = MappedNames.insert(KV: {K, Symbol}); |
110 | if (WarnAmbiguous && !ItAndIsNew.second && |
111 | ItAndIsNew.first->second != Symbol) { |
112 | warn(Message: "symbol " + Symbol + " is equivalent to earlier symbol " + |
113 | ItAndIsNew.first->second, |
114 | Whence: NewSymbolFile.getBufferIdentifier() + ":" + |
115 | Twine(LineIt.line_number()), |
116 | Hint: "later symbol will not be the target of any remappings" ); |
117 | } |
118 | } |
119 | |
120 | // Figure out which new symbol each old symbol is equivalent to. |
121 | for (line_iterator LineIt(OldSymbolFile, /*SkipBlanks=*/true, '#'); |
122 | !LineIt.is_at_eof(); ++LineIt) { |
123 | StringRef Symbol = *LineIt; |
124 | |
125 | auto K = Reader.lookup(FunctionName: Symbol); |
126 | StringRef NewSymbol = MappedNames.lookup(Val: K); |
127 | |
128 | if (NewSymbol.empty()) { |
129 | if (WarnIncomplete && !UnparseableSymbols.count(V: Symbol)) { |
130 | warn(Message: "no new symbol matches old symbol " + Symbol, |
131 | Whence: OldSymbolFile.getBufferIdentifier() + ":" + |
132 | Twine(LineIt.line_number())); |
133 | } |
134 | continue; |
135 | } |
136 | |
137 | Out << Symbol << " " << NewSymbol << "\n" ; |
138 | } |
139 | } |
140 | |
141 | int main(int argc, const char *argv[]) { |
142 | InitLLVM X(argc, argv); |
143 | |
144 | cl::HideUnrelatedOptions(Categories: {&CXXMapCategory, &getColorCategory()}); |
145 | cl::ParseCommandLineOptions(argc, argv, Overview: "LLVM C++ mangled name remapper\n" ); |
146 | |
147 | auto OldSymbolBufOrError = |
148 | MemoryBuffer::getFileOrSTDIN(Filename: OldSymbolFile, /*IsText=*/true); |
149 | if (!OldSymbolBufOrError) |
150 | exitWithErrorCode(EC: OldSymbolBufOrError.getError(), Whence: OldSymbolFile); |
151 | |
152 | auto NewSymbolBufOrError = |
153 | MemoryBuffer::getFileOrSTDIN(Filename: NewSymbolFile, /*IsText=*/true); |
154 | if (!NewSymbolBufOrError) |
155 | exitWithErrorCode(EC: NewSymbolBufOrError.getError(), Whence: NewSymbolFile); |
156 | |
157 | auto RemappingBufOrError = |
158 | MemoryBuffer::getFileOrSTDIN(Filename: RemappingFile, /*IsText=*/true); |
159 | if (!RemappingBufOrError) |
160 | exitWithErrorCode(EC: RemappingBufOrError.getError(), Whence: RemappingFile); |
161 | |
162 | std::error_code EC; |
163 | raw_fd_ostream OS(OutputFilename.data(), EC, sys::fs::OF_TextWithCRLF); |
164 | if (EC) |
165 | exitWithErrorCode(EC, Whence: OutputFilename); |
166 | |
167 | remapSymbols(OldSymbolFile&: *OldSymbolBufOrError.get(), NewSymbolFile&: *NewSymbolBufOrError.get(), |
168 | RemappingFile&: *RemappingBufOrError.get(), Out&: OS); |
169 | } |
170 | |