2010-06-09 00:52:24 +08:00
|
|
|
//===-- Debugger.cpp --------------------------------------------*- C++ -*-===//
|
|
|
|
//
|
|
|
|
// The LLVM Compiler Infrastructure
|
|
|
|
//
|
|
|
|
// This file is distributed under the University of Illinois Open Source
|
|
|
|
// License. See LICENSE.TXT for details.
|
|
|
|
//
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
|
2011-06-24 01:59:56 +08:00
|
|
|
#include "lldb/Core/Debugger.h"
|
|
|
|
|
2016-03-02 10:18:18 +08:00
|
|
|
// C Includes
|
|
|
|
// C++ Includes
|
2011-06-24 01:59:56 +08:00
|
|
|
#include <map>
|
2016-05-18 09:59:10 +08:00
|
|
|
#include <mutex>
|
2011-06-24 01:59:56 +08:00
|
|
|
|
2016-03-02 10:18:18 +08:00
|
|
|
// Other libraries and framework includes
|
2014-06-13 10:37:02 +08:00
|
|
|
#include "llvm/ADT/StringRef.h"
|
2016-03-02 10:18:18 +08:00
|
|
|
#include "llvm/Support/DynamicLibrary.h"
|
2011-06-30 06:27:15 +08:00
|
|
|
|
2016-03-02 10:18:18 +08:00
|
|
|
// Project includes
|
2015-02-05 06:00:53 +08:00
|
|
|
#include "lldb/Core/FormatEntity.h"
|
<rdar://problem/11757916>
Make breakpoint setting by file and line much more efficient by only looking for inlined breakpoint locations if we are setting a breakpoint in anything but a source implementation file. Implementing this complex for a many reasons. Turns out that parsing compile units lazily had some issues with respect to how we need to do things with DWARF in .o files. So the fixes in the checkin for this makes these changes:
- Add a new setting called "target.inline-breakpoint-strategy" which can be set to "never", "always", or "headers". "never" will never try and set any inlined breakpoints (fastest). "always" always looks for inlined breakpoint locations (slowest, but most accurate). "headers", which is the default setting, will only look for inlined breakpoint locations if the breakpoint is set in what are consudered to be header files, which is realy defined as "not in an implementation source file".
- modify the breakpoint setting by file and line to check the current "target.inline-breakpoint-strategy" setting and act accordingly
- Modify compile units to be able to get their language and other info lazily. This allows us to create compile units from the debug map and not have to fill all of the details in, and then lazily discover this information as we go on debuggging. This is needed to avoid parsing all .o files when setting breakpoints in implementation only files (no inlines). Otherwise we would need to parse the .o file, the object file (mach-o in our case) and the symbol file (DWARF in the object file) just to see what the compile unit was.
- modify the "SymbolFileDWARFDebugMap" to subclass lldb_private::Module so that the virtual "GetObjectFile()" and "GetSymbolVendor()" functions can be intercepted when the .o file contenst are later lazilly needed. Prior to this fix, when we first instantiated the "SymbolFileDWARFDebugMap" class, we would also make modules, object files and symbol files for every .o file in the debug map because we needed to fix up the sections in the .o files with information that is in the executable debug map. Now we lazily do this in the DebugMapModule::GetObjectFile()
Cleaned up header includes a bit as well.
llvm-svn: 162860
2012-08-30 05:13:06 +08:00
|
|
|
#include "lldb/Core/Module.h"
|
2015-09-03 09:40:51 +08:00
|
|
|
#include "lldb/Core/PluginInterface.h"
|
2012-10-20 02:02:49 +08:00
|
|
|
#include "lldb/Core/PluginManager.h"
|
2011-05-10 04:18:18 +08:00
|
|
|
#include "lldb/Core/RegisterValue.h"
|
2010-06-09 00:52:24 +08:00
|
|
|
#include "lldb/Core/State.h"
|
2011-06-03 07:58:26 +08:00
|
|
|
#include "lldb/Core/StreamAsynchronousIO.h"
|
2012-02-21 10:23:08 +08:00
|
|
|
#include "lldb/Core/StreamCallback.h"
|
2014-01-28 07:43:24 +08:00
|
|
|
#include "lldb/Core/StreamFile.h"
|
2010-09-19 10:33:57 +08:00
|
|
|
#include "lldb/Core/StreamString.h"
|
2014-06-13 10:37:02 +08:00
|
|
|
#include "lldb/Core/StructuredData.h"
|
2010-06-09 00:52:24 +08:00
|
|
|
#include "lldb/Core/Timer.h"
|
2011-06-30 06:27:15 +08:00
|
|
|
#include "lldb/Core/ValueObject.h"
|
Added a new thread and frame format that can be used to display a function
name + arguments when the data is available. It seems to work really well,
but some more testing is needed before we make this on by default.
The new function format name is:
${function.name-with-args}
To see how to use these formats see the website:
http://lldb.llvm.org/formats.html
Here is a sample backtrace of debugging LLDB with LLDB using this new format
value:
(lldb) thread backtrace all
* thread #1: tid = 0x2203, 0x00007fff88a17bca libsystem_kernel.dylib __psynch_cvwait + 10, stop reason = signal SIGINT, name = <lldb.driver.main-thread>, queue = com.apple.main-thread
frame #0: 0x00007fff88a17bca libsystem_kernel.dylib __psynch_cvwait + 10
frame #1: 0x00007fff884ae274 libsystem_c.dylib _pthread_cond_wait + 840
frame #2: 0x00000001010778ea LLDB lldb_private::Condition::Wait(this=0x0000000104846770, mutex=0x0000000104846730, abstime=0x0000000000000000, timed_out=0x00007fff5fbfdea7) + 138 at Condition.cpp:92
frame #3: 0x0000000101244c21 LLDB lldb_private::Predicate<bool>::WaitForValueEqualTo(this=0x0000000104846728, value=true, abstime=0x0000000000000000, timed_out=0x00007fff5fbfdea7) + 209 at Predicate.h:317
frame #4: 0x0000000100f6eeb2 LLDB lldb_private::Listener::WaitForEventsInternal(this=0x0000000104846660, timeout=0x0000000000000000, broadcaster=0x0000000000000000, broadcaster_names=0x0000000000000000, num_broadcaster_names=0x00000000, event_type_mask=0x00000000, event_sp=0x00007fff5fbfe030) + 386 at Listener.cpp:388
frame #5: 0x0000000100f6f231 LLDB lldb_private::Listener::WaitForEvent(this=0x0000000104846660, timeout=0x0000000000000000, event_sp=0x00007fff5fbfe030) + 81 at Listener.cpp:436
frame #6: 0x0000000100098dcd LLDB lldb::SBListener::WaitForEvent(this=0x00007fff5fbff0f0, timeout_secs=0xffffffff, event=0x00007fff5fbfe430) + 685 at SBListener.cpp:181
frame #7: 0x000000010000628c lldb Driver::MainLoop(this=0x00007fff5fbff620) + 5244 at Driver.cpp:1325
frame #8: 0x0000000100006ca3 lldb main(argc=1, argv=0x00007fff5fbff758, envp=0x00007fff5fbff768) + 419 at Driver.cpp:1460
frame #9: 0x0000000100000d54 lldb start + 52
thread #3: tid = 0x2703, 0x00007fff88a17df2 libsystem_kernel.dylib select$DARWIN_EXTSN + 10, name = <lldb.comm.debugger.input>
frame #0: 0x00007fff88a17df2 libsystem_kernel.dylib select$DARWIN_EXTSN + 10
frame #1: 0x0000000100f3f072 LLDB lldb_private::ConnectionFileDescriptor::BytesAvailable(this=0x000000010524d040, timeout_usec=0x004c4b40, error_ptr=0x0000000105640a18) + 722 at ConnectionFileDescriptor.cpp:542
frame #2: 0x0000000100f3e6dd LLDB lldb_private::ConnectionFileDescriptor::Read(this=0x000000010524d040, dst=0x0000000105640a60, dst_len=1024, timeout_usec=0x004c4b40, status=0x0000000105640a14, error_ptr=0x0000000105640a18) + 301 at ConnectionFileDescriptor.cpp:273
frame #3: 0x0000000100f3b8f7 LLDB lldb_private::Communication::ReadFromConnection(this=0x0000000104846270, dst=0x0000000105640a60, dst_len=1024, timeout_usec=0x004c4b40, status=0x0000000105640a14, error_ptr=0x0000000105640a18) + 167 at Communication.cpp:317
frame #4: 0x0000000100f3b197 LLDB lldb_private::Communication::ReadThread(p=0x0000000104846270) + 327 at Communication.cpp:344
frame #5: 0x0000000101078923 LLDB ThreadCreateTrampoline(arg=0x00000001045f6650) + 227 at Host.cpp:549
frame #6: 0x00007fff884aa8bf libsystem_c.dylib _pthread_start + 335
frame #7: 0x00007fff884adb75 libsystem_c.dylib thread_start + 13
thread #4: tid = 0x2803, 0x00007fff88a17df2 libsystem_kernel.dylib select$DARWIN_EXTSN + 10, name = <lldb.comm.driver.editline>
frame #0: 0x00007fff88a17df2 libsystem_kernel.dylib select$DARWIN_EXTSN + 10
frame #1: 0x0000000100f3f072 LLDB lldb_private::ConnectionFileDescriptor::BytesAvailable(this=0x0000000105700370, timeout_usec=0x004c4b40, error_ptr=0x00000001056c3a18) + 722 at ConnectionFileDescriptor.cpp:542
frame #2: 0x0000000100f3e6dd LLDB lldb_private::ConnectionFileDescriptor::Read(this=0x0000000105700370, dst=0x00000001056c3a60, dst_len=1024, timeout_usec=0x004c4b40, status=0x00000001056c3a14, error_ptr=0x00000001056c3a18) + 301 at ConnectionFileDescriptor.cpp:273
frame #3: 0x0000000100f3b8f7 LLDB lldb_private::Communication::ReadFromConnection(this=0x0000000105700000, dst=0x00000001056c3a60, dst_len=1024, timeout_usec=0x004c4b40, status=0x00000001056c3a14, error_ptr=0x00000001056c3a18) + 167 at Communication.cpp:317
frame #4: 0x0000000100f3b197 LLDB lldb_private::Communication::ReadThread(p=0x0000000105700000) + 327 at Communication.cpp:344
frame #5: 0x0000000101078923 LLDB ThreadCreateTrampoline(arg=0x0000000105700430) + 227 at Host.cpp:549
frame #6: 0x00007fff884aa8bf libsystem_c.dylib _pthread_start + 335
frame #7: 0x00007fff884adb75 libsystem_c.dylib thread_start + 13
thread #5: tid = 0x2903, 0x00007fff88a17df2 libsystem_kernel.dylib select$DARWIN_EXTSN + 10, name = <lldb.comm.driver.editline_output>
frame #0: 0x00007fff88a17df2 libsystem_kernel.dylib select$DARWIN_EXTSN + 10
frame #1: 0x0000000100f3f072 LLDB lldb_private::ConnectionFileDescriptor::BytesAvailable(this=0x00000001057178f0, timeout_usec=0x004c4b40, error_ptr=0x0000000105980a18) + 722 at ConnectionFileDescriptor.cpp:542
frame #2: 0x0000000100f3e6dd LLDB lldb_private::ConnectionFileDescriptor::Read(this=0x00000001057178f0, dst=0x0000000105980a60, dst_len=1024, timeout_usec=0x004c4b40, status=0x0000000105980a14, error_ptr=0x0000000105980a18) + 301 at ConnectionFileDescriptor.cpp:273
frame #3: 0x0000000100f3b8f7 LLDB lldb_private::Communication::ReadFromConnection(this=0x0000000105717580, dst=0x0000000105980a60, dst_len=1024, timeout_usec=0x004c4b40, status=0x0000000105980a14, error_ptr=0x0000000105980a18) + 167 at Communication.cpp:317
frame #4: 0x0000000100f3b197 LLDB lldb_private::Communication::ReadThread(p=0x0000000105717580) + 327 at Communication.cpp:344
frame #5: 0x0000000101078923 LLDB ThreadCreateTrampoline(arg=0x00000001057179b0) + 227 at Host.cpp:549
frame #6: 0x00007fff884aa8bf libsystem_c.dylib _pthread_start + 335
frame #7: 0x00007fff884adb75 libsystem_c.dylib thread_start + 13
thread #6: tid = 0x2a03, 0x00007fff88a18af2 libsystem_kernel.dylib read + 10, name = <lldb.driver.commandline_io>
frame #0: 0x00007fff88a18af2 libsystem_kernel.dylib read + 10
frame #1: 0x0000000100050c3b libedit.3.dylib read_init + 247
frame #2: 0x0000000100050e96 libedit.3.dylib el_wgetc + 155
frame #3: 0x000000010005115d libedit.3.dylib el_wgets + 578
frame #4: 0x000000010005debc libedit.3.dylib el_gets + 37
frame #5: 0x000000010000d409 lldb IOChannel::LibeditGetInput(this=0x0000000105700490, new_line=0x0000000105a03db0) + 89 at IOChannel.cpp:311
frame #6: 0x000000010000d8b6 lldb IOChannel::Run(this=0x0000000105700490) + 806 at IOChannel.cpp:391
frame #7: 0x000000010000d57d lldb IOChannel::IOReadThread(ptr=0x0000000105700490) + 29 at IOChannel.cpp:345
frame #8: 0x0000000101078923 LLDB ThreadCreateTrampoline(arg=0x00000001057179f0) + 227 at Host.cpp:549
frame #9: 0x00007fff884aa8bf libsystem_c.dylib _pthread_start + 335
frame #10: 0x00007fff884adb75 libsystem_c.dylib thread_start + 13
(lldb)
llvm-svn: 148110
2012-01-13 16:39:16 +08:00
|
|
|
#include "lldb/Core/ValueObjectVariable.h"
|
2013-01-29 07:47:25 +08:00
|
|
|
#include "lldb/DataFormatters/DataVisualization.h"
|
|
|
|
#include "lldb/DataFormatters/FormatManager.h"
|
2014-03-26 06:03:52 +08:00
|
|
|
#include "lldb/DataFormatters/TypeSummary.h"
|
2015-10-20 08:23:46 +08:00
|
|
|
#include "lldb/Expression/REPL.h"
|
2014-10-07 05:22:36 +08:00
|
|
|
#include "lldb/Host/ConnectionFileDescriptor.h"
|
2014-08-22 01:29:12 +08:00
|
|
|
#include "lldb/Host/HostInfo.h"
|
2011-02-08 07:24:47 +08:00
|
|
|
#include "lldb/Host/Terminal.h"
|
2014-09-10 04:54:56 +08:00
|
|
|
#include "lldb/Host/ThreadLauncher.h"
|
2010-06-23 09:19:29 +08:00
|
|
|
#include "lldb/Interpreter/CommandInterpreter.h"
|
2015-03-04 09:58:01 +08:00
|
|
|
#include "lldb/Interpreter/OptionValueProperties.h"
|
2012-08-23 01:17:09 +08:00
|
|
|
#include "lldb/Interpreter/OptionValueSInt64.h"
|
|
|
|
#include "lldb/Interpreter/OptionValueString.h"
|
<rdar://problem/11757916>
Make breakpoint setting by file and line much more efficient by only looking for inlined breakpoint locations if we are setting a breakpoint in anything but a source implementation file. Implementing this complex for a many reasons. Turns out that parsing compile units lazily had some issues with respect to how we need to do things with DWARF in .o files. So the fixes in the checkin for this makes these changes:
- Add a new setting called "target.inline-breakpoint-strategy" which can be set to "never", "always", or "headers". "never" will never try and set any inlined breakpoints (fastest). "always" always looks for inlined breakpoint locations (slowest, but most accurate). "headers", which is the default setting, will only look for inlined breakpoint locations if the breakpoint is set in what are consudered to be header files, which is realy defined as "not in an implementation source file".
- modify the breakpoint setting by file and line to check the current "target.inline-breakpoint-strategy" setting and act accordingly
- Modify compile units to be able to get their language and other info lazily. This allows us to create compile units from the debug map and not have to fill all of the details in, and then lazily discover this information as we go on debuggging. This is needed to avoid parsing all .o files when setting breakpoints in implementation only files (no inlines). Otherwise we would need to parse the .o file, the object file (mach-o in our case) and the symbol file (DWARF in the object file) just to see what the compile unit was.
- modify the "SymbolFileDWARFDebugMap" to subclass lldb_private::Module so that the virtual "GetObjectFile()" and "GetSymbolVendor()" functions can be intercepted when the .o file contenst are later lazilly needed. Prior to this fix, when we first instantiated the "SymbolFileDWARFDebugMap" class, we would also make modules, object files and symbol files for every .o file in the debug map because we needed to fix up the sections in the .o files with information that is in the executable debug map. Now we lazily do this in the DebugMapModule::GetObjectFile()
Cleaned up header includes a bit as well.
llvm-svn: 162860
2012-08-30 05:13:06 +08:00
|
|
|
#include "lldb/Symbol/CompileUnit.h"
|
|
|
|
#include "lldb/Symbol/Function.h"
|
|
|
|
#include "lldb/Symbol/Symbol.h"
|
Added a new thread and frame format that can be used to display a function
name + arguments when the data is available. It seems to work really well,
but some more testing is needed before we make this on by default.
The new function format name is:
${function.name-with-args}
To see how to use these formats see the website:
http://lldb.llvm.org/formats.html
Here is a sample backtrace of debugging LLDB with LLDB using this new format
value:
(lldb) thread backtrace all
* thread #1: tid = 0x2203, 0x00007fff88a17bca libsystem_kernel.dylib __psynch_cvwait + 10, stop reason = signal SIGINT, name = <lldb.driver.main-thread>, queue = com.apple.main-thread
frame #0: 0x00007fff88a17bca libsystem_kernel.dylib __psynch_cvwait + 10
frame #1: 0x00007fff884ae274 libsystem_c.dylib _pthread_cond_wait + 840
frame #2: 0x00000001010778ea LLDB lldb_private::Condition::Wait(this=0x0000000104846770, mutex=0x0000000104846730, abstime=0x0000000000000000, timed_out=0x00007fff5fbfdea7) + 138 at Condition.cpp:92
frame #3: 0x0000000101244c21 LLDB lldb_private::Predicate<bool>::WaitForValueEqualTo(this=0x0000000104846728, value=true, abstime=0x0000000000000000, timed_out=0x00007fff5fbfdea7) + 209 at Predicate.h:317
frame #4: 0x0000000100f6eeb2 LLDB lldb_private::Listener::WaitForEventsInternal(this=0x0000000104846660, timeout=0x0000000000000000, broadcaster=0x0000000000000000, broadcaster_names=0x0000000000000000, num_broadcaster_names=0x00000000, event_type_mask=0x00000000, event_sp=0x00007fff5fbfe030) + 386 at Listener.cpp:388
frame #5: 0x0000000100f6f231 LLDB lldb_private::Listener::WaitForEvent(this=0x0000000104846660, timeout=0x0000000000000000, event_sp=0x00007fff5fbfe030) + 81 at Listener.cpp:436
frame #6: 0x0000000100098dcd LLDB lldb::SBListener::WaitForEvent(this=0x00007fff5fbff0f0, timeout_secs=0xffffffff, event=0x00007fff5fbfe430) + 685 at SBListener.cpp:181
frame #7: 0x000000010000628c lldb Driver::MainLoop(this=0x00007fff5fbff620) + 5244 at Driver.cpp:1325
frame #8: 0x0000000100006ca3 lldb main(argc=1, argv=0x00007fff5fbff758, envp=0x00007fff5fbff768) + 419 at Driver.cpp:1460
frame #9: 0x0000000100000d54 lldb start + 52
thread #3: tid = 0x2703, 0x00007fff88a17df2 libsystem_kernel.dylib select$DARWIN_EXTSN + 10, name = <lldb.comm.debugger.input>
frame #0: 0x00007fff88a17df2 libsystem_kernel.dylib select$DARWIN_EXTSN + 10
frame #1: 0x0000000100f3f072 LLDB lldb_private::ConnectionFileDescriptor::BytesAvailable(this=0x000000010524d040, timeout_usec=0x004c4b40, error_ptr=0x0000000105640a18) + 722 at ConnectionFileDescriptor.cpp:542
frame #2: 0x0000000100f3e6dd LLDB lldb_private::ConnectionFileDescriptor::Read(this=0x000000010524d040, dst=0x0000000105640a60, dst_len=1024, timeout_usec=0x004c4b40, status=0x0000000105640a14, error_ptr=0x0000000105640a18) + 301 at ConnectionFileDescriptor.cpp:273
frame #3: 0x0000000100f3b8f7 LLDB lldb_private::Communication::ReadFromConnection(this=0x0000000104846270, dst=0x0000000105640a60, dst_len=1024, timeout_usec=0x004c4b40, status=0x0000000105640a14, error_ptr=0x0000000105640a18) + 167 at Communication.cpp:317
frame #4: 0x0000000100f3b197 LLDB lldb_private::Communication::ReadThread(p=0x0000000104846270) + 327 at Communication.cpp:344
frame #5: 0x0000000101078923 LLDB ThreadCreateTrampoline(arg=0x00000001045f6650) + 227 at Host.cpp:549
frame #6: 0x00007fff884aa8bf libsystem_c.dylib _pthread_start + 335
frame #7: 0x00007fff884adb75 libsystem_c.dylib thread_start + 13
thread #4: tid = 0x2803, 0x00007fff88a17df2 libsystem_kernel.dylib select$DARWIN_EXTSN + 10, name = <lldb.comm.driver.editline>
frame #0: 0x00007fff88a17df2 libsystem_kernel.dylib select$DARWIN_EXTSN + 10
frame #1: 0x0000000100f3f072 LLDB lldb_private::ConnectionFileDescriptor::BytesAvailable(this=0x0000000105700370, timeout_usec=0x004c4b40, error_ptr=0x00000001056c3a18) + 722 at ConnectionFileDescriptor.cpp:542
frame #2: 0x0000000100f3e6dd LLDB lldb_private::ConnectionFileDescriptor::Read(this=0x0000000105700370, dst=0x00000001056c3a60, dst_len=1024, timeout_usec=0x004c4b40, status=0x00000001056c3a14, error_ptr=0x00000001056c3a18) + 301 at ConnectionFileDescriptor.cpp:273
frame #3: 0x0000000100f3b8f7 LLDB lldb_private::Communication::ReadFromConnection(this=0x0000000105700000, dst=0x00000001056c3a60, dst_len=1024, timeout_usec=0x004c4b40, status=0x00000001056c3a14, error_ptr=0x00000001056c3a18) + 167 at Communication.cpp:317
frame #4: 0x0000000100f3b197 LLDB lldb_private::Communication::ReadThread(p=0x0000000105700000) + 327 at Communication.cpp:344
frame #5: 0x0000000101078923 LLDB ThreadCreateTrampoline(arg=0x0000000105700430) + 227 at Host.cpp:549
frame #6: 0x00007fff884aa8bf libsystem_c.dylib _pthread_start + 335
frame #7: 0x00007fff884adb75 libsystem_c.dylib thread_start + 13
thread #5: tid = 0x2903, 0x00007fff88a17df2 libsystem_kernel.dylib select$DARWIN_EXTSN + 10, name = <lldb.comm.driver.editline_output>
frame #0: 0x00007fff88a17df2 libsystem_kernel.dylib select$DARWIN_EXTSN + 10
frame #1: 0x0000000100f3f072 LLDB lldb_private::ConnectionFileDescriptor::BytesAvailable(this=0x00000001057178f0, timeout_usec=0x004c4b40, error_ptr=0x0000000105980a18) + 722 at ConnectionFileDescriptor.cpp:542
frame #2: 0x0000000100f3e6dd LLDB lldb_private::ConnectionFileDescriptor::Read(this=0x00000001057178f0, dst=0x0000000105980a60, dst_len=1024, timeout_usec=0x004c4b40, status=0x0000000105980a14, error_ptr=0x0000000105980a18) + 301 at ConnectionFileDescriptor.cpp:273
frame #3: 0x0000000100f3b8f7 LLDB lldb_private::Communication::ReadFromConnection(this=0x0000000105717580, dst=0x0000000105980a60, dst_len=1024, timeout_usec=0x004c4b40, status=0x0000000105980a14, error_ptr=0x0000000105980a18) + 167 at Communication.cpp:317
frame #4: 0x0000000100f3b197 LLDB lldb_private::Communication::ReadThread(p=0x0000000105717580) + 327 at Communication.cpp:344
frame #5: 0x0000000101078923 LLDB ThreadCreateTrampoline(arg=0x00000001057179b0) + 227 at Host.cpp:549
frame #6: 0x00007fff884aa8bf libsystem_c.dylib _pthread_start + 335
frame #7: 0x00007fff884adb75 libsystem_c.dylib thread_start + 13
thread #6: tid = 0x2a03, 0x00007fff88a18af2 libsystem_kernel.dylib read + 10, name = <lldb.driver.commandline_io>
frame #0: 0x00007fff88a18af2 libsystem_kernel.dylib read + 10
frame #1: 0x0000000100050c3b libedit.3.dylib read_init + 247
frame #2: 0x0000000100050e96 libedit.3.dylib el_wgetc + 155
frame #3: 0x000000010005115d libedit.3.dylib el_wgets + 578
frame #4: 0x000000010005debc libedit.3.dylib el_gets + 37
frame #5: 0x000000010000d409 lldb IOChannel::LibeditGetInput(this=0x0000000105700490, new_line=0x0000000105a03db0) + 89 at IOChannel.cpp:311
frame #6: 0x000000010000d8b6 lldb IOChannel::Run(this=0x0000000105700490) + 806 at IOChannel.cpp:391
frame #7: 0x000000010000d57d lldb IOChannel::IOReadThread(ptr=0x0000000105700490) + 29 at IOChannel.cpp:345
frame #8: 0x0000000101078923 LLDB ThreadCreateTrampoline(arg=0x00000001057179f0) + 227 at Host.cpp:549
frame #9: 0x00007fff884aa8bf libsystem_c.dylib _pthread_start + 335
frame #10: 0x00007fff884adb75 libsystem_c.dylib thread_start + 13
(lldb)
llvm-svn: 148110
2012-01-13 16:39:16 +08:00
|
|
|
#include "lldb/Symbol/VariableList.h"
|
2015-10-20 08:23:46 +08:00
|
|
|
#include "lldb/Target/Language.h"
|
2010-06-09 00:52:24 +08:00
|
|
|
#include "lldb/Target/Process.h"
|
2010-09-19 10:33:57 +08:00
|
|
|
#include "lldb/Target/RegisterContext.h"
|
2013-12-03 03:35:49 +08:00
|
|
|
#include "lldb/Target/SectionLoadList.h"
|
2010-09-19 10:33:57 +08:00
|
|
|
#include "lldb/Target/StopInfo.h"
|
2016-08-19 12:21:48 +08:00
|
|
|
#include "lldb/Target/StructuredDataPlugin.h"
|
2013-05-21 06:29:23 +08:00
|
|
|
#include "lldb/Target/Target.h"
|
2016-09-07 04:57:50 +08:00
|
|
|
#include "lldb/Target/TargetList.h"
|
2010-06-09 00:52:24 +08:00
|
|
|
#include "lldb/Target/Thread.h"
|
2011-10-14 15:41:33 +08:00
|
|
|
#include "lldb/Utility/AnsiTerminal.h"
|
2016-09-07 04:57:50 +08:00
|
|
|
#include "lldb/lldb-private.h"
|
2010-06-09 00:52:24 +08:00
|
|
|
|
|
|
|
using namespace lldb;
|
|
|
|
using namespace lldb_private;
|
|
|
|
|
2010-07-01 00:22:25 +08:00
|
|
|
static lldb::user_id_t g_unique_id = 1;
|
2014-10-25 06:06:29 +08:00
|
|
|
static size_t g_debugger_event_thread_stack_bytes = 8 * 1024 * 1024;
|
2010-07-01 00:22:25 +08:00
|
|
|
|
2010-09-19 10:33:57 +08:00
|
|
|
#pragma mark Static Functions
|
|
|
|
|
|
|
|
typedef std::vector<DebuggerSP> DebuggerList;
|
2016-09-07 04:57:50 +08:00
|
|
|
static std::recursive_mutex *g_debugger_list_mutex_ptr =
|
|
|
|
nullptr; // NOTE: intentional leak to avoid issues with C++ destructor chain
|
|
|
|
static DebuggerList *g_debugger_list_ptr =
|
|
|
|
nullptr; // NOTE: intentional leak to avoid issues with C++ destructor chain
|
|
|
|
|
|
|
|
OptionEnumValueElement g_show_disassembly_enum_values[] = {
|
|
|
|
{Debugger::eStopDisassemblyTypeNever, "never",
|
|
|
|
"Never show disassembly when displaying a stop context."},
|
|
|
|
{Debugger::eStopDisassemblyTypeNoDebugInfo, "no-debuginfo",
|
|
|
|
"Show disassembly when there is no debug information."},
|
|
|
|
{Debugger::eStopDisassemblyTypeNoSource, "no-source",
|
|
|
|
"Show disassembly when there is no source information, or the source file "
|
|
|
|
"is missing when displaying a stop context."},
|
|
|
|
{Debugger::eStopDisassemblyTypeAlways, "always",
|
|
|
|
"Always show disassembly when displaying a stop context."},
|
|
|
|
{0, nullptr, nullptr}};
|
|
|
|
|
|
|
|
OptionEnumValueElement g_language_enumerators[] = {
|
|
|
|
{eScriptLanguageNone, "none", "Disable scripting languages."},
|
|
|
|
{eScriptLanguagePython, "python",
|
|
|
|
"Select python as the default scripting language."},
|
|
|
|
{eScriptLanguageDefault, "default",
|
|
|
|
"Select the lldb default as the default scripting language."},
|
|
|
|
{0, nullptr, nullptr}};
|
|
|
|
|
|
|
|
#define MODULE_WITH_FUNC \
|
|
|
|
"{ " \
|
|
|
|
"${module.file.basename}{`${function.name-with-args}${function.pc-offset}}}"
|
2012-08-23 01:17:09 +08:00
|
|
|
#define FILE_AND_LINE "{ at ${line.file.basename}:${line.number}}"
|
2015-07-29 08:42:47 +08:00
|
|
|
#define IS_OPTIMIZED "{${function.is-optimized} [opt]}"
|
2012-08-23 01:17:09 +08:00
|
|
|
|
2016-09-07 04:57:50 +08:00
|
|
|
#define DEFAULT_THREAD_FORMAT \
|
|
|
|
"thread #${thread.index}: tid = ${thread.id%tid}" \
|
|
|
|
"{, ${frame.pc}}" MODULE_WITH_FUNC FILE_AND_LINE \
|
|
|
|
"{, name = '${thread.name}'}" \
|
|
|
|
"{, queue = '${thread.queue}'}" \
|
|
|
|
"{, activity = '${thread.info.activity.name}'}" \
|
|
|
|
"{, ${thread.info.trace_messages} messages}" \
|
|
|
|
"{, stop reason = ${thread.stop-reason}}" \
|
|
|
|
"{\\nReturn value: ${thread.return-value}}" \
|
|
|
|
"{\\nCompleted expression: ${thread.completed-expression}}" \
|
|
|
|
"\\n"
|
|
|
|
|
|
|
|
#define DEFAULT_FRAME_FORMAT \
|
|
|
|
"frame #${frame.index}: ${frame.pc}" MODULE_WITH_FUNC FILE_AND_LINE \
|
|
|
|
IS_OPTIMIZED "\\n"
|
2012-08-23 01:17:09 +08:00
|
|
|
|
2015-02-14 07:24:21 +08:00
|
|
|
// Three parts to this disassembly format specification:
|
|
|
|
// 1. If this is a new function/symbol (no previous symbol/function), print
|
|
|
|
// dylib`funcname:\n
|
2016-09-07 04:57:50 +08:00
|
|
|
// 2. If this is a symbol context change (different from previous
|
|
|
|
// symbol/function), print
|
2015-02-14 07:24:21 +08:00
|
|
|
// dylib`funcname:\n
|
2016-09-07 04:57:50 +08:00
|
|
|
// 3. print
|
|
|
|
// address <+offset>:
|
|
|
|
#define DEFAULT_DISASSEMBLY_FORMAT \
|
|
|
|
"{${function.initial-function}{${module.file.basename}`}{${function.name-" \
|
|
|
|
"without-args}}:\n}{${function.changed}\n{${module.file.basename}`}{${" \
|
|
|
|
"function.name-without-args}}:\n}{${current-pc-arrow} " \
|
|
|
|
"}${addr-file-or-load}{ " \
|
|
|
|
"<${function.concrete-only-addr-offset-no-padding}>}: "
|
2015-02-14 07:24:21 +08:00
|
|
|
|
|
|
|
// gdb's disassembly format can be emulated with
|
2016-09-07 04:57:50 +08:00
|
|
|
// ${current-pc-arrow}${addr-file-or-load}{
|
|
|
|
// <${function.name-without-args}${function.concrete-only-addr-offset-no-padding}>}:
|
2015-02-14 07:24:21 +08:00
|
|
|
|
|
|
|
// lldb's original format for disassembly would look like this format string -
|
2016-09-07 04:57:50 +08:00
|
|
|
// {${function.initial-function}{${module.file.basename}`}{${function.name-without-args}}:\n}{${function.changed}\n{${module.file.basename}`}{${function.name-without-args}}:\n}{${current-pc-arrow}
|
|
|
|
// }{${addr-file-or-load}}:
|
|
|
|
|
add stop column highlighting support
This change introduces optional marking of the column within a source
line where a thread is stopped. This marking will show up when the
source code for a thread stop is displayed, when the debug info
knows the column information, and if the optional column marking is
enabled.
There are two separate methods for handling the marking of the stop
column:
* via ANSI terminal codes, which are added inline to the source line
display. The default ANSI mark-up is to underline the column.
* via a pure text-based caret that is added in the appropriate column
in a newly-inserted blank line underneath the source line in
question.
There are some new options that control how this all works.
* settings set stop-show-column
This takes one of 4 values:
* ansi-or-caret: use the ANSI terminal code mechanism if LLDB
is running with color enabled; if not, use the caret-based,
pure text method (see the "caret" mode below).
* ansi: only use the ANSI terminal code mechanism to highlight
the stop line. If LLDB is running with color disabled, no
stop column marking will occur.
* caret: only use the pure text caret method, which introduces
a newly-inserted line underneath the current line, where
the only character in the new line is a caret that highlights
the stop column in question.
* none: no stop column marking will be attempted.
* settings set stop-show-column-ansi-prefix
This is a text format that indicates the ANSI formatting
code to insert into the stream immediately preceding the
column where the stop column character will be marked up.
It defaults to ${ansi.underline}; however, it can contain
any valid LLDB format codes, e.g.
${ansi.fg.red}${ansi.bold}${ansi.underline}
* settings set stop-show-column-ansi-suffix
This is the text format that specifies the ANSI terminal
codes to end the markup that was started with the prefix
described above. It defaults to: ${ansi.normal}. This
should be sufficient for the common cases.
Significant leg-work was done by Adrian Prantl. (Thanks, Adrian!)
differential review: https://reviews.llvm.org/D20835
reviewers: clayborg, jingham
llvm-svn: 282105
2016-09-22 04:13:14 +08:00
|
|
|
#define DEFAULT_STOP_SHOW_COLUMN_ANSI_PREFIX "${ansi.underline}"
|
|
|
|
#define DEFAULT_STOP_SHOW_COLUMN_ANSI_SUFFIX "${ansi.normal}"
|
|
|
|
|
|
|
|
static OptionEnumValueElement s_stop_show_column_values[] = {
|
|
|
|
{eStopShowColumnAnsiOrCaret, "ansi-or-caret",
|
|
|
|
"Highlight the stop column with ANSI terminal codes when color/ANSI mode "
|
|
|
|
"is enabled; otherwise, fall back to using a text-only caret (^) as if "
|
|
|
|
"\"caret-only\" mode was selected."},
|
|
|
|
{eStopShowColumnAnsi, "ansi", "Highlight the stop column with ANSI "
|
|
|
|
"terminal codes when running LLDB with "
|
|
|
|
"color/ANSI enabled."},
|
|
|
|
{eStopShowColumnCaret, "caret",
|
|
|
|
"Highlight the stop column with a caret character (^) underneath the stop "
|
|
|
|
"column. This method introduces a new line in source listings that "
|
|
|
|
"display thread stop locations."},
|
|
|
|
{eStopShowColumnNone, "none", "Do not highlight the stop column."},
|
|
|
|
{0, nullptr, nullptr}};
|
|
|
|
|
2016-09-07 04:57:50 +08:00
|
|
|
static PropertyDefinition g_properties[] = {
|
|
|
|
{"auto-confirm", OptionValue::eTypeBoolean, true, false, nullptr, nullptr,
|
|
|
|
"If true all confirmation prompts will receive their default reply."},
|
|
|
|
{"disassembly-format", OptionValue::eTypeFormatEntity, true, 0,
|
|
|
|
DEFAULT_DISASSEMBLY_FORMAT, nullptr, "The default disassembly format "
|
|
|
|
"string to use when disassembling "
|
|
|
|
"instruction sequences."},
|
|
|
|
{"frame-format", OptionValue::eTypeFormatEntity, true, 0,
|
|
|
|
DEFAULT_FRAME_FORMAT, nullptr, "The default frame format string to use "
|
|
|
|
"when displaying stack frame information "
|
|
|
|
"for threads."},
|
|
|
|
{"notify-void", OptionValue::eTypeBoolean, true, false, nullptr, nullptr,
|
|
|
|
"Notify the user explicitly if an expression returns void (default: "
|
|
|
|
"false)."},
|
|
|
|
{"prompt", OptionValue::eTypeString, true,
|
|
|
|
OptionValueString::eOptionEncodeCharacterEscapeSequences, "(lldb) ",
|
|
|
|
nullptr, "The debugger command line prompt displayed for the user."},
|
|
|
|
{"script-lang", OptionValue::eTypeEnum, true, eScriptLanguagePython,
|
|
|
|
nullptr, g_language_enumerators,
|
|
|
|
"The script language to be used for evaluating user-written scripts."},
|
|
|
|
{"stop-disassembly-count", OptionValue::eTypeSInt64, true, 4, nullptr,
|
|
|
|
nullptr, "The number of disassembly lines to show when displaying a "
|
|
|
|
"stopped context."},
|
|
|
|
{"stop-disassembly-display", OptionValue::eTypeEnum, true,
|
|
|
|
Debugger::eStopDisassemblyTypeNoDebugInfo, nullptr,
|
|
|
|
g_show_disassembly_enum_values,
|
|
|
|
"Control when to display disassembly when displaying a stopped context."},
|
|
|
|
{"stop-line-count-after", OptionValue::eTypeSInt64, true, 3, nullptr,
|
|
|
|
nullptr, "The number of sources lines to display that come after the "
|
|
|
|
"current source line when displaying a stopped context."},
|
|
|
|
{"stop-line-count-before", OptionValue::eTypeSInt64, true, 3, nullptr,
|
|
|
|
nullptr, "The number of sources lines to display that come before the "
|
|
|
|
"current source line when displaying a stopped context."},
|
add stop column highlighting support
This change introduces optional marking of the column within a source
line where a thread is stopped. This marking will show up when the
source code for a thread stop is displayed, when the debug info
knows the column information, and if the optional column marking is
enabled.
There are two separate methods for handling the marking of the stop
column:
* via ANSI terminal codes, which are added inline to the source line
display. The default ANSI mark-up is to underline the column.
* via a pure text-based caret that is added in the appropriate column
in a newly-inserted blank line underneath the source line in
question.
There are some new options that control how this all works.
* settings set stop-show-column
This takes one of 4 values:
* ansi-or-caret: use the ANSI terminal code mechanism if LLDB
is running with color enabled; if not, use the caret-based,
pure text method (see the "caret" mode below).
* ansi: only use the ANSI terminal code mechanism to highlight
the stop line. If LLDB is running with color disabled, no
stop column marking will occur.
* caret: only use the pure text caret method, which introduces
a newly-inserted line underneath the current line, where
the only character in the new line is a caret that highlights
the stop column in question.
* none: no stop column marking will be attempted.
* settings set stop-show-column-ansi-prefix
This is a text format that indicates the ANSI formatting
code to insert into the stream immediately preceding the
column where the stop column character will be marked up.
It defaults to ${ansi.underline}; however, it can contain
any valid LLDB format codes, e.g.
${ansi.fg.red}${ansi.bold}${ansi.underline}
* settings set stop-show-column-ansi-suffix
This is the text format that specifies the ANSI terminal
codes to end the markup that was started with the prefix
described above. It defaults to: ${ansi.normal}. This
should be sufficient for the common cases.
Significant leg-work was done by Adrian Prantl. (Thanks, Adrian!)
differential review: https://reviews.llvm.org/D20835
reviewers: clayborg, jingham
llvm-svn: 282105
2016-09-22 04:13:14 +08:00
|
|
|
{"stop-show-column", OptionValue::eTypeEnum, false,
|
|
|
|
eStopShowColumnAnsiOrCaret, nullptr, s_stop_show_column_values,
|
|
|
|
"If true, LLDB will use the column information from the debug info to "
|
|
|
|
"mark the current position when displaying a stopped context."},
|
|
|
|
{"stop-show-column-ansi-prefix", OptionValue::eTypeFormatEntity, true, 0,
|
|
|
|
DEFAULT_STOP_SHOW_COLUMN_ANSI_PREFIX, nullptr,
|
|
|
|
"When displaying the column marker in a color-enabled (i.e. ANSI) "
|
|
|
|
"terminal, use the ANSI terminal code specified in this format at the "
|
|
|
|
"immediately before the column to be marked."},
|
|
|
|
{"stop-show-column-ansi-suffix", OptionValue::eTypeFormatEntity, true, 0,
|
|
|
|
DEFAULT_STOP_SHOW_COLUMN_ANSI_SUFFIX, nullptr,
|
|
|
|
"When displaying the column marker in a color-enabled (i.e. ANSI) "
|
|
|
|
"terminal, use the ANSI terminal code specified in this format "
|
|
|
|
"immediately after the column to be marked."},
|
2016-09-07 04:57:50 +08:00
|
|
|
{"term-width", OptionValue::eTypeSInt64, true, 80, nullptr, nullptr,
|
|
|
|
"The maximum number of columns to use for displaying text."},
|
|
|
|
{"thread-format", OptionValue::eTypeFormatEntity, true, 0,
|
|
|
|
DEFAULT_THREAD_FORMAT, nullptr, "The default thread format string to use "
|
|
|
|
"when displaying thread information."},
|
|
|
|
{"use-external-editor", OptionValue::eTypeBoolean, true, false, nullptr,
|
|
|
|
nullptr, "Whether to use an external editor or not."},
|
|
|
|
{"use-color", OptionValue::eTypeBoolean, true, true, nullptr, nullptr,
|
|
|
|
"Whether to use Ansi color codes or not."},
|
|
|
|
{"auto-one-line-summaries", OptionValue::eTypeBoolean, true, true, nullptr,
|
|
|
|
nullptr, "If true, LLDB will automatically display small structs in "
|
|
|
|
"one-liner format (default: true)."},
|
|
|
|
{"auto-indent", OptionValue::eTypeBoolean, true, true, nullptr, nullptr,
|
|
|
|
"If true, LLDB will auto indent/outdent code. Currently only supported in "
|
|
|
|
"the REPL (default: true)."},
|
|
|
|
{"print-decls", OptionValue::eTypeBoolean, true, true, nullptr, nullptr,
|
|
|
|
"If true, LLDB will print the values of variables declared in an "
|
|
|
|
"expression. Currently only supported in the REPL (default: true)."},
|
|
|
|
{"tab-size", OptionValue::eTypeUInt64, true, 4, nullptr, nullptr,
|
|
|
|
"The tab size to use when indenting code in multi-line input mode "
|
|
|
|
"(default: 4)."},
|
|
|
|
{"escape-non-printables", OptionValue::eTypeBoolean, true, true, nullptr,
|
|
|
|
nullptr, "If true, LLDB will automatically escape non-printable and "
|
|
|
|
"escape characters when formatting strings."},
|
|
|
|
{nullptr, OptionValue::eTypeInvalid, true, 0, nullptr, nullptr, nullptr}};
|
|
|
|
|
|
|
|
enum {
|
|
|
|
ePropertyAutoConfirm = 0,
|
|
|
|
ePropertyDisassemblyFormat,
|
|
|
|
ePropertyFrameFormat,
|
|
|
|
ePropertyNotiftVoid,
|
|
|
|
ePropertyPrompt,
|
|
|
|
ePropertyScriptLanguage,
|
|
|
|
ePropertyStopDisassemblyCount,
|
|
|
|
ePropertyStopDisassemblyDisplay,
|
|
|
|
ePropertyStopLineCountAfter,
|
|
|
|
ePropertyStopLineCountBefore,
|
add stop column highlighting support
This change introduces optional marking of the column within a source
line where a thread is stopped. This marking will show up when the
source code for a thread stop is displayed, when the debug info
knows the column information, and if the optional column marking is
enabled.
There are two separate methods for handling the marking of the stop
column:
* via ANSI terminal codes, which are added inline to the source line
display. The default ANSI mark-up is to underline the column.
* via a pure text-based caret that is added in the appropriate column
in a newly-inserted blank line underneath the source line in
question.
There are some new options that control how this all works.
* settings set stop-show-column
This takes one of 4 values:
* ansi-or-caret: use the ANSI terminal code mechanism if LLDB
is running with color enabled; if not, use the caret-based,
pure text method (see the "caret" mode below).
* ansi: only use the ANSI terminal code mechanism to highlight
the stop line. If LLDB is running with color disabled, no
stop column marking will occur.
* caret: only use the pure text caret method, which introduces
a newly-inserted line underneath the current line, where
the only character in the new line is a caret that highlights
the stop column in question.
* none: no stop column marking will be attempted.
* settings set stop-show-column-ansi-prefix
This is a text format that indicates the ANSI formatting
code to insert into the stream immediately preceding the
column where the stop column character will be marked up.
It defaults to ${ansi.underline}; however, it can contain
any valid LLDB format codes, e.g.
${ansi.fg.red}${ansi.bold}${ansi.underline}
* settings set stop-show-column-ansi-suffix
This is the text format that specifies the ANSI terminal
codes to end the markup that was started with the prefix
described above. It defaults to: ${ansi.normal}. This
should be sufficient for the common cases.
Significant leg-work was done by Adrian Prantl. (Thanks, Adrian!)
differential review: https://reviews.llvm.org/D20835
reviewers: clayborg, jingham
llvm-svn: 282105
2016-09-22 04:13:14 +08:00
|
|
|
ePropertyStopShowColumn,
|
|
|
|
ePropertyStopShowColumnAnsiPrefix,
|
|
|
|
ePropertyStopShowColumnAnsiSuffix,
|
2016-09-07 04:57:50 +08:00
|
|
|
ePropertyTerminalWidth,
|
|
|
|
ePropertyThreadFormat,
|
|
|
|
ePropertyUseExternalEditor,
|
|
|
|
ePropertyUseColor,
|
|
|
|
ePropertyAutoOneLineSummaries,
|
|
|
|
ePropertyAutoIndent,
|
|
|
|
ePropertyPrintDecls,
|
|
|
|
ePropertyTabSize,
|
|
|
|
ePropertyEscapeNonPrintables
|
2012-08-23 01:17:09 +08:00
|
|
|
};
|
2010-09-19 10:33:57 +08:00
|
|
|
|
2016-03-02 10:18:18 +08:00
|
|
|
LoadPluginCallbackType Debugger::g_load_plugin_callback = nullptr;
|
2012-09-01 08:38:36 +08:00
|
|
|
|
2016-09-07 04:57:50 +08:00
|
|
|
Error Debugger::SetPropertyValue(const ExecutionContext *exe_ctx,
|
|
|
|
VarSetOperationType op,
|
|
|
|
const char *property_path, const char *value) {
|
|
|
|
bool is_load_script =
|
|
|
|
strcmp(property_path, "target.load-script-from-symbol-file") == 0;
|
|
|
|
bool is_escape_non_printables =
|
|
|
|
strcmp(property_path, "escape-non-printables") == 0;
|
|
|
|
TargetSP target_sp;
|
|
|
|
LoadScriptFromSymFile load_script_old_value;
|
|
|
|
if (is_load_script && exe_ctx->GetTargetSP()) {
|
|
|
|
target_sp = exe_ctx->GetTargetSP();
|
|
|
|
load_script_old_value =
|
|
|
|
target_sp->TargetProperties::GetLoadScriptFromSymbolFile();
|
|
|
|
}
|
|
|
|
Error error(Properties::SetPropertyValue(exe_ctx, op, property_path, value));
|
|
|
|
if (error.Success()) {
|
|
|
|
// FIXME it would be nice to have "on-change" callbacks for properties
|
|
|
|
if (strcmp(property_path, g_properties[ePropertyPrompt].name) == 0) {
|
2016-09-24 02:06:53 +08:00
|
|
|
llvm::StringRef new_prompt = GetPrompt();
|
2016-09-07 04:57:50 +08:00
|
|
|
std::string str = lldb_utility::ansi::FormatAnsiTerminalCodes(
|
|
|
|
new_prompt, GetUseColor());
|
|
|
|
if (str.length())
|
2016-11-03 04:34:10 +08:00
|
|
|
new_prompt = str;
|
2016-09-07 04:57:50 +08:00
|
|
|
GetCommandInterpreter().UpdatePrompt(new_prompt);
|
|
|
|
EventSP prompt_change_event_sp(
|
|
|
|
new Event(CommandInterpreter::eBroadcastBitResetPrompt,
|
|
|
|
new EventDataBytes(new_prompt)));
|
|
|
|
GetCommandInterpreter().BroadcastEvent(prompt_change_event_sp);
|
|
|
|
} else if (strcmp(property_path, g_properties[ePropertyUseColor].name) ==
|
|
|
|
0) {
|
|
|
|
// use-color changed. Ping the prompt so it can reset the ansi terminal
|
|
|
|
// codes.
|
|
|
|
SetPrompt(GetPrompt());
|
|
|
|
} else if (is_load_script && target_sp &&
|
|
|
|
load_script_old_value == eLoadScriptFromSymFileWarn) {
|
|
|
|
if (target_sp->TargetProperties::GetLoadScriptFromSymbolFile() ==
|
|
|
|
eLoadScriptFromSymFileTrue) {
|
|
|
|
std::list<Error> errors;
|
|
|
|
StreamString feedback_stream;
|
|
|
|
if (!target_sp->LoadScriptingResources(errors, &feedback_stream)) {
|
|
|
|
StreamFileSP stream_sp(GetErrorFile());
|
|
|
|
if (stream_sp) {
|
|
|
|
for (auto error : errors) {
|
|
|
|
stream_sp->Printf("%s\n", error.AsCString());
|
2013-05-21 06:29:23 +08:00
|
|
|
}
|
2016-09-07 04:57:50 +08:00
|
|
|
if (feedback_stream.GetSize())
|
|
|
|
stream_sp->Printf("%s", feedback_stream.GetData());
|
|
|
|
}
|
2013-05-21 06:29:23 +08:00
|
|
|
}
|
2016-09-07 04:57:50 +08:00
|
|
|
}
|
|
|
|
} else if (is_escape_non_printables) {
|
|
|
|
DataVisualization::ForceUpdate();
|
2012-09-01 08:38:36 +08:00
|
|
|
}
|
2016-09-07 04:57:50 +08:00
|
|
|
}
|
|
|
|
return error;
|
2012-09-01 08:38:36 +08:00
|
|
|
}
|
|
|
|
|
2016-09-07 04:57:50 +08:00
|
|
|
bool Debugger::GetAutoConfirm() const {
|
|
|
|
const uint32_t idx = ePropertyAutoConfirm;
|
|
|
|
return m_collection_sp->GetPropertyAtIndexAsBoolean(
|
|
|
|
nullptr, idx, g_properties[idx].default_uint_value != 0);
|
2011-11-22 05:44:34 +08:00
|
|
|
}
|
|
|
|
|
2016-09-07 04:57:50 +08:00
|
|
|
const FormatEntity::Entry *Debugger::GetDisassemblyFormat() const {
|
|
|
|
const uint32_t idx = ePropertyDisassemblyFormat;
|
|
|
|
return m_collection_sp->GetPropertyAtIndexAsFormatEntity(nullptr, idx);
|
2014-10-11 07:07:36 +08:00
|
|
|
}
|
|
|
|
|
2016-09-07 04:57:50 +08:00
|
|
|
const FormatEntity::Entry *Debugger::GetFrameFormat() const {
|
|
|
|
const uint32_t idx = ePropertyFrameFormat;
|
|
|
|
return m_collection_sp->GetPropertyAtIndexAsFormatEntity(nullptr, idx);
|
2012-08-10 02:18:47 +08:00
|
|
|
}
|
|
|
|
|
2016-09-07 04:57:50 +08:00
|
|
|
bool Debugger::GetNotifyVoid() const {
|
|
|
|
const uint32_t idx = ePropertyNotiftVoid;
|
|
|
|
return m_collection_sp->GetPropertyAtIndexAsBoolean(
|
|
|
|
nullptr, idx, g_properties[idx].default_uint_value != 0);
|
2011-11-22 05:44:34 +08:00
|
|
|
}
|
|
|
|
|
2016-09-24 02:06:53 +08:00
|
|
|
llvm::StringRef Debugger::GetPrompt() const {
|
2016-09-07 04:57:50 +08:00
|
|
|
const uint32_t idx = ePropertyPrompt;
|
|
|
|
return m_collection_sp->GetPropertyAtIndexAsString(
|
|
|
|
nullptr, idx, g_properties[idx].default_cstr_value);
|
2011-11-22 05:44:34 +08:00
|
|
|
}
|
|
|
|
|
2016-09-24 02:06:53 +08:00
|
|
|
void Debugger::SetPrompt(llvm::StringRef p) {
|
2016-09-07 04:57:50 +08:00
|
|
|
const uint32_t idx = ePropertyPrompt;
|
|
|
|
m_collection_sp->SetPropertyAtIndexAsString(nullptr, idx, p);
|
2016-09-24 02:06:53 +08:00
|
|
|
llvm::StringRef new_prompt = GetPrompt();
|
2016-09-07 04:57:50 +08:00
|
|
|
std::string str =
|
|
|
|
lldb_utility::ansi::FormatAnsiTerminalCodes(new_prompt, GetUseColor());
|
|
|
|
if (str.length())
|
2016-11-03 04:34:10 +08:00
|
|
|
new_prompt = str;
|
2016-09-07 04:57:50 +08:00
|
|
|
GetCommandInterpreter().UpdatePrompt(new_prompt);
|
2011-11-22 05:44:34 +08:00
|
|
|
}
|
|
|
|
|
2016-09-07 04:57:50 +08:00
|
|
|
const FormatEntity::Entry *Debugger::GetThreadFormat() const {
|
|
|
|
const uint32_t idx = ePropertyThreadFormat;
|
|
|
|
return m_collection_sp->GetPropertyAtIndexAsFormatEntity(nullptr, idx);
|
2011-11-22 05:44:34 +08:00
|
|
|
}
|
|
|
|
|
2016-09-07 04:57:50 +08:00
|
|
|
lldb::ScriptLanguage Debugger::GetScriptLanguage() const {
|
|
|
|
const uint32_t idx = ePropertyScriptLanguage;
|
|
|
|
return (lldb::ScriptLanguage)m_collection_sp->GetPropertyAtIndexAsEnumeration(
|
|
|
|
nullptr, idx, g_properties[idx].default_uint_value);
|
2011-11-22 05:44:34 +08:00
|
|
|
}
|
|
|
|
|
2016-09-07 04:57:50 +08:00
|
|
|
bool Debugger::SetScriptLanguage(lldb::ScriptLanguage script_lang) {
|
|
|
|
const uint32_t idx = ePropertyScriptLanguage;
|
|
|
|
return m_collection_sp->SetPropertyAtIndexAsEnumeration(nullptr, idx,
|
|
|
|
script_lang);
|
2011-11-22 05:44:34 +08:00
|
|
|
}
|
|
|
|
|
2016-09-07 04:57:50 +08:00
|
|
|
uint32_t Debugger::GetTerminalWidth() const {
|
|
|
|
const uint32_t idx = ePropertyTerminalWidth;
|
|
|
|
return m_collection_sp->GetPropertyAtIndexAsSInt64(
|
|
|
|
nullptr, idx, g_properties[idx].default_uint_value);
|
2011-11-22 05:44:34 +08:00
|
|
|
}
|
|
|
|
|
2016-09-07 04:57:50 +08:00
|
|
|
bool Debugger::SetTerminalWidth(uint32_t term_width) {
|
|
|
|
const uint32_t idx = ePropertyTerminalWidth;
|
|
|
|
return m_collection_sp->SetPropertyAtIndexAsSInt64(nullptr, idx, term_width);
|
2011-11-22 05:44:34 +08:00
|
|
|
}
|
|
|
|
|
2016-09-07 04:57:50 +08:00
|
|
|
bool Debugger::GetUseExternalEditor() const {
|
|
|
|
const uint32_t idx = ePropertyUseExternalEditor;
|
|
|
|
return m_collection_sp->GetPropertyAtIndexAsBoolean(
|
|
|
|
nullptr, idx, g_properties[idx].default_uint_value != 0);
|
2011-11-22 05:44:34 +08:00
|
|
|
}
|
|
|
|
|
2016-09-07 04:57:50 +08:00
|
|
|
bool Debugger::SetUseExternalEditor(bool b) {
|
|
|
|
const uint32_t idx = ePropertyUseExternalEditor;
|
|
|
|
return m_collection_sp->SetPropertyAtIndexAsBoolean(nullptr, idx, b);
|
2011-11-22 05:44:34 +08:00
|
|
|
}
|
|
|
|
|
2016-09-07 04:57:50 +08:00
|
|
|
bool Debugger::GetUseColor() const {
|
|
|
|
const uint32_t idx = ePropertyUseColor;
|
|
|
|
return m_collection_sp->GetPropertyAtIndexAsBoolean(
|
|
|
|
nullptr, idx, g_properties[idx].default_uint_value != 0);
|
2013-05-24 04:47:45 +08:00
|
|
|
}
|
|
|
|
|
2016-09-07 04:57:50 +08:00
|
|
|
bool Debugger::SetUseColor(bool b) {
|
|
|
|
const uint32_t idx = ePropertyUseColor;
|
|
|
|
bool ret = m_collection_sp->SetPropertyAtIndexAsBoolean(nullptr, idx, b);
|
|
|
|
SetPrompt(GetPrompt());
|
|
|
|
return ret;
|
2013-05-24 04:47:45 +08:00
|
|
|
}
|
|
|
|
|
add stop column highlighting support
This change introduces optional marking of the column within a source
line where a thread is stopped. This marking will show up when the
source code for a thread stop is displayed, when the debug info
knows the column information, and if the optional column marking is
enabled.
There are two separate methods for handling the marking of the stop
column:
* via ANSI terminal codes, which are added inline to the source line
display. The default ANSI mark-up is to underline the column.
* via a pure text-based caret that is added in the appropriate column
in a newly-inserted blank line underneath the source line in
question.
There are some new options that control how this all works.
* settings set stop-show-column
This takes one of 4 values:
* ansi-or-caret: use the ANSI terminal code mechanism if LLDB
is running with color enabled; if not, use the caret-based,
pure text method (see the "caret" mode below).
* ansi: only use the ANSI terminal code mechanism to highlight
the stop line. If LLDB is running with color disabled, no
stop column marking will occur.
* caret: only use the pure text caret method, which introduces
a newly-inserted line underneath the current line, where
the only character in the new line is a caret that highlights
the stop column in question.
* none: no stop column marking will be attempted.
* settings set stop-show-column-ansi-prefix
This is a text format that indicates the ANSI formatting
code to insert into the stream immediately preceding the
column where the stop column character will be marked up.
It defaults to ${ansi.underline}; however, it can contain
any valid LLDB format codes, e.g.
${ansi.fg.red}${ansi.bold}${ansi.underline}
* settings set stop-show-column-ansi-suffix
This is the text format that specifies the ANSI terminal
codes to end the markup that was started with the prefix
described above. It defaults to: ${ansi.normal}. This
should be sufficient for the common cases.
Significant leg-work was done by Adrian Prantl. (Thanks, Adrian!)
differential review: https://reviews.llvm.org/D20835
reviewers: clayborg, jingham
llvm-svn: 282105
2016-09-22 04:13:14 +08:00
|
|
|
StopShowColumn Debugger::GetStopShowColumn() const {
|
|
|
|
const uint32_t idx = ePropertyStopShowColumn;
|
|
|
|
return (lldb::StopShowColumn)m_collection_sp->GetPropertyAtIndexAsEnumeration(
|
|
|
|
nullptr, idx, g_properties[idx].default_uint_value);
|
|
|
|
}
|
|
|
|
|
|
|
|
const FormatEntity::Entry *Debugger::GetStopShowColumnAnsiPrefix() const {
|
|
|
|
const uint32_t idx = ePropertyStopShowColumnAnsiPrefix;
|
|
|
|
return m_collection_sp->GetPropertyAtIndexAsFormatEntity(nullptr, idx);
|
|
|
|
}
|
|
|
|
|
|
|
|
const FormatEntity::Entry *Debugger::GetStopShowColumnAnsiSuffix() const {
|
|
|
|
const uint32_t idx = ePropertyStopShowColumnAnsiSuffix;
|
|
|
|
return m_collection_sp->GetPropertyAtIndexAsFormatEntity(nullptr, idx);
|
|
|
|
}
|
|
|
|
|
2016-09-07 04:57:50 +08:00
|
|
|
uint32_t Debugger::GetStopSourceLineCount(bool before) const {
|
|
|
|
const uint32_t idx =
|
|
|
|
before ? ePropertyStopLineCountBefore : ePropertyStopLineCountAfter;
|
|
|
|
return m_collection_sp->GetPropertyAtIndexAsSInt64(
|
|
|
|
nullptr, idx, g_properties[idx].default_uint_value);
|
2012-08-23 01:17:09 +08:00
|
|
|
}
|
2011-11-22 05:44:34 +08:00
|
|
|
|
2016-09-07 04:57:50 +08:00
|
|
|
Debugger::StopDisassemblyType Debugger::GetStopDisassemblyDisplay() const {
|
|
|
|
const uint32_t idx = ePropertyStopDisassemblyDisplay;
|
|
|
|
return (Debugger::StopDisassemblyType)
|
|
|
|
m_collection_sp->GetPropertyAtIndexAsEnumeration(
|
|
|
|
nullptr, idx, g_properties[idx].default_uint_value);
|
2012-08-23 01:17:09 +08:00
|
|
|
}
|
2011-11-22 05:44:34 +08:00
|
|
|
|
2016-09-07 04:57:50 +08:00
|
|
|
uint32_t Debugger::GetDisassemblyLineCount() const {
|
|
|
|
const uint32_t idx = ePropertyStopDisassemblyCount;
|
|
|
|
return m_collection_sp->GetPropertyAtIndexAsSInt64(
|
|
|
|
nullptr, idx, g_properties[idx].default_uint_value);
|
2012-08-23 01:17:09 +08:00
|
|
|
}
|
2011-11-22 05:44:34 +08:00
|
|
|
|
2016-09-07 04:57:50 +08:00
|
|
|
bool Debugger::GetAutoOneLineSummaries() const {
|
|
|
|
const uint32_t idx = ePropertyAutoOneLineSummaries;
|
|
|
|
return m_collection_sp->GetPropertyAtIndexAsBoolean(nullptr, idx, true);
|
2014-11-06 05:20:48 +08:00
|
|
|
}
|
2013-10-26 07:09:40 +08:00
|
|
|
|
2016-09-07 04:57:50 +08:00
|
|
|
bool Debugger::GetEscapeNonPrintables() const {
|
|
|
|
const uint32_t idx = ePropertyEscapeNonPrintables;
|
|
|
|
return m_collection_sp->GetPropertyAtIndexAsBoolean(nullptr, idx, true);
|
2013-10-26 07:09:40 +08:00
|
|
|
}
|
|
|
|
|
2016-09-07 04:57:50 +08:00
|
|
|
bool Debugger::GetAutoIndent() const {
|
|
|
|
const uint32_t idx = ePropertyAutoIndent;
|
|
|
|
return m_collection_sp->GetPropertyAtIndexAsBoolean(nullptr, idx, true);
|
2015-10-20 07:11:07 +08:00
|
|
|
}
|
|
|
|
|
2016-09-07 04:57:50 +08:00
|
|
|
bool Debugger::SetAutoIndent(bool b) {
|
|
|
|
const uint32_t idx = ePropertyAutoIndent;
|
|
|
|
return m_collection_sp->SetPropertyAtIndexAsBoolean(nullptr, idx, b);
|
2015-10-20 07:11:07 +08:00
|
|
|
}
|
|
|
|
|
2016-09-07 04:57:50 +08:00
|
|
|
bool Debugger::GetPrintDecls() const {
|
|
|
|
const uint32_t idx = ePropertyPrintDecls;
|
|
|
|
return m_collection_sp->GetPropertyAtIndexAsBoolean(nullptr, idx, true);
|
2015-10-20 07:11:07 +08:00
|
|
|
}
|
|
|
|
|
2016-09-07 04:57:50 +08:00
|
|
|
bool Debugger::SetPrintDecls(bool b) {
|
|
|
|
const uint32_t idx = ePropertyPrintDecls;
|
|
|
|
return m_collection_sp->SetPropertyAtIndexAsBoolean(nullptr, idx, b);
|
2015-10-20 07:11:07 +08:00
|
|
|
}
|
|
|
|
|
2016-09-07 04:57:50 +08:00
|
|
|
uint32_t Debugger::GetTabSize() const {
|
|
|
|
const uint32_t idx = ePropertyTabSize;
|
|
|
|
return m_collection_sp->GetPropertyAtIndexAsUInt64(
|
|
|
|
nullptr, idx, g_properties[idx].default_uint_value);
|
2015-10-20 07:11:07 +08:00
|
|
|
}
|
|
|
|
|
2016-09-07 04:57:50 +08:00
|
|
|
bool Debugger::SetTabSize(uint32_t tab_size) {
|
|
|
|
const uint32_t idx = ePropertyTabSize;
|
|
|
|
return m_collection_sp->SetPropertyAtIndexAsUInt64(nullptr, idx, tab_size);
|
2015-10-20 07:11:07 +08:00
|
|
|
}
|
|
|
|
|
2010-09-19 10:33:57 +08:00
|
|
|
#pragma mark Debugger
|
|
|
|
|
2016-09-07 04:57:50 +08:00
|
|
|
// const DebuggerPropertiesSP &
|
|
|
|
// Debugger::GetSettings() const
|
2012-08-23 01:17:09 +08:00
|
|
|
//{
|
|
|
|
// return m_properties_sp;
|
|
|
|
//}
|
|
|
|
//
|
2010-11-19 07:32:35 +08:00
|
|
|
|
2016-09-07 04:57:50 +08:00
|
|
|
void Debugger::Initialize(LoadPluginCallbackType load_plugin_callback) {
|
|
|
|
assert(g_debugger_list_ptr == nullptr &&
|
|
|
|
"Debugger::Initialize called more than once!");
|
|
|
|
g_debugger_list_mutex_ptr = new std::recursive_mutex();
|
|
|
|
g_debugger_list_ptr = new DebuggerList();
|
|
|
|
g_load_plugin_callback = load_plugin_callback;
|
2010-06-09 00:52:24 +08:00
|
|
|
}
|
|
|
|
|
2016-09-07 04:57:50 +08:00
|
|
|
void Debugger::Terminate() {
|
|
|
|
assert(g_debugger_list_ptr &&
|
|
|
|
"Debugger::Terminate called without a matching Debugger::Initialize!");
|
2015-04-01 05:03:22 +08:00
|
|
|
|
2016-09-07 04:57:50 +08:00
|
|
|
if (g_debugger_list_ptr && g_debugger_list_mutex_ptr) {
|
|
|
|
// Clear our master list of debugger objects
|
2016-05-27 00:51:23 +08:00
|
|
|
{
|
2016-09-07 04:57:50 +08:00
|
|
|
std::lock_guard<std::recursive_mutex> guard(*g_debugger_list_mutex_ptr);
|
|
|
|
for (const auto &debugger : *g_debugger_list_ptr)
|
|
|
|
debugger->Clear();
|
|
|
|
g_debugger_list_ptr->clear();
|
2016-05-27 00:51:23 +08:00
|
|
|
}
|
2016-09-07 04:57:50 +08:00
|
|
|
}
|
2010-06-09 00:52:24 +08:00
|
|
|
}
|
|
|
|
|
2016-09-07 04:57:50 +08:00
|
|
|
void Debugger::SettingsInitialize() { Target::SettingsInitialize(); }
|
2011-03-11 06:14:10 +08:00
|
|
|
|
2016-09-07 04:57:50 +08:00
|
|
|
void Debugger::SettingsTerminate() { Target::SettingsTerminate(); }
|
2011-03-11 06:14:10 +08:00
|
|
|
|
2016-09-07 04:57:50 +08:00
|
|
|
bool Debugger::LoadPlugin(const FileSpec &spec, Error &error) {
|
|
|
|
if (g_load_plugin_callback) {
|
|
|
|
llvm::sys::DynamicLibrary dynlib =
|
|
|
|
g_load_plugin_callback(shared_from_this(), spec, error);
|
|
|
|
if (dynlib.isValid()) {
|
|
|
|
m_loaded_plugins.push_back(dynlib);
|
|
|
|
return true;
|
2012-09-29 07:57:51 +08:00
|
|
|
}
|
2016-09-07 04:57:50 +08:00
|
|
|
} else {
|
|
|
|
// The g_load_plugin_callback is registered in SBDebugger::Initialize()
|
|
|
|
// and if the public API layer isn't available (code is linking against
|
|
|
|
// all of the internal LLDB static libraries), then we can't load plugins
|
|
|
|
error.SetErrorString("Public API layer is not available");
|
|
|
|
}
|
|
|
|
return false;
|
2012-09-29 07:57:51 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
static FileSpec::EnumerateDirectoryResult
|
2016-09-07 04:57:50 +08:00
|
|
|
LoadPluginCallback(void *baton, FileSpec::FileType file_type,
|
|
|
|
const FileSpec &file_spec) {
|
|
|
|
Error error;
|
|
|
|
|
|
|
|
static ConstString g_dylibext("dylib");
|
|
|
|
static ConstString g_solibext("so");
|
|
|
|
|
|
|
|
if (!baton)
|
|
|
|
return FileSpec::eEnumerateDirectoryResultQuit;
|
|
|
|
|
|
|
|
Debugger *debugger = (Debugger *)baton;
|
|
|
|
|
|
|
|
// If we have a regular file, a symbolic link or unknown file type, try
|
|
|
|
// and process the file. We must handle unknown as sometimes the directory
|
|
|
|
// enumeration might be enumerating a file system that doesn't have correct
|
|
|
|
// file type information.
|
|
|
|
if (file_type == FileSpec::eFileTypeRegular ||
|
|
|
|
file_type == FileSpec::eFileTypeSymbolicLink ||
|
|
|
|
file_type == FileSpec::eFileTypeUnknown) {
|
|
|
|
FileSpec plugin_file_spec(file_spec);
|
|
|
|
plugin_file_spec.ResolvePath();
|
|
|
|
|
|
|
|
if (plugin_file_spec.GetFileNameExtension() != g_dylibext &&
|
|
|
|
plugin_file_spec.GetFileNameExtension() != g_solibext) {
|
|
|
|
return FileSpec::eEnumerateDirectoryResultNext;
|
2012-09-29 07:57:51 +08:00
|
|
|
}
|
|
|
|
|
2016-09-07 04:57:50 +08:00
|
|
|
Error plugin_load_error;
|
|
|
|
debugger->LoadPlugin(plugin_file_spec, plugin_load_error);
|
|
|
|
|
|
|
|
return FileSpec::eEnumerateDirectoryResultNext;
|
|
|
|
} else if (file_type == FileSpec::eFileTypeUnknown ||
|
|
|
|
file_type == FileSpec::eFileTypeDirectory ||
|
|
|
|
file_type == FileSpec::eFileTypeSymbolicLink) {
|
|
|
|
// Try and recurse into anything that a directory or symbolic link.
|
|
|
|
// We must also do this for unknown as sometimes the directory enumeration
|
|
|
|
// might be enumerating a file system that doesn't have correct file type
|
|
|
|
// information.
|
|
|
|
return FileSpec::eEnumerateDirectoryResultEnter;
|
|
|
|
}
|
|
|
|
|
|
|
|
return FileSpec::eEnumerateDirectoryResultNext;
|
|
|
|
}
|
|
|
|
|
|
|
|
void Debugger::InstanceInitialize() {
|
|
|
|
FileSpec dir_spec;
|
|
|
|
const bool find_directories = true;
|
|
|
|
const bool find_files = true;
|
|
|
|
const bool find_other = true;
|
|
|
|
char dir_path[PATH_MAX];
|
|
|
|
if (HostInfo::GetLLDBPath(ePathTypeLLDBSystemPlugins, dir_spec)) {
|
|
|
|
if (dir_spec.Exists() && dir_spec.GetPath(dir_path, sizeof(dir_path))) {
|
|
|
|
FileSpec::EnumerateDirectory(dir_path, find_directories, find_files,
|
|
|
|
find_other, LoadPluginCallback, this);
|
2012-09-29 07:57:51 +08:00
|
|
|
}
|
2016-09-07 04:57:50 +08:00
|
|
|
}
|
2014-08-22 01:29:12 +08:00
|
|
|
|
2016-09-07 04:57:50 +08:00
|
|
|
if (HostInfo::GetLLDBPath(ePathTypeLLDBUserPlugins, dir_spec)) {
|
|
|
|
if (dir_spec.Exists() && dir_spec.GetPath(dir_path, sizeof(dir_path))) {
|
|
|
|
FileSpec::EnumerateDirectory(dir_path, find_directories, find_files,
|
|
|
|
find_other, LoadPluginCallback, this);
|
2012-09-29 07:57:51 +08:00
|
|
|
}
|
2016-09-07 04:57:50 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
PluginManager::DebuggerInitialize(*this);
|
2012-09-29 07:57:51 +08:00
|
|
|
}
|
|
|
|
|
2016-09-07 04:57:50 +08:00
|
|
|
DebuggerSP Debugger::CreateInstance(lldb::LogOutputCallback log_callback,
|
|
|
|
void *baton) {
|
|
|
|
DebuggerSP debugger_sp(new Debugger(log_callback, baton));
|
|
|
|
if (g_debugger_list_ptr && g_debugger_list_mutex_ptr) {
|
|
|
|
std::lock_guard<std::recursive_mutex> guard(*g_debugger_list_mutex_ptr);
|
|
|
|
g_debugger_list_ptr->push_back(debugger_sp);
|
|
|
|
}
|
|
|
|
debugger_sp->InstanceInitialize();
|
|
|
|
return debugger_sp;
|
2010-06-23 09:19:29 +08:00
|
|
|
}
|
|
|
|
|
2016-09-07 04:57:50 +08:00
|
|
|
void Debugger::Destroy(DebuggerSP &debugger_sp) {
|
|
|
|
if (!debugger_sp)
|
|
|
|
return;
|
2011-09-16 05:36:42 +08:00
|
|
|
|
2016-09-07 04:57:50 +08:00
|
|
|
debugger_sp->Clear();
|
|
|
|
|
|
|
|
if (g_debugger_list_ptr && g_debugger_list_mutex_ptr) {
|
|
|
|
std::lock_guard<std::recursive_mutex> guard(*g_debugger_list_mutex_ptr);
|
|
|
|
DebuggerList::iterator pos, end = g_debugger_list_ptr->end();
|
|
|
|
for (pos = g_debugger_list_ptr->begin(); pos != end; ++pos) {
|
|
|
|
if ((*pos).get() == debugger_sp.get()) {
|
|
|
|
g_debugger_list_ptr->erase(pos);
|
|
|
|
return;
|
|
|
|
}
|
2011-01-22 09:02:07 +08:00
|
|
|
}
|
2016-09-07 04:57:50 +08:00
|
|
|
}
|
2011-01-22 09:02:07 +08:00
|
|
|
}
|
|
|
|
|
2011-09-17 16:33:22 +08:00
|
|
|
DebuggerSP
|
2016-09-07 04:57:50 +08:00
|
|
|
Debugger::FindDebuggerWithInstanceName(const ConstString &instance_name) {
|
|
|
|
DebuggerSP debugger_sp;
|
|
|
|
if (g_debugger_list_ptr && g_debugger_list_mutex_ptr) {
|
|
|
|
std::lock_guard<std::recursive_mutex> guard(*g_debugger_list_mutex_ptr);
|
|
|
|
DebuggerList::iterator pos, end = g_debugger_list_ptr->end();
|
|
|
|
for (pos = g_debugger_list_ptr->begin(); pos != end; ++pos) {
|
|
|
|
if ((*pos)->m_instance_name == instance_name) {
|
|
|
|
debugger_sp = *pos;
|
|
|
|
break;
|
|
|
|
}
|
2012-08-23 02:39:03 +08:00
|
|
|
}
|
2016-09-07 04:57:50 +08:00
|
|
|
}
|
|
|
|
return debugger_sp;
|
|
|
|
}
|
|
|
|
|
|
|
|
TargetSP Debugger::FindTargetWithProcessID(lldb::pid_t pid) {
|
|
|
|
TargetSP target_sp;
|
|
|
|
if (g_debugger_list_ptr && g_debugger_list_mutex_ptr) {
|
|
|
|
std::lock_guard<std::recursive_mutex> guard(*g_debugger_list_mutex_ptr);
|
|
|
|
DebuggerList::iterator pos, end = g_debugger_list_ptr->end();
|
|
|
|
for (pos = g_debugger_list_ptr->begin(); pos != end; ++pos) {
|
|
|
|
target_sp = (*pos)->GetTargetList().FindTargetWithProcessID(pid);
|
|
|
|
if (target_sp)
|
|
|
|
break;
|
2010-06-23 09:19:29 +08:00
|
|
|
}
|
2016-09-07 04:57:50 +08:00
|
|
|
}
|
|
|
|
return target_sp;
|
|
|
|
}
|
|
|
|
|
|
|
|
TargetSP Debugger::FindTargetWithProcess(Process *process) {
|
|
|
|
TargetSP target_sp;
|
|
|
|
if (g_debugger_list_ptr && g_debugger_list_mutex_ptr) {
|
|
|
|
std::lock_guard<std::recursive_mutex> guard(*g_debugger_list_mutex_ptr);
|
|
|
|
DebuggerList::iterator pos, end = g_debugger_list_ptr->end();
|
|
|
|
for (pos = g_debugger_list_ptr->begin(); pos != end; ++pos) {
|
|
|
|
target_sp = (*pos)->GetTargetList().FindTargetWithProcess(process);
|
|
|
|
if (target_sp)
|
|
|
|
break;
|
2011-11-16 13:37:56 +08:00
|
|
|
}
|
2016-09-07 04:57:50 +08:00
|
|
|
}
|
|
|
|
return target_sp;
|
|
|
|
}
|
|
|
|
|
|
|
|
Debugger::Debugger(lldb::LogOutputCallback log_callback, void *baton)
|
|
|
|
: UserID(g_unique_id++),
|
|
|
|
Properties(OptionValuePropertiesSP(new OptionValueProperties())),
|
|
|
|
m_input_file_sp(new StreamFile(stdin, false)),
|
|
|
|
m_output_file_sp(new StreamFile(stdout, false)),
|
|
|
|
m_error_file_sp(new StreamFile(stderr, false)),
|
|
|
|
m_broadcaster_manager_sp(BroadcasterManager::MakeBroadcasterManager()),
|
|
|
|
m_terminal_state(), m_target_list(*this), m_platform_list(),
|
|
|
|
m_listener_sp(Listener::MakeListener("lldb.Debugger")),
|
|
|
|
m_source_manager_ap(), m_source_file_cache(),
|
|
|
|
m_command_interpreter_ap(
|
|
|
|
new CommandInterpreter(*this, eScriptLanguageDefault, false)),
|
|
|
|
m_input_reader_stack(), m_instance_name(), m_loaded_plugins(),
|
|
|
|
m_event_handler_thread(), m_io_handler_thread(),
|
|
|
|
m_sync_broadcaster(nullptr, "lldb.debugger.sync"),
|
|
|
|
m_forward_listener_sp(), m_clear_once() {
|
|
|
|
char instance_cstr[256];
|
|
|
|
snprintf(instance_cstr, sizeof(instance_cstr), "debugger_%d", (int)GetID());
|
|
|
|
m_instance_name.SetCString(instance_cstr);
|
|
|
|
if (log_callback)
|
|
|
|
m_log_callback_stream_sp.reset(new StreamCallback(log_callback, baton));
|
|
|
|
m_command_interpreter_ap->Initialize();
|
|
|
|
// Always add our default platform to the platform list
|
|
|
|
PlatformSP default_platform_sp(Platform::GetHostPlatform());
|
|
|
|
assert(default_platform_sp);
|
|
|
|
m_platform_list.Append(default_platform_sp, true);
|
|
|
|
|
|
|
|
m_collection_sp->Initialize(g_properties);
|
|
|
|
m_collection_sp->AppendProperty(
|
|
|
|
ConstString("target"),
|
|
|
|
ConstString("Settings specify to debugging targets."), true,
|
|
|
|
Target::GetGlobalProperties()->GetValueProperties());
|
|
|
|
m_collection_sp->AppendProperty(
|
|
|
|
ConstString("platform"), ConstString("Platform settings."), true,
|
|
|
|
Platform::GetGlobalPlatformProperties()->GetValueProperties());
|
|
|
|
if (m_command_interpreter_ap) {
|
|
|
|
m_collection_sp->AppendProperty(
|
|
|
|
ConstString("interpreter"),
|
|
|
|
ConstString("Settings specify to the debugger's command interpreter."),
|
|
|
|
true, m_command_interpreter_ap->GetValueProperties());
|
|
|
|
}
|
|
|
|
OptionValueSInt64 *term_width =
|
|
|
|
m_collection_sp->GetPropertyAtIndexAsOptionValueSInt64(
|
|
|
|
nullptr, ePropertyTerminalWidth);
|
|
|
|
term_width->SetMinimumValue(10);
|
|
|
|
term_width->SetMaximumValue(1024);
|
|
|
|
|
|
|
|
// Turn off use-color if this is a dumb terminal.
|
|
|
|
const char *term = getenv("TERM");
|
|
|
|
if (term && !strcmp(term, "dumb"))
|
|
|
|
SetUseColor(false);
|
|
|
|
}
|
|
|
|
|
|
|
|
Debugger::~Debugger() { Clear(); }
|
|
|
|
|
|
|
|
void Debugger::Clear() {
|
|
|
|
//----------------------------------------------------------------------
|
|
|
|
// Make sure we call this function only once. With the C++ global
|
|
|
|
// destructor chain having a list of debuggers and with code that can be
|
|
|
|
// running on other threads, we need to ensure this doesn't happen
|
|
|
|
// multiple times.
|
|
|
|
//
|
|
|
|
// The following functions call Debugger::Clear():
|
|
|
|
// Debugger::~Debugger();
|
|
|
|
// static void Debugger::Destroy(lldb::DebuggerSP &debugger_sp);
|
|
|
|
// static void Debugger::Terminate();
|
|
|
|
//----------------------------------------------------------------------
|
|
|
|
std::call_once(m_clear_once, [this]() {
|
|
|
|
ClearIOHandlers();
|
|
|
|
StopIOHandlerThread();
|
|
|
|
StopEventHandlerThread();
|
|
|
|
m_listener_sp->Clear();
|
|
|
|
int num_targets = m_target_list.GetNumTargets();
|
|
|
|
for (int i = 0; i < num_targets; i++) {
|
|
|
|
TargetSP target_sp(m_target_list.GetTargetAtIndex(i));
|
|
|
|
if (target_sp) {
|
|
|
|
ProcessSP process_sp(target_sp->GetProcessSP());
|
|
|
|
if (process_sp)
|
|
|
|
process_sp->Finalize();
|
|
|
|
target_sp->Destroy();
|
|
|
|
}
|
2012-08-23 08:22:02 +08:00
|
|
|
}
|
2016-09-07 04:57:50 +08:00
|
|
|
m_broadcaster_manager_sp->Clear();
|
2013-05-24 04:47:45 +08:00
|
|
|
|
2016-09-07 04:57:50 +08:00
|
|
|
// Close the input file _before_ we close the input read communications
|
|
|
|
// class
|
|
|
|
// as it does NOT own the input file, our m_input_file does.
|
|
|
|
m_terminal_state.Clear();
|
|
|
|
if (m_input_file_sp)
|
|
|
|
m_input_file_sp->GetFile().Close();
|
|
|
|
|
|
|
|
m_command_interpreter_ap->Clear();
|
|
|
|
});
|
2010-06-09 00:52:24 +08:00
|
|
|
}
|
|
|
|
|
2016-09-07 04:57:50 +08:00
|
|
|
bool Debugger::GetCloseInputOnEOF() const {
|
|
|
|
// return m_input_comm.GetCloseOnEOF();
|
|
|
|
return false;
|
2011-09-16 05:36:42 +08:00
|
|
|
}
|
|
|
|
|
2016-09-07 04:57:50 +08:00
|
|
|
void Debugger::SetCloseInputOnEOF(bool b) {
|
|
|
|
// m_input_comm.SetCloseOnEOF(b);
|
2011-05-29 12:06:55 +08:00
|
|
|
}
|
|
|
|
|
2016-09-07 04:57:50 +08:00
|
|
|
bool Debugger::GetAsyncExecution() {
|
|
|
|
return !m_command_interpreter_ap->GetSynchronous();
|
2011-05-29 12:06:55 +08:00
|
|
|
}
|
|
|
|
|
2016-09-07 04:57:50 +08:00
|
|
|
void Debugger::SetAsyncExecution(bool async_execution) {
|
|
|
|
m_command_interpreter_ap->SetSynchronous(!async_execution);
|
2010-06-09 00:52:24 +08:00
|
|
|
}
|
|
|
|
|
2016-09-07 04:57:50 +08:00
|
|
|
void Debugger::SetInputFileHandle(FILE *fh, bool tranfer_ownership) {
|
|
|
|
if (m_input_file_sp)
|
|
|
|
m_input_file_sp->GetFile().SetStream(fh, tranfer_ownership);
|
|
|
|
else
|
|
|
|
m_input_file_sp.reset(new StreamFile(fh, tranfer_ownership));
|
|
|
|
|
|
|
|
File &in_file = m_input_file_sp->GetFile();
|
|
|
|
if (!in_file.IsValid())
|
|
|
|
in_file.SetStream(stdin, true);
|
|
|
|
|
|
|
|
// Save away the terminal state if that is relevant, so that we can restore it
|
|
|
|
// in RestoreInputState.
|
|
|
|
SaveInputTerminalState();
|
2010-06-09 00:52:24 +08:00
|
|
|
}
|
|
|
|
|
2016-09-07 04:57:50 +08:00
|
|
|
void Debugger::SetOutputFileHandle(FILE *fh, bool tranfer_ownership) {
|
|
|
|
if (m_output_file_sp)
|
|
|
|
m_output_file_sp->GetFile().SetStream(fh, tranfer_ownership);
|
|
|
|
else
|
|
|
|
m_output_file_sp.reset(new StreamFile(fh, tranfer_ownership));
|
2014-01-28 07:43:24 +08:00
|
|
|
|
2016-09-07 04:57:50 +08:00
|
|
|
File &out_file = m_output_file_sp->GetFile();
|
|
|
|
if (!out_file.IsValid())
|
|
|
|
out_file.SetStream(stdout, false);
|
2010-06-09 00:52:24 +08:00
|
|
|
|
2016-09-07 04:57:50 +08:00
|
|
|
// do not create the ScriptInterpreter just for setting the output file handle
|
|
|
|
// as the constructor will know how to do the right thing on its own
|
|
|
|
const bool can_create = false;
|
|
|
|
ScriptInterpreter *script_interpreter =
|
|
|
|
GetCommandInterpreter().GetScriptInterpreter(can_create);
|
|
|
|
if (script_interpreter)
|
|
|
|
script_interpreter->ResetOutputFileHandle(fh);
|
2010-06-09 00:52:24 +08:00
|
|
|
}
|
|
|
|
|
2016-09-07 04:57:50 +08:00
|
|
|
void Debugger::SetErrorFileHandle(FILE *fh, bool tranfer_ownership) {
|
|
|
|
if (m_error_file_sp)
|
|
|
|
m_error_file_sp->GetFile().SetStream(fh, tranfer_ownership);
|
|
|
|
else
|
|
|
|
m_error_file_sp.reset(new StreamFile(fh, tranfer_ownership));
|
|
|
|
|
|
|
|
File &err_file = m_error_file_sp->GetFile();
|
|
|
|
if (!err_file.IsValid())
|
|
|
|
err_file.SetStream(stderr, false);
|
2010-06-09 00:52:24 +08:00
|
|
|
}
|
|
|
|
|
2016-09-07 04:57:50 +08:00
|
|
|
void Debugger::SaveInputTerminalState() {
|
|
|
|
if (m_input_file_sp) {
|
|
|
|
File &in_file = m_input_file_sp->GetFile();
|
|
|
|
if (in_file.GetDescriptor() != File::kInvalidDescriptor)
|
|
|
|
m_terminal_state.Save(in_file.GetDescriptor(), true);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void Debugger::RestoreInputTerminalState() { m_terminal_state.Restore(); }
|
|
|
|
|
|
|
|
ExecutionContext Debugger::GetSelectedExecutionContext() {
|
|
|
|
ExecutionContext exe_ctx;
|
|
|
|
TargetSP target_sp(GetSelectedTarget());
|
|
|
|
exe_ctx.SetTargetSP(target_sp);
|
|
|
|
|
|
|
|
if (target_sp) {
|
|
|
|
ProcessSP process_sp(target_sp->GetProcessSP());
|
|
|
|
exe_ctx.SetProcessSP(process_sp);
|
|
|
|
if (process_sp && !process_sp->IsRunning()) {
|
|
|
|
ThreadSP thread_sp(process_sp->GetThreadList().GetSelectedThread());
|
|
|
|
if (thread_sp) {
|
|
|
|
exe_ctx.SetThreadSP(thread_sp);
|
|
|
|
exe_ctx.SetFrameSP(thread_sp->GetSelectedFrame());
|
|
|
|
if (exe_ctx.GetFramePtr() == nullptr)
|
|
|
|
exe_ctx.SetFrameSP(thread_sp->GetStackFrameAtIndex(0));
|
|
|
|
}
|
2014-01-28 07:43:24 +08:00
|
|
|
}
|
2016-09-07 04:57:50 +08:00
|
|
|
}
|
|
|
|
return exe_ctx;
|
2012-12-01 04:23:19 +08:00
|
|
|
}
|
|
|
|
|
2016-09-07 04:57:50 +08:00
|
|
|
void Debugger::DispatchInputInterrupt() {
|
|
|
|
std::lock_guard<std::recursive_mutex> guard(m_input_reader_stack.GetMutex());
|
|
|
|
IOHandlerSP reader_sp(m_input_reader_stack.Top());
|
|
|
|
if (reader_sp)
|
|
|
|
reader_sp->Interrupt();
|
2012-12-01 04:23:19 +08:00
|
|
|
}
|
|
|
|
|
2016-09-07 04:57:50 +08:00
|
|
|
void Debugger::DispatchInputEndOfFile() {
|
|
|
|
std::lock_guard<std::recursive_mutex> guard(m_input_reader_stack.GetMutex());
|
|
|
|
IOHandlerSP reader_sp(m_input_reader_stack.Top());
|
|
|
|
if (reader_sp)
|
|
|
|
reader_sp->GotEOF();
|
2010-06-09 00:52:24 +08:00
|
|
|
}
|
|
|
|
|
2016-09-07 04:57:50 +08:00
|
|
|
void Debugger::ClearIOHandlers() {
|
|
|
|
// The bottom input reader should be the main debugger input reader. We do
|
|
|
|
// not want to close that one here.
|
|
|
|
std::lock_guard<std::recursive_mutex> guard(m_input_reader_stack.GetMutex());
|
|
|
|
while (m_input_reader_stack.GetSize() > 1) {
|
2016-05-18 09:59:10 +08:00
|
|
|
IOHandlerSP reader_sp(m_input_reader_stack.Top());
|
2011-02-10 09:15:13 +08:00
|
|
|
if (reader_sp)
|
2016-09-07 04:57:50 +08:00
|
|
|
PopIOHandler(reader_sp);
|
|
|
|
}
|
2010-11-20 04:47:54 +08:00
|
|
|
}
|
|
|
|
|
2016-09-07 04:57:50 +08:00
|
|
|
void Debugger::ExecuteIOHandlers() {
|
|
|
|
while (true) {
|
2016-05-18 09:59:10 +08:00
|
|
|
IOHandlerSP reader_sp(m_input_reader_stack.Top());
|
2016-09-07 04:57:50 +08:00
|
|
|
if (!reader_sp)
|
|
|
|
break;
|
2010-11-20 04:47:54 +08:00
|
|
|
|
2016-09-07 04:57:50 +08:00
|
|
|
reader_sp->Run();
|
2010-12-21 02:35:50 +08:00
|
|
|
|
2016-09-07 04:57:50 +08:00
|
|
|
// Remove all input readers that are done from the top of the stack
|
|
|
|
while (true) {
|
|
|
|
IOHandlerSP top_reader_sp = m_input_reader_stack.Top();
|
|
|
|
if (top_reader_sp && top_reader_sp->GetIsDone())
|
|
|
|
PopIOHandler(top_reader_sp);
|
|
|
|
else
|
|
|
|
break;
|
This patch captures and serializes all output being written by the
command line driver, including the lldb prompt being output by
editline, the asynchronous process output & error messages, and
asynchronous messages written by target stop-hooks.
As part of this it introduces a new Stream class,
StreamAsynchronousIO. A StreamAsynchronousIO object is created with a
broadcaster, who will eventually broadcast the stream's data for a
listener to handle, and an event type indicating what type of event
the broadcaster will broadcast. When the Write method is called on a
StreamAsynchronousIO object, the data is appended to an internal
string. When the Flush method is called on a StreamAsynchronousIO
object, it broadcasts it's data string and clears the string.
Anything in lldb-core that needs to generate asynchronous output for
the end-user should use the StreamAsynchronousIO objects.
I have also added a new notification type for InputReaders, to let
them know that a asynchronous output has been written. This is to
allow the input readers to, for example, refresh their prompts and
lines, if desired. I added the case statements to all the input
readers to catch this notification, but I haven't added any code for
handling them yet (except to the IOChannel input reader).
llvm-svn: 130721
2011-05-03 04:41:46 +08:00
|
|
|
}
|
2016-09-07 04:57:50 +08:00
|
|
|
}
|
|
|
|
ClearIOHandlers();
|
This patch captures and serializes all output being written by the
command line driver, including the lldb prompt being output by
editline, the asynchronous process output & error messages, and
asynchronous messages written by target stop-hooks.
As part of this it introduces a new Stream class,
StreamAsynchronousIO. A StreamAsynchronousIO object is created with a
broadcaster, who will eventually broadcast the stream's data for a
listener to handle, and an event type indicating what type of event
the broadcaster will broadcast. When the Write method is called on a
StreamAsynchronousIO object, the data is appended to an internal
string. When the Flush method is called on a StreamAsynchronousIO
object, it broadcasts it's data string and clears the string.
Anything in lldb-core that needs to generate asynchronous output for
the end-user should use the StreamAsynchronousIO objects.
I have also added a new notification type for InputReaders, to let
them know that a asynchronous output has been written. This is to
allow the input readers to, for example, refresh their prompts and
lines, if desired. I added the case statements to all the input
readers to catch this notification, but I haven't added any code for
handling them yet (except to the IOChannel input reader).
llvm-svn: 130721
2011-05-03 04:41:46 +08:00
|
|
|
}
|
|
|
|
|
2016-09-07 04:57:50 +08:00
|
|
|
bool Debugger::IsTopIOHandler(const lldb::IOHandlerSP &reader_sp) {
|
|
|
|
return m_input_reader_stack.IsTop(reader_sp);
|
2014-01-28 07:43:24 +08:00
|
|
|
}
|
|
|
|
|
2016-09-07 04:57:50 +08:00
|
|
|
bool Debugger::CheckTopIOHandlerTypes(IOHandler::Type top_type,
|
|
|
|
IOHandler::Type second_top_type) {
|
|
|
|
return m_input_reader_stack.CheckTopIOHandlerTypes(top_type, second_top_type);
|
2015-10-20 07:11:07 +08:00
|
|
|
}
|
|
|
|
|
2016-09-07 04:57:50 +08:00
|
|
|
void Debugger::PrintAsync(const char *s, size_t len, bool is_stdout) {
|
|
|
|
lldb::StreamFileSP stream = is_stdout ? GetOutputFile() : GetErrorFile();
|
|
|
|
m_input_reader_stack.PrintAsync(stream.get(), s, len);
|
2015-05-27 20:40:32 +08:00
|
|
|
}
|
2011-05-10 07:06:58 +08:00
|
|
|
|
2016-09-07 04:57:50 +08:00
|
|
|
ConstString Debugger::GetTopIOHandlerControlSequence(char ch) {
|
|
|
|
return m_input_reader_stack.GetTopIOHandlerControlSequence(ch);
|
2011-05-10 07:06:58 +08:00
|
|
|
}
|
|
|
|
|
2016-09-07 04:57:50 +08:00
|
|
|
const char *Debugger::GetIOHandlerCommandPrefix() {
|
|
|
|
return m_input_reader_stack.GetTopIOHandlerCommandPrefix();
|
2015-01-15 08:52:41 +08:00
|
|
|
}
|
|
|
|
|
2016-09-07 04:57:50 +08:00
|
|
|
const char *Debugger::GetIOHandlerHelpPrologue() {
|
|
|
|
return m_input_reader_stack.GetTopIOHandlerHelpPrologue();
|
2015-01-15 08:52:41 +08:00
|
|
|
}
|
|
|
|
|
2016-09-07 04:57:50 +08:00
|
|
|
void Debugger::RunIOHandler(const IOHandlerSP &reader_sp) {
|
|
|
|
PushIOHandler(reader_sp);
|
2015-05-27 20:40:32 +08:00
|
|
|
|
2016-09-07 04:57:50 +08:00
|
|
|
IOHandlerSP top_reader_sp = reader_sp;
|
|
|
|
while (top_reader_sp) {
|
|
|
|
top_reader_sp->Run();
|
2015-05-27 20:40:32 +08:00
|
|
|
|
2016-09-07 04:57:50 +08:00
|
|
|
if (top_reader_sp.get() == reader_sp.get()) {
|
|
|
|
if (PopIOHandler(reader_sp))
|
|
|
|
break;
|
|
|
|
}
|
2015-05-27 20:40:32 +08:00
|
|
|
|
2016-09-07 04:57:50 +08:00
|
|
|
while (true) {
|
|
|
|
top_reader_sp = m_input_reader_stack.Top();
|
|
|
|
if (top_reader_sp && top_reader_sp->GetIsDone())
|
|
|
|
PopIOHandler(top_reader_sp);
|
|
|
|
else
|
|
|
|
break;
|
2014-06-20 08:23:57 +08:00
|
|
|
}
|
2016-09-07 04:57:50 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void Debugger::AdoptTopIOHandlerFilesIfInvalid(StreamFileSP &in,
|
|
|
|
StreamFileSP &out,
|
|
|
|
StreamFileSP &err) {
|
|
|
|
// Before an IOHandler runs, it must have in/out/err streams.
|
|
|
|
// This function is called when one ore more of the streams
|
|
|
|
// are nullptr. We use the top input reader's in/out/err streams,
|
|
|
|
// or fall back to the debugger file handles, or we fall back
|
|
|
|
// onto stdin/stdout/stderr as a last resort.
|
|
|
|
|
|
|
|
std::lock_guard<std::recursive_mutex> guard(m_input_reader_stack.GetMutex());
|
|
|
|
IOHandlerSP top_reader_sp(m_input_reader_stack.Top());
|
|
|
|
// If no STDIN has been set, then set it appropriately
|
|
|
|
if (!in) {
|
|
|
|
if (top_reader_sp)
|
|
|
|
in = top_reader_sp->GetInputStreamFile();
|
|
|
|
else
|
|
|
|
in = GetInputFile();
|
2010-06-09 00:52:24 +08:00
|
|
|
|
2016-09-07 04:57:50 +08:00
|
|
|
// If there is nothing, use stdin
|
2014-01-28 07:43:24 +08:00
|
|
|
if (!in)
|
2016-09-07 04:57:50 +08:00
|
|
|
in = StreamFileSP(new StreamFile(stdin, false));
|
|
|
|
}
|
|
|
|
// If no STDOUT has been set, then set it appropriately
|
|
|
|
if (!out) {
|
|
|
|
if (top_reader_sp)
|
|
|
|
out = top_reader_sp->GetOutputStreamFile();
|
|
|
|
else
|
|
|
|
out = GetOutputFile();
|
|
|
|
|
|
|
|
// If there is nothing, use stdout
|
2014-01-28 07:43:24 +08:00
|
|
|
if (!out)
|
2016-09-07 04:57:50 +08:00
|
|
|
out = StreamFileSP(new StreamFile(stdout, false));
|
|
|
|
}
|
|
|
|
// If no STDERR has been set, then set it appropriately
|
|
|
|
if (!err) {
|
|
|
|
if (top_reader_sp)
|
|
|
|
err = top_reader_sp->GetErrorStreamFile();
|
|
|
|
else
|
|
|
|
err = GetErrorFile();
|
|
|
|
|
|
|
|
// If there is nothing, use stderr
|
2014-01-28 07:43:24 +08:00
|
|
|
if (!err)
|
2016-09-07 04:57:50 +08:00
|
|
|
err = StreamFileSP(new StreamFile(stdout, false));
|
|
|
|
}
|
2010-06-09 00:52:24 +08:00
|
|
|
}
|
|
|
|
|
2016-09-07 04:57:50 +08:00
|
|
|
void Debugger::PushIOHandler(const IOHandlerSP &reader_sp) {
|
|
|
|
if (!reader_sp)
|
|
|
|
return;
|
2016-05-18 09:59:10 +08:00
|
|
|
|
2016-09-07 04:57:50 +08:00
|
|
|
std::lock_guard<std::recursive_mutex> guard(m_input_reader_stack.GetMutex());
|
2015-05-27 20:40:32 +08:00
|
|
|
|
2016-09-07 04:57:50 +08:00
|
|
|
// Get the current top input reader...
|
|
|
|
IOHandlerSP top_reader_sp(m_input_reader_stack.Top());
|
2016-05-18 09:59:10 +08:00
|
|
|
|
2016-09-07 04:57:50 +08:00
|
|
|
// Don't push the same IO handler twice...
|
|
|
|
if (reader_sp == top_reader_sp)
|
|
|
|
return;
|
2014-01-28 07:43:24 +08:00
|
|
|
|
2016-09-07 04:57:50 +08:00
|
|
|
// Push our new input reader
|
|
|
|
m_input_reader_stack.Push(reader_sp);
|
|
|
|
reader_sp->Activate();
|
2015-05-27 20:40:32 +08:00
|
|
|
|
2016-09-07 04:57:50 +08:00
|
|
|
// Interrupt the top input reader to it will exit its Run() function
|
|
|
|
// and let this new input reader take over
|
|
|
|
if (top_reader_sp) {
|
|
|
|
top_reader_sp->Deactivate();
|
|
|
|
top_reader_sp->Cancel();
|
|
|
|
}
|
2010-06-09 00:52:24 +08:00
|
|
|
}
|
|
|
|
|
2016-09-07 04:57:50 +08:00
|
|
|
bool Debugger::PopIOHandler(const IOHandlerSP &pop_reader_sp) {
|
|
|
|
if (!pop_reader_sp)
|
|
|
|
return false;
|
2015-05-27 20:40:32 +08:00
|
|
|
|
2016-09-07 04:57:50 +08:00
|
|
|
std::lock_guard<std::recursive_mutex> guard(m_input_reader_stack.GetMutex());
|
2010-06-09 00:52:24 +08:00
|
|
|
|
2016-09-07 04:57:50 +08:00
|
|
|
// The reader on the stop of the stack is done, so let the next
|
|
|
|
// read on the stack refresh its prompt and if there is one...
|
|
|
|
if (m_input_reader_stack.IsEmpty())
|
|
|
|
return false;
|
2010-06-09 00:52:24 +08:00
|
|
|
|
2016-09-07 04:57:50 +08:00
|
|
|
IOHandlerSP reader_sp(m_input_reader_stack.Top());
|
2015-05-27 20:40:32 +08:00
|
|
|
|
2016-09-07 04:57:50 +08:00
|
|
|
if (pop_reader_sp != reader_sp)
|
|
|
|
return false;
|
2015-05-27 20:40:32 +08:00
|
|
|
|
2016-09-07 04:57:50 +08:00
|
|
|
reader_sp->Deactivate();
|
|
|
|
reader_sp->Cancel();
|
|
|
|
m_input_reader_stack.Pop();
|
2015-05-27 20:40:32 +08:00
|
|
|
|
2016-09-07 04:57:50 +08:00
|
|
|
reader_sp = m_input_reader_stack.Top();
|
|
|
|
if (reader_sp)
|
|
|
|
reader_sp->Activate();
|
2010-06-23 09:19:29 +08:00
|
|
|
|
2016-09-07 04:57:50 +08:00
|
|
|
return true;
|
2015-05-27 20:40:32 +08:00
|
|
|
}
|
2014-01-28 07:43:24 +08:00
|
|
|
|
2016-09-07 04:57:50 +08:00
|
|
|
StreamSP Debugger::GetAsyncOutputStream() {
|
|
|
|
return StreamSP(new StreamAsynchronousIO(*this, true));
|
2011-06-03 07:58:26 +08:00
|
|
|
}
|
|
|
|
|
2016-09-07 04:57:50 +08:00
|
|
|
StreamSP Debugger::GetAsyncErrorStream() {
|
|
|
|
return StreamSP(new StreamAsynchronousIO(*this, false));
|
|
|
|
}
|
2011-06-03 07:58:26 +08:00
|
|
|
|
2016-09-07 04:57:50 +08:00
|
|
|
size_t Debugger::GetNumDebuggers() {
|
|
|
|
if (g_debugger_list_ptr && g_debugger_list_mutex_ptr) {
|
|
|
|
std::lock_guard<std::recursive_mutex> guard(*g_debugger_list_mutex_ptr);
|
|
|
|
return g_debugger_list_ptr->size();
|
|
|
|
}
|
|
|
|
return 0;
|
2012-02-15 10:34:21 +08:00
|
|
|
}
|
|
|
|
|
2016-09-07 04:57:50 +08:00
|
|
|
lldb::DebuggerSP Debugger::GetDebuggerAtIndex(size_t index) {
|
|
|
|
DebuggerSP debugger_sp;
|
|
|
|
|
|
|
|
if (g_debugger_list_ptr && g_debugger_list_mutex_ptr) {
|
|
|
|
std::lock_guard<std::recursive_mutex> guard(*g_debugger_list_mutex_ptr);
|
|
|
|
if (index < g_debugger_list_ptr->size())
|
|
|
|
debugger_sp = g_debugger_list_ptr->at(index);
|
|
|
|
}
|
2012-03-31 04:53:46 +08:00
|
|
|
|
2016-09-07 04:57:50 +08:00
|
|
|
return debugger_sp;
|
2012-02-15 10:34:21 +08:00
|
|
|
}
|
|
|
|
|
2016-09-07 04:57:50 +08:00
|
|
|
DebuggerSP Debugger::FindDebuggerWithID(lldb::user_id_t id) {
|
|
|
|
DebuggerSP debugger_sp;
|
2010-07-01 00:22:25 +08:00
|
|
|
|
2016-09-07 04:57:50 +08:00
|
|
|
if (g_debugger_list_ptr && g_debugger_list_mutex_ptr) {
|
|
|
|
std::lock_guard<std::recursive_mutex> guard(*g_debugger_list_mutex_ptr);
|
|
|
|
DebuggerList::iterator pos, end = g_debugger_list_ptr->end();
|
|
|
|
for (pos = g_debugger_list_ptr->begin(); pos != end; ++pos) {
|
|
|
|
if ((*pos)->GetID() == id) {
|
|
|
|
debugger_sp = *pos;
|
|
|
|
break;
|
|
|
|
}
|
2010-07-01 00:22:25 +08:00
|
|
|
}
|
2016-09-07 04:57:50 +08:00
|
|
|
}
|
|
|
|
return debugger_sp;
|
2010-07-01 00:22:25 +08:00
|
|
|
}
|
2010-09-04 08:03:46 +08:00
|
|
|
|
2014-03-20 14:08:21 +08:00
|
|
|
#if 0
|
2010-09-19 10:33:57 +08:00
|
|
|
static void
|
2013-11-04 17:33:30 +08:00
|
|
|
TestPromptFormats (StackFrame *frame)
|
2010-09-19 10:33:57 +08:00
|
|
|
{
|
2016-03-02 10:18:18 +08:00
|
|
|
if (frame == nullptr)
|
2010-09-19 10:33:57 +08:00
|
|
|
return;
|
|
|
|
|
|
|
|
StreamString s;
|
|
|
|
const char *prompt_format =
|
|
|
|
"{addr = '${addr}'\n}"
|
2014-10-11 07:07:36 +08:00
|
|
|
"{addr-file-or-load = '${addr-file-or-load}'\n}"
|
|
|
|
"{current-pc-arrow = '${current-pc-arrow}'\n}"
|
2010-09-19 10:33:57 +08:00
|
|
|
"{process.id = '${process.id}'\n}"
|
|
|
|
"{process.name = '${process.name}'\n}"
|
|
|
|
"{process.file.basename = '${process.file.basename}'\n}"
|
|
|
|
"{process.file.fullpath = '${process.file.fullpath}'\n}"
|
|
|
|
"{thread.id = '${thread.id}'\n}"
|
|
|
|
"{thread.index = '${thread.index}'\n}"
|
|
|
|
"{thread.name = '${thread.name}'\n}"
|
|
|
|
"{thread.queue = '${thread.queue}'\n}"
|
|
|
|
"{thread.stop-reason = '${thread.stop-reason}'\n}"
|
|
|
|
"{target.arch = '${target.arch}'\n}"
|
|
|
|
"{module.file.basename = '${module.file.basename}'\n}"
|
|
|
|
"{module.file.fullpath = '${module.file.fullpath}'\n}"
|
|
|
|
"{file.basename = '${file.basename}'\n}"
|
|
|
|
"{file.fullpath = '${file.fullpath}'\n}"
|
|
|
|
"{frame.index = '${frame.index}'\n}"
|
|
|
|
"{frame.pc = '${frame.pc}'\n}"
|
|
|
|
"{frame.sp = '${frame.sp}'\n}"
|
|
|
|
"{frame.fp = '${frame.fp}'\n}"
|
|
|
|
"{frame.flags = '${frame.flags}'\n}"
|
|
|
|
"{frame.reg.rdi = '${frame.reg.rdi}'\n}"
|
|
|
|
"{frame.reg.rip = '${frame.reg.rip}'\n}"
|
|
|
|
"{frame.reg.rsp = '${frame.reg.rsp}'\n}"
|
|
|
|
"{frame.reg.rbp = '${frame.reg.rbp}'\n}"
|
|
|
|
"{frame.reg.rflags = '${frame.reg.rflags}'\n}"
|
|
|
|
"{frame.reg.xmm0 = '${frame.reg.xmm0}'\n}"
|
|
|
|
"{frame.reg.carp = '${frame.reg.carp}'\n}"
|
|
|
|
"{function.id = '${function.id}'\n}"
|
2014-10-11 07:07:36 +08:00
|
|
|
"{function.changed = '${function.changed}'\n}"
|
|
|
|
"{function.initial-function = '${function.initial-function}'\n}"
|
2010-09-19 10:33:57 +08:00
|
|
|
"{function.name = '${function.name}'\n}"
|
2014-10-11 07:07:36 +08:00
|
|
|
"{function.name-without-args = '${function.name-without-args}'\n}"
|
2012-01-15 01:04:19 +08:00
|
|
|
"{function.name-with-args = '${function.name-with-args}'\n}"
|
2010-09-19 10:33:57 +08:00
|
|
|
"{function.addr-offset = '${function.addr-offset}'\n}"
|
2014-10-11 07:07:36 +08:00
|
|
|
"{function.concrete-only-addr-offset-no-padding = '${function.concrete-only-addr-offset-no-padding}'\n}"
|
2010-09-19 10:33:57 +08:00
|
|
|
"{function.line-offset = '${function.line-offset}'\n}"
|
|
|
|
"{function.pc-offset = '${function.pc-offset}'\n}"
|
|
|
|
"{line.file.basename = '${line.file.basename}'\n}"
|
|
|
|
"{line.file.fullpath = '${line.file.fullpath}'\n}"
|
|
|
|
"{line.number = '${line.number}'\n}"
|
|
|
|
"{line.start-addr = '${line.start-addr}'\n}"
|
|
|
|
"{line.end-addr = '${line.end-addr}'\n}"
|
|
|
|
;
|
|
|
|
|
|
|
|
SymbolContext sc (frame->GetSymbolContext(eSymbolContextEverything));
|
|
|
|
ExecutionContext exe_ctx;
|
2010-10-04 09:05:56 +08:00
|
|
|
frame->CalculateExecutionContext(exe_ctx);
|
2013-05-24 04:47:45 +08:00
|
|
|
if (Debugger::FormatPrompt (prompt_format, &sc, &exe_ctx, &sc.line_entry.range.GetBaseAddress(), s))
|
2010-09-19 10:33:57 +08:00
|
|
|
{
|
|
|
|
printf("%s\n", s.GetData());
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
printf ("what we got: %s\n", s.GetData());
|
|
|
|
}
|
|
|
|
}
|
2014-03-20 14:08:21 +08:00
|
|
|
#endif
|
2010-09-19 10:33:57 +08:00
|
|
|
|
2016-09-07 04:57:50 +08:00
|
|
|
bool Debugger::FormatDisassemblerAddress(const FormatEntity::Entry *format,
|
|
|
|
const SymbolContext *sc,
|
|
|
|
const SymbolContext *prev_sc,
|
|
|
|
const ExecutionContext *exe_ctx,
|
|
|
|
const Address *addr, Stream &s) {
|
|
|
|
FormatEntity::Entry format_entry;
|
|
|
|
|
|
|
|
if (format == nullptr) {
|
|
|
|
if (exe_ctx != nullptr && exe_ctx->HasTargetScope())
|
|
|
|
format = exe_ctx->GetTargetRef().GetDebugger().GetDisassemblyFormat();
|
|
|
|
if (format == nullptr) {
|
|
|
|
FormatEntity::Parse("${addr}: ", format_entry);
|
|
|
|
format = &format_entry;
|
2014-10-11 07:07:36 +08:00
|
|
|
}
|
2016-09-07 04:57:50 +08:00
|
|
|
}
|
|
|
|
bool function_changed = false;
|
|
|
|
bool initial_function = false;
|
|
|
|
if (prev_sc && (prev_sc->function || prev_sc->symbol)) {
|
|
|
|
if (sc && (sc->function || sc->symbol)) {
|
|
|
|
if (prev_sc->symbol && sc->symbol) {
|
|
|
|
if (!sc->symbol->Compare(prev_sc->symbol->GetName(),
|
|
|
|
prev_sc->symbol->GetType())) {
|
|
|
|
function_changed = true;
|
|
|
|
}
|
|
|
|
} else if (prev_sc->function && sc->function) {
|
|
|
|
if (prev_sc->function->GetMangled() != sc->function->GetMangled()) {
|
|
|
|
function_changed = true;
|
2014-10-11 07:07:36 +08:00
|
|
|
}
|
2016-09-07 04:57:50 +08:00
|
|
|
}
|
2014-10-11 07:07:36 +08:00
|
|
|
}
|
2016-09-07 04:57:50 +08:00
|
|
|
}
|
|
|
|
// The first context on a list of instructions will have a prev_sc that
|
|
|
|
// has no Function or Symbol -- if SymbolContext had an IsValid() method, it
|
|
|
|
// would return false. But we do get a prev_sc pointer.
|
|
|
|
if ((sc && (sc->function || sc->symbol)) && prev_sc &&
|
|
|
|
(prev_sc->function == nullptr && prev_sc->symbol == nullptr)) {
|
|
|
|
initial_function = true;
|
|
|
|
}
|
|
|
|
return FormatEntity::Format(*format, s, sc, exe_ctx, addr, nullptr,
|
|
|
|
function_changed, initial_function);
|
|
|
|
}
|
|
|
|
|
|
|
|
void Debugger::SetLoggingCallback(lldb::LogOutputCallback log_callback,
|
|
|
|
void *baton) {
|
|
|
|
// For simplicity's sake, I am not going to deal with how to close down any
|
|
|
|
// open logging streams, I just redirect everything from here on out to the
|
|
|
|
// callback.
|
|
|
|
m_log_callback_stream_sp.reset(new StreamCallback(log_callback, baton));
|
|
|
|
}
|
|
|
|
|
|
|
|
bool Debugger::EnableLog(const char *channel, const char **categories,
|
|
|
|
const char *log_file, uint32_t log_options,
|
|
|
|
Stream &error_stream) {
|
|
|
|
StreamSP log_stream_sp;
|
|
|
|
if (m_log_callback_stream_sp) {
|
|
|
|
log_stream_sp = m_log_callback_stream_sp;
|
|
|
|
// For now when using the callback mode you always get thread & timestamp.
|
|
|
|
log_options |=
|
|
|
|
LLDB_LOG_OPTION_PREPEND_TIMESTAMP | LLDB_LOG_OPTION_PREPEND_THREAD_NAME;
|
|
|
|
} else if (log_file == nullptr || *log_file == '\0') {
|
|
|
|
log_stream_sp = GetOutputFile();
|
|
|
|
} else {
|
|
|
|
LogStreamMap::iterator pos = m_log_streams.find(log_file);
|
|
|
|
if (pos != m_log_streams.end())
|
|
|
|
log_stream_sp = pos->second.lock();
|
|
|
|
if (!log_stream_sp) {
|
|
|
|
uint32_t options = File::eOpenOptionWrite | File::eOpenOptionCanCreate |
|
|
|
|
File::eOpenOptionCloseOnExec | File::eOpenOptionAppend;
|
|
|
|
if (!(log_options & LLDB_LOG_OPTION_APPEND))
|
|
|
|
options |= File::eOpenOptionTruncate;
|
|
|
|
|
|
|
|
log_stream_sp.reset(new StreamFile(log_file, options));
|
|
|
|
m_log_streams[log_file] = log_stream_sp;
|
2014-10-11 07:07:36 +08:00
|
|
|
}
|
2016-09-07 04:57:50 +08:00
|
|
|
}
|
|
|
|
assert(log_stream_sp);
|
2013-05-24 04:47:45 +08:00
|
|
|
|
2016-09-07 04:57:50 +08:00
|
|
|
if (log_options == 0)
|
|
|
|
log_options =
|
|
|
|
LLDB_LOG_OPTION_PREPEND_THREAD_NAME | LLDB_LOG_OPTION_THREADSAFE;
|
2012-02-21 10:23:08 +08:00
|
|
|
|
2016-09-07 04:57:50 +08:00
|
|
|
return Log::EnableLogChannel(log_stream_sp, log_options, channel, categories,
|
|
|
|
error_stream);
|
2012-02-21 10:23:08 +08:00
|
|
|
}
|
|
|
|
|
2016-09-07 04:57:50 +08:00
|
|
|
SourceManager &Debugger::GetSourceManager() {
|
|
|
|
if (!m_source_manager_ap)
|
|
|
|
m_source_manager_ap.reset(new SourceManager(shared_from_this()));
|
|
|
|
return *m_source_manager_ap;
|
2013-03-19 08:20:55 +08:00
|
|
|
}
|
|
|
|
|
2014-01-28 07:43:24 +08:00
|
|
|
// This function handles events that were broadcast by the process.
|
2016-09-07 04:57:50 +08:00
|
|
|
void Debugger::HandleBreakpointEvent(const EventSP &event_sp) {
|
|
|
|
using namespace lldb;
|
|
|
|
const uint32_t event_type =
|
|
|
|
Breakpoint::BreakpointEventData::GetBreakpointEventTypeFromEvent(
|
|
|
|
event_sp);
|
|
|
|
|
|
|
|
// if (event_type & eBreakpointEventTypeAdded
|
|
|
|
// || event_type & eBreakpointEventTypeRemoved
|
|
|
|
// || event_type & eBreakpointEventTypeEnabled
|
|
|
|
// || event_type & eBreakpointEventTypeDisabled
|
|
|
|
// || event_type & eBreakpointEventTypeCommandChanged
|
|
|
|
// || event_type & eBreakpointEventTypeConditionChanged
|
|
|
|
// || event_type & eBreakpointEventTypeIgnoreChanged
|
|
|
|
// || event_type & eBreakpointEventTypeLocationsResolved)
|
|
|
|
// {
|
|
|
|
// // Don't do anything about these events, since the breakpoint
|
|
|
|
// commands already echo these actions.
|
|
|
|
// }
|
|
|
|
//
|
|
|
|
if (event_type & eBreakpointEventTypeLocationsAdded) {
|
|
|
|
uint32_t num_new_locations =
|
|
|
|
Breakpoint::BreakpointEventData::GetNumBreakpointLocationsFromEvent(
|
|
|
|
event_sp);
|
|
|
|
if (num_new_locations > 0) {
|
|
|
|
BreakpointSP breakpoint =
|
|
|
|
Breakpoint::BreakpointEventData::GetBreakpointFromEvent(event_sp);
|
|
|
|
StreamSP output_sp(GetAsyncOutputStream());
|
|
|
|
if (output_sp) {
|
|
|
|
output_sp->Printf("%d location%s added to breakpoint %d\n",
|
|
|
|
num_new_locations, num_new_locations == 1 ? "" : "s",
|
|
|
|
breakpoint->GetID());
|
|
|
|
output_sp->Flush();
|
|
|
|
}
|
2014-01-28 07:43:24 +08:00
|
|
|
}
|
2016-09-07 04:57:50 +08:00
|
|
|
}
|
|
|
|
// else if (event_type & eBreakpointEventTypeLocationsRemoved)
|
|
|
|
// {
|
|
|
|
// // These locations just get disabled, not sure it is worth spamming
|
|
|
|
// folks about this on the command line.
|
|
|
|
// }
|
|
|
|
// else if (event_type & eBreakpointEventTypeLocationsResolved)
|
|
|
|
// {
|
|
|
|
// // This might be an interesting thing to note, but I'm going to
|
|
|
|
// leave it quiet for now, it just looked noisy.
|
|
|
|
// }
|
|
|
|
}
|
|
|
|
|
|
|
|
size_t Debugger::GetProcessSTDOUT(Process *process, Stream *stream) {
|
|
|
|
size_t total_bytes = 0;
|
|
|
|
if (stream == nullptr)
|
|
|
|
stream = GetOutputFile().get();
|
|
|
|
|
|
|
|
if (stream) {
|
|
|
|
// The process has stuff waiting for stdout; get it and write it out to the
|
|
|
|
// appropriate place.
|
|
|
|
if (process == nullptr) {
|
|
|
|
TargetSP target_sp = GetTargetList().GetSelectedTarget();
|
|
|
|
if (target_sp)
|
|
|
|
process = target_sp->GetProcessSP().get();
|
2014-01-28 07:43:24 +08:00
|
|
|
}
|
2016-09-07 04:57:50 +08:00
|
|
|
if (process) {
|
|
|
|
Error error;
|
|
|
|
size_t len;
|
|
|
|
char stdio_buffer[1024];
|
|
|
|
while ((len = process->GetSTDOUT(stdio_buffer, sizeof(stdio_buffer),
|
|
|
|
error)) > 0) {
|
|
|
|
stream->Write(stdio_buffer, len);
|
|
|
|
total_bytes += len;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
stream->Flush();
|
|
|
|
}
|
|
|
|
return total_bytes;
|
|
|
|
}
|
|
|
|
|
|
|
|
size_t Debugger::GetProcessSTDERR(Process *process, Stream *stream) {
|
|
|
|
size_t total_bytes = 0;
|
|
|
|
if (stream == nullptr)
|
|
|
|
stream = GetOutputFile().get();
|
|
|
|
|
|
|
|
if (stream) {
|
|
|
|
// The process has stuff waiting for stderr; get it and write it out to the
|
|
|
|
// appropriate place.
|
|
|
|
if (process == nullptr) {
|
|
|
|
TargetSP target_sp = GetTargetList().GetSelectedTarget();
|
|
|
|
if (target_sp)
|
|
|
|
process = target_sp->GetProcessSP().get();
|
2014-01-28 07:43:24 +08:00
|
|
|
}
|
2016-09-07 04:57:50 +08:00
|
|
|
if (process) {
|
|
|
|
Error error;
|
|
|
|
size_t len;
|
|
|
|
char stdio_buffer[1024];
|
|
|
|
while ((len = process->GetSTDERR(stdio_buffer, sizeof(stdio_buffer),
|
|
|
|
error)) > 0) {
|
|
|
|
stream->Write(stdio_buffer, len);
|
|
|
|
total_bytes += len;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
stream->Flush();
|
|
|
|
}
|
|
|
|
return total_bytes;
|
2014-01-28 07:43:24 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
// This function handles events that were broadcast by the process.
|
2016-09-07 04:57:50 +08:00
|
|
|
void Debugger::HandleProcessEvent(const EventSP &event_sp) {
|
|
|
|
using namespace lldb;
|
|
|
|
const uint32_t event_type = event_sp->GetType();
|
|
|
|
ProcessSP process_sp =
|
|
|
|
(event_type == Process::eBroadcastBitStructuredData)
|
|
|
|
? EventDataStructuredData::GetProcessFromEvent(event_sp.get())
|
|
|
|
: Process::ProcessEventData::GetProcessFromEvent(event_sp.get());
|
|
|
|
|
|
|
|
StreamSP output_stream_sp = GetAsyncOutputStream();
|
|
|
|
StreamSP error_stream_sp = GetAsyncErrorStream();
|
|
|
|
const bool gui_enabled = IsForwardingEvents();
|
|
|
|
|
|
|
|
if (!gui_enabled) {
|
|
|
|
bool pop_process_io_handler = false;
|
|
|
|
assert(process_sp);
|
|
|
|
|
|
|
|
bool state_is_stopped = false;
|
|
|
|
const bool got_state_changed =
|
|
|
|
(event_type & Process::eBroadcastBitStateChanged) != 0;
|
|
|
|
const bool got_stdout = (event_type & Process::eBroadcastBitSTDOUT) != 0;
|
|
|
|
const bool got_stderr = (event_type & Process::eBroadcastBitSTDERR) != 0;
|
|
|
|
const bool got_structured_data =
|
|
|
|
(event_type & Process::eBroadcastBitStructuredData) != 0;
|
|
|
|
|
|
|
|
if (got_state_changed) {
|
|
|
|
StateType event_state =
|
|
|
|
Process::ProcessEventData::GetStateFromEvent(event_sp.get());
|
|
|
|
state_is_stopped = StateIsStoppedState(event_state, false);
|
|
|
|
}
|
2014-10-21 09:00:42 +08:00
|
|
|
|
2016-09-07 04:57:50 +08:00
|
|
|
// Display running state changes first before any STDIO
|
|
|
|
if (got_state_changed && !state_is_stopped) {
|
|
|
|
Process::HandleProcessStateChangedEvent(event_sp, output_stream_sp.get(),
|
|
|
|
pop_process_io_handler);
|
|
|
|
}
|
2014-10-21 09:00:42 +08:00
|
|
|
|
2016-09-07 04:57:50 +08:00
|
|
|
// Now display and STDOUT
|
|
|
|
if (got_stdout || got_state_changed) {
|
|
|
|
GetProcessSTDOUT(process_sp.get(), output_stream_sp.get());
|
|
|
|
}
|
2014-10-21 09:00:42 +08:00
|
|
|
|
2016-09-07 04:57:50 +08:00
|
|
|
// Now display and STDERR
|
|
|
|
if (got_stderr || got_state_changed) {
|
|
|
|
GetProcessSTDERR(process_sp.get(), error_stream_sp.get());
|
|
|
|
}
|
2014-03-01 02:22:24 +08:00
|
|
|
|
2016-09-07 04:57:50 +08:00
|
|
|
// Give structured data events an opportunity to display.
|
|
|
|
if (got_structured_data) {
|
|
|
|
StructuredDataPluginSP plugin_sp =
|
|
|
|
EventDataStructuredData::GetPluginFromEvent(event_sp.get());
|
|
|
|
if (plugin_sp) {
|
|
|
|
auto structured_data_sp =
|
|
|
|
EventDataStructuredData::GetObjectFromEvent(event_sp.get());
|
|
|
|
if (output_stream_sp) {
|
|
|
|
StreamString content_stream;
|
|
|
|
Error error =
|
|
|
|
plugin_sp->GetDescription(structured_data_sp, content_stream);
|
|
|
|
if (error.Success()) {
|
|
|
|
if (!content_stream.GetString().empty()) {
|
|
|
|
// Add newline.
|
|
|
|
content_stream.PutChar('\n');
|
|
|
|
content_stream.Flush();
|
|
|
|
|
|
|
|
// Print it.
|
2016-11-03 04:34:10 +08:00
|
|
|
output_stream_sp->PutCString(content_stream.GetString());
|
2016-08-19 12:21:48 +08:00
|
|
|
}
|
2016-09-07 04:57:50 +08:00
|
|
|
} else {
|
|
|
|
error_stream_sp->Printf("Failed to print structured "
|
|
|
|
"data with plugin %s: %s",
|
|
|
|
plugin_sp->GetPluginName().AsCString(),
|
|
|
|
error.AsCString());
|
|
|
|
}
|
2016-08-19 12:21:48 +08:00
|
|
|
}
|
2016-09-07 04:57:50 +08:00
|
|
|
}
|
2014-03-01 02:22:24 +08:00
|
|
|
}
|
2014-01-28 07:43:24 +08:00
|
|
|
|
2016-09-07 04:57:50 +08:00
|
|
|
// Now display any stopped state changes after any STDIO
|
|
|
|
if (got_state_changed && state_is_stopped) {
|
|
|
|
Process::HandleProcessStateChangedEvent(event_sp, output_stream_sp.get(),
|
|
|
|
pop_process_io_handler);
|
2014-01-28 07:43:24 +08:00
|
|
|
}
|
|
|
|
|
2016-09-07 04:57:50 +08:00
|
|
|
output_stream_sp->Flush();
|
|
|
|
error_stream_sp->Flush();
|
|
|
|
|
|
|
|
if (pop_process_io_handler)
|
|
|
|
process_sp->PopProcessIOHandler();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void Debugger::HandleThreadEvent(const EventSP &event_sp) {
|
|
|
|
// At present the only thread event we handle is the Frame Changed event,
|
|
|
|
// and all we do for that is just reprint the thread status for that thread.
|
|
|
|
using namespace lldb;
|
|
|
|
const uint32_t event_type = event_sp->GetType();
|
|
|
|
if (event_type == Thread::eBroadcastBitStackChanged ||
|
|
|
|
event_type == Thread::eBroadcastBitThreadSelected) {
|
|
|
|
ThreadSP thread_sp(
|
|
|
|
Thread::ThreadEventData::GetThreadFromEvent(event_sp.get()));
|
|
|
|
if (thread_sp) {
|
|
|
|
thread_sp->GetStatus(*GetAsyncOutputStream(), 0, 1, 1);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
bool Debugger::IsForwardingEvents() { return (bool)m_forward_listener_sp; }
|
|
|
|
|
|
|
|
void Debugger::EnableForwardEvents(const ListenerSP &listener_sp) {
|
|
|
|
m_forward_listener_sp = listener_sp;
|
|
|
|
}
|
|
|
|
|
|
|
|
void Debugger::CancelForwardEvents(const ListenerSP &listener_sp) {
|
|
|
|
m_forward_listener_sp.reset();
|
|
|
|
}
|
|
|
|
|
|
|
|
void Debugger::DefaultEventHandler() {
|
|
|
|
ListenerSP listener_sp(GetListener());
|
|
|
|
ConstString broadcaster_class_target(Target::GetStaticBroadcasterClass());
|
|
|
|
ConstString broadcaster_class_process(Process::GetStaticBroadcasterClass());
|
|
|
|
ConstString broadcaster_class_thread(Thread::GetStaticBroadcasterClass());
|
|
|
|
BroadcastEventSpec target_event_spec(broadcaster_class_target,
|
|
|
|
Target::eBroadcastBitBreakpointChanged);
|
|
|
|
|
|
|
|
BroadcastEventSpec process_event_spec(
|
|
|
|
broadcaster_class_process,
|
|
|
|
Process::eBroadcastBitStateChanged | Process::eBroadcastBitSTDOUT |
|
|
|
|
Process::eBroadcastBitSTDERR | Process::eBroadcastBitStructuredData);
|
|
|
|
|
|
|
|
BroadcastEventSpec thread_event_spec(broadcaster_class_thread,
|
|
|
|
Thread::eBroadcastBitStackChanged |
|
|
|
|
Thread::eBroadcastBitThreadSelected);
|
|
|
|
|
|
|
|
listener_sp->StartListeningForEventSpec(m_broadcaster_manager_sp,
|
|
|
|
target_event_spec);
|
|
|
|
listener_sp->StartListeningForEventSpec(m_broadcaster_manager_sp,
|
|
|
|
process_event_spec);
|
|
|
|
listener_sp->StartListeningForEventSpec(m_broadcaster_manager_sp,
|
|
|
|
thread_event_spec);
|
|
|
|
listener_sp->StartListeningForEvents(
|
|
|
|
m_command_interpreter_ap.get(),
|
|
|
|
CommandInterpreter::eBroadcastBitQuitCommandReceived |
|
|
|
|
CommandInterpreter::eBroadcastBitAsynchronousOutputData |
|
|
|
|
CommandInterpreter::eBroadcastBitAsynchronousErrorData);
|
|
|
|
|
|
|
|
// Let the thread that spawned us know that we have started up and
|
|
|
|
// that we are now listening to all required events so no events get missed
|
|
|
|
m_sync_broadcaster.BroadcastEvent(eBroadcastBitEventThreadIsListening);
|
|
|
|
|
|
|
|
bool done = false;
|
|
|
|
while (!done) {
|
|
|
|
EventSP event_sp;
|
|
|
|
if (listener_sp->WaitForEvent(std::chrono::microseconds(0), event_sp)) {
|
|
|
|
if (event_sp) {
|
|
|
|
Broadcaster *broadcaster = event_sp->GetBroadcaster();
|
|
|
|
if (broadcaster) {
|
|
|
|
uint32_t event_type = event_sp->GetType();
|
|
|
|
ConstString broadcaster_class(broadcaster->GetBroadcasterClass());
|
|
|
|
if (broadcaster_class == broadcaster_class_process) {
|
|
|
|
HandleProcessEvent(event_sp);
|
|
|
|
} else if (broadcaster_class == broadcaster_class_target) {
|
|
|
|
if (Breakpoint::BreakpointEventData::GetEventDataFromEvent(
|
|
|
|
event_sp.get())) {
|
|
|
|
HandleBreakpointEvent(event_sp);
|
|
|
|
}
|
|
|
|
} else if (broadcaster_class == broadcaster_class_thread) {
|
|
|
|
HandleThreadEvent(event_sp);
|
|
|
|
} else if (broadcaster == m_command_interpreter_ap.get()) {
|
|
|
|
if (event_type &
|
|
|
|
CommandInterpreter::eBroadcastBitQuitCommandReceived) {
|
|
|
|
done = true;
|
|
|
|
} else if (event_type &
|
|
|
|
CommandInterpreter::eBroadcastBitAsynchronousErrorData) {
|
|
|
|
const char *data = reinterpret_cast<const char *>(
|
|
|
|
EventDataBytes::GetBytesFromEvent(event_sp.get()));
|
|
|
|
if (data && data[0]) {
|
|
|
|
StreamSP error_sp(GetAsyncErrorStream());
|
|
|
|
if (error_sp) {
|
|
|
|
error_sp->PutCString(data);
|
|
|
|
error_sp->Flush();
|
2014-01-28 07:43:24 +08:00
|
|
|
}
|
2016-09-07 04:57:50 +08:00
|
|
|
}
|
|
|
|
} else if (event_type & CommandInterpreter::
|
|
|
|
eBroadcastBitAsynchronousOutputData) {
|
|
|
|
const char *data = reinterpret_cast<const char *>(
|
|
|
|
EventDataBytes::GetBytesFromEvent(event_sp.get()));
|
|
|
|
if (data && data[0]) {
|
|
|
|
StreamSP output_sp(GetAsyncOutputStream());
|
|
|
|
if (output_sp) {
|
|
|
|
output_sp->PutCString(data);
|
|
|
|
output_sp->Flush();
|
|
|
|
}
|
|
|
|
}
|
2014-01-28 07:43:24 +08:00
|
|
|
}
|
2016-09-07 04:57:50 +08:00
|
|
|
}
|
2014-01-28 07:43:24 +08:00
|
|
|
}
|
|
|
|
|
2016-09-07 04:57:50 +08:00
|
|
|
if (m_forward_listener_sp)
|
|
|
|
m_forward_listener_sp->AddEvent(event_sp);
|
|
|
|
}
|
2014-10-16 02:03:59 +08:00
|
|
|
}
|
2016-09-07 04:57:50 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
lldb::thread_result_t Debugger::EventHandlerThread(lldb::thread_arg_t arg) {
|
|
|
|
((Debugger *)arg)->DefaultEventHandler();
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool Debugger::StartEventHandlerThread() {
|
|
|
|
if (!m_event_handler_thread.IsJoinable()) {
|
|
|
|
// We must synchronize with the DefaultEventHandler() thread to ensure
|
|
|
|
// it is up and running and listening to events before we return from
|
|
|
|
// this function. We do this by listening to events for the
|
|
|
|
// eBroadcastBitEventThreadIsListening from the m_sync_broadcaster
|
|
|
|
ListenerSP listener_sp(
|
|
|
|
Listener::MakeListener("lldb.debugger.event-handler"));
|
|
|
|
listener_sp->StartListeningForEvents(&m_sync_broadcaster,
|
|
|
|
eBroadcastBitEventThreadIsListening);
|
|
|
|
|
|
|
|
// Use larger 8MB stack for this thread
|
|
|
|
m_event_handler_thread = ThreadLauncher::LaunchThread(
|
|
|
|
"lldb.debugger.event-handler", EventHandlerThread, this, nullptr,
|
|
|
|
g_debugger_event_thread_stack_bytes);
|
|
|
|
|
|
|
|
// Make sure DefaultEventHandler() is running and listening to events before
|
|
|
|
// we return
|
|
|
|
// from this function. We are only listening for events of type
|
|
|
|
// eBroadcastBitEventThreadIsListening so we don't need to check the event,
|
|
|
|
// we just need
|
|
|
|
// to wait an infinite amount of time for it (nullptr timeout as the first
|
|
|
|
// parameter)
|
|
|
|
lldb::EventSP event_sp;
|
|
|
|
listener_sp->WaitForEvent(std::chrono::microseconds(0), event_sp);
|
|
|
|
}
|
|
|
|
return m_event_handler_thread.IsJoinable();
|
|
|
|
}
|
|
|
|
|
|
|
|
void Debugger::StopEventHandlerThread() {
|
|
|
|
if (m_event_handler_thread.IsJoinable()) {
|
|
|
|
GetCommandInterpreter().BroadcastEvent(
|
|
|
|
CommandInterpreter::eBroadcastBitQuitCommandReceived);
|
|
|
|
m_event_handler_thread.Join(nullptr);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
lldb::thread_result_t Debugger::IOHandlerThread(lldb::thread_arg_t arg) {
|
|
|
|
Debugger *debugger = (Debugger *)arg;
|
|
|
|
debugger->ExecuteIOHandlers();
|
|
|
|
debugger->StopEventHandlerThread();
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool Debugger::HasIOHandlerThread() { return m_io_handler_thread.IsJoinable(); }
|
|
|
|
|
|
|
|
bool Debugger::StartIOHandlerThread() {
|
|
|
|
if (!m_io_handler_thread.IsJoinable())
|
|
|
|
m_io_handler_thread = ThreadLauncher::LaunchThread(
|
|
|
|
"lldb.debugger.io-handler", IOHandlerThread, this, nullptr,
|
|
|
|
8 * 1024 * 1024); // Use larger 8MB stack for this thread
|
|
|
|
return m_io_handler_thread.IsJoinable();
|
|
|
|
}
|
|
|
|
|
|
|
|
void Debugger::StopIOHandlerThread() {
|
|
|
|
if (m_io_handler_thread.IsJoinable()) {
|
|
|
|
if (m_input_file_sp)
|
|
|
|
m_input_file_sp->GetFile().Close();
|
|
|
|
m_io_handler_thread.Join(nullptr);
|
|
|
|
}
|
2014-01-28 07:43:24 +08:00
|
|
|
}
|
|
|
|
|
2016-09-07 04:57:50 +08:00
|
|
|
void Debugger::JoinIOHandlerThread() {
|
|
|
|
if (HasIOHandlerThread()) {
|
|
|
|
thread_result_t result;
|
|
|
|
m_io_handler_thread.Join(&result);
|
|
|
|
m_io_handler_thread = LLDB_INVALID_HOST_THREAD;
|
|
|
|
}
|
2014-01-28 07:43:24 +08:00
|
|
|
}
|
|
|
|
|
2016-09-07 04:57:50 +08:00
|
|
|
Target *Debugger::GetDummyTarget() {
|
|
|
|
return m_target_list.GetDummyTarget(*this).get();
|
2014-01-28 07:43:24 +08:00
|
|
|
}
|
|
|
|
|
2016-09-07 04:57:50 +08:00
|
|
|
Target *Debugger::GetSelectedOrDummyTarget(bool prefer_dummy) {
|
|
|
|
Target *target = nullptr;
|
|
|
|
if (!prefer_dummy) {
|
|
|
|
target = m_target_list.GetSelectedTarget().get();
|
|
|
|
if (target)
|
|
|
|
return target;
|
|
|
|
}
|
2015-10-20 07:11:07 +08:00
|
|
|
|
2016-09-07 04:57:50 +08:00
|
|
|
return GetDummyTarget();
|
2014-01-28 07:43:24 +08:00
|
|
|
}
|
|
|
|
|
2016-09-07 04:57:50 +08:00
|
|
|
Error Debugger::RunREPL(LanguageType language, const char *repl_options) {
|
|
|
|
Error err;
|
|
|
|
FileSpec repl_executable;
|
2014-01-28 07:43:24 +08:00
|
|
|
|
2016-09-07 04:57:50 +08:00
|
|
|
if (language == eLanguageTypeUnknown) {
|
|
|
|
std::set<LanguageType> repl_languages;
|
2015-10-20 07:11:07 +08:00
|
|
|
|
2016-09-07 04:57:50 +08:00
|
|
|
Language::GetLanguagesSupportingREPLs(repl_languages);
|
2014-11-22 09:42:44 +08:00
|
|
|
|
2016-09-07 04:57:50 +08:00
|
|
|
if (repl_languages.size() == 1) {
|
|
|
|
language = *repl_languages.begin();
|
|
|
|
} else if (repl_languages.empty()) {
|
|
|
|
err.SetErrorStringWithFormat(
|
|
|
|
"LLDB isn't configured with REPL support for any languages.");
|
|
|
|
return err;
|
|
|
|
} else {
|
|
|
|
err.SetErrorStringWithFormat(
|
|
|
|
"Multiple possible REPL languages. Please specify a language.");
|
|
|
|
return err;
|
2014-12-06 09:28:03 +08:00
|
|
|
}
|
2016-09-07 04:57:50 +08:00
|
|
|
}
|
2014-01-28 07:43:24 +08:00
|
|
|
|
2016-09-07 04:57:50 +08:00
|
|
|
Target *const target =
|
|
|
|
nullptr; // passing in an empty target means the REPL must create one
|
2015-10-22 03:31:17 +08:00
|
|
|
|
2016-09-07 04:57:50 +08:00
|
|
|
REPLSP repl_sp(REPL::Create(err, language, this, target, repl_options));
|
2015-10-20 08:23:46 +08:00
|
|
|
|
2016-09-07 04:57:50 +08:00
|
|
|
if (!err.Success()) {
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!repl_sp) {
|
|
|
|
err.SetErrorStringWithFormat("couldn't find a REPL for %s",
|
|
|
|
Language::GetNameForLanguageType(language));
|
2015-10-20 08:23:46 +08:00
|
|
|
return err;
|
2016-09-07 04:57:50 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
repl_sp->SetCompilerOptions(repl_options);
|
|
|
|
repl_sp->RunLoop();
|
|
|
|
|
|
|
|
return err;
|
2015-10-20 08:23:46 +08:00
|
|
|
}
|