| /* | 
 |  * Copyright (C) 2017 The Android Open Source Project | 
 |  * | 
 |  * Licensed under the Apache License, Version 2.0 (the "License"); | 
 |  * you may not use this file except in compliance with the License. | 
 |  * You may obtain a copy of the License at | 
 |  * | 
 |  *      http://www.apache.org/licenses/LICENSE-2.0 | 
 |  * | 
 |  * Unless required by applicable law or agreed to in writing, software | 
 |  * distributed under the License is distributed on an "AS IS" BASIS, | 
 |  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | 
 |  * See the License for the specific language governing permissions and | 
 |  * limitations under the License. | 
 |  */ | 
 |  | 
 | #include "perfetto/ext/base/utils.h" | 
 |  | 
 | #include "perfetto/base/build_config.h" | 
 |  | 
 | #if PERFETTO_BUILDFLAG(PERFETTO_OS_WIN) | 
 | #include <Windows.h> | 
 | #else | 
 | #include <fcntl.h> | 
 | #include <signal.h> | 
 | #include <unistd.h> | 
 | #endif | 
 |  | 
 | #include <stdint.h> | 
 |  | 
 | #include <algorithm> | 
 | #include <random> | 
 | #include <thread> | 
 |  | 
 | #include "perfetto/ext/base/file_utils.h" | 
 | #include "perfetto/ext/base/pipe.h" | 
 | #include "perfetto/ext/base/temp_file.h" | 
 | #include "test/gtest_and_gmock.h" | 
 |  | 
 | namespace perfetto { | 
 | namespace base { | 
 | namespace { | 
 |  | 
 | TEST(UtilsTest, ArraySize) { | 
 |   char char_arr_1[1]; | 
 |   char char_arr_4[4]; | 
 |   EXPECT_EQ(1u, ArraySize(char_arr_1)); | 
 |   EXPECT_EQ(4u, ArraySize(char_arr_4)); | 
 |  | 
 |   int32_t int32_arr_1[1]; | 
 |   int32_t int32_arr_4[4]; | 
 |   EXPECT_EQ(1u, ArraySize(int32_arr_1)); | 
 |   EXPECT_EQ(4u, ArraySize(int32_arr_4)); | 
 |  | 
 |   uint64_t int64_arr_1[1]; | 
 |   uint64_t int64_arr_4[4]; | 
 |   EXPECT_EQ(1u, ArraySize(int64_arr_1)); | 
 |   EXPECT_EQ(4u, ArraySize(int64_arr_4)); | 
 |  | 
 |   char kString[] = "foo"; | 
 |   EXPECT_EQ(4u, ArraySize(kString)); | 
 |  | 
 |   struct Bar { | 
 |     int32_t a; | 
 |     int32_t b; | 
 |   }; | 
 |   Bar bar_1[1]; | 
 |   Bar bar_4[4]; | 
 |   EXPECT_EQ(1u, ArraySize(bar_1)); | 
 |   EXPECT_EQ(4u, ArraySize(bar_4)); | 
 | } | 
 |  | 
 | TEST(UtilsTest, PipeBlockingRW) { | 
 |   Pipe pipe = Pipe::Create(); | 
 |   std::string expected; | 
 |   expected.resize(1024 * 512u); | 
 |   for (size_t i = 0; i < expected.size(); i++) | 
 |     expected[i] = '!' + static_cast<char>(i % 64); | 
 |  | 
 |   std::thread writer([&] { | 
 |     std::string tx = expected; | 
 |     std::minstd_rand0 rnd_engine(0); | 
 |  | 
 |     while (!tx.empty()) { | 
 |       size_t wsize = static_cast<size_t>(rnd_engine() % 4096) + 1; | 
 |       wsize = std::min(wsize, tx.size()); | 
 |       WriteAllHandle(*pipe.wr, &tx[0], wsize); | 
 |       tx.erase(0, wsize); | 
 |     } | 
 |     pipe.wr.reset(); | 
 |   }); | 
 |  | 
 |   std::string actual; | 
 |   ASSERT_TRUE(ReadPlatformHandle(*pipe.rd, &actual)); | 
 |   ASSERT_EQ(actual, expected); | 
 |   writer.join(); | 
 | } | 
 |  | 
 | // Tests that WriteAllHandle and ReadPlatformHandle work as advertised. | 
 | // TODO(primiano): normalize File handling on Windows. Right now some places | 
 | // use POSIX-compat APIs that use "int" file descriptors (_open, _read, _write), | 
 | // some other places use WINAPI files (CreateFile(), ReadFile()), where the file | 
 | // is a HANDLE. | 
 | TEST(UtilsTest, ReadWritePlatformHandle) { | 
 |   auto tmp = TempDir::Create(); | 
 |   std::string payload = "foo\nbar\0baz\r\nqux"; | 
 |   std::string tmp_path = tmp.path() + "/temp.txt"; | 
 |  | 
 |   // Write a file using PlatformHandle. Note: the {} blocks are to make sure | 
 |   // that the file is automatically closed via RAII before being reopened. | 
 |   { | 
 |     ScopedPlatformHandle handle { | 
 | #if PERFETTO_BUILDFLAG(PERFETTO_OS_WIN) | 
 |       ::CreateFileA(tmp_path.c_str(), GENERIC_WRITE, 0, nullptr, CREATE_ALWAYS, | 
 |                     FILE_ATTRIBUTE_NORMAL, nullptr) | 
 | #else | 
 |       OpenFile(tmp_path, O_WRONLY | O_CREAT | O_TRUNC, 0644) | 
 | #endif | 
 |     }; | 
 |     ASSERT_TRUE(handle); | 
 |     ASSERT_EQ(WriteAllHandle(*handle, payload.data(), payload.size()), | 
 |               static_cast<ssize_t>(payload.size())); | 
 |   } | 
 |  | 
 |   // Read it back. | 
 |   { | 
 |     ScopedPlatformHandle handle { | 
 | #if PERFETTO_BUILDFLAG(PERFETTO_OS_WIN) | 
 |       ::CreateFileA(tmp_path.c_str(), GENERIC_READ, 0, nullptr, OPEN_EXISTING, | 
 |                     FILE_ATTRIBUTE_NORMAL, nullptr) | 
 | #else | 
 |       OpenFile(tmp_path, O_RDONLY) | 
 | #endif | 
 |     }; | 
 |     ASSERT_TRUE(handle); | 
 |     std::string actual = "preserve_existing:"; | 
 |     ASSERT_TRUE(ReadPlatformHandle(*handle, &actual)); | 
 |     ASSERT_EQ(actual, "preserve_existing:" + payload); | 
 |   } | 
 |  | 
 |   ASSERT_EQ(remove(tmp_path.c_str()), 0); | 
 | } | 
 |  | 
 | // Fuchsia doesn't currently support sigaction(), see | 
 | // fuchsia.atlassian.net/browse/ZX-560. | 
 | #if PERFETTO_BUILDFLAG(PERFETTO_OS_LINUX) ||   \ | 
 |     PERFETTO_BUILDFLAG(PERFETTO_OS_ANDROID) || \ | 
 |     PERFETTO_BUILDFLAG(PERFETTO_OS_APPLE) | 
 | TEST(UtilsTest, EintrWrapper) { | 
 |   Pipe pipe = Pipe::Create(); | 
 |  | 
 |   struct sigaction sa = {}; | 
 |   struct sigaction old_sa = {}; | 
 |  | 
 | // Glibc headers for sa_sigaction trigger this. | 
 | #pragma GCC diagnostic push | 
 | #if defined(__clang__) | 
 | #pragma GCC diagnostic ignored "-Wdisabled-macro-expansion" | 
 | #endif | 
 |   sa.sa_sigaction = [](int, siginfo_t*, void*) {}; | 
 | #pragma GCC diagnostic pop | 
 |  | 
 |   ASSERT_EQ(0, sigaction(SIGUSR2, &sa, &old_sa)); | 
 |   int parent_pid = getpid(); | 
 |   pid_t pid = fork(); | 
 |   ASSERT_NE(-1, pid); | 
 |   if (pid == 0 /* child */) { | 
 |     usleep(5000); | 
 |     kill(parent_pid, SIGUSR2); | 
 |     ignore_result(WriteAll(*pipe.wr, "foo\0", 4)); | 
 |     _exit(0); | 
 |   } | 
 |  | 
 |   char buf[6] = {}; | 
 |   EXPECT_EQ(4, PERFETTO_EINTR(read(*pipe.rd, buf, sizeof(buf)))); | 
 |   EXPECT_TRUE(close(*pipe.rd) == 0 || errno == EINTR); | 
 |   pipe.wr.reset(); | 
 |  | 
 |   // A 2nd close should fail with the proper errno. | 
 |   int res = close(*pipe.rd); | 
 |   auto err = errno; | 
 |   EXPECT_EQ(-1, res); | 
 |   EXPECT_EQ(EBADF, err); | 
 |   pipe.rd.release(); | 
 |  | 
 |   // Restore the old handler. | 
 |   sigaction(SIGUSR2, &old_sa, nullptr); | 
 | } | 
 | #endif  // LINUX | ANDROID | APPLE | 
 |  | 
 | TEST(UtilsTest, Align) { | 
 |   EXPECT_EQ(0u, AlignUp<4>(0)); | 
 |   EXPECT_EQ(4u, AlignUp<4>(1)); | 
 |   EXPECT_EQ(4u, AlignUp<4>(3)); | 
 |   EXPECT_EQ(4u, AlignUp<4>(4)); | 
 |   EXPECT_EQ(8u, AlignUp<4>(5)); | 
 |   EXPECT_EQ(0u, AlignUp<16>(0)); | 
 |   EXPECT_EQ(16u, AlignUp<16>(1)); | 
 |   EXPECT_EQ(16u, AlignUp<16>(15)); | 
 |   EXPECT_EQ(16u, AlignUp<16>(16)); | 
 |   EXPECT_EQ(32u, AlignUp<16>(17)); | 
 |   EXPECT_EQ(0xffffff00u, AlignUp<16>(0xffffff00 - 1)); | 
 | } | 
 |  | 
 | }  // namespace | 
 | }  // namespace base | 
 | }  // namespace perfetto |