diff options
258 files changed, 18884 insertions, 9222 deletions
diff --git a/include/lldb/API/LLDB.h b/include/lldb/API/LLDB.h index 93bc3bc121e2..b9c3198b73e5 100644 --- a/include/lldb/API/LLDB.h +++ b/include/lldb/API/LLDB.h @@ -33,13 +33,14 @@ #include "lldb/API/SBFrame.h" #include "lldb/API/SBFunction.h" #include "lldb/API/SBHostOS.h" -#include "lldb/API/SBInputReader.h" #include "lldb/API/SBInstruction.h" #include "lldb/API/SBInstructionList.h" #include "lldb/API/SBLineEntry.h" #include "lldb/API/SBListener.h" #include "lldb/API/SBModule.h" #include "lldb/API/SBProcess.h" +#include "lldb/API/SBQueue.h" +#include "lldb/API/SBQueueItem.h" #include "lldb/API/SBSourceManager.h" #include "lldb/API/SBStream.h" #include "lldb/API/SBStringList.h" diff --git a/include/lldb/API/SBAddress.h b/include/lldb/API/SBAddress.h index c5e8cc685a4c..58a7d2bfb1da 100644 --- a/include/lldb/API/SBAddress.h +++ b/include/lldb/API/SBAddress.h @@ -118,6 +118,7 @@ protected: friend class SBTarget; friend class SBThread; friend class SBValue; + friend class SBQueueItem; lldb_private::Address * operator->(); diff --git a/include/lldb/API/SBCommandInterpreter.h b/include/lldb/API/SBCommandInterpreter.h index 9b2583cd85ca..184a6b473126 100644 --- a/include/lldb/API/SBCommandInterpreter.h +++ b/include/lldb/API/SBCommandInterpreter.h @@ -122,6 +122,36 @@ public: SBCommandInterpreter (lldb_private::CommandInterpreter *interpreter_ptr = NULL); // Access using SBDebugger::GetCommandInterpreter(); + //---------------------------------------------------------------------- + /// Return true if the command interpreter is the active IO handler. + /// + /// This indicates that any input coming into the debugger handles will + /// go to the command interpreter and will result in LLDB command line + /// commands being executed. + //---------------------------------------------------------------------- + bool + IsActive (); + + //---------------------------------------------------------------------- + /// Get the string that needs to be written to the debugger stdin file + /// handle when a control character is typed. + /// + /// Some GUI programs will intercept "control + char" sequences and want + /// to have them do what normally would happen when using a real + /// terminal, so this function allows GUI programs to emulate this + /// functionality. + /// + /// @param[in] ch + /// The character that was typed along with the control key + /// + /// @return + /// The string that should be written into the file handle that is + /// feeding the input stream for the debugger, or NULL if there is + /// no string for this control key. + //---------------------------------------------------------------------- + const char * + GetIOHandlerControlSequence(char ch); + protected: lldb_private::CommandInterpreter & diff --git a/include/lldb/API/SBDebugger.h b/include/lldb/API/SBDebugger.h index 80e6969cbd3a..2386ffc968de 100644 --- a/include/lldb/API/SBDebugger.h +++ b/include/lldb/API/SBDebugger.h @@ -17,6 +17,16 @@ namespace lldb { + +class SBInputReader +{ +public: + SBInputReader(); + ~SBInputReader(); + SBError Initialize(lldb::SBDebugger&, unsigned long (*)(void*, lldb::SBInputReader*, lldb::InputReaderAction, char const*, unsigned long), void*, lldb::InputReaderGranularity, char const*, char const*, bool); + void SetIsDone(bool); + bool IsActive() const; +}; class SBDebugger { public: @@ -231,12 +241,6 @@ public: void PushInputReader (lldb::SBInputReader &reader); - void - NotifyTopInputReader (lldb::InputReaderAction notification); - - bool - InputReaderIsTopReader (const lldb::SBInputReader &reader); - const char * GetInstanceName (); @@ -313,6 +317,10 @@ public: GetSyntheticForType (SBTypeNameSpecifier); #endif + void + RunCommandInterpreter (bool auto_handle_events, + bool spawn_thread); + private: friend class SBCommandInterpreter; diff --git a/include/lldb/API/SBDefines.h b/include/lldb/API/SBDefines.h index 2cdf92170d8d..8779d43d1f40 100644 --- a/include/lldb/API/SBDefines.h +++ b/include/lldb/API/SBDefines.h @@ -48,7 +48,6 @@ class SBFileSpecList; class SBFrame; class SBFunction; class SBHostOS; -class SBInputReader; class SBInstruction; class SBInstructionList; class SBLineEntry; diff --git a/include/lldb/API/SBError.h b/include/lldb/API/SBError.h index 12b34ec6dbc3..25d7e81a3be5 100644 --- a/include/lldb/API/SBError.h +++ b/include/lldb/API/SBError.h @@ -71,7 +71,6 @@ protected: friend class SBDebugger; friend class SBCommunication; friend class SBHostOS; - friend class SBInputReader; friend class SBPlatform; friend class SBProcess; friend class SBThread; diff --git a/include/lldb/API/SBInputReader.h b/include/lldb/API/SBInputReader.h deleted file mode 100644 index 61f7de4fde4c..000000000000 --- a/include/lldb/API/SBInputReader.h +++ /dev/null @@ -1,97 +0,0 @@ -//===-- SBInputReader.h -----------------------------------------*- C++ -*-===// -// -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// - -#ifndef LLDB_SBInputReader_h_ -#define LLDB_SBInputReader_h_ - -#include "lldb/API/SBDefines.h" - -namespace lldb { - -class SBInputReader -{ -public: - - typedef size_t (*Callback) (void *baton, - SBInputReader *reader, - InputReaderAction notification, - const char *bytes, - size_t bytes_len); - - SBInputReader (); - - SBInputReader (const lldb::InputReaderSP &reader_sp); - - SBInputReader (const lldb::SBInputReader &rhs); - - ~SBInputReader (); - - - SBError - Initialize (SBDebugger &debugger, - Callback callback, - void *callback_baton, - lldb::InputReaderGranularity granularity, - const char *end_token, - const char *prompt, - bool echo); - - bool - IsValid () const; - - const lldb::SBInputReader & - operator = (const lldb::SBInputReader &rhs); - - bool - IsActive () const; - - bool - IsDone () const; - - void - SetIsDone (bool value); - - InputReaderGranularity - GetGranularity (); - -protected: - friend class SBDebugger; - - lldb_private::InputReader * - operator->() const; - - lldb::InputReaderSP & - operator *(); - - const lldb::InputReaderSP & - operator *() const; - - lldb_private::InputReader * - get() const; - - lldb_private::InputReader & - ref() const; - -private: - - static size_t - PrivateCallback (void *baton, - lldb_private::InputReader &reader, - lldb::InputReaderAction notification, - const char *bytes, - size_t bytes_len); - - lldb::InputReaderSP m_opaque_sp; - Callback m_callback_function; - void *m_callback_baton; -}; - -} // namespace lldb - -#endif // LLDB_SBInputReader_h_ diff --git a/include/lldb/API/SBModule.h b/include/lldb/API/SBModule.h index f5955b39734d..e85654bccc72 100644 --- a/include/lldb/API/SBModule.h +++ b/include/lldb/API/SBModule.h @@ -235,6 +235,25 @@ public: lldb::SBTypeList FindTypes (const char* type); + //------------------------------------------------------------------ + /// Get a type using its type ID. + /// + /// Each symbol file reader will assign different user IDs to their + /// types, but it is sometimes useful when debugging type issues to + /// be able to grab a type using its type ID. + /// + /// For DWARF debug info, the type ID is the DIE offset. + /// + /// @param[in] uid + /// The type user ID. + /// + /// @return + /// An SBType for the given type ID, or an empty SBType if the + /// type was not found. + //------------------------------------------------------------------ + lldb::SBType + GetTypeByID (lldb::user_id_t uid); + lldb::SBType GetBasicType(lldb::BasicType type); diff --git a/include/lldb/API/SBProcess.h b/include/lldb/API/SBProcess.h index 4ecaeaa49920..f2846710c614 100644 --- a/include/lldb/API/SBProcess.h +++ b/include/lldb/API/SBProcess.h @@ -13,6 +13,7 @@ #include "lldb/API/SBDefines.h" #include "lldb/API/SBError.h" #include "lldb/API/SBTarget.h" +#include "lldb/API/SBQueue.h" #include <stdio.h> namespace lldb { @@ -142,6 +143,15 @@ public: SetSelectedThreadByIndexID (uint32_t index_id); //------------------------------------------------------------------ + // Queue related functions + //------------------------------------------------------------------ + uint32_t + GetNumQueues (); + + lldb::SBQueue + GetQueueAtIndex (size_t index); + + //------------------------------------------------------------------ // Stepping related functions //------------------------------------------------------------------ @@ -312,6 +322,7 @@ protected: friend class SBTarget; friend class SBThread; friend class SBValue; + friend class lldb_private::QueueImpl; lldb::ProcessSP GetSP() const; diff --git a/include/lldb/API/SBQueue.h b/include/lldb/API/SBQueue.h new file mode 100644 index 000000000000..6ab9aa09f466 --- /dev/null +++ b/include/lldb/API/SBQueue.h @@ -0,0 +1,83 @@ +//===-- SBQueue.h -----------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef LLDB_SBQueue_h_ +#define LLDB_SBQueue_h_ + +#include <vector> + +#include "lldb/lldb-forward.h" +#include "lldb/API/SBDefines.h" +#include "lldb/API/SBQueueItem.h" + +namespace lldb { + +class SBQueue +{ +public: + SBQueue (); + + SBQueue (const QueueSP& queue_sp); + + SBQueue (const SBQueue& rhs); + + const SBQueue & + operator= (const lldb::SBQueue& rhs); + + ~SBQueue(); + + bool + IsValid() const; + + void + Clear (); + + lldb::SBProcess + GetProcess (); + + lldb::queue_id_t + GetQueueID () const; + + const char * + GetName () const; + + uint32_t + GetIndexID () const; + + uint32_t + GetNumThreads (); + + lldb::SBThread + GetThreadAtIndex (uint32_t); + + uint32_t + GetNumPendingItems (); + + lldb::SBQueueItem + GetPendingItemAtIndex (uint32_t); + +protected: + friend class SBProcess; + + void + SetQueue (const lldb::QueueSP& queue_sp); + + void + FetchThreads (); + + void + FetchItems (); + +private: + std::shared_ptr<lldb_private::QueueImpl> m_opaque_sp; +}; + +} // namespace lldb + +#endif // LLDB_SBQueue_h_ diff --git a/include/lldb/API/SBQueueItem.h b/include/lldb/API/SBQueueItem.h new file mode 100644 index 000000000000..355c5ac90a23 --- /dev/null +++ b/include/lldb/API/SBQueueItem.h @@ -0,0 +1,58 @@ +//===-- SBQueueItem.h -------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef LLDB_SBQueueItem_h_ +#define LLDB_SBQueueItem_h_ + +#include "lldb/API/SBDefines.h" +#include "lldb/API/SBAddress.h" +#include "lldb/API/SBThread.h" + +namespace lldb { + +class SBQueueItem +{ +public: + SBQueueItem (); + + SBQueueItem (const lldb::QueueItemSP& queue_item_sp); + + ~SBQueueItem(); + + bool + IsValid() const; + + void + Clear (); + + lldb::QueueItemKind + GetKind () const; + + void + SetKind (lldb::QueueItemKind kind); + + lldb::SBAddress + GetAddress () const; + + void + SetAddress (lldb::SBAddress addr); + + void + SetQueueItem (const lldb::QueueItemSP& queue_item_sp); + + SBThread + GetExtendedBacktraceThread (const char *type); + +private: + lldb::QueueItemSP m_queue_item_sp; +}; + +} // namespace lldb + +#endif // LLDB_SBQueueItem_h_ diff --git a/include/lldb/API/SBTarget.h b/include/lldb/API/SBTarget.h index b41c11811549..230dffc68251 100644 --- a/include/lldb/API/SBTarget.h +++ b/include/lldb/API/SBTarget.h @@ -646,9 +646,47 @@ public: void Clear (); + //------------------------------------------------------------------ + /// Resolve a current load address into a section offset address. + /// + /// @param[in] vm_addr + /// A virtual address from the current process state that is to + /// be translated into a section offset address. + /// + /// @return + /// An SBAddress which will be valid if \a vm_addr was + /// successfully resolved into a section offset address, or an + /// invalid SBAddress if \a vm_addr doesn't resolve to a section + /// in a module. + //------------------------------------------------------------------ lldb::SBAddress ResolveLoadAddress (lldb::addr_t vm_addr); + //------------------------------------------------------------------ + /// Resolve a current load address into a section offset address + /// using the process stop ID to identify a time in the past. + /// + /// @param[in] stop_id + /// Each time a process stops, the process stop ID integer gets + /// incremented. These stop IDs are used to identify past times + /// and can be used in history objects as a cheap way to store + /// the time at which the sample was taken. Specifying + /// UINT32_MAX will always resolve the address using the + /// currently loaded sections. + /// + /// @param[in] vm_addr + /// A virtual address from the current process state that is to + /// be translated into a section offset address. + /// + /// @return + /// An SBAddress which will be valid if \a vm_addr was + /// successfully resolved into a section offset address, or an + /// invalid SBAddress if \a vm_addr doesn't resolve to a section + /// in a module. + //------------------------------------------------------------------ + lldb::SBAddress + ResolvePastLoadAddress (uint32_t stop_id, lldb::addr_t vm_addr); + SBSymbolContext ResolveSymbolContextForAddress (const SBAddress& addr, uint32_t resolve_scope); diff --git a/include/lldb/API/SBThread.h b/include/lldb/API/SBThread.h index 6542dca1f95a..04b6c86e6d89 100644 --- a/include/lldb/API/SBThread.h +++ b/include/lldb/API/SBThread.h @@ -214,6 +214,8 @@ protected: friend class SBProcess; friend class SBDebugger; friend class SBValue; + friend class lldb_private::QueueImpl; + friend class SBQueueItem; void SetThread (const lldb::ThreadSP& lldb_object_sp); diff --git a/include/lldb/API/SBType.h b/include/lldb/API/SBType.h index 3729b2f84b90..2cd9b4459a33 100644 --- a/include/lldb/API/SBType.h +++ b/include/lldb/API/SBType.h @@ -106,6 +106,9 @@ public: GetReferenceType(); lldb::SBType + GetTypedefedType(); + + lldb::SBType GetDereferencedType(); lldb::SBType diff --git a/include/lldb/API/SBTypeFormat.h b/include/lldb/API/SBTypeFormat.h index cd6345fbe6fa..eb45ff2b0eba 100644 --- a/include/lldb/API/SBTypeFormat.h +++ b/include/lldb/API/SBTypeFormat.h @@ -22,6 +22,9 @@ public: SBTypeFormat (lldb::Format format, uint32_t options = 0); // see lldb::eTypeOption values + + SBTypeFormat (const char* type, + uint32_t options = 0); // see lldb::eTypeOption values SBTypeFormat (const lldb::SBTypeFormat &rhs); @@ -33,6 +36,9 @@ public: lldb::Format GetFormat (); + const char* + GetTypeName (); + uint32_t GetOptions(); @@ -40,6 +46,9 @@ public: SetFormat (lldb::Format); void + SetTypeName (const char*); + + void SetOptions (uint32_t); bool @@ -73,8 +82,15 @@ protected: SBTypeFormat (const lldb::TypeFormatImplSP &); + enum class Type + { + eTypeKeepSame, + eTypeFormat, + eTypeEnum + }; + bool - CopyOnWrite_Impl(); + CopyOnWrite_Impl(Type); }; diff --git a/include/lldb/Breakpoint/Breakpoint.h b/include/lldb/Breakpoint/Breakpoint.h index e82d6eacf7a8..749ff8d1a09c 100644 --- a/include/lldb/Breakpoint/Breakpoint.h +++ b/include/lldb/Breakpoint/Breakpoint.h @@ -612,12 +612,30 @@ protected: /// Only the Target can make a breakpoint, and it owns the breakpoint lifespans. /// The constructor takes a filter and a resolver. Up in Target there are convenience /// variants that make breakpoints for some common cases. + /// + /// @param[in] target + /// The target in which the breakpoint will be set. + /// + /// @param[in] filter_sp + /// Shared pointer to the search filter that restricts the search domain of the breakpoint. + /// + /// @param[in] resolver_sp + /// Shared pointer to the resolver object that will determine breakpoint matches. + /// + /// @param hardware + /// If true, request a hardware breakpoint to be used to implement the breakpoint locations. + /// + /// @param resolve_indirect_symbols + /// If true, and the address of a given breakpoint location in this breakpoint is set on an + /// indirect symbol (i.e. Symbol::IsIndirect returns true) then the actual breakpoint site will + /// be set on the target of the indirect symbol. //------------------------------------------------------------------ // This is the generic constructor Breakpoint(Target &target, lldb::SearchFilterSP &filter_sp, lldb::BreakpointResolverSP &resolver_sp, - bool hardware); + bool hardware, + bool resolve_indirect_symbols = true); friend class BreakpointLocation; // To call the following two when determining whether to stop. @@ -643,6 +661,7 @@ private: BreakpointOptions m_options; // Settable breakpoint options BreakpointLocationList m_locations; // The list of locations currently found for this breakpoint. std::string m_kind_description; + bool m_resolve_indirect_symbols; void SendBreakpointChangedEvent (lldb::BreakpointEventType eventKind); diff --git a/include/lldb/Breakpoint/BreakpointLocation.h b/include/lldb/Breakpoint/BreakpointLocation.h index f4ba21a385f8..ac4c28bb6e5f 100644 --- a/include/lldb/Breakpoint/BreakpointLocation.h +++ b/include/lldb/Breakpoint/BreakpointLocation.h @@ -321,7 +321,59 @@ public: //------------------------------------------------------------------ bool InvokeCallback (StoppointCallbackContext *context); + + //------------------------------------------------------------------ + /// Returns whether we should resolve Indirect functions in setting the breakpoint site + /// for this location. + /// + /// @return + /// \b true if the breakpoint SITE for this location should be set on the + /// resolved location for Indirect functions. + //------------------------------------------------------------------ + bool + ShouldResolveIndirectFunctions () + { + return m_should_resolve_indirect_functions; + } + //------------------------------------------------------------------ + /// Returns whether the address set in the breakpoint site for this location was found by resolving + /// an indirect symbol. + /// + /// @return + /// \b true or \b false as given in the description above. + //------------------------------------------------------------------ + bool + IsIndirect () + { + return m_is_indirect; + } + + void + SetIsIndirect (bool is_indirect) + { + m_is_indirect = is_indirect; + } + + //------------------------------------------------------------------ + /// Returns whether the address set in the breakpoint location was re-routed to the target of a + /// re-exported symbol. + /// + /// @return + /// \b true or \b false as given in the description above. + //------------------------------------------------------------------ + bool + IsReExported () + { + return m_is_reexported; + } + + void + SetIsReExported (bool is_reexported) + { + m_is_reexported = is_reexported; + } + protected: friend class BreakpointLocationList; friend class Process; @@ -375,12 +427,16 @@ private: Breakpoint &owner, const Address &addr, lldb::tid_t tid, - bool hardware); - + bool hardware, + bool check_for_resolver = true); + //------------------------------------------------------------------ // Data members: //------------------------------------------------------------------ bool m_being_created; + bool m_should_resolve_indirect_functions; + bool m_is_reexported; + bool m_is_indirect; Address m_address; ///< The address defining this location. Breakpoint &m_owner; ///< The breakpoint that produced this object. std::unique_ptr<BreakpointOptions> m_options_ap; ///< Breakpoint options pointer, NULL if we're using our breakpoint's options. @@ -390,6 +446,12 @@ private: size_t m_condition_hash; ///< For testing whether the condition source code changed. void + SetShouldResolveIndirectFunctions (bool do_resolve) + { + m_should_resolve_indirect_functions = do_resolve; + } + + void SendBreakpointLocationChangedEvent (lldb::BreakpointEventType eventKind); DISALLOW_COPY_AND_ASSIGN (BreakpointLocation); diff --git a/include/lldb/Breakpoint/BreakpointLocationList.h b/include/lldb/Breakpoint/BreakpointLocationList.h index b25208659c7f..ec34641b727c 100644 --- a/include/lldb/Breakpoint/BreakpointLocationList.h +++ b/include/lldb/Breakpoint/BreakpointLocationList.h @@ -236,7 +236,7 @@ protected: /// Returns breakpoint location id. //------------------------------------------------------------------ lldb::BreakpointLocationSP - Create (const Address &addr); + Create (const Address &addr, bool resolve_indirect_symbols); void StartRecordingNewLocations(BreakpointLocationCollection &new_locations); @@ -246,6 +246,7 @@ protected: lldb::BreakpointLocationSP AddLocation (const Address &addr, + bool resolve_indirect_symbols, bool *new_location = NULL); bool diff --git a/include/lldb/Core/ArchSpec.h b/include/lldb/Core/ArchSpec.h index 7f2fd77a0934..4ec8e06c8b35 100644 --- a/include/lldb/Core/ArchSpec.h +++ b/include/lldb/Core/ArchSpec.h @@ -13,6 +13,7 @@ #if defined(__cplusplus) #include "lldb/lldb-private.h" +#include "lldb/Core/ConstString.h" #include "llvm/ADT/StringRef.h" #include "llvm/ADT/Triple.h" @@ -90,6 +91,7 @@ public: eCore_x86_32_i486sx, eCore_x86_64_x86_64, + eCore_x86_64_x86_64h, // Haswell enabled x86_64 eCore_uknownMach32, eCore_uknownMach64, kNumCores, @@ -203,6 +205,27 @@ public: GetMachine () const; //------------------------------------------------------------------ + /// Returns the distribution id of the architecture. + /// + /// This will be something like "ubuntu", "fedora", etc. on Linux. + /// + /// @return A ConstString ref containing the distribution id, + /// potentially empty. + //------------------------------------------------------------------ + const ConstString& + GetDistributionId () const; + + //------------------------------------------------------------------ + /// Set the distribution id of the architecture. + /// + /// This will be something like "ubuntu", "fedora", etc. on Linux. + /// This should be the same value returned by + /// Host::GetDistributionId (). + ///------------------------------------------------------------------ + void + SetDistributionId (const char* distribution_id); + + //------------------------------------------------------------------ /// Tests if this ArchSpec is valid. /// /// @return True if the current architecture is valid, false @@ -400,6 +423,8 @@ protected: Core m_core; lldb::ByteOrder m_byte_order; + ConstString m_distribution_id; + // Called when m_def or m_entry are changed. Fills in all remaining // members with default values. void diff --git a/include/lldb/Core/Communication.h b/include/lldb/Core/Communication.h index 98d4dfd011b8..2dde55044171 100644 --- a/include/lldb/Core/Communication.h +++ b/include/lldb/Core/Communication.h @@ -286,6 +286,8 @@ public: virtual bool StopReadThread (Error *error_ptr = NULL); + virtual bool + JoinReadThread (Error *error_ptr = NULL); //------------------------------------------------------------------ /// Checks if there is a currently running read thread. /// diff --git a/include/lldb/Core/ConnectionFileDescriptor.h b/include/lldb/Core/ConnectionFileDescriptor.h index 3a2f0dd1ed0b..15598c9b1335 100644 --- a/include/lldb/Core/ConnectionFileDescriptor.h +++ b/include/lldb/Core/ConnectionFileDescriptor.h @@ -11,9 +11,7 @@ #define liblldb_ConnectionFileDescriptor_h_ // C Includes -#ifdef _WIN32 -typedef unsigned short in_port_t; -#else +#ifndef _WIN32 #include <sys/socket.h> #include <sys/types.h> #include <netinet/in.h> @@ -68,14 +66,17 @@ public: // If the read file descriptor is a socket, then return // the port number that is being used by the socket. - in_port_t + uint16_t GetReadPort () const; // If the write file descriptor is a socket, then return // the port number that is being used by the socket. - in_port_t + uint16_t GetWritePort () const; + uint16_t + GetBoundPort (uint32_t timeout_sec); + protected: typedef enum @@ -95,7 +96,7 @@ protected: BytesAvailable (uint32_t timeout_usec, Error *error_ptr); lldb::ConnectionStatus - SocketListen (uint16_t listen_port_num, Error *error_ptr); + SocketListen (const char *host_and_port, Error *error_ptr); lldb::ConnectionStatus ConnectTCP (const char *host_and_port, Error *error_ptr); @@ -117,15 +118,16 @@ protected: FDType m_fd_send_type; FDType m_fd_recv_type; std::unique_ptr<SocketAddress> m_udp_send_sockaddr; - bool m_should_close_fd; // True if this class should close the file descriptor when it goes away. uint32_t m_socket_timeout_usec; int m_pipe_read; // A pipe that we select on the reading end of along with int m_pipe_write; // m_fd_recv so we can force ourselves out of the select. - Mutex m_mutex; + Mutex m_mutex; + Predicate<uint16_t> m_port_predicate; // Used when binding to port zero to wait for the thread that creates the socket, binds and listens to resolve the port number + bool m_should_close_fd; // True if this class should close the file descriptor when it goes away. bool m_shutting_down; // This marks that we are shutting down so if we get woken up from BytesAvailable // to disconnect, we won't try to read again. - static in_port_t + static uint16_t GetSocketPort (int fd); static int diff --git a/include/lldb/Core/Debugger.h b/include/lldb/Core/Debugger.h index a3c6d5eeb93d..e80ec8516793 100644 --- a/include/lldb/Core/Debugger.h +++ b/include/lldb/Core/Debugger.h @@ -19,9 +19,8 @@ #include "lldb/lldb-public.h" #include "lldb/Core/Broadcaster.h" #include "lldb/Core/Communication.h" -#include "lldb/Core/InputReaderStack.h" +#include "lldb/Core/IOHandler.h" #include "lldb/Core/Listener.h" -#include "lldb/Core/StreamFile.h" #include "lldb/Core/SourceManager.h" #include "lldb/Core/UserID.h" #include "lldb/Core/UserSettingsController.h" @@ -91,23 +90,25 @@ public: void SetAsyncExecution (bool async); - File & + lldb::StreamFileSP GetInputFile () { - return m_input_file.GetFile(); + return m_input_file_sp; } - File & + lldb::StreamFileSP GetOutputFile () { - return m_output_file.GetFile(); + return m_output_file_sp; } - File & + lldb::StreamFileSP GetErrorFile () { - return m_error_file.GetFile(); + return m_error_file_sp; } + + void SetInputFileHandle (FILE *fh, bool tranfer_ownership); @@ -124,18 +125,6 @@ public: void RestoreInputTerminalState(); - Stream& - GetOutputStream () - { - return m_output_file; - } - - Stream& - GetErrorStream () - { - return m_error_file; - } - lldb::StreamSP GetAsyncOutputStream (); @@ -200,24 +189,38 @@ public: void DispatchInputEndOfFile (); + //------------------------------------------------------------------ + // If any of the streams are not set, set them to the in/out/err + // stream of the top most input reader to ensure they at least have + // something + //------------------------------------------------------------------ void - DispatchInput (const char *bytes, size_t bytes_len); + AdoptTopIOHandlerFilesIfInvalid (lldb::StreamFileSP &in, + lldb::StreamFileSP &out, + lldb::StreamFileSP &err); void - WriteToDefaultReader (const char *bytes, size_t bytes_len); + PushIOHandler (const lldb::IOHandlerSP& reader_sp); + bool + PopIOHandler (const lldb::IOHandlerSP& reader_sp); + + // Synchronously run an input reader until it is done void - PushInputReader (const lldb::InputReaderSP& reader_sp); + RunIOHandler (const lldb::IOHandlerSP& reader_sp); + + bool + IsTopIOHandler (const lldb::IOHandlerSP& reader_sp); + + ConstString + GetTopIOHandlerControlSequence(char ch); bool - PopInputReader (const lldb::InputReaderSP& reader_sp); + HideTopIOHandler(); void - NotifyTopInputReader (lldb::InputReaderAction notification); + RefreshTopIOHandler(); - bool - InputReaderIsTopReader (const lldb::InputReaderSP& reader_sp); - static lldb::DebuggerSP FindDebuggerWithID (lldb::user_id_t id); @@ -240,7 +243,7 @@ public: void - CleanUpInputReaders (); + ClearIOHandlers (); static int TestDebuggerRefCount (); @@ -338,29 +341,65 @@ public: bool LoadPlugin (const FileSpec& spec, Error& error); + void + ExecuteIOHanders(); + + bool + IsForwardingEvents (); + + void + EnableForwardEvents (const lldb::ListenerSP &listener_sp); + + void + CancelForwardEvents (const lldb::ListenerSP &listener_sp); protected: - static void - DispatchInputCallback (void *baton, const void *bytes, size_t bytes_len); + friend class CommandInterpreter; + + bool + StartEventHandlerThread(); - lldb::InputReaderSP - GetCurrentInputReader (); - void - ActivateInputReader (const lldb::InputReaderSP &reader_sp); + StopEventHandlerThread(); + + static lldb::thread_result_t + EventHandlerThread (lldb::thread_arg_t arg); bool - CheckIfTopInputReaderIsDone (); + StartIOHandlerThread(); + + void + StopIOHandlerThread(); + static lldb::thread_result_t + IOHandlerThread (lldb::thread_arg_t arg); + + void + DefaultEventHandler(); + + void + HandleBreakpointEvent (const lldb::EventSP &event_sp); + + void + HandleProcessEvent (const lldb::EventSP &event_sp); + + void + HandleThreadEvent (const lldb::EventSP &event_sp); + + size_t + GetProcessSTDOUT (Process *process, Stream *stream); + + size_t + GetProcessSTDERR (Process *process, Stream *stream); + SourceManager::SourceFileCache & GetSourceFileCache () { return m_source_file_cache; } - Communication m_input_comm; - StreamFile m_input_file; - StreamFile m_output_file; - StreamFile m_error_file; + lldb::StreamFileSP m_input_file_sp; + lldb::StreamFileSP m_output_file_sp; + lldb::StreamFileSP m_error_file_sp; TerminalState m_terminal_state; TargetList m_target_list; PlatformList m_platform_list; @@ -370,8 +409,7 @@ protected: // source file cache. std::unique_ptr<CommandInterpreter> m_command_interpreter_ap; - InputReaderStack m_input_reader_stack; - std::string m_input_reader_data; + IOHandlerStack m_input_reader_stack; typedef std::map<std::string, lldb::StreamWP> LogStreamMap; LogStreamMap m_log_streams; lldb::StreamSP m_log_callback_stream_sp; @@ -379,7 +417,10 @@ protected: static LoadPluginCallbackType g_load_plugin_callback; typedef std::vector<lldb::DynamicLibrarySP> LoadedPluginsList; LoadedPluginsList m_loaded_plugins; - + lldb::thread_t m_event_handler_thread; + lldb::thread_t m_io_handler_thread; + lldb::ListenerSP m_forward_listener_sp; + bool m_event_handler_thread_alive; void InstanceInitialize (); diff --git a/include/lldb/Core/Disassembler.h b/include/lldb/Core/Disassembler.h index f434d56943d4..06a703b4c1aa 100644 --- a/include/lldb/Core/Disassembler.h +++ b/include/lldb/Core/Disassembler.h @@ -175,6 +175,9 @@ public: uint32_t GetIndexOfInstructionAtLoadAddress (lldb::addr_t load_addr, Target &target); + uint32_t + GetIndexOfInstructionAtAddress (const Address &addr); + void Clear(); diff --git a/include/lldb/Core/IOHandler.h b/include/lldb/Core/IOHandler.h new file mode 100644 index 000000000000..a7d4880da0d3 --- /dev/null +++ b/include/lldb/Core/IOHandler.h @@ -0,0 +1,646 @@ +//===-- IOHandler.h ---------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_IOHandler_h_ +#define liblldb_IOHandler_h_ + +#include <string.h> + +#include <stack> + +#include "lldb/lldb-public.h" +#include "lldb/lldb-enumerations.h" +#include "lldb/Core/ConstString.h" +#include "lldb/Core/Error.h" +#include "lldb/Core/Flags.h" +#include "lldb/Core/StringList.h" +#include "lldb/Core/ValueObjectList.h" +#include "lldb/Host/Mutex.h" + +namespace curses +{ + class Application; + typedef std::unique_ptr<Application> ApplicationAP; +} + +namespace lldb_private { + + class IOHandler + { + public: + IOHandler (Debugger &debugger); + + IOHandler (Debugger &debugger, + const lldb::StreamFileSP &input_sp, + const lldb::StreamFileSP &output_sp, + const lldb::StreamFileSP &error_sp, + uint32_t flags); + + virtual + ~IOHandler (); + + // Each IOHandler gets to run until it is done. It should read data + // from the "in" and place output into "out" and "err and return + // when done. + virtual void + Run () = 0; + + // Hide any characters that have been displayed so far so async + // output can be displayed. Refresh() will be called after the + // output has been displayed. + virtual void + Hide () = 0; + + // Called when the async output has been received in order to update + // the input reader (refresh the prompt and redisplay any current + // line(s) that are being edited + virtual void + Refresh () = 0; + + virtual void + Interrupt () = 0; + + virtual void + GotEOF() = 0; + + virtual bool + IsActive () + { + return m_active && !m_done; + } + + virtual void + SetIsDone (bool b) + { + m_done = b; + } + + virtual bool + GetIsDone () + { + return m_done; + } + + virtual void + Activate () + { + m_active = true; + } + + virtual void + Deactivate () + { + m_active = false; + } + + virtual const char * + GetPrompt () + { + // Prompt support isn't mandatory + return NULL; + } + + virtual bool + SetPrompt (const char *prompt) + { + // Prompt support isn't mandatory + return false; + } + + virtual ConstString + GetControlSequence (char ch) + { + return ConstString(); + } + + int + GetInputFD(); + + int + GetOutputFD(); + + int + GetErrorFD(); + + FILE * + GetInputFILE(); + + FILE * + GetOutputFILE(); + + FILE * + GetErrorFILE(); + + lldb::StreamFileSP & + GetInputStreamFile(); + + lldb::StreamFileSP & + GetOutputStreamFile(); + + lldb::StreamFileSP & + GetErrorStreamFile(); + + Debugger & + GetDebugger() + { + return m_debugger; + } + + void * + GetUserData () + { + return m_user_data; + } + + void + SetUserData (void *user_data) + { + m_user_data = user_data; + } + + Flags & + GetFlags () + { + return m_flags; + } + + const Flags & + GetFlags () const + { + return m_flags; + } + + //------------------------------------------------------------------ + /// Check if the input is being supplied interactively by a user + /// + /// This will return true if the input stream is a terminal (tty or + /// pty) and can cause IO handlers to do different things (like + /// for a comfirmation when deleting all breakpoints). + //------------------------------------------------------------------ + bool + GetIsInteractive (); + + //------------------------------------------------------------------ + /// Check if the input is coming from a real terminal. + /// + /// A real terminal has a valid size with a certain number of rows + /// and colums. If this function returns true, then terminal escape + /// sequences are expected to work (cursor movement escape sequences, + /// clearning lines, etc). + //------------------------------------------------------------------ + bool + GetIsRealTerminal (); + + protected: + Debugger &m_debugger; + lldb::StreamFileSP m_input_sp; + lldb::StreamFileSP m_output_sp; + lldb::StreamFileSP m_error_sp; + Flags m_flags; + void *m_user_data; + bool m_done; + bool m_active; + + private: + DISALLOW_COPY_AND_ASSIGN (IOHandler); + }; + + + //------------------------------------------------------------------ + /// A delegate class for use with IOHandler subclasses. + /// + /// The IOHandler delegate is designed to be mixed into classes so + /// they can use an IOHandler subclass to fetch input and notify the + /// object that inherits from this delegate class when a token is + /// received. + //------------------------------------------------------------------ + class IOHandlerDelegate + { + public: + enum class Completion { + None, + LLDBCommand, + Expression + }; + + IOHandlerDelegate (Completion completion = Completion::None) : + m_completion(completion), + m_io_handler_done (false) + { + } + + virtual + ~IOHandlerDelegate() + { + } + + virtual void + IOHandlerActivated (IOHandler &io_handler) + { + } + + virtual int + IOHandlerComplete (IOHandler &io_handler, + const char *current_line, + const char *cursor, + const char *last_char, + int skip_first_n_matches, + int max_matches, + StringList &matches); + + //------------------------------------------------------------------ + /// Called when a line or lines have been retrieved. + /// + /// This funtion can handle the current line and possibly call + /// IOHandler::SetIsDone(true) when the IO handler is done like when + /// "quit" is entered as a command, of when an empty line is + /// received. It is up to the delegate to determine when a line + /// should cause a IOHandler to exit. + //------------------------------------------------------------------ + virtual void + IOHandlerInputComplete (IOHandler &io_handler, std::string &data) = 0; + + //------------------------------------------------------------------ + /// Called when a line in \a lines has been updated when doing + /// multi-line input. + /// + /// @return + /// Return an enumeration to indicate the status of the current + /// line: + /// Success - The line is good and should be added to the + /// multiple lines + /// Error - There is an error with the current line and it + /// need to be re-edited before it is acceptable + /// Done - The lines collection is complete and ready to be + /// returned. + //------------------------------------------------------------------ + virtual LineStatus + IOHandlerLinesUpdated (IOHandler &io_handler, + StringList &lines, + uint32_t line_idx, + Error &error) + { + return LineStatus::Done; // Stop getting lines on the first line that is updated + // subclasses should do something more intelligent here. + // This function will not be called on IOHandler objects + // that are getting single lines. + } + + + virtual ConstString + GetControlSequence (char ch) + { + return ConstString(); + } + + protected: + Completion m_completion; // Support for common builtin completions + bool m_io_handler_done; + }; + + //---------------------------------------------------------------------- + // IOHandlerDelegateMultiline + // + // A IOHandlerDelegate that handles terminating multi-line input when + // the last line is equal to "end_line" which is specified in the + // constructor. + //---------------------------------------------------------------------- + class IOHandlerDelegateMultiline : + public IOHandlerDelegate + { + public: + IOHandlerDelegateMultiline (const char *end_line, + Completion completion = Completion::None) : + IOHandlerDelegate (completion), + m_end_line((end_line && end_line[0]) ? end_line : "") + { + } + + virtual + ~IOHandlerDelegateMultiline () + { + } + + virtual ConstString + GetControlSequence (char ch) + { + if (ch == 'd') + return ConstString (m_end_line + "\n"); + return ConstString(); + } + + virtual LineStatus + IOHandlerLinesUpdated (IOHandler &io_handler, + StringList &lines, + uint32_t line_idx, + Error &error) + { + if (line_idx == UINT32_MAX) + { + // Remove the last empty line from "lines" so it doesn't appear + // in our final expression and return true to indicate we are done + // getting lines + lines.PopBack(); + return LineStatus::Done; + } + else if (line_idx + 1 == lines.GetSize()) + { + // The last line was edited, if this line is empty, then we are done + // getting our multiple lines. + if (lines[line_idx] == m_end_line) + return LineStatus::Done; + } + return LineStatus::Success; + } + protected: + const std::string m_end_line; + }; + + + class IOHandlerEditline : public IOHandler + { + public: + IOHandlerEditline (Debugger &debugger, + const char *editline_name, // Used for saving history files + const char *prompt, + bool multi_line, + IOHandlerDelegate &delegate); + + IOHandlerEditline (Debugger &debugger, + const lldb::StreamFileSP &input_sp, + const lldb::StreamFileSP &output_sp, + const lldb::StreamFileSP &error_sp, + uint32_t flags, + const char *editline_name, // Used for saving history files + const char *prompt, + bool multi_line, + IOHandlerDelegate &delegate); + + virtual + ~IOHandlerEditline (); + + virtual void + Run (); + + virtual void + Hide (); + + virtual void + Refresh (); + + virtual void + Interrupt (); + + virtual void + GotEOF(); + + virtual void + Activate () + { + IOHandler::Activate(); + m_delegate.IOHandlerActivated(*this); + } + + virtual ConstString + GetControlSequence (char ch) + { + return m_delegate.GetControlSequence (ch); + } + + virtual const char * + GetPrompt (); + + virtual bool + SetPrompt (const char *prompt); + + bool + GetLine (std::string &line); + + bool + GetLines (StringList &lines); + + private: + static LineStatus + LineCompletedCallback (Editline *editline, + StringList &lines, + uint32_t line_idx, + Error &error, + void *baton); + + static int AutoCompleteCallback (const char *current_line, + const char *cursor, + const char *last_char, + int skip_first_n_matches, + int max_matches, + StringList &matches, + void *baton); + + protected: + std::unique_ptr<Editline> m_editline_ap; + IOHandlerDelegate &m_delegate; + std::string m_prompt; + bool m_multi_line; + }; + + class IOHandlerConfirm : + public IOHandlerEditline, + public IOHandlerDelegate + { + public: + IOHandlerConfirm (Debugger &debugger, + const char *prompt, + bool default_response); + + virtual + ~IOHandlerConfirm (); + + bool + GetResponse () const + { + return m_user_response; + } + + virtual int + IOHandlerComplete (IOHandler &io_handler, + const char *current_line, + const char *cursor, + const char *last_char, + int skip_first_n_matches, + int max_matches, + StringList &matches); + + virtual void + IOHandlerInputComplete (IOHandler &io_handler, std::string &data); + + protected: + const bool m_default_response; + bool m_user_response; + }; + + class IOHandlerCursesGUI : + public IOHandler + { + public: + IOHandlerCursesGUI (Debugger &debugger); + + virtual + ~IOHandlerCursesGUI (); + + virtual void + Run (); + + virtual void + Hide (); + + virtual void + Refresh (); + + virtual void + Interrupt (); + + virtual void + GotEOF(); + + virtual void + Activate (); + + virtual void + Deactivate (); + + protected: + curses::ApplicationAP m_app_ap; + }; + + class IOHandlerCursesValueObjectList : + public IOHandler + { + public: + IOHandlerCursesValueObjectList (Debugger &debugger, ValueObjectList &valobj_list); + + virtual + ~IOHandlerCursesValueObjectList (); + + virtual void + Run (); + + virtual void + Hide (); + + virtual void + Refresh (); + + virtual void + Interrupt (); + + virtual void + GotEOF(); + protected: + ValueObjectList m_valobj_list; + }; + + class IOHandlerStack + { + public: + + IOHandlerStack () : + m_stack(), + m_mutex(Mutex::eMutexTypeRecursive), + m_top (NULL) + { + } + + ~IOHandlerStack () + { + } + + size_t + GetSize () const + { + Mutex::Locker locker (m_mutex); + return m_stack.size(); + } + + void + Push (const lldb::IOHandlerSP& sp) + { + if (sp) + { + Mutex::Locker locker (m_mutex); + m_stack.push (sp); + // Set m_top the non-locking IsTop() call + m_top = sp.get(); + } + } + + bool + IsEmpty () const + { + Mutex::Locker locker (m_mutex); + return m_stack.empty(); + } + + lldb::IOHandlerSP + Top () + { + lldb::IOHandlerSP sp; + { + Mutex::Locker locker (m_mutex); + if (!m_stack.empty()) + sp = m_stack.top(); + } + return sp; + } + + void + Pop () + { + Mutex::Locker locker (m_mutex); + if (!m_stack.empty()) + m_stack.pop(); + // Set m_top the non-locking IsTop() call + if (m_stack.empty()) + m_top = NULL; + else + m_top = m_stack.top().get(); + } + + Mutex & + GetMutex() + { + return m_mutex; + } + + bool + IsTop (const lldb::IOHandlerSP &io_handler_sp) const + { + return m_top == io_handler_sp.get(); + } + + ConstString + GetTopIOHandlerControlSequence (char ch) + { + if (m_top) + return m_top->GetControlSequence(ch); + return ConstString(); + } + + protected: + + std::stack<lldb::IOHandlerSP> m_stack; + mutable Mutex m_mutex; + IOHandler *m_top; + + private: + + DISALLOW_COPY_AND_ASSIGN (IOHandlerStack); + }; + +} // namespace lldb_private + +#endif // #ifndef liblldb_IOHandler_h_ diff --git a/include/lldb/Core/InputReader.h b/include/lldb/Core/InputReader.h deleted file mode 100644 index fa86e9a39e2b..000000000000 --- a/include/lldb/Core/InputReader.h +++ /dev/null @@ -1,274 +0,0 @@ -//===-- InputReader.h -------------------------------------------*- C++ -*-===// -// -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// - -#ifndef liblldb_InputReader_h_ -#define liblldb_InputReader_h_ - -#include <string.h> - -#include "lldb/lldb-public.h" -#include "lldb/lldb-enumerations.h" -#include "lldb/Core/Error.h" -#include "lldb/Core/StringList.h" -#include "lldb/Host/Predicate.h" - - -namespace lldb_private { - -class InputReader -{ -public: - - typedef size_t (*Callback) (void *baton, - InputReader &reader, - lldb::InputReaderAction notification, - const char *bytes, - size_t bytes_len); - - struct HandlerData - { - InputReader& reader; - const char *bytes; - size_t bytes_len; - void* baton; - - HandlerData(InputReader& r, - const char* b, - size_t l, - void* t) : - reader(r), - bytes(b), - bytes_len(l), - baton(t) - { - } - - lldb::StreamSP - GetOutStream(); - - bool - GetBatchMode(); - }; - - struct InitializationParameters - { - private: - void* m_baton; - lldb::InputReaderGranularity m_token_size; - char* m_end_token; - char* m_prompt; - bool m_echo; - bool m_save_user_input; - public: - InitializationParameters() : - m_baton(NULL), - m_token_size(lldb::eInputReaderGranularityLine), - m_echo(true), - m_save_user_input(false) - { - SetEndToken("DONE"); - SetPrompt("> "); - } - - InitializationParameters& - SetEcho(bool e) - { - m_echo = e; - return *this; - } - - InitializationParameters& - SetSaveUserInput(bool s) - { - m_save_user_input = s; - return *this; - } - - InitializationParameters& - SetBaton(void* b) - { - m_baton = b; - return *this; - } - - InitializationParameters& - SetGranularity(lldb::InputReaderGranularity g) - { - m_token_size = g; - return *this; - } - - InitializationParameters& - SetEndToken(const char* e) - { - m_end_token = new char[strlen(e)+1]; - ::strcpy(m_end_token,e); - return *this; - } - - InitializationParameters& - SetPrompt(const char* p) - { - m_prompt = new char[strlen(p)+1]; - ::strcpy(m_prompt,p); - return *this; - } - - friend class InputReaderEZ; - - }; - - InputReader (Debugger &debugger); - - virtual - ~InputReader (); - - virtual Error - Initialize (Callback callback, - void *baton, - lldb::InputReaderGranularity token_size, - const char *end_token, - const char *prompt, - bool echo); - - virtual Error Initialize(void* baton, - lldb::InputReaderGranularity token_size = lldb::eInputReaderGranularityLine, - const char* end_token = "DONE", - const char *prompt = "> ", - bool echo = true) - { - return Error("unimplemented"); - } - - virtual Error - Initialize(InitializationParameters& params) - { - return Error("unimplemented"); - } - - // to use these handlers instead of the Callback function, you must subclass - // InputReaderEZ, and redefine the handlers for the events you care about - virtual void - ActivateHandler(HandlerData&) {} - - virtual void - DeactivateHandler(HandlerData&) {} - - virtual void - ReactivateHandler(HandlerData&) {} - - virtual void - AsynchronousOutputWrittenHandler(HandlerData&) {} - - virtual void - GotTokenHandler(HandlerData&) {} - - virtual void - InterruptHandler(HandlerData&) {} - - virtual void - EOFHandler(HandlerData&) {} - - virtual void - DoneHandler(HandlerData&) {} - - bool - IsDone () const - { - return m_done; - } - - void - SetIsDone (bool b) - { - m_done = b; - } - - lldb::InputReaderGranularity - GetGranularity () const - { - return m_granularity; - } - - bool - GetEcho () const - { - return m_echo; - } - - StringList& - GetUserInput() - { - return m_user_input; - } - - virtual bool - GetSaveUserInput() - { - return false; - } - - // Subclasses _can_ override this function to get input as it comes in - // without any granularity - virtual size_t - HandleRawBytes (const char *bytes, size_t bytes_len); - - Debugger & - GetDebugger() - { - return m_debugger; - } - - bool - IsActive () const - { - return m_active; - } - - const char * - GetPrompt () const; - - void - RefreshPrompt(); - - // If you want to read from an input reader synchronously, then just initialize the - // reader and then call WaitOnReaderIsDone, which will return when the reader is popped. - void - WaitOnReaderIsDone (); - - static const char * - GranularityAsCString (lldb::InputReaderGranularity granularity); - -protected: - friend class Debugger; - - void - Notify (lldb::InputReaderAction notification); - - Debugger &m_debugger; - Callback m_callback; - void *m_callback_baton; - std::string m_end_token; - std::string m_prompt; - lldb::InputReaderGranularity m_granularity; - bool m_done; - bool m_echo; - bool m_active; - Predicate<bool> m_reader_done; - StringList m_user_input; - bool m_save_user_input; - -private: - DISALLOW_COPY_AND_ASSIGN (InputReader); - -}; - -} // namespace lldb_private - -#endif // #ifndef liblldb_InputReader_h_ diff --git a/include/lldb/Core/InputReaderEZ.h b/include/lldb/Core/InputReaderEZ.h deleted file mode 100644 index 85561b6f0a9e..000000000000 --- a/include/lldb/Core/InputReaderEZ.h +++ /dev/null @@ -1,87 +0,0 @@ -//===-- InputReaderEZ.h -----------------------------------------*- C++ -*-===// -// -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// - -#ifndef liblldb_InputReaderEZ_h_ -#define liblldb_InputReaderEZ_h_ - -#include "lldb/Core/InputReader.h" - -namespace lldb_private { - -class InputReaderEZ : public InputReader -{ - -private: - - static size_t Callback_Impl(void *baton, - InputReader &reader, - lldb::InputReaderAction notification, - const char *bytes, - size_t bytes_len); -public: - - InputReaderEZ (Debugger &debugger) : - InputReader(debugger) - {} - - virtual - ~InputReaderEZ (); - - using InputReader::Initialize; - virtual Error - Initialize(void* baton, - lldb::InputReaderGranularity token_size = lldb::eInputReaderGranularityLine, - const char* end_token = "DONE", - const char *prompt = "> ", - bool echo = true); - - virtual Error - Initialize(InitializationParameters& params); - - virtual void - ActivateHandler(HandlerData&) {} - - virtual void - DeactivateHandler(HandlerData&) {} - - virtual void - ReactivateHandler(HandlerData&) {} - - virtual void - AsynchronousOutputWrittenHandler(HandlerData&) {} - - virtual void - GotTokenHandler(HandlerData&) {} - - virtual void - InterruptHandler(HandlerData&) {} - - virtual void - EOFHandler(HandlerData&) {} - - virtual void - DoneHandler(HandlerData&) {} - - virtual bool - GetSaveUserInput() - { - return m_save_user_input; - } - -protected: - friend class Debugger; - -private: - DISALLOW_COPY_AND_ASSIGN (InputReaderEZ); - -}; - -} // namespace lldb_private - -#endif // #ifndef liblldb_InputReaderEZ_h_ diff --git a/include/lldb/Core/InputReaderStack.h b/include/lldb/Core/InputReaderStack.h deleted file mode 100644 index a73b97cad571..000000000000 --- a/include/lldb/Core/InputReaderStack.h +++ /dev/null @@ -1,58 +0,0 @@ -//===-- InputReaderStack.h --------------------------------------*- C++ -*-===// -// -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// - -#ifndef liblldb_InputReaderStack_h_ -#define liblldb_InputReaderStack_h_ - -#include <stack> - -#include "lldb/lldb-private.h" -#include "lldb/Host/Mutex.h" - -namespace lldb_private { - -class InputReaderStack -{ -public: - - InputReaderStack (); - - ~InputReaderStack (); - - size_t - GetSize () const; - - void - Push (const lldb::InputReaderSP& reader_sp); - - bool - IsEmpty () const; - - lldb::InputReaderSP - Top (); - - void - Pop (); - - Mutex & - GetStackMutex (); - -protected: - - std::stack<lldb::InputReaderSP> m_input_readers; - mutable Mutex m_input_readers_mutex; - -private: - - DISALLOW_COPY_AND_ASSIGN (InputReaderStack); -}; - -} // namespace lldb_private - -#endif // liblldb_InputReaderStack_h_ diff --git a/include/lldb/Core/MappedHash.h b/include/lldb/Core/MappedHash.h index 80d249d4cc9e..4b77ff1ab8fc 100644 --- a/include/lldb/Core/MappedHash.h +++ b/include/lldb/Core/MappedHash.h @@ -382,9 +382,9 @@ public: lldb::offset_t offset = m_header.Read (data, 0); if (offset != LLDB_INVALID_OFFSET && IsValid ()) { - m_hash_indexes = (uint32_t *)data.GetData (&offset, m_header.bucket_count * sizeof(uint32_t)); - m_hash_values = (uint32_t *)data.GetData (&offset, m_header.hashes_count * sizeof(uint32_t)); - m_hash_offsets = (uint32_t *)data.GetData (&offset, m_header.hashes_count * sizeof(uint32_t)); + m_hash_indexes = (const uint32_t *)data.GetData (&offset, m_header.bucket_count * sizeof(uint32_t)); + m_hash_values = (const uint32_t *)data.GetData (&offset, m_header.hashes_count * sizeof(uint32_t)); + m_hash_offsets = (const uint32_t *)data.GetData (&offset, m_header.hashes_count * sizeof(uint32_t)); } } @@ -542,9 +542,9 @@ public: protected: // Implementation agnostic information HeaderType m_header; - uint32_t *m_hash_indexes; - uint32_t *m_hash_values; - uint32_t *m_hash_offsets; + const uint32_t *m_hash_indexes; + const uint32_t *m_hash_values; + const uint32_t *m_hash_offsets; }; }; diff --git a/include/lldb/Core/Module.h b/include/lldb/Core/Module.h index cae5a30be704..56650582791b 100644 --- a/include/lldb/Core/Module.h +++ b/include/lldb/Core/Module.h @@ -113,10 +113,18 @@ public: /// @param[in] target /// The target in which to apply the section load addresses. /// - /// @param[in] offset - /// The offset to apply to all file addresses for all top - /// level sections in the object file as each section load - /// address is being set. + /// @param[in] value + /// if \a value_is_offset is true, then value is the offset to + /// apply to all file addresses for all top level sections in + /// the object file as each section load address is being set. + /// If \a value_is_offset is false, then "value" is the new + /// absolute base address for the image. + /// + /// @param[in] value_is_offset + /// If \b true, then \a value is an offset to apply to each + /// file address of each top level section. + /// If \b false, then \a value is the image base address that + /// will be used to rigidly slide all loadable sections. /// /// @param[out] changed /// If any section load addresses were changed in \a target, @@ -133,7 +141,8 @@ public: //------------------------------------------------------------------ bool SetLoadAddress (Target &target, - lldb::addr_t offset, + lldb::addr_t value, + bool value_is_offset, bool &changed); //------------------------------------------------------------------ diff --git a/include/lldb/Core/Opcode.h b/include/lldb/Core/Opcode.h index fd80231a44e7..57b8077477ce 100644 --- a/include/lldb/Core/Opcode.h +++ b/include/lldb/Core/Opcode.h @@ -15,7 +15,9 @@ // C++ Includes // Other libraries and framework includes +#include "llvm/Support/MathExtras.h" // Project includes +#include "lldb/Host/Endian.h" #include "lldb/lldb-public.h" namespace lldb @@ -39,31 +41,31 @@ namespace lldb_private { eTypeBytes }; - Opcode () : m_type (eTypeInvalid) + Opcode () : m_byte_order (lldb::eByteOrderInvalid), m_type (eTypeInvalid) { } - Opcode (uint8_t inst) : m_type (eType8) + Opcode (uint8_t inst, lldb::ByteOrder order) : m_byte_order (order), m_type (eType8) { m_data.inst8 = inst; } - Opcode (uint16_t inst) : m_type (eType16) + Opcode (uint16_t inst, lldb::ByteOrder order) : m_byte_order (order), m_type (eType16) { m_data.inst16 = inst; } - Opcode (uint32_t inst) : m_type (eType32) + Opcode (uint32_t inst, lldb::ByteOrder order) : m_byte_order (order), m_type (eType32) { m_data.inst32 = inst; } - Opcode (uint64_t inst) : m_type (eType64) + Opcode (uint64_t inst, lldb::ByteOrder order) : m_byte_order (order), m_type (eType64) { m_data.inst64 = inst; } - Opcode (uint8_t *bytes, size_t length) + Opcode (uint8_t *bytes, size_t length) : m_byte_order (lldb::eByteOrderInvalid) { SetOpcodeBytes (bytes, length); } @@ -71,6 +73,7 @@ namespace lldb_private { void Clear() { + m_byte_order = lldb::eByteOrderInvalid; m_type = Opcode::eTypeInvalid; } Opcode::Type @@ -102,7 +105,7 @@ namespace lldb_private { { case Opcode::eTypeInvalid: break; case Opcode::eType8: return m_data.inst8; - case Opcode::eType16: return m_data.inst16; + case Opcode::eType16: return GetEndianSwap() ? llvm::ByteSwap_16(m_data.inst16) : m_data.inst16; case Opcode::eType16_2: break; case Opcode::eType32: break; case Opcode::eType64: break; @@ -118,9 +121,9 @@ namespace lldb_private { { case Opcode::eTypeInvalid: break; case Opcode::eType8: return m_data.inst8; - case Opcode::eType16: return m_data.inst16; + case Opcode::eType16: return GetEndianSwap() ? llvm::ByteSwap_16(m_data.inst16) : m_data.inst16; case Opcode::eType16_2: // passthrough - case Opcode::eType32: return m_data.inst32; + case Opcode::eType32: return GetEndianSwap() ? llvm::ByteSwap_32(m_data.inst32) : m_data.inst32; case Opcode::eType64: break; case Opcode::eTypeBytes: break; } @@ -134,48 +137,53 @@ namespace lldb_private { { case Opcode::eTypeInvalid: break; case Opcode::eType8: return m_data.inst8; - case Opcode::eType16: return m_data.inst16; + case Opcode::eType16: return GetEndianSwap() ? llvm::ByteSwap_16(m_data.inst16) : m_data.inst16; case Opcode::eType16_2: // passthrough - case Opcode::eType32: return m_data.inst32; - case Opcode::eType64: return m_data.inst64; + case Opcode::eType32: return GetEndianSwap() ? llvm::ByteSwap_32(m_data.inst32) : m_data.inst32; + case Opcode::eType64: return GetEndianSwap() ? llvm::ByteSwap_64(m_data.inst64) : m_data.inst64; case Opcode::eTypeBytes: break; } return invalid_opcode; } void - SetOpcode8 (uint8_t inst) + SetOpcode8 (uint8_t inst, lldb::ByteOrder order) { m_type = eType8; m_data.inst8 = inst; + m_byte_order = order; } void - SetOpcode16 (uint16_t inst) + SetOpcode16 (uint16_t inst, lldb::ByteOrder order) { m_type = eType16; m_data.inst16 = inst; + m_byte_order = order; } void - SetOpcode16_2 (uint32_t inst) + SetOpcode16_2 (uint32_t inst, lldb::ByteOrder order) { m_type = eType16_2; m_data.inst32 = inst; + m_byte_order = order; } void - SetOpcode32 (uint32_t inst) + SetOpcode32 (uint32_t inst, lldb::ByteOrder order) { m_type = eType32; m_data.inst32 = inst; + m_byte_order = order; } void - SetOpcode64 (uint64_t inst) + SetOpcode64 (uint64_t inst, lldb::ByteOrder order) { m_type = eType64; m_data.inst64 = inst; + m_byte_order = order; } void @@ -187,6 +195,7 @@ namespace lldb_private { m_data.inst.length = length; assert (length < sizeof (m_data.inst.bytes)); memcpy (m_data.inst.bytes, bytes, length); + m_byte_order = lldb::eByteOrderInvalid; } else { @@ -249,6 +258,15 @@ namespace lldb_private { lldb::ByteOrder GetDataByteOrder () const; + bool + GetEndianSwap() const + { + return (m_byte_order == lldb::eByteOrderBig && lldb::endian::InlHostByteOrder() == lldb::eByteOrderLittle) || + (m_byte_order == lldb::eByteOrderLittle && lldb::endian::InlHostByteOrder() == lldb::eByteOrderBig); + } + + lldb::ByteOrder m_byte_order; + Opcode::Type m_type; union { diff --git a/include/lldb/Core/SourceManager.h b/include/lldb/Core/SourceManager.h index b850df774bac..d8c9eccd3477 100644 --- a/include/lldb/Core/SourceManager.h +++ b/include/lldb/Core/SourceManager.h @@ -70,6 +70,15 @@ public: return m_source_map_mod_id; } + const char * + PeekLineData (uint32_t line); + + uint32_t + GetLineLength (uint32_t line, bool include_newline_chars); + + uint32_t + GetNumLines (); + protected: bool @@ -167,11 +176,11 @@ public: uint32_t start_line, uint32_t end_line, std::vector<uint32_t> &match_lines); - -protected: - + FileSP GetFile (const FileSpec &file_spec); + +protected: //------------------------------------------------------------------ // Classes that inherit from SourceManager can see and modify these diff --git a/include/lldb/Core/StreamAsynchronousIO.h b/include/lldb/Core/StreamAsynchronousIO.h index 0e3e9ee9bcf1..a73a9567fe83 100644 --- a/include/lldb/Core/StreamAsynchronousIO.h +++ b/include/lldb/Core/StreamAsynchronousIO.h @@ -13,7 +13,6 @@ #include <string> #include "lldb/Core/Stream.h" -#include "lldb/Core/StreamString.h" namespace lldb_private { @@ -35,7 +34,7 @@ public: private: Broadcaster &m_broadcaster; uint32_t m_broadcast_event_type; - StreamString m_accumulated_data; + std::string m_accumulated_data; }; } // namespace lldb_private diff --git a/include/lldb/Core/StringList.h b/include/lldb/Core/StringList.h index 5503274173cb..b68ab4be2d6d 100644 --- a/include/lldb/Core/StringList.h +++ b/include/lldb/Core/StringList.h @@ -34,6 +34,9 @@ public: AppendString (const std::string &s); void + AppendString (std::string &&s); + + void AppendString (const char *str); void @@ -51,6 +54,34 @@ public: size_t GetSize () const; + void + SetSize (size_t n) + { + m_strings.resize(n); + } + + size_t + GetMaxStringLength () const; + + std::string & + operator [](size_t idx) + { + // No bounds checking, verify "idx" is good prior to calling this function + return m_strings[idx]; + } + + const std::string & + operator [](size_t idx) const + { + // No bounds checking, verify "idx" is good prior to calling this function + return m_strings[idx]; + } + + void + PopBack () + { + m_strings.pop_back(); + } const char * GetStringAtIndex (size_t idx) const; @@ -64,6 +95,12 @@ public: LongestCommonPrefix (std::string &common_prefix); void + InsertStringAtIndex (size_t idx, const std::string &str); + + void + InsertStringAtIndex (size_t idx, std::string &&str); + + void InsertStringAtIndex (size_t id, const char *str); void @@ -73,11 +110,14 @@ public: RemoveBlankLines (); size_t + SplitIntoLines (const std::string &lines); + + size_t SplitIntoLines (const char *lines, size_t len); std::string CopyList(const char* item_preamble = NULL, - const char* items_sep = "\n"); + const char* items_sep = "\n") const; StringList& operator << (const char* str); diff --git a/include/lldb/Core/ValueObject.h b/include/lldb/Core/ValueObject.h index 1ef421676ee1..e2847c778484 100644 --- a/include/lldb/Core/ValueObject.h +++ b/include/lldb/Core/ValueObject.h @@ -499,6 +499,10 @@ public: GetValueAsCString (); virtual bool + GetValueAsCString (const lldb_private::TypeFormatImpl& format, + std::string& destination); + + bool GetValueAsCString (lldb::Format format, std::string& destination); @@ -615,7 +619,8 @@ public: DumpPrintableRepresentation (Stream& s, ValueObjectRepresentationStyle val_obj_display = eValueObjectRepresentationStyleSummary, lldb::Format custom_format = lldb::eFormatInvalid, - PrintableRepresentationSpecialCases special = ePrintableRepresentationSpecialCasesAllow); + PrintableRepresentationSpecialCases special = ePrintableRepresentationSpecialCasesAllow, + bool do_dump_error = true); bool GetValueIsValid () const; diff --git a/include/lldb/Core/ValueObjectList.h b/include/lldb/Core/ValueObjectList.h index 5bfe40b2e952..6565367cc61c 100644 --- a/include/lldb/Core/ValueObjectList.h +++ b/include/lldb/Core/ValueObjectList.h @@ -75,6 +75,12 @@ public: void Swap (ValueObjectList &value_object_list); + void + Clear () + { + m_value_objects.clear(); + } + protected: typedef std::vector<lldb::ValueObjectSP> collection; //------------------------------------------------------------------ diff --git a/include/lldb/DataFormatters/CXXFormatterFunctions.h b/include/lldb/DataFormatters/CXXFormatterFunctions.h index 415ef9be59ef..c53ef9589eea 100644 --- a/include/lldb/DataFormatters/CXXFormatterFunctions.h +++ b/include/lldb/DataFormatters/CXXFormatterFunctions.h @@ -79,6 +79,9 @@ namespace lldb_private { bool LibcxxWStringSummaryProvider (ValueObject& valobj, Stream& stream); // libc++ std::wstring + + bool + LibcxxSmartPointerSummaryProvider (ValueObject& valobj, Stream& stream); // libc++ std::shared_ptr<> and std::weak_ptr<> bool ObjCClassSummaryProvider (ValueObject& valobj, Stream& stream); @@ -594,10 +597,11 @@ namespace lldb_private { virtual ~LibcxxVectorBoolSyntheticFrontEnd (); private: + ClangASTType m_bool_type; ExecutionContextRef m_exe_ctx_ref; uint64_t m_count; lldb::addr_t m_base_data_address; - EvaluateExpressionOptions m_options; + std::map<size_t,lldb::ValueObjectSP> m_children; }; SyntheticChildrenFrontEnd* LibcxxVectorBoolSyntheticFrontEndCreator (CXXSyntheticChildren*, lldb::ValueObjectSP); diff --git a/include/lldb/DataFormatters/FormatManager.h b/include/lldb/DataFormatters/FormatManager.h index 750e53008318..24ec877ee515 100644 --- a/include/lldb/DataFormatters/FormatManager.h +++ b/include/lldb/DataFormatters/FormatManager.h @@ -20,7 +20,7 @@ #include "lldb/DataFormatters/FormatCache.h" #include "lldb/DataFormatters/FormatClasses.h" -#include "lldb/DataFormatters/FormatNavigator.h" +#include "lldb/DataFormatters/FormattersContainer.h" #include "lldb/DataFormatters/TypeCategory.h" #include "lldb/DataFormatters/TypeCategoryMap.h" @@ -44,7 +44,7 @@ public: FormatManager (); NamedSummariesMap& - GetNamedSummaryNavigator () + GetNamedSummaryContainer () { return m_named_summaries_map; } diff --git a/include/lldb/DataFormatters/FormatNavigator.h b/include/lldb/DataFormatters/FormattersContainer.h index 1b82776fb28c..de838d1ab9c1 100644 --- a/include/lldb/DataFormatters/FormatNavigator.h +++ b/include/lldb/DataFormatters/FormattersContainer.h @@ -1,4 +1,4 @@ -//===-- FormatNavigator.h ----------------------------------------*- C++ -*-===// +//===-- FormattersContainer.h ----------------------------------------*- C++ -*-===// // // The LLVM Compiler Infrastructure // @@ -7,8 +7,8 @@ // //===----------------------------------------------------------------------===// -#ifndef lldb_FormatNavigator_h_ -#define lldb_FormatNavigator_h_ +#ifndef lldb_FormattersContainer_h_ +#define lldb_FormattersContainer_h_ // C Includes // C++ Includes @@ -104,7 +104,7 @@ GetValidTypeName_Impl (const ConstString& type) } template<typename KeyType, typename ValueType> -class FormatNavigator; +class FormattersContainer; template<typename KeyType, typename ValueType> class FormatMap @@ -243,13 +243,13 @@ protected: return m_map_mutex; } - friend class FormatNavigator<KeyType, ValueType>; + friend class FormattersContainer<KeyType, ValueType>; friend class FormatManager; }; template<typename KeyType, typename ValueType> -class FormatNavigator +class FormattersContainer { protected: typedef FormatMap<KeyType,ValueType> BackEndType; @@ -260,11 +260,11 @@ public: typedef typename MapType::key_type MapKeyType; typedef typename MapType::mapped_type MapValueType; typedef typename BackEndType::CallbackType CallbackType; - typedef typename std::shared_ptr<FormatNavigator<KeyType, ValueType> > SharedPointer; + typedef typename std::shared_ptr<FormattersContainer<KeyType, ValueType> > SharedPointer; friend class TypeCategoryImpl; - FormatNavigator(std::string name, + FormattersContainer(std::string name, IFormatChangeListener* lst) : m_format_map(lst), m_name(name), @@ -350,7 +350,7 @@ protected: std::string m_name; - DISALLOW_COPY_AND_ASSIGN(FormatNavigator); + DISALLOW_COPY_AND_ASSIGN(FormattersContainer); ConstString m_id_cs; @@ -470,6 +470,7 @@ protected: { for (const FormattersMatchCandidate& candidate : candidates) { + // FIXME: could we do the IsMatch() check first? if (Get(candidate.GetTypeName(),entry)) { if (candidate.IsMatch(entry) == false) @@ -491,4 +492,4 @@ protected: } // namespace lldb_private -#endif // lldb_FormatNavigator_h_ +#endif // lldb_FormattersContainer_h_ diff --git a/include/lldb/DataFormatters/TypeCategory.h b/include/lldb/DataFormatters/TypeCategory.h index 082395a04616..491fef29b5c5 100644 --- a/include/lldb/DataFormatters/TypeCategory.h +++ b/include/lldb/DataFormatters/TypeCategory.h @@ -19,108 +19,131 @@ #include "lldb/lldb-enumerations.h" #include "lldb/DataFormatters/FormatClasses.h" -#include "lldb/DataFormatters/FormatNavigator.h" +#include "lldb/DataFormatters/FormattersContainer.h" -namespace lldb_private { - class TypeCategoryImpl +namespace lldb_private { + + template <typename FormatterImpl> + class FormatterContainerPair { - private: - typedef FormatNavigator<ConstString, TypeFormatImpl> ValueNavigator; - typedef FormatNavigator<lldb::RegularExpressionSP, TypeFormatImpl> RegexValueNavigator; + public: + typedef FormattersContainer<ConstString, FormatterImpl> ExactMatchContainer; + typedef FormattersContainer<lldb::RegularExpressionSP, FormatterImpl> RegexMatchContainer; - typedef FormatNavigator<ConstString, TypeSummaryImpl> SummaryNavigator; - typedef FormatNavigator<lldb::RegularExpressionSP, TypeSummaryImpl> RegexSummaryNavigator; + typedef typename ExactMatchContainer::MapType ExactMatchMap; + typedef typename RegexMatchContainer::MapType RegexMatchMap; + + typedef typename ExactMatchContainer::MapValueType MapValueType; - typedef FormatNavigator<ConstString, TypeFilterImpl> FilterNavigator; - typedef FormatNavigator<lldb::RegularExpressionSP, TypeFilterImpl> RegexFilterNavigator; + typedef typename ExactMatchContainer::SharedPointer ExactMatchContainerSP; + typedef typename RegexMatchContainer::SharedPointer RegexMatchContainerSP; -#ifndef LLDB_DISABLE_PYTHON - typedef FormatNavigator<ConstString, ScriptedSyntheticChildren> SynthNavigator; - typedef FormatNavigator<lldb::RegularExpressionSP, ScriptedSyntheticChildren> RegexSynthNavigator; -#endif // #ifndef LLDB_DISABLE_PYTHON - - typedef ValueNavigator::MapType ValueMap; - typedef RegexValueNavigator::MapType RegexValueMap; - - typedef SummaryNavigator::MapType SummaryMap; - typedef RegexSummaryNavigator::MapType RegexSummaryMap; + FormatterContainerPair (const char* exact_name, + const char* regex_name, + IFormatChangeListener* clist) : + m_exact_sp(new ExactMatchContainer(std::string(exact_name),clist)), + m_regex_sp(new RegexMatchContainer(std::string(regex_name),clist)) + { + } + + ~FormatterContainerPair () = default; + + ExactMatchContainerSP + GetExactMatch () const + { + return m_exact_sp; + } - typedef FilterNavigator::MapType FilterMap; - typedef RegexFilterNavigator::MapType RegexFilterMap; + RegexMatchContainerSP + GetRegexMatch () const + { + return m_regex_sp; + } + + private: + ExactMatchContainerSP m_exact_sp; + RegexMatchContainerSP m_regex_sp; + }; + class TypeCategoryImpl + { + private: + typedef FormatterContainerPair<TypeFormatImpl> FormatContainer; + typedef FormatterContainerPair<TypeSummaryImpl> SummaryContainer; + typedef FormatterContainerPair<TypeFilterImpl> FilterContainer; + #ifndef LLDB_DISABLE_PYTHON - typedef SynthNavigator::MapType SynthMap; - typedef RegexSynthNavigator::MapType RegexSynthMap; + typedef FormatterContainerPair<ScriptedSyntheticChildren> SynthContainer; #endif // #ifndef LLDB_DISABLE_PYTHON - + public: typedef uint16_t FormatCategoryItems; static const uint16_t ALL_ITEM_TYPES = UINT16_MAX; - typedef ValueNavigator::SharedPointer ValueNavigatorSP; - typedef RegexValueNavigator::SharedPointer RegexValueNavigatorSP; + typedef FormatContainer::ExactMatchContainerSP FormatContainerSP; + typedef FormatContainer::RegexMatchContainerSP RegexFormatContainerSP; - typedef SummaryNavigator::SharedPointer SummaryNavigatorSP; - typedef RegexSummaryNavigator::SharedPointer RegexSummaryNavigatorSP; + typedef SummaryContainer::ExactMatchContainerSP SummaryContainerSP; + typedef SummaryContainer::RegexMatchContainerSP RegexSummaryContainerSP; - typedef FilterNavigator::SharedPointer FilterNavigatorSP; - typedef RegexFilterNavigator::SharedPointer RegexFilterNavigatorSP; + typedef FilterContainer::ExactMatchContainerSP FilterContainerSP; + typedef FilterContainer::RegexMatchContainerSP RegexFilterContainerSP; #ifndef LLDB_DISABLE_PYTHON - typedef SynthNavigator::SharedPointer SynthNavigatorSP; - typedef RegexSynthNavigator::SharedPointer RegexSynthNavigatorSP; + typedef SynthContainer::ExactMatchContainerSP SynthContainerSP; + typedef SynthContainer::RegexMatchContainerSP RegexSynthContainerSP; #endif // #ifndef LLDB_DISABLE_PYTHON TypeCategoryImpl (IFormatChangeListener* clist, ConstString name); - ValueNavigatorSP - GetValueNavigator () + FormatContainerSP + GetTypeFormatsContainer () { - return ValueNavigatorSP(m_value_nav); + return m_format_cont.GetExactMatch(); } - RegexValueNavigatorSP - GetRegexValueNavigator () + RegexFormatContainerSP + GetRegexTypeFormatsContainer () { - return RegexValueNavigatorSP(m_regex_value_nav); + return m_format_cont.GetRegexMatch(); } - SummaryNavigatorSP - GetSummaryNavigator () + SummaryContainerSP + GetTypeSummariesContainer () { - return SummaryNavigatorSP(m_summary_nav); + return m_summary_cont.GetExactMatch(); } - RegexSummaryNavigatorSP - GetRegexSummaryNavigator () + RegexSummaryContainerSP + GetRegexTypeSummariesContainer () { - return RegexSummaryNavigatorSP(m_regex_summary_nav); + return m_summary_cont.GetRegexMatch(); } - FilterNavigatorSP - GetFilterNavigator () + FilterContainerSP + GetTypeFiltersContainer () { - return FilterNavigatorSP(m_filter_nav); + return m_filter_cont.GetExactMatch(); } - RegexFilterNavigatorSP - GetRegexFilterNavigator () + RegexFilterContainerSP + GetRegexTypeFiltersContainer () { - return RegexFilterNavigatorSP(m_regex_filter_nav); + return m_filter_cont.GetRegexMatch(); } - ValueNavigator::MapValueType + FormatContainer::MapValueType GetFormatForType (lldb::TypeNameSpecifierImplSP type_sp); - SummaryNavigator::MapValueType + SummaryContainer::MapValueType GetSummaryForType (lldb::TypeNameSpecifierImplSP type_sp); - FilterNavigator::MapValueType + FilterContainer::MapValueType GetFilterForType (lldb::TypeNameSpecifierImplSP type_sp); #ifndef LLDB_DISABLE_PYTHON - SynthNavigator::MapValueType + SynthContainer::MapValueType GetSyntheticForType (lldb::TypeNameSpecifierImplSP type_sp); #endif @@ -130,32 +153,32 @@ namespace lldb_private { lldb::TypeNameSpecifierImplSP GetTypeNameSpecifierForSummaryAtIndex (size_t index); - ValueNavigator::MapValueType + FormatContainer::MapValueType GetFormatAtIndex (size_t index); - SummaryNavigator::MapValueType + SummaryContainer::MapValueType GetSummaryAtIndex (size_t index); - FilterNavigator::MapValueType + FilterContainer::MapValueType GetFilterAtIndex (size_t index); lldb::TypeNameSpecifierImplSP GetTypeNameSpecifierForFilterAtIndex (size_t index); #ifndef LLDB_DISABLE_PYTHON - SynthNavigatorSP - GetSyntheticNavigator () + SynthContainerSP + GetTypeSyntheticsContainer () { - return SynthNavigatorSP(m_synth_nav); + return m_synth_cont.GetExactMatch(); } - RegexSynthNavigatorSP - GetRegexSyntheticNavigator () + RegexSynthContainerSP + GetRegexTypeSyntheticsContainer () { - return RegexSynthNavigatorSP(m_regex_synth_nav); + return m_synth_cont.GetRegexMatch(); } - SynthNavigator::MapValueType + SynthContainer::MapValueType GetSyntheticAtIndex (size_t index); lldb::TypeNameSpecifierImplSP @@ -222,18 +245,14 @@ namespace lldb_private { typedef std::shared_ptr<TypeCategoryImpl> SharedPointer; private: - ValueNavigator::SharedPointer m_value_nav; - RegexValueNavigator::SharedPointer m_regex_value_nav; + FormatContainer m_format_cont; - SummaryNavigator::SharedPointer m_summary_nav; - RegexSummaryNavigator::SharedPointer m_regex_summary_nav; + SummaryContainer m_summary_cont; - FilterNavigator::SharedPointer m_filter_nav; - RegexFilterNavigator::SharedPointer m_regex_filter_nav; + FilterContainer m_filter_cont; #ifndef LLDB_DISABLE_PYTHON - SynthNavigator::SharedPointer m_synth_nav; - RegexSynthNavigator::SharedPointer m_regex_synth_nav; + SynthContainer m_synth_cont; #endif // #ifndef LLDB_DISABLE_PYTHON bool m_enabled; @@ -257,18 +276,18 @@ namespace lldb_private { friend class TypeCategoryMap; - friend class FormatNavigator<ConstString, TypeFormatImpl>; - friend class FormatNavigator<lldb::RegularExpressionSP, TypeFormatImpl>; + friend class FormattersContainer<ConstString, TypeFormatImpl>; + friend class FormattersContainer<lldb::RegularExpressionSP, TypeFormatImpl>; - friend class FormatNavigator<ConstString, TypeSummaryImpl>; - friend class FormatNavigator<lldb::RegularExpressionSP, TypeSummaryImpl>; + friend class FormattersContainer<ConstString, TypeSummaryImpl>; + friend class FormattersContainer<lldb::RegularExpressionSP, TypeSummaryImpl>; - friend class FormatNavigator<ConstString, TypeFilterImpl>; - friend class FormatNavigator<lldb::RegularExpressionSP, TypeFilterImpl>; + friend class FormattersContainer<ConstString, TypeFilterImpl>; + friend class FormattersContainer<lldb::RegularExpressionSP, TypeFilterImpl>; #ifndef LLDB_DISABLE_PYTHON - friend class FormatNavigator<ConstString, ScriptedSyntheticChildren>; - friend class FormatNavigator<lldb::RegularExpressionSP, ScriptedSyntheticChildren>; + friend class FormattersContainer<ConstString, ScriptedSyntheticChildren>; + friend class FormattersContainer<lldb::RegularExpressionSP, ScriptedSyntheticChildren>; #endif // #ifndef LLDB_DISABLE_PYTHON }; diff --git a/include/lldb/DataFormatters/TypeCategoryMap.h b/include/lldb/DataFormatters/TypeCategoryMap.h index 6bed18719893..88aaeb23bfd8 100644 --- a/include/lldb/DataFormatters/TypeCategoryMap.h +++ b/include/lldb/DataFormatters/TypeCategoryMap.h @@ -18,7 +18,7 @@ #include "lldb/lldb-public.h" #include "lldb/lldb-enumerations.h" -#include "lldb/DataFormatters/FormatNavigator.h" +#include "lldb/DataFormatters/FormattersContainer.h" #include "lldb/DataFormatters/TypeCategory.h" namespace lldb_private { @@ -144,7 +144,7 @@ namespace lldb_private { return m_map_mutex; } - friend class FormatNavigator<KeyType, ValueType>; + friend class FormattersContainer<KeyType, ValueType>; friend class FormatManager; }; } // namespace lldb_private diff --git a/include/lldb/DataFormatters/TypeFormat.h b/include/lldb/DataFormatters/TypeFormat.h index 77135c448ed1..20fa8f2d4e7f 100644 --- a/include/lldb/DataFormatters/TypeFormat.h +++ b/include/lldb/DataFormatters/TypeFormat.h @@ -130,15 +130,12 @@ namespace lldb_private { uint32_t m_flags; }; - TypeFormatImpl (lldb::Format f = lldb::eFormatInvalid, - const Flags& flags = Flags()); + TypeFormatImpl (const Flags& flags = Flags()); typedef std::shared_ptr<TypeFormatImpl> SharedPointer; typedef bool(*ValueCallback)(void*, ConstString, const lldb::TypeFormatImplSP&); - ~TypeFormatImpl () - { - } + virtual ~TypeFormatImpl () = default; bool Cascades () const @@ -173,6 +170,66 @@ namespace lldb_private { { m_flags.SetSkipReferences(value); } + + uint32_t + GetOptions () + { + return m_flags.GetValue(); + } + + void + SetOptions (uint32_t value) + { + m_flags.SetValue(value); + } + + uint32_t& + GetRevision () + { + return m_my_revision; + } + + enum class Type + { + eTypeUnknown, + eTypeFormat, + eTypeEnum + }; + + virtual Type + GetType () + { + return Type::eTypeUnknown; + } + + // we are using a ValueObject* instead of a ValueObjectSP because we do not need to hold on to this for + // extended periods of time and we trust the ValueObject to stay around for as long as it is required + // for us to generate its value + virtual bool + FormatObject (ValueObject *valobj, + std::string& dest) const = 0; + + virtual std::string + GetDescription() = 0; + + protected: + Flags m_flags; + uint32_t m_my_revision; + + private: + DISALLOW_COPY_AND_ASSIGN(TypeFormatImpl); + }; + + class TypeFormatImpl_Format : public TypeFormatImpl + { + public: + TypeFormatImpl_Format (lldb::Format f = lldb::eFormatInvalid, + const TypeFormatImpl::Flags& flags = Flags()); + + typedef std::shared_ptr<TypeFormatImpl_Format> SharedPointer; + typedef bool(*ValueCallback)(void*, ConstString, const TypeFormatImpl_Format::SharedPointer&); + + virtual ~TypeFormatImpl_Format () = default; lldb::Format GetFormat () const @@ -186,35 +243,69 @@ namespace lldb_private { m_format = fmt; } - uint32_t - GetOptions () + virtual TypeFormatImpl::Type + GetType () { - return m_flags.GetValue(); + return TypeFormatImpl::Type::eTypeFormat; + } + + virtual bool + FormatObject (ValueObject *valobj, + std::string& dest) const; + + virtual std::string + GetDescription(); + + protected: + lldb::Format m_format; + + private: + DISALLOW_COPY_AND_ASSIGN(TypeFormatImpl_Format); + }; + + class TypeFormatImpl_EnumType : public TypeFormatImpl + { + public: + TypeFormatImpl_EnumType (ConstString type_name = ConstString(""), + const TypeFormatImpl::Flags& flags = Flags()); + + typedef std::shared_ptr<TypeFormatImpl_EnumType> SharedPointer; + typedef bool(*ValueCallback)(void*, ConstString, const TypeFormatImpl_EnumType::SharedPointer&); + + ~TypeFormatImpl_EnumType () = default; + + ConstString + GetTypeName () + { + return m_enum_type; } void - SetOptions (uint32_t value) + SetTypeName (ConstString enum_type) { - m_flags.SetValue(value); + m_enum_type = enum_type; } - uint32_t& - GetRevision () + virtual TypeFormatImpl::Type + GetType () { - return m_my_revision; + return TypeFormatImpl::Type::eTypeEnum; } - std::string + virtual bool + FormatObject (ValueObject *valobj, + std::string& dest) const; + + virtual std::string GetDescription(); protected: - Flags m_flags; - lldb::Format m_format; - uint32_t m_my_revision; + ConstString m_enum_type; + mutable std::map<void*,ClangASTType> m_types; private: - DISALLOW_COPY_AND_ASSIGN(TypeFormatImpl); - }; + DISALLOW_COPY_AND_ASSIGN(TypeFormatImpl_EnumType); + }; } // namespace lldb_private #endif // lldb_TypeFormat_h_ diff --git a/include/lldb/DataFormatters/TypeSynthetic.h b/include/lldb/DataFormatters/TypeSynthetic.h index 18b9d011e96a..a25f11d64392 100644 --- a/include/lldb/DataFormatters/TypeSynthetic.h +++ b/include/lldb/DataFormatters/TypeSynthetic.h @@ -32,10 +32,24 @@ namespace lldb_private { { protected: ValueObject &m_backend; + + void + SetValid (bool valid) + { + m_valid = valid; + } + + bool + IsValid () + { + return m_valid; + } + public: SyntheticChildrenFrontEnd (ValueObject &backend) : - m_backend(backend) + m_backend(backend), + m_valid(true) {} virtual @@ -71,6 +85,7 @@ namespace lldb_private { typedef std::unique_ptr<SyntheticChildrenFrontEnd> AutoPointer; private: + bool m_valid; DISALLOW_COPY_AND_ASSIGN(SyntheticChildrenFrontEnd); }; diff --git a/include/lldb/DataFormatters/ValueObjectPrinter.h b/include/lldb/DataFormatters/ValueObjectPrinter.h index 4e23ceedcc16..375bb50c876d 100644 --- a/include/lldb/DataFormatters/ValueObjectPrinter.h +++ b/include/lldb/DataFormatters/ValueObjectPrinter.h @@ -265,9 +265,6 @@ public: bool PrintValueObject (); - bool - PrintChildrenOneLiner (bool hide_names); - protected: // only this class (and subclasses, if any) should ever be concerned with @@ -366,6 +363,9 @@ protected: PrintChildrenIfNeeded (bool value_printed, bool summary_printed); + bool + PrintChildrenOneLiner (bool hide_names); + private: ValueObject *m_orig_valobj; @@ -386,6 +386,8 @@ private: std::string m_summary; std::string m_error; + friend class StringSummaryFormat; + DISALLOW_COPY_AND_ASSIGN(ValueObjectPrinter); }; diff --git a/include/lldb/Expression/ClangFunction.h b/include/lldb/Expression/ClangFunction.h index 9c14ddb53230..e150d389b416 100644 --- a/include/lldb/Expression/ClangFunction.h +++ b/include/lldb/Expression/ClangFunction.h @@ -300,7 +300,7 @@ public: /// @param[in] args_addr /// The address of the argument struct. /// - /// @param[in] ret_value + /// @param[out] ret_value /// The value returned by the function. /// /// @return diff --git a/include/lldb/Expression/IRForTarget.h b/include/lldb/Expression/IRForTarget.h index 566328772502..502f796d15a1 100644 --- a/include/lldb/Expression/IRForTarget.h +++ b/include/lldb/Expression/IRForTarget.h @@ -30,6 +30,7 @@ namespace llvm { class GlobalValue; class GlobalVariable; class Instruction; + class IntegerType; class Module; class StoreInst; class DataLayout; @@ -650,6 +651,7 @@ private: StaticDataAllocator m_data_allocator; ///< The allocator to use for constant strings llvm::Constant *m_CFStringCreateWithBytes; ///< The address of the function CFStringCreateWithBytes, cast to the appropriate function pointer type llvm::Constant *m_sel_registerName; ///< The address of the function sel_registerName, cast to the appropriate function pointer type + llvm::IntegerType *m_intptr_ty; ///< The type of an integer large enough to hold a pointer. lldb_private::Stream *m_error_stream; ///< If non-NULL, the stream on which errors should be printed llvm::StoreInst *m_result_store; ///< If non-NULL, the store instruction that writes to the result variable. If m_has_side_effects is true, this is NULL. diff --git a/include/lldb/Host/Editline.h b/include/lldb/Host/Editline.h new file mode 100644 index 000000000000..b92de1052f29 --- /dev/null +++ b/include/lldb/Host/Editline.h @@ -0,0 +1,209 @@ +//===-- Editline.h ----------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_Editline_h_ +#define liblldb_Editline_h_ +#if defined(__cplusplus) + +#include "lldb/lldb-private.h" + +#include <stdio.h> +#ifdef _WIN32 +#include "lldb/Host/windows/editlinewin.h" +#else +#include <histedit.h> +#endif + +#include <string> +#include <vector> + +#include "lldb/Host/Condition.h" +#include "lldb/Host/FileSpec.h" +#include "lldb/Host/Mutex.h" + +namespace lldb_private { + +//---------------------------------------------------------------------- +/// @class Editline Editline.h "lldb/Host/Editline.h" +/// @brief A class that encapsulates editline functionality. +//---------------------------------------------------------------------- +class Editline +{ +public: + typedef LineStatus (*LineCompletedCallbackType) ( + Editline *editline, + StringList &lines, + uint32_t line_idx, + Error &error, + void *baton); + + typedef int (*CompleteCallbackType) ( + const char *current_line, + const char *cursor, + const char *last_char, + int skip_first_n_matches, + int max_matches, + StringList &matches, + void *baton); + + typedef int (*GetCharCallbackType) ( + ::EditLine *, + char *c); + + Editline(const char *prog, // Used for the history file and for editrc program name + const char *prompt, + FILE *fin, + FILE *fout, + FILE *ferr); + + ~Editline(); + + Error + GetLine (std::string &line); + + Error + GetLines (const std::string &end_line, StringList &lines); + + bool + LoadHistory (); + + bool + SaveHistory (); + + FILE * + GetInputFile (); + + FILE * + GetOutputFile (); + + FILE * + GetErrorFile (); + + bool + GettingLine () const + { + return m_getting_line; + } + + void + Hide (); + + void + Refresh(); + + void + Interrupt (); + + void + SetAutoCompleteCallback (CompleteCallbackType callback, + void *baton) + { + m_completion_callback = callback; + m_completion_callback_baton = baton; + } + + void + SetLineCompleteCallback (LineCompletedCallbackType callback, + void *baton) + { + m_line_complete_callback = callback; + m_line_complete_callback_baton = baton; + } + + size_t + Push (const char *bytes, size_t len); + + // Cache bytes and use them for input without using a FILE. Calling this function + // will set the getc callback in the editline + size_t + SetInputBuffer (const char *c, size_t len); + + static int + GetCharFromInputFileCallback (::EditLine *e, char *c); + + void + SetGetCharCallback (GetCharCallbackType callback); + + const char * + GetPrompt(); + + void + SetPrompt (const char *p); + +private: + + Error + PrivateGetLine(std::string &line); + + FileSpec + GetHistoryFile(); + + unsigned char + HandleCompletion (int ch); + + int + GetChar (char *c); + + + static unsigned char + CallbackEditPrevLine (::EditLine *e, int ch); + + static unsigned char + CallbackEditNextLine (::EditLine *e, int ch); + + static unsigned char + CallbackComplete (::EditLine *e, int ch); + + static const char * + GetPromptCallback (::EditLine *e); + + static Editline * + GetClientData (::EditLine *e); + + static FILE * + GetFilePointer (::EditLine *e, int fd); + + static int + GetCharInputBufferCallback (::EditLine *e, char *c); + + enum class Command + { + None = 0, + EditPrevLine, + EditNextLine, + }; + ::EditLine *m_editline; + ::History *m_history; + ::HistEvent m_history_event; + std::string m_program; + std::string m_prompt; + std::string m_lines_prompt; + std::string m_getc_buffer; + Mutex m_getc_mutex; + Condition m_getc_cond; + CompleteCallbackType m_completion_callback; + void *m_completion_callback_baton; +// Mutex m_gets_mutex; // Make sure only one thread + LineCompletedCallbackType m_line_complete_callback; + void *m_line_complete_callback_baton; + Command m_lines_command; + uint32_t m_lines_curr_line; + uint32_t m_lines_max_line; + bool m_prompt_with_line_numbers; + bool m_getting_line; + bool m_got_eof; // Set to true when we detect EOF + bool m_interrupted; + + DISALLOW_COPY_AND_ASSIGN(Editline); +}; + +} // namespace lldb_private + +#endif // #if defined(__cplusplus) +#endif // liblldb_Host_h_ diff --git a/include/lldb/Host/File.h b/include/lldb/Host/File.h index 607efa029c09..814d96059f37 100644 --- a/include/lldb/Host/File.h +++ b/include/lldb/Host/File.h @@ -51,7 +51,10 @@ public: m_descriptor (kInvalidDescriptor), m_stream (kInvalidStream), m_options (0), - m_owned (false) + m_own_stream (false), + m_own_descriptor (false), + m_is_interactive (eLazyBoolCalculate), + m_is_real_terminal (eLazyBoolCalculate) { } @@ -59,7 +62,10 @@ public: m_descriptor (kInvalidDescriptor), m_stream (fh), m_options (0), - m_owned (transfer_ownership) + m_own_stream (transfer_ownership), + m_own_descriptor (false), + m_is_interactive (eLazyBoolCalculate), + m_is_real_terminal (eLazyBoolCalculate) { } @@ -111,13 +117,15 @@ public: uint32_t options, uint32_t permissions = lldb::eFilePermissionsFileDefault); - File (int fd, bool tranfer_ownership) : + File (int fd, bool transfer_ownership) : m_descriptor (fd), m_stream (kInvalidStream), m_options (0), - m_owned (tranfer_ownership) + m_own_stream (false), + m_own_descriptor (transfer_ownership) { } + //------------------------------------------------------------------ /// Destructor. /// @@ -458,6 +466,32 @@ public: static uint32_t GetPermissions (const char *path, Error &error); + + //------------------------------------------------------------------ + /// Return true if this file is interactive. + /// + /// @return + /// True if this file is a terminal (tty or pty), false + /// otherwise. + //------------------------------------------------------------------ + bool + GetIsInteractive (); + + //------------------------------------------------------------------ + /// Return true if this file from a real terminal. + /// + /// Just knowing a file is a interactive isn't enough, we also need + /// to know if the terminal has a width and height so we can do + /// cursor movement and other terminal maninpulations by sending + /// escape sequences. + /// + /// @return + /// True if this file is a terminal (tty, not a pty) that has + /// a non-zero width and height, false otherwise. + //------------------------------------------------------------------ + bool + GetIsRealTerminal (); + //------------------------------------------------------------------ /// Output printf formatted output to the stream. /// @@ -476,6 +510,12 @@ public: size_t PrintfVarArg(const char *format, va_list args); + + void + SetOptions (uint32_t options) + { + m_options = options; + } protected: @@ -491,13 +531,19 @@ protected: return m_stream != kInvalidStream; } + void + CalculateInteractiveAndTerminal (); + //------------------------------------------------------------------ // Member variables //------------------------------------------------------------------ int m_descriptor; FILE *m_stream; uint32_t m_options; - bool m_owned; + bool m_own_stream; + bool m_own_descriptor; + LazyBool m_is_interactive; + LazyBool m_is_real_terminal; }; } // namespace lldb_private diff --git a/include/lldb/Host/FileSpec.h b/include/lldb/Host/FileSpec.h index 086c8f200567..dfc4e4ae0fe3 100644 --- a/include/lldb/Host/FileSpec.h +++ b/include/lldb/Host/FileSpec.h @@ -534,7 +534,7 @@ public: /// as many bytes as possible. /// /// @return - /// A shared pointer to the memeory mapped data. This shared + /// A shared pointer to the memory mapped data. This shared /// pointer can contain a NULL DataBuffer pointer, so the contained /// pointer must be checked prior to using it. //------------------------------------------------------------------ diff --git a/include/lldb/Host/Host.h b/include/lldb/Host/Host.h index fe0f6f62b3bc..862b1ed79432 100644 --- a/include/lldb/Host/Host.h +++ b/include/lldb/Host/Host.h @@ -202,6 +202,23 @@ public: GetTargetTriple (); //------------------------------------------------------------------ + /// Gets the name of the distribution (i.e. distributor id). + /// + /// On Linux, this will return the equivalent of lsb_release -i. + /// Android will return 'android'. Other systems may return + /// nothing. + /// + /// @return + /// A ConstString reference containing the OS distribution id. + /// The return string will be all lower case, with whitespace + /// replaced with underscores. The return string will be + /// empty (result.AsCString() will return NULL) if the distribution + /// cannot be obtained. + //------------------------------------------------------------------ + static const ConstString & + GetDistributionId (); + + //------------------------------------------------------------------ /// Get the process ID for the calling process. /// /// @return @@ -459,7 +476,15 @@ public: static bool GetProcessInfo (lldb::pid_t pid, ProcessInstanceInfo &proc_info); - + +#if defined (__APPLE__) || defined (__linux__) || defined (__FreeBSD__) || defined (__GLIBC__) + static short + GetPosixspawnFlags (ProcessLaunchInfo &launch_info); + + static Error + LaunchProcessPosixSpawn (const char *exe_path, ProcessLaunchInfo &launch_info, ::pid_t &pid); +#endif + static lldb::pid_t LaunchApplication (const FileSpec &app_file_spec); diff --git a/include/lldb/Host/HostGetOpt.h b/include/lldb/Host/HostGetOpt.h new file mode 100644 index 000000000000..6fb7b51dddba --- /dev/null +++ b/include/lldb/Host/HostGetOpt.h @@ -0,0 +1,20 @@ +//===-- GetOpt.h ------------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +#pragma once + +#ifndef _MSC_VER + +#include <unistd.h> +#include <getopt.h> + +#else + +#include <lldb/Host/windows/GetOptInc.h> + +#endif
\ No newline at end of file diff --git a/include/lldb/Host/SocketAddress.h b/include/lldb/Host/SocketAddress.h index 5e79e94fa9ec..4dc62102103a 100644 --- a/include/lldb/Host/SocketAddress.h +++ b/include/lldb/Host/SocketAddress.h @@ -18,7 +18,6 @@ #include <winsock2.h> #include <WS2tcpip.h> typedef ADDRESS_FAMILY sa_family_t; -typedef USHORT in_port_t; #else #include <sys/socket.h> #include <netdb.h> @@ -103,7 +102,7 @@ public: //------------------------------------------------------------------ // Get the port if the socket address for the family has a port //------------------------------------------------------------------ - in_port_t + uint16_t GetPort () const; //------------------------------------------------------------------ @@ -111,7 +110,7 @@ public: // The family must be set correctly prior to calling this function. //------------------------------------------------------------------ bool - SetPort (in_port_t port); + SetPort (uint16_t port); //------------------------------------------------------------------ // Set the socket address according to the first match from a call @@ -121,10 +120,12 @@ public: // address. //------------------------------------------------------------------ bool - SetAddress (const struct addrinfo *hints_ptr, // Optional hints where the family, protocol and other things can be specified. - const char *host, // Hostname ("foo.bar.com" or "foo" or IP address string ("123.234.12.1" or "2001:0db8:85a3:0000:0000:8a2e:0370:7334") - const char *service, // Protocol name ("tcp", "http", etc) or a raw port number string ("81") - struct addrinfo *addr_info_ptr); // If non-NULL, this will get filled in with the match + getaddrinfo (const char *host, // Hostname ("foo.bar.com" or "foo" or IP address string ("123.234.12.1" or "2001:0db8:85a3:0000:0000:8a2e:0370:7334") + const char *service, // Protocol name ("tcp", "http", etc) or a raw port number string ("81") + int ai_family = PF_UNSPEC, + int ai_socktype = 0, + int ai_protocol = 0, + int ai_flags = 0); //------------------------------------------------------------------ // Quick way to set the SocketAddress to localhost given the family. @@ -133,7 +134,11 @@ public: //------------------------------------------------------------------ bool SetToLocalhost (sa_family_t family, - in_port_t port); + uint16_t port); + + bool + SetToAnyAddress (sa_family_t family, + uint16_t port); //------------------------------------------------------------------ // Returns true if there is a valid socket address in this object. diff --git a/include/lldb/Interpreter/CommandInterpreter.h b/include/lldb/Interpreter/CommandInterpreter.h index 31fcc38eed9a..bcb9b5538c84 100644 --- a/include/lldb/Interpreter/CommandInterpreter.h +++ b/include/lldb/Interpreter/CommandInterpreter.h @@ -17,6 +17,7 @@ #include "lldb/lldb-private.h" #include "lldb/Core/Broadcaster.h" #include "lldb/Core/Debugger.h" +#include "lldb/Core/IOHandler.h" #include "lldb/Core/Log.h" #include "lldb/Interpreter/CommandHistory.h" #include "lldb/Interpreter/CommandObject.h" @@ -29,7 +30,8 @@ namespace lldb_private { class CommandInterpreter : public Broadcaster, - public Properties + public Properties, + public IOHandlerDelegate { public: typedef std::map<std::string, OptionArgVectorSP> OptionArgMap; @@ -213,10 +215,10 @@ public: void HandleCommandsFromFile (FileSpec &file, ExecutionContext *context, - bool stop_on_continue, - bool stop_on_error, - bool echo_commands, - bool print_results, + LazyBool stop_on_continue, + LazyBool stop_on_error, + LazyBool echo_commands, + LazyBool print_results, LazyBool add_to_history, CommandReturnObject &result); @@ -305,7 +307,8 @@ public: ExecutionContext GetExecutionContext() { - return m_exe_ctx_ref.Lock(); + const bool thread_and_frame_only_if_stopped = true; + return m_exe_ctx_ref.Lock(thread_and_frame_only_if_stopped); } void @@ -317,20 +320,12 @@ public: const char * ProcessEmbeddedScriptCommands (const char *arg); - const char * - GetPrompt (); - void - SetPrompt (const char *); - - bool Confirm (const char *message, bool default_answer); + UpdatePrompt (const char *); - static size_t - GetConfirmationInputReaderCallback (void *baton, - InputReader &reader, - lldb::InputReaderAction action, - const char *bytes, - size_t bytes_len); + bool + Confirm (const char *message, + bool default_answer); void LoadCommandDictionary (); @@ -395,8 +390,12 @@ public: bool GetBatchCommandMode () { return m_batch_command_mode; } - void - SetBatchCommandMode (bool value) { m_batch_command_mode = value; } + bool + SetBatchCommandMode (bool value) { + const bool old_value = m_batch_command_mode; + m_batch_command_mode = value; + return old_value; + } void ChildrenTruncated () @@ -435,6 +434,25 @@ public: return m_command_history; } + bool + IsActive (); + + void + RunCommandInterpreter (bool auto_handle_events, + bool spawn_thread); + + void + GetLLDBCommandsFromIOHandler (const char *prompt, + IOHandlerDelegate &delegate, + bool asynchronously, + void *baton); + + void + GetPythonCommandsFromIOHandler (const char *prompt, + IOHandlerDelegate &delegate, + bool asynchronously, + void *baton); + //------------------------------------------------------------------ // Properties //------------------------------------------------------------------ @@ -450,12 +468,31 @@ public: protected: friend class Debugger; + //------------------------------------------------------------------ + // IOHandlerDelegate functions + //------------------------------------------------------------------ + virtual void + IOHandlerInputComplete (IOHandler &io_handler, + std::string &line); + + virtual ConstString + GetControlSequence (char ch) + { + if (ch == 'd') + return ConstString("quit\n"); + return ConstString(); + } + + size_t + GetProcessOutput (); + void SetSynchronous (bool value); lldb::CommandObjectSP GetCommandSP (const char *cmd, bool include_aliases = true, bool exact = true, StringList *matches = NULL); + private: Error @@ -473,10 +510,12 @@ private: CommandHistory m_command_history; std::string m_repeat_command; // Stores the command that will be executed for an empty command string. std::unique_ptr<ScriptInterpreter> m_script_interpreter_ap; + lldb::IOHandlerSP m_command_io_handler_sp; char m_comment_char; bool m_batch_command_mode; ChildrenTruncatedWarningStatus m_truncation_warning; // Whether we truncated children and whether the user has been told uint32_t m_command_source_depth; + std::vector<uint32_t> m_command_source_flags; }; diff --git a/include/lldb/Interpreter/PythonDataObjects.h b/include/lldb/Interpreter/PythonDataObjects.h index 2762d452c0c8..55df4fd1b0a1 100644 --- a/include/lldb/Interpreter/PythonDataObjects.h +++ b/include/lldb/Interpreter/PythonDataObjects.h @@ -31,7 +31,7 @@ namespace lldb_private { { } - PythonObject (PyObject* py_obj) : + explicit PythonObject (PyObject* py_obj) : m_py_obj(NULL) { Reset (py_obj); @@ -43,7 +43,7 @@ namespace lldb_private { Reset (rhs.m_py_obj); } - PythonObject (const lldb::ScriptInterpreterObjectSP &script_object_sp); + explicit PythonObject (const lldb::ScriptInterpreterObjectSP &script_object_sp); virtual ~PythonObject () @@ -51,18 +51,10 @@ namespace lldb_private { Reset (NULL); } - const PythonObject & - operator = (const PythonObject &rhs) - { - if (this != &rhs) - Reset (rhs.m_py_obj); - return *this; - } - bool Reset (const PythonObject &object) { - return Reset(object.GetPythonObject()); + return Reset(object.get()); } virtual bool @@ -90,11 +82,11 @@ namespace lldb_private { Dump (Stream &strm) const; PyObject* - GetPythonObject () const + get () const { return m_py_obj; } - + PythonString Repr (); @@ -159,7 +151,7 @@ namespace lldb_private { { public: - PythonList (); + PythonList (bool create_empty); PythonList (PyObject* py_obj); PythonList (const PythonObject &object); PythonList (const lldb::ScriptInterpreterObjectSP &script_object_sp); @@ -186,7 +178,7 @@ namespace lldb_private { { public: - PythonDictionary (); + explicit PythonDictionary (bool create_empty); PythonDictionary (PyObject* object); PythonDictionary (const PythonObject &object); PythonDictionary (const lldb::ScriptInterpreterObjectSP &script_object_sp); @@ -221,6 +213,9 @@ namespace lldb_private { GetValueAtPosition (uint32_t pos) const; void + SetItemForKey (const PythonString &key, PyObject *value); + + void SetItemForKey (const PythonString &key, const PythonObject& value); }; diff --git a/include/lldb/Interpreter/ScriptInterpreter.h b/include/lldb/Interpreter/ScriptInterpreter.h index 9f529b822910..1d62c9b0fb52 100644 --- a/include/lldb/Interpreter/ScriptInterpreter.h +++ b/include/lldb/Interpreter/ScriptInterpreter.h @@ -245,11 +245,13 @@ public: return true; } - virtual bool + virtual Error ExecuteMultipleLines (const char *in_string, const ExecuteScriptOptions &options = ExecuteScriptOptions()) { - return true; + Error error; + error.SetErrorString("not implemented"); + return error; } virtual bool diff --git a/include/lldb/Interpreter/ScriptInterpreterPython.h b/include/lldb/Interpreter/ScriptInterpreterPython.h index b729cb628007..ba532808673f 100644 --- a/include/lldb/Interpreter/ScriptInterpreterPython.h +++ b/include/lldb/Interpreter/ScriptInterpreterPython.h @@ -19,16 +19,21 @@ #include "lldb/lldb-python.h" #include "lldb/lldb-private.h" +#include "lldb/Core/IOHandler.h" #include "lldb/Interpreter/ScriptInterpreter.h" -#include "lldb/Core/InputReader.h" +#include "lldb/Interpreter/PythonDataObjects.h" #include "lldb/Host/Terminal.h" namespace lldb_private { -class ScriptInterpreterPython : public ScriptInterpreter +class ScriptInterpreterPython : + public ScriptInterpreter, + public IOHandlerDelegateMultiline { public: + friend class IOHandlerPythonInterpreter; + ScriptInterpreterPython (CommandInterpreter &interpreter); ~ScriptInterpreterPython (); @@ -47,7 +52,7 @@ public: void *ret_value, const ExecuteScriptOptions &options = ExecuteScriptOptions()); - bool + lldb_private::Error ExecuteMultipleLines (const char *in_string, const ExecuteScriptOptions &options = ExecuteScriptOptions()); @@ -134,20 +139,20 @@ public: bool GenerateWatchpointCommandCallbackData (StringList &input, std::string& output); - static size_t - GenerateBreakpointOptionsCommandCallback (void *baton, - InputReader &reader, - lldb::InputReaderAction notification, - const char *bytes, - size_t bytes_len); - - static size_t - GenerateWatchpointOptionsCommandCallback (void *baton, - InputReader &reader, - lldb::InputReaderAction notification, - const char *bytes, - size_t bytes_len); - +// static size_t +// GenerateBreakpointOptionsCommandCallback (void *baton, +// InputReader &reader, +// lldb::InputReaderAction notification, +// const char *bytes, +// size_t bytes_len); +// +// static size_t +// GenerateWatchpointOptionsCommandCallback (void *baton, +// InputReader &reader, +// lldb::InputReaderAction notification, +// const char *bytes, +// size_t bytes_len); + static bool BreakpointCallbackFunction (void *baton, StoppointCallbackContext *context, @@ -238,9 +243,6 @@ public: virtual void ResetOutputFileHandle (FILE *new_fh); - static lldb::thread_result_t - RunEmbeddedPythonInterpreter (lldb::thread_arg_t baton); - static void InitializePrivate (); @@ -266,10 +268,29 @@ public: SWIGPythonScriptKeyword_Frame swig_run_script_keyword_frame, SWIGPython_GetDynamicSetting swig_plugin_get); + const char * + GetDictionaryName () + { + return m_dictionary_name.c_str(); + } + + + //---------------------------------------------------------------------- + // IOHandlerDelegate + //---------------------------------------------------------------------- + virtual void + IOHandlerActivated (IOHandler &io_handler); + + virtual void + IOHandlerInputComplete (IOHandler &io_handler, std::string &data); + protected: bool - EnterSession (bool init_lldb_globals); + EnterSession (uint16_t on_entry_flags, + FILE *in, + FILE *out, + FILE *err); void LeaveSession (); @@ -279,8 +300,6 @@ protected: void RestoreTerminalState (); - -private: class SynchronicityHandler { @@ -322,7 +341,7 @@ private: private: DISALLOW_COPY_AND_ASSIGN (ScriptInterpreterPythonObject); }; - +public: class Locker : public ScriptInterpreterLocker { public: @@ -331,7 +350,8 @@ private: { AcquireLock = 0x0001, InitSession = 0x0002, - InitGlobals = 0x0004 + InitGlobals = 0x0004, + NoSTDIN = 0x0008 }; enum OnLeave @@ -344,7 +364,9 @@ private: Locker (ScriptInterpreterPython *py_interpreter = NULL, uint16_t on_entry = AcquireLock | InitSession, uint16_t on_leave = FreeLock | TearDownSession, - FILE* wait_msg_handle = NULL); + FILE *in = NULL, + FILE *out = NULL, + FILE *err = NULL); ~Locker (); @@ -354,7 +376,7 @@ private: DoAcquireLock (); bool - DoInitSession (bool init_lldb_globals); + DoInitSession (uint16_t on_entry_flags, FILE *in, FILE *out, FILE *err); bool DoFreeLock (); @@ -367,59 +389,40 @@ private: bool m_teardown_session; ScriptInterpreterPython *m_python_interpreter; - FILE* m_tmp_fh; +// FILE* m_tmp_fh; PyGILState_STATE m_GILState; }; - - class PythonInputReaderManager - { - public: - PythonInputReaderManager (ScriptInterpreterPython *interpreter); - - explicit operator bool() - { - return m_error; - } - - ~PythonInputReaderManager(); - - private: - - static size_t - InputReaderCallback (void *baton, - InputReader &reader, - lldb::InputReaderAction notification, - const char *bytes, - size_t bytes_len); - - static lldb::thread_result_t - RunPythonInputReader (lldb::thread_arg_t baton); - - ScriptInterpreterPython *m_interpreter; - lldb::DebuggerSP m_debugger_sp; - lldb::InputReaderSP m_reader_sp; - bool m_error; +private: + + enum ActiveIOHandler { + eIOHandlerNone, + eIOHandlerBreakpoint, + eIOHandlerWatchpoint }; + PythonObject & + GetMainModule (); + + PythonDictionary & + GetSessionDictionary (); + + PythonDictionary & + GetSysModuleDictionary (); - static size_t - InputReaderCallback (void *baton, - InputReader &reader, - lldb::InputReaderAction notification, - const char *bytes, - size_t bytes_len); - - - lldb_utility::PseudoTerminal m_embedded_thread_pty; - lldb_utility::PseudoTerminal m_embedded_python_pty; - lldb::InputReaderSP m_embedded_thread_input_reader_sp; - lldb::InputReaderSP m_embedded_python_input_reader_sp; - FILE *m_dbg_stdout; - PyObject *m_new_sysout; - PyObject *m_old_sysout; - PyObject *m_old_syserr; - PyObject *m_run_one_line; + bool + GetEmbeddedInterpreterModuleObjects (); + + PythonObject m_saved_stdin; + PythonObject m_saved_stdout; + PythonObject m_saved_stderr; + PythonObject m_main_module; + PythonObject m_lldb_module; + PythonDictionary m_session_dict; + PythonDictionary m_sys_module_dict; + PythonObject m_run_one_line_function; + PythonObject m_run_one_line_str_global; std::string m_dictionary_name; TerminalState m_terminal_state; + ActiveIOHandler m_active_io_handler; bool m_session_is_active; bool m_pty_slave_is_open; bool m_valid_session; diff --git a/include/lldb/Symbol/FuncUnwinders.h b/include/lldb/Symbol/FuncUnwinders.h index fa48dc27e123..7af063402289 100644 --- a/include/lldb/Symbol/FuncUnwinders.h +++ b/include/lldb/Symbol/FuncUnwinders.h @@ -31,7 +31,7 @@ public: // instructions are finished for migrating breakpoints past the // stack frame setup instructions when we don't have line table information. - FuncUnwinders (lldb_private::UnwindTable& unwind_table, lldb_private::UnwindAssembly *assembly_profiler, AddressRange range); + FuncUnwinders (lldb_private::UnwindTable& unwind_table, const lldb::UnwindAssemblySP& assembly_profiler, AddressRange range); ~FuncUnwinders (); @@ -77,7 +77,7 @@ public: private: UnwindTable& m_unwind_table; - UnwindAssembly *m_assembly_profiler; + lldb::UnwindAssemblySP m_assembly_profiler; AddressRange m_range; Mutex m_mutex; diff --git a/include/lldb/Symbol/Function.h b/include/lldb/Symbol/Function.h index 787f81c5ad27..dcea24c0b632 100644 --- a/include/lldb/Symbol/Function.h +++ b/include/lldb/Symbol/Function.h @@ -608,6 +608,17 @@ public: size_t MemorySize () const; + lldb::DisassemblerSP + GetInstructions (const ExecutionContext &exe_ctx, + const char *flavor, + bool prefer_file_cache); + + bool + GetDisassembly (const ExecutionContext &exe_ctx, + const char *flavor, + bool prefer_file_cache, + Stream &strm); + protected: enum diff --git a/include/lldb/Symbol/ObjectFile.h b/include/lldb/Symbol/ObjectFile.h index ad500f5413b8..afa1f9b40902 100644 --- a/include/lldb/Symbol/ObjectFile.h +++ b/include/lldb/Symbol/ObjectFile.h @@ -451,6 +451,21 @@ public: } //------------------------------------------------------------------ + /// Sets the load address for an entire module, assuming a rigid + /// slide of sections, if possible in the implementation. + /// + /// @return + /// Returns true iff any section's load address changed. + //------------------------------------------------------------------ + virtual bool + SetLoadAddress(Target &target, + lldb::addr_t value, + bool value_is_offset) + { + return false; + } + + //------------------------------------------------------------------ /// Gets whether endian swapping should occur when extracting data /// from this object file. /// diff --git a/include/lldb/Symbol/Symbol.h b/include/lldb/Symbol/Symbol.h index 75e0900ab640..db32ba373e42 100644 --- a/include/lldb/Symbol/Symbol.h +++ b/include/lldb/Symbol/Symbol.h @@ -291,6 +291,17 @@ public: virtual void DumpSymbolContext (Stream *s); + lldb::DisassemblerSP + GetInstructions (const ExecutionContext &exe_ctx, + const char *flavor, + bool prefer_file_cache); + + bool + GetDisassembly (const ExecutionContext &exe_ctx, + const char *flavor, + bool prefer_file_cache, + Stream &strm); + protected: uint32_t m_uid; // User ID (usually the original symbol table index) diff --git a/include/lldb/Symbol/SymbolContext.h b/include/lldb/Symbol/SymbolContext.h index a0501440f18c..6fdd828bd9f2 100644 --- a/include/lldb/Symbol/SymbolContext.h +++ b/include/lldb/Symbol/SymbolContext.h @@ -17,6 +17,7 @@ #include "lldb/Core/Address.h" #include "lldb/Core/Mangled.h" #include "lldb/Symbol/LineEntry.h" +#include "lldb/Utility/Iterable.h" namespace lldb_private { @@ -552,6 +553,14 @@ protected: // Member variables. //------------------------------------------------------------------ collection m_symbol_contexts; ///< The list of symbol contexts. + +public: + typedef AdaptedIterable<collection, SymbolContext, vector_adapter> SymbolContextIterable; + SymbolContextIterable + SymbolContexts() + { + return SymbolContextIterable(m_symbol_contexts); + } }; bool operator== (const SymbolContext& lhs, const SymbolContext& rhs); diff --git a/include/lldb/Symbol/Type.h b/include/lldb/Symbol/Type.h index 920f571fa1e9..da327439936c 100644 --- a/include/lldb/Symbol/Type.h +++ b/include/lldb/Symbol/Type.h @@ -417,7 +417,15 @@ public: return type_sp->GetClangLayoutType().GetLValueReferenceType(); return clang_type.GetLValueReferenceType(); } - + + ClangASTType + GetTypedefedType () const + { + if (type_sp) + return type_sp->GetClangFullType().GetTypedefedType(); + return clang_type.GetTypedefedType(); + } + ClangASTType GetDereferencedType () const { @@ -513,6 +521,9 @@ public: GetReferenceType () const; TypeImpl + GetTypedefedType () const; + + TypeImpl GetDereferencedType () const; TypeImpl diff --git a/include/lldb/Symbol/UnwindTable.h b/include/lldb/Symbol/UnwindTable.h index cefb91eb371a..3a99eb463df4 100644 --- a/include/lldb/Symbol/UnwindTable.h +++ b/include/lldb/Symbol/UnwindTable.h @@ -57,7 +57,7 @@ private: bool m_initialized; // delay some initialization until ObjectFile is set up - UnwindAssembly* m_assembly_profiler; + lldb::UnwindAssemblySP m_assembly_profiler; DWARFCallFrameInfo* m_eh_frame; diff --git a/include/lldb/Target/DynamicLoader.h b/include/lldb/Target/DynamicLoader.h index 272f64f33113..6652a5ec144f 100644 --- a/include/lldb/Target/DynamicLoader.h +++ b/include/lldb/Target/DynamicLoader.h @@ -246,6 +246,60 @@ public: protected: //------------------------------------------------------------------ + // Utility methods for derived classes + //------------------------------------------------------------------ + + /// Checks to see if the target module has changed, updates the target + /// accordingly and returns the target executable module. + lldb::ModuleSP + GetTargetExecutable(); + + /// Updates the load address of every allocatable section in @p module. + /// + /// @param module The module to traverse. + /// + /// @param link_map_addr The virtual address of the link map for the @p module. + /// + /// @param base_addr The virtual base address @p module is loaded at. + virtual void + UpdateLoadedSections(lldb::ModuleSP module, + lldb::addr_t link_map_addr, + lldb::addr_t base_addr); + + // Utility method so base classes can share implementation of UpdateLoadedSections + void + UpdateLoadedSectionsCommon(lldb::ModuleSP module, + lldb::addr_t base_addr); + + /// Removes the loaded sections from the target in @p module. + /// + /// @param module The module to traverse. + virtual void + UnloadSections(const lldb::ModuleSP module); + + // Utility method so base classes can share implementation of UnloadSections + void + UnloadSectionsCommon(const lldb::ModuleSP module); + + /// Locates or creates a module given by @p file and updates/loads the + /// resulting module at the virtual base address @p base_addr. + lldb::ModuleSP + LoadModuleAtAddress(const lldb_private::FileSpec &file, lldb::addr_t link_map_addr, lldb::addr_t base_addr); + + const lldb_private::SectionList * + GetSectionListFromModule(const lldb::ModuleSP module) const; + + // Read an unsigned int of the given size from memory at the given addr. + // Return -1 if the read fails, otherwise return the result as an int64_t. + int64_t + ReadUnsignedIntWithSizeInBytes(lldb::addr_t addr, int size_in_bytes); + + // Read a pointer from memory at the given addr. + // Return LLDB_INVALID_ADDRESS if the read fails. + lldb::addr_t + ReadPointer(lldb::addr_t addr); + + //------------------------------------------------------------------ // Member variables. //------------------------------------------------------------------ Process* m_process; ///< The process that this dynamic loader plug-in is tracking. diff --git a/include/lldb/Target/ExecutionContext.h b/include/lldb/Target/ExecutionContext.h index 4038e70b0c56..f825c2e72e6d 100644 --- a/include/lldb/Target/ExecutionContext.h +++ b/include/lldb/Target/ExecutionContext.h @@ -298,7 +298,7 @@ public: /// any valid weak references in this object. //------------------------------------------------------------------ ExecutionContext - Lock () const; + Lock (bool thread_and_frame_only_if_stopped) const; //------------------------------------------------------------------ /// Returns true if this object has a weak reference to a thread. @@ -402,7 +402,7 @@ public: ExecutionContext (const lldb::ThreadWP &thread_wp); ExecutionContext (const lldb::StackFrameWP &frame_wp); ExecutionContext (const ExecutionContextRef &exe_ctx_ref); - ExecutionContext (const ExecutionContextRef *exe_ctx_ref); + ExecutionContext (const ExecutionContextRef *exe_ctx_ref, bool thread_and_frame_only_if_stopped = false); // These two variants take in a locker, and grab the target, lock the API mutex into locker, then // fill in the rest of the shared pointers. diff --git a/include/lldb/Target/Platform.h b/include/lldb/Target/Platform.h index 355c0528bed8..80011fd120de 100644 --- a/include/lldb/Target/Platform.h +++ b/include/lldb/Target/Platform.h @@ -314,9 +314,9 @@ namespace lldb_private { /// An error object. //------------------------------------------------------------------ virtual Error - GetFile (const FileSpec &platform_file, - const UUID *uuid_ptr, - FileSpec &local_file); + GetFileWithUUID (const FileSpec &platform_file, + const UUID *uuid_ptr, + FileSpec &local_file); //---------------------------------------------------------------------- // Locate the scripting resource given a module specification. @@ -835,6 +835,29 @@ namespace lldb_private { return LLDB_INVALID_QUEUE_ID; } + //------------------------------------------------------------------ + /// Provide a list of trap handler function names for this platform + /// + /// The unwinder needs to treat trap handlers specially -- the stack + /// frame may not be aligned correctly for a trap handler (the kernel + /// often won't perturb the stack pointer, or won't re-align it properly, + /// in the process of calling the handler) and the frame above the handler + /// needs to be treated by the unwinder's "frame 0" rules instead of its + /// "middle of the stack frame" rules. + /// + /// In a user process debugging scenario, the list of trap handlers is + /// typically just "_sigtramp". + /// + /// The Platform base class provides the m_trap_handlers ivar but it does + /// not populate it. Subclasses should add the names of the asynchronous + /// signal handler routines as needed. For most Unix platforms, add _sigtramp. + /// + /// @return + /// A list of symbol names. The list may be empty. + //------------------------------------------------------------------ + virtual const std::vector<ConstString> & + GetTrapHandlerSymbolNames (); + protected: bool m_is_host; // Set to true when we are able to actually set the OS version while @@ -867,6 +890,24 @@ namespace lldb_private { std::string m_ssh_opts; bool m_ignores_remote_hostname; std::string m_local_cache_directory; + std::vector<ConstString> m_trap_handlers; + bool m_calculated_trap_handlers; + + //------------------------------------------------------------------ + /// Ask the Platform subclass to fill in the list of trap handler names + /// + /// For most Unix user process environments, this will be a single + /// function name, _sigtramp. More specialized environments may have + /// additional handler names. The unwinder code needs to know when a + /// trap handler is on the stack because the unwind rules for the frame + /// that caused the trap are different. + /// + /// The base class Platform ivar m_trap_handlers should be updated by + /// the Platform subclass when this method is called. If there are no + /// predefined trap handlers, this method may be a no-op. + //------------------------------------------------------------------ + virtual void + CalculateTrapHandlerSymbolNames () = 0; const char * GetCachedUserName (uint32_t uid) @@ -1115,7 +1156,9 @@ namespace lldb_private { bool m_ssh; std::string m_ssh_opts; + private: + DISALLOW_COPY_AND_ASSIGN(OptionGroupPlatformSSH); }; diff --git a/include/lldb/Target/Process.h b/include/lldb/Target/Process.h index cda9b4f57118..f52b54da52cf 100644 --- a/include/lldb/Target/Process.h +++ b/include/lldb/Target/Process.h @@ -43,6 +43,7 @@ #include "lldb/Interpreter/Options.h" #include "lldb/Target/ExecutionContextScope.h" #include "lldb/Target/Memory.h" +#include "lldb/Target/QueueList.h" #include "lldb/Target/ThreadList.h" #include "lldb/Target/UnixSignals.h" #include "lldb/Utility/PseudoTerminal.h" @@ -534,7 +535,8 @@ public: m_resume_count (0), m_monitor_callback (NULL), m_monitor_callback_baton (NULL), - m_monitor_signals (false) + m_monitor_signals (false), + m_hijack_listener_sp () { } @@ -553,7 +555,8 @@ public: m_resume_count (0), m_monitor_callback (NULL), m_monitor_callback_baton (NULL), - m_monitor_signals (false) + m_monitor_signals (false), + m_hijack_listener_sp () { if (stdin_path) { @@ -780,6 +783,7 @@ public: m_flags.Clear(); m_file_actions.clear(); m_resume_count = 0; + m_hijack_listener_sp.reset(); } bool @@ -799,6 +803,18 @@ public: m_monitor_signals = monitor_signals; } + Host::MonitorChildProcessCallback + GetMonitorProcessCallback () + { + return m_monitor_callback; + } + + const void* + GetMonitorProcessBaton () const + { + return m_monitor_callback_baton; + } + bool MonitorProcess () const { @@ -818,6 +834,19 @@ public: { return m_pty; } + + lldb::ListenerSP + GetHijackListener () const + { + return m_hijack_listener_sp; + } + + void + SetHijackListener (const lldb::ListenerSP &listener_sp) + { + m_hijack_listener_sp = listener_sp; + } + protected: std::string m_working_dir; @@ -830,7 +859,7 @@ protected: Host::MonitorChildProcessCallback m_monitor_callback; void *m_monitor_callback_baton; bool m_monitor_signals; - + lldb::ListenerSP m_hijack_listener_sp; }; //---------------------------------------------------------------------- @@ -863,6 +892,7 @@ public: ProcessInfo::operator= (launch_info); SetProcessPluginName (launch_info.GetProcessPluginName()); SetResumeCount (launch_info.GetResumeCount()); + SetHijackListener(launch_info.GetHijackListener()); } bool @@ -952,7 +982,22 @@ public: return true; return false; } + + lldb::ListenerSP + GetHijackListener () const + { + return m_hijack_listener_sp; + } + + void + SetHijackListener (const lldb::ListenerSP &listener_sp) + { + m_hijack_listener_sp = listener_sp; + } + + protected: + lldb::ListenerSP m_hijack_listener_sp; std::string m_plugin_name; uint32_t m_resume_count; // How many times do we resume after launching bool m_wait_for_launch; @@ -1366,10 +1411,11 @@ class Process : public ExecutionContextScope, public PluginInterface { -friend class ThreadList; -friend class ClangFunction; // For WaitForStateChangeEventsPrivate -friend class ProcessEventData; -friend class StopInfo; + friend class ClangFunction; // For WaitForStateChangeEventsPrivate + friend class ProcessEventData; + friend class StopInfo; + friend class Target; + friend class ThreadList; public: @@ -2112,21 +2158,15 @@ public: /// @param[in] process_name /// The name of the process to attach to. /// - /// @param[in] wait_for_launch - /// If \b true, wait for the process to be launched and attach - /// as soon as possible after it does launch. If \b false, then - /// search for a matching process the currently exists. - /// /// @param[in] attach_info /// Information on how to do the attach. For example, GetUserID() /// will return the uid to attach as. /// /// @return - /// Returns \a pid if attaching was successful, or - /// LLDB_INVALID_PROCESS_ID if attaching fails. + /// Returns an error object. //------------------------------------------------------------------ virtual Error - DoAttachToProcessWithName (const char *process_name, bool wait_for_launch, const ProcessAttachInfo &attach_info) + DoAttachToProcessWithName (const char *process_name, const ProcessAttachInfo &attach_info) { Error error; error.SetErrorString("attach by name is not supported"); @@ -2225,7 +2265,7 @@ public: //------------------------------------------------------------------ virtual Error DoLaunch (Module *exe_module, - const ProcessLaunchInfo &launch_info) + ProcessLaunchInfo &launch_info) { Error error; error.SetErrorStringWithFormat("error: %s does not support launching processes", GetPluginName().GetCString()); @@ -2991,15 +3031,11 @@ public: //------------------------------------------------------------------ virtual lldb::addr_t - ResolveIndirectFunction(const Address *address, Error &error) - { - error.SetErrorStringWithFormat("error: %s does not support indirect functions in the debug process", GetPluginName().GetCString()); - return LLDB_INVALID_ADDRESS; - } + ResolveIndirectFunction(const Address *address, Error &error); virtual Error - GetMemoryRegionInfo (lldb::addr_t load_addr, - MemoryRegionInfo &range_info) + GetMemoryRegionInfo (lldb::addr_t load_addr, + MemoryRegionInfo &range_info) { Error error; error.SetErrorString ("Process::GetMemoryRegionInfo() not supported"); @@ -3317,10 +3353,10 @@ public: { return m_thread_list.Threads(); } - + uint32_t GetNextThreadIndexID (uint64_t thread_id); - + lldb::ThreadSP CreateOSPluginThread (lldb::tid_t tid, lldb::addr_t context); @@ -3334,6 +3370,27 @@ public: AssignIndexIDToThread(uint64_t thread_id); //------------------------------------------------------------------ + // Queue Queries + //------------------------------------------------------------------ + + void + UpdateQueueListIfNeeded (); + + QueueList & + GetQueueList () + { + UpdateQueueListIfNeeded(); + return m_queue_list; + } + + QueueList::QueueIterable + Queues () + { + UpdateQueueListIfNeeded(); + return m_queue_list.Queues(); + } + + //------------------------------------------------------------------ // Event Handling //------------------------------------------------------------------ lldb::StateType @@ -3343,10 +3400,15 @@ public: // is set to the event which triggered the stop. If wait_always = false, // and the process is already stopped, this function returns immediately. lldb::StateType - WaitForProcessToStop (const TimeValue *timeout, lldb::EventSP *event_sp_ptr = NULL, bool wait_always = true); + WaitForProcessToStop (const TimeValue *timeout, + lldb::EventSP *event_sp_ptr = NULL, + bool wait_always = true, + Listener *hijack_listener = NULL); lldb::StateType - WaitForStateChangedEvents (const TimeValue *timeout, lldb::EventSP &event_sp); + WaitForStateChangedEvents (const TimeValue *timeout, + lldb::EventSP &event_sp, + Listener *hijack_listener); // Pass NULL to use builtin listener Event * PeekAtStateChangedEvents (); @@ -3513,6 +3575,12 @@ public: void SetSTDIOFileDescriptor (int file_descriptor); + void + WatchForSTDIN (IOHandler &io_handler); + + void + CancelWatchForSTDIN (bool exited); + //------------------------------------------------------------------ // Add a permanent region of memory that should never be read or // written to. This can be used to ensure that memory reads or writes @@ -3645,6 +3713,12 @@ protected: { return IS_VALID_LLDB_HOST_THREAD(m_private_state_thread); } + + void + ForceNextEventDelivery() + { + m_force_next_event_delivery = true; + } //------------------------------------------------------------------ // Type definitions @@ -3685,6 +3759,8 @@ protected: ///< m_thread_list_real, but might be different if there is an OS plug-in creating memory threads ThreadList m_extended_thread_list; ///< Owner for extended threads that may be generated, cleared on natural stops uint32_t m_extended_thread_stop_id; ///< The natural stop id when extended_thread_list was last updated + QueueList m_queue_list; ///< The list of libdispatch queues at a given stop point + uint32_t m_queue_list_stop_id; ///< The natural stop id when queue list was last fetched std::vector<Notifications> m_notifications; ///< The list of notifications that this process can deliver. std::vector<lldb::addr_t> m_image_tokens; Listener &m_listener; @@ -3695,7 +3771,7 @@ protected: std::unique_ptr<SystemRuntime> m_system_runtime_ap; UnixSignals m_unix_signals; /// This is the current signal set for this process. lldb::ABISP m_abi_sp; - lldb::InputReaderSP m_process_input_reader; + lldb::IOHandlerSP m_process_input_reader; Communication m_stdio_communication; Mutex m_stdio_communication_mutex; std::string m_stdout_data; @@ -3715,7 +3791,9 @@ protected: bool m_resume_requested; // If m_currently_handling_event or m_currently_handling_do_on_removals are true, Resume will only request a resume, using this flag to check. bool m_finalize_called; bool m_clear_thread_plans_on_stop; + bool m_force_next_event_delivery; lldb::StateType m_last_broadcast_state; /// This helps with the Public event coalescing in ShouldBroadcastEvent. + std::map<lldb::addr_t,lldb::addr_t> m_resolved_indirect_addresses; bool m_destroy_in_process; enum { @@ -3790,21 +3868,14 @@ protected: STDIOReadThreadBytesReceived (void *baton, const void *src, size_t src_len); void - PushProcessInputReader (); + PushProcessIOHandler (); void - PopProcessInputReader (); + PopProcessIOHandler (); void - ResetProcessInputReader (); - - static size_t - ProcessInputReaderCallback (void *baton, - InputReader &reader, - lldb::InputReaderAction notification, - const char *bytes, - size_t bytes_len); - + ResetProcessIOHandler (); + Error HaltForDestroyOrDetach(lldb::EventSP &exit_event_sp); diff --git a/include/lldb/Target/Queue.h b/include/lldb/Target/Queue.h new file mode 100644 index 000000000000..32ee24aebc11 --- /dev/null +++ b/include/lldb/Target/Queue.h @@ -0,0 +1,189 @@ +//===-- Queue.h ------------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_Queue_h_ +#define liblldb_Queue_h_ + +#include <vector> +#include <string> + +#include "lldb/lldb-forward.h" +#include "lldb/lldb-enumerations.h" +#include "lldb/lldb-private.h" +#include "lldb/Target/QueueItem.h" + + +namespace lldb_private { + +//------------------------------------------------------------------ +// Queue: +// This class represents a libdispatch aka Grand Central Dispatch +// queue in the process. +// +// A program using libdispatch will create queues, put work items +// (functions, blocks) on the queues. The system will create / +// reassign pthreads to execute the work items for the queues. A +// serial queue will be associated with a single thread (or possibly +// no thread, if it is not doing any work). A concurrent queue may +// be associated with multiple threads. +//------------------------------------------------------------------ + + +class Queue : + public std::enable_shared_from_this<Queue> +{ +public: + + Queue (lldb::ProcessSP process_sp, lldb::queue_id_t queue_id, const char *queue_name); + + ~Queue (); + + //------------------------------------------------------------------ + /// Get the QueueID for this Queue + /// + /// A 64-bit ID number that uniquely identifies a queue at this particular + /// stop_id. Currently the libdispatch serialnum is used for the QueueID; + /// it is a number that starts at 1 for each process and increments with + /// each queue. A serialnum is not reused for a different queue in the + /// lifetime of that process execution. + /// + /// @return + /// The QueueID for this Queue. + //------------------------------------------------------------------ + lldb::queue_id_t + GetID (); + + //------------------------------------------------------------------ + /// Get the name of this Queue + /// + /// @return + /// The name of the queue, if one is available. + /// A NULL pointer is returned if none is available. + //------------------------------------------------------------------ + const char * + GetName (); + + //------------------------------------------------------------------ + /// Get the IndexID for this Queue + /// + /// This is currently the same as GetID(). If it changes in the future, + /// it will be a small integer value (starting with 1) assigned to + /// each queue that is seen during a Process lifetime. + /// + /// Both the GetID and GetIndexID are being retained for Queues to + /// maintain similar API to the Thread class, and allow for the + /// possibility of GetID changing to a different source in the future. + /// + /// @return + /// The IndexID for this queue. + //------------------------------------------------------------------ + uint32_t + GetIndexID (); + + //------------------------------------------------------------------ + /// Return the threads currently associated with this queue + /// + /// Zero, one, or many threads may be executing code for a queue at + /// a given point in time. This call returns the list of threads + /// that are currently executing work for this queue. + /// + /// @return + /// The threads currently performing work for this queue + //------------------------------------------------------------------ + std::vector<lldb::ThreadSP> + GetThreads (); + + //------------------------------------------------------------------ + /// Return the items that are currently enqueued + /// + /// "Enqueued" means that the item has been added to the queue to + /// be done, but has not yet been done. When the item is going to + /// be processed it is "dequeued". + /// + /// @return + /// The vector of enqueued items for this queue + //------------------------------------------------------------------ + const std::vector<lldb::QueueItemSP> & + GetPendingItems(); + + lldb::ProcessSP + GetProcess() const + { + return m_process_wp.lock(); + } + + //------------------------------------------------------------------ + /// Get the number of work items that this queue is currently running + /// + /// @return + /// The number of work items currently executing. For a serial + /// queue, this will be 0 or 1. For a concurrent queue, this + /// may be any number. + //------------------------------------------------------------------ + uint32_t + GetNumRunningWorkItems () const; + + //------------------------------------------------------------------ + /// Get the number of work items enqueued on this queue + /// + /// @return + /// The number of work items currently enqueued, waiting to + /// execute. + //------------------------------------------------------------------ + uint32_t + GetNumPendingWorkItems () const; + + //------------------------------------------------------------------ + /// Get the dispatch_queue_t structure address for this Queue + /// + /// Get the address in the inferior process' memory of this Queue's + /// dispatch_queue_t structure. + /// + /// @return + /// The address of the dispatch_queue_t structure, if known. + /// LLDB_INVALID_ADDRESS will be returned if it is unavailable. + //------------------------------------------------------------------ + lldb::addr_t + GetLibdispatchQueueAddress () const; + + + void + SetNumRunningWorkItems (uint32_t count); + + void + SetNumPendingWorkItems (uint32_t count); + + void + SetLibdispatchQueueAddress (lldb::addr_t dispatch_queue_t_addr); + + void + PushPendingQueueItem (lldb::QueueItemSP item) + { + m_pending_items.push_back (item); + } + +private: + //------------------------------------------------------------------ + // For Queue only + //------------------------------------------------------------------ + + lldb::ProcessWP m_process_wp; + lldb::queue_id_t m_queue_id; + std::string m_queue_name; + uint32_t m_running_work_items_count; + uint32_t m_pending_work_items_count; + std::vector<lldb::QueueItemSP> m_pending_items; + lldb::addr_t m_dispatch_queue_t_addr; // address of libdispatch dispatch_queue_t for this Queue + + DISALLOW_COPY_AND_ASSIGN (Queue); +}; + +} // namespace lldb_private + +#endif // liblldb_Queue_h_ diff --git a/include/lldb/Target/QueueItem.h b/include/lldb/Target/QueueItem.h new file mode 100644 index 000000000000..76270da3bee6 --- /dev/null +++ b/include/lldb/Target/QueueItem.h @@ -0,0 +1,244 @@ +//===-- QueueItem.h ---------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_QueueItem_h_ +#define liblldb_QueueItem_h_ + +#include <vector> + +#include "lldb/lldb-private.h" +#include "lldb/lldb-enumerations.h" + +#include "lldb/Core/Address.h" +#include "lldb/Core/ConstString.h" + + +namespace lldb_private { + +//------------------------------------------------------------------ +// QueueItem: +// This class represents a work item enqueued on a libdispatch aka +// Grand Central Dispatch (GCD) queue. Most often, this will be a +// function or block. +// "enqueued" here means that the work item has been added to a queue +// but it has not yet started executing. When it is "dequeued", +// execution of the item begins. +//------------------------------------------------------------------ + + +class QueueItem : + public std::enable_shared_from_this<QueueItem> +{ +public: + + QueueItem (lldb::QueueSP queue_sp); + + ~QueueItem (); + + //------------------------------------------------------------------ + /// Get the kind of work item this is + /// + /// @return + /// The type of work item that this QueueItem object + /// represents. eQueueItemKindUnknown may be returned. + //------------------------------------------------------------------ + lldb::QueueItemKind + GetKind () const; + + //------------------------------------------------------------------ + /// Set the type of work item this is + /// + /// @param [in] item_kind + /// Set the kind of this work item object. + //------------------------------------------------------------------ + void + SetKind (lldb::QueueItemKind item_kind); + + //------------------------------------------------------------------ + /// Get the code address that will be executed when this work item + /// is executed. + /// + /// @return + /// The address that will be invoked when this work item is + /// executed. Not all types of QueueItems will have an + /// address associated with them; check that the returned + /// Address is valid, or check that the WorkItemKind is a + /// kind that involves an address, such as eQueueItemKindFunction + /// or eQueueItemKindBlock. + //------------------------------------------------------------------ + lldb_private::Address & + GetAddress (); + + //------------------------------------------------------------------ + /// Set the work item address for this object + /// + /// @param [in] addr + /// The address that will be invoked when this work item + /// is executed. + //------------------------------------------------------------------ + void + SetAddress (lldb_private::Address addr); + + //------------------------------------------------------------------ + /// Check if this QueueItem object is valid + /// + /// If the weak pointer to the parent Queue cannot be revivified, + /// it is invalid. + /// + /// @return + /// True if this object is valid. + //------------------------------------------------------------------ + bool + IsValid () + { + return m_queue_wp.lock() != NULL; + } + + //------------------------------------------------------------------ + /// Get an extended backtrace thread for this queue item, if available + /// + /// If the backtrace/thread information was collected when this item + /// was enqueued, this call will provide it. + /// + /// @param [in] type + /// The type of extended backtrace being requested, e.g. "libdispatch" + /// or "pthread". + /// + /// @return + /// A thread shared pointer which will have a reference to an extended + /// thread if one was available. + //------------------------------------------------------------------ + lldb::ThreadSP + GetExtendedBacktraceThread (ConstString type); + + void + SetItemThatEnqueuedThis (lldb::addr_t address_of_item) + { + m_item_that_enqueued_this_ref = address_of_item; + } + + lldb::addr_t + GetItemThatEnqueuedThis () + { + return m_item_that_enqueued_this_ref; + } + + void + SetEnqueueingThreadID (lldb::tid_t tid) + { + m_enqueueing_thread_id = tid; + } + + lldb::tid_t + GetEnqueueingThreadID () + { + return m_enqueueing_thread_id; + } + + void + SetEnqueueingQueueID (lldb::queue_id_t qid) + { + m_enqueueing_queue_id = qid; + } + + lldb::queue_id_t + GetEnqueueingQueueID () + { + return m_enqueueing_queue_id; + } + + void + SetTargetQueueID (lldb::queue_id_t qid) + { + m_target_queue_id = qid; + } + + void + SetStopID (uint32_t stop_id) + { + m_stop_id = stop_id; + } + + uint32_t + GetStopID () + { + return m_stop_id; + } + + void + SetEnqueueingBacktrace (std::vector<lldb::addr_t> backtrace) + { + m_backtrace = backtrace; + } + + std::vector<lldb::addr_t> & + GetEnqueueingBacktrace () + { + return m_backtrace; + } + + void + SetThreadLabel (std::string thread_name) + { + m_thread_label = thread_name; + } + + std::string + GetThreadLabel () + { + return m_thread_label; + } + + void + SetQueueLabel (std::string queue_name) + { + m_queue_label = queue_name; + } + + std::string + GetQueueLabel () + { + return m_queue_label; + } + + void + SetTargetQueueLabel (std::string queue_name) + { + m_target_queue_label = queue_name; + } + +protected: + lldb::QueueWP m_queue_wp; + lldb::QueueItemKind m_kind; + lldb_private::Address m_address; + + lldb::addr_t m_item_that_enqueued_this_ref; // a handle that we can pass into libBacktraceRecording + // to get the QueueItem that enqueued this item + lldb::tid_t m_enqueueing_thread_id; // thread that enqueued this item + lldb::queue_id_t m_enqueueing_queue_id; // Queue that enqueued this item, if it was a queue + lldb::queue_id_t m_target_queue_id; + uint32_t m_stop_id; // indicates when this backtrace was recorded in time + std::vector<lldb::addr_t> m_backtrace; + std::string m_thread_label; + std::string m_queue_label; + std::string m_target_queue_label; + + +private: + //------------------------------------------------------------------ + // For QueueItem only + //------------------------------------------------------------------ + + DISALLOW_COPY_AND_ASSIGN (QueueItem); + +}; + +} // namespace lldb_private + +#endif // liblldb_QueueItem_h_ diff --git a/include/lldb/Target/QueueList.h b/include/lldb/Target/QueueList.h new file mode 100644 index 000000000000..964c1099233e --- /dev/null +++ b/include/lldb/Target/QueueList.h @@ -0,0 +1,141 @@ +//===-- QueueList.h --------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_QueueList_h_ +#define liblldb_QueueList_h_ + +#include <vector> + +#include "lldb/lldb-private.h" +#include "lldb/Core/UserID.h" +#include "lldb/Utility/Iterable.h" + + +namespace lldb_private { + +//------------------------------------------------------------------ +// QueueList: +// This is the container for libdispatch aka Grand Central Dispatch +// Queue objects. +// +// Each Process will have a QueueList. When the process execution is +// paused, the QueueList may be populated with Queues by the +// SystemRuntime. +//------------------------------------------------------------------ + +class QueueList +{ +friend class Process; + +public: + + QueueList (Process *process); + + ~QueueList (); + + //------------------------------------------------------------------ + /// Get the number of libdispatch queues that are available + /// + /// @return + /// The number of queues that are stored in the QueueList. + //------------------------------------------------------------------ + uint32_t + GetSize(); + + //------------------------------------------------------------------ + /// Get the Queue at a given index number + /// + /// @param [in] idx + /// The index number (0-based) of the queue. + /// @return + /// The Queue at that index number. + //------------------------------------------------------------------ + lldb::QueueSP + GetQueueAtIndex (uint32_t idx); + + typedef std::vector<lldb::QueueSP> collection; + typedef LockingAdaptedIterable<collection, lldb::QueueSP, vector_adapter> QueueIterable; + + //------------------------------------------------------------------ + /// Iterate over the list of queues + /// + /// @return + /// An Iterable object which can be used to loop over the queues + /// that exist. + //------------------------------------------------------------------ + QueueIterable + Queues () + { + return QueueIterable(m_queues, m_mutex); + } + + //------------------------------------------------------------------ + /// Clear out the list of queues from the QueueList + //------------------------------------------------------------------ + void + Clear(); + + //------------------------------------------------------------------ + /// Add a Queue to the QueueList + /// + /// @param [in] queue + /// Used by the SystemRuntime to populate the QueueList + //------------------------------------------------------------------ + void + AddQueue (lldb::QueueSP queue); + + //------------------------------------------------------------------ + /// Find a queue in the QueueList by QueueID + /// + /// @param [in] qid + /// The QueueID (same as returned by Thread::GetQueueID()) to find. + /// + /// @return + /// A QueueSP to the queue requested, if it is present in the QueueList. + /// An empty QueueSP willbe returned if this queue was not found. + //------------------------------------------------------------------ + lldb::QueueSP + FindQueueByID (lldb::queue_id_t qid); + + //------------------------------------------------------------------ + /// Find a queue in the QueueList by IndexID + /// + /// @param [in] index_id + /// Find a queue by IndexID. This is an integer associated with each + /// unique queue seen during a debug session and will not be reused + /// for a different queue. Unlike the QueueID, a 64-bit value, this + /// will tend to be an integral value like 1 or 7. + /// + /// @return + /// A QueueSP to the queue requested, if it is present in the QueueList. + /// An empty QueueSP willbe returned if this queue was not found. + //------------------------------------------------------------------ + lldb::QueueSP + FindQueueByIndexID (uint32_t index_id); + + lldb_private::Mutex & + GetMutex (); + +protected: + + //------------------------------------------------------------------ + // Classes that inherit from Process can see and modify these + //------------------------------------------------------------------ + Process *m_process; ///< The process that manages this queue list. + uint32_t m_stop_id; ///< The process stop ID that this queue list is valid for. + collection m_queues; ///< The queues for this process. + Mutex m_mutex; + +private: + QueueList (); +}; + +} // namespace lldb_private + +#endif // liblldb_QueueList_h_ diff --git a/include/lldb/Target/SectionLoadHistory.h b/include/lldb/Target/SectionLoadHistory.h new file mode 100644 index 000000000000..50dcfd3cc870 --- /dev/null +++ b/include/lldb/Target/SectionLoadHistory.h @@ -0,0 +1,109 @@ +//===-- SectionLoadHistory.h ------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_SectionLoadHistory_h_ +#define liblldb_SectionLoadHistory_h_ + +// C Includes +// C++ Includes +#include <map> + +// Project includes +#include "lldb/lldb-public.h" +#include "lldb/Host/Mutex.h" + +namespace lldb_private { + +class SectionLoadHistory +{ +public: + enum { + // Pass eStopIDNow to any function that takes a stop ID to get + // the current value. + eStopIDNow = UINT32_MAX + }; + //------------------------------------------------------------------ + // Constructors and Destructors + //------------------------------------------------------------------ + SectionLoadHistory () : + m_stop_id_to_section_load_list(), + m_mutex (Mutex::eMutexTypeRecursive) + { + } + + ~SectionLoadHistory() + { + // Call clear since this takes a lock and clears the section load list + // in case another thread is currently using this section load list + Clear(); + } + + SectionLoadList & + GetCurrentSectionLoadList (); + + bool + IsEmpty() const; + + void + Clear (); + + uint32_t + GetLastStopID() const; + + // Get the section load address given a process stop ID + lldb::addr_t + GetSectionLoadAddress (uint32_t stop_id, + const lldb::SectionSP §ion_sp); + + bool + ResolveLoadAddress (uint32_t stop_id, + lldb::addr_t load_addr, + Address &so_addr); + + bool + SetSectionLoadAddress (uint32_t stop_id, + const lldb::SectionSP §ion_sp, + lldb::addr_t load_addr, + bool warn_multiple = false); + + // The old load address should be specified when unloading to ensure we get + // the correct instance of the section as a shared library could be loaded + // at more than one location. + bool + SetSectionUnloaded (uint32_t stop_id, + const lldb::SectionSP §ion_sp, + lldb::addr_t load_addr); + + // Unload all instances of a section. This function can be used on systems + // that don't support multiple copies of the same shared library to be + // loaded at the same time. + size_t + SetSectionUnloaded (uint32_t stop_id, + const lldb::SectionSP §ion_sp); + + void + Dump (Stream &s, + Target *target); + +protected: + + SectionLoadList * + GetSectionLoadListForStopID (uint32_t stop_id, bool read_only); + + typedef std::map<uint32_t, lldb::SectionLoadListSP> StopIDToSectionLoadList; + StopIDToSectionLoadList m_stop_id_to_section_load_list; + mutable Mutex m_mutex; + +private: + DISALLOW_COPY_AND_ASSIGN (SectionLoadHistory); +}; + +} // namespace lldb_private + +#endif // liblldb_SectionLoadHistory_h_ diff --git a/include/lldb/Target/SectionLoadList.h b/include/lldb/Target/SectionLoadList.h index ac05bf7a9cb4..6a9bbab93c98 100644 --- a/include/lldb/Target/SectionLoadList.h +++ b/include/lldb/Target/SectionLoadList.h @@ -36,6 +36,8 @@ public: { } + SectionLoadList (const SectionLoadList& rhs); + ~SectionLoadList() { // Call clear since this takes a lock and clears the section load list @@ -43,6 +45,9 @@ public: Clear(); } + void + operator=(const SectionLoadList &rhs); + bool IsEmpty() const; @@ -79,9 +84,6 @@ protected: addr_to_sect_collection m_addr_to_sect; sect_to_addr_collection m_sect_to_addr; mutable Mutex m_mutex; - -private: - DISALLOW_COPY_AND_ASSIGN (SectionLoadList); }; } // namespace lldb_private diff --git a/include/lldb/Target/SystemRuntime.h b/include/lldb/Target/SystemRuntime.h index 7a0703e6cc34..363ce122c4f3 100644 --- a/include/lldb/Target/SystemRuntime.h +++ b/include/lldb/Target/SystemRuntime.h @@ -20,9 +20,10 @@ #include "lldb/Core/ConstString.h" #include "lldb/Core/ModuleList.h" #include "lldb/Core/PluginInterface.h" +#include "lldb/Target/QueueList.h" +#include "lldb/Target/QueueItem.h" #include "lldb/lldb-private.h" - namespace lldb_private { //---------------------------------------------------------------------- @@ -103,6 +104,14 @@ public: virtual void ModulesDidLoad(lldb_private::ModuleList &module_list); + //------------------------------------------------------------------ + /// Called before detaching from a process. + /// + /// This will give a SystemRuntime plugin a chance to free any resources + /// in the inferior process before we detach. + //------------------------------------------------------------------ + virtual void + Detach (); //------------------------------------------------------------------ /// Return a list of thread origin extended backtraces that may @@ -160,6 +169,107 @@ public: virtual lldb::ThreadSP GetExtendedBacktraceThread (lldb::ThreadSP thread, ConstString type); + //------------------------------------------------------------------ + /// Get the extended backtrace thread for a QueueItem + /// + /// A QueueItem represents a function/block that will be executed on + /// a libdispatch queue in the future, or it represents a function/block + /// that is currently executing on a thread. + /// + /// This method will report a thread backtrace of the function that + /// enqueued it originally, if possible. + /// + /// @param [in] queue_item_sp + /// The QueueItem that we are getting an extended backtrace for. + /// + /// @param [in] type + /// The type of extended backtrace to fetch. The types supported + /// are returned from SystemRuntime::GetExtendedBacktraceTypes. + /// + /// @return + /// If an extended backtrace is available, it is returned. Else + /// an empty ThreadSP is returned. + //------------------------------------------------------------------ + virtual lldb::ThreadSP + GetExtendedBacktraceForQueueItem (lldb::QueueItemSP queue_item_sp, ConstString type) + { + return lldb::ThreadSP(); + } + + //------------------------------------------------------------------ + /// Populate the Process' QueueList with libdispatch / GCD queues that exist. + /// + /// When process execution is paused, the SystemRuntime may be called to fill + /// in the list of Queues that currently exist. + /// + /// @param [out] queue_list + /// This QueueList will be cleared, and any queues that currently exist + /// will be added. An empty QueueList will be returned if no queues + /// exist or if this Systemruntime does not support libdispatch queues. + //------------------------------------------------------------------ + virtual void + PopulateQueueList (lldb_private::QueueList &queue_list) + { + } + + //------------------------------------------------------------------ + /// Get the queue name for a thread given a thread's dispatch_qaddr. + /// + /// On systems using libdispatch queues, a thread may be associated with a queue. + /// There will be a call to get the thread's dispatch_qaddr. At the dispatch_qaddr + /// we will find the address of this thread's dispatch_queue_t structure. + /// Given the address of the dispatch_queue_t structure for a thread, + /// get the queue name and return it. + /// + /// @param [in] dispatch_qaddr + /// The address of the dispatch_queue_t structure for this thread. + /// + /// @return + /// The string of this queue's name. An empty string is returned if the + /// name could not be found. + //------------------------------------------------------------------ + virtual std::string + GetQueueNameFromThreadQAddress (lldb::addr_t dispatch_qaddr) + { + return ""; + } + + //------------------------------------------------------------------ + /// Get the QueueID for the libdispatch queue given the thread's dispatch_qaddr. + /// + /// On systems using libdispatch queues, a thread may be associated with a queue. + /// There will be a call to get the thread's dispatch_qaddr. At the dispatch_qaddr + /// we will find the address of this thread's dispatch_queue_t structure. + /// Given the address of the dispatch_queue_t structure for a thread, + /// get the queue ID and return it. + /// + /// @param [in] dispatch_qaddr + /// The address of the dispatch_queue_t structure for this thread. + /// + /// @return + /// The queue ID, or if it could not be retrieved, LLDB_INVALID_QUEUE_ID. + //------------------------------------------------------------------ + virtual lldb::queue_id_t + GetQueueIDFromThreadQAddress (lldb::addr_t dispatch_qaddr) + { + return LLDB_INVALID_QUEUE_ID; + } + + //------------------------------------------------------------------ + /// Get the pending work items for a libdispatch Queue + /// + /// If this system/process is using libdispatch and the runtime can do so, + /// retrieve the list of pending work items for the specified Queue and + /// add it to the Queue. + /// + /// @param [in] queue + /// The queue of interest. + //------------------------------------------------------------------ + virtual void + PopulatePendingItemsForQueue (lldb_private::Queue *queue) + { + } + protected: //------------------------------------------------------------------ // Member variables. diff --git a/include/lldb/Target/Target.h b/include/lldb/Target/Target.h index d874891a6aff..e65a511ab77a 100644 --- a/include/lldb/Target/Target.h +++ b/include/lldb/Target/Target.h @@ -35,7 +35,7 @@ #include "lldb/Target/ABI.h" #include "lldb/Target/ExecutionContextScope.h" #include "lldb/Target/PathMappingList.h" -#include "lldb/Target/SectionLoadList.h" +#include "lldb/Target/SectionLoadHistory.h" namespace lldb_private { @@ -164,6 +164,9 @@ public: bool GetUseFastStepping() const; + + bool + GetDisplayExpressionsInCrashlogs () const; LoadScriptFromSymFile GetLoadScriptFromSymbolFile() const; @@ -174,6 +177,11 @@ public: MemoryModuleLoadLevel GetMemoryModuleLoadLevel() const; + bool + GetUserSpecifiedTrapHandlerNames (Args &args) const; + + void + SetUserSpecifiedTrapHandlerNames (const Args &args); }; typedef std::shared_ptr<TargetProperties> TargetPropertiesSP; @@ -526,6 +534,10 @@ public: void Destroy(); + + Error + Launch (Listener &listener, + ProcessLaunchInfo &launch_info); //------------------------------------------------------------------ // This part handles the breakpoints. @@ -630,7 +642,8 @@ public: CreateBreakpoint (lldb::SearchFilterSP &filter_sp, lldb::BreakpointResolverSP &resolver_sp, bool internal, - bool request_hardware); + bool request_hardware, + bool resolve_indirect_symbols); // Use this to create a watchpoint: lldb::WatchpointSP @@ -1001,14 +1014,14 @@ public: SectionLoadList& GetSectionLoadList() { - return m_section_load_list; + return m_section_load_history.GetCurrentSectionLoadList(); } - const SectionLoadList& - GetSectionLoadList() const - { - return m_section_load_list; - } +// const SectionLoadList& +// GetSectionLoadList() const +// { +// return const_cast<SectionLoadHistory *>(&m_section_load_history)->GetCurrentSectionLoadList(); +// } static Target * GetTargetFromContexts (const ExecutionContext *exe_ctx_ptr, @@ -1048,6 +1061,26 @@ public: Error Install(ProcessLaunchInfo *launch_info); + + bool + ResolveLoadAddress (lldb::addr_t load_addr, + Address &so_addr, + uint32_t stop_id = SectionLoadHistory::eStopIDNow); + + bool + SetSectionLoadAddress (const lldb::SectionSP §ion, + lldb::addr_t load_addr, + bool warn_multiple = false); + + bool + SetSectionUnloaded (const lldb::SectionSP §ion_sp); + + bool + SetSectionUnloaded (const lldb::SectionSP §ion_sp, lldb::addr_t load_addr); + + void + ClearAllLoadedSections (); + // Since expressions results can persist beyond the lifetime of a process, // and the const expression results are available after a process is gone, // we provide a way for expressions to be evaluated from the Target itself. @@ -1144,7 +1177,7 @@ public: std::unique_ptr<ThreadSpec> m_thread_spec_ap; bool m_active; - // Use AddStopHook to make a new empty stop hook. The GetCommandPointer and fill it with commands, + // Use CreateStopHook to make a new empty stop hook. The GetCommandPointer and fill it with commands, // and SetSpecifier to set the specifier shared pointer (can be null, that will match anything.) StopHook (lldb::TargetSP target_sp, lldb::user_id_t uid); friend class Target; @@ -1153,8 +1186,8 @@ public: // Add an empty stop hook to the Target's stop hook list, and returns a shared pointer to it in new_hook. // Returns the id of the new hook. - lldb::user_id_t - AddStopHook (StopHookSP &new_hook); + StopHookSP + CreateStopHook (); void RunStopHooks (); @@ -1250,7 +1283,7 @@ protected: Mutex m_mutex; ///< An API mutex that is used by the lldb::SB* classes make the SB interface thread safe ArchSpec m_arch; ModuleList m_images; ///< The list of images for this process (shared libraries and anything dynamically loaded). - SectionLoadList m_section_load_list; + SectionLoadHistory m_section_load_history; BreakpointList m_breakpoint_list; BreakpointList m_internal_breakpoint_list; lldb::BreakpointSP m_last_created_breakpoint; @@ -1260,7 +1293,6 @@ protected: // we can correctly tear down everything that we need to, so the only // class that knows about the process lifespan is this target class. lldb::ProcessSP m_process_sp; - bool m_valid; lldb::SearchFilterSP m_search_filter_sp; PathMappingList m_image_search_paths; std::unique_ptr<ClangASTContext> m_scratch_ast_context_ap; @@ -1273,8 +1305,8 @@ protected: typedef std::map<lldb::user_id_t, StopHookSP> StopHookCollection; StopHookCollection m_stop_hooks; lldb::user_id_t m_stop_hook_next_id; + bool m_valid; bool m_suppress_stop_hooks; - bool m_suppress_synthetic_value; static void ImageSearchPathsChanged (const PathMappingList &path_list, diff --git a/include/lldb/Target/Thread.h b/include/lldb/Target/Thread.h index 4f78b0fe6998..20687e977bff 100644 --- a/include/lldb/Target/Thread.h +++ b/include/lldb/Target/Thread.h @@ -44,6 +44,9 @@ public: const RegularExpression * GetSymbolsToAvoidRegexp(); + FileSpecList & + GetLibrariesToAvoid() const; + bool GetTraceEnabledState() const; }; @@ -410,6 +413,55 @@ public: DumpUsingSettingsFormat (Stream &strm, uint32_t frame_idx); //------------------------------------------------------------------ + /// Default implementation for stepping into. + /// + /// This function is designed to be used by commands where the + /// process is publicly stopped. + /// + /// @param[in] source_step + /// If true and the frame has debug info, then do a source level + /// step in, else do a single instruction step in. + /// + /// @param[in] avoid_code_without_debug_info + /// If \a true, then avoid stepping into code that doesn't have + /// debug info, else step into any code regardless of wether it + /// has debug info. + /// + /// @return + /// An error that describes anything that went wrong + //------------------------------------------------------------------ + virtual Error + StepIn (bool source_step, + bool avoid_code_without_debug_info); + + //------------------------------------------------------------------ + /// Default implementation for stepping over. + /// + /// This function is designed to be used by commands where the + /// process is publicly stopped. + /// + /// @param[in] source_step + /// If true and the frame has debug info, then do a source level + /// step over, else do a single instruction step over. + /// + /// @return + /// An error that describes anything that went wrong + //------------------------------------------------------------------ + virtual Error + StepOver (bool source_step); + + //------------------------------------------------------------------ + /// Default implementation for stepping out. + /// + /// This function is designed to be used by commands where the + /// process is publicly stopped. + /// + /// @return + /// An error that describes anything that went wrong + //------------------------------------------------------------------ + virtual Error + StepOut (); + //------------------------------------------------------------------ /// Retrieves the per-thread data area. /// Most OSs maintain a per-thread pointer (e.g. the FS register on /// x64), which we return the value of here. diff --git a/include/lldb/Target/ThreadList.h b/include/lldb/Target/ThreadList.h index f4dfdb23ec0f..65d9b74098a6 100644 --- a/include/lldb/Target/ThreadList.h +++ b/include/lldb/Target/ThreadList.h @@ -45,6 +45,8 @@ public: void AddThread (const lldb::ThreadSP &thread_sp); + void + InsertThread (const lldb::ThreadSP &thread_sp, uint32_t idx); // Return the selected thread if there is one. Otherwise, return the thread // selected at index 0. lldb::ThreadSP diff --git a/include/lldb/Target/ThreadPlanStepInRange.h b/include/lldb/Target/ThreadPlanStepInRange.h index dbc8446b2e18..2f741f179bd4 100644 --- a/include/lldb/Target/ThreadPlanStepInRange.h +++ b/include/lldb/Target/ThreadPlanStepInRange.h @@ -73,7 +73,7 @@ protected: SetFlagsToDefault (); bool - FrameMatchesAvoidRegexp (); + FrameMatchesAvoidCriteria (); private: diff --git a/include/lldb/Target/UnwindAssembly.h b/include/lldb/Target/UnwindAssembly.h index 6a4ae0c30f27..254382ac029d 100644 --- a/include/lldb/Target/UnwindAssembly.h +++ b/include/lldb/Target/UnwindAssembly.h @@ -17,10 +17,11 @@ namespace lldb_private { class UnwindAssembly : + public std::enable_shared_from_this<UnwindAssembly>, public PluginInterface { public: - static UnwindAssembly* + static lldb::UnwindAssemblySP FindPlugin (const ArchSpec &arch); virtual diff --git a/include/lldb/Utility/Iterable.h b/include/lldb/Utility/Iterable.h index 9b91cae37eef..17335373e72d 100644 --- a/include/lldb/Utility/Iterable.h +++ b/include/lldb/Utility/Iterable.h @@ -147,9 +147,14 @@ public: return m_iter >= rhs.m_iter; } - friend AdaptedConstIterator operator+(typename BackingIterator::difference_type, AdaptedConstIterator &); - friend typename BackingIterator::difference_type operator-(AdaptedConstIterator &, AdaptedConstIterator &); - friend void swap(AdaptedConstIterator &, AdaptedConstIterator &); + template <typename C1, typename E1, E1 (*A1)(typename C1::const_iterator &)> + friend AdaptedConstIterator<C1, E1, A1> operator+(typename C1::const_iterator::difference_type, AdaptedConstIterator<C1, E1, A1> &); + + template <typename C1, typename E1, E1 (*A1)(typename C1::const_iterator &)> + friend typename C1::const_iterator::difference_type operator-(AdaptedConstIterator<C1, E1, A1> &, AdaptedConstIterator<C1, E1, A1> &); + + template <typename C1, typename E1, E1 (*A1)(typename C1::const_iterator &)> + friend void swap(AdaptedConstIterator<C1, E1, A1> &, AdaptedConstIterator<C1, E1, A1> &); }; template <typename C, typename E, E (*A)(typename C::const_iterator &)> diff --git a/include/lldb/lldb-enumerations.h b/include/lldb/lldb-enumerations.h index 0b341575be46..c8294960a7b2 100644 --- a/include/lldb/lldb-enumerations.h +++ b/include/lldb/lldb-enumerations.h @@ -726,6 +726,19 @@ namespace lldb { eFilePermissionsDirectoryDefault = eFilePermissionsUserRWX, } FilePermissions; + //---------------------------------------------------------------------- + // Queue work item types + // + // The different types of work that can be enqueued on a libdispatch + // aka Grand Central Dispatch (GCD) queue. + //---------------------------------------------------------------------- + typedef enum QueueItemKind + { + eQueueItemKindUnknown = 0, + eQueueItemKindFunction, + eQueueItemKindBlock + } QueueItemKind; + } // namespace lldb diff --git a/include/lldb/lldb-forward.h b/include/lldb/lldb-forward.h index 68d85f081e25..43e589e39521 100644 --- a/include/lldb/lldb-forward.h +++ b/include/lldb/lldb-forward.h @@ -82,6 +82,7 @@ class Disassembler; struct DumpValueObjectOptions; class DynamicLibrary; class DynamicLoader; +class Editline; class EmulateInstruction; class Error; class EvaluateExpressionOptions; @@ -102,9 +103,9 @@ class FuncUnwinders; class Function; class FunctionInfo; class InlineFunctionInfo; -class InputReader; class Instruction; class InstructionList; +class IOHandler; class IRExecutionUnit; class LanguageRuntime; class SystemRuntime; @@ -181,6 +182,8 @@ class SearchFilter; class Section; class SectionImpl; class SectionList; +class SectionLoadHistory; +class SectionLoadList; class Settings; class SourceManager; class SourceManagerImpl; @@ -214,6 +217,9 @@ class TypeFilterImpl; #ifndef LLDB_DISABLE_PYTHON class ScriptedSyntheticChildren; #endif +class Queue; +class QueueItem; +class QueueImpl; class Target; class TargetList; class Thread; @@ -297,8 +303,8 @@ namespace lldb { typedef std::shared_ptr<lldb_private::Function> FunctionSP; typedef std::shared_ptr<lldb_private::FuncUnwinders> FuncUnwindersSP; typedef std::shared_ptr<lldb_private::InlineFunctionInfo> InlineFunctionInfoSP; - typedef std::shared_ptr<lldb_private::InputReader> InputReaderSP; typedef std::shared_ptr<lldb_private::Instruction> InstructionSP; + typedef std::shared_ptr<lldb_private::IOHandler> IOHandlerSP; typedef std::shared_ptr<lldb_private::LanguageRuntime> LanguageRuntimeSP; typedef std::shared_ptr<lldb_private::SystemRuntime> SystemRuntimeSP; typedef std::shared_ptr<lldb_private::LineTable> LineTableSP; @@ -334,12 +340,16 @@ namespace lldb { typedef std::shared_ptr<lldb_private::RegisterCheckpoint> RegisterCheckpointSP; typedef std::shared_ptr<lldb_private::RegisterContext> RegisterContextSP; typedef std::shared_ptr<lldb_private::RegularExpression> RegularExpressionSP; + typedef std::shared_ptr<lldb_private::Queue> QueueSP; + typedef std::weak_ptr<lldb_private::Queue> QueueWP; + typedef std::shared_ptr<lldb_private::QueueItem> QueueItemSP; typedef std::shared_ptr<lldb_private::ScriptInterpreterObject> ScriptInterpreterObjectSP; #ifndef LLDB_DISABLE_PYTHON typedef std::shared_ptr<lldb_private::ScriptSummaryFormat> ScriptSummaryFormatSP; #endif // #ifndef LLDB_DISABLE_PYTHON typedef std::shared_ptr<lldb_private::Section> SectionSP; typedef std::weak_ptr<lldb_private::Section> SectionWP; + typedef std::shared_ptr<lldb_private::SectionLoadList> SectionLoadListSP; typedef std::shared_ptr<lldb_private::SearchFilter> SearchFilterSP; typedef std::shared_ptr<lldb_private::Settings> SettingsSP; typedef std::shared_ptr<lldb_private::StackFrame> StackFrameSP; @@ -349,6 +359,7 @@ namespace lldb { typedef std::shared_ptr<lldb_private::StoppointLocation> StoppointLocationSP; typedef std::shared_ptr<lldb_private::Stream> StreamSP; typedef std::weak_ptr<lldb_private::Stream> StreamWP; + typedef std::shared_ptr<lldb_private::StreamFile> StreamFileSP; typedef std::shared_ptr<lldb_private::StringSummaryFormat> StringTypeSummaryImplSP; typedef std::shared_ptr<lldb_private::SymbolFile> SymbolFileSP; typedef std::shared_ptr<lldb_private::SymbolFileType> SymbolFileTypeSP; @@ -373,6 +384,7 @@ namespace lldb { #ifndef LLDB_DISABLE_PYTHON typedef std::shared_ptr<lldb_private::ScriptedSyntheticChildren> ScriptedSyntheticChildrenSP; #endif + typedef std::shared_ptr<lldb_private::UnwindAssembly> UnwindAssemblySP; typedef std::shared_ptr<lldb_private::UnwindPlan> UnwindPlanSP; typedef lldb_private::SharingPtr<lldb_private::ValueObject> ValueObjectSP; typedef std::shared_ptr<lldb_private::Value> ValueSP; diff --git a/include/lldb/lldb-private-enumerations.h b/include/lldb/lldb-private-enumerations.h index 98919422be58..c2273f5dfe2e 100644 --- a/include/lldb/lldb-private-enumerations.h +++ b/include/lldb/lldb-private-enumerations.h @@ -117,7 +117,9 @@ typedef enum PathType ePathTypeHeaderDir, // Find LLDB header file directory ePathTypePythonDir, // Find Python modules (PYTHONPATH) directory ePathTypeLLDBSystemPlugins, // System plug-ins directory - ePathTypeLLDBUserPlugins // User plug-ins directory + ePathTypeLLDBUserPlugins, // User plug-ins directory + ePathTypeLLDBTempSystemDir // The LLDB temp directory for this system + } PathType; @@ -251,6 +253,15 @@ typedef enum MemoryModuleLoadLevel { } MemoryModuleLoadLevel; +//---------------------------------------------------------------------- +// Result enums for when reading multiple lines from IOHandlers +//---------------------------------------------------------------------- +enum class LineStatus { + Success, // The line that was just edited if good and should be added to the lines + Error, // There is an error with the current line and it needs to be re-edited before it can be accepted + Done // Lines are complete +}; + } // namespace lldb_private diff --git a/include/lldb/lldb-private-log.h b/include/lldb/lldb-private-log.h index 48cce69d36cc..9d8d735dcf41 100644 --- a/include/lldb/lldb-private-log.h +++ b/include/lldb/lldb-private-log.h @@ -45,6 +45,7 @@ #define LIBLLDB_LOG_MMAP (1u << 23) #define LIBLLDB_LOG_OS (1u << 24) #define LIBLLDB_LOG_PLATFORM (1u << 25) +#define LIBLLDB_LOG_SYSTEM_RUNTIME (1u << 26) #define LIBLLDB_LOG_ALL (UINT32_MAX) #define LIBLLDB_LOG_DEFAULT (LIBLLDB_LOG_PROCESS |\ LIBLLDB_LOG_THREAD |\ diff --git a/source/API/SBBreakpoint.cpp b/source/API/SBBreakpoint.cpp index 11ad149fdddc..fbdc0e32f498 100644 --- a/source/API/SBBreakpoint.cpp +++ b/source/API/SBBreakpoint.cpp @@ -23,6 +23,7 @@ #include "lldb/Core/Stream.h" #include "lldb/Core/StreamFile.h" #include "lldb/Target/Process.h" +#include "lldb/Target/SectionLoadList.h" #include "lldb/Target/Target.h" #include "lldb/Target/Thread.h" #include "lldb/Target/ThreadSpec.h" diff --git a/source/API/SBCommandInterpreter.cpp b/source/API/SBCommandInterpreter.cpp index ac77e2e41126..f1faa13ba981 100644 --- a/source/API/SBCommandInterpreter.cpp +++ b/source/API/SBCommandInterpreter.cpp @@ -107,6 +107,22 @@ SBCommandInterpreter::AliasExists (const char *cmd) return false; } +bool +SBCommandInterpreter::IsActive () +{ + if (m_opaque_ptr) + return m_opaque_ptr->IsActive (); + return false; +} + +const char * +SBCommandInterpreter::GetIOHandlerControlSequence(char ch) +{ + if (m_opaque_ptr) + return m_opaque_ptr->GetDebugger().GetTopIOHandlerControlSequence (ch).GetCString(); + return NULL; +} + lldb::ReturnStatus SBCommandInterpreter::HandleCommand (const char *command_line, SBCommandReturnObject &result, bool add_to_history) { diff --git a/source/API/SBDebugger.cpp b/source/API/SBDebugger.cpp index 10c0b7dea208..8d6887a6c280 100644 --- a/source/API/SBDebugger.cpp +++ b/source/API/SBDebugger.cpp @@ -20,7 +20,6 @@ #include "lldb/API/SBError.h" #include "lldb/API/SBEvent.h" #include "lldb/API/SBFrame.h" -#include "lldb/API/SBInputReader.h" #include "lldb/API/SBProcess.h" #include "lldb/API/SBSourceManager.h" #include "lldb/API/SBStream.h" @@ -37,6 +36,7 @@ #include "lldb/Core/Debugger.h" #include "lldb/Core/State.h" +#include "lldb/Core/StreamFile.h" #include "lldb/DataFormatters/DataVisualization.h" #include "lldb/Host/DynamicLibrary.h" #include "lldb/Interpreter/Args.h" @@ -49,6 +49,29 @@ using namespace lldb; using namespace lldb_private; +SBInputReader::SBInputReader() +{ +} +SBInputReader::~SBInputReader() +{ +} + +SBError +SBInputReader::Initialize(lldb::SBDebugger& sb_debugger, unsigned long (*)(void*, lldb::SBInputReader*, lldb::InputReaderAction, char const*, unsigned long), void*, lldb::InputReaderGranularity, char const*, char const*, bool) +{ + return SBError(); +} + +void +SBInputReader::SetIsDone(bool) +{ +} +bool +SBInputReader::IsActive() const +{ + return false; +} + static lldb::DynamicLibrarySP LoadPlugin (const lldb::DebuggerSP &debugger_sp, const FileSpec& spec, Error& error) { @@ -111,7 +134,7 @@ SBDebugger::Clear () log->Printf ("SBDebugger(%p)::Clear ()", m_opaque_sp.get()); if (m_opaque_sp) - m_opaque_sp->CleanUpInputReaders (); + m_opaque_sp->ClearIOHandlers (); m_opaque_sp.reset(); } @@ -309,7 +332,11 @@ FILE * SBDebugger::GetInputFileHandle () { if (m_opaque_sp) - return m_opaque_sp->GetInputFile().GetStream(); + { + StreamFileSP stream_file_sp (m_opaque_sp->GetInputFile()); + if (stream_file_sp) + return stream_file_sp->GetFile().GetStream(); + } return NULL; } @@ -317,7 +344,11 @@ FILE * SBDebugger::GetOutputFileHandle () { if (m_opaque_sp) - return m_opaque_sp->GetOutputFile().GetStream(); + { + StreamFileSP stream_file_sp (m_opaque_sp->GetOutputFile()); + if (stream_file_sp) + return stream_file_sp->GetFile().GetStream(); + } return NULL; } @@ -325,7 +356,12 @@ FILE * SBDebugger::GetErrorFileHandle () { if (m_opaque_sp) - return m_opaque_sp->GetErrorFile().GetStream(); + if (m_opaque_sp) + { + StreamFileSP stream_file_sp (m_opaque_sp->GetErrorFile()); + if (stream_file_sp) + return stream_file_sp->GetFile().GetStream(); + } return NULL; } @@ -885,17 +921,17 @@ SBDebugger::DispatchInput (void* baton, const void *data, size_t data_len) void SBDebugger::DispatchInput (const void *data, size_t data_len) { - Log *log(GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); - - if (log) - log->Printf ("SBDebugger(%p)::DispatchInput (data=\"%.*s\", size_t=%" PRIu64 ")", - m_opaque_sp.get(), - (int) data_len, - (const char *) data, - (uint64_t)data_len); - - if (m_opaque_sp) - m_opaque_sp->DispatchInput ((const char *) data, data_len); +// Log *log(GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); +// +// if (log) +// log->Printf ("SBDebugger(%p)::DispatchInput (data=\"%.*s\", size_t=%" PRIu64 ")", +// m_opaque_sp.get(), +// (int) data_len, +// (const char *) data, +// (uint64_t)data_len); +// +// if (m_opaque_sp) +// m_opaque_sp->DispatchInput ((const char *) data, data_len); } void @@ -911,54 +947,18 @@ SBDebugger::DispatchInputEndOfFile () if (m_opaque_sp) m_opaque_sp->DispatchInputEndOfFile (); } - -bool -SBDebugger::InputReaderIsTopReader (const lldb::SBInputReader &reader) -{ - Log *log(GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); - - if (log) - log->Printf ("SBDebugger(%p)::InputReaderIsTopReader (SBInputReader(%p))", m_opaque_sp.get(), &reader); - - if (m_opaque_sp && reader.IsValid()) - { - InputReaderSP reader_sp (*reader); - return m_opaque_sp->InputReaderIsTopReader (reader_sp); - } - - return false; -} - void SBDebugger::PushInputReader (SBInputReader &reader) { - Log *log(GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); - - if (log) - log->Printf ("SBDebugger(%p)::PushInputReader (SBInputReader(%p))", m_opaque_sp.get(), &reader); - - if (m_opaque_sp && reader.IsValid()) - { - TargetSP target_sp (m_opaque_sp->GetSelectedTarget()); - Mutex::Locker api_locker; - if (target_sp) - api_locker.Lock(target_sp->GetAPIMutex()); - InputReaderSP reader_sp(*reader); - m_opaque_sp->PushInputReader (reader_sp); - } } void -SBDebugger::NotifyTopInputReader (InputReaderAction notification) +SBDebugger::RunCommandInterpreter (bool auto_handle_events, + bool spawn_thread) { - Log *log(GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); - - if (log) - log->Printf ("SBDebugger(%p)::NotifyTopInputReader (%d)", m_opaque_sp.get(), notification); - if (m_opaque_sp) - m_opaque_sp->NotifyTopInputReader (notification); + m_opaque_sp->GetCommandInterpreter().RunCommandInterpreter(auto_handle_events, spawn_thread); } void @@ -1050,7 +1050,7 @@ SBDebugger::GetInternalVariableValue (const char *var_name, const char *debugger if (!value_str.empty()) { StringList string_list; - string_list.SplitIntoLines(value_str.c_str(), value_str.size()); + string_list.SplitIntoLines(value_str); return SBStringList(&string_list); } } diff --git a/source/API/SBFrame.cpp b/source/API/SBFrame.cpp index 1a1a63bd0671..cff460208070 100644 --- a/source/API/SBFrame.cpp +++ b/source/API/SBFrame.cpp @@ -1386,20 +1386,22 @@ SBFrame::EvaluateExpression (const char *expr, const SBExpressionOptions &option frame = exe_ctx.GetFramePtr(); if (frame) { -#ifdef LLDB_CONFIGURATION_DEBUG - StreamString frame_description; - frame->DumpUsingSettingsFormat (&frame_description); - Host::SetCrashDescriptionWithFormat ("SBFrame::EvaluateExpression (expr = \"%s\", fetch_dynamic_value = %u) %s", - expr, options.GetFetchDynamicValue(), frame_description.GetString().c_str()); -#endif - exe_results = target->EvaluateExpression (expr, + if (target->GetDisplayExpressionsInCrashlogs()) + { + StreamString frame_description; + frame->DumpUsingSettingsFormat (&frame_description); + Host::SetCrashDescriptionWithFormat ("SBFrame::EvaluateExpression (expr = \"%s\", fetch_dynamic_value = %u) %s", + expr, options.GetFetchDynamicValue(), frame_description.GetString().c_str()); + } + + exe_results = target->EvaluateExpression (expr, frame, expr_value_sp, options.ref()); expr_result.SetSP(expr_value_sp, options.GetFetchDynamicValue()); -#ifdef LLDB_CONFIGURATION_DEBUG - Host::SetCrashDescription (NULL); -#endif + + if (target->GetDisplayExpressionsInCrashlogs()) + Host::SetCrashDescription (NULL); } else { diff --git a/source/API/SBInputReader.cpp b/source/API/SBInputReader.cpp deleted file mode 100644 index 82b75c869f08..000000000000 --- a/source/API/SBInputReader.cpp +++ /dev/null @@ -1,216 +0,0 @@ -//===-- SBInputReader.cpp ---------------------------------------*- C++ -*-===// -// -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// - - -#include "lldb/lldb-enumerations.h" - -#include "lldb/API/SBDebugger.h" -#include "lldb/API/SBError.h" -#include "lldb/API/SBInputReader.h" -#include "lldb/API/SBStream.h" -#include "lldb/API/SBStringList.h" -#include "lldb/Core/InputReader.h" -#include "lldb/Core/Log.h" - - -using namespace lldb; -using namespace lldb_private; - -SBInputReader::SBInputReader () : - m_opaque_sp (), - m_callback_function (NULL), - m_callback_baton (NULL) - -{ -} - -SBInputReader::SBInputReader (const lldb::InputReaderSP &reader_sp) : - m_opaque_sp (reader_sp) -{ - Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); - - if (log) - log->Printf ("SBInputReader::SBInputReader (reader_sp=%p) => SBInputReader(%p)", reader_sp.get(), - m_opaque_sp.get()); -} - -SBInputReader::SBInputReader (const SBInputReader &rhs) : - m_opaque_sp (rhs.m_opaque_sp) -{ - Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); - - if (log) - log->Printf("SBInputReader::SBInputReader (rhs.sp=%p) => SBInputReader(%p)", - rhs.m_opaque_sp.get(), m_opaque_sp.get()); -} - -SBInputReader::~SBInputReader () -{ -} - -size_t -SBInputReader::PrivateCallback -( - void *baton, - InputReader &reader, - lldb::InputReaderAction notification, - const char *bytes, - size_t bytes_len -) -{ - SBInputReader *sb_reader = (SBInputReader *)baton; - return sb_reader->m_callback_function (sb_reader->m_callback_baton, - sb_reader, - notification, - bytes, - bytes_len); -} - -SBError -SBInputReader::Initialize -( - SBDebugger &debugger, - Callback callback_function, - void *callback_baton, - lldb::InputReaderGranularity granularity, - const char *end_token, - const char *prompt, - bool echo -) -{ - Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); - - if (log) - log->Printf("SBInputReader(%p)::Initialize (SBDebugger(%p), callback_function=%p, callback_baton=%p, " - "granularity=%s, end_token=\"%s\", prompt=\"%s\", echo=%i)", - m_opaque_sp.get(), - debugger.get(), - callback_function, - callback_baton, - InputReader::GranularityAsCString (granularity), end_token, prompt, - echo); - - SBError sb_error; - m_opaque_sp.reset (new InputReader (debugger.ref())); - - m_callback_function = callback_function; - m_callback_baton = callback_baton; - - if (m_opaque_sp) - { - sb_error.SetError (m_opaque_sp->Initialize (SBInputReader::PrivateCallback, - this, - granularity, - end_token, - prompt, - echo)); - } - - if (sb_error.Fail()) - { - m_opaque_sp.reset (); - m_callback_function = NULL; - m_callback_baton = NULL; - } - - if (log) - { - SBStream sstr; - sb_error.GetDescription (sstr); - log->Printf ("SBInputReader(%p)::Initialize (...) => SBError(%p): %s", m_opaque_sp.get(), - sb_error.get(), sstr.GetData()); - } - - return sb_error; -} - -bool -SBInputReader::IsValid () const -{ - return (m_opaque_sp.get() != NULL); -} - -const SBInputReader & -SBInputReader::operator = (const SBInputReader &rhs) -{ - if (this != &rhs) - m_opaque_sp = rhs.m_opaque_sp; - return *this; -} - -InputReader * -SBInputReader::operator->() const -{ - return m_opaque_sp.get(); -} - -lldb::InputReaderSP & -SBInputReader::operator *() -{ - return m_opaque_sp; -} - -const lldb::InputReaderSP & -SBInputReader::operator *() const -{ - return m_opaque_sp; -} - -InputReader * -SBInputReader::get() const -{ - return m_opaque_sp.get(); -} - -InputReader & -SBInputReader::ref() const -{ - assert (m_opaque_sp.get()); - return *m_opaque_sp; -} - -bool -SBInputReader::IsDone () const -{ - if (m_opaque_sp) - return m_opaque_sp->IsDone(); - else - return true; -} - -void -SBInputReader::SetIsDone (bool value) -{ - if (m_opaque_sp) - m_opaque_sp->SetIsDone (value); -} - -bool -SBInputReader::IsActive () const -{ - Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); - - bool ret_value = false; - if (m_opaque_sp) - ret_value = m_opaque_sp->IsActive(); - - if (log) - log->Printf ("SBInputReader(%p)::IsActive () => %i", m_opaque_sp.get(), ret_value); - - return ret_value; -} - -InputReaderGranularity -SBInputReader::GetGranularity () -{ - if (m_opaque_sp) - return m_opaque_sp->GetGranularity(); - else - return eInputReaderGranularityInvalid; -} diff --git a/source/API/SBModule.cpp b/source/API/SBModule.cpp index 0285cf304d4d..c8543d4de298 100644 --- a/source/API/SBModule.cpp +++ b/source/API/SBModule.cpp @@ -69,7 +69,7 @@ SBModule::SBModule (lldb::SBProcess &process, lldb::addr_t header_addr) : { Target &target = process_sp->GetTarget(); bool changed = false; - m_opaque_sp->SetLoadAddress(target, 0, changed); + m_opaque_sp->SetLoadAddress(target, 0, true, changed); target.GetImages().Append(m_opaque_sp); } } @@ -579,6 +579,23 @@ SBModule::FindTypes (const char *type) return retval; } +lldb::SBType +SBModule::GetTypeByID (lldb::user_id_t uid) +{ + ModuleSP module_sp (GetSP ()); + if (module_sp) + { + SymbolVendor* vendor = module_sp->GetSymbolVendor(); + if (vendor) + { + Type *type_ptr = vendor->ResolveTypeUID(uid); + if (type_ptr) + return SBType(type_ptr->shared_from_this()); + } + } + return SBType(); +} + lldb::SBTypeList SBModule::GetTypes (uint32_t type_mask) { diff --git a/source/API/SBProcess.cpp b/source/API/SBProcess.cpp index 557006f24345..235388b5f25c 100644 --- a/source/API/SBProcess.cpp +++ b/source/API/SBProcess.cpp @@ -535,6 +535,53 @@ SBProcess::GetThreadAtIndex (size_t index) } uint32_t +SBProcess::GetNumQueues () +{ + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + + uint32_t num_queues = 0; + ProcessSP process_sp(GetSP()); + if (process_sp) + { + Process::StopLocker stop_locker; + + Mutex::Locker api_locker (process_sp->GetTarget().GetAPIMutex()); + num_queues = process_sp->GetQueueList().GetSize(); + } + + if (log) + log->Printf ("SBProcess(%p)::GetNumQueues () => %d", process_sp.get(), num_queues); + + return num_queues; +} + +SBQueue +SBProcess::GetQueueAtIndex (size_t index) +{ + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + + SBQueue sb_queue; + QueueSP queue_sp; + ProcessSP process_sp(GetSP()); + if (process_sp) + { + Process::StopLocker stop_locker; + Mutex::Locker api_locker (process_sp->GetTarget().GetAPIMutex()); + queue_sp = process_sp->GetQueueList().GetQueueAtIndex(index); + sb_queue.SetQueue (queue_sp); + } + + if (log) + { + log->Printf ("SBProcess(%p)::GetQueueAtIndex (index=%d) => SBQueue(%p)", + process_sp.get(), (uint32_t) index, queue_sp.get()); + } + + return sb_queue; +} + + +uint32_t SBProcess::GetStopID(bool include_expression_stops) { ProcessSP process_sp(GetSP()); diff --git a/source/API/SBQueue.cpp b/source/API/SBQueue.cpp new file mode 100644 index 000000000000..8d67a48d6b81 --- /dev/null +++ b/source/API/SBQueue.cpp @@ -0,0 +1,368 @@ +//===-- SBQueue.cpp ---------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/lldb-python.h" + +#include "lldb/API/SBQueue.h" + +#include "lldb/API/SBProcess.h" +#include "lldb/API/SBThread.h" +#include "lldb/Core/Log.h" +#include "lldb/Target/Process.h" +#include "lldb/Target/Queue.h" +#include "lldb/Target/QueueItem.h" +#include "lldb/Target/Thread.h" + +using namespace lldb; +using namespace lldb_private; + +namespace lldb_private +{ + + class QueueImpl + { + public: + QueueImpl () : + m_queue_wp(), + m_threads(), + m_thread_list_fetched(false), + m_pending_items(), + m_pending_items_fetched(false) + { + } + + QueueImpl (const lldb::QueueSP &queue_sp) : + m_queue_wp(), + m_threads(), + m_thread_list_fetched(false), + m_pending_items(), + m_pending_items_fetched(false) + { + m_queue_wp = queue_sp; + } + + QueueImpl (const QueueImpl &rhs) + { + if (&rhs == this) + return; + m_queue_wp = rhs.m_queue_wp; + m_threads = rhs.m_threads; + m_thread_list_fetched = rhs.m_thread_list_fetched; + m_pending_items = rhs.m_pending_items; + m_pending_items_fetched = rhs.m_pending_items_fetched; + } + + ~QueueImpl () + { + } + + bool + IsValid () + { + return m_queue_wp.lock() != NULL; + } + + void + Clear () + { + m_queue_wp.reset(); + m_thread_list_fetched = false; + m_threads.clear(); + m_pending_items_fetched = false; + m_pending_items.clear(); + } + + void + SetQueue (const lldb::QueueSP &queue_sp) + { + Clear(); + m_queue_wp = queue_sp; + } + + lldb::queue_id_t + GetQueueID () const + { + lldb::queue_id_t result = LLDB_INVALID_QUEUE_ID; + lldb::QueueSP queue_sp = m_queue_wp.lock(); + if (queue_sp) + { + result = queue_sp->GetID(); + } + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + if (log) + log->Printf ("SBQueue(%p)::GetQueueID () => 0x%" PRIx64, this, result); + return result; + } + + uint32_t + GetIndexID () const + { + uint32_t result = LLDB_INVALID_INDEX32; + lldb::QueueSP queue_sp = m_queue_wp.lock(); + if (queue_sp) + { + result = queue_sp->GetIndexID(); + } + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + if (log) + log->Printf ("SBQueueImpl(%p)::GetIndexID () => %d", this, result); + return result; + } + + const char * + GetName () const + { + const char *name = NULL; + lldb::QueueSP queue_sp = m_queue_wp.lock (); + if (queue_sp.get()) + { + name = queue_sp->GetName(); + } + + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + if (log) + log->Printf ("SBQueueImpl(%p)::GetName () => %s", this, name ? name : "NULL"); + + return name; + } + + void + FetchThreads () + { + if (m_thread_list_fetched == false) + { + lldb::QueueSP queue_sp = m_queue_wp.lock(); + if (queue_sp) + { + Process::StopLocker stop_locker; + if (stop_locker.TryLock (&queue_sp->GetProcess()->GetRunLock())) + { + const std::vector<ThreadSP> thread_list(queue_sp->GetThreads()); + m_thread_list_fetched = true; + const uint32_t num_threads = thread_list.size(); + for (uint32_t idx = 0; idx < num_threads; ++idx) + { + ThreadSP thread_sp = thread_list[idx]; + if (thread_sp && thread_sp->IsValid()) + { + m_threads.push_back (thread_sp); + } + } + } + } + } + } + + void + FetchItems () + { + if (m_pending_items_fetched == false) + { + QueueSP queue_sp = m_queue_wp.lock(); + if (queue_sp) + { + Process::StopLocker stop_locker; + if (stop_locker.TryLock (&queue_sp->GetProcess()->GetRunLock())) + { + const std::vector<QueueItemSP> queue_items(queue_sp->GetPendingItems()); + m_pending_items_fetched = true; + const uint32_t num_pending_items = queue_items.size(); + for (uint32_t idx = 0; idx < num_pending_items; ++idx) + { + QueueItemSP item = queue_items[idx]; + if (item && item->IsValid()) + { + m_pending_items.push_back (item); + } + } + } + } + } + } + + uint32_t + GetNumThreads () + { + uint32_t result = 0; + + FetchThreads(); + if (m_thread_list_fetched) + { + result = m_threads.size(); + } + return result; + } + + lldb::SBThread + GetThreadAtIndex (uint32_t idx) + { + FetchThreads(); + + SBThread sb_thread; + QueueSP queue_sp = m_queue_wp.lock(); + if (queue_sp && idx < m_threads.size()) + { + ProcessSP process_sp = queue_sp->GetProcess(); + if (process_sp) + { + ThreadSP thread_sp = m_threads[idx].lock(); + if (thread_sp) + { + sb_thread.SetThread (thread_sp); + } + } + } + return sb_thread; + } + + + uint32_t + GetNumPendingItems () + { + uint32_t result = 0; + FetchItems(); + + if (m_pending_items_fetched) + { + result = m_pending_items.size(); + } + return result; + } + + lldb::SBQueueItem + GetPendingItemAtIndex (uint32_t idx) + { + SBQueueItem result; + FetchItems(); + if (m_pending_items_fetched && idx < m_pending_items.size()) + { + result.SetQueueItem (m_pending_items[idx]); + } + return result; + } + + lldb::SBProcess + GetProcess () + { + SBProcess result; + QueueSP queue_sp = m_queue_wp.lock(); + if (queue_sp) + { + result.SetSP (queue_sp->GetProcess()); + } + return result; + } + + private: + lldb::QueueWP m_queue_wp; + std::vector<lldb::ThreadWP> m_threads; // threads currently executing this queue's items + bool m_thread_list_fetched; // have we tried to fetch the threads list already? + std::vector<lldb::QueueItemSP> m_pending_items; // items currently enqueued + bool m_pending_items_fetched; // have we tried to fetch the item list already? + }; + +} + +SBQueue::SBQueue () : + m_opaque_sp (new QueueImpl()) +{ +} + +SBQueue::SBQueue (const QueueSP& queue_sp) : + m_opaque_sp (new QueueImpl (queue_sp)) +{ +} + +SBQueue::SBQueue (const SBQueue &rhs) +{ + if (&rhs == this) + return; + + m_opaque_sp = rhs.m_opaque_sp; +} + +const lldb::SBQueue & +SBQueue::operator = (const lldb::SBQueue &rhs) +{ + m_opaque_sp = rhs.m_opaque_sp; + return *this; +} + +SBQueue::~SBQueue() +{ +} + +bool +SBQueue::IsValid() const +{ + return m_opaque_sp->IsValid(); +} + + +void +SBQueue::Clear () +{ + m_opaque_sp->Clear(); +} + + +void +SBQueue::SetQueue (const QueueSP& queue_sp) +{ + m_opaque_sp->SetQueue (queue_sp); +} + +lldb::queue_id_t +SBQueue::GetQueueID () const +{ + return m_opaque_sp->GetQueueID (); +} + +uint32_t +SBQueue::GetIndexID () const +{ + return m_opaque_sp->GetIndexID (); +} + +const char * +SBQueue::GetName () const +{ + return m_opaque_sp->GetName (); +} + +uint32_t +SBQueue::GetNumThreads () +{ + return m_opaque_sp->GetNumThreads (); +} + +SBThread +SBQueue::GetThreadAtIndex (uint32_t idx) +{ + return m_opaque_sp->GetThreadAtIndex (idx); +} + + +uint32_t +SBQueue::GetNumPendingItems () +{ + return m_opaque_sp->GetNumPendingItems (); +} + +SBQueueItem +SBQueue::GetPendingItemAtIndex (uint32_t idx) +{ + return m_opaque_sp->GetPendingItemAtIndex (idx); +} + +SBProcess +SBQueue::GetProcess () +{ + return m_opaque_sp->GetProcess(); +} diff --git a/source/API/SBQueueItem.cpp b/source/API/SBQueueItem.cpp new file mode 100644 index 000000000000..481d51e55426 --- /dev/null +++ b/source/API/SBQueueItem.cpp @@ -0,0 +1,120 @@ +//===-- SBQueueItem.cpp -----------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/lldb-python.h" +#include "lldb/lldb-forward.h" + +#include "lldb/API/SBAddress.h" +#include "lldb/API/SBQueueItem.h" +#include "lldb/API/SBThread.h" +#include "lldb/Core/Address.h" +#include "lldb/Target/QueueItem.h" +#include "lldb/Target/Thread.h" + +using namespace lldb; +using namespace lldb_private; + +//---------------------------------------------------------------------- +// Constructors +//---------------------------------------------------------------------- +SBQueueItem::SBQueueItem () : + m_queue_item_sp() +{ +} + +SBQueueItem::SBQueueItem (const QueueItemSP& queue_item_sp) : + m_queue_item_sp (queue_item_sp) +{ +} + +//---------------------------------------------------------------------- +// Destructor +//---------------------------------------------------------------------- +SBQueueItem::~SBQueueItem() +{ + m_queue_item_sp.reset(); +} + +bool +SBQueueItem::IsValid() const +{ + return m_queue_item_sp.get() != NULL; +} + + +void +SBQueueItem::Clear () +{ + m_queue_item_sp.reset(); +} + + +void +SBQueueItem::SetQueueItem (const QueueItemSP& queue_item_sp) +{ + m_queue_item_sp = queue_item_sp; +} + + +lldb::QueueItemKind +SBQueueItem::GetKind () const +{ + QueueItemKind result = eQueueItemKindUnknown; + if (m_queue_item_sp) + { + result = m_queue_item_sp->GetKind (); + } + return result; +} + +void +SBQueueItem::SetKind (lldb::QueueItemKind kind) +{ + if (m_queue_item_sp) + { + m_queue_item_sp->SetKind (kind); + } +} + +SBAddress +SBQueueItem::GetAddress () const +{ + SBAddress result; + if (m_queue_item_sp) + { + result.SetAddress (&m_queue_item_sp->GetAddress()); + } + return result; +} + +void +SBQueueItem::SetAddress (SBAddress addr) +{ + if (m_queue_item_sp) + { + m_queue_item_sp->SetAddress (addr.ref()); + } +} + +SBThread +SBQueueItem::GetExtendedBacktraceThread (const char *type) +{ + SBThread result; + if (m_queue_item_sp) + { + ThreadSP thread_sp; + ConstString type_const (type); + thread_sp = m_queue_item_sp->GetExtendedBacktraceThread (type_const); + if (thread_sp) + { + result.SetThread (thread_sp); + } + } + return result; +} diff --git a/source/API/SBTarget.cpp b/source/API/SBTarget.cpp index c8bc2171436d..224349c0bce6 100644 --- a/source/API/SBTarget.cpp +++ b/source/API/SBTarget.cpp @@ -52,6 +52,7 @@ #include "lldb/Symbol/VariableList.h" #include "lldb/Target/LanguageRuntime.h" #include "lldb/Target/Process.h" + #include "lldb/Target/Target.h" #include "lldb/Target/TargetList.h" @@ -688,57 +689,26 @@ SBTarget::Launch return sb_process; } } - else - { - if (listener.IsValid()) - process_sp = target_sp->CreateProcess (listener.ref(), NULL, NULL); - else - process_sp = target_sp->CreateProcess (target_sp->GetDebugger().GetListener(), NULL, NULL); - } - if (process_sp) - { - sb_process.SetSP (process_sp); - if (getenv("LLDB_LAUNCH_FLAG_DISABLE_STDIO")) - launch_flags |= eLaunchFlagDisableSTDIO; + if (getenv("LLDB_LAUNCH_FLAG_DISABLE_STDIO")) + launch_flags |= eLaunchFlagDisableSTDIO; - ProcessLaunchInfo launch_info (stdin_path, stdout_path, stderr_path, working_directory, launch_flags); - - Module *exe_module = target_sp->GetExecutableModulePointer(); - if (exe_module) - launch_info.SetExecutableFile(exe_module->GetPlatformFileSpec(), true); - if (argv) - launch_info.GetArguments().AppendArguments (argv); - if (envp) - launch_info.GetEnvironmentEntries ().SetArguments (envp); - - error.SetError (process_sp->Launch (launch_info)); - if (error.Success()) - { - // We we are stopping at the entry point, we can return now! - if (stop_at_entry) - return sb_process; - - // Make sure we are stopped at the entry - StateType state = process_sp->WaitForProcessToStop (NULL); - if (state == eStateStopped) - { - // resume the process to skip the entry point - error.SetError (process_sp->Resume()); - if (error.Success()) - { - // If we are doing synchronous mode, then wait for the - // process to stop yet again! - if (target_sp->GetDebugger().GetAsyncExecution () == false) - process_sp->WaitForProcessToStop (NULL); - } - } - } - } + ProcessLaunchInfo launch_info (stdin_path, stdout_path, stderr_path, working_directory, launch_flags); + + Module *exe_module = target_sp->GetExecutableModulePointer(); + if (exe_module) + launch_info.SetExecutableFile(exe_module->GetPlatformFileSpec(), true); + if (argv) + launch_info.GetArguments().AppendArguments (argv); + if (envp) + launch_info.GetEnvironmentEntries ().SetArguments (envp); + + if (listener.IsValid()) + error.SetError (target_sp->Launch(listener.ref(), launch_info)); else - { - error.SetErrorString ("unable to create lldb_private::Process"); - } + error.SetError (target_sp->Launch(target_sp->GetDebugger().GetListener(), launch_info)); + + sb_process.SetSP(target_sp->GetProcessSP()); } else { @@ -749,7 +719,7 @@ SBTarget::Launch if (log) { log->Printf ("SBTarget(%p)::Launch (...) => SBProcess(%p)", - target_sp.get(), process_sp.get()); + target_sp.get(), sb_process.GetSP().get()); } return sb_process; @@ -761,7 +731,6 @@ SBTarget::Launch (SBLaunchInfo &sb_launch_info, SBError& error) Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); SBProcess sb_process; - ProcessSP process_sp; TargetSP target_sp(GetSP()); if (log) @@ -773,7 +742,8 @@ SBTarget::Launch (SBLaunchInfo &sb_launch_info, SBError& error) { Mutex::Locker api_locker (target_sp->GetAPIMutex()); StateType state = eStateInvalid; - process_sp = target_sp->GetProcessSP(); + { + ProcessSP process_sp = target_sp->GetProcessSP(); if (process_sp) { state = process_sp->GetState(); @@ -787,58 +757,20 @@ SBTarget::Launch (SBLaunchInfo &sb_launch_info, SBError& error) return sb_process; } } - - if (state != eStateConnected) - process_sp = target_sp->CreateProcess (target_sp->GetDebugger().GetListener(), NULL, NULL); - - if (process_sp) - { - sb_process.SetSP (process_sp); - lldb_private::ProcessLaunchInfo &launch_info = sb_launch_info.ref(); + } - Module *exe_module = target_sp->GetExecutableModulePointer(); - if (exe_module) - launch_info.SetExecutableFile(exe_module->GetPlatformFileSpec(), true); + lldb_private::ProcessLaunchInfo &launch_info = sb_launch_info.ref(); - const ArchSpec &arch_spec = target_sp->GetArchitecture(); - if (arch_spec.IsValid()) - launch_info.GetArchitecture () = arch_spec; - - error.SetError (process_sp->Launch (launch_info)); - const bool synchronous_execution = target_sp->GetDebugger().GetAsyncExecution () == false; - if (error.Success()) - { - if (launch_info.GetFlags().Test(eLaunchFlagStopAtEntry)) - { - // If we are doing synchronous mode, then wait for the initial - // stop to happen, else, return and let the caller watch for - // the stop - if (synchronous_execution) - process_sp->WaitForProcessToStop (NULL); - // We we are stopping at the entry point, we can return now! - return sb_process; - } - - // Make sure we are stopped at the entry - StateType state = process_sp->WaitForProcessToStop (NULL); - if (state == eStateStopped) - { - // resume the process to skip the entry point - error.SetError (process_sp->Resume()); - if (error.Success()) - { - // If we are doing synchronous mode, then wait for the - // process to stop yet again! - if (synchronous_execution) - process_sp->WaitForProcessToStop (NULL); - } - } - } - } - else - { - error.SetErrorString ("unable to create lldb_private::Process"); - } + Module *exe_module = target_sp->GetExecutableModulePointer(); + if (exe_module) + launch_info.SetExecutableFile(exe_module->GetPlatformFileSpec(), true); + + const ArchSpec &arch_spec = target_sp->GetArchitecture(); + if (arch_spec.IsValid()) + launch_info.GetArchitecture () = arch_spec; + + error.SetError (target_sp->Launch (target_sp->GetDebugger().GetListener(), launch_info)); + sb_process.SetSP(target_sp->GetProcessSP()); } else { @@ -848,8 +780,8 @@ SBTarget::Launch (SBLaunchInfo &sb_launch_info, SBError& error) log = lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API); if (log) { - log->Printf ("SBTarget(%p)::Launch (...) => SBProcess(%p)", - target_sp.get(), process_sp.get()); + log->Printf ("SBTarget(%p)::Launch (...) => SBProcess(%p)", + target_sp.get(), sb_process.GetSP().get()); } return sb_process; @@ -1263,7 +1195,7 @@ SBTarget::ResolveLoadAddress (lldb::addr_t vm_addr) if (target_sp) { Mutex::Locker api_locker (target_sp->GetAPIMutex()); - if (target_sp->GetSectionLoadList().ResolveLoadAddress (vm_addr, addr)) + if (target_sp->ResolveLoadAddress (vm_addr, addr)) return sb_addr; } @@ -1273,6 +1205,26 @@ SBTarget::ResolveLoadAddress (lldb::addr_t vm_addr) return sb_addr; } + +lldb::SBAddress +SBTarget::ResolvePastLoadAddress (uint32_t stop_id, lldb::addr_t vm_addr) +{ + lldb::SBAddress sb_addr; + Address &addr = sb_addr.ref(); + TargetSP target_sp(GetSP()); + if (target_sp) + { + Mutex::Locker api_locker (target_sp->GetAPIMutex()); + if (target_sp->ResolveLoadAddress (vm_addr, addr)) + return sb_addr; + } + + // We have a load address that isn't in a section, just return an address + // with the offset filled in (the address) and the section set to NULL + addr.SetRawAddress(vm_addr); + return sb_addr; +} + SBSymbolContext SBTarget::ResolveSymbolContextForAddress (const SBAddress& addr, uint32_t resolve_scope) @@ -2479,10 +2431,14 @@ SBTarget::SetSectionLoadAddress (lldb::SBSection section, } else { - if (target_sp->GetSectionLoadList().SetSectionLoadAddress (section_sp, section_base_addr)) + ProcessSP process_sp (target_sp->GetProcessSP()); + uint32_t stop_id = 0; + if (process_sp) + stop_id = process_sp->GetStopID(); + + if (target_sp->SetSectionLoadAddress (section_sp, section_base_addr)) { // Flush info in the process (stack frames, etc) - ProcessSP process_sp (target_sp->GetProcessSP()); if (process_sp) process_sp->Flush(); } @@ -2511,10 +2467,14 @@ SBTarget::ClearSectionLoadAddress (lldb::SBSection section) } else { - if (target_sp->GetSectionLoadList().SetSectionUnloaded (section.GetSP())) + ProcessSP process_sp (target_sp->GetProcessSP()); + uint32_t stop_id = 0; + if (process_sp) + stop_id = process_sp->GetStopID(); + + if (target_sp->SetSectionUnloaded (section.GetSP())) { // Flush info in the process (stack frames, etc) - ProcessSP process_sp (target_sp->GetProcessSP()); if (process_sp) process_sp->Flush(); } @@ -2539,7 +2499,7 @@ SBTarget::SetModuleLoadAddress (lldb::SBModule module, int64_t slide_offset) if (module_sp) { bool changed = false; - if (module_sp->SetLoadAddress (*target_sp, slide_offset, changed)) + if (module_sp->SetLoadAddress (*target_sp, slide_offset, true, changed)) { // The load was successful, make sure that at least some sections // changed before we notify that our module was loaded. @@ -2586,13 +2546,18 @@ SBTarget::ClearModuleLoadAddress (lldb::SBModule module) SectionList *section_list = objfile->GetSectionList(); if (section_list) { + ProcessSP process_sp (target_sp->GetProcessSP()); + uint32_t stop_id = 0; + if (process_sp) + stop_id = process_sp->GetStopID(); + bool changed = false; const size_t num_sections = section_list->GetSize(); for (size_t sect_idx = 0; sect_idx < num_sections; ++sect_idx) { SectionSP section_sp (section_list->GetSectionAtIndex(sect_idx)); if (section_sp) - changed |= target_sp->GetSectionLoadList().SetSectionUnloaded (section_sp) > 0; + changed |= target_sp->SetSectionUnloaded (section_sp) > 0; } if (changed) { diff --git a/source/API/SBType.cpp b/source/API/SBType.cpp index 3055c2752083..5ca7ddf3d813 100644 --- a/source/API/SBType.cpp +++ b/source/API/SBType.cpp @@ -186,6 +186,14 @@ SBType::GetReferenceType() } SBType +SBType::GetTypedefedType() +{ + if (!IsValid()) + return SBType(); + return SBType(TypeImplSP(new TypeImpl(m_opaque_sp->GetTypedefedType()))); +} + +SBType SBType::GetDereferencedType() { if (!IsValid()) diff --git a/source/API/SBTypeCategory.cpp b/source/API/SBTypeCategory.cpp index 08fdefad1be8..9fe4dad01a9f 100644 --- a/source/API/SBTypeCategory.cpp +++ b/source/API/SBTypeCategory.cpp @@ -87,7 +87,7 @@ SBTypeCategory::GetNumFormats () if (!IsValid()) return 0; - return m_opaque_sp->GetValueNavigator()->GetCount() + m_opaque_sp->GetRegexValueNavigator()->GetCount(); + return m_opaque_sp->GetTypeFormatsContainer()->GetCount() + m_opaque_sp->GetRegexTypeFormatsContainer()->GetCount(); } uint32_t @@ -95,7 +95,7 @@ SBTypeCategory::GetNumSummaries () { if (!IsValid()) return 0; - return m_opaque_sp->GetSummaryNavigator()->GetCount() + m_opaque_sp->GetRegexSummaryNavigator()->GetCount(); + return m_opaque_sp->GetTypeSummariesContainer()->GetCount() + m_opaque_sp->GetRegexTypeSummariesContainer()->GetCount(); } uint32_t @@ -103,7 +103,7 @@ SBTypeCategory::GetNumFilters () { if (!IsValid()) return 0; - return m_opaque_sp->GetFilterNavigator()->GetCount() + m_opaque_sp->GetRegexFilterNavigator()->GetCount(); + return m_opaque_sp->GetTypeFiltersContainer()->GetCount() + m_opaque_sp->GetRegexTypeFiltersContainer()->GetCount(); } #ifndef LLDB_DISABLE_PYTHON @@ -112,7 +112,7 @@ SBTypeCategory::GetNumSynthetics () { if (!IsValid()) return 0; - return m_opaque_sp->GetSyntheticNavigator()->GetCount() + m_opaque_sp->GetRegexSyntheticNavigator()->GetCount(); + return m_opaque_sp->GetTypeSyntheticsContainer()->GetCount() + m_opaque_sp->GetRegexTypeSyntheticsContainer()->GetCount(); } #endif @@ -162,9 +162,9 @@ SBTypeCategory::GetFilterForType (SBTypeNameSpecifier spec) lldb::SyntheticChildrenSP children_sp; if (spec.IsRegex()) - m_opaque_sp->GetRegexFilterNavigator()->GetExact(ConstString(spec.GetName()), children_sp); + m_opaque_sp->GetRegexTypeFiltersContainer()->GetExact(ConstString(spec.GetName()), children_sp); else - m_opaque_sp->GetFilterNavigator()->GetExact(ConstString(spec.GetName()), children_sp); + m_opaque_sp->GetTypeFiltersContainer()->GetExact(ConstString(spec.GetName()), children_sp); if (!children_sp) return lldb::SBTypeFilter(); @@ -186,9 +186,9 @@ SBTypeCategory::GetFormatForType (SBTypeNameSpecifier spec) lldb::TypeFormatImplSP format_sp; if (spec.IsRegex()) - m_opaque_sp->GetRegexValueNavigator()->GetExact(ConstString(spec.GetName()), format_sp); + m_opaque_sp->GetRegexTypeFormatsContainer()->GetExact(ConstString(spec.GetName()), format_sp); else - m_opaque_sp->GetValueNavigator()->GetExact(ConstString(spec.GetName()), format_sp); + m_opaque_sp->GetTypeFormatsContainer()->GetExact(ConstString(spec.GetName()), format_sp); if (!format_sp) return lldb::SBTypeFormat(); @@ -209,9 +209,9 @@ SBTypeCategory::GetSummaryForType (SBTypeNameSpecifier spec) lldb::TypeSummaryImplSP summary_sp; if (spec.IsRegex()) - m_opaque_sp->GetRegexSummaryNavigator()->GetExact(ConstString(spec.GetName()), summary_sp); + m_opaque_sp->GetRegexTypeSummariesContainer()->GetExact(ConstString(spec.GetName()), summary_sp); else - m_opaque_sp->GetSummaryNavigator()->GetExact(ConstString(spec.GetName()), summary_sp); + m_opaque_sp->GetTypeSummariesContainer()->GetExact(ConstString(spec.GetName()), summary_sp); if (!summary_sp) return lldb::SBTypeSummary(); @@ -233,9 +233,9 @@ SBTypeCategory::GetSyntheticForType (SBTypeNameSpecifier spec) lldb::SyntheticChildrenSP children_sp; if (spec.IsRegex()) - m_opaque_sp->GetRegexSyntheticNavigator()->GetExact(ConstString(spec.GetName()), children_sp); + m_opaque_sp->GetRegexTypeSyntheticsContainer()->GetExact(ConstString(spec.GetName()), children_sp); else - m_opaque_sp->GetSyntheticNavigator()->GetExact(ConstString(spec.GetName()), children_sp); + m_opaque_sp->GetTypeSyntheticsContainer()->GetExact(ConstString(spec.GetName()), children_sp); if (!children_sp) return lldb::SBTypeSynthetic(); @@ -312,9 +312,9 @@ SBTypeCategory::AddTypeFormat (SBTypeNameSpecifier type_name, return false; if (type_name.IsRegex()) - m_opaque_sp->GetRegexValueNavigator()->Add(lldb::RegularExpressionSP(new RegularExpression(type_name.GetName())), format.GetSP()); + m_opaque_sp->GetRegexTypeFormatsContainer()->Add(lldb::RegularExpressionSP(new RegularExpression(type_name.GetName())), format.GetSP()); else - m_opaque_sp->GetValueNavigator()->Add(ConstString(type_name.GetName()), format.GetSP()); + m_opaque_sp->GetTypeFormatsContainer()->Add(ConstString(type_name.GetName()), format.GetSP()); return true; } @@ -329,9 +329,9 @@ SBTypeCategory::DeleteTypeFormat (SBTypeNameSpecifier type_name) return false; if (type_name.IsRegex()) - return m_opaque_sp->GetRegexValueNavigator()->Delete(ConstString(type_name.GetName())); + return m_opaque_sp->GetRegexTypeFormatsContainer()->Delete(ConstString(type_name.GetName())); else - return m_opaque_sp->GetValueNavigator()->Delete(ConstString(type_name.GetName())); + return m_opaque_sp->GetTypeFormatsContainer()->Delete(ConstString(type_name.GetName())); } #ifndef LLDB_DISABLE_PYTHON @@ -383,9 +383,9 @@ SBTypeCategory::AddTypeSummary (SBTypeNameSpecifier type_name, } if (type_name.IsRegex()) - m_opaque_sp->GetRegexSummaryNavigator()->Add(lldb::RegularExpressionSP(new RegularExpression(type_name.GetName())), summary.GetSP()); + m_opaque_sp->GetRegexTypeSummariesContainer()->Add(lldb::RegularExpressionSP(new RegularExpression(type_name.GetName())), summary.GetSP()); else - m_opaque_sp->GetSummaryNavigator()->Add(ConstString(type_name.GetName()), summary.GetSP()); + m_opaque_sp->GetTypeSummariesContainer()->Add(ConstString(type_name.GetName()), summary.GetSP()); return true; } @@ -401,9 +401,9 @@ SBTypeCategory::DeleteTypeSummary (SBTypeNameSpecifier type_name) return false; if (type_name.IsRegex()) - return m_opaque_sp->GetRegexSummaryNavigator()->Delete(ConstString(type_name.GetName())); + return m_opaque_sp->GetRegexTypeSummariesContainer()->Delete(ConstString(type_name.GetName())); else - return m_opaque_sp->GetSummaryNavigator()->Delete(ConstString(type_name.GetName())); + return m_opaque_sp->GetTypeSummariesContainer()->Delete(ConstString(type_name.GetName())); } bool @@ -420,9 +420,9 @@ SBTypeCategory::AddTypeFilter (SBTypeNameSpecifier type_name, return false; if (type_name.IsRegex()) - m_opaque_sp->GetRegexFilterNavigator()->Add(lldb::RegularExpressionSP(new RegularExpression(type_name.GetName())), filter.GetSP()); + m_opaque_sp->GetRegexTypeFiltersContainer()->Add(lldb::RegularExpressionSP(new RegularExpression(type_name.GetName())), filter.GetSP()); else - m_opaque_sp->GetFilterNavigator()->Add(ConstString(type_name.GetName()), filter.GetSP()); + m_opaque_sp->GetTypeFiltersContainer()->Add(ConstString(type_name.GetName()), filter.GetSP()); return true; } @@ -437,9 +437,9 @@ SBTypeCategory::DeleteTypeFilter (SBTypeNameSpecifier type_name) return false; if (type_name.IsRegex()) - return m_opaque_sp->GetRegexFilterNavigator()->Delete(ConstString(type_name.GetName())); + return m_opaque_sp->GetRegexTypeFiltersContainer()->Delete(ConstString(type_name.GetName())); else - return m_opaque_sp->GetFilterNavigator()->Delete(ConstString(type_name.GetName())); + return m_opaque_sp->GetTypeFiltersContainer()->Delete(ConstString(type_name.GetName())); } #ifndef LLDB_DISABLE_PYTHON @@ -491,9 +491,9 @@ SBTypeCategory::AddTypeSynthetic (SBTypeNameSpecifier type_name, } if (type_name.IsRegex()) - m_opaque_sp->GetRegexSyntheticNavigator()->Add(lldb::RegularExpressionSP(new RegularExpression(type_name.GetName())), synth.GetSP()); + m_opaque_sp->GetRegexTypeSyntheticsContainer()->Add(lldb::RegularExpressionSP(new RegularExpression(type_name.GetName())), synth.GetSP()); else - m_opaque_sp->GetSyntheticNavigator()->Add(ConstString(type_name.GetName()), synth.GetSP()); + m_opaque_sp->GetTypeSyntheticsContainer()->Add(ConstString(type_name.GetName()), synth.GetSP()); return true; } @@ -508,9 +508,9 @@ SBTypeCategory::DeleteTypeSynthetic (SBTypeNameSpecifier type_name) return false; if (type_name.IsRegex()) - return m_opaque_sp->GetRegexSyntheticNavigator()->Delete(ConstString(type_name.GetName())); + return m_opaque_sp->GetRegexTypeSyntheticsContainer()->Delete(ConstString(type_name.GetName())); else - return m_opaque_sp->GetSyntheticNavigator()->Delete(ConstString(type_name.GetName())); + return m_opaque_sp->GetTypeSyntheticsContainer()->Delete(ConstString(type_name.GetName())); } #endif // LLDB_DISABLE_PYTHON diff --git a/source/API/SBTypeFormat.cpp b/source/API/SBTypeFormat.cpp index 34ab404a206a..d3ec9bc00bd0 100644 --- a/source/API/SBTypeFormat.cpp +++ b/source/API/SBTypeFormat.cpp @@ -25,7 +25,13 @@ m_opaque_sp() SBTypeFormat::SBTypeFormat (lldb::Format format, uint32_t options) -: m_opaque_sp(TypeFormatImplSP(new TypeFormatImpl(format,options))) +: m_opaque_sp(TypeFormatImplSP(new TypeFormatImpl_Format(format,options))) +{ +} + +SBTypeFormat::SBTypeFormat (const char* type, + uint32_t options) +: m_opaque_sp(TypeFormatImplSP(new TypeFormatImpl_EnumType(ConstString(type ? type : ""),options))) { } @@ -47,11 +53,19 @@ SBTypeFormat::IsValid() const lldb::Format SBTypeFormat::GetFormat () { - if (IsValid()) - return m_opaque_sp->GetFormat(); + if (IsValid() && m_opaque_sp->GetType() == TypeFormatImpl::Type::eTypeFormat) + return ((TypeFormatImpl_Format*)m_opaque_sp.get())->GetFormat(); return lldb::eFormatInvalid; } +const char* +SBTypeFormat::GetTypeName () +{ + if (IsValid() && m_opaque_sp->GetType() == TypeFormatImpl::Type::eTypeEnum) + return ((TypeFormatImpl_EnumType*)m_opaque_sp.get())->GetTypeName().AsCString(""); + return ""; +} + uint32_t SBTypeFormat::GetOptions() { @@ -63,14 +77,21 @@ SBTypeFormat::GetOptions() void SBTypeFormat::SetFormat (lldb::Format fmt) { - if (CopyOnWrite_Impl()) - m_opaque_sp->SetFormat(fmt); + if (CopyOnWrite_Impl(Type::eTypeFormat)) + ((TypeFormatImpl_Format*)m_opaque_sp.get())->SetFormat(fmt); +} + +void +SBTypeFormat::SetTypeName (const char* type) +{ + if (CopyOnWrite_Impl(Type::eTypeEnum)) + ((TypeFormatImpl_EnumType*)m_opaque_sp.get())->SetTypeName(ConstString(type ? type : "")); } void SBTypeFormat::SetOptions (uint32_t value) { - if (CopyOnWrite_Impl()) + if (CopyOnWrite_Impl(Type::eTypeKeepSame)) m_opaque_sp->SetOptions(value); } @@ -143,13 +164,30 @@ SBTypeFormat::SBTypeFormat (const lldb::TypeFormatImplSP &typeformat_impl_sp) : } bool -SBTypeFormat::CopyOnWrite_Impl() +SBTypeFormat::CopyOnWrite_Impl(Type type) { if (!IsValid()) return false; - if (m_opaque_sp.unique()) + + if (m_opaque_sp.unique() && + ((type == Type::eTypeKeepSame) || + (type == Type::eTypeFormat && m_opaque_sp->GetType() == TypeFormatImpl::Type::eTypeFormat) || + (type == Type::eTypeEnum && m_opaque_sp->GetType() == TypeFormatImpl::Type::eTypeEnum)) + ) return true; - SetSP(TypeFormatImplSP(new TypeFormatImpl(GetFormat(),GetOptions()))); + if (type == Type::eTypeKeepSame) + { + if (m_opaque_sp->GetType() == TypeFormatImpl::Type::eTypeFormat) + type = Type::eTypeFormat; + else + type = Type::eTypeEnum; + } + + if (type == Type::eTypeFormat) + SetSP(TypeFormatImplSP(new TypeFormatImpl_Format(GetFormat(),GetOptions()))); + else + SetSP(TypeFormatImplSP(new TypeFormatImpl_EnumType(ConstString(GetTypeName()),GetOptions()))); + return true; } diff --git a/source/API/SBValue.cpp b/source/API/SBValue.cpp index 51b6790dd2b8..4bd018352ff2 100644 --- a/source/API/SBValue.cpp +++ b/source/API/SBValue.cpp @@ -96,7 +96,24 @@ public: bool IsValid () { - return m_valobj_sp.get() != NULL; + if (m_valobj_sp.get() == NULL) + return false; + else + { + // FIXME: This check is necessary but not sufficient. We for sure don't want to touch SBValues whose owning + // targets have gone away. This check is a little weak in that it enforces that restriction when you call + // IsValid, but since IsValid doesn't lock the target, you have no guarantee that the SBValue won't go + // invalid after you call this... + // Also, an SBValue could depend on data from one of the modules in the target, and those could go away + // independently of the target, for instance if a module is unloaded. But right now, neither SBValues + // nor ValueObjects know which modules they depend on. So I have no good way to make that check without + // tracking that in all the ValueObject subclasses. + TargetSP target_sp = m_valobj_sp->GetTargetSP(); + if (target_sp && target_sp->IsValid()) + return true; + else + return false; + } } lldb::ValueObjectSP @@ -120,6 +137,8 @@ public: Target *target = value_sp->GetTargetSP().get(); if (target) api_locker.Lock(target->GetAPIMutex()); + else + return ValueObjectSP(); ProcessSP process_sp(value_sp->GetProcessSP()); if (process_sp && !stop_locker.TryLock (&process_sp->GetRunLock())) @@ -276,7 +295,7 @@ SBValue::IsValid () // If this function ever changes to anything that does more than just // check if the opaque shared pointer is non NULL, then we need to update // all "if (m_opaque_sp)" code in this file. - return m_opaque_sp.get() != NULL && m_opaque_sp->GetRootSP().get() != NULL; + return m_opaque_sp.get() != NULL && m_opaque_sp->IsValid() && m_opaque_sp->GetRootSP().get() != NULL; } void diff --git a/source/Breakpoint/Breakpoint.cpp b/source/Breakpoint/Breakpoint.cpp index 32c0b1066f8e..5ce064fc41a0 100644 --- a/source/Breakpoint/Breakpoint.cpp +++ b/source/Breakpoint/Breakpoint.cpp @@ -45,14 +45,19 @@ Breakpoint::GetEventIdentifier () //---------------------------------------------------------------------- // Breakpoint constructor //---------------------------------------------------------------------- -Breakpoint::Breakpoint(Target &target, SearchFilterSP &filter_sp, BreakpointResolverSP &resolver_sp, bool hardware) : +Breakpoint::Breakpoint(Target &target, + SearchFilterSP &filter_sp, + BreakpointResolverSP &resolver_sp, + bool hardware, + bool resolve_indirect_symbols) : m_being_created(true), m_hardware(hardware), m_target (target), m_filter_sp (filter_sp), m_resolver_sp (resolver_sp), m_options (), - m_locations (*this) + m_locations (*this), + m_resolve_indirect_symbols(resolve_indirect_symbols) { m_being_created = false; } @@ -87,7 +92,7 @@ Breakpoint::GetTarget () const BreakpointLocationSP Breakpoint::AddLocation (const Address &addr, bool *new_location) { - return m_locations.AddLocation (addr, new_location); + return m_locations.AddLocation (addr, m_resolve_indirect_symbols, new_location); } BreakpointLocationSP diff --git a/source/Breakpoint/BreakpointLocation.cpp b/source/Breakpoint/BreakpointLocation.cpp index 5009e862d84b..2c75a11e9788 100644 --- a/source/Breakpoint/BreakpointLocation.cpp +++ b/source/Breakpoint/BreakpointLocation.cpp @@ -39,16 +39,29 @@ BreakpointLocation::BreakpointLocation Breakpoint &owner, const Address &addr, lldb::tid_t tid, - bool hardware + bool hardware, + bool check_for_resolver ) : StoppointLocation (loc_id, addr.GetOpcodeLoadAddress(&owner.GetTarget()), hardware), m_being_created(true), + m_should_resolve_indirect_functions (false), + m_is_reexported (false), + m_is_indirect (false), m_address (addr), m_owner (owner), m_options_ap (), m_bp_site_sp (), m_condition_mutex () { + if (check_for_resolver) + { + Symbol *symbol = m_address.CalculateSymbolContextSymbol(); + if (symbol && symbol->IsIndirect()) + { + SetShouldResolveIndirectFunctions (true); + } + } + SetThreadID (tid); m_being_created = false; } @@ -545,7 +558,10 @@ BreakpointLocation::GetDescription (Stream *s, lldb::DescriptionLevel level) if (level == lldb::eDescriptionLevelFull || level == eDescriptionLevelInitial) { - s->PutCString("where = "); + if (IsReExported()) + s->PutCString ("re-exported target = "); + else + s->PutCString("where = "); sc.DumpStopContext (s, m_owner.GetTarget().GetProcessSP().get(), m_address, false, true, false); } else @@ -584,7 +600,10 @@ BreakpointLocation::GetDescription (Stream *s, lldb::DescriptionLevel level) if (sc.symbol) { s->EOL(); - s->Indent("symbol = "); + if (IsReExported()) + s->Indent ("re-exported target = "); + else + s->Indent("symbol = "); s->PutCString(sc.symbol->GetMangled().GetName().AsCString("<unknown>")); } } @@ -612,6 +631,24 @@ BreakpointLocation::GetDescription (Stream *s, lldb::DescriptionLevel level) m_address.Dump(s, exe_scope, Address::DumpStyleLoadAddress, Address::DumpStyleFileAddress); else m_address.Dump(s, exe_scope, Address::DumpStyleLoadAddress, Address::DumpStyleModuleWithFileAddress); + + if (IsIndirect() && m_bp_site_sp) + { + Address resolved_address; + resolved_address.SetLoadAddress(m_bp_site_sp->GetLoadAddress(), target); + Symbol *resolved_symbol = resolved_address.CalculateSymbolContextSymbol(); + if (resolved_symbol) + { + if (level == eDescriptionLevelFull || level == eDescriptionLevelInitial) + s->Printf (", "); + else if (level == lldb::eDescriptionLevelVerbose) + { + s->EOL(); + s->Indent(); + } + s->Printf ("indirect target = %s", resolved_symbol->GetName().GetCString()); + } + } if (level == lldb::eDescriptionLevelVerbose) { diff --git a/source/Breakpoint/BreakpointLocationList.cpp b/source/Breakpoint/BreakpointLocationList.cpp index 18147deca3ec..917c776e75d2 100644 --- a/source/Breakpoint/BreakpointLocationList.cpp +++ b/source/Breakpoint/BreakpointLocationList.cpp @@ -19,8 +19,10 @@ #include "lldb/Core/ArchSpec.h" #include "lldb/Core/Module.h" #include "lldb/Core/Section.h" +#include "lldb/Target/SectionLoadList.h" #include "lldb/Target/Target.h" + using namespace lldb; using namespace lldb_private; @@ -39,12 +41,12 @@ BreakpointLocationList::~BreakpointLocationList() } BreakpointLocationSP -BreakpointLocationList::Create (const Address &addr) +BreakpointLocationList::Create (const Address &addr, bool resolve_indirect_symbols) { Mutex::Locker locker (m_mutex); // The location ID is just the size of the location list + 1 lldb::break_id_t bp_loc_id = ++m_next_id; - BreakpointLocationSP bp_loc_sp (new BreakpointLocation (bp_loc_id, m_owner, addr, LLDB_INVALID_THREAD_ID, m_owner.IsHardware())); + BreakpointLocationSP bp_loc_sp (new BreakpointLocation (bp_loc_id, m_owner, addr, LLDB_INVALID_THREAD_ID, m_owner.IsHardware(), resolve_indirect_symbols)); m_locations.push_back (bp_loc_sp); m_address_to_location[addr] = bp_loc_sp; return bp_loc_sp; @@ -245,7 +247,7 @@ BreakpointLocationList::GetDescription (Stream *s, lldb::DescriptionLevel level) } BreakpointLocationSP -BreakpointLocationList::AddLocation (const Address &addr, bool *new_location) +BreakpointLocationList::AddLocation (const Address &addr, bool resolve_indirect_symbols, bool *new_location) { Mutex::Locker locker (m_mutex); @@ -254,7 +256,7 @@ BreakpointLocationList::AddLocation (const Address &addr, bool *new_location) BreakpointLocationSP bp_loc_sp (FindByAddress(addr)); if (!bp_loc_sp) { - bp_loc_sp = Create (addr); + bp_loc_sp = Create (addr, resolve_indirect_symbols); if (bp_loc_sp) { bp_loc_sp->ResolveBreakpointSite(); diff --git a/source/Breakpoint/BreakpointResolverName.cpp b/source/Breakpoint/BreakpointResolverName.cpp index c82dd5ee050b..cf5d89cb7a8b 100644 --- a/source/Breakpoint/BreakpointResolverName.cpp +++ b/source/Breakpoint/BreakpointResolverName.cpp @@ -272,6 +272,8 @@ BreakpointResolverName::SearchCallback { if (func_list.GetContextAtIndex(i, sc)) { + bool is_reexported = false; + if (sc.block && sc.block->GetInlinedFunctionInfo()) { if (!sc.block->GetStartAddress(break_addr)) @@ -293,7 +295,10 @@ BreakpointResolverName::SearchCallback { const Symbol *actual_symbol = sc.symbol->ResolveReExportedSymbol(m_breakpoint->GetTarget()); if (actual_symbol) + { + is_reexported = true; break_addr = actual_symbol->GetAddress(); + } } else { @@ -313,6 +318,7 @@ BreakpointResolverName::SearchCallback if (filter.AddressPasses(break_addr)) { BreakpointLocationSP bp_loc_sp (m_breakpoint->AddLocation(break_addr, &new_location)); + bp_loc_sp->SetIsReExported(is_reexported); if (bp_loc_sp && new_location && !m_breakpoint->IsInternal()) { if (log) diff --git a/source/Commands/CommandObjectBreakpointCommand.cpp b/source/Commands/CommandObjectBreakpointCommand.cpp index e540461dadae..532d6cedc83e 100644 --- a/source/Commands/CommandObjectBreakpointCommand.cpp +++ b/source/Commands/CommandObjectBreakpointCommand.cpp @@ -16,6 +16,7 @@ #include "CommandObjectBreakpointCommand.h" #include "CommandObjectBreakpoint.h" +#include "lldb/Core/IOHandler.h" #include "lldb/Interpreter/CommandInterpreter.h" #include "lldb/Interpreter/CommandReturnObject.h" #include "lldb/Target/Target.h" @@ -34,7 +35,9 @@ using namespace lldb_private; //------------------------------------------------------------------------- -class CommandObjectBreakpointCommandAdd : public CommandObjectParsed +class CommandObjectBreakpointCommandAdd : + public CommandObjectParsed, + public IOHandlerDelegateMultiline { public: @@ -43,6 +46,7 @@ public: "add", "Add a set of commands to a breakpoint, to be executed whenever the breakpoint is hit.", NULL), + IOHandlerDelegateMultiline ("DONE", IOHandlerDelegate::Completion::LLDBCommand), m_options (interpreter) { SetHelpLong ( @@ -207,42 +211,47 @@ one command per line.\n" ); return &m_options; } - void - CollectDataForBreakpointCommandCallback (BreakpointOptions *bp_options, - CommandReturnObject &result) + virtual void + IOHandlerActivated (IOHandler &io_handler) { - InputReaderSP reader_sp (new InputReader(m_interpreter.GetDebugger())); - std::unique_ptr<BreakpointOptions::CommandData> data_ap(new BreakpointOptions::CommandData()); - if (reader_sp && data_ap.get()) + StreamFileSP output_sp(io_handler.GetOutputStreamFile()); + if (output_sp) { - BatonSP baton_sp (new BreakpointOptions::CommandBaton (data_ap.release())); - bp_options->SetCallback (BreakpointOptionsCallbackFunction, baton_sp); - - Error err (reader_sp->Initialize (CommandObjectBreakpointCommandAdd::GenerateBreakpointCommandCallback, - bp_options, // baton - eInputReaderGranularityLine, // token size, to pass to callback function - "DONE", // end token - "> ", // prompt - true)); // echo input - if (err.Success()) - { - m_interpreter.GetDebugger().PushInputReader (reader_sp); - result.SetStatus (eReturnStatusSuccessFinishNoResult); - } - else - { - result.AppendError (err.AsCString()); - result.SetStatus (eReturnStatusFailed); - } + output_sp->PutCString(g_reader_instructions); + output_sp->Flush(); } - else + } + + + virtual void + IOHandlerInputComplete (IOHandler &io_handler, std::string &line) + { + io_handler.SetIsDone(true); + + BreakpointOptions *bp_options = (BreakpointOptions *) io_handler.GetUserData(); + if (bp_options) { - result.AppendError("out of memory"); - result.SetStatus (eReturnStatusFailed); + std::unique_ptr<BreakpointOptions::CommandData> data_ap(new BreakpointOptions::CommandData()); + if (data_ap.get()) + { + data_ap->user_source.SplitIntoLines (line.c_str(), line.size()); + BatonSP baton_sp (new BreakpointOptions::CommandBaton (data_ap.release())); + bp_options->SetCallback (BreakpointOptionsCallbackFunction, baton_sp); + } } } + void + CollectDataForBreakpointCommandCallback (BreakpointOptions *bp_options, + CommandReturnObject &result) + { + m_interpreter.GetLLDBCommandsFromIOHandler ("> ", // Prompt + *this, // IOHandlerDelegate + true, // Run IOHandler in async mode + bp_options); // Baton for the "io_handler" that will be passed back into our IOHandlerDelegate functions + } + /// Set a one-liner as the callback for the breakpoint. void SetBreakpointCommandCallback (BreakpointOptions *bp_options, @@ -262,93 +271,6 @@ one command per line.\n" ); return; } - - static size_t - GenerateBreakpointCommandCallback (void *baton, - InputReader &reader, - lldb::InputReaderAction notification, - const char *bytes, - size_t bytes_len) - { - StreamSP out_stream = reader.GetDebugger().GetAsyncOutputStream(); - bool batch_mode = reader.GetDebugger().GetCommandInterpreter().GetBatchCommandMode(); - - switch (notification) - { - case eInputReaderActivate: - if (!batch_mode) - { - out_stream->Printf ("%s\n", g_reader_instructions); - if (reader.GetPrompt()) - out_stream->Printf ("%s", reader.GetPrompt()); - out_stream->Flush(); - } - break; - - case eInputReaderDeactivate: - break; - - case eInputReaderReactivate: - if (reader.GetPrompt() && !batch_mode) - { - out_stream->Printf ("%s", reader.GetPrompt()); - out_stream->Flush(); - } - break; - - case eInputReaderAsynchronousOutputWritten: - break; - - case eInputReaderGotToken: - if (bytes && bytes_len && baton) - { - BreakpointOptions *bp_options = (BreakpointOptions *) baton; - if (bp_options) - { - Baton *bp_options_baton = bp_options->GetBaton(); - if (bp_options_baton) - ((BreakpointOptions::CommandData *)bp_options_baton->m_data)->user_source.AppendString (bytes, bytes_len); - } - } - if (!reader.IsDone() && reader.GetPrompt() && !batch_mode) - { - out_stream->Printf ("%s", reader.GetPrompt()); - out_stream->Flush(); - } - break; - - case eInputReaderInterrupt: - { - // Finish, and cancel the breakpoint command. - reader.SetIsDone (true); - BreakpointOptions *bp_options = (BreakpointOptions *) baton; - if (bp_options) - { - Baton *bp_options_baton = bp_options->GetBaton (); - if (bp_options_baton) - { - ((BreakpointOptions::CommandData *) bp_options_baton->m_data)->user_source.Clear(); - ((BreakpointOptions::CommandData *) bp_options_baton->m_data)->script_source.clear(); - } - } - if (!batch_mode) - { - out_stream->Printf ("Warning: No command attached to breakpoint.\n"); - out_stream->Flush(); - } - } - break; - - case eInputReaderEndOfFile: - reader.SetIsDone (true); - break; - - case eInputReaderDone: - break; - } - - return bytes_len; - } static bool BreakpointOptionsCallbackFunction (void *baton, @@ -623,7 +545,7 @@ private: }; const char * -CommandObjectBreakpointCommandAdd::g_reader_instructions = "Enter your debugger command(s). Type 'DONE' to end."; +CommandObjectBreakpointCommandAdd::g_reader_instructions = "Enter your debugger command(s). Type 'DONE' to end.\n"; // FIXME: "script-type" needs to have its contents determined dynamically, so somebody can add a new scripting // language to lldb and have it pickable here without having to change this enumeration by hand and rebuild lldb proper. diff --git a/source/Commands/CommandObjectBreakpointCommand.h b/source/Commands/CommandObjectBreakpointCommand.h index afedb7602cdd..e91790779510 100644 --- a/source/Commands/CommandObjectBreakpointCommand.h +++ b/source/Commands/CommandObjectBreakpointCommand.h @@ -19,7 +19,6 @@ #include "lldb/lldb-types.h" #include "lldb/Interpreter/Options.h" -#include "lldb/Core/InputReader.h" #include "lldb/Interpreter/CommandObject.h" #include "lldb/Interpreter/CommandReturnObject.h" #include "lldb/Interpreter/CommandObjectMultiword.h" diff --git a/source/Commands/CommandObjectCommands.cpp b/source/Commands/CommandObjectCommands.cpp index 6824ead0d9e0..7bfdec094d6c 100644 --- a/source/Commands/CommandObjectCommands.cpp +++ b/source/Commands/CommandObjectCommands.cpp @@ -18,8 +18,7 @@ // Project includes #include "lldb/Core/Debugger.h" -#include "lldb/Core/InputReader.h" -#include "lldb/Core/InputReaderEZ.h" +#include "lldb/Core/IOHandler.h" #include "lldb/Core/StringList.h" #include "lldb/Interpreter/Args.h" #include "lldb/Interpreter/CommandHistory.h" @@ -309,7 +308,9 @@ protected: CommandOptions (CommandInterpreter &interpreter) : Options (interpreter), - m_stop_on_error (true) + m_stop_on_error (true), + m_silent_run (false), + m_stop_on_continue (true) { } @@ -321,23 +322,21 @@ protected: { Error error; const int short_option = m_getopt_table[option_idx].val; - bool success; switch (short_option) { case 'e': error = m_stop_on_error.SetValueFromCString(option_arg); break; + case 'c': - m_stop_on_continue = Args::StringToBoolean(option_arg, true, &success); - if (!success) - error.SetErrorStringWithFormat("invalid value for stop-on-continue: %s", option_arg); + error = m_stop_on_continue.SetValueFromCString(option_arg); break; + case 's': - m_silent_run = Args::StringToBoolean(option_arg, true, &success); - if (!success) - error.SetErrorStringWithFormat("invalid value for silent-run: %s", option_arg); + error = m_silent_run.SetValueFromCString(option_arg); break; + default: error.SetErrorStringWithFormat ("unrecognized option '%c'", short_option); break; @@ -350,8 +349,8 @@ protected: OptionParsingStarting () { m_stop_on_error.Clear(); - m_silent_run = false; - m_stop_on_continue = true; + m_silent_run.Clear(); + m_stop_on_continue.Clear(); } const OptionDefinition* @@ -367,8 +366,8 @@ protected: // Instance variables to hold the values for command options. OptionValueBoolean m_stop_on_error; - bool m_silent_run; - bool m_stop_on_continue; + OptionValueBoolean m_silent_run; + OptionValueBoolean m_stop_on_continue; }; bool @@ -379,22 +378,40 @@ protected: { const char *filename = command.GetArgumentAtIndex(0); - result.AppendMessageWithFormat ("Executing commands in '%s'.\n", filename); - FileSpec cmd_file (filename, true); ExecutionContext *exe_ctx = NULL; // Just use the default context. - bool echo_commands = !m_options.m_silent_run; - bool print_results = true; - bool stop_on_error = m_options.m_stop_on_error.OptionWasSet() ? (bool)m_options.m_stop_on_error : m_interpreter.GetStopCmdSourceOnError(); - - m_interpreter.HandleCommandsFromFile (cmd_file, - exe_ctx, - m_options.m_stop_on_continue, - stop_on_error, - echo_commands, - print_results, - eLazyBoolCalculate, - result); + + // If any options were set, then use them + if (m_options.m_stop_on_error.OptionWasSet() || + m_options.m_silent_run.OptionWasSet() || + m_options.m_stop_on_continue.OptionWasSet()) + { + // Use user set settings + LazyBool print_command = m_options.m_silent_run.GetCurrentValue() ? eLazyBoolNo : eLazyBoolYes; + m_interpreter.HandleCommandsFromFile (cmd_file, + exe_ctx, + m_options.m_stop_on_continue.GetCurrentValue() ? eLazyBoolYes : eLazyBoolNo, // Stop on continue + m_options.m_stop_on_error.GetCurrentValue() ? eLazyBoolYes : eLazyBoolNo, // Stop on error + print_command, // Echo command + print_command, // Print command output + eLazyBoolCalculate, // Add to history + result); + + } + else + { + // No options were set, inherit any settings from nested "command source" commands, + // or set to sane default settings... + m_interpreter.HandleCommandsFromFile (cmd_file, + exe_ctx, + eLazyBoolCalculate, // Stop on continue + eLazyBoolCalculate, // Stop on error + eLazyBoolCalculate, // Echo command + eLazyBoolCalculate, // Print command output + eLazyBoolCalculate, // Add to history + result); + + } } else { @@ -423,7 +440,7 @@ CommandObjectCommandsSource::CommandOptions::g_option_table[] = static const char *g_python_command_instructions = "Enter your Python command(s). Type 'DONE' to end.\n" "You must define a Python function with this signature:\n" - "def my_command_impl(debugger, args, result, internal_dict):"; + "def my_command_impl(debugger, args, result, internal_dict):\n"; class CommandObjectCommandsAlias : public CommandObjectRaw @@ -856,7 +873,9 @@ protected: //------------------------------------------------------------------------- #pragma mark CommandObjectCommandsAddRegex -class CommandObjectCommandsAddRegex : public CommandObjectParsed +class CommandObjectCommandsAddRegex : + public CommandObjectParsed, + public IOHandlerDelegate { public: CommandObjectCommandsAddRegex (CommandInterpreter &interpreter) : @@ -864,6 +883,7 @@ public: "command regex", "Allow the user to create a regular expression command.", "command regex <cmd-name> [s/<regex>/<subst>/ ...]"), + IOHandlerDelegate(IOHandlerDelegate::Completion::LLDBCommand), m_options (interpreter) { SetHelpLong( @@ -899,6 +919,97 @@ public: protected: + + virtual void + IOHandlerActivated (IOHandler &io_handler) + { + StreamFileSP output_sp(io_handler.GetOutputStreamFile()); + if (output_sp) + { + output_sp->PutCString("Enter one of more sed substitution commands in the form: 's/<regex>/<subst>/'.\nTerminate the substitution list with an empty line.\n"); + output_sp->Flush(); + } + } + + virtual void + IOHandlerInputComplete (IOHandler &io_handler, std::string &data) + { + io_handler.SetIsDone(true); + if (m_regex_cmd_ap.get()) + { + StringList lines; + if (lines.SplitIntoLines (data)) + { + const size_t num_lines = lines.GetSize(); + bool check_only = false; + for (size_t i=0; i<num_lines; ++i) + { + printf ("regex[%zu] = %s\n", i, lines[i].c_str()); + llvm::StringRef bytes_strref (lines[i]); + Error error = AppendRegexSubstitution (bytes_strref, check_only); + if (error.Fail()) + { + if (!m_interpreter.GetDebugger().GetCommandInterpreter().GetBatchCommandMode()) + { + StreamSP out_stream = m_interpreter.GetDebugger().GetAsyncOutputStream(); + out_stream->Printf("error: %s\n", error.AsCString()); + } + } + } + } + if (m_regex_cmd_ap->HasRegexEntries()) + { + CommandObjectSP cmd_sp (m_regex_cmd_ap.release()); + m_interpreter.AddCommand(cmd_sp->GetCommandName(), cmd_sp, true); + } + } + } + + virtual LineStatus + IOHandlerLinesUpdated (IOHandler &io_handler, + StringList &lines, + uint32_t line_idx, + Error &error) + { + if (line_idx == UINT32_MAX) + { + // Return true to indicate we are done getting lines (this + // is a "fake" line - the real terminating blank line was + // removed during a previous call with the code below) + error.Clear(); + return LineStatus::Done; + } + else + { + const size_t num_lines = lines.GetSize(); + if (line_idx + 1 == num_lines) + { + // The last line was edited, if this line is empty, then we are done + // getting our multiple lines. + if (lines[line_idx].empty()) + { + // Remove the last empty line from "lines" so it doesn't appear + // in our final expression and return true to indicate we are done + // getting lines + lines.PopBack(); + return LineStatus::Done; + } + } + // Check the current line to make sure it is formatted correctly + bool check_only = true; + llvm::StringRef regex_sed(lines[line_idx]); + error = AppendRegexSubstitution (regex_sed, check_only); + if (error.Fail()) + { + return LineStatus::Error; + } + else + { + return LineStatus::Success; + } + } + } + bool DoExecute (Args& command, CommandReturnObject &result) { @@ -920,21 +1031,18 @@ protected: if (argc == 1) { - InputReaderSP reader_sp (new InputReader(m_interpreter.GetDebugger())); - if (reader_sp) + Debugger &debugger = m_interpreter.GetDebugger(); + const bool multiple_lines = true; // Get multiple lines + IOHandlerSP io_handler_sp (new IOHandlerEditline (debugger, + "lldb", // Name of input reader for history + "\033[K> ", // Prompt and clear line + multiple_lines, + *this)); + + if (io_handler_sp) { - error =reader_sp->Initialize (CommandObjectCommandsAddRegex::InputReaderCallback, - this, // baton - eInputReaderGranularityLine, // token size, to pass to callback function - NULL, // end token - "> ", // prompt - true); // echo input - if (error.Success()) - { - m_interpreter.GetDebugger().PushInputReader (reader_sp); - result.SetStatus (eReturnStatusSuccessFinishNoResult); - return true; - } + debugger.PushIOHandler(io_handler_sp); + result.SetStatus (eReturnStatusSuccessFinishNoResult); } } else @@ -942,7 +1050,8 @@ protected: for (size_t arg_idx = 1; arg_idx < argc; ++arg_idx) { llvm::StringRef arg_strref (command.GetArgumentAtIndex(arg_idx)); - error = AppendRegexSubstitution (arg_strref); + bool check_only = false; + error = AppendRegexSubstitution (arg_strref, check_only); if (error.Fail()) break; } @@ -963,7 +1072,7 @@ protected: } Error - AppendRegexSubstitution (const llvm::StringRef ®ex_sed) + AppendRegexSubstitution (const llvm::StringRef ®ex_sed, bool check_only) { Error error; @@ -1053,10 +1162,14 @@ protected: regex_sed.data()); return error; } - std::string regex(regex_sed.substr(first_separator_char_pos + 1, second_separator_char_pos - first_separator_char_pos - 1)); - std::string subst(regex_sed.substr(second_separator_char_pos + 1, third_separator_char_pos - second_separator_char_pos - 1)); - m_regex_cmd_ap->AddRegexCommand (regex.c_str(), - subst.c_str()); + + if (check_only == false) + { + std::string regex(regex_sed.substr(first_separator_char_pos + 1, second_separator_char_pos - first_separator_char_pos - 1)); + std::string subst(regex_sed.substr(second_separator_char_pos + 1, third_separator_char_pos - second_separator_char_pos - 1)); + m_regex_cmd_ap->AddRegexCommand (regex.c_str(), + subst.c_str()); + } return error; } @@ -1073,89 +1186,6 @@ protected: } } - void - InputReaderDidCancel() - { - m_regex_cmd_ap.reset(); - } - - static size_t - InputReaderCallback (void *baton, - InputReader &reader, - lldb::InputReaderAction notification, - const char *bytes, - size_t bytes_len) - { - CommandObjectCommandsAddRegex *add_regex_cmd = (CommandObjectCommandsAddRegex *) baton; - bool batch_mode = reader.GetDebugger().GetCommandInterpreter().GetBatchCommandMode(); - - switch (notification) - { - case eInputReaderActivate: - if (!batch_mode) - { - StreamSP out_stream = reader.GetDebugger().GetAsyncOutputStream (); - out_stream->Printf("%s\n", "Enter regular expressions in the form 's/<regex>/<subst>/' and terminate with an empty line:"); - out_stream->Flush(); - } - break; - case eInputReaderReactivate: - break; - - case eInputReaderDeactivate: - break; - - case eInputReaderAsynchronousOutputWritten: - break; - - case eInputReaderGotToken: - while (bytes_len > 0 && (bytes[bytes_len-1] == '\r' || bytes[bytes_len-1] == '\n')) - --bytes_len; - if (bytes_len == 0) - reader.SetIsDone(true); - else if (bytes) - { - llvm::StringRef bytes_strref (bytes, bytes_len); - Error error (add_regex_cmd->AppendRegexSubstitution (bytes_strref)); - if (error.Fail()) - { - if (!batch_mode) - { - StreamSP out_stream = reader.GetDebugger().GetAsyncOutputStream(); - out_stream->Printf("error: %s\n", error.AsCString()); - out_stream->Flush(); - } - add_regex_cmd->InputReaderDidCancel (); - reader.SetIsDone (true); - } - } - break; - - case eInputReaderInterrupt: - { - reader.SetIsDone (true); - if (!batch_mode) - { - StreamSP out_stream = reader.GetDebugger().GetAsyncOutputStream(); - out_stream->PutCString("Regular expression command creations was cancelled.\n"); - out_stream->Flush(); - } - add_regex_cmd->InputReaderDidCancel (); - } - break; - - case eInputReaderEndOfFile: - reader.SetIsDone (true); - break; - - case eInputReaderDone: - add_regex_cmd->AddRegexCommandToInterpreter(); - break; - } - - return bytes_len; - } - private: std::unique_ptr<CommandObjectRegexCommand> m_regex_cmd_ap; @@ -1526,7 +1556,9 @@ CommandObjectCommandsScriptImport::CommandOptions::g_option_table[] = // CommandObjectCommandsScriptAdd //------------------------------------------------------------------------- -class CommandObjectCommandsScriptAdd : public CommandObjectParsed +class CommandObjectCommandsScriptAdd : + public CommandObjectParsed, + public IOHandlerDelegateMultiline { public: CommandObjectCommandsScriptAdd(CommandInterpreter &interpreter) : @@ -1534,6 +1566,7 @@ public: "command script add", "Add a scripted function as an LLDB command.", NULL), + IOHandlerDelegateMultiline ("DONE"), m_options (interpreter) { CommandArgumentEntry arg1; @@ -1567,7 +1600,7 @@ protected: public: CommandOptions (CommandInterpreter &interpreter) : - Options (interpreter) + Options (interpreter) { } @@ -1586,7 +1619,7 @@ protected: m_funct_name = std::string(option_arg); break; case 's': - m_synchronous = (ScriptedCommandSynchronicity) Args::StringToOptionEnum(option_arg, g_option_table[option_idx].enum_values, 0, error); + m_synchronicity = (ScriptedCommandSynchronicity) Args::StringToOptionEnum(option_arg, g_option_table[option_idx].enum_values, 0, error); if (!error.Success()) error.SetErrorStringWithFormat ("unrecognized value for synchronicity '%s'", option_arg); break; @@ -1602,7 +1635,7 @@ protected: OptionParsingStarting () { m_funct_name = ""; - m_synchronous = eScriptedCommandSynchronicitySynchronous; + m_synchronicity = eScriptedCommandSynchronicitySynchronous; } const OptionDefinition* @@ -1618,128 +1651,81 @@ protected: // Instance variables to hold the values for command options. std::string m_funct_name; - ScriptedCommandSynchronicity m_synchronous; + ScriptedCommandSynchronicity m_synchronicity; }; -private: - class PythonAliasReader : public InputReaderEZ + virtual void + IOHandlerActivated (IOHandler &io_handler) { - private: - CommandInterpreter& m_interpreter; - std::string m_cmd_name; - ScriptedCommandSynchronicity m_synchronous; - StringList m_user_input; - DISALLOW_COPY_AND_ASSIGN (PythonAliasReader); - public: - PythonAliasReader(Debugger& debugger, - CommandInterpreter& interpreter, - std::string cmd_name, - ScriptedCommandSynchronicity synch) : - InputReaderEZ(debugger), - m_interpreter(interpreter), - m_cmd_name(cmd_name), - m_synchronous(synch), - m_user_input() - {} - - virtual - ~PythonAliasReader() + StreamFileSP output_sp(io_handler.GetOutputStreamFile()); + if (output_sp) { + output_sp->PutCString(g_python_command_instructions); + output_sp->Flush(); } + } + + + virtual void + IOHandlerInputComplete (IOHandler &io_handler, std::string &data) + { + StreamFileSP error_sp = io_handler.GetErrorStreamFile(); - virtual void ActivateHandler(HandlerData& data) + ScriptInterpreter *interpreter = m_interpreter.GetScriptInterpreter(); + if (interpreter) { - StreamSP out_stream = data.GetOutStream(); - bool batch_mode = data.GetBatchMode(); - if (!batch_mode) - { - out_stream->Printf ("%s\n", g_python_command_instructions); - if (data.reader.GetPrompt()) - out_stream->Printf ("%s", data.reader.GetPrompt()); - out_stream->Flush(); - } - } - virtual void ReactivateHandler(HandlerData& data) - { - StreamSP out_stream = data.GetOutStream(); - bool batch_mode = data.GetBatchMode(); - if (data.reader.GetPrompt() && !batch_mode) + StringList lines; + lines.SplitIntoLines(data); + if (lines.GetSize() > 0) { - out_stream->Printf ("%s", data.reader.GetPrompt()); - out_stream->Flush(); - } - } - virtual void GotTokenHandler(HandlerData& data) - { - StreamSP out_stream = data.GetOutStream(); - bool batch_mode = data.GetBatchMode(); - if (data.bytes && data.bytes_len) - { - m_user_input.AppendString(data.bytes, data.bytes_len); - } - if (!data.reader.IsDone() && data.reader.GetPrompt() && !batch_mode) - { - out_stream->Printf ("%s", data.reader.GetPrompt()); - out_stream->Flush(); + std::string funct_name_str; + if (interpreter->GenerateScriptAliasFunction (lines, funct_name_str)) + { + if (funct_name_str.empty()) + { + error_sp->Printf ("error: unable to obtain a function name, didn't add python command.\n"); + error_sp->Flush(); + } + else + { + // everything should be fine now, let's add this alias + + CommandObjectSP command_obj_sp(new CommandObjectPythonFunction (m_interpreter, + m_cmd_name, + funct_name_str.c_str(), + m_synchronicity)); + + if (!m_interpreter.AddUserCommand(m_cmd_name, command_obj_sp, true)) + { + error_sp->Printf ("error: unable to add selected command, didn't add python command.\n"); + error_sp->Flush(); + } + } + } + else + { + error_sp->Printf ("error: unable to create function, didn't add python command.\n"); + error_sp->Flush(); + } } - } - virtual void InterruptHandler(HandlerData& data) - { - StreamSP out_stream = data.GetOutStream(); - bool batch_mode = data.GetBatchMode(); - data.reader.SetIsDone (true); - if (!batch_mode) + else { - out_stream->Printf ("Warning: No script attached.\n"); - out_stream->Flush(); + error_sp->Printf ("error: empty function, didn't add python command.\n"); + error_sp->Flush(); } } - virtual void EOFHandler(HandlerData& data) + else { - data.reader.SetIsDone (true); + error_sp->Printf ("error: script interpreter missing, didn't add python command.\n"); + error_sp->Flush(); } - virtual void DoneHandler(HandlerData& data) - { - StreamSP out_stream = data.GetOutStream(); - - ScriptInterpreter *interpreter = data.reader.GetDebugger().GetCommandInterpreter().GetScriptInterpreter(); - if (!interpreter) - { - out_stream->Printf ("Script interpreter missing: no script attached.\n"); - out_stream->Flush(); - return; - } - std::string funct_name_str; - if (!interpreter->GenerateScriptAliasFunction (m_user_input, - funct_name_str)) - { - out_stream->Printf ("Unable to create function: no script attached.\n"); - out_stream->Flush(); - return; - } - if (funct_name_str.empty()) - { - out_stream->Printf ("Unable to obtain a function name: no script attached.\n"); - out_stream->Flush(); - return; - } - // everything should be fine now, let's add this alias - - CommandObjectSP command_obj_sp(new CommandObjectPythonFunction(m_interpreter, - m_cmd_name, - funct_name_str.c_str(), - m_synchronous)); - - if (!m_interpreter.AddUserCommand(m_cmd_name, command_obj_sp, true)) - { - out_stream->Printf ("Unable to add selected command: no script attached.\n"); - out_stream->Flush(); - return; - } - } - }; - + + io_handler.SetIsDone(true); + + + } + protected: bool DoExecute (Args& command, CommandReturnObject &result) @@ -1761,45 +1747,24 @@ protected: return false; } - std::string cmd_name = command.GetArgumentAtIndex(0); + // Store the command name and synchronicity in case we get multi-line input + m_cmd_name = command.GetArgumentAtIndex(0); + m_synchronicity = m_options.m_synchronicity; if (m_options.m_funct_name.empty()) { - InputReaderSP reader_sp (new PythonAliasReader (m_interpreter.GetDebugger(), - m_interpreter, - cmd_name, - m_options.m_synchronous)); - - if (reader_sp) - { - - InputReaderEZ::InitializationParameters ipr; - - Error err (reader_sp->Initialize (ipr.SetBaton(NULL).SetPrompt(" "))); - if (err.Success()) - { - m_interpreter.GetDebugger().PushInputReader (reader_sp); - result.SetStatus (eReturnStatusSuccessFinishNoResult); - } - else - { - result.AppendError (err.AsCString()); - result.SetStatus (eReturnStatusFailed); - } - } - else - { - result.AppendError("out of memory"); - result.SetStatus (eReturnStatusFailed); - } + m_interpreter.GetPythonCommandsFromIOHandler (" ", // Prompt + *this, // IOHandlerDelegate + true, // Run IOHandler in async mode + NULL); // Baton for the "io_handler" that will be passed back into our IOHandlerDelegate functions } else { CommandObjectSP new_cmd(new CommandObjectPythonFunction(m_interpreter, - cmd_name, + m_cmd_name, m_options.m_funct_name, - m_options.m_synchronous)); - if (m_interpreter.AddUserCommand(cmd_name, new_cmd, true)) + m_synchronicity)); + if (m_interpreter.AddUserCommand(m_cmd_name, new_cmd, true)) { result.SetStatus (eReturnStatusSuccessFinishNoResult); } @@ -1815,6 +1780,8 @@ protected: } CommandOptions m_options; + std::string m_cmd_name; + ScriptedCommandSynchronicity m_synchronicity; }; static OptionEnumValueElement g_script_synchro_type[] = diff --git a/source/Commands/CommandObjectDisassemble.cpp b/source/Commands/CommandObjectDisassemble.cpp index fc148b1899f6..f9c683b364ce 100644 --- a/source/Commands/CommandObjectDisassemble.cpp +++ b/source/Commands/CommandObjectDisassemble.cpp @@ -27,6 +27,7 @@ #include "lldb/Symbol/Function.h" #include "lldb/Symbol/Symbol.h" #include "lldb/Target/Process.h" +#include "lldb/Target/SectionLoadList.h" #include "lldb/Target/StackFrame.h" #include "lldb/Target/Target.h" diff --git a/source/Commands/CommandObjectExpression.cpp b/source/Commands/CommandObjectExpression.cpp index 5ca44ff920d6..c772a2e58912 100644 --- a/source/Commands/CommandObjectExpression.cpp +++ b/source/Commands/CommandObjectExpression.cpp @@ -17,7 +17,6 @@ // Project includes #include "lldb/Interpreter/Args.h" #include "lldb/Core/Value.h" -#include "lldb/Core/InputReader.h" #include "lldb/Core/ValueObjectVariable.h" #include "lldb/DataFormatters/ValueObjectPrinter.h" #include "lldb/Expression/ClangExpressionVariable.h" @@ -197,6 +196,7 @@ CommandObjectExpression::CommandObjectExpression (CommandInterpreter &interprete "Evaluate a C/ObjC/C++ expression in the current program context, using user defined variables and variables currently in scope.", NULL, eFlagProcessMustBePaused | eFlagTryTargetAPILock), + IOHandlerDelegate (IOHandlerDelegate::Completion::Expression), m_option_group (interpreter), m_format_options (eFormatDefault), m_command_options (), @@ -254,87 +254,6 @@ CommandObjectExpression::GetOptions () return &m_option_group; } -size_t -CommandObjectExpression::MultiLineExpressionCallback -( - void *baton, - InputReader &reader, - lldb::InputReaderAction notification, - const char *bytes, - size_t bytes_len -) -{ - CommandObjectExpression *cmd_object_expr = (CommandObjectExpression *) baton; - bool batch_mode = reader.GetDebugger().GetCommandInterpreter().GetBatchCommandMode(); - - switch (notification) - { - case eInputReaderActivate: - if (!batch_mode) - { - StreamSP async_strm_sp(reader.GetDebugger().GetAsyncOutputStream()); - if (async_strm_sp) - { - async_strm_sp->PutCString("Enter expressions, then terminate with an empty line to evaluate:\n"); - async_strm_sp->Flush(); - } - } - // Fall through - case eInputReaderReactivate: - break; - - case eInputReaderDeactivate: - break; - - case eInputReaderAsynchronousOutputWritten: - break; - - case eInputReaderGotToken: - ++cmd_object_expr->m_expr_line_count; - if (bytes && bytes_len) - { - cmd_object_expr->m_expr_lines.append (bytes, bytes_len + 1); - } - - if (bytes_len == 0) - reader.SetIsDone(true); - break; - - case eInputReaderInterrupt: - cmd_object_expr->m_expr_lines.clear(); - reader.SetIsDone (true); - if (!batch_mode) - { - StreamSP async_strm_sp (reader.GetDebugger().GetAsyncOutputStream()); - if (async_strm_sp) - { - async_strm_sp->PutCString("Expression evaluation cancelled.\n"); - async_strm_sp->Flush(); - } - } - break; - - case eInputReaderEndOfFile: - reader.SetIsDone (true); - break; - - case eInputReaderDone: - if (cmd_object_expr->m_expr_lines.size() > 0) - { - StreamSP output_stream = reader.GetDebugger().GetAsyncOutputStream(); - StreamSP error_stream = reader.GetDebugger().GetAsyncErrorStream(); - cmd_object_expr->EvaluateExpression (cmd_object_expr->m_expr_lines.c_str(), - output_stream.get(), - error_stream.get()); - output_stream->Flush(); - error_stream->Flush(); - } - break; - } - - return bytes_len; -} - bool CommandObjectExpression::EvaluateExpression ( @@ -373,6 +292,8 @@ CommandObjectExpression::EvaluateExpression if (m_command_options.timeout > 0) options.SetTimeoutUsec(m_command_options.timeout); + else + options.SetTimeoutUsec(0); exe_results = target->EvaluateExpression (expr, exe_ctx.GetFramePtr(), @@ -443,6 +364,48 @@ CommandObjectExpression::EvaluateExpression return true; } +void +CommandObjectExpression::IOHandlerInputComplete (IOHandler &io_handler, std::string &line) +{ + io_handler.SetIsDone(true); +// StreamSP output_stream = io_handler.GetDebugger().GetAsyncOutputStream(); +// StreamSP error_stream = io_handler.GetDebugger().GetAsyncErrorStream(); + StreamFileSP output_sp(io_handler.GetOutputStreamFile()); + StreamFileSP error_sp(io_handler.GetErrorStreamFile()); + + EvaluateExpression (line.c_str(), + output_sp.get(), + error_sp.get()); + if (output_sp) + output_sp->Flush(); + if (error_sp) + error_sp->Flush(); +} + +LineStatus +CommandObjectExpression::IOHandlerLinesUpdated (IOHandler &io_handler, + StringList &lines, + uint32_t line_idx, + Error &error) +{ + if (line_idx == UINT32_MAX) + { + // Remove the last line from "lines" so it doesn't appear + // in our final expression + lines.PopBack(); + error.Clear(); + return LineStatus::Done; + } + else if (line_idx + 1 == lines.GetSize()) + { + // The last line was edited, if this line is empty, then we are done + // getting our multiple lines. + if (lines[line_idx].empty()) + return LineStatus::Done; + } + return LineStatus::Success; +} + bool CommandObjectExpression::DoExecute ( @@ -459,31 +422,21 @@ CommandObjectExpression::DoExecute m_expr_lines.clear(); m_expr_line_count = 0; - InputReaderSP reader_sp (new InputReader(m_interpreter.GetDebugger())); - if (reader_sp) - { - Error err (reader_sp->Initialize (CommandObjectExpression::MultiLineExpressionCallback, - this, // baton - eInputReaderGranularityLine, // token size, to pass to callback function - NULL, // end token - NULL, // prompt - true)); // echo input - if (err.Success()) - { - m_interpreter.GetDebugger().PushInputReader (reader_sp); - result.SetStatus (eReturnStatusSuccessFinishNoResult); - } - else - { - result.AppendError (err.AsCString()); - result.SetStatus (eReturnStatusFailed); - } - } - else + Debugger &debugger = GetCommandInterpreter().GetDebugger(); + const bool multiple_lines = true; // Get multiple lines + IOHandlerSP io_handler_sp (new IOHandlerEditline (debugger, + "lldb-expr", // Name of input reader for history + NULL, // No prompt + multiple_lines, + *this)); + + StreamFileSP output_sp(io_handler_sp->GetOutputStreamFile()); + if (output_sp) { - result.AppendError("out of memory"); - result.SetStatus (eReturnStatusFailed); + output_sp->PutCString("Enter expressions, then terminate with an empty line to evaluate:\n"); + output_sp->Flush(); } + debugger.PushIOHandler(io_handler_sp); return result.Succeeded(); } diff --git a/source/Commands/CommandObjectExpression.h b/source/Commands/CommandObjectExpression.h index e0703a22a4cc..c943f0e8023d 100644 --- a/source/Commands/CommandObjectExpression.h +++ b/source/Commands/CommandObjectExpression.h @@ -14,6 +14,7 @@ // C++ Includes // Other libraries and framework includes // Project includes +#include "lldb/Core/IOHandler.h" #include "lldb/Interpreter/CommandObject.h" #include "lldb/Interpreter/OptionGroupFormat.h" #include "lldb/Interpreter/OptionGroupValueObjectDisplay.h" @@ -21,7 +22,9 @@ namespace lldb_private { -class CommandObjectExpression : public CommandObjectRaw +class CommandObjectExpression : + public CommandObjectRaw, + public IOHandlerDelegate { public: @@ -71,17 +74,23 @@ public: GetOptions (); protected: + + //------------------------------------------------------------------ + // IOHandler::Delegate functions + //------------------------------------------------------------------ + virtual void + IOHandlerInputComplete (IOHandler &io_handler, + std::string &line); + + virtual LineStatus + IOHandlerLinesUpdated (IOHandler &io_handler, + StringList &lines, + uint32_t line_idx, + Error &error); virtual bool DoExecute (const char *command, CommandReturnObject &result); - static size_t - MultiLineExpressionCallback (void *baton, - InputReader &reader, - lldb::InputReaderAction notification, - const char *bytes, - size_t bytes_len); - bool EvaluateExpression (const char *expr, Stream *output_stream, diff --git a/source/Commands/CommandObjectGUI.cpp b/source/Commands/CommandObjectGUI.cpp new file mode 100644 index 000000000000..3d05335e92e4 --- /dev/null +++ b/source/Commands/CommandObjectGUI.cpp @@ -0,0 +1,61 @@ +//===-- CommandObjectGUI.cpp ------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/lldb-python.h" + +#include "CommandObjectGUI.h" + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/lldb-private.h" +#include "lldb/Interpreter/CommandInterpreter.h" +#include "lldb/Interpreter/CommandReturnObject.h" + +using namespace lldb; +using namespace lldb_private; + +//------------------------------------------------------------------------- +// CommandObjectGUI +//------------------------------------------------------------------------- + +CommandObjectGUI::CommandObjectGUI (CommandInterpreter &interpreter) : + CommandObjectParsed (interpreter, "gui", "Switch into the curses based GUI mode.", "gui") +{ +} + +CommandObjectGUI::~CommandObjectGUI () +{ +} + +bool +CommandObjectGUI::DoExecute (Args& args, CommandReturnObject &result) +{ +#ifndef LLDB_DISABLE_CURSES + if (args.GetArgumentCount() == 0) + { + Debugger &debugger = m_interpreter.GetDebugger(); + IOHandlerSP io_handler_sp (new IOHandlerCursesGUI (debugger)); + if (io_handler_sp) + debugger.PushIOHandler(io_handler_sp); + result.SetStatus (eReturnStatusSuccessFinishResult); + } + else + { + result.AppendError("the gui command takes no arguments."); + result.SetStatus (eReturnStatusFailed); + } + return true; +#else + result.AppendError("lldb was not build with gui support"); + return false; +#endif +} + diff --git a/source/Commands/CommandObjectGUI.h b/source/Commands/CommandObjectGUI.h new file mode 100644 index 000000000000..72ddb961c266 --- /dev/null +++ b/source/Commands/CommandObjectGUI.h @@ -0,0 +1,43 @@ +//===-- CommandObjectGUI.h --------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_CommandObjectGUI_h_ +#define liblldb_CommandObjectGUI_h_ + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/Interpreter/CommandObject.h" + +namespace lldb_private { + +//------------------------------------------------------------------------- +// CommandObjectGUI +//------------------------------------------------------------------------- + +class CommandObjectGUI : public CommandObjectParsed +{ +public: + + CommandObjectGUI (CommandInterpreter &interpreter); + + virtual + ~CommandObjectGUI (); + +protected: + virtual bool + DoExecute (Args& args, + CommandReturnObject &result); + +}; + +} // namespace lldb_private + +#endif // liblldb_CommandObjectGUI_h_ diff --git a/source/Commands/CommandObjectMultiword.cpp b/source/Commands/CommandObjectMultiword.cpp index f84b401f3aa6..69b178da46ba 100644 --- a/source/Commands/CommandObjectMultiword.cpp +++ b/source/Commands/CommandObjectMultiword.cpp @@ -235,18 +235,19 @@ CommandObjectMultiword::HandleCompletion // completers will override this. word_complete = true; + const char *arg0 = input.GetArgumentAtIndex(0); if (cursor_index == 0) { CommandObject::AddNamesMatchingPartialString (m_subcommand_dict, - input.GetArgumentAtIndex(0), + arg0, matches); if (matches.GetSize() == 1 && matches.GetStringAtIndex(0) != NULL - && strcmp (input.GetArgumentAtIndex(0), matches.GetStringAtIndex(0)) == 0) + && strcmp (arg0, matches.GetStringAtIndex(0)) == 0) { StringList temp_matches; - CommandObject *cmd_obj = GetSubcommandObject (input.GetArgumentAtIndex(0), + CommandObject *cmd_obj = GetSubcommandObject (arg0, &temp_matches); if (cmd_obj != NULL) { @@ -270,7 +271,7 @@ CommandObjectMultiword::HandleCompletion } else { - CommandObject *sub_command_object = GetSubcommandObject (input.GetArgumentAtIndex(0), + CommandObject *sub_command_object = GetSubcommandObject (arg0, &matches); if (sub_command_object == NULL) { diff --git a/source/Commands/CommandObjectProcess.cpp b/source/Commands/CommandObjectProcess.cpp index 2933c78ca908..49a392286c6a 100644 --- a/source/Commands/CommandObjectProcess.cpp +++ b/source/Commands/CommandObjectProcess.cpp @@ -49,7 +49,7 @@ public: virtual ~CommandObjectProcessLaunchOrAttach () {} protected: bool - StopProcessIfNecessary (Process *&process, StateType &state, CommandReturnObject &result) + StopProcessIfNecessary (Process *process, StateType &state, CommandReturnObject &result) { state = eStateInvalid; if (process) @@ -187,12 +187,10 @@ protected: { Debugger &debugger = m_interpreter.GetDebugger(); Target *target = debugger.GetSelectedTarget().get(); - Error error; // If our listener is NULL, users aren't allows to launch - char filename[PATH_MAX]; - const Module *exe_module = target->GetExecutableModulePointer(); + ModuleSP exe_module_sp = target->GetExecutableModule(); - if (exe_module == NULL) + if (exe_module_sp == NULL) { result.AppendError ("no file in target, create a debug target using the 'target create' command"); result.SetStatus (eReturnStatusFailed); @@ -200,23 +198,31 @@ protected: } StateType state = eStateInvalid; - Process *process = m_exe_ctx.GetProcessPtr(); - if (!StopProcessIfNecessary(process, state, result)) + if (!StopProcessIfNecessary(m_exe_ctx.GetProcessPtr(), state, result)) return false; const char *target_settings_argv0 = target->GetArg0(); - exe_module->GetFileSpec().GetPath (filename, sizeof(filename)); + if (target->GetDisableASLR()) + m_options.launch_info.GetFlags().Set (eLaunchFlagDisableASLR); + + if (target->GetDisableSTDIO()) + m_options.launch_info.GetFlags().Set (eLaunchFlagDisableSTDIO); + Args environment; + target->GetEnvironmentAsArgs (environment); + if (environment.GetArgumentCount() > 0) + m_options.launch_info.GetEnvironmentEntries ().AppendArguments (environment); + if (target_settings_argv0) { m_options.launch_info.GetArguments().AppendArgument (target_settings_argv0); - m_options.launch_info.SetExecutableFile(exe_module->GetPlatformFileSpec(), false); + m_options.launch_info.SetExecutableFile(exe_module_sp->GetPlatformFileSpec(), false); } else { - m_options.launch_info.SetExecutableFile(exe_module->GetPlatformFileSpec(), true); + m_options.launch_info.SetExecutableFile(exe_module_sp->GetPlatformFileSpec(), true); } if (launch_args.GetArgumentCount() == 0) @@ -228,122 +234,33 @@ protected: else { m_options.launch_info.GetArguments().AppendArguments (launch_args); - // Save the arguments for subsequent runs in the current target. target->SetRunArguments (launch_args); } - if (target->GetDisableASLR()) - m_options.launch_info.GetFlags().Set (eLaunchFlagDisableASLR); - - if (target->GetDisableSTDIO()) - m_options.launch_info.GetFlags().Set (eLaunchFlagDisableSTDIO); - - m_options.launch_info.GetFlags().Set (eLaunchFlagDebug); - - Args environment; - target->GetEnvironmentAsArgs (environment); - if (environment.GetArgumentCount() > 0) - m_options.launch_info.GetEnvironmentEntries ().AppendArguments (environment); - - // Get the value of synchronous execution here. If you wait till after you have started to - // run, then you could have hit a breakpoint, whose command might switch the value, and - // then you'll pick up that incorrect value. - bool synchronous_execution = m_interpreter.GetSynchronous (); - - PlatformSP platform_sp (target->GetPlatform()); - - // Finalize the file actions, and if none were given, default to opening - // up a pseudo terminal - const bool default_to_use_pty = platform_sp ? platform_sp->IsHost() : false; - m_options.launch_info.FinalizeFileActions (target, default_to_use_pty); - - if (state == eStateConnected) - { - if (m_options.launch_info.GetFlags().Test (eLaunchFlagLaunchInTTY)) - { - result.AppendWarning("can't launch in tty when launching through a remote connection"); - m_options.launch_info.GetFlags().Clear (eLaunchFlagLaunchInTTY); - } - } - - if (!m_options.launch_info.GetArchitecture().IsValid()) - m_options.launch_info.GetArchitecture() = target->GetArchitecture(); + Error error = target->Launch(debugger.GetListener(), m_options.launch_info); - if (platform_sp && platform_sp->CanDebugProcess ()) - { - process = target->GetPlatform()->DebugProcess (m_options.launch_info, - debugger, - target, - debugger.GetListener(), - error).get(); - } - else - { - const char *plugin_name = m_options.launch_info.GetProcessPluginName(); - process = target->CreateProcess (debugger.GetListener(), plugin_name, NULL).get(); - if (process) - error = process->Launch (m_options.launch_info); - } - - if (process == NULL) - { - result.SetError (error, "failed to launch or debug process"); - return false; - } - - if (error.Success()) { - const char *archname = exe_module->GetArchitecture().GetArchitectureName(); - - result.AppendMessageWithFormat ("Process %" PRIu64 " launched: '%s' (%s)\n", process->GetID(), filename, archname); - result.SetDidChangeProcessState (true); - if (m_options.launch_info.GetFlags().Test(eLaunchFlagStopAtEntry) == false) + const char *archname = exe_module_sp->GetArchitecture().GetArchitectureName(); + ProcessSP process_sp (target->GetProcessSP()); + if (process_sp) { - result.SetStatus (eReturnStatusSuccessContinuingNoResult); - StateType state = process->WaitForProcessToStop (NULL, NULL, false); - - if (state == eStateStopped) - { - error = process->Resume(); - if (error.Success()) - { - if (synchronous_execution) - { - state = process->WaitForProcessToStop (NULL); - const bool must_be_alive = true; - if (!StateIsStoppedState(state, must_be_alive)) - { - result.AppendErrorWithFormat ("process isn't stopped: %s", StateAsCString(state)); - } - result.SetDidChangeProcessState (true); - result.SetStatus (eReturnStatusSuccessFinishResult); - } - else - { - result.SetStatus (eReturnStatusSuccessContinuingNoResult); - } - } - else - { - result.AppendErrorWithFormat ("process resume at entry point failed: %s", error.AsCString()); - result.SetStatus (eReturnStatusFailed); - } - } - else - { - result.AppendErrorWithFormat ("initial process state wasn't stopped: %s", StateAsCString(state)); - result.SetStatus (eReturnStatusFailed); - } + result.AppendMessageWithFormat ("Process %" PRIu64 " launched: '%s' (%s)\n", process_sp->GetID(), exe_module_sp->GetFileSpec().GetPath().c_str(), archname); + result.SetStatus (eReturnStatusSuccessFinishResult); + result.SetDidChangeProcessState (true); + } + else + { + result.AppendError("no error returned from Target::Launch, and target has no process"); + result.SetStatus (eReturnStatusFailed); } } else { - result.AppendErrorWithFormat ("process launch failed: %s", error.AsCString()); + result.AppendError(error.AsCString()); result.SetStatus (eReturnStatusFailed); } - return result.Succeeded(); } @@ -615,37 +532,36 @@ protected: if (error.Success()) { + ListenerSP listener_sp (new Listener("lldb.CommandObjectProcessAttach.DoExecute.attach.hijack")); + m_options.attach_info.SetHijackListener(listener_sp); + process->HijackProcessEvents(listener_sp.get()); error = process->Attach (m_options.attach_info); if (error.Success()) { result.SetStatus (eReturnStatusSuccessContinuingNoResult); - } - else - { - result.AppendErrorWithFormat ("attach failed: %s\n", error.AsCString()); - result.SetStatus (eReturnStatusFailed); - return false; - } - // If we're synchronous, wait for the stopped event and report that. - // Otherwise just return. - // FIXME: in the async case it will now be possible to get to the command - // interpreter with a state eStateAttaching. Make sure we handle that correctly. - StateType state = process->WaitForProcessToStop (NULL); - - result.SetDidChangeProcessState (true); + StateType state = process->WaitForProcessToStop (NULL, NULL, false, listener_sp.get()); - if (state == eStateStopped) - { - result.AppendMessageWithFormat ("Process %" PRIu64 " %s\n", process->GetID(), StateAsCString (state)); - result.SetStatus (eReturnStatusSuccessFinishNoResult); + process->RestoreProcessEvents(); + + result.SetDidChangeProcessState (true); + + if (state == eStateStopped) + { + result.AppendMessageWithFormat ("Process %" PRIu64 " %s\n", process->GetID(), StateAsCString (state)); + result.SetStatus (eReturnStatusSuccessFinishNoResult); + } + else + { + result.AppendError ("attach failed: process did not stop (no such process or permission problem?)"); + process->Destroy(); + result.SetStatus (eReturnStatusFailed); + } } else { - result.AppendError ("attach failed: process did not stop (no such process or permission problem?)"); - process->Destroy(); + result.AppendErrorWithFormat ("attach failed: %s\n", error.AsCString()); result.SetStatus (eReturnStatusFailed); - return false; } } } @@ -1170,7 +1086,7 @@ protected: if (process) { - error = process->ConnectRemote (&process->GetTarget().GetDebugger().GetOutputStream(), remote_url); + error = process->ConnectRemote (process->GetTarget().GetDebugger().GetOutputFile().get(), remote_url); if (error.Fail()) { diff --git a/source/Commands/CommandObjectQuit.cpp b/source/Commands/CommandObjectQuit.cpp index d04ecdd9885c..ffe2a9240726 100644 --- a/source/Commands/CommandObjectQuit.cpp +++ b/source/Commands/CommandObjectQuit.cpp @@ -92,7 +92,8 @@ CommandObjectQuit::DoExecute (Args& command, CommandReturnObject &result) return false; } } - m_interpreter.BroadcastEvent (CommandInterpreter::eBroadcastBitQuitCommandReceived); + const uint32_t event_type = CommandInterpreter::eBroadcastBitQuitCommandReceived; + m_interpreter.BroadcastEvent (event_type); result.SetStatus (eReturnStatusQuit); return true; } diff --git a/source/Commands/CommandObjectRegister.cpp b/source/Commands/CommandObjectRegister.cpp index 7cbfaa5d60fc..deaf2ab3793e 100644 --- a/source/Commands/CommandObjectRegister.cpp +++ b/source/Commands/CommandObjectRegister.cpp @@ -29,6 +29,7 @@ #include "lldb/Target/ExecutionContext.h" #include "lldb/Target/Process.h" #include "lldb/Target/RegisterContext.h" +#include "lldb/Target/SectionLoadList.h" #include "lldb/Target/Thread.h" using namespace lldb; diff --git a/source/Commands/CommandObjectSource.cpp b/source/Commands/CommandObjectSource.cpp index 1f6873611a22..bf2a42e0bea0 100644 --- a/source/Commands/CommandObjectSource.cpp +++ b/source/Commands/CommandObjectSource.cpp @@ -28,6 +28,7 @@ #include "lldb/Symbol/Function.h" #include "lldb/Symbol/Symbol.h" #include "lldb/Target/Process.h" +#include "lldb/Target/SectionLoadList.h" #include "lldb/Target/TargetList.h" #include "lldb/Interpreter/CommandCompletions.h" #include "lldb/Interpreter/Options.h" diff --git a/source/Commands/CommandObjectTarget.cpp b/source/Commands/CommandObjectTarget.cpp index 8e7e68aad39a..308b72f355d3 100644 --- a/source/Commands/CommandObjectTarget.cpp +++ b/source/Commands/CommandObjectTarget.cpp @@ -19,7 +19,7 @@ // Project includes #include "lldb/Interpreter/Args.h" #include "lldb/Core/Debugger.h" -#include "lldb/Core/InputReader.h" +#include "lldb/Core/IOHandler.h" #include "lldb/Core/Module.h" #include "lldb/Core/ModuleSpec.h" #include "lldb/Core/Section.h" @@ -49,6 +49,7 @@ #include "lldb/Symbol/UnwindPlan.h" #include "lldb/Symbol/VariableList.h" #include "lldb/Target/Process.h" +#include "lldb/Target/SectionLoadList.h" #include "lldb/Target/StackFrame.h" #include "lldb/Target/Thread.h" #include "lldb/Target/ThreadSpec.h" @@ -2893,7 +2894,8 @@ protected: if (m_slide_option.GetOptionValue().OptionWasSet()) { const addr_t slide = m_slide_option.GetOptionValue().GetCurrentValue(); - module->SetLoadAddress (*target, slide, changed); + const bool slide_is_offset = true; + module->SetLoadAddress (*target, slide, slide_is_offset, changed); } else { @@ -4758,7 +4760,9 @@ private: // CommandObjectTargetStopHookAdd //------------------------------------------------------------------------- -class CommandObjectTargetStopHookAdd : public CommandObjectParsed +class CommandObjectTargetStopHookAdd : + public CommandObjectParsed, + public IOHandlerDelegateMultiline { public: @@ -4925,9 +4929,10 @@ public: CommandObjectTargetStopHookAdd (CommandInterpreter &interpreter) : CommandObjectParsed (interpreter, - "target stop-hook add ", + "target stop-hook add", "Add a hook to be executed when the target stops.", "target stop-hook add"), + IOHandlerDelegateMultiline ("DONE", IOHandlerDelegate::Completion::LLDBCommand), m_options (interpreter) { } @@ -4936,102 +4941,61 @@ public: { } - static size_t - ReadCommandsCallbackFunction (void *baton, - InputReader &reader, - lldb::InputReaderAction notification, - const char *bytes, - size_t bytes_len) +protected: + + virtual void + IOHandlerActivated (IOHandler &io_handler) { - StreamSP out_stream = reader.GetDebugger().GetAsyncOutputStream(); - Target::StopHook *new_stop_hook = ((Target::StopHook *) baton); - static bool got_interrupted; - bool batch_mode = reader.GetDebugger().GetCommandInterpreter().GetBatchCommandMode(); - - switch (notification) + StreamFileSP output_sp(io_handler.GetOutputStreamFile()); + if (output_sp) { - case eInputReaderActivate: - if (!batch_mode) - { - out_stream->Printf ("%s\n", "Enter your stop hook command(s). Type 'DONE' to end."); - if (reader.GetPrompt()) - out_stream->Printf ("%s", reader.GetPrompt()); - out_stream->Flush(); - } - got_interrupted = false; - break; - - case eInputReaderDeactivate: - break; - - case eInputReaderReactivate: - if (reader.GetPrompt() && !batch_mode) - { - out_stream->Printf ("%s", reader.GetPrompt()); - out_stream->Flush(); - } - got_interrupted = false; - break; - - case eInputReaderAsynchronousOutputWritten: - break; - - case eInputReaderGotToken: - if (bytes && bytes_len && baton) + output_sp->PutCString("Enter your stop hook command(s). Type 'DONE' to end.\n"); + output_sp->Flush(); + } + } + + + virtual void + IOHandlerInputComplete (IOHandler &io_handler, std::string &line) + { + if (m_stop_hook_sp) + { + if (line.empty()) { - StringList *commands = new_stop_hook->GetCommandPointer(); - if (commands) + StreamFileSP error_sp(io_handler.GetErrorStreamFile()); + if (error_sp) { - commands->AppendString (bytes, bytes_len); + error_sp->Printf("error: stop hook #%" PRIu64 " aborted, no commands.\n", m_stop_hook_sp->GetID()); + error_sp->Flush(); } + Target *target = m_interpreter.GetDebugger().GetSelectedTarget().get(); + if (target) + target->RemoveStopHookByID(m_stop_hook_sp->GetID()); } - if (!reader.IsDone() && reader.GetPrompt() && !batch_mode) - { - out_stream->Printf ("%s", reader.GetPrompt()); - out_stream->Flush(); - } - break; - - case eInputReaderInterrupt: + else { - // Finish, and cancel the stop hook. - new_stop_hook->GetTarget()->RemoveStopHookByID(new_stop_hook->GetID()); - if (!batch_mode) + m_stop_hook_sp->GetCommandPointer()->SplitIntoLines(line); + StreamFileSP output_sp(io_handler.GetOutputStreamFile()); + if (output_sp) { - out_stream->Printf ("Stop hook cancelled.\n"); - out_stream->Flush(); + output_sp->Printf("Stop hook #%" PRIu64 " added.\n", m_stop_hook_sp->GetID()); + output_sp->Flush(); } - - reader.SetIsDone (true); } - got_interrupted = true; - break; - - case eInputReaderEndOfFile: - reader.SetIsDone (true); - break; - - case eInputReaderDone: - if (!got_interrupted && !batch_mode) - { - out_stream->Printf ("Stop hook #%" PRIu64 " added.\n", new_stop_hook->GetID()); - out_stream->Flush(); - } - break; + m_stop_hook_sp.reset(); } - - return bytes_len; + io_handler.SetIsDone(true); } - -protected: + bool DoExecute (Args& command, CommandReturnObject &result) { + m_stop_hook_sp.reset(); + Target *target = m_interpreter.GetDebugger().GetSelectedTarget().get(); if (target) { - Target::StopHookSP new_hook_sp; - target->AddStopHook (new_hook_sp); + Target::StopHookSP new_hook_sp = target->CreateStopHook(); // First step, make the specifier. std::unique_ptr<SymbolContextSpecifier> specifier_ap; @@ -5104,31 +5068,12 @@ protected: } else { - // Otherwise gather up the command list, we'll push an input reader and suck the data from that directly into - // the new stop hook's command string. - InputReaderSP reader_sp (new InputReader(m_interpreter.GetDebugger())); - if (!reader_sp) - { - result.AppendError("out of memory\n"); - result.SetStatus (eReturnStatusFailed); - target->RemoveStopHookByID (new_hook_sp->GetID()); - return false; - } - - Error err (reader_sp->Initialize (CommandObjectTargetStopHookAdd::ReadCommandsCallbackFunction, - new_hook_sp.get(), // baton - eInputReaderGranularityLine, // token size, to pass to callback function - "DONE", // end token - "> ", // prompt - true)); // echo input - if (!err.Success()) - { - result.AppendError (err.AsCString()); - result.SetStatus (eReturnStatusFailed); - target->RemoveStopHookByID (new_hook_sp->GetID()); - return false; - } - m_interpreter.GetDebugger().PushInputReader (reader_sp); + m_stop_hook_sp = new_hook_sp; + m_interpreter.GetLLDBCommandsFromIOHandler ("> ", // Prompt + *this, // IOHandlerDelegate + true, // Run IOHandler in async mode + NULL); // Baton for the "io_handler" that will be passed back into our IOHandlerDelegate functions + } result.SetStatus (eReturnStatusSuccessFinishNoResult); } @@ -5142,6 +5087,7 @@ protected: } private: CommandOptions m_options; + Target::StopHookSP m_stop_hook_sp; }; OptionDefinition diff --git a/source/Commands/CommandObjectType.cpp b/source/Commands/CommandObjectType.cpp index 1c695b37c566..f1b1d2c1900c 100644 --- a/source/Commands/CommandObjectType.cpp +++ b/source/Commands/CommandObjectType.cpp @@ -19,7 +19,7 @@ #include "lldb/Core/ConstString.h" #include "lldb/Core/Debugger.h" -#include "lldb/Core/InputReaderEZ.h" +#include "lldb/Core/IOHandler.h" #include "lldb/Core/RegularExpression.h" #include "lldb/Core/State.h" #include "lldb/Core/StringList.h" @@ -42,7 +42,6 @@ public: TypeSummaryImpl::Flags m_flags; StringList m_target_types; - StringList m_user_source; bool m_regex; @@ -74,7 +73,6 @@ public: bool m_skip_references; bool m_cascade; bool m_regex; - StringList m_user_source; StringList m_target_types; std::string m_category; @@ -88,7 +86,6 @@ public: m_skip_references(sref), m_cascade(casc), m_regex(regx), - m_user_source(), m_target_types(), m_category(catg) { @@ -98,9 +95,36 @@ public: }; +static bool +WarnOnPotentialUnquotedUnsignedType (Args& command, CommandReturnObject &result) +{ + for (int idx = 0; idx < command.GetArgumentCount(); idx++) + { + const char* arg = command.GetArgumentAtIndex(idx); + if (idx+1 < command.GetArgumentCount()) + { + if (arg && 0 == strcmp(arg,"unsigned")) + { + const char* next = command.GetArgumentAtIndex(idx+1); + if (next && + (0 == strcmp(next, "int") || + 0 == strcmp(next, "short") || + 0 == strcmp(next, "char") || + 0 == strcmp(next, "long"))) + { + result.AppendWarningWithFormat("%s %s being treated as two types. if you meant the combined type name use quotes, as in \"%s %s\"\n", + arg,next,arg,next); + return true; + } + } + } + } + return false; +} - -class CommandObjectTypeSummaryAdd : public CommandObjectParsed +class CommandObjectTypeSummaryAdd : + public CommandObjectParsed, + public IOHandlerDelegateMultiline { private: @@ -153,10 +177,6 @@ private: return &m_options; } - void - CollectPythonScript(ScriptAddOptions *options, - CommandReturnObject &result); - bool Execute_ScriptSummary (Args& command, CommandReturnObject &result); @@ -178,6 +198,147 @@ public: { } + virtual void + IOHandlerActivated (IOHandler &io_handler) + { + static const char *g_summary_addreader_instructions = "Enter your Python command(s). Type 'DONE' to end.\n" + "def function (valobj,internal_dict):\n" + " \"\"\"valobj: an SBValue which you want to provide a summary for\n" + " internal_dict: an LLDB support object not to be used\"\"\""; + + StreamFileSP output_sp(io_handler.GetOutputStreamFile()); + if (output_sp) + { + output_sp->PutCString(g_summary_addreader_instructions); + output_sp->Flush(); + } + } + + + virtual void + IOHandlerInputComplete (IOHandler &io_handler, std::string &data) + { + StreamFileSP error_sp = io_handler.GetErrorStreamFile(); + +#ifndef LLDB_DISABLE_PYTHON + ScriptInterpreter *interpreter = m_interpreter.GetScriptInterpreter(); + if (interpreter) + { + StringList lines; + lines.SplitIntoLines(data); + if (lines.GetSize() > 0) + { + ScriptAddOptions *options_ptr = ((ScriptAddOptions*)io_handler.GetUserData()); + if (options_ptr) + { + ScriptAddOptions::SharedPointer options(options_ptr); // this will ensure that we get rid of the pointer when going out of scope + + ScriptInterpreter *interpreter = m_interpreter.GetScriptInterpreter(); + if (interpreter) + { + std::string funct_name_str; + if (interpreter->GenerateTypeScriptFunction (lines, funct_name_str)) + { + if (funct_name_str.empty()) + { + error_sp->Printf ("unable to obtain a valid function name from the script interpreter.\n"); + error_sp->Flush(); + } + else + { + // now I have a valid function name, let's add this as script for every type in the list + + TypeSummaryImplSP script_format; + script_format.reset(new ScriptSummaryFormat(options->m_flags, + funct_name_str.c_str(), + lines.CopyList(" ").c_str())); + + Error error; + + for (size_t i = 0; i < options->m_target_types.GetSize(); i++) + { + const char *type_name = options->m_target_types.GetStringAtIndex(i); + CommandObjectTypeSummaryAdd::AddSummary(ConstString(type_name), + script_format, + (options->m_regex ? CommandObjectTypeSummaryAdd::eRegexSummary : CommandObjectTypeSummaryAdd::eRegularSummary), + options->m_category, + &error); + if (error.Fail()) + { + error_sp->Printf ("error: %s", error.AsCString()); + error_sp->Flush(); + } + } + + if (options->m_name) + { + CommandObjectTypeSummaryAdd::AddSummary (options->m_name, + script_format, + CommandObjectTypeSummaryAdd::eNamedSummary, + options->m_category, + &error); + if (error.Fail()) + { + CommandObjectTypeSummaryAdd::AddSummary (options->m_name, + script_format, + CommandObjectTypeSummaryAdd::eNamedSummary, + options->m_category, + &error); + if (error.Fail()) + { + error_sp->Printf ("error: %s", error.AsCString()); + error_sp->Flush(); + } + } + else + { + error_sp->Printf ("error: %s", error.AsCString()); + error_sp->Flush(); + } + } + else + { + if (error.AsCString()) + { + error_sp->Printf ("error: %s", error.AsCString()); + error_sp->Flush(); + } + } + } + } + else + { + error_sp->Printf ("error: unable to generate a function.\n"); + error_sp->Flush(); + } + } + else + { + error_sp->Printf ("error: no script interpreter.\n"); + error_sp->Flush(); + } + } + else + { + error_sp->Printf ("error: internal synchronization information missing or invalid.\n"); + error_sp->Flush(); + } + } + else + { + error_sp->Printf ("error: empty function, didn't add python command.\n"); + error_sp->Flush(); + } + } + else + { + error_sp->Printf ("error: script interpreter missing, didn't add python command.\n"); + error_sp->Flush(); + } +#endif // #ifndef LLDB_DISABLE_PYTHON + io_handler.SetIsDone(true); + } + static bool AddSummary(ConstString type_name, lldb::TypeSummaryImplSP entry, @@ -190,7 +351,19 @@ protected: }; -class CommandObjectTypeSynthAdd : public CommandObjectParsed +static const char *g_synth_addreader_instructions = "Enter your Python command(s). Type 'DONE' to end.\n" +"You must define a Python class with these methods:\n" +" def __init__(self, valobj, dict):\n" +" def num_children(self):\n" +" def get_child_at_index(self, index):\n" +" def get_child_index(self, name):\n" +" def update(self):\n" +" '''Optional'''\n" +"class synthProvider:\n"; + +class CommandObjectTypeSynthAdd : + public CommandObjectParsed, + public IOHandlerDelegateMultiline { private: @@ -200,7 +373,7 @@ private: public: CommandOptions (CommandInterpreter &interpreter) : - Options (interpreter) + Options (interpreter) { } @@ -296,9 +469,6 @@ private: return &m_options; } - void - CollectPythonScript (SynthAddOptions *options, - CommandReturnObject &result); bool Execute_HandwritePython (Args& command, CommandReturnObject &result); @@ -307,8 +477,139 @@ private: protected: bool - DoExecute (Args& command, CommandReturnObject &result); + DoExecute (Args& command, CommandReturnObject &result) + { + WarnOnPotentialUnquotedUnsignedType(command, result); + + if (m_options.handwrite_python) + return Execute_HandwritePython(command, result); + else if (m_options.is_class_based) + return Execute_PythonClass(command, result); + else + { + result.AppendError("must either provide a children list, a Python class name, or use -P and type a Python class line-by-line"); + result.SetStatus(eReturnStatusFailed); + return false; + } + } + + virtual void + IOHandlerActivated (IOHandler &io_handler) + { + StreamFileSP output_sp(io_handler.GetOutputStreamFile()); + if (output_sp) + { + output_sp->PutCString(g_synth_addreader_instructions); + output_sp->Flush(); + } + } + + virtual void + IOHandlerInputComplete (IOHandler &io_handler, std::string &data) + { + StreamFileSP error_sp = io_handler.GetErrorStreamFile(); + +#ifndef LLDB_DISABLE_PYTHON + ScriptInterpreter *interpreter = m_interpreter.GetScriptInterpreter(); + if (interpreter) + { + StringList lines; + lines.SplitIntoLines(data); + if (lines.GetSize() > 0) + { + SynthAddOptions *options_ptr = ((SynthAddOptions*)io_handler.GetUserData()); + if (options_ptr) + { + SynthAddOptions::SharedPointer options(options_ptr); // this will ensure that we get rid of the pointer when going out of scope + + ScriptInterpreter *interpreter = m_interpreter.GetScriptInterpreter(); + if (interpreter) + { + std::string class_name_str; + if (interpreter->GenerateTypeSynthClass (lines, class_name_str)) + { + if (class_name_str.empty()) + { + error_sp->Printf ("error: unable to obtain a proper name for the class.\n"); + error_sp->Flush(); + } + else + { + // everything should be fine now, let's add the synth provider class + + SyntheticChildrenSP synth_provider; + synth_provider.reset(new ScriptedSyntheticChildren(SyntheticChildren::Flags().SetCascades(options->m_cascade). + SetSkipPointers(options->m_skip_pointers). + SetSkipReferences(options->m_skip_references), + class_name_str.c_str())); + + + lldb::TypeCategoryImplSP category; + DataVisualization::Categories::GetCategory(ConstString(options->m_category.c_str()), category); + + Error error; + + for (size_t i = 0; i < options->m_target_types.GetSize(); i++) + { + const char *type_name = options->m_target_types.GetStringAtIndex(i); + ConstString const_type_name(type_name); + if (const_type_name) + { + if (!CommandObjectTypeSynthAdd::AddSynth(const_type_name, + synth_provider, + options->m_regex ? CommandObjectTypeSynthAdd::eRegexSynth : CommandObjectTypeSynthAdd::eRegularSynth, + options->m_category, + &error)) + { + error_sp->Printf("error: %s\n", error.AsCString()); + error_sp->Flush(); + break; + } + } + else + { + error_sp->Printf ("error: invalid type name.\n"); + error_sp->Flush(); + break; + } + } + } + } + else + { + error_sp->Printf ("error: unable to generate a class.\n"); + error_sp->Flush(); + } + } + else + { + error_sp->Printf ("error: no script interpreter.\n"); + error_sp->Flush(); + } + } + else + { + error_sp->Printf ("error: internal synchronization data missing.\n"); + error_sp->Flush(); + } + } + else + { + error_sp->Printf ("error: empty function, didn't add python command.\n"); + error_sp->Flush(); + } + } + else + { + error_sp->Printf ("error: script interpreter missing, didn't add python command.\n"); + error_sp->Flush(); + } + +#endif // #ifndef LLDB_DISABLE_PYTHON + io_handler.SetIsDone(true); + } + public: enum SynthFormatType @@ -371,6 +672,7 @@ private: m_skip_references = false; m_regex = false; m_category.assign("default"); + m_custom_type_name.clear(); } virtual Error SetOptionValue (CommandInterpreter &interpreter, @@ -400,6 +702,9 @@ private: case 'x': m_regex = true; break; + case 't': + m_custom_type_name.assign(option_value); + break; default: error.SetErrorStringWithFormat ("unrecognized option '%c'", short_option); break; @@ -419,6 +724,7 @@ private: bool m_skip_pointers; bool m_regex; std::string m_category; + std::string m_custom_type_name; }; OptionGroupOptions m_option_group; @@ -480,7 +786,7 @@ public: ); // Add the "--format" to all options groups - m_option_group.Append (&m_format_options, OptionGroupFormat::OPTION_GROUP_FORMAT, LLDB_OPT_SET_ALL); + m_option_group.Append (&m_format_options, OptionGroupFormat::OPTION_GROUP_FORMAT, LLDB_OPT_SET_1); m_option_group.Append (&m_command_options); m_option_group.Finalize(); @@ -504,7 +810,7 @@ protected: } const Format format = m_format_options.GetFormat(); - if (format == eFormatInvalid) + if (format == eFormatInvalid && m_command_options.m_custom_type_name.empty()) { result.AppendErrorWithFormat ("%s needs a valid format.\n", m_cmd_name.c_str()); result.SetStatus(eReturnStatusFailed); @@ -513,10 +819,16 @@ protected: TypeFormatImplSP entry; - entry.reset(new TypeFormatImpl(format, - TypeFormatImpl::Flags().SetCascades(m_command_options.m_cascade). - SetSkipPointers(m_command_options.m_skip_pointers). - SetSkipReferences(m_command_options.m_skip_references))); + if (m_command_options.m_custom_type_name.empty()) + entry.reset(new TypeFormatImpl_Format(format, + TypeFormatImpl::Flags().SetCascades(m_command_options.m_cascade). + SetSkipPointers(m_command_options.m_skip_pointers). + SetSkipReferences(m_command_options.m_skip_references))); + else + entry.reset(new TypeFormatImpl_EnumType(ConstString(m_command_options.m_custom_type_name.c_str()), + TypeFormatImpl::Flags().SetCascades(m_command_options.m_cascade). + SetSkipPointers(m_command_options.m_skip_pointers). + SetSkipReferences(m_command_options.m_skip_references))); // now I have a valid format, let's add it to every type @@ -525,6 +837,8 @@ protected: if (!category_sp) return false; + WarnOnPotentialUnquotedUnsignedType(command, result); + for (size_t i = 0; i < argc; i++) { const char* typeA = command.GetArgumentAtIndex(i); @@ -540,11 +854,11 @@ protected: result.SetStatus(eReturnStatusFailed); return false; } - category_sp->GetRegexSummaryNavigator()->Delete(typeCS); - category_sp->GetRegexValueNavigator()->Add(typeRX, entry); + category_sp->GetRegexTypeSummariesContainer()->Delete(typeCS); + category_sp->GetRegexTypeFormatsContainer()->Add(typeRX, entry); } else - category_sp->GetValueNavigator()->Add(typeCS, entry); + category_sp->GetTypeFormatsContainer()->Add(typeCS, entry); } else { @@ -562,11 +876,12 @@ protected: OptionDefinition CommandObjectTypeFormatAdd::CommandOptions::g_option_table[] = { - { LLDB_OPT_SET_ALL, false, "category", 'w', OptionParser::eRequiredArgument, NULL, 0, eArgTypeName, "Add this to the given category instead of the default one."}, - { LLDB_OPT_SET_ALL, false, "cascade", 'C', OptionParser::eRequiredArgument, NULL, 0, eArgTypeBoolean, "If true, cascade through typedef chains."}, - { LLDB_OPT_SET_ALL, false, "skip-pointers", 'p', OptionParser::eNoArgument, NULL, 0, eArgTypeNone, "Don't use this format for pointers-to-type objects."}, - { LLDB_OPT_SET_ALL, false, "skip-references", 'r', OptionParser::eNoArgument, NULL, 0, eArgTypeNone, "Don't use this format for references-to-type objects."}, + { LLDB_OPT_SET_ALL, false, "category", 'w', OptionParser::eRequiredArgument, NULL, 0, eArgTypeName, "Add this to the given category instead of the default one."}, + { LLDB_OPT_SET_ALL, false, "cascade", 'C', OptionParser::eRequiredArgument, NULL, 0, eArgTypeBoolean, "If true, cascade through typedef chains."}, + { LLDB_OPT_SET_ALL, false, "skip-pointers", 'p', OptionParser::eNoArgument, NULL, 0, eArgTypeNone, "Don't use this format for pointers-to-type objects."}, + { LLDB_OPT_SET_ALL, false, "skip-references", 'r', OptionParser::eNoArgument, NULL, 0, eArgTypeNone, "Don't use this format for references-to-type objects."}, { LLDB_OPT_SET_ALL, false, "regex", 'x', OptionParser::eNoArgument, NULL, 0, eArgTypeNone, "Type names are actually regular expressions."}, + { LLDB_OPT_SET_2, false, "type", 't', OptionParser::eRequiredArgument, NULL, 0, eArgTypeName, "Format variables as if they were of this type."}, { 0, false, NULL, 0, 0, NULL, 0, eArgTypeNone, NULL } }; @@ -817,8 +1132,8 @@ private: PerCategoryCallback(void* param, const lldb::TypeCategoryImplSP& cate) { - cate->GetValueNavigator()->Clear(); - cate->GetRegexValueNavigator()->Clear(); + cate->GetTypeFormatsContainer()->Clear(); + cate->GetRegexTypeFormatsContainer()->Clear(); return true; } @@ -996,6 +1311,7 @@ protected: param = new CommandObjectTypeFormatList_LoopCallbackParam(this,&result,NULL,cate_regex); DataVisualization::Categories::LoopThrough(PerCategoryCallback,param); + delete param; if (cate_regex) delete cate_regex; @@ -1029,12 +1345,12 @@ private: cate_name, (cate->IsEnabled() ? "enabled" : "disabled")); - cate->GetValueNavigator()->LoopThrough(CommandObjectTypeFormatList_LoopCallback, param_vp); + cate->GetTypeFormatsContainer()->LoopThrough(CommandObjectTypeFormatList_LoopCallback, param_vp); - if (cate->GetRegexSummaryNavigator()->GetCount() > 0) + if (cate->GetRegexTypeSummariesContainer()->GetCount() > 0) { result->GetOutputStream().Printf("Regex-based summaries (slower):\n"); - cate->GetRegexValueNavigator()->LoopThrough(CommandObjectTypeRXFormatList_LoopCallback, param_vp); + cate->GetRegexTypeFormatsContainer()->LoopThrough(CommandObjectTypeRXFormatList_LoopCallback, param_vp); } return true; } @@ -1089,176 +1405,6 @@ CommandObjectTypeFormatList::CommandOptions::g_option_table[] = // CommandObjectTypeSummaryAdd //------------------------------------------------------------------------- -static const char *g_summary_addreader_instructions = "Enter your Python command(s). Type 'DONE' to end.\n" - "def function (valobj,internal_dict):\n" - " \"\"\"valobj: an SBValue which you want to provide a summary for\n" - " internal_dict: an LLDB support object not to be used\"\"\""; - -class TypeScriptAddInputReader : public InputReaderEZ -{ -private: - DISALLOW_COPY_AND_ASSIGN (TypeScriptAddInputReader); -public: - TypeScriptAddInputReader(Debugger& debugger) : - InputReaderEZ(debugger) - {} - - virtual - ~TypeScriptAddInputReader() - { - } - - virtual void ActivateHandler(HandlerData& data) - { - StreamSP out_stream = data.reader.GetDebugger().GetAsyncOutputStream(); - bool batch_mode = data.reader.GetDebugger().GetCommandInterpreter().GetBatchCommandMode(); - if (!batch_mode) - { - out_stream->Printf ("%s\n", g_summary_addreader_instructions); - if (data.reader.GetPrompt()) - out_stream->Printf ("%s", data.reader.GetPrompt()); - out_stream->Flush(); - } - } - - virtual void ReactivateHandler(HandlerData& data) - { - StreamSP out_stream = data.reader.GetDebugger().GetAsyncOutputStream(); - bool batch_mode = data.reader.GetDebugger().GetCommandInterpreter().GetBatchCommandMode(); - if (data.reader.GetPrompt() && !batch_mode) - { - out_stream->Printf ("%s", data.reader.GetPrompt()); - out_stream->Flush(); - } - } - virtual void GotTokenHandler(HandlerData& data) - { - StreamSP out_stream = data.reader.GetDebugger().GetAsyncOutputStream(); - bool batch_mode = data.reader.GetDebugger().GetCommandInterpreter().GetBatchCommandMode(); - if (data.bytes && data.bytes_len && data.baton) - { - ((ScriptAddOptions*)data.baton)->m_user_source.AppendString(data.bytes, data.bytes_len); - } - if (!data.reader.IsDone() && data.reader.GetPrompt() && !batch_mode) - { - out_stream->Printf ("%s", data.reader.GetPrompt()); - out_stream->Flush(); - } - } - virtual void InterruptHandler(HandlerData& data) - { - StreamSP out_stream = data.reader.GetDebugger().GetAsyncOutputStream(); - bool batch_mode = data.reader.GetDebugger().GetCommandInterpreter().GetBatchCommandMode(); - data.reader.SetIsDone (true); - if (!batch_mode) - { - out_stream->Printf ("Warning: No command attached to breakpoint.\n"); - out_stream->Flush(); - } - } - virtual void EOFHandler(HandlerData& data) - { - data.reader.SetIsDone (true); - } - virtual void DoneHandler(HandlerData& data) - { - StreamSP out_stream = data.reader.GetDebugger().GetAsyncOutputStream(); - ScriptAddOptions *options_ptr = ((ScriptAddOptions*)data.baton); - if (!options_ptr) - { - out_stream->Printf ("internal synchronization information missing or invalid.\n"); - out_stream->Flush(); - return; - } - - ScriptAddOptions::SharedPointer options(options_ptr); // this will ensure that we get rid of the pointer when going out of scope - - ScriptInterpreter *interpreter = data.reader.GetDebugger().GetCommandInterpreter().GetScriptInterpreter(); - if (!interpreter) - { - out_stream->Printf ("no script interpreter.\n"); - out_stream->Flush(); - return; - } - std::string funct_name_str; - if (!interpreter->GenerateTypeScriptFunction (options->m_user_source, - funct_name_str)) - { - out_stream->Printf ("unable to generate a function.\n"); - out_stream->Flush(); - return; - } - if (funct_name_str.empty()) - { - out_stream->Printf ("unable to obtain a valid function name from the script interpreter.\n"); - out_stream->Flush(); - return; - } - // now I have a valid function name, let's add this as script for every type in the list - - TypeSummaryImplSP script_format; - script_format.reset(new ScriptSummaryFormat(options->m_flags, - funct_name_str.c_str(), - options->m_user_source.CopyList(" ").c_str())); - - Error error; - - for (size_t i = 0; i < options->m_target_types.GetSize(); i++) - { - const char *type_name = options->m_target_types.GetStringAtIndex(i); - CommandObjectTypeSummaryAdd::AddSummary(ConstString(type_name), - script_format, - (options->m_regex ? CommandObjectTypeSummaryAdd::eRegexSummary : CommandObjectTypeSummaryAdd::eRegularSummary), - options->m_category, - &error); - if (error.Fail()) - { - out_stream->Printf ("%s", error.AsCString()); - out_stream->Flush(); - return; - } - } - - if (options->m_name) - { - CommandObjectTypeSummaryAdd::AddSummary (options->m_name, - script_format, - CommandObjectTypeSummaryAdd::eNamedSummary, - options->m_category, - &error); - if (error.Fail()) - { - CommandObjectTypeSummaryAdd::AddSummary (options->m_name, - script_format, - CommandObjectTypeSummaryAdd::eNamedSummary, - options->m_category, - &error); - if (error.Fail()) - { - out_stream->Printf ("%s", error.AsCString()); - out_stream->Flush(); - return; - } - } - else - { - out_stream->Printf ("%s", error.AsCString()); - out_stream->Flush(); - return; - } - } - else - { - if (error.AsCString()) - { - out_stream->PutCString (error.AsCString()); - out_stream->Flush(); - } - return; - } - } -}; - #endif // #ifndef LLDB_DISABLE_PYTHON Error @@ -1339,35 +1485,9 @@ CommandObjectTypeSummaryAdd::CommandOptions::OptionParsingStarting () m_category = "default"; } + + #ifndef LLDB_DISABLE_PYTHON -void -CommandObjectTypeSummaryAdd::CollectPythonScript (ScriptAddOptions *options, - CommandReturnObject &result) -{ - InputReaderSP reader_sp (new TypeScriptAddInputReader(m_interpreter.GetDebugger())); - if (reader_sp && options) - { - - InputReaderEZ::InitializationParameters ipr; - - Error err (reader_sp->Initialize (ipr.SetBaton(options).SetPrompt(" "))); - if (err.Success()) - { - m_interpreter.GetDebugger().PushInputReader (reader_sp); - result.SetStatus (eReturnStatusSuccessFinishNoResult); - } - else - { - result.AppendError (err.AsCString()); - result.SetStatus (eReturnStatusFailed); - } - } - else - { - result.AppendError("out of memory"); - result.SetStatus (eReturnStatusFailed); - } -} bool CommandObjectTypeSummaryAdd::Execute_ScriptSummary (Args& command, CommandReturnObject &result) @@ -1393,7 +1513,7 @@ CommandObjectTypeSummaryAdd::Execute_ScriptSummary (Args& command, CommandReturn return false; } - std::string code = (" " + m_options.m_python_function + "(valobj,internal_dict)"); + std::string code = (" " + m_options.m_python_function + "(valobj,internal_dict)"); script_format.reset(new ScriptSummaryFormat(m_options.m_flags, funct_name, @@ -1432,14 +1552,15 @@ CommandObjectTypeSummaryAdd::Execute_ScriptSummary (Args& command, CommandReturn return false; } - std::string code = " " + m_options.m_python_script; + std::string code = " " + m_options.m_python_script; script_format.reset(new ScriptSummaryFormat(m_options.m_flags, funct_name_str.c_str(), code.c_str())); } - else // use an InputReader to grab Python code from the user - { + else + { + // Use an IOHandler to grab Python code from the user ScriptAddOptions *options = new ScriptAddOptions(m_options.m_flags, m_options.m_regex, m_options.m_name, @@ -1458,7 +1579,12 @@ CommandObjectTypeSummaryAdd::Execute_ScriptSummary (Args& command, CommandReturn } } - CollectPythonScript(options,result); + m_interpreter.GetPythonCommandsFromIOHandler (" ", // Prompt + *this, // IOHandlerDelegate + true, // Run IOHandler in async mode + options); // Baton for the "io_handler" that will be passed back into our IOHandlerDelegate functions + result.SetStatus(eReturnStatusSuccessFinishNoResult); + return result.Succeeded(); } @@ -1590,6 +1716,7 @@ CommandObjectTypeSummaryAdd::CommandObjectTypeSummaryAdd (CommandInterpreter &in "type summary add", "Add a new summary style for a type.", NULL), + IOHandlerDelegateMultiline ("DONE"), m_options (interpreter) { CommandArgumentEntry type_arg; @@ -1671,6 +1798,8 @@ CommandObjectTypeSummaryAdd::CommandObjectTypeSummaryAdd (CommandInterpreter &in bool CommandObjectTypeSummaryAdd::DoExecute (Args& command, CommandReturnObject &result) { + WarnOnPotentialUnquotedUnsignedType(command, result); + if (m_options.m_is_add_script) { #ifndef LLDB_DISABLE_PYTHON @@ -1720,8 +1849,8 @@ CommandObjectTypeSummaryAdd::AddSummary(ConstString type_name, return false; } - category->GetRegexSummaryNavigator()->Delete(type_name); - category->GetRegexSummaryNavigator()->Add(typeRX, entry); + category->GetRegexTypeSummariesContainer()->Delete(type_name); + category->GetRegexTypeSummariesContainer()->Add(typeRX, entry); return true; } @@ -1733,7 +1862,7 @@ CommandObjectTypeSummaryAdd::AddSummary(ConstString type_name, } else { - category->GetSummaryNavigator()->Add(type_name, entry); + category->GetTypeSummariesContainer()->Add(type_name, entry); return true; } } @@ -1994,8 +2123,8 @@ private: PerCategoryCallback(void* param, const lldb::TypeCategoryImplSP& cate) { - cate->GetSummaryNavigator()->Clear(); - cate->GetRegexSummaryNavigator()->Clear(); + cate->GetTypeSummariesContainer()->Clear(); + cate->GetRegexTypeSummariesContainer()->Clear(); return true; } @@ -2178,7 +2307,8 @@ protected: param = new CommandObjectTypeSummaryList_LoopCallbackParam(this,&result,NULL,cate_regex); DataVisualization::Categories::LoopThrough(PerCategoryCallback,param); - + delete param; + if (DataVisualization::NamedSummaryFormats::GetCount() > 0) { result.GetOutputStream().Printf("Named summaries:\n"); @@ -2226,12 +2356,12 @@ private: cate_name, (cate->IsEnabled() ? "enabled" : "disabled")); - cate->GetSummaryNavigator()->LoopThrough(CommandObjectTypeSummaryList_LoopCallback, param_vp); + cate->GetTypeSummariesContainer()->LoopThrough(CommandObjectTypeSummaryList_LoopCallback, param_vp); - if (cate->GetRegexSummaryNavigator()->GetCount() > 0) + if (cate->GetRegexTypeSummariesContainer()->GetCount() > 0) { result->GetOutputStream().Printf("Regex-based summaries (slower):\n"); - cate->GetRegexSummaryNavigator()->LoopThrough(CommandObjectTypeRXSummaryList_LoopCallback, param_vp); + cate->GetRegexTypeSummariesContainer()->LoopThrough(CommandObjectTypeRXSummaryList_LoopCallback, param_vp); } return true; } @@ -2741,6 +2871,7 @@ protected: param = new CommandObjectTypeFilterList_LoopCallbackParam(this,&result,NULL,cate_regex); DataVisualization::Categories::LoopThrough(PerCategoryCallback,param); + delete param; if (cate_regex) delete cate_regex; @@ -2774,12 +2905,12 @@ private: cate_name, (cate->IsEnabled() ? "enabled" : "disabled")); - cate->GetFilterNavigator()->LoopThrough(CommandObjectTypeFilterList_LoopCallback, param_vp); + cate->GetTypeFiltersContainer()->LoopThrough(CommandObjectTypeFilterList_LoopCallback, param_vp); - if (cate->GetRegexFilterNavigator()->GetCount() > 0) + if (cate->GetRegexTypeFiltersContainer()->GetCount() > 0) { result->GetOutputStream().Printf("Regex-based filters (slower):\n"); - cate->GetRegexFilterNavigator()->LoopThrough(CommandObjectTypeFilterRXList_LoopCallback, param_vp); + cate->GetRegexTypeFiltersContainer()->LoopThrough(CommandObjectTypeFilterRXList_LoopCallback, param_vp); } return true; @@ -2955,7 +3086,8 @@ protected: param = new CommandObjectTypeSynthList_LoopCallbackParam(this,&result,NULL,cate_regex); DataVisualization::Categories::LoopThrough(PerCategoryCallback,param); - + delete param; + if (cate_regex) delete cate_regex; @@ -2988,12 +3120,12 @@ private: cate_name, (cate->IsEnabled() ? "enabled" : "disabled")); - cate->GetSyntheticNavigator()->LoopThrough(CommandObjectTypeSynthList_LoopCallback, param_vp); + cate->GetTypeSyntheticsContainer()->LoopThrough(CommandObjectTypeSynthList_LoopCallback, param_vp); - if (cate->GetRegexSyntheticNavigator()->GetCount() > 0) + if (cate->GetRegexTypeSyntheticsContainer()->GetCount() > 0) { result->GetOutputStream().Printf("Regex-based synthetic providers (slower):\n"); - cate->GetRegexSyntheticNavigator()->LoopThrough(CommandObjectTypeSynthRXList_LoopCallback, param_vp); + cate->GetRegexTypeSyntheticsContainer()->LoopThrough(CommandObjectTypeSynthRXList_LoopCallback, param_vp); } return true; @@ -3179,8 +3311,8 @@ protected: lldb::TypeCategoryImplSP category; DataVisualization::Categories::GetCategory(ConstString(m_options.m_category.c_str()), category); - bool delete_category = category->GetFilterNavigator()->Delete(typeCS); - delete_category = category->GetRegexFilterNavigator()->Delete(typeCS) || delete_category; + bool delete_category = category->GetTypeFiltersContainer()->Delete(typeCS); + delete_category = category->GetRegexTypeFiltersContainer()->Delete(typeCS) || delete_category; if (delete_category) { @@ -3345,8 +3477,8 @@ protected: lldb::TypeCategoryImplSP category; DataVisualization::Categories::GetCategory(ConstString(m_options.m_category.c_str()), category); - bool delete_category = category->GetSyntheticNavigator()->Delete(typeCS); - delete_category = category->GetRegexSyntheticNavigator()->Delete(typeCS) || delete_category; + bool delete_category = category->GetTypeSyntheticsContainer()->Delete(typeCS); + delete_category = category->GetRegexTypeSyntheticsContainer()->Delete(typeCS) || delete_category; if (delete_category) { @@ -3484,8 +3616,8 @@ protected: } else DataVisualization::Categories::GetCategory(ConstString(NULL), category); - category->GetFilterNavigator()->Clear(); - category->GetRegexFilterNavigator()->Clear(); + category->GetTypeFiltersContainer()->Clear(); + category->GetRegexTypeFiltersContainer()->Clear(); } result.SetStatus(eReturnStatusSuccessFinishResult); @@ -3613,8 +3745,8 @@ protected: } else DataVisualization::Categories::GetCategory(ConstString(NULL), category); - category->GetSyntheticNavigator()->Clear(); - category->GetRegexSyntheticNavigator()->Clear(); + category->GetTypeSyntheticsContainer()->Clear(); + category->GetRegexTypeSyntheticsContainer()->Clear(); } result.SetStatus(eReturnStatusSuccessFinishResult); @@ -3631,193 +3763,6 @@ CommandObjectTypeSynthClear::CommandOptions::g_option_table[] = }; -//------------------------------------------------------------------------- -// TypeSynthAddInputReader -//------------------------------------------------------------------------- - -static const char *g_synth_addreader_instructions = "Enter your Python command(s). Type 'DONE' to end.\n" - "You must define a Python class with these methods:\n" - " def __init__(self, valobj, dict):\n" - " def num_children(self):\n" - " def get_child_at_index(self, index):\n" - " def get_child_index(self, name):\n" - "Optionally, you can also define a method:\n" - " def update(self):\n" - "if your synthetic provider is holding on to any per-object state variables (currently, this is not implemented because of the way LLDB handles instances of SBValue and you should not rely on object persistence and per-object state)\n" - "class synthProvider:"; - -class TypeSynthAddInputReader : public InputReaderEZ -{ -public: - TypeSynthAddInputReader(Debugger& debugger) : - InputReaderEZ(debugger) - {} - - virtual - ~TypeSynthAddInputReader() - { - } - - virtual void ActivateHandler(HandlerData& data) - { - StreamSP out_stream = data.GetOutStream(); - bool batch_mode = data.GetBatchMode(); - if (!batch_mode) - { - out_stream->Printf ("%s\n", g_synth_addreader_instructions); - if (data.reader.GetPrompt()) - out_stream->Printf ("%s", data.reader.GetPrompt()); - out_stream->Flush(); - } - } - - virtual void ReactivateHandler(HandlerData& data) - { - StreamSP out_stream = data.GetOutStream(); - bool batch_mode = data.GetBatchMode(); - if (data.reader.GetPrompt() && !batch_mode) - { - out_stream->Printf ("%s", data.reader.GetPrompt()); - out_stream->Flush(); - } - } - virtual void GotTokenHandler(HandlerData& data) - { - StreamSP out_stream = data.GetOutStream(); - bool batch_mode = data.GetBatchMode(); - if (data.bytes && data.bytes_len && data.baton) - { - ((SynthAddOptions*)data.baton)->m_user_source.AppendString(data.bytes, data.bytes_len); - } - if (!data.reader.IsDone() && data.reader.GetPrompt() && !batch_mode) - { - out_stream->Printf ("%s", data.reader.GetPrompt()); - out_stream->Flush(); - } - } - virtual void InterruptHandler(HandlerData& data) - { - StreamSP out_stream = data.GetOutStream(); - bool batch_mode = data.GetBatchMode(); - data.reader.SetIsDone (true); - if (!batch_mode) - { - out_stream->Printf ("Warning: No command attached to breakpoint.\n"); - out_stream->Flush(); - } - } - virtual void EOFHandler(HandlerData& data) - { - data.reader.SetIsDone (true); - } - virtual void DoneHandler(HandlerData& data) - { - StreamSP out_stream = data.GetOutStream(); - SynthAddOptions *options_ptr = ((SynthAddOptions*)data.baton); - if (!options_ptr) - { - out_stream->Printf ("internal synchronization data missing.\n"); - out_stream->Flush(); - return; - } - - SynthAddOptions::SharedPointer options(options_ptr); // this will ensure that we get rid of the pointer when going out of scope - - ScriptInterpreter *interpreter = data.reader.GetDebugger().GetCommandInterpreter().GetScriptInterpreter(); - if (!interpreter) - { - out_stream->Printf ("no script interpreter.\n"); - out_stream->Flush(); - return; - } - std::string class_name_str; - if (!interpreter->GenerateTypeSynthClass (options->m_user_source, - class_name_str)) - { - out_stream->Printf ("unable to generate a class.\n"); - out_stream->Flush(); - return; - } - if (class_name_str.empty()) - { - out_stream->Printf ("unable to obtain a proper name for the class.\n"); - out_stream->Flush(); - return; - } - - // everything should be fine now, let's add the synth provider class - - SyntheticChildrenSP synth_provider; - synth_provider.reset(new ScriptedSyntheticChildren(SyntheticChildren::Flags().SetCascades(options->m_cascade). - SetSkipPointers(options->m_skip_pointers). - SetSkipReferences(options->m_skip_references), - class_name_str.c_str())); - - - lldb::TypeCategoryImplSP category; - DataVisualization::Categories::GetCategory(ConstString(options->m_category.c_str()), category); - - Error error; - - for (size_t i = 0; i < options->m_target_types.GetSize(); i++) - { - const char *type_name = options->m_target_types.GetStringAtIndex(i); - ConstString typeCS(type_name); - if (typeCS) - { - if (!CommandObjectTypeSynthAdd::AddSynth(typeCS, - synth_provider, - options->m_regex ? CommandObjectTypeSynthAdd::eRegexSynth : CommandObjectTypeSynthAdd::eRegularSynth, - options->m_category, - &error)) - { - out_stream->Printf("%s\n", error.AsCString()); - out_stream->Flush(); - return; - } - } - else - { - out_stream->Printf ("invalid type name.\n"); - out_stream->Flush(); - return; - } - } - } - -private: - DISALLOW_COPY_AND_ASSIGN (TypeSynthAddInputReader); -}; - -void -CommandObjectTypeSynthAdd::CollectPythonScript (SynthAddOptions *options, - CommandReturnObject &result) -{ - InputReaderSP reader_sp (new TypeSynthAddInputReader(m_interpreter.GetDebugger())); - if (reader_sp && options) - { - - InputReaderEZ::InitializationParameters ipr; - - Error err (reader_sp->Initialize (ipr.SetBaton(options).SetPrompt(" "))); - if (err.Success()) - { - m_interpreter.GetDebugger().PushInputReader (reader_sp); - result.SetStatus (eReturnStatusSuccessFinishNoResult); - } - else - { - result.AppendError (err.AsCString()); - result.SetStatus (eReturnStatusFailed); - } - } - else - { - result.AppendError("out of memory"); - result.SetStatus (eReturnStatusFailed); - } -} - bool CommandObjectTypeSynthAdd::Execute_HandwritePython (Args& command, CommandReturnObject &result) { @@ -3842,7 +3787,11 @@ CommandObjectTypeSynthAdd::Execute_HandwritePython (Args& command, CommandReturn } } - CollectPythonScript(options,result); + m_interpreter.GetPythonCommandsFromIOHandler (" ", // Prompt + *this, // IOHandlerDelegate + true, // Run IOHandler in async mode + options); // Baton for the "io_handler" that will be passed back into our IOHandlerDelegate functions + result.SetStatus(eReturnStatusSuccessFinishNoResult); return result.Succeeded(); } @@ -3921,6 +3870,7 @@ CommandObjectTypeSynthAdd::CommandObjectTypeSynthAdd (CommandInterpreter &interp "type synthetic add", "Add a new synthetic provider for a type.", NULL), + IOHandlerDelegateMultiline ("DONE"), m_options (interpreter) { CommandArgumentEntry type_arg; @@ -3979,32 +3929,17 @@ CommandObjectTypeSynthAdd::AddSynth(ConstString type_name, return false; } - category->GetRegexSyntheticNavigator()->Delete(type_name); - category->GetRegexSyntheticNavigator()->Add(typeRX, entry); + category->GetRegexTypeSyntheticsContainer()->Delete(type_name); + category->GetRegexTypeSyntheticsContainer()->Add(typeRX, entry); return true; } else { - category->GetSyntheticNavigator()->Add(type_name, entry); + category->GetTypeSyntheticsContainer()->Add(type_name, entry); return true; } } - -bool -CommandObjectTypeSynthAdd::DoExecute (Args& command, CommandReturnObject &result) -{ - if (m_options.handwrite_python) - return Execute_HandwritePython(command, result); - else if (m_options.is_class_based) - return Execute_PythonClass(command, result); - else - { - result.AppendError("must either provide a children list, a Python class name, or use -P and type a Python class line-by-line"); - result.SetStatus(eReturnStatusFailed); - return false; - } -} OptionDefinition CommandObjectTypeSynthAdd::CommandOptions::g_option_table[] = @@ -4173,14 +4108,14 @@ private: return false; } - category->GetRegexFilterNavigator()->Delete(type_name); - category->GetRegexFilterNavigator()->Add(typeRX, entry); + category->GetRegexTypeFiltersContainer()->Delete(type_name); + category->GetRegexTypeFiltersContainer()->Add(typeRX, entry); return true; } else { - category->GetFilterNavigator()->Add(type_name, entry); + category->GetTypeFiltersContainer()->Add(type_name, entry); return true; } } @@ -4279,6 +4214,8 @@ protected: Error error; + WarnOnPotentialUnquotedUnsignedType(command, result); + for (size_t i = 0; i < argc; i++) { const char* typeA = command.GetArgumentAtIndex(i); diff --git a/source/Commands/CommandObjectWatchpointCommand.cpp b/source/Commands/CommandObjectWatchpointCommand.cpp index e19216d74fce..0083ff140e5a 100644 --- a/source/Commands/CommandObjectWatchpointCommand.cpp +++ b/source/Commands/CommandObjectWatchpointCommand.cpp @@ -16,6 +16,7 @@ #include "CommandObjectWatchpointCommand.h" #include "CommandObjectWatchpoint.h" +#include "lldb/Core/IOHandler.h" #include "lldb/Interpreter/CommandInterpreter.h" #include "lldb/Interpreter/CommandReturnObject.h" #include "lldb/Target/Target.h" @@ -34,7 +35,9 @@ using namespace lldb_private; //------------------------------------------------------------------------- -class CommandObjectWatchpointCommandAdd : public CommandObjectParsed +class CommandObjectWatchpointCommandAdd : + public CommandObjectParsed, + public IOHandlerDelegateMultiline { public: @@ -43,6 +46,7 @@ public: "add", "Add a set of commands to a watchpoint, to be executed whenever the watchpoint is hit.", NULL), + IOHandlerDelegateMultiline("DONE", IOHandlerDelegate::Completion::LLDBCommand), m_options (interpreter) { SetHelpLong ( @@ -185,40 +189,45 @@ but do NOT enter more than one command per line. \n" ); return &m_options; } - void - CollectDataForWatchpointCommandCallback (WatchpointOptions *wp_options, - CommandReturnObject &result) + virtual void + IOHandlerActivated (IOHandler &io_handler) { - InputReaderSP reader_sp (new InputReader(m_interpreter.GetDebugger())); - std::unique_ptr<WatchpointOptions::CommandData> data_ap(new WatchpointOptions::CommandData()); - if (reader_sp && data_ap.get()) + StreamFileSP output_sp(io_handler.GetOutputStreamFile()); + if (output_sp) { - BatonSP baton_sp (new WatchpointOptions::CommandBaton (data_ap.release())); - wp_options->SetCallback (WatchpointOptionsCallbackFunction, baton_sp); - - Error err (reader_sp->Initialize (CommandObjectWatchpointCommandAdd::GenerateWatchpointCommandCallback, - wp_options, // callback_data - eInputReaderGranularityLine, // token size, to pass to callback function - "DONE", // end token - "> ", // prompt - true)); // echo input - if (err.Success()) - { - m_interpreter.GetDebugger().PushInputReader (reader_sp); - result.SetStatus (eReturnStatusSuccessFinishNoResult); - } - else - { - result.AppendError (err.AsCString()); - result.SetStatus (eReturnStatusFailed); - } + output_sp->PutCString("Enter your debugger command(s). Type 'DONE' to end.\n"); + output_sp->Flush(); } - else + } + + + virtual void + IOHandlerInputComplete (IOHandler &io_handler, std::string &line) + { + io_handler.SetIsDone(true); + + // The WatchpointOptions object is owned by the watchpoint or watchpoint location + WatchpointOptions *wp_options = (WatchpointOptions *) io_handler.GetUserData(); + if (wp_options) { - result.AppendError("out of memory"); - result.SetStatus (eReturnStatusFailed); + std::unique_ptr<WatchpointOptions::CommandData> data_ap(new WatchpointOptions::CommandData()); + if (data_ap.get()) + { + data_ap->user_source.SplitIntoLines(line); + BatonSP baton_sp (new WatchpointOptions::CommandBaton (data_ap.release())); + wp_options->SetCallback (WatchpointOptionsCallbackFunction, baton_sp); + } } + } + void + CollectDataForWatchpointCommandCallback (WatchpointOptions *wp_options, + CommandReturnObject &result) + { + m_interpreter.GetLLDBCommandsFromIOHandler ("> ", // Prompt + *this, // IOHandlerDelegate + true, // Run IOHandler in async mode + wp_options); // Baton for the "io_handler" that will be passed back into our IOHandlerDelegate functions } /// Set a one-liner as the callback for the watchpoint. @@ -240,93 +249,6 @@ but do NOT enter more than one command per line. \n" ); return; } - - static size_t - GenerateWatchpointCommandCallback (void *callback_data, - InputReader &reader, - lldb::InputReaderAction notification, - const char *bytes, - size_t bytes_len) - { - StreamSP out_stream = reader.GetDebugger().GetAsyncOutputStream(); - bool batch_mode = reader.GetDebugger().GetCommandInterpreter().GetBatchCommandMode(); - - switch (notification) - { - case eInputReaderActivate: - if (!batch_mode) - { - out_stream->Printf ("%s\n", g_reader_instructions); - if (reader.GetPrompt()) - out_stream->Printf ("%s", reader.GetPrompt()); - out_stream->Flush(); - } - break; - - case eInputReaderDeactivate: - break; - - case eInputReaderReactivate: - if (reader.GetPrompt() && !batch_mode) - { - out_stream->Printf ("%s", reader.GetPrompt()); - out_stream->Flush(); - } - break; - - case eInputReaderAsynchronousOutputWritten: - break; - - case eInputReaderGotToken: - if (bytes && bytes_len && callback_data) - { - WatchpointOptions *wp_options = (WatchpointOptions *) callback_data; - if (wp_options) - { - Baton *wp_options_baton = wp_options->GetBaton(); - if (wp_options_baton) - ((WatchpointOptions::CommandData *)wp_options_baton->m_data)->user_source.AppendString (bytes, bytes_len); - } - } - if (!reader.IsDone() && reader.GetPrompt() && !batch_mode) - { - out_stream->Printf ("%s", reader.GetPrompt()); - out_stream->Flush(); - } - break; - - case eInputReaderInterrupt: - { - // Finish, and cancel the watchpoint command. - reader.SetIsDone (true); - WatchpointOptions *wp_options = (WatchpointOptions *) callback_data; - if (wp_options) - { - Baton *wp_options_baton = wp_options->GetBaton (); - if (wp_options_baton) - { - ((WatchpointOptions::CommandData *) wp_options_baton->m_data)->user_source.Clear(); - ((WatchpointOptions::CommandData *) wp_options_baton->m_data)->script_source.clear(); - } - } - if (!batch_mode) - { - out_stream->Printf ("Warning: No command attached to watchpoint.\n"); - out_stream->Flush(); - } - } - break; - - case eInputReaderEndOfFile: - reader.SetIsDone (true); - break; - - case eInputReaderDone: - break; - } - - return bytes_len; - } static bool WatchpointOptionsCallbackFunction (void *baton, @@ -579,12 +501,8 @@ protected: private: CommandOptions m_options; - static const char *g_reader_instructions; - }; -const char * -CommandObjectWatchpointCommandAdd::g_reader_instructions = "Enter your debugger command(s). Type 'DONE' to end."; // FIXME: "script-type" needs to have its contents determined dynamically, so somebody can add a new scripting // language to lldb and have it pickable here without having to change this enumeration by hand and rebuild lldb proper. diff --git a/source/Commands/CommandObjectWatchpointCommand.h b/source/Commands/CommandObjectWatchpointCommand.h index c2faf7187db9..3bc9b3537db7 100644 --- a/source/Commands/CommandObjectWatchpointCommand.h +++ b/source/Commands/CommandObjectWatchpointCommand.h @@ -19,9 +19,6 @@ #include "lldb/lldb-types.h" #include "lldb/Interpreter/Options.h" -#include "lldb/Core/InputReader.h" -#include "lldb/Interpreter/CommandObject.h" -#include "lldb/Interpreter/CommandReturnObject.h" #include "lldb/Interpreter/CommandObjectMultiword.h" diff --git a/source/Core/Address.cpp b/source/Core/Address.cpp index de2165cff84e..5ac2bcce70f0 100644 --- a/source/Core/Address.cpp +++ b/source/Core/Address.cpp @@ -16,6 +16,7 @@ #include "lldb/Symbol/VariableList.h" #include "lldb/Target/ExecutionContext.h" #include "lldb/Target/Process.h" +#include "lldb/Target/SectionLoadList.h" #include "lldb/Target/Target.h" #include "lldb/Symbol/SymbolVendor.h" @@ -327,15 +328,27 @@ Address::GetLoadAddress (Target *target) const addr_t Address::GetCallableLoadAddress (Target *target, bool is_indirect) const { - if (is_indirect && target) { + addr_t code_addr = LLDB_INVALID_ADDRESS; + + if (is_indirect && target) + { ProcessSP processSP = target->GetProcessSP(); Error error; if (processSP.get()) - return processSP->ResolveIndirectFunction(this, error); + { + code_addr = processSP->ResolveIndirectFunction(this, error); + if (!error.Success()) + code_addr = LLDB_INVALID_ADDRESS; + } } - - addr_t code_addr = GetLoadAddress (target); - + else + { + code_addr = GetLoadAddress (target); + } + + if (code_addr == LLDB_INVALID_ADDRESS) + return code_addr; + if (target) return target->GetCallableLoadAddress (code_addr, GetAddressClass()); return code_addr; diff --git a/source/Core/ArchSpec.cpp b/source/Core/ArchSpec.cpp index f2eb3751a4b5..f4fa22437a9c 100644 --- a/source/Core/ArchSpec.cpp +++ b/source/Core/ArchSpec.cpp @@ -104,6 +104,7 @@ static const CoreDefinition g_core_definitions[ArchSpec::kNumCores] = { eByteOrderLittle, 4, 1, 15, llvm::Triple::x86 , ArchSpec::eCore_x86_32_i486sx , "i486sx" }, { eByteOrderLittle, 8, 1, 15, llvm::Triple::x86_64 , ArchSpec::eCore_x86_64_x86_64 , "x86_64" }, + { eByteOrderLittle, 8, 1, 15, llvm::Triple::x86_64 , ArchSpec::eCore_x86_64_x86_64h , "x86_64h" }, { eByteOrderLittle, 4, 4, 4 , llvm::Triple::UnknownArch , ArchSpec::eCore_uknownMach32 , "unknown-mach-32" }, { eByteOrderLittle, 8, 4, 4 , llvm::Triple::UnknownArch , ArchSpec::eCore_uknownMach64 , "unknown-mach-64" } }; @@ -205,10 +206,11 @@ static const ArchDefinitionEntry g_macho_arch_entries[] = { ArchSpec::eCore_x86_32_i386 , llvm::MachO::CPU_TYPE_I386 , 3 , UINT32_MAX , SUBTYPE_MASK }, { ArchSpec::eCore_x86_32_i486 , llvm::MachO::CPU_TYPE_I386 , 4 , UINT32_MAX , SUBTYPE_MASK }, { ArchSpec::eCore_x86_32_i486sx , llvm::MachO::CPU_TYPE_I386 , 0x84 , UINT32_MAX , SUBTYPE_MASK }, - { ArchSpec::eCore_x86_32_i386 , llvm::MachO::CPU_TYPE_I386 , CPU_ANY, UINT32_MAX , UINT32_MAX }, + { ArchSpec::eCore_x86_32_i386 , llvm::MachO::CPU_TYPE_I386 , CPU_ANY, UINT32_MAX , UINT32_MAX }, { ArchSpec::eCore_x86_64_x86_64 , llvm::MachO::CPU_TYPE_X86_64 , 3 , UINT32_MAX , SUBTYPE_MASK }, { ArchSpec::eCore_x86_64_x86_64 , llvm::MachO::CPU_TYPE_X86_64 , 4 , UINT32_MAX , SUBTYPE_MASK }, - { ArchSpec::eCore_x86_64_x86_64 , llvm::MachO::CPU_TYPE_X86_64 , CPU_ANY, UINT32_MAX , UINT32_MAX }, + { ArchSpec::eCore_x86_64_x86_64h , llvm::MachO::CPU_TYPE_X86_64 , 8 , UINT32_MAX , SUBTYPE_MASK }, + { ArchSpec::eCore_x86_64_x86_64 , llvm::MachO::CPU_TYPE_X86_64 , CPU_ANY, UINT32_MAX , UINT32_MAX }, // Catch any unknown mach architectures so we can always use the object and symbol mach-o files { ArchSpec::eCore_uknownMach32 , 0 , 0 , 0xFF000000u, 0x00000000u }, { ArchSpec::eCore_uknownMach64 , llvm::MachO::CPU_ARCH_ABI64 , 0 , 0xFF000000u, 0x00000000u } @@ -349,14 +351,16 @@ FindArchDefinitionEntry (const ArchDefinition *def, ArchSpec::Core core) ArchSpec::ArchSpec() : m_triple (), m_core (kCore_invalid), - m_byte_order (eByteOrderInvalid) + m_byte_order (eByteOrderInvalid), + m_distribution_id () { } ArchSpec::ArchSpec (const char *triple_cstr, Platform *platform) : m_triple (), m_core (kCore_invalid), - m_byte_order (eByteOrderInvalid) + m_byte_order (eByteOrderInvalid), + m_distribution_id () { if (triple_cstr) SetTriple(triple_cstr, platform); @@ -366,7 +370,8 @@ ArchSpec::ArchSpec (const char *triple_cstr, Platform *platform) : ArchSpec::ArchSpec (const char *triple_cstr) : m_triple (), m_core (kCore_invalid), - m_byte_order (eByteOrderInvalid) + m_byte_order (eByteOrderInvalid), + m_distribution_id () { if (triple_cstr) SetTriple(triple_cstr); @@ -375,7 +380,8 @@ ArchSpec::ArchSpec (const char *triple_cstr) : ArchSpec::ArchSpec(const llvm::Triple &triple) : m_triple (), m_core (kCore_invalid), - m_byte_order (eByteOrderInvalid) + m_byte_order (eByteOrderInvalid), + m_distribution_id () { SetTriple(triple); } @@ -383,7 +389,8 @@ ArchSpec::ArchSpec(const llvm::Triple &triple) : ArchSpec::ArchSpec (ArchitectureType arch_type, uint32_t cpu, uint32_t subtype) : m_triple (), m_core (kCore_invalid), - m_byte_order (eByteOrderInvalid) + m_byte_order (eByteOrderInvalid), + m_distribution_id () { SetArchitecture (arch_type, cpu, subtype); } @@ -403,6 +410,7 @@ ArchSpec::operator= (const ArchSpec& rhs) m_triple = rhs.m_triple; m_core = rhs.m_core; m_byte_order = rhs.m_byte_order; + m_distribution_id = rhs.m_distribution_id; } return *this; } @@ -413,6 +421,7 @@ ArchSpec::Clear() m_triple = llvm::Triple(); m_core = kCore_invalid; m_byte_order = eByteOrderInvalid; + m_distribution_id.Clear (); } //===----------------------------------------------------------------------===// @@ -468,6 +477,18 @@ ArchSpec::GetMachine () const return llvm::Triple::UnknownArch; } +const ConstString& +ArchSpec::GetDistributionId () const +{ + return m_distribution_id; +} + +void +ArchSpec::SetDistributionId (const char* distribution_id) +{ + m_distribution_id.SetCString (distribution_id); +} + uint32_t ArchSpec::GetAddressByteSize() const { @@ -763,6 +784,8 @@ ArchSpec::IsCompatibleMatch (const ArchSpec& rhs) const bool ArchSpec::IsEqualTo (const ArchSpec& rhs, bool exact_match) const { + // explicitly ignoring m_distribution_id in this method. + if (GetByteOrder() != rhs.GetByteOrder()) return false; @@ -873,7 +896,7 @@ cores_match (const ArchSpec::Core core1, const ArchSpec::Core core2, bool try_in if (core2 == ArchSpec::kCore_arm_any) return true; break; - + case ArchSpec::kCore_x86_32_any: if ((core2 >= ArchSpec::kCore_x86_32_first && core2 <= ArchSpec::kCore_x86_32_last) || (core2 == ArchSpec::kCore_x86_32_any)) return true; diff --git a/source/Core/Broadcaster.cpp b/source/Core/Broadcaster.cpp index 5af7497c8da9..88f39961832f 100644 --- a/source/Core/Broadcaster.cpp +++ b/source/Core/Broadcaster.cpp @@ -313,18 +313,22 @@ Broadcaster::RestoreBroadcaster () { Mutex::Locker event_types_locker(m_listeners_mutex); - Log *log(lldb_private::GetLogIfAnyCategoriesSet (LIBLLDB_LOG_EVENTS)); - if (log) + if (!m_hijacking_listeners.empty()) { - Listener *listener = m_hijacking_listeners.back(); - log->Printf ("%p Broadcaster(\"%s\")::RestoreBroadcaster (about to pop listener(\"%s\")=%p)", - this, - m_broadcaster_name.AsCString(""), - listener->m_name.c_str(), - listener); + Log *log(lldb_private::GetLogIfAnyCategoriesSet (LIBLLDB_LOG_EVENTS)); + if (log) + { + Listener *listener = m_hijacking_listeners.back(); + log->Printf ("%p Broadcaster(\"%s\")::RestoreBroadcaster (about to pop listener(\"%s\")=%p)", + this, + m_broadcaster_name.AsCString(""), + listener->m_name.c_str(), + listener); + } + m_hijacking_listeners.pop_back(); } - m_hijacking_listeners.pop_back(); - m_hijacking_masks.pop_back(); + if (!m_hijacking_masks.empty()) + m_hijacking_masks.pop_back(); } ConstString & diff --git a/source/Core/Communication.cpp b/source/Core/Communication.cpp index 6ea7a11426be..f05ce320b5be 100644 --- a/source/Core/Communication.cpp +++ b/source/Core/Communication.cpp @@ -269,6 +269,16 @@ Communication::StopReadThread (Error *error_ptr) return status; } +bool +Communication::JoinReadThread (Error *error_ptr) +{ + if (!IS_VALID_LLDB_HOST_THREAD(m_read_thread)) + return true; + + bool success = Host::ThreadJoin (m_read_thread, NULL, error_ptr); + m_read_thread = LLDB_INVALID_HOST_THREAD; + return success; +} size_t Communication::GetCachedBytes (void *dst, size_t dst_len) diff --git a/source/Core/ConnectionFileDescriptor.cpp b/source/Core/ConnectionFileDescriptor.cpp index 5764a212ab43..ed876e52c9af 100644 --- a/source/Core/ConnectionFileDescriptor.cpp +++ b/source/Core/ConnectionFileDescriptor.cpp @@ -97,11 +97,11 @@ ConnectionFileDescriptor::ConnectionFileDescriptor () : m_fd_send_type (eFDTypeFile), m_fd_recv_type (eFDTypeFile), m_udp_send_sockaddr (new SocketAddress()), - m_should_close_fd (false), m_socket_timeout_usec(0), m_pipe_read(-1), m_pipe_write(-1), m_mutex (Mutex::eMutexTypeRecursive), + m_should_close_fd (false), m_shutting_down (false) { Log *log(lldb_private::GetLogIfAnyCategoriesSet (LIBLLDB_LOG_CONNECTION | LIBLLDB_LOG_OBJECT)); @@ -116,11 +116,11 @@ ConnectionFileDescriptor::ConnectionFileDescriptor (int fd, bool owns_fd) : m_fd_send_type (eFDTypeFile), m_fd_recv_type (eFDTypeFile), m_udp_send_sockaddr (new SocketAddress()), - m_should_close_fd (owns_fd), m_socket_timeout_usec(0), m_pipe_read(-1), m_pipe_write(-1), m_mutex (Mutex::eMutexTypeRecursive), + m_should_close_fd (owns_fd), m_shutting_down (false) { Log *log(lldb_private::GetLogIfAnyCategoriesSet (LIBLLDB_LOG_CONNECTION | LIBLLDB_LOG_OBJECT)); @@ -218,12 +218,15 @@ ConnectionFileDescriptor::Connect (const char *s, Error *error_ptr) if (s && s[0]) { - char *end = NULL; if (strstr(s, "listen://")) { // listen://HOST:PORT - unsigned long listen_port = ::strtoul(s + strlen("listen://"), &end, 0); - return SocketListen (listen_port, error_ptr); + return SocketListen (s + strlen("listen://"), error_ptr); + } + else if (strstr(s, "accept://")) + { + // unix://SOCKNAME + return NamedSocketAccept (s + strlen("accept://"), error_ptr); } else if (strstr(s, "unix-accept://")) { @@ -363,6 +366,9 @@ ConnectionFileDescriptor::Disconnect (Error *error_ptr) if (log) log->Printf ("%p ConnectionFileDescriptor::Disconnect ()", this); + // Reset the port predicate when disconnecting and don't broadcast + m_port_predicate.SetValue(0, eBroadcastNever); + ConnectionStatus status = eConnectionStatusSuccess; if (m_fd_send < 0 && m_fd_recv < 0) @@ -1281,16 +1287,31 @@ ConnectionFileDescriptor::NamedSocketConnect (const char *socket_name, Error *er } ConnectionStatus -ConnectionFileDescriptor::SocketListen (uint16_t listen_port_num, Error *error_ptr) +ConnectionFileDescriptor::SocketListen (const char *host_and_port, Error *error_ptr) { Log *log(lldb_private::GetLogIfAnyCategoriesSet (LIBLLDB_LOG_CONNECTION)); if (log) - log->Printf ("%p ConnectionFileDescriptor::SocketListen (port = %i)", this, listen_port_num); + log->Printf ("%p ConnectionFileDescriptor::SocketListen (%s)", this, host_and_port); Disconnect (NULL); m_fd_send_type = m_fd_recv_type = eFDTypeSocket; - int listen_port = ::socket (AF_INET, SOCK_STREAM, IPPROTO_TCP); - if (listen_port == -1) + std::string host_str; + std::string port_str; + int32_t port = INT32_MIN; + if (!DecodeHostAndPort (host_and_port, host_str, port_str, port, error_ptr)) + { + // Might be just a port number + port = Args::StringToSInt32(host_and_port, -1); + if (port == -1) + return eConnectionStatusError; + else + host_str.clear(); + } + const sa_family_t family = AF_INET; + const int socktype = SOCK_STREAM; + const int protocol = IPPROTO_TCP; + int listen_fd = ::socket (family, socktype, protocol); + if (listen_fd == -1) { if (error_ptr) error_ptr->SetErrorToErrno(); @@ -1298,41 +1319,119 @@ ConnectionFileDescriptor::SocketListen (uint16_t listen_port_num, Error *error_p } // enable local address reuse - SetSocketOption (listen_port, SOL_SOCKET, SO_REUSEADDR, 1); + SetSocketOption (listen_fd, SOL_SOCKET, SO_REUSEADDR, 1); - SocketAddress localhost; - if (localhost.SetToLocalhost (AF_INET, listen_port_num)) + SocketAddress listen_addr; + if (host_str.empty()) + listen_addr.SetToLocalhost(family, port); + else if (host_str.compare("*") == 0) + listen_addr.SetToAnyAddress(family, port); + else { - int err = ::bind (listen_port, localhost, localhost.GetLength()); + if (!listen_addr.getaddrinfo(host_str.c_str(), port_str.c_str(), family, socktype, protocol)) + { + if (error_ptr) + error_ptr->SetErrorStringWithFormat("unable to resolve hostname '%s'", host_str.c_str()); + Close (listen_fd, eFDTypeSocket, NULL); + return eConnectionStatusError; + } + } + + SocketAddress anyaddr; + if (anyaddr.SetToAnyAddress (family, port)) + { + int err = ::bind (listen_fd, anyaddr, anyaddr.GetLength()); if (err == -1) { if (error_ptr) error_ptr->SetErrorToErrno(); - Close (listen_port, eFDTypeSocket, NULL); + Close (listen_fd, eFDTypeSocket, NULL); return eConnectionStatusError; } - err = ::listen (listen_port, 1); + err = ::listen (listen_fd, 1); if (err == -1) { if (error_ptr) error_ptr->SetErrorToErrno(); - Close (listen_port, eFDTypeSocket, NULL); + Close (listen_fd, eFDTypeSocket, NULL); return eConnectionStatusError; } - m_fd_send = m_fd_recv = ::accept (listen_port, NULL, 0); + // We were asked to listen on port zero which means we + // must now read the actual port that was given to us + // as port zero is a special code for "find an open port + // for me". + if (port == 0) + port = GetSocketPort(listen_fd); + + // Set the port predicate since when doing a listen://<host>:<port> + // it often needs to accept the incoming connection which is a blocking + // system call. Allowing access to the bound port using a predicate allows + // us to wait for the port predicate to be set to a non-zero value from + // another thread in an efficient manor. + m_port_predicate.SetValue(port, eBroadcastAlways); + + + bool accept_connection = false; + + // Loop until we are happy with our connection + while (!accept_connection) + { + struct sockaddr_in accept_addr; + ::memset (&accept_addr, 0, sizeof accept_addr); +#if !(defined (__linux__) || defined(_MSC_VER)) + accept_addr.sin_len = sizeof accept_addr; +#endif + socklen_t accept_addr_len = sizeof accept_addr; + + int fd = ::accept (listen_fd, (struct sockaddr *)&accept_addr, &accept_addr_len); + + if (fd == -1) + { + if (error_ptr) + error_ptr->SetErrorToErrno(); + break; + } + + if (listen_addr.sockaddr_in().sin_addr.s_addr == INADDR_ANY) + { + accept_connection = true; + m_fd_send = m_fd_recv = fd; + } + else + { + if ( +#if !(defined(__linux__) || (defined(_MSC_VER))) + accept_addr_len == listen_addr.sockaddr_in().sin_len && +#endif + accept_addr.sin_addr.s_addr == listen_addr.sockaddr_in().sin_addr.s_addr) + { + accept_connection = true; + m_fd_send = m_fd_recv = fd; + } + else + { + ::close (fd); + m_fd_send = m_fd_recv = -1; + const uint8_t *accept_ip = (const uint8_t *)&accept_addr.sin_addr.s_addr; + const uint8_t *listen_ip = (const uint8_t *)&listen_addr.sockaddr_in().sin_addr.s_addr; + ::fprintf (stderr, "error: rejecting incoming connection from %u.%u.%u.%u (expecting %u.%u.%u.%u)\n", + accept_ip[0], accept_ip[1], accept_ip[2], accept_ip[3], + listen_ip[0], listen_ip[1], listen_ip[2], listen_ip[3]); + } + } + } + if (m_fd_send == -1) { - if (error_ptr) - error_ptr->SetErrorToErrno(); - Close (listen_port, eFDTypeSocket, NULL); + Close (listen_fd, eFDTypeSocket, NULL); return eConnectionStatusError; } } // We are done with the listen port - Close (listen_port, eFDTypeSocket, NULL); + Close (listen_fd, eFDTypeSocket, NULL); m_should_close_fd = true; @@ -1446,7 +1545,7 @@ ConnectionFileDescriptor::ConnectUDP (const char *host_and_port, Error *error_pt { // Socket was created, now lets bind to the requested port SocketAddress addr; - addr.SetToLocalhost (AF_INET, 0); + addr.SetToAnyAddress (AF_INET, 0); if (::bind (m_fd_recv, addr, addr.GetLength()) == -1) { @@ -1581,21 +1680,23 @@ ConnectionFileDescriptor::SetSocketReceiveTimeout (uint32_t timeout_usec) return false; } -in_port_t +uint16_t ConnectionFileDescriptor::GetSocketPort (int fd) { // We bound to port zero, so we need to figure out which port we actually bound to - SocketAddress sock_addr; - socklen_t sock_addr_len = sock_addr.GetMaxLength (); - if (::getsockname (fd, sock_addr, &sock_addr_len) == 0) - return sock_addr.GetPort (); - + if (fd >= 0) + { + SocketAddress sock_addr; + socklen_t sock_addr_len = sock_addr.GetMaxLength (); + if (::getsockname (fd, sock_addr, &sock_addr_len) == 0) + return sock_addr.GetPort (); + } return 0; } // If the read file descriptor is a socket, then return // the port number that is being used by the socket. -in_port_t +uint16_t ConnectionFileDescriptor::GetReadPort () const { return ConnectionFileDescriptor::GetSocketPort (m_fd_recv); @@ -1603,10 +1704,23 @@ ConnectionFileDescriptor::GetReadPort () const // If the write file descriptor is a socket, then return // the port number that is being used by the socket. -in_port_t +uint16_t ConnectionFileDescriptor::GetWritePort () const { return ConnectionFileDescriptor::GetSocketPort (m_fd_send); } - +uint16_t +ConnectionFileDescriptor::GetBoundPort (uint32_t timeout_sec) +{ + uint16_t bound_port = 0; + if (timeout_sec == UINT32_MAX) + m_port_predicate.WaitForValueNotEqualTo (0, bound_port); + else + { + TimeValue timeout = TimeValue::Now(); + timeout.OffsetWithSeconds(timeout_sec); + m_port_predicate.WaitForValueNotEqualTo (0, bound_port, &timeout); + } + return bound_port; +} diff --git a/source/Core/DataExtractor.cpp b/source/Core/DataExtractor.cpp index d1f3c09c2305..b42c6ff31449 100644 --- a/source/Core/DataExtractor.cpp +++ b/source/Core/DataExtractor.cpp @@ -37,6 +37,7 @@ #include "lldb/Symbol/ClangASTContext.h" #include "lldb/Target/ExecutionContext.h" #include "lldb/Target/ExecutionContextScope.h" +#include "lldb/Target/SectionLoadList.h" #include "lldb/Target/Target.h" using namespace lldb; @@ -45,69 +46,97 @@ using namespace lldb_private; static inline uint16_t ReadInt16(const unsigned char* ptr, offset_t offset) { - return *(uint16_t *)(ptr + offset); + uint16_t value; + memcpy (&value, ptr + offset, 2); + return value; } + static inline uint32_t ReadInt32 (const unsigned char* ptr, offset_t offset) { - return *(uint32_t *)(ptr + offset); + uint32_t value; + memcpy (&value, ptr + offset, 4); + return value; } static inline uint64_t ReadInt64(const unsigned char* ptr, offset_t offset) { - return *(uint64_t *)(ptr + offset); + uint64_t value; + memcpy (&value, ptr + offset, 8); + return value; } static inline uint16_t ReadInt16(const void* ptr) { - return *(uint16_t *)(ptr); + uint16_t value; + memcpy (&value, ptr, 2); + return value; } + static inline uint32_t ReadInt32 (const void* ptr) { - return *(uint32_t *)(ptr); + uint32_t value; + memcpy (&value, ptr, 4); + return value; } static inline uint64_t ReadInt64(const void* ptr) { - return *(uint64_t *)(ptr); + uint64_t value; + memcpy (&value, ptr, 8); + return value; } static inline uint16_t ReadSwapInt16(const unsigned char* ptr, offset_t offset) { - return llvm::ByteSwap_16(*(uint16_t *)(ptr + offset)); + uint16_t value; + memcpy (&value, ptr + offset, 2); + return llvm::ByteSwap_16(value); } static inline uint32_t ReadSwapInt32 (const unsigned char* ptr, offset_t offset) { - return llvm::ByteSwap_32(*(uint32_t *)(ptr + offset)); + uint32_t value; + memcpy (&value, ptr + offset, 4); + return llvm::ByteSwap_32(value); } + static inline uint64_t ReadSwapInt64(const unsigned char* ptr, offset_t offset) { - return llvm::ByteSwap_64(*(uint64_t *)(ptr + offset)); + uint64_t value; + memcpy (&value, ptr + offset, 8); + return llvm::ByteSwap_64(value); } static inline uint16_t ReadSwapInt16(const void* ptr) { - return llvm::ByteSwap_16(*(uint16_t *)(ptr)); + uint16_t value; + memcpy (&value, ptr, 2); + return llvm::ByteSwap_16(value); } static inline uint32_t ReadSwapInt32 (const void* ptr) { - return llvm::ByteSwap_32(*(uint32_t *)(ptr)); + uint32_t value; + memcpy (&value, ptr, 4); + return llvm::ByteSwap_32(value); } + static inline uint64_t ReadSwapInt64(const void* ptr) { - return llvm::ByteSwap_64(*(uint64_t *)(ptr)); + uint64_t value; + memcpy (&value, ptr, 8); + return llvm::ByteSwap_64(value); } #define NON_PRINTABLE_CHAR '.' @@ -502,13 +531,17 @@ uint32_t DataExtractor::GetU32 (offset_t *offset_ptr) const { uint32_t val = 0; - const uint32_t *data = (const uint32_t *)GetData (offset_ptr, sizeof(val)); + const uint8_t *data = (const uint8_t *)GetData (offset_ptr, sizeof(val)); if (data) { if (m_byte_order != lldb::endian::InlHostByteOrder()) + { val = ReadSwapInt32 (data); + } else - val = *data; + { + memcpy (&val, data, 4); + } } return val; } @@ -561,13 +594,17 @@ uint64_t DataExtractor::GetU64 (offset_t *offset_ptr) const { uint64_t val = 0; - const uint64_t *data = (const uint64_t *)GetData (offset_ptr, sizeof(val)); + const uint8_t *data = (const uint8_t *)GetData (offset_ptr, sizeof(val)); if (data) { if (m_byte_order != lldb::endian::InlHostByteOrder()) + { val = ReadSwapInt64 (data); + } else - val = *data; + { + memcpy (&val, data, 8); + } } return val; } @@ -1808,6 +1845,7 @@ DataExtractor::Dump (Stream *s, case ArchSpec::eCore_x86_32_i486: case ArchSpec::eCore_x86_32_i486sx: case ArchSpec::eCore_x86_64_x86_64: + case ArchSpec::eCore_x86_64_x86_64h: // clang will assert when contructing the apfloat if we use a 16 byte integer value if (GetAPInt (*this, &offset, 10, apint)) { diff --git a/source/Core/Debugger.cpp b/source/Core/Debugger.cpp index b57c6051a961..5b346ed636d6 100644 --- a/source/Core/Debugger.cpp +++ b/source/Core/Debugger.cpp @@ -18,13 +18,13 @@ #include "lldb/lldb-private.h" #include "lldb/Core/ConnectionFileDescriptor.h" -#include "lldb/Core/InputReader.h" #include "lldb/Core/Module.h" #include "lldb/Core/PluginManager.h" #include "lldb/Core/RegisterValue.h" #include "lldb/Core/State.h" #include "lldb/Core/StreamAsynchronousIO.h" #include "lldb/Core/StreamCallback.h" +#include "lldb/Core/StreamFile.h" #include "lldb/Core/StreamString.h" #include "lldb/Core/Timer.h" #include "lldb/Core/ValueObject.h" @@ -180,6 +180,7 @@ Debugger::SetPropertyValue (const ExecutionContext *exe_ctx, std::string str = lldb_utility::ansi::FormatAnsiTerminalCodes (new_prompt, GetUseColor()); if (str.length()) new_prompt = str.c_str(); + GetCommandInterpreter().UpdatePrompt(new_prompt); EventSP prompt_change_event_sp (new Event(CommandInterpreter::eBroadcastBitResetPrompt, new EventDataBytes (new_prompt))); GetCommandInterpreter().BroadcastEvent (prompt_change_event_sp); } @@ -196,12 +197,16 @@ Debugger::SetPropertyValue (const ExecutionContext *exe_ctx, StreamString feedback_stream; if (!target_sp->LoadScriptingResources(errors,&feedback_stream)) { - for (auto error : errors) + StreamFileSP stream_sp (GetErrorFile()); + if (stream_sp) { - GetErrorStream().Printf("%s\n",error.AsCString()); + for (auto error : errors) + { + stream_sp->Printf("%s\n",error.AsCString()); + } + if (feedback_stream.GetSize()) + stream_sp->Printf("%s",feedback_stream.GetData()); } - if (feedback_stream.GetSize()) - GetErrorStream().Printf("%s",feedback_stream.GetData()); } } } @@ -246,8 +251,7 @@ Debugger::SetPrompt(const char *p) std::string str = lldb_utility::ansi::FormatAnsiTerminalCodes (new_prompt, GetUseColor()); if (str.length()) new_prompt = str.c_str(); - EventSP prompt_change_event_sp (new Event(CommandInterpreter::eBroadcastBitResetPrompt, new EventDataBytes (new_prompt)));; - GetCommandInterpreter().BroadcastEvent (prompt_change_event_sp); + GetCommandInterpreter().UpdatePrompt(new_prompt); } const char * @@ -611,10 +615,9 @@ Debugger::FindTargetWithProcess (Process *process) Debugger::Debugger (lldb::LogOutputCallback log_callback, void *baton) : UserID (g_unique_id++), Properties(OptionValuePropertiesSP(new OptionValueProperties())), - m_input_comm("debugger.input"), - m_input_file (), - m_output_file (), - m_error_file (), + 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_terminal_state (), m_target_list (*this), m_platform_list (), @@ -623,8 +626,11 @@ Debugger::Debugger (lldb::LogOutputCallback log_callback, void *baton) : m_source_file_cache(), m_command_interpreter_ap (new CommandInterpreter (*this, eScriptLanguageDefault, false)), m_input_reader_stack (), - m_input_reader_data (), - m_instance_name() + m_instance_name (), + m_loaded_plugins (), + m_event_handler_thread (LLDB_INVALID_HOST_THREAD), + m_io_handler_thread (LLDB_INVALID_HOST_THREAD), + m_event_handler_thread_alive(false) { char instance_cstr[256]; snprintf(instance_cstr, sizeof(instance_cstr), "debugger_%d", (int)GetID()); @@ -667,7 +673,9 @@ Debugger::~Debugger () void Debugger::Clear() { - CleanUpInputReaders(); + ClearIOHandlers(); + StopIOHandlerThread(); + StopEventHandlerThread(); m_listener.Clear(); int num_targets = m_target_list.GetNumTargets(); for (int i = 0; i < num_targets; i++) @@ -686,23 +694,21 @@ Debugger::Clear() // 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(); - GetInputFile().Close (); - // Now that we have closed m_input_file, we can now tell our input communication - // class to close down. Its read thread should quickly exit after we close - // the input file handle above. - m_input_comm.Clear (); + if (m_input_file_sp) + m_input_file_sp->GetFile().Close (); } bool Debugger::GetCloseInputOnEOF () const { - return m_input_comm.GetCloseOnEOF(); +// return m_input_comm.GetCloseOnEOF(); + return false; } void Debugger::SetCloseInputOnEOF (bool b) { - m_input_comm.SetCloseOnEOF(b); +// m_input_comm.SetCloseOnEOF(b); } bool @@ -721,37 +727,28 @@ Debugger::SetAsyncExecution (bool async_execution) void Debugger::SetInputFileHandle (FILE *fh, bool tranfer_ownership) { - File &in_file = GetInputFile(); - in_file.SetStream (fh, 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() == false) in_file.SetStream (stdin, true); - // Disconnect from any old connection if we had one - m_input_comm.Disconnect (); - // Pass false as the second argument to ConnectionFileDescriptor below because - // our "in_file" above will already take ownership if requested and we don't - // want to objects trying to own and close a file descriptor. - m_input_comm.SetConnection (new ConnectionFileDescriptor (in_file.GetDescriptor(), false)); - m_input_comm.SetReadThreadBytesReceivedCallback (Debugger::DispatchInputCallback, this); - // Save away the terminal state if that is relevant, so that we can restore it in RestoreInputState. SaveInputTerminalState (); - - Error error; - if (m_input_comm.StartReadThread (&error) == false) - { - File &err_file = GetErrorFile(); - - err_file.Printf ("error: failed to main input read thread: %s", error.AsCString() ? error.AsCString() : "unkown error"); - exit(1); - } } void Debugger::SetOutputFileHandle (FILE *fh, bool tranfer_ownership) { - File &out_file = GetOutputFile(); - out_file.SetStream (fh, 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)); + + File &out_file = m_output_file_sp->GetFile(); if (out_file.IsValid() == false) out_file.SetStream (stdout, false); @@ -766,8 +763,12 @@ Debugger::SetOutputFileHandle (FILE *fh, bool tranfer_ownership) void Debugger::SetErrorFileHandle (FILE *fh, bool tranfer_ownership) { - File &err_file = GetErrorFile(); - err_file.SetStream (fh, 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() == false) err_file.SetStream (stderr, false); } @@ -775,9 +776,12 @@ Debugger::SetErrorFileHandle (FILE *fh, bool tranfer_ownership) void Debugger::SaveInputTerminalState () { - File &in_file = GetInputFile(); - if (in_file.GetDescriptor() != File::kInvalidDescriptor) - m_terminal_state.Save(in_file.GetDescriptor(), true); + 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 @@ -812,245 +816,211 @@ Debugger::GetSelectedExecutionContext () return exe_ctx; } -InputReaderSP -Debugger::GetCurrentInputReader () -{ - InputReaderSP reader_sp; - - if (!m_input_reader_stack.IsEmpty()) - { - // Clear any finished readers from the stack - while (CheckIfTopInputReaderIsDone()) ; - - if (!m_input_reader_stack.IsEmpty()) - reader_sp = m_input_reader_stack.Top(); - } - - return reader_sp; -} - -void -Debugger::DispatchInputCallback (void *baton, const void *bytes, size_t bytes_len) -{ - if (bytes_len > 0) - ((Debugger *)baton)->DispatchInput ((char *)bytes, bytes_len); - else - ((Debugger *)baton)->DispatchInputEndOfFile (); -} - - -void -Debugger::DispatchInput (const char *bytes, size_t bytes_len) -{ - if (bytes == NULL || bytes_len == 0) - return; - - WriteToDefaultReader (bytes, bytes_len); -} - void Debugger::DispatchInputInterrupt () { - m_input_reader_data.clear(); - - InputReaderSP reader_sp (GetCurrentInputReader ()); + Mutex::Locker locker (m_input_reader_stack.GetMutex()); + IOHandlerSP reader_sp (m_input_reader_stack.Top()); if (reader_sp) - { - reader_sp->Notify (eInputReaderInterrupt); - - // If notifying the reader of the interrupt finished the reader, we should pop it off the stack. - while (CheckIfTopInputReaderIsDone ()) ; - } + reader_sp->Interrupt(); } void Debugger::DispatchInputEndOfFile () { - m_input_reader_data.clear(); - - InputReaderSP reader_sp (GetCurrentInputReader ()); + Mutex::Locker locker (m_input_reader_stack.GetMutex()); + IOHandlerSP reader_sp (m_input_reader_stack.Top()); if (reader_sp) - { - reader_sp->Notify (eInputReaderEndOfFile); - - // If notifying the reader of the end-of-file finished the reader, we should pop it off the stack. - while (CheckIfTopInputReaderIsDone ()) ; - } + reader_sp->GotEOF(); } void -Debugger::CleanUpInputReaders () +Debugger::ClearIOHandlers () { - m_input_reader_data.clear(); - // The bottom input reader should be the main debugger input reader. We do not want to close that one here. + Mutex::Locker locker (m_input_reader_stack.GetMutex()); while (m_input_reader_stack.GetSize() > 1) { - InputReaderSP reader_sp (GetCurrentInputReader ()); + IOHandlerSP reader_sp (m_input_reader_stack.Top()); if (reader_sp) { - reader_sp->Notify (eInputReaderEndOfFile); - reader_sp->SetIsDone (true); + m_input_reader_stack.Pop(); + reader_sp->SetIsDone(true); + reader_sp->Interrupt(); } } } void -Debugger::NotifyTopInputReader (InputReaderAction notification) +Debugger::ExecuteIOHanders() { - InputReaderSP reader_sp (GetCurrentInputReader()); - if (reader_sp) - { - reader_sp->Notify (notification); + + while (1) + { + IOHandlerSP reader_sp(m_input_reader_stack.Top()); + if (!reader_sp) + break; - // Flush out any input readers that are done. - while (CheckIfTopInputReaderIsDone ()) - /* Do nothing. */; + reader_sp->Activate(); + reader_sp->Run(); + reader_sp->Deactivate(); + + // Remove all input readers that are done from the top of the stack + while (1) + { + IOHandlerSP top_reader_sp = m_input_reader_stack.Top(); + if (top_reader_sp && top_reader_sp->GetIsDone()) + m_input_reader_stack.Pop(); + else + break; + } } + ClearIOHandlers(); } bool -Debugger::InputReaderIsTopReader (const InputReaderSP& reader_sp) +Debugger::IsTopIOHandler (const lldb::IOHandlerSP& reader_sp) { - InputReaderSP top_reader_sp (GetCurrentInputReader()); + return m_input_reader_stack.IsTop (reader_sp); +} + - return (reader_sp.get() == top_reader_sp.get()); +ConstString +Debugger::GetTopIOHandlerControlSequence(char ch) +{ + return m_input_reader_stack.GetTopIOHandlerControlSequence (ch); } - void -Debugger::WriteToDefaultReader (const char *bytes, size_t bytes_len) +Debugger::RunIOHandler (const IOHandlerSP& reader_sp) { - if (bytes && bytes_len) - m_input_reader_data.append (bytes, bytes_len); - - if (m_input_reader_data.empty()) - return; + Mutex::Locker locker (m_input_reader_stack.GetMutex()); + PushIOHandler (reader_sp); + reader_sp->Activate(); + reader_sp->Run(); + PopIOHandler (reader_sp); +} - while (!m_input_reader_stack.IsEmpty() && !m_input_reader_data.empty()) +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 NULL. 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. + + Mutex::Locker locker (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) { - // Get the input reader from the top of the stack - InputReaderSP reader_sp (GetCurrentInputReader ()); - if (!reader_sp) - break; - - size_t bytes_handled = reader_sp->HandleRawBytes (m_input_reader_data.c_str(), - m_input_reader_data.size()); - if (bytes_handled) - { - m_input_reader_data.erase (0, bytes_handled); - } + if (top_reader_sp) + in = top_reader_sp->GetInputStreamFile(); else - { - // No bytes were handled, we might not have reached our - // granularity, just return and wait for more data - break; - } + in = GetInputFile(); + + // If there is nothing, use stdin + if (!in) + 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 + if (!out) + 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 + if (!err) + err = StreamFileSP(new StreamFile(stdout, false)); + } - - // Flush out any input readers that are done. - while (CheckIfTopInputReaderIsDone ()) - /* Do nothing. */; - } void -Debugger::PushInputReader (const InputReaderSP& reader_sp) +Debugger::PushIOHandler (const IOHandlerSP& reader_sp) { if (!reader_sp) return; - // Deactivate the old top reader - InputReaderSP top_reader_sp (GetCurrentInputReader ()); + // Got the current top input reader... + IOHandlerSP top_reader_sp (m_input_reader_stack.Top()); - if (top_reader_sp) - top_reader_sp->Notify (eInputReaderDeactivate); - + // Push our new input reader m_input_reader_stack.Push (reader_sp); - reader_sp->Notify (eInputReaderActivate); - ActivateInputReader (reader_sp); + + // 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(); } bool -Debugger::PopInputReader (const InputReaderSP& pop_reader_sp) +Debugger::PopIOHandler (const IOHandlerSP& pop_reader_sp) { bool result = false; + + Mutex::Locker locker (m_input_reader_stack.GetMutex()); // The reader on the stop of the stack is done, so let the next // read on the stack referesh its prompt and if there is one... if (!m_input_reader_stack.IsEmpty()) { - // Cannot call GetCurrentInputReader here, as that would cause an infinite loop. - InputReaderSP reader_sp(m_input_reader_stack.Top()); + IOHandlerSP reader_sp(m_input_reader_stack.Top()); if (!pop_reader_sp || pop_reader_sp.get() == reader_sp.get()) { + reader_sp->Deactivate(); m_input_reader_stack.Pop (); - reader_sp->Notify (eInputReaderDeactivate); - reader_sp->Notify (eInputReaderDone); - result = true; + + reader_sp = m_input_reader_stack.Top(); + if (reader_sp) + reader_sp->Activate(); - if (!m_input_reader_stack.IsEmpty()) - { - reader_sp = m_input_reader_stack.Top(); - if (reader_sp) - { - ActivateInputReader (reader_sp); - reader_sp->Notify (eInputReaderReactivate); - } - } + result = true; } } return result; } bool -Debugger::CheckIfTopInputReaderIsDone () +Debugger::HideTopIOHandler() { - bool result = false; - if (!m_input_reader_stack.IsEmpty()) + Mutex::Locker locker; + + if (locker.TryLock(m_input_reader_stack.GetMutex())) { - // Cannot call GetCurrentInputReader here, as that would cause an infinite loop. - InputReaderSP reader_sp(m_input_reader_stack.Top()); - - if (reader_sp && reader_sp->IsDone()) - { - result = true; - PopInputReader (reader_sp); - } + IOHandlerSP reader_sp(m_input_reader_stack.Top()); + if (reader_sp) + reader_sp->Hide(); + return true; } - return result; + return false; } void -Debugger::ActivateInputReader (const InputReaderSP &reader_sp) +Debugger::RefreshTopIOHandler() { - int input_fd = m_input_file.GetFile().GetDescriptor(); - - if (input_fd >= 0) - { - Terminal tty(input_fd); - - tty.SetEcho(reader_sp->GetEcho()); - - switch (reader_sp->GetGranularity()) - { - case eInputReaderGranularityByte: - case eInputReaderGranularityWord: - tty.SetCanonical (false); - break; - - case eInputReaderGranularityLine: - case eInputReaderGranularityAll: - tty.SetCanonical (true); - break; - - default: - break; - } - } + IOHandlerSP reader_sp(m_input_reader_stack.Top()); + if (reader_sp) + reader_sp->Refresh(); } + StreamSP Debugger::GetAsyncOutputStream () { @@ -2624,7 +2594,7 @@ Debugger::EnableLog (const char *channel, const char **categories, const char *l } else if (log_file == NULL || *log_file == '\0') { - log_stream_sp.reset(new StreamFile(GetOutputFile().GetDescriptor(), false)); + log_stream_sp = GetOutputFile(); } else { @@ -2680,3 +2650,514 @@ Debugger::GetSourceManager () } + +// This function handles events that were broadcast by the process. +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); + StreamFileSP output_sp (GetOutputFile()); + if (output_sp) + { + output_sp->Printf("%d location%s added to breakpoint %d\n", + num_new_locations, + num_new_locations == 1 ? "" : "s", + breakpoint->GetID()); + RefreshTopIOHandler(); + } + } + } +// 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 == NULL) + stream = GetOutputFile().get(); + + if (stream) + { + // The process has stuff waiting for stdout; get it and write it out to the appropriate place. + if (process == NULL) + { + TargetSP target_sp = GetTargetList().GetSelectedTarget(); + if (target_sp) + process = target_sp->GetProcessSP().get(); + } + 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 == NULL) + stream = GetOutputFile().get(); + + if (stream) + { + // The process has stuff waiting for stderr; get it and write it out to the appropriate place. + if (process == NULL) + { + TargetSP target_sp = GetTargetList().GetSelectedTarget(); + if (target_sp) + process = target_sp->GetProcessSP().get(); + } + 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; +} + +// This function handles events that were broadcast by the process. +void +Debugger::HandleProcessEvent (const EventSP &event_sp) +{ + using namespace lldb; + const uint32_t event_type = event_sp->GetType(); + ProcessSP process_sp = Process::ProcessEventData::GetProcessFromEvent(event_sp.get()); + + const bool gui_enabled = IsForwardingEvents(); + bool top_io_handler_hid = false; + if (gui_enabled == false) + top_io_handler_hid = HideTopIOHandler(); + + assert (process_sp); + + if (event_type & Process::eBroadcastBitSTDOUT) + { + // The process has stdout available, get it and write it out to the + // appropriate place. + if (top_io_handler_hid) + GetProcessSTDOUT (process_sp.get(), NULL); + } + else if (event_type & Process::eBroadcastBitSTDERR) + { + // The process has stderr available, get it and write it out to the + // appropriate place. + if (top_io_handler_hid) + GetProcessSTDERR (process_sp.get(), NULL); + } + else if (event_type & Process::eBroadcastBitStateChanged) + { + // Drain all stout and stderr so we don't see any output come after + // we print our prompts + if (top_io_handler_hid) + { + StreamFileSP stream_sp (GetOutputFile()); + GetProcessSTDOUT (process_sp.get(), stream_sp.get()); + GetProcessSTDERR (process_sp.get(), NULL); + // Something changed in the process; get the event and report the process's current status and location to + // the user. + StateType event_state = Process::ProcessEventData::GetStateFromEvent (event_sp.get()); + if (event_state == eStateInvalid) + return; + + switch (event_state) + { + case eStateInvalid: + case eStateUnloaded: + case eStateConnected: + case eStateAttaching: + case eStateLaunching: + case eStateStepping: + case eStateDetached: + { + stream_sp->Printf("Process %" PRIu64 " %s\n", + process_sp->GetID(), + StateAsCString (event_state)); + } + break; + + case eStateRunning: + // Don't be chatty when we run... + break; + + case eStateExited: + process_sp->GetStatus(*stream_sp); + break; + + case eStateStopped: + case eStateCrashed: + case eStateSuspended: + // Make sure the program hasn't been auto-restarted: + if (Process::ProcessEventData::GetRestartedFromEvent (event_sp.get())) + { + size_t num_reasons = Process::ProcessEventData::GetNumRestartedReasons(event_sp.get()); + if (num_reasons > 0) + { + // FIXME: Do we want to report this, or would that just be annoyingly chatty? + if (num_reasons == 1) + { + const char *reason = Process::ProcessEventData::GetRestartedReasonAtIndex (event_sp.get(), 0); + stream_sp->Printf("Process %" PRIu64 " stopped and restarted: %s\n", + process_sp->GetID(), + reason ? reason : "<UNKNOWN REASON>"); + } + else + { + stream_sp->Printf("Process %" PRIu64 " stopped and restarted, reasons:\n", + process_sp->GetID()); + + + for (size_t i = 0; i < num_reasons; i++) + { + const char *reason = Process::ProcessEventData::GetRestartedReasonAtIndex (event_sp.get(), i); + stream_sp->Printf("\t%s\n", reason ? reason : "<UNKNOWN REASON>"); + } + } + } + } + else + { + // Lock the thread list so it doesn't change on us + ThreadList &thread_list = process_sp->GetThreadList(); + Mutex::Locker locker (thread_list.GetMutex()); + + ThreadSP curr_thread (thread_list.GetSelectedThread()); + ThreadSP thread; + StopReason curr_thread_stop_reason = eStopReasonInvalid; + if (curr_thread) + curr_thread_stop_reason = curr_thread->GetStopReason(); + if (!curr_thread || + !curr_thread->IsValid() || + curr_thread_stop_reason == eStopReasonInvalid || + curr_thread_stop_reason == eStopReasonNone) + { + // Prefer a thread that has just completed its plan over another thread as current thread. + ThreadSP plan_thread; + ThreadSP other_thread; + const size_t num_threads = thread_list.GetSize(); + size_t i; + for (i = 0; i < num_threads; ++i) + { + thread = thread_list.GetThreadAtIndex(i); + StopReason thread_stop_reason = thread->GetStopReason(); + switch (thread_stop_reason) + { + case eStopReasonInvalid: + case eStopReasonNone: + break; + + case eStopReasonTrace: + case eStopReasonBreakpoint: + case eStopReasonWatchpoint: + case eStopReasonSignal: + case eStopReasonException: + case eStopReasonExec: + case eStopReasonThreadExiting: + if (!other_thread) + other_thread = thread; + break; + case eStopReasonPlanComplete: + if (!plan_thread) + plan_thread = thread; + break; + } + } + if (plan_thread) + thread_list.SetSelectedThreadByID (plan_thread->GetID()); + else if (other_thread) + thread_list.SetSelectedThreadByID (other_thread->GetID()); + else + { + if (curr_thread && curr_thread->IsValid()) + thread = curr_thread; + else + thread = thread_list.GetThreadAtIndex(0); + + if (thread) + thread_list.SetSelectedThreadByID (thread->GetID()); + } + } + + if (GetTargetList().GetSelectedTarget().get() == &process_sp->GetTarget()) + { + const bool only_threads_with_stop_reason = true; + const uint32_t start_frame = 0; + const uint32_t num_frames = 1; + const uint32_t num_frames_with_source = 1; + process_sp->GetStatus(*stream_sp); + process_sp->GetThreadStatus (*stream_sp, + only_threads_with_stop_reason, + start_frame, + num_frames, + num_frames_with_source); + } + else + { + uint32_t target_idx = GetTargetList().GetIndexOfTarget(process_sp->GetTarget().shared_from_this()); + if (target_idx != UINT32_MAX) + stream_sp->Printf ("Target %d: (", target_idx); + else + stream_sp->Printf ("Target <unknown index>: ("); + process_sp->GetTarget().Dump (stream_sp.get(), eDescriptionLevelBrief); + stream_sp->Printf (") stopped.\n"); + } + } + break; + } + } + } + + if (top_io_handler_hid) + RefreshTopIOHandler(); +} + +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) + { + HideTopIOHandler(); + StreamFileSP stream_sp (GetOutputFile()); + thread_sp->GetStatus(*stream_sp, 0, 1, 1); + RefreshTopIOHandler(); + } + } +} + +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() +{ + Listener& listener(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); + + BroadcastEventSpec thread_event_spec (broadcaster_class_thread, + Thread::eBroadcastBitStackChanged | + Thread::eBroadcastBitThreadSelected ); + + listener.StartListeningForEventSpec (*this, target_event_spec); + listener.StartListeningForEventSpec (*this, process_event_spec); + listener.StartListeningForEventSpec (*this, thread_event_spec); + listener.StartListeningForEvents (m_command_interpreter_ap.get(), + CommandInterpreter::eBroadcastBitQuitCommandReceived | + CommandInterpreter::eBroadcastBitAsynchronousOutputData | + CommandInterpreter::eBroadcastBitAsynchronousErrorData ); + + bool done = false; + while (!done) + { +// Mutex::Locker locker; +// if (locker.TryLock(m_input_reader_stack.GetMutex())) +// { +// if (m_input_reader_stack.IsEmpty()) +// break; +// } +// + EventSP event_sp; + if (listener.WaitForEvent(NULL, 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]) + { + StreamFileSP error_sp (GetErrorFile()); + if (error_sp) + { + HideTopIOHandler(); + error_sp->PutCString(data); + error_sp->Flush(); + RefreshTopIOHandler(); + } + } + } + else if (event_type & CommandInterpreter::eBroadcastBitAsynchronousOutputData) + { + const char *data = reinterpret_cast<const char *>(EventDataBytes::GetBytesFromEvent (event_sp.get())); + if (data && data[0]) + { + StreamFileSP output_sp (GetOutputFile()); + if (output_sp) + { + HideTopIOHandler(); + output_sp->PutCString(data); + output_sp->Flush(); + RefreshTopIOHandler(); + } + } + } + } + } + + if (m_forward_listener_sp) + m_forward_listener_sp->AddEvent(event_sp); + } + } + } +} + +lldb::thread_result_t +Debugger::EventHandlerThread (lldb::thread_arg_t arg) +{ + ((Debugger *)arg)->DefaultEventHandler(); + return NULL; +} + +bool +Debugger::StartEventHandlerThread() +{ + if (!IS_VALID_LLDB_HOST_THREAD(m_event_handler_thread)) + m_event_handler_thread = Host::ThreadCreate("lldb.debugger.event-handler", EventHandlerThread, this, NULL); + return IS_VALID_LLDB_HOST_THREAD(m_event_handler_thread); +} + +void +Debugger::StopEventHandlerThread() +{ + if (IS_VALID_LLDB_HOST_THREAD(m_event_handler_thread)) + { + GetCommandInterpreter().BroadcastEvent(CommandInterpreter::eBroadcastBitQuitCommandReceived); + Host::ThreadJoin(m_event_handler_thread, NULL, NULL); + m_event_handler_thread = LLDB_INVALID_HOST_THREAD; + } +} + + +lldb::thread_result_t +Debugger::IOHandlerThread (lldb::thread_arg_t arg) +{ + Debugger *debugger = (Debugger *)arg; + debugger->ExecuteIOHanders(); + debugger->StopEventHandlerThread(); + return NULL; +} + +bool +Debugger::StartIOHandlerThread() +{ + if (!IS_VALID_LLDB_HOST_THREAD(m_io_handler_thread)) + m_io_handler_thread = Host::ThreadCreate("lldb.debugger.io-handler", IOHandlerThread, this, NULL); + return IS_VALID_LLDB_HOST_THREAD(m_io_handler_thread); +} + +void +Debugger::StopIOHandlerThread() +{ + if (IS_VALID_LLDB_HOST_THREAD(m_io_handler_thread)) + { + if (m_input_file_sp) + m_input_file_sp->GetFile().Close(); + Host::ThreadJoin(m_io_handler_thread, NULL, NULL); + m_io_handler_thread = LLDB_INVALID_HOST_THREAD; + } +} + + diff --git a/source/Core/Disassembler.cpp b/source/Core/Disassembler.cpp index 7f830acba1f7..1d2b8cf04c32 100644 --- a/source/Core/Disassembler.cpp +++ b/source/Core/Disassembler.cpp @@ -35,6 +35,7 @@ #include "lldb/Symbol/ObjectFile.h" #include "lldb/Target/ExecutionContext.h" #include "lldb/Target/Process.h" +#include "lldb/Target/SectionLoadList.h" #include "lldb/Target/StackFrame.h" #include "lldb/Target/Target.h" @@ -1044,10 +1045,8 @@ InstructionList::GetIndexOfNextBranchInstruction(uint32_t start) const } uint32_t -InstructionList::GetIndexOfInstructionAtLoadAddress (lldb::addr_t load_addr, Target &target) +InstructionList::GetIndexOfInstructionAtAddress (const Address &address) { - Address address; - address.SetLoadAddress(load_addr, &target); size_t num_instructions = m_instructions.size(); uint32_t index = UINT32_MAX; for (size_t i = 0; i < num_instructions; i++) @@ -1061,6 +1060,15 @@ InstructionList::GetIndexOfInstructionAtLoadAddress (lldb::addr_t load_addr, Tar return index; } + +uint32_t +InstructionList::GetIndexOfInstructionAtLoadAddress (lldb::addr_t load_addr, Target &target) +{ + Address address; + address.SetLoadAddress(load_addr, &target); + return GetIndexOfInstructionAtAddress(address); +} + size_t Disassembler::ParseInstructions (const ExecutionContext *exe_ctx, const AddressRange &range, @@ -1235,25 +1243,25 @@ PseudoInstruction::SetOpcode (size_t opcode_size, void *opcode_data) case 8: { uint8_t value8 = *((uint8_t *) opcode_data); - m_opcode.SetOpcode8 (value8); + m_opcode.SetOpcode8 (value8, eByteOrderInvalid); break; } case 16: { uint16_t value16 = *((uint16_t *) opcode_data); - m_opcode.SetOpcode16 (value16); + m_opcode.SetOpcode16 (value16, eByteOrderInvalid); break; } case 32: { uint32_t value32 = *((uint32_t *) opcode_data); - m_opcode.SetOpcode32 (value32); + m_opcode.SetOpcode32 (value32, eByteOrderInvalid); break; } case 64: { uint64_t value64 = *((uint64_t *) opcode_data); - m_opcode.SetOpcode64 (value64); + m_opcode.SetOpcode64 (value64, eByteOrderInvalid); break; } default: diff --git a/source/Core/DynamicLoader.cpp b/source/Core/DynamicLoader.cpp index 82f84048b32a..1f545b727a1e 100644 --- a/source/Core/DynamicLoader.cpp +++ b/source/Core/DynamicLoader.cpp @@ -10,7 +10,11 @@ #include "lldb/lldb-private.h" #include "lldb/Target/DynamicLoader.h" #include "lldb/Target/Process.h" +#include "lldb/Target/Target.h" #include "lldb/Core/PluginManager.h" +#include "lldb/Core/Module.h" +#include "lldb/Core/ModuleSpec.h" +#include "lldb/Core/Section.h" using namespace lldb; using namespace lldb_private; @@ -74,3 +78,137 @@ DynamicLoader::SetStopWhenImagesChange (bool stop) m_process->SetStopOnSharedLibraryEvents (stop); } +ModuleSP +DynamicLoader::GetTargetExecutable() +{ + Target &target = m_process->GetTarget(); + ModuleSP executable = target.GetExecutableModule(); + + if (executable.get()) + { + if (executable->GetFileSpec().Exists()) + { + ModuleSpec module_spec (executable->GetFileSpec(), executable->GetArchitecture()); + ModuleSP module_sp (new Module (module_spec)); + + // Check if the executable has changed and set it to the target executable if they differ. + if (module_sp.get() && module_sp->GetUUID().IsValid() && executable->GetUUID().IsValid()) + { + if (module_sp->GetUUID() != executable->GetUUID()) + executable.reset(); + } + else if (executable->FileHasChanged()) + { + executable.reset(); + } + + if (!executable.get()) + { + executable = target.GetSharedModule(module_spec); + if (executable.get() != target.GetExecutableModulePointer()) + { + // Don't load dependent images since we are in dyld where we will know + // and find out about all images that are loaded + const bool get_dependent_images = false; + target.SetExecutableModule(executable, get_dependent_images); + } + } + } + } + return executable; +} + +void +DynamicLoader::UpdateLoadedSections(ModuleSP module, addr_t link_map_addr, addr_t base_addr) +{ + UpdateLoadedSectionsCommon(module, base_addr); +} + +void +DynamicLoader::UpdateLoadedSectionsCommon(ModuleSP module, addr_t base_addr) +{ + bool changed; + const bool base_addr_is_offset = true; + module->SetLoadAddress(m_process->GetTarget(), base_addr, base_addr_is_offset, changed); +} + +void +DynamicLoader::UnloadSections(const ModuleSP module) +{ + UnloadSectionsCommon(module); +} + +void +DynamicLoader::UnloadSectionsCommon(const ModuleSP module) +{ + Target &target = m_process->GetTarget(); + const SectionList *sections = GetSectionListFromModule(module); + + assert(sections && "SectionList missing from unloaded module."); + + const size_t num_sections = sections->GetSize(); + for (size_t i = 0; i < num_sections; ++i) + { + SectionSP section_sp (sections->GetSectionAtIndex(i)); + target.SetSectionUnloaded(section_sp); + } +} + + +const SectionList * +DynamicLoader::GetSectionListFromModule(const ModuleSP module) const +{ + SectionList *sections = nullptr; + if (module.get()) + { + ObjectFile *obj_file = module->GetObjectFile(); + if (obj_file) + { + sections = obj_file->GetSectionList(); + } + } + return sections; +} + +ModuleSP +DynamicLoader::LoadModuleAtAddress(const FileSpec &file, addr_t link_map_addr, addr_t base_addr) +{ + Target &target = m_process->GetTarget(); + ModuleList &modules = target.GetImages(); + ModuleSP module_sp; + + ModuleSpec module_spec (file, target.GetArchitecture()); + if ((module_sp = modules.FindFirstModule (module_spec))) + { + UpdateLoadedSections(module_sp, link_map_addr, base_addr); + } + else if ((module_sp = target.GetSharedModule(module_spec))) + { + UpdateLoadedSections(module_sp, link_map_addr, base_addr); + } + + return module_sp; +} + +int64_t +DynamicLoader::ReadUnsignedIntWithSizeInBytes(addr_t addr, int size_in_bytes) +{ + Error error; + + uint64_t value = m_process->ReadUnsignedIntegerFromMemory(addr, size_in_bytes, 0, error); + if (error.Fail()) + return -1; + else + return (int64_t)value; +} + +addr_t +DynamicLoader::ReadPointer(addr_t addr) +{ + Error error; + addr_t value = m_process->ReadPointerFromMemory(addr, error); + if (error.Fail()) + return LLDB_INVALID_ADDRESS; + else + return value; +} diff --git a/source/Core/IOHandler.cpp b/source/Core/IOHandler.cpp new file mode 100644 index 000000000000..bdec19ccb06f --- /dev/null +++ b/source/Core/IOHandler.cpp @@ -0,0 +1,5294 @@ +//===-- IOHandler.cpp -------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + + +#include "lldb/lldb-python.h" + +#include <string> + +#include "lldb/Breakpoint/BreakpointLocation.h" +#include "lldb/Core/IOHandler.h" +#include "lldb/Core/Debugger.h" +#include "lldb/Core/State.h" +#include "lldb/Core/StreamFile.h" +#include "lldb/Core/ValueObjectRegister.h" +#include "lldb/Host/Editline.h" +#include "lldb/Interpreter/CommandCompletions.h" +#include "lldb/Interpreter/CommandInterpreter.h" +#include "lldb/Symbol/Block.h" +#include "lldb/Symbol/Function.h" +#include "lldb/Symbol/Symbol.h" +#include "lldb/Target/RegisterContext.h" +#include "lldb/Target/ThreadPlan.h" + +#ifndef LLDB_DISABLE_CURSES +#include <ncurses.h> +#include <panel.h> +#endif + +using namespace lldb; +using namespace lldb_private; + +IOHandler::IOHandler (Debugger &debugger) : + IOHandler (debugger, + StreamFileSP(), // Adopt STDIN from top input reader + StreamFileSP(), // Adopt STDOUT from top input reader + StreamFileSP(), // Adopt STDERR from top input reader + 0) // Flags +{ +} + + +IOHandler::IOHandler (Debugger &debugger, + const lldb::StreamFileSP &input_sp, + const lldb::StreamFileSP &output_sp, + const lldb::StreamFileSP &error_sp, + uint32_t flags) : + m_debugger (debugger), + m_input_sp (input_sp), + m_output_sp (output_sp), + m_error_sp (error_sp), + m_flags (flags), + m_user_data (NULL), + m_done (false), + m_active (false) +{ + // If any files are not specified, then adopt them from the top input reader. + if (!m_input_sp || !m_output_sp || !m_error_sp) + debugger.AdoptTopIOHandlerFilesIfInvalid (m_input_sp, + m_output_sp, + m_error_sp); +} + +IOHandler::~IOHandler() +{ +} + + +int +IOHandler::GetInputFD() +{ + if (m_input_sp) + return m_input_sp->GetFile().GetDescriptor(); + return -1; +} + +int +IOHandler::GetOutputFD() +{ + if (m_output_sp) + return m_output_sp->GetFile().GetDescriptor(); + return -1; +} + +int +IOHandler::GetErrorFD() +{ + if (m_error_sp) + return m_error_sp->GetFile().GetDescriptor(); + return -1; +} + +FILE * +IOHandler::GetInputFILE() +{ + if (m_input_sp) + return m_input_sp->GetFile().GetStream(); + return NULL; +} + +FILE * +IOHandler::GetOutputFILE() +{ + if (m_output_sp) + return m_output_sp->GetFile().GetStream(); + return NULL; +} + +FILE * +IOHandler::GetErrorFILE() +{ + if (m_error_sp) + return m_error_sp->GetFile().GetStream(); + return NULL; +} + +StreamFileSP & +IOHandler::GetInputStreamFile() +{ + return m_input_sp; +} + +StreamFileSP & +IOHandler::GetOutputStreamFile() +{ + return m_output_sp; +} + + +StreamFileSP & +IOHandler::GetErrorStreamFile() +{ + return m_error_sp; +} + +bool +IOHandler::GetIsInteractive () +{ + return GetInputStreamFile()->GetFile().GetIsInteractive (); +} + +bool +IOHandler::GetIsRealTerminal () +{ + return GetInputStreamFile()->GetFile().GetIsRealTerminal(); +} + +IOHandlerConfirm::IOHandlerConfirm (Debugger &debugger, + const char *prompt, + bool default_response) : + IOHandlerEditline(debugger, + NULL, // NULL editline_name means no history loaded/saved + NULL, + false, // Multi-line + *this), + m_default_response (default_response), + m_user_response (default_response) +{ + StreamString prompt_stream; + prompt_stream.PutCString(prompt); + if (m_default_response) + prompt_stream.Printf(": [Y/n] "); + else + prompt_stream.Printf(": [y/N] "); + + SetPrompt (prompt_stream.GetString().c_str()); + +} + + +IOHandlerConfirm::~IOHandlerConfirm () +{ +} + +int +IOHandlerConfirm::IOHandlerComplete (IOHandler &io_handler, + const char *current_line, + const char *cursor, + const char *last_char, + int skip_first_n_matches, + int max_matches, + StringList &matches) +{ + if (current_line == cursor) + { + if (m_default_response) + { + matches.AppendString("y"); + } + else + { + matches.AppendString("n"); + } + } + return matches.GetSize(); +} + +void +IOHandlerConfirm::IOHandlerInputComplete (IOHandler &io_handler, std::string &line) +{ + if (line.empty()) + { + // User just hit enter, set the response to the default + m_user_response = m_default_response; + io_handler.SetIsDone(true); + return; + } + + if (line.size() == 1) + { + switch (line[0]) + { + case 'y': + case 'Y': + m_user_response = true; + io_handler.SetIsDone(true); + return; + case 'n': + case 'N': + m_user_response = false; + io_handler.SetIsDone(true); + return; + default: + break; + } + } + + if (line == "yes" || line == "YES" || line == "Yes") + { + m_user_response = true; + io_handler.SetIsDone(true); + } + else if (line == "no" || line == "NO" || line == "No") + { + m_user_response = false; + io_handler.SetIsDone(true); + } +} + +int +IOHandlerDelegate::IOHandlerComplete (IOHandler &io_handler, + const char *current_line, + const char *cursor, + const char *last_char, + int skip_first_n_matches, + int max_matches, + StringList &matches) +{ + switch (m_completion) + { + case Completion::None: + break; + + case Completion::LLDBCommand: + return io_handler.GetDebugger().GetCommandInterpreter().HandleCompletion (current_line, + cursor, + last_char, + skip_first_n_matches, + max_matches, + matches); + + case Completion::Expression: + { + bool word_complete = false; + const char *word_start = cursor; + if (cursor > current_line) + --word_start; + while (word_start > current_line && !isspace(*word_start)) + --word_start; + CommandCompletions::InvokeCommonCompletionCallbacks (io_handler.GetDebugger().GetCommandInterpreter(), + CommandCompletions::eVariablePathCompletion, + word_start, + skip_first_n_matches, + max_matches, + NULL, + word_complete, + matches); + + size_t num_matches = matches.GetSize(); + if (num_matches > 0) + { + std::string common_prefix; + matches.LongestCommonPrefix (common_prefix); + const size_t partial_name_len = strlen(word_start); + + // If we matched a unique single command, add a space... + // Only do this if the completer told us this was a complete word, however... + if (num_matches == 1 && word_complete) + { + common_prefix.push_back(' '); + } + common_prefix.erase (0, partial_name_len); + matches.InsertStringAtIndex(0, std::move(common_prefix)); + } + return num_matches; + } + break; + } + + + return 0; +} + + +IOHandlerEditline::IOHandlerEditline (Debugger &debugger, + const char *editline_name, // Used for saving history files + const char *prompt, + bool multi_line, + IOHandlerDelegate &delegate) : + IOHandlerEditline(debugger, + StreamFileSP(), // Inherit input from top input reader + StreamFileSP(), // Inherit output from top input reader + StreamFileSP(), // Inherit error from top input reader + 0, // Flags + editline_name, // Used for saving history files + prompt, + multi_line, + delegate) +{ +} + +IOHandlerEditline::IOHandlerEditline (Debugger &debugger, + const lldb::StreamFileSP &input_sp, + const lldb::StreamFileSP &output_sp, + const lldb::StreamFileSP &error_sp, + uint32_t flags, + const char *editline_name, // Used for saving history files + const char *prompt, + bool multi_line, + IOHandlerDelegate &delegate) : + IOHandler (debugger, input_sp, output_sp, error_sp, flags), + m_editline_ap (), + m_delegate (delegate), + m_prompt (), + m_multi_line (multi_line) +{ + SetPrompt(prompt); + + bool use_editline = false; + +#ifndef _MSC_VER + use_editline = m_input_sp->GetFile().GetIsRealTerminal(); +#else + use_editline = true; +#endif + + if (use_editline) + { + m_editline_ap.reset(new Editline (editline_name, + prompt ? prompt : "", + GetInputFILE (), + GetOutputFILE (), + GetErrorFILE ())); + m_editline_ap->SetLineCompleteCallback (LineCompletedCallback, this); + m_editline_ap->SetAutoCompleteCallback (AutoCompleteCallback, this); + } + +} + +IOHandlerEditline::~IOHandlerEditline () +{ + m_editline_ap.reset(); +} + + +bool +IOHandlerEditline::GetLine (std::string &line) +{ + if (m_editline_ap) + { + return m_editline_ap->GetLine(line).Success(); + } + else + { + line.clear(); + + FILE *in = GetInputFILE(); + if (in) + { + if (GetIsInteractive()) + { + const char *prompt = GetPrompt(); + if (prompt && prompt[0]) + { + FILE *out = GetOutputFILE(); + if (out) + { + ::fprintf(out, "%s", prompt); + ::fflush(out); + } + } + } + char buffer[256]; + bool done = false; + bool got_line = false; + while (!done) + { + if (fgets(buffer, sizeof(buffer), in) == NULL) + done = true; + else + { + got_line = true; + size_t buffer_len = strlen(buffer); + assert (buffer[buffer_len] == '\0'); + char last_char = buffer[buffer_len-1]; + if (last_char == '\r' || last_char == '\n') + { + done = true; + // Strip trailing newlines + while (last_char == '\r' || last_char == '\n') + { + --buffer_len; + if (buffer_len == 0) + break; + last_char = buffer[buffer_len-1]; + } + } + line.append(buffer, buffer_len); + } + } + // We might have gotten a newline on a line by itself + // make sure to return true in this case. + return got_line; + } + else + { + // No more input file, we are done... + SetIsDone(true); + } + return false; + } +} + + +LineStatus +IOHandlerEditline::LineCompletedCallback (Editline *editline, + StringList &lines, + uint32_t line_idx, + Error &error, + void *baton) +{ + IOHandlerEditline *editline_reader = (IOHandlerEditline *) baton; + return editline_reader->m_delegate.IOHandlerLinesUpdated(*editline_reader, lines, line_idx, error); +} + +int +IOHandlerEditline::AutoCompleteCallback (const char *current_line, + const char *cursor, + const char *last_char, + int skip_first_n_matches, + int max_matches, + StringList &matches, + void *baton) +{ + IOHandlerEditline *editline_reader = (IOHandlerEditline *) baton; + if (editline_reader) + return editline_reader->m_delegate.IOHandlerComplete (*editline_reader, + current_line, + cursor, + last_char, + skip_first_n_matches, + max_matches, + matches); + return 0; +} + +const char * +IOHandlerEditline::GetPrompt () +{ + if (m_editline_ap) + return m_editline_ap->GetPrompt (); + else if (m_prompt.empty()) + return NULL; + return m_prompt.c_str(); +} + +bool +IOHandlerEditline::SetPrompt (const char *p) +{ + if (p && p[0]) + m_prompt = p; + else + m_prompt.clear(); + if (m_editline_ap) + m_editline_ap->SetPrompt (m_prompt.empty() ? NULL : m_prompt.c_str()); + return true; +} + +bool +IOHandlerEditline::GetLines (StringList &lines) +{ + bool success = false; + if (m_editline_ap) + { + std::string end_token; + success = m_editline_ap->GetLines(end_token, lines).Success(); + } + else + { + LineStatus lines_status = LineStatus::Success; + + while (lines_status == LineStatus::Success) + { + std::string line; + if (GetLine(line)) + { + lines.AppendString(line); + Error error; + lines_status = m_delegate.IOHandlerLinesUpdated(*this, lines, lines.GetSize() - 1, error); + } + else + { + lines_status = LineStatus::Done; + } + } + success = lines.GetSize() > 0; + } + return success; +} + +// Each IOHandler gets to run until it is done. It should read data +// from the "in" and place output into "out" and "err and return +// when done. +void +IOHandlerEditline::Run () +{ + std::string line; + while (IsActive()) + { + if (m_multi_line) + { + StringList lines; + if (GetLines (lines)) + { + line = lines.CopyList(); + m_delegate.IOHandlerInputComplete(*this, line); + } + else + { + m_done = true; + } + } + else + { + if (GetLine(line)) + { + m_delegate.IOHandlerInputComplete(*this, line); + } + else + { + m_done = true; + } + } + } +} + +void +IOHandlerEditline::Hide () +{ + if (m_editline_ap && m_editline_ap->GettingLine()) + m_editline_ap->Hide(); +} + + +void +IOHandlerEditline::Refresh () +{ + if (m_editline_ap && m_editline_ap->GettingLine()) + m_editline_ap->Refresh(); + else + { + const char *prompt = GetPrompt(); + if (prompt && prompt[0]) + { + FILE *out = GetOutputFILE(); + if (out) + { + ::fprintf(out, "%s", prompt); + ::fflush(out); + } + } + } +} + +void +IOHandlerEditline::Interrupt () +{ + if (m_editline_ap) + m_editline_ap->Interrupt(); +} + +void +IOHandlerEditline::GotEOF() +{ + if (m_editline_ap) + m_editline_ap->Interrupt(); +} + +// we may want curses to be disabled for some builds +// for instance, windows +#ifndef LLDB_DISABLE_CURSES + +#include "lldb/Core/ValueObject.h" +#include "lldb/Symbol/VariableList.h" +#include "lldb/Target/Target.h" +#include "lldb/Target/Process.h" +#include "lldb/Target/Thread.h" +#include "lldb/Target/StackFrame.h" + +#define KEY_RETURN 10 +#define KEY_ESCAPE 27 + +namespace curses +{ + class Menu; + class MenuDelegate; + class Window; + class WindowDelegate; + typedef std::shared_ptr<Menu> MenuSP; + typedef std::shared_ptr<MenuDelegate> MenuDelegateSP; + typedef std::shared_ptr<Window> WindowSP; + typedef std::shared_ptr<WindowDelegate> WindowDelegateSP; + typedef std::vector<MenuSP> Menus; + typedef std::vector<WindowSP> Windows; + typedef std::vector<WindowDelegateSP> WindowDelegates; + +#if 0 +type summary add -s "x=${var.x}, y=${var.y}" curses::Point +type summary add -s "w=${var.width}, h=${var.height}" curses::Size +type summary add -s "${var.origin%S} ${var.size%S}" curses::Rect +#endif + struct Point + { + int x; + int y; + + Point (int _x = 0, int _y = 0) : + x(_x), + y(_y) + { + } + + void + Clear () + { + x = 0; + y = 0; + } + + Point & + operator += (const Point &rhs) + { + x += rhs.x; + y += rhs.y; + return *this; + } + + void + Dump () + { + printf ("(x=%i, y=%i)\n", x, y); + } + + }; + + bool operator == (const Point &lhs, const Point &rhs) + { + return lhs.x == rhs.x && lhs.y == rhs.y; + } + bool operator != (const Point &lhs, const Point &rhs) + { + return lhs.x != rhs.x || lhs.y != rhs.y; + } + + struct Size + { + int width; + int height; + Size (int w = 0, int h = 0) : + width (w), + height (h) + { + } + + void + Clear () + { + width = 0; + height = 0; + } + + void + Dump () + { + printf ("(w=%i, h=%i)\n", width, height); + } + + }; + + bool operator == (const Size &lhs, const Size &rhs) + { + return lhs.width == rhs.width && lhs.height == rhs.height; + } + bool operator != (const Size &lhs, const Size &rhs) + { + return lhs.width != rhs.width || lhs.height != rhs.height; + } + + struct Rect + { + Point origin; + Size size; + + Rect () : + origin(), + size() + { + } + + Rect (const Point &p, const Size &s) : + origin (p), + size (s) + { + } + + void + Clear () + { + origin.Clear(); + size.Clear(); + } + + void + Dump () + { + printf ("(x=%i, y=%i), w=%i, h=%i)\n", origin.x, origin.y, size.width, size.height); + } + + void + Inset (int w, int h) + { + if (size.width > w*2) + size.width -= w*2; + origin.x += w; + + if (size.height > h*2) + size.height -= h*2; + origin.y += h; + } + // Return a status bar rectangle which is the last line of + // this rectangle. This rectangle will be modified to not + // include the status bar area. + Rect + MakeStatusBar () + { + Rect status_bar; + if (size.height > 1) + { + status_bar.origin.x = origin.x; + status_bar.origin.y = size.height; + status_bar.size.width = size.width; + status_bar.size.height = 1; + --size.height; + } + return status_bar; + } + + // Return a menubar rectangle which is the first line of + // this rectangle. This rectangle will be modified to not + // include the menubar area. + Rect + MakeMenuBar () + { + Rect menubar; + if (size.height > 1) + { + menubar.origin.x = origin.x; + menubar.origin.y = origin.y; + menubar.size.width = size.width; + menubar.size.height = 1; + ++origin.y; + --size.height; + } + return menubar; + } + + void + HorizontalSplitPercentage (float top_percentage, Rect &top, Rect &bottom) const + { + float top_height = top_percentage * size.height; + HorizontalSplit (top_height, top, bottom); + } + + void + HorizontalSplit (int top_height, Rect &top, Rect &bottom) const + { + top = *this; + if (top_height < size.height) + { + top.size.height = top_height; + bottom.origin.x = origin.x; + bottom.origin.y = origin.y + top.size.height; + bottom.size.width = size.width; + bottom.size.height = size.height - top.size.height; + } + else + { + bottom.Clear(); + } + } + + void + VerticalSplitPercentage (float left_percentage, Rect &left, Rect &right) const + { + float left_width = left_percentage * size.width; + VerticalSplit (left_width, left, right); + } + + + void + VerticalSplit (int left_width, Rect &left, Rect &right) const + { + left = *this; + if (left_width < size.width) + { + left.size.width = left_width; + right.origin.x = origin.x + left.size.width; + right.origin.y = origin.y; + right.size.width = size.width - left.size.width; + right.size.height = size.height; + } + else + { + right.Clear(); + } + } + }; + + bool operator == (const Rect &lhs, const Rect &rhs) + { + return lhs.origin == rhs.origin && lhs.size == rhs.size; + } + bool operator != (const Rect &lhs, const Rect &rhs) + { + return lhs.origin != rhs.origin || lhs.size != rhs.size; + } + + enum HandleCharResult + { + eKeyNotHandled = 0, + eKeyHandled = 1, + eQuitApplication = 2 + }; + + enum class MenuActionResult + { + Handled, + NotHandled, + Quit // Exit all menus and quit + }; + + struct KeyHelp + { + int ch; + const char *description; + }; + + class WindowDelegate + { + public: + virtual + ~WindowDelegate() + { + } + + virtual bool + WindowDelegateDraw (Window &window, bool force) + { + return false; // Drawing not handled + } + + virtual HandleCharResult + WindowDelegateHandleChar (Window &window, int key) + { + return eKeyNotHandled; + } + + virtual const char * + WindowDelegateGetHelpText () + { + return NULL; + } + + virtual KeyHelp * + WindowDelegateGetKeyHelp () + { + return NULL; + } + }; + + class HelpDialogDelegate : + public WindowDelegate + { + public: + HelpDialogDelegate (const char *text, KeyHelp *key_help_array); + + virtual + ~HelpDialogDelegate(); + + virtual bool + WindowDelegateDraw (Window &window, bool force); + + virtual HandleCharResult + WindowDelegateHandleChar (Window &window, int key); + + size_t + GetNumLines() const + { + return m_text.GetSize(); + } + + size_t + GetMaxLineLength () const + { + return m_text.GetMaxStringLength(); + } + + protected: + StringList m_text; + int m_first_visible_line; + }; + + + class Window + { + public: + + Window (const char *name) : + m_name (name), + m_window (NULL), + m_panel (NULL), + m_parent (NULL), + m_subwindows (), + m_delegate_sp (), + m_curr_active_window_idx (UINT32_MAX), + m_prev_active_window_idx (UINT32_MAX), + m_delete (false), + m_needs_update (true), + m_can_activate (true), + m_is_subwin (false) + { + } + + Window (const char *name, WINDOW *w, bool del = true) : + m_name (name), + m_window (NULL), + m_panel (NULL), + m_parent (NULL), + m_subwindows (), + m_delegate_sp (), + m_curr_active_window_idx (UINT32_MAX), + m_prev_active_window_idx (UINT32_MAX), + m_delete (del), + m_needs_update (true), + m_can_activate (true), + m_is_subwin (false) + { + if (w) + Reset(w); + } + + Window (const char *name, const Rect &bounds) : + m_name (name), + m_window (NULL), + m_parent (NULL), + m_subwindows (), + m_delegate_sp (), + m_curr_active_window_idx (UINT32_MAX), + m_prev_active_window_idx (UINT32_MAX), + m_delete (true), + m_needs_update (true), + m_can_activate (true), + m_is_subwin (false) + { + Reset (::newwin (bounds.size.height, bounds.size.width, bounds.origin.y, bounds.origin.y)); + } + + virtual + ~Window () + { + RemoveSubWindows (); + Reset (); + } + + void + Reset (WINDOW *w = NULL, bool del = true) + { + if (m_window == w) + return; + + if (m_panel) + { + ::del_panel (m_panel); + m_panel = NULL; + } + if (m_window && m_delete) + { + ::delwin (m_window); + m_window = NULL; + m_delete = false; + } + if (w) + { + m_window = w; + m_panel = ::new_panel (m_window); + m_delete = del; + } + } + + void AttributeOn (attr_t attr) { ::wattron (m_window, attr); } + void AttributeOff (attr_t attr) { ::wattroff (m_window, attr); } + void Box (chtype v_char = ACS_VLINE, chtype h_char = ACS_HLINE) { ::box(m_window, v_char, h_char); } + void Clear () { ::wclear (m_window); } + void Erase () { ::werase (m_window); } + Rect GetBounds () { return Rect (GetParentOrigin(), GetSize()); } // Get the rectangle in our parent window + int GetChar () { return ::wgetch (m_window); } + int GetCursorX () { return getcurx (m_window); } + int GetCursorY () { return getcury (m_window); } + Rect GetFrame () { return Rect (Point(), GetSize()); } // Get our rectangle in our own coordinate system + Point GetParentOrigin() { return Point (GetParentX(), GetParentY()); } + Size GetSize() { return Size (GetWidth(), GetHeight()); } + int GetParentX () { return getparx (m_window); } + int GetParentY () { return getpary (m_window); } + int GetMaxX() { return getmaxx (m_window); } + int GetMaxY() { return getmaxy (m_window); } + int GetWidth() { return GetMaxX(); } + int GetHeight() { return GetMaxY(); } + void MoveCursor (int x, int y) { ::wmove (m_window, y, x); } + void MoveWindow (int x, int y) { MoveWindow(Point(x,y)); } + void Resize (int w, int h) { ::wresize(m_window, h, w); } + void Resize (const Size &size) { ::wresize(m_window, size.height, size.width); } + void PutChar (int ch) { ::waddch (m_window, ch); } + void PutCString (const char *s, int len = -1) { ::waddnstr (m_window, s, len); } + void Refresh () { ::wrefresh (m_window); } + void DeferredRefresh () + { + // We are using panels, so we don't need to call this... + //::wnoutrefresh(m_window); + } + void SetBackground (int color_pair_idx) { ::wbkgd (m_window,COLOR_PAIR(color_pair_idx)); } + void UnderlineOn () { AttributeOn(A_UNDERLINE); } + void UnderlineOff () { AttributeOff(A_UNDERLINE); } + + void PutCStringTruncated (const char *s, int right_pad) + { + int bytes_left = GetWidth() - GetCursorX(); + if (bytes_left > right_pad) + { + bytes_left -= right_pad; + ::waddnstr (m_window, s, bytes_left); + } + } + + void + MoveWindow (const Point &origin) + { + const bool moving_window = origin != GetParentOrigin(); + if (m_is_subwin && moving_window) + { + // Can't move subwindows, must delete and re-create + Size size = GetSize(); + Reset (::subwin (m_parent->m_window, + size.height, + size.width, + origin.y, + origin.x), true); + } + else + { + ::mvwin (m_window, origin.y, origin.x); + } + } + + void + SetBounds (const Rect &bounds) + { + const bool moving_window = bounds.origin != GetParentOrigin(); + if (m_is_subwin && moving_window) + { + // Can't move subwindows, must delete and re-create + Reset (::subwin (m_parent->m_window, + bounds.size.height, + bounds.size.width, + bounds.origin.y, + bounds.origin.x), true); + } + else + { + if (moving_window) + MoveWindow(bounds.origin); + Resize (bounds.size); + } + } + + void + Printf (const char *format, ...) __attribute__ ((format (printf, 2, 3))) + { + va_list args; + va_start (args, format); + vwprintw(m_window, format, args); + va_end (args); + } + + void + Touch () + { + ::touchwin (m_window); + if (m_parent) + m_parent->Touch(); + } + + WindowSP + CreateSubWindow (const char *name, const Rect &bounds, bool make_active) + { + WindowSP subwindow_sp; + if (m_window) + { + subwindow_sp.reset(new Window(name, ::subwin (m_window, + bounds.size.height, + bounds.size.width, + bounds.origin.y, + bounds.origin.x), true)); + subwindow_sp->m_is_subwin = true; + } + else + { + subwindow_sp.reset(new Window(name, ::newwin (bounds.size.height, + bounds.size.width, + bounds.origin.y, + bounds.origin.x), true)); + subwindow_sp->m_is_subwin = false; + } + subwindow_sp->m_parent = this; + if (make_active) + { + m_prev_active_window_idx = m_curr_active_window_idx; + m_curr_active_window_idx = m_subwindows.size(); + } + m_subwindows.push_back(subwindow_sp); + ::top_panel (subwindow_sp->m_panel); + m_needs_update = true; + return subwindow_sp; + } + + bool + RemoveSubWindow (Window *window) + { + Windows::iterator pos, end = m_subwindows.end(); + size_t i = 0; + for (pos = m_subwindows.begin(); pos != end; ++pos, ++i) + { + if ((*pos).get() == window) + { + if (m_prev_active_window_idx == i) + m_prev_active_window_idx = UINT32_MAX; + else if (m_prev_active_window_idx != UINT32_MAX && m_prev_active_window_idx > i) + --m_prev_active_window_idx; + + if (m_curr_active_window_idx == i) + m_curr_active_window_idx = UINT32_MAX; + else if (m_curr_active_window_idx != UINT32_MAX && m_curr_active_window_idx > i) + --m_curr_active_window_idx; + window->Erase(); + m_subwindows.erase(pos); + m_needs_update = true; + if (m_parent) + m_parent->Touch(); + else + ::touchwin (stdscr); + return true; + } + } + return false; + } + + WindowSP + FindSubWindow (const char *name) + { + Windows::iterator pos, end = m_subwindows.end(); + size_t i = 0; + for (pos = m_subwindows.begin(); pos != end; ++pos, ++i) + { + if ((*pos)->m_name.compare(name) == 0) + return *pos; + } + return WindowSP(); + } + + void + RemoveSubWindows () + { + m_curr_active_window_idx = UINT32_MAX; + m_prev_active_window_idx = UINT32_MAX; + for (Windows::iterator pos = m_subwindows.begin(); + pos != m_subwindows.end(); + pos = m_subwindows.erase(pos)) + { + (*pos)->Erase(); + } + if (m_parent) + m_parent->Touch(); + else + ::touchwin (stdscr); + } + + WINDOW * + get() + { + return m_window; + } + + operator WINDOW *() + { + return m_window; + } + + //---------------------------------------------------------------------- + // Window drawing utilities + //---------------------------------------------------------------------- + void + DrawTitleBox (const char *title, const char *bottom_message = NULL) + { + attr_t attr = 0; + if (IsActive()) + attr = A_BOLD | COLOR_PAIR(2); + else + attr = 0; + if (attr) + AttributeOn(attr); + + Box(); + MoveCursor(3, 0); + + if (title && title[0]) + { + PutChar ('<'); + PutCString (title); + PutChar ('>'); + } + + if (bottom_message && bottom_message[0]) + { + int bottom_message_length = strlen(bottom_message); + int x = GetWidth() - 3 - (bottom_message_length + 2); + + if (x > 0) + { + MoveCursor (x, GetHeight() - 1); + PutChar ('['); + PutCString(bottom_message); + PutChar (']'); + } + else + { + MoveCursor (1, GetHeight() - 1); + PutChar ('['); + PutCStringTruncated (bottom_message, 1); + } + } + if (attr) + AttributeOff(attr); + + } + + virtual void + Draw (bool force) + { + if (m_delegate_sp && m_delegate_sp->WindowDelegateDraw (*this, force)) + return; + + for (auto &subwindow_sp : m_subwindows) + subwindow_sp->Draw(force); + } + + bool + CreateHelpSubwindow () + { + if (m_delegate_sp) + { + const char *text = m_delegate_sp->WindowDelegateGetHelpText (); + KeyHelp *key_help = m_delegate_sp->WindowDelegateGetKeyHelp (); + if ((text && text[0]) || key_help) + { + std::auto_ptr<HelpDialogDelegate> help_delegate_ap(new HelpDialogDelegate(text, key_help)); + const size_t num_lines = help_delegate_ap->GetNumLines(); + const size_t max_length = help_delegate_ap->GetMaxLineLength(); + Rect bounds = GetBounds(); + bounds.Inset(1, 1); + if (max_length + 4 < bounds.size.width) + { + bounds.origin.x += (bounds.size.width - max_length + 4)/2; + bounds.size.width = max_length + 4; + } + else + { + if (bounds.size.width > 100) + { + const int inset_w = bounds.size.width / 4; + bounds.origin.x += inset_w; + bounds.size.width -= 2*inset_w; + } + } + + if (num_lines + 2 < bounds.size.height) + { + bounds.origin.y += (bounds.size.height - num_lines + 2)/2; + bounds.size.height = num_lines + 2; + } + else + { + if (bounds.size.height > 100) + { + const int inset_h = bounds.size.height / 4; + bounds.origin.y += inset_h; + bounds.size.height -= 2*inset_h; + } + } + WindowSP help_window_sp; + Window *parent_window = GetParent(); + if (parent_window) + help_window_sp = parent_window->CreateSubWindow("Help", bounds, true); + else + help_window_sp = CreateSubWindow("Help", bounds, true); + help_window_sp->SetDelegate(WindowDelegateSP(help_delegate_ap.release())); + return true; + } + } + return false; + } + + virtual HandleCharResult + HandleChar (int key) + { + // Always check the active window first + HandleCharResult result = eKeyNotHandled; + WindowSP active_window_sp = GetActiveWindow (); + if (active_window_sp) + { + result = active_window_sp->HandleChar (key); + if (result != eKeyNotHandled) + return result; + } + + if (m_delegate_sp) + { + result = m_delegate_sp->WindowDelegateHandleChar (*this, key); + if (result != eKeyNotHandled) + return result; + } + + // Then check for any windows that want any keys + // that weren't handled. This is typically only + // for a menubar. + // Make a copy of the subwindows in case any HandleChar() + // functions muck with the subwindows. If we don't do this, + // we can crash when iterating over the subwindows. + Windows subwindows (m_subwindows); + for (auto subwindow_sp : subwindows) + { + if (subwindow_sp->m_can_activate == false) + { + HandleCharResult result = subwindow_sp->HandleChar(key); + if (result != eKeyNotHandled) + return result; + } + } + + return eKeyNotHandled; + } + + bool + SetActiveWindow (Window *window) + { + const size_t num_subwindows = m_subwindows.size(); + for (size_t i=0; i<num_subwindows; ++i) + { + if (m_subwindows[i].get() == window) + { + m_prev_active_window_idx = m_curr_active_window_idx; + ::top_panel (window->m_panel); + m_curr_active_window_idx = i; + return true; + } + } + return false; + } + + WindowSP + GetActiveWindow () + { + if (!m_subwindows.empty()) + { + if (m_curr_active_window_idx >= m_subwindows.size()) + { + if (m_prev_active_window_idx < m_subwindows.size()) + { + m_curr_active_window_idx = m_prev_active_window_idx; + m_prev_active_window_idx = UINT32_MAX; + } + else if (IsActive()) + { + m_prev_active_window_idx = UINT32_MAX; + m_curr_active_window_idx = UINT32_MAX; + + // Find first window that wants to be active if this window is active + const size_t num_subwindows = m_subwindows.size(); + for (size_t i=0; i<num_subwindows; ++i) + { + if (m_subwindows[i]->GetCanBeActive()) + { + m_curr_active_window_idx = i; + break; + } + } + } + } + + if (m_curr_active_window_idx < m_subwindows.size()) + return m_subwindows[m_curr_active_window_idx]; + } + return WindowSP(); + } + + bool + GetCanBeActive () const + { + return m_can_activate; + } + + void + SetCanBeActive (bool b) + { + m_can_activate = b; + } + + const WindowDelegateSP & + GetDelegate () const + { + return m_delegate_sp; + } + + void + SetDelegate (const WindowDelegateSP &delegate_sp) + { + m_delegate_sp = delegate_sp; + } + + Window * + GetParent () const + { + return m_parent; + } + + bool + IsActive () const + { + if (m_parent) + return m_parent->GetActiveWindow().get() == this; + else + return true; // Top level window is always active + } + + void + SelectNextWindowAsActive () + { + // Move active focus to next window + const size_t num_subwindows = m_subwindows.size(); + if (m_curr_active_window_idx == UINT32_MAX) + { + uint32_t idx = 0; + for (auto subwindow_sp : m_subwindows) + { + if (subwindow_sp->GetCanBeActive()) + { + m_curr_active_window_idx = idx; + break; + } + ++idx; + } + } + else if (m_curr_active_window_idx + 1 < num_subwindows) + { + bool handled = false; + m_prev_active_window_idx = m_curr_active_window_idx; + for (size_t idx=m_curr_active_window_idx + 1; idx<num_subwindows; ++idx) + { + if (m_subwindows[idx]->GetCanBeActive()) + { + m_curr_active_window_idx = idx; + handled = true; + break; + } + } + if (!handled) + { + for (size_t idx=0; idx<=m_prev_active_window_idx; ++idx) + { + if (m_subwindows[idx]->GetCanBeActive()) + { + m_curr_active_window_idx = idx; + break; + } + } + } + } + else + { + m_prev_active_window_idx = m_curr_active_window_idx; + for (size_t idx=0; idx<num_subwindows; ++idx) + { + if (m_subwindows[idx]->GetCanBeActive()) + { + m_curr_active_window_idx = idx; + break; + } + } + } + } + + const char * + GetName () const + { + return m_name.c_str(); + } + protected: + std::string m_name; + WINDOW *m_window; + PANEL *m_panel; + Window *m_parent; + Window |