LevelDB project 1 10225501460 林子骥 10211900416 郭夏辉
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

353 lines
12 KiB

1 month ago
  1. // Copyright (c) 2011 The LevelDB Authors. All rights reserved.
  2. // Use of this source code is governed by a BSD-style license that can be
  3. // found in the LICENSE file. See the AUTHORS file for names of contributors.
  4. #include <sys/resource.h>
  5. #include <sys/wait.h>
  6. #include <unistd.h>
  7. #include <cstdio>
  8. #include <cstdlib>
  9. #include <cstring>
  10. #include <string>
  11. #include <unordered_set>
  12. #include <vector>
  13. #include "gtest/gtest.h"
  14. #include "leveldb/env.h"
  15. #include "port/port.h"
  16. #include "util/env_posix_test_helper.h"
  17. #include "util/testutil.h"
  18. #if HAVE_O_CLOEXEC
  19. namespace {
  20. // Exit codes for the helper process spawned by TestCloseOnExec* tests.
  21. // Useful for debugging test failures.
  22. constexpr int kTextCloseOnExecHelperExecFailedCode = 61;
  23. constexpr int kTextCloseOnExecHelperDup2FailedCode = 62;
  24. constexpr int kTextCloseOnExecHelperFoundOpenFdCode = 63;
  25. // Global set by main() and read in TestCloseOnExec.
  26. //
  27. // The argv[0] value is stored in a std::vector instead of a std::string because
  28. // std::string does not return a mutable pointer to its buffer until C++17.
  29. //
  30. // The vector stores the string pointed to by argv[0], plus the trailing null.
  31. std::vector<char>* GetArgvZero() {
  32. static std::vector<char> program_name;
  33. return &program_name;
  34. }
  35. // Command-line switch used to run this test as the CloseOnExecSwitch helper.
  36. static const char kTestCloseOnExecSwitch[] = "--test-close-on-exec-helper";
  37. // Executed in a separate process by TestCloseOnExec* tests.
  38. //
  39. // main() delegates to this function when the test executable is launched with
  40. // a special command-line switch. TestCloseOnExec* tests fork()+exec() the test
  41. // executable and pass the special command-line switch.
  42. //
  43. // main() delegates to this function when the test executable is launched with
  44. // a special command-line switch. TestCloseOnExec* tests fork()+exec() the test
  45. // executable and pass the special command-line switch.
  46. //
  47. // When main() delegates to this function, the process probes whether a given
  48. // file descriptor is open, and communicates the result via its exit code.
  49. int TestCloseOnExecHelperMain(char* pid_arg) {
  50. int fd = std::atoi(pid_arg);
  51. // When given the same file descriptor twice, dup2() returns -1 if the
  52. // file descriptor is closed, or the given file descriptor if it is open.
  53. if (::dup2(fd, fd) == fd) {
  54. std::fprintf(stderr, "Unexpected open fd %d\n", fd);
  55. return kTextCloseOnExecHelperFoundOpenFdCode;
  56. }
  57. // Double-check that dup2() is saying the file descriptor is closed.
  58. if (errno != EBADF) {
  59. std::fprintf(stderr, "Unexpected errno after calling dup2 on fd %d: %s\n",
  60. fd, std::strerror(errno));
  61. return kTextCloseOnExecHelperDup2FailedCode;
  62. }
  63. return 0;
  64. }
  65. // File descriptors are small non-negative integers.
  66. //
  67. // Returns void so the implementation can use ASSERT_EQ.
  68. void GetMaxFileDescriptor(int* result_fd) {
  69. // Get the maximum file descriptor number.
  70. ::rlimit fd_rlimit;
  71. ASSERT_EQ(0, ::getrlimit(RLIMIT_NOFILE, &fd_rlimit));
  72. *result_fd = fd_rlimit.rlim_cur;
  73. }
  74. // Iterates through all possible FDs and returns the currently open ones.
  75. //
  76. // Returns void so the implementation can use ASSERT_EQ.
  77. void GetOpenFileDescriptors(std::unordered_set<int>* open_fds) {
  78. int max_fd = 0;
  79. GetMaxFileDescriptor(&max_fd);
  80. for (int fd = 0; fd < max_fd; ++fd) {
  81. if (::dup2(fd, fd) != fd) {
  82. // When given the same file descriptor twice, dup2() returns -1 if the
  83. // file descriptor is closed, or the given file descriptor if it is open.
  84. //
  85. // Double-check that dup2() is saying the fd is closed.
  86. ASSERT_EQ(EBADF, errno)
  87. << "dup2() should set errno to EBADF on closed file descriptors";
  88. continue;
  89. }
  90. open_fds->insert(fd);
  91. }
  92. }
  93. // Finds an FD open since a previous call to GetOpenFileDescriptors().
  94. //
  95. // |baseline_open_fds| is the result of a previous GetOpenFileDescriptors()
  96. // call. Assumes that exactly one FD was opened since that call.
  97. //
  98. // Returns void so the implementation can use ASSERT_EQ.
  99. void GetNewlyOpenedFileDescriptor(
  100. const std::unordered_set<int>& baseline_open_fds, int* result_fd) {
  101. std::unordered_set<int> open_fds;
  102. GetOpenFileDescriptors(&open_fds);
  103. for (int fd : baseline_open_fds) {
  104. ASSERT_EQ(1, open_fds.count(fd))
  105. << "Previously opened file descriptor was closed during test setup";
  106. open_fds.erase(fd);
  107. }
  108. ASSERT_EQ(1, open_fds.size())
  109. << "Expected exactly one newly opened file descriptor during test setup";
  110. *result_fd = *open_fds.begin();
  111. }
  112. // Check that a fork()+exec()-ed child process does not have an extra open FD.
  113. void CheckCloseOnExecDoesNotLeakFDs(
  114. const std::unordered_set<int>& baseline_open_fds) {
  115. // Prepare the argument list for the child process.
  116. // execv() wants mutable buffers.
  117. char switch_buffer[sizeof(kTestCloseOnExecSwitch)];
  118. std::memcpy(switch_buffer, kTestCloseOnExecSwitch,
  119. sizeof(kTestCloseOnExecSwitch));
  120. int probed_fd;
  121. GetNewlyOpenedFileDescriptor(baseline_open_fds, &probed_fd);
  122. std::string fd_string = std::to_string(probed_fd);
  123. std::vector<char> fd_buffer(fd_string.begin(), fd_string.end());
  124. fd_buffer.emplace_back('\0');
  125. // The helper process is launched with the command below.
  126. // env_posix_tests --test-close-on-exec-helper 3
  127. char* child_argv[] = {GetArgvZero()->data(), switch_buffer, fd_buffer.data(),
  128. nullptr};
  129. constexpr int kForkInChildProcessReturnValue = 0;
  130. int child_pid = fork();
  131. if (child_pid == kForkInChildProcessReturnValue) {
  132. ::execv(child_argv[0], child_argv);
  133. std::fprintf(stderr, "Error spawning child process: %s\n", strerror(errno));
  134. std::exit(kTextCloseOnExecHelperExecFailedCode);
  135. }
  136. int child_status = 0;
  137. ASSERT_EQ(child_pid, ::waitpid(child_pid, &child_status, 0));
  138. ASSERT_TRUE(WIFEXITED(child_status))
  139. << "The helper process did not exit with an exit code";
  140. ASSERT_EQ(0, WEXITSTATUS(child_status))
  141. << "The helper process encountered an error";
  142. }
  143. } // namespace
  144. #endif // HAVE_O_CLOEXEC
  145. namespace leveldb {
  146. static const int kReadOnlyFileLimit = 4;
  147. static const int kMMapLimit = 4;
  148. class EnvPosixTest : public testing::Test {
  149. public:
  150. static void SetFileLimits(int read_only_file_limit, int mmap_limit) {
  151. EnvPosixTestHelper::SetReadOnlyFDLimit(read_only_file_limit);
  152. EnvPosixTestHelper::SetReadOnlyMMapLimit(mmap_limit);
  153. }
  154. EnvPosixTest() : env_(Env::Default()) {}
  155. Env* env_;
  156. };
  157. TEST_F(EnvPosixTest, TestOpenOnRead) {
  158. // Write some test data to a single file that will be opened |n| times.
  159. std::string test_dir;
  160. ASSERT_LEVELDB_OK(env_->GetTestDirectory(&test_dir));
  161. std::string test_file = test_dir + "/open_on_read.txt";
  162. FILE* f = std::fopen(test_file.c_str(), "we");
  163. ASSERT_TRUE(f != nullptr);
  164. const char kFileData[] = "abcdefghijklmnopqrstuvwxyz";
  165. fputs(kFileData, f);
  166. std::fclose(f);
  167. // Open test file some number above the sum of the two limits to force
  168. // open-on-read behavior of POSIX Env leveldb::RandomAccessFile.
  169. const int kNumFiles = kReadOnlyFileLimit + kMMapLimit + 5;
  170. leveldb::RandomAccessFile* files[kNumFiles] = {0};
  171. for (int i = 0; i < kNumFiles; i++) {
  172. ASSERT_LEVELDB_OK(env_->NewRandomAccessFile(test_file, &files[i]));
  173. }
  174. char scratch;
  175. Slice read_result;
  176. for (int i = 0; i < kNumFiles; i++) {
  177. ASSERT_LEVELDB_OK(files[i]->Read(i, 1, &read_result, &scratch));
  178. ASSERT_EQ(kFileData[i], read_result[0]);
  179. }
  180. for (int i = 0; i < kNumFiles; i++) {
  181. delete files[i];
  182. }
  183. ASSERT_LEVELDB_OK(env_->RemoveFile(test_file));
  184. }
  185. #if HAVE_O_CLOEXEC
  186. TEST_F(EnvPosixTest, TestCloseOnExecSequentialFile) {
  187. std::unordered_set<int> open_fds;
  188. GetOpenFileDescriptors(&open_fds);
  189. std::string test_dir;
  190. ASSERT_LEVELDB_OK(env_->GetTestDirectory(&test_dir));
  191. std::string file_path = test_dir + "/close_on_exec_sequential.txt";
  192. ASSERT_LEVELDB_OK(WriteStringToFile(env_, "0123456789", file_path));
  193. leveldb::SequentialFile* file = nullptr;
  194. ASSERT_LEVELDB_OK(env_->NewSequentialFile(file_path, &file));
  195. CheckCloseOnExecDoesNotLeakFDs(open_fds);
  196. delete file;
  197. ASSERT_LEVELDB_OK(env_->RemoveFile(file_path));
  198. }
  199. TEST_F(EnvPosixTest, TestCloseOnExecRandomAccessFile) {
  200. std::unordered_set<int> open_fds;
  201. GetOpenFileDescriptors(&open_fds);
  202. std::string test_dir;
  203. ASSERT_LEVELDB_OK(env_->GetTestDirectory(&test_dir));
  204. std::string file_path = test_dir + "/close_on_exec_random_access.txt";
  205. ASSERT_LEVELDB_OK(WriteStringToFile(env_, "0123456789", file_path));
  206. // Exhaust the RandomAccessFile mmap limit. This way, the test
  207. // RandomAccessFile instance below is backed by a file descriptor, not by an
  208. // mmap region.
  209. leveldb::RandomAccessFile* mmapped_files[kMMapLimit];
  210. for (int i = 0; i < kMMapLimit; i++) {
  211. ASSERT_LEVELDB_OK(env_->NewRandomAccessFile(file_path, &mmapped_files[i]));
  212. }
  213. leveldb::RandomAccessFile* file = nullptr;
  214. ASSERT_LEVELDB_OK(env_->NewRandomAccessFile(file_path, &file));
  215. CheckCloseOnExecDoesNotLeakFDs(open_fds);
  216. delete file;
  217. for (int i = 0; i < kMMapLimit; i++) {
  218. delete mmapped_files[i];
  219. }
  220. ASSERT_LEVELDB_OK(env_->RemoveFile(file_path));
  221. }
  222. TEST_F(EnvPosixTest, TestCloseOnExecWritableFile) {
  223. std::unordered_set<int> open_fds;
  224. GetOpenFileDescriptors(&open_fds);
  225. std::string test_dir;
  226. ASSERT_LEVELDB_OK(env_->GetTestDirectory(&test_dir));
  227. std::string file_path = test_dir + "/close_on_exec_writable.txt";
  228. ASSERT_LEVELDB_OK(WriteStringToFile(env_, "0123456789", file_path));
  229. leveldb::WritableFile* file = nullptr;
  230. ASSERT_LEVELDB_OK(env_->NewWritableFile(file_path, &file));
  231. CheckCloseOnExecDoesNotLeakFDs(open_fds);
  232. delete file;
  233. ASSERT_LEVELDB_OK(env_->RemoveFile(file_path));
  234. }
  235. TEST_F(EnvPosixTest, TestCloseOnExecAppendableFile) {
  236. std::unordered_set<int> open_fds;
  237. GetOpenFileDescriptors(&open_fds);
  238. std::string test_dir;
  239. ASSERT_LEVELDB_OK(env_->GetTestDirectory(&test_dir));
  240. std::string file_path = test_dir + "/close_on_exec_appendable.txt";
  241. ASSERT_LEVELDB_OK(WriteStringToFile(env_, "0123456789", file_path));
  242. leveldb::WritableFile* file = nullptr;
  243. ASSERT_LEVELDB_OK(env_->NewAppendableFile(file_path, &file));
  244. CheckCloseOnExecDoesNotLeakFDs(open_fds);
  245. delete file;
  246. ASSERT_LEVELDB_OK(env_->RemoveFile(file_path));
  247. }
  248. TEST_F(EnvPosixTest, TestCloseOnExecLockFile) {
  249. std::unordered_set<int> open_fds;
  250. GetOpenFileDescriptors(&open_fds);
  251. std::string test_dir;
  252. ASSERT_LEVELDB_OK(env_->GetTestDirectory(&test_dir));
  253. std::string file_path = test_dir + "/close_on_exec_lock.txt";
  254. ASSERT_LEVELDB_OK(WriteStringToFile(env_, "0123456789", file_path));
  255. leveldb::FileLock* lock = nullptr;
  256. ASSERT_LEVELDB_OK(env_->LockFile(file_path, &lock));
  257. CheckCloseOnExecDoesNotLeakFDs(open_fds);
  258. ASSERT_LEVELDB_OK(env_->UnlockFile(lock));
  259. ASSERT_LEVELDB_OK(env_->RemoveFile(file_path));
  260. }
  261. TEST_F(EnvPosixTest, TestCloseOnExecLogger) {
  262. std::unordered_set<int> open_fds;
  263. GetOpenFileDescriptors(&open_fds);
  264. std::string test_dir;
  265. ASSERT_LEVELDB_OK(env_->GetTestDirectory(&test_dir));
  266. std::string file_path = test_dir + "/close_on_exec_logger.txt";
  267. ASSERT_LEVELDB_OK(WriteStringToFile(env_, "0123456789", file_path));
  268. leveldb::Logger* file = nullptr;
  269. ASSERT_LEVELDB_OK(env_->NewLogger(file_path, &file));
  270. CheckCloseOnExecDoesNotLeakFDs(open_fds);
  271. delete file;
  272. ASSERT_LEVELDB_OK(env_->RemoveFile(file_path));
  273. }
  274. #endif // HAVE_O_CLOEXEC
  275. } // namespace leveldb
  276. int main(int argc, char** argv) {
  277. #if HAVE_O_CLOEXEC
  278. // Check if we're invoked as a helper program, or as the test suite.
  279. for (int i = 1; i < argc; ++i) {
  280. if (!std::strcmp(argv[i], kTestCloseOnExecSwitch)) {
  281. return TestCloseOnExecHelperMain(argv[i + 1]);
  282. }
  283. }
  284. // Save argv[0] early, because googletest may modify argv.
  285. GetArgvZero()->assign(argv[0], argv[0] + std::strlen(argv[0]) + 1);
  286. #endif // HAVE_O_CLOEXEC
  287. // All tests currently run with the same read-only file limits.
  288. leveldb::EnvPosixTest::SetFileLimits(leveldb::kReadOnlyFileLimit,
  289. leveldb::kMMapLimit);
  290. testing::InitGoogleTest(&argc, argv);
  291. return RUN_ALL_TESTS();
  292. }