summaryrefslogtreecommitdiff
path: root/tdbg.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'tdbg.cpp')
-rw-r--r--tdbg.cpp148
1 files changed, 148 insertions, 0 deletions
diff --git a/tdbg.cpp b/tdbg.cpp
new file mode 100644
index 0000000..b1c8078
--- /dev/null
+++ b/tdbg.cpp
@@ -0,0 +1,148 @@
+#include <lldb/API/LLDB.h>
+
+#include <iostream>
+#include <string>
+#include <thread>
+#include <chrono>
+
+using namespace lldb;
+
+void print_variables(SBFrame &frame) {
+ SBValueList vars = frame.GetVariables(true, true, false, true);
+
+ for (uint32_t i = 0; i < vars.GetSize(); ++i) {
+ SBValue var = vars.GetValueAtIndex(i);
+ std::cout << var.GetName() << " = ";
+
+ if (!var.IsValid()) {
+ std::cout << "(invalid)\n";
+ }
+ else if (var.GetType().IsPointerType()) {
+ uint64_t addr = var.GetValueAsUnsigned();
+ if (addr == 0) {
+ std::cout << "(null pointer)\n";
+ } else {
+ std::cout << "(pointer at 0x" << std::hex << addr << std::dec << ")\n";
+ }
+ }
+ else if (var.GetNumChildren() > 0) {
+ std::cout << "{ ";
+ uint32_t n = var.GetNumChildren();
+ for (uint32_t j = 0; j < n; ++j) {
+ SBValue child = var.GetChildAtIndex(j);
+ std::cout << child.GetName() << ": " << (child.IsValid() ? child.GetValue() : "(invalid)");
+ if (j + 1 < n) std::cout << ", ";
+ }
+ std::cout << " }\n";
+ } else {
+ std::cout << var.GetValue() << "\n";
+ }
+ }
+}
+
+
+void print_backtrace(SBThread &thread) {
+ int num_frames = thread.GetNumFrames();
+ for (int i = 0; i < num_frames; ++i) {
+ SBFrame frame = thread.GetFrameAtIndex(i);
+ std::cout << "#" << i << " " << frame.GetFunctionName()
+ << " at line " << frame.GetLineEntry().GetLine() << "\n";
+ }
+}
+
+int main(int argc, char** argv) {
+ if (argc < 2) {
+ std::cerr << "Usage: " << argv[0] << " <target_executable>\n";
+ return 1;
+ }
+
+ const char* target_path = argv[1];
+
+ // Initialize LLDB.
+ SBDebugger::Initialize();
+ SBDebugger debugger = SBDebugger::Create();
+ debugger.SetAsync(true);
+
+ SBTarget target = debugger.CreateTarget(target_path);
+ if (!target.IsValid()) {
+ std::cerr << "Failed to create target for " << target_path << "\n";
+ return 1;
+ }
+
+ SBBreakpoint bp = target.BreakpointCreateByName("main");
+ std::cout << "Breakpoint set at main\n";
+
+ SBProcess process = target.LaunchSimple(nullptr, nullptr, ".");
+ if (!process.IsValid()) {
+ std::cerr << "Failed to launch process\n";
+ return 1;
+ }
+
+ std::cout << "Process launched\n";
+
+ SBListener listener = debugger.GetListener();
+ SBEvent event;
+
+ while (true) {
+ // Wait for events from LLDB.
+ if (listener.WaitForEvent(1, event)) {
+ StateType state = SBProcess::GetStateFromEvent(event);
+
+ switch (state) {
+ case eStateStopped:
+ {
+ std::cout << "\nProcess stopped!\n";
+ SBThread thread = process.GetSelectedThread();
+ SBFrame frame = thread.GetFrameAtIndex(0);
+ std::cout << "Stopped at function: " << frame.GetFunctionName()
+ << ", line: " << frame.GetLineEntry().GetLine() << "\n";
+
+ // REPL loop for user commands.
+ std::string cmd;
+ while (true) {
+ std::cout << "(tdbg) ";
+ std::getline(std::cin, cmd);
+ if (cmd == "c") {
+ process.Continue();
+ break; // exit REPL, wait for next stop
+ } else if (cmd == "s") {
+ thread.StepInto();
+ break;
+ } else if (cmd == "n") {
+ thread.StepOver();
+ break;
+ } else if (cmd == "bt") {
+ print_backtrace(thread);
+ } else if (cmd == "v") {
+ print_variables(frame);
+ } else if (cmd == "q") {
+ process.Kill();
+ goto cleanup;
+ } else {
+ std::cout << "Commands: c=continue, s=step in, n=step over, bt=backtrace, v=variables, q=quit\n";
+ }
+ }
+ break;
+ }
+
+ case eStateExited:
+ {
+ std::cout << "Process exited\n";
+ goto cleanup;
+ }
+
+ case eStateRunning:
+ break;
+
+ default:
+ break;
+ }
+ } else {
+ std::this_thread::sleep_for(std::chrono::milliseconds(50));
+ }
+ }
+
+cleanup:
+ SBDebugger::Terminate();
+ return 0;
+}