[zirrcon][zxtest] Migrate elf-tls zxtest
In addition, refactored to utilize more cpp constructs.
Test: elf-tls-test
ZX-3717 #done
Change-Id: I1790623cb83d636fae4c41fbff593b387b099960
diff --git a/zircon/system/utest/core/elf-tls/BUILD.gn b/zircon/system/utest/core/elf-tls/BUILD.gn
index 39eb10b..536d2d2 100644
--- a/zircon/system/utest/core/elf-tls/BUILD.gn
+++ b/zircon/system/utest/core/elf-tls/BUILD.gn
@@ -9,7 +9,7 @@
]
deps = [
"$zx/system/ulib/fdio",
- "$zx/system/ulib/unittest",
"$zx/system/ulib/zircon",
+ "$zx/system/ulib/zxtest",
]
}
diff --git a/zircon/system/utest/core/elf-tls/tls.cpp b/zircon/system/utest/core/elf-tls/tls.cpp
index c1ffd1c..4bba335 100644
--- a/zircon/system/utest/core/elf-tls/tls.cpp
+++ b/zircon/system/utest/core/elf-tls/tls.cpp
@@ -8,23 +8,18 @@
#include <stdint.h>
#include <threads.h>
-#include <unittest/unittest.h>
+#include <vector>
+#include <limits>
-static thread_local bool u1 = true;
-static thread_local uint8_t u8 = UINT8_MAX;
-static thread_local uint16_t u16 = UINT16_MAX;
-static thread_local uint32_t u32 = UINT32_MAX;
-static thread_local uint64_t u64 = UINT64_MAX;
-static thread_local uintptr_t uptr = UINTPTR_MAX;
-static thread_local int8_t i8 = INT8_MAX;
-static thread_local int16_t i16 = INT16_MAX;
-static thread_local int32_t i32 = INT32_MAX;
-static thread_local int64_t i64 = INT64_MAX;
-static thread_local intptr_t iptr = INTPTR_MAX;
-static thread_local float f32 = FLT_MAX;
-static thread_local double f64 = DBL_MAX;
-static thread_local void* ptr = &ptr;
-static thread_local struct {
+#include <zxtest/zxtest.h>
+
+namespace {
+
+constexpr char kMainThreadError[] = "MainThread: Unexpected initialized value";
+constexpr char kBackgroundThreadError[] = "BackgroundThread: Unexpected initialized value";
+
+
+struct Bits {
uint64_t bits0 : 9;
uint64_t bits1 : 9;
uint64_t bits2 : 9;
@@ -32,7 +27,7 @@
uint64_t bits4 : 9;
uint64_t bits5 : 9;
uint64_t bits6 : 9;
- double f64;
+ double f64;
uint64_t bits7 : 9;
uint64_t bits8 : 9;
uint64_t bits9 : 9;
@@ -40,7 +35,23 @@
uint64_t bits11 : 9;
uint64_t bits12 : 9;
uint64_t bits13 : 9;
-} bits = {
+};
+
+thread_local bool u1 = true;
+thread_local uint8_t u8 = std::numeric_limits<uint8_t>::max();
+thread_local uint16_t u16 = std::numeric_limits<uint16_t>::max();
+thread_local uint32_t u32 = std::numeric_limits<uint32_t>::max();
+thread_local uint64_t u64 = std::numeric_limits<uint64_t>::max();
+thread_local uintptr_t uptr = std::numeric_limits<uintptr_t>::max();
+thread_local int8_t i8 = std::numeric_limits<int8_t>::max();
+thread_local int16_t i16 = std::numeric_limits<int16_t>::max();
+thread_local int32_t i32 = std::numeric_limits<int32_t>::max();
+thread_local int64_t i64 = std::numeric_limits<int64_t>::max();
+thread_local intptr_t iptr = std::numeric_limits<intptr_t>::max();
+thread_local float f32 = std::numeric_limits<float>::max();
+thread_local double f64 = std::numeric_limits<double>::max();
+thread_local void* ptr = &ptr;
+thread_local Bits bits = {
0x1ffu,
0x1ffu,
0x1ffu,
@@ -48,7 +59,7 @@
0x1ffu,
0x1ffu,
0x1ffu,
- DBL_MAX,
+ std::numeric_limits<double>::max(),
0x1ffu,
0x1ffu,
0x1ffu,
@@ -57,120 +68,340 @@
0x1ffu,
0x1ffu,
};
+
+struct BasicInitializerInfo {
+ bool u1;
+ uint8_t u8;
+ uint16_t u16;
+ uint32_t u32;
+ uint64_t u64;
+ uintptr_t uptr;
+ int8_t i8;
+ int16_t i16;
+ int32_t i32;
+ int64_t i64;
+ intptr_t iptr;
+ float f32;
+ double f64;
+ void* ptr_addr;
+ void* ptr_val;
+ Bits bits;
+};
+
+int GetBasicInitializers(void* arg) {
+ auto info = reinterpret_cast<BasicInitializerInfo*>(arg);
+ info->u1 = u1;
+ info->u8 = u8;
+ info->u16 = u16;
+ info->u32 = u32;
+ info->u64 = u64;
+ info->uptr = uptr;
+ info->i8 = i8;
+ info->i16 = i16;
+ info->i32 = i32;
+ info->i64 = i64;
+ info->iptr = iptr;
+ info->f32 = f32;
+ info->f64 = f64;
+ info->ptr_addr = &ptr;
+ info->ptr_val = ptr;
+ info->bits.bits0 = bits.bits0;
+ info->bits.bits1 = bits.bits1;
+ info->bits.bits2 = bits.bits2;
+ info->bits.bits3 = bits.bits3;
+ info->bits.bits4 = bits.bits4;
+ info->bits.bits5 = bits.bits5;
+ info->bits.bits6 = bits.bits7;
+ info->bits.bits7 = bits.bits7;
+ info->bits.bits8 = bits.bits8;
+ info->bits.bits9 = bits.bits9;
+ info->bits.bits10 = bits.bits10;
+ info->bits.bits11 = bits.bits11;
+ info->bits.bits12 = bits.bits12;
+ info->bits.bits13 = bits.bits13;
+ return 0;
+}
+
+void VerifyBasicInitializers(const BasicInitializerInfo* info, const char* error_message) {
+ ASSERT_EQ(info->u1, true, "%s", error_message);
+ ASSERT_EQ(info->u8, std::numeric_limits<uint8_t>::max(), "%s", error_message);
+ ASSERT_EQ(info->u16, std::numeric_limits<uint16_t>::max(), "%s", error_message);
+ ASSERT_EQ(info->u32, std::numeric_limits<uint32_t>::max(), "%s", error_message);
+ ASSERT_EQ(info->u64, std::numeric_limits<uint64_t>::max(), "%s", error_message);
+ ASSERT_EQ(info->uptr, std::numeric_limits<uintptr_t>::max(), "%s", error_message);
+ ASSERT_EQ(info->i8, std::numeric_limits<int8_t>::max(), "%s", error_message);
+ ASSERT_EQ(info->i16, std::numeric_limits<int16_t>::max(), "%s", error_message);
+ ASSERT_EQ(info->i32, std::numeric_limits<int32_t>::max(), "%s", error_message);
+ ASSERT_EQ(info->i64, std::numeric_limits<int64_t>::max(), "%s", error_message);
+ ASSERT_EQ(info->iptr, std::numeric_limits<intptr_t>::max(), "%s", error_message);
+ ASSERT_EQ(info->f32, std::numeric_limits<float>::max(), "%s", error_message);
+ ASSERT_EQ(info->f64, std::numeric_limits<double>::max(), "%s", error_message);
+ ASSERT_EQ(info->ptr_addr, info->ptr_val, "%s", error_message);
+ ASSERT_EQ(info->bits.bits0, 0x1ffu, "%s", error_message);
+ ASSERT_EQ(info->bits.bits1, 0x1ffu, "%s", error_message);
+ ASSERT_EQ(info->bits.bits2, 0x1ffu, "%s", error_message);
+ ASSERT_EQ(info->bits.bits3, 0x1ffu, "%s", error_message);
+ ASSERT_EQ(info->bits.bits4, 0x1ffu, "%s", error_message);
+ ASSERT_EQ(info->bits.bits5, 0x1ffu, "%s", error_message);
+ ASSERT_EQ(info->bits.bits6, 0x1ffu, "%s", error_message);
+ ASSERT_EQ(info->bits.bits7, 0x1ffu, "%s", error_message);
+ ASSERT_EQ(info->bits.bits8, 0x1ffu, "%s", error_message);
+ ASSERT_EQ(info->bits.bits9, 0x1ffu, "%s", error_message);
+ ASSERT_EQ(info->bits.bits10, 0x1ffu, "%s", error_message);
+ ASSERT_EQ(info->bits.bits11, 0x1ffu, "%s", error_message);
+ ASSERT_EQ(info->bits.bits12, 0x1ffu, "%s", error_message);
+ ASSERT_EQ(info->bits.bits13, 0x1ffu, "%s", error_message);
+}
+
+constexpr char kThreadNameBasicInitializer[]= "GetInitializers";
+
+TEST(ExecutableTlsTest, BasicInitalizersInThread) {
+ thrd_t thread;
+ auto info = std::make_unique<BasicInitializerInfo>();
+
+ int ret = thrd_create_with_name(&thread, &GetBasicInitializers, info.get(), kThreadNameBasicInitializer);
+ ASSERT_EQ(ret, thrd_success, "unable to create GetInitializers thread");
+
+ ret = thrd_join(thread, nullptr);
+ ASSERT_EQ(ret, thrd_success, "unable to join GetInitializers thread");
+ VerifyBasicInitializers(info.get(), kBackgroundThreadError);
+}
+
+TEST(ExecutableTlsTest, BasicInitalizersInMain) {
+ auto info = std::make_unique<BasicInitializerInfo>();
+
+ ASSERT_EQ(GetBasicInitializers(info.get()), 0);
+ VerifyBasicInitializers(info.get(), kMainThreadError);
+}
+
+constexpr int kArraySize = 1024;
+
#define BYTES_4 0xffu, 0xffu, 0xffu, 0xffu,
#define BYTES_16 BYTES_4 BYTES_4 BYTES_4 BYTES_4
#define BYTES_64 BYTES_16 BYTES_16 BYTES_16 BYTES_16
#define BYTES_256 BYTES_64 BYTES_64 BYTES_64 BYTES_64
#define BYTES_1024 BYTES_256 BYTES_256 BYTES_256 BYTES_256
-static thread_local uint8_t array[1024] = { BYTES_1024 };
-static thread_local struct Ctor {
- Ctor() : x_(UINT64_MAX) {}
- uint64_t x_;
-} ctor;
-static thread_local uint8_t big_array[1 << 20];
+
+thread_local uint8_t array[kArraySize] = { BYTES_1024 };
+
+struct ArrayInfo {
+ uint8_t array[kArraySize];
+};
+
+int GetArray(void* arg) {
+ auto info = reinterpret_cast<ArrayInfo*>(arg);
+ memcpy(info->array, array, kArraySize);
+ return 0;
+}
+
+constexpr char kThreadNameArrayInitializer[]= "ArrayInitializers";
+
+TEST(ExecutableTlsTest, ArrayInitializerInThread) {
+ thrd_t thread;
+ auto info = std::make_unique<ArrayInfo>();
+ int ret = thrd_create_with_name(&thread, &GetArray, info.get(), kThreadNameArrayInitializer);
+ ASSERT_EQ(ret, thrd_success, "unable to create GetArray thread");
+ ret = thrd_join(thread, nullptr);
+ ASSERT_EQ(ret, thrd_success, "unable to join GetArray thread");
+ for (const auto byte : info->array) {
+ ASSERT_EQ(byte, std::numeric_limits<uint8_t>::max(), "%s", kBackgroundThreadError);
+ }
+}
+
+TEST(ExecutableTlsTest, ArrayInitializerInMain) {
+ auto info = std::make_unique<ArrayInfo>();
+ ASSERT_EQ(GetArray(info.get()), 0);
+ for (const auto byte : info->array) {
+ ASSERT_EQ(byte, std::numeric_limits<uint8_t>::max(), "%s", kMainThreadError);
+ }
+}
+
+
+constexpr int kBitArraySize = 1 << 20;
+thread_local uint8_t big_array[kBitArraySize];
+
+struct BigArrayInfo {
+ uint8_t big_array[kBitArraySize];
+};
+
+int GetBigArray(void* arg) {
+ auto info = reinterpret_cast<BigArrayInfo*>(arg);
+ memcpy(info->big_array, big_array, kBitArraySize);
+ return 0;
+}
+
+void VerifyBigArray(BigArrayInfo const* const info, const char *error_message) {
+ uint8_t sum = 0u;
+ for (const auto byte : info->big_array) {
+ sum |= byte;
+ }
+ ASSERT_EQ(sum, 0u, "%s", error_message);
+}
+
+constexpr char kThreadNameBigArray[]= "GetBigArray";
+
+TEST(ExecutableTlsTest, BigArrayInitializerInThread) {
+ thrd_t thread;
+ auto info = std::make_unique<BigArrayInfo>();
+
+ int ret = thrd_create_with_name(&thread, &GetBigArray, info.get(), kThreadNameBigArray);
+ ASSERT_EQ(ret, thrd_success, "unable to create GetBigArray thread");
+
+ ret = thrd_join(thread, nullptr);
+ ASSERT_EQ(ret, thrd_success, "unable to join GetBigArray thread");
+ VerifyBigArray(info.get(), kBackgroundThreadError);
+}
+
+TEST(ExecutableTlsTest, BigArrayInitializerInMain) {
+ auto info = std::make_unique<BigArrayInfo>();
+
+ ASSERT_EQ(GetBigArray(info.get()), 0);
+ VerifyBigArray(info.get(), kMainThreadError);
+}
+
+struct Ctor {
+ Ctor() : x(std::numeric_limits<uint64_t>::max()) {}
+ uint64_t x;
+};
+
+thread_local Ctor ctor;
+
+struct CtorInfo {
+ Ctor ctor;
+};
+
+int GetCtor(void* arg) {
+ auto info = reinterpret_cast<CtorInfo*>(arg);
+ info->ctor = ctor;
+ return 0;
+}
+
+constexpr char kThreadNameStructureInitializer[]= "StructureInitializer";
+
+TEST(ExecutableTlsTest, StructureInitializerInThread) {
+ thrd_t thread;
+ auto info = std::make_unique<CtorInfo>();
+ int ret = thrd_create_with_name(&thread, &GetCtor, info.get(), kThreadNameStructureInitializer);
+ ASSERT_EQ(ret, thrd_success, "unable to create GetCtor thread");
+ ret = thrd_join(thread, nullptr);
+ ASSERT_EQ(ret, thrd_success, "unable to join GetCtor thread");
+ ASSERT_EQ(info->ctor.x, std::numeric_limits<uint64_t>::max(), "%s", kBackgroundThreadError);
+}
+
+TEST(ExecutableTlsTest, StructureInitalizierInMain) {
+ auto info = std::make_unique<CtorInfo>();
+ ASSERT_EQ(GetCtor(info.get()), 0);
+ ASSERT_EQ(info->ctor.x, std::numeric_limits<uint64_t>::max(), "%s", kMainThreadError);
+}
+
+
__attribute__((aligned(0x1000))) thread_local int aligned_var = 123;
-bool CheckInitializers() {
- BEGIN_TEST;
+struct AlignmentInfo {
+ int* aligned_var_addr;
+ int aligned_var_value;
+};
- ASSERT_EQ(u1, true, "unexpected initialized value");
- ASSERT_EQ(u8, UINT8_MAX, "unexpected initialized value");
- ASSERT_EQ(u16, UINT16_MAX, "unexpected initialized value");
- ASSERT_EQ(u32, UINT32_MAX, "unexpected initialized value");
- ASSERT_EQ(u64, UINT64_MAX, "unexpected initialized value");
- ASSERT_EQ(uptr, UINTPTR_MAX, "unexpected initialized value");
- ASSERT_EQ(i8, INT8_MAX, "unexpected initialized value");
- ASSERT_EQ(i16, INT16_MAX, "unexpected initialized value");
- ASSERT_EQ(i32, INT32_MAX, "unexpected initialized value");
- ASSERT_EQ(i64, INT64_MAX, "unexpected initialized value");
- ASSERT_EQ(iptr, INTPTR_MAX, "unexpected initialized value");
- ASSERT_EQ(f32, FLT_MAX, "unexpected initialized value");
- ASSERT_EQ(f64, DBL_MAX, "unexpected initialized value");
- ASSERT_EQ(ptr, &ptr, "unexpected initialized value");
-
- ASSERT_EQ(bits.bits0, 0x1ffu, "unexpected initialized value");
- ASSERT_EQ(bits.bits1, 0x1ffu, "unexpected initialized value");
- ASSERT_EQ(bits.bits2, 0x1ffu, "unexpected initialized value");
- ASSERT_EQ(bits.bits3, 0x1ffu, "unexpected initialized value");
- ASSERT_EQ(bits.bits4, 0x1ffu, "unexpected initialized value");
- ASSERT_EQ(bits.bits5, 0x1ffu, "unexpected initialized value");
- ASSERT_EQ(bits.bits6, 0x1ffu, "unexpected initialized value");
- ASSERT_EQ(bits.f64, DBL_MAX, "unexpected initialized value");
- ASSERT_EQ(bits.bits7, 0x1ffu, "unexpected initialized value");
- ASSERT_EQ(bits.bits8, 0x1ffu, "unexpected initialized value");
- ASSERT_EQ(bits.bits9, 0x1ffu, "unexpected initialized value");
- ASSERT_EQ(bits.bits10, 0x1ffu, "unexpected initialized value");
- ASSERT_EQ(bits.bits11, 0x1ffu, "unexpected initialized value");
- ASSERT_EQ(bits.bits12, 0x1ffu, "unexpected initialized value");
- ASSERT_EQ(bits.bits13, 0x1ffu, "unexpected initialized value");
-
- for (auto& byte : array)
- ASSERT_EQ(byte, UINT8_MAX, "unexpected initialized value");
-
- ASSERT_EQ(ctor.x_, UINT64_MAX, "unexpected initialized value");
-
- uint8_t sum = 0u;
- for (auto& byte : big_array)
- sum |= byte;
- ASSERT_EQ(sum, 0u, "unexpected initialized value");
-
+int GetAlignment(void* arg) {
+ auto info = reinterpret_cast<AlignmentInfo*>(arg);
// TODO(ZX-1646): Make this work on ARM64.
- EXPECT_EQ((uintptr_t)&aligned_var % 0x1000, 0);
- EXPECT_EQ(aligned_var, 123);
-
- END_TEST;
+ info->aligned_var_value = aligned_var;
+ info->aligned_var_addr = &aligned_var;
+ return 0;
}
-bool TestArraySpam(uintptr_t idx) {
- BEGIN_TEST;
+void VerifyAlignment(AlignmentInfo const* const info, const char *error_message) {
+ // TODO(ZX-1646): Make this work on ARM64.
+ EXPECT_EQ(reinterpret_cast<uintptr_t>(info->aligned_var_addr) % 0x1000, 0, "%s", error_message);
+ EXPECT_EQ(info->aligned_var_value, 123, "%s", error_message);
+}
- for (uintptr_t iteration = 0; iteration < 100; ++iteration) {
- auto starting_value = static_cast<uint8_t>(idx + iteration);
- auto value = starting_value;
+constexpr char kThreadNameGetAlignment[]= "GetInitializers";
+
+TEST(ExecutableTlsTest, AlignmentInitializerInThread) {
+ thrd_t thread;
+ auto info = std::make_unique<AlignmentInfo>();
+
+ int ret = thrd_create_with_name(&thread, &GetAlignment, info.get(), kThreadNameGetAlignment);
+ ASSERT_EQ(ret, thrd_success, "unable to create GetAlignment thread");
+
+ ret = thrd_join(thread, nullptr);
+ ASSERT_EQ(ret, thrd_success, "unable to join GetAlignment thread");
+ VerifyAlignment(info.get(), kBackgroundThreadError);
+}
+
+TEST(ExecutableTlsTest, AlignmentInitializierInMain) {
+ auto info = std::make_unique<AlignmentInfo>();
+
+ ASSERT_EQ(GetAlignment(info.get()), 0);
+ VerifyAlignment(info.get(), kMainThreadError);
+}
+
+
+struct ArraySpamInfo {
+ uint8_t index;
+ bool failure;
+ uint8_t failure_offset;
+ uint8_t actual_value;
+ uint8_t expected_value;
+};
+
+int TestArraySpam(void* arg) {
+ auto info = reinterpret_cast<ArraySpamInfo*>(arg);
+ for (uint8_t iteration = 0; iteration < 100; ++iteration) {
+ uint8_t starting_value = static_cast<uint8_t>(info->index + iteration);
+ uint8_t value = starting_value;
for (auto& byte : array) {
byte = value;
++value;
}
sched_yield();
value = starting_value;
- for (auto& byte : array) {
- ASSERT_EQ(byte, value, "unexpected value read back!");
+ uint8_t failure_offset = 0;
+ for (const auto byte : array) {
+ if (byte != value) {
+ info->failure = true;
+ info->actual_value = byte;
+ info->expected_value = value;
+ info->failure_offset = failure_offset;
+ return 0;
+ }
++value;
+ ++failure_offset;
}
}
+ info->failure = false;
- END_TEST;
-}
-
-int TestThread(void* arg) {
- auto idx = reinterpret_cast<uintptr_t>(arg);
-
- CheckInitializers();
- TestArraySpam(idx);
return 0;
}
-bool ExecutableTlsTest() {
- BEGIN_TEST;
+constexpr char kThreadNameArraySpam[]= "TestArraySpam";
- constexpr uintptr_t thread_count = 64u;
- thrd_t threads[thread_count];
- for (uintptr_t idx = 0u; idx < thread_count; ++idx) {
- auto arg = reinterpret_cast<void*>(idx);
- int ret = thrd_create_with_name(&threads[idx], &TestThread, arg, "elf tls test");
- ASSERT_EQ(ret, thrd_success, "unable to create test thread");
+TEST(ExecutableTlsTest, ArrayInitializerSpamThread) {
+ constexpr int kThreadCount = 64;
+ std::vector<thrd_t> threads(kThreadCount);
+ std::vector<ArraySpamInfo> info(kThreadCount);
+
+ for (uint8_t index = 0; index < kThreadCount; ++index) {
+ info[index].index = index;
+ int ret = thrd_create_with_name(&threads[index], &TestArraySpam, &info[index], kThreadNameArraySpam);
+ ASSERT_EQ(ret, thrd_success, "unable to create TestArraySpamInfo thread");
}
- for (uintptr_t idx = 0u; idx < thread_count; ++idx) {
- int ret = thrd_join(threads[idx], nullptr);
- ASSERT_EQ(ret, thrd_success, "unable to join test thread");
+ for (uint8_t index = 0; index < kThreadCount; ++index) {
+ int ret = thrd_join(threads[index], nullptr);
+ ASSERT_EQ(ret, thrd_success, "unable to join TestArraySpamInfo thread");
+ ASSERT_FALSE(info[index].failure, "%s: thread=%d ExpectedValue=%d ActualValue=%d FailureOffset=%d",
+ kBackgroundThreadError, index, info[index].expected_value, info[index].actual_value,
+ info[index].failure_offset);
}
-
- TestThread(nullptr);
-
- END_TEST;
}
-BEGIN_TEST_CASE(elf_tls_tests)
-RUN_TEST(ExecutableTlsTest)
-END_TEST_CASE(elf_tls_tests)
+TEST(ExecutableTlsTest, ArrayInitializierSpamMain) {
+ auto info = std::make_unique<ArraySpamInfo>();
+ ASSERT_EQ(TestArraySpam(info.get()), 0);
+ ASSERT_FALSE(info->failure, "%s: ExpectedValue=%d ActualValue=%d FailureOffset=%d",
+ kMainThreadError, info->expected_value, info->actual_value, info->failure_offset);
+}
+
+} // namespace