|  | /* | 
|  | * Copyright (C) 2015 The Android Open Source Project | 
|  | * | 
|  | * Licensed under the Apache License, Version 2.0 (the "License"); | 
|  | * you may not use this file except in compliance with the License. | 
|  | * You may obtain a copy of the License at | 
|  | * | 
|  | *      http://www.apache.org/licenses/LICENSE-2.0 | 
|  | * | 
|  | * Unless required by applicable law or agreed to in writing, software | 
|  | * distributed under the License is distributed on an "AS IS" BASIS, | 
|  | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | 
|  | * See the License for the specific language governing permissions and | 
|  | * limitations under the License. | 
|  | */ | 
|  |  | 
|  | #ifndef ART_COMPILER_CFI_TEST_H_ | 
|  | #define ART_COMPILER_CFI_TEST_H_ | 
|  |  | 
|  | #include <memory> | 
|  | #include <sstream> | 
|  | #include <vector> | 
|  |  | 
|  | #include "arch/instruction_set.h" | 
|  | #include "base/enums.h" | 
|  | #include "debug/dwarf/dwarf_test.h" | 
|  | #include "disassembler.h" | 
|  | #include "dwarf/dwarf_constants.h" | 
|  | #include "dwarf/headers.h" | 
|  | #include "gtest/gtest.h" | 
|  | #include "thread.h" | 
|  |  | 
|  | namespace art { | 
|  |  | 
|  | class CFITest : public dwarf::DwarfTest { | 
|  | public: | 
|  | void GenerateExpected(FILE* f, InstructionSet isa, const char* isa_str, | 
|  | ArrayRef<const uint8_t> actual_asm, | 
|  | ArrayRef<const uint8_t> actual_cfi) { | 
|  | std::vector<std::string> lines; | 
|  | // Print the raw bytes. | 
|  | fprintf(f, "static constexpr uint8_t expected_asm_%s[] = {", isa_str); | 
|  | HexDump(f, actual_asm); | 
|  | fprintf(f, "\n};\n"); | 
|  | fprintf(f, "static constexpr uint8_t expected_cfi_%s[] = {", isa_str); | 
|  | HexDump(f, actual_cfi); | 
|  | fprintf(f, "\n};\n"); | 
|  | // Pretty-print CFI opcodes. | 
|  | constexpr bool is64bit = false; | 
|  | dwarf::DebugFrameOpCodeWriter<> initial_opcodes; | 
|  | dwarf::WriteCIE(is64bit, dwarf::Reg(8), initial_opcodes, &debug_frame_data_); | 
|  | std::vector<uintptr_t> debug_frame_patches; | 
|  | dwarf::WriteFDE(is64bit, | 
|  | /* cie_pointer= */ 0, | 
|  | /* code_address= */ 0, | 
|  | actual_asm.size(), | 
|  | actual_cfi, | 
|  | &debug_frame_data_); | 
|  | ReformatCfi(Objdump(false, "-W"), &lines); | 
|  | // Pretty-print assembly. | 
|  | const uint8_t* asm_base = actual_asm.data(); | 
|  | const uint8_t* asm_end = asm_base + actual_asm.size(); | 
|  | auto* opts = new DisassemblerOptions(false, | 
|  | asm_base, | 
|  | asm_end, | 
|  | true, | 
|  | is64bit | 
|  | ? &Thread::DumpThreadOffset<PointerSize::k64> | 
|  | : &Thread::DumpThreadOffset<PointerSize::k32>); | 
|  | std::unique_ptr<Disassembler> disasm(Disassembler::Create(isa, opts)); | 
|  | std::stringstream stream; | 
|  | const uint8_t* base = actual_asm.data() + (isa == InstructionSet::kThumb2 ? 1 : 0); | 
|  | disasm->Dump(stream, base, base + actual_asm.size()); | 
|  | ReformatAsm(&stream, &lines); | 
|  | // Print CFI and assembly interleaved. | 
|  | std::stable_sort(lines.begin(), lines.end(), CompareByAddress); | 
|  | for (const std::string& line : lines) { | 
|  | fprintf(f, "// %s\n", line.c_str()); | 
|  | } | 
|  | fprintf(f, "\n"); | 
|  | } | 
|  |  | 
|  | private: | 
|  | // Helper - get offset just past the end of given string. | 
|  | static size_t FindEndOf(const std::string& str, const char* substr) { | 
|  | size_t pos = str.find(substr); | 
|  | CHECK_NE(std::string::npos, pos); | 
|  | return pos + strlen(substr); | 
|  | } | 
|  |  | 
|  | // Spit to lines and remove raw instruction bytes. | 
|  | static void ReformatAsm(std::stringstream* stream, | 
|  | std::vector<std::string>* output) { | 
|  | std::string line; | 
|  | while (std::getline(*stream, line)) { | 
|  | line = line.substr(0, FindEndOf(line, ": ")) + | 
|  | line.substr(FindEndOf(line, "\t")); | 
|  | size_t pos; | 
|  | while ((pos = line.find("  ")) != std::string::npos) { | 
|  | line = line.replace(pos, 2, " "); | 
|  | } | 
|  | while (!line.empty() && line.back() == ' ') { | 
|  | line.pop_back(); | 
|  | } | 
|  | output->push_back(line); | 
|  | } | 
|  | } | 
|  |  | 
|  | // Find interesting parts of objdump output and prefix the lines with address. | 
|  | static void ReformatCfi(const std::vector<std::string>& lines, | 
|  | std::vector<std::string>* output) { | 
|  | std::string address; | 
|  | for (const std::string& line : lines) { | 
|  | if (line.find("DW_CFA_nop") != std::string::npos) { | 
|  | // Ignore. | 
|  | } else if (line.find("DW_CFA_advance_loc") != std::string::npos) { | 
|  | // The last 8 characters are the address. | 
|  | address = "0x" + line.substr(line.size() - 8); | 
|  | } else if (line.find("DW_CFA_") != std::string::npos) { | 
|  | std::string new_line(line); | 
|  | // "bad register" warning is caused by always using host (x86) objdump. | 
|  | const char* bad_reg = "bad register: "; | 
|  | size_t pos; | 
|  | if ((pos = new_line.find(bad_reg)) != std::string::npos) { | 
|  | new_line = new_line.replace(pos, strlen(bad_reg), ""); | 
|  | } | 
|  | // Remove register names in parentheses since they have x86 names. | 
|  | if ((pos = new_line.find(" (")) != std::string::npos) { | 
|  | new_line = new_line.replace(pos, FindEndOf(new_line, ")") - pos, ""); | 
|  | } | 
|  | // Use the .cfi_ prefix. | 
|  | new_line = ".cfi_" + new_line.substr(FindEndOf(new_line, "DW_CFA_")); | 
|  | output->push_back(address + ": " + new_line); | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | // Compare strings by the address prefix. | 
|  | static bool CompareByAddress(const std::string& lhs, const std::string& rhs) { | 
|  | EXPECT_EQ(lhs[10], ':'); | 
|  | EXPECT_EQ(rhs[10], ':'); | 
|  | return strncmp(lhs.c_str(), rhs.c_str(), 10) < 0; | 
|  | } | 
|  |  | 
|  | // Pretty-print byte array.  12 bytes per line. | 
|  | static void HexDump(FILE* f, ArrayRef<const uint8_t> data) { | 
|  | for (size_t i = 0; i < data.size(); i++) { | 
|  | fprintf(f, i % 12 == 0 ? "\n    " : " ");  // Whitespace. | 
|  | fprintf(f, "0x%02X,", data[i]); | 
|  | } | 
|  | } | 
|  | }; | 
|  |  | 
|  | }  // namespace art | 
|  |  | 
|  | #endif  // ART_COMPILER_CFI_TEST_H_ |