• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /**
2  * Copyright (c) 2021-2022 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;
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(ref_idx_++)).data);
64         }
65     }
66 
67 private:
68     panda_file::ProtoDataAccessor &pda_;
69     std::ostream &out_;
70     uint32_t ref_idx_ {0};
71 };
72 
73 class PandaFileHelper {
74 public:
PandaFileHelper(const char * file_name)75     explicit PandaFileHelper(const char *file_name) : file_(panda_file::OpenPandaFile(file_name)) {}
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 file_id = panda_file::File::EntityId(id);
86         panda_file::MethodDataAccessor mda(*file_, file_id);
87         panda_file::ProtoDataAccessor pda(*file_, panda_file::MethodDataAccessor::GetProtoId(*file_, file_id));
88         std::ostringstream ss;
89         TypePrinter type_printer(pda, ss);
90 
91         type_printer.Dump(pda.GetReturnType());
92         ss << ' ';
93 
94         auto class_name = ClassHelper::GetName(file_->GetStringData(mda.GetClassId()).data);
95         ss << class_name << "::" << file_->GetStringData(mda.GetNameId()).data;
96 
97         ss << '(';
98         bool first_arg = true;
99         // inject class name as the first argument of non static method for consitency with panda::Method::GetFullName
100         if (!mda.IsStatic()) {
101             first_arg = false;
102             ss << class_name;
103         }
104         for (uint32_t arg_idx = 0; arg_idx < pda.GetNumArgs(); ++arg_idx) {
105             if (!first_arg) {
106                 ss << ", ";
107             }
108             first_arg = false;
109             type_printer.Dump(pda.GetArgType(arg_idx));
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[])
135     {
136         panda::PandArgParser pa_parser;
137         PandArg<std::string> input_file {"input_file", "", "Input file path"};
138         pa_parser.EnableTail();
139         pa_parser.PushBackTail(&input_file);
140         std::array<char, NAME_MAX> tmpfile_buf {"/tmp/fixed_aot_XXXXXX"};
141         // Remove temporary file at the function exit
142         auto finalizer = [](const char *file_name) {
143             if (file_name != nullptr) {
144                 remove(file_name);
145             }
146         };
147         std::unique_ptr<const char, decltype(finalizer)> temp_file_remover(nullptr, finalizer);
148 
149         panda::Span<const char *> sp(argv, argc);
150         panda::aoutdump::Options options(sp[0]);
151         options.AddOptions(&pa_parser);
152         if (!pa_parser.Parse(argc, argv)) {
153             std::cerr << "Parse options failed: " << pa_parser.GetErrorString() << std::endl;
154             return -1;
155         }
156         if (input_file.GetValue().empty()) {
157             std::cerr << "Please specify input file\n";
158             return -1;
159         }
160         Expected<std::unique_ptr<AotFile>, std::string> aot_res;
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(tmpfile_buf, input_file)) {
164                 return -1;
165             }
166             temp_file_remover.reset(tmpfile_buf.data());
167             aot_res = AotFile::Open(tmpfile_buf.data(), 0, true);
168         } else {
169             aot_res = AotFile::Open(input_file.GetValue(), 0, true);
170         }
171         if (!aot_res) {
172             std::cerr << "Open AOT file failed: " << aot_res.Error() << std::endl;
173             return -1;
174         }
175         auto aot_file = std::move(aot_res.Value());
176         std::ostream *output_stream;
177         std::ofstream out_fstream;
178         if (options.WasSetOutputFile()) {
179             out_fstream.open(options.GetOutputFile());
180             output_stream = &out_fstream;
181         } else {
182             output_stream = &std::cerr;
183         }
184         auto &stream = *output_stream;
185         DumpHeader(stream, aot_file);
186         DumpFiles(stream, aot_file, options);
187         return 0;
188     }
189 
FixElfHeader(std::array<char,NAME_MAX> & tmpfile_buf,PandArg<std::string> & input_file)190     bool FixElfHeader(std::array<char, NAME_MAX> &tmpfile_buf, PandArg<std::string> &input_file)
191     {
192         int fd = mkstemp(tmpfile_buf.data());
193         if (fd == -1) {
194             std::cerr << "Failed to open temporary file\n";
195             return false;
196         }
197         close(fd);
198         std::ofstream ostm(tmpfile_buf.data(), std::ios::binary);
199         std::ifstream istm(input_file.GetValue(), std::ios::binary);
200         if (!ostm.is_open() || !istm.is_open()) {
201             std::cerr << "Cannot open tmpfile or input file\n";
202             return false;
203         }
204         std::vector<char> buffer(std::istreambuf_iterator<char>(istm), {});
205         Elf64_Ehdr *header = reinterpret_cast<Elf64_Ehdr *>(buffer.data());
206         header->e_machine = EM_X86_64;
207         ostm.write(buffer.data(), buffer.size());
208         return true;
209     }
210 
DumpHeader(std::ostream & stream,std::unique_ptr<panda::compiler::AotFile> & aot_file)211     void DumpHeader(std::ostream &stream, std::unique_ptr<panda::compiler::AotFile> &aot_file)
212     {
213         auto aot_header = aot_file->GetAotHeader();
214         stream << "header:" << std::endl;
215         stream << "  magic: " << aot_header->magic.data() << std::endl;
216         stream << "  version: " << aot_header->version.data() << std::endl;
217         stream << "  filename: " << aot_file->GetFileName() << std::endl;
218         stream << "  cmdline: " << aot_file->GetCommandLine() << std::endl;
219         stream << "  checksum: " << aot_header->checksum << std::endl;
220         stream << "  env checksum: " << aot_header->environment_checksum << std::endl;
221         stream << "  arch: " << GetArchString(static_cast<Arch>(aot_header->arch)) << std::endl;
222         stream << "  gc_type: " << mem::GCStringFromType(static_cast<mem::GCType>(aot_header->gc_type)) << std::endl;
223         stream << "  files_count: " << aot_header->files_count << std::endl;
224         stream << "  files_offset: " << aot_header->files_offset << std::endl;
225         stream << "  classes_offset: " << aot_header->classes_offset << std::endl;
226         stream << "  methods_offset: " << aot_header->methods_offset << std::endl;
227         stream << "  bitmap_offset: " << aot_header->bitmap_offset << std::endl;
228         stream << "  strtab_offset: " << aot_header->strtab_offset << std::endl;
229     }
230 
DumpClassHashTable(std::ostream & stream,panda::compiler::AotPandaFile & aot_panda_file,PandaFileHelper & pfile)231     void DumpClassHashTable(std::ostream &stream, panda::compiler::AotPandaFile &aot_panda_file, PandaFileHelper &pfile)
232     {
233         stream << "  class_hash_table:" << std::endl;
234         constexpr int ALIGN_SIZE = 32;
235         stream << std::left << std::setfill(' ') << std::setw(ALIGN_SIZE) << "i" << std::left << std::setfill(' ')
236                << std::setw(ALIGN_SIZE) << "next_pos";
237         stream << std::left << std::setfill(' ') << std::setw(ALIGN_SIZE) << "entity_id_offset" << std::left
238                << std::setfill(' ') << std::setw(ALIGN_SIZE) << "descriptor" << std::endl;
239         auto class_hash_table = aot_panda_file.GetClassHashTable();
240         auto hash_table_size = class_hash_table.size();
241         for (size_t i = 0; i < hash_table_size; i++) {
242             auto entity_pair = class_hash_table[i];
243             if (entity_pair.descriptor_hash != 0) {
244                 auto descriptor = pfile.GetClassName(entity_pair.entity_id_offset);
245                 stream << std::left << std::setfill(' ') << std::setw(ALIGN_SIZE) << (i + 1);
246                 stream << std::left << std::setfill(' ') << std::dec << std::setw(ALIGN_SIZE) << entity_pair.next_pos;
247                 stream << std::left << std::setfill(' ') << std::dec << std::setw(ALIGN_SIZE)
248                        << entity_pair.entity_id_offset;
249                 stream << std::left << std::setfill(' ') << std::setw(ALIGN_SIZE) << descriptor << std::endl;
250             } else {
251                 stream << std::left << std::setfill(' ') << std::setw(ALIGN_SIZE) << (i + 1) << std::endl;
252             }
253         }
254     }
255 
DumpFiles(std::ostream & stream,std::unique_ptr<panda::compiler::AotFile> & aot_file,panda::aoutdump::Options & options)256     void DumpFiles(std::ostream &stream, std::unique_ptr<panda::compiler::AotFile> &aot_file,
257                    panda::aoutdump::Options &options)
258     {
259         stream << "files:" << std::endl;
260         for (decltype(auto) file_header : aot_file->FileHeaders()) {
261             AotPandaFile aot_panda_file(aot_file.get(), &file_header);
262             auto file_name = aot_file->GetString(file_header.file_name_str);
263             PandaFileHelper pfile(file_name);
264             stream << "- name: " << file_name << std::endl;
265             stream << "  classes:\n";
266             for (decltype(auto) class_header : aot_panda_file.GetClassHeaders()) {
267                 AotClass klass(aot_file.get(), &class_header);
268                 stream << "  - class_id: " << class_header.class_id << std::endl;
269                 stream << "    name: " << pfile.GetClassName(class_header.class_id) << std::endl;
270                 auto methods_bitmap = klass.GetBitmap();
271                 BitMemoryRegion rgn(methods_bitmap.data(), methods_bitmap.size());
272                 stream << "    methods_bitmap: " << rgn << std::endl;
273                 stream << "    methods:\n";
274                 for (decltype(auto) method_header : klass.GetMethodHeaders()) {
275                     auto method_name = pfile.GetMethodName(method_header.method_id);
276                     if (options.WasSetMethodRegex() && !method_name.empty()) {
277                         static std::regex rgx(options.GetMethodRegex());
278                         if (!std::regex_match(method_name, rgx)) {
279                             continue;
280                         }
281                     }
282                     stream << "    - id: " << std::dec << method_header.method_id << std::endl;
283                     stream << "      name: " << method_name << std::endl;
284                     stream << "      code_offset: 0x" << std::hex << method_header.code_offset << std::dec << std::endl;
285                     stream << "      code_size: " << method_header.code_size << std::endl;
286                     auto code_info = aot_panda_file.GetMethodCodeInfo(&method_header);
287                     if (options.GetShowCode() == "disasm") {
288                         stream << "      code: |\n";
289                         PrintCode("        ", *aot_file, code_info, stream, pfile);
290                     }
291                 }
292             }
293             DumpClassHashTable(stream, aot_panda_file, pfile);
294         }
295     }
296 
PrintCode(const char * prefix,const AotFile & aot_file,const CodeInfo & code_info,std::ostream & stream,const PandaFileHelper & pfile) const297     void PrintCode(const char *prefix, const AotFile &aot_file, const CodeInfo &code_info, std::ostream &stream,
298                    const PandaFileHelper &pfile) const
299     {
300         Arch arch = static_cast<Arch>(aot_file.GetAotHeader()->arch);
301         switch (arch) {
302 #ifdef PANDA_COMPILER_TARGET_AARCH64
303             case Arch::AARCH64:
304                 return PrintCodeArm64(prefix, code_info, stream, pfile);
305 #endif  // PANDA_COMPILER_TARGET_AARCH64
306 #ifdef PANDA_COMPILER_TARGET_X86_64
307             case Arch::X86_64:
308                 return PrintCodeX86_64(prefix, code_info, stream, pfile);
309 #endif  // PANDA_COMPILER_TARGET_X86_64
310             default:
311                 stream << prefix << "Unsupported target arch: " << GetArchString(arch) << std::endl;
312                 break;
313         }
314     }
315 
316 #ifdef PANDA_COMPILER_TARGET_AARCH64
PrintCodeArm64(const char * prefix,const CodeInfo & code_info,std::ostream & stream,const PandaFileHelper & pfile) const317     void PrintCodeArm64(const char *prefix, const CodeInfo &code_info, std::ostream &stream,
318                         const PandaFileHelper &pfile) const
319     {
320         Span<const uint8_t> code = code_info.GetCodeSpan();
321 
322         Decoder decoder(allocator_);
323         Disassembler disasm(allocator_);
324         decoder.AppendVisitor(&disasm);
325         auto start_instr = reinterpret_cast<const Instruction *>(code.data());
326         auto end_instr = reinterpret_cast<const Instruction *>(code.end());
327         uint32_t pc = 0;
328 
329         for (auto instr = start_instr; instr < end_instr; instr += vixl::aarch64::kInstructionSize) {
330             auto stackmap = code_info.FindStackMapForNativePc(pc, Arch::AARCH64);
331             if (stackmap.IsValid()) {
332                 PrintStackmap(stream, prefix, code_info, stackmap, Arch::AARCH64, pfile);
333             }
334             decoder.Decode(instr);
335             stream << prefix << std::hex << std::setw(8U) << std::setfill('0') << instr - start_instr << ": "
336                    << disasm.GetOutput() << std::endl;
337             pc += vixl::aarch64::kInstructionSize;
338         }
339         stream << std::dec;
340     }
341 #endif  // PANDA_COMPILER_TARGET_AARCH64
342 #ifdef PANDA_COMPILER_TARGET_X86_64
PrintCodeX86_64(const char * prefix,const CodeInfo & code_info,std::ostream & stream,const PandaFileHelper & pfile) const343     void PrintCodeX86_64(const char *prefix, const CodeInfo &code_info, std::ostream &stream,
344                          const PandaFileHelper &pfile) const
345     {
346         Span<const uint8_t> code = code_info.GetCodeSpan();
347         constexpr size_t LENGTH = ZYDIS_MAX_INSTRUCTION_LENGTH;  // 15 bytes is max inst length in amd64
348         size_t code_size = code.size();
349 
350         // Initialize decoder context
351         ZydisDecoder decoder;
352         if (!ZYAN_SUCCESS(ZydisDecoderInit(&decoder, ZYDIS_MACHINE_MODE_LONG_64, ZYDIS_ADDRESS_WIDTH_64))) {
353             LOG(FATAL, AOT) << "ZydisDecoderInit failed";
354         }
355 
356         // Initialize formatter
357         ZydisFormatter formatter;
358         if (!ZYAN_SUCCESS(ZydisFormatterInit(&formatter, ZYDIS_FORMATTER_STYLE_INTEL))) {
359             LOG(FATAL, AOT) << "ZydisFormatterInit failed";
360         }
361 
362         for (size_t pos = 0; pos < code_size;) {
363             ZydisDecodedInstruction instruction;
364             constexpr auto BUF_SIZE = 256;
365             std::array<char, BUF_SIZE> buffer;
366             auto len = std::min(LENGTH, code_size - pos);
367             if (!ZYAN_SUCCESS(ZydisDecoderDecodeBuffer(&decoder, &code[pos], len, &instruction))) {
368                 LOG(FATAL, AOT) << "ZydisDecoderDecodeBuffer failed";
369             }
370             if (!ZYAN_SUCCESS(ZydisFormatterFormatInstruction(&formatter, &instruction, buffer.data(), buffer.size(),
371                                                               uintptr_t(&code[pos])))) {
372                 LOG(FATAL, AOT) << "ZydisFormatterFormatInstruction failed";
373             }
374             auto stackmap = code_info.FindStackMapForNativePc(pos, Arch::X86_64);
375             if (stackmap.IsValid()) {
376                 PrintStackmap(stream, prefix, code_info, stackmap, Arch::X86_64, pfile);
377             }
378             stream << prefix << std::hex << std::setw(8U) << std::setfill('0') << pos << ": " << buffer.data()
379                    << std::endl;
380             pos += instruction.length;
381         }
382     }
383 #endif  // PANDA_COMPILER_TARGET_X86_64
384 
PrintStackmap(std::ostream & stream,const char * prefix,const CodeInfo & code_info,const StackMap & stackmap,Arch arch,const PandaFileHelper & pfile) const385     void PrintStackmap(std::ostream &stream, const char *prefix, const CodeInfo &code_info, const StackMap &stackmap,
386                        Arch arch, const PandaFileHelper &pfile) const
387     {
388         stream << prefix << "          ";
389         code_info.Dump(stream, stackmap, arch);
390         stream << std::endl;
391         if (stackmap.HasInlineInfoIndex()) {
392             for (auto ii : const_cast<CodeInfo &>(code_info).GetInlineInfos(stackmap)) {
393                 stream << prefix << "          ";
394                 code_info.DumpInlineInfo(stream, stackmap, ii.GetRow() - stackmap.GetInlineInfoIndex());
395                 auto id =
396                     const_cast<CodeInfo &>(code_info).GetMethod(stackmap, ii.GetRow() - stackmap.GetInlineInfoIndex());
397                 stream << ", method: " << pfile.GetMethodName(std::get<uint32_t>(id));
398                 stream << std::endl;
399             }
400         }
401     }
402 
403 private:
404     [[maybe_unused]] panda::ArenaAllocator *allocator_;
405 };
406 
407 }  // namespace panda::aoutdump
408 
main(int argc,const char * argv[])409 int main(int argc, const char *argv[])
410 {
411     panda::mem::MemConfig::Initialize(panda::operator""_MB(64ULL), panda::operator""_MB(64ULL),
412                                       panda::operator""_MB(64ULL), panda::operator""_MB(32ULL));
413     panda::PoolManager::Initialize();
414     auto allocator = new panda::ArenaAllocator(panda::SpaceType::SPACE_TYPE_COMPILER);
415     panda::aoutdump::AotDump aotdump(allocator);
416 
417     auto result = aotdump.Run(argc, argv);
418 
419     delete allocator;
420     panda::PoolManager::Finalize();
421     panda::mem::MemConfig::Finalize();
422     return result;
423 }
424