ascii-chat 0.8.38
Real-time terminal-based video chat with ASCII art conversion
Loading...
Searching...
No Matches
lldb_controller.cpp
Go to the documentation of this file.
1
6#include "lldb_controller.h"
7
8#include <lldb/API/SBBreakpoint.h>
9#include <lldb/API/SBBreakpointLocation.h>
10#include <lldb/API/SBCommandInterpreter.h>
11#include <lldb/API/SBDebugger.h>
12#include <lldb/API/SBError.h>
13#include <lldb/API/SBEvent.h>
14#include <lldb/API/SBFileSpec.h>
15#include <lldb/API/SBFrame.h>
16#include <lldb/API/SBListener.h>
17#include <lldb/API/SBProcess.h>
18#include <lldb/API/SBStream.h>
19#include <lldb/API/SBTarget.h>
20#include <lldb/API/SBThread.h>
21#include <lldb/API/SBType.h>
22#include <lldb/API/SBValue.h>
23#include <lldb/API/SBValueList.h>
24
25#include <chrono>
26#include <cstdio>
27
28namespace ascii_query {
29
30// =============================================================================
31// Constructor / Destructor
32// =============================================================================
33
35
37
38// =============================================================================
39// Initialization
40// =============================================================================
41
43 if (initialized_) {
44 return true;
45 }
46
47 // Initialize LLDB
48 lldb::SBDebugger::Initialize();
49
50 // Create debugger instance
51 debugger_ = lldb::SBDebugger::Create(false); // false = no source manager
52 if (!debugger_.IsValid()) {
53 setError("Failed to create LLDB debugger instance");
54 return false;
55 }
56
57 // Don't echo commands
58 debugger_.SetAsync(true);
59
60 // Create a listener for process events
61 listener_ = debugger_.GetListener();
62 if (!listener_.IsValid()) {
63 setError("Failed to create LLDB listener");
64 lldb::SBDebugger::Destroy(debugger_);
65 return false;
66 }
67
68 initialized_ = true;
69 clearError();
70 return true;
71}
72
74 if (!initialized_) {
75 return;
76 }
77
78 // Detach if still attached
79 if (isAttached()) {
80 detach();
81 }
82
83 // Delete target if valid
84 if (target_.IsValid()) {
85 debugger_.DeleteTarget(target_);
86 }
87
88 // Destroy debugger
89 if (debugger_.IsValid()) {
90 lldb::SBDebugger::Destroy(debugger_);
91 }
92
93 // Terminate LLDB
94 lldb::SBDebugger::Terminate();
95
96 initialized_ = false;
97}
98
99// =============================================================================
100// Process Attachment
101// =============================================================================
102
103bool LLDBController::attach(pid_t pid) {
104 if (!initialized_) {
105 setError("LLDB not initialized");
106 return false;
107 }
108
109 // Create an empty target - LLDB will fill it in when we attach
110 lldb::SBError error;
111 target_ = debugger_.CreateTarget("", "", "", false, error);
112 if (!target_.IsValid()) {
113 setError("Failed to create target: " + std::string(error.GetCString() ? error.GetCString() : "unknown error"));
114 return false;
115 }
116
117 // Attach to process
118 lldb::SBAttachInfo attach_info(pid);
119 attach_info.SetListener(listener_);
120
121 process_ = target_.Attach(attach_info, error);
122 if (!process_.IsValid() || error.Fail()) {
123 setError("Failed to attach to PID " + std::to_string(pid) + ": " +
124 std::string(error.GetCString() ? error.GetCString() : "unknown error"));
125 debugger_.DeleteTarget(target_);
126 return false;
127 }
128
129 clearError();
130 return true;
131}
132
133bool LLDBController::attachByName(const std::string &process_name, bool wait_for) {
134 if (!initialized_) {
135 setError("LLDB not initialized");
136 return false;
137 }
138
139 // Create an empty target
140 lldb::SBError error;
141 target_ = debugger_.CreateTarget("", "", "", false, error);
142 if (!target_.IsValid()) {
143 setError("Failed to create target: " + std::string(error.GetCString() ? error.GetCString() : "unknown error"));
144 return false;
145 }
146
147 // Attach by name
148 lldb::SBAttachInfo attach_info;
149 attach_info.SetExecutable(process_name.c_str());
150 attach_info.SetWaitForLaunch(wait_for, false); // false = don't async
151 attach_info.SetListener(listener_);
152
153 process_ = target_.Attach(attach_info, error);
154 if (!process_.IsValid() || error.Fail()) {
155 setError("Failed to attach to process '" + process_name +
156 "': " + std::string(error.GetCString() ? error.GetCString() : "unknown error"));
157 debugger_.DeleteTarget(target_);
158 return false;
159 }
160
161 clearError();
162 return true;
163}
164
166 if (!isAttached()) {
167 return;
168 }
169
170 lldb::SBError error = process_.Detach();
171 if (error.Fail()) {
172 setError("Detach failed: " + std::string(error.GetCString() ? error.GetCString() : "unknown error"));
173 } else {
174 clearError();
175 }
176}
177
179 if (!process_.IsValid()) {
180 return false;
181 }
182
183 lldb::StateType state = process_.GetState();
184 return state != lldb::eStateInvalid && state != lldb::eStateDetached && state != lldb::eStateExited;
185}
186
188 if (!process_.IsValid()) {
189 return 0;
190 }
191 return static_cast<pid_t>(process_.GetProcessID());
192}
193
194std::string LLDBController::targetName() const {
195 if (!target_.IsValid()) {
196 return "";
197 }
198
199 lldb::SBFileSpec exe = target_.GetExecutable();
200 if (!exe.IsValid()) {
201 return "";
202 }
203
204 const char *filename = exe.GetFilename();
205 return filename ? filename : "";
206}
207
208// =============================================================================
209// Process Control
210// =============================================================================
211
213 if (!isAttached()) {
214 setError("Not attached to a process");
215 return false;
216 }
217
218 // Sync state first to ensure we have the current process state
219 syncProcessState();
220
221 // Check if already stopped
222 lldb::StateType current = process_.GetState();
223 if (current == lldb::eStateStopped || current == lldb::eStateSuspended) {
224 clearError();
225 return true;
226 }
227
228 // Only try to stop if running
229 if (current != lldb::eStateRunning && current != lldb::eStateStepping) {
230 setError("Process is not running (state: " + std::to_string(static_cast<int>(current)) + ")");
231 return false;
232 }
233
234 lldb::SBError error = process_.Stop();
235 if (error.Fail()) {
236 setError("Failed to stop process: " + std::string(error.GetCString() ? error.GetCString() : "unknown error"));
237 return false;
238 }
239
240 // Wait for process to actually stop (async mode)
241 if (!waitForState(lldb::eStateStopped, 5)) {
242 setError("Timeout waiting for process to stop");
243 return false;
244 }
245
246 clearError();
247 return true;
248}
249
251 if (!isAttached()) {
252 setError("Not attached to a process");
253 return false;
254 }
255
256 // Sync state first
257 syncProcessState();
258
259 // Check if already running
260 lldb::StateType current = process_.GetState();
261 if (current == lldb::eStateRunning || current == lldb::eStateStepping) {
262 clearError();
263 return true;
264 }
265
266 lldb::SBError error = process_.Continue();
267 if (error.Fail()) {
268 setError("Failed to resume process: " + std::string(error.GetCString() ? error.GetCString() : "unknown error"));
269 return false;
270 }
271
272 // Wait briefly for process to start running (async mode)
273 // Note: We don't wait long because running is the target state
274 waitForState(lldb::eStateRunning, 2);
275
276 clearError();
277 return true;
278}
279
281 lldb::SBThread thread = getSelectedThreadInternal();
282 if (!thread.IsValid()) {
283 setError("No valid thread selected");
284 return false;
285 }
286
287 thread.StepInto();
288 clearError();
289 return true;
290}
291
293 lldb::SBThread thread = getSelectedThreadInternal();
294 if (!thread.IsValid()) {
295 setError("No valid thread selected");
296 return false;
297 }
298
299 thread.StepOver();
300 clearError();
301 return true;
302}
303
305 lldb::SBThread thread = getSelectedThreadInternal();
306 if (!thread.IsValid()) {
307 setError("No valid thread selected");
308 return false;
309 }
310
311 thread.StepOut();
312 clearError();
313 return true;
314}
315
317 if (!process_.IsValid()) {
319 }
320
321 lldb::StateType lldb_state = process_.GetState();
322 switch (lldb_state) {
323 case lldb::eStateInvalid:
325 case lldb::eStateRunning:
326 case lldb::eStateStepping:
328 case lldb::eStateStopped:
329 case lldb::eStateSuspended:
331 case lldb::eStateExited:
333 case lldb::eStateCrashed:
335 case lldb::eStateDetached:
337 default:
339 }
340}
341
342const std::string &LLDBController::lastError() const { return last_error_; }
343
344// =============================================================================
345// Thread Information
346// =============================================================================
347
348std::vector<ThreadInfo> LLDBController::getThreads() const {
349 std::vector<ThreadInfo> result;
350
351 if (!process_.IsValid()) {
352 return result;
353 }
354
355 // Sync state to ensure we have fresh thread info
356 syncProcessState();
357
358 lldb::SBThread selected = process_.GetSelectedThread();
359 uint64_t selected_id = selected.IsValid() ? selected.GetThreadID() : 0;
360
361 uint32_t num_threads = process_.GetNumThreads();
362 result.reserve(num_threads);
363
364 for (uint32_t i = 0; i < num_threads; i++) {
365 lldb::SBThread thread = process_.GetThreadAtIndex(i);
366 if (thread.IsValid()) {
367 bool is_selected = (thread.GetThreadID() == selected_id);
368 result.push_back(threadToInfo(thread, is_selected));
369 }
370 }
371
372 return result;
373}
374
375std::optional<ThreadInfo> LLDBController::getSelectedThread() const {
376 lldb::SBThread thread = getSelectedThreadInternal();
377 if (!thread.IsValid()) {
378 return std::nullopt;
379 }
380 return threadToInfo(thread, true);
381}
382
384 if (!process_.IsValid()) {
385 setError("No valid process");
386 return false;
387 }
388
389 uint32_t num_threads = process_.GetNumThreads();
390 for (uint32_t i = 0; i < num_threads; i++) {
391 lldb::SBThread thread = process_.GetThreadAtIndex(i);
392 if (thread.IsValid() && thread.GetThreadID() == thread_id) {
393 process_.SetSelectedThread(thread);
394 clearError();
395 return true;
396 }
397 }
398
399 setError("Thread ID " + std::to_string(thread_id) + " not found");
400 return false;
401}
402
403// =============================================================================
404// Stack Frames
405// =============================================================================
406
407std::vector<FrameInfo> LLDBController::getFrames(uint32_t max_frames) const {
408 std::vector<FrameInfo> result;
409
410 lldb::SBThread thread = getSelectedThreadInternal();
411 if (!thread.IsValid()) {
412 return result;
413 }
414
415 uint32_t num_frames = thread.GetNumFrames();
416 if (max_frames > 0 && num_frames > max_frames) {
417 num_frames = max_frames;
418 }
419
420 result.reserve(num_frames);
421 for (uint32_t i = 0; i < num_frames; i++) {
422 lldb::SBFrame frame = thread.GetFrameAtIndex(i);
423 if (frame.IsValid()) {
424 result.push_back(frameToInfo(frame));
425 }
426 }
427
428 return result;
429}
430
431std::optional<FrameInfo> LLDBController::getFrame(uint32_t frame_index) const {
432 lldb::SBFrame frame = getFrameInternal(frame_index);
433 if (!frame.IsValid()) {
434 return std::nullopt;
435 }
436 return frameToInfo(frame);
437}
438
439// =============================================================================
440// Variable Reading
441// =============================================================================
442
443std::optional<VariableInfo> LLDBController::readVariable(const std::string &name, uint32_t frame_index,
444 uint32_t expand_depth) const {
445 lldb::SBFrame frame = getFrameInternal(frame_index);
446 if (!frame.IsValid()) {
447 return std::nullopt;
448 }
449
450 // Try to find the variable
451 // First, check if it's a path expression (has dots or arrows)
452 lldb::SBValue value;
453
454 if (name.find('.') != std::string::npos || name.find("->") != std::string::npos || name.find('[') != std::string::npos) {
455 // Path expression - use GetValueForVariablePath
456 value = frame.GetValueForVariablePath(name.c_str());
457 } else {
458 // Simple name - try FindVariable first
459 value = frame.FindVariable(name.c_str());
460
461 // If not found, try as a register
462 if (!value.IsValid()) {
463 value = frame.FindRegister(name.c_str());
464 }
465 }
466
467 if (!value.IsValid()) {
468 return std::nullopt;
469 }
470
471 return valueToInfo(value, expand_depth);
472}
473
474std::vector<VariableInfo> LLDBController::listVariables(uint32_t frame_index, bool include_args, bool include_locals,
475 bool include_statics) const {
476 std::vector<VariableInfo> result;
477
478 lldb::SBFrame frame = getFrameInternal(frame_index);
479 if (!frame.IsValid()) {
480 return result;
481 }
482
483 lldb::SBValueList vars = frame.GetVariables(include_args, include_locals, include_statics,
484 true); // true = in_scope_only
485
486 uint32_t num_vars = vars.GetSize();
487 result.reserve(num_vars);
488
489 for (uint32_t i = 0; i < num_vars; i++) {
490 lldb::SBValue value = vars.GetValueAtIndex(i);
491 if (value.IsValid()) {
492 result.push_back(valueToInfo(value, 0)); // Don't expand by default
493 }
494 }
495
496 return result;
497}
498
499// =============================================================================
500// Breakpoints
501// =============================================================================
502
503int32_t LLDBController::setBreakpoint(const std::string &file, uint32_t line, const std::string &condition) {
504 if (!target_.IsValid()) {
505 setError("No valid target");
506 return -1;
507 }
508
509 lldb::SBBreakpoint bp = target_.BreakpointCreateByLocation(file.c_str(), line);
510 if (!bp.IsValid()) {
511 setError("Failed to create breakpoint at " + file + ":" + std::to_string(line));
512 return -1;
513 }
514
515 if (!condition.empty()) {
516 bp.SetCondition(condition.c_str());
517 }
518
519 clearError();
520 return static_cast<int32_t>(bp.GetID());
521}
522
523bool LLDBController::removeBreakpoint(int32_t breakpoint_id) {
524 if (!target_.IsValid()) {
525 setError("No valid target");
526 return false;
527 }
528
529 bool result = target_.BreakpointDelete(static_cast<lldb::break_id_t>(breakpoint_id));
530 if (!result) {
531 setError("Failed to delete breakpoint " + std::to_string(breakpoint_id));
532 } else {
533 clearError();
534 }
535 return result;
536}
537
538std::vector<BreakpointInfo> LLDBController::getBreakpoints() const {
539 std::vector<BreakpointInfo> result;
540
541 if (!target_.IsValid()) {
542 return result;
543 }
544
545 uint32_t num_breakpoints = target_.GetNumBreakpoints();
546 result.reserve(num_breakpoints);
547
548 for (uint32_t i = 0; i < num_breakpoints; i++) {
549 lldb::SBBreakpoint bp = target_.GetBreakpointAtIndex(i);
550 if (bp.IsValid()) {
551 result.push_back(breakpointToInfo(bp));
552 }
553 }
554
555 return result;
556}
557
558std::optional<BreakpointInfo> LLDBController::getBreakpoint(int32_t breakpoint_id) const {
559 if (!target_.IsValid()) {
560 return std::nullopt;
561 }
562
563 lldb::SBBreakpoint bp = target_.FindBreakpointByID(static_cast<lldb::break_id_t>(breakpoint_id));
564 if (!bp.IsValid()) {
565 return std::nullopt;
566 }
567
568 return breakpointToInfo(bp);
569}
570
571bool LLDBController::waitForBreakpoint(uint32_t timeout_ms) {
572 if (!process_.IsValid()) {
573 setError("No valid process");
574 return false;
575 }
576
577 // First, resume the process if it's stopped
578 if (state() == ProcessState::Stopped) {
579 if (!resume()) {
580 return false;
581 }
582 }
583
584 // Wait for process to stop
585 lldb::SBEvent event;
586 uint32_t timeout_sec = (timeout_ms + 999) / 1000; // Round up to seconds
587
588 while (true) {
589 bool got_event = listener_.WaitForEvent(timeout_sec, event);
590 if (!got_event) {
591 setError("Timeout waiting for breakpoint");
592 return false;
593 }
594
595 lldb::StateType new_state = lldb::SBProcess::GetStateFromEvent(event);
596 if (new_state == lldb::eStateStopped) {
597 clearError();
598 return true;
599 } else if (new_state == lldb::eStateExited || new_state == lldb::eStateCrashed ||
600 new_state == lldb::eStateDetached) {
601 setError("Process exited/crashed while waiting for breakpoint");
602 return false;
603 }
604 }
605}
606
607// =============================================================================
608// Expression Evaluation
609// =============================================================================
610
611std::optional<VariableInfo> LLDBController::evaluateExpression(const std::string &expression,
612 uint32_t frame_index) const {
613 lldb::SBFrame frame = getFrameInternal(frame_index);
614 if (!frame.IsValid()) {
615 return std::nullopt;
616 }
617
618 lldb::SBExpressionOptions options;
619 options.SetIgnoreBreakpoints(true);
620 options.SetFetchDynamicValue(lldb::eDynamicDontRunTarget);
621
622 lldb::SBValue result = frame.EvaluateExpression(expression.c_str(), options);
623 if (!result.IsValid()) {
624 return std::nullopt;
625 }
626
627 lldb::SBError error = result.GetError();
628 if (error.Fail()) {
629 return std::nullopt;
630 }
631
632 return valueToInfo(result, 0);
633}
634
635// =============================================================================
636// Private Helpers
637// =============================================================================
638
639void LLDBController::setError(const std::string &msg) const { last_error_ = msg; }
640
641void LLDBController::clearError() const { last_error_.clear(); }
642
643void LLDBController::syncProcessState() const {
644 if (!listener_.IsValid() || !process_.IsValid()) {
645 return;
646 }
647
648 // Drain all pending events to synchronize LLDB's internal state
649 lldb::SBEvent event;
650 while (listener_.PeekAtNextEvent(event)) {
651 listener_.GetNextEvent(event);
652 }
653}
654
655bool LLDBController::waitForState(lldb::StateType target_state, uint32_t timeout_sec) const {
656 if (!listener_.IsValid() || !process_.IsValid()) {
657 return false;
658 }
659
660 // Check if already in target state
661 lldb::StateType current = process_.GetState();
662 if (current == target_state) {
663 return true;
664 }
665
666 // Wait for state change event
667 lldb::SBEvent event;
668 auto start = std::chrono::steady_clock::now();
669
670 while (true) {
671 // Check timeout
672 auto elapsed = std::chrono::steady_clock::now() - start;
673 auto elapsed_sec = std::chrono::duration_cast<std::chrono::seconds>(elapsed).count();
674 if (elapsed_sec >= timeout_sec) {
675 return false;
676 }
677
678 // Wait for event with 1 second timeout
679 bool got_event = listener_.WaitForEvent(1, event);
680 if (got_event) {
681 lldb::StateType new_state = lldb::SBProcess::GetStateFromEvent(event);
682 if (new_state == target_state) {
683 return true;
684 }
685 // Check for terminal states
686 if (new_state == lldb::eStateExited || new_state == lldb::eStateCrashed ||
687 new_state == lldb::eStateDetached) {
688 return false;
689 }
690 }
691
692 // Re-check process state (might have been updated)
693 current = process_.GetState();
694 if (current == target_state) {
695 return true;
696 }
697 }
698}
699
700lldb::SBThread LLDBController::getSelectedThreadInternal() const {
701 if (!process_.IsValid()) {
702 return lldb::SBThread();
703 }
704 return process_.GetSelectedThread();
705}
706
707lldb::SBFrame LLDBController::getFrameInternal(uint32_t index) const {
708 lldb::SBThread thread = getSelectedThreadInternal();
709 if (!thread.IsValid()) {
710 return lldb::SBFrame();
711 }
712 return thread.GetFrameAtIndex(index);
713}
714
715ThreadInfo LLDBController::threadToInfo(lldb::SBThread &thread, bool is_selected) {
716 ThreadInfo info;
717 info.id = thread.GetThreadID();
718 info.index = thread.GetIndexID();
719 info.is_selected = is_selected;
720
721 const char *name = thread.GetName();
722 info.name = name ? name : "";
723
724 // Get stop reason
725 lldb::StopReason stop_reason = thread.GetStopReason();
726 switch (stop_reason) {
727 case lldb::eStopReasonNone:
728 info.stop_reason = "none";
729 break;
730 case lldb::eStopReasonBreakpoint:
731 info.stop_reason = "breakpoint";
732 break;
733 case lldb::eStopReasonWatchpoint:
734 info.stop_reason = "watchpoint";
735 break;
736 case lldb::eStopReasonSignal:
737 info.stop_reason = "signal";
738 break;
739 case lldb::eStopReasonException:
740 info.stop_reason = "exception";
741 break;
742 case lldb::eStopReasonPlanComplete:
743 info.stop_reason = "step_complete";
744 break;
745 default:
746 info.stop_reason = "other";
747 break;
748 }
749
750 // Get current frame info
751 lldb::SBFrame frame = thread.GetFrameAtIndex(0);
752 if (frame.IsValid()) {
753 const char *func_name = frame.GetFunctionName();
754 info.function = func_name ? func_name : "";
755
756 lldb::SBLineEntry line_entry = frame.GetLineEntry();
757 if (line_entry.IsValid()) {
758 lldb::SBFileSpec file_spec = line_entry.GetFileSpec();
759 if (file_spec.IsValid()) {
760 const char *filename = file_spec.GetFilename();
761 info.file = filename ? filename : "";
762 }
763 info.line = line_entry.GetLine();
764 }
765 }
766
767 return info;
768}
769
770FrameInfo LLDBController::frameToInfo(lldb::SBFrame &frame) {
771 FrameInfo info;
772 info.index = frame.GetFrameID();
773 info.pc = frame.GetPC();
774 info.fp = frame.GetFP();
775
776 const char *func_name = frame.GetFunctionName();
777 info.function = func_name ? func_name : "";
778
779 lldb::SBLineEntry line_entry = frame.GetLineEntry();
780 if (line_entry.IsValid()) {
781 lldb::SBFileSpec file_spec = line_entry.GetFileSpec();
782 if (file_spec.IsValid()) {
783 const char *filename = file_spec.GetFilename();
784 info.file = filename ? filename : "";
785 }
786 info.line = line_entry.GetLine();
787 }
788
789 return info;
790}
791
792VariableInfo LLDBController::valueToInfo(lldb::SBValue value, uint32_t expand_depth) const {
793 VariableInfo info;
794 info.is_valid = value.IsValid();
795
796 if (!info.is_valid) {
797 return info;
798 }
799
800 const char *name = value.GetName();
801 info.name = name ? name : "";
802
803 lldb::SBType type = value.GetType();
804 if (type.IsValid()) {
805 const char *type_name = type.GetName();
806 info.type = type_name ? type_name : "";
807 info.size = type.GetByteSize();
808 info.is_pointer = type.IsPointerType();
809 info.is_aggregate = type.IsAggregateType();
810 }
811
812 // Get value
813 const char *val_str = value.GetValue();
814 info.value = val_str ? val_str : "";
815
816 // Get summary (for strings, etc.)
817 const char *summary = value.GetSummary();
818 info.summary = summary ? summary : "";
819
820 // Get address
821 info.address = value.GetLoadAddress();
822
823 // Check for errors
824 lldb::SBError error = value.GetError();
825 if (error.Fail()) {
826 info.is_valid = false;
827 }
828
829 // Expand children if requested
830 if (expand_depth > 0 && info.is_aggregate) {
831 uint32_t num_children = value.GetNumChildren();
832 // Limit to reasonable number of children
833 if (num_children > 100) {
834 num_children = 100;
835 }
836
837 info.children.reserve(num_children);
838 for (uint32_t i = 0; i < num_children; i++) {
839 lldb::SBValue child = value.GetChildAtIndex(i);
840 if (child.IsValid()) {
841 info.children.push_back(valueToInfo(child, expand_depth - 1));
842 }
843 }
844 }
845
846 return info;
847}
848
849BreakpointInfo LLDBController::breakpointToInfo(lldb::SBBreakpoint bp) {
850 BreakpointInfo info;
851 info.id = static_cast<int32_t>(bp.GetID());
852 info.enabled = bp.IsEnabled();
853 info.hit_count = bp.GetHitCount();
854 info.line = 0; // Initialize to 0 for unresolved breakpoints
855 info.file = ""; // Initialize to empty for unresolved breakpoints
856
857 const char *condition = bp.GetCondition();
858 info.condition = condition ? condition : "";
859
860 // Get location info from first location
861 info.resolved = (bp.GetNumResolvedLocations() > 0);
862
863 if (bp.GetNumLocations() > 0) {
864 lldb::SBBreakpointLocation loc = bp.GetLocationAtIndex(0);
865 if (loc.IsValid()) {
866 lldb::SBAddress addr = loc.GetAddress();
867 if (addr.IsValid()) {
868 lldb::SBLineEntry line_entry = addr.GetLineEntry();
869 if (line_entry.IsValid()) {
870 lldb::SBFileSpec file_spec = line_entry.GetFileSpec();
871 if (file_spec.IsValid()) {
872 char path[1024];
873 file_spec.GetPath(path, sizeof(path));
874 info.file = path;
875 }
876 info.line = line_entry.GetLine();
877 }
878 }
879 }
880 }
881
882 return info;
883}
884
885} // namespace ascii_query
int thread_id
int32_t setBreakpoint(const std::string &file, uint32_t line, const std::string &condition="")
Set a breakpoint at file:line.
std::optional< VariableInfo > evaluateExpression(const std::string &expression, uint32_t frame_index=0) const
Evaluate an expression in the current context.
bool attachByName(const std::string &process_name, bool wait_for=false)
Attach to a process by name.
bool removeBreakpoint(int32_t breakpoint_id)
Remove a breakpoint.
bool stepInto()
Single step the current thread (step into)
pid_t targetPid() const
Get the PID of the attached process.
std::string targetName() const
Get the name of the attached process.
bool selectThread(uint64_t thread_id)
Select a thread by ID.
std::optional< VariableInfo > readVariable(const std::string &name, uint32_t frame_index=0, uint32_t expand_depth=0) const
Read a variable from the current frame.
const std::string & lastError() const
Get the last error message.
std::vector< VariableInfo > listVariables(uint32_t frame_index=0, bool include_args=true, bool include_locals=true, bool include_statics=false) const
List all variables in scope at a frame.
bool initialize()
Initialize LLDB. Must be called before any other methods.
void shutdown()
Shutdown LLDB and release resources.
bool stop()
Stop the target process.
bool isAttached() const
Check if attached to a process.
std::vector< ThreadInfo > getThreads() const
Get list of all threads.
std::optional< FrameInfo > getFrame(uint32_t frame_index) const
Get a specific frame by index.
std::vector< BreakpointInfo > getBreakpoints() const
Get all breakpoints.
bool resume()
Resume the target process.
std::vector< FrameInfo > getFrames(uint32_t max_frames=0) const
Get stack frames for the selected thread.
bool stepOver()
Step over the current line.
std::optional< BreakpointInfo > getBreakpoint(int32_t breakpoint_id) const
Get information about a specific breakpoint.
bool stepOut()
Step out of the current function.
void detach()
Detach from the current process.
bool attach(pid_t pid)
Attach to a process by PID.
ProcessState state() const
Get the current process state.
std::optional< ThreadInfo > getSelectedThread() const
Get the currently selected thread.
bool waitForBreakpoint(uint32_t timeout_ms=0)
Wait for any breakpoint to be hit.
LLDB process attachment and control wrapper.
ProcessState
Process state enumeration.
@ Exited
Process has exited.
@ Crashed
Process crashed.
@ Detached
Detached from process.
@ Invalid
No valid process attached.
@ Running
Process is running normally.
@ Stopped
Process is stopped (breakpoint, signal, etc.)