1 //===-- PerfTests.cpp -----------------------------------------------------===// 2 // 3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4 // See https://llvm.org/LICENSE.txt for license information. 5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6 // 7 //===----------------------------------------------------------------------===// 8 9 #ifdef __x86_64__ 10 11 #include "Perf.h" 12 13 #include "llvm/Support/Error.h" 14 15 #include "gtest/gtest.h" 16 17 #include <chrono> 18 #include <cstdint> 19 20 using namespace lldb_private; 21 using namespace process_linux; 22 using namespace llvm; 23 24 /// Helper function to read current TSC value. 25 /// 26 /// This code is based on llvm/xray. 27 static Expected<uint64_t> readTsc() { 28 29 unsigned int eax, ebx, ecx, edx; 30 31 // We check whether rdtscp support is enabled. According to the x86_64 manual, 32 // level should be set at 0x80000001, and we should have a look at bit 27 in 33 // EDX. That's 0x8000000 (or 1u << 27). 34 __asm__ __volatile__("cpuid" 35 : "=a"(eax), "=b"(ebx), "=c"(ecx), "=d"(edx) 36 : "0"(0x80000001)); 37 if (!(edx & (1u << 27))) { 38 return createStringError(inconvertibleErrorCode(), 39 "Missing rdtscp support."); 40 } 41 42 unsigned cpu; 43 unsigned long rax, rdx; 44 45 __asm__ __volatile__("rdtscp\n" : "=a"(rax), "=d"(rdx), "=c"(cpu)::); 46 47 return (rdx << 32) + rax; 48 } 49 50 // Test TSC to walltime conversion based on perf conversion values. 51 TEST(Perf, TscConversion) { 52 // This test works by first reading the TSC value directly before 53 // and after sleeping, then converting these values to nanoseconds, and 54 // finally ensuring the difference is approximately equal to the sleep time. 55 // 56 // There will be slight overhead associated with the sleep call, so it isn't 57 // reasonable to expect the difference to be exactly equal to the sleep time. 58 59 const int SLEEP_SECS = 1; 60 std::chrono::nanoseconds SLEEP_NANOS{std::chrono::seconds(SLEEP_SECS)}; 61 62 Expected<LinuxPerfZeroTscConversion> params = 63 LoadPerfTscConversionParameters(); 64 65 // Skip the test if the conversion parameters aren't available. 66 if (!params) 67 GTEST_SKIP() << toString(params.takeError()); 68 69 Expected<uint64_t> tsc_before_sleep = readTsc(); 70 sleep(SLEEP_SECS); 71 Expected<uint64_t> tsc_after_sleep = readTsc(); 72 73 // Skip the test if we are unable to read the TSC value. 74 if (!tsc_before_sleep) 75 GTEST_SKIP() << toString(tsc_before_sleep.takeError()); 76 if (!tsc_after_sleep) 77 GTEST_SKIP() << toString(tsc_after_sleep.takeError()); 78 79 std::chrono::nanoseconds converted_tsc_diff = 80 params->Convert(*tsc_after_sleep) - params->Convert(*tsc_before_sleep); 81 82 std::chrono::microseconds acceptable_overhead(500); 83 84 ASSERT_GE(converted_tsc_diff.count(), SLEEP_NANOS.count()); 85 ASSERT_LT(converted_tsc_diff.count(), 86 (SLEEP_NANOS + acceptable_overhead).count()); 87 } 88 89 size_t ReadCylicBufferWrapper(void *buf, size_t buf_size, void *cyc_buf, 90 size_t cyc_buf_size, size_t cyc_start, 91 size_t offset) { 92 llvm::MutableArrayRef<uint8_t> dst(reinterpret_cast<uint8_t *>(buf), 93 buf_size); 94 llvm::ArrayRef<uint8_t> src(reinterpret_cast<uint8_t *>(cyc_buf), 95 cyc_buf_size); 96 ReadCyclicBuffer(dst, src, cyc_start, offset); 97 return dst.size(); 98 } 99 100 TEST(CyclicBuffer, EdgeCases) { 101 size_t bytes_read; 102 uint8_t cyclic_buffer[6] = {'l', 'i', 'c', 'c', 'y', 'c'}; 103 104 // We will always leave the last bytes untouched 105 // so that string comparisons work. 106 char smaller_buffer[4] = {}; 107 108 // empty buffer to read into 109 bytes_read = ReadCylicBufferWrapper(smaller_buffer, 0, cyclic_buffer, 110 sizeof(cyclic_buffer), 3, 0); 111 ASSERT_EQ(0u, bytes_read); 112 113 // empty cyclic buffer 114 bytes_read = ReadCylicBufferWrapper(smaller_buffer, sizeof(smaller_buffer), 115 cyclic_buffer, 0, 3, 0); 116 ASSERT_EQ(0u, bytes_read); 117 118 // bigger offset 119 bytes_read = 120 ReadCylicBufferWrapper(smaller_buffer, sizeof(smaller_buffer), 121 cyclic_buffer, sizeof(cyclic_buffer), 3, 6); 122 ASSERT_EQ(0u, bytes_read); 123 124 // wrong offset 125 bytes_read = 126 ReadCylicBufferWrapper(smaller_buffer, sizeof(smaller_buffer), 127 cyclic_buffer, sizeof(cyclic_buffer), 3, 7); 128 ASSERT_EQ(0u, bytes_read); 129 130 // wrong start 131 bytes_read = 132 ReadCylicBufferWrapper(smaller_buffer, sizeof(smaller_buffer), 133 cyclic_buffer, sizeof(cyclic_buffer), 3, 7); 134 ASSERT_EQ(0u, bytes_read); 135 } 136 137 TEST(CyclicBuffer, EqualSizeBuffer) { 138 size_t bytes_read = 0; 139 uint8_t cyclic_buffer[6] = {'l', 'i', 'c', 'c', 'y', 'c'}; 140 141 char cyclic[] = "cyclic"; 142 for (size_t i = 0; i < sizeof(cyclic); i++) { 143 // We will always leave the last bytes untouched 144 // so that string comparisons work. 145 char equal_size_buffer[7] = {}; 146 bytes_read = 147 ReadCylicBufferWrapper(equal_size_buffer, sizeof(cyclic_buffer), 148 cyclic_buffer, sizeof(cyclic_buffer), 3, i); 149 ASSERT_EQ((sizeof(cyclic) - i - 1), bytes_read); 150 ASSERT_STREQ(equal_size_buffer, (cyclic + i)); 151 } 152 } 153 154 TEST(CyclicBuffer, SmallerSizeBuffer) { 155 size_t bytes_read; 156 uint8_t cyclic_buffer[6] = {'l', 'i', 'c', 'c', 'y', 'c'}; 157 158 // We will always leave the last bytes untouched 159 // so that string comparisons work. 160 char smaller_buffer[4] = {}; 161 bytes_read = 162 ReadCylicBufferWrapper(smaller_buffer, (sizeof(smaller_buffer) - 1), 163 cyclic_buffer, sizeof(cyclic_buffer), 3, 0); 164 ASSERT_EQ(3u, bytes_read); 165 ASSERT_STREQ(smaller_buffer, "cyc"); 166 167 bytes_read = 168 ReadCylicBufferWrapper(smaller_buffer, (sizeof(smaller_buffer) - 1), 169 cyclic_buffer, sizeof(cyclic_buffer), 3, 1); 170 ASSERT_EQ(3u, bytes_read); 171 ASSERT_STREQ(smaller_buffer, "ycl"); 172 173 bytes_read = 174 ReadCylicBufferWrapper(smaller_buffer, (sizeof(smaller_buffer) - 1), 175 cyclic_buffer, sizeof(cyclic_buffer), 3, 2); 176 ASSERT_EQ(3u, bytes_read); 177 ASSERT_STREQ(smaller_buffer, "cli"); 178 179 bytes_read = 180 ReadCylicBufferWrapper(smaller_buffer, (sizeof(smaller_buffer) - 1), 181 cyclic_buffer, sizeof(cyclic_buffer), 3, 3); 182 ASSERT_EQ(3u, bytes_read); 183 ASSERT_STREQ(smaller_buffer, "lic"); 184 185 { 186 char smaller_buffer[4] = {}; 187 bytes_read = 188 ReadCylicBufferWrapper(smaller_buffer, (sizeof(smaller_buffer) - 1), 189 cyclic_buffer, sizeof(cyclic_buffer), 3, 4); 190 ASSERT_EQ(2u, bytes_read); 191 ASSERT_STREQ(smaller_buffer, "ic"); 192 } 193 { 194 char smaller_buffer[4] = {}; 195 bytes_read = 196 ReadCylicBufferWrapper(smaller_buffer, (sizeof(smaller_buffer) - 1), 197 cyclic_buffer, sizeof(cyclic_buffer), 3, 5); 198 ASSERT_EQ(1u, bytes_read); 199 ASSERT_STREQ(smaller_buffer, "c"); 200 } 201 } 202 203 TEST(CyclicBuffer, BiggerSizeBuffer) { 204 size_t bytes_read = 0; 205 uint8_t cyclic_buffer[6] = {'l', 'i', 'c', 'c', 'y', 'c'}; 206 207 char cyclic[] = "cyclic"; 208 for (size_t i = 0; i < sizeof(cyclic); i++) { 209 // We will always leave the last bytes untouched 210 // so that string comparisons work. 211 char bigger_buffer[10] = {}; 212 bytes_read = 213 ReadCylicBufferWrapper(bigger_buffer, (sizeof(bigger_buffer) - 1), 214 cyclic_buffer, sizeof(cyclic_buffer), 3, i); 215 ASSERT_EQ((sizeof(cyclic) - i - 1), bytes_read); 216 ASSERT_STREQ(bigger_buffer, (cyclic + i)); 217 } 218 } 219 220 #endif // __x86_64__ 221