blob: b02d380c1c37de4abfafc93c00ad8329ff3ba9cf [file] [log] [blame]
// Copyright 2023, VIXL authors
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//
// * Redistributions of source code must retain the above copyright notice,
// this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above copyright notice,
// this list of conditions and the following disclaimer in the documentation
// and/or other materials provided with the distribution.
// * Neither the name of ARM Limited nor the names of its contributors may be
// used to endorse or promote products derived from this software without
// specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS CONTRIBUTORS "AS IS" AND
// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// Tests for the simulator debugger.
#include <fstream>
#include <regex>
#include "test-runner.h"
#include "test-utils.h"
#include "aarch64/macro-assembler-aarch64.h"
#include "aarch64/simulator-aarch64.h"
#include "aarch64/test-utils-aarch64.h"
namespace vixl {
namespace aarch64 {
#define __ masm->
#define TEST(name) TEST_(AARCH64_DBG_##name)
//
// Regex for various types of printing/tracing output.
//
// Matches traced/printed general purpose register output from the simulator,
// e.g:
// "# x0: 0x000000000badbeef"
const std::string x_register_trace = "#[\\s]+(x\\d{1,2}|lr|sp): 0x[0-9a-f]+";
// Matches traced/printed vector register output from the simulator, e.g:
// "# v0: 0x7ff0f0007f80f0017ff0f0007f80f000"
const std::string v_register_trace = "#[\\s]+(v\\d{1,2}): 0x[0-9a-f]+";
#ifdef VIXL_INCLUDE_SIMULATOR_AARCH64
// Run tests with the simulator.
// Generate some basic code which immediately breaks into the debugger.
// This serves as a sandbox for all debugger tests to run in.
void GenerateDebuggerAsm(MacroAssembler* masm) {
// Create a breakpoint here to break into the debugger.
__ Brk(0);
// Do some arithmetic.
__ Add(x1, x0, 5);
__ Mov(x2, 2);
__ Sub(x3, x1, x2);
__ Ret();
}
// Setup the test environment with the debugger assembler and simulator.
#define SETUP() \
MacroAssembler masm; \
masm.SetCPUFeatures(CPUFeatures::None()); \
masm.SetGenerateSimulatorCode(true); \
GenerateDebuggerAsm(&masm); \
masm.FinalizeCode(); \
Instruction* start = masm.GetBuffer()->GetStartAddress<Instruction*>(); \
Decoder decoder; \
std::istringstream input_stream; \
char ostream_filename[] = "/tmp/vixl-test-debugger-XXXXXX"; \
FILE* output_stream = fdopen(mkstemp(ostream_filename), "w"); \
/* Disassemble the generated code so we can use the addresses later. */ \
PrintDisassembler disassembler(output_stream); \
disassembler.DisassembleBuffer(start, masm.GetSizeOfCodeGenerated()); \
fflush(output_stream); \
Simulator simulator(&decoder, output_stream); \
simulator.GetDebugger()->SetInputStream(&input_stream); \
simulator.SetColouredTrace(Test::coloured_trace()); \
simulator.SetCPUFeatures(CPUFeatures::None()); \
simulator.SetDebuggerEnabled(true); \
/* Setup a map so that commands and their output can be checked. */ \
std::unordered_map<std::string, std::string> command_map
// Add a command to the input stream queue and map its expected output so that
// it can be checked at the end of simulation.
#define SETUP_CMD(cmd, expected_output) \
{ \
std::string cmd_str(cmd); \
cmd_str += "\n"; \
std::string exp_out(expected_output); \
input_stream.str(input_stream.str() + cmd_str); \
command_map.insert({cmd_str, exp_out}); \
}
// Run the simulator.
#define RUN() \
simulator.RunFrom(start); \
fclose(output_stream)
// Read the output file stream and check that the expected output from each
// command is found directly following it.
#define CHECK_OUTPUT() \
std::ifstream file_stream(ostream_filename); \
std::ostringstream ostream; \
ostream << file_stream.rdbuf(); \
for (const auto& iter : command_map) { \
std::string cmd = iter.first; \
std::string expected = iter.second; \
/* We assume the expected output follows the command that was issued. */ \
std::regex regex(cmd + expected); \
if (!std::regex_search(ostream.str(), regex)) { \
printf("output = \n\"%s\"\n", ostream.str().c_str()); \
/* Remove the newlines. */ \
cmd.erase(cmd.size() - 1, 1); \
std::string err = \
cmd + " - failed: \"" + expected + "\" not found in output "; \
VIXL_ABORT_WITH_MSG(err.c_str()); \
} \
} \
std::remove(ostream_filename)
#define GET_INSTRUCTION_ADDRESS(instruction) \
GetInstructionAddress(ostream_filename, instruction)
// Get the address of an instruction from the given filename.
std::string GetInstructionAddress(std::string filename,
std::string instruction) {
std::ifstream file_stream(filename);
std::ostringstream ostream;
ostream << file_stream.rdbuf();
// Match the instruction string and capture the address of that instruction.
// Note: leading 0's are matched but not captured.
std::smatch sub_matches;
std::string str = ostream.str();
std::string regex_str = "(0x)0*([0-9a-f]+) [0-9a-f]+\t\t";
regex_str += instruction;
std::regex regex(regex_str);
if (std::regex_search(str, sub_matches, regex) && sub_matches.size() == 3) {
return sub_matches[1].str() + sub_matches[2].str();
} else {
std::string err = regex_str + " not found in output ";
VIXL_ABORT_WITH_MSG(err.c_str());
}
}
#endif // VIXL_INCLUDE_SIMULATOR_AARCH64
} // namespace aarch64
} // namespace vixl