Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[lldb] Interactive command line interface nonfunctional on Windows with cross-platform libedit #121715

Open
lulle2007200 opened this issue Jan 6, 2025 · 1 comment

Comments

@lulle2007200
Copy link

I have built LLDB 19.1.6 on windows with libedit support using this cross-platform libedit port: https://github.com/michaeljclark/libedit.

Here is the cmake command used:

cmake -S llvm -B build -G Ninja `
-DCMAKE_BUILD_TYPE=RelWithDebInfo -DCMAKE_INSTALL_PREFIX="install" `
-DCMAKE_CXX_COMPILER=cl -DCMAKE_C_COMPILER=cl `
-DLLDB_EMBED_PYTHON_HOME=0 `
-DLLVM_ENABLE_PROJECTS="lldb;clang" `
-DLLDB_INCLUDE_TESTS=Off `
-DLLDB_ENABLE_LIBEDIT=On `
-DLLDB_ENABLE_CURSES=Off `
-DLLDB_ENABLE_LZMA=Off `
-DLLDB_ENABLE_LIBXML2=Off `
-DLLDB_ENABLE_PYTHON=Off `
-DLLDB_ENABLE_LUA=Off `
-DLibEdit_LIBRARIES="C:\Program Files (x86)\Windows Kits\10\Lib\10.0.22621.0\um\x64\Crypt32.Lib;C:\Program Files (x86)\Windows Kits\10\Lib\10.0.22621.0\um\x64\Winmm.Lib;C:\Program Files (x86)\Windows Kits\10\Lib\10.0.22621.0\um\x64\UserEnv.Lib;C:\Program Files (x86)\Windows Kits\10\Lib\10.0.22621.0\um\x64\Secur32.Lib;C:\Users\Christoph\Dev\libedit\install\lib\edit.lib;C:\libedit\install\lib\compat.lib;C:\libedit\install\lib\regex.lib;C:\libedit\install\lib\terminfo.lib;C:\libedit\install\lib\edit.lib;C:\libedit\install\lib\pdcurses.lib" `
-DLibEdit_INCLUDE_DIRS="C:\libedit\libedit"

The getline function libedit provides in libedit\compat\win32 is incomplete and doesn't return -1 on errors or when no characters were read and doesn't handle memory allocations. I have replaced it with the following:

ssize_t getline(char **lineptr, size_t *n, FILE *stream)
{
   size_t lim = *n;
   size_t size = *n;
   char *s = *lineptr;

   if(s && !lim){
      abort();
   }

   if(!s){
      size = 0;
      lim = 0;
   }

   int c, i = 0;
   while (--lim > 0 && (c=fgetc(stream)) != EOF && c != '\n'){
      if(i >= size){
         size += 80;
         char *ptr = (char*)realloc(s, size);
         if(!ptr){
            goto error;
         }
         s = ptr;
      }
      s[i++] = c;
   }

   if (c == '\n'){
      if(i >= size){
         size += 80;
         char *ptr = (char*)realloc(s, size);
         if(!ptr){
            goto error;
         }
         s = ptr;
      }
      s[i++] = c;
   }

   if(i >= size){
      size += 80;
      char *ptr = (char*)realloc(s, size);
      if(!ptr){
         goto error;
      }
      s = ptr;
   }
   s[i] = '\0';

   *lineptr = s;
   *n = size;

   if(i){
      return i;
   }
   return -1;

   error:
   free(s);
   *lineptr = NULL;
   *n = 0;
   return -1;
}

Pretty ugly, but works.

The resulting lldb executable is not functional.
Launching and attaching to a process by pid (e.g. lldb --attach-pid xxxx) etc. works fine, but the interactive command line is non-functional and does not accept any user keyboard input (command line stays blank).

Here is a call stack when suspending lldb:

SelectHelper.cpp Line 228   C++                   liblldb.dll!SelectHelper::Select()                                                                                                                                                                                                
ConnectionFileDescriptorPosix.cpp Line 477  C++   liblldb.dll!lldb_private::ConnectionFileDescriptor::BytesAvailable(const lldb_private::Timeout<std::ratio<1,1000000>> & timeout, lldb_private::Status * error_ptr)                                                                
ConnectionFileDescriptorPosix.cpp Line 272  C++   liblldb.dll!lldb_private::ConnectionFileDescriptor::Read(void * dst, unsigned __int64 dst_len, const lldb_private::Timeout<std::ratio<1,1000000>> & timeout, lldb::ConnectionStatus & status, lldb_private::Status * error_ptr)   
Editline.cpp Line 576   C++                       liblldb.dll!lldb_private::Editline::GetCharacter(wchar_t * c)                                                                                                                                                                     
read.c Line 401 C                                 liblldb.dll!el_wgetc(editline * el, wchar_t * cp)                                                                                                                                                                                 
read.c Line 225 C                                 liblldb.dll!read_getcmd(editline * el, unsigned char * cmdnum, wchar_t * ch)                                                                                                                                                  
read.c Line 514 C                                 liblldb.dll!el_wgets(editline * el, int * nread)                                                                                                                                                                              
eln.c Line 72   C                                 liblldb.dll!el_gets(editline * el, int * nread)                                                                                                                                                                               
Editline.cpp Line 1497  C++                       liblldb.dll!lldb_private::Editline::GetLine(std::string & line, bool & interrupted)                                                                                                                                           
IOHandler.cpp Line 354  C++                       liblldb.dll!lldb_private::IOHandlerEditline::GetLine(std::string & line, bool & interrupted)                                                                                                                                  
IOHandler.cpp Line 596  C++                       liblldb.dll!lldb_private::IOHandlerEditline::Run()                                                                                                                                                                                
Debugger.cpp Line 1107  C++                       liblldb.dll!lldb_private::Debugger::RunIOHandlers()                                                                                                                                                                           
CommandInterpreter.cpp Line 3404    C++           liblldb.dll!lldb_private::CommandInterpreter::RunCommandInterpreter(lldb_private::CommandInterpreterRunOptions & options)                                                                                                         
Line 1253 C++                                     liblldb.dll!lldb::SBDebugger::RunCommandInterpreter(bool auto_handle_events, bool spawn_thread) SBDebugger.cpp                                                                                                                  
Driver.cpp Line 621 C++                           lldb.exe!Driver::MainLoop()                                                                                                                                                                                                   
Driver.cpp Line 807 C++                           lldb.exe!main(int argc, const char * * argv)                                                                                                                                                                                  
	

In particular, the call

const int num_set_fds = ::select(nfds, read_fdset_ptr, write_fdset_ptr, error_fdset_ptr, tv_ptr);

in SelectHelper.cpp line 214 always fails and the loop

while (handle == m_io_sp->GetWaitableHandle()) { ... }`

in ConnectionFileDescriptorPosix.cpp line 473 is never left.

I'm not too familiar with the inner workings of lldb.

@llvmbot
Copy link
Member

llvmbot commented Jan 6, 2025

@llvm/issue-subscribers-lldb

Author: None (lulle2007200)

I have built LLDB 19.1.6 on windows with libedit support using this cross-platform libedit port: [https://github.com/michaeljclark/libedit](https://github.com/michaeljclark/libedit).

Here is the cmake command used:

cmake -S llvm -B build -G Ninja `
-DCMAKE_BUILD_TYPE=RelWithDebInfo -DCMAKE_INSTALL_PREFIX="install" `
-DCMAKE_CXX_COMPILER=cl -DCMAKE_C_COMPILER=cl `
-DLLDB_EMBED_PYTHON_HOME=0 `
-DLLVM_ENABLE_PROJECTS="lldb;clang" `
-DLLDB_INCLUDE_TESTS=Off `
-DLLDB_ENABLE_LIBEDIT=On `
-DLLDB_ENABLE_CURSES=Off `
-DLLDB_ENABLE_LZMA=Off `
-DLLDB_ENABLE_LIBXML2=Off `
-DLLDB_ENABLE_PYTHON=Off `
-DLLDB_ENABLE_LUA=Off `
-DLibEdit_LIBRARIES="C:\Program Files (x86)\Windows Kits\10\Lib\10.0.22621.0\um\x64\Crypt32.Lib;C:\Program Files (x86)\Windows Kits\10\Lib\10.0.22621.0\um\x64\Winmm.Lib;C:\Program Files (x86)\Windows Kits\10\Lib\10.0.22621.0\um\x64\UserEnv.Lib;C:\Program Files (x86)\Windows Kits\10\Lib\10.0.22621.0\um\x64\Secur32.Lib;C:\Users\Christoph\Dev\libedit\install\lib\edit.lib;C:\libedit\install\lib\compat.lib;C:\libedit\install\lib\regex.lib;C:\libedit\install\lib\terminfo.lib;C:\libedit\install\lib\edit.lib;C:\libedit\install\lib\pdcurses.lib" `
-DLibEdit_INCLUDE_DIRS="C:\libedit\libedit"

The getline function libedit provides in libedit\compat\win32 is incomplete and doesn't return -1 on errors or when no characters were read and doesn't handle memory allocations. I have replaced it with the following:

ssize_t getline(char **lineptr, size_t *n, FILE *stream)
{
   size_t lim = *n;
   size_t size = *n;
   char *s = *lineptr;

   if(s &amp;&amp; !lim){
      abort();
   }

   if(!s){
      size = 0;
      lim = 0;
   }

   int c, i = 0;
   while (--lim &gt; 0 &amp;&amp; (c=fgetc(stream)) != EOF &amp;&amp; c != '\n'){
      if(i &gt;= size){
         size += 80;
         char *ptr = (char*)realloc(s, size);
         if(!ptr){
            goto error;
         }
         s = ptr;
      }
      s[i++] = c;
   }

   if (c == '\n'){
      if(i &gt;= size){
         size += 80;
         char *ptr = (char*)realloc(s, size);
         if(!ptr){
            goto error;
         }
         s = ptr;
      }
      s[i++] = c;
   }

   if(i &gt;= size){
      size += 80;
      char *ptr = (char*)realloc(s, size);
      if(!ptr){
         goto error;
      }
      s = ptr;
   }
   s[i] = '\0';

   *lineptr = s;
   *n = size;

   if(i){
      return i;
   }
   return -1;

   error:
   free(s);
   *lineptr = NULL;
   *n = 0;
   return -1;
}

Pretty ugly, but works.

The resulting lldb executable is not functional.
Launching and attaching to a process by pid (e.g. lldb --attach-pid xxxx) etc. works fine, but the interactive command line is non-functional and does not accept any user keyboard input (command line stays blank).

Here is a call stack when suspending lldb:

SelectHelper.cpp Line 228   C++                   liblldb.dll!SelectHelper::Select()                                                                                                                                                                                                
ConnectionFileDescriptorPosix.cpp Line 477  C++   liblldb.dll!lldb_private::ConnectionFileDescriptor::BytesAvailable(const lldb_private::Timeout&lt;std::ratio&lt;1,1000000&gt;&gt; &amp; timeout, lldb_private::Status * error_ptr)                                                                
ConnectionFileDescriptorPosix.cpp Line 272  C++   liblldb.dll!lldb_private::ConnectionFileDescriptor::Read(void * dst, unsigned __int64 dst_len, const lldb_private::Timeout&lt;std::ratio&lt;1,1000000&gt;&gt; &amp; timeout, lldb::ConnectionStatus &amp; status, lldb_private::Status * error_ptr)   
Editline.cpp Line 576   C++                       liblldb.dll!lldb_private::Editline::GetCharacter(wchar_t * c)                                                                                                                                                                     
read.c Line 401 C                                 liblldb.dll!el_wgetc(editline * el, wchar_t * cp)                                                                                                                                                                                 
read.c Line 225 C                                 liblldb.dll!read_getcmd(editline * el, unsigned char * cmdnum, wchar_t * ch)                                                                                                                                                  
read.c Line 514 C                                 liblldb.dll!el_wgets(editline * el, int * nread)                                                                                                                                                                              
eln.c Line 72   C                                 liblldb.dll!el_gets(editline * el, int * nread)                                                                                                                                                                               
Editline.cpp Line 1497  C++                       liblldb.dll!lldb_private::Editline::GetLine(std::string &amp; line, bool &amp; interrupted)                                                                                                                                           
IOHandler.cpp Line 354  C++                       liblldb.dll!lldb_private::IOHandlerEditline::GetLine(std::string &amp; line, bool &amp; interrupted)                                                                                                                                  
IOHandler.cpp Line 596  C++                       liblldb.dll!lldb_private::IOHandlerEditline::Run()                                                                                                                                                                                
Debugger.cpp Line 1107  C++                       liblldb.dll!lldb_private::Debugger::RunIOHandlers()                                                                                                                                                                           
CommandInterpreter.cpp Line 3404    C++           liblldb.dll!lldb_private::CommandInterpreter::RunCommandInterpreter(lldb_private::CommandInterpreterRunOptions &amp; options)                                                                                                         
Line 1253 C++                                     liblldb.dll!lldb::SBDebugger::RunCommandInterpreter(bool auto_handle_events, bool spawn_thread) SBDebugger.cpp                                                                                                                  
Driver.cpp Line 621 C++                           lldb.exe!Driver::MainLoop()                                                                                                                                                                                                   
Driver.cpp Line 807 C++                           lldb.exe!main(int argc, const char * * argv)                                                                                                                                                                                  
	

In particular, the call

const int num_set_fds = ::select(nfds, read_fdset_ptr, write_fdset_ptr, error_fdset_ptr, tv_ptr);

in SelectHelper.cpp line 214 always fails and the loop

while (handle == m_io_sp-&gt;GetWaitableHandle()) { ... }`

in ConnectionFileDescriptorPosix.cpp line 473 is never left.

I'm not too familiar with the inner workings of lldb.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

3 participants