Implement advanced argument parsing for target and environment variables, allow dynamic sidebar resizing, improve variable value display, and refactor layout constants into a struct

Author Mitja Felicijan <mitja.felicijan@gmail.com> 2026-01-17 01:54:34 +0100
Committer Mitja Felicijan <mitja.felicijan@gmail.com> 2026-01-17 01:54:34 +0100
Commit be0ff3e893599a7db18be0761e4386ba1fdc4294 (patch)
-rw-r--r-- tdbg.cpp 123
1 files changed, 95 insertions, 28 deletions
diff --git a/tdbg.cpp b/tdbg.cpp
...
17
  
17
  
18
using namespace lldb;
18
using namespace lldb;
19
  
19
  
20
const int LOG_WINDOW_HEIGHT = 10;
20
struct LayoutConfig {
21
const int STATUS_WINDOW_HEIGHT = 1;
21
	int log_height = 10;
22
const int BREAKPOINTS_WINDOW_HEIGHT = 10;
22
	int status_height = 1;
23
const int SIDEBAR_WIDTH = 50;
23
	int breakpoints_height = 10;
  
24
	int sidebar_width = 50;
  
25
} layout_config;
24
  
26
  
25
// https://unicodeplus.com
27
// https://unicodeplus.com
26
const uint32_t SCROLLBAR_THUMB = 0x2593; // Dark shade
28
const uint32_t SCROLLBAR_THUMB = 0x2593; // Dark shade
...
165
	char type_char = get_type_char(val.GetType());
167
	char type_char = get_type_char(val.GetType());
166
	std::string prefix = std::string("(") + type_char + ") ";
168
	std::string prefix = std::string("(") + type_char + ") ";
167
  
169
  
  
170
	std::string val_str = val.GetValue() ? val.GetValue() : "";
  
171
	std::string summary_str = val.GetSummary() ? val.GetSummary() : "";
168
	std::string value;
172
	std::string value;
  
173
  
169
	if (!val.IsValid()) value = "(invalid)";
174
	if (!val.IsValid()) value = "(invalid)";
170
	else if (val.GetValue()) value = val.GetValue();
175
	else {
171
	else if (val.GetSummary()) value = val.GetSummary();
176
		if (!val_str.empty() && !summary_str.empty()) value = val_str + " " + summary_str;
  
177
		else if (!val_str.empty()) value = val_str;
  
178
		else if (!summary_str.empty()) value = summary_str;
  
179
	}
172
  
180
  
173
	std::string indent_str(indent * 2, ' ');
181
	std::string indent_str(indent * 2, ' ');
174
	std::string content = original_name;
182
	std::string content = original_name;
...
198
	std::string name = name_override.empty() ? (val.GetName() ? val.GetName() : "") : name_override;
206
	std::string name = name_override.empty() ? (val.GetName() ? val.GetName() : "") : name_override;
199
	char type_char = get_type_char(val.GetType());
207
	char type_char = get_type_char(val.GetType());
200
  
208
  
  
209
	std::string val_str = val.GetValue() ? val.GetValue() : "";
  
210
	std::string summary_str = val.GetSummary() ? val.GetSummary() : "";
201
	std::string value;
211
	std::string value;
  
212
  
202
	if (!val.IsValid()) value = "(invalid)";
213
	if (!val.IsValid()) value = "(invalid)";
203
	else if (val.GetValue()) value = val.GetValue();
214
	else {
204
	else if (val.GetSummary()) value = val.GetSummary();
215
		if (!val_str.empty() && !summary_str.empty()) value = val_str + " " + summary_str;
  
216
		else if (!val_str.empty()) value = val_str;
  
217
		else if (!summary_str.empty()) value = summary_str;
  
218
	}
205
  
219
  
206
	std::string indent_str(indent * 2, ' ');
220
	std::string indent_str(indent * 2, ' ');
207
	std::string line = get_timestamp() + " " + indent_str + "(" + type_char + ") " + name;
221
	std::string line = get_timestamp() + " " + indent_str + "(" + type_char + ") " + name;
...
588
}
602
}
589
  
603
  
590
int main(int argc, char** argv) {
604
int main(int argc, char** argv) {
591
	if (argc < 2) {
605
	std::vector<std::string> target_env;
592
		std::cerr << "Usage: " << argv[0] << " <target_executable>\n";
606
	std::vector<std::string> debuggee_args;
  
607
	std::string target_path;
  
608
  
  
609
	for (int i = 1; i < argc; ++i) {
  
610
		std::string arg = argv[i];
  
611
		if (arg == "-e" && i + 1 < argc) {
  
612
			target_env.push_back(argv[++i]);
  
613
		} else if (arg == "--") {
  
614
			for (int j = i + 1; j < argc; ++j) {
  
615
				debuggee_args.push_back(argv[j]);
  
616
			}
  
617
			break;
  
618
		} else if (target_path.empty()) {
  
619
			target_path = arg;
  
620
		} else {
  
621
			debuggee_args.push_back(arg);
  
622
		}
  
623
	}
  
624
  
  
625
	if (target_path.empty()) {
  
626
		std::cerr << "Usage: " << argv[0] << " [-e KEY=VALUE] ... <target_executable> [-- arg1 arg2 ...]\n";
593
		return 1;
627
		return 1;
594
	}
628
	}
595
  
629
  
...
599
		close(log_fd);
633
		close(log_fd);
600
	}
634
	}
601
  
635
  
602
	const char* target_path = argv[1];
  
603
  
  
604
	LLDBGuard lldb_guard;
636
	LLDBGuard lldb_guard;
605
	SBDebugger debugger = SBDebugger::Create();
637
	SBDebugger debugger = SBDebugger::Create();
606
	debugger.SetAsync(false); 
638
	debugger.SetAsync(false); 
607
  
639
  
608
	SBTarget target = debugger.CreateTarget(target_path);
640
	SBTarget target = debugger.CreateTarget(target_path.c_str());
609
	if (!target.IsValid()) {
641
	if (!target.IsValid()) {
610
		std::cerr << "Failed to create target for " << target_path << "\n";
642
		std::cerr << "Failed to create target for " << target_path << "\n";
611
		return 1;
643
		return 1;
...
635
  
667
  
636
		int width = tb_width();
668
		int width = tb_width();
637
		int height = tb_height();
669
		int height = tb_height();
638
		int main_window_height = height - LOG_WINDOW_HEIGHT - STATUS_WINDOW_HEIGHT;
670
		int main_window_height = height - layout_config.log_height - layout_config.status_height;
639
		int split_x = width - SIDEBAR_WIDTH;
671
		int split_x = width - layout_config.sidebar_width;
640
		int locals_window_height = main_window_height - BREAKPOINTS_WINDOW_HEIGHT;
672
		int locals_window_height = main_window_height - layout_config.breakpoints_height;
641
  
673
  
642
		SBFrame frame;
674
		SBFrame frame;
643
		if (process.IsValid() && process.GetState() != eStateExited) {
675
		if (process.IsValid() && process.GetState() != eStateExited) {
...
672
		}
704
		}
673
  
705
  
674
		draw_source_view(frame, 0, 0, split_x, main_window_height, source_cache, source_scroll_offset);
706
		draw_source_view(frame, 0, 0, split_x, main_window_height, source_cache, source_scroll_offset);
675
		draw_variables_view(frame, split_x, 0, SIDEBAR_WIDTH, locals_window_height, locals_scroll_offset);
707
		draw_variables_view(frame, split_x, 0, layout_config.sidebar_width, locals_window_height, locals_scroll_offset);
676
		draw_breakpoints_view(target, split_x, locals_window_height, SIDEBAR_WIDTH, BREAKPOINTS_WINDOW_HEIGHT);
708
		draw_breakpoints_view(target, split_x, locals_window_height, layout_config.sidebar_width, layout_config.breakpoints_height);
677
		draw_log_view(0, main_window_height, width, LOG_WINDOW_HEIGHT, log_buffer, mode, input_buffer, log_scroll_offset);
709
		draw_log_view(0, main_window_height, width, layout_config.log_height, log_buffer, mode, input_buffer, log_scroll_offset);
678
		draw_status_bar(process, mode, width, height);
710
		draw_status_bar(process, mode, width, height);
679
  
711
  
680
		tb_present();
712
		tb_present();
...
696
								}
728
								}
697
							}
729
							}
698
							log_msg(log_buffer, "Launching...");
730
							log_msg(log_buffer, "Launching...");
699
							process = target.LaunchSimple(nullptr, nullptr, ".");
731
  
700
							if (!process.IsValid()) log_msg(log_buffer, "Launch failed");
732
							std::vector<const char*> launch_argv;
701
							else log_msg(log_buffer, "Launched");
733
							launch_argv.push_back(target_path.c_str());
  
734
							for (const auto& arg : debuggee_args) {
  
735
								launch_argv.push_back(arg.c_str());
  
736
							}
  
737
							launch_argv.push_back(nullptr);
  
738
  
  
739
							std::vector<const char*> launch_env;
  
740
							for (const auto& env : target_env) {
  
741
								launch_env.push_back(env.c_str());
  
742
							}
  
743
							launch_env.push_back(nullptr);
  
744
  
  
745
							SBLaunchInfo launch_info(launch_argv.data());
  
746
							launch_info.SetEnvironmentEntries(launch_env.data(), true);
  
747
							launch_info.SetWorkingDirectory(".");
  
748
  
  
749
							SBError error;
  
750
							process = target.Launch(launch_info, error);
  
751
  
  
752
							if (!process.IsValid() || error.Fail()) {
  
753
								std::string err_msg = "Launch failed";
  
754
								if (error.GetCString()) {
  
755
									err_msg += ": ";
  
756
									err_msg += error.GetCString();
  
757
								}
  
758
								log_msg(log_buffer, err_msg);
  
759
							} else {
  
760
								log_msg(log_buffer, "Launched");
  
761
							}
702
						} else {
762
						} else {
703
							log_msg(log_buffer, "Already running");
763
							log_msg(log_buffer, "Already running");
704
						}
764
						}
...
719
								case 's': if (thread.IsValid()) thread.StepInto(); break;
779
								case 's': if (thread.IsValid()) thread.StepInto(); break;
720
								case 'o': if (thread.IsValid()) thread.StepOut(); break;
780
								case 'o': if (thread.IsValid()) thread.StepOut(); break;
721
								case 'c': process.Continue(); break;
781
								case 'c': process.Continue(); break;
  
782
								case '<': layout_config.sidebar_width = std::min(width - 20, layout_config.sidebar_width + 2); break;
  
783
								case '>': layout_config.sidebar_width = std::max(20, layout_config.sidebar_width - 2); break;
  
784
							}
  
785
						} else {
  
786
							switch (ev.ch) {
  
787
								case '<': layout_config.sidebar_width = std::min(width - 20, layout_config.sidebar_width + 2); break;
  
788
								case '>': layout_config.sidebar_width = std::max(20, layout_config.sidebar_width - 2); break;
722
							}
789
							}
723
						}
790
						}
724
					}
791
					}
...
762
					}
829
					}
763
				}
830
				}
764
			} else if (ev.type == TB_EVENT_MOUSE) {
831
			} else if (ev.type == TB_EVENT_MOUSE) {
765
				int main_window_height = tb_height() - LOG_WINDOW_HEIGHT - STATUS_WINDOW_HEIGHT;
832
				int main_window_height = tb_height() - layout_config.log_height - layout_config.status_height;
766
  
833
  
767
				// Log window scrolling
834
				// Log window scrolling
768
				int log_start_y = main_window_height;
835
				int log_start_y = main_window_height;
769
				int log_end_y = tb_height() - STATUS_WINDOW_HEIGHT;
836
				int log_end_y = tb_height() - layout_config.status_height;
770
				if (ev.y >= log_start_y && ev.y < log_end_y) {
837
				if (ev.y >= log_start_y && ev.y < log_end_y) {
771
					if (ev.key == TB_KEY_MOUSE_WHEEL_UP) {
838
					if (ev.key == TB_KEY_MOUSE_WHEEL_UP) {
772
						int max_scroll = std::max(0, (int)log_buffer.size() - (LOG_WINDOW_HEIGHT - 2));
839
						int max_scroll = std::max(0, (int)log_buffer.size() - (layout_config.log_height - 2));
773
						if (log_scroll_offset < max_scroll) {
840
						if (log_scroll_offset < max_scroll) {
774
							log_scroll_offset++;
841
							log_scroll_offset++;
775
						}
842
						}
...
808
				}
875
				}
809
  
876
  
810
				// Locals window scrolling
877
				// Locals window scrolling
811
				int split_x = tb_width() - SIDEBAR_WIDTH;
878
				int split_x = tb_width() - layout_config.sidebar_width;
812
				int locals_window_height = main_window_height - BREAKPOINTS_WINDOW_HEIGHT;
879
				int locals_window_height = main_window_height - layout_config.breakpoints_height;
813
				if (ev.x >= split_x && ev.y < locals_window_height) {
880
				if (ev.x >= split_x && ev.y < locals_window_height) {
814
					std::vector<VarLine> lines;
881
					std::vector<VarLine> lines;
815
					if (frame.IsValid()) {
882
					if (frame.IsValid()) {
816
						SBValueList vars = frame.GetVariables(true, true, false, true);
883
						SBValueList vars = frame.GetVariables(true, true, false, true);
817
						for (uint32_t i = 0; i < vars.GetSize(); ++i) {
884
						for (uint32_t i = 0; i < vars.GetSize(); ++i) {
818
							collect_variables_recursive(vars.GetValueAtIndex(i), 0, lines, SIDEBAR_WIDTH - 2);
885
							collect_variables_recursive(vars.GetValueAtIndex(i), 0, lines, layout_config.sidebar_width - 2);
819
						}
886
						}
820
					}
887
					}
821
					int max_scroll = std::max(0, (int)lines.size() - (locals_window_height - 2));
888
					int max_scroll = std::max(0, (int)lines.size() - (locals_window_height - 2));
...