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