IOChannel.cpp revision 882c554207e94f66ba8d652302a9d817f275dc4e
1//===-- IOChannel.cpp -------------------------------------------*- C++ -*-===// 2// 3// The LLVM Compiler Infrastructure 4// 5// This file is distributed under the University of Illinois Open Source 6// License. See LICENSE.TXT for details. 7// 8//===----------------------------------------------------------------------===// 9 10#include "IOChannel.h" 11 12#include <map> 13 14#include "lldb/API/SBCommandInterpreter.h" 15#include "lldb/API/SBDebugger.h" 16#include "lldb/API/SBError.h" 17#include "lldb/API/SBEvent.h" 18#include "lldb/API/SBFileSpec.h" 19#include "lldb/API/SBHostOS.h" 20#include "lldb/API/SBListener.h" 21#include "lldb/API/SBStringList.h" 22 23#include <string.h> 24#include <limits.h> 25 26using namespace lldb; 27 28typedef std::map<EditLine *, std::string> PromptMap; 29const char *g_default_prompt = "(lldb) "; 30PromptMap g_prompt_map; 31 32// Printing the following string causes libedit to back up to the beginning of the line & blank it out. 33const char undo_prompt_string[4] = { (char) 13, (char) 27, (char) 91, (char) 75}; 34 35static const char* 36el_prompt(EditLine *el) 37{ 38 PromptMap::const_iterator pos = g_prompt_map.find (el); 39 if (pos == g_prompt_map.end()) 40 return g_default_prompt; 41 return pos->second.c_str(); 42} 43 44const char * 45IOChannel::GetPrompt () 46{ 47 PromptMap::const_iterator pos = g_prompt_map.find (m_edit_line); 48 if (pos == g_prompt_map.end()) 49 return g_default_prompt; 50 return pos->second.c_str(); 51} 52 53unsigned char 54IOChannel::ElCompletionFn (EditLine *e, int ch) 55{ 56 IOChannel *io_channel; 57 if (el_get(e, EL_CLIENTDATA, &io_channel) == 0) 58 { 59 return io_channel->HandleCompletion (e, ch); 60 } 61 else 62 { 63 return CC_ERROR; 64 } 65} 66 67unsigned char 68IOChannel::HandleCompletion (EditLine *e, int ch) 69{ 70 assert (e == m_edit_line); 71 72 const LineInfo *line_info = el_line(m_edit_line); 73 SBStringList completions; 74 int page_size = 40; 75 76 int num_completions = m_driver->GetDebugger().GetCommandInterpreter().HandleCompletion (line_info->buffer, 77 line_info->cursor, 78 line_info->lastchar, 79 0, 80 -1, 81 completions); 82 83 if (num_completions == -1) 84 { 85 el_insertstr (m_edit_line, m_completion_key); 86 return CC_REDISPLAY; 87 } 88 89 // If we get a longer match display that first. 90 const char *completion_str = completions.GetStringAtIndex(0); 91 if (completion_str != NULL && *completion_str != '\0') 92 { 93 el_insertstr (m_edit_line, completion_str); 94 return CC_REDISPLAY; 95 } 96 97 if (num_completions > 1) 98 { 99 const char *comment = "\nAvailable completions:"; 100 101 int num_elements = num_completions + 1; 102 OutWrite(comment, strlen (comment), NO_ASYNC); 103 if (num_completions < page_size) 104 { 105 for (int i = 1; i < num_elements; i++) 106 { 107 completion_str = completions.GetStringAtIndex(i); 108 OutWrite("\n\t", 2, NO_ASYNC); 109 OutWrite(completion_str, strlen (completion_str), NO_ASYNC); 110 } 111 OutWrite ("\n", 1, NO_ASYNC); 112 } 113 else 114 { 115 int cur_pos = 1; 116 char reply; 117 int got_char; 118 while (cur_pos < num_elements) 119 { 120 int endpoint = cur_pos + page_size; 121 if (endpoint > num_elements) 122 endpoint = num_elements; 123 for (; cur_pos < endpoint; cur_pos++) 124 { 125 completion_str = completions.GetStringAtIndex(cur_pos); 126 OutWrite("\n\t", 2, NO_ASYNC); 127 OutWrite(completion_str, strlen (completion_str), NO_ASYNC); 128 } 129 130 if (cur_pos >= num_elements) 131 { 132 OutWrite("\n", 1, NO_ASYNC); 133 break; 134 } 135 136 OutWrite("\nMore (Y/n/a): ", strlen ("\nMore (Y/n/a): "), NO_ASYNC); 137 reply = 'n'; 138 got_char = el_getc(m_edit_line, &reply); 139 if (got_char == -1 || reply == 'n') 140 break; 141 if (reply == 'a') 142 page_size = num_elements - cur_pos; 143 } 144 } 145 146 } 147 148 if (num_completions == 0) 149 return CC_REFRESH_BEEP; 150 else 151 return CC_REDISPLAY; 152} 153 154IOChannel::IOChannel 155( 156 FILE *editline_in, 157 FILE *editline_out, 158 FILE *out, 159 FILE *err, 160 Driver *driver 161) : 162 SBBroadcaster ("IOChannel"), 163 m_output_mutex (), 164 m_enter_elgets_time (), 165 m_driver (driver), 166 m_read_thread (LLDB_INVALID_HOST_THREAD), 167 m_read_thread_should_exit (false), 168 m_out_file (out), 169 m_err_file (err), 170 m_command_queue (), 171 m_completion_key ("\t"), 172 m_edit_line (::el_init (SBHostOS::GetProgramFileSpec().GetFilename(), editline_in, editline_out, editline_out)), 173 m_history (history_init()), 174 m_history_event(), 175 m_getting_command (false), 176 m_expecting_prompt (false), 177 m_prompt_str (), 178 m_refresh_request_pending (false) 179{ 180 assert (m_edit_line); 181 ::el_set (m_edit_line, EL_PROMPT, el_prompt); 182 ::el_set (m_edit_line, EL_EDITOR, "emacs"); 183 ::el_set (m_edit_line, EL_HIST, history, m_history); 184 185 // Source $PWD/.editrc then $HOME/.editrc 186 ::el_source (m_edit_line, NULL); 187 188 el_set (m_edit_line, EL_ADDFN, "lldb_complete", 189 "LLDB completion function", 190 IOChannel::ElCompletionFn); 191 el_set (m_edit_line, EL_BIND, m_completion_key, "lldb_complete", NULL); 192 el_set (m_edit_line, EL_BIND, "^r", "em-inc-search-prev", NULL); // Cycle through backwards search, entering string 193 el_set (m_edit_line, EL_CLIENTDATA, this); 194 195 assert (m_history); 196 ::history (m_history, &m_history_event, H_SETSIZE, 800); 197 ::history (m_history, &m_history_event, H_SETUNIQUE, 1); 198 // Load history 199 HistorySaveLoad (false); 200 201 // Set up mutex to make sure OutErr, OutWrite and RefreshPrompt do not interfere 202 // with each other when writing. 203 204 int error; 205 ::pthread_mutexattr_t attr; 206 error = ::pthread_mutexattr_init (&attr); 207 assert (error == 0); 208 error = ::pthread_mutexattr_settype (&attr, PTHREAD_MUTEX_RECURSIVE); 209 assert (error == 0); 210 error = ::pthread_mutex_init (&m_output_mutex, &attr); 211 assert (error == 0); 212 error = ::pthread_mutexattr_destroy (&attr); 213 assert (error == 0); 214 215 // Initialize time that ::el_gets was last called. 216 217 m_enter_elgets_time.tv_sec = 0; 218 m_enter_elgets_time.tv_usec = 0; 219} 220 221IOChannel::~IOChannel () 222{ 223 // Save history 224 HistorySaveLoad (true); 225 226 if (m_history != NULL) 227 { 228 ::history_end (m_history); 229 m_history = NULL; 230 } 231 232 if (m_edit_line != NULL) 233 { 234 ::el_end (m_edit_line); 235 m_edit_line = NULL; 236 } 237 238 ::pthread_mutex_destroy (&m_output_mutex); 239} 240 241void 242IOChannel::HistorySaveLoad (bool save) 243{ 244 if (m_history != NULL) 245 { 246 char history_path[PATH_MAX]; 247 ::snprintf (history_path, sizeof(history_path), "~/.%s-history", SBHostOS::GetProgramFileSpec().GetFilename()); 248 if ((size_t)SBFileSpec::ResolvePath (history_path, history_path, sizeof(history_path)) < sizeof(history_path) - 1) 249 { 250 const char *path_ptr = history_path; 251 if (save) 252 ::history (m_history, &m_history_event, H_SAVE, path_ptr); 253 else 254 ::history (m_history, &m_history_event, H_LOAD, path_ptr); 255 } 256 } 257} 258 259void 260IOChannel::LibeditOutputBytesReceived (void *baton, const void *src, size_t src_len) 261{ 262 // Make this a member variable. 263 // static std::string prompt_str; 264 IOChannel *io_channel = (IOChannel *) baton; 265 IOLocker locker (io_channel->m_output_mutex); 266 const char *bytes = (const char *) src; 267 268 if (io_channel->IsGettingCommand() && io_channel->m_expecting_prompt) 269 { 270 io_channel->m_prompt_str.append (bytes, src_len); 271 // Log this to make sure the prompt is really what you think it is. 272 if (io_channel->m_prompt_str.find (el_prompt(io_channel->m_edit_line)) == 0) 273 { 274 io_channel->m_expecting_prompt = false; 275 io_channel->m_refresh_request_pending = false; 276 io_channel->OutWrite (io_channel->m_prompt_str.c_str(), 277 io_channel->m_prompt_str.size(), NO_ASYNC); 278 io_channel->m_prompt_str.clear(); 279 } 280 } 281 else 282 { 283 if (io_channel->m_prompt_str.size() > 0) 284 io_channel->m_prompt_str.clear(); 285 std::string tmp_str (bytes, src_len); 286 if (tmp_str.find (el_prompt (io_channel->m_edit_line)) == 0) 287 io_channel->m_refresh_request_pending = false; 288 io_channel->OutWrite (bytes, src_len, NO_ASYNC); 289 } 290} 291 292bool 293IOChannel::LibeditGetInput (std::string &new_line) 294{ 295 if (m_edit_line != NULL) 296 { 297 int line_len = 0; 298 299 // Set boolean indicating whether or not el_gets is trying to get input (i.e. whether or not to attempt 300 // to refresh the prompt after writing data). 301 SetGettingCommand (true); 302 m_expecting_prompt = true; 303 304 // Call el_gets to prompt the user and read the user's input. 305 const char *line = ::el_gets (m_edit_line, &line_len); 306 307 // Re-set the boolean indicating whether or not el_gets is trying to get input. 308 SetGettingCommand (false); 309 310 if (line) 311 { 312 // strip any newlines off the end of the string... 313 while (line_len > 0 && (line[line_len - 1] == '\n' || line[line_len - 1] == '\r')) 314 --line_len; 315 if (line_len > 0) 316 { 317 ::history (m_history, &m_history_event, H_ENTER, line); 318 new_line.assign (line, line_len); // Omit the newline 319 } 320 else 321 { 322 // Someone just hit ENTER, return the empty string 323 new_line.clear(); 324 } 325 // Return true to indicate success even if a string is empty 326 return true; 327 } 328 } 329 // Return false to indicate failure. This can happen when the file handle 330 // is closed (EOF). 331 new_line.clear(); 332 return false; 333} 334 335void * 336IOChannel::IOReadThread (void *ptr) 337{ 338 IOChannel *myself = static_cast<IOChannel *> (ptr); 339 myself->Run(); 340 return NULL; 341} 342 343void 344IOChannel::Run () 345{ 346 SBListener listener("IOChannel::Run"); 347 std::string new_line; 348 349 SBBroadcaster interpreter_broadcaster (m_driver->GetDebugger().GetCommandInterpreter().GetBroadcaster()); 350 listener.StartListeningForEvents (interpreter_broadcaster, 351 SBCommandInterpreter::eBroadcastBitResetPrompt | 352 SBCommandInterpreter::eBroadcastBitThreadShouldExit | 353 SBCommandInterpreter::eBroadcastBitQuitCommandReceived); 354 355 listener.StartListeningForEvents (*this, 356 IOChannel::eBroadcastBitThreadShouldExit); 357 358 listener.StartListeningForEvents (*m_driver, 359 Driver::eBroadcastBitReadyForInput | 360 Driver::eBroadcastBitThreadShouldExit); 361 362 // Let anyone know that the IO channel is up and listening and ready for events 363 BroadcastEventByType (eBroadcastBitThreadDidStart); 364 bool done = false; 365 while (!done) 366 { 367 SBEvent event; 368 369 listener.WaitForEvent (UINT32_MAX, event); 370 if (!event.IsValid()) 371 continue; 372 373 const uint32_t event_type = event.GetType(); 374 375 if (event.GetBroadcaster().IsValid()) 376 { 377 if (event.BroadcasterMatchesPtr (m_driver)) 378 { 379 if (event_type & Driver::eBroadcastBitReadyForInput) 380 { 381 std::string line; 382 383 if (CommandQueueIsEmpty()) 384 { 385 if (LibeditGetInput(line) == false) 386 { 387 // EOF or some other file error occurred 388 done = true; 389 continue; 390 } 391 } 392 else 393 { 394 GetCommandFromQueue (line); 395 } 396 397 // TO BE DONE: FIGURE OUT WHICH COMMANDS SHOULD NOT BE REPEATED IF USER PRESSES PLAIN 'RETURN' 398 // AND TAKE CARE OF THAT HERE. 399 400 SBEvent line_event(IOChannel::eBroadcastBitHasUserInput, 401 line.c_str(), 402 line.size()); 403 BroadcastEvent (line_event); 404 } 405 else if (event_type & Driver::eBroadcastBitThreadShouldExit) 406 { 407 done = true; 408 break; 409 } 410 } 411 else if (event.BroadcasterMatchesRef (interpreter_broadcaster)) 412 { 413 switch (event_type) 414 { 415 case SBCommandInterpreter::eBroadcastBitResetPrompt: 416 { 417 const char *new_prompt = SBEvent::GetCStringFromEvent (event); 418 if (new_prompt) 419 g_prompt_map[m_edit_line] = new_prompt; 420 } 421 break; 422 423 case SBCommandInterpreter::eBroadcastBitThreadShouldExit: 424 case SBCommandInterpreter::eBroadcastBitQuitCommandReceived: 425 done = true; 426 break; 427 } 428 } 429 else if (event.BroadcasterMatchesPtr (this)) 430 { 431 if (event_type & IOChannel::eBroadcastBitThreadShouldExit) 432 { 433 done = true; 434 break; 435 } 436 } 437 } 438 } 439 BroadcastEventByType (IOChannel::eBroadcastBitThreadDidExit); 440 m_driver = NULL; 441 m_read_thread = NULL; 442} 443 444bool 445IOChannel::Start () 446{ 447 if (IS_VALID_LLDB_HOST_THREAD(m_read_thread)) 448 return true; 449 450 m_read_thread = SBHostOS::ThreadCreate ("<lldb.driver.commandline_io>", IOChannel::IOReadThread, this, 451 NULL); 452 453 return (IS_VALID_LLDB_HOST_THREAD(m_read_thread)); 454} 455 456bool 457IOChannel::Stop () 458{ 459 if (!IS_VALID_LLDB_HOST_THREAD(m_read_thread)) 460 return true; 461 462 BroadcastEventByType (eBroadcastBitThreadShouldExit); 463 464 // Don't call Host::ThreadCancel since el_gets won't respond to this 465 // function call -- the thread will just die and all local variables in 466 // IOChannel::Run() won't get destructed down which is bad since there is 467 // a local listener holding onto broadcasters... To ensure proper shutdown, 468 // a ^D (control-D) sequence (0x04) should be written to other end of the 469 // the "in" file handle that was passed into the contructor as closing the 470 // file handle doesn't seem to make el_gets() exit.... 471 return SBHostOS::ThreadJoin (m_read_thread, NULL, NULL); 472} 473 474void 475IOChannel::RefreshPrompt () 476{ 477 // If we are not in the middle of getting input from the user, there is no need to 478 // refresh the prompt. 479 IOLocker locker (m_output_mutex); 480 if (! IsGettingCommand()) 481 return; 482 483 // If we haven't finished writing the prompt, there's no need to refresh it. 484 if (m_expecting_prompt) 485 return; 486 487 if (m_refresh_request_pending) 488 return; 489 490 ::el_set (m_edit_line, EL_REFRESH); 491 m_refresh_request_pending = true; 492} 493 494void 495IOChannel::OutWrite (const char *buffer, size_t len, bool asynchronous) 496{ 497 if (len == 0) 498 return; 499 500 // Use the mutex to make sure OutWrite and ErrWrite do not interfere with each other's output. 501 IOLocker locker (m_output_mutex); 502 if (asynchronous) 503 ::fwrite (undo_prompt_string, 1, 4, m_out_file); 504 ::fwrite (buffer, 1, len, m_out_file); 505 if (asynchronous) 506 m_driver->GetDebugger().NotifyTopInputReader (eInputReaderAsynchronousOutputWritten); 507} 508 509void 510IOChannel::ErrWrite (const char *buffer, size_t len, bool asynchronous) 511{ 512 if (len == 0) 513 return; 514 515 // Use the mutex to make sure OutWrite and ErrWrite do not interfere with each other's output. 516 IOLocker locker (m_output_mutex); 517 if (asynchronous) 518 ::fwrite (undo_prompt_string, 1, 4, m_err_file); 519 ::fwrite (buffer, 1, len, m_err_file); 520 if (asynchronous) 521 m_driver->GetDebugger().NotifyTopInputReader (eInputReaderAsynchronousOutputWritten); 522} 523 524void 525IOChannel::AddCommandToQueue (const char *command) 526{ 527 m_command_queue.push (std::string(command)); 528} 529 530bool 531IOChannel::GetCommandFromQueue (std::string &cmd) 532{ 533 if (m_command_queue.empty()) 534 return false; 535 cmd.swap(m_command_queue.front()); 536 m_command_queue.pop (); 537 return true; 538} 539 540int 541IOChannel::CommandQueueSize () const 542{ 543 return m_command_queue.size(); 544} 545 546void 547IOChannel::ClearCommandQueue () 548{ 549 while (!m_command_queue.empty()) 550 m_command_queue.pop(); 551} 552 553bool 554IOChannel::CommandQueueIsEmpty () const 555{ 556 return m_command_queue.empty(); 557} 558 559bool 560IOChannel::IsGettingCommand () const 561{ 562 return m_getting_command; 563} 564 565void 566IOChannel::SetGettingCommand (bool new_value) 567{ 568 m_getting_command = new_value; 569} 570 571IOLocker::IOLocker (pthread_mutex_t &mutex) : 572 m_mutex_ptr (&mutex) 573{ 574 if (m_mutex_ptr) 575 ::pthread_mutex_lock (m_mutex_ptr); 576 577} 578 579IOLocker::~IOLocker () 580{ 581 if (m_mutex_ptr) 582 ::pthread_mutex_unlock (m_mutex_ptr); 583} 584