feat(sdb): support sdb
This commit is contained in:
parent
8500df8a6e
commit
e828e140cd
22 changed files with 985 additions and 44 deletions
4
npc/utils/CMakeLists.txt
Normal file
4
npc/utils/CMakeLists.txt
Normal file
|
@ -0,0 +1,4 @@
|
|||
add_subdirectory(disasm)
|
||||
if (ENABLE_SDB)
|
||||
add_subdirectory(sdb)
|
||||
endif()
|
4
npc/utils/disasm/CMakeLists.txt
Normal file
4
npc/utils/disasm/CMakeLists.txt
Normal file
|
@ -0,0 +1,4 @@
|
|||
add_library(disasm disasm.cpp)
|
||||
target_include_directories(disasm PUBLIC include)
|
||||
llvm_map_components_to_libnames(LLVM_LIBS ${LLVM_TARGETS_TO_BUILD})
|
||||
target_link_libraries(disasm PUBLIC ${LLVM_LIBS})
|
87
npc/utils/disasm/disasm.cpp
Normal file
87
npc/utils/disasm/disasm.cpp
Normal file
|
@ -0,0 +1,87 @@
|
|||
#include <disasm.hpp>
|
||||
#include <llvm/MC/MCAsmInfo.h>
|
||||
#include <llvm/MC/MCContext.h>
|
||||
#include <llvm/MC/MCDisassembler/MCDisassembler.h>
|
||||
#include <llvm/MC/MCInstPrinter.h>
|
||||
#include <llvm/Support/raw_ostream.h>
|
||||
#if LLVM_VERSION_MAJOR >= 14
|
||||
#include <llvm/MC/TargetRegistry.h>
|
||||
#if LLVM_VERSION_MAJOR >= 15
|
||||
#include <llvm/MC/MCSubtargetInfo.h>
|
||||
#endif
|
||||
#else
|
||||
#include <llvm/Support/TargetRegistry.h>
|
||||
#endif
|
||||
#include <iostream>
|
||||
#include <llvm/Support/TargetSelect.h>
|
||||
#include <sstream>
|
||||
|
||||
#if defined(__GNUC__) && !defined(__clang__)
|
||||
#pragma GCC diagnostic pop
|
||||
#endif
|
||||
|
||||
#if LLVM_VERSION_MAJOR < 11
|
||||
#error Please use LLVM with major version >= 11
|
||||
#endif
|
||||
|
||||
Disassembler::Disassembler(std::string triple) : triple(triple) {
|
||||
llvm::InitializeAllTargetInfos();
|
||||
llvm::InitializeAllTargetMCs();
|
||||
llvm::InitializeAllAsmParsers();
|
||||
llvm::InitializeAllDisassemblers();
|
||||
|
||||
std::string errstr;
|
||||
|
||||
llvm::MCInstrInfo *gMII = nullptr;
|
||||
llvm::MCRegisterInfo *gMRI = nullptr;
|
||||
auto target = llvm::TargetRegistry::lookupTarget(triple, errstr);
|
||||
if (!target) {
|
||||
llvm::errs() << "Can't find target for " << triple << ": " << errstr
|
||||
<< "\n";
|
||||
assert(0);
|
||||
}
|
||||
|
||||
llvm::MCTargetOptions MCOptions;
|
||||
gSTI = target->createMCSubtargetInfo(triple, "", "");
|
||||
std::string isa = target->getName();
|
||||
if (isa == "riscv32" || isa == "riscv64") {
|
||||
gSTI->ApplyFeatureFlag("+m");
|
||||
gSTI->ApplyFeatureFlag("+a");
|
||||
gSTI->ApplyFeatureFlag("+c");
|
||||
gSTI->ApplyFeatureFlag("+f");
|
||||
gSTI->ApplyFeatureFlag("+d");
|
||||
}
|
||||
gMII = target->createMCInstrInfo();
|
||||
gMRI = target->createMCRegInfo(triple);
|
||||
auto AsmInfo = target->createMCAsmInfo(*gMRI, triple, MCOptions);
|
||||
#if LLVM_VERSION_MAJOR >= 13
|
||||
auto llvmTripleTwine = llvm::Twine(triple);
|
||||
auto llvmtriple = llvm::Triple(llvmTripleTwine);
|
||||
auto Ctx = new llvm::MCContext(llvmtriple, AsmInfo, gMRI, nullptr);
|
||||
#else
|
||||
auto Ctx = new llvm::MCContext(AsmInfo, gMRI, nullptr);
|
||||
#endif
|
||||
gDisassembler = target->createMCDisassembler(*gSTI, *Ctx);
|
||||
gIP = target->createMCInstPrinter(llvm::Triple(triple),
|
||||
AsmInfo->getAssemblerDialect(), *AsmInfo,
|
||||
*gMII, *gMRI);
|
||||
gIP->setPrintImmHex(true);
|
||||
gIP->setPrintBranchImmAsAddress(true);
|
||||
if (isa == "riscv32" || isa == "riscv64")
|
||||
gIP->applyTargetSpecificCLOption("no-aliases");
|
||||
}
|
||||
|
||||
std::string Disassembler::disassemble(uint64_t pc, uint8_t *code, int nbyte) {
|
||||
llvm::MCInst inst;
|
||||
llvm::ArrayRef<uint8_t> arr(code, nbyte);
|
||||
uint64_t dummy_size = 0;
|
||||
gDisassembler->getInstruction(inst, dummy_size, arr, pc, llvm::nulls());
|
||||
|
||||
std::stringstream ss;
|
||||
ss << "0x" << std::hex << pc << ": ";
|
||||
std::string s = ss.str();
|
||||
llvm::raw_string_ostream os{s};
|
||||
gIP->printInst(&inst, pc, "", *gSTI, os);
|
||||
|
||||
return s;
|
||||
}
|
17
npc/utils/disasm/include/disasm.hpp
Normal file
17
npc/utils/disasm/include/disasm.hpp
Normal file
|
@ -0,0 +1,17 @@
|
|||
#ifndef _NPC_UTILS_DISASM_
|
||||
#define _NPC_UTILS_DISASM_
|
||||
#include "llvm/MC/MCDisassembler/MCDisassembler.h"
|
||||
#include "llvm/MC/MCInstPrinter.h"
|
||||
|
||||
class Disassembler {
|
||||
llvm::MCDisassembler *gDisassembler = nullptr;
|
||||
llvm::MCSubtargetInfo *gSTI = nullptr;
|
||||
llvm::MCInstPrinter *gIP = nullptr;
|
||||
std::string triple;
|
||||
|
||||
public:
|
||||
Disassembler(std::string);
|
||||
std::string disassemble(uint64_t pc, uint8_t *code, int nbyte);
|
||||
};
|
||||
|
||||
#endif
|
14
npc/utils/sdb/CMakeLists.txt
Normal file
14
npc/utils/sdb/CMakeLists.txt
Normal file
|
@ -0,0 +1,14 @@
|
|||
find_package(Readline REQUIRED)
|
||||
find_package(FLEX REQUIRED)
|
||||
find_package(BISON REQUIRED)
|
||||
set(PARSER_DIR "${CMAKE_CURRENT_BINARY_DIR}")
|
||||
set(LEXER_OUT "${PARSER_DIR}/lexer.c")
|
||||
set(PARSER_OUT "${PARSER_DIR}/parser.c")
|
||||
flex_target(LEXER addrexp.l "${LEXER_OUT}" DEFINES_FILE "${PARSER_DIR}/addrexp_lex.h")
|
||||
bison_target(PARSER addrexp.y "${PARSER_OUT}" DEFINES_FILE "${PARSER_DIR}/addrexp.h")
|
||||
add_flex_bison_dependency(LEXER PARSER)
|
||||
|
||||
add_library(sdb sdb.cpp console.cpp "${LEXER_OUT}" "${PARSER_OUT}")
|
||||
target_link_libraries(sdb PRIVATE ${Readline_LIBRARY})
|
||||
target_include_directories(sdb PRIVATE ${Readline_INCLUDE_DIR})
|
||||
target_include_directories(sdb PUBLIC include)
|
26
npc/utils/sdb/addrexp.l
Normal file
26
npc/utils/sdb/addrexp.l
Normal file
|
@ -0,0 +1,26 @@
|
|||
%{
|
||||
#include <types.h>
|
||||
#include <stdbool.h>
|
||||
#include "addrexp.h"
|
||||
static bool success = false;
|
||||
void yyerror(word_t *result, const char *err);
|
||||
word_t reg_str2val(const char *name, bool*);
|
||||
%}
|
||||
%option noyywrap
|
||||
|
||||
%%
|
||||
|
||||
0[xX][0-9a-fA-F]+ { yylval = strtoul(yytext, NULL, 16); return HEX_NUMBER; }
|
||||
[0-9]+ { yylval = strtoul(yytext, NULL, 10); return NUMBER; }
|
||||
$[asgprt$][0-9pa][0-9]? {
|
||||
yylval = reg_str2val(yytext + 1, &success);
|
||||
if(!success) {
|
||||
yyerror(NULL, "Failed to convert reg to value");
|
||||
return YYerror;
|
||||
}
|
||||
return REGISTER;
|
||||
}
|
||||
[+\-*/<=()] { return *yytext; }
|
||||
[ \t] { }
|
||||
. { printf("Unexpected character: %s\n", yytext); return YYerror; }
|
||||
%%
|
58
npc/utils/sdb/addrexp.y
Normal file
58
npc/utils/sdb/addrexp.y
Normal file
|
@ -0,0 +1,58 @@
|
|||
%code requires {
|
||||
#include <types.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
extern int yylex(void);
|
||||
}
|
||||
%{
|
||||
#include <types.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
void yyerror(word_t *result, const char *err) {
|
||||
fprintf(stderr, "%s", err);
|
||||
}
|
||||
int pmem_read(int raddr);
|
||||
%}
|
||||
|
||||
%token NUMBER HEX_NUMBER
|
||||
%token REGISTER
|
||||
%locations
|
||||
%start input
|
||||
%define api.value.type { word_t }
|
||||
%parse-param { uint32_t *result }
|
||||
%left '-' '+'
|
||||
%left '*' '/'
|
||||
|
||||
%%
|
||||
input
|
||||
: expression { *result = $1; }
|
||||
;
|
||||
|
||||
expression
|
||||
: number { $$ = $1; }
|
||||
| expression '>' '=' expression { $$ = ($1 >= $4); }
|
||||
| expression '<' '=' expression { $$ = ($1 <= $4); }
|
||||
| expression '=' '=' expression { $$ = ($1 == $4); }
|
||||
| expression '!' '=' expression { $$ = ($1 == $4); }
|
||||
| expression '>' expression { $$ = ($1 > $3); }
|
||||
| expression '<' expression { $$ = ($1 < $3); }
|
||||
| expression '+' expression { $$ = $1 + $3; }
|
||||
| expression '-' expression { $$ = $1 - $3; }
|
||||
| expression '*' expression { $$ = $1 * $3; }
|
||||
| expression '/' expression {
|
||||
if($3 == 0) {
|
||||
fprintf(stderr, "Error: divide by zero at" FMT_WORD " / " FMT_WORD "\n", $1, $3);
|
||||
YYABORT;
|
||||
};
|
||||
$$ = $1 / $3;
|
||||
}
|
||||
| '-' number { $$ = -$2; }
|
||||
| '*' expression { $$ = pmem_read($2); }
|
||||
| '(' expression ')' { $$ = $2; }
|
||||
|
||||
number
|
||||
: REGISTER
|
||||
| NUMBER
|
||||
| HEX_NUMBER
|
||||
|
||||
%%
|
215
npc/utils/sdb/console.cpp
Normal file
215
npc/utils/sdb/console.cpp
Normal file
|
@ -0,0 +1,215 @@
|
|||
// cpp-readline library
|
||||
//
|
||||
// @author zmij
|
||||
// @date Nov 30, 2015
|
||||
|
||||
#include <console.hpp>
|
||||
|
||||
#include <algorithm>
|
||||
#include <fstream>
|
||||
#include <functional>
|
||||
#include <iostream>
|
||||
#include <iterator>
|
||||
#include <sstream>
|
||||
#include <unordered_map>
|
||||
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
#include <readline/history.h>
|
||||
#include <readline/readline.h>
|
||||
|
||||
namespace CppReadline {
|
||||
namespace {
|
||||
|
||||
Console *currentConsole = nullptr;
|
||||
HISTORY_STATE *emptyHistory = history_get_history_state();
|
||||
|
||||
} /* namespace */
|
||||
|
||||
struct Console::Impl {
|
||||
using RegisteredCommands =
|
||||
std::unordered_map<std::string, Console::CommandFunction>;
|
||||
|
||||
::std::string greeting_;
|
||||
// These are hardcoded commands. They do not do anything and are catched manually in the executeCommand function.
|
||||
RegisteredCommands commands_;
|
||||
HISTORY_STATE *history_ = nullptr;
|
||||
|
||||
Impl(::std::string const &greeting) : greeting_(greeting), commands_() {}
|
||||
~Impl() { free(history_); }
|
||||
|
||||
Impl(Impl const &) = delete;
|
||||
Impl(Impl &&) = delete;
|
||||
Impl &operator=(Impl const &) = delete;
|
||||
Impl &operator=(Impl &&) = delete;
|
||||
};
|
||||
|
||||
// Here we set default commands, they do nothing since we quit with them
|
||||
// Quitting behaviour is hardcoded in readLine()
|
||||
Console::Console(std::string const &greeting) : pimpl_{new Impl{greeting}} {
|
||||
// Init readline basics
|
||||
rl_attempted_completion_function = &Console::getCommandCompletions;
|
||||
|
||||
// These are default hardcoded commands.
|
||||
// Help command lists available commands.
|
||||
pimpl_->commands_["help"] = [this](const Arguments &) {
|
||||
auto commands = getRegisteredCommands();
|
||||
std::cout << "Available commands are:\n";
|
||||
for (auto &command : commands)
|
||||
std::cout << "\t" << command << "\n";
|
||||
return ReturnCode::Ok;
|
||||
};
|
||||
// Run command executes all commands in an external file.
|
||||
pimpl_->commands_["run"] = [this](const Arguments &input) {
|
||||
if (input.size() < 2) {
|
||||
std::cout << "Usage: " << input[0] << " script_filename\n";
|
||||
return 1;
|
||||
}
|
||||
return executeFile(input[1]);
|
||||
};
|
||||
// Quit and Exit simply terminate the console.
|
||||
pimpl_->commands_["quit"] = [this](const Arguments &) {
|
||||
return ReturnCode::Quit;
|
||||
};
|
||||
|
||||
pimpl_->commands_["exit"] = [this](const Arguments &) {
|
||||
return ReturnCode::Quit;
|
||||
};
|
||||
}
|
||||
|
||||
Console::~Console() = default;
|
||||
|
||||
void Console::registerCommand(const std::string &s, CommandFunction f) {
|
||||
pimpl_->commands_[s] = f;
|
||||
}
|
||||
|
||||
std::vector<std::string> Console::getRegisteredCommands() const {
|
||||
std::vector<std::string> allCommands;
|
||||
for (auto &pair : pimpl_->commands_)
|
||||
allCommands.push_back(pair.first);
|
||||
|
||||
return allCommands;
|
||||
}
|
||||
|
||||
void Console::saveState() {
|
||||
free(pimpl_->history_);
|
||||
pimpl_->history_ = history_get_history_state();
|
||||
}
|
||||
|
||||
void Console::reserveConsole() {
|
||||
if (currentConsole == this)
|
||||
return;
|
||||
|
||||
// Save state of other Console
|
||||
if (currentConsole)
|
||||
currentConsole->saveState();
|
||||
|
||||
// Else we swap state
|
||||
if (!pimpl_->history_)
|
||||
history_set_history_state(emptyHistory);
|
||||
else
|
||||
history_set_history_state(pimpl_->history_);
|
||||
|
||||
// Tell others we are using the console
|
||||
currentConsole = this;
|
||||
}
|
||||
|
||||
void Console::setGreeting(const std::string &greeting) {
|
||||
pimpl_->greeting_ = greeting;
|
||||
}
|
||||
|
||||
std::string Console::getGreeting() const { return pimpl_->greeting_; }
|
||||
|
||||
int Console::executeCommand(const std::string &command) {
|
||||
// Convert input to vector
|
||||
std::vector<std::string> inputs;
|
||||
{
|
||||
std::istringstream iss(command);
|
||||
std::copy(std::istream_iterator<std::string>(iss),
|
||||
std::istream_iterator<std::string>(), std::back_inserter(inputs));
|
||||
}
|
||||
|
||||
if (inputs.size() == 0)
|
||||
return ReturnCode::Ok;
|
||||
|
||||
Impl::RegisteredCommands::iterator it;
|
||||
if ((it = pimpl_->commands_.find(inputs[0])) != end(pimpl_->commands_)) {
|
||||
return static_cast<int>((it->second)(inputs));
|
||||
}
|
||||
|
||||
std::cout << "Command '" << inputs[0] << "' not found.\n";
|
||||
return ReturnCode::Error;
|
||||
}
|
||||
|
||||
int Console::executeFile(const std::string &filename) {
|
||||
std::ifstream input(filename);
|
||||
if (!input) {
|
||||
std::cout << "Could not find the specified file to execute.\n";
|
||||
return ReturnCode::Error;
|
||||
}
|
||||
std::string command;
|
||||
int counter = 0, result;
|
||||
|
||||
while (std::getline(input, command)) {
|
||||
if (command[0] == '#')
|
||||
continue; // Ignore comments
|
||||
// Report what the Console is executing.
|
||||
std::cout << "[" << counter << "] " << command << '\n';
|
||||
if ((result = executeCommand(command)))
|
||||
return result;
|
||||
++counter;
|
||||
std::cout << '\n';
|
||||
}
|
||||
|
||||
// If we arrived successfully at the end, all is ok
|
||||
return ReturnCode::Ok;
|
||||
}
|
||||
|
||||
int Console::readLine() {
|
||||
reserveConsole();
|
||||
|
||||
char *buffer = readline(pimpl_->greeting_.c_str());
|
||||
if (!buffer) {
|
||||
std::cout
|
||||
<< '\n'; // EOF doesn't put last endline so we put that so that it looks uniform.
|
||||
return ReturnCode::Quit;
|
||||
}
|
||||
|
||||
// TODO: Maybe add commands to history only if succeeded?
|
||||
if (buffer[0] != '\0')
|
||||
add_history(buffer);
|
||||
|
||||
std::string line(buffer);
|
||||
free(buffer);
|
||||
|
||||
return executeCommand(line);
|
||||
}
|
||||
|
||||
char **Console::getCommandCompletions(const char *text, int start, int) {
|
||||
char **completionList = nullptr;
|
||||
|
||||
if (start == 0)
|
||||
completionList = rl_completion_matches(text, &Console::commandIterator);
|
||||
|
||||
return completionList;
|
||||
}
|
||||
|
||||
char *Console::commandIterator(const char *text, int state) {
|
||||
static Impl::RegisteredCommands::iterator it;
|
||||
if (!currentConsole)
|
||||
return nullptr;
|
||||
auto &commands = currentConsole->pimpl_->commands_;
|
||||
|
||||
if (state == 0)
|
||||
it = begin(commands);
|
||||
|
||||
while (it != end(commands)) {
|
||||
auto &command = it->first;
|
||||
++it;
|
||||
if (command.find(text) != std::string::npos) {
|
||||
return strdup(command.c_str());
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
} // namespace CppReadline
|
145
npc/utils/sdb/include/console.hpp
Normal file
145
npc/utils/sdb/include/console.hpp
Normal file
|
@ -0,0 +1,145 @@
|
|||
// cpp-readline library
|
||||
//
|
||||
// @author zmij
|
||||
// @date Nov 30, 2015
|
||||
|
||||
#ifndef CONSOLE_CONSOLE_HEADER_FILE
|
||||
#define CONSOLE_CONSOLE_HEADER_FILE
|
||||
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace CppReadline {
|
||||
class Console {
|
||||
public:
|
||||
/**
|
||||
* @brief This is the function type that is used to interface with the Console class.
|
||||
*
|
||||
* These are the functions that are going to get called by Console
|
||||
* when the user types in a message. The vector will hold the
|
||||
* command elements, and the function needs to return its result.
|
||||
* The result can either be Quit (-1), OK (0), or an arbitrary
|
||||
* error (>=1).
|
||||
*/
|
||||
using Arguments = std::vector<std::string>;
|
||||
using CommandFunction = std::function<int(const Arguments &)>;
|
||||
|
||||
enum ReturnCode {
|
||||
Quit = -1,
|
||||
Ok = 0,
|
||||
Error = 1 // Or greater!
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Basic constructor.
|
||||
*
|
||||
* The Console comes with two predefined commands: "quit" and
|
||||
* "exit", which both terminate the console, "help" which prints a
|
||||
* list of all registered commands, and "run" which executes script
|
||||
* files.
|
||||
*
|
||||
* These commands can be overridden or unregistered - but remember
|
||||
* to leave at least one to quit ;).
|
||||
*
|
||||
* @param greeting This represents the prompt of the Console.
|
||||
*/
|
||||
explicit Console(std::string const &greeting);
|
||||
|
||||
/**
|
||||
* @brief Basic destructor.
|
||||
*
|
||||
* Frees the history which is been produced by GNU readline.
|
||||
*/
|
||||
~Console();
|
||||
|
||||
/**
|
||||
* @brief This function registers a new command within the Console.
|
||||
*
|
||||
* If the command already existed, it overwrites the previous entry.
|
||||
*
|
||||
* @param s The name of the command as inserted by the user.
|
||||
* @param f The function that will be called once the user writes the command.
|
||||
*/
|
||||
void registerCommand(const std::string &s, CommandFunction f);
|
||||
|
||||
/**
|
||||
* @brief This function returns a list with the currently available commands.
|
||||
*
|
||||
* @return A vector containing all registered commands names.
|
||||
*/
|
||||
std::vector<std::string> getRegisteredCommands() const;
|
||||
|
||||
/**
|
||||
* @brief Sets the prompt for this Console.
|
||||
*
|
||||
* @param greeting The new greeting.
|
||||
*/
|
||||
void setGreeting(const std::string &greeting);
|
||||
|
||||
/**
|
||||
* @brief Gets the current prompt of this Console.
|
||||
*
|
||||
* @return The currently set greeting.
|
||||
*/
|
||||
std::string getGreeting() const;
|
||||
|
||||
/**
|
||||
* @brief This function executes an arbitrary string as if it was inserted via stdin.
|
||||
*
|
||||
* @param command The command that needs to be executed.
|
||||
*
|
||||
* @return The result of the operation.
|
||||
*/
|
||||
int executeCommand(const std::string &command);
|
||||
|
||||
/**
|
||||
* @brief This function calls an external script and executes all commands inside.
|
||||
*
|
||||
* This function stops execution as soon as any single command returns something
|
||||
* different from 0, be it a quit code or an error code.
|
||||
*
|
||||
* @param filename The pathname of the script.
|
||||
*
|
||||
* @return What the last command executed returned.
|
||||
*/
|
||||
int executeFile(const std::string &filename);
|
||||
|
||||
/**
|
||||
* @brief This function executes a single command from the user via stdin.
|
||||
*
|
||||
* @return The result of the operation.
|
||||
*/
|
||||
int readLine();
|
||||
|
||||
private:
|
||||
Console(const Console &) = delete;
|
||||
Console(Console &&) = delete;
|
||||
Console &operator=(Console const &) = delete;
|
||||
Console &operator=(Console &&) = delete;
|
||||
|
||||
struct Impl;
|
||||
using PImpl = ::std::unique_ptr<Impl>;
|
||||
PImpl pimpl_;
|
||||
|
||||
/**
|
||||
* @brief This function saves the current state so that some other Console can make use of the GNU readline facilities.
|
||||
*/
|
||||
void saveState();
|
||||
/**
|
||||
* @brief This function reserves the use of the GNU readline facilities to the calling Console instance.
|
||||
*/
|
||||
void reserveConsole();
|
||||
|
||||
// GNU newline interface to our commands.
|
||||
using commandCompleterFunction = char **(const char *text, int start,
|
||||
int end);
|
||||
using commandIteratorFunction = char *(const char *text, int state);
|
||||
|
||||
static commandCompleterFunction getCommandCompletions;
|
||||
static commandIteratorFunction commandIterator;
|
||||
};
|
||||
} // namespace CppReadline
|
||||
|
||||
#endif
|
149
npc/utils/sdb/include/sdb.hpp
Normal file
149
npc/utils/sdb/include/sdb.hpp
Normal file
|
@ -0,0 +1,149 @@
|
|||
#ifndef _SDB_SDB_HEADER_FILE_
|
||||
#define _SDB_SDB_HEADER_FILE_
|
||||
|
||||
#include <components.hpp>
|
||||
#include <console.hpp>
|
||||
#include <difftest.hpp>
|
||||
#include <memory>
|
||||
#include <stdexcept>
|
||||
#include <types.h>
|
||||
|
||||
namespace cr = CppReadline;
|
||||
using ret = cr::Console::ReturnCode;
|
||||
|
||||
namespace SDB {
|
||||
|
||||
enum SDBStatus {
|
||||
SDB_SUCCESS,
|
||||
SDB_WRONG_ARGUMENT,
|
||||
};
|
||||
|
||||
struct Handler {
|
||||
const std::vector<const char *> names;
|
||||
cr::Console::CommandFunction f;
|
||||
};
|
||||
|
||||
template <const DifftestInterface &funcs> class _SDBHandlers {
|
||||
using CPUState = CPUStateBase<uint32_t, 32>;
|
||||
|
||||
private:
|
||||
std::vector<Handler> all_handlers;
|
||||
|
||||
public:
|
||||
static CPUState cpu;
|
||||
|
||||
private:
|
||||
static _SDBHandlers<funcs> *instance;
|
||||
static int cmd_continue(const cr::Console::Arguments &input);
|
||||
static int cmd_step(const std::vector<std::string> &input);
|
||||
static int cmd_info_registers(const std::vector<std::string> &input);
|
||||
static int cmd_print(const std::vector<std::string> &input);
|
||||
_SDBHandlers<funcs>(std::vector<Handler> all_handlers)
|
||||
: all_handlers(all_handlers){};
|
||||
|
||||
public:
|
||||
_SDBHandlers<funcs>(const _SDBHandlers<funcs> &) = delete;
|
||||
_SDBHandlers<funcs> operator=(const _SDBHandlers<funcs> &) = delete;
|
||||
|
||||
static _SDBHandlers<funcs> *getInstance() {
|
||||
if (instance == nullptr) {
|
||||
std::vector<Handler> all_handlers{
|
||||
Handler{{"c", "continue"}, &_SDBHandlers::cmd_continue},
|
||||
Handler{{"si", "step-instruction"}, &_SDBHandlers::cmd_step},
|
||||
};
|
||||
instance = new _SDBHandlers<funcs>(all_handlers);
|
||||
}
|
||||
return instance;
|
||||
}
|
||||
|
||||
void registerHandlers(cr::Console *c);
|
||||
};
|
||||
|
||||
template <const DifftestInterface &funcs>
|
||||
_SDBHandlers<funcs> *_SDBHandlers<funcs>::instance = nullptr;
|
||||
|
||||
template <const DifftestInterface &funcs>
|
||||
CPUState _SDBHandlers<funcs>::cpu = CPUState();
|
||||
|
||||
template <const DifftestInterface &funcs>
|
||||
int _SDBHandlers<funcs>::cmd_continue(const cr::Console::Arguments &input) {
|
||||
if (input.size() > 1)
|
||||
return SDB_WRONG_ARGUMENT;
|
||||
funcs.exec(-1);
|
||||
return SDB_SUCCESS;
|
||||
}
|
||||
|
||||
template <const DifftestInterface &funcs>
|
||||
int _SDBHandlers<funcs>::cmd_step(const std::vector<std::string> &input) {
|
||||
if (input.size() > 2) {
|
||||
return SDB_WRONG_ARGUMENT;
|
||||
}
|
||||
uint64_t step_count = input.size() == 2 ? std::stoull(input[1]) : 1;
|
||||
funcs.exec(step_count);
|
||||
return SDB_SUCCESS;
|
||||
}
|
||||
|
||||
template <const DifftestInterface &funcs>
|
||||
int _SDBHandlers<funcs>::cmd_info_registers(
|
||||
const std::vector<std::string> &input) {
|
||||
if (input.size() > 1)
|
||||
return SDB_WRONG_ARGUMENT;
|
||||
std::cout << _SDBHandlers<funcs>::getInstance()->cpu << std::endl;
|
||||
return SDB_SUCCESS;
|
||||
}
|
||||
|
||||
template <const DifftestInterface &funcs>
|
||||
int _SDBHandlers<funcs>::cmd_print(const std::vector<std::string> &input) {
|
||||
exit(1);
|
||||
}
|
||||
|
||||
template <const DifftestInterface &funcs>
|
||||
void _SDBHandlers<funcs>::registerHandlers(cr::Console *c) {
|
||||
for (auto &h : this->all_handlers) {
|
||||
for (auto &name : h.names) {
|
||||
c->registerCommand(name, h.f);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template <const DifftestInterface &funcs> class SDB {
|
||||
private:
|
||||
std::unique_ptr<CppReadline::Console> c;
|
||||
using SDBHandlers = _SDBHandlers<funcs>;
|
||||
|
||||
public:
|
||||
SDB(std::string const &greeting = "\033[1;34m(npc)\033[0m ") {
|
||||
c = std::make_unique<CppReadline::Console>(greeting);
|
||||
SDBHandlers::getInstance()->registerHandlers(c.get());
|
||||
};
|
||||
|
||||
word_t reg_str2val(const char *name, bool *success) {
|
||||
try {
|
||||
*success = true;
|
||||
return SDBHandlers::getInstance()->cpu.at(name);
|
||||
} catch (std::runtime_error) {
|
||||
*success = false;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
int main_loop() {
|
||||
int retCode;
|
||||
do {
|
||||
retCode = c->readLine();
|
||||
// We can also change the prompt based on last return value:
|
||||
if (retCode == ret::Ok)
|
||||
c->setGreeting("\033[1;34m(npc)\033[0m ");
|
||||
else
|
||||
c->setGreeting("\033[1;31m(npc)\033[0m ");
|
||||
|
||||
if (retCode == SDB_WRONG_ARGUMENT) {
|
||||
std::cout << "Wrong argument give to command\n";
|
||||
}
|
||||
} while (retCode != ret::Quit);
|
||||
return 0;
|
||||
}
|
||||
};
|
||||
} // namespace SDB
|
||||
|
||||
#endif
|
10
npc/utils/sdb/sdb.cpp
Normal file
10
npc/utils/sdb/sdb.cpp
Normal file
|
@ -0,0 +1,10 @@
|
|||
#include <components.hpp>
|
||||
#include <console.hpp>
|
||||
#include <difftest.hpp>
|
||||
#include <sdb.hpp>
|
||||
#include <types.h>
|
||||
|
||||
namespace cr = CppReadline;
|
||||
using ret = cr::Console::ReturnCode;
|
||||
|
||||
namespace SDB {}
|
Loading…
Add table
Add a link
Reference in a new issue