11109fb3cacc8bb667979780c2b4b12ce5bb64549David Srbecky/*
21109fb3cacc8bb667979780c2b4b12ce5bb64549David Srbecky * Copyright (C) 2015 The Android Open Source Project
31109fb3cacc8bb667979780c2b4b12ce5bb64549David Srbecky *
41109fb3cacc8bb667979780c2b4b12ce5bb64549David Srbecky * Licensed under the Apache License, Version 2.0 (the "License");
51109fb3cacc8bb667979780c2b4b12ce5bb64549David Srbecky * you may not use this file except in compliance with the License.
61109fb3cacc8bb667979780c2b4b12ce5bb64549David Srbecky * You may obtain a copy of the License at
71109fb3cacc8bb667979780c2b4b12ce5bb64549David Srbecky *
81109fb3cacc8bb667979780c2b4b12ce5bb64549David Srbecky *      http://www.apache.org/licenses/LICENSE-2.0
91109fb3cacc8bb667979780c2b4b12ce5bb64549David Srbecky *
101109fb3cacc8bb667979780c2b4b12ce5bb64549David Srbecky * Unless required by applicable law or agreed to in writing, software
111109fb3cacc8bb667979780c2b4b12ce5bb64549David Srbecky * distributed under the License is distributed on an "AS IS" BASIS,
121109fb3cacc8bb667979780c2b4b12ce5bb64549David Srbecky * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
131109fb3cacc8bb667979780c2b4b12ce5bb64549David Srbecky * See the License for the specific language governing permissions and
141109fb3cacc8bb667979780c2b4b12ce5bb64549David Srbecky * limitations under the License.
151109fb3cacc8bb667979780c2b4b12ce5bb64549David Srbecky */
161109fb3cacc8bb667979780c2b4b12ce5bb64549David Srbecky
171109fb3cacc8bb667979780c2b4b12ce5bb64549David Srbecky#ifndef ART_COMPILER_CFI_TEST_H_
181109fb3cacc8bb667979780c2b4b12ce5bb64549David Srbecky#define ART_COMPILER_CFI_TEST_H_
191109fb3cacc8bb667979780c2b4b12ce5bb64549David Srbecky
201109fb3cacc8bb667979780c2b4b12ce5bb64549David Srbecky#include <vector>
211109fb3cacc8bb667979780c2b4b12ce5bb64549David Srbecky#include <memory>
221109fb3cacc8bb667979780c2b4b12ce5bb64549David Srbecky#include <sstream>
231109fb3cacc8bb667979780c2b4b12ce5bb64549David Srbecky
241109fb3cacc8bb667979780c2b4b12ce5bb64549David Srbecky#include "arch/instruction_set.h"
254fda4eb799c95be266f52aaf3461a440ea86b841David Srbecky#include "debug/dwarf/dwarf_constants.h"
264fda4eb799c95be266f52aaf3461a440ea86b841David Srbecky#include "debug/dwarf/dwarf_test.h"
274fda4eb799c95be266f52aaf3461a440ea86b841David Srbecky#include "debug/dwarf/headers.h"
281109fb3cacc8bb667979780c2b4b12ce5bb64549David Srbecky#include "disassembler/disassembler.h"
291109fb3cacc8bb667979780c2b4b12ce5bb64549David Srbecky#include "gtest/gtest.h"
301109fb3cacc8bb667979780c2b4b12ce5bb64549David Srbecky
311109fb3cacc8bb667979780c2b4b12ce5bb64549David Srbeckynamespace art {
321109fb3cacc8bb667979780c2b4b12ce5bb64549David Srbecky
33ad5fa8c5b26a325dc2a9521b87188755046c17f3David Srbeckyconstexpr dwarf::CFIFormat kCFIFormat = dwarf::DW_DEBUG_FRAME_FORMAT;
34ad5fa8c5b26a325dc2a9521b87188755046c17f3David Srbecky
351109fb3cacc8bb667979780c2b4b12ce5bb64549David Srbeckyclass CFITest : public dwarf::DwarfTest {
361109fb3cacc8bb667979780c2b4b12ce5bb64549David Srbecky public:
371109fb3cacc8bb667979780c2b4b12ce5bb64549David Srbecky  void GenerateExpected(FILE* f, InstructionSet isa, const char* isa_str,
381109fb3cacc8bb667979780c2b4b12ce5bb64549David Srbecky                        const std::vector<uint8_t>& actual_asm,
391109fb3cacc8bb667979780c2b4b12ce5bb64549David Srbecky                        const std::vector<uint8_t>& actual_cfi) {
401109fb3cacc8bb667979780c2b4b12ce5bb64549David Srbecky    std::vector<std::string> lines;
411109fb3cacc8bb667979780c2b4b12ce5bb64549David Srbecky    // Print the raw bytes.
421109fb3cacc8bb667979780c2b4b12ce5bb64549David Srbecky    fprintf(f, "static constexpr uint8_t expected_asm_%s[] = {", isa_str);
431109fb3cacc8bb667979780c2b4b12ce5bb64549David Srbecky    HexDump(f, actual_asm);
441109fb3cacc8bb667979780c2b4b12ce5bb64549David Srbecky    fprintf(f, "\n};\n");
451109fb3cacc8bb667979780c2b4b12ce5bb64549David Srbecky    fprintf(f, "static constexpr uint8_t expected_cfi_%s[] = {", isa_str);
461109fb3cacc8bb667979780c2b4b12ce5bb64549David Srbecky    HexDump(f, actual_cfi);
471109fb3cacc8bb667979780c2b4b12ce5bb64549David Srbecky    fprintf(f, "\n};\n");
481109fb3cacc8bb667979780c2b4b12ce5bb64549David Srbecky    // Pretty-print CFI opcodes.
49b536247b1ce5de640eec81dddac47802cd074363David Srbecky    constexpr bool is64bit = false;
50b536247b1ce5de640eec81dddac47802cd074363David Srbecky    dwarf::DebugFrameOpCodeWriter<> initial_opcodes;
516d8c8f0344a706df651567387ede683ab3ec1b5fDavid Srbecky    dwarf::WriteCIE(is64bit, dwarf::Reg(8),
526d8c8f0344a706df651567387ede683ab3ec1b5fDavid Srbecky                    initial_opcodes, kCFIFormat, &debug_frame_data_);
53ad5fa8c5b26a325dc2a9521b87188755046c17f3David Srbecky    std::vector<uintptr_t> debug_frame_patches;
546d8c8f0344a706df651567387ede683ab3ec1b5fDavid Srbecky    dwarf::WriteFDE(is64bit, 0, 0, 0, actual_asm.size(), ArrayRef<const uint8_t>(actual_cfi),
556d8c8f0344a706df651567387ede683ab3ec1b5fDavid Srbecky                    kCFIFormat, 0, &debug_frame_data_, &debug_frame_patches);
561109fb3cacc8bb667979780c2b4b12ce5bb64549David Srbecky    ReformatCfi(Objdump(false, "-W"), &lines);
571109fb3cacc8bb667979780c2b4b12ce5bb64549David Srbecky    // Pretty-print assembly.
58a6e95b32d499811bbb37602fc7446a5a0d05b9f8Aart Bik    const uint8_t* asm_base = actual_asm.data();
59a6e95b32d499811bbb37602fc7446a5a0d05b9f8Aart Bik    const uint8_t* asm_end = asm_base + actual_asm.size();
60a6e95b32d499811bbb37602fc7446a5a0d05b9f8Aart Bik    auto* opts = new DisassemblerOptions(false, asm_base, asm_end, true);
611109fb3cacc8bb667979780c2b4b12ce5bb64549David Srbecky    std::unique_ptr<Disassembler> disasm(Disassembler::Create(isa, opts));
621109fb3cacc8bb667979780c2b4b12ce5bb64549David Srbecky    std::stringstream stream;
631109fb3cacc8bb667979780c2b4b12ce5bb64549David Srbecky    const uint8_t* base = actual_asm.data() + (isa == kThumb2 ? 1 : 0);
641109fb3cacc8bb667979780c2b4b12ce5bb64549David Srbecky    disasm->Dump(stream, base, base + actual_asm.size());
651109fb3cacc8bb667979780c2b4b12ce5bb64549David Srbecky    ReformatAsm(&stream, &lines);
661109fb3cacc8bb667979780c2b4b12ce5bb64549David Srbecky    // Print CFI and assembly interleaved.
671109fb3cacc8bb667979780c2b4b12ce5bb64549David Srbecky    std::stable_sort(lines.begin(), lines.end(), CompareByAddress);
681109fb3cacc8bb667979780c2b4b12ce5bb64549David Srbecky    for (const std::string& line : lines) {
691109fb3cacc8bb667979780c2b4b12ce5bb64549David Srbecky      fprintf(f, "// %s\n", line.c_str());
701109fb3cacc8bb667979780c2b4b12ce5bb64549David Srbecky    }
711109fb3cacc8bb667979780c2b4b12ce5bb64549David Srbecky    fprintf(f, "\n");
721109fb3cacc8bb667979780c2b4b12ce5bb64549David Srbecky  }
731109fb3cacc8bb667979780c2b4b12ce5bb64549David Srbecky
741109fb3cacc8bb667979780c2b4b12ce5bb64549David Srbecky private:
751109fb3cacc8bb667979780c2b4b12ce5bb64549David Srbecky  // Helper - get offset just past the end of given string.
761109fb3cacc8bb667979780c2b4b12ce5bb64549David Srbecky  static size_t FindEndOf(const std::string& str, const char* substr) {
771109fb3cacc8bb667979780c2b4b12ce5bb64549David Srbecky    size_t pos = str.find(substr);
781109fb3cacc8bb667979780c2b4b12ce5bb64549David Srbecky    CHECK_NE(std::string::npos, pos);
791109fb3cacc8bb667979780c2b4b12ce5bb64549David Srbecky    return pos + strlen(substr);
801109fb3cacc8bb667979780c2b4b12ce5bb64549David Srbecky  }
811109fb3cacc8bb667979780c2b4b12ce5bb64549David Srbecky
821109fb3cacc8bb667979780c2b4b12ce5bb64549David Srbecky  // Spit to lines and remove raw instruction bytes.
831109fb3cacc8bb667979780c2b4b12ce5bb64549David Srbecky  static void ReformatAsm(std::stringstream* stream,
841109fb3cacc8bb667979780c2b4b12ce5bb64549David Srbecky                          std::vector<std::string>* output) {
851109fb3cacc8bb667979780c2b4b12ce5bb64549David Srbecky    std::string line;
861109fb3cacc8bb667979780c2b4b12ce5bb64549David Srbecky    while (std::getline(*stream, line)) {
871109fb3cacc8bb667979780c2b4b12ce5bb64549David Srbecky      line = line.substr(0, FindEndOf(line, ": ")) +
881109fb3cacc8bb667979780c2b4b12ce5bb64549David Srbecky             line.substr(FindEndOf(line, "\t"));
891109fb3cacc8bb667979780c2b4b12ce5bb64549David Srbecky      size_t pos;
901109fb3cacc8bb667979780c2b4b12ce5bb64549David Srbecky      while ((pos = line.find("  ")) != std::string::npos) {
911109fb3cacc8bb667979780c2b4b12ce5bb64549David Srbecky        line = line.replace(pos, 2, " ");
921109fb3cacc8bb667979780c2b4b12ce5bb64549David Srbecky      }
931109fb3cacc8bb667979780c2b4b12ce5bb64549David Srbecky      while (!line.empty() && line.back() == ' ') {
941109fb3cacc8bb667979780c2b4b12ce5bb64549David Srbecky        line.pop_back();
951109fb3cacc8bb667979780c2b4b12ce5bb64549David Srbecky      }
961109fb3cacc8bb667979780c2b4b12ce5bb64549David Srbecky      output->push_back(line);
971109fb3cacc8bb667979780c2b4b12ce5bb64549David Srbecky    }
981109fb3cacc8bb667979780c2b4b12ce5bb64549David Srbecky  }
991109fb3cacc8bb667979780c2b4b12ce5bb64549David Srbecky
1001109fb3cacc8bb667979780c2b4b12ce5bb64549David Srbecky  // Find interesting parts of objdump output and prefix the lines with address.
1011109fb3cacc8bb667979780c2b4b12ce5bb64549David Srbecky  static void ReformatCfi(const std::vector<std::string>& lines,
1021109fb3cacc8bb667979780c2b4b12ce5bb64549David Srbecky                          std::vector<std::string>* output) {
1031109fb3cacc8bb667979780c2b4b12ce5bb64549David Srbecky    std::string address;
1041109fb3cacc8bb667979780c2b4b12ce5bb64549David Srbecky    for (const std::string& line : lines) {
1051109fb3cacc8bb667979780c2b4b12ce5bb64549David Srbecky      if (line.find("DW_CFA_nop") != std::string::npos) {
1061109fb3cacc8bb667979780c2b4b12ce5bb64549David Srbecky        // Ignore.
1071109fb3cacc8bb667979780c2b4b12ce5bb64549David Srbecky      } else if (line.find("DW_CFA_advance_loc") != std::string::npos) {
1081109fb3cacc8bb667979780c2b4b12ce5bb64549David Srbecky        // The last 8 characters are the address.
1091109fb3cacc8bb667979780c2b4b12ce5bb64549David Srbecky        address = "0x" + line.substr(line.size() - 8);
1101109fb3cacc8bb667979780c2b4b12ce5bb64549David Srbecky      } else if (line.find("DW_CFA_") != std::string::npos) {
1111109fb3cacc8bb667979780c2b4b12ce5bb64549David Srbecky        std::string new_line(line);
1121109fb3cacc8bb667979780c2b4b12ce5bb64549David Srbecky        // "bad register" warning is caused by always using host (x86) objdump.
1131109fb3cacc8bb667979780c2b4b12ce5bb64549David Srbecky        const char* bad_reg = "bad register: ";
1141109fb3cacc8bb667979780c2b4b12ce5bb64549David Srbecky        size_t pos;
1151109fb3cacc8bb667979780c2b4b12ce5bb64549David Srbecky        if ((pos = new_line.find(bad_reg)) != std::string::npos) {
1161109fb3cacc8bb667979780c2b4b12ce5bb64549David Srbecky          new_line = new_line.replace(pos, strlen(bad_reg), "");
1171109fb3cacc8bb667979780c2b4b12ce5bb64549David Srbecky        }
1181109fb3cacc8bb667979780c2b4b12ce5bb64549David Srbecky        // Remove register names in parentheses since they have x86 names.
1191109fb3cacc8bb667979780c2b4b12ce5bb64549David Srbecky        if ((pos = new_line.find(" (")) != std::string::npos) {
1201109fb3cacc8bb667979780c2b4b12ce5bb64549David Srbecky          new_line = new_line.replace(pos, FindEndOf(new_line, ")") - pos, "");
1211109fb3cacc8bb667979780c2b4b12ce5bb64549David Srbecky        }
1221109fb3cacc8bb667979780c2b4b12ce5bb64549David Srbecky        // Use the .cfi_ prefix.
1231109fb3cacc8bb667979780c2b4b12ce5bb64549David Srbecky        new_line = ".cfi_" + new_line.substr(FindEndOf(new_line, "DW_CFA_"));
1241109fb3cacc8bb667979780c2b4b12ce5bb64549David Srbecky        output->push_back(address + ": " + new_line);
1251109fb3cacc8bb667979780c2b4b12ce5bb64549David Srbecky      }
1261109fb3cacc8bb667979780c2b4b12ce5bb64549David Srbecky    }
1271109fb3cacc8bb667979780c2b4b12ce5bb64549David Srbecky  }
1281109fb3cacc8bb667979780c2b4b12ce5bb64549David Srbecky
1291109fb3cacc8bb667979780c2b4b12ce5bb64549David Srbecky  // Compare strings by the address prefix.
1301109fb3cacc8bb667979780c2b4b12ce5bb64549David Srbecky  static bool CompareByAddress(const std::string& lhs, const std::string& rhs) {
1311109fb3cacc8bb667979780c2b4b12ce5bb64549David Srbecky    EXPECT_EQ(lhs[10], ':');
1321109fb3cacc8bb667979780c2b4b12ce5bb64549David Srbecky    EXPECT_EQ(rhs[10], ':');
1331109fb3cacc8bb667979780c2b4b12ce5bb64549David Srbecky    return strncmp(lhs.c_str(), rhs.c_str(), 10) < 0;
1341109fb3cacc8bb667979780c2b4b12ce5bb64549David Srbecky  }
1351109fb3cacc8bb667979780c2b4b12ce5bb64549David Srbecky
1361109fb3cacc8bb667979780c2b4b12ce5bb64549David Srbecky  // Pretty-print byte array.  12 bytes per line.
1371109fb3cacc8bb667979780c2b4b12ce5bb64549David Srbecky  static void HexDump(FILE* f, const std::vector<uint8_t>& data) {
1381109fb3cacc8bb667979780c2b4b12ce5bb64549David Srbecky    for (size_t i = 0; i < data.size(); i++) {
1391109fb3cacc8bb667979780c2b4b12ce5bb64549David Srbecky      fprintf(f, i % 12 == 0 ? "\n    " : " ");  // Whitespace.
1401109fb3cacc8bb667979780c2b4b12ce5bb64549David Srbecky      fprintf(f, "0x%02X,", data[i]);
1411109fb3cacc8bb667979780c2b4b12ce5bb64549David Srbecky    }
1421109fb3cacc8bb667979780c2b4b12ce5bb64549David Srbecky  }
1431109fb3cacc8bb667979780c2b4b12ce5bb64549David Srbecky};
1441109fb3cacc8bb667979780c2b4b12ce5bb64549David Srbecky
1451109fb3cacc8bb667979780c2b4b12ce5bb64549David Srbecky}  // namespace art
1461109fb3cacc8bb667979780c2b4b12ce5bb64549David Srbecky
1471109fb3cacc8bb667979780c2b4b12ce5bb64549David Srbecky#endif  // ART_COMPILER_CFI_TEST_H_
148