• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2021-2023 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 panda::compiler;  // NOLINT(google-build-using-namespace)
48 
49 namespace panda::aoutdump {
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 panda::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(panda::ArenaAllocator * allocator)129     explicit AotDump(panda::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         panda::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         panda::Span<const char *> sp(argv, argc);
150         panda::aoutdump::Options options(sp[0]);
151         options.AddOptions(&paParser);
152         if (!paParser.Parse(argc, argv)) {
153             std::cerr << "Parse options failed: " << paParser.GetErrorString() << std::endl;
154             return -1;
155         }
156         if (inputFile.GetValue().empty()) {
157             std::cerr << "Please specify input file\n";
158             return -1;
159         }
160         Expected<std::unique_ptr<AotFile>, std::string> aotRes;
161         // Fix elf header for cross platform files. Cross opening is available only in X86_64 arch.
162         if (RUNTIME_ARCH == Arch::X86_64) {
163             if (!FixElfHeader(tmpfileBuf, inputFile)) {
164                 return -1;
165             }
166             tempFileRemover.reset(tmpfileBuf.data());
167             aotRes = AotFile::Open(tmpfileBuf.data(), 0, true);
168         } else {
169             aotRes = AotFile::Open(inputFile.GetValue(), 0, true);
170         }
171         if (!aotRes) {
172             std::cerr << "Open AOT file failed: " << aotRes.Error() << std::endl;
173             return -1;
174         }
175 
176         DumpAll(std::move(aotRes.Value()), options);
177         return 0;
178     }
179 
DumpAll(std::unique_ptr<AotFile> aotFile,panda::aoutdump::Options options)180     void DumpAll(std::unique_ptr<AotFile> aotFile, panda::aoutdump::Options options)
181     {
182         std::ostream *outputStream;
183         std::ofstream outFstream;
184         if (options.WasSetOutputFile()) {
185             outFstream.open(options.GetOutputFile());
186             outputStream = &outFstream;
187         } else {
188             outputStream = &std::cerr;
189         }
190         auto &stream = *outputStream;
191         DumpHeader(stream, aotFile);
192         DumpFiles(stream, aotFile, options);
193     }
194 
FixElfHeader(std::array<char,NAME_MAX> & tmpfileBuf,PandArg<std::string> & inputFile)195     bool FixElfHeader(std::array<char, NAME_MAX> &tmpfileBuf, PandArg<std::string> &inputFile)
196     {
197         int fd = mkstemp(tmpfileBuf.data());
198         if (fd == -1) {
199             std::cerr << "Failed to open temporary file\n";
200             return false;
201         }
202         close(fd);
203         std::ofstream ostm(tmpfileBuf.data(), std::ios::binary);
204         std::ifstream istm(inputFile.GetValue(), std::ios::binary);
205         if (!ostm.is_open() || !istm.is_open()) {
206             std::cerr << "Cannot open tmpfile or input file\n";
207             return false;
208         }
209         std::vector<char> buffer(std::istreambuf_iterator<char>(istm), {});
210         auto *header = reinterpret_cast<Elf64_Ehdr *>(buffer.data());
211         header->e_machine = EM_X86_64;
212         ostm.write(buffer.data(), buffer.size());
213         return true;
214     }
215 
DumpHeader(std::ostream & stream,std::unique_ptr<panda::compiler::AotFile> & aotFile)216     void DumpHeader(std::ostream &stream, std::unique_ptr<panda::compiler::AotFile> &aotFile)
217     {
218         auto aotHeader = aotFile->GetAotHeader();
219         stream << "header:" << std::endl;
220         stream << "  magic: " << aotHeader->magic.data() << std::endl;
221         stream << "  version: " << aotHeader->version.data() << std::endl;
222         stream << "  filename: " << aotFile->GetFileName() << std::endl;
223         stream << "  cmdline: " << aotFile->GetCommandLine() << std::endl;
224         stream << "  checksum: " << aotHeader->checksum << std::endl;
225         stream << "  env checksum: " << aotHeader->environmentChecksum << std::endl;
226         stream << "  arch: " << GetArchString(static_cast<Arch>(aotHeader->arch)) << std::endl;
227         stream << "  gc_type: " << mem::GCStringFromType(static_cast<mem::GCType>(aotHeader->gcType)) << std::endl;
228         stream << "  files_count: " << aotHeader->filesCount << std::endl;
229         stream << "  files_offset: " << aotHeader->filesOffset << std::endl;
230         stream << "  classes_offset: " << aotHeader->classesOffset << std::endl;
231         stream << "  methods_offset: " << aotHeader->methodsOffset << std::endl;
232         stream << "  bitmap_offset: " << aotHeader->bitmapOffset << std::endl;
233         stream << "  strtab_offset: " << aotHeader->strtabOffset << std::endl;
234     }
235 
DumpClassHashTable(std::ostream & stream,panda::compiler::AotPandaFile & aotPandaFile,PandaFileHelper & pfile)236     void DumpClassHashTable(std::ostream &stream, panda::compiler::AotPandaFile &aotPandaFile, PandaFileHelper &pfile)
237     {
238         stream << "  class_hash_table:" << std::endl;
239         constexpr int ALIGN_SIZE = 32;
240         stream << std::left << std::setfill(' ') << std::setw(ALIGN_SIZE) << "i" << std::left << std::setfill(' ')
241                << std::setw(ALIGN_SIZE) << "next_pos";
242         stream << std::left << std::setfill(' ') << std::setw(ALIGN_SIZE) << "entity_id_offset" << std::left
243                << std::setfill(' ') << std::setw(ALIGN_SIZE) << "descriptor" << std::endl;
244         auto classHashTable = aotPandaFile.GetClassHashTable();
245         auto hashTableSize = classHashTable.size();
246         for (size_t i = 0; i < hashTableSize; i++) {
247             auto entityPair = classHashTable[i];
248             if (entityPair.descriptorHash != 0) {
249                 auto descriptor = pfile.GetClassName(entityPair.entityIdOffset);
250                 stream << std::left << std::setfill(' ') << std::setw(ALIGN_SIZE) << (i + 1);
251                 stream << std::left << std::setfill(' ') << std::dec << std::setw(ALIGN_SIZE) << entityPair.nextPos;
252                 stream << std::left << std::setfill(' ') << std::dec << std::setw(ALIGN_SIZE)
253                        << entityPair.entityIdOffset;
254                 stream << std::left << std::setfill(' ') << std::setw(ALIGN_SIZE) << descriptor << std::endl;
255             } else {
256                 stream << std::left << std::setfill(' ') << std::setw(ALIGN_SIZE) << (i + 1) << std::endl;
257             }
258         }
259     }
260 
DumpFiles(std::ostream & stream,std::unique_ptr<panda::compiler::AotFile> & aotFile,panda::aoutdump::Options & options)261     void DumpFiles(std::ostream &stream, std::unique_ptr<panda::compiler::AotFile> &aotFile,
262                    panda::aoutdump::Options &options)
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                     auto methodName = pfile.GetMethodName(methodHeader.methodId);
281                     if (options.WasSetMethodRegex() && !methodName.empty()) {
282                         static std::regex rgx(options.GetMethodRegex());
283                         if (!std::regex_match(methodName, rgx)) {
284                             continue;
285                         }
286                     }
287                     stream << "    - id: " << std::dec << methodHeader.methodId << std::endl;
288                     stream << "      name: " << methodName << std::endl;
289                     stream << "      code_offset: 0x" << std::hex << methodHeader.codeOffset << std::dec << std::endl;
290                     stream << "      code_size: " << methodHeader.codeSize << std::endl;
291                     auto codeInfo = aotPandaFile.GetMethodCodeInfo(&methodHeader);
292                     if (options.GetShowCode() == "disasm") {
293                         stream << "      code: |\n";
294                         PrintCode("        ", *aotFile, codeInfo, stream, pfile);
295                     }
296                 }
297             }
298             DumpClassHashTable(stream, aotPandaFile, pfile);
299         }
300     }
301 
PrintCode(const char * prefix,const AotFile & aotFile,const CodeInfo & codeInfo,std::ostream & stream,const PandaFileHelper & pfile) const302     void PrintCode(const char *prefix, const AotFile &aotFile, const CodeInfo &codeInfo, std::ostream &stream,
303                    const PandaFileHelper &pfile) const
304     {
305         Arch arch = static_cast<Arch>(aotFile.GetAotHeader()->arch);
306         switch (arch) {
307 #ifdef PANDA_COMPILER_TARGET_AARCH64
308             case Arch::AARCH64:
309                 return PrintCodeArm64(prefix, codeInfo, stream, pfile);
310 #endif  // PANDA_COMPILER_TARGET_AARCH64
311 #ifdef PANDA_COMPILER_TARGET_X86_64
312             case Arch::X86_64:
313                 return PrintCodeX8664(prefix, codeInfo, stream, pfile);
314 #endif  // PANDA_COMPILER_TARGET_X86_64
315             default:
316                 stream << prefix << "Unsupported target arch: " << GetArchString(arch) << std::endl;
317                 break;
318         }
319     }
320 
321 #ifdef PANDA_COMPILER_TARGET_AARCH64
PrintCodeArm64(const char * prefix,const CodeInfo & codeInfo,std::ostream & stream,const PandaFileHelper & pfile) const322     void PrintCodeArm64(const char *prefix, const CodeInfo &codeInfo, std::ostream &stream,
323                         const PandaFileHelper &pfile) const
324     {
325         Span<const uint8_t> code = codeInfo.GetCodeSpan();
326 
327         Decoder decoder(allocator_);
328         Disassembler disasm(allocator_);
329         decoder.AppendVisitor(&disasm);
330         auto startInstr = reinterpret_cast<const Instruction *>(code.data());
331         auto endInstr = reinterpret_cast<const Instruction *>(code.end());
332         uint32_t pc = 0;
333 
334         // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
335         for (auto instr = startInstr; instr < endInstr; instr += vixl::aarch64::kInstructionSize) {
336             auto stackmap = codeInfo.FindStackMapForNativePc(pc, Arch::AARCH64);
337             if (stackmap.IsValid()) {
338                 PrintStackmap(stream, prefix, codeInfo, stackmap, Arch::AARCH64, pfile);
339             }
340             decoder.Decode(instr);
341             stream << prefix << std::hex << std::setw(8U) << std::setfill('0') << instr - startInstr << ": "
342                    << disasm.GetOutput() << std::endl;
343             pc += vixl::aarch64::kInstructionSize;
344         }
345         stream << std::dec;
346     }
347 #endif  // PANDA_COMPILER_TARGET_AARCH64
348 #ifdef PANDA_COMPILER_TARGET_X86_64
PrintCodeX8664(const char * prefix,const CodeInfo & codeInfo,std::ostream & stream,const PandaFileHelper & pfile) const349     void PrintCodeX8664(const char *prefix, const CodeInfo &codeInfo, std::ostream &stream,
350                         const PandaFileHelper &pfile) const
351     {
352         Span<const uint8_t> code = codeInfo.GetCodeSpan();
353         constexpr size_t LENGTH = ZYDIS_MAX_INSTRUCTION_LENGTH;  // 15 bytes is max inst length in amd64
354         size_t codeSize = code.size();
355 
356         // Initialize decoder context
357         ZydisDecoder decoder;
358         if (!ZYAN_SUCCESS(ZydisDecoderInit(&decoder, ZYDIS_MACHINE_MODE_LONG_64, ZYDIS_ADDRESS_WIDTH_64))) {
359             LOG(FATAL, AOT) << "ZydisDecoderInit failed";
360         }
361 
362         // Initialize formatter
363         ZydisFormatter formatter;
364         if (!ZYAN_SUCCESS(ZydisFormatterInit(&formatter, ZYDIS_FORMATTER_STYLE_INTEL))) {
365             LOG(FATAL, AOT) << "ZydisFormatterInit failed";
366         }
367 
368         for (size_t pos = 0; pos < codeSize;) {
369             ZydisDecodedInstruction instruction;
370             constexpr auto BUF_SIZE = 256;
371             std::array<char, BUF_SIZE> buffer;  // NOLINT(cppcoreguidelines-pro-type-member-init)
372             auto len = std::min(LENGTH, codeSize - pos);
373             if (!ZYAN_SUCCESS(ZydisDecoderDecodeBuffer(&decoder, &code[pos], len, &instruction))) {
374                 LOG(FATAL, AOT) << "ZydisDecoderDecodeBuffer failed";
375             }
376             if (!ZYAN_SUCCESS(ZydisFormatterFormatInstruction(&formatter, &instruction, buffer.data(), buffer.size(),
377                                                               uintptr_t(&code[pos])))) {
378                 LOG(FATAL, AOT) << "ZydisFormatterFormatInstruction failed";
379             }
380             auto stackmap = codeInfo.FindStackMapForNativePc(pos, Arch::X86_64);
381             if (stackmap.IsValid()) {
382                 PrintStackmap(stream, prefix, codeInfo, stackmap, Arch::X86_64, pfile);
383             }
384             stream << prefix << std::hex << std::setw(8U) << std::setfill('0') << pos << ": " << buffer.data()
385                    << std::endl;
386             pos += instruction.length;
387         }
388     }
389 #endif  // PANDA_COMPILER_TARGET_X86_64
390 
PrintStackmap(std::ostream & stream,const char * prefix,const CodeInfo & codeInfo,const StackMap & stackmap,Arch arch,const PandaFileHelper & pfile) const391     void PrintStackmap(std::ostream &stream, const char *prefix, const CodeInfo &codeInfo, const StackMap &stackmap,
392                        Arch arch, const PandaFileHelper &pfile) const
393     {
394         stream << prefix << "          ";
395         codeInfo.Dump(stream, stackmap, arch);
396         stream << std::endl;
397         if (stackmap.HasInlineInfoIndex()) {
398             for (auto ii : const_cast<CodeInfo &>(codeInfo).GetInlineInfos(stackmap)) {
399                 stream << prefix << "          ";
400                 codeInfo.DumpInlineInfo(stream, stackmap, ii.GetRow() - stackmap.GetInlineInfoIndex());
401                 auto id =
402                     const_cast<CodeInfo &>(codeInfo).GetMethod(stackmap, ii.GetRow() - stackmap.GetInlineInfoIndex());
403                 stream << ", method: " << pfile.GetMethodName(std::get<uint32_t>(id));
404                 stream << std::endl;
405             }
406         }
407     }
408 
409 private:
410     [[maybe_unused]] panda::ArenaAllocator *allocator_;
411 };
412 
413 }  // namespace panda::aoutdump
414 
main(int argc,const char * argv[])415 int main(int argc, const char *argv[])
416 {
417     // NOLINTBEGIN(readability-magic-numbers)
418     panda::mem::MemConfig::Initialize(panda::operator""_MB(64ULL), panda::operator""_MB(64ULL),
419                                       panda::operator""_MB(64ULL), panda::operator""_MB(32ULL), 0, 0);
420     // NOLINTEND(readability-magic-numbers)
421     panda::PoolManager::Initialize();
422     auto allocator = new panda::ArenaAllocator(panda::SpaceType::SPACE_TYPE_COMPILER);
423     panda::aoutdump::AotDump aotdump(allocator);
424 
425     auto result = aotdump.Run(argc, argv);
426 
427     delete allocator;
428     panda::PoolManager::Finalize();
429     panda::mem::MemConfig::Finalize();
430     return result;
431 }
432