1// Licensed to the .NET Foundation under one or more agreements.
2// The .NET Foundation licenses this file to you under the MIT license.
3// See the LICENSE file in the project root for more information.
4
5#include <cstdarg>
6#include <cstdlib>
7#include "sosplugin.h"
8#include <string.h>
9#include <string>
10
11#define CONVERT_FROM_SIGN_EXTENDED(offset) ((ULONG_PTR)(offset))
12
13ULONG g_currentThreadIndex = -1;
14ULONG g_currentThreadSystemId = -1;
15char *g_coreclrDirectory;
16
17LLDBServices::LLDBServices(lldb::SBDebugger &debugger, lldb::SBCommandReturnObject &returnObject, lldb::SBProcess *process, lldb::SBThread *thread) :
18 m_ref(1),
19 m_debugger(debugger),
20 m_returnObject(returnObject),
21 m_currentProcess(process),
22 m_currentThread(thread)
23{
24 returnObject.SetStatus(lldb::eReturnStatusSuccessFinishResult);
25}
26
27LLDBServices::~LLDBServices()
28{
29}
30
31//----------------------------------------------------------------------------
32// IUnknown
33//----------------------------------------------------------------------------
34
35HRESULT
36LLDBServices::QueryInterface(
37 REFIID InterfaceId,
38 PVOID* Interface
39 )
40{
41 if (InterfaceId == __uuidof(IUnknown) ||
42 InterfaceId == __uuidof(ILLDBServices))
43 {
44 *Interface = (ILLDBServices*)this;
45 AddRef();
46 return S_OK;
47 }
48 else
49 {
50 *Interface = NULL;
51 return E_NOINTERFACE;
52 }
53}
54
55ULONG
56LLDBServices::AddRef()
57{
58 LONG ref = InterlockedIncrement(&m_ref);
59 return ref;
60}
61
62ULONG
63LLDBServices::Release()
64{
65 LONG ref = InterlockedDecrement(&m_ref);
66 if (ref == 0)
67 {
68 delete this;
69 }
70 return ref;
71}
72
73//----------------------------------------------------------------------------
74// ILLDBServices
75//----------------------------------------------------------------------------
76
77PCSTR
78LLDBServices::GetCoreClrDirectory()
79{
80 return g_coreclrDirectory;
81}
82
83DWORD_PTR
84LLDBServices::GetExpression(
85 PCSTR exp)
86{
87 if (exp == nullptr)
88 {
89 return 0;
90 }
91
92 lldb::SBFrame frame = GetCurrentFrame();
93 if (!frame.IsValid())
94 {
95 return 0;
96 }
97
98 DWORD_PTR result = 0;
99 lldb::SBError error;
100 std::string str;
101
102 // To be compatible with windbg/dbgeng, we need to emulate the default
103 // hex radix (because sos prints addresses and other hex values without
104 // the 0x) by first prepending 0x and if that fails use the actual
105 // undecorated expression.
106 str.append("0x");
107 str.append(exp);
108
109 result = GetExpression(frame, error, str.c_str());
110 if (error.Fail())
111 {
112 result = GetExpression(frame, error, exp);
113 }
114
115 return result;
116}
117
118// Internal function
119DWORD_PTR
120LLDBServices::GetExpression(
121 /* const */ lldb::SBFrame& frame,
122 lldb::SBError& error,
123 PCSTR exp)
124{
125 DWORD_PTR result = 0;
126
127 lldb::SBValue value = frame.EvaluateExpression(exp, lldb::eNoDynamicValues);
128 if (value.IsValid())
129 {
130 result = value.GetValueAsUnsigned(error);
131 }
132
133 return result;
134}
135
136//
137// lldb doesn't have a way or API to unwind an arbitrary context (IP, SP)
138// and return the next frame so we have to stick with the native frames
139// lldb has found and find the closest frame to the incoming context SP.
140//
141HRESULT
142LLDBServices::VirtualUnwind(
143 DWORD threadID,
144 ULONG32 contextSize,
145 PBYTE context)
146{
147 lldb::SBProcess process;
148 lldb::SBThread thread;
149
150 if (context == NULL || contextSize < sizeof(DT_CONTEXT))
151 {
152 return E_INVALIDARG;
153 }
154
155 process = GetCurrentProcess();
156 if (!process.IsValid())
157 {
158 return E_FAIL;
159 }
160
161 thread = process.GetThreadByID(threadID);
162 if (!thread.IsValid())
163 {
164 return E_FAIL;
165 }
166
167 DT_CONTEXT *dtcontext = (DT_CONTEXT*)context;
168 lldb::SBFrame frameFound;
169
170#ifdef DBG_TARGET_AMD64
171 DWORD64 spToFind = dtcontext->Rsp;
172#elif DBG_TARGET_X86
173 DWORD spToFind = dtcontext->Esp;
174#elif DBG_TARGET_ARM
175 DWORD spToFind = dtcontext->Sp;
176#elif DBG_TARGET_ARM64
177 DWORD64 spToFind = dtcontext->Sp;
178#else
179#error "spToFind undefined for this platform"
180#endif
181
182 int numFrames = thread.GetNumFrames();
183 for (int i = 0; i < numFrames; i++)
184 {
185 lldb::SBFrame frame = thread.GetFrameAtIndex(i);
186 if (!frame.IsValid())
187 {
188 break;
189 }
190 lldb::addr_t sp = frame.GetSP();
191
192 if ((i + 1) < numFrames)
193 {
194 lldb::SBFrame frameNext = thread.GetFrameAtIndex(i + 1);
195 if (frameNext.IsValid())
196 {
197 lldb::addr_t spNext = frameNext.GetSP();
198
199 // An exact match of the current frame's SP would be nice
200 // but sometimes the incoming context is between lldb frames
201 if (spToFind >= sp && spToFind < spNext)
202 {
203 frameFound = frameNext;
204 break;
205 }
206 }
207 }
208 }
209
210 if (!frameFound.IsValid())
211 {
212 return E_FAIL;
213 }
214
215 GetContextFromFrame(frameFound, dtcontext);
216
217 return S_OK;
218}
219
220bool
221ExceptionBreakpointCallback(
222 void *baton,
223 lldb::SBProcess &process,
224 lldb::SBThread &thread,
225 lldb::SBBreakpointLocation &location)
226{
227 lldb::SBDebugger debugger = process.GetTarget().GetDebugger();
228
229 // Send the normal and error output to stdout/stderr since we
230 // don't have a return object from the command interpreter.
231 lldb::SBCommandReturnObject result;
232 result.SetImmediateOutputFile(stdout);
233 result.SetImmediateErrorFile(stderr);
234
235 // Save the process and thread to be used by the current process/thread
236 // helper functions.
237 LLDBServices* client = new LLDBServices(debugger, result, &process, &thread);
238 return ((PFN_EXCEPTION_CALLBACK)baton)(client) == S_OK;
239}
240
241lldb::SBBreakpoint g_exceptionbp;
242
243HRESULT
244LLDBServices::SetExceptionCallback(
245 PFN_EXCEPTION_CALLBACK callback)
246{
247 if (!g_exceptionbp.IsValid())
248 {
249 lldb::SBTarget target = m_debugger.GetSelectedTarget();
250 if (!target.IsValid())
251 {
252 return E_FAIL;
253 }
254 lldb::SBBreakpoint exceptionbp = target.BreakpointCreateForException(lldb::LanguageType::eLanguageTypeC_plus_plus, false, true);
255 if (!exceptionbp.IsValid())
256 {
257 return E_FAIL;
258 }
259#ifdef FLAGS_ANONYMOUS_ENUM
260 exceptionbp.AddName("DoNotDeleteOrDisable");
261#endif
262 exceptionbp.SetCallback(ExceptionBreakpointCallback, (void *)callback);
263 g_exceptionbp = exceptionbp;
264 }
265 return S_OK;
266}
267
268HRESULT
269LLDBServices::ClearExceptionCallback()
270{
271 if (g_exceptionbp.IsValid())
272 {
273 lldb::SBTarget target = m_debugger.GetSelectedTarget();
274 if (!target.IsValid())
275 {
276 return E_FAIL;
277 }
278 target.BreakpointDelete(g_exceptionbp.GetID());
279 g_exceptionbp = lldb::SBBreakpoint();
280 }
281 return S_OK;
282}
283
284//----------------------------------------------------------------------------
285// IDebugControl2
286//----------------------------------------------------------------------------
287
288// Checks for a user interrupt, such a Ctrl-C
289// or stop button.
290// This method is reentrant.
291HRESULT
292LLDBServices::GetInterrupt()
293{
294 return E_FAIL;
295}
296
297// Sends output through clients
298// output callbacks if the mask is allowed
299// by the current output control mask and
300// according to the output distribution
301// settings.
302HRESULT
303LLDBServices::Output(
304 ULONG mask,
305 PCSTR format,
306 ...)
307{
308 va_list args;
309 va_start (args, format);
310 HRESULT result = OutputVaList(mask, format, args);
311 va_end (args);
312 return result;
313}
314
315HRESULT
316LLDBServices::OutputVaList(
317 ULONG mask,
318 PCSTR format,
319 va_list args)
320{
321 HRESULT result = S_OK;
322 char str[1024];
323
324 va_list args_copy;
325 va_copy (args_copy, args);
326
327 // Try and format our string into a fixed buffer first and see if it fits
328 size_t length = ::vsnprintf(str, sizeof(str), format, args);
329 if (length < sizeof(str))
330 {
331 OutputString(mask, str);
332 }
333 else
334 {
335 // Our stack buffer wasn't big enough to contain the entire formatted
336 // string, so lets let vasprintf create the string for us!
337 char *str_ptr = nullptr;
338 length = ::vasprintf(&str_ptr, format, args_copy);
339 if (str_ptr)
340 {
341 OutputString(mask, str_ptr);
342 ::free (str_ptr);
343 }
344 else
345 {
346 result = E_FAIL;
347 }
348 }
349
350 va_end (args_copy);
351
352 return result;
353}
354
355// The following methods allow direct control
356// over the distribution of the given output
357// for situations where something other than
358// the default is desired. These methods require
359// extra work in the engine so they should
360// only be used when necessary.
361HRESULT
362LLDBServices::ControlledOutput(
363 ULONG outputControl,
364 ULONG mask,
365 PCSTR format,
366 ...)
367{
368 va_list args;
369 va_start (args, format);
370 HRESULT result = ControlledOutputVaList(outputControl, mask, format, args);
371 va_end (args);
372 return result;
373}
374
375HRESULT
376LLDBServices::ControlledOutputVaList(
377 ULONG outputControl,
378 ULONG mask,
379 PCSTR format,
380 va_list args)
381{
382 return OutputVaList(mask, format, args);
383}
384
385// Returns information about the debuggee such
386// as user vs. kernel, dump vs. live, etc.
387HRESULT
388LLDBServices::GetDebuggeeType(
389 PULONG debugClass,
390 PULONG qualifier)
391{
392 *debugClass = DEBUG_CLASS_USER_WINDOWS;
393 *qualifier = 0;
394 return S_OK;
395}
396
397// Returns the page size for the currently executing
398// processor context. The page size may vary between
399// processor types.
400HRESULT
401LLDBServices::GetPageSize(
402 PULONG size)
403{
404 *size = 4096;
405 return S_OK;
406}
407
408HRESULT
409LLDBServices::GetExecutingProcessorType(
410 PULONG type)
411{
412#ifdef DBG_TARGET_AMD64
413 *type = IMAGE_FILE_MACHINE_AMD64;
414#elif DBG_TARGET_ARM
415 *type = IMAGE_FILE_MACHINE_ARMNT;
416#elif DBG_TARGET_ARM64
417 *type = IMAGE_FILE_MACHINE_ARM64;
418#elif DBG_TARGET_X86
419 *type = IMAGE_FILE_MACHINE_I386;
420#else
421#error "Unsupported target"
422#endif
423 return S_OK;
424}
425
426HRESULT
427LLDBServices::Execute(
428 ULONG outputControl,
429 PCSTR command,
430 ULONG flags)
431{
432 lldb::SBCommandInterpreter interpreter = m_debugger.GetCommandInterpreter();
433
434 lldb::SBCommandReturnObject result;
435 lldb::ReturnStatus status = interpreter.HandleCommand(command, result);
436
437 return status <= lldb::eReturnStatusSuccessContinuingResult ? S_OK : E_FAIL;
438}
439
440// PAL raise exception function and exception record pointer variable name
441// See coreclr\src\pal\src\exception\seh-unwind.cpp for the details. This
442// function depends on RtlpRaisException not being inlined or optimized.
443#define FUNCTION_NAME "RtlpRaiseException"
444#define VARIABLE_NAME "ExceptionRecord"
445
446HRESULT
447LLDBServices::GetLastEventInformation(
448 PULONG type,
449 PULONG processId,
450 PULONG threadId,
451 PVOID extraInformation,
452 ULONG extraInformationSize,
453 PULONG extraInformationUsed,
454 PSTR description,
455 ULONG descriptionSize,
456 PULONG descriptionUsed)
457{
458 if (extraInformationSize < sizeof(DEBUG_LAST_EVENT_INFO_EXCEPTION) ||
459 type == NULL || processId == NULL || threadId == NULL || extraInformationUsed == NULL)
460 {
461 return E_INVALIDARG;
462 }
463
464 *type = DEBUG_EVENT_EXCEPTION;
465 *processId = 0;
466 *threadId = 0;
467 *extraInformationUsed = sizeof(DEBUG_LAST_EVENT_INFO_EXCEPTION);
468
469 DEBUG_LAST_EVENT_INFO_EXCEPTION *pdle = (DEBUG_LAST_EVENT_INFO_EXCEPTION *)extraInformation;
470 pdle->FirstChance = 1;
471
472 lldb::SBProcess process = GetCurrentProcess();
473 if (!process.IsValid())
474 {
475 return E_FAIL;
476 }
477 lldb::SBThread thread = GetCurrentThread();
478 if (!thread.IsValid())
479 {
480 return E_FAIL;
481 }
482
483 *processId = process.GetProcessID();
484 *threadId = thread.GetThreadID();
485
486 // Enumerate each stack frame at the special "throw"
487 // breakpoint and find the raise exception function
488 // with the exception record parameter.
489 int numFrames = thread.GetNumFrames();
490 for (int i = 0; i < numFrames; i++)
491 {
492 lldb::SBFrame frame = thread.GetFrameAtIndex(i);
493 if (!frame.IsValid())
494 {
495 break;
496 }
497
498 const char *functionName = frame.GetFunctionName();
499 if (functionName == NULL || strncmp(functionName, FUNCTION_NAME, sizeof(FUNCTION_NAME) - 1) != 0)
500 {
501 continue;
502 }
503
504 lldb::SBValue exValue = frame.FindVariable(VARIABLE_NAME);
505 if (!exValue.IsValid())
506 {
507 break;
508 }
509
510 lldb::SBError error;
511 ULONG64 pExceptionRecord = exValue.GetValueAsUnsigned(error);
512 if (error.Fail())
513 {
514 break;
515 }
516
517 process.ReadMemory(pExceptionRecord, &pdle->ExceptionRecord, sizeof(pdle->ExceptionRecord), error);
518 if (error.Fail())
519 {
520 break;
521 }
522
523 return S_OK;
524 }
525
526 return E_FAIL;
527}
528
529HRESULT
530LLDBServices::Disassemble(
531 ULONG64 offset,
532 ULONG flags,
533 PSTR buffer,
534 ULONG bufferSize,
535 PULONG disassemblySize,
536 PULONG64 endOffset)
537{
538 lldb::SBInstruction instruction;
539 lldb::SBInstructionList list;
540 lldb::SBTarget target;
541 lldb::SBAddress address;
542 lldb::SBError error;
543 lldb::SBData data;
544 std::string str;
545 HRESULT hr = S_OK;
546 ULONG size = 0;
547 uint8_t byte;
548 int cch;
549
550 // lldb doesn't expect sign-extended address
551 offset = CONVERT_FROM_SIGN_EXTENDED(offset);
552
553 if (buffer == NULL)
554 {
555 hr = E_INVALIDARG;
556 goto exit;
557 }
558 *buffer = 0;
559
560 target = m_debugger.GetSelectedTarget();
561 if (!target.IsValid())
562 {
563 hr = E_INVALIDARG;
564 goto exit;
565 }
566 address = target.ResolveLoadAddress(offset);
567 if (!address.IsValid())
568 {
569 hr = E_INVALIDARG;
570 goto exit;
571 }
572 list = target.ReadInstructions(address, 1, "intel");
573 if (!list.IsValid())
574 {
575 hr = E_FAIL;
576 goto exit;
577 }
578 instruction = list.GetInstructionAtIndex(0);
579 if (!instruction.IsValid())
580 {
581 hr = E_FAIL;
582 goto exit;
583 }
584 cch = snprintf(buffer, bufferSize, "%016llx ", (unsigned long long)offset);
585 buffer += cch;
586 bufferSize -= cch;
587
588 size = instruction.GetByteSize();
589 data = instruction.GetData(target);
590 for (int i = 0; i < size && bufferSize > 0; i++)
591 {
592 byte = data.GetUnsignedInt8(error, i);
593 if (error.Fail())
594 {
595 hr = E_FAIL;
596 goto exit;
597 }
598 cch = snprintf(buffer, bufferSize, "%02x", byte);
599 buffer += cch;
600 bufferSize -= cch;
601 }
602 // Pad the data bytes to 16 chars
603 cch = size * 2;
604 while (bufferSize > 0)
605 {
606 *buffer++ = ' ';
607 bufferSize--;
608 if (++cch >= 21)
609 break;
610 }
611
612 cch = snprintf(buffer, bufferSize, "%s", instruction.GetMnemonic(target));
613 buffer += cch;
614 bufferSize -= cch;
615
616 // Pad the mnemonic to 8 chars
617 while (bufferSize > 0)
618 {
619 *buffer++ = ' ';
620 bufferSize--;
621 if (++cch >= 8)
622 break;
623 }
624 snprintf(buffer, bufferSize, "%s\n", instruction.GetOperands(target));
625
626exit:
627 if (disassemblySize != NULL)
628 {
629 *disassemblySize = size;
630 }
631 if (endOffset != NULL)
632 {
633 *endOffset = offset + size;
634 }
635 return hr;
636}
637
638// Internal output string function
639void
640LLDBServices::OutputString(
641 ULONG mask,
642 PCSTR str)
643{
644 if (mask == DEBUG_OUTPUT_ERROR)
645 {
646 m_returnObject.SetStatus(lldb::eReturnStatusFailed);
647 }
648 // Can not use AppendMessage or AppendWarning because they add a newline. SetError
649 // can not be used for DEBUG_OUTPUT_ERROR mask because it caches the error strings
650 // seperately from the normal output so error/normal texts are not intermixed
651 // correctly.
652 m_returnObject.Printf("%s", str);
653}
654
655//----------------------------------------------------------------------------
656// IDebugControl4
657//----------------------------------------------------------------------------
658
659HRESULT
660LLDBServices::GetContextStackTrace(
661 PVOID startContext,
662 ULONG startContextSize,
663 PDEBUG_STACK_FRAME frames,
664 ULONG framesSize,
665 PVOID frameContexts,
666 ULONG frameContextsSize,
667 ULONG frameContextsEntrySize,
668 PULONG framesFilled)
669{
670 DT_CONTEXT *currentContext = (DT_CONTEXT*)frameContexts;
671 PDEBUG_STACK_FRAME currentFrame = frames;
672 lldb::SBThread thread;
673 lldb::SBFrame frame;
674 ULONG cFrames = 0;
675 HRESULT hr = S_OK;
676
677 // Doesn't support a starting context
678 if (startContext != NULL || frames == NULL || frameContexts == NULL || frameContextsEntrySize != sizeof(DT_CONTEXT))
679 {
680 hr = E_INVALIDARG;
681 goto exit;
682 }
683
684 thread = GetCurrentThread();
685 if (!thread.IsValid())
686 {
687 hr = E_FAIL;
688 goto exit;
689 }
690
691 frame = thread.GetFrameAtIndex(0);
692 for (int i = 0; i < thread.GetNumFrames(); i++)
693 {
694 if (!frame.IsValid() || (cFrames > framesSize) || ((char *)currentContext > ((char *)frameContexts + frameContextsSize)))
695 {
696 break;
697 }
698 lldb::SBFrame framePrevious;
699 lldb::SBFrame frameNext;
700
701 currentFrame->InstructionOffset = frame.GetPC();
702 currentFrame->StackOffset = frame.GetSP();
703
704 currentFrame->FuncTableEntry = 0;
705 currentFrame->Params[0] = 0;
706 currentFrame->Params[1] = 0;
707 currentFrame->Params[2] = 0;
708 currentFrame->Params[3] = 0;
709 currentFrame->Virtual = i == 0 ? TRUE : FALSE;
710 currentFrame->FrameNumber = frame.GetFrameID();
711
712 frameNext = thread.GetFrameAtIndex(i + 1);
713 if (frameNext.IsValid())
714 {
715 currentFrame->ReturnOffset = frameNext.GetPC();
716 }
717
718 if (framePrevious.IsValid())
719 {
720 currentFrame->FrameOffset = framePrevious.GetSP();
721 }
722 else
723 {
724 currentFrame->FrameOffset = frame.GetSP();
725 }
726
727 GetContextFromFrame(frame, currentContext);
728
729 framePrevious = frame;
730 frame = frameNext;
731 currentContext++;
732 currentFrame++;
733 cFrames++;
734 }
735
736exit:
737 if (framesFilled != NULL)
738 {
739 *framesFilled = cFrames;
740 }
741 return hr;
742}
743
744//----------------------------------------------------------------------------
745// IDebugDataSpaces
746//----------------------------------------------------------------------------
747
748HRESULT
749LLDBServices::ReadVirtual(
750 ULONG64 offset,
751 PVOID buffer,
752 ULONG bufferSize,
753 PULONG bytesRead)
754{
755 lldb::SBError error;
756 size_t read = 0;
757
758 // lldb doesn't expect sign-extended address
759 offset = CONVERT_FROM_SIGN_EXTENDED(offset);
760
761 lldb::SBProcess process = GetCurrentProcess();
762 if (!process.IsValid())
763 {
764 goto exit;
765 }
766
767 read = process.ReadMemory(offset, buffer, bufferSize, error);
768
769exit:
770 if (bytesRead)
771 {
772 *bytesRead = read;
773 }
774 return error.Success() || (read != 0) ? S_OK : E_FAIL;
775}
776
777HRESULT
778LLDBServices::WriteVirtual(
779 ULONG64 offset,
780 PVOID buffer,
781 ULONG bufferSize,
782 PULONG bytesWritten)
783{
784 lldb::SBError error;
785 size_t written = 0;
786
787 // lldb doesn't expect sign-extended address
788 offset = CONVERT_FROM_SIGN_EXTENDED(offset);
789
790 lldb::SBProcess process = GetCurrentProcess();
791 if (!process.IsValid())
792 {
793 goto exit;
794 }
795
796 written = process.WriteMemory(offset, buffer, bufferSize, error);
797
798exit:
799 if (bytesWritten)
800 {
801 *bytesWritten = written;
802 }
803 return error.Success() || (written != 0) ? S_OK : E_FAIL;
804}
805
806//----------------------------------------------------------------------------
807// IDebugSymbols
808//----------------------------------------------------------------------------
809
810HRESULT
811LLDBServices::GetSymbolOptions(
812 PULONG options)
813{
814 *options = SYMOPT_LOAD_LINES;
815 return S_OK;
816}
817
818HRESULT
819LLDBServices::GetNameByOffset(
820 ULONG64 offset,
821 PSTR nameBuffer,
822 ULONG nameBufferSize,
823 PULONG nameSize,
824 PULONG64 displacement)
825{
826 ULONG64 disp = DEBUG_INVALID_OFFSET;
827 HRESULT hr = S_OK;
828
829 lldb::SBTarget target;
830 lldb::SBAddress address;
831 lldb::SBModule module;
832 lldb::SBFileSpec file;
833 lldb::SBSymbol symbol;
834 std::string str;
835
836 // lldb doesn't expect sign-extended address
837 offset = CONVERT_FROM_SIGN_EXTENDED(offset);
838
839 target = m_debugger.GetSelectedTarget();
840 if (!target.IsValid())
841 {
842 hr = E_FAIL;
843 goto exit;
844 }
845
846 address = target.ResolveLoadAddress(offset);
847 if (!address.IsValid())
848 {
849 hr = E_INVALIDARG;
850 goto exit;
851 }
852
853 module = address.GetModule();
854 if (!module.IsValid())
855 {
856 hr = E_FAIL;
857 goto exit;
858 }
859
860 file = module.GetFileSpec();
861 if (file.IsValid())
862 {
863 str.append(file.GetFilename());
864 }
865
866 symbol = address.GetSymbol();
867 if (symbol.IsValid())
868 {
869 lldb::SBAddress startAddress = symbol.GetStartAddress();
870 disp = address.GetOffset() - startAddress.GetOffset();
871
872 const char *name = symbol.GetName();
873 if (name)
874 {
875 if (file.IsValid())
876 {
877 str.append("!");
878 }
879 str.append(name);
880 }
881 }
882
883 str.append(1, '\0');
884
885exit:
886 if (nameSize)
887 {
888 *nameSize = str.length();
889 }
890 if (nameBuffer)
891 {
892 str.copy(nameBuffer, nameBufferSize);
893 }
894 if (displacement)
895 {
896 *displacement = disp;
897 }
898 return hr;
899}
900
901HRESULT
902LLDBServices::GetNumberModules(
903 PULONG loaded,
904 PULONG unloaded)
905{
906 ULONG numModules = 0;
907 HRESULT hr = S_OK;
908
909 lldb::SBTarget target = m_debugger.GetSelectedTarget();
910 if (!target.IsValid())
911 {
912 hr = E_FAIL;
913 goto exit;
914 }
915
916 numModules = target.GetNumModules();
917
918exit:
919 if (loaded)
920 {
921 *loaded = numModules;
922 }
923 if (unloaded)
924 {
925 *unloaded = 0;
926 }
927 return hr;
928}
929
930HRESULT LLDBServices::GetModuleByIndex(
931 ULONG index,
932 PULONG64 base)
933{
934 ULONG64 moduleBase = UINT64_MAX;
935
936 lldb::SBTarget target;
937 lldb::SBModule module;
938
939 target = m_debugger.GetSelectedTarget();
940 if (!target.IsValid())
941 {
942 goto exit;
943 }
944
945 module = target.GetModuleAtIndex(index);
946 if (!module.IsValid())
947 {
948 goto exit;
949 }
950
951 moduleBase = GetModuleBase(target, module);
952
953exit:
954 if (base)
955 {
956 *base = moduleBase;
957 }
958 return moduleBase == UINT64_MAX ? E_FAIL : S_OK;
959}
960
961HRESULT
962LLDBServices::GetModuleByModuleName(
963 PCSTR name,
964 ULONG startIndex,
965 PULONG index,
966 PULONG64 base)
967{
968 ULONG64 moduleBase = UINT64_MAX;
969 ULONG moduleIndex = UINT32_MAX;
970
971 lldb::SBTarget target;
972 lldb::SBModule module;
973 lldb::SBFileSpec fileSpec;
974 fileSpec.SetFilename(name);
975
976 target = m_debugger.GetSelectedTarget();
977 if (!target.IsValid())
978 {
979 goto exit;
980 }
981
982 module = target.FindModule(fileSpec);
983 if (!module.IsValid())
984 {
985 goto exit;
986 }
987
988 moduleBase = GetModuleBase(target, module);
989
990 if (index)
991 {
992 int numModules = target.GetNumModules();
993 for (int mi = startIndex; mi < numModules; mi++)
994 {
995 lldb::SBModule mod = target.GetModuleAtIndex(mi);
996 if (module == mod)
997 {
998 moduleIndex = mi;
999 break;
1000 }
1001 }
1002 }
1003
1004exit:
1005 if (index)
1006 {
1007 *index = moduleIndex;
1008 }
1009 if (base)
1010 {
1011 *base = moduleBase;
1012 }
1013 return moduleBase == UINT64_MAX ? E_FAIL : S_OK;
1014}
1015
1016HRESULT
1017LLDBServices::GetModuleByOffset(
1018 ULONG64 offset,
1019 ULONG startIndex,
1020 PULONG index,
1021 PULONG64 base)
1022{
1023 ULONG64 moduleBase = UINT64_MAX;
1024 ULONG moduleIndex = UINT32_MAX;
1025
1026 lldb::SBTarget target;
1027 int numModules;
1028
1029 // lldb doesn't expect sign-extended address
1030 offset = CONVERT_FROM_SIGN_EXTENDED(offset);
1031
1032 target = m_debugger.GetSelectedTarget();
1033 if (!target.IsValid())
1034 {
1035 goto exit;
1036 }
1037
1038 numModules = target.GetNumModules();
1039 for (int mi = startIndex; mi < numModules; mi++)
1040 {
1041 lldb::SBModule module = target.GetModuleAtIndex(mi);
1042
1043 int numSections = module.GetNumSections();
1044 for (int si = 0; si < numSections; si++)
1045 {
1046 lldb::SBSection section = module.GetSectionAtIndex(si);
1047 if (section.IsValid())
1048 {
1049 lldb::addr_t baseAddress = section.GetLoadAddress(target);
1050 if (baseAddress != LLDB_INVALID_ADDRESS)
1051 {
1052 if (offset > baseAddress)
1053 {
1054 if ((offset - baseAddress) < section.GetByteSize())
1055 {
1056 moduleIndex = mi;
1057 moduleBase = baseAddress - section.GetFileOffset();
1058 goto exit;
1059 }
1060 }
1061 }
1062 }
1063 }
1064 }
1065
1066exit:
1067 if (index)
1068 {
1069 *index = moduleIndex;
1070 }
1071 if (base)
1072 {
1073 *base = moduleBase;
1074 }
1075 return moduleBase == UINT64_MAX ? E_FAIL : S_OK;
1076}
1077
1078HRESULT
1079LLDBServices::GetModuleNames(
1080 ULONG index,
1081 ULONG64 base,
1082 PSTR imageNameBuffer,
1083 ULONG imageNameBufferSize,
1084 PULONG imageNameSize,
1085 PSTR moduleNameBuffer,
1086 ULONG moduleNameBufferSize,
1087 PULONG moduleNameSize,
1088 PSTR loadedImageNameBuffer,
1089 ULONG loadedImageNameBufferSize,
1090 PULONG loadedImageNameSize)
1091{
1092 lldb::SBTarget target;
1093 lldb::SBFileSpec fileSpec;
1094 HRESULT hr = S_OK;
1095
1096 // lldb doesn't expect sign-extended address
1097 base = CONVERT_FROM_SIGN_EXTENDED(base);
1098
1099 target = m_debugger.GetSelectedTarget();
1100 if (!target.IsValid())
1101 {
1102 hr = E_FAIL;
1103 goto exit;
1104 }
1105
1106 if (index != DEBUG_ANY_ID)
1107 {
1108 lldb::SBModule module = target.GetModuleAtIndex(index);
1109 if (module.IsValid())
1110 {
1111 fileSpec = module.GetFileSpec();
1112 }
1113 }
1114 else
1115 {
1116 int numModules = target.GetNumModules();
1117 for (int mi = 0; mi < numModules; mi++)
1118 {
1119 lldb::SBModule module = target.GetModuleAtIndex(mi);
1120 if (module.IsValid())
1121 {
1122 ULONG64 moduleBase = GetModuleBase(target, module);
1123 if (base == moduleBase)
1124 {
1125 fileSpec = module.GetFileSpec();
1126 break;
1127 }
1128 }
1129 }
1130 }
1131
1132 if (!fileSpec.IsValid())
1133 {
1134 hr = E_FAIL;
1135 goto exit;
1136 }
1137
1138exit:
1139 if (imageNameBuffer)
1140 {
1141 int size = fileSpec.GetPath(imageNameBuffer, imageNameBufferSize);
1142 if (imageNameSize)
1143 {
1144 *imageNameSize = size;
1145 }
1146 }
1147 if (moduleNameBuffer)
1148 {
1149 const char *fileName = fileSpec.GetFilename();
1150 if (fileName == NULL)
1151 {
1152 fileName = "";
1153 }
1154 stpncpy(moduleNameBuffer, fileName, moduleNameBufferSize);
1155 if (moduleNameSize)
1156 {
1157 *moduleNameSize = strlen(fileName);
1158 }
1159 }
1160 if (loadedImageNameBuffer)
1161 {
1162 int size = fileSpec.GetPath(loadedImageNameBuffer, loadedImageNameBufferSize);
1163 if (loadedImageNameSize)
1164 {
1165 *loadedImageNameSize = size;
1166 }
1167 }
1168 return hr;
1169}
1170
1171HRESULT
1172LLDBServices::GetLineByOffset(
1173 ULONG64 offset,
1174 PULONG fileLine,
1175 PSTR fileBuffer,
1176 ULONG fileBufferSize,
1177 PULONG fileSize,
1178 PULONG64 displacement)
1179{
1180 ULONG64 disp = DEBUG_INVALID_OFFSET;
1181 HRESULT hr = S_OK;
1182 ULONG line = 0;
1183
1184 lldb::SBTarget target;
1185 lldb::SBAddress address;
1186 lldb::SBFileSpec file;
1187 lldb::SBLineEntry lineEntry;
1188 std::string str;
1189
1190 // lldb doesn't expect sign-extended address
1191 offset = CONVERT_FROM_SIGN_EXTENDED(offset);
1192
1193 target = m_debugger.GetSelectedTarget();
1194 if (!target.IsValid())
1195 {
1196 hr = E_FAIL;
1197 goto exit;
1198 }
1199
1200 address = target.ResolveLoadAddress(offset);
1201 if (!address.IsValid())
1202 {
1203 hr = E_INVALIDARG;
1204 goto exit;
1205 }
1206
1207 if (displacement)
1208 {
1209 lldb::SBSymbol symbol = address.GetSymbol();
1210 if (symbol.IsValid())
1211 {
1212 lldb::SBAddress startAddress = symbol.GetStartAddress();
1213 disp = address.GetOffset() - startAddress.GetOffset();
1214 }
1215 }
1216
1217 lineEntry = address.GetLineEntry();
1218 if (!lineEntry.IsValid())
1219 {
1220 hr = E_FAIL;
1221 goto exit;
1222 }
1223
1224 line = lineEntry.GetLine();
1225 file = lineEntry.GetFileSpec();
1226 if (file.IsValid())
1227 {
1228 str.append(file.GetDirectory());
1229 str.append(1, '/');
1230 str.append(file.GetFilename());
1231 }
1232
1233 str.append(1, '\0');
1234
1235exit:
1236 if (fileLine)
1237 {
1238 *fileLine = line;
1239 }
1240 if (fileSize)
1241 {
1242 *fileSize = str.length();
1243 }
1244 if (fileBuffer)
1245 {
1246 str.copy(fileBuffer, fileBufferSize);
1247 }
1248 if (displacement)
1249 {
1250 *displacement = disp;
1251 }
1252 return hr;
1253}
1254
1255HRESULT
1256LLDBServices::GetSourceFileLineOffsets(
1257 PCSTR file,
1258 PULONG64 buffer,
1259 ULONG bufferLines,
1260 PULONG fileLines)
1261{
1262 if (fileLines != NULL)
1263 {
1264 *fileLines = (ULONG)-1;
1265 }
1266 return E_NOTIMPL;
1267}
1268
1269HRESULT
1270LLDBServices::FindSourceFile(
1271 ULONG startElement,
1272 PCSTR file,
1273 ULONG flags,
1274 PULONG foundElement,
1275 PSTR buffer,
1276 ULONG bufferSize,
1277 PULONG foundSize)
1278{
1279 return E_NOTIMPL;
1280}
1281
1282// Internal functions
1283PCSTR
1284LLDBServices::GetModuleDirectory(
1285 PCSTR name)
1286{
1287 lldb::SBTarget target = m_debugger.GetSelectedTarget();
1288 if (!target.IsValid())
1289 {
1290 return NULL;
1291 }
1292
1293 lldb::SBFileSpec fileSpec;
1294 fileSpec.SetFilename(name);
1295
1296 lldb::SBModule module = target.FindModule(fileSpec);
1297 if (!module.IsValid())
1298 {
1299 return NULL;
1300 }
1301
1302 return module.GetFileSpec().GetDirectory();
1303}
1304
1305ULONG64
1306LLDBServices::GetModuleBase(
1307 /* const */ lldb::SBTarget& target,
1308 /* const */ lldb::SBModule& module)
1309{
1310 // Find the first section with an valid base address
1311 int numSections = module.GetNumSections();
1312 for (int si = 0; si < numSections; si++)
1313 {
1314 lldb::SBSection section = module.GetSectionAtIndex(si);
1315 if (section.IsValid())
1316 {
1317 lldb::addr_t baseAddress = section.GetLoadAddress(target);
1318 if (baseAddress != LLDB_INVALID_ADDRESS)
1319 {
1320 return baseAddress - section.GetFileOffset();
1321 }
1322 }
1323 }
1324
1325 return UINT64_MAX;
1326}
1327
1328//----------------------------------------------------------------------------
1329// IDebugSystemObjects
1330//----------------------------------------------------------------------------
1331
1332HRESULT
1333LLDBServices::GetCurrentProcessId(
1334 PULONG id)
1335{
1336 if (id == NULL)
1337 {
1338 return E_INVALIDARG;
1339 }
1340
1341 lldb::SBProcess process = GetCurrentProcess();
1342 if (!process.IsValid())
1343 {
1344 *id = 0;
1345 return E_FAIL;
1346 }
1347
1348 *id = process.GetProcessID();
1349 return S_OK;
1350}
1351
1352HRESULT
1353LLDBServices::GetCurrentThreadId(
1354 PULONG id)
1355{
1356 if (id == NULL)
1357 {
1358 return E_INVALIDARG;
1359 }
1360
1361 lldb::SBThread thread = GetCurrentThread();
1362 if (!thread.IsValid())
1363 {
1364 *id = 0;
1365 return E_FAIL;
1366 }
1367
1368 // This is allow the a valid current TID to be returned to
1369 // workaround a bug in lldb on core dumps.
1370 if (g_currentThreadIndex != -1)
1371 {
1372 *id = g_currentThreadIndex;
1373 return S_OK;
1374 }
1375
1376 *id = thread.GetIndexID();
1377 return S_OK;
1378}
1379
1380HRESULT
1381LLDBServices::SetCurrentThreadId(
1382 ULONG id)
1383{
1384 lldb::SBProcess process = GetCurrentProcess();
1385 if (!process.IsValid())
1386 {
1387 return E_FAIL;
1388 }
1389
1390 if (!process.SetSelectedThreadByIndexID(id))
1391 {
1392 return E_FAIL;
1393 }
1394
1395 return S_OK;
1396}
1397
1398HRESULT
1399LLDBServices::GetCurrentThreadSystemId(
1400 PULONG sysId)
1401{
1402 if (sysId == NULL)
1403 {
1404 return E_INVALIDARG;
1405 }
1406
1407 lldb::SBThread thread = GetCurrentThread();
1408 if (!thread.IsValid())
1409 {
1410 *sysId = 0;
1411 return E_FAIL;
1412 }
1413
1414 // This is allow the a valid current TID to be returned to
1415 // workaround a bug in lldb on core dumps.
1416 if (g_currentThreadSystemId != -1)
1417 {
1418 *sysId = g_currentThreadSystemId;
1419 return S_OK;
1420 }
1421
1422 *sysId = thread.GetThreadID();
1423 return S_OK;
1424}
1425
1426HRESULT
1427LLDBServices::GetThreadIdBySystemId(
1428 ULONG sysId,
1429 PULONG threadId)
1430{
1431 HRESULT hr = E_FAIL;
1432 ULONG id = 0;
1433
1434 lldb::SBProcess process;
1435 lldb::SBThread thread;
1436
1437 if (threadId == NULL)
1438 {
1439 return E_INVALIDARG;
1440 }
1441
1442 process = GetCurrentProcess();
1443 if (!process.IsValid())
1444 {
1445 goto exit;
1446 }
1447
1448 // If we have a "fake" thread OS (system) id and a fake thread index,
1449 // we need to return fake thread index.
1450 if (g_currentThreadSystemId == sysId && g_currentThreadIndex != -1)
1451 {
1452 id = g_currentThreadIndex;
1453 }
1454 else
1455 {
1456 thread = process.GetThreadByID(sysId);
1457 if (!thread.IsValid())
1458 {
1459 goto exit;
1460 }
1461
1462 id = thread.GetIndexID();
1463 }
1464 hr = S_OK;
1465
1466exit:
1467 *threadId = id;
1468 return hr;
1469}
1470
1471HRESULT
1472LLDBServices::GetThreadContextById(
1473 /* in */ ULONG32 threadID,
1474 /* in */ ULONG32 contextFlags,
1475 /* in */ ULONG32 contextSize,
1476 /* out */ PBYTE context)
1477{
1478 lldb::SBProcess process;
1479 lldb::SBThread thread;
1480 lldb::SBFrame frame;
1481 DT_CONTEXT *dtcontext;
1482 HRESULT hr = E_FAIL;
1483
1484 if (context == NULL || contextSize < sizeof(DT_CONTEXT))
1485 {
1486 goto exit;
1487 }
1488 memset(context, 0, contextSize);
1489
1490 process = GetCurrentProcess();
1491 if (!process.IsValid())
1492 {
1493 goto exit;
1494 }
1495
1496 // If we have a "fake" thread OS (system) id and a fake thread index,
1497 // use the fake thread index to get the context.
1498 if (g_currentThreadSystemId == threadID && g_currentThreadIndex != -1)
1499 {
1500 thread = process.GetThreadByIndexID(g_currentThreadIndex);
1501 }
1502 else
1503 {
1504 thread = process.GetThreadByID(threadID);
1505 }
1506
1507 if (!thread.IsValid())
1508 {
1509 goto exit;
1510 }
1511
1512 frame = thread.GetFrameAtIndex(0);
1513 if (!frame.IsValid())
1514 {
1515 goto exit;
1516 }
1517
1518 dtcontext = (DT_CONTEXT*)context;
1519 dtcontext->ContextFlags = contextFlags;
1520
1521 GetContextFromFrame(frame, dtcontext);
1522 hr = S_OK;
1523
1524exit:
1525 return hr;
1526}
1527
1528// Internal function
1529void
1530LLDBServices::GetContextFromFrame(
1531 /* const */ lldb::SBFrame& frame,
1532 DT_CONTEXT *dtcontext)
1533{
1534#ifdef DBG_TARGET_AMD64
1535 dtcontext->Rip = frame.GetPC();
1536 dtcontext->Rsp = frame.GetSP();
1537 dtcontext->Rbp = frame.GetFP();
1538 dtcontext->EFlags = GetRegister(frame, "rflags");
1539
1540 dtcontext->Rax = GetRegister(frame, "rax");
1541 dtcontext->Rbx = GetRegister(frame, "rbx");
1542 dtcontext->Rcx = GetRegister(frame, "rcx");
1543 dtcontext->Rdx = GetRegister(frame, "rdx");
1544 dtcontext->Rsi = GetRegister(frame, "rsi");
1545 dtcontext->Rdi = GetRegister(frame, "rdi");
1546 dtcontext->R8 = GetRegister(frame, "r8");
1547 dtcontext->R9 = GetRegister(frame, "r9");
1548 dtcontext->R10 = GetRegister(frame, "r10");
1549 dtcontext->R11 = GetRegister(frame, "r11");
1550 dtcontext->R12 = GetRegister(frame, "r12");
1551 dtcontext->R13 = GetRegister(frame, "r13");
1552 dtcontext->R14 = GetRegister(frame, "r14");
1553 dtcontext->R15 = GetRegister(frame, "r15");
1554
1555 dtcontext->SegCs = GetRegister(frame, "cs");
1556 dtcontext->SegSs = GetRegister(frame, "ss");
1557 dtcontext->SegDs = GetRegister(frame, "ds");
1558 dtcontext->SegEs = GetRegister(frame, "es");
1559 dtcontext->SegFs = GetRegister(frame, "fs");
1560 dtcontext->SegGs = GetRegister(frame, "gs");
1561#elif DBG_TARGET_ARM
1562 dtcontext->Pc = frame.GetPC();
1563 dtcontext->Sp = frame.GetSP();
1564 dtcontext->Lr = GetRegister(frame, "lr");
1565 dtcontext->Cpsr = GetRegister(frame, "cpsr");
1566
1567 dtcontext->R0 = GetRegister(frame, "r0");
1568 dtcontext->R1 = GetRegister(frame, "r1");
1569 dtcontext->R2 = GetRegister(frame, "r2");
1570 dtcontext->R3 = GetRegister(frame, "r3");
1571 dtcontext->R4 = GetRegister(frame, "r4");
1572 dtcontext->R5 = GetRegister(frame, "r5");
1573 dtcontext->R6 = GetRegister(frame, "r6");
1574 dtcontext->R7 = GetRegister(frame, "r7");
1575 dtcontext->R8 = GetRegister(frame, "r8");
1576 dtcontext->R9 = GetRegister(frame, "r9");
1577 dtcontext->R10 = GetRegister(frame, "r10");
1578 dtcontext->R11 = GetRegister(frame, "r11");
1579 dtcontext->R12 = GetRegister(frame, "r12");
1580#elif DBG_TARGET_X86
1581 dtcontext->Eip = frame.GetPC();
1582 dtcontext->Esp = frame.GetSP();
1583 dtcontext->Ebp = frame.GetFP();
1584 dtcontext->EFlags = GetRegister(frame, "eflags");
1585
1586 dtcontext->Edi = GetRegister(frame, "edi");
1587 dtcontext->Esi = GetRegister(frame, "esi");
1588 dtcontext->Ebx = GetRegister(frame, "ebx");
1589 dtcontext->Edx = GetRegister(frame, "edx");
1590 dtcontext->Ecx = GetRegister(frame, "ecx");
1591 dtcontext->Eax = GetRegister(frame, "eax");
1592
1593 dtcontext->SegCs = GetRegister(frame, "cs");
1594 dtcontext->SegSs = GetRegister(frame, "ss");
1595 dtcontext->SegDs = GetRegister(frame, "ds");
1596 dtcontext->SegEs = GetRegister(frame, "es");
1597 dtcontext->SegFs = GetRegister(frame, "fs");
1598 dtcontext->SegGs = GetRegister(frame, "gs");
1599#endif
1600}
1601
1602// Internal function
1603DWORD_PTR
1604LLDBServices::GetRegister(
1605 /* const */ lldb::SBFrame& frame,
1606 const char *name)
1607{
1608 lldb::SBValue regValue = frame.FindRegister(name);
1609
1610 lldb::SBError error;
1611 DWORD_PTR result = regValue.GetValueAsUnsigned(error);
1612
1613 return result;
1614}
1615
1616//----------------------------------------------------------------------------
1617// IDebugRegisters
1618//----------------------------------------------------------------------------
1619
1620HRESULT
1621LLDBServices::GetValueByName(
1622 PCSTR name,
1623 PDWORD_PTR debugValue)
1624{
1625 lldb::SBFrame frame = GetCurrentFrame();
1626 if (!frame.IsValid())
1627 {
1628 *debugValue = 0;
1629 return E_FAIL;
1630 }
1631
1632 lldb::SBValue value = frame.FindRegister(name);
1633 if (!value.IsValid())
1634 {
1635 *debugValue = 0;
1636 return E_FAIL;
1637 }
1638
1639 *debugValue = value.GetValueAsUnsigned();
1640 return S_OK;
1641}
1642
1643HRESULT
1644LLDBServices::GetInstructionOffset(
1645 PULONG64 offset)
1646{
1647 lldb::SBFrame frame = GetCurrentFrame();
1648 if (!frame.IsValid())
1649 {
1650 *offset = 0;
1651 return E_FAIL;
1652 }
1653
1654 *offset = frame.GetPC();
1655 return S_OK;
1656}
1657
1658HRESULT
1659LLDBServices::GetStackOffset(
1660 PULONG64 offset)
1661{
1662 lldb::SBFrame frame = GetCurrentFrame();
1663 if (!frame.IsValid())
1664 {
1665 *offset = 0;
1666 return E_FAIL;
1667 }
1668
1669 *offset = frame.GetSP();
1670 return S_OK;
1671}
1672
1673HRESULT
1674LLDBServices::GetFrameOffset(
1675 PULONG64 offset)
1676{
1677 lldb::SBFrame frame = GetCurrentFrame();
1678 if (!frame.IsValid())
1679 {
1680 *offset = 0;
1681 return E_FAIL;
1682 }
1683
1684 *offset = frame.GetFP();
1685 return S_OK;
1686}
1687
1688//----------------------------------------------------------------------------
1689// Helper functions
1690//----------------------------------------------------------------------------
1691
1692lldb::SBProcess
1693LLDBServices::GetCurrentProcess()
1694{
1695 lldb::SBProcess process;
1696
1697 if (m_currentProcess == nullptr)
1698 {
1699 lldb::SBTarget target = m_debugger.GetSelectedTarget();
1700 if (target.IsValid())
1701 {
1702 process = target.GetProcess();
1703 }
1704 }
1705 else
1706 {
1707 process = *m_currentProcess;
1708 }
1709
1710 return process;
1711}
1712
1713lldb::SBThread
1714LLDBServices::GetCurrentThread()
1715{
1716 lldb::SBThread thread;
1717
1718 if (m_currentThread == nullptr)
1719 {
1720 lldb::SBProcess process = GetCurrentProcess();
1721 if (process.IsValid())
1722 {
1723 thread = process.GetSelectedThread();
1724 }
1725 }
1726 else
1727 {
1728 thread = *m_currentThread;
1729 }
1730
1731 return thread;
1732}
1733
1734lldb::SBFrame
1735LLDBServices::GetCurrentFrame()
1736{
1737 lldb::SBFrame frame;
1738
1739 lldb::SBThread thread = GetCurrentThread();
1740 if (thread.IsValid())
1741 {
1742 frame = thread.GetSelectedFrame();
1743 }
1744
1745 return frame;
1746}
1747