• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2021-2024 Huawei Device Co., Ltd.
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at
6  *
7  * http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
15 
16 #include "aot_manager.h"
17 #include "aotdump_options.h"
18 #include "class_data_accessor.h"
19 #include "file.h"
20 #include "file-inl.h"
21 #include "mem/arena_allocator.h"
22 #include "mem/gc/gc_types.h"
23 #include "mem/pool_manager.h"
24 #include "method_data_accessor.h"
25 #include "method_data_accessor-inl.h"
26 #include "proto_data_accessor.h"
27 #include "proto_data_accessor-inl.h"
28 #include "runtime/include/class_helper.h"
29 #include "utils/arch.h"
30 #include "utils/bit_memory_region-inl.h"
31 
32 #ifdef PANDA_COMPILER_TARGET_AARCH64
33 #include "aarch64/disasm-aarch64.h"
34 using vixl::aarch64::Decoder;
35 using vixl::aarch64::Disassembler;
36 using vixl::aarch64::Instruction;
37 #endif  // PANDA_COMPILER_TARGET_AARCH64
38 #ifdef PANDA_COMPILER_TARGET_X86_64
39 #include "Zydis/Zydis.h"
40 #endif  // PANDA_COMPILER_TARGET_X86_64
41 
42 #include <fstream>
43 #include <iomanip>
44 #include <elf.h>
45 #include <regex>
46 
47 using namespace ark::compiler;  // NOLINT(*-build-using-namespace)
48 
49 namespace ark::aotdump {
50 
51 class TypePrinter {
52 public:
TypePrinter(panda_file::ProtoDataAccessor & pda,std::ostream & out)53     explicit TypePrinter(panda_file::ProtoDataAccessor &pda, std::ostream &out) : pda_(pda), out_(out) {}
54     NO_COPY_SEMANTIC(TypePrinter);
55     NO_MOVE_SEMANTIC(TypePrinter);
56     ~TypePrinter() = default;
57 
Dump(panda_file::Type type)58     void Dump(panda_file::Type type)
59     {
60         if (!type.IsReference()) {
61             out_ << type;
62         } else {
63             out_ << ClassHelper::GetName(pda_.GetPandaFile().GetStringData(pda_.GetReferenceType(refIdx_++)).data);
64         }
65     }
66 
67 private:
68     panda_file::ProtoDataAccessor &pda_;
69     std::ostream &out_;
70     uint32_t refIdx_ {0};
71 };
72 
73 class PandaFileHelper {
74 public:
PandaFileHelper(const char * fileName)75     explicit PandaFileHelper(const char *fileName) : file_(panda_file::OpenPandaFile(fileName)) {}
76     NO_COPY_SEMANTIC(PandaFileHelper);
77     NO_MOVE_SEMANTIC(PandaFileHelper);
78     ~PandaFileHelper() = default;
79 
GetMethodName(uint32_t id) const80     std::string GetMethodName(uint32_t id) const
81     {
82         if (!file_) {
83             return "-";
84         }
85         auto fileId = panda_file::File::EntityId(id);
86         panda_file::MethodDataAccessor mda(*file_, fileId);
87         panda_file::ProtoDataAccessor pda(*file_, panda_file::MethodDataAccessor::GetProtoId(*file_, fileId));
88         std::ostringstream ss;
89         TypePrinter typePrinter(pda, ss);
90 
91         typePrinter.Dump(pda.GetReturnType());
92         ss << ' ';
93 
94         auto className = ClassHelper::GetName(file_->GetStringData(mda.GetClassId()).data);
95         ss << className << "::" << file_->GetStringData(mda.GetNameId()).data;
96 
97         ss << '(';
98         bool firstArg = true;
99         // inject class name as the first argument of non static method for consitency with ark::Method::GetFullName
100         if (!mda.IsStatic()) {
101             firstArg = false;
102             ss << className;
103         }
104         for (uint32_t argIdx = 0; argIdx < pda.GetNumArgs(); ++argIdx) {
105             if (!firstArg) {
106                 ss << ", ";
107             }
108             firstArg = false;
109             typePrinter.Dump(pda.GetArgType(argIdx));
110         }
111         ss << ')';
112         return ss.str();
113     }
114 
GetClassName(uint32_t id) const115     std::string GetClassName(uint32_t id) const
116     {
117         if (!file_) {
118             return "-";
119         }
120         return ClassHelper::GetName(file_->GetStringData(panda_file::File::EntityId(id)).data);
121     }
122 
123 private:
124     std::unique_ptr<const panda_file::File> file_;
125 };
126 
127 class AotDump {
128 public:
AotDump(ark::ArenaAllocator * allocator)129     explicit AotDump(ark::ArenaAllocator *allocator) : allocator_(allocator) {}
130     NO_COPY_SEMANTIC(AotDump);
131     NO_MOVE_SEMANTIC(AotDump);
132     ~AotDump() = default;
133 
Run(int argc,const char * argv[])134     int Run(int argc, const char *argv[])  // NOLINT(modernize-avoid-c-arrays)
135     {
136         ark::PandArgParser paParser;
137         PandArg<std::string> inputFile {"input_file", "", "Input file path"};
138         paParser.EnableTail();
139         paParser.PushBackTail(&inputFile);
140         std::array<char, NAME_MAX> tmpfileBuf {"/tmp/fixed_aot_XXXXXX"};
141         // Remove temporary file at the function exit
142         auto finalizer = [](const char *fileName) {
143             if (fileName != nullptr) {
144                 remove(fileName);
145             }
146         };
147         std::unique_ptr<const char, decltype(finalizer)> tempFileRemover(nullptr, finalizer);
148 
149         ark::Span<const char *> sp(argv, argc);
150         ark::aotdump::Options options(sp[0]);
151         options.AddOptions(&paParser);
152         options_ = &options;
153         if (!paParser.Parse(argc, argv)) {
154             std::cerr << "Parse options failed: " << paParser.GetErrorString() << std::endl;
155             return -1;
156         }
157         if (inputFile.GetValue().empty()) {
158             std::cerr << "Please specify input file\n";
159             return -1;
160         }
161         Expected<std::unique_ptr<AotFile>, std::string> aotRes;
162         // Fix elf header for cross platform files. Cross opening is available only in X86_64 arch.
163         if (RUNTIME_ARCH == Arch::X86_64) {
164             if (!FixElfHeader(tmpfileBuf, inputFile)) {
165                 return -1;
166             }
167             tempFileRemover.reset(tmpfileBuf.data());
168             aotRes = AotFile::Open(tmpfileBuf.data(), 0, true);
169         } else {
170             aotRes = AotFile::Open(inputFile.GetValue(), 0, true);
171         }
172         if (!aotRes) {
173             std::cerr << "Open AOT file failed: " << aotRes.Error() << std::endl;
174             return -1;
175         }
176         aotFile_ = std::move(aotRes.Value());
177         DumpAll();
178         return 0;
179     }
180 
DumpAll()181     void DumpAll()
182     {
183         std::ofstream outFstream;
184         if (options_->WasSetOutputFile()) {
185             outFstream.open(options_->GetOutputFile());
186             stream_ = &outFstream;
187         } else {
188             stream_ = &std::cerr;
189         }
190         DumpHeader();
191         DumpFiles();
192         stream_->flush();
193         aotFile_.reset();
194     }
195 
FixElfHeader(std::array<char,NAME_MAX> & tmpfileBuf,PandArg<std::string> & inputFile)196     bool FixElfHeader(std::array<char, NAME_MAX> &tmpfileBuf, PandArg<std::string> &inputFile)
197     {
198         int fd = mkstemp(tmpfileBuf.data());
199         if (fd == -1) {
200             std::cerr << "Failed to open temporary file\n";
201             return false;
202         }
203         close(fd);
204         std::ofstream ostm(tmpfileBuf.data(), std::ios::binary);
205         std::ifstream istm(inputFile.GetValue(), std::ios::binary);
206         if (!ostm.is_open() || !istm.is_open()) {
207             std::cerr << "Cannot open tmpfile or input file\n";
208             return false;
209         }
210         std::vector<char> buffer(std::istreambuf_iterator<char>(istm), {});
211         auto *header = reinterpret_cast<Elf64_Ehdr *>(buffer.data());
212         header->e_machine = EM_X86_64;
213         ostm.write(buffer.data(), buffer.size());
214         return true;
215     }
216 
DumpHeader()217     void DumpHeader()
218     {
219         auto aotHeader = aotFile_->GetAotHeader();
220         (*stream_) << "header:" << std::endl;
221         (*stream_) << "  magic: " << aotHeader->magic.data() << std::endl;
222         (*stream_) << "  version: " << aotHeader->version.data() << std::endl;
223         (*stream_) << "  filename: " << aotFile_->GetFileName() << std::endl;
224         (*stream_) << "  cmdline: " << aotFile_->GetCommandLine() << std::endl;
225         (*stream_) << "  checksum: " << aotHeader->checksum << std::endl;
226         (*stream_) << "  env checksum: " << aotHeader->environmentChecksum << std::endl;
227         (*stream_) << "  arch: " << GetArchString(static_cast<Arch>(aotHeader->arch)) << std::endl;
228         (*stream_) << "  gc_type: " << mem::GCStringFromType(static_cast<mem::GCType>(aotHeader->gcType)) << std::endl;
229         (*stream_) << "  files_count: " << aotHeader->filesCount << std::endl;
230         (*stream_) << "  files_offset: " << aotHeader->filesOffset << std::endl;
231         (*stream_) << "  classes_offset: " << aotHeader->classesOffset << std::endl;
232         (*stream_) << "  methods_offset: " << aotHeader->methodsOffset << std::endl;
233         (*stream_) << "  bitmap_offset: " << aotHeader->bitmapOffset << std::endl;
234         (*stream_) << "  strtab_offset: " << aotHeader->strtabOffset << std::endl;
235     }
236 
DumpClassHashTable(ark::compiler::AotPandaFile & aotPandaFile,PandaFileHelper & pfile)237     void DumpClassHashTable(ark::compiler::AotPandaFile &aotPandaFile, PandaFileHelper &pfile)
238     {
239         (*stream_) << "  class_hash_table:" << std::endl;
240         constexpr int ALIGN_SIZE = 32;
241         (*stream_) << std::left << std::setfill(' ') << std::setw(ALIGN_SIZE) << "i" << std::left << std::setfill(' ')
242                    << std::setw(ALIGN_SIZE) << "next_pos";
243         (*stream_) << std::left << std::setfill(' ') << std::setw(ALIGN_SIZE) << "entity_id_offset" << std::left
244                    << std::setfill(' ') << std::setw(ALIGN_SIZE) << "descriptor" << std::endl;
245         auto classHashTable = aotPandaFile.GetClassHashTable();
246         auto hashTableSize = classHashTable.size();
247         for (size_t i = 0; i < hashTableSize; i++) {
248             auto entityPair = classHashTable[i];
249             if (entityPair.descriptorHash != 0) {
250                 auto descriptor = pfile.GetClassName(entityPair.entityIdOffset);
251                 (*stream_) << std::left << std::setfill(' ') << std::setw(ALIGN_SIZE) << (i + 1);
252                 (*stream_) << std::left << std::setfill(' ') << std::dec << std::setw(ALIGN_SIZE) << entityPair.nextPos;
253                 (*stream_) << std::left << std::setfill(' ') << std::dec << std::setw(ALIGN_SIZE)
254                            << entityPair.entityIdOffset;
255                 (*stream_) << std::left << std::setfill(' ') << std::setw(ALIGN_SIZE) << descriptor << std::endl;
256             } else {
257                 (*stream_) << std::left << std::setfill(' ') << std::setw(ALIGN_SIZE) << (i + 1) << std::endl;
258             }
259         }
260     }
261 
DumpFiles()262     void DumpFiles()
263     {
264         (*stream_) << "files:" << std::endl;
265         for (decltype(auto) fileHeader : aotFile_->FileHeaders()) {
266             AotPandaFile aotPandaFile(aotFile_.get(), &fileHeader);
267             auto fileName = aotFile_->GetString(fileHeader.fileNameStr);
268             PandaFileHelper pfile(fileName);
269             (*stream_) << "- name: " << fileName << std::endl;
270             (*stream_) << "  classes:\n";
271             for (decltype(auto) classHeader : aotPandaFile.GetClassHeaders()) {
272                 AotClass klass(aotFile_.get(), &classHeader);
273                 (*stream_) << "  - class_id: " << classHeader.classId << std::endl;
274                 (*stream_) << "    name: " << pfile.GetClassName(classHeader.classId) << std::endl;
275                 auto methodsBitmap = klass.GetBitmap();
276                 BitMemoryRegion rgn(methodsBitmap.data(), methodsBitmap.size());
277                 (*stream_) << "    methods_bitmap: " << rgn << std::endl;
278                 (*stream_) << "    methods:\n";
279                 for (decltype(auto) methodHeader : klass.GetMethodHeaders()) {
280                     DumpMethodHeader(pfile, aotPandaFile, methodHeader);
281                 }
282             }
283             DumpClassHashTable(aotPandaFile, pfile);
284         }
285     }
286 
DumpMethodHeader(const PandaFileHelper & pfile,const AotPandaFile & aotPandaFile,const MethodHeader & methodHeader)287     void DumpMethodHeader(const PandaFileHelper &pfile, const AotPandaFile &aotPandaFile,
288                           const MethodHeader &methodHeader)
289     {
290         auto methodName = pfile.GetMethodName(methodHeader.methodId);
291         if (options_->WasSetMethodRegex() && !methodName.empty()) {
292             static std::regex rgx(options_->GetMethodRegex());
293             if (!std::regex_match(methodName, rgx)) {
294                 return;
295             }
296         }
297         (*stream_) << "    - id: " << std::dec << methodHeader.methodId << std::endl;
298         (*stream_) << "      name: " << methodName << std::endl;
299         (*stream_) << "      code_offset: 0x" << std::hex << methodHeader.codeOffset << std::dec << std::endl;
300         (*stream_) << "      code_size: " << methodHeader.codeSize << std::endl;
301         auto codeInfo = aotPandaFile.GetMethodCodeInfo(&methodHeader);
302         if (options_->GetShowCode() == "disasm") {
303             (*stream_) << "      code: |\n";
304             PrintCode("        ", codeInfo, pfile);
305         }
306     }
307 
PrintCode(const char * prefix,const CodeInfo & codeInfo,const PandaFileHelper & pfile) const308     void PrintCode(const char *prefix, const CodeInfo &codeInfo, const PandaFileHelper &pfile) const
309     {
310         Arch arch = static_cast<Arch>(aotFile_->GetAotHeader()->arch);
311         switch (arch) {
312 #ifdef PANDA_COMPILER_TARGET_AARCH64
313             case Arch::AARCH64:
314                 return PrintCodeArm64(prefix, codeInfo, pfile);
315 #endif  // PANDA_COMPILER_TARGET_AARCH64
316 #ifdef PANDA_COMPILER_TARGET_X86_64
317             case Arch::X86_64:
318                 return PrintCodeX8664(prefix, codeInfo, pfile);
319 #endif  // PANDA_COMPILER_TARGET_X86_64
320             default:
321                 (*stream_) << prefix << "Unsupported target arch: " << GetArchString(arch) << std::endl;
322                 break;
323         }
324     }
325 
326 #ifdef PANDA_COMPILER_TARGET_AARCH64
PrintCodeArm64(const char * prefix,const CodeInfo & codeInfo,const PandaFileHelper & pfile) const327     void PrintCodeArm64(const char *prefix, const CodeInfo &codeInfo, const PandaFileHelper &pfile) const
328     {
329         Span<const uint8_t> code = codeInfo.GetCodeSpan();
330 
331         Decoder decoder(allocator_);
332         Disassembler disasm(allocator_);
333         decoder.AppendVisitor(&disasm);
334         auto startInstr = reinterpret_cast<const Instruction *>(code.data());
335         auto endInstr = reinterpret_cast<const Instruction *>(code.end());
336         uint32_t pc = 0;
337 
338         // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
339         for (auto instr = startInstr; instr < endInstr; instr += vixl::aarch64::kInstructionSize) {
340             auto stackmap = codeInfo.FindStackMapForNativePc(pc, Arch::AARCH64);
341             if (stackmap.IsValid()) {
342                 PrintStackmap(prefix, codeInfo, stackmap, Arch::AARCH64, pfile);
343             }
344             decoder.Decode(instr);
345             (*stream_) << prefix << std::hex << std::setw(8U) << std::setfill('0') << instr - startInstr << ": "
346                        << disasm.GetOutput() << std::endl;
347             pc += vixl::aarch64::kInstructionSize;
348         }
349         (*stream_) << std::dec;
350     }
351 #endif  // PANDA_COMPILER_TARGET_AARCH64
352 #ifdef PANDA_COMPILER_TARGET_X86_64
PrintCodeX8664(const char * prefix,const CodeInfo & codeInfo,const PandaFileHelper & pfile) const353     void PrintCodeX8664(const char *prefix, const CodeInfo &codeInfo, const PandaFileHelper &pfile) const
354     {
355         Span<const uint8_t> code = codeInfo.GetCodeSpan();
356         constexpr size_t LENGTH = ZYDIS_MAX_INSTRUCTION_LENGTH;  // 15 bytes is max inst length in amd64
357         size_t codeSize = code.size();
358 
359         // Initialize decoder context
360         ZydisDecoder decoder;
361         if (!ZYAN_SUCCESS(ZydisDecoderInit(&decoder, ZYDIS_MACHINE_MODE_LONG_64, ZYDIS_ADDRESS_WIDTH_64))) {
362             LOG(FATAL, AOT) << "ZydisDecoderInit failed";
363         }
364 
365         // Initialize formatter
366         ZydisFormatter formatter;
367         if (!ZYAN_SUCCESS(ZydisFormatterInit(&formatter, ZYDIS_FORMATTER_STYLE_INTEL))) {
368             LOG(FATAL, AOT) << "ZydisFormatterInit failed";
369         }
370 
371         for (size_t pos = 0; pos < codeSize;) {
372             ZydisDecodedInstruction instruction;
373             constexpr auto BUF_SIZE = 256;
374             std::array<char, BUF_SIZE> buffer;  // NOLINT(cppcoreguidelines-pro-type-member-init)
375             auto len = std::min(LENGTH, codeSize - pos);
376             if (!ZYAN_SUCCESS(ZydisDecoderDecodeBuffer(&decoder, &code[pos], len, &instruction))) {
377                 LOG(FATAL, AOT) << "ZydisDecoderDecodeBuffer failed";
378             }
379             if (!ZYAN_SUCCESS(ZydisFormatterFormatInstruction(&formatter, &instruction, buffer.data(), buffer.size(),
380                                                               uintptr_t(&code[pos])))) {
381                 LOG(FATAL, AOT) << "ZydisFormatterFormatInstruction failed";
382             }
383             auto stackmap = codeInfo.FindStackMapForNativePc(pos, Arch::X86_64);
384             if (stackmap.IsValid()) {
385                 PrintStackmap(prefix, codeInfo, stackmap, Arch::X86_64, pfile);
386             }
387             (*stream_) << prefix << std::hex << std::setw(8U) << std::setfill('0') << pos << ": " << buffer.data()
388                        << std::endl;
389             pos += instruction.length;
390         }
391     }
392 #endif  // PANDA_COMPILER_TARGET_X86_64
393 
PrintStackmap(const char * prefix,const CodeInfo & codeInfo,const StackMap & stackmap,Arch arch,const PandaFileHelper & pfile) const394     void PrintStackmap(const char *prefix, const CodeInfo &codeInfo, const StackMap &stackmap, Arch arch,
395                        const PandaFileHelper &pfile) const
396     {
397         (*stream_) << prefix << "          ";
398         codeInfo.Dump(*stream_, stackmap, arch);
399         (*stream_) << std::endl;
400         if (stackmap.HasInlineInfoIndex()) {
401             for (auto ii : const_cast<CodeInfo &>(codeInfo).GetInlineInfos(stackmap)) {
402                 (*stream_) << prefix << "          ";
403                 codeInfo.DumpInlineInfo(*stream_, stackmap, ii.GetRow() - stackmap.GetInlineInfoIndex());
404                 auto id =
405                     const_cast<CodeInfo &>(codeInfo).GetMethod(stackmap, ii.GetRow() - stackmap.GetInlineInfoIndex());
406                 (*stream_) << ", method: " << pfile.GetMethodName(std::get<uint32_t>(id));
407                 (*stream_) << std::endl;
408             }
409         }
410     }
411 
412 private:
413     [[maybe_unused]] ark::ArenaAllocator *allocator_;
414     std::ostream *stream_ {};
415     ark::aotdump::Options *options_ {};
416     std::unique_ptr<AotFile> aotFile_ {};
417 };
418 
419 }  // namespace ark::aotdump
420 
main(int argc,const char * argv[])421 int main(int argc, const char *argv[])
422 {
423     // NOLINTBEGIN(readability-magic-numbers)
424     ark::mem::MemConfig::Initialize(ark::operator""_MB(64ULL), ark::operator""_MB(64ULL), ark::operator""_MB(64ULL),
425                                     ark::operator""_MB(32ULL), 0, 0);
426     // NOLINTEND(readability-magic-numbers)
427     ark::PoolManager::Initialize();
428     auto allocator = new ark::ArenaAllocator(ark::SpaceType::SPACE_TYPE_COMPILER);
429     ark::aotdump::AotDump aotdump(allocator);
430 
431     auto result = aotdump.Run(argc, argv);
432 
433     delete allocator;
434     ark::PoolManager::Finalize();
435     ark::mem::MemConfig::Finalize();
436     return result;
437 }
438