LLDB  mainline
IOHandler.cpp
Go to the documentation of this file.
1 //===-- IOHandler.cpp -------------------------------------------*- C++ -*-===//
2 //
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 //
7 //===----------------------------------------------------------------------===//
8 
9 #include "lldb/Core/IOHandler.h"
10 
11 #ifndef LLDB_DISABLE_CURSES
12 #include <curses.h>
13 #include <panel.h>
14 #endif
15 
16 #if defined(__APPLE__)
17 #include <deque>
18 #endif
19 #include <string>
20 
21 #include "lldb/Core/Debugger.h"
22 #include "lldb/Core/StreamFile.h"
23 #include "lldb/Host/File.h"
24 #include "lldb/Utility/Predicate.h"
25 #include "lldb/Utility/Status.h"
28 #include "lldb/lldb-forward.h"
29 
30 #ifndef LLDB_DISABLE_LIBEDIT
31 #include "lldb/Host/Editline.h"
32 #endif
35 #ifndef LLDB_DISABLE_CURSES
37 #include "lldb/Core/Module.h"
38 #include "lldb/Core/ValueObject.h"
40 #include "lldb/Symbol/Block.h"
41 #include "lldb/Symbol/Function.h"
42 #include "lldb/Symbol/Symbol.h"
44 #include "lldb/Target/Process.h"
46 #include "lldb/Target/StackFrame.h"
47 #include "lldb/Target/StopInfo.h"
48 #include "lldb/Target/Target.h"
49 #include "lldb/Target/Thread.h"
50 #include "lldb/Utility/State.h"
51 #endif
52 
53 #include "llvm/ADT/StringRef.h"
54 
55 #ifdef _MSC_VER
57 #endif
58 
59 #include <memory>
60 #include <mutex>
61 
62 #include <assert.h>
63 #include <ctype.h>
64 #include <errno.h>
65 #include <locale.h>
66 #include <stdint.h>
67 #include <stdio.h>
68 #include <string.h>
69 #include <type_traits>
70 
71 using namespace lldb;
72 using namespace lldb_private;
73 
74 IOHandler::IOHandler(Debugger &debugger, IOHandler::Type type)
75  : IOHandler(debugger, type,
76  StreamFileSP(), // Adopt STDIN from top input reader
77  StreamFileSP(), // Adopt STDOUT from top input reader
78  StreamFileSP(), // Adopt STDERR from top input reader
79  0, // Flags
80  nullptr // Shadow file recorder
81  ) {}
82 
84  const lldb::StreamFileSP &input_sp,
85  const lldb::StreamFileSP &output_sp,
86  const lldb::StreamFileSP &error_sp, uint32_t flags,
87  repro::DataRecorder *data_recorder)
88  : m_debugger(debugger), m_input_sp(input_sp), m_output_sp(output_sp),
89  m_error_sp(error_sp), m_data_recorder(data_recorder), m_popped(false),
90  m_flags(flags), m_type(type), m_user_data(nullptr), m_done(false),
91  m_active(false) {
92  // If any files are not specified, then adopt them from the top input reader.
93  if (!m_input_sp || !m_output_sp || !m_error_sp)
95  m_error_sp);
96 }
97 
98 IOHandler::~IOHandler() = default;
99 
101  return (m_input_sp ? m_input_sp->GetFile().GetDescriptor() : -1);
102 }
103 
105  return (m_output_sp ? m_output_sp->GetFile().GetDescriptor() : -1);
106 }
107 
109  return (m_error_sp ? m_error_sp->GetFile().GetDescriptor() : -1);
110 }
111 
113  return (m_input_sp ? m_input_sp->GetFile().GetStream() : nullptr);
114 }
115 
117  return (m_output_sp ? m_output_sp->GetFile().GetStream() : nullptr);
118 }
119 
121  return (m_error_sp ? m_error_sp->GetFile().GetStream() : nullptr);
122 }
123 
124 StreamFileSP &IOHandler::GetInputStreamFile() { return m_input_sp; }
125 
126 StreamFileSP &IOHandler::GetOutputStreamFile() { return m_output_sp; }
127 
128 StreamFileSP &IOHandler::GetErrorStreamFile() { return m_error_sp; }
129 
131  return GetInputStreamFile()->GetFile().GetIsInteractive();
132 }
133 
135  return GetInputStreamFile()->GetFile().GetIsRealTerminal();
136 }
137 
139 
141 
142 void IOHandlerStack::PrintAsync(Stream *stream, const char *s, size_t len) {
143  if (stream) {
144  std::lock_guard<std::recursive_mutex> guard(m_mutex);
145  if (m_top)
146  m_top->PrintAsync(stream, s, len);
147  }
148 }
149 
150 IOHandlerConfirm::IOHandlerConfirm(Debugger &debugger, llvm::StringRef prompt,
151  bool default_response)
153  debugger, IOHandler::Type::Confirm,
154  nullptr, // nullptr editline_name means no history loaded/saved
155  llvm::StringRef(), // No prompt
156  llvm::StringRef(), // No continuation prompt
157  false, // Multi-line
158  false, // Don't colorize the prompt (i.e. the confirm message.)
159  0, *this, nullptr),
160  m_default_response(default_response), m_user_response(default_response) {
161  StreamString prompt_stream;
162  prompt_stream.PutCString(prompt);
163  if (m_default_response)
164  prompt_stream.Printf(": [Y/n] ");
165  else
166  prompt_stream.Printf(": [y/N] ");
167 
168  SetPrompt(prompt_stream.GetString());
169 }
170 
172 
174  IOHandler &io_handler, const char *current_line, const char *cursor,
175  const char *last_char, int skip_first_n_matches, int max_matches,
176  StringList &matches, StringList &descriptions) {
177  if (current_line == cursor) {
178  if (m_default_response) {
179  matches.AppendString("y");
180  } else {
181  matches.AppendString("n");
182  }
183  }
184  return matches.GetSize();
185 }
186 
188  std::string &line) {
189  if (line.empty()) {
190  // User just hit enter, set the response to the default
192  io_handler.SetIsDone(true);
193  return;
194  }
195 
196  if (line.size() == 1) {
197  switch (line[0]) {
198  case 'y':
199  case 'Y':
200  m_user_response = true;
201  io_handler.SetIsDone(true);
202  return;
203  case 'n':
204  case 'N':
205  m_user_response = false;
206  io_handler.SetIsDone(true);
207  return;
208  default:
209  break;
210  }
211  }
212 
213  if (line == "yes" || line == "YES" || line == "Yes") {
214  m_user_response = true;
215  io_handler.SetIsDone(true);
216  } else if (line == "no" || line == "NO" || line == "No") {
217  m_user_response = false;
218  io_handler.SetIsDone(true);
219  }
220 }
221 
223  IOHandler &io_handler, const char *current_line, const char *cursor,
224  const char *last_char, int skip_first_n_matches, int max_matches,
225  StringList &matches, StringList &descriptions) {
226  switch (m_completion) {
227  case Completion::None:
228  break;
229 
231  return io_handler.GetDebugger().GetCommandInterpreter().HandleCompletion(
232  current_line, cursor, last_char, skip_first_n_matches, max_matches,
233  matches, descriptions);
234  case Completion::Expression: {
235  CompletionResult result;
236  CompletionRequest request(current_line, current_line - cursor,
237  skip_first_n_matches, max_matches, result);
239  io_handler.GetDebugger().GetCommandInterpreter(),
241  result.GetMatches(matches);
242  result.GetDescriptions(descriptions);
243 
244  size_t num_matches = request.GetNumberOfMatches();
245  if (num_matches > 0) {
246  std::string common_prefix;
247  matches.LongestCommonPrefix(common_prefix);
248  const size_t partial_name_len = request.GetCursorArgumentPrefix().size();
249 
250  // If we matched a unique single command, add a space... Only do this if
251  // the completer told us this was a complete word, however...
252  if (num_matches == 1 && request.GetWordComplete()) {
253  common_prefix.push_back(' ');
254  }
255  common_prefix.erase(0, partial_name_len);
256  matches.InsertStringAtIndex(0, std::move(common_prefix));
257  }
258  return num_matches;
259  } break;
260  }
261 
262  return 0;
263 }
264 
266  Debugger &debugger, IOHandler::Type type,
267  const char *editline_name, // Used for saving history files
268  llvm::StringRef prompt, llvm::StringRef continuation_prompt,
269  bool multi_line, bool color_prompts, uint32_t line_number_start,
270  IOHandlerDelegate &delegate, repro::DataRecorder *data_recorder)
271  : IOHandlerEditline(debugger, type,
272  StreamFileSP(), // Inherit input from top input reader
273  StreamFileSP(), // Inherit output from top input reader
274  StreamFileSP(), // Inherit error from top input reader
275  0, // Flags
276  editline_name, // Used for saving history files
277  prompt, continuation_prompt, multi_line, color_prompts,
278  line_number_start, delegate, data_recorder) {}
279 
281  Debugger &debugger, IOHandler::Type type,
282  const lldb::StreamFileSP &input_sp, const lldb::StreamFileSP &output_sp,
283  const lldb::StreamFileSP &error_sp, uint32_t flags,
284  const char *editline_name, // Used for saving history files
285  llvm::StringRef prompt, llvm::StringRef continuation_prompt,
286  bool multi_line, bool color_prompts, uint32_t line_number_start,
287  IOHandlerDelegate &delegate, repro::DataRecorder *data_recorder)
288  : IOHandler(debugger, type, input_sp, output_sp, error_sp, flags,
289  data_recorder),
290 #ifndef LLDB_DISABLE_LIBEDIT
291  m_editline_up(),
292 #endif
293  m_delegate(delegate), m_prompt(), m_continuation_prompt(),
294  m_current_lines_ptr(nullptr), m_base_line_number(line_number_start),
296  m_color_prompts(color_prompts), m_interrupt_exits(true),
297  m_editing(false) {
298  SetPrompt(prompt);
299 
300 #ifndef LLDB_DISABLE_LIBEDIT
301  bool use_editline = false;
302 
303  use_editline = m_input_sp->GetFile().GetIsRealTerminal();
304 
305  if (use_editline) {
306  m_editline_up.reset(new Editline(editline_name, GetInputFILE(),
308  m_color_prompts));
309  m_editline_up->SetIsInputCompleteCallback(IsInputCompleteCallback, this);
310  m_editline_up->SetAutoCompleteCallback(AutoCompleteCallback, this);
311  // See if the delegate supports fixing indentation
312  const char *indent_chars = delegate.IOHandlerGetFixIndentationCharacters();
313  if (indent_chars) {
314  // The delegate does support indentation, hook it up so when any
315  // indentation character is typed, the delegate gets a chance to fix it
316  m_editline_up->SetFixIndentationCallback(FixIndentationCallback, this,
317  indent_chars);
318  }
319  }
320 #endif
322  SetPrompt(prompt);
323  SetContinuationPrompt(continuation_prompt);
324 }
325 
327 #ifndef LLDB_DISABLE_LIBEDIT
328  m_editline_up.reset();
329 #endif
330 }
331 
335 }
336 
340 }
341 
342 bool IOHandlerEditline::GetLine(std::string &line, bool &interrupted) {
343 #ifndef LLDB_DISABLE_LIBEDIT
344  if (m_editline_up) {
345  bool b = m_editline_up->GetLine(line, interrupted);
346  if (m_data_recorder)
347  m_data_recorder->Record(line, true);
348  return b;
349  } else {
350 #endif
351  line.clear();
352 
353  FILE *in = GetInputFILE();
354  if (in) {
355  if (GetIsInteractive()) {
356  const char *prompt = nullptr;
357 
358  if (m_multi_line && m_curr_line_idx > 0)
359  prompt = GetContinuationPrompt();
360 
361  if (prompt == nullptr)
362  prompt = GetPrompt();
363 
364  if (prompt && prompt[0]) {
365  FILE *out = GetOutputFILE();
366  if (out) {
367  ::fprintf(out, "%s", prompt);
368  ::fflush(out);
369  }
370  }
371  }
372  char buffer[256];
373  bool done = false;
374  bool got_line = false;
375  m_editing = true;
376  while (!done) {
377  if (fgets(buffer, sizeof(buffer), in) == nullptr) {
378  const int saved_errno = errno;
379  if (feof(in))
380  done = true;
381  else if (ferror(in)) {
382  if (saved_errno != EINTR)
383  done = true;
384  }
385  } else {
386  got_line = true;
387  size_t buffer_len = strlen(buffer);
388  assert(buffer[buffer_len] == '\0');
389  char last_char = buffer[buffer_len - 1];
390  if (last_char == '\r' || last_char == '\n') {
391  done = true;
392  // Strip trailing newlines
393  while (last_char == '\r' || last_char == '\n') {
394  --buffer_len;
395  if (buffer_len == 0)
396  break;
397  last_char = buffer[buffer_len - 1];
398  }
399  }
400  line.append(buffer, buffer_len);
401  }
402  }
403  m_editing = false;
404  if (m_data_recorder && got_line)
405  m_data_recorder->Record(line, true);
406  // We might have gotten a newline on a line by itself make sure to return
407  // true in this case.
408  return got_line;
409  } else {
410  // No more input file, we are done...
411  SetIsDone(true);
412  }
413  return false;
414 #ifndef LLDB_DISABLE_LIBEDIT
415  }
416 #endif
417 }
418 
419 #ifndef LLDB_DISABLE_LIBEDIT
420 bool IOHandlerEditline::IsInputCompleteCallback(Editline *editline,
421  StringList &lines,
422  void *baton) {
423  IOHandlerEditline *editline_reader = (IOHandlerEditline *)baton;
424  return editline_reader->m_delegate.IOHandlerIsInputComplete(*editline_reader,
425  lines);
426 }
427 
428 int IOHandlerEditline::FixIndentationCallback(Editline *editline,
429  const StringList &lines,
430  int cursor_position,
431  void *baton) {
432  IOHandlerEditline *editline_reader = (IOHandlerEditline *)baton;
433  return editline_reader->m_delegate.IOHandlerFixIndentation(
434  *editline_reader, lines, cursor_position);
435 }
436 
437 int IOHandlerEditline::AutoCompleteCallback(
438  const char *current_line, const char *cursor, const char *last_char,
439  int skip_first_n_matches, int max_matches, StringList &matches,
440  StringList &descriptions, void *baton) {
441  IOHandlerEditline *editline_reader = (IOHandlerEditline *)baton;
442  if (editline_reader)
443  return editline_reader->m_delegate.IOHandlerComplete(
444  *editline_reader, current_line, cursor, last_char, skip_first_n_matches,
445  max_matches, matches, descriptions);
446  return 0;
447 }
448 #endif
449 
451 #ifndef LLDB_DISABLE_LIBEDIT
452  if (m_editline_up) {
453  return m_editline_up->GetPrompt();
454  } else {
455 #endif
456  if (m_prompt.empty())
457  return nullptr;
458 #ifndef LLDB_DISABLE_LIBEDIT
459  }
460 #endif
461  return m_prompt.c_str();
462 }
463 
464 bool IOHandlerEditline::SetPrompt(llvm::StringRef prompt) {
465  m_prompt = prompt;
466 
467 #ifndef LLDB_DISABLE_LIBEDIT
468  if (m_editline_up)
469  m_editline_up->SetPrompt(m_prompt.empty() ? nullptr : m_prompt.c_str());
470 #endif
471  return true;
472 }
473 
475  return (m_continuation_prompt.empty() ? nullptr
476  : m_continuation_prompt.c_str());
477 }
478 
479 void IOHandlerEditline::SetContinuationPrompt(llvm::StringRef prompt) {
480  m_continuation_prompt = prompt;
481 
482 #ifndef LLDB_DISABLE_LIBEDIT
483  if (m_editline_up)
484  m_editline_up->SetContinuationPrompt(m_continuation_prompt.empty()
485  ? nullptr
486  : m_continuation_prompt.c_str());
487 #endif
488 }
489 
491  m_base_line_number = line;
492 }
493 
495 #ifndef LLDB_DISABLE_LIBEDIT
496  if (m_editline_up)
497  return m_editline_up->GetCurrentLine();
498 #endif
499  return m_curr_line_idx;
500 }
501 
502 bool IOHandlerEditline::GetLines(StringList &lines, bool &interrupted) {
503  m_current_lines_ptr = &lines;
504 
505  bool success = false;
506 #ifndef LLDB_DISABLE_LIBEDIT
507  if (m_editline_up) {
508  return m_editline_up->GetLines(m_base_line_number, lines, interrupted);
509  } else {
510 #endif
511  bool done = false;
512  Status error;
513 
514  while (!done) {
515  // Show line numbers if we are asked to
516  std::string line;
517  if (m_base_line_number > 0 && GetIsInteractive()) {
518  FILE *out = GetOutputFILE();
519  if (out)
520  ::fprintf(out, "%u%s", m_base_line_number + (uint32_t)lines.GetSize(),
521  GetPrompt() == nullptr ? " " : "");
522  }
523 
524  m_curr_line_idx = lines.GetSize();
525 
526  bool interrupted = false;
527  if (GetLine(line, interrupted) && !interrupted) {
528  lines.AppendString(line);
529  done = m_delegate.IOHandlerIsInputComplete(*this, lines);
530  } else {
531  done = true;
532  }
533  }
534  success = lines.GetSize() > 0;
535 #ifndef LLDB_DISABLE_LIBEDIT
536  }
537 #endif
538  return success;
539 }
540 
541 // Each IOHandler gets to run until it is done. It should read data from the
542 // "in" and place output into "out" and "err and return when done.
544  std::string line;
545  while (IsActive()) {
546  bool interrupted = false;
547  if (m_multi_line) {
548  StringList lines;
549  if (GetLines(lines, interrupted)) {
550  if (interrupted) {
553 
554  } else {
555  line = lines.CopyList();
556  m_delegate.IOHandlerInputComplete(*this, line);
557  }
558  } else {
559  m_done = true;
560  }
561  } else {
562  if (GetLine(line, interrupted)) {
563  if (interrupted)
565  else
566  m_delegate.IOHandlerInputComplete(*this, line);
567  } else {
568  m_done = true;
569  }
570  }
571  }
572 }
573 
575 #ifndef LLDB_DISABLE_LIBEDIT
576  if (m_editline_up)
577  m_editline_up->Cancel();
578 #endif
579 }
580 
582  // Let the delgate handle it first
583  if (m_delegate.IOHandlerInterrupt(*this))
584  return true;
585 
586 #ifndef LLDB_DISABLE_LIBEDIT
587  if (m_editline_up)
588  return m_editline_up->Interrupt();
589 #endif
590  return false;
591 }
592 
594 #ifndef LLDB_DISABLE_LIBEDIT
595  if (m_editline_up)
596  m_editline_up->Interrupt();
597 #endif
598 }
599 
600 void IOHandlerEditline::PrintAsync(Stream *stream, const char *s, size_t len) {
601 #ifndef LLDB_DISABLE_LIBEDIT
602  if (m_editline_up)
603  m_editline_up->PrintAsync(stream, s, len);
604  else
605 #endif
606  {
607 #ifdef _MSC_VER
608  const char *prompt = GetPrompt();
609  if (prompt) {
610  // Back up over previous prompt using Windows API
611  CONSOLE_SCREEN_BUFFER_INFO screen_buffer_info;
612  HANDLE console_handle = GetStdHandle(STD_OUTPUT_HANDLE);
613  GetConsoleScreenBufferInfo(console_handle, &screen_buffer_info);
614  COORD coord = screen_buffer_info.dwCursorPosition;
615  coord.X -= strlen(prompt);
616  if (coord.X < 0)
617  coord.X = 0;
618  SetConsoleCursorPosition(console_handle, coord);
619  }
620 #endif
621  IOHandler::PrintAsync(stream, s, len);
622 #ifdef _MSC_VER
623  if (prompt)
625  strlen(prompt));
626 #endif
627  }
628 }
629 
630 // we may want curses to be disabled for some builds for instance, windows
631 #ifndef LLDB_DISABLE_CURSES
632 
633 #define KEY_RETURN 10
634 #define KEY_ESCAPE 27
635 
636 namespace curses {
637 class Menu;
638 class MenuDelegate;
639 class Window;
641 typedef std::shared_ptr<Menu> MenuSP;
642 typedef std::shared_ptr<MenuDelegate> MenuDelegateSP;
643 typedef std::shared_ptr<Window> WindowSP;
644 typedef std::shared_ptr<WindowDelegate> WindowDelegateSP;
645 typedef std::vector<MenuSP> Menus;
646 typedef std::vector<WindowSP> Windows;
647 typedef std::vector<WindowDelegateSP> WindowDelegates;
648 
649 #if 0
650 type summary add -s "x=${var.x}, y=${var.y}" curses::Point
651 type summary add -s "w=${var.width}, h=${var.height}" curses::Size
652 type summary add -s "${var.origin%S} ${var.size%S}" curses::Rect
653 #endif
654 
655 struct Point {
656  int x;
657  int y;
658 
659  Point(int _x = 0, int _y = 0) : x(_x), y(_y) {}
660 
661  void Clear() {
662  x = 0;
663  y = 0;
664  }
665 
666  Point &operator+=(const Point &rhs) {
667  x += rhs.x;
668  y += rhs.y;
669  return *this;
670  }
671 
672  void Dump() { printf("(x=%i, y=%i)\n", x, y); }
673 };
674 
675 bool operator==(const Point &lhs, const Point &rhs) {
676  return lhs.x == rhs.x && lhs.y == rhs.y;
677 }
678 
679 bool operator!=(const Point &lhs, const Point &rhs) {
680  return lhs.x != rhs.x || lhs.y != rhs.y;
681 }
682 
683 struct Size {
684  int width;
685  int height;
686  Size(int w = 0, int h = 0) : width(w), height(h) {}
687 
688  void Clear() {
689  width = 0;
690  height = 0;
691  }
692 
693  void Dump() { printf("(w=%i, h=%i)\n", width, height); }
694 };
695 
696 bool operator==(const Size &lhs, const Size &rhs) {
697  return lhs.width == rhs.width && lhs.height == rhs.height;
698 }
699 
700 bool operator!=(const Size &lhs, const Size &rhs) {
701  return lhs.width != rhs.width || lhs.height != rhs.height;
702 }
703 
704 struct Rect {
707 
708  Rect() : origin(), size() {}
709 
710  Rect(const Point &p, const Size &s) : origin(p), size(s) {}
711 
712  void Clear() {
713  origin.Clear();
714  size.Clear();
715  }
716 
717  void Dump() {
718  printf("(x=%i, y=%i), w=%i, h=%i)\n", origin.x, origin.y, size.width,
719  size.height);
720  }
721 
722  void Inset(int w, int h) {
723  if (size.width > w * 2)
724  size.width -= w * 2;
725  origin.x += w;
726 
727  if (size.height > h * 2)
728  size.height -= h * 2;
729  origin.y += h;
730  }
731 
732  // Return a status bar rectangle which is the last line of this rectangle.
733  // This rectangle will be modified to not include the status bar area.
735  Rect status_bar;
736  if (size.height > 1) {
737  status_bar.origin.x = origin.x;
738  status_bar.origin.y = size.height;
739  status_bar.size.width = size.width;
740  status_bar.size.height = 1;
741  --size.height;
742  }
743  return status_bar;
744  }
745 
746  // Return a menubar rectangle which is the first line of this rectangle. This
747  // rectangle will be modified to not include the menubar area.
749  Rect menubar;
750  if (size.height > 1) {
751  menubar.origin.x = origin.x;
752  menubar.origin.y = origin.y;
753  menubar.size.width = size.width;
754  menubar.size.height = 1;
755  ++origin.y;
756  --size.height;
757  }
758  return menubar;
759  }
760 
761  void HorizontalSplitPercentage(float top_percentage, Rect &top,
762  Rect &bottom) const {
763  float top_height = top_percentage * size.height;
764  HorizontalSplit(top_height, top, bottom);
765  }
766 
767  void HorizontalSplit(int top_height, Rect &top, Rect &bottom) const {
768  top = *this;
769  if (top_height < size.height) {
770  top.size.height = top_height;
771  bottom.origin.x = origin.x;
772  bottom.origin.y = origin.y + top.size.height;
773  bottom.size.width = size.width;
774  bottom.size.height = size.height - top.size.height;
775  } else {
776  bottom.Clear();
777  }
778  }
779 
780  void VerticalSplitPercentage(float left_percentage, Rect &left,
781  Rect &right) const {
782  float left_width = left_percentage * size.width;
783  VerticalSplit(left_width, left, right);
784  }
785 
786  void VerticalSplit(int left_width, Rect &left, Rect &right) const {
787  left = *this;
788  if (left_width < size.width) {
789  left.size.width = left_width;
790  right.origin.x = origin.x + left.size.width;
791  right.origin.y = origin.y;
792  right.size.width = size.width - left.size.width;
793  right.size.height = size.height;
794  } else {
795  right.Clear();
796  }
797  }
798 };
799 
800 bool operator==(const Rect &lhs, const Rect &rhs) {
801  return lhs.origin == rhs.origin && lhs.size == rhs.size;
802 }
803 
804 bool operator!=(const Rect &lhs, const Rect &rhs) {
805  return lhs.origin != rhs.origin || lhs.size != rhs.size;
806 }
807 
812 };
813 
814 enum class MenuActionResult {
815  Handled,
816  NotHandled,
817  Quit // Exit all menus and quit
818 };
819 
820 struct KeyHelp {
821  int ch;
822  const char *description;
823 };
824 
826 public:
827  virtual ~WindowDelegate() = default;
828 
829  virtual bool WindowDelegateDraw(Window &window, bool force) {
830  return false; // Drawing not handled
831  }
832 
834  return eKeyNotHandled;
835  }
836 
837  virtual const char *WindowDelegateGetHelpText() { return nullptr; }
838 
839  virtual KeyHelp *WindowDelegateGetKeyHelp() { return nullptr; }
840 };
841 
843 public:
844  HelpDialogDelegate(const char *text, KeyHelp *key_help_array);
845 
846  ~HelpDialogDelegate() override;
847 
848  bool WindowDelegateDraw(Window &window, bool force) override;
849 
850  HandleCharResult WindowDelegateHandleChar(Window &window, int key) override;
851 
852  size_t GetNumLines() const { return m_text.GetSize(); }
853 
854  size_t GetMaxLineLength() const { return m_text.GetMaxStringLength(); }
855 
856 protected:
859 };
860 
861 class Window {
862 public:
863  Window(const char *name)
864  : m_name(name), m_window(nullptr), m_panel(nullptr), m_parent(nullptr),
865  m_subwindows(), m_delegate_sp(), m_curr_active_window_idx(UINT32_MAX),
866  m_prev_active_window_idx(UINT32_MAX), m_delete(false),
867  m_needs_update(true), m_can_activate(true), m_is_subwin(false) {}
868 
869  Window(const char *name, WINDOW *w, bool del = true)
870  : m_name(name), m_window(nullptr), m_panel(nullptr), m_parent(nullptr),
871  m_subwindows(), m_delegate_sp(), m_curr_active_window_idx(UINT32_MAX),
872  m_prev_active_window_idx(UINT32_MAX), m_delete(del),
873  m_needs_update(true), m_can_activate(true), m_is_subwin(false) {
874  if (w)
875  Reset(w);
876  }
877 
878  Window(const char *name, const Rect &bounds)
879  : m_name(name), m_window(nullptr), m_parent(nullptr), m_subwindows(),
880  m_delegate_sp(), m_curr_active_window_idx(UINT32_MAX),
881  m_prev_active_window_idx(UINT32_MAX), m_delete(true),
882  m_needs_update(true), m_can_activate(true), m_is_subwin(false) {
883  Reset(::newwin(bounds.size.height, bounds.size.width, bounds.origin.y,
884  bounds.origin.y));
885  }
886 
887  virtual ~Window() {
888  RemoveSubWindows();
889  Reset();
890  }
891 
892  void Reset(WINDOW *w = nullptr, bool del = true) {
893  if (m_window == w)
894  return;
895 
896  if (m_panel) {
897  ::del_panel(m_panel);
898  m_panel = nullptr;
899  }
900  if (m_window && m_delete) {
901  ::delwin(m_window);
902  m_window = nullptr;
903  m_delete = false;
904  }
905  if (w) {
906  m_window = w;
907  m_panel = ::new_panel(m_window);
908  m_delete = del;
909  }
910  }
911 
912  void AttributeOn(attr_t attr) { ::wattron(m_window, attr); }
913  void AttributeOff(attr_t attr) { ::wattroff(m_window, attr); }
914  void Box(chtype v_char = ACS_VLINE, chtype h_char = ACS_HLINE) {
915  ::box(m_window, v_char, h_char);
916  }
917  void Clear() { ::wclear(m_window); }
918  void Erase() { ::werase(m_window); }
920  return Rect(GetParentOrigin(), GetSize());
921  } // Get the rectangle in our parent window
922  int GetChar() { return ::wgetch(m_window); }
923  int GetCursorX() { return getcurx(m_window); }
924  int GetCursorY() { return getcury(m_window); }
926  return Rect(Point(), GetSize());
927  } // Get our rectangle in our own coordinate system
928  Point GetParentOrigin() { return Point(GetParentX(), GetParentY()); }
929  Size GetSize() { return Size(GetWidth(), GetHeight()); }
930  int GetParentX() { return getparx(m_window); }
931  int GetParentY() { return getpary(m_window); }
932  int GetMaxX() { return getmaxx(m_window); }
933  int GetMaxY() { return getmaxy(m_window); }
934  int GetWidth() { return GetMaxX(); }
935  int GetHeight() { return GetMaxY(); }
936  void MoveCursor(int x, int y) { ::wmove(m_window, y, x); }
937  void MoveWindow(int x, int y) { MoveWindow(Point(x, y)); }
938  void Resize(int w, int h) { ::wresize(m_window, h, w); }
939  void Resize(const Size &size) {
940  ::wresize(m_window, size.height, size.width);
941  }
942  void PutChar(int ch) { ::waddch(m_window, ch); }
943  void PutCString(const char *s, int len = -1) { ::waddnstr(m_window, s, len); }
944  void Refresh() { ::wrefresh(m_window); }
946  // We are using panels, so we don't need to call this...
947  //::wnoutrefresh(m_window);
948  }
949  void SetBackground(int color_pair_idx) {
950  ::wbkgd(m_window, COLOR_PAIR(color_pair_idx));
951  }
952  void UnderlineOn() { AttributeOn(A_UNDERLINE); }
953  void UnderlineOff() { AttributeOff(A_UNDERLINE); }
954 
955  void PutCStringTruncated(const char *s, int right_pad) {
956  int bytes_left = GetWidth() - GetCursorX();
957  if (bytes_left > right_pad) {
958  bytes_left -= right_pad;
959  ::waddnstr(m_window, s, bytes_left);
960  }
961  }
962 
963  void MoveWindow(const Point &origin) {
964  const bool moving_window = origin != GetParentOrigin();
965  if (m_is_subwin && moving_window) {
966  // Can't move subwindows, must delete and re-create
967  Size size = GetSize();
968  Reset(::subwin(m_parent->m_window, size.height, size.width, origin.y,
969  origin.x),
970  true);
971  } else {
972  ::mvwin(m_window, origin.y, origin.x);
973  }
974  }
975 
976  void SetBounds(const Rect &bounds) {
977  const bool moving_window = bounds.origin != GetParentOrigin();
978  if (m_is_subwin && moving_window) {
979  // Can't move subwindows, must delete and re-create
980  Reset(::subwin(m_parent->m_window, bounds.size.height, bounds.size.width,
981  bounds.origin.y, bounds.origin.x),
982  true);
983  } else {
984  if (moving_window)
985  MoveWindow(bounds.origin);
986  Resize(bounds.size);
987  }
988  }
989 
990  void Printf(const char *format, ...) __attribute__((format(printf, 2, 3))) {
991  va_list args;
992  va_start(args, format);
993  vwprintw(m_window, format, args);
994  va_end(args);
995  }
996 
997  void Touch() {
998  ::touchwin(m_window);
999  if (m_parent)
1000  m_parent->Touch();
1001  }
1002 
1003  WindowSP CreateSubWindow(const char *name, const Rect &bounds,
1004  bool make_active) {
1005  auto get_window = [this, &bounds]() {
1006  return m_window
1007  ? ::subwin(m_window, bounds.size.height, bounds.size.width,
1008  bounds.origin.y, bounds.origin.x)
1009  : ::newwin(bounds.size.height, bounds.size.width,
1010  bounds.origin.y, bounds.origin.x);
1011  };
1012  WindowSP subwindow_sp = std::make_shared<Window>(name, get_window(), true);
1013  subwindow_sp->m_is_subwin = subwindow_sp.operator bool();
1014  subwindow_sp->m_parent = this;
1015  if (make_active) {
1016  m_prev_active_window_idx = m_curr_active_window_idx;
1017  m_curr_active_window_idx = m_subwindows.size();
1018  }
1019  m_subwindows.push_back(subwindow_sp);
1020  ::top_panel(subwindow_sp->m_panel);
1021  m_needs_update = true;
1022  return subwindow_sp;
1023  }
1024 
1025  bool RemoveSubWindow(Window *window) {
1026  Windows::iterator pos, end = m_subwindows.end();
1027  size_t i = 0;
1028  for (pos = m_subwindows.begin(); pos != end; ++pos, ++i) {
1029  if ((*pos).get() == window) {
1030  if (m_prev_active_window_idx == i)
1031  m_prev_active_window_idx = UINT32_MAX;
1032  else if (m_prev_active_window_idx != UINT32_MAX &&
1033  m_prev_active_window_idx > i)
1034  --m_prev_active_window_idx;
1035 
1036  if (m_curr_active_window_idx == i)
1037  m_curr_active_window_idx = UINT32_MAX;
1038  else if (m_curr_active_window_idx != UINT32_MAX &&
1039  m_curr_active_window_idx > i)
1040  --m_curr_active_window_idx;
1041  window->Erase();
1042  m_subwindows.erase(pos);
1043  m_needs_update = true;
1044  if (m_parent)
1045  m_parent->Touch();
1046  else
1047  ::touchwin(stdscr);
1048  return true;
1049  }
1050  }
1051  return false;
1052  }
1053 
1054  WindowSP FindSubWindow(const char *name) {
1055  Windows::iterator pos, end = m_subwindows.end();
1056  size_t i = 0;
1057  for (pos = m_subwindows.begin(); pos != end; ++pos, ++i) {
1058  if ((*pos)->m_name == name)
1059  return *pos;
1060  }
1061  return WindowSP();
1062  }
1063 
1065  m_curr_active_window_idx = UINT32_MAX;
1066  m_prev_active_window_idx = UINT32_MAX;
1067  for (Windows::iterator pos = m_subwindows.begin();
1068  pos != m_subwindows.end(); pos = m_subwindows.erase(pos)) {
1069  (*pos)->Erase();
1070  }
1071  if (m_parent)
1072  m_parent->Touch();
1073  else
1074  ::touchwin(stdscr);
1075  }
1076 
1077  WINDOW *get() { return m_window; }
1078 
1079  operator WINDOW *() { return m_window; }
1080 
1081  // Window drawing utilities
1082  void DrawTitleBox(const char *title, const char *bottom_message = nullptr) {
1083  attr_t attr = 0;
1084  if (IsActive())
1085  attr = A_BOLD | COLOR_PAIR(2);
1086  else
1087  attr = 0;
1088  if (attr)
1089  AttributeOn(attr);
1090 
1091  Box();
1092  MoveCursor(3, 0);
1093 
1094  if (title && title[0]) {
1095  PutChar('<');
1096  PutCString(title);
1097  PutChar('>');
1098  }
1099 
1100  if (bottom_message && bottom_message[0]) {
1101  int bottom_message_length = strlen(bottom_message);
1102  int x = GetWidth() - 3 - (bottom_message_length + 2);
1103 
1104  if (x > 0) {
1105  MoveCursor(x, GetHeight() - 1);
1106  PutChar('[');
1107  PutCString(bottom_message);
1108  PutChar(']');
1109  } else {
1110  MoveCursor(1, GetHeight() - 1);
1111  PutChar('[');
1112  PutCStringTruncated(bottom_message, 1);
1113  }
1114  }
1115  if (attr)
1116  AttributeOff(attr);
1117  }
1118 
1119  virtual void Draw(bool force) {
1120  if (m_delegate_sp && m_delegate_sp->WindowDelegateDraw(*this, force))
1121  return;
1122 
1123  for (auto &subwindow_sp : m_subwindows)
1124  subwindow_sp->Draw(force);
1125  }
1126 
1128  if (m_delegate_sp) {
1129  const char *text = m_delegate_sp->WindowDelegateGetHelpText();
1130  KeyHelp *key_help = m_delegate_sp->WindowDelegateGetKeyHelp();
1131  if ((text && text[0]) || key_help) {
1132  std::unique_ptr<HelpDialogDelegate> help_delegate_up(
1133  new HelpDialogDelegate(text, key_help));
1134  const size_t num_lines = help_delegate_up->GetNumLines();
1135  const size_t max_length = help_delegate_up->GetMaxLineLength();
1136  Rect bounds = GetBounds();
1137  bounds.Inset(1, 1);
1138  if (max_length + 4 < static_cast<size_t>(bounds.size.width)) {
1139  bounds.origin.x += (bounds.size.width - max_length + 4) / 2;
1140  bounds.size.width = max_length + 4;
1141  } else {
1142  if (bounds.size.width > 100) {
1143  const int inset_w = bounds.size.width / 4;
1144  bounds.origin.x += inset_w;
1145  bounds.size.width -= 2 * inset_w;
1146  }
1147  }
1148 
1149  if (num_lines + 2 < static_cast<size_t>(bounds.size.height)) {
1150  bounds.origin.y += (bounds.size.height - num_lines + 2) / 2;
1151  bounds.size.height = num_lines + 2;
1152  } else {
1153  if (bounds.size.height > 100) {
1154  const int inset_h = bounds.size.height / 4;
1155  bounds.origin.y += inset_h;
1156  bounds.size.height -= 2 * inset_h;
1157  }
1158  }
1159  WindowSP help_window_sp;
1160  Window *parent_window = GetParent();
1161  if (parent_window)
1162  help_window_sp = parent_window->CreateSubWindow("Help", bounds, true);
1163  else
1164  help_window_sp = CreateSubWindow("Help", bounds, true);
1165  help_window_sp->SetDelegate(
1166  WindowDelegateSP(help_delegate_up.release()));
1167  return true;
1168  }
1169  }
1170  return false;
1171  }
1172 
1173  virtual HandleCharResult HandleChar(int key) {
1174  // Always check the active window first
1176  WindowSP active_window_sp = GetActiveWindow();
1177  if (active_window_sp) {
1178  result = active_window_sp->HandleChar(key);
1179  if (result != eKeyNotHandled)
1180  return result;
1181  }
1182 
1183  if (m_delegate_sp) {
1184  result = m_delegate_sp->WindowDelegateHandleChar(*this, key);
1185  if (result != eKeyNotHandled)
1186  return result;
1187  }
1188 
1189  // Then check for any windows that want any keys that weren't handled. This
1190  // is typically only for a menubar. Make a copy of the subwindows in case
1191  // any HandleChar() functions muck with the subwindows. If we don't do
1192  // this, we can crash when iterating over the subwindows.
1193  Windows subwindows(m_subwindows);
1194  for (auto subwindow_sp : subwindows) {
1195  if (!subwindow_sp->m_can_activate) {
1196  HandleCharResult result = subwindow_sp->HandleChar(key);
1197  if (result != eKeyNotHandled)
1198  return result;
1199  }
1200  }
1201 
1202  return eKeyNotHandled;
1203  }
1204 
1205  bool SetActiveWindow(Window *window) {
1206  const size_t num_subwindows = m_subwindows.size();
1207  for (size_t i = 0; i < num_subwindows; ++i) {
1208  if (m_subwindows[i].get() == window) {
1209  m_prev_active_window_idx = m_curr_active_window_idx;
1210  ::top_panel(window->m_panel);
1211  m_curr_active_window_idx = i;
1212  return true;
1213  }
1214  }
1215  return false;
1216  }
1217 
1218  WindowSP GetActiveWindow() {
1219  if (!m_subwindows.empty()) {
1220  if (m_curr_active_window_idx >= m_subwindows.size()) {
1221  if (m_prev_active_window_idx < m_subwindows.size()) {
1222  m_curr_active_window_idx = m_prev_active_window_idx;
1223  m_prev_active_window_idx = UINT32_MAX;
1224  } else if (IsActive()) {
1225  m_prev_active_window_idx = UINT32_MAX;
1226  m_curr_active_window_idx = UINT32_MAX;
1227 
1228  // Find first window that wants to be active if this window is active
1229  const size_t num_subwindows = m_subwindows.size();
1230  for (size_t i = 0; i < num_subwindows; ++i) {
1231  if (m_subwindows[i]->GetCanBeActive()) {
1232  m_curr_active_window_idx = i;
1233  break;
1234  }
1235  }
1236  }
1237  }
1238 
1239  if (m_curr_active_window_idx < m_subwindows.size())
1240  return m_subwindows[m_curr_active_window_idx];
1241  }
1242  return WindowSP();
1243  }
1244 
1245  bool GetCanBeActive() const { return m_can_activate; }
1246 
1247  void SetCanBeActive(bool b) { m_can_activate = b; }
1248 
1249  const WindowDelegateSP &GetDelegate() const { return m_delegate_sp; }
1250 
1251  void SetDelegate(const WindowDelegateSP &delegate_sp) {
1252  m_delegate_sp = delegate_sp;
1253  }
1254 
1255  Window *GetParent() const { return m_parent; }
1256 
1257  bool IsActive() const {
1258  if (m_parent)
1259  return m_parent->GetActiveWindow().get() == this;
1260  else
1261  return true; // Top level window is always active
1262  }
1263 
1265  // Move active focus to next window
1266  const size_t num_subwindows = m_subwindows.size();
1267  if (m_curr_active_window_idx == UINT32_MAX) {
1268  uint32_t idx = 0;
1269  for (auto subwindow_sp : m_subwindows) {
1270  if (subwindow_sp->GetCanBeActive()) {
1271  m_curr_active_window_idx = idx;
1272  break;
1273  }
1274  ++idx;
1275  }
1276  } else if (m_curr_active_window_idx + 1 < num_subwindows) {
1277  bool handled = false;
1278  m_prev_active_window_idx = m_curr_active_window_idx;
1279  for (size_t idx = m_curr_active_window_idx + 1; idx < num_subwindows;
1280  ++idx) {
1281  if (m_subwindows[idx]->GetCanBeActive()) {
1282  m_curr_active_window_idx = idx;
1283  handled = true;
1284  break;
1285  }
1286  }
1287  if (!handled) {
1288  for (size_t idx = 0; idx <= m_prev_active_window_idx; ++idx) {
1289  if (m_subwindows[idx]->GetCanBeActive()) {
1290  m_curr_active_window_idx = idx;
1291  break;
1292  }
1293  }
1294  }
1295  } else {
1296  m_prev_active_window_idx = m_curr_active_window_idx;
1297  for (size_t idx = 0; idx < num_subwindows; ++idx) {
1298  if (m_subwindows[idx]->GetCanBeActive()) {
1299  m_curr_active_window_idx = idx;
1300  break;
1301  }
1302  }
1303  }
1304  }
1305 
1306  const char *GetName() const { return m_name.c_str(); }
1307 
1308 protected:
1309  std::string m_name;
1310  WINDOW *m_window;
1311  PANEL *m_panel;
1313  Windows m_subwindows;
1314  WindowDelegateSP m_delegate_sp;
1317  bool m_delete;
1321 
1322 private:
1323  DISALLOW_COPY_AND_ASSIGN(Window);
1324 };
1325 
1327 public:
1328  virtual ~MenuDelegate() = default;
1329 
1330  virtual MenuActionResult MenuDelegateAction(Menu &menu) = 0;
1331 };
1332 
1333 class Menu : public WindowDelegate {
1334 public:
1335  enum class Type { Invalid, Bar, Item, Separator };
1336 
1337  // Menubar or separator constructor
1338  Menu(Type type);
1339 
1340  // Menuitem constructor
1341  Menu(const char *name, const char *key_name, int key_value,
1342  uint64_t identifier);
1343 
1344  ~Menu() override = default;
1345 
1346  const MenuDelegateSP &GetDelegate() const { return m_delegate_sp; }
1347 
1348  void SetDelegate(const MenuDelegateSP &delegate_sp) {
1349  m_delegate_sp = delegate_sp;
1350  }
1351 
1352  void RecalculateNameLengths();
1353 
1354  void AddSubmenu(const MenuSP &menu_sp);
1355 
1356  int DrawAndRunMenu(Window &window);
1357 
1358  void DrawMenuTitle(Window &window, bool highlight);
1359 
1360  bool WindowDelegateDraw(Window &window, bool force) override;
1361 
1362  HandleCharResult WindowDelegateHandleChar(Window &window, int key) override;
1363 
1365  MenuActionResult result = MenuActionResult::NotHandled;
1366  if (m_delegate_sp) {
1367  result = m_delegate_sp->MenuDelegateAction(menu);
1368  if (result != MenuActionResult::NotHandled)
1369  return result;
1370  } else if (m_parent) {
1371  result = m_parent->ActionPrivate(menu);
1372  if (result != MenuActionResult::NotHandled)
1373  return result;
1374  }
1375  return m_canned_result;
1376  }
1377 
1379  // Call the recursive action so it can try to handle it with the menu
1380  // delegate, and if not, try our parent menu
1381  return ActionPrivate(*this);
1382  }
1383 
1384  void SetCannedResult(MenuActionResult result) { m_canned_result = result; }
1385 
1386  Menus &GetSubmenus() { return m_submenus; }
1387 
1388  const Menus &GetSubmenus() const { return m_submenus; }
1389 
1390  int GetSelectedSubmenuIndex() const { return m_selected; }
1391 
1392  void SetSelectedSubmenuIndex(int idx) { m_selected = idx; }
1393 
1394  Type GetType() const { return m_type; }
1395 
1396  int GetStartingColumn() const { return m_start_col; }
1397 
1398  void SetStartingColumn(int col) { m_start_col = col; }
1399 
1400  int GetKeyValue() const { return m_key_value; }
1401 
1402  void SetKeyValue(int key_value) { m_key_value = key_value; }
1403 
1404  std::string &GetName() { return m_name; }
1405 
1406  std::string &GetKeyName() { return m_key_name; }
1407 
1408  int GetDrawWidth() const {
1409  return m_max_submenu_name_length + m_max_submenu_key_name_length + 8;
1410  }
1411 
1412  uint64_t GetIdentifier() const { return m_identifier; }
1413 
1414  void SetIdentifier(uint64_t identifier) { m_identifier = identifier; }
1415 
1416 protected:
1417  std::string m_name;
1418  std::string m_key_name;
1419  uint64_t m_identifier;
1427  Menus m_submenus;
1430  MenuDelegateSP m_delegate_sp;
1431 };
1432 
1433 // Menubar or separator constructor
1434 Menu::Menu(Type type)
1435  : m_name(), m_key_name(), m_identifier(0), m_type(type), m_key_value(0),
1436  m_start_col(0), m_max_submenu_name_length(0),
1437  m_max_submenu_key_name_length(0), m_selected(0), m_parent(nullptr),
1438  m_submenus(), m_canned_result(MenuActionResult::NotHandled),
1439  m_delegate_sp() {}
1440 
1441 // Menuitem constructor
1442 Menu::Menu(const char *name, const char *key_name, int key_value,
1443  uint64_t identifier)
1444  : m_name(), m_key_name(), m_identifier(identifier), m_type(Type::Invalid),
1448  m_delegate_sp() {
1449  if (name && name[0]) {
1450  m_name = name;
1451  m_type = Type::Item;
1452  if (key_name && key_name[0])
1453  m_key_name = key_name;
1454  } else {
1456  }
1457 }
1458 
1462  Menus &submenus = GetSubmenus();
1463  const size_t num_submenus = submenus.size();
1464  for (size_t i = 0; i < num_submenus; ++i) {
1465  Menu *submenu = submenus[i].get();
1466  if (static_cast<size_t>(m_max_submenu_name_length) < submenu->m_name.size())
1467  m_max_submenu_name_length = submenu->m_name.size();
1468  if (static_cast<size_t>(m_max_submenu_key_name_length) <
1469  submenu->m_key_name.size())
1470  m_max_submenu_key_name_length = submenu->m_key_name.size();
1471  }
1472 }
1473 
1474 void Menu::AddSubmenu(const MenuSP &menu_sp) {
1475  menu_sp->m_parent = this;
1476  if (static_cast<size_t>(m_max_submenu_name_length) < menu_sp->m_name.size())
1477  m_max_submenu_name_length = menu_sp->m_name.size();
1478  if (static_cast<size_t>(m_max_submenu_key_name_length) <
1479  menu_sp->m_key_name.size())
1480  m_max_submenu_key_name_length = menu_sp->m_key_name.size();
1481  m_submenus.push_back(menu_sp);
1482 }
1483 
1484 void Menu::DrawMenuTitle(Window &window, bool highlight) {
1485  if (m_type == Type::Separator) {
1486  window.MoveCursor(0, window.GetCursorY());
1487  window.PutChar(ACS_LTEE);
1488  int width = window.GetWidth();
1489  if (width > 2) {
1490  width -= 2;
1491  for (int i = 0; i < width; ++i)
1492  window.PutChar(ACS_HLINE);
1493  }
1494  window.PutChar(ACS_RTEE);
1495  } else {
1496  const int shortcut_key = m_key_value;
1497  bool underlined_shortcut = false;
1498  const attr_t hilgight_attr = A_REVERSE;
1499  if (highlight)
1500  window.AttributeOn(hilgight_attr);
1501  if (isprint(shortcut_key)) {
1502  size_t lower_pos = m_name.find(tolower(shortcut_key));
1503  size_t upper_pos = m_name.find(toupper(shortcut_key));
1504  const char *name = m_name.c_str();
1505  size_t pos = std::min<size_t>(lower_pos, upper_pos);
1506  if (pos != std::string::npos) {
1507  underlined_shortcut = true;
1508  if (pos > 0) {
1509  window.PutCString(name, pos);
1510  name += pos;
1511  }
1512  const attr_t shortcut_attr = A_UNDERLINE | A_BOLD;
1513  window.AttributeOn(shortcut_attr);
1514  window.PutChar(name[0]);
1515  window.AttributeOff(shortcut_attr);
1516  name++;
1517  if (name[0])
1518  window.PutCString(name);
1519  }
1520  }
1521 
1522  if (!underlined_shortcut) {
1523  window.PutCString(m_name.c_str());
1524  }
1525 
1526  if (highlight)
1527  window.AttributeOff(hilgight_attr);
1528 
1529  if (m_key_name.empty()) {
1530  if (!underlined_shortcut && isprint(m_key_value)) {
1531  window.AttributeOn(COLOR_PAIR(3));
1532  window.Printf(" (%c)", m_key_value);
1533  window.AttributeOff(COLOR_PAIR(3));
1534  }
1535  } else {
1536  window.AttributeOn(COLOR_PAIR(3));
1537  window.Printf(" (%s)", m_key_name.c_str());
1538  window.AttributeOff(COLOR_PAIR(3));
1539  }
1540  }
1541 }
1542 
1543 bool Menu::WindowDelegateDraw(Window &window, bool force) {
1544  Menus &submenus = GetSubmenus();
1545  const size_t num_submenus = submenus.size();
1546  const int selected_idx = GetSelectedSubmenuIndex();
1547  Menu::Type menu_type = GetType();
1548  switch (menu_type) {
1549  case Menu::Type::Bar: {
1550  window.SetBackground(2);
1551  window.MoveCursor(0, 0);
1552  for (size_t i = 0; i < num_submenus; ++i) {
1553  Menu *menu = submenus[i].get();
1554  if (i > 0)
1555  window.PutChar(' ');
1556  menu->SetStartingColumn(window.GetCursorX());
1557  window.PutCString("| ");
1558  menu->DrawMenuTitle(window, false);
1559  }
1560  window.PutCString(" |");
1561  window.DeferredRefresh();
1562  } break;
1563 
1564  case Menu::Type::Item: {
1565  int y = 1;
1566  int x = 3;
1567  // Draw the menu
1568  int cursor_x = 0;
1569  int cursor_y = 0;
1570  window.Erase();
1571  window.SetBackground(2);
1572  window.Box();
1573  for (size_t i = 0; i < num_submenus; ++i) {
1574  const bool is_selected = (i == static_cast<size_t>(selected_idx));
1575  window.MoveCursor(x, y + i);
1576  if (is_selected) {
1577  // Remember where we want the cursor to be
1578  cursor_x = x - 1;
1579  cursor_y = y + i;
1580  }
1581  submenus[i]->DrawMenuTitle(window, is_selected);
1582  }
1583  window.MoveCursor(cursor_x, cursor_y);
1584  window.DeferredRefresh();
1585  } break;
1586 
1587  default:
1588  case Menu::Type::Separator:
1589  break;
1590  }
1591  return true; // Drawing handled...
1592 }
1593 
1596 
1597  Menus &submenus = GetSubmenus();
1598  const size_t num_submenus = submenus.size();
1599  const int selected_idx = GetSelectedSubmenuIndex();
1600  Menu::Type menu_type = GetType();
1601  if (menu_type == Menu::Type::Bar) {
1602  MenuSP run_menu_sp;
1603  switch (key) {
1604  case KEY_DOWN:
1605  case KEY_UP:
1606  // Show last menu or first menu
1607  if (selected_idx < static_cast<int>(num_submenus))
1608  run_menu_sp = submenus[selected_idx];
1609  else if (!submenus.empty())
1610  run_menu_sp = submenus.front();
1611  result = eKeyHandled;
1612  break;
1613 
1614  case KEY_RIGHT:
1615  ++m_selected;
1616  if (m_selected >= static_cast<int>(num_submenus))
1617  m_selected = 0;
1618  if (m_selected < static_cast<int>(num_submenus))
1619  run_menu_sp = submenus[m_selected];
1620  else if (!submenus.empty())
1621  run_menu_sp = submenus.front();
1622  result = eKeyHandled;
1623  break;
1624 
1625  case KEY_LEFT:
1626  --m_selected;
1627  if (m_selected < 0)
1628  m_selected = num_submenus - 1;
1629  if (m_selected < static_cast<int>(num_submenus))
1630  run_menu_sp = submenus[m_selected];
1631  else if (!submenus.empty())
1632  run_menu_sp = submenus.front();
1633  result = eKeyHandled;
1634  break;
1635 
1636  default:
1637  for (size_t i = 0; i < num_submenus; ++i) {
1638  if (submenus[i]->GetKeyValue() == key) {
1640  run_menu_sp = submenus[i];
1641  result = eKeyHandled;
1642  break;
1643  }
1644  }
1645  break;
1646  }
1647 
1648  if (run_menu_sp) {
1649  // Run the action on this menu in case we need to populate the menu with
1650  // dynamic content and also in case check marks, and any other menu
1651  // decorations need to be calculated
1652  if (run_menu_sp->Action() == MenuActionResult::Quit)
1653  return eQuitApplication;
1654 
1655  Rect menu_bounds;
1656  menu_bounds.origin.x = run_menu_sp->GetStartingColumn();
1657  menu_bounds.origin.y = 1;
1658  menu_bounds.size.width = run_menu_sp->GetDrawWidth();
1659  menu_bounds.size.height = run_menu_sp->GetSubmenus().size() + 2;
1660  if (m_menu_window_sp)
1661  window.GetParent()->RemoveSubWindow(m_menu_window_sp.get());
1662 
1664  run_menu_sp->GetName().c_str(), menu_bounds, true);
1665  m_menu_window_sp->SetDelegate(run_menu_sp);
1666  }
1667  } else if (menu_type == Menu::Type::Item) {
1668  switch (key) {
1669  case KEY_DOWN:
1670  if (m_submenus.size() > 1) {
1671  const int start_select = m_selected;
1672  while (++m_selected != start_select) {
1673  if (static_cast<size_t>(m_selected) >= num_submenus)
1674  m_selected = 0;
1676  continue;
1677  else
1678  break;
1679  }
1680  return eKeyHandled;
1681  }
1682  break;
1683 
1684  case KEY_UP:
1685  if (m_submenus.size() > 1) {
1686  const int start_select = m_selected;
1687  while (--m_selected != start_select) {
1688  if (m_selected < static_cast<int>(0))
1689  m_selected = num_submenus - 1;
1691  continue;
1692  else
1693  break;
1694  }
1695  return eKeyHandled;
1696  }
1697  break;
1698 
1699  case KEY_RETURN:
1700  if (static_cast<size_t>(selected_idx) < num_submenus) {
1701  if (submenus[selected_idx]->Action() == MenuActionResult::Quit)
1702  return eQuitApplication;
1703  window.GetParent()->RemoveSubWindow(&window);
1704  return eKeyHandled;
1705  }
1706  break;
1707 
1708  case KEY_ESCAPE: // Beware: pressing escape key has 1 to 2 second delay in
1709  // case other chars are entered for escaped sequences
1710  window.GetParent()->RemoveSubWindow(&window);
1711  return eKeyHandled;
1712 
1713  default:
1714  for (size_t i = 0; i < num_submenus; ++i) {
1715  Menu *menu = submenus[i].get();
1716  if (menu->GetKeyValue() == key) {
1718  window.GetParent()->RemoveSubWindow(&window);
1719  if (menu->Action() == MenuActionResult::Quit)
1720  return eQuitApplication;
1721  return eKeyHandled;
1722  }
1723  }
1724  break;
1725  }
1726  } else if (menu_type == Menu::Type::Separator) {
1727  }
1728  return result;
1729 }
1730 
1732 public:
1733  Application(FILE *in, FILE *out)
1734  : m_window_sp(), m_screen(nullptr), m_in(in), m_out(out) {}
1735 
1737  m_window_delegates.clear();
1738  m_window_sp.reset();
1739  if (m_screen) {
1740  ::delscreen(m_screen);
1741  m_screen = nullptr;
1742  }
1743  }
1744 
1745  void Initialize() {
1746  ::setlocale(LC_ALL, "");
1747  ::setlocale(LC_CTYPE, "");
1748  m_screen = ::newterm(nullptr, m_out, m_in);
1749  ::start_color();
1750  ::curs_set(0);
1751  ::noecho();
1752  ::keypad(stdscr, TRUE);
1753  }
1754 
1755  void Terminate() { ::endwin(); }
1756 
1757  void Run(Debugger &debugger) {
1758  bool done = false;
1759  int delay_in_tenths_of_a_second = 1;
1760 
1761  // Alas the threading model in curses is a bit lame so we need to resort to
1762  // polling every 0.5 seconds. We could poll for stdin ourselves and then
1763  // pass the keys down but then we need to translate all of the escape
1764  // sequences ourselves. So we resort to polling for input because we need
1765  // to receive async process events while in this loop.
1766 
1767  halfdelay(delay_in_tenths_of_a_second); // Poll using some number of tenths
1768  // of seconds seconds when calling
1769  // Window::GetChar()
1770 
1771  ListenerSP listener_sp(
1772  Listener::MakeListener("lldb.IOHandler.curses.Application"));
1773  ConstString broadcaster_class_target(Target::GetStaticBroadcasterClass());
1774  ConstString broadcaster_class_process(Process::GetStaticBroadcasterClass());
1775  ConstString broadcaster_class_thread(Thread::GetStaticBroadcasterClass());
1776  debugger.EnableForwardEvents(listener_sp);
1777 
1778  bool update = true;
1779 #if defined(__APPLE__)
1780  std::deque<int> escape_chars;
1781 #endif
1782 
1783  while (!done) {
1784  if (update) {
1785  m_window_sp->Draw(false);
1786  // All windows should be calling Window::DeferredRefresh() instead of
1787  // Window::Refresh() so we can do a single update and avoid any screen
1788  // blinking
1789  update_panels();
1790 
1791  // Cursor hiding isn't working on MacOSX, so hide it in the top left
1792  // corner
1793  m_window_sp->MoveCursor(0, 0);
1794 
1795  doupdate();
1796  update = false;
1797  }
1798 
1799 #if defined(__APPLE__)
1800  // Terminal.app doesn't map its function keys correctly, F1-F4 default
1801  // to: \033OP, \033OQ, \033OR, \033OS, so lets take care of this here if
1802  // possible
1803  int ch;
1804  if (escape_chars.empty())
1805  ch = m_window_sp->GetChar();
1806  else {
1807  ch = escape_chars.front();
1808  escape_chars.pop_front();
1809  }
1810  if (ch == KEY_ESCAPE) {
1811  int ch2 = m_window_sp->GetChar();
1812  if (ch2 == 'O') {
1813  int ch3 = m_window_sp->GetChar();
1814  switch (ch3) {
1815  case 'P':
1816  ch = KEY_F(1);
1817  break;
1818  case 'Q':
1819  ch = KEY_F(2);
1820  break;
1821  case 'R':
1822  ch = KEY_F(3);
1823  break;
1824  case 'S':
1825  ch = KEY_F(4);
1826  break;
1827  default:
1828  escape_chars.push_back(ch2);
1829  if (ch3 != -1)
1830  escape_chars.push_back(ch3);
1831  break;
1832  }
1833  } else if (ch2 != -1)
1834  escape_chars.push_back(ch2);
1835  }
1836 #else
1837  int ch = m_window_sp->GetChar();
1838 
1839 #endif
1840  if (ch == -1) {
1841  if (feof(m_in) || ferror(m_in)) {
1842  done = true;
1843  } else {
1844  // Just a timeout from using halfdelay(), check for events
1845  EventSP event_sp;
1846  while (listener_sp->PeekAtNextEvent()) {
1847  listener_sp->GetEvent(event_sp, std::chrono::seconds(0));
1848 
1849  if (event_sp) {
1850  Broadcaster *broadcaster = event_sp->GetBroadcaster();
1851  if (broadcaster) {
1852  // uint32_t event_type = event_sp->GetType();
1853  ConstString broadcaster_class(
1854  broadcaster->GetBroadcasterClass());
1855  if (broadcaster_class == broadcaster_class_process) {
1857  nullptr);
1858  update = true;
1859  continue; // Don't get any key, just update our view
1860  }
1861  }
1862  }
1863  }
1864  }
1865  } else {
1866  HandleCharResult key_result = m_window_sp->HandleChar(ch);
1867  switch (key_result) {
1868  case eKeyHandled:
1869  debugger.GetCommandInterpreter().UpdateExecutionContext(nullptr);
1870  update = true;
1871  break;
1872  case eKeyNotHandled:
1873  break;
1874  case eQuitApplication:
1875  done = true;
1876  break;
1877  }
1878  }
1879  }
1880 
1881  debugger.CancelForwardEvents(listener_sp);
1882  }
1883 
1884  WindowSP &GetMainWindow() {
1885  if (!m_window_sp)
1886  m_window_sp = std::make_shared<Window>("main", stdscr, false);
1887  return m_window_sp;
1888  }
1889 
1890  WindowDelegates &GetWindowDelegates() { return m_window_delegates; }
1891 
1892 protected:
1893  WindowSP m_window_sp;
1894  WindowDelegates m_window_delegates;
1895  SCREEN *m_screen;
1896  FILE *m_in;
1897  FILE *m_out;
1898 };
1899 
1900 } // namespace curses
1901 
1902 using namespace curses;
1903 
1904 struct Row {
1907  // The process stop ID when the children were calculated.
1909  int row_idx;
1910  int x;
1911  int y;
1913  bool expanded;
1915  std::vector<Row> children;
1916 
1917  Row(const ValueObjectSP &v, Row *p)
1918  : value(v, lldb::eDynamicDontRunTarget, true), parent(p), row_idx(0),
1919  x(1), y(1), might_have_children(v ? v->MightHaveChildren() : false),
1920  expanded(false), calculated_children(false), children() {}
1921 
1922  size_t GetDepth() const {
1923  if (parent)
1924  return 1 + parent->GetDepth();
1925  return 0;
1926  }
1927 
1928  void Expand() {
1929  expanded = true;
1930  }
1931 
1932  std::vector<Row> &GetChildren() {
1933  ProcessSP process_sp = value.GetProcessSP();
1934  auto stop_id = process_sp->GetStopID();
1935  if (process_sp && stop_id != children_stop_id) {
1936  children_stop_id = stop_id;
1937  calculated_children = false;
1938  }
1939  if (!calculated_children) {
1940  children.clear();
1941  calculated_children = true;
1942  ValueObjectSP valobj = value.GetSP();
1943  if (valobj) {
1944  const size_t num_children = valobj->GetNumChildren();
1945  for (size_t i = 0; i < num_children; ++i) {
1946  children.push_back(Row(valobj->GetChildAtIndex(i, true), this));
1947  }
1948  }
1949  }
1950  return children;
1951  }
1952 
1953  void Unexpand() {
1954  expanded = false;
1955  calculated_children = false;
1956  children.clear();
1957  }
1958 
1959  void DrawTree(Window &window) {
1960  if (parent)
1961  parent->DrawTreeForChild(window, this, 0);
1962 
1963  if (might_have_children) {
1964  // It we can get UTF8 characters to work we should try to use the
1965  // "symbol" UTF8 string below
1966  // const char *symbol = "";
1967  // if (row.expanded)
1968  // symbol = "\xe2\x96\xbd ";
1969  // else
1970  // symbol = "\xe2\x96\xb7 ";
1971  // window.PutCString (symbol);
1972 
1973  // The ACS_DARROW and ACS_RARROW don't look very nice they are just a 'v'
1974  // or '>' character...
1975  // if (expanded)
1976  // window.PutChar (ACS_DARROW);
1977  // else
1978  // window.PutChar (ACS_RARROW);
1979  // Since we can't find any good looking right arrow/down arrow symbols,
1980  // just use a diamond...
1981  window.PutChar(ACS_DIAMOND);
1982  window.PutChar(ACS_HLINE);
1983  }
1984  }
1985 
1986  void DrawTreeForChild(Window &window, Row *child, uint32_t reverse_depth) {
1987  if (parent)
1988  parent->DrawTreeForChild(window, this, reverse_depth + 1);
1989 
1990  if (&GetChildren().back() == child) {
1991  // Last child
1992  if (reverse_depth == 0) {
1993  window.PutChar(ACS_LLCORNER);
1994  window.PutChar(ACS_HLINE);
1995  } else {
1996  window.PutChar(' ');
1997  window.PutChar(' ');
1998  }
1999  } else {
2000  if (reverse_depth == 0) {
2001  window.PutChar(ACS_LTEE);
2002  window.PutChar(ACS_HLINE);
2003  } else {
2004  window.PutChar(ACS_VLINE);
2005  window.PutChar(' ');
2006  }
2007  }
2008  }
2009 };
2010 
2013 };
2014 
2015 class TreeItem;
2016 
2018 public:
2019  TreeDelegate() = default;
2020  virtual ~TreeDelegate() = default;
2021 
2022  virtual void TreeDelegateDrawTreeItem(TreeItem &item, Window &window) = 0;
2023  virtual void TreeDelegateGenerateChildren(TreeItem &item) = 0;
2024  virtual bool TreeDelegateItemSelected(
2025  TreeItem &item) = 0; // Return true if we need to update views
2026 };
2027 
2028 typedef std::shared_ptr<TreeDelegate> TreeDelegateSP;
2029 
2030 class TreeItem {
2031 public:
2032  TreeItem(TreeItem *parent, TreeDelegate &delegate, bool might_have_children)
2033  : m_parent(parent), m_delegate(delegate), m_user_data(nullptr),
2034  m_identifier(0), m_row_idx(-1), m_children(),
2035  m_might_have_children(might_have_children), m_is_expanded(false) {}
2036 
2037  TreeItem &operator=(const TreeItem &rhs) {
2038  if (this != &rhs) {
2039  m_parent = rhs.m_parent;
2040  m_delegate = rhs.m_delegate;
2041  m_user_data = rhs.m_user_data;
2042  m_identifier = rhs.m_identifier;
2043  m_row_idx = rhs.m_row_idx;
2044  m_children = rhs.m_children;
2045  m_might_have_children = rhs.m_might_have_children;
2046  m_is_expanded = rhs.m_is_expanded;
2047  }
2048  return *this;
2049  }
2050 
2051  size_t GetDepth() const {
2052  if (m_parent)
2053  return 1 + m_parent->GetDepth();
2054  return 0;
2055  }
2056 
2057  int GetRowIndex() const { return m_row_idx; }
2058 
2059  void ClearChildren() { m_children.clear(); }
2060 
2061  void Resize(size_t n, const TreeItem &t) { m_children.resize(n, t); }
2062 
2063  TreeItem &operator[](size_t i) { return m_children[i]; }
2064 
2065  void SetRowIndex(int row_idx) { m_row_idx = row_idx; }
2066 
2067  size_t GetNumChildren() {
2068  m_delegate.TreeDelegateGenerateChildren(*this);
2069  return m_children.size();
2070  }
2071 
2072  void ItemWasSelected() { m_delegate.TreeDelegateItemSelected(*this); }
2073 
2074  void CalculateRowIndexes(int &row_idx) {
2075  SetRowIndex(row_idx);
2076  ++row_idx;
2077 
2078  const bool expanded = IsExpanded();
2079 
2080  // The root item must calculate its children, or we must calculate the
2081  // number of children if the item is expanded
2082  if (m_parent == nullptr || expanded)
2083  GetNumChildren();
2084 
2085  for (auto &item : m_children) {
2086  if (expanded)
2087  item.CalculateRowIndexes(row_idx);
2088  else
2089  item.SetRowIndex(-1);
2090  }
2091  }
2092 
2093  TreeItem *GetParent() { return m_parent; }
2094 
2095  bool IsExpanded() const { return m_is_expanded; }
2096 
2097  void Expand() { m_is_expanded = true; }
2098 
2099  void Unexpand() { m_is_expanded = false; }
2100 
2101  bool Draw(Window &window, const int first_visible_row,
2102  const uint32_t selected_row_idx, int &row_idx, int &num_rows_left) {
2103  if (num_rows_left <= 0)
2104  return false;
2105 
2106  if (m_row_idx >= first_visible_row) {
2107  window.MoveCursor(2, row_idx + 1);
2108 
2109  if (m_parent)
2110  m_parent->DrawTreeForChild(window, this, 0);
2111 
2112  if (m_might_have_children) {
2113  // It we can get UTF8 characters to work we should try to use the
2114  // "symbol" UTF8 string below
2115  // const char *symbol = "";
2116  // if (row.expanded)
2117  // symbol = "\xe2\x96\xbd ";
2118  // else
2119  // symbol = "\xe2\x96\xb7 ";
2120  // window.PutCString (symbol);
2121 
2122  // The ACS_DARROW and ACS_RARROW don't look very nice they are just a
2123  // 'v' or '>' character...
2124  // if (expanded)
2125  // window.PutChar (ACS_DARROW);
2126  // else
2127  // window.PutChar (ACS_RARROW);
2128  // Since we can't find any good looking right arrow/down arrow symbols,
2129  // just use a diamond...
2130  window.PutChar(ACS_DIAMOND);
2131  window.PutChar(ACS_HLINE);
2132  }
2133  bool highlight = (selected_row_idx == static_cast<size_t>(m_row_idx)) &&
2134  window.IsActive();
2135 
2136  if (highlight)
2137  window.AttributeOn(A_REVERSE);
2138 
2139  m_delegate.TreeDelegateDrawTreeItem(*this, window);
2140 
2141  if (highlight)
2142  window.AttributeOff(A_REVERSE);
2143  ++row_idx;
2144  --num_rows_left;
2145  }
2146 
2147  if (num_rows_left <= 0)
2148  return false; // We are done drawing...
2149 
2150  if (IsExpanded()) {
2151  for (auto &item : m_children) {
2152  // If we displayed all the rows and item.Draw() returns false we are
2153  // done drawing and can exit this for loop
2154  if (!item.Draw(window, first_visible_row, selected_row_idx, row_idx,
2155  num_rows_left))
2156  break;
2157  }
2158  }
2159  return num_rows_left >= 0; // Return true if not done drawing yet
2160  }
2161 
2162  void DrawTreeForChild(Window &window, TreeItem *child,
2163  uint32_t reverse_depth) {
2164  if (m_parent)
2165  m_parent->DrawTreeForChild(window, this, reverse_depth + 1);
2166 
2167  if (&m_children.back() == child) {
2168  // Last child
2169  if (reverse_depth == 0) {
2170  window.PutChar(ACS_LLCORNER);
2171  window.PutChar(ACS_HLINE);
2172  } else {
2173  window.PutChar(' ');
2174  window.PutChar(' ');
2175  }
2176  } else {
2177  if (reverse_depth == 0) {
2178  window.PutChar(ACS_LTEE);
2179  window.PutChar(ACS_HLINE);
2180  } else {
2181  window.PutChar(ACS_VLINE);
2182  window.PutChar(' ');
2183  }
2184  }
2185  }
2186 
2188  if (static_cast<uint32_t>(m_row_idx) == row_idx)
2189  return this;
2190  if (m_children.empty())
2191  return nullptr;
2192  if (IsExpanded()) {
2193  for (auto &item : m_children) {
2194  TreeItem *selected_item_ptr = item.GetItemForRowIndex(row_idx);
2195  if (selected_item_ptr)
2196  return selected_item_ptr;
2197  }
2198  }
2199  return nullptr;
2200  }
2201 
2202  void *GetUserData() const { return m_user_data; }
2203 
2204  void SetUserData(void *user_data) { m_user_data = user_data; }
2205 
2206  uint64_t GetIdentifier() const { return m_identifier; }
2207 
2208  void SetIdentifier(uint64_t identifier) { m_identifier = identifier; }
2209 
2210  void SetMightHaveChildren(bool b) { m_might_have_children = b; }
2211 
2212 protected:
2216  uint64_t m_identifier;
2217  int m_row_idx; // Zero based visible row index, -1 if not visible or for the
2218  // root item
2219  std::vector<TreeItem> m_children;
2222 };
2223 
2225 public:
2226  TreeWindowDelegate(Debugger &debugger, const TreeDelegateSP &delegate_sp)
2227  : m_debugger(debugger), m_delegate_sp(delegate_sp),
2228  m_root(nullptr, *delegate_sp, true), m_selected_item(nullptr),
2229  m_num_rows(0), m_selected_row_idx(0), m_first_visible_row(0),
2230  m_min_x(0), m_min_y(0), m_max_x(0), m_max_y(0) {}
2231 
2232  int NumVisibleRows() const { return m_max_y - m_min_y; }
2233 
2234  bool WindowDelegateDraw(Window &window, bool force) override {
2235  ExecutionContext exe_ctx(
2236  m_debugger.GetCommandInterpreter().GetExecutionContext());
2237  Process *process = exe_ctx.GetProcessPtr();
2238 
2239  bool display_content = false;
2240  if (process) {
2241  StateType state = process->GetState();
2242  if (StateIsStoppedState(state, true)) {
2243  // We are stopped, so it is ok to
2244  display_content = true;
2245  } else if (StateIsRunningState(state)) {
2246  return true; // Don't do any updating when we are running
2247  }
2248  }
2249 
2250  m_min_x = 2;
2251  m_min_y = 1;
2252  m_max_x = window.GetWidth() - 1;
2253  m_max_y = window.GetHeight() - 1;
2254 
2255  window.Erase();
2256  window.DrawTitleBox(window.GetName());
2257 
2258  if (display_content) {
2259  const int num_visible_rows = NumVisibleRows();
2260  m_num_rows = 0;
2261  m_root.CalculateRowIndexes(m_num_rows);
2262 
2263  // If we unexpanded while having something selected our total number of
2264  // rows is less than the num visible rows, then make sure we show all the
2265  // rows by setting the first visible row accordingly.
2266  if (m_first_visible_row > 0 && m_num_rows < num_visible_rows)
2267  m_first_visible_row = 0;
2268 
2269  // Make sure the selected row is always visible
2270  if (m_selected_row_idx < m_first_visible_row)
2271  m_first_visible_row = m_selected_row_idx;
2272  else if (m_first_visible_row + num_visible_rows <= m_selected_row_idx)
2273  m_first_visible_row = m_selected_row_idx - num_visible_rows + 1;
2274 
2275  int row_idx = 0;
2276  int num_rows_left = num_visible_rows;
2277  m_root.Draw(window, m_first_visible_row, m_selected_row_idx, row_idx,
2278  num_rows_left);
2279  // Get the selected row
2280  m_selected_item = m_root.GetItemForRowIndex(m_selected_row_idx);
2281  } else {
2282  m_selected_item = nullptr;
2283  }
2284 
2285  window.DeferredRefresh();
2286 
2287  return true; // Drawing handled
2288  }
2289 
2290  const char *WindowDelegateGetHelpText() override {
2291  return "Thread window keyboard shortcuts:";
2292  }
2293 
2295  static curses::KeyHelp g_source_view_key_help[] = {
2296  {KEY_UP, "Select previous item"},
2297  {KEY_DOWN, "Select next item"},
2298  {KEY_RIGHT, "Expand the selected item"},
2299  {KEY_LEFT,
2300  "Unexpand the selected item or select parent if not expanded"},
2301  {KEY_PPAGE, "Page up"},
2302  {KEY_NPAGE, "Page down"},
2303  {'h', "Show help dialog"},
2304  {' ', "Toggle item expansion"},
2305  {',', "Page up"},
2306  {'.', "Page down"},
2307  {'\0', nullptr}};
2308  return g_source_view_key_help;
2309  }
2310 
2312  switch (c) {
2313  case ',':
2314  case KEY_PPAGE:
2315  // Page up key
2316  if (m_first_visible_row > 0) {
2317  if (m_first_visible_row > m_max_y)
2318  m_first_visible_row -= m_max_y;
2319  else
2320  m_first_visible_row = 0;
2321  m_selected_row_idx = m_first_visible_row;
2322  m_selected_item = m_root.GetItemForRowIndex(m_selected_row_idx);
2323  if (m_selected_item)
2324  m_selected_item->ItemWasSelected();
2325  }
2326  return eKeyHandled;
2327 
2328  case '.':
2329  case KEY_NPAGE:
2330  // Page down key
2331  if (m_num_rows > m_max_y) {
2332  if (m_first_visible_row + m_max_y < m_num_rows) {
2333  m_first_visible_row += m_max_y;
2334  m_selected_row_idx = m_first_visible_row;
2335  m_selected_item = m_root.GetItemForRowIndex(m_selected_row_idx);
2336  if (m_selected_item)
2337  m_selected_item->ItemWasSelected();
2338  }
2339  }
2340  return eKeyHandled;
2341 
2342  case KEY_UP:
2343  if (m_selected_row_idx > 0) {
2344  --m_selected_row_idx;
2345  m_selected_item = m_root.GetItemForRowIndex(m_selected_row_idx);
2346  if (m_selected_item)
2347  m_selected_item->ItemWasSelected();
2348  }
2349  return eKeyHandled;
2350 
2351  case KEY_DOWN:
2352  if (m_selected_row_idx + 1 < m_num_rows) {
2353  ++m_selected_row_idx;
2354  m_selected_item = m_root.GetItemForRowIndex(m_selected_row_idx);
2355  if (m_selected_item)
2356  m_selected_item->ItemWasSelected();
2357  }
2358  return eKeyHandled;
2359 
2360  case KEY_RIGHT:
2361  if (m_selected_item) {
2362  if (!m_selected_item->IsExpanded())
2363  m_selected_item->Expand();
2364  }
2365  return eKeyHandled;
2366 
2367  case KEY_LEFT:
2368  if (m_selected_item) {
2369  if (m_selected_item->IsExpanded())
2370  m_selected_item->Unexpand();
2371  else if (m_selected_item->GetParent()) {
2372  m_selected_row_idx = m_selected_item->GetParent()->GetRowIndex();
2373  m_selected_item = m_root.GetItemForRowIndex(m_selected_row_idx);
2374  if (m_selected_item)
2375  m_selected_item->ItemWasSelected();
2376  }
2377  }
2378  return eKeyHandled;
2379 
2380  case ' ':
2381  // Toggle expansion state when SPACE is pressed
2382  if (m_selected_item) {
2383  if (m_selected_item->IsExpanded())
2384  m_selected_item->Unexpand();
2385  else
2386  m_selected_item->Expand();
2387  }
2388  return eKeyHandled;
2389 
2390  case 'h':
2391  window.CreateHelpSubwindow();
2392  return eKeyHandled;
2393 
2394  default:
2395  break;
2396  }
2397  return eKeyNotHandled;
2398  }
2399 
2400 protected:
2408  int m_min_x;
2409  int m_min_y;
2410  int m_max_x;
2411  int m_max_y;
2412 };
2413 
2415 public:
2418  "frame #${frame.index}: {${function.name}${function.pc-offset}}}",
2419  m_format);
2420  }
2421 
2422  ~FrameTreeDelegate() override = default;
2423 
2424  void TreeDelegateDrawTreeItem(TreeItem &item, Window &window) override {
2425  Thread *thread = (Thread *)item.GetUserData();
2426  if (thread) {
2427  const uint64_t frame_idx = item.GetIdentifier();
2428  StackFrameSP frame_sp = thread->GetStackFrameAtIndex(frame_idx);
2429  if (frame_sp) {
2430  StreamString strm;
2431  const SymbolContext &sc =
2432  frame_sp->GetSymbolContext(eSymbolContextEverything);
2433  ExecutionContext exe_ctx(frame_sp);
2434  if (FormatEntity::Format(m_format, strm, &sc, &exe_ctx, nullptr,
2435  nullptr, false, false)) {
2436  int right_pad = 1;
2437  window.PutCStringTruncated(strm.GetString().str().c_str(), right_pad);
2438  }
2439  }
2440  }
2441  }
2442 
2444  // No children for frames yet...
2445  }
2446 
2447  bool TreeDelegateItemSelected(TreeItem &item) override {
2448  Thread *thread = (Thread *)item.GetUserData();
2449  if (thread) {
2450  thread->GetProcess()->GetThreadList().SetSelectedThreadByID(
2451  thread->GetID());
2452  const uint64_t frame_idx = item.GetIdentifier();
2453  thread->SetSelectedFrameByIndex(frame_idx);
2454  return true;
2455  }
2456  return false;
2457  }
2458 
2459 protected:
2461 };
2462 
2464 public:
2466  : TreeDelegate(), m_debugger(debugger), m_tid(LLDB_INVALID_THREAD_ID),
2467  m_stop_id(UINT32_MAX) {
2468  FormatEntity::Parse("thread #${thread.index}: tid = ${thread.id}{, stop "
2469  "reason = ${thread.stop-reason}}",
2470  m_format);
2471  }
2472 
2473  ~ThreadTreeDelegate() override = default;
2474 
2475  ProcessSP GetProcess() {
2476  return m_debugger.GetCommandInterpreter()
2477  .GetExecutionContext()
2478  .GetProcessSP();
2479  }
2480 
2481  ThreadSP GetThread(const TreeItem &item) {
2482  ProcessSP process_sp = GetProcess();
2483  if (process_sp)
2484  return process_sp->GetThreadList().FindThreadByID(item.GetIdentifier());
2485  return ThreadSP();
2486  }
2487 
2488  void TreeDelegateDrawTreeItem(TreeItem &item, Window &window) override {
2489  ThreadSP thread_sp = GetThread(item);
2490  if (thread_sp) {
2491  StreamString strm;
2492  ExecutionContext exe_ctx(thread_sp);
2493  if (FormatEntity::Format(m_format, strm, nullptr, &exe_ctx, nullptr,
2494  nullptr, false, false)) {
2495  int right_pad = 1;
2496  window.PutCStringTruncated(strm.GetString().str().c_str(), right_pad);
2497  }
2498  }
2499  }
2500 
2502  ProcessSP process_sp = GetProcess();
2503  if (process_sp && process_sp->IsAlive()) {
2504  StateType state = process_sp->GetState();
2505  if (StateIsStoppedState(state, true)) {
2506  ThreadSP thread_sp = GetThread(item);
2507  if (thread_sp) {
2508  if (m_stop_id == process_sp->GetStopID() &&
2509  thread_sp->GetID() == m_tid)
2510  return; // Children are already up to date
2511  if (!m_frame_delegate_sp) {
2512  // Always expand the thread item the first time we show it
2513  m_frame_delegate_sp = std::make_shared<FrameTreeDelegate>();
2514  }
2515 
2516  m_stop_id = process_sp->GetStopID();
2517  m_tid = thread_sp->GetID();
2518 
2519  TreeItem t(&item, *m_frame_delegate_sp, false);
2520  size_t num_frames = thread_sp->GetStackFrameCount();
2521  item.Resize(num_frames, t);
2522  for (size_t i = 0; i < num_frames; ++i) {
2523  item[i].SetUserData(thread_sp.get());
2524  item[i].SetIdentifier(i);
2525  }
2526  }
2527  return;
2528  }
2529  }
2530  item.ClearChildren();
2531  }
2532 
2533  bool TreeDelegateItemSelected(TreeItem &item) override {
2534  ProcessSP process_sp = GetProcess();
2535  if (process_sp && process_sp->IsAlive()) {
2536  StateType state = process_sp->GetState();
2537  if (StateIsStoppedState(state, true)) {
2538  ThreadSP thread_sp = GetThread(item);
2539  if (thread_sp) {
2540  ThreadList &thread_list = thread_sp->GetProcess()->GetThreadList();
2541  std::lock_guard<std::recursive_mutex> guard(thread_list.GetMutex());
2542  ThreadSP selected_thread_sp = thread_list.GetSelectedThread();
2543  if (selected_thread_sp->GetID() != thread_sp->GetID()) {
2544  thread_list.SetSelectedThreadByID(thread_sp->GetID());
2545  return true;
2546  }
2547  }
2548  }
2549  }
2550  return false;
2551  }
2552 
2553 protected:
2555  std::shared_ptr<FrameTreeDelegate> m_frame_delegate_sp;
2559 };
2560 
2562 public:
2564  : TreeDelegate(), m_thread_delegate_sp(), m_debugger(debugger),
2565  m_stop_id(UINT32_MAX) {
2566  FormatEntity::Parse("process ${process.id}{, name = ${process.name}}",
2567  m_format);
2568  }
2569 
2570  ~ThreadsTreeDelegate() override = default;
2571 
2572  ProcessSP GetProcess() {
2573  return m_debugger.GetCommandInterpreter()
2574  .GetExecutionContext()
2575  .GetProcessSP();
2576  }
2577 
2578  void TreeDelegateDrawTreeItem(TreeItem &item, Window &window) override {
2579  ProcessSP process_sp = GetProcess();
2580  if (process_sp && process_sp->IsAlive()) {
2581  StreamString strm;
2582  ExecutionContext exe_ctx(process_sp);
2583  if (FormatEntity::Format(m_format, strm, nullptr, &exe_ctx, nullptr,
2584  nullptr, false, false)) {
2585  int right_pad = 1;
2586  window.PutCStringTruncated(strm.GetString().str().c_str(), right_pad);
2587  }
2588  }
2589  }
2590 
2592  ProcessSP process_sp = GetProcess();
2593  if (process_sp && process_sp->IsAlive()) {
2594  StateType state = process_sp->GetState();
2595  if (StateIsStoppedState(state, true)) {
2596  const uint32_t stop_id = process_sp->GetStopID();
2597  if (m_stop_id == stop_id)
2598  return; // Children are already up to date
2599 
2600  m_stop_id = stop_id;
2601 
2602  if (!m_thread_delegate_sp) {
2603  // Always expand the thread item the first time we show it
2604  // item.Expand();
2605  m_thread_delegate_sp =
2606  std::make_shared<ThreadTreeDelegate>(m_debugger);
2607  }
2608 
2609  TreeItem t(&item, *m_thread_delegate_sp, false);
2610  ThreadList &threads = process_sp->GetThreadList();
2611  std::lock_guard<std::recursive_mutex> guard(threads.GetMutex());
2612  size_t num_threads = threads.GetSize();
2613  item.Resize(num_threads, t);
2614  for (size_t i = 0; i < num_threads; ++i) {
2615  item[i].SetIdentifier(threads.GetThreadAtIndex(i)->GetID());
2616  item[i].SetMightHaveChildren(true);
2617  }
2618  return;
2619  }
2620  }
2621  item.ClearChildren();
2622  }
2623 
2624  bool TreeDelegateItemSelected(TreeItem &item) override { return false; }
2625 
2626 protected:
2627  std::shared_ptr<ThreadTreeDelegate> m_thread_delegate_sp;
2631 };
2632 
2634 public:
2636  : m_rows(), m_selected_row(nullptr),
2637  m_selected_row_idx(0), m_first_visible_row(0), m_num_rows(0),
2638  m_max_x(0), m_max_y(0) {}
2639 
2641  : m_rows(), m_selected_row(nullptr),
2642  m_selected_row_idx(0), m_first_visible_row(0), m_num_rows(0),
2643  m_max_x(0), m_max_y(0) {
2644  SetValues(valobj_list);
2645  }
2646 
2647  ~ValueObjectListDelegate() override = default;
2648 
2649  void SetValues(ValueObjectList &valobj_list) {
2650  m_selected_row = nullptr;
2651  m_selected_row_idx = 0;
2652  m_first_visible_row = 0;
2653  m_num_rows = 0;
2654  m_rows.clear();
2655  for (auto &valobj_sp : valobj_list.GetObjects())
2656  m_rows.push_back(Row(valobj_sp, nullptr));
2657  }
2658 
2659  bool WindowDelegateDraw(Window &window, bool force) override {
2660  m_num_rows = 0;
2661  m_min_x = 2;
2662  m_min_y = 1;
2663  m_max_x = window.GetWidth() - 1;
2664  m_max_y = window.GetHeight() - 1;
2665 
2666  window.Erase();
2667  window.DrawTitleBox(window.GetName());
2668 
2669  const int num_visible_rows = NumVisibleRows();
2670  const int num_rows = CalculateTotalNumberRows(m_rows);
2671 
2672  // If we unexpanded while having something selected our total number of
2673  // rows is less than the num visible rows, then make sure we show all the
2674  // rows by setting the first visible row accordingly.
2675  if (m_first_visible_row > 0 && num_rows < num_visible_rows)
2676  m_first_visible_row = 0;
2677 
2678  // Make sure the selected row is always visible
2679  if (m_selected_row_idx < m_first_visible_row)
2680  m_first_visible_row = m_selected_row_idx;
2681  else if (m_first_visible_row + num_visible_rows <= m_selected_row_idx)
2682  m_first_visible_row = m_selected_row_idx - num_visible_rows + 1;
2683 
2684  DisplayRows(window, m_rows, g_options);
2685 
2686  window.DeferredRefresh();
2687 
2688  // Get the selected row
2689  m_selected_row = GetRowForRowIndex(m_selected_row_idx);
2690  // Keep the cursor on the selected row so the highlight and the cursor are
2691  // always on the same line
2692  if (m_selected_row)
2693  window.MoveCursor(m_selected_row->x, m_selected_row->y);
2694 
2695  return true; // Drawing handled
2696  }
2697 
2699  static curses::KeyHelp g_source_view_key_help[] = {
2700  {KEY_UP, "Select previous item"},
2701  {KEY_DOWN, "Select next item"},
2702  {KEY_RIGHT, "Expand selected item"},
2703  {KEY_LEFT, "Unexpand selected item or select parent if not expanded"},
2704  {KEY_PPAGE, "Page up"},
2705  {KEY_NPAGE, "Page down"},
2706  {'A', "Format as annotated address"},
2707  {'b', "Format as binary"},
2708  {'B', "Format as hex bytes with ASCII"},
2709  {'c', "Format as character"},
2710  {'d', "Format as a signed integer"},
2711  {'D', "Format selected value using the default format for the type"},
2712  {'f', "Format as float"},
2713  {'h', "Show help dialog"},
2714  {'i', "Format as instructions"},
2715  {'o', "Format as octal"},
2716  {'p', "Format as pointer"},
2717  {'s', "Format as C string"},
2718  {'t', "Toggle showing/hiding type names"},
2719  {'u', "Format as an unsigned integer"},
2720  {'x', "Format as hex"},
2721  {'X', "Format as uppercase hex"},
2722  {' ', "Toggle item expansion"},
2723  {',', "Page up"},
2724  {'.', "Page down"},
2725  {'\0', nullptr}};
2726  return g_source_view_key_help;
2727  }
2728 
2730  switch (c) {
2731  case 'x':
2732  case 'X':
2733  case 'o':
2734  case 's':
2735  case 'u':
2736  case 'd':
2737  case 'D':
2738  case 'i':
2739  case 'A':
2740  case 'p':
2741  case 'c':
2742  case 'b':
2743  case 'B':
2744  case 'f':
2745  // Change the format for the currently selected item
2746  if (m_selected_row) {
2747  auto valobj_sp = m_selected_row->value.GetSP();
2748  if (valobj_sp)
2749  valobj_sp->SetFormat(FormatForChar(c));
2750  }
2751  return eKeyHandled;
2752 
2753  case 't':
2754  // Toggle showing type names
2755  g_options.show_types = !g_options.show_types;
2756  return eKeyHandled;
2757 
2758  case ',':
2759  case KEY_PPAGE:
2760  // Page up key
2761  if (m_first_visible_row > 0) {
2762  if (static_cast<int>(m_first_visible_row) > m_max_y)
2763  m_first_visible_row -= m_max_y;
2764  else
2765  m_first_visible_row = 0;
2766  m_selected_row_idx = m_first_visible_row;
2767  }
2768  return eKeyHandled;
2769 
2770  case '.':
2771  case KEY_NPAGE:
2772  // Page down key
2773  if (m_num_rows > static_cast<size_t>(m_max_y)) {
2774  if (m_first_visible_row + m_max_y < m_num_rows) {
2775  m_first_visible_row += m_max_y;
2776  m_selected_row_idx = m_first_visible_row;
2777  }
2778  }
2779  return eKeyHandled;
2780 
2781  case KEY_UP:
2782  if (m_selected_row_idx > 0)
2783  --m_selected_row_idx;
2784  return eKeyHandled;
2785 
2786  case KEY_DOWN:
2787  if (m_selected_row_idx + 1 < m_num_rows)
2788  ++m_selected_row_idx;
2789  return eKeyHandled;
2790 
2791  case KEY_RIGHT:
2792  if (m_selected_row) {
2793  if (!m_selected_row->expanded)
2794  m_selected_row->Expand();
2795  }
2796  return eKeyHandled;
2797 
2798  case KEY_LEFT:
2799  if (m_selected_row) {
2800  if (m_selected_row->expanded)
2801  m_selected_row->Unexpand();
2802  else if (m_selected_row->parent)
2803  m_selected_row_idx = m_selected_row->parent->row_idx;
2804  }
2805  return eKeyHandled;
2806 
2807  case ' ':
2808  // Toggle expansion state when SPACE is pressed
2809  if (m_selected_row) {
2810  if (m_selected_row->expanded)
2811  m_selected_row->Unexpand();
2812  else
2813  m_selected_row->Expand();
2814  }
2815  return eKeyHandled;
2816 
2817  case 'h':
2818  window.CreateHelpSubwindow();
2819  return eKeyHandled;
2820 
2821  default:
2822  break;
2823  }
2824  return eKeyNotHandled;
2825  }
2826 
2827 protected:
2828  std::vector<Row> m_rows;
2833  int m_min_x;
2834  int m_min_y;
2835  int m_max_x;
2836  int m_max_y;
2837 
2838  static Format FormatForChar(int c) {
2839  switch (c) {
2840  case 'x':
2841  return eFormatHex;
2842  case 'X':
2843  return eFormatHexUppercase;
2844  case 'o':
2845  return eFormatOctal;
2846  case 's':
2847  return eFormatCString;
2848  case 'u':
2849  return eFormatUnsigned;
2850  case 'd':
2851  return eFormatDecimal;
2852  case 'D':
2853  return eFormatDefault;
2854  case 'i':
2855  return eFormatInstruction;
2856  case 'A':
2857  return eFormatAddressInfo;
2858  case 'p':
2859  return eFormatPointer;
2860  case 'c':
2861  return eFormatChar;
2862  case 'b':
2863  return eFormatBinary;
2864  case 'B':
2865  return eFormatBytesWithASCII;
2866  case 'f':
2867  return eFormatFloat;
2868  }
2869  return eFormatDefault;
2870  }
2871 
2872  bool DisplayRowObject(Window &window, Row &row, DisplayOptions &options,
2873  bool highlight, bool last_child) {
2874  ValueObject *valobj = row.value.GetSP().get();
2875 
2876  if (valobj == nullptr)
2877  return false;
2878 
2879  const char *type_name =
2880  options.show_types ? valobj->GetTypeName().GetCString() : nullptr;
2881  const char *name = valobj->GetName().GetCString();
2882  const char *value = valobj->GetValueAsCString();
2883  const char *summary = valobj->GetSummaryAsCString();
2884 
2885  window.MoveCursor(row.x, row.y);
2886 
2887  row.DrawTree(window);
2888 
2889  if (highlight)
2890  window.AttributeOn(A_REVERSE);
2891 
2892  if (type_name && type_name[0])
2893  window.Printf("(%s) ", type_name);
2894 
2895  if (name && name[0])
2896  window.PutCString(name);
2897 
2898  attr_t changd_attr = 0;
2899  if (valobj->GetValueDidChange())
2900  changd_attr = COLOR_PAIR(5) | A_BOLD;
2901 
2902  if (value && value[0]) {
2903  window.PutCString(" = ");
2904  if (changd_attr)
2905  window.AttributeOn(changd_attr);
2906  window.PutCString(value);
2907  if (changd_attr)
2908  window.AttributeOff(changd_attr);
2909  }
2910 
2911  if (summary && summary[0]) {
2912  window.PutChar(' ');
2913  if (changd_attr)
2914  window.AttributeOn(changd_attr);
2915  window.PutCString(summary);
2916  if (changd_attr)
2917  window.AttributeOff(changd_attr);
2918  }
2919 
2920  if (highlight)
2921  window.AttributeOff(A_REVERSE);
2922 
2923  return true;
2924  }
2925 
2926  void DisplayRows(Window &window, std::vector<Row> &rows,
2927  DisplayOptions &options) {
2928  // > 0x25B7
2929  // \/ 0x25BD
2930 
2931  bool window_is_active = window.IsActive();
2932  for (auto &row : rows) {
2933  const bool last_child = row.parent && &rows[rows.size() - 1] == &row;
2934  // Save the row index in each Row structure
2935  row.row_idx = m_num_rows;
2936  if ((m_num_rows >= m_first_visible_row) &&
2937  ((m_num_rows - m_first_visible_row) <
2938  static_cast<size_t>(NumVisibleRows()))) {
2939  row.x = m_min_x;
2940  row.y = m_num_rows - m_first_visible_row + 1;
2941  if (DisplayRowObject(window, row, options,
2942  window_is_active &&
2943  m_num_rows == m_selected_row_idx,
2944  last_child)) {
2945  ++m_num_rows;
2946  } else {
2947  row.x = 0;
2948  row.y = 0;
2949  }
2950  } else {
2951  row.x = 0;
2952  row.y = 0;
2953  ++m_num_rows;
2954  }
2955 
2956  auto &children = row.GetChildren();
2957  if (row.expanded && !children.empty()) {
2958  DisplayRows(window, children, options);
2959  }
2960  }
2961  }
2962 
2963  int CalculateTotalNumberRows(std::vector<Row> &rows) {
2964  int row_count = 0;
2965  for (auto &row : rows) {
2966  ++row_count;
2967  if (row.expanded)
2968  row_count += CalculateTotalNumberRows(row.GetChildren());
2969  }
2970  return row_count;
2971  }
2972 
2973  static Row *GetRowForRowIndexImpl(std::vector<Row> &rows, size_t &row_index) {
2974  for (auto &row : rows) {
2975  if (row_index == 0)
2976  return &row;
2977  else {
2978  --row_index;
2979  auto &children = row.GetChildren();
2980  if (row.expanded && !children.empty()) {
2981  Row *result = GetRowForRowIndexImpl(children, row_index);
2982  if (result)
2983  return result;
2984  }
2985  }
2986  }
2987  return nullptr;
2988  }
2989 
2990  Row *GetRowForRowIndex(size_t row_index) {
2991  return GetRowForRowIndexImpl(m_rows, row_index);
2992  }
2993 
2994  int NumVisibleRows() const { return m_max_y - m_min_y; }
2995 
2997 };
2998 
3000 public:
3002  : ValueObjectListDelegate(), m_debugger(debugger),
3003  m_frame_block(nullptr) {}
3004 
3005  ~FrameVariablesWindowDelegate() override = default;
3006 
3007  const char *WindowDelegateGetHelpText() override {
3008  return "Frame variable window keyboard shortcuts:";
3009  }
3010 
3011  bool WindowDelegateDraw(Window &window, bool force) override {
3012  ExecutionContext exe_ctx(
3013  m_debugger.GetCommandInterpreter().GetExecutionContext());
3014  Process *process = exe_ctx.GetProcessPtr();
3015  Block *frame_block = nullptr;
3016  StackFrame *frame = nullptr;
3017 
3018  if (process) {
3019  StateType state = process->GetState();
3020  if (StateIsStoppedState(state, true)) {
3021  frame = exe_ctx.GetFramePtr();
3022  if (frame)
3023  frame_block = frame->GetFrameBlock();
3024  } else if (StateIsRunningState(state)) {
3025  return true; // Don't do any updating when we are running
3026  }
3027  }
3028 
3029  ValueObjectList local_values;
3030  if (frame_block) {
3031  // Only update the variables if they have changed
3032  if (m_frame_block != frame_block) {
3033  m_frame_block = frame_block;
3034 
3035  VariableList *locals = frame->GetVariableList(true);
3036  if (locals) {
3037  const DynamicValueType use_dynamic = eDynamicDontRunTarget;
3038  const size_t num_locals = locals->GetSize();
3039  for (size_t i = 0; i < num_locals; ++i) {
3040  ValueObjectSP value_sp = frame->GetValueObjectForFrameVariable(
3041  locals->GetVariableAtIndex(i), use_dynamic);
3042  if (value_sp) {
3043  ValueObjectSP synthetic_value_sp = value_sp->GetSyntheticValue();
3044  if (synthetic_value_sp)
3045  local_values.Append(synthetic_value_sp);
3046  else
3047  local_values.Append(value_sp);
3048  }
3049  }
3050  // Update the values
3051  SetValues(local_values);
3052  }
3053  }
3054  } else {
3055  m_frame_block = nullptr;
3056  // Update the values with an empty list if there is no frame
3057  SetValues(local_values);
3058  }
3059 
3060  return ValueObjectListDelegate::WindowDelegateDraw(window, force);
3061  }
3062 
3063 protected:
3066 };
3067 
3069 public:
3071  : ValueObjectListDelegate(), m_debugger(debugger) {}
3072 
3073  ~RegistersWindowDelegate() override = default;
3074 
3075  const char *WindowDelegateGetHelpText() override {
3076  return "Register window keyboard shortcuts:";
3077  }
3078 
3079  bool WindowDelegateDraw(Window &window, bool force) override {
3080  ExecutionContext exe_ctx(
3081  m_debugger.GetCommandInterpreter().GetExecutionContext());
3082  StackFrame *frame = exe_ctx.GetFramePtr();
3083 
3084  ValueObjectList value_list;
3085  if (frame) {
3086  if (frame->GetStackID() != m_stack_id) {
3087  m_stack_id = frame->GetStackID();
3088  RegisterContextSP reg_ctx(frame->GetRegisterContext());
3089  if (reg_ctx) {
3090  const uint32_t num_sets = reg_ctx->GetRegisterSetCount();
3091  for (uint32_t set_idx = 0; set_idx < num_sets; ++set_idx) {
3092  value_list.Append(
3093  ValueObjectRegisterSet::Create(frame, reg_ctx, set_idx));
3094  }
3095  }
3096  SetValues(value_list);
3097  }
3098  } else {
3099  Process *process = exe_ctx.GetProcessPtr();
3100  if (process && process->IsAlive())
3101  return true; // Don't do any updating if we are running
3102  else {
3103  // Update the values with an empty list if there is no process or the
3104  // process isn't alive anymore
3105  SetValues(value_list);
3106  }
3107  }
3108  return ValueObjectListDelegate::WindowDelegateDraw(window, force);
3109  }
3110 
3111 protected:
3114 };
3115 
3116 static const char *CursesKeyToCString(int ch) {
3117  static char g_desc[32];
3118  if (ch >= KEY_F0 && ch < KEY_F0 + 64) {
3119  snprintf(g_desc, sizeof(g_desc), "F%u", ch - KEY_F0);
3120  return g_desc;
3121  }
3122  switch (ch) {
3123  case KEY_DOWN:
3124  return "down";
3125  case KEY_UP:
3126  return "up";
3127  case KEY_LEFT:
3128  return "left";
3129  case KEY_RIGHT:
3130  return "right";
3131  case KEY_HOME:
3132  return "home";
3133  case KEY_BACKSPACE:
3134  return "backspace";
3135  case KEY_DL:
3136  return "delete-line";
3137  case KEY_IL:
3138  return "insert-line";
3139  case KEY_DC:
3140  return "delete-char";
3141  case KEY_IC:
3142  return "insert-char";
3143  case KEY_CLEAR:
3144  return "clear";
3145  case KEY_EOS:
3146  return "clear-to-eos";
3147  case KEY_EOL:
3148  return "clear-to-eol";
3149  case KEY_SF:
3150  return "scroll-forward";
3151  case KEY_SR:
3152  return "scroll-backward";
3153  case KEY_NPAGE:
3154  return "page-down";
3155  case KEY_PPAGE:
3156  return "page-up";
3157  case KEY_STAB:
3158  return "set-tab";
3159  case KEY_CTAB:
3160  return "clear-tab";
3161  case KEY_CATAB:
3162  return "clear-all-tabs";
3163  case KEY_ENTER:
3164  return "enter";
3165  case KEY_PRINT:
3166  return "print";
3167  case KEY_LL:
3168  return "lower-left key";
3169  case KEY_A1:
3170  return "upper left of keypad";
3171  case KEY_A3:
3172  return "upper right of keypad";
3173  case KEY_B2:
3174  return "center of keypad";
3175  case KEY_C1:
3176  return "lower left of keypad";
3177  case KEY_C3:
3178  return "lower right of keypad";
3179  case KEY_BTAB:
3180  return "back-tab key";
3181  case KEY_BEG:
3182  return "begin key";
3183  case KEY_CANCEL:
3184  return "cancel key";
3185  case KEY_CLOSE:
3186  return "close key";
3187  case KEY_COMMAND:
3188  return "command key";
3189  case KEY_COPY:
3190  return "copy key";
3191  case KEY_CREATE:
3192  return "create key";
3193  case KEY_END:
3194  return "end key";
3195  case KEY_EXIT:
3196  return "exit key";
3197  case KEY_FIND:
3198  return "find key";
3199  case KEY_HELP:
3200  return "help key";
3201  case KEY_MARK:
3202  return "mark key";
3203  case KEY_MESSAGE:
3204  return "message key";
3205  case KEY_MOVE:
3206  return "move key";
3207  case KEY_NEXT:
3208  return "next key";
3209  case KEY_OPEN:
3210  return "open key";
3211  case KEY_OPTIONS:
3212  return "options key";
3213  case KEY_PREVIOUS:
3214  return "previous key";
3215  case KEY_REDO:
3216  return "redo key";
3217  case KEY_REFERENCE:
3218  return "reference key";
3219  case KEY_REFRESH:
3220  return "refresh key";
3221  case KEY_REPLACE:
3222  return "replace key";
3223  case KEY_RESTART:
3224  return "restart key";
3225  case KEY_RESUME:
3226  return "resume key";
3227  case KEY_SAVE:
3228  return "save key";
3229  case KEY_SBEG:
3230  return "shifted begin key";
3231  case KEY_SCANCEL:
3232  return "shifted cancel key";
3233  case KEY_SCOMMAND:
3234  return "shifted command key";
3235  case KEY_SCOPY:
3236  return "shifted copy key";
3237  case KEY_SCREATE:
3238  return "shifted create key";
3239  case KEY_SDC:
3240  return "shifted delete-character key";
3241  case KEY_SDL:
3242  return "shifted delete-line key";
3243  case KEY_SELECT:
3244  return "select key";
3245  case KEY_SEND:
3246  return "shifted end key";
3247  case KEY_SEOL:
3248  return "shifted clear-to-end-of-line key";
3249  case KEY_SEXIT:
3250  return "shifted exit key";
3251  case KEY_SFIND:
3252  return "shifted find key";
3253  case KEY_SHELP:
3254  return "shifted help key";
3255  case KEY_SHOME:
3256  return "shifted home key";
3257  case KEY_SIC:
3258  return "shifted insert-character key";
3259  case KEY_SLEFT:
3260  return "shifted left-arrow key";
3261  case KEY_SMESSAGE:
3262  return "shifted message key";
3263  case KEY_SMOVE:
3264  return "shifted move key";
3265  case KEY_SNEXT:
3266  return "shifted next key";
3267  case KEY_SOPTIONS:
3268  return "shifted options key";
3269  case KEY_SPREVIOUS:
3270  return "shifted previous key";
3271  case KEY_SPRINT:
3272  return "shifted print key";
3273  case KEY_SREDO:
3274  return "shifted redo key";
3275  case KEY_SREPLACE:
3276  return "shifted replace key";
3277  case KEY_SRIGHT:
3278  return "shifted right-arrow key";
3279  case KEY_SRSUME:
3280  return "shifted resume key";
3281  case KEY_SSAVE:
3282  return "shifted save key";
3283  case KEY_SSUSPEND:
3284  return "shifted suspend key";
3285  case KEY_SUNDO:
3286  return "shifted undo key";
3287  case KEY_SUSPEND:
3288  return "suspend key";
3289  case KEY_UNDO:
3290  return "undo key";
3291  case KEY_MOUSE:
3292  return "Mouse event has occurred";
3293  case KEY_RESIZE:
3294  return "Terminal resize event";
3295 #ifdef KEY_EVENT
3296  case KEY_EVENT:
3297  return "We were interrupted by an event";
3298 #endif
3299  case KEY_RETURN:
3300  return "return";
3301  case ' ':
3302  return "space";
3303  case '\t':
3304  return "tab";
3305  case KEY_ESCAPE:
3306  return "escape";
3307  default:
3308  if (isprint(ch))
3309  snprintf(g_desc, sizeof(g_desc), "%c", ch);
3310  else
3311  snprintf(g_desc, sizeof(g_desc), "\\x%2.2x", ch);
3312  return g_desc;
3313  }
3314  return nullptr;
3315 }
3316 
3318  KeyHelp *key_help_array)
3319  : m_text(), m_first_visible_line(0) {
3320  if (text && text[0]) {
3321  m_text.SplitIntoLines(text);
3322  m_text.AppendString("");
3323  }
3324  if (key_help_array) {
3325  for (KeyHelp *key = key_help_array; key->ch; ++key) {
3326  StreamString key_description;
3327  key_description.Printf("%10s - %s", CursesKeyToCString(key->ch),
3328  key->description);
3329  m_text.AppendString(key_description.GetString());
3330  }
3331  }
3332 }
3333 
3335 
3337  window.Erase();
3338  const int window_height = window.GetHeight();
3339  int x = 2;
3340  int y = 1;
3341  const int min_y = y;
3342  const int max_y = window_height - 1 - y;
3343  const size_t num_visible_lines = max_y - min_y + 1;
3344  const size_t num_lines = m_text.GetSize();
3345  const char *bottom_message;
3346  if (num_lines <= num_visible_lines)
3347  bottom_message = "Press any key to exit";
3348  else
3349  bottom_message = "Use arrows to scroll, any other key to exit";
3350  window.DrawTitleBox(window.GetName(), bottom_message);
3351  while (y <= max_y) {
3352  window.MoveCursor(x, y);
3353  window.PutCStringTruncated(
3354  m_text.GetStringAtIndex(m_first_visible_line + y - min_y), 1);
3355  ++y;
3356  }
3357  return true;
3358 }
3359 
3361  int key) {
3362  bool done = false;
3363  const size_t num_lines = m_text.GetSize();
3364  const size_t num_visible_lines = window.GetHeight() - 2;
3365 
3366  if (num_lines <= num_visible_lines) {
3367  done = true;
3368  // If we have all lines visible and don't need scrolling, then any key
3369  // press will cause us to exit
3370  } else {
3371  switch (key) {
3372  case KEY_UP:
3373  if (m_first_visible_line > 0)
3375  break;
3376 
3377  case KEY_DOWN:
3378  if (m_first_visible_line + num_visible_lines < num_lines)
3380  break;
3381 
3382  case KEY_PPAGE:
3383  case ',':
3384  if (m_first_visible_line > 0) {
3385  if (static_cast<size_t>(m_first_visible_line) >= num_visible_lines)
3386  m_first_visible_line -= num_visible_lines;
3387  else
3389  }
3390  break;
3391 
3392  case KEY_NPAGE:
3393  case '.':
3394  if (m_first_visible_line + num_visible_lines < num_lines) {
3395  m_first_visible_line += num_visible_lines;
3396  if (static_cast<size_t>(m_first_visible_line) > num_lines)
3397  m_first_visible_line = num_lines - num_visible_lines;
3398  }
3399  break;
3400 
3401  default:
3402  done = true;
3403  break;
3404  }
3405  }
3406  if (done)
3407  window.GetParent()->RemoveSubWindow(&window);
3408  return eKeyHandled;
3409 }
3410 
3412 public:
3413  enum {
3414  eMenuID_LLDB = 1,
3417 
3421 
3429 
3434 
3440 
3442  eMenuID_HelpGUIHelp
3443  };
3444 
3446  : WindowDelegate(), MenuDelegate(), m_app(app), m_debugger(debugger) {}
3447 
3448  ~ApplicationDelegate() override = default;
3449 
3450  bool WindowDelegateDraw(Window &window, bool force) override {
3451  return false; // Drawing not handled, let standard window drawing happen
3452  }
3453 
3454  HandleCharResult WindowDelegateHandleChar(Window &window, int key) override {
3455  switch (key) {
3456  case '\t':
3457  window.SelectNextWindowAsActive();
3458  return eKeyHandled;
3459 
3460  case 'h':
3461  window.CreateHelpSubwindow();
3462  return eKeyHandled;
3463 
3464  case KEY_ESCAPE:
3465  return eQuitApplication;
3466 
3467  default:
3468  break;
3469  }
3470  return eKeyNotHandled;
3471  }
3472 
3473  const char *WindowDelegateGetHelpText() override {
3474  return "Welcome to the LLDB curses GUI.\n\n"
3475  "Press the TAB key to change the selected view.\n"
3476  "Each view has its own keyboard shortcuts, press 'h' to open a "
3477  "dialog to display them.\n\n"
3478  "Common key bindings for all views:";
3479  }
3480 
3482  static curses::KeyHelp g_source_view_key_help[] = {
3483  {'\t', "Select next view"},
3484  {'h', "Show help dialog with view specific key bindings"},
3485  {',', "Page up"},
3486  {'.', "Page down"},
3487  {KEY_UP, "Select previous"},
3488  {KEY_DOWN, "Select next"},
3489  {KEY_LEFT, "Unexpand or select parent"},
3490  {KEY_RIGHT, "Expand"},
3491  {KEY_PPAGE, "Page up"},
3492  {KEY_NPAGE, "Page down"},
3493  {'\0', nullptr}};
3494  return g_source_view_key_help;
3495  }
3496 
3498  switch (menu.GetIdentifier()) {
3499  case eMenuID_ThreadStepIn: {
3500  ExecutionContext exe_ctx =
3501  m_debugger.GetCommandInterpreter().GetExecutionContext();
3502  if (exe_ctx.HasThreadScope()) {
3503  Process *process = exe_ctx.GetProcessPtr();
3504  if (process && process->IsAlive() &&
3505  StateIsStoppedState(process->GetState(), true))
3506  exe_ctx.GetThreadRef().StepIn(true);
3507  }
3508  }
3510 
3511  case eMenuID_ThreadStepOut: {
3512  ExecutionContext exe_ctx =
3513  m_debugger.GetCommandInterpreter().GetExecutionContext();
3514  if (exe_ctx.HasThreadScope()) {
3515  Process *process = exe_ctx.GetProcessPtr();
3516  if (process && process->IsAlive() &&
3517  StateIsStoppedState(process->GetState(), true))
3518  exe_ctx.GetThreadRef().StepOut();
3519  }
3520  }
3522 
3523  case eMenuID_ThreadStepOver: {
3524  ExecutionContext exe_ctx =
3525  m_debugger.GetCommandInterpreter().GetExecutionContext();
3526  if (exe_ctx.HasThreadScope()) {
3527  Process *process = exe_ctx.GetProcessPtr();
3528  if (process && process->IsAlive() &&
3529  StateIsStoppedState(process->GetState(), true))
3530  exe_ctx.GetThreadRef().StepOver(true);
3531  }
3532  }
3534 
3535  case eMenuID_ProcessContinue: {
3536  ExecutionContext exe_ctx =
3537  m_debugger.GetCommandInterpreter().GetExecutionContext();
3538  if (exe_ctx.HasProcessScope()) {
3539  Process *process = exe_ctx.GetProcessPtr();
3540  if (process && process->IsAlive() &&
3541  StateIsStoppedState(process->GetState(), true))
3542  process->Resume();
3543  }
3544  }
3546 
3547  case eMenuID_ProcessKill: {
3548  ExecutionContext exe_ctx =
3549  m_debugger.GetCommandInterpreter().GetExecutionContext();
3550  if (exe_ctx.HasProcessScope()) {
3551  Process *process = exe_ctx.GetProcessPtr();
3552  if (process && process->IsAlive())
3553  process->Destroy(false);
3554  }
3555  }
3557 
3558  case eMenuID_ProcessHalt: {
3559  ExecutionContext exe_ctx =
3560  m_debugger.GetCommandInterpreter().GetExecutionContext();
3561  if (exe_ctx.HasProcessScope()) {
3562  Process *process = exe_ctx.GetProcessPtr();
3563  if (process && process->IsAlive())
3564  process->Halt();
3565  }
3566  }
3568 
3569  case eMenuID_ProcessDetach: {
3570  ExecutionContext exe_ctx =
3571  m_debugger.GetCommandInterpreter().GetExecutionContext();
3572  if (exe_ctx.HasProcessScope()) {
3573  Process *process = exe_ctx.GetProcessPtr();
3574  if (process && process->IsAlive())
3575  process->Detach(false);
3576  }
3577  }
3579 
3580  case eMenuID_Process: {
3581  // Populate the menu with all of the threads if the process is stopped
3582  // when the Process menu gets selected and is about to display its
3583  // submenu.
3584  Menus &submenus = menu.GetSubmenus();
3585  ExecutionContext exe_ctx =
3586  m_debugger.GetCommandInterpreter().GetExecutionContext();
3587  Process *process = exe_ctx.GetProcessPtr();
3588  if (process && process->IsAlive() &&
3589  StateIsStoppedState(process->GetState(), true)) {
3590  if (submenus.size() == 7)
3592  else if (submenus.size() > 8)
3593  submenus.erase(submenus.begin() + 8, submenus.end());
3594 
3595  ThreadList &threads = process->GetThreadList();
3596  std::lock_guard<std::recursive_mutex> guard(threads.GetMutex());
3597  size_t num_threads = threads.GetSize();
3598  for (size_t i = 0; i < num_threads; ++i) {
3599  ThreadSP thread_sp = threads.GetThreadAtIndex(i);
3600  char menu_char = '\0';
3601  if (i < 9)
3602  menu_char = '1' + i;
3603  StreamString thread_menu_title;
3604  thread_menu_title.Printf("Thread %u", thread_sp->GetIndexID());
3605  const char *thread_name = thread_sp->GetName();
3606  if (thread_name && thread_name[0])
3607  thread_menu_title.Printf(" %s", thread_name);
3608  else {
3609  const char *queue_name = thread_sp->GetQueueName();
3610  if (queue_name && queue_name[0])
3611  thread_menu_title.Printf(" %s", queue_name);
3612  }
3613  menu.AddSubmenu(
3614  MenuSP(new Menu(thread_menu_title.GetString().str().c_str(),
3615  nullptr, menu_char, thread_sp->GetID())));
3616  }
3617  } else if (submenus.size() > 7) {
3618  // Remove the separator and any other thread submenu items that were
3619  // previously added
3620  submenus.erase(submenus.begin() + 7, submenus.end());
3621  }
3622  // Since we are adding and removing items we need to recalculate the name
3623  // lengths
3624  menu.RecalculateNameLengths();
3625  }
3627 
3628  case eMenuID_ViewVariables: {
3629  WindowSP main_window_sp = m_app.GetMainWindow();
3630  WindowSP source_window_sp = main_window_sp->FindSubWindow("Source");
3631  WindowSP variables_window_sp = main_window_sp->FindSubWindow("Variables");
3632  WindowSP registers_window_sp = main_window_sp->FindSubWindow("Registers");
3633  const Rect source_bounds = source_window_sp->GetBounds();
3634 
3635  if (variables_window_sp) {
3636  const Rect variables_bounds = variables_window_sp->GetBounds();
3637 
3638  main_window_sp->RemoveSubWindow(variables_window_sp.get());
3639 
3640  if (registers_window_sp) {
3641  // We have a registers window, so give all the area back to the
3642  // registers window
3643  Rect registers_bounds = variables_bounds;
3644  registers_bounds.size.width = source_bounds.size.width;
3645  registers_window_sp->SetBounds(registers_bounds);
3646  } else {
3647  // We have no registers window showing so give the bottom area back
3648  // to the source view
3649  source_window_sp->Resize(source_bounds.size.width,
3650  source_bounds.size.height +
3651  variables_bounds.size.height);
3652  }
3653  } else {
3654  Rect new_variables_rect;
3655  if (registers_window_sp) {
3656  // We have a registers window so split the area of the registers
3657  // window into two columns where the left hand side will be the
3658  // variables and the right hand side will be the registers
3659  const Rect variables_bounds = registers_window_sp->GetBounds();
3660  Rect new_registers_rect;
3661  variables_bounds.VerticalSplitPercentage(0.50, new_variables_rect,
3662  new_registers_rect);
3663  registers_window_sp->SetBounds(new_registers_rect);
3664  } else {
3665  // No variables window, grab the bottom part of the source window
3666  Rect new_source_rect;
3667  source_bounds.HorizontalSplitPercentage(0.70, new_source_rect,
3668  new_variables_rect);
3669  source_window_sp->SetBounds(new_source_rect);
3670  }
3671  WindowSP new_window_sp = main_window_sp->CreateSubWindow(
3672  "Variables", new_variables_rect, false);
3673  new_window_sp->SetDelegate(
3675  }
3676  touchwin(stdscr);
3677  }
3679 
3680  case eMenuID_ViewRegisters: {
3681  WindowSP main_window_sp = m_app.GetMainWindow();
3682  WindowSP source_window_sp = main_window_sp->FindSubWindow("Source");
3683  WindowSP variables_window_sp = main_window_sp->FindSubWindow("Variables");
3684  WindowSP registers_window_sp = main_window_sp->FindSubWindow("Registers");
3685  const Rect source_bounds = source_window_sp->GetBounds();
3686 
3687  if (registers_window_sp) {
3688  if (variables_window_sp) {
3689  const Rect variables_bounds = variables_window_sp->GetBounds();
3690 
3691  // We have a variables window, so give all the area back to the
3692  // variables window
3693  variables_window_sp->Resize(variables_bounds.size.width +
3694  registers_window_sp->GetWidth(),
3695  variables_bounds.size.height);
3696  } else {
3697  // We have no variables window showing so give the bottom area back
3698  // to the source view
3699  source_window_sp->Resize(source_bounds.size.width,
3700  source_bounds.size.height +
3701  registers_window_sp->GetHeight());
3702  }
3703  main_window_sp->RemoveSubWindow(registers_window_sp.get());
3704  } else {
3705  Rect new_regs_rect;
3706  if (variables_window_sp) {
3707  // We have a variables window, split it into two columns where the
3708  // left hand side will be the variables and the right hand side will
3709  // be the registers
3710  const Rect variables_bounds = variables_window_sp->GetBounds();
3711  Rect new_vars_rect;
3712  variables_bounds.VerticalSplitPercentage(0.50, new_vars_rect,
3713  new_regs_rect);
3714  variables_window_sp->SetBounds(new_vars_rect);
3715  } else {
3716  // No registers window, grab the bottom part of the source window
3717  Rect new_source_rect;
3718  source_bounds.HorizontalSplitPercentage(0.70, new_source_rect,
3719  new_regs_rect);
3720  source_window_sp->SetBounds(new_source_rect);
3721  }
3722  WindowSP new_window_sp =
3723  main_window_sp->CreateSubWindow("Registers", new_regs_rect, false);
3724  new_window_sp->SetDelegate(
3725  WindowDelegateSP(new RegistersWindowDelegate(m_debugger)));
3726  }
3727  touchwin(stdscr);
3728  }
3730 
3731  case eMenuID_HelpGUIHelp:
3732  m_app.GetMainWindow()->CreateHelpSubwindow();
3734 
3735  default:
3736  break;
3737  }
3738 
3740  }
3741 
3742 protected:
3745 };
3746 
3748 public:
3749  StatusBarWindowDelegate(Debugger &debugger) : m_debugger(debugger) {
3750  FormatEntity::Parse("Thread: ${thread.id%tid}", m_format);
3751  }
3752 
3753  ~StatusBarWindowDelegate() override = default;
3754 
3755  bool WindowDelegateDraw(Window &window, bool force) override {
3756  ExecutionContext exe_ctx =
3757  m_debugger.GetCommandInterpreter().GetExecutionContext();
3758  Process *process = exe_ctx.GetProcessPtr();
3759  Thread *thread = exe_ctx.GetThreadPtr();
3760  StackFrame *frame = exe_ctx.GetFramePtr();
3761  window.Erase();
3762  window.SetBackground(2);
3763  window.MoveCursor(0, 0);
3764  if (process) {
3765  const StateType state = process->GetState();
3766  window.Printf("Process: %5" PRIu64 " %10s", process->GetID(),
3767  StateAsCString(state));
3768 
3769  if (StateIsStoppedState(state, true)) {
3770  StreamString strm;
3771  if (thread && FormatEntity::Format(m_format, strm, nullptr, &exe_ctx,
3772  nullptr, nullptr, false, false)) {
3773  window.MoveCursor(40, 0);
3774  window.PutCStringTruncated(strm.GetString().str().c_str(), 1);
3775  }
3776 
3777  window.MoveCursor(60, 0);
3778  if (frame)
3779  window.Printf("Frame: %3u PC = 0x%16.16" PRIx64,
3780  frame->GetFrameIndex(),
3782  exe_ctx.GetTargetPtr()));
3783  } else if (state == eStateExited) {
3784  const char *exit_desc = process->GetExitDescription();
3785  const int exit_status = process->GetExitStatus();
3786  if (exit_desc && exit_desc[0])
3787  window.Printf(" with status = %i (%s)", exit_status, exit_desc);
3788  else
3789  window.Printf(" with status = %i", exit_status);
3790  }
3791  }
3792  window.DeferredRefresh();
3793  return true;
3794  }
3795 
3796 protected:
3799 };
3800 
3802 public:
3804  : WindowDelegate(), m_debugger(debugger), m_sc(), m_file_sp(),
3805  m_disassembly_scope(nullptr), m_disassembly_sp(), m_disassembly_range(),
3806  m_title(), m_line_width(4), m_selected_line(0), m_pc_line(0),
3807  m_stop_id(0), m_frame_idx(UINT32_MAX), m_first_visible_line(0),
3808  m_min_x(0), m_min_y(0), m_max_x(0), m_max_y(0) {}
3809 
3810  ~SourceFileWindowDelegate() override = default;
3811 
3812  void Update(const SymbolContext &sc) { m_sc = sc; }
3813 
3814  uint32_t NumVisibleLines() const { return m_max_y - m_min_y; }
3815 
3816  const char *WindowDelegateGetHelpText() override {
3817  return "Source/Disassembly window keyboard shortcuts:";
3818  }
3819 
3821  static curses::KeyHelp g_source_view_key_help[] = {
3822  {KEY_RETURN, "Run to selected line with one shot breakpoint"},
3823  {KEY_UP, "Select previous source line"},
3824  {KEY_DOWN, "Select next source line"},
3825  {KEY_PPAGE, "Page up"},
3826  {KEY_NPAGE, "Page down"},
3827  {'b', "Set breakpoint on selected source/disassembly line"},
3828  {'c', "Continue process"},
3829  {'d', "Detach and resume process"},
3830  {'D', "Detach with process suspended"},
3831  {'h', "Show help dialog"},
3832  {'k', "Kill process"},
3833  {'n', "Step over (source line)"},
3834  {'N', "Step over (single instruction)"},
3835  {'o', "Step out"},
3836  {'s', "Step in (source line)"},
3837  {'S', "Step in (single instruction)"},
3838  {',', "Page up"},
3839  {'.', "Page down"},
3840  {'\0', nullptr}};
3841  return g_source_view_key_help;
3842  }
3843 
3844  bool WindowDelegateDraw(Window &window, bool force) override {
3845  ExecutionContext exe_ctx =
3846  m_debugger.GetCommandInterpreter().GetExecutionContext();
3847  Process *process = exe_ctx.GetProcessPtr();
3848  Thread *thread = nullptr;
3849 
3850  bool update_location = false;
3851  if (process) {
3852  StateType state = process->GetState();
3853  if (StateIsStoppedState(state, true)) {
3854  // We are stopped, so it is ok to
3855  update_location = true;
3856  }
3857  }
3858 
3859  m_min_x = 1;
3860  m_min_y = 2;
3861  m_max_x = window.GetMaxX() - 1;
3862  m_max_y = window.GetMaxY() - 1;
3863 
3864  const uint32_t num_visible_lines = NumVisibleLines();
3865  StackFrameSP frame_sp;
3866  bool set_selected_line_to_pc = false;
3867 
3868  if (update_location) {
3869  const bool process_alive = process ? process->IsAlive() : false;
3870  bool thread_changed = false;
3871  if (process_alive) {
3872  thread = exe_ctx.GetThreadPtr();
3873  if (thread) {
3874  frame_sp = thread->GetSelectedFrame();
3875  auto tid = thread->GetID();
3876  thread_changed = tid != m_tid;
3877  m_tid = tid;
3878  } else {
3879  if (m_tid != LLDB_INVALID_THREAD_ID) {
3880  thread_changed = true;
3881  m_tid = LLDB_INVALID_THREAD_ID;
3882  }
3883  }
3884  }
3885  const uint32_t stop_id = process ? process->GetStopID() : 0;
3886  const bool stop_id_changed = stop_id != m_stop_id;
3887  bool frame_changed = false;
3888  m_stop_id = stop_id;
3889  m_title.Clear();
3890  if (frame_sp) {
3891  m_sc = frame_sp->GetSymbolContext(eSymbolContextEverything);
3892  if (m_sc.module_sp) {
3893  m_title.Printf(
3894  "%s", m_sc.module_sp->GetFileSpec().GetFilename().GetCString());
3895  ConstString func_name = m_sc.GetFunctionName();
3896  if (func_name)
3897  m_title.Printf("`%s", func_name.GetCString());
3898  }
3899  const uint32_t frame_idx = frame_sp->GetFrameIndex();
3900  frame_changed = frame_idx != m_frame_idx;
3901  m_frame_idx = frame_idx;
3902  } else {
3903  m_sc.Clear(true);
3904  frame_changed = m_frame_idx != UINT32_MAX;
3905  m_frame_idx = UINT32_MAX;
3906  }
3907 
3908  const bool context_changed =
3909  thread_changed || frame_changed || stop_id_changed;
3910 
3911  if (process_alive) {
3912  if (m_sc.line_entry.IsValid()) {
3913  m_pc_line = m_sc.line_entry.line;
3914  if (m_pc_line != UINT32_MAX)
3915  --m_pc_line; // Convert to zero based line number...
3916  // Update the selected line if the stop ID changed...
3917  if (context_changed)
3918  m_selected_line = m_pc_line;
3919 
3920  if (m_file_sp && m_file_sp->FileSpecMatches(m_sc.line_entry.file)) {
3921  // Same file, nothing to do, we should either have the lines or not
3922  // (source file missing)
3923  if (m_selected_line >= static_cast<size_t>(m_first_visible_line)) {
3924  if (m_selected_line >= m_first_visible_line + num_visible_lines)
3925  m_first_visible_line = m_selected_line - 10;
3926  } else {
3927  if (m_selected_line > 10)
3928  m_first_visible_line = m_selected_line - 10;
3929  else
3931  }
3932  } else {
3933  // File changed, set selected line to the line with the PC
3934  m_selected_line = m_pc_line;
3935  m_file_sp =
3936  m_debugger.GetSourceManager().GetFile(m_sc.line_entry.file);
3937  if (m_file_sp) {
3938  const size_t num_lines = m_file_sp->GetNumLines();
3939  m_line_width = 1;
3940  for (size_t n = num_lines; n >= 10; n = n / 10)
3941  ++m_line_width;
3942 
3943  if (num_lines < num_visible_lines ||
3944  m_selected_line < num_visible_lines)
3946  else
3947  m_first_visible_line = m_selected_line - 10;
3948  }
3949  }
3950  } else {
3951  m_file_sp.reset();
3952  }
3953 
3954  if (!m_file_sp || m_file_sp->GetNumLines() == 0) {
3955  // Show disassembly
3956  bool prefer_file_cache = false;
3957  if (m_sc.function) {
3958  if (m_disassembly_scope != m_sc.function) {
3959  m_disassembly_scope = m_sc.function;
3960  m_disassembly_sp = m_sc.function->GetInstructions(
3961  exe_ctx, nullptr, prefer_file_cache);
3962  if (m_disassembly_sp) {
3963  set_selected_line_to_pc = true;
3964  m_disassembly_range = m_sc.function->GetAddressRange();
3965  } else {
3966  m_disassembly_range.Clear();
3967  }
3968  } else {
3969  set_selected_line_to_pc = context_changed;
3970  }
3971  } else if (m_sc.symbol) {
3972  if (m_disassembly_scope != m_sc.symbol) {
3973  m_disassembly_scope = m_sc.symbol;
3974  m_disassembly_sp = m_sc.symbol->GetInstructions(
3975  exe_ctx, nullptr, prefer_file_cache);
3976  if (m_disassembly_sp) {
3977  set_selected_line_to_pc = true;
3978  m_disassembly_range.GetBaseAddress() =
3979  m_sc.symbol->GetAddress();
3980  m_disassembly_range.SetByteSize(m_sc.symbol->GetByteSize());
3981  } else {
3982  m_disassembly_range.Clear();
3983  }
3984  } else {
3985  set_selected_line_to_pc = context_changed;
3986  }
3987  }
3988  }
3989  } else {
3990  m_pc_line = UINT32_MAX;
3991  }
3992  }
3993 
3994  const int window_width = window.GetWidth();
3995  window.Erase();
3996  window.DrawTitleBox("Sources");
3997  if (!m_title.GetString().empty()) {
3998  window.AttributeOn(A_REVERSE);
3999  window.MoveCursor(1, 1);
4000  window.PutChar(' ');
4001  window.PutCStringTruncated(m_title.GetString().str().c_str(), 1);
4002  int x = window.GetCursorX();
4003  if (x < window_width - 1) {
4004  window.Printf("%*s", window_width - x - 1, "");
4005  }
4006  window.AttributeOff(A_REVERSE);
4007  }
4008 
4009  Target *target = exe_ctx.GetTargetPtr();
4010  const size_t num_source_lines = GetNumSourceLines();
4011  if (num_source_lines > 0) {
4012  // Display source
4013  BreakpointLines bp_lines;
4014  if (target) {
4015  BreakpointList &bp_list = target->GetBreakpointList();
4016  const size_t num_bps = bp_list.GetSize();
4017  for (size_t bp_idx = 0; bp_idx < num_bps; ++bp_idx) {
4018  BreakpointSP bp_sp = bp_list.GetBreakpointAtIndex(bp_idx);
4019  const size_t num_bps_locs = bp_sp->GetNumLocations();
4020  for (size_t bp_loc_idx = 0; bp_loc_idx < num_bps_locs; ++bp_loc_idx) {
4021  BreakpointLocationSP bp_loc_sp =
4022  bp_sp->GetLocationAtIndex(bp_loc_idx);
4023  LineEntry bp_loc_line_entry;
4024  if (bp_loc_sp->GetAddress().CalculateSymbolContextLineEntry(
4025  bp_loc_line_entry)) {
4026  if (m_file_sp->GetFileSpec() == bp_loc_line_entry.file) {
4027  bp_lines.insert(bp_loc_line_entry.line);
4028  }
4029  }
4030  }
4031  }
4032  }
4033 
4034  const attr_t selected_highlight_attr = A_REVERSE;
4035  const attr_t pc_highlight_attr = COLOR_PAIR(1);
4036 
4037  for (size_t i = 0; i < num_visible_lines; ++i) {
4038  const uint32_t curr_line = m_first_visible_line + i;
4039  if (curr_line < num_source_lines) {
4040  const int line_y = m_min_y + i;
4041  window.MoveCursor(1, line_y);
4042  const bool is_pc_line = curr_line == m_pc_line;
4043  const bool line_is_selected = m_selected_line == curr_line;
4044  // Highlight the line as the PC line first, then if the selected line
4045  // isn't the same as the PC line, highlight it differently
4046  attr_t highlight_attr = 0;
4047  attr_t bp_attr = 0;
4048  if (is_pc_line)
4049  highlight_attr = pc_highlight_attr;
4050  else if (line_is_selected)
4051  highlight_attr = selected_highlight_attr;
4052 
4053  if (bp_lines.find(curr_line + 1) != bp_lines.end())
4054  bp_attr = COLOR_PAIR(2);
4055 
4056  if (bp_attr)
4057  window.AttributeOn(bp_attr);
4058 
4059  window.Printf(" %*u ", m_line_width, curr_line + 1);
4060 
4061  if (bp_attr)
4062  window.AttributeOff(bp_attr);
4063 
4064  window.PutChar(ACS_VLINE);
4065  // Mark the line with the PC with a diamond
4066  if (is_pc_line)
4067  window.PutChar(ACS_DIAMOND);
4068  else
4069  window.PutChar(' ');
4070 
4071  if (highlight_attr)
4072  window.AttributeOn(highlight_attr);
4073  const uint32_t line_len =
4074  m_file_sp->GetLineLength(curr_line + 1, false);
4075  if (line_len > 0)
4076  window.PutCString(m_file_sp->PeekLineData(curr_line + 1), line_len);
4077 
4078  if (is_pc_line && frame_sp &&
4079  frame_sp->GetConcreteFrameIndex() == 0) {
4080  StopInfoSP stop_info_sp;
4081  if (thread)
4082  stop_info_sp = thread->GetStopInfo();
4083  if (stop_info_sp) {
4084  const char *stop_description = stop_info_sp->GetDescription();
4085  if (stop_description && stop_description[0]) {
4086  size_t stop_description_len = strlen(stop_description);
4087  int desc_x = window_width - stop_description_len - 16;
4088  window.Printf("%*s", desc_x - window.GetCursorX(), "");
4089  // window.MoveCursor(window_width - stop_description_len - 15,
4090  // line_y);
4091  window.Printf("<<< Thread %u: %s ", thread->GetIndexID(),
4092  stop_description);
4093  }
4094  } else {
4095  window.Printf("%*s", window_width - window.GetCursorX() - 1, "");
4096  }
4097  }
4098  if (highlight_attr)
4099  window.AttributeOff(highlight_attr);
4100  } else {
4101  break;
4102  }
4103  }
4104  } else {
4105  size_t num_disassembly_lines = GetNumDisassemblyLines();
4106  if (num_disassembly_lines > 0) {
4107  // Display disassembly
4108  BreakpointAddrs bp_file_addrs;
4109  Target *target = exe_ctx.GetTargetPtr();
4110  if (target) {
4111  BreakpointList &bp_list = target->GetBreakpointList();
4112  const size_t num_bps = bp_list.GetSize();
4113  for (size_t bp_idx = 0; bp_idx < num_bps; ++bp_idx) {
4114  BreakpointSP bp_sp = bp_list.GetBreakpointAtIndex(bp_idx);
4115  const size_t num_bps_locs = bp_sp->GetNumLocations();
4116  for (size_t bp_loc_idx = 0; bp_loc_idx < num_bps_locs;
4117  ++bp_loc_idx) {
4118  BreakpointLocationSP bp_loc_sp =
4119  bp_sp->GetLocationAtIndex(bp_loc_idx);
4120  LineEntry bp_loc_line_entry;
4121  const lldb::addr_t file_addr =
4122  bp_loc_sp->GetAddress().GetFileAddress();
4123  if (file_addr != LLDB_INVALID_ADDRESS) {
4124  if (m_disassembly_range.ContainsFileAddress(file_addr))
4125  bp_file_addrs.insert(file_addr);
4126  }
4127  }
4128  }
4129  }
4130 
4131  const attr_t selected_highlight_attr = A_REVERSE;
4132  const attr_t pc_highlight_attr = COLOR_PAIR(1);
4133 
4134  StreamString strm;
4135 
4136  InstructionList &insts = m_disassembly_sp->GetInstructionList();
4137  Address pc_address;
4138 
4139  if (frame_sp)
4140  pc_address = frame_sp->GetFrameCodeAddress();
4141  const uint32_t pc_idx =
4142  pc_address.IsValid()
4143  ? insts.GetIndexOfInstructionAtAddress(pc_address)
4144  : UINT32_MAX;
4145  if (set_selected_line_to_pc) {
4146  m_selected_line = pc_idx;
4147  }
4148 
4149  const uint32_t non_visible_pc_offset = (num_visible_lines / 5);
4150  if (static_cast<size_t>(m_first_visible_line) >= num_disassembly_lines)
4152 
4153  if (pc_idx < num_disassembly_lines) {
4154  if (pc_idx < static_cast<uint32_t>(m_first_visible_line) ||
4155  pc_idx >= m_first_visible_line + num_visible_lines)
4156  m_first_visible_line = pc_idx - non_visible_pc_offset;
4157  }
4158 
4159  for (size_t i = 0; i < num_visible_lines; ++i) {
4160  const uint32_t inst_idx = m_first_visible_line + i;
4161  Instruction *inst = insts.GetInstructionAtIndex(inst_idx).get();
4162  if (!inst)
4163  break;
4164 
4165  const int line_y = m_min_y + i;
4166  window.MoveCursor(1, line_y);
4167  const bool is_pc_line = frame_sp && inst_idx == pc_idx;
4168  const bool line_is_selected = m_selected_line == inst_idx;
4169  // Highlight the line as the PC line first, then if the selected line
4170  // isn't the same as the PC line, highlight it differently
4171  attr_t highlight_attr = 0;
4172  attr_t bp_attr = 0;
4173  if (is_pc_line)
4174  highlight_attr = pc_highlight_attr;
4175  else if (line_is_selected)
4176  highlight_attr = selected_highlight_attr;
4177 
4178  if (bp_file_addrs.find(inst->GetAddress().GetFileAddress()) !=
4179  bp_file_addrs.end())
4180  bp_attr = COLOR_PAIR(2);
4181 
4182  if (bp_attr)
4183  window.AttributeOn(bp_attr);
4184 
4185  window.Printf(" 0x%16.16llx ",
4186  static_cast<unsigned long long>(
4187  inst->GetAddress().GetLoadAddress(target)));
4188 
4189  if (bp_attr)
4190  window.AttributeOff(bp_attr);
4191 
4192  window.PutChar(ACS_VLINE);
4193  // Mark the line with the PC with a diamond
4194  if (is_pc_line)
4195  window.PutChar(ACS_DIAMOND);
4196  else
4197  window.PutChar(' ');
4198 
4199  if (highlight_attr)
4200  window.AttributeOn(highlight_attr);
4201 
4202  const char *mnemonic = inst->GetMnemonic(&exe_ctx);
4203  const char *operands = inst->GetOperands(&exe_ctx);
4204  const char *comment = inst->GetComment(&exe_ctx);
4205 
4206  if (mnemonic != nullptr && mnemonic[0] == '\0')
4207  mnemonic = nullptr;
4208  if (operands != nullptr && operands[0] == '\0')
4209  operands = nullptr;
4210  if (comment != nullptr && comment[0] == '\0')
4211  comment = nullptr;
4212 
4213  strm.Clear();
4214 
4215  if (mnemonic != nullptr && operands != nullptr && comment != nullptr)
4216  strm.Printf("%-8s %-25s ; %s", mnemonic, operands, comment);
4217  else if (mnemonic != nullptr && operands != nullptr)
4218  strm.Printf("%-8s %s", mnemonic, operands);
4219  else if (mnemonic != nullptr)
4220  strm.Printf("%s", mnemonic);
4221 
4222  int right_pad = 1;
4223  window.PutCStringTruncated(strm.GetData(), right_pad);
4224 
4225  if (is_pc_line && frame_sp &&
4226  frame_sp->GetConcreteFrameIndex() == 0) {
4227  StopInfoSP stop_info_sp;
4228  if (thread)
4229  stop_info_sp = thread->GetStopInfo();
4230  if (stop_info_sp) {
4231  const char *stop_description = stop_info_sp->GetDescription();
4232  if (stop_description && stop_description[0]) {
4233  size_t stop_description_len = strlen(stop_description);
4234  int desc_x = window_width - stop_description_len - 16;
4235  window.Printf("%*s", desc_x - window.GetCursorX(), "");
4236  // window.MoveCursor(window_width - stop_description_len - 15,
4237  // line_y);
4238  window.Printf("<<< Thread %u: %s ", thread->GetIndexID(),
4239  stop_description);
4240  }
4241  } else {
4242  window.Printf("%*s", window_width - window.GetCursorX() - 1, "");
4243  }
4244  }
4245  if (highlight_attr)
4246  window.AttributeOff(highlight_attr);
4247  }
4248  }
4249  }
4250  window.DeferredRefresh();
4251  return true; // Drawing handled
4252  }
4253 
4254  size_t GetNumLines() {
4255  size_t num_lines = GetNumSourceLines();
4256  if (num_lines == 0)
4257  num_lines = GetNumDisassemblyLines();
4258  return num_lines;
4259  }
4260 
4261  size_t GetNumSourceLines() const {
4262  if (m_file_sp)
4263  return m_file_sp->GetNumLines();
4264  return 0;
4265  }
4266 
4267  size_t GetNumDisassemblyLines() const {
4268  if (m_disassembly_sp)
4269  return m_disassembly_sp->GetInstructionList().GetSize();
4270  return 0;
4271  }
4272 
4274  const uint32_t num_visible_lines = NumVisibleLines();
4275  const size_t num_lines = GetNumLines();
4276 
4277  switch (c) {
4278  case ',':
4279  case KEY_PPAGE:
4280  // Page up key
4281  if (static_cast<uint32_t>(m_first_visible_line) > num_visible_lines)
4282  m_first_visible_line -= num_visible_lines;
4283  else
4285  m_selected_line = m_first_visible_line;
4286  return eKeyHandled;
4287 
4288  case '.':
4289  case KEY_NPAGE:
4290  // Page down key
4291  {
4292  if (m_first_visible_line + num_visible_lines < num_lines)
4293  m_first_visible_line += num_visible_lines;
4294  else if (num_lines < num_visible_lines)
4296  else
4297  m_first_visible_line = num_lines - num_visible_lines;
4298  m_selected_line = m_first_visible_line;
4299  }
4300  return eKeyHandled;
4301 
4302  case KEY_UP:
4303  if (m_selected_line > 0) {
4304  m_selected_line--;
4305  if (static_cast<size_t>(m_first_visible_line) > m_selected_line)
4306  m_first_visible_line = m_selected_line;
4307  }
4308  return eKeyHandled;
4309 
4310  case KEY_DOWN:
4311  if (m_selected_line + 1 < num_lines) {
4312  m_selected_line++;
4313  if (m_first_visible_line + num_visible_lines < m_selected_line)
4315  }
4316  return eKeyHandled;
4317 
4318  case '\r':
4319  case '\n':
4320  case KEY_ENTER:
4321  // Set a breakpoint and run to the line using a one shot breakpoint
4322  if (GetNumSourceLines() > 0) {
4323  ExecutionContext exe_ctx =
4324  m_debugger.GetCommandInterpreter().GetExecutionContext();
4325  if (exe_ctx.HasProcessScope() && exe_ctx.GetProcessRef().IsAlive()) {
4326  BreakpointSP bp_sp = exe_ctx.GetTargetRef().CreateBreakpoint(
4327  nullptr, // Don't limit the breakpoint to certain modules
4328  m_file_sp->GetFileSpec(), // Source file
4329  m_selected_line +
4330  1, // Source line number (m_selected_line is zero based)
4331  0, // Unspecified column.
4332  0, // No offset
4333  eLazyBoolCalculate, // Check inlines using global setting
4334  eLazyBoolCalculate, // Skip prologue using global setting,
4335  false, // internal
4336  false, // request_hardware
4337  eLazyBoolCalculate); // move_to_nearest_code
4338  // Make breakpoint one shot
4339  bp_sp->GetOptions()->SetOneShot(true);
4340  exe_ctx.GetProcessRef().Resume();
4341  }
4342  } else if (m_selected_line < GetNumDisassemblyLines()) {
4343  const Instruction *inst = m_disassembly_sp->GetInstructionList()
4344  .GetInstructionAtIndex(m_selected_line)
4345  .get();
4346  ExecutionContext exe_ctx =
4347  m_debugger.GetCommandInterpreter().GetExecutionContext();
4348  if (exe_ctx.HasTargetScope()) {
4349  Address addr = inst->GetAddress();
4350  BreakpointSP bp_sp = exe_ctx.GetTargetRef().CreateBreakpoint(
4351  addr, // lldb_private::Address
4352  false, // internal
4353  false); // request_hardware
4354  // Make breakpoint one shot
4355  bp_sp->GetOptions()->SetOneShot(true);
4356  exe_ctx.GetProcessRef().Resume();
4357  }
4358  }
4359  return eKeyHandled;
4360 
4361  case 'b': // 'b' == toggle breakpoint on currently selected line
4362  if (m_selected_line < GetNumSourceLines()) {
4363  ExecutionContext exe_ctx =
4364  m_debugger.GetCommandInterpreter().GetExecutionContext();
4365  if (exe_ctx.HasTargetScope()) {
4366  BreakpointSP bp_sp = exe_ctx.GetTargetRef().CreateBreakpoint(
4367  nullptr, // Don't limit the breakpoint to certain modules
4368  m_file_sp->GetFileSpec(), // Source file
4369  m_selected_line +
4370  1, // Source line number (m_selected_line is zero based)
4371  0, // No column specified.
4372  0, // No offset
4373  eLazyBoolCalculate, // Check inlines using global setting
4374  eLazyBoolCalculate, // Skip prologue using global setting,
4375  false, // internal
4376  false, // request_hardware
4377  eLazyBoolCalculate); // move_to_nearest_code
4378  }
4379  } else if (m_selected_line < GetNumDisassemblyLines()) {
4380  const Instruction *inst = m_disassembly_sp->GetInstructionList()
4381  .GetInstructionAtIndex(m_selected_line)
4382  .get();
4383  ExecutionContext exe_ctx =
4384  m_debugger.GetCommandInterpreter().GetExecutionContext();
4385  if (exe_ctx.HasTargetScope()) {
4386  Address addr = inst->GetAddress();
4387  BreakpointSP bp_sp = exe_ctx.GetTargetRef().CreateBreakpoint(
4388  addr, // lldb_private::Address
4389  false, // internal
4390  false); // request_hardware
4391  }
4392  }
4393  return eKeyHandled;
4394 
4395  case 'd': // 'd' == detach and let run
4396  case 'D': // 'D' == detach and keep stopped
4397  {
4398  ExecutionContext exe_ctx =
4399  m_debugger.GetCommandInterpreter().GetExecutionContext();
4400  if (exe_ctx.HasProcessScope())
4401  exe_ctx.GetProcessRef().Detach(c == 'D');
4402  }
4403  return eKeyHandled;
4404 
4405  case 'k':
4406  // 'k' == kill
4407  {
4408  ExecutionContext exe_ctx =
4409  m_debugger.GetCommandInterpreter().GetExecutionContext();
4410  if (exe_ctx.HasProcessScope())
4411  exe_ctx.GetProcessRef().Destroy(false);
4412  }
4413  return eKeyHandled;
4414 
4415  case 'c':
4416  // 'c' == continue
4417  {
4418  ExecutionContext exe_ctx =
4419  m_debugger.GetCommandInterpreter().GetExecutionContext();
4420  if (exe_ctx.HasProcessScope())
4421  exe_ctx.GetProcessRef().Resume();
4422  }
4423  return eKeyHandled;
4424 
4425  case 'o':
4426  // 'o' == step out
4427  {
4428  ExecutionContext exe_ctx =
4429  m_debugger.GetCommandInterpreter().GetExecutionContext();
4430  if (exe_ctx.HasThreadScope() &&
4431  StateIsStoppedState(exe_ctx.GetProcessRef().GetState(), true)) {
4432  exe_ctx.GetThreadRef().StepOut();
4433  }
4434  }
4435  return eKeyHandled;
4436 
4437  case 'n': // 'n' == step over
4438  case 'N': // 'N' == step over instruction
4439  {
4440  ExecutionContext exe_ctx =
4441  m_debugger.GetCommandInterpreter().GetExecutionContext();
4442  if (exe_ctx.HasThreadScope() &&
4443  StateIsStoppedState(exe_ctx.GetProcessRef().GetState(), true)) {
4444  bool source_step = (c == 'n');
4445  exe_ctx.GetThreadRef().StepOver(source_step);
4446  }
4447  }
4448  return eKeyHandled;
4449 
4450  case 's': // 's' == step into
4451  case 'S': // 'S' == step into instruction
4452  {
4453  ExecutionContext exe_ctx =
4454  m_debugger.GetCommandInterpreter().GetExecutionContext();
4455  if (exe_ctx.HasThreadScope() &&
4456  StateIsStoppedState(exe_ctx.GetProcessRef().GetState(), true)) {
4457  bool source_step = (c == 's');
4458  exe_ctx.GetThreadRef().StepIn(source_step);
4459  }
4460  }
4461  return eKeyHandled;
4462 
4463  case 'h':
4464  window.CreateHelpSubwindow();
4465  return eKeyHandled;
4466 
4467  default:
4468  break;
4469  }
4470  return eKeyNotHandled;
4471  }
4472 
4473 protected:
4474  typedef std::set<uint32_t> BreakpointLines;
4475  typedef std::set<lldb::addr_t> BreakpointAddrs;
4476 
4481  lldb::DisassemblerSP m_disassembly_sp;
4486  uint32_t m_selected_line; // The selected line
4487  uint32_t m_pc_line; // The line with the PC
4491  int m_min_x;
4492  int m_min_y;
4493  int m_max_x;
4494  int m_max_y;
4495 };
4496 
4498 
4499 IOHandlerCursesGUI::IOHandlerCursesGUI(Debugger &debugger)
4500  : IOHandler(debugger, IOHandler::Type::Curses) {}
4501 
4504  if (!m_app_ap) {
4506 
4507  // This is both a window and a menu delegate
4508  std::shared_ptr<ApplicationDelegate> app_delegate_sp(
4510 
4511  MenuDelegateSP app_menu_delegate_sp =
4512  std::static_pointer_cast<MenuDelegate>(app_delegate_sp);
4513  MenuSP lldb_menu_sp(
4514  new Menu("LLDB", "F1", KEY_F(1), ApplicationDelegate::eMenuID_LLDB));
4515  MenuSP exit_menuitem_sp(
4516  new Menu("Exit", nullptr, 'x', ApplicationDelegate::eMenuID_LLDBExit));
4517  exit_menuitem_sp->SetCannedResult(MenuActionResult::Quit);
4518  lldb_menu_sp->AddSubmenu(MenuSP(new Menu(
4519  "About LLDB", nullptr, 'a', ApplicationDelegate::eMenuID_LLDBAbout)));
4520  lldb_menu_sp->AddSubmenu(MenuSP(new Menu(Menu::Type::Separator)));
4521  lldb_menu_sp->AddSubmenu(exit_menuitem_sp);
4522 
4523  MenuSP target_menu_sp(new Menu("Target", "F2", KEY_F(2),
4525  target_menu_sp->AddSubmenu(MenuSP(new Menu(
4526  "Create", nullptr, 'c', ApplicationDelegate::eMenuID_TargetCreate)));
4527  target_menu_sp->AddSubmenu(MenuSP(new Menu(
4528  "Delete", nullptr, 'd', ApplicationDelegate::eMenuID_TargetDelete)));
4529 
4530  MenuSP process_menu_sp(new Menu("Process", "F3", KEY_F(3),
4532  process_menu_sp->AddSubmenu(MenuSP(new Menu(
4533  "Attach", nullptr, 'a', ApplicationDelegate::eMenuID_ProcessAttach)));
4534  process_menu_sp->AddSubmenu(MenuSP(new Menu(
4535  "Detach", nullptr, 'd', ApplicationDelegate::eMenuID_ProcessDetach)));
4536  process_menu_sp->AddSubmenu(MenuSP(new Menu(
4537  "Launch", nullptr, 'l', ApplicationDelegate::eMenuID_ProcessLaunch)));
4538  process_menu_sp->AddSubmenu(MenuSP(new Menu(Menu::Type::Separator)));
4539  process_menu_sp->AddSubmenu(
4540  MenuSP(new Menu("Continue", nullptr, 'c',
4542  process_menu_sp->AddSubmenu(MenuSP(new Menu(
4543  "Halt", nullptr, 'h', ApplicationDelegate::eMenuID_ProcessHalt)));
4544  process_menu_sp->AddSubmenu(MenuSP(new Menu(
4545  "Kill", nullptr, 'k', ApplicationDelegate::eMenuID_ProcessKill)));
4546 
4547  MenuSP thread_menu_sp(new Menu("Thread", "F4", KEY_F(4),
4549  thread_menu_sp->AddSubmenu(MenuSP(new Menu(
4550  "Step In", nullptr, 'i', ApplicationDelegate::eMenuID_ThreadStepIn)));
4551  thread_menu_sp->AddSubmenu(
4552  MenuSP(new Menu("Step Over", nullptr, 'v',
4554  thread_menu_sp->AddSubmenu(MenuSP(new Menu(
4555  "Step Out", nullptr, 'o', ApplicationDelegate::eMenuID_ThreadStepOut)));
4556 
4557  MenuSP view_menu_sp(
4558  new Menu("View", "F5", KEY_F(5), ApplicationDelegate::eMenuID_View));
4559  view_menu_sp->AddSubmenu(
4560  MenuSP(new Menu("Backtrace", nullptr, 'b',
4562  view_menu_sp->AddSubmenu(
4563  MenuSP(new Menu("Registers", nullptr, 'r',
4565  view_menu_sp->AddSubmenu(MenuSP(new Menu(
4566  "Source", nullptr, 's', ApplicationDelegate::eMenuID_ViewSource)));
4567  view_menu_sp->AddSubmenu(
4568  MenuSP(new Menu("Variables", nullptr, 'v',
4570 
4571  MenuSP help_menu_sp(
4572  new Menu("Help", "F6", KEY_F(6), ApplicationDelegate::eMenuID_Help));
4573  help_menu_sp->AddSubmenu(MenuSP(new Menu(
4574  "GUI Help", nullptr, 'g', ApplicationDelegate::eMenuID_HelpGUIHelp)));
4575 
4576  m_app_ap->Initialize();
4577  WindowSP &main_window_sp = m_app_ap->GetMainWindow();
4578 
4579  MenuSP menubar_sp(new Menu(Menu::Type::Bar));
4580  menubar_sp->AddSubmenu(lldb_menu_sp);
4581  menubar_sp->AddSubmenu(target_menu_sp);
4582  menubar_sp->AddSubmenu(process_menu_sp);
4583  menubar_sp->AddSubmenu(thread_menu_sp);
4584  menubar_sp->AddSubmenu(view_menu_sp);
4585  menubar_sp->AddSubmenu(help_menu_sp);
4586  menubar_sp->SetDelegate(app_menu_delegate_sp);
4587 
4588  Rect content_bounds = main_window_sp->GetFrame();
4589  Rect menubar_bounds = content_bounds.MakeMenuBar();
4590  Rect status_bounds = content_bounds.MakeStatusBar();
4591  Rect source_bounds;
4592  Rect variables_bounds;
4593  Rect threads_bounds;
4594  Rect source_variables_bounds;
4595  content_bounds.VerticalSplitPercentage(0.80, source_variables_bounds,
4596  threads_bounds);
4597  source_variables_bounds.HorizontalSplitPercentage(0.70, source_bounds,
4598  variables_bounds);
4599 
4600  WindowSP menubar_window_sp =
4601  main_window_sp->CreateSubWindow("Menubar", menubar_bounds, false);
4602  // Let the menubar get keys if the active window doesn't handle the keys
4603  // that are typed so it can respond to menubar key presses.
4604  menubar_window_sp->SetCanBeActive(
4605  false); // Don't let the menubar become the active window
4606  menubar_window_sp->SetDelegate(menubar_sp);
4607 
4608  WindowSP source_window_sp(
4609  main_window_sp->CreateSubWindow("Source", source_bounds, true));
4610  WindowSP variables_window_sp(
4611  main_window_sp->CreateSubWindow("Variables", variables_bounds, false));
4612  WindowSP threads_window_sp(
4613  main_window_sp->CreateSubWindow("Threads", threads_bounds, false));
4614  WindowSP status_window_sp(
4615  main_window_sp->CreateSubWindow("Status", status_bounds, false));
4616  status_window_sp->SetCanBeActive(
4617  false); // Don't let the status bar become the active window
4618  main_window_sp->SetDelegate(
4619  std::static_pointer_cast<WindowDelegate>(app_delegate_sp));
4620  source_window_sp->SetDelegate(
4622  variables_window_sp->SetDelegate(
4624  TreeDelegateSP thread_delegate_sp(new ThreadsTreeDelegate(m_debugger));
4625  threads_window_sp->SetDelegate(WindowDelegateSP(
4626  new TreeWindowDelegate(m_debugger, thread_delegate_sp)));
4627  status_window_sp->SetDelegate(
4629 
4630  // Show the main help window once the first time the curses GUI is launched
4631  static bool g_showed_help = false;
4632  if (!g_showed_help) {
4633  g_showed_help = true;
4634  main_window_sp->CreateHelpSubwindow();
4635  }
4636 
4637  init_pair(1, COLOR_WHITE, COLOR_BLUE);
4638  init_pair(2, COLOR_BLACK, COLOR_WHITE);
4639  init_pair(3, COLOR_MAGENTA, COLOR_WHITE);
4640  init_pair(4, COLOR_MAGENTA, COLOR_BLACK);
4641  init_pair(5, COLOR_RED, COLOR_BLACK);
4642  }
4643 }
4644 
4646 
4648  m_app_ap->Run(m_debugger);
4649  SetIsDone(true);
4650 }
4651 
4653 
4655 
4656 bool IOHandlerCursesGUI::Interrupt() { return false; }
4657 
4659 
4660 #endif // LLDB_DISABLE_CURSES
void SetIdentifier(uint64_t identifier)
Definition: IOHandler.cpp:1414
static bool InvokeCommonCompletionCallbacks(CommandInterpreter &interpreter, uint32_t completion_mask, lldb_private::CompletionRequest &request, SearchFilter *searcher)
ApplicationDelegate(Application &app, Debugger &debugger)
Definition: IOHandler.cpp:3445
void SetMightHaveChildren(bool b)
Definition: IOHandler.cpp:2210
A class to manage flag bits.
Definition: Debugger.h:82
lldb::ProcessSP GetProcessSP() const
Rect GetBounds()
Definition: IOHandler.cpp:919
size_t PutCString(llvm::StringRef cstr)
Output a C string to the stream.
Definition: Stream.cpp:61
A line table entry class.
Definition: LineEntry.h:20
KeyHelp * WindowDelegateGetKeyHelp() override
Definition: IOHandler.cpp:3481
lldb::StreamFileSP & GetInputStreamFile()
Definition: IOHandler.cpp:124
Debugger & m_debugger
Definition: IOHandler.cpp:3744
void ItemWasSelected()
Definition: IOHandler.cpp:2072
void Unexpand()
Definition: IOHandler.cpp:1953
std::shared_ptr< Menu > MenuSP
Definition: IOHandler.cpp:640
void TreeDelegateGenerateChildren(TreeItem &item) override
Definition: IOHandler.cpp:2501
void SetBaseLineNumber(uint32_t line)
Definition: IOHandler.cpp:490
virtual Status StepOut()
Default implementation for stepping out.
Definition: Thread.cpp:2182
bool HasTargetScope() const
Returns true the ExecutionContext object contains a valid target.
HandleCharResult WindowDelegateHandleChar(Window &window, int c) override
Definition: IOHandler.cpp:2311
lldb::StackFrameSP GetSelectedFrame()
Definition: Thread.cpp:317
uint32_t children_stop_id
Definition: IOHandler.cpp:1908
uint32_t GetCurrentLineIndex() const
Definition: IOHandler.cpp:494
virtual const char * GetValueAsCString()
bool HasThreadScope() const
Returns true the ExecutionContext object contains a valid target, process, and thread.
ThreadsTreeDelegate(Debugger &debugger)
Definition: IOHandler.cpp:2563
const char * description
Definition: IOHandler.cpp:822
void VerticalSplit(int left_width, Rect &left, Rect &right) const
Definition: IOHandler.cpp:786
const char * StateAsCString(lldb::StateType state)
Converts a StateType to a C string.
Definition: State.cpp:14
void Resize(int w, int h)
Definition: IOHandler.cpp:938
Enumerations for broadcasting.
Definition: SBLaunchInfo.h:14
bool expanded
Definition: IOHandler.cpp:1913
lldb::ThreadSP GetSelectedThread()
Definition: ThreadList.cpp:649
FormatEntity::Entry m_format
Definition: IOHandler.cpp:2460
Definition: Debugger.h:71
static Row * GetRowForRowIndexImpl(std::vector< Row > &rows, size_t &row_index)
Definition: IOHandler.cpp:2973
void AttributeOn(attr_t attr)
Definition: IOHandler.cpp:912
static bool isprint(char32_t codepoint)
CommandInterpreter & GetCommandInterpreter()
Definition: Debugger.h:152
A stream class that can stream formatted output to a file.
Definition: Stream.h:28
Type GetType() const
Definition: IOHandler.cpp:1394
WindowSP GetActiveWindow()
Definition: IOHandler.cpp:1218
Rect MakeMenuBar()
Definition: IOHandler.cpp:748
bool WindowDelegateDraw(Window &window, bool force) override
Definition: IOHandler.cpp:2234
BreakpointList & GetBreakpointList(bool internal=false)
Definition: Target.cpp:283
int y
Definition: IOHandler.cpp:1911
const char * WindowDelegateGetHelpText() override
Definition: IOHandler.cpp:3075
Status Halt(bool clear_thread_plans=false, bool use_run_lock=true)
Halts a running process.
Definition: Process.cpp:3127
void TreeDelegateDrawTreeItem(TreeItem &item, Window &window) override
Definition: IOHandler.cpp:2488
void MoveWindow(const Point &origin)
Definition: IOHandler.cpp:963
Point & operator+=(const Point &rhs)
Definition: IOHandler.cpp:666
std::shared_ptr< TreeDelegate > TreeDelegateSP
Definition: IOHandler.cpp:2028
Defines a symbol context baton that can be handed other debug core functions.
Definition: SymbolContext.h:33
static const char * CursesKeyToCString(int ch)
Definition: IOHandler.cpp:3116
Rect(const Point &p, const Size &s)
Definition: IOHandler.cpp:710
virtual ConstString GetTypeName()
TreeItem * m_parent
Definition: IOHandler.cpp:2213
lldb::addr_t GetLoadAddress(Target *target) const
Get the load address.
Definition: Address.cpp:292
std::vector< Row > children
Definition: IOHandler.cpp:1915
Row * GetRowForRowIndex(size_t row_index)
Definition: IOHandler.cpp:2990
WindowSP CreateSubWindow(const char *name, const Rect &bounds, bool make_active)
Definition: IOHandler.cpp:1003
size_t GetSize() const
Returns the number of elements in this breakpoint list.
Window * m_parent
Definition: IOHandler.cpp:1312
Application(FILE *in, FILE *out)
Definition: IOHandler.cpp:1733
void SetBackground(int color_pair_idx)
Definition: IOHandler.cpp:949
void SetValues(ValueObjectList &valobj_list)
Definition: IOHandler.cpp:2649
std::shared_ptr< FrameTreeDelegate > m_frame_delegate_sp
Definition: IOHandler.cpp:2555
lldb::StreamFileSP m_input_sp
Definition: IOHandler.h:167
virtual Status StepIn(bool source_step, LazyBool step_in_avoids_code_without_debug_info=eLazyBoolCalculate, LazyBool step_out_avoids_code_without_debug_info=eLazyBoolCalculate)
Default implementation for stepping into.
Definition: Thread.cpp:2113
void PrintAsync(Stream *stream, const char *s, size_t len) override
Definition: IOHandler.cpp:600
ConstString GetName() const
Window(const char *name, const Rect &bounds)
Definition: IOHandler.cpp:878
bool DisplayRowObject(Window &window, Row &row, DisplayOptions &options, bool highlight, bool last_child)
Definition: IOHandler.cpp:2872
int GetExitStatus()
Get the exit status for a process.
Definition: Process.cpp:1128
bool WindowDelegateDraw(Window &window, bool force) override
Definition: IOHandler.cpp:2659
bool RemoveSubWindow(Window *window)
Definition: IOHandler.cpp:1025
HandleCharResult WindowDelegateHandleChar(Window &window, int c) override
Definition: IOHandler.cpp:4273
void EnableForwardEvents(const lldb::ListenerSP &listener_sp)
Definition: Debugger.cpp:1521
bool GetCanBeActive() const
Definition: IOHandler.cpp:1245
Menu(Type type)
Definition: IOHandler.cpp:1434
std::string & GetName()
Definition: IOHandler.cpp:1404
bool TreeDelegateItemSelected(TreeItem &item) override
Definition: IOHandler.cpp:2624
const char * GetExitDescription()
Get a textual description of what the process exited.
Definition: Process.cpp:1136
bool HasProcessScope() const
Returns true the ExecutionContext object contains a valid target and process.
size_t GetNumDisassemblyLines() const
Definition: IOHandler.cpp:4267
virtual bool IOHandlerIsInputComplete(IOHandler &io_handler, StringList &lines)
Called to determine whether typing enter after the last line in lines should end input.
Definition: IOHandler.h:263
lldb::user_id_t GetID() const
Get accessor for the user ID.
Definition: UserID.h:49
Point(int _x=0, int _y=0)
Definition: IOHandler.cpp:659
Thread & GetThreadRef() const
Returns a reference to the thread object.
uint32_t NumVisibleLines() const
Definition: IOHandler.cpp:3814
void SetDelegate(const WindowDelegateSP &delegate_sp)
Definition: IOHandler.cpp:1251
ThreadList & GetThreadList()
Definition: Process.h:2045
void DisplayRows(Window &window, std::vector< Row > &rows, DisplayOptions &options)
Definition: IOHandler.cpp:2926
void Resize(size_t n, const TreeItem &t)
Definition: IOHandler.cpp:2061
void Inset(int w, int h)
Definition: IOHandler.cpp:722
lldb::StateType GetState()
Get accessor for the current process state.
Definition: Process.cpp:1347
MenuActionResult ActionPrivate(Menu &menu)
Definition: IOHandler.cpp:1364
void LongestCommonPrefix(std::string &common_prefix)
Definition: StringList.cpp:103
uint32_t m_prev_active_window_idx
Definition: IOHandler.cpp:1316
lldb::addr_t GetFileAddress() const
Get the file address.
Definition: Address.cpp:272
void TreeDelegateGenerateChildren(TreeItem &item) override
Definition: IOHandler.cpp:2591
An event broadcasting class.
Definition: Broadcaster.h:250
HandleCharResult
Definition: IOHandler.cpp:808
lldb::user_id_t m_tid
Definition: IOHandler.cpp:2556
"lldb/Target/ExecutionContext.h" A class that contains an execution context.
bool GetIsRealTerminal()
Check if the input is coming from a real terminal.
Definition: IOHandler.cpp:134
size_t GetNumSourceLines() const
Definition: IOHandler.cpp:4261
const char * GetMnemonic(const ExecutionContext *exe_ctx)
Definition: Disassembler.h:86
void SetContinuationPrompt(llvm::StringRef prompt)
Definition: IOHandler.cpp:479
void VerticalSplitPercentage(float left_percentage, Rect &left, Rect &right) const
Definition: IOHandler.cpp:780
Debugger & m_debugger
Definition: IOHandler.cpp:2554
int CalculateTotalNumberRows(std::vector< Row > &rows)
Definition: IOHandler.cpp:2963
void MoveWindow(int x, int y)
Definition: IOHandler.cpp:937
int HandleCompletion(const char *current_line, const char *cursor, const char *last_char, int match_start_point, int max_return_elements, StringList &matches, StringList &descriptions)
const char * GetOperands(const ExecutionContext *exe_ctx)
Definition: Disassembler.h:91
const char * GetData() const
Definition: StreamString.h:43
Status Detach(bool keep_stopped)
Detaches from a running or stopped process.
Definition: Process.cpp:3223
lldb::StreamFileSP & GetErrorStreamFile()
Definition: IOHandler.cpp:128
A class that describes a single lexical block.
Definition: Block.h:41
void UnderlineOff()
Definition: IOHandler.cpp:953
SourceManager::FileSP m_file_sp
Definition: IOHandler.cpp:4479
VariableList * GetVariableList(bool get_file_globals)
Retrieve the list of variables that are in scope at this StackFrame&#39;s pc.
Definition: StackFrame.cpp:414
int NumVisibleRows() const
Definition: IOHandler.cpp:2232
A delegate class for use with IOHandler subclasses.
Definition: IOHandler.h:188
ValueObjectListDelegate(ValueObjectList &valobj_list)
Definition: IOHandler.cpp:2640
StackFrame * GetFramePtr() const
Returns a pointer to the frame object.
FormatEntity::Entry m_format
Definition: IOHandler.cpp:2630
void AdoptTopIOHandlerFilesIfInvalid(lldb::StreamFileSP &in, lldb::StreamFileSP &out, lldb::StreamFileSP &err)
Definition: Debugger.cpp:1050
bool WindowDelegateDraw(Window &window, bool force) override
Definition: IOHandler.cpp:3011
bool m_might_have_children
Definition: IOHandler.cpp:2220
uint32_t GetFrameIndex() const
Query this frame to find what frame it is in this Thread&#39;s StackFrameList.
Definition: StackFrame.cpp:166
void SetRowIndex(int row_idx)
Definition: IOHandler.cpp:2065
int row_idx
Definition: IOHandler.cpp:1909
void DrawTitleBox(const char *title, const char *bottom_message=nullptr)
Definition: IOHandler.cpp:1082
virtual void Deactivate()
Definition: IOHandler.h:94
virtual ConstString & GetBroadcasterClass() const
void SetUserData(void *user_data)
Definition: IOHandler.cpp:2204
bool SetActiveWindow(Window *window)
Definition: IOHandler.cpp:1205
TreeWindowDelegate(Debugger &debugger, const TreeDelegateSP &delegate_sp)
Definition: IOHandler.cpp:2226
std::shared_ptr< Window > WindowSP
Definition: IOHandler.cpp:643
void ClearChildren()
Definition: IOHandler.cpp:2059
MenuActionResult
Definition: IOHandler.cpp:814
void Clear()
Definition: IOHandler.cpp:712
MenuActionResult MenuDelegateAction(Menu &menu) override
Definition: IOHandler.cpp:3497
void Run(Debugger &debugger)
Definition: IOHandler.cpp:1757
void CalculateRowIndexes(int &row_idx)
Definition: IOHandler.cpp:2074
HandleCharResult WindowDelegateHandleChar(Window &window, int key) override
Definition: IOHandler.cpp:3360
"lldb/Utility/ArgCompletionRequest.h"
size_t GetMaxLineLength() const
Definition: IOHandler.cpp:854
std::vector< Row > & GetChildren()
Definition: IOHandler.cpp:1932
bool WaitForValueEqualTo(T value, const Timeout< std::micro > &timeout=llvm::None)
Wait for m_value to be equal to value.
Definition: Predicate.h:156
Process has exited and can&#39;t be examined.
size_t GetNumChildren()
Definition: IOHandler.cpp:2067
AddressRange m_disassembly_range
Definition: IOHandler.cpp:4482
bool StateIsRunningState(lldb::StateType state)
Check if a state represents a state where the process or thread is running.
Definition: State.cpp:68
bool operator==(const Address &lhs, const Address &rhs)
Definition: Address.cpp:973
void DrawMenuTitle(Window &window, bool highlight)
Definition: IOHandler.cpp:1484
void RecalculateNameLengths()
Definition: IOHandler.cpp:1459
WindowSP & GetMainWindow()
Definition: IOHandler.cpp:1884
#define UINT32_MAX
Definition: lldb-defines.h:31
lldb::VariableSP GetVariableAtIndex(size_t idx) const
HandleCharResult WindowDelegateHandleChar(Window &window, int c) override
Definition: IOHandler.cpp:2729
void Clear()
Definition: IOHandler.cpp:688
WindowDelegates & GetWindowDelegates()
Definition: IOHandler.cpp:1890
lldb::ThreadSP GetThreadAtIndex(uint32_t idx, bool can_update=true)
Definition: ThreadList.cpp:90
bool WindowDelegateDraw(Window &window, bool force) override
Definition: IOHandler.cpp:3844
int GetKeyValue() const
Definition: IOHandler.cpp:1400
Target * GetTargetPtr() const
Returns a pointer to the target object.
void SetCannedResult(MenuActionResult result)
Definition: IOHandler.cpp:1384
#define LLDB_INVALID_ADDRESS
Invalid value definitions.
Definition: lldb-defines.h:85
bool IsActive() const
Definition: IOHandler.cpp:1257
virtual void IOHandlerInputInterrupted(IOHandler &io_handler, std::string &data)
Definition: IOHandler.h:246
Debugger & m_debugger
Definition: IOHandler.cpp:2401
uint64_t user_id_t
Definition: lldb-types.h:84
KeyHelp * WindowDelegateGetKeyHelp() override
Definition: IOHandler.cpp:3820
bool GetLines(StringList &lines, bool &interrupted)
Definition: IOHandler.cpp:502
uint64_t m_identifier
Definition: IOHandler.cpp:2216
void IOHandlerInputComplete(IOHandler &io_handler, std::string &data) override
Called when a line or lines have been retrieved.
Definition: IOHandler.cpp:187
Node * Parse(llvm::StringRef expr, llvm::BumpPtrAllocator &alloc)
Parse the given postfix expression.
bool StateIsStoppedState(lldb::StateType state, bool must_exist)
Check if a state represents a state where the process or thread is stopped.
Definition: State.cpp:89
uint32_t GetStopID() const
Definition: Process.h:1337
bool GetIsInteractive()
Check if the input is being supplied interactively by a user.
Definition: IOHandler.cpp:130
size_t GetDepth() const
Definition: IOHandler.cpp:2051
void Append(const lldb::ValueObjectSP &val_obj_sp)
ProcessSP GetProcess()
Definition: IOHandler.cpp:2572
const char * GetStringAtIndex(size_t idx) const
Definition: StringList.cpp:82
KeyHelp * WindowDelegateGetKeyHelp() override
Definition: IOHandler.cpp:2698
TreeItem * m_selected_item
Definition: IOHandler.cpp:2404
void UnderlineOn()
Definition: IOHandler.cpp:952
const char * GetPrompt() override
Definition: IOHandler.cpp:450
bool GetLine(std::string &line, bool &interrupted)
Definition: IOHandler.cpp:342
IOHandlerConfirm(Debugger &debugger, llvm::StringRef prompt, bool default_response)
Definition: IOHandler.cpp:150
const char * WindowDelegateGetHelpText() override
Definition: IOHandler.cpp:3473
void SetStartingColumn(int col)
Definition: IOHandler.cpp:1398
void Update(const SymbolContext &sc)
Definition: IOHandler.cpp:3812
SourceFileWindowDelegate(Debugger &debugger)
Definition: IOHandler.cpp:3803
void UpdateExecutionContext(ExecutionContext *override_context)
void SetBounds(const Rect &bounds)
Definition: IOHandler.cpp:976
std::set< uint32_t > BreakpointLines
Definition: IOHandler.cpp:4474
bool WindowDelegateDraw(Window &window, bool force) override
Definition: IOHandler.cpp:1543
const char * GetName() const
Definition: IOHandler.cpp:1306
void TreeDelegateDrawTreeItem(TreeItem &item, Window &window) override
Definition: IOHandler.cpp:2578
uint32_t GetIndexOfInstructionAtAddress(const Address &addr)
const Menus & GetSubmenus() const
Definition: IOHandler.cpp:1388
void Unexpand()
Definition: IOHandler.cpp:2099
bool might_have_children
Definition: IOHandler.cpp:1912
llvm::StringRef GetString() const
Size(int w=0, int h=0)
Definition: IOHandler.cpp:686
std::string m_key_name
Definition: IOHandler.cpp:1418
KeyHelp * WindowDelegateGetKeyHelp() override
Definition: IOHandler.cpp:2294
FormatEntity::Entry m_format
Definition: IOHandler.cpp:2558
WindowSP FindSubWindow(const char *name)
Definition: IOHandler.cpp:1054
int GetStartingColumn() const
Definition: IOHandler.cpp:1396
lldb::addr_t GetOpcodeLoadAddress(Target *target, AddressClass addr_class=AddressClass::eInvalid) const
Get the load address as an opcode load address.
Definition: Address.cpp:349
void CancelForwardEvents(const lldb::ListenerSP &listener_sp)
Definition: Debugger.cpp:1525
ThreadSP GetThread(const TreeItem &item)
Definition: IOHandler.cpp:2481
Predicate< bool > m_popped
Definition: IOHandler.h:171
Row * parent
Definition: IOHandler.cpp:1906
FileSpec file
The source file, possibly mapped by the target.source-map setting.
Definition: LineEntry.h:151
int GetDrawWidth() const
Definition: IOHandler.cpp:1408
IOHandlerEditline(Debugger &debugger, IOHandler::Type type, const char *editline_name, llvm::StringRef prompt, llvm::StringRef continuation_prompt, bool multi_line, bool color_prompts, uint32_t line_number_start, IOHandlerDelegate &delegate, repro::DataRecorder *data_recorder)
Definition: IOHandler.cpp:265
std::string m_name
Definition: IOHandler.cpp:1417
uint32_t line
The source line number, or zero if there is no line number information.
Definition: LineEntry.h:154
void PrintAsync(Stream *stream, const char *s, size_t len)
Definition: IOHandler.cpp:142
Block * GetFrameBlock()
Get the current lexical scope block for this StackFrame, if possible.
Definition: StackFrame.cpp:242
A plug-in interface definition class for debugging a process.
Definition: Process.h:353
Window * GetParent() const
Definition: IOHandler.cpp:1255
Process * GetProcessPtr() const
Returns a pointer to the process object.
virtual void Draw(bool force)
Definition: IOHandler.cpp:1119
TreeItem * GetParent()
Definition: IOHandler.cpp:2093
bool WindowDelegateDraw(Window &window, bool force) override
Definition: IOHandler.cpp:3450
std::shared_ptr< ThreadTreeDelegate > m_thread_delegate_sp
Definition: IOHandler.cpp:2627
bool TreeDelegateItemSelected(TreeItem &item) override
Definition: IOHandler.cpp:2447
std::vector< Row > m_rows
Definition: IOHandler.cpp:2828
Status Resume()
Resumes all of a process&#39;s threads as configured using the Thread run control functions.
Definition: Process.cpp:1385
size_t Printf(const char *format,...) __attribute__((format(printf
Output printf formatted output to the stream.
Definition: Stream.cpp:106
Debugger & GetDebugger()
Definition: IOHandler.h:131
virtual bool IOHandlerInterrupt(IOHandler &io_handler)
Definition: IOHandler.h:282
std::shared_ptr< WindowDelegate > WindowDelegateSP
Definition: IOHandler.cpp:644
Rect MakeStatusBar()
Definition: IOHandler.cpp:734
TreeItem & operator[](size_t i)
Definition: IOHandler.cpp:2063
FrameVariablesWindowDelegate(Debugger &debugger)
Definition: IOHandler.cpp:3001
bool WindowDelegateDraw(Window &window, bool force) override
Definition: IOHandler.cpp:3079
virtual ~Window()
Definition: IOHandler.cpp:887
std::string CopyList(const char *item_preamble=nullptr, const char *items_sep="\) const
Definition: StringList.cpp:195
bool IsValid() const
Check if the object state is valid.
Definition: Address.h:343
A section + offset based address class.
Definition: Address.h:80
bool SetSelectedFrameByIndex(uint32_t frame_idx, bool broadcast=false)
Definition: Thread.cpp:334
size_t GetDepth() const
Definition: IOHandler.cpp:1922
void PutChar(int ch)
Definition: IOHandler.cpp:942
int m_max_submenu_name_length
Definition: IOHandler.cpp:1423
General Outline: Allows adding and removing breakpoints and find by ID and index. ...
bool m_is_expanded
Definition: IOHandler.cpp:2221
bool operator!=(const Address &lhs, const Address &rhs)
Definition: Address.cpp:979
void SetCanBeActive(bool b)
Definition: IOHandler.cpp:1247
lldb::ProcessSP GetProcess() const
Definition: Thread.h:154
virtual bool IsActive()
Definition: IOHandler.h:84
Windows m_subwindows
Definition: IOHandler.cpp:1313
void AddSubmenu(const MenuSP &menu_sp)
Definition: IOHandler.cpp:1474
lldb::BreakpointSP GetBreakpointAtIndex(size_t i) const
Returns a shared pointer to the breakpoint with index i.
void MoveCursor(int x, int y)
Definition: IOHandler.cpp:936
void SetPopped(bool b)
Definition: IOHandler.cpp:138
void GetMatches(StringList &matches) const
Adds all collected completion matches to the given list.