gvisor.dev/gvisor@v0.0.0-20240520182842-f9d4d51c7e0f/test/syscalls/linux/getrusage.cc (about) 1 // Copyright 2018 The gVisor Authors. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 #include <signal.h> 16 #include <sys/mman.h> 17 #include <sys/resource.h> 18 #include <sys/types.h> 19 #include <sys/wait.h> 20 21 #include "gtest/gtest.h" 22 #include "absl/time/clock.h" 23 #include "absl/time/time.h" 24 #include "test/util/logging.h" 25 #include "test/util/memory_util.h" 26 #include "test/util/multiprocess_util.h" 27 #include "test/util/signal_util.h" 28 #include "test/util/test_util.h" 29 30 namespace gvisor { 31 namespace testing { 32 33 namespace { 34 35 TEST(GetrusageTest, BasicFork) { 36 pid_t pid = fork(); 37 if (pid == 0) { 38 struct rusage rusage_self; 39 TEST_PCHECK(getrusage(RUSAGE_SELF, &rusage_self) == 0); 40 struct rusage rusage_children; 41 TEST_PCHECK(getrusage(RUSAGE_CHILDREN, &rusage_children) == 0); 42 // The child has consumed some memory. 43 TEST_CHECK(rusage_self.ru_maxrss != 0); 44 // The child has no children of its own. 45 TEST_CHECK(rusage_children.ru_maxrss == 0); 46 _exit(0); 47 } 48 ASSERT_THAT(pid, SyscallSucceeds()); 49 int status; 50 ASSERT_THAT(RetryEINTR(waitpid)(pid, &status, 0), SyscallSucceeds()); 51 struct rusage rusage_self; 52 ASSERT_THAT(getrusage(RUSAGE_SELF, &rusage_self), SyscallSucceeds()); 53 struct rusage rusage_children; 54 ASSERT_THAT(getrusage(RUSAGE_CHILDREN, &rusage_children), SyscallSucceeds()); 55 // The parent has consumed some memory. 56 EXPECT_GT(rusage_self.ru_maxrss, 0); 57 // The child has consumed some memory, and because it has exited we can get 58 // its max RSS. 59 EXPECT_GT(rusage_children.ru_maxrss, 0); 60 } 61 62 // Verifies that a process can get the max resident set size of its grandchild, 63 // i.e. that maxrss propagates correctly from children to waiting parents. 64 TEST(GetrusageTest, Grandchild) { 65 constexpr int kGrandchildSizeKb = 1024; 66 pid_t pid = fork(); 67 if (pid == 0) { 68 pid = fork(); 69 if (pid == 0) { 70 int flags = MAP_ANONYMOUS | MAP_POPULATE | MAP_PRIVATE; 71 void* addr = 72 mmap(nullptr, kGrandchildSizeKb * 1024, PROT_WRITE, flags, -1, 0); 73 TEST_PCHECK(addr != MAP_FAILED); 74 } else { 75 int status; 76 TEST_PCHECK(RetryEINTR(waitpid)(pid, &status, 0) == pid); 77 } 78 _exit(0); 79 } 80 ASSERT_THAT(pid, SyscallSucceeds()); 81 int status; 82 ASSERT_THAT(RetryEINTR(waitpid)(pid, &status, 0), SyscallSucceeds()); 83 struct rusage rusage_self; 84 ASSERT_THAT(getrusage(RUSAGE_SELF, &rusage_self), SyscallSucceeds()); 85 struct rusage rusage_children; 86 ASSERT_THAT(getrusage(RUSAGE_CHILDREN, &rusage_children), SyscallSucceeds()); 87 // The parent has consumed some memory. 88 EXPECT_GT(rusage_self.ru_maxrss, 0); 89 // The child should consume next to no memory, but the grandchild will 90 // consume at least 1MB. Verify that usage bubbles up to the grandparent. 91 EXPECT_GT(rusage_children.ru_maxrss, kGrandchildSizeKb); 92 } 93 94 // Verifies that processes ignoring SIGCHLD do not have updated child maxrss 95 // updated. 96 TEST(GetrusageTest, IgnoreSIGCHLD) { 97 const auto rest = [] { 98 struct sigaction sa; 99 sa.sa_handler = SIG_IGN; 100 sa.sa_flags = 0; 101 auto cleanup = TEST_CHECK_NO_ERRNO_AND_VALUE(ScopedSigaction(SIGCHLD, sa)); 102 pid_t pid = fork(); 103 if (pid == 0) { 104 struct rusage rusage_self; 105 TEST_PCHECK(getrusage(RUSAGE_SELF, &rusage_self) == 0); 106 // The child has consumed some memory. 107 TEST_CHECK(rusage_self.ru_maxrss != 0); 108 _exit(0); 109 } 110 TEST_CHECK_SUCCESS(pid); 111 int status; 112 TEST_CHECK_ERRNO(RetryEINTR(waitpid)(pid, &status, 0), ECHILD); 113 struct rusage rusage_self; 114 TEST_CHECK_SUCCESS(getrusage(RUSAGE_SELF, &rusage_self)); 115 struct rusage rusage_children; 116 TEST_CHECK_SUCCESS(getrusage(RUSAGE_CHILDREN, &rusage_children)); 117 // The parent has consumed some memory. 118 TEST_CHECK(rusage_self.ru_maxrss > 0); 119 // The child's maxrss should not have propagated up. 120 TEST_CHECK(rusage_children.ru_maxrss == 0); 121 }; 122 // Execute inside a forked process so that rusage_children is clean. 123 EXPECT_THAT(InForkedProcess(rest), IsPosixErrorOkAndHolds(0)); 124 } 125 126 // Verifies that zombie processes do not update their parent's maxrss. Only 127 // reaped processes should do this. 128 TEST(GetrusageTest, IgnoreZombie) { 129 const auto rest = [] { 130 pid_t pid = fork(); 131 if (pid == 0) { 132 struct rusage rusage_self; 133 TEST_PCHECK(getrusage(RUSAGE_SELF, &rusage_self) == 0); 134 struct rusage rusage_children; 135 TEST_PCHECK(getrusage(RUSAGE_CHILDREN, &rusage_children) == 0); 136 // The child has consumed some memory. 137 TEST_CHECK(rusage_self.ru_maxrss != 0); 138 // The child has no children of its own. 139 TEST_CHECK(rusage_children.ru_maxrss == 0); 140 _exit(0); 141 } 142 TEST_CHECK_SUCCESS(pid); 143 // Give the child time to exit. Because we don't call wait, the child should 144 // remain a zombie. 145 absl::SleepFor(absl::Seconds(5)); 146 struct rusage rusage_self; 147 TEST_CHECK_SUCCESS(getrusage(RUSAGE_SELF, &rusage_self)); 148 struct rusage rusage_children; 149 TEST_CHECK_SUCCESS(getrusage(RUSAGE_CHILDREN, &rusage_children)); 150 // The parent has consumed some memory. 151 TEST_CHECK(rusage_self.ru_maxrss > 0); 152 // The child has consumed some memory, but hasn't been reaped. 153 TEST_CHECK(rusage_children.ru_maxrss == 0); 154 }; 155 // Execute inside a forked process so that rusage_children is clean. 156 EXPECT_THAT(InForkedProcess(rest), IsPosixErrorOkAndHolds(0)); 157 } 158 159 TEST(GetrusageTest, Wait4) { 160 pid_t pid = fork(); 161 if (pid == 0) { 162 struct rusage rusage_self; 163 TEST_PCHECK(getrusage(RUSAGE_SELF, &rusage_self) == 0); 164 struct rusage rusage_children; 165 TEST_PCHECK(getrusage(RUSAGE_CHILDREN, &rusage_children) == 0); 166 // The child has consumed some memory. 167 TEST_CHECK(rusage_self.ru_maxrss != 0); 168 // The child has no children of its own. 169 TEST_CHECK(rusage_children.ru_maxrss == 0); 170 _exit(0); 171 } 172 ASSERT_THAT(pid, SyscallSucceeds()); 173 struct rusage rusage_children; 174 int status; 175 ASSERT_THAT(RetryEINTR(wait4)(pid, &status, 0, &rusage_children), 176 SyscallSucceeds()); 177 // The child has consumed some memory, and because it has exited we can get 178 // its max RSS. 179 EXPECT_GT(rusage_children.ru_maxrss, 0); 180 } 181 182 } // namespace 183 184 } // namespace testing 185 } // namespace gvisor