From 82207f964a416e3f200d89114fba5e55fbda2c4e Mon Sep 17 00:00:00 2001
From: Recolic Keghart <root@recolic.net>
Date: Sun, 15 Dec 2019 03:31:56 +0800
Subject: [PATCH] >  Manual commit:  Finished watchpoints. U201614531 recolic
 Linux RECOLICPC 5.4.2-arch1-1 #1 SMP PREEMPT Thu, 05 Dec 2019 12:29:40 +0000
 x86_64 GNU/Linux  03:31:56 up 3 days, 10:50,  1 user,  load average: 1.28,
 1.79, 1.68 a81d98bac18313b314650ef918308dafb0838b90

---
 nemu/include/common.h                | 10 +++++
 nemu/include/monitor/expr.h          |  2 +-
 nemu/include/monitor/watchpoint.h    | 31 ++++++++++++---
 nemu/src/monitor/cpu-exec.cc         | 19 ++++++++-
 nemu/src/monitor/debug/expr.cc       | 19 ++-------
 nemu/src/monitor/debug/ui.cc         | 58 ++++++++++++++++++++++------
 nemu/src/monitor/debug/watchpoint.cc | 37 +++++++++++-------
 7 files changed, 129 insertions(+), 47 deletions(-)

diff --git a/nemu/include/common.h b/nemu/include/common.h
index 44d6c1a..b6e40c4 100644
--- a/nemu/include/common.h
+++ b/nemu/include/common.h
@@ -16,6 +16,7 @@
 #include <stdint.h>
 #include <assert.h>
 #include <string.h>
+#include <string>
 
 typedef uint32_t rtlreg_t;
 
@@ -27,4 +28,13 @@ typedef uint16_t ioaddr_t;
 #include "debug.h"
 #include "macro.h"
 
+#include <iomanip>
+#include <sstream>
+inline std::string num2hex(uint32_t n) {
+    static std::stringstream ss;
+    ss.str("");
+    ss << "0x" << std::setfill('0') << std::setw(8) << std::hex << n << std::dec;
+    return ss.str();
+}
+
 #endif
diff --git a/nemu/include/monitor/expr.h b/nemu/include/monitor/expr.h
index c240505..b5d5e6e 100644
--- a/nemu/include/monitor/expr.h
+++ b/nemu/include/monitor/expr.h
@@ -3,6 +3,6 @@
 
 #include "common.h"
 
-uint32_t expr(char *, bool *);
+uint32_t evaluate_expr(std::string e);
 
 #endif
diff --git a/nemu/include/monitor/watchpoint.h b/nemu/include/monitor/watchpoint.h
index f0a250f..7d4c0e5 100644
--- a/nemu/include/monitor/watchpoint.h
+++ b/nemu/include/monitor/watchpoint.h
@@ -3,13 +3,34 @@
 
 #include "common.h"
 
-typedef struct watchpoint {
-  int NO;
-  struct watchpoint *next;
+#include <string>
+#include <list>
+#include <iostream>
 
-  /* TODO: Add more members if necessary */
+#include "monitor/expr.h"
 
+struct WP {
+  std::string expr;
+  uint32_t curr_value;
+  int id;
+
+  bool evalulate() {
+    auto new_value = evaluate_expr(expr);
+    std::swap(new_value, curr_value);
+    return new_value != curr_value;
+  }
+  WP(std::string e, int id) : expr(e), id(id) {
+    evalulate(); // initial expr value.
+  }
+};
+
+extern std::list<WP> watchpoints;
+// WARNING: Not thread-safe
+extern int max_watchpoint_id;
+
+inline std::ostream & operator<< (std::ostream &os, const WP &watchpoint) {
+  return os << "watchpoint " << watchpoint.id << "{expr=" << watchpoint.expr << ", value=" << num2hex(watchpoint.curr_value) << "}";
+}
 
-} WP;
 
 #endif
diff --git a/nemu/src/monitor/cpu-exec.cc b/nemu/src/monitor/cpu-exec.cc
index 5c46817..3234777 100644
--- a/nemu/src/monitor/cpu-exec.cc
+++ b/nemu/src/monitor/cpu-exec.cc
@@ -1,5 +1,10 @@
 #include "nemu.h"
 #include "monitor/monitor.h"
+#include "monitor/watchpoint.h"
+
+#include <rlib/stdio.hpp>
+#include <rlib/3rdparty/prettyprint.hpp>
+using namespace rlib::_3rdparty::std;
 
 /* The assembly code of instructions executed is only output to the screen
  * when the number of instructions executed is less than this value.
@@ -40,7 +45,19 @@ void cpu_exec(uint64_t n) {
 
 #ifdef DEBUG
     /* TODO: check watchpoints here. */
-
+    decltype(watchpoints) updated_watchpoints;
+    for(auto &watchpoint : watchpoints) {
+      if(watchpoint.evalulate()) {
+        // Value changed.
+        updated_watchpoints.push_back(watchpoint);
+      }
+    }
+    if(! updated_watchpoints.empty()) {
+      nemu_state = NEMU_STOP;
+      rlib::println("Watchpoint(s) triggered:");
+      rlib::println(updated_watchpoints);
+      return;
+    }
 #endif
 
 #ifdef HAS_IOE
diff --git a/nemu/src/monitor/debug/expr.cc b/nemu/src/monitor/debug/expr.cc
index 9d42827..68fc01c 100644
--- a/nemu/src/monitor/debug/expr.cc
+++ b/nemu/src/monitor/debug/expr.cc
@@ -99,20 +99,7 @@ static bool make_token(char *e) {
   return true;
 }
 
-uint32_t expr(char *e, bool *success) {
-  if (!make_token(e)) {
-    *success = false;
-    return 0;
-  }
-
-  try {
-    auto res = parse_one(std::string(e));
-    *success = true;
-    return res;
-  }
-  catch(std::exception &e) {
-    rlib::println("Exception caught:", e.what());
-    *success = false;
-    return 0;
-  }
+uint32_t evaluate_expr(std::string e) {
+  auto res = parse_one(e);
+  return res;
 }
diff --git a/nemu/src/monitor/debug/ui.cc b/nemu/src/monitor/debug/ui.cc
index 75bc742..253554a 100644
--- a/nemu/src/monitor/debug/ui.cc
+++ b/nemu/src/monitor/debug/ui.cc
@@ -17,6 +17,9 @@ using rlib::string;
 #include <sstream>
 #include <iomanip>
 
+#include <rlib/3rdparty/prettyprint.hpp>
+using namespace rlib::_3rdparty::std;
+
 void cpu_exec(uint64_t);
 
 /* We use the `readline' library to provide more flexibility to read from stdin. */
@@ -49,6 +52,8 @@ static int cmd_n(char *args) {
 
 static int cmd_info(char *args);
 static int cmd_x(char *args);
+static int cmd_w(char *args);
+static int cmd_d(char *args);
 
 static int cmd_q(char *args) {
   return -1;
@@ -64,8 +69,10 @@ static struct {
   { "help", "Display informations about all supported commands", cmd_help },
   { "c", "Continue the execution of the program", cmd_c },
   { "n", "= GDB `n`", cmd_n },
-  { "info", "= GDB `info`, only supporting `info r`", cmd_info },
-  { "x", "x <bytes> <start address>", cmd_x },
+  { "info", "= GDB `info`, supporting `info r` / `info w`", cmd_info },
+  { "x", "x <bytes> <start address>, dump memory content.", cmd_x },
+  { "w", "w <expr>, add watchpoint for $expr", cmd_w },
+  { "d", "d <watchpoint id>, delete watchpoint by id", cmd_d },
   { "q", "Exit NEMU", cmd_q },
 
   /* TODO: Add more commands */
@@ -140,7 +147,8 @@ void ui_mainloop(int is_batch_mode) {
   }
 }
 
-auto dumpReg(uint32_t val) {
+// 3rdparty prettyprint will print rlib::string as array. Let's convert it to normal string.
+std::string dumpReg(uint32_t val) {
   return string("{}{}[32b=0x{}{}, {}L16b=0x{}]").format(std::setfill('0'), std::setw(8), std::hex, val, std::setw(4), (uint16_t)val);
 }
 auto dumpMem(uint32_t begin_addr, uint64_t size) {
@@ -157,13 +165,19 @@ auto dumpMem(uint32_t begin_addr, uint64_t size) {
 }
 
 static int cmd_info(char *_args) {
-  if(_args == NULL || "r"_rs != string(_args).strip())
-    throw std::runtime_error("Error: only 'info r' is supported.");
-  println("Registers:");
-  printfln("%eax={}, %ebx={}, %ecx={}, %edx={}", dumpReg(cpu.eax), dumpReg(cpu.ebx), dumpReg(cpu.ecx), dumpReg(cpu.edx));
-  printfln("%esp={}, %ebp={}, %esi={}, %edi={}", dumpReg(cpu.esp), dumpReg(cpu.ebp), dumpReg(cpu.esi), dumpReg(cpu.edi));
-  printfln("%eip={}", dumpReg(cpu.eip));
-  return 0;
+  if(_args == NULL)
+    throw std::runtime_error("Usage: info <what>");
+  if(string(_args).strip() == "r") {
+    println("Registers:");
+    printfln("%eax={}, %ebx={}, %ecx={}, %edx={}", dumpReg(cpu.eax), dumpReg(cpu.ebx), dumpReg(cpu.ecx), dumpReg(cpu.edx));
+    printfln("%esp={}, %ebp={}, %esi={}, %edi={}", dumpReg(cpu.esp), dumpReg(cpu.ebp), dumpReg(cpu.esi), dumpReg(cpu.edi));
+    printfln("%eip={}", dumpReg(cpu.eip));
+  }
+  else if(string(_args).strip() == "w") {
+    println("Watchpoints:");
+    println(watchpoints);
+  }
+ return 0;
 }
 
 static int cmd_x(char *_args) {
@@ -176,4 +190,26 @@ static int cmd_x(char *_args) {
   printfln("Dumping {}{} bytes from {}{}{}:{}", std::dec, args[0], std::hex, args[1], std::dec, dumpMem(std::stoull(args[1], 0, 16), args[0].as<uint64_t>()));
   
   return 0;
-}
\ No newline at end of file
+}
+
+static int cmd_w(char *_args) {
+  if(_args == NULL)
+    throw std::invalid_argument("w <expr>");
+  
+  watchpoints.emplace_front(std::string(_args), ++max_watchpoint_id);
+  auto iter = watchpoints.begin(); // not thread-safe.
+  rlib::println("Add watchpoint:", *iter);
+  return 0;
+}
+
+static int cmd_d(char *_args) {
+  if(_args == NULL)
+    throw std::invalid_argument("d <wp id>");
+
+  auto to_remove = rlib::string(_args).as<int>();
+  
+  watchpoints.remove_if([&](auto &wp){
+    return wp.id == to_remove;
+  });
+  return 0;
+}
diff --git a/nemu/src/monitor/debug/watchpoint.cc b/nemu/src/monitor/debug/watchpoint.cc
index 2402f38..aae24d9 100644
--- a/nemu/src/monitor/debug/watchpoint.cc
+++ b/nemu/src/monitor/debug/watchpoint.cc
@@ -1,23 +1,34 @@
 #include "monitor/watchpoint.h"
 #include "monitor/expr.h"
 
-#define NR_WP 32
 
-static WP wp_pool[NR_WP];
-static WP *head, *free_;
+/*
+For DOCUMENT WRITER:
+DO NOT USE THE TERM "POOL" IF YOU DON't WANT AN real OBJECT POOL!!!
 
-void init_wp_pool() {
-  int i;
-  for (i = 0; i < NR_WP; i ++) {
-    wp_pool[i].NO = i;
-    wp_pool[i].next = &wp_pool[i + 1];
-  }
-  wp_pool[NR_WP - 1].next = NULL;
+#include <rlib/pool.hpp>
 
-  head = NULL;
-  free_ = wp_pool;
+rlib::fixed_object_pool<WP> wp_pool(NR_WP);
+
+void init_wp_pool() {}
+
+// thread-safe
+WP *new_wp() {
+  auto tmp = wp_pool.try_borrow_one();
+  assert(tmp != nullptr);
+  return tmp;
+}
+
+// thread-safe
+void free_wp(WP *pobj) {
+  wp_pool.release_one(pobj);
 }
+*/
+
+std::list<WP> watchpoints;
+int max_watchpoint_id = 0;
+void init_wp_pool() {}
+
 
-/* TODO: Implement the functionality of watchpoint */
 
 
-- 
GitLab