[Lldb-commits] [lldb] 92fb574 - [lldb] [Host] Add setters for common teletype properties to Terminal
Michał Górny via lldb-commits
lldb-commits at lists.llvm.org
Thu Oct 21 01:33:45 PDT 2021
Author: Michał Górny
Date: 2021-10-21T10:33:38+02:00
New Revision: 92fb574c9f20aef56948c51f4921a92efbb72a5b
URL: https://github.com/llvm/llvm-project/commit/92fb574c9f20aef56948c51f4921a92efbb72a5b
DIFF: https://github.com/llvm/llvm-project/commit/92fb574c9f20aef56948c51f4921a92efbb72a5b.diff
LOG: [lldb] [Host] Add setters for common teletype properties to Terminal
Add setters for common teletype properties to the Terminal class:
- SetRaw() to enable common raw mode options
- SetBaudRate() to set the baud rate
- SetStopBits() to select the number of stop bits
- SetParity() to control parity bit in the output
- SetHardwareControlFlow() to enable or disable hardware control flow
(if supported)
Differential Revision: https://reviews.llvm.org/D111030
Added:
Modified:
lldb/include/lldb/Host/Terminal.h
lldb/source/Host/common/Terminal.cpp
lldb/unittests/Host/posix/TerminalTest.cpp
Removed:
################################################################################
diff --git a/lldb/include/lldb/Host/Terminal.h b/lldb/include/lldb/Host/Terminal.h
index f8f5d5d16ef90..ff5db4c6b9b05 100644
--- a/lldb/include/lldb/Host/Terminal.h
+++ b/lldb/include/lldb/Host/Terminal.h
@@ -20,6 +20,14 @@ class TerminalState;
class Terminal {
public:
+ enum class Parity {
+ No,
+ Even,
+ Odd,
+ Space,
+ Mark,
+ };
+
Terminal(int fd = -1) : m_fd(fd) {}
~Terminal() = default;
@@ -38,6 +46,16 @@ class Terminal {
llvm::Error SetCanonical(bool enabled);
+ llvm::Error SetRaw();
+
+ llvm::Error SetBaudRate(unsigned int baud_rate);
+
+ llvm::Error SetStopBits(unsigned int stop_bits);
+
+ llvm::Error SetParity(Parity parity);
+
+ llvm::Error SetHardwareFlowControl(bool enabled);
+
protected:
struct Data;
diff --git a/lldb/source/Host/common/Terminal.cpp b/lldb/source/Host/common/Terminal.cpp
index 7050bae0c3c1c..6b7c2821d3783 100644
--- a/lldb/source/Host/common/Terminal.cpp
+++ b/lldb/source/Host/common/Terminal.cpp
@@ -94,6 +94,267 @@ llvm::Error Terminal::SetCanonical(bool enabled) {
#endif // LLDB_ENABLE_TERMIOS
}
+llvm::Error Terminal::SetRaw() {
+ llvm::Expected<Data> data = GetData();
+ if (!data)
+ return data.takeError();
+
+#if LLDB_ENABLE_TERMIOS
+ struct termios &fd_termios = data->m_termios;
+ ::cfmakeraw(&fd_termios);
+
+ // Make sure only one character is needed to return from a read
+ // (cfmakeraw() doesn't do this on NetBSD)
+ fd_termios.c_cc[VMIN] = 1;
+ fd_termios.c_cc[VTIME] = 0;
+
+ return SetData(data.get());
+#endif // #if LLDB_ENABLE_TERMIOS
+}
+
+static llvm::Optional<speed_t> baudRateToConst(unsigned int baud_rate) {
+ switch (baud_rate) {
+#if defined(B50)
+ case 50:
+ return B50;
+#endif
+#if defined(B75)
+ case 75:
+ return B75;
+#endif
+#if defined(B110)
+ case 110:
+ return B110;
+#endif
+#if defined(B134)
+ case 134:
+ return B134;
+#endif
+#if defined(B150)
+ case 150:
+ return B150;
+#endif
+#if defined(B200)
+ case 200:
+ return B200;
+#endif
+#if defined(B300)
+ case 300:
+ return B300;
+#endif
+#if defined(B600)
+ case 600:
+ return B600;
+#endif
+#if defined(B1200)
+ case 1200:
+ return B1200;
+#endif
+#if defined(B1800)
+ case 1800:
+ return B1800;
+#endif
+#if defined(B2400)
+ case 2400:
+ return B2400;
+#endif
+#if defined(B4800)
+ case 4800:
+ return B4800;
+#endif
+#if defined(B9600)
+ case 9600:
+ return B9600;
+#endif
+#if defined(B19200)
+ case 19200:
+ return B19200;
+#endif
+#if defined(B38400)
+ case 38400:
+ return B38400;
+#endif
+#if defined(B57600)
+ case 57600:
+ return B57600;
+#endif
+#if defined(B115200)
+ case 115200:
+ return B115200;
+#endif
+#if defined(B230400)
+ case 230400:
+ return B230400;
+#endif
+#if defined(B460800)
+ case 460800:
+ return B460800;
+#endif
+#if defined(B500000)
+ case 500000:
+ return B500000;
+#endif
+#if defined(B576000)
+ case 576000:
+ return B576000;
+#endif
+#if defined(B921600)
+ case 921600:
+ return B921600;
+#endif
+#if defined(B1000000)
+ case 1000000:
+ return B1000000;
+#endif
+#if defined(B1152000)
+ case 1152000:
+ return B1152000;
+#endif
+#if defined(B1500000)
+ case 1500000:
+ return B1500000;
+#endif
+#if defined(B2000000)
+ case 2000000:
+ return B2000000;
+#endif
+#if defined(B76800)
+ case 76800:
+ return B76800;
+#endif
+#if defined(B153600)
+ case 153600:
+ return B153600;
+#endif
+#if defined(B307200)
+ case 307200:
+ return B307200;
+#endif
+#if defined(B614400)
+ case 614400:
+ return B614400;
+#endif
+#if defined(B2500000)
+ case 2500000:
+ return B2500000;
+#endif
+#if defined(B3000000)
+ case 3000000:
+ return B3000000;
+#endif
+#if defined(B3500000)
+ case 3500000:
+ return B3500000;
+#endif
+#if defined(B4000000)
+ case 4000000:
+ return B4000000;
+#endif
+ default:
+ return llvm::None;
+ }
+}
+
+llvm::Error Terminal::SetBaudRate(unsigned int baud_rate) {
+ llvm::Expected<Data> data = GetData();
+ if (!data)
+ return data.takeError();
+
+#if LLDB_ENABLE_TERMIOS
+ struct termios &fd_termios = data->m_termios;
+ llvm::Optional<speed_t> val = baudRateToConst(baud_rate);
+ if (!val) // invalid value
+ return llvm::createStringError(llvm::inconvertibleErrorCode(),
+ "baud rate %d unsupported by the platform",
+ baud_rate);
+ if (::cfsetispeed(&fd_termios, val.getValue()) != 0)
+ return llvm::createStringError(
+ std::error_code(errno, std::generic_category()),
+ "setting input baud rate failed");
+ if (::cfsetospeed(&fd_termios, val.getValue()) != 0)
+ return llvm::createStringError(
+ std::error_code(errno, std::generic_category()),
+ "setting output baud rate failed");
+ return SetData(data.get());
+#endif // #if LLDB_ENABLE_TERMIOS
+}
+
+llvm::Error Terminal::SetStopBits(unsigned int stop_bits) {
+ llvm::Expected<Data> data = GetData();
+ if (!data)
+ return data.takeError();
+
+#if LLDB_ENABLE_TERMIOS
+ struct termios &fd_termios = data->m_termios;
+ switch (stop_bits) {
+ case 1:
+ fd_termios.c_cflag &= ~CSTOPB;
+ break;
+ case 2:
+ fd_termios.c_cflag |= CSTOPB;
+ break;
+ default:
+ return llvm::createStringError(
+ llvm::inconvertibleErrorCode(),
+ "invalid stop bit count: %d (must be 1 or 2)", stop_bits);
+ }
+ return SetData(data.get());
+#endif // #if LLDB_ENABLE_TERMIOS
+}
+
+llvm::Error Terminal::SetParity(Terminal::Parity parity) {
+ llvm::Expected<Data> data = GetData();
+ if (!data)
+ return data.takeError();
+
+#if LLDB_ENABLE_TERMIOS
+ struct termios &fd_termios = data->m_termios;
+ fd_termios.c_cflag &= ~(
+#if defined(CMSPAR)
+ CMSPAR |
+#endif
+ PARENB | PARODD);
+
+ if (parity != Parity::No) {
+ fd_termios.c_cflag |= PARENB;
+ if (parity == Parity::Odd || parity == Parity::Mark)
+ fd_termios.c_cflag |= PARODD;
+ if (parity == Parity::Mark || parity == Parity::Space) {
+#if defined(CMSPAR)
+ fd_termios.c_cflag |= CMSPAR;
+#else
+ return llvm::createStringError(
+ llvm::inconvertibleErrorCode(),
+ "space/mark parity is not supported by the platform");
+#endif
+ }
+ }
+ return SetData(data.get());
+#endif // #if LLDB_ENABLE_TERMIOS
+}
+
+llvm::Error Terminal::SetHardwareFlowControl(bool enabled) {
+ llvm::Expected<Data> data = GetData();
+ if (!data)
+ return data.takeError();
+
+#if LLDB_ENABLE_TERMIOS
+#if defined(CRTSCTS)
+ struct termios &fd_termios = data->m_termios;
+ fd_termios.c_cflag &= ~CRTSCTS;
+ if (enabled)
+ fd_termios.c_cflag |= CRTSCTS;
+ return SetData(data.get());
+#else // !defined(CRTSCTS)
+ if (enabled)
+ return llvm::createStringError(
+ llvm::inconvertibleErrorCode(),
+ "hardware flow control is not supported by the platform");
+ return llvm::Error::success();
+#endif // defined(CRTSCTS)
+#endif // #if LLDB_ENABLE_TERMIOS
+}
+
TerminalState::TerminalState(Terminal term, bool save_process_group)
: m_tty(term) {
Save(term, save_process_group);
diff --git a/lldb/unittests/Host/posix/TerminalTest.cpp b/lldb/unittests/Host/posix/TerminalTest.cpp
index cdc80bea95679..4a81bea88d9eb 100644
--- a/lldb/unittests/Host/posix/TerminalTest.cpp
+++ b/lldb/unittests/Host/posix/TerminalTest.cpp
@@ -72,6 +72,149 @@ TEST_F(TerminalTest, SetCanonical) {
EXPECT_EQ(terminfo.c_lflag & ICANON, 0U);
}
+TEST_F(TerminalTest, SetRaw) {
+ struct termios terminfo;
+
+ ASSERT_THAT_ERROR(m_term.SetRaw(), llvm::Succeeded());
+ ASSERT_EQ(tcgetattr(m_fd, &terminfo), 0);
+ // NB: cfmakeraw() on glibc disables IGNBRK, on FreeBSD sets it
+ EXPECT_EQ(terminfo.c_iflag &
+ (BRKINT | PARMRK | ISTRIP | INLCR | IGNCR | ICRNL | IXON),
+ 0U);
+ EXPECT_EQ(terminfo.c_oflag & OPOST, 0U);
+ EXPECT_EQ(terminfo.c_lflag & (ICANON | ECHO | ISIG | IEXTEN), 0U);
+ EXPECT_EQ(terminfo.c_cflag & (CSIZE | PARENB), 0U | CS8);
+ EXPECT_EQ(terminfo.c_cc[VMIN], 1);
+ EXPECT_EQ(terminfo.c_cc[VTIME], 0);
+}
+
+TEST_F(TerminalTest, SetBaudRate) {
+ struct termios terminfo;
+
+ ASSERT_THAT_ERROR(m_term.SetBaudRate(38400), llvm::Succeeded());
+ ASSERT_EQ(tcgetattr(m_fd, &terminfo), 0);
+ EXPECT_EQ(cfgetispeed(&terminfo), static_cast<speed_t>(B38400));
+ EXPECT_EQ(cfgetospeed(&terminfo), static_cast<speed_t>(B38400));
+
+ ASSERT_THAT_ERROR(m_term.SetBaudRate(115200), llvm::Succeeded());
+ ASSERT_EQ(tcgetattr(m_fd, &terminfo), 0);
+ EXPECT_EQ(cfgetispeed(&terminfo), static_cast<speed_t>(B115200));
+ EXPECT_EQ(cfgetospeed(&terminfo), static_cast<speed_t>(B115200));
+
+ // uncommon value
+#if defined(B153600)
+ ASSERT_THAT_ERROR(m_term.SetBaudRate(153600), llvm::Succeeded());
+ ASSERT_EQ(tcgetattr(m_fd, &terminfo), 0);
+ EXPECT_EQ(cfgetispeed(&terminfo), static_cast<speed_t>(B153600));
+ EXPECT_EQ(cfgetospeed(&terminfo), static_cast<speed_t>(B153600));
+#else
+ ASSERT_THAT_ERROR(m_term.SetBaudRate(153600),
+ llvm::Failed<llvm::ErrorInfoBase>(testing::Property(
+ &llvm::ErrorInfoBase::message,
+ "baud rate 153600 unsupported by the platform")));
+#endif
+}
+
+TEST_F(TerminalTest, SetStopBits) {
+ struct termios terminfo;
+
+ ASSERT_THAT_ERROR(m_term.SetStopBits(1), llvm::Succeeded());
+ ASSERT_EQ(tcgetattr(m_fd, &terminfo), 0);
+ EXPECT_EQ(terminfo.c_cflag & CSTOPB, 0U);
+
+ ASSERT_THAT_ERROR(m_term.SetStopBits(2), llvm::Succeeded());
+ ASSERT_EQ(tcgetattr(m_fd, &terminfo), 0);
+ EXPECT_NE(terminfo.c_cflag & CSTOPB, 0U);
+
+ ASSERT_THAT_ERROR(m_term.SetStopBits(0),
+ llvm::Failed<llvm::ErrorInfoBase>(testing::Property(
+ &llvm::ErrorInfoBase::message,
+ "invalid stop bit count: 0 (must be 1 or 2)")));
+ ASSERT_THAT_ERROR(m_term.SetStopBits(3),
+ llvm::Failed<llvm::ErrorInfoBase>(testing::Property(
+ &llvm::ErrorInfoBase::message,
+ "invalid stop bit count: 3 (must be 1 or 2)")));
+}
+
+TEST_F(TerminalTest, SetParity) {
+ struct termios terminfo;
+
+ ASSERT_THAT_ERROR(m_term.SetParity(Terminal::Parity::No), llvm::Succeeded());
+ ASSERT_EQ(tcgetattr(m_fd, &terminfo), 0);
+ EXPECT_EQ(terminfo.c_cflag & PARENB, 0U);
+
+ ASSERT_THAT_ERROR(m_term.SetParity(Terminal::Parity::Even),
+ llvm::Succeeded());
+ ASSERT_EQ(tcgetattr(m_fd, &terminfo), 0);
+#if !defined(__linux__) // Linux pty devices strip PARENB
+ EXPECT_NE(terminfo.c_cflag & PARENB, 0U);
+#endif
+ EXPECT_EQ(terminfo.c_cflag & PARODD, 0U);
+#if defined(CMSPAR)
+ EXPECT_EQ(terminfo.c_cflag & CMSPAR, 0U);
+#endif
+
+ ASSERT_THAT_ERROR(m_term.SetParity(Terminal::Parity::Odd), llvm::Succeeded());
+ ASSERT_EQ(tcgetattr(m_fd, &terminfo), 0);
+#if !defined(__linux__) // Linux pty devices strip PARENB
+ EXPECT_NE(terminfo.c_cflag & PARENB, 0U);
+#endif
+ EXPECT_NE(terminfo.c_cflag & PARODD, 0U);
+#if defined(CMSPAR)
+ EXPECT_EQ(terminfo.c_cflag & CMSPAR, 0U);
+#endif
+
+#if defined(CMSPAR)
+ ASSERT_THAT_ERROR(m_term.SetParity(Terminal::Parity::Space),
+ llvm::Succeeded());
+ ASSERT_EQ(tcgetattr(m_fd, &terminfo), 0);
+#if !defined(__linux__) // Linux pty devices strip PARENB
+ EXPECT_NE(terminfo.c_cflag & PARENB, 0U);
+#endif
+ EXPECT_EQ(terminfo.c_cflag & PARODD, 0U);
+ EXPECT_NE(terminfo.c_cflag & CMSPAR, 0U);
+
+ ASSERT_THAT_ERROR(m_term.SetParity(Terminal::Parity::Mark),
+ llvm::Succeeded());
+ ASSERT_EQ(tcgetattr(m_fd, &terminfo), 0);
+#if !defined(__linux__) // Linux pty devices strip PARENB
+ EXPECT_NE(terminfo.c_cflag & PARENB, 0U);
+#endif
+ EXPECT_NE(terminfo.c_cflag & PARODD, 0U);
+ EXPECT_NE(terminfo.c_cflag & CMSPAR, 0U);
+#else
+ ASSERT_THAT_ERROR(m_term.SetParity(Terminal::Parity::Space),
+ llvm::Failed<llvm::ErrorInfoBase>(testing::Property(
+ &llvm::ErrorInfoBase::message,
+ "space/mark parity is not supported by the platform")));
+ ASSERT_THAT_ERROR(m_term.SetParity(Terminal::Parity::Mark),
+ llvm::Failed<llvm::ErrorInfoBase>(testing::Property(
+ &llvm::ErrorInfoBase::message,
+ "space/mark parity is not supported by the platform")));
+#endif
+}
+
+TEST_F(TerminalTest, SetHardwareFlowControl) {
+#if defined(CRTSCTS)
+ struct termios terminfo;
+
+ ASSERT_THAT_ERROR(m_term.SetHardwareFlowControl(true), llvm::Succeeded());
+ ASSERT_EQ(tcgetattr(m_fd, &terminfo), 0);
+ EXPECT_NE(terminfo.c_cflag & CRTSCTS, 0U);
+
+ ASSERT_THAT_ERROR(m_term.SetHardwareFlowControl(false), llvm::Succeeded());
+ ASSERT_EQ(tcgetattr(m_fd, &terminfo), 0);
+ EXPECT_EQ(terminfo.c_cflag & CRTSCTS, 0U);
+#else
+ ASSERT_THAT_ERROR(
+ m_term.SetHardwareFlowControl(true),
+ llvm::Failed<llvm::ErrorInfoBase>(testing::Property(
+ &llvm::ErrorInfoBase::message,
+ "hardware flow control is not supported by the platform")));
+ ASSERT_THAT_ERROR(m_term.SetHardwareFlowControl(false), llvm::Succeeded());
+#endif
+}
+
TEST_F(TerminalTest, SaveRestoreRAII) {
struct termios orig_terminfo;
struct termios terminfo;
More information about the lldb-commits
mailing list