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