1 // Copyright 2023, VIXL authors
2 // All rights reserved.
3 //
4 // Redistribution and use in source and binary forms, with or without
5 // modification, are permitted provided that the following conditions are met:
6 //
7 // * Redistributions of source code must retain the above copyright notice,
8 // this list of conditions and the following disclaimer.
9 // * Redistributions in binary form must reproduce the above copyright notice,
10 // this list of conditions and the following disclaimer in the documentation
11 // and/or other materials provided with the distribution.
12 // * Neither the name of ARM Limited nor the names of its contributors may be
13 // used to endorse or promote products derived from this software without
14 // specific prior written permission.
15 //
16 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS CONTRIBUTORS "AS IS" AND
17 // ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18 // WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19 // DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
20 // FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21 // DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
22 // SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
23 // CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
24 // OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
25 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26
27 // Tests for the simulator debugger.
28
29 #include <fstream>
30 #include <regex>
31
32 #include "test-runner.h"
33 #include "test-utils.h"
34
35 #include "aarch64/macro-assembler-aarch64.h"
36 #include "aarch64/simulator-aarch64.h"
37 #include "aarch64/test-utils-aarch64.h"
38
39 namespace vixl {
40 namespace aarch64 {
41
42 #define __ masm->
43 #define TEST(name) TEST_(AARCH64_DBG_##name)
44
45 //
46 // Regex for various types of printing/tracing output.
47 //
48
49 // Matches traced/printed general purpose register output from the simulator,
50 // e.g:
51 // "# x0: 0x000000000badbeef"
52 const std::string x_register_trace = "#[\\s]+(x\\d{1,2}|lr|sp): 0x[0-9a-f]+";
53 // Matches traced/printed vector register output from the simulator, e.g:
54 // "# v0: 0x7ff0f0007f80f0017ff0f0007f80f000"
55 const std::string v_register_trace = "#[\\s]+(v\\d{1,2}): 0x[0-9a-f]+";
56
57 #ifdef VIXL_INCLUDE_SIMULATOR_AARCH64
58 // Run tests with the simulator.
59
60 // Generate some basic code which immediately breaks into the debugger.
61 // This serves as a sandbox for all debugger tests to run in.
GenerateDebuggerAsm(MacroAssembler* masm)62 void GenerateDebuggerAsm(MacroAssembler* masm) {
63 // Create a breakpoint here to break into the debugger.
64 __ Brk(0);
65
66 // Do some arithmetic.
67 __ Add(x1, x0, 5);
68 __ Mov(x2, 2);
69 __ Sub(x3, x1, x2);
70
71 __ Ret();
72 }
73
74 // Setup the test environment with the debugger assembler and simulator.
75 #define SETUP() \
76 MacroAssembler masm; \
77 masm.SetCPUFeatures(CPUFeatures::None()); \
78 masm.SetGenerateSimulatorCode(true); \
79 GenerateDebuggerAsm(&masm); \
80 masm.FinalizeCode(); \
81 Instruction* start = masm.GetBuffer()->GetStartAddress<Instruction*>(); \
82 Decoder decoder; \
83 std::istringstream input_stream; \
84 char ostream_filename[] = "/tmp/vixl-test-debugger-XXXXXX"; \
85 FILE* output_stream = fdopen(mkstemp(ostream_filename), "w"); \
86 /* Disassemble the generated code so we can use the addresses later. */ \
87 PrintDisassembler disassembler(output_stream); \
88 disassembler.DisassembleBuffer(start, masm.GetSizeOfCodeGenerated()); \
89 fflush(output_stream); \
90 Simulator simulator(&decoder, output_stream); \
91 simulator.GetDebugger()->SetInputStream(&input_stream); \
92 simulator.SetColouredTrace(Test::coloured_trace()); \
93 simulator.SetCPUFeatures(CPUFeatures::None()); \
94 simulator.SetDebuggerEnabled(true); \
95 /* Setup a map so that commands and their output can be checked. */ \
96 std::unordered_map<std::string, std::string> command_map
97
98 // Add a command to the input stream queue and map its expected output so that
99 // it can be checked at the end of simulation.
100 #define SETUP_CMD(cmd, expected_output) \
101 { \
102 std::string cmd_str(cmd); \
103 cmd_str += "\n"; \
104 std::string exp_out(expected_output); \
105 input_stream.str(input_stream.str() + cmd_str); \
106 command_map.insert({cmd_str, exp_out}); \
107 }
108
109 // Run the simulator.
110 #define RUN() \
111 simulator.RunFrom(start); \
112 fclose(output_stream)
113
114 // Read the output file stream and check that the expected output from each
115 // command is found directly following it.
116 #define CHECK_OUTPUT() \
117 std::ifstream file_stream(ostream_filename); \
118 std::ostringstream ostream; \
119 ostream << file_stream.rdbuf(); \
120 for (const auto& iter : command_map) { \
121 std::string cmd = iter.first; \
122 std::string expected = iter.second; \
123 /* We assume the expected output follows the command that was issued. */ \
124 std::regex regex(cmd + expected); \
125 if (!std::regex_search(ostream.str(), regex)) { \
126 printf("output = \n\"%s\"\n", ostream.str().c_str()); \
127 /* Remove the newlines. */ \
128 cmd.erase(cmd.size() - 1, 1); \
129 std::string err = \
130 cmd + " - failed: \"" + expected + "\" not found in output "; \
131 VIXL_ABORT_WITH_MSG(err.c_str()); \
132 } \
133 } \
134 std::remove(ostream_filename)
135
136 #define GET_INSTRUCTION_ADDRESS(instruction) \
137 GetInstructionAddress(ostream_filename, instruction)
138
139 // Get the address of an instruction from the given filename.
GetInstructionAddress(std::string filename, std::string instruction)140 std::string GetInstructionAddress(std::string filename,
141 std::string instruction) {
142 std::ifstream file_stream(filename);
143 std::ostringstream ostream;
144 ostream << file_stream.rdbuf();
145
146 // Match the instruction string and capture the address of that instruction.
147 // Note: leading 0's are matched but not captured.
148 std::smatch sub_matches;
149 std::string str = ostream.str();
150 std::string regex_str = "(0x)0*([0-9a-f]+) [0-9a-f]+\t\t";
151 regex_str += instruction;
152 std::regex regex(regex_str);
153 if (std::regex_search(str, sub_matches, regex) && sub_matches.size() == 3) {
154 return sub_matches[1].str() + sub_matches[2].str();
155 } else {
156 std::string err = regex_str + " not found in output ";
157 VIXL_ABORT_WITH_MSG(err.c_str());
158 }
159 }
160
161 #endif // VIXL_INCLUDE_SIMULATOR_AARCH64
162
163 } // namespace aarch64
164 } // namespace vixl
165