| OLD | NEW |
| (Empty) | |
| 1 // Copyright 2014 The Crashpad Authors. All rights reserved. |
| 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 "util/mach/scoped_task_suspend.h" |
| 16 |
| 17 #include <errno.h> |
| 18 #include <fcntl.h> |
| 19 #include <stdint.h> |
| 20 |
| 21 #include <algorithm> |
| 22 |
| 23 #include "base/logging.h" |
| 24 #include "gtest/gtest.h" |
| 25 #include "util/file/fd_io.h" |
| 26 #include "util/misc/clock.h" |
| 27 #include "util/posix/scoped_fcntl_flags.h" |
| 28 #include "util/test/errors.h" |
| 29 #include "util/test/mac/mach_multiprocess.h" |
| 30 |
| 31 namespace crashpad { |
| 32 namespace test { |
| 33 namespace { |
| 34 |
| 35 // Reads a byte from |fd|. Returns |true| if a byte could be read, and |false| |
| 36 // otherwise. If |fd| is a file descriptor set to O_NONBLOCK mode, this will |
| 37 // return |false| immediately if no more data is available to be read. |
| 38 // Otherwise, this will only return false when |fd| is at end-of-file. |
| 39 // Terminates execution if an error condition is detected. |
| 40 bool ReadByte(int fd) { |
| 41 char c; |
| 42 ssize_t rv = ReadFD(fd, &c, sizeof(c)); |
| 43 if (rv < 0 && errno == EAGAIN) { |
| 44 rv = 0; |
| 45 } |
| 46 PCHECK(rv >= 0) << "read"; |
| 47 CHECK(rv <= static_cast<ssize_t>(sizeof(c))) << "read"; |
| 48 |
| 49 return rv > 0; |
| 50 } |
| 51 |
| 52 // Reads all data from |fd| until there is no more data to be read. If |fd| is |
| 53 // a file descriptor set to O_NONBLOCK mode, this will return as soon as no more |
| 54 // data is available to be read. Otherwise, this will not return until |fd| is |
| 55 // at end-of-file. |
| 56 void DrainPipe(int fd) { |
| 57 while (ReadByte(fd)) { |
| 58 } |
| 59 } |
| 60 |
| 61 class ScopedTaskSuspendTest final : public MachMultiprocess { |
| 62 public: |
| 63 ScopedTaskSuspendTest() : MachMultiprocess() {} |
| 64 ~ScopedTaskSuspendTest() {} |
| 65 |
| 66 private: |
| 67 // MachMultiprocess: |
| 68 |
| 69 virtual void MachMultiprocessParent() override { |
| 70 int read_fd = ReadPipeFD(); |
| 71 |
| 72 // Make sure that the child is running. This is a blocking read, because the |
| 73 // parent should wait for the child to start sending data. |
| 74 char c; |
| 75 CheckedReadFD(read_fd, &c, sizeof(c)); |
| 76 |
| 77 { |
| 78 // Suspend the child process. |
| 79 ScopedTaskSuspend suspend(ChildTask()); |
| 80 |
| 81 // Make the read pipe non-blocking so that it can be drained and tested |
| 82 // for EOF. |
| 83 ScopedFcntlFlags fcntl_flags(read_fd, O_NONBLOCK, 0); |
| 84 |
| 85 DrainPipe(read_fd); |
| 86 |
| 87 // This extra sleep and drain shouldn’t be necessary, but there seem to |
| 88 // be scheduling bugs or delays in data appearing on the pipe. |
| 89 SleepNanoseconds(1E5); // 100 microseconds |
| 90 DrainPipe(read_fd); |
| 91 |
| 92 EXPECT_FALSE(ReadByte(read_fd)); |
| 93 |
| 94 // Sleep for a little while, giving the child process a chance to measure |
| 95 // that one of its loop iterations took a long time. |
| 96 SleepNanoseconds(1E7); // 10 milliseconds |
| 97 |
| 98 // Make sure that the child hasn’t sent anything else while it was |
| 99 // supposed to be suspended. |
| 100 EXPECT_FALSE(ReadByte(read_fd)); |
| 101 } |
| 102 |
| 103 // Make sure that the child has resumed. This is a blocking read, because |
| 104 // the parent should wait for the child to resume sending data. |
| 105 CheckedReadFD(read_fd, &c, sizeof(c)); |
| 106 |
| 107 // Tell the child that it’s permitted to leave its loop. |
| 108 CheckedWriteFD(WritePipeFD(), &c, sizeof(c)); |
| 109 |
| 110 // This is a blocking drain, which waits for read_fd to reach EOF. |
| 111 DrainPipe(read_fd); |
| 112 CheckedReadFDAtEOF(read_fd); |
| 113 } |
| 114 |
| 115 virtual void MachMultiprocessChild() override { |
| 116 int read_fd = ReadPipeFD(); |
| 117 int write_fd = WritePipeFD(); |
| 118 |
| 119 // Make the read pipe non-blocking so that it can be monitored to read the |
| 120 // byte that will cause the loop to terminate. |
| 121 ScopedFcntlFlags fcntl_flags(read_fd, O_NONBLOCK, 0); |
| 122 |
| 123 // Loop for a while, computing the longest duration for an iteration of the |
| 124 // loop. Inside each loop, send a byte to the parent process via the pipe, |
| 125 // so that the parent will be able to tell when the child is running and |
| 126 // when it is suspended. |
| 127 uint64_t now = ClockMonotonicNanoseconds(); |
| 128 uint64_t last = now; |
| 129 uint64_t max_delay = 0; |
| 130 do { |
| 131 char c = '\0'; |
| 132 CheckedWriteFD(write_fd, &c, sizeof(c)); |
| 133 |
| 134 uint64_t delay = now - last; |
| 135 max_delay = std::max(max_delay, delay); |
| 136 last = now; |
| 137 now = ClockMonotonicNanoseconds(); |
| 138 } while (!ReadByte(read_fd)); |
| 139 |
| 140 // The longest iteration of the loop should be at least as long as the time |
| 141 // that the parent process had this process suspended, but because of the |
| 142 // tricky nature of scheduling, just make sure that the longest iteration |
| 143 // took a perceptible amount of time. |
| 144 EXPECT_GE(max_delay, 1E4); // 10 microseconds |
| 145 } |
| 146 |
| 147 DISALLOW_COPY_AND_ASSIGN(ScopedTaskSuspendTest); |
| 148 }; |
| 149 |
| 150 TEST(ScopedTaskSuspend, ScopedTaskSuspend) { |
| 151 ScopedTaskSuspendTest scoped_task_suspend_test; |
| 152 scoped_task_suspend_test.Run(); |
| 153 } |
| 154 |
| 155 } // namespace |
| 156 } // namespace test |
| 157 } // namespace crashpad |
| OLD | NEW |