[lldb] [test/Register] Add read/write tests for multithreaded process
authorMichał Górny <mgorny@moritz.systems>
Mon, 12 Oct 2020 10:57:14 +0000 (12:57 +0200)
committerMichał Górny <mgorny@moritz.systems>
Wed, 14 Oct 2020 09:08:36 +0000 (11:08 +0200)
Add a test to verify that 'register read' and 'register write' commands
work correctly in a multithreaded program, in particular that they read
or write registers for the correct thread.  The tests use locking
to ensure that events are serialized and the test can execute reliably.

Differential Revision: https://reviews.llvm.org/D89248

lldb/test/Shell/Register/Inputs/x86-multithread-read.cpp [new file with mode: 0644]
lldb/test/Shell/Register/Inputs/x86-multithread-write.cpp [new file with mode: 0644]
lldb/test/Shell/Register/x86-multithread-read.test [new file with mode: 0644]
lldb/test/Shell/Register/x86-multithread-write.test [new file with mode: 0644]

diff --git a/lldb/test/Shell/Register/Inputs/x86-multithread-read.cpp b/lldb/test/Shell/Register/Inputs/x86-multithread-read.cpp
new file mode 100644 (file)
index 0000000..c5f571f
--- /dev/null
@@ -0,0 +1,61 @@
+#include <cstdint>
+#include <mutex>
+#include <thread>
+
+std::mutex t1_mutex, t2_mutex;
+
+struct test_data {
+  uint32_t eax;
+  uint32_t ebx;
+
+  struct alignas(16) {
+    uint64_t mantissa;
+    uint16_t sign_exp;
+  } st0;
+};
+
+void t_func(std::mutex &t_mutex, const test_data &t_data) {
+  std::lock_guard<std::mutex> t_lock(t_mutex);
+
+  asm volatile(
+    "finit\t\n"
+    "fldt %2\t\n"
+    "int3\n\t"
+    :
+    : "a"(t_data.eax), "b"(t_data.ebx), "m"(t_data.st0)
+    : "st"
+  );
+}
+
+int main() {
+  test_data t1_data = {
+    .eax = 0x05060708,
+    .ebx = 0x15161718,
+    .st0 = {0x8070605040302010, 0x4000},
+  };
+  test_data t2_data = {
+    .eax = 0x25262728,
+    .ebx = 0x35363738,
+    .st0 = {0x8171615141312111, 0xc000},
+  };
+
+  // block both threads from proceeding
+  std::unique_lock<std::mutex> m1_lock(t1_mutex);
+  std::unique_lock<std::mutex> m2_lock(t2_mutex);
+
+  // start both threads
+  std::thread t1(t_func, std::ref(t1_mutex), std::ref(t1_data));
+  std::thread t2(t_func, std::ref(t2_mutex), std::ref(t2_data));
+
+  // release lock on thread 1 to make it interrupt the program
+  m1_lock.unlock();
+  // wait for thread 1 to finish
+  t1.join();
+
+  // release lock on thread 2
+  m2_lock.unlock();
+  // wait for thread 2 to finish
+  t2.join();
+
+  return 0;
+}
diff --git a/lldb/test/Shell/Register/Inputs/x86-multithread-write.cpp b/lldb/test/Shell/Register/Inputs/x86-multithread-write.cpp
new file mode 100644 (file)
index 0000000..320f9e9
--- /dev/null
@@ -0,0 +1,66 @@
+#include <cinttypes>
+#include <cstdint>
+#include <cstdio>
+#include <mutex>
+#include <thread>
+
+std::mutex t1_mutex, t2_mutex;
+
+struct test_data {
+  uint32_t eax;
+  uint32_t ebx;
+
+  struct alignas(16) {
+    uint8_t data[10];
+  } st0;
+};
+
+constexpr test_data filler = {
+  .eax = 0xffffffff,
+  .ebx = 0xffffffff,
+  .st0 = {{0x1f, 0x2f, 0x3f, 0x4f, 0x5f, 0x6f, 0x7f, 0x8f, 0x80, 0x40}},
+};
+
+void t_func(std::mutex &t_mutex) {
+  std::lock_guard<std::mutex> t_lock(t_mutex);
+  test_data out = filler;
+
+  asm volatile(
+    "finit\t\n"
+    "fldt %2\t\n"
+    "int3\n\t"
+    "fstpt %2\t\n"
+    : "+a"(out.eax), "+b"(out.ebx)
+    : "m"(out.st0)
+    : "memory", "st"
+  );
+
+  printf("eax = 0x%08" PRIx32 "\n", out.eax);
+  printf("ebx = 0x%08" PRIx32 "\n", out.ebx);
+  printf("st0 = { ");
+  for (int i = 0; i < sizeof(out.st0.data); ++i)
+    printf("0x%02" PRIx8 " ", out.st0.data[i]);
+  printf("}\n");
+}
+
+int main() {
+  // block both threads from proceeding
+  std::unique_lock<std::mutex> m1_lock(t1_mutex);
+  std::unique_lock<std::mutex> m2_lock(t2_mutex);
+
+  // start both threads
+  std::thread t1(t_func, std::ref(t1_mutex));
+  std::thread t2(t_func, std::ref(t2_mutex));
+
+  // release lock on thread 1 to make it interrupt the program
+  m1_lock.unlock();
+  // wait for thread 1 to finish
+  t1.join();
+
+  // release lock on thread 2
+  m2_lock.unlock();
+  // wait for thread 2 to finish
+  t2.join();
+
+  return 0;
+}
diff --git a/lldb/test/Shell/Register/x86-multithread-read.test b/lldb/test/Shell/Register/x86-multithread-read.test
new file mode 100644 (file)
index 0000000..6bff2b1
--- /dev/null
@@ -0,0 +1,23 @@
+# XFAIL: system-windows
+# REQUIRES: native && (target-x86 || target-x86_64)
+# RUN: %clangxx_host %p/Inputs/x86-multithread-read.cpp -o %t -pthread
+# RUN: %lldb -b -s %s %t | FileCheck %s
+
+process launch
+# CHECK: Process {{[0-9]+}} stopped
+
+register read --all
+# CHECK-DAG: eax = 0x05060708
+# CHECK-DAG: ebx = 0x15161718
+# CHECK-DAG: st{{(mm)?}}0 = {0x10 0x20 0x30 0x40 0x50 0x60 0x70 0x80 0x00 0x40}
+
+process continue
+# CHECK: Process {{[0-9]+}} stopped
+
+register read --all
+# CHECK-DAG: eax = 0x25262728
+# CHECK-DAG: ebx = 0x35363738
+# CHECK-DAG: st{{(mm)?}}0 = {0x11 0x21 0x31 0x41 0x51 0x61 0x71 0x81 0x00 0xc0}
+
+process continue
+# CHECK: Process {{[0-9]+}} exited with status = 0
diff --git a/lldb/test/Shell/Register/x86-multithread-write.test b/lldb/test/Shell/Register/x86-multithread-write.test
new file mode 100644 (file)
index 0000000..bf0cd33
--- /dev/null
@@ -0,0 +1,31 @@
+# XFAIL: system-windows
+# XFAIL: system-darwin
+# REQUIRES: native && (target-x86 || target-x86_64)
+# RUN: %clangxx_host %p/Inputs/x86-multithread-write.cpp -o %t -pthread
+# RUN: %lldb -b -s %s %t | FileCheck %s
+
+process launch
+# CHECK: Process {{[0-9]+}} stopped
+
+register write eax 0x05060708
+register write ebx 0x15161718
+# TODO: the need to call 'read' is probably a bug in the Linux plugin
+register read st0
+register write st0 "{0x10 0x20 0x30 0x40 0x50 0x60 0x70 0x80 0x00 0x40}"
+
+process continue
+# CHECK-DAG: eax = 0x05060708
+# CHECK-DAG: ebx = 0x15161718
+# CHECK-DAG: st0 = { 0x10 0x20 0x30 0x40 0x50 0x60 0x70 0x80 0x00 0x40 }
+# CHECK: Process {{[0-9]+}} stopped
+
+register write eax 0x25262728
+register write ebx 0x35363738
+register read st0
+register write st0 "{0x11 0x21 0x31 0x41 0x51 0x61 0x71 0x81 0x00 0xc0}"
+
+process continue
+# CHECK-DAG: eax = 0x25262728
+# CHECK-DAG: ebx = 0x35363738
+# CHECK-DAG: st0 = { 0x11 0x21 0x31 0x41 0x51 0x61 0x71 0x81 0x00 0xc0 }
+# CHECK: Process {{[0-9]+}} exited with status = 0