[lldb] [test/Register] Add read/write tests for multithreaded process

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
This commit is contained in:
Michał Górny 2020-10-12 12:57:14 +02:00
parent d95d3d2a42
commit caedbc317a
4 changed files with 181 additions and 0 deletions

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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

View File

@ -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