Home | History | Annotate | Download | only in tests
      1 /*
      2  * Copyright (C) 2014 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 #include <pty.h>
     18 
     19 #include <gtest/gtest.h>
     20 
     21 #include <pthread.h>
     22 #include <sys/ioctl.h>
     23 
     24 #include <atomic>
     25 
     26 #include <android-base/file.h>
     27 
     28 #include "utils.h"
     29 
     30 TEST(pty, openpty) {
     31   int master, slave;
     32   char name[32];
     33   struct winsize w = { 123, 456, 9999, 999 };
     34   ASSERT_EQ(0, openpty(&master, &slave, name, NULL, &w));
     35   ASSERT_NE(-1, master);
     36   ASSERT_NE(-1, slave);
     37   ASSERT_NE(master, slave);
     38 
     39   char tty_name[32];
     40   ASSERT_EQ(0, ttyname_r(slave, tty_name, sizeof(tty_name)));
     41   ASSERT_STREQ(tty_name, name);
     42 
     43   struct winsize w_actual;
     44   ASSERT_EQ(0, ioctl(slave, TIOCGWINSZ, &w_actual));
     45   ASSERT_EQ(w_actual.ws_row, w.ws_row);
     46   ASSERT_EQ(w_actual.ws_col, w.ws_col);
     47   ASSERT_EQ(w_actual.ws_xpixel, w.ws_xpixel);
     48   ASSERT_EQ(w_actual.ws_ypixel, w.ws_ypixel);
     49 
     50   close(master);
     51   close(slave);
     52 }
     53 
     54 TEST(pty, forkpty) {
     55   pid_t sid = getsid(0);
     56 
     57   int master;
     58   pid_t pid = forkpty(&master, NULL, NULL, NULL);
     59   ASSERT_NE(-1, pid);
     60 
     61   if (pid == 0) {
     62     // We're the child.
     63     ASSERT_NE(sid, getsid(0));
     64     _exit(0);
     65   }
     66 
     67   ASSERT_EQ(sid, getsid(0));
     68 
     69   AssertChildExited(pid, 0);
     70 
     71   close(master);
     72 }
     73 
     74 struct PtyReader_28979140_Arg {
     75   int main_cpu_id;
     76   int slave_fd;
     77   uint32_t data_count;
     78   bool finished;
     79   std::atomic<bool> matched;
     80 };
     81 
     82 static void PtyReader_28979140(PtyReader_28979140_Arg* arg) {
     83   arg->finished = false;
     84   cpu_set_t cpus;
     85   ASSERT_EQ(0, sched_getaffinity(0, sizeof(cpu_set_t), &cpus));
     86   CPU_CLR(arg->main_cpu_id, &cpus);
     87   ASSERT_EQ(0, sched_setaffinity(0, sizeof(cpu_set_t), &cpus));
     88 
     89   uint32_t counter = 0;
     90   while (counter <= arg->data_count) {
     91     char buf[4096];  // Use big buffer to read to hit the bug more easily.
     92     size_t to_read = std::min(sizeof(buf), (arg->data_count + 1 - counter) * sizeof(uint32_t));
     93     ASSERT_TRUE(android::base::ReadFully(arg->slave_fd, buf, to_read));
     94     size_t num_of_value = to_read / sizeof(uint32_t);
     95     uint32_t* p = reinterpret_cast<uint32_t*>(buf);
     96     while (num_of_value-- > 0) {
     97       if (*p++ != counter++) {
     98         arg->matched = false;
     99       }
    100     }
    101   }
    102   close(arg->slave_fd);
    103   arg->finished = true;
    104 }
    105 
    106 TEST(pty, bug_28979140) {
    107   // This test is to test a kernel bug, which uses a lock free ring-buffer to
    108   // pass data through a raw pty, but missing necessary memory barriers.
    109   cpu_set_t cpus;
    110   ASSERT_EQ(0, sched_getaffinity(0, sizeof(cpu_set_t), &cpus));
    111   if (CPU_COUNT(&cpus) < 2) {
    112     GTEST_LOG_(INFO) << "This test tests bug happens only on multiprocessors.";
    113     return;
    114   }
    115   constexpr uint32_t TEST_DATA_COUNT = 2000000;
    116 
    117   // 1. Open raw pty.
    118   int master;
    119   int slave;
    120   ASSERT_EQ(0, openpty(&master, &slave, nullptr, nullptr, nullptr));
    121   termios tattr;
    122   ASSERT_EQ(0, tcgetattr(slave, &tattr));
    123   cfmakeraw(&tattr);
    124   ASSERT_EQ(0, tcsetattr(slave, TCSADRAIN, &tattr));
    125 
    126   // 2. Make master thread and slave thread running on different cpus:
    127   // master thread uses first available cpu, and slave thread uses other cpus.
    128   PtyReader_28979140_Arg arg;
    129   arg.main_cpu_id = -1;
    130   for (int i = 0; i < CPU_SETSIZE; i++) {
    131     if (CPU_ISSET(i, &cpus)) {
    132       arg.main_cpu_id = i;
    133       break;
    134     }
    135   }
    136   ASSERT_GE(arg.main_cpu_id, 0);
    137 
    138   // 3. Create thread for slave reader.
    139   pthread_t thread;
    140   arg.slave_fd = slave;
    141   arg.data_count = TEST_DATA_COUNT;
    142   arg.matched = true;
    143   ASSERT_EQ(0, pthread_create(&thread, nullptr,
    144                               reinterpret_cast<void*(*)(void*)>(PtyReader_28979140),
    145                               &arg));
    146 
    147   CPU_ZERO(&cpus);
    148   CPU_SET(arg.main_cpu_id, &cpus);
    149   ASSERT_EQ(0, sched_setaffinity(0, sizeof(cpu_set_t), &cpus));
    150 
    151   // 4. Send data to slave.
    152   // Send a bunch of data at a time, so it is easier to catch the bug that some data isn't seen
    153   // by the reader thread on another cpu.
    154   uint32_t counter_buf[100];
    155   uint32_t counter = 0;
    156   while (counter <= TEST_DATA_COUNT) {
    157     for (size_t i = 0; i < sizeof(counter_buf) / sizeof(counter_buf[0]); ++i) {
    158       counter_buf[i] = counter++;
    159     }
    160     ASSERT_TRUE(android::base::WriteFully(master, &counter_buf, sizeof(counter_buf)));
    161     ASSERT_TRUE(arg.matched) << "failed at count = " << counter;
    162   }
    163   ASSERT_EQ(0, pthread_join(thread, nullptr));
    164   ASSERT_TRUE(arg.finished);
    165   ASSERT_TRUE(arg.matched);
    166   close(master);
    167 }
    168