Index: vendor/lldb/dist/include/lldb/Core/Debugger.h =================================================================== --- vendor/lldb/dist/include/lldb/Core/Debugger.h (revision 320022) +++ vendor/lldb/dist/include/lldb/Core/Debugger.h (revision 320023) @@ -1,428 +1,430 @@ //===-- Debugger.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_Debugger_h_ #define liblldb_Debugger_h_ // C Includes #include // C++ Includes #include #include // Other libraries and framework includes // Project includes #include "lldb/Core/Broadcaster.h" #include "lldb/Core/FormatEntity.h" #include "lldb/Core/IOHandler.h" #include "lldb/Core/SourceManager.h" #include "lldb/Core/UserSettingsController.h" #include "lldb/Host/HostThread.h" #include "lldb/Host/Terminal.h" #include "lldb/Target/ExecutionContext.h" // for ExecutionContext #include "lldb/Target/Platform.h" #include "lldb/Target/TargetList.h" #include "lldb/Utility/ConstString.h" // for ConstString #include "lldb/Utility/FileSpec.h" // for FileSpec #include "lldb/Utility/Status.h" // for Status #include "lldb/Utility/UserID.h" #include "lldb/lldb-defines.h" // for DISALLOW_COPY_AND_ASSIGN #include "lldb/lldb-enumerations.h" // for ScriptLanguage, Langua... #include "lldb/lldb-forward.h" // for StreamFileSP, DebuggerSP #include "lldb/lldb-private-enumerations.h" // for VarSetOperationType #include "lldb/lldb-private-types.h" // for LoadPluginCallbackType #include "lldb/lldb-types.h" // for LogOutputCallback, thr... #include "llvm/ADT/ArrayRef.h" // for ArrayRef #include "llvm/ADT/StringMap.h" // for StringMap #include "llvm/ADT/StringRef.h" // for StringRef #include "llvm/Support/DynamicLibrary.h" // for DynamicLibrary #include "llvm/Support/Threading.h" #include // for assert #include // for size_t #include namespace lldb_private { class Address; } namespace lldb_private { class CommandInterpreter; } namespace lldb_private { class Process; } namespace lldb_private { class Stream; } namespace lldb_private { class SymbolContext; } namespace lldb_private { class Target; } namespace llvm { class raw_ostream; } namespace lldb_private { //---------------------------------------------------------------------- /// @class Debugger Debugger.h "lldb/Core/Debugger.h" /// @brief A class to manage flag bits. /// /// Provides a global root objects for the debugger core. //---------------------------------------------------------------------- class Debugger : public std::enable_shared_from_this, public UserID, public Properties { friend class SourceManager; // For GetSourceFileCache. public: ~Debugger() override; static lldb::DebuggerSP CreateInstance(lldb::LogOutputCallback log_callback = nullptr, void *baton = nullptr); static lldb::TargetSP FindTargetWithProcessID(lldb::pid_t pid); static lldb::TargetSP FindTargetWithProcess(Process *process); static void Initialize(LoadPluginCallbackType load_plugin_callback); static void Terminate(); static void SettingsInitialize(); static void SettingsTerminate(); static void Destroy(lldb::DebuggerSP &debugger_sp); static lldb::DebuggerSP FindDebuggerWithID(lldb::user_id_t id); static lldb::DebuggerSP FindDebuggerWithInstanceName(const ConstString &instance_name); static size_t GetNumDebuggers(); static lldb::DebuggerSP GetDebuggerAtIndex(size_t index); static bool FormatDisassemblerAddress(const FormatEntity::Entry *format, const SymbolContext *sc, const SymbolContext *prev_sc, const ExecutionContext *exe_ctx, const Address *addr, Stream &s); void Clear(); bool GetAsyncExecution(); void SetAsyncExecution(bool async); lldb::StreamFileSP GetInputFile() { return m_input_file_sp; } lldb::StreamFileSP GetOutputFile() { return m_output_file_sp; } lldb::StreamFileSP GetErrorFile() { return m_error_file_sp; } void SetInputFileHandle(FILE *fh, bool tranfer_ownership); void SetOutputFileHandle(FILE *fh, bool tranfer_ownership); void SetErrorFileHandle(FILE *fh, bool tranfer_ownership); void SaveInputTerminalState(); void RestoreInputTerminalState(); lldb::StreamSP GetAsyncOutputStream(); lldb::StreamSP GetAsyncErrorStream(); CommandInterpreter &GetCommandInterpreter() { assert(m_command_interpreter_ap.get()); return *m_command_interpreter_ap; } lldb::ListenerSP GetListener() { return m_listener_sp; } // This returns the Debugger's scratch source manager. It won't be able to // look up files in debug // information, but it can look up files by absolute path and display them to // you. // To get the target's source manager, call GetSourceManager on the target // instead. SourceManager &GetSourceManager(); lldb::TargetSP GetSelectedTarget() { return m_target_list.GetSelectedTarget(); } ExecutionContext GetSelectedExecutionContext(); //------------------------------------------------------------------ /// Get accessor for the target list. /// /// The target list is part of the global debugger object. This /// the single debugger shared instance to control where targets /// get created and to allow for tracking and searching for targets /// based on certain criteria. /// /// @return /// A global shared target list. //------------------------------------------------------------------ TargetList &GetTargetList() { return m_target_list; } PlatformList &GetPlatformList() { return m_platform_list; } void DispatchInputInterrupt(); 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 AdoptTopIOHandlerFilesIfInvalid(lldb::StreamFileSP &in, lldb::StreamFileSP &out, lldb::StreamFileSP &err); void PushIOHandler(const lldb::IOHandlerSP &reader_sp); bool PopIOHandler(const lldb::IOHandlerSP &reader_sp); // Synchronously run an input reader until it is done void RunIOHandler(const lldb::IOHandlerSP &reader_sp); bool IsTopIOHandler(const lldb::IOHandlerSP &reader_sp); bool CheckTopIOHandlerTypes(IOHandler::Type top_type, IOHandler::Type second_top_type); void PrintAsync(const char *s, size_t len, bool is_stdout); ConstString GetTopIOHandlerControlSequence(char ch); const char *GetIOHandlerCommandPrefix(); const char *GetIOHandlerHelpPrologue(); void ClearIOHandlers(); bool GetCloseInputOnEOF() const; void SetCloseInputOnEOF(bool b); bool EnableLog(llvm::StringRef channel, llvm::ArrayRef categories, llvm::StringRef log_file, uint32_t log_options, llvm::raw_ostream &error_stream); void SetLoggingCallback(lldb::LogOutputCallback log_callback, void *baton); //---------------------------------------------------------------------- // Properties Functions //---------------------------------------------------------------------- enum StopDisassemblyType { eStopDisassemblyTypeNever = 0, eStopDisassemblyTypeNoDebugInfo, eStopDisassemblyTypeNoSource, eStopDisassemblyTypeAlways }; Status SetPropertyValue(const ExecutionContext *exe_ctx, VarSetOperationType op, llvm::StringRef property_path, llvm::StringRef value) override; bool GetAutoConfirm() const; const FormatEntity::Entry *GetDisassemblyFormat() const; const FormatEntity::Entry *GetFrameFormat() const; + const FormatEntity::Entry *GetFrameFormatUnique() const; + const FormatEntity::Entry *GetThreadFormat() const; const FormatEntity::Entry *GetThreadStopFormat() const; lldb::ScriptLanguage GetScriptLanguage() const; bool SetScriptLanguage(lldb::ScriptLanguage script_lang); uint32_t GetTerminalWidth() const; bool SetTerminalWidth(uint32_t term_width); llvm::StringRef GetPrompt() const; void SetPrompt(llvm::StringRef p); void SetPrompt(const char *) = delete; bool GetUseExternalEditor() const; bool SetUseExternalEditor(bool use_external_editor_p); bool GetUseColor() const; bool SetUseColor(bool use_color); lldb::StopShowColumn GetStopShowColumn() const; const FormatEntity::Entry *GetStopShowColumnAnsiPrefix() const; const FormatEntity::Entry *GetStopShowColumnAnsiSuffix() const; uint32_t GetStopSourceLineCount(bool before) const; StopDisassemblyType GetStopDisassemblyDisplay() const; uint32_t GetDisassemblyLineCount() const; bool GetAutoOneLineSummaries() const; bool GetAutoIndent() const; bool SetAutoIndent(bool b); bool GetPrintDecls() const; bool SetPrintDecls(bool b); uint32_t GetTabSize() const; bool SetTabSize(uint32_t tab_size); bool GetEscapeNonPrintables() const; bool GetNotifyVoid() const; const ConstString &GetInstanceName() { return m_instance_name; } bool LoadPlugin(const FileSpec &spec, Status &error); void ExecuteIOHandlers(); bool IsForwardingEvents(); void EnableForwardEvents(const lldb::ListenerSP &listener_sp); void CancelForwardEvents(const lldb::ListenerSP &listener_sp); bool IsHandlingEvents() const { return m_event_handler_thread.IsJoinable(); } Status RunREPL(lldb::LanguageType language, const char *repl_options); // This is for use in the command interpreter, when you either want the // selected target, or if no target // is present you want to prime the dummy target with entities that will be // copied over to new targets. Target *GetSelectedOrDummyTarget(bool prefer_dummy = false); Target *GetDummyTarget(); lldb::BroadcasterManagerSP GetBroadcasterManager() { return m_broadcaster_manager_sp; } protected: friend class CommandInterpreter; friend class REPL; bool StartEventHandlerThread(); void StopEventHandlerThread(); static lldb::thread_result_t EventHandlerThread(lldb::thread_arg_t arg); bool HasIOHandlerThread(); bool StartIOHandlerThread(); void StopIOHandlerThread(); void JoinIOHandlerThread(); 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; } void InstanceInitialize(); lldb::StreamFileSP m_input_file_sp; lldb::StreamFileSP m_output_file_sp; lldb::StreamFileSP m_error_file_sp; lldb::BroadcasterManagerSP m_broadcaster_manager_sp; // The debugger acts as a // broadcaster manager of // last resort. // It needs to get constructed before the target_list or any other // member that might want to broadcast through the debugger. TerminalState m_terminal_state; TargetList m_target_list; PlatformList m_platform_list; lldb::ListenerSP m_listener_sp; std::unique_ptr m_source_manager_ap; // This is a scratch // source manager that we // return if we have no // targets. SourceManager::SourceFileCache m_source_file_cache; // All the source managers // for targets created in // this debugger used this // shared // source file cache. std::unique_ptr m_command_interpreter_ap; IOHandlerStack m_input_reader_stack; llvm::StringMap> m_log_streams; std::shared_ptr m_log_callback_stream_sp; ConstString m_instance_name; static LoadPluginCallbackType g_load_plugin_callback; typedef std::vector LoadedPluginsList; LoadedPluginsList m_loaded_plugins; HostThread m_event_handler_thread; HostThread m_io_handler_thread; Broadcaster m_sync_broadcaster; lldb::ListenerSP m_forward_listener_sp; llvm::once_flag m_clear_once; //---------------------------------------------------------------------- // Events for m_sync_broadcaster //---------------------------------------------------------------------- enum { eBroadcastBitEventThreadIsListening = (1 << 0), }; private: // Use Debugger::CreateInstance() to get a shared pointer to a new // debugger object Debugger(lldb::LogOutputCallback m_log_callback, void *baton); DISALLOW_COPY_AND_ASSIGN(Debugger); }; } // namespace lldb_private #endif // liblldb_Debugger_h_ Index: vendor/lldb/dist/include/lldb/Target/StackFrame.h =================================================================== --- vendor/lldb/dist/include/lldb/Target/StackFrame.h (revision 320022) +++ vendor/lldb/dist/include/lldb/Target/StackFrame.h (revision 320023) @@ -1,557 +1,564 @@ //===-- StackFrame.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_StackFrame_h_ #define liblldb_StackFrame_h_ // C Includes // C++ Includes #include #include // Other libraries and framework includes // Project includes #include "lldb/Utility/Flags.h" #include "lldb/Core/Scalar.h" #include "lldb/Core/ValueObjectList.h" #include "lldb/Symbol/SymbolContext.h" #include "lldb/Target/ExecutionContextScope.h" #include "lldb/Target/StackID.h" #include "lldb/Utility/Status.h" #include "lldb/Utility/StreamString.h" #include "lldb/Utility/UserID.h" namespace lldb_private { /// @class StackFrame StackFrame.h "lldb/Target/StackFrame.h" /// /// @brief This base class provides an interface to stack frames. /// /// StackFrames may have a Canonical Frame Address (CFA) or not. /// A frame may have a plain pc value or it may have a pc value + stop_id /// to indicate a specific point in the debug session so the correct section /// load list is used for symbolication. /// /// Local variables may be available, or not. A register context may be /// available, or not. class StackFrame : public ExecutionContextScope, public std::enable_shared_from_this { public: enum ExpressionPathOption { eExpressionPathOptionCheckPtrVsMember = (1u << 0), eExpressionPathOptionsNoFragileObjcIvar = (1u << 1), eExpressionPathOptionsNoSyntheticChildren = (1u << 2), eExpressionPathOptionsNoSyntheticArrayRange = (1u << 3), eExpressionPathOptionsAllowDirectIVarAccess = (1u << 4), eExpressionPathOptionsInspectAnonymousUnions = (1u << 5) }; //------------------------------------------------------------------ /// Construct a StackFrame object without supplying a RegisterContextSP. /// /// This is the one constructor that doesn't take a RegisterContext /// parameter. This ctor may be called when creating a history StackFrame; /// these are used if we've collected a stack trace of pc addresses at /// some point in the past. We may only have pc values. We may have pc /// values and the stop_id when the stack trace was recorded. We may have a /// CFA, or more likely, we won't. /// /// @param [in] thread_sp /// The Thread that this frame belongs to. /// /// @param [in] frame_idx /// This StackFrame's frame index number in the Thread. If inlined stack /// frames are being created, this may differ from the concrete_frame_idx /// which is the frame index without any inlined stack frames. /// /// @param [in] concrete_frame_idx /// The StackFrame's frame index number in the Thread without any inlined /// stack frames being included in the index. /// /// @param [in] cfa /// The Canonical Frame Address (this terminology from DWARF) for this /// stack frame. The CFA for a stack frame does not change over the /// span of the stack frame's existence. It is often the value of the /// caller's stack pointer before the call instruction into this frame's /// function. It is usually not the same as the frame pointer register's /// value. /// /// @param [in] cfa_is_valid /// A history stack frame may not have a CFA value collected. We want to /// distinguish between "no CFA available" and a CFA of /// LLDB_INVALID_ADDRESS. /// /// @param [in] pc /// The current pc value of this stack frame. /// /// @param [in] stop_id /// The stop_id which should be used when looking up symbols for the pc /// value, /// if appropriate. This argument is ignored if stop_id_is_valid is false. /// /// @param [in] stop_id_is_valid /// If the stop_id argument provided is not needed for this StackFrame, this /// should be false. If this is a history stack frame and we know the /// stop_id /// when the pc value was collected, that stop_id should be provided and /// this /// will be true. /// /// @param [in] is_history_frame /// If this is a historical stack frame -- possibly without CFA or registers /// or /// local variables -- then this should be set to true. /// /// @param [in] sc_ptr /// Optionally seed the StackFrame with the SymbolContext information that /// has /// already been discovered. //------------------------------------------------------------------ StackFrame(const lldb::ThreadSP &thread_sp, lldb::user_id_t frame_idx, lldb::user_id_t concrete_frame_idx, lldb::addr_t cfa, bool cfa_is_valid, lldb::addr_t pc, uint32_t stop_id, bool stop_id_is_valid, bool is_history_frame, const SymbolContext *sc_ptr); StackFrame(const lldb::ThreadSP &thread_sp, lldb::user_id_t frame_idx, lldb::user_id_t concrete_frame_idx, const lldb::RegisterContextSP ®_context_sp, lldb::addr_t cfa, lldb::addr_t pc, const SymbolContext *sc_ptr); StackFrame(const lldb::ThreadSP &thread_sp, lldb::user_id_t frame_idx, lldb::user_id_t concrete_frame_idx, const lldb::RegisterContextSP ®_context_sp, lldb::addr_t cfa, const Address &pc, const SymbolContext *sc_ptr); ~StackFrame() override; lldb::ThreadSP GetThread() const { return m_thread_wp.lock(); } StackID &GetStackID(); //------------------------------------------------------------------ /// Get an Address for the current pc value in this StackFrame. /// /// May not be the same as the actual PC value for inlined stack frames. /// /// @return /// The Address object set to the current PC value. //------------------------------------------------------------------ const Address &GetFrameCodeAddress(); //------------------------------------------------------------------ /// Change the pc value for a given thread. /// /// Change the current pc value for the frame on this thread. /// /// @param[in] pc /// The load address that the pc will be set to. /// /// @return /// true if the pc was changed. false if this failed -- possibly /// because this frame is not a live StackFrame. //------------------------------------------------------------------ bool ChangePC(lldb::addr_t pc); //------------------------------------------------------------------ /// Provide a SymbolContext for this StackFrame's current pc value. /// /// The StackFrame maintains this SymbolContext and adds additional /// information /// to it on an as-needed basis. This helps to avoid different functions /// looking up symbolic information for a given pc value multiple times. /// /// @params [in] resolve_scope /// Flags from the SymbolContextItem enumerated type which specify what /// type of symbol context is needed by this caller. /// /// @return /// A SymbolContext reference which includes the types of information /// requested by resolve_scope, if they are available. //------------------------------------------------------------------ const SymbolContext &GetSymbolContext(uint32_t resolve_scope); //------------------------------------------------------------------ /// Return the Canonical Frame Address (DWARF term) for this frame. /// /// The CFA is typically the value of the stack pointer register before /// the call invocation is made. It will not change during the lifetime /// of a stack frame. It is often not the same thing as the frame pointer /// register value. /// /// Live StackFrames will always have a CFA but other types of frames may /// not be able to supply one. /// /// @param [out] value /// The address of the CFA for this frame, if available. /// /// @param [out] error_ptr /// If there is an error determining the CFA address, this may contain a /// string explaining the failure. /// /// @return /// Returns true if the CFA value was successfully set in value. Some /// frames may be unable to provide this value; they will return false. //------------------------------------------------------------------ bool GetFrameBaseValue(Scalar &value, Status *error_ptr); //------------------------------------------------------------------ /// Get the DWARFExpression corresponding to the Canonical Frame Address. /// /// Often a register (bp), but sometimes a register + offset. /// /// @param [out] error_ptr /// If there is an error determining the CFA address, this may contain a /// string explaining the failure. /// /// @return /// Returns the corresponding DWARF expression, or NULL. //------------------------------------------------------------------ DWARFExpression *GetFrameBaseExpression(Status *error_ptr); //------------------------------------------------------------------ /// Get the current lexical scope block for this StackFrame, if possible. /// /// If debug information is available for this stack frame, return a /// pointer to the innermost lexical Block that the frame is currently /// executing. /// /// @return /// A pointer to the current Block. nullptr is returned if this can /// not be provided. //------------------------------------------------------------------ Block *GetFrameBlock(); //------------------------------------------------------------------ /// Get the RegisterContext for this frame, if possible. /// /// Returns a shared pointer to the RegisterContext for this stack frame. /// Only a live StackFrame object will be able to return a RegisterContext - /// callers must be prepared for an empty shared pointer being returned. /// /// Even a live StackFrame RegisterContext may not be able to provide all /// registers. Only the currently executing frame (frame 0) can reliably /// provide every register in the register context. /// /// @return /// The RegisterContext shared point for this frame. //------------------------------------------------------------------ lldb::RegisterContextSP GetRegisterContext(); const lldb::RegisterContextSP &GetRegisterContextSP() const { return m_reg_context_sp; } //------------------------------------------------------------------ /// Retrieve the list of variables that are in scope at this StackFrame's pc. /// /// A frame that is not live may return an empty VariableList for a given /// pc value even though variables would be available at this point if /// it were a live stack frame. /// /// @param[in] get_file_globals /// Whether to also retrieve compilation-unit scoped variables /// that are visible to the entire compilation unit (e.g. file /// static in C, globals that are homed in this CU). /// /// @return /// A pointer to a list of variables. //------------------------------------------------------------------ VariableList *GetVariableList(bool get_file_globals); //------------------------------------------------------------------ /// Retrieve the list of variables that are in scope at this StackFrame's pc. /// /// A frame that is not live may return an empty VariableListSP for a /// given pc value even though variables would be available at this point /// if it were a live stack frame. /// /// @param[in] get_file_globals /// Whether to also retrieve compilation-unit scoped variables /// that are visible to the entire compilation unit (e.g. file /// static in C, globals that are homed in this CU). /// /// @return /// A pointer to a list of variables. //------------------------------------------------------------------ lldb::VariableListSP GetInScopeVariableList(bool get_file_globals, bool must_have_valid_location = false); //------------------------------------------------------------------ /// Create a ValueObject for a variable name / pathname, possibly /// including simple dereference/child selection syntax. /// /// @param[in] var_expr /// The string specifying a variable to base the VariableObject off /// of. /// /// @param[in] use_dynamic /// Whether the correct dynamic type of an object pointer should be /// determined before creating the object, or if the static type is /// sufficient. One of the DynamicValueType enumerated values. /// /// @param[in] options /// An unsigned integer of flags, values from /// StackFrame::ExpressionPathOption /// enum. /// @param[in] var_sp /// A VariableSP that will be set to the variable described in the /// var_expr path. /// /// @param[in] error /// Record any errors encountered while evaluating var_expr. /// /// @return /// A shared pointer to the ValueObject described by var_expr. //------------------------------------------------------------------ lldb::ValueObjectSP GetValueForVariableExpressionPath( llvm::StringRef var_expr, lldb::DynamicValueType use_dynamic, uint32_t options, lldb::VariableSP &var_sp, Status &error); //------------------------------------------------------------------ /// Determine whether this StackFrame has debug information available or not /// /// @return // true if debug information is available for this frame (function, // compilation unit, block, etc.) //------------------------------------------------------------------ bool HasDebugInformation(); //------------------------------------------------------------------ /// Return the disassembly for the instructions of this StackFrame's function /// as a single C string. /// /// @return // C string with the assembly instructions for this function. //------------------------------------------------------------------ const char *Disassemble(); //------------------------------------------------------------------ /// Print a description for this frame using the frame-format formatter /// settings. /// /// @param [in] strm /// The Stream to print the description to. /// + /// @param [in] show_unique + /// Whether to print the function arguments or not for backtrace unique. + /// /// @param [in] frame_marker /// Optional string that will be prepended to the frame output description. //------------------------------------------------------------------ - void DumpUsingSettingsFormat(Stream *strm, + void DumpUsingSettingsFormat(Stream *strm, bool show_unique = false, const char *frame_marker = nullptr); //------------------------------------------------------------------ /// Print a description for this frame using a default format. /// /// @param [in] strm /// The Stream to print the description to. /// /// @param [in] show_frame_index /// Whether to print the frame number or not. /// /// @param [in] show_fullpaths /// Whether to print the full source paths or just the file base name. //------------------------------------------------------------------ void Dump(Stream *strm, bool show_frame_index, bool show_fullpaths); //------------------------------------------------------------------ /// Print a description of this stack frame and/or the source context/assembly /// for this stack frame. /// /// @param[in] strm /// The Stream to send the output to. /// /// @param[in] show_frame_info /// If true, print the frame info by calling DumpUsingSettingsFormat(). /// /// @param[in] show_source /// If true, print source or disassembly as per the user's settings. /// + /// @param[in] show_unique + /// If true, print using backtrace unique style, without function + /// arguments as per the user's settings. + /// /// @param[in] frame_marker /// Passed to DumpUsingSettingsFormat() for the frame info printing. /// /// @return /// Returns true if successful. //------------------------------------------------------------------ bool GetStatus(Stream &strm, bool show_frame_info, bool show_source, - const char *frame_marker = nullptr); + bool show_unique = false, const char *frame_marker = nullptr); //------------------------------------------------------------------ /// Query whether this frame is a concrete frame on the call stack, /// or if it is an inlined frame derived from the debug information /// and presented by the debugger. /// /// @return /// true if this is an inlined frame. //------------------------------------------------------------------ bool IsInlined(); //------------------------------------------------------------------ /// Query this frame to find what frame it is in this Thread's StackFrameList. /// /// @return /// StackFrame index 0 indicates the currently-executing function. Inline /// frames are included in this frame index count. //------------------------------------------------------------------ uint32_t GetFrameIndex() const; //------------------------------------------------------------------ /// Query this frame to find what frame it is in this Thread's StackFrameList, /// not counting inlined frames. /// /// @return /// StackFrame index 0 indicates the currently-executing function. Inline /// frames are not included in this frame index count; their concrete /// frame index will be the same as the concrete frame that they are /// derived from. //------------------------------------------------------------------ uint32_t GetConcreteFrameIndex() const { return m_concrete_frame_index; } //------------------------------------------------------------------ /// Create a ValueObject for a given Variable in this StackFrame. /// /// @params [in] variable_sp /// The Variable to base this ValueObject on /// /// @params [in] use_dynamic /// Whether the correct dynamic type of the variable should be /// determined before creating the ValueObject, or if the static type /// is sufficient. One of the DynamicValueType enumerated values. /// /// @return // A ValueObject for this variable. //------------------------------------------------------------------ lldb::ValueObjectSP GetValueObjectForFrameVariable(const lldb::VariableSP &variable_sp, lldb::DynamicValueType use_dynamic); //------------------------------------------------------------------ /// Add an arbitrary Variable object (e.g. one that specifics a global or /// static) /// to a StackFrame's list of ValueObjects. /// /// @params [in] variable_sp /// The Variable to base this ValueObject on /// /// @params [in] use_dynamic /// Whether the correct dynamic type of the variable should be /// determined before creating the ValueObject, or if the static type /// is sufficient. One of the DynamicValueType enumerated values. /// /// @return // A ValueObject for this variable. //------------------------------------------------------------------ lldb::ValueObjectSP TrackGlobalVariable(const lldb::VariableSP &variable_sp, lldb::DynamicValueType use_dynamic); //------------------------------------------------------------------ /// Query this frame to determine what the default language should be /// when parsing expressions given the execution context. /// /// @return /// The language of the frame if known, else lldb::eLanguageTypeUnknown. //------------------------------------------------------------------ lldb::LanguageType GetLanguage(); // similar to GetLanguage(), but is allowed to take a potentially incorrect // guess // if exact information is not available lldb::LanguageType GuessLanguage(); //------------------------------------------------------------------ /// Attempt to econstruct the ValueObject for a given raw address touched by /// the current instruction. The ExpressionPath should indicate how to get /// to this value using "frame variable." /// /// @params [in] addr /// The raw address. /// /// @return /// The ValueObject if found. If valid, it has a valid ExpressionPath. //------------------------------------------------------------------ lldb::ValueObjectSP GuessValueForAddress(lldb::addr_t addr); //------------------------------------------------------------------ /// Attempt to reconstruct the ValueObject for the address contained in a /// given register plus an offset. The ExpressionPath should indicate how to /// get to this value using "frame variable." /// /// @params [in] reg /// The name of the register. /// /// @params [in] offset /// The offset from the register. Particularly important for sp... /// /// @return /// The ValueObject if found. If valid, it has a valid ExpressionPath. //------------------------------------------------------------------ lldb::ValueObjectSP GuessValueForRegisterAndOffset(ConstString reg, int64_t offset); //------------------------------------------------------------------ // lldb::ExecutionContextScope pure virtual functions //------------------------------------------------------------------ lldb::TargetSP CalculateTarget() override; lldb::ProcessSP CalculateProcess() override; lldb::ThreadSP CalculateThread() override; lldb::StackFrameSP CalculateStackFrame() override; void CalculateExecutionContext(ExecutionContext &exe_ctx) override; protected: friend class StackFrameList; void SetSymbolContextScope(SymbolContextScope *symbol_scope); void UpdateCurrentFrameFromPreviousFrame(StackFrame &prev_frame); void UpdatePreviousFrameFromCurrentFrame(StackFrame &curr_frame); bool HasCachedData() const; private: //------------------------------------------------------------------ // For StackFrame only //------------------------------------------------------------------ lldb::ThreadWP m_thread_wp; uint32_t m_frame_index; uint32_t m_concrete_frame_index; lldb::RegisterContextSP m_reg_context_sp; StackID m_id; Address m_frame_code_addr; // The frame code address (might not be the same as // the actual PC for inlined frames) as a // section/offset address SymbolContext m_sc; Flags m_flags; Scalar m_frame_base; Status m_frame_base_error; bool m_cfa_is_valid; // Does this frame have a CFA? Different from CFA == // LLDB_INVALID_ADDRESS uint32_t m_stop_id; bool m_stop_id_is_valid; // Does this frame have a stop_id? Use it when // referring to the m_frame_code_addr. bool m_is_history_frame; lldb::VariableListSP m_variable_list_sp; ValueObjectList m_variable_list_value_objects; // Value objects for each // variable in // m_variable_list_sp StreamString m_disassembly; std::recursive_mutex m_mutex; DISALLOW_COPY_AND_ASSIGN(StackFrame); }; } // namespace lldb_private #endif // liblldb_StackFrame_h_ Index: vendor/lldb/dist/include/lldb/Target/StackFrameList.h =================================================================== --- vendor/lldb/dist/include/lldb/Target/StackFrameList.h (revision 320022) +++ vendor/lldb/dist/include/lldb/Target/StackFrameList.h (revision 320023) @@ -1,117 +1,118 @@ //===-- StackFrameList.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_StackFrameList_h_ #define liblldb_StackFrameList_h_ // C Includes // C++ Includes #include #include #include // Other libraries and framework includes // Project includes #include "lldb/Target/StackFrame.h" namespace lldb_private { class StackFrameList { public: //------------------------------------------------------------------ // Constructors and Destructors //------------------------------------------------------------------ StackFrameList(Thread &thread, const lldb::StackFrameListSP &prev_frames_sp, bool show_inline_frames); ~StackFrameList(); uint32_t GetNumFrames(bool can_create = true); lldb::StackFrameSP GetFrameAtIndex(uint32_t idx); lldb::StackFrameSP GetFrameWithConcreteFrameIndex(uint32_t unwind_idx); lldb::StackFrameSP GetFrameWithStackID(const StackID &stack_id); // Mark a stack frame as the current frame uint32_t SetSelectedFrame(lldb_private::StackFrame *frame); uint32_t GetSelectedFrameIndex() const; // Mark a stack frame as the current frame using the frame index bool SetSelectedFrameByIndex(uint32_t idx); uint32_t GetVisibleStackFrameIndex(uint32_t idx) { if (m_current_inlined_depth < UINT32_MAX) return idx - m_current_inlined_depth; else return idx; } void CalculateCurrentInlinedDepth(); void SetDefaultFileAndLineToSelectedFrame(); void Clear(); void InvalidateFrames(uint32_t start_idx); void Dump(Stream *s); lldb::StackFrameSP GetStackFrameSPForStackFramePtr(StackFrame *stack_frame_ptr); size_t GetStatus(Stream &strm, uint32_t first_frame, uint32_t num_frames, bool show_frame_info, uint32_t num_frames_with_source, + bool show_unique = false, const char *frame_marker = nullptr); protected: friend class Thread; bool SetFrameAtIndex(uint32_t idx, lldb::StackFrameSP &frame_sp); static void Merge(std::unique_ptr &curr_ap, lldb::StackFrameListSP &prev_sp); void GetFramesUpTo(uint32_t end_idx); bool GetAllFramesFetched() { return m_concrete_frames_fetched == UINT32_MAX; } void SetAllFramesFetched() { m_concrete_frames_fetched = UINT32_MAX; } bool DecrementCurrentInlinedDepth(); void ResetCurrentInlinedDepth(); uint32_t GetCurrentInlinedDepth(); void SetCurrentInlinedDepth(uint32_t new_depth); typedef std::vector collection; typedef collection::iterator iterator; typedef collection::const_iterator const_iterator; Thread &m_thread; lldb::StackFrameListSP m_prev_frames_sp; mutable std::recursive_mutex m_mutex; collection m_frames; uint32_t m_selected_frame_idx; uint32_t m_concrete_frames_fetched; uint32_t m_current_inlined_depth; lldb::addr_t m_current_inlined_pc; bool m_show_inlined_frames; private: DISALLOW_COPY_AND_ASSIGN(StackFrameList); }; } // namespace lldb_private #endif // liblldb_StackFrameList_h_ Index: vendor/lldb/dist/include/lldb/Target/Thread.h =================================================================== --- vendor/lldb/dist/include/lldb/Target/Thread.h (revision 320022) +++ vendor/lldb/dist/include/lldb/Target/Thread.h (revision 320023) @@ -1,1364 +1,1364 @@ //===-- Thread.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_Thread_h_ #define liblldb_Thread_h_ // C Includes // C++ Includes #include #include #include #include // Other libraries and framework includes // Project includes #include "lldb/Core/Broadcaster.h" #include "lldb/Core/Event.h" #include "lldb/Core/StructuredData.h" #include "lldb/Core/UserSettingsController.h" #include "lldb/Target/ExecutionContextScope.h" #include "lldb/Target/RegisterCheckpoint.h" #include "lldb/Target/StackFrameList.h" #include "lldb/Utility/UserID.h" #include "lldb/lldb-private.h" #define LLDB_THREAD_MAX_STOP_EXC_DATA 8 namespace lldb_private { class ThreadProperties : public Properties { public: ThreadProperties(bool is_global); ~ThreadProperties() override; //------------------------------------------------------------------ /// The regular expression returned determines symbols that this /// thread won't stop in during "step-in" operations. /// /// @return /// A pointer to a regular expression to compare against symbols, /// or nullptr if all symbols are allowed. /// //------------------------------------------------------------------ const RegularExpression *GetSymbolsToAvoidRegexp(); FileSpecList &GetLibrariesToAvoid() const; bool GetTraceEnabledState() const; bool GetStepInAvoidsNoDebug() const; bool GetStepOutAvoidsNoDebug() const; }; typedef std::shared_ptr ThreadPropertiesSP; class Thread : public std::enable_shared_from_this, public ThreadProperties, public UserID, public ExecutionContextScope, public Broadcaster { public: //------------------------------------------------------------------ /// Broadcaster event bits definitions. //------------------------------------------------------------------ enum { eBroadcastBitStackChanged = (1 << 0), eBroadcastBitThreadSuspended = (1 << 1), eBroadcastBitThreadResumed = (1 << 2), eBroadcastBitSelectedFrameChanged = (1 << 3), eBroadcastBitThreadSelected = (1 << 4) }; static ConstString &GetStaticBroadcasterClass(); ConstString &GetBroadcasterClass() const override { return GetStaticBroadcasterClass(); } class ThreadEventData : public EventData { public: ThreadEventData(const lldb::ThreadSP thread_sp); ThreadEventData(const lldb::ThreadSP thread_sp, const StackID &stack_id); ThreadEventData(); ~ThreadEventData() override; static const ConstString &GetFlavorString(); const ConstString &GetFlavor() const override { return ThreadEventData::GetFlavorString(); } void Dump(Stream *s) const override; static const ThreadEventData *GetEventDataFromEvent(const Event *event_ptr); static lldb::ThreadSP GetThreadFromEvent(const Event *event_ptr); static StackID GetStackIDFromEvent(const Event *event_ptr); static lldb::StackFrameSP GetStackFrameFromEvent(const Event *event_ptr); lldb::ThreadSP GetThread() const { return m_thread_sp; } StackID GetStackID() const { return m_stack_id; } private: lldb::ThreadSP m_thread_sp; StackID m_stack_id; DISALLOW_COPY_AND_ASSIGN(ThreadEventData); }; struct ThreadStateCheckpoint { uint32_t orig_stop_id; // Dunno if I need this yet but it is an interesting // bit of data. lldb::StopInfoSP stop_info_sp; // You have to restore the stop info or you // might continue with the wrong signals. std::vector m_completed_plan_stack; lldb::RegisterCheckpointSP register_backup_sp; // You need to restore the registers, of course... uint32_t current_inlined_depth; lldb::addr_t current_inlined_pc; }; //------------------------------------------------------------------ /// Constructor /// /// @param [in] process /// /// @param [in] tid /// /// @param [in] use_invalid_index_id /// Optional parameter, defaults to false. The only subclass that /// is likely to set use_invalid_index_id == true is the HistoryThread /// class. In that case, the Thread we are constructing represents /// a thread from earlier in the program execution. We may have the /// tid of the original thread that they represent but we don't want /// to reuse the IndexID of that thread, or create a new one. If a /// client wants to know the original thread's IndexID, they should use /// Thread::GetExtendedBacktraceOriginatingIndexID(). //------------------------------------------------------------------ Thread(Process &process, lldb::tid_t tid, bool use_invalid_index_id = false); ~Thread() override; static void SettingsInitialize(); static void SettingsTerminate(); static const ThreadPropertiesSP &GetGlobalProperties(); lldb::ProcessSP GetProcess() const { return m_process_wp.lock(); } int GetResumeSignal() const { return m_resume_signal; } void SetResumeSignal(int signal) { m_resume_signal = signal; } lldb::StateType GetState() const; void SetState(lldb::StateType state); //------------------------------------------------------------------ /// Sets the USER resume state for this thread. If you set a thread to /// suspended with /// this API, it won't take part in any of the arbitration for ShouldResume, /// and will stay /// suspended even when other threads do get to run. /// /// N.B. This is not the state that is used internally by thread plans to /// implement /// staying on one thread while stepping over a breakpoint, etc. The is the /// TemporaryResume state, and if you are implementing some bit of strategy in /// the stepping /// machinery you should be using that state and not the user resume state. /// /// If you are just preparing all threads to run, you should not override the /// threads that are /// marked as suspended by the debugger. In that case, pass override_suspend /// = false. If you want /// to force the thread to run (e.g. the "thread continue" command, or are /// resetting the state /// (e.g. in SBThread::Resume()), then pass true to override_suspend. /// @return /// The User resume state for this thread. //------------------------------------------------------------------ void SetResumeState(lldb::StateType state, bool override_suspend = false) { if (m_resume_state == lldb::eStateSuspended && !override_suspend) return; m_resume_state = state; } //------------------------------------------------------------------ /// Gets the USER resume state for this thread. This is not the same as what /// this thread is going to do for any particular step, however if this thread /// returns eStateSuspended, then the process control logic will never allow /// this /// thread to run. /// /// @return /// The User resume state for this thread. //------------------------------------------------------------------ lldb::StateType GetResumeState() const { return m_resume_state; } // This function is called on all the threads before "ShouldResume" and // "WillResume" in case a thread needs to change its state before the // ThreadList polls all the threads to figure out which ones actually // will get to run and how. void SetupForResume(); // Do not override this function, it is for thread plan logic only bool ShouldResume(lldb::StateType resume_state); // Override this to do platform specific tasks before resume. virtual void WillResume(lldb::StateType resume_state) {} // This clears generic thread state after a resume. If you subclass this, // be sure to call it. virtual void DidResume(); // This notifies the thread when a private stop occurs. virtual void DidStop(); virtual void RefreshStateAfterStop() = 0; void WillStop(); bool ShouldStop(Event *event_ptr); Vote ShouldReportStop(Event *event_ptr); Vote ShouldReportRun(Event *event_ptr); void Flush(); // Return whether this thread matches the specification in ThreadSpec. This // is a virtual // method because at some point we may extend the thread spec with a platform // specific // dictionary of attributes, which then only the platform specific Thread // implementation // would know how to match. For now, this just calls through to the // ThreadSpec's // ThreadPassesBasicTests method. virtual bool MatchesSpec(const ThreadSpec *spec); lldb::StopInfoSP GetStopInfo(); lldb::StopReason GetStopReason(); bool StopInfoIsUpToDate() const; // This sets the stop reason to a "blank" stop reason, so you can call // functions on the thread // without having the called function run with whatever stop reason you // stopped with. void SetStopInfoToNothing(); bool ThreadStoppedForAReason(); static const char *RunModeAsCString(lldb::RunMode mode); static const char *StopReasonAsCString(lldb::StopReason reason); virtual const char *GetInfo() { return nullptr; } //------------------------------------------------------------------ /// Retrieve a dictionary of information about this thread /// /// On Mac OS X systems there may be voucher information. /// The top level dictionary returned will have an "activity" key and the /// value of the activity is a dictionary. Keys in that dictionary will /// be "name" and "id", among others. /// There may also be "trace_messages" (an array) with each entry in that /// array /// being a dictionary (keys include "message" with the text of the trace /// message). //------------------------------------------------------------------ StructuredData::ObjectSP GetExtendedInfo() { if (m_extended_info_fetched == false) { m_extended_info = FetchThreadExtendedInfo(); m_extended_info_fetched = true; } return m_extended_info; } virtual const char *GetName() { return nullptr; } virtual void SetName(const char *name) {} //------------------------------------------------------------------ /// Whether this thread can be associated with a libdispatch queue /// /// The Thread may know if it is associated with a libdispatch queue, /// it may know definitively that it is NOT associated with a libdispatch /// queue, or it may be unknown whether it is associated with a libdispatch /// queue. /// /// @return /// eLazyBoolNo if this thread is definitely not associated with a /// libdispatch queue (e.g. on a non-Darwin system where GCD aka /// libdispatch is not available). /// /// eLazyBoolYes this thread is associated with a libdispatch queue. /// /// eLazyBoolCalculate this thread may be associated with a libdispatch /// queue but the thread doesn't know one way or the other. //------------------------------------------------------------------ virtual lldb_private::LazyBool GetAssociatedWithLibdispatchQueue() { return eLazyBoolNo; } virtual void SetAssociatedWithLibdispatchQueue( lldb_private::LazyBool associated_with_libdispatch_queue) {} //------------------------------------------------------------------ /// Retrieve the Queue ID for the queue currently using this Thread /// /// If this Thread is doing work on behalf of a libdispatch/GCD queue, /// retrieve the QueueID. /// /// This is a unique identifier for the libdispatch/GCD queue in a /// process. Often starting at 1 for the initial system-created /// queues and incrementing, a QueueID will not be reused for a /// different queue during the lifetime of a process. /// /// @return /// A QueueID if the Thread subclass implements this, else /// LLDB_INVALID_QUEUE_ID. //------------------------------------------------------------------ virtual lldb::queue_id_t GetQueueID() { return LLDB_INVALID_QUEUE_ID; } virtual void SetQueueID(lldb::queue_id_t new_val) {} //------------------------------------------------------------------ /// Retrieve the Queue name for the queue currently using this Thread /// /// If this Thread is doing work on behalf of a libdispatch/GCD queue, /// retrieve the Queue name. /// /// @return /// The Queue name, if the Thread subclass implements this, else /// nullptr. //------------------------------------------------------------------ virtual const char *GetQueueName() { return nullptr; } virtual void SetQueueName(const char *name) {} //------------------------------------------------------------------ /// Retrieve the Queue kind for the queue currently using this Thread /// /// If this Thread is doing work on behalf of a libdispatch/GCD queue, /// retrieve the Queue kind - either eQueueKindSerial or /// eQueueKindConcurrent, indicating that this queue processes work /// items serially or concurrently. /// /// @return /// The Queue kind, if the Thread subclass implements this, else /// eQueueKindUnknown. //------------------------------------------------------------------ virtual lldb::QueueKind GetQueueKind() { return lldb::eQueueKindUnknown; } virtual void SetQueueKind(lldb::QueueKind kind) {} //------------------------------------------------------------------ /// Retrieve the Queue for this thread, if any. /// /// @return /// A QueueSP for the queue that is currently associated with this /// thread. /// An empty shared pointer indicates that this thread is not /// associated with a queue, or libdispatch queues are not /// supported on this target. //------------------------------------------------------------------ virtual lldb::QueueSP GetQueue() { return lldb::QueueSP(); } //------------------------------------------------------------------ /// Retrieve the address of the libdispatch_queue_t struct for queue /// currently using this Thread /// /// If this Thread is doing work on behalf of a libdispatch/GCD queue, /// retrieve the address of the libdispatch_queue_t structure describing /// the queue. /// /// This address may be reused for different queues later in the Process /// lifetime and should not be used to identify a queue uniquely. Use /// the GetQueueID() call for that. /// /// @return /// The Queue's libdispatch_queue_t address if the Thread subclass /// implements this, else LLDB_INVALID_ADDRESS. //------------------------------------------------------------------ virtual lldb::addr_t GetQueueLibdispatchQueueAddress() { return LLDB_INVALID_ADDRESS; } virtual void SetQueueLibdispatchQueueAddress(lldb::addr_t dispatch_queue_t) {} //------------------------------------------------------------------ /// Whether this Thread already has all the Queue information cached or not /// /// A Thread may be associated with a libdispatch work Queue at a given /// public stop event. If so, the thread can satisify requests like /// GetQueueLibdispatchQueueAddress, GetQueueKind, GetQueueName, and /// GetQueueID /// either from information from the remote debug stub when it is initially /// created, or it can query the SystemRuntime for that information. /// /// This method allows the SystemRuntime to discover if a thread has this /// information already, instead of calling the thread to get the information /// and having the thread call the SystemRuntime again. //------------------------------------------------------------------ virtual bool ThreadHasQueueInformation() const { return false; } virtual uint32_t GetStackFrameCount() { return GetStackFrameList()->GetNumFrames(); } virtual lldb::StackFrameSP GetStackFrameAtIndex(uint32_t idx) { return GetStackFrameList()->GetFrameAtIndex(idx); } virtual lldb::StackFrameSP GetFrameWithConcreteFrameIndex(uint32_t unwind_idx); bool DecrementCurrentInlinedDepth() { return GetStackFrameList()->DecrementCurrentInlinedDepth(); } uint32_t GetCurrentInlinedDepth() { return GetStackFrameList()->GetCurrentInlinedDepth(); } Status ReturnFromFrameWithIndex(uint32_t frame_idx, lldb::ValueObjectSP return_value_sp, bool broadcast = false); Status ReturnFromFrame(lldb::StackFrameSP frame_sp, lldb::ValueObjectSP return_value_sp, bool broadcast = false); Status JumpToLine(const FileSpec &file, uint32_t line, bool can_leave_function, std::string *warnings = nullptr); virtual lldb::StackFrameSP GetFrameWithStackID(const StackID &stack_id) { if (stack_id.IsValid()) return GetStackFrameList()->GetFrameWithStackID(stack_id); return lldb::StackFrameSP(); } uint32_t GetSelectedFrameIndex() { return GetStackFrameList()->GetSelectedFrameIndex(); } lldb::StackFrameSP GetSelectedFrame(); uint32_t SetSelectedFrame(lldb_private::StackFrame *frame, bool broadcast = false); bool SetSelectedFrameByIndex(uint32_t frame_idx, bool broadcast = false); bool SetSelectedFrameByIndexNoisily(uint32_t frame_idx, Stream &output_stream); void SetDefaultFileAndLineToSelectedFrame() { GetStackFrameList()->SetDefaultFileAndLineToSelectedFrame(); } virtual lldb::RegisterContextSP GetRegisterContext() = 0; virtual lldb::RegisterContextSP CreateRegisterContextForFrame(StackFrame *frame) = 0; virtual void ClearStackFrames(); virtual bool SetBackingThread(const lldb::ThreadSP &thread_sp) { return false; } virtual lldb::ThreadSP GetBackingThread() const { return lldb::ThreadSP(); } virtual void ClearBackingThread() { // Subclasses can use this function if a thread is actually backed by // another thread. This is currently used for the OperatingSystem plug-ins // where they might have a thread that is in memory, yet its registers // are available through the lldb_private::Thread subclass for the current // lldb_private::Process class. Since each time the process stops the // backing // threads for memory threads can change, we need a way to clear the backing // thread for all memory threads each time we stop. } // If stop_format is true, this will be the form used when we print stop info. // If false, it will be the form we use for thread list and co. void DumpUsingSettingsFormat(Stream &strm, uint32_t frame_idx, bool stop_format); bool GetDescription(Stream &s, lldb::DescriptionLevel level, bool print_json_thread, bool print_json_stopinfo); //------------------------------------------------------------------ /// 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] step_in_avoids_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 whether it /// has debug info. /// /// @param[in] step_out_avoids_code_without_debug_info /// If \a true, then if you step out to code with no debug info, keep /// stepping out till you get to code with debug info. /// /// @return /// An error that describes anything that went wrong //------------------------------------------------------------------ virtual Status StepIn(bool source_step, LazyBool step_in_avoids_code_without_debug_info = eLazyBoolCalculate, LazyBool step_out_avoids_code_without_debug_info = eLazyBoolCalculate); //------------------------------------------------------------------ /// 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 Status StepOver( bool source_step, LazyBool step_out_avoids_code_without_debug_info = eLazyBoolCalculate); //------------------------------------------------------------------ /// 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 Status 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. /// /// @return /// LLDB_INVALID_ADDRESS if not supported, otherwise the thread /// pointer value. //------------------------------------------------------------------ virtual lldb::addr_t GetThreadPointer(); //------------------------------------------------------------------ /// Retrieves the per-module TLS block for a thread. /// /// @param[in] module /// The module to query TLS data for. /// /// @param[in] tls_file_addr /// The thread local address in module /// @return /// If the thread has TLS data allocated for the /// module, the address of the TLS block. Otherwise /// LLDB_INVALID_ADDRESS is returned. //------------------------------------------------------------------ virtual lldb::addr_t GetThreadLocalData(const lldb::ModuleSP module, lldb::addr_t tls_file_addr); //------------------------------------------------------------------ /// Check whether this thread is safe to run functions /// /// The SystemRuntime may know of certain thread states (functions in /// process of execution, for instance) which can make it unsafe for /// functions to be called. /// /// @return /// True if it is safe to call functions on this thread. /// False if function calls should be avoided on this thread. //------------------------------------------------------------------ virtual bool SafeToCallFunctions(); //------------------------------------------------------------------ // Thread Plan Providers: // This section provides the basic thread plans that the Process control // machinery uses to run the target. ThreadPlan.h provides more details on // how this mechanism works. // The thread provides accessors to a set of plans that perform basic // operations. // The idea is that particular Platform plugins can override these methods to // provide the implementation of these basic operations appropriate to their // environment. // // NB: All the QueueThreadPlanXXX providers return Shared Pointers to // Thread plans. This is useful so that you can modify the plans after // creation in ways specific to that plan type. Also, it is often necessary // for // ThreadPlans that utilize other ThreadPlans to implement their task to keep // a shared // pointer to the sub-plan. // But besides that, the shared pointers should only be held onto by entities // who live no longer // than the thread containing the ThreadPlan. // FIXME: If this becomes a problem, we can make a version that just returns a // pointer, // which it is clearly unsafe to hold onto, and a shared pointer version, and // only allow // ThreadPlan and Co. to use the latter. That is made more annoying to do // because there's // no elegant way to friend a method to all sub-classes of a given class. // //------------------------------------------------------------------ //------------------------------------------------------------------ /// Queues the base plan for a thread. /// The version returned by Process does some things that are useful, /// like handle breakpoints and signals, so if you return a plugin specific /// one you probably want to call through to the Process one for anything /// your plugin doesn't explicitly handle. /// /// @param[in] abort_other_plans /// \b true if we discard the currently queued plans and replace them with /// this one. /// Otherwise this plan will go on the end of the plan stack. /// /// @return /// A shared pointer to the newly queued thread plan, or nullptr if the /// plan could not be queued. //------------------------------------------------------------------ virtual lldb::ThreadPlanSP QueueFundamentalPlan(bool abort_other_plans); //------------------------------------------------------------------ /// Queues the plan used to step one instruction from the current PC of \a /// thread. /// /// @param[in] step_over /// \b true if we step over calls to functions, false if we step in. /// /// @param[in] abort_other_plans /// \b true if we discard the currently queued plans and replace them with /// this one. /// Otherwise this plan will go on the end of the plan stack. /// /// @param[in] stop_other_threads /// \b true if we will stop other threads while we single step this one. /// /// @return /// A shared pointer to the newly queued thread plan, or nullptr if the /// plan could not be queued. //------------------------------------------------------------------ virtual lldb::ThreadPlanSP QueueThreadPlanForStepSingleInstruction( bool step_over, bool abort_other_plans, bool stop_other_threads); //------------------------------------------------------------------ /// Queues the plan used to step through an address range, stepping over /// function calls. /// /// @param[in] abort_other_plans /// \b true if we discard the currently queued plans and replace them with /// this one. /// Otherwise this plan will go on the end of the plan stack. /// /// @param[in] type /// Type of step to do, only eStepTypeInto and eStepTypeOver are supported /// by this plan. /// /// @param[in] range /// The address range to step through. /// /// @param[in] addr_context /// When dealing with stepping through inlined functions the current PC is /// not enough information to know /// what "step" means. For instance a series of nested inline functions /// might start at the same address. // The \a addr_context provides the current symbol context the step /// is supposed to be out of. // FIXME: Currently unused. /// /// @param[in] stop_other_threads /// \b true if we will stop other threads while we single step this one. /// /// @param[in] step_out_avoids_code_without_debug_info /// If eLazyBoolYes, if the step over steps out it will continue to step /// out till it comes to a frame with debug info. /// If eLazyBoolCalculate, we will consult the default set in the thread. /// /// @return /// A shared pointer to the newly queued thread plan, or nullptr if the /// plan could not be queued. //------------------------------------------------------------------ virtual lldb::ThreadPlanSP QueueThreadPlanForStepOverRange( bool abort_other_plans, const AddressRange &range, const SymbolContext &addr_context, lldb::RunMode stop_other_threads, LazyBool step_out_avoids_code_without_debug_info = eLazyBoolCalculate); // Helper function that takes a LineEntry to step, insted of an AddressRange. // This may combine multiple // LineEntries of the same source line number to step over a longer address // range in a single operation. virtual lldb::ThreadPlanSP QueueThreadPlanForStepOverRange( bool abort_other_plans, const LineEntry &line_entry, const SymbolContext &addr_context, lldb::RunMode stop_other_threads, LazyBool step_out_avoids_code_without_debug_info = eLazyBoolCalculate); //------------------------------------------------------------------ /// Queues the plan used to step through an address range, stepping into /// functions. /// /// @param[in] abort_other_plans /// \b true if we discard the currently queued plans and replace them with /// this one. /// Otherwise this plan will go on the end of the plan stack. /// /// @param[in] type /// Type of step to do, only eStepTypeInto and eStepTypeOver are supported /// by this plan. /// /// @param[in] range /// The address range to step through. /// /// @param[in] addr_context /// When dealing with stepping through inlined functions the current PC is /// not enough information to know /// what "step" means. For instance a series of nested inline functions /// might start at the same address. // The \a addr_context provides the current symbol context the step /// is supposed to be out of. // FIXME: Currently unused. /// /// @param[in] step_in_target /// Name if function we are trying to step into. We will step out if we /// don't land in that function. /// /// @param[in] stop_other_threads /// \b true if we will stop other threads while we single step this one. /// /// @param[in] step_in_avoids_code_without_debug_info /// If eLazyBoolYes we will step out if we step into code with no debug /// info. /// If eLazyBoolCalculate we will consult the default set in the thread. /// /// @param[in] step_out_avoids_code_without_debug_info /// If eLazyBoolYes, if the step over steps out it will continue to step /// out till it comes to a frame with debug info. /// If eLazyBoolCalculate, it will consult the default set in the thread. /// /// @return /// A shared pointer to the newly queued thread plan, or nullptr if the /// plan could not be queued. //------------------------------------------------------------------ virtual lldb::ThreadPlanSP QueueThreadPlanForStepInRange( bool abort_other_plans, const AddressRange &range, const SymbolContext &addr_context, const char *step_in_target, lldb::RunMode stop_other_threads, LazyBool step_in_avoids_code_without_debug_info = eLazyBoolCalculate, LazyBool step_out_avoids_code_without_debug_info = eLazyBoolCalculate); // Helper function that takes a LineEntry to step, insted of an AddressRange. // This may combine multiple // LineEntries of the same source line number to step over a longer address // range in a single operation. virtual lldb::ThreadPlanSP QueueThreadPlanForStepInRange( bool abort_other_plans, const LineEntry &line_entry, const SymbolContext &addr_context, const char *step_in_target, lldb::RunMode stop_other_threads, LazyBool step_in_avoids_code_without_debug_info = eLazyBoolCalculate, LazyBool step_out_avoids_code_without_debug_info = eLazyBoolCalculate); //------------------------------------------------------------------ /// Queue the plan used to step out of the function at the current PC of /// \a thread. /// /// @param[in] abort_other_plans /// \b true if we discard the currently queued plans and replace them with /// this one. /// Otherwise this plan will go on the end of the plan stack. /// /// @param[in] addr_context /// When dealing with stepping through inlined functions the current PC is /// not enough information to know /// what "step" means. For instance a series of nested inline functions /// might start at the same address. // The \a addr_context provides the current symbol context the step /// is supposed to be out of. // FIXME: Currently unused. /// /// @param[in] first_insn /// \b true if this is the first instruction of a function. /// /// @param[in] stop_other_threads /// \b true if we will stop other threads while we single step this one. /// /// @param[in] stop_vote /// @param[in] run_vote /// See standard meanings for the stop & run votes in ThreadPlan.h. /// /// @param[in] step_out_avoids_code_without_debug_info /// If eLazyBoolYes, if the step over steps out it will continue to step /// out till it comes to a frame with debug info. /// If eLazyBoolCalculate, it will consult the default set in the thread. /// /// @return /// A shared pointer to the newly queued thread plan, or nullptr if the /// plan could not be queued. //------------------------------------------------------------------ virtual lldb::ThreadPlanSP QueueThreadPlanForStepOut( bool abort_other_plans, SymbolContext *addr_context, bool first_insn, bool stop_other_threads, Vote stop_vote, // = eVoteYes, Vote run_vote, // = eVoteNoOpinion); uint32_t frame_idx, LazyBool step_out_avoids_code_without_debug_info = eLazyBoolCalculate); //------------------------------------------------------------------ /// Queue the plan used to step out of the function at the current PC of /// a thread. This version does not consult the should stop here callback, /// and should only /// be used by other thread plans when they need to retain control of the step /// out. /// /// @param[in] abort_other_plans /// \b true if we discard the currently queued plans and replace them with /// this one. /// Otherwise this plan will go on the end of the plan stack. /// /// @param[in] addr_context /// When dealing with stepping through inlined functions the current PC is /// not enough information to know /// what "step" means. For instance a series of nested inline functions /// might start at the same address. // The \a addr_context provides the current symbol context the step /// is supposed to be out of. // FIXME: Currently unused. /// /// @param[in] first_insn /// \b true if this is the first instruction of a function. /// /// @param[in] stop_other_threads /// \b true if we will stop other threads while we single step this one. /// /// @param[in] stop_vote /// @param[in] run_vote /// See standard meanings for the stop & run votes in ThreadPlan.h. /// /// @param[in] continue_to_next_branch /// Normally this will enqueue a plan that will put a breakpoint on the /// return address and continue /// to there. If continue_to_next_branch is true, this is an operation not /// involving the user -- /// e.g. stepping "next" in a source line and we instruction stepped into /// another function -- /// so instead of putting a breakpoint on the return address, advance the /// breakpoint to the /// end of the source line that is doing the call, or until the next flow /// control instruction. /// If the return value from the function call is to be retrieved / /// displayed to the user, you must stop /// on the return address. The return value may be stored in volatile /// registers which are overwritten /// before the next branch instruction. /// /// @return /// A shared pointer to the newly queued thread plan, or nullptr if the /// plan could not be queued. //------------------------------------------------------------------ virtual lldb::ThreadPlanSP QueueThreadPlanForStepOutNoShouldStop( bool abort_other_plans, SymbolContext *addr_context, bool first_insn, bool stop_other_threads, Vote stop_vote, // = eVoteYes, Vote run_vote, // = eVoteNoOpinion); uint32_t frame_idx, bool continue_to_next_branch = false); //------------------------------------------------------------------ /// Gets the plan used to step through the code that steps from a function /// call site at the current PC into the actual function call. /// /// /// @param[in] return_stack_id /// The stack id that we will return to (by setting backstop breakpoints on /// the return /// address to that frame) if we fail to step through. /// /// @param[in] abort_other_plans /// \b true if we discard the currently queued plans and replace them with /// this one. /// Otherwise this plan will go on the end of the plan stack. /// /// @param[in] stop_other_threads /// \b true if we will stop other threads while we single step this one. /// /// @return /// A shared pointer to the newly queued thread plan, or nullptr if the /// plan could not be queued. //------------------------------------------------------------------ virtual lldb::ThreadPlanSP QueueThreadPlanForStepThrough(StackID &return_stack_id, bool abort_other_plans, bool stop_other_threads); //------------------------------------------------------------------ /// Gets the plan used to continue from the current PC. /// This is a simple plan, mostly useful as a backstop when you are continuing /// for some particular purpose. /// /// @param[in] abort_other_plans /// \b true if we discard the currently queued plans and replace them with /// this one. /// Otherwise this plan will go on the end of the plan stack. /// /// @param[in] target_addr /// The address to which we're running. /// /// @param[in] stop_other_threads /// \b true if we will stop other threads while we single step this one. /// /// @return /// A shared pointer to the newly queued thread plan, or nullptr if the /// plan could not be queued. //------------------------------------------------------------------ virtual lldb::ThreadPlanSP QueueThreadPlanForRunToAddress(bool abort_other_plans, Address &target_addr, bool stop_other_threads); virtual lldb::ThreadPlanSP QueueThreadPlanForStepUntil(bool abort_other_plans, lldb::addr_t *address_list, size_t num_addresses, bool stop_others, uint32_t frame_idx); virtual lldb::ThreadPlanSP QueueThreadPlanForStepScripted(bool abort_other_plans, const char *class_name, bool stop_other_threads); //------------------------------------------------------------------ // Thread Plan accessors: //------------------------------------------------------------------ //------------------------------------------------------------------ /// Gets the plan which will execute next on the plan stack. /// /// @return /// A pointer to the next executed plan. //------------------------------------------------------------------ ThreadPlan *GetCurrentPlan(); //------------------------------------------------------------------ /// Unwinds the thread stack for the innermost expression plan currently /// on the thread plan stack. /// /// @return /// An error if the thread plan could not be unwound. //------------------------------------------------------------------ Status UnwindInnermostExpression(); //------------------------------------------------------------------ /// Gets the outer-most plan that was popped off the plan stack in the /// most recent stop. Useful for printing the stop reason accurately. /// /// @return /// A pointer to the last completed plan. //------------------------------------------------------------------ lldb::ThreadPlanSP GetCompletedPlan(); //------------------------------------------------------------------ /// Gets the outer-most return value from the completed plans /// /// @return /// A ValueObjectSP, either empty if there is no return value, /// or containing the return value. //------------------------------------------------------------------ lldb::ValueObjectSP GetReturnValueObject(); //------------------------------------------------------------------ /// Gets the outer-most expression variable from the completed plans /// /// @return /// A ExpressionVariableSP, either empty if there is no /// plan completed an expression during the current stop /// or the expression variable that was made for the completed expression. //------------------------------------------------------------------ lldb::ExpressionVariableSP GetExpressionVariable(); //------------------------------------------------------------------ /// Checks whether the given plan is in the completed plans for this /// stop. /// /// @param[in] plan /// Pointer to the plan you're checking. /// /// @return /// Returns true if the input plan is in the completed plan stack, /// false otherwise. //------------------------------------------------------------------ bool IsThreadPlanDone(ThreadPlan *plan); //------------------------------------------------------------------ /// Checks whether the given plan is in the discarded plans for this /// stop. /// /// @param[in] plan /// Pointer to the plan you're checking. /// /// @return /// Returns true if the input plan is in the discarded plan stack, /// false otherwise. //------------------------------------------------------------------ bool WasThreadPlanDiscarded(ThreadPlan *plan); //------------------------------------------------------------------ /// Check if we have completed plan to override breakpoint stop reason /// /// @return /// Returns true if completed plan stack is not empty /// false otherwise. //------------------------------------------------------------------ bool CompletedPlanOverridesBreakpoint(); //------------------------------------------------------------------ /// Queues a generic thread plan. /// /// @param[in] plan_sp /// The plan to queue. /// /// @param[in] abort_other_plans /// \b true if we discard the currently queued plans and replace them with /// this one. /// Otherwise this plan will go on the end of the plan stack. /// /// @return /// A pointer to the last completed plan. //------------------------------------------------------------------ void QueueThreadPlan(lldb::ThreadPlanSP &plan_sp, bool abort_other_plans); //------------------------------------------------------------------ /// Discards the plans queued on the plan stack of the current thread. This /// is /// arbitrated by the "Master" ThreadPlans, using the "OkayToDiscard" call. // But if \a force is true, all thread plans are discarded. //------------------------------------------------------------------ void DiscardThreadPlans(bool force); //------------------------------------------------------------------ /// Discards the plans queued on the plan stack of the current thread up to /// and /// including up_to_plan_sp. // // @param[in] up_to_plan_sp // Discard all plans up to and including this one. //------------------------------------------------------------------ void DiscardThreadPlansUpToPlan(lldb::ThreadPlanSP &up_to_plan_sp); void DiscardThreadPlansUpToPlan(ThreadPlan *up_to_plan_ptr); //------------------------------------------------------------------ /// Discards the plans queued on the plan stack of the current thread up to /// and /// including the plan in that matches \a thread_index counting only /// the non-Private plans. /// /// @param[in] up_to_plan_sp /// Discard all plans up to and including this user plan given by this /// index. /// /// @return /// \b true if there was a thread plan with that user index, \b false /// otherwise. //------------------------------------------------------------------ bool DiscardUserThreadPlansUpToIndex(uint32_t thread_index); //------------------------------------------------------------------ /// Prints the current plan stack. /// /// @param[in] s /// The stream to which to dump the plan stack info. /// //------------------------------------------------------------------ void DumpThreadPlans( Stream *s, lldb::DescriptionLevel desc_level = lldb::eDescriptionLevelVerbose, bool include_internal = true, bool ignore_boring = false) const; virtual bool CheckpointThreadState(ThreadStateCheckpoint &saved_state); virtual bool RestoreRegisterStateFromCheckpoint(ThreadStateCheckpoint &saved_state); virtual bool RestoreThreadStateFromCheckpoint(ThreadStateCheckpoint &saved_state); void EnableTracer(bool value, bool single_step); void SetTracer(lldb::ThreadPlanTracerSP &tracer_sp); //------------------------------------------------------------------ // Get the thread index ID. The index ID that is guaranteed to not // be re-used by a process. They start at 1 and increase with each // new thread. This allows easy command line access by a unique ID // that is easier to type than the actual system thread ID. //------------------------------------------------------------------ uint32_t GetIndexID() const; //------------------------------------------------------------------ // Get the originating thread's index ID. // In the case of an "extended" thread -- a thread which represents // the stack that enqueued/spawned work that is currently executing -- // we need to provide the IndexID of the thread that actually did // this work. We don't want to just masquerade as that thread's IndexID // by using it in our own IndexID because that way leads to madness - // but the driver program which is iterating over extended threads // may ask for the OriginatingThreadID to display that information // to the user. // Normal threads will return the same thing as GetIndexID(); //------------------------------------------------------------------ virtual uint32_t GetExtendedBacktraceOriginatingIndexID() { return GetIndexID(); } //------------------------------------------------------------------ // The API ID is often the same as the Thread::GetID(), but not in // all cases. Thread::GetID() is the user visible thread ID that // clients would want to see. The API thread ID is the thread ID // that is used when sending data to/from the debugging protocol. //------------------------------------------------------------------ virtual lldb::user_id_t GetProtocolID() const { return GetID(); } //------------------------------------------------------------------ // lldb::ExecutionContextScope pure virtual functions //------------------------------------------------------------------ lldb::TargetSP CalculateTarget() override; lldb::ProcessSP CalculateProcess() override; lldb::ThreadSP CalculateThread() override; lldb::StackFrameSP CalculateStackFrame() override; void CalculateExecutionContext(ExecutionContext &exe_ctx) override; lldb::StackFrameSP GetStackFrameSPForStackFramePtr(StackFrame *stack_frame_ptr); size_t GetStatus(Stream &strm, uint32_t start_frame, uint32_t num_frames, - uint32_t num_frames_with_source, - bool stop_format); + uint32_t num_frames_with_source, bool stop_format, + bool only_stacks = false); size_t GetStackFrameStatus(Stream &strm, uint32_t first_frame, uint32_t num_frames, bool show_frame_info, uint32_t num_frames_with_source); // We need a way to verify that even though we have a thread in a shared // pointer that the object itself is still valid. Currently this won't be // the case if DestroyThread() was called. DestroyThread is called when // a thread has been removed from the Process' thread list. bool IsValid() const { return !m_destroy_called; } // Sets and returns a valid stop info based on the process stop ID and the // current thread plan. If the thread stop ID does not match the process' // stop ID, the private stop reason is not set and an invalid StopInfoSP may // be returned. // // NOTE: This function must be called before the current thread plan is // moved to the completed plan stack (in Thread::ShouldStop()). // // NOTE: If subclasses override this function, ensure they do not overwrite // the m_actual_stop_info if it is valid. The stop info may be a // "checkpointed and restored" stop info, so if it is still around it is // right even if you have not calculated this yourself, or if it disagrees // with what you might have calculated. virtual lldb::StopInfoSP GetPrivateStopInfo(); //---------------------------------------------------------------------- // Ask the thread subclass to set its stop info. // // Thread subclasses should call Thread::SetStopInfo(...) with the // reason the thread stopped. // // @return // True if Thread::SetStopInfo(...) was called, false otherwise. //---------------------------------------------------------------------- virtual bool CalculateStopInfo() = 0; //---------------------------------------------------------------------- // Gets the temporary resume state for a thread. // // This value gets set in each thread by complex debugger logic in // Thread::ShouldResume() and an appropriate thread resume state will get // set in each thread every time the process is resumed prior to calling // Process::DoResume(). The lldb_private::Process subclass should adhere // to the thread resume state request which will be one of: // // eStateRunning - thread will resume when process is resumed // eStateStepping - thread should step 1 instruction and stop when process // is resumed // eStateSuspended - thread should not execute any instructions when // process is resumed //---------------------------------------------------------------------- lldb::StateType GetTemporaryResumeState() const { return m_temporary_resume_state; } void SetStopInfo(const lldb::StopInfoSP &stop_info_sp); void ResetStopInfo(); void SetShouldReportStop(Vote vote); //---------------------------------------------------------------------- /// Sets the extended backtrace token for this thread /// /// Some Thread subclasses may maintain a token to help with providing /// an extended backtrace. The SystemRuntime plugin will set/request this. /// /// @param [in] token //---------------------------------------------------------------------- virtual void SetExtendedBacktraceToken(uint64_t token) {} //---------------------------------------------------------------------- /// Gets the extended backtrace token for this thread /// /// Some Thread subclasses may maintain a token to help with providing /// an extended backtrace. The SystemRuntime plugin will set/request this. /// /// @return /// The token needed by the SystemRuntime to create an extended backtrace. /// LLDB_INVALID_ADDRESS is returned if no token is available. //---------------------------------------------------------------------- virtual uint64_t GetExtendedBacktraceToken() { return LLDB_INVALID_ADDRESS; } protected: friend class ThreadPlan; friend class ThreadList; friend class ThreadEventData; friend class StackFrameList; friend class StackFrame; friend class OperatingSystem; // This is necessary to make sure thread assets get destroyed while the thread // is still in good shape // to call virtual thread methods. This must be called by classes that derive // from Thread in their destructor. virtual void DestroyThread(); void PushPlan(lldb::ThreadPlanSP &plan_sp); void PopPlan(); void DiscardPlan(); ThreadPlan *GetPreviousPlan(ThreadPlan *plan); typedef std::vector plan_stack; virtual lldb_private::Unwind *GetUnwinder(); // Check to see whether the thread is still at the last breakpoint hit that // stopped it. virtual bool IsStillAtLastBreakpointHit(); // Some threads are threads that are made up by OperatingSystem plugins that // are threads that exist and are context switched out into memory. The // OperatingSystem plug-in need a ways to know if a thread is "real" or made // up. virtual bool IsOperatingSystemPluginThread() const { return false; } // Subclasses that have a way to get an extended info dictionary for this // thread should // fill virtual lldb_private::StructuredData::ObjectSP FetchThreadExtendedInfo() { return StructuredData::ObjectSP(); } lldb::StackFrameListSP GetStackFrameList(); void SetTemporaryResumeState(lldb::StateType new_state) { m_temporary_resume_state = new_state; } void FunctionOptimizationWarning(lldb_private::StackFrame *frame); //------------------------------------------------------------------ // Classes that inherit from Process can see and modify these //------------------------------------------------------------------ lldb::ProcessWP m_process_wp; ///< The process that owns this thread. lldb::StopInfoSP m_stop_info_sp; ///< The private stop reason for this thread uint32_t m_stop_info_stop_id; // This is the stop id for which the StopInfo is // valid. Can use this so you know that // the thread's m_stop_info_sp is current and you don't have to fetch it again uint32_t m_stop_info_override_stop_id; // The stop ID containing the last time // the stop info was checked against // the stop info override const uint32_t m_index_id; ///< A unique 1 based index assigned to each thread ///for easy UI/command line access. lldb::RegisterContextSP m_reg_context_sp; ///< The register context for this ///thread's current register state. lldb::StateType m_state; ///< The state of our process. mutable std::recursive_mutex m_state_mutex; ///< Multithreaded protection for m_state. plan_stack m_plan_stack; ///< The stack of plans this thread is executing. plan_stack m_completed_plan_stack; ///< Plans that have been completed by this ///stop. They get deleted when the thread ///resumes. plan_stack m_discarded_plan_stack; ///< Plans that have been discarded by this ///stop. They get deleted when the thread ///resumes. mutable std::recursive_mutex m_frame_mutex; ///< Multithreaded protection for m_state. lldb::StackFrameListSP m_curr_frames_sp; ///< The stack frames that get lazily ///populated after a thread stops. lldb::StackFrameListSP m_prev_frames_sp; ///< The previous stack frames from ///the last time this thread stopped. int m_resume_signal; ///< The signal that should be used when continuing this ///thread. lldb::StateType m_resume_state; ///< This state is used to force a thread to ///be suspended from outside the ThreadPlan ///logic. lldb::StateType m_temporary_resume_state; ///< This state records what the ///thread was told to do by the ///thread plan logic for the current ///resume. /// It gets set in Thread::ShouldResume. std::unique_ptr m_unwinder_ap; bool m_destroy_called; // This is used internally to make sure derived Thread // classes call DestroyThread. LazyBool m_override_should_notify; private: bool m_extended_info_fetched; // Have we tried to retrieve the m_extended_info // for this thread? StructuredData::ObjectSP m_extended_info; // The extended info for this thread private: bool PlanIsBasePlan(ThreadPlan *plan_ptr); void BroadcastSelectedFrameChange(StackID &new_frame_id); DISALLOW_COPY_AND_ASSIGN(Thread); }; } // namespace lldb_private #endif // liblldb_Thread_h_ Index: vendor/lldb/dist/include/lldb/Utility/Status.h =================================================================== --- vendor/lldb/dist/include/lldb/Utility/Status.h (revision 320022) +++ vendor/lldb/dist/include/lldb/Utility/Status.h (revision 320023) @@ -1,278 +1,279 @@ //===-- Status.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_UTILITY_STATUS_H #define LLDB_UTILITY_STATUS_H #include "lldb/lldb-defines.h" #include "lldb/lldb-enumerations.h" // for ErrorType, ErrorType... #include "llvm/ADT/StringRef.h" // for StringRef #include "llvm/Support/Error.h" #include "llvm/Support/FormatVariadic.h" #include #include // for uint32_t #include #include // for error_code #include // for forward namespace llvm { class raw_ostream; } namespace lldb_private { //---------------------------------------------------------------------- /// @class Status Status.h "lldb/Utility/Status.h" /// @brief An error handling class. /// /// This class is designed to be able to hold any error code that can be /// encountered on a given platform. The errors are stored as a value /// of type Status::ValueType. This value should be large enough to hold /// any and all errors that the class supports. Each error has an /// associated type that is of type lldb::ErrorType. New types /// can be added to support new error types, and architecture specific /// types can be enabled. In the future we may wish to switch to a /// registration mechanism where new error types can be registered at /// runtime instead of a hard coded scheme. /// /// All errors in this class also know how to generate a string /// representation of themselves for printing results and error codes. /// The string value will be fetched on demand and its string value will /// be cached until the error is cleared of the value of the error /// changes. //---------------------------------------------------------------------- class Status { public: //------------------------------------------------------------------ /// Every error value that this object can contain needs to be able /// to fit into ValueType. //------------------------------------------------------------------ typedef uint32_t ValueType; //------------------------------------------------------------------ /// Default constructor. /// /// Initialize the error object with a generic success value. /// /// @param[in] err /// An error code. /// /// @param[in] type /// The type for \a err. //------------------------------------------------------------------ Status(); explicit Status(ValueType err, lldb::ErrorType type = lldb::eErrorTypeGeneric); /* implicit */ Status(std::error_code EC); explicit Status(const char *format, ...) __attribute__((format(printf, 2, 3))); Status(const Status &rhs); //------------------------------------------------------------------ /// Assignment operator. /// /// @param[in] err /// An error code. /// /// @return /// A const reference to this object. //------------------------------------------------------------------ const Status &operator=(const Status &rhs); //------------------------------------------------------------------ /// Assignment operator from a kern_return_t. /// /// Sets the type to \c MachKernel and the error code to \a err. /// /// @param[in] err /// A mach error code. /// /// @return /// A const reference to this object. //------------------------------------------------------------------ const Status &operator=(uint32_t err); ~Status(); // llvm::Error support - explicit Status(llvm::Error error); + explicit Status(llvm::Error error) { *this = std::move(error); } + const Status &operator=(llvm::Error error); llvm::Error ToError() const; //------------------------------------------------------------------ /// Get the error string associated with the current error. // /// Gets the error value as a NULL terminated C string. The error /// string will be fetched and cached on demand. The error string /// will be retrieved from a callback that is appropriate for the /// type of the error and will be cached until the error value is /// changed or cleared. /// /// @return /// The error as a NULL terminated C string value if the error /// is valid and is able to be converted to a string value, /// NULL otherwise. //------------------------------------------------------------------ const char *AsCString(const char *default_error_str = "unknown error") const; //------------------------------------------------------------------ /// Clear the object state. /// /// Reverts the state of this object to contain a generic success /// value and frees any cached error string value. //------------------------------------------------------------------ void Clear(); //------------------------------------------------------------------ /// Test for error condition. /// /// @return /// \b true if this object contains an error, \b false /// otherwise. //------------------------------------------------------------------ bool Fail() const; //------------------------------------------------------------------ /// Access the error value. /// /// @return /// The error value. //------------------------------------------------------------------ ValueType GetError() const; //------------------------------------------------------------------ /// Access the error type. /// /// @return /// The error type enumeration value. //------------------------------------------------------------------ lldb::ErrorType GetType() const; //------------------------------------------------------------------ /// Set accessor from a kern_return_t. /// /// Set accesssor for the error value to \a err and the error type /// to \c MachKernel. /// /// @param[in] err /// A mach error code. //------------------------------------------------------------------ void SetMachError(uint32_t err); void SetExpressionError(lldb::ExpressionResults, const char *mssg); int SetExpressionErrorWithFormat(lldb::ExpressionResults, const char *format, ...) __attribute__((format(printf, 3, 4))); //------------------------------------------------------------------ /// Set accesssor with an error value and type. /// /// Set accesssor for the error value to \a err and the error type /// to \a type. /// /// @param[in] err /// A mach error code. /// /// @param[in] type /// The type for \a err. //------------------------------------------------------------------ void SetError(ValueType err, lldb::ErrorType type); //------------------------------------------------------------------ /// Set the current error to errno. /// /// Update the error value to be \c errno and update the type to /// be \c Status::POSIX. //------------------------------------------------------------------ void SetErrorToErrno(); //------------------------------------------------------------------ /// Set the current error to a generic error. /// /// Update the error value to be \c LLDB_GENERIC_ERROR and update the /// type to be \c Status::Generic. //------------------------------------------------------------------ void SetErrorToGenericError(); //------------------------------------------------------------------ /// Set the current error string to \a err_str. /// /// Set accessor for the error string value for a generic errors, /// or to supply additional details above and beyond the standard /// error strings that the standard type callbacks typically /// provide. This allows custom strings to be supplied as an /// error explanation. The error string value will remain until the /// error value is cleared or a new error value/type is assigned. /// /// @param err_str /// The new custom error string to copy and cache. //------------------------------------------------------------------ void SetErrorString(llvm::StringRef err_str); //------------------------------------------------------------------ /// Set the current error string to a formatted error string. /// /// @param format /// A printf style format string //------------------------------------------------------------------ int SetErrorStringWithFormat(const char *format, ...) __attribute__((format(printf, 2, 3))); int SetErrorStringWithVarArg(const char *format, va_list args); template void SetErrorStringWithFormatv(const char *format, Args &&... args) { SetErrorString(llvm::formatv(format, std::forward(args)...).str()); } //------------------------------------------------------------------ /// Test for success condition. /// /// Returns true if the error code in this object is considered a /// successful return value. /// /// @return /// \b true if this object contains an value that describes /// success (non-erro), \b false otherwise. //------------------------------------------------------------------ bool Success() const; //------------------------------------------------------------------ /// Test for a failure due to a generic interrupt. /// /// Returns true if the error code in this object was caused by an interrupt. /// At present only supports Posix EINTR. /// /// @return /// \b true if this object contains an value that describes /// failure due to interrupt, \b false otherwise. //------------------------------------------------------------------ bool WasInterrupted() const; protected: //------------------------------------------------------------------ /// Member variables //------------------------------------------------------------------ ValueType m_code; ///< Status code as an integer value. lldb::ErrorType m_type; ///< The type of the above error code. mutable std::string m_string; ///< A string representation of the error code. }; } // namespace lldb_private namespace llvm { template <> struct format_provider { static void format(const lldb_private::Status &error, llvm::raw_ostream &OS, llvm::StringRef Options); }; } #endif // #ifndef LLDB_UTILITY_STATUS_H Index: vendor/lldb/dist/packages/Python/lldbsuite/test/expression_command/call-restarts/TestCallThatRestarts.py =================================================================== --- vendor/lldb/dist/packages/Python/lldbsuite/test/expression_command/call-restarts/TestCallThatRestarts.py (revision 320022) +++ vendor/lldb/dist/packages/Python/lldbsuite/test/expression_command/call-restarts/TestCallThatRestarts.py (revision 320023) @@ -1,184 +1,186 @@ """ Test calling a function that hits a signal set to auto-restart, make sure the call completes. """ from __future__ import print_function import lldb from lldbsuite.test.decorators import * from lldbsuite.test.lldbtest import * from lldbsuite.test import lldbutil class ExprCommandThatRestartsTestCase(TestBase): mydir = TestBase.compute_mydir(__file__) + NO_DEBUG_INFO_TESTCASE = True def setUp(self): # Call super's setUp(). TestBase.setUp(self) self.main_source = "lotta-signals.c" self.main_source_spec = lldb.SBFileSpec(self.main_source) @skipIfFreeBSD # llvm.org/pr19246: intermittent failure @skipIfDarwin # llvm.org/pr19246: intermittent failure @skipIfWindows # Test relies on signals, unsupported on Windows + @expectedFlakeyAndroid(bugnumber="llvm.org/pr19246") def test(self): """Test calling function that hits a signal and restarts.""" self.build() self.call_function() def check_after_call(self, num_sigchld): after_call = self.sigchld_no.GetValueAsSigned(-1) self.assertTrue( after_call - self.start_sigchld_no == num_sigchld, "Really got %d SIGCHLD signals through the call." % (num_sigchld)) self.start_sigchld_no = after_call # Check that we are back where we were before: frame = self.thread.GetFrameAtIndex(0) self.assertTrue( self.orig_frame_pc == frame.GetPC(), "Restored the zeroth frame correctly") def call_function(self): exe_name = "a.out" exe = os.path.join(os.getcwd(), exe_name) target = self.dbg.CreateTarget(exe) self.assertTrue(target, VALID_TARGET) empty = lldb.SBFileSpec() breakpoint = target.BreakpointCreateBySourceRegex( 'Stop here in main.', self.main_source_spec) self.assertTrue(breakpoint.GetNumLocations() > 0, VALID_BREAKPOINT) # Launch the process, and do not stop at the entry point. process = target.LaunchSimple( None, None, self.get_process_working_directory()) self.assertTrue(process, PROCESS_IS_VALID) # Frame #0 should be at our breakpoint. threads = lldbutil.get_threads_stopped_at_breakpoint( process, breakpoint) self.assertTrue(len(threads) == 1) self.thread = threads[0] # Make sure the SIGCHLD behavior is pass/no-stop/no-notify: return_obj = lldb.SBCommandReturnObject() self.dbg.GetCommandInterpreter().HandleCommand( "process handle SIGCHLD -s 0 -p 1 -n 0", return_obj) self.assertTrue(return_obj.Succeeded(), "Set SIGCHLD to pass, no-stop") # The sigchld_no variable should be 0 at this point. self.sigchld_no = target.FindFirstGlobalVariable("sigchld_no") self.assertTrue( self.sigchld_no.IsValid(), "Got a value for sigchld_no") self.start_sigchld_no = self.sigchld_no.GetValueAsSigned(-1) self.assertTrue( self.start_sigchld_no != -1, "Got an actual value for sigchld_no") options = lldb.SBExpressionOptions() # processing 30 signals takes a while, increase the expression timeout # a bit options.SetTimeoutInMicroSeconds(3000000) # 3s options.SetUnwindOnError(True) frame = self.thread.GetFrameAtIndex(0) # Store away the PC to check that the functions unwind to the right # place after calls self.orig_frame_pc = frame.GetPC() num_sigchld = 30 value = frame.EvaluateExpression( "call_me (%d)" % (num_sigchld), options) self.assertTrue(value.IsValid()) self.assertTrue(value.GetError().Success()) self.assertTrue(value.GetValueAsSigned(-1) == num_sigchld) self.check_after_call(num_sigchld) # Okay, now try with a breakpoint in the called code in the case where # we are ignoring breakpoint hits. handler_bkpt = target.BreakpointCreateBySourceRegex( "Got sigchld %d.", self.main_source_spec) self.assertTrue(handler_bkpt.GetNumLocations() > 0) options.SetIgnoreBreakpoints(True) options.SetUnwindOnError(True) value = frame.EvaluateExpression( "call_me (%d)" % (num_sigchld), options) self.assertTrue(value.IsValid() and value.GetError().Success()) self.assertTrue(value.GetValueAsSigned(-1) == num_sigchld) self.check_after_call(num_sigchld) # Now set the signal to print but not stop and make sure that calling # still works: self.dbg.GetCommandInterpreter().HandleCommand( "process handle SIGCHLD -s 0 -p 1 -n 1", return_obj) self.assertTrue( return_obj.Succeeded(), "Set SIGCHLD to pass, no-stop, notify") value = frame.EvaluateExpression( "call_me (%d)" % (num_sigchld), options) self.assertTrue(value.IsValid() and value.GetError().Success()) self.assertTrue(value.GetValueAsSigned(-1) == num_sigchld) self.check_after_call(num_sigchld) # Now set this unwind on error to false, and make sure that we still # complete the call: options.SetUnwindOnError(False) value = frame.EvaluateExpression( "call_me (%d)" % (num_sigchld), options) self.assertTrue(value.IsValid() and value.GetError().Success()) self.assertTrue(value.GetValueAsSigned(-1) == num_sigchld) self.check_after_call(num_sigchld) # Okay, now set UnwindOnError to true, and then make the signal behavior to stop # and see that now we do stop at the signal point: self.dbg.GetCommandInterpreter().HandleCommand( "process handle SIGCHLD -s 1 -p 1 -n 1", return_obj) self.assertTrue( return_obj.Succeeded(), "Set SIGCHLD to pass, stop, notify") value = frame.EvaluateExpression( "call_me (%d)" % (num_sigchld), options) self.assertTrue( value.IsValid() and value.GetError().Success() == False) # Set signal handling back to no-stop, and continue and we should end # up back in out starting frame: self.dbg.GetCommandInterpreter().HandleCommand( "process handle SIGCHLD -s 0 -p 1 -n 1", return_obj) self.assertTrue( return_obj.Succeeded(), "Set SIGCHLD to pass, no-stop, notify") error = process.Continue() self.assertTrue( error.Success(), "Continuing after stopping for signal succeeds.") frame = self.thread.GetFrameAtIndex(0) self.assertTrue( frame.GetPC() == self.orig_frame_pc, "Continuing returned to the place we started.") Index: vendor/lldb/dist/packages/Python/lldbsuite/test/functionalities/thread/num_threads/TestNumThreads.py =================================================================== --- vendor/lldb/dist/packages/Python/lldbsuite/test/functionalities/thread/num_threads/TestNumThreads.py (revision 320022) +++ vendor/lldb/dist/packages/Python/lldbsuite/test/functionalities/thread/num_threads/TestNumThreads.py (revision 320023) @@ -1,61 +1,121 @@ """ Test number of threads. """ from __future__ import print_function import os import time import lldb from lldbsuite.test.lldbtest import * import lldbsuite.test.lldbutil as lldbutil class NumberOfThreadsTestCase(TestBase): mydir = TestBase.compute_mydir(__file__) def setUp(self): # Call super's setUp(). TestBase.setUp(self) - # Find the line number to break inside main(). - self.line = line_number('main.cpp', '// Set break point at this line.') + # Find the line numbers for our break points. + self.thread3_notify_all_line = line_number('main.cpp', '// Set thread3 break point on notify_all at this line.') + self.thread3_before_lock_line = line_number('main.cpp', '// Set thread3 break point on lock at this line.') - def test(self): + def test_number_of_threads(self): """Test number of threads.""" self.build() exe = os.path.join(os.getcwd(), "a.out") self.runCmd("file " + exe, CURRENT_EXECUTABLE_SET) # This should create a breakpoint with 1 location. lldbutil.run_break_set_by_file_and_line( - self, "main.cpp", self.line, num_expected_locations=1) + self, "main.cpp", self.thread3_notify_all_line, num_expected_locations=1) - # The breakpoint list should show 3 locations. + # The breakpoint list should show 1 location. self.expect( "breakpoint list -f", "Breakpoint location shown correctly", substrs=[ "1: file = 'main.cpp', line = %d, exact_match = 0, locations = 1" % - self.line]) + self.thread3_notify_all_line]) # Run the program. self.runCmd("run", RUN_SUCCEEDED) # Stopped once. self.expect("thread list", STOPPED_DUE_TO_BREAKPOINT, substrs=["stop reason = breakpoint 1."]) # Get the target process target = self.dbg.GetSelectedTarget() process = target.GetProcess() # Get the number of threads num_threads = process.GetNumThreads() # Using std::thread may involve extra threads, so we assert that there are # at least 4 rather than exactly 4. self.assertTrue( - num_threads >= 4, + num_threads >= 13, 'Number of expected threads and actual threads do not match.') + + def test_unique_stacks(self): + """Test backtrace unique with multiple threads executing the same stack.""" + self.build() + exe = os.path.join(os.getcwd(), "a.out") + self.runCmd("file " + exe, CURRENT_EXECUTABLE_SET) + + # Set a break point on the thread3 notify all (should get hit on threads 4-13). + lldbutil.run_break_set_by_file_and_line( + self, "main.cpp", self.thread3_before_lock_line, num_expected_locations=1) + + # Run the program. + self.runCmd("run", RUN_SUCCEEDED) + + # Stopped once. + self.expect("thread list", STOPPED_DUE_TO_BREAKPOINT, + substrs=["stop reason = breakpoint 1."]) + + process = self.process() + + # Get the number of threads + num_threads = process.GetNumThreads() + + # Using std::thread may involve extra threads, so we assert that there are + # at least 10 thread3's rather than exactly 10. + self.assertTrue( + num_threads >= 10, + 'Number of expected threads and actual threads do not match.') + + # Attempt to walk each of the thread's executing the thread3 function to + # the same breakpoint. + def is_thread3(thread): + for frame in thread: + if "thread3" in frame.GetFunctionName(): return True + return False + + expect_threads = "" + for i in range(num_threads): + thread = process.GetThreadAtIndex(i) + self.assertTrue(thread.IsValid()) + if not is_thread3(thread): + continue + + # If we aren't stopped out the thread breakpoint try to resume. + if thread.GetStopReason() != lldb.eStopReasonBreakpoint: + self.runCmd("thread continue %d"%(i+1)) + self.assertEqual(thread.GetStopReason(), lldb.eStopReasonBreakpoint) + + expect_threads += " #%d"%(i+1) + + # Construct our expected back trace string + expect_string = "10 thread(s)%s" % (expect_threads) + + # Now that we are stopped, we should have 10 threads waiting in the + # thread3 function. All of these threads should show as one stack. + self.expect("thread backtrace unique", + "Backtrace with unique stack shown correctly", + substrs=[expect_string, + "main.cpp:%d"%self.thread3_before_lock_line]) Index: vendor/lldb/dist/packages/Python/lldbsuite/test/functionalities/thread/num_threads/main.cpp =================================================================== --- vendor/lldb/dist/packages/Python/lldbsuite/test/functionalities/thread/num_threads/main.cpp (revision 320022) +++ vendor/lldb/dist/packages/Python/lldbsuite/test/functionalities/thread/num_threads/main.cpp (revision 320023) @@ -1,50 +1,62 @@ +#include "pseudo_barrier.h" #include #include #include +#include std::mutex mutex; std::condition_variable cond; +pseudo_barrier_t thread3_barrier; void * thread3(void *input) { - std::unique_lock lock(mutex); - cond.notify_all(); // Set break point at this line. + pseudo_barrier_wait(thread3_barrier); + std::unique_lock lock(mutex); // Set thread3 break point on lock at this line. + cond.notify_all(); // Set thread3 break point on notify_all at this line. return NULL; } void * thread2(void *input) { std::unique_lock lock(mutex); - cond.notify_all(); + cond.notify_all(); // release main thread cond.wait(lock); return NULL; } void * thread1(void *input) { std::thread thread_2(thread2, nullptr); thread_2.join(); return NULL; } int main() { std::unique_lock lock(mutex); std::thread thread_1(thread1, nullptr); - cond.wait(lock); + cond.wait(lock); // wait for thread2 - std::thread thread_3(thread3, nullptr); - cond.wait(lock); + pseudo_barrier_init(thread3_barrier, 10); + std::vector thread_3s; + for (int i = 0; i < 10; i++) { + thread_3s.push_back(std::thread(thread3, nullptr)); + } + + cond.wait(lock); // wait for thread_3s + lock.unlock(); thread_1.join(); - thread_3.join(); + for (auto &t : thread_3s){ + t.join(); + } return 0; } Index: vendor/lldb/dist/packages/Python/lldbsuite/test/functionalities/unwind/noreturn/TestNoreturnUnwind.py =================================================================== --- vendor/lldb/dist/packages/Python/lldbsuite/test/functionalities/unwind/noreturn/TestNoreturnUnwind.py (revision 320022) +++ vendor/lldb/dist/packages/Python/lldbsuite/test/functionalities/unwind/noreturn/TestNoreturnUnwind.py (revision 320023) @@ -1,85 +1,86 @@ """ Test that we can backtrace correctly with 'noreturn' functions on the stack """ from __future__ import print_function import os import time import lldb from lldbsuite.test.decorators import * from lldbsuite.test.lldbtest import * from lldbsuite.test import lldbutil class NoreturnUnwind(TestBase): mydir = TestBase.compute_mydir(__file__) @skipIfWindows # clang-cl does not support gcc style attributes. # clang does not preserve LR in noreturn functions, making unwinding impossible @skipIf(compiler="clang", archs=['arm'], oslist=['linux']) + @expectedFailureAll(bugnumber="llvm.org/pr33452", triple='^mips') def test(self): """Test that we can backtrace correctly with 'noreturn' functions on the stack""" self.build() self.setTearDownCleanup() exe = os.path.join(os.getcwd(), "a.out") target = self.dbg.CreateTarget(exe) self.assertTrue(target, VALID_TARGET) process = target.LaunchSimple( None, None, self.get_process_working_directory()) if not process: self.fail("SBTarget.Launch() failed") if process.GetState() != lldb.eStateStopped: self.fail("Process should be in the 'stopped' state, " "instead the actual state is: '%s'" % lldbutil.state_type_to_str(process.GetState())) thread = process.GetThreadAtIndex(0) abort_frame_number = 0 for f in thread.frames: # Some C libraries mangle the abort symbol into __GI_abort. if f.GetFunctionName() in ["abort", "__GI_abort"]: break abort_frame_number = abort_frame_number + 1 if self.TraceOn(): print("Backtrace once we're stopped:") for f in thread.frames: print(" %d %s" % (f.GetFrameID(), f.GetFunctionName())) # I'm going to assume that abort() ends up calling/invoking another # function before halting the process. In which case if abort_frame_number # equals 0, we didn't find abort() in the backtrace. if abort_frame_number == len(thread.frames): self.fail("Unable to find abort() in backtrace.") func_c_frame_number = abort_frame_number + 1 if thread.GetFrameAtIndex( func_c_frame_number).GetFunctionName() != "func_c": self.fail("Did not find func_c() above abort().") # This depends on whether we see the func_b inlined function in the backtrace # or not. I'm not interested in testing that aspect of the backtrace here # right now. if thread.GetFrameAtIndex( func_c_frame_number + 1).GetFunctionName() == "func_b": func_a_frame_number = func_c_frame_number + 2 else: func_a_frame_number = func_c_frame_number + 1 if thread.GetFrameAtIndex( func_a_frame_number).GetFunctionName() != "func_a": self.fail("Did not find func_a() above func_c().") main_frame_number = func_a_frame_number + 1 if thread.GetFrameAtIndex( main_frame_number).GetFunctionName() != "main": self.fail("Did not find main() above func_a().") Index: vendor/lldb/dist/packages/Python/lldbsuite/test/lang/c/register_variables/test.c =================================================================== --- vendor/lldb/dist/packages/Python/lldbsuite/test/lang/c/register_variables/test.c (revision 320022) +++ vendor/lldb/dist/packages/Python/lldbsuite/test/lang/c/register_variables/test.c (revision 320023) @@ -1,44 +1,44 @@ #include -#if defined(__arm__) || defined(__aarch64__) +#if defined(__arm__) || defined(__aarch64__) || defined (__mips__) // Clang does not accept regparm attribute on these platforms. // Fortunately, the default calling convention passes arguments in registers // anyway. #define REGPARM(N) #else #define REGPARM(N) __attribute__((regparm(N))) #endif struct bar { int m1; int m2; }; void f1(int a, struct bar *b) __attribute__((noinline)) REGPARM(2); void f1(int a, struct bar *b) { b->m2 = b->m1 + a; // set breakpoint here } void f2(struct bar *b) __attribute__((noinline)) REGPARM(1); void f2(struct bar *b) { int c = b->m2; printf("%d\n", c); // set breakpoint here } float f3() __attribute__((noinline)); float f3() { return 3.14f; } int main() { struct bar myBar = { 3, 4 }; f1(2, &myBar); f2(&myBar); float f = f3(); printf("%f\n", f); // set breakpoint here return 0; } Index: vendor/lldb/dist/packages/Python/lldbsuite/test/lang/objc/objc-new-syntax/TestObjCNewSyntax.py =================================================================== --- vendor/lldb/dist/packages/Python/lldbsuite/test/lang/objc/objc-new-syntax/TestObjCNewSyntax.py (revision 320022) +++ vendor/lldb/dist/packages/Python/lldbsuite/test/lang/objc/objc-new-syntax/TestObjCNewSyntax.py (revision 320023) @@ -1,264 +1,268 @@ """Test that the Objective-C syntax for dictionary/array literals and indexing works""" from __future__ import print_function import unittest2 import os import time import platform from distutils.version import StrictVersion import lldb from lldbsuite.test.decorators import * from lldbsuite.test.lldbtest import * from lldbsuite.test import lldbutil class ObjCNewSyntaxTestCase(TestBase): mydir = TestBase.compute_mydir(__file__) def setUp(self): # Call super's setUp(). TestBase.setUp(self) # Find the line number to break inside main(). self.line = line_number('main.m', '// Set breakpoint 0 here.') def runToBreakpoint(self): self.build() exe = os.path.join(os.getcwd(), "a.out") self.runCmd("file " + exe, CURRENT_EXECUTABLE_SET) # Break inside the foo function which takes a bar_ptr argument. lldbutil.run_break_set_by_file_and_line( self, "main.m", self.line, num_expected_locations=1, loc_exact=True) self.runCmd("run", RUN_SUCCEEDED) # The stop reason of the thread should be breakpoint. self.expect("thread list", STOPPED_DUE_TO_BREAKPOINT, substrs=['stopped', 'stop reason = breakpoint']) # The breakpoint should have a hit count of 1. self.expect("breakpoint list -f", BREAKPOINT_HIT_ONCE, substrs=[' resolved, hit count = 1']) @skipUnlessDarwin @expectedFailureAll( oslist=['macosx'], compiler='clang', compiler_version=[ '<', '7.0.0']) @skipIf(macos_version=["<", "10.12"]) @expectedFailureAll(archs=["i[3-6]86"]) def test_read_array(self): self.runToBreakpoint() self.expect( "expr --object-description -- immutable_array[0]", VARIABLES_DISPLAYED_CORRECTLY, substrs=["foo"]) self.expect( "expr --object-description -- mutable_array[0]", VARIABLES_DISPLAYED_CORRECTLY, substrs=["foo"]) @skipUnlessDarwin @expectedFailureAll( oslist=['macosx'], compiler='clang', compiler_version=[ '<', '7.0.0']) @skipIf(macos_version=["<", "10.12"]) @expectedFailureAll(archs=["i[3-6]86"]) def test_update_array(self): self.runToBreakpoint() self.expect( "expr --object-description -- mutable_array[0] = @\"bar\"", VARIABLES_DISPLAYED_CORRECTLY, substrs=["bar"]) self.expect( "expr --object-description -- mutable_array[0]", VARIABLES_DISPLAYED_CORRECTLY, substrs=["bar"]) @skipUnlessDarwin @expectedFailureAll( oslist=['macosx'], compiler='clang', compiler_version=[ '<', '7.0.0']) @skipIf(macos_version=["<", "10.12"]) @expectedFailureAll(archs=["i[3-6]86"]) def test_read_dictionary(self): self.runToBreakpoint() self.expect( "expr --object-description -- immutable_dictionary[@\"key\"]", VARIABLES_DISPLAYED_CORRECTLY, substrs=["value"]) self.expect( "expr --object-description -- mutable_dictionary[@\"key\"]", VARIABLES_DISPLAYED_CORRECTLY, substrs=["value"]) @skipUnlessDarwin @expectedFailureAll( oslist=['macosx'], compiler='clang', compiler_version=[ '<', '7.0.0']) @skipIf(macos_version=["<", "10.12"]) @expectedFailureAll(archs=["i[3-6]86"]) + @expectedFailureAll( + bugnumber="rdar://32777981") def test_update_dictionary(self): self.runToBreakpoint() self.expect( "expr --object-description -- mutable_dictionary[@\"key\"] = @\"object\"", VARIABLES_DISPLAYED_CORRECTLY, substrs=["object"]) self.expect( "expr --object-description -- mutable_dictionary[@\"key\"]", VARIABLES_DISPLAYED_CORRECTLY, substrs=["object"]) @skipUnlessDarwin @expectedFailureAll( oslist=['macosx'], compiler='clang', compiler_version=[ '<', '7.0.0']) @skipIf(macos_version=["<", "10.12"]) @expectedFailureAll(archs=["i[3-6]86"]) def test_array_literal(self): self.runToBreakpoint() self.expect( "expr --object-description -- @[ @\"foo\", @\"bar\" ]", VARIABLES_DISPLAYED_CORRECTLY, substrs=[ "NSArray", "foo", "bar"]) @skipUnlessDarwin @expectedFailureAll( oslist=['macosx'], compiler='clang', compiler_version=[ '<', '7.0.0']) @skipIf(macos_version=["<", "10.12"]) @expectedFailureAll(archs=["i[3-6]86"]) + @expectedFailureAll( + bugnumber="rdar://32777981") def test_dictionary_literal(self): self.runToBreakpoint() self.expect( "expr --object-description -- @{ @\"key\" : @\"object\" }", VARIABLES_DISPLAYED_CORRECTLY, substrs=[ "key", "object"]) @skipUnlessDarwin @expectedFailureAll( oslist=['macosx'], compiler='clang', compiler_version=[ '<', '7.0.0']) @skipIf(macos_version=["<", "10.12"]) @expectedFailureAll(archs=["i[3-6]86"]) def test_char_literal(self): self.runToBreakpoint() self.expect("expr --object-description -- @'a'", VARIABLES_DISPLAYED_CORRECTLY, substrs=[str(ord('a'))]) @skipUnlessDarwin @expectedFailureAll( oslist=['macosx'], compiler='clang', compiler_version=[ '<', '7.0.0']) @skipIf(macos_version=["<", "10.12"]) @expectedFailureAll(archs=["i[3-6]86"]) def test_integer_literals(self): self.runToBreakpoint() self.expect( "expr --object-description -- @1", VARIABLES_DISPLAYED_CORRECTLY, substrs=["1"]) self.expect( "expr --object-description -- @1l", VARIABLES_DISPLAYED_CORRECTLY, substrs=["1"]) self.expect( "expr --object-description -- @1ul", VARIABLES_DISPLAYED_CORRECTLY, substrs=["1"]) self.expect( "expr --object-description -- @1ll", VARIABLES_DISPLAYED_CORRECTLY, substrs=["1"]) self.expect( "expr --object-description -- @1ull", VARIABLES_DISPLAYED_CORRECTLY, substrs=["1"]) @skipUnlessDarwin @expectedFailureAll( oslist=['macosx'], compiler='clang', compiler_version=[ '<', '7.0.0']) @skipIf(macos_version=["<", "10.12"]) @expectedFailureAll(archs=["i[3-6]86"]) def test_float_literal(self): self.runToBreakpoint() self.expect("expr -- @123.45", VARIABLES_DISPLAYED_CORRECTLY, substrs=["NSNumber", "123.45"]) @skipUnlessDarwin @expectedFailureAll( oslist=['macosx'], compiler='clang', compiler_version=[ '<', '7.0.0']) @skipIf(macos_version=["<", "10.12"]) @expectedFailureAll(archs=["i[3-6]86"]) def test_expressions_in_literals(self): self.runToBreakpoint() self.expect( "expr --object-description -- @( 1 + 3 )", VARIABLES_DISPLAYED_CORRECTLY, substrs=["4"]) self.expect( "expr -- @((char*)\"Hello world\" + 6)", VARIABLES_DISPLAYED_CORRECTLY, substrs=[ "NSString", "world"]) Index: vendor/lldb/dist/packages/Python/lldbsuite/test/macosx/find-dsym/bundle-with-dot-in-filename/Makefile =================================================================== --- vendor/lldb/dist/packages/Python/lldbsuite/test/macosx/find-dsym/bundle-with-dot-in-filename/Makefile (nonexistent) +++ vendor/lldb/dist/packages/Python/lldbsuite/test/macosx/find-dsym/bundle-with-dot-in-filename/Makefile (revision 320023) @@ -0,0 +1,21 @@ +CC ?= clang + +ifeq "$(ARCH)" "" + ARCH = x86_64 +endif + +CFLAGS ?= -g -O0 -arch $(ARCH) + +all: clean + $(CC) $(CFLAGS) -dynamiclib -o com.apple.sbd bundle.c + mkdir com.apple.sbd.xpc + mv com.apple.sbd com.apple.sbd.xpc/ + mkdir -p com.apple.sbd.xpc.dSYM/Contents/Resources/DWARF + mv com.apple.sbd.dSYM/Contents/Resources/DWARF/com.apple.sbd com.apple.sbd.xpc.dSYM/Contents/Resources/DWARF/ + rm -rf com.apple.sbd.dSYM + mkdir hide.app + tar cf - com.apple.sbd.xpc com.apple.sbd.xpc.dSYM | ( cd hide.app;tar xBpf -) + $(CC) $(CFLAGS) -o find-bundle-with-dots-in-fn main.c + +clean: + rm -rf a.out a.out.dSYM hide.app com.apple.sbd com.apple.sbd.dSYM com.apple.sbd.xpc com.apple.sbd.xpc.dSYM find-bundle-with-dots-in-fn find-bundle-with-dots-in-fn.dSYM Property changes on: vendor/lldb/dist/packages/Python/lldbsuite/test/macosx/find-dsym/bundle-with-dot-in-filename/Makefile ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: vendor/lldb/dist/packages/Python/lldbsuite/test/macosx/find-dsym/bundle-with-dot-in-filename/TestBundleWithDotInFilename.py =================================================================== --- vendor/lldb/dist/packages/Python/lldbsuite/test/macosx/find-dsym/bundle-with-dot-in-filename/TestBundleWithDotInFilename.py (nonexistent) +++ vendor/lldb/dist/packages/Python/lldbsuite/test/macosx/find-dsym/bundle-with-dot-in-filename/TestBundleWithDotInFilename.py (revision 320023) @@ -0,0 +1,71 @@ +"""Test that a dSYM can be found when a binary is in a bundle hnd has dots in the filename.""" + +from __future__ import print_function + +#import unittest2 +import os.path +from time import sleep + +import lldb +from lldbsuite.test.decorators import * +from lldbsuite.test.lldbtest import * +from lldbsuite.test import lldbutil + + +exe_name = 'find-bundle-with-dots-in-fn' # must match Makefile + +class BundleWithDotInFilenameTestCase(TestBase): + + mydir = TestBase.compute_mydir(__file__) + + @skipIfRemote + @skipUnlessDarwin + # This test is explicitly a dSYM test, it doesn't need to run for any other config, but + # the following doesn't work, fixme. + # @skipIf(debug_info=no_match(["dsym"]), bugnumber="This test is looking explicitly for a dSYM") + + def setUp(self): + TestBase.setUp(self) + self.source = 'main.c' + + def tearDown(self): + # Destroy process before TestBase.tearDown() + self.dbg.GetSelectedTarget().GetProcess().Destroy() + + # Call super's tearDown(). + TestBase.tearDown(self) + + def test_attach_and_check_dsyms(self): + """Test attach to binary, see if the bundle dSYM is found""" + exe = os.path.join(os.getcwd(), exe_name) + self.build() + popen = self.spawnSubprocess(exe) + self.addTearDownHook(self.cleanupSubprocesses) + + # Give the inferior time to start up, dlopen a bundle, remove the bundle it linked in + sleep(5) + + # Since the library that was dlopen()'ed is now removed, lldb will need to find the + # binary & dSYM via target.exec-search-paths + settings_str = "settings set target.exec-search-paths " + self.get_process_working_directory() + "/hide.app" + self.runCmd(settings_str) + + self.runCmd("process attach -p " + str(popen.pid)) + + target = self.dbg.GetSelectedTarget() + self.assertTrue(target.IsValid(), 'Should have a valid Target after attaching to process') + + setup_complete = target.FindFirstGlobalVariable("setup_is_complete") + self.assertTrue(setup_complete.GetValueAsUnsigned() == 1, 'Check that inferior process has completed setup') + + # Find the bundle module, see if we found the dSYM too (they're both in "hide.app") + i = 0 + while i < target.GetNumModules(): + mod = target.GetModuleAtIndex(i) + if mod.GetFileSpec().GetFilename() == 'com.apple.sbd': + dsym_name = mod.GetSymbolFileSpec().GetFilename() + self.assertTrue (dsym_name == 'com.apple.sbd', "Check that we found the dSYM for the bundle that was loaded") + i=i+1 + +if __name__ == '__main__': + unittest.main() Property changes on: vendor/lldb/dist/packages/Python/lldbsuite/test/macosx/find-dsym/bundle-with-dot-in-filename/TestBundleWithDotInFilename.py ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: vendor/lldb/dist/packages/Python/lldbsuite/test/macosx/find-dsym/bundle-with-dot-in-filename/bundle.c =================================================================== --- vendor/lldb/dist/packages/Python/lldbsuite/test/macosx/find-dsym/bundle-with-dot-in-filename/bundle.c (nonexistent) +++ vendor/lldb/dist/packages/Python/lldbsuite/test/macosx/find-dsym/bundle-with-dot-in-filename/bundle.c (revision 320023) @@ -0,0 +1,4 @@ +int foo () +{ + return 5; +} Property changes on: vendor/lldb/dist/packages/Python/lldbsuite/test/macosx/find-dsym/bundle-with-dot-in-filename/bundle.c ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: vendor/lldb/dist/packages/Python/lldbsuite/test/macosx/find-dsym/bundle-with-dot-in-filename/main.c =================================================================== --- vendor/lldb/dist/packages/Python/lldbsuite/test/macosx/find-dsym/bundle-with-dot-in-filename/main.c (nonexistent) +++ vendor/lldb/dist/packages/Python/lldbsuite/test/macosx/find-dsym/bundle-with-dot-in-filename/main.c (revision 320023) @@ -0,0 +1,28 @@ +#include +#include +#include + +int setup_is_complete = 0; + +int main() +{ + + void *handle = dlopen ("com.apple.sbd.xpc/com.apple.sbd", RTLD_NOW); + if (handle) + { + if (dlsym(handle, "foo")) + { + system ("/bin/rm -rf com.apple.sbd.xpc com.apple.sbd.xpc.dSYM"); + setup_is_complete = 1; + + // At this point we want lldb to attach to the process. If lldb attaches + // before we've removed the dlopen'ed bundle, lldb will find the bundle + // at its actual filepath and not have to do any tricky work, invalidating + // the test. + + for (int loop_limiter = 0; loop_limiter < 100; loop_limiter++) + sleep (1); + } + } + return 0; +} Property changes on: vendor/lldb/dist/packages/Python/lldbsuite/test/macosx/find-dsym/bundle-with-dot-in-filename/main.c ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: vendor/lldb/dist/packages/Python/lldbsuite/test/macosx/find-dsym/deep-bundle/Info.plist =================================================================== --- vendor/lldb/dist/packages/Python/lldbsuite/test/macosx/find-dsym/deep-bundle/Info.plist (nonexistent) +++ vendor/lldb/dist/packages/Python/lldbsuite/test/macosx/find-dsym/deep-bundle/Info.plist (revision 320023) @@ -0,0 +1,44 @@ + + + + + BuildMachineOSBuild + 16B2657 + CFBundleDevelopmentRegion + en + CFBundleExecutable + MyFramework + CFBundleIdentifier + com.apple.test.framework + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + MyFramework + CFBundlePackageType + FMWK + CFBundleShortVersionString + 113 + CFBundleSignature + ???? + CFBundleSupportedPlatforms + + MacOSX + + CFBundleVersion + 113 + DTCompiler + com.apple.compilers.llvm.clang.1_0 + DTPlatformBuild + 9L120i + DTPlatformVersion + GM + DTSDKBuild + 17A261x + DTSDKName + macosx10.13 + DTXcode + 0900 + DTXcodeBuild + 9L120i + + Index: vendor/lldb/dist/packages/Python/lldbsuite/test/macosx/find-dsym/deep-bundle/Makefile =================================================================== --- vendor/lldb/dist/packages/Python/lldbsuite/test/macosx/find-dsym/deep-bundle/Makefile (nonexistent) +++ vendor/lldb/dist/packages/Python/lldbsuite/test/macosx/find-dsym/deep-bundle/Makefile (revision 320023) @@ -0,0 +1,28 @@ +CC ?= clang + +ifeq "$(ARCH)" "" + ARCH = x86_64 +endif + +CFLAGS ?= -g -O0 -arch $(ARCH) + +all: clean + $(CC) $(CFLAGS) -install_name $(PWD)/MyFramework.framework/Versions/A/MyFramework -dynamiclib -o MyFramework myframework.c + mkdir -p MyFramework.framework/Versions/A/Headers + mkdir -p MyFramework.framework/Versions/A/Resources + cp MyFramework MyFramework.framework/Versions/A + cp MyFramework.h MyFramework.framework/Versions/A/Headers + cp Info.plist MyFramework.framework/Versions/A/Resources + ( cd MyFramework.framework/Versions ; ln -s A Current ) + ( cd MyFramework.framework/ ; ln -s Versions/Current/Headers . ) + ( cd MyFramework.framework/ ; ln -s Versions/Current/MyFramework . ) + ( cd MyFramework.framework/ ; ln -s Versions/Current/Resources . ) + mv MyFramework.dSYM MyFramework.framework.dSYM + mkdir hide.app + rm -f MyFramework + tar cf - MyFramework.framework MyFramework.framework.dSYM | ( cd hide.app;tar xBpf -) + $(CC) $(CFLAGS) -o deep-bundle main.c -F. -framework MyFramework + + +clean: + rm -rf a.out a.out.dSYM deep-bundle deep-bundle.dSYM MyFramework.framework MyFramework.framework.dSYM MyFramework MyFramework.dSYM hide.app Property changes on: vendor/lldb/dist/packages/Python/lldbsuite/test/macosx/find-dsym/deep-bundle/Makefile ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: vendor/lldb/dist/packages/Python/lldbsuite/test/macosx/find-dsym/deep-bundle/MyFramework.h =================================================================== --- vendor/lldb/dist/packages/Python/lldbsuite/test/macosx/find-dsym/deep-bundle/MyFramework.h (nonexistent) +++ vendor/lldb/dist/packages/Python/lldbsuite/test/macosx/find-dsym/deep-bundle/MyFramework.h (revision 320023) @@ -0,0 +1 @@ +int foo (); Property changes on: vendor/lldb/dist/packages/Python/lldbsuite/test/macosx/find-dsym/deep-bundle/MyFramework.h ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: vendor/lldb/dist/packages/Python/lldbsuite/test/macosx/find-dsym/deep-bundle/TestDeepBundle.py =================================================================== --- vendor/lldb/dist/packages/Python/lldbsuite/test/macosx/find-dsym/deep-bundle/TestDeepBundle.py (nonexistent) +++ vendor/lldb/dist/packages/Python/lldbsuite/test/macosx/find-dsym/deep-bundle/TestDeepBundle.py (revision 320023) @@ -0,0 +1,75 @@ +"""Test that a dSYM can be found when a binary is in a deep bundle with multiple pathname components.""" + +from __future__ import print_function + +#import unittest2 +import os.path +from time import sleep + +import lldb +from lldbsuite.test.decorators import * +from lldbsuite.test.lldbtest import * +from lldbsuite.test import lldbutil + + +exe_name = 'deep-bundle' # must match Makefile + +class DeepBundleTestCase(TestBase): + + mydir = TestBase.compute_mydir(__file__) + + @skipIfRemote + @skipUnlessDarwin + # This test is explicitly a dSYM test, it doesn't need to run for any other config, but + # the following doesn't work, fixme. + # @skipIf(debug_info=no_match(["dsym"]), bugnumber="This test is looking explicitly for a dSYM") + + def setUp(self): + TestBase.setUp(self) + self.source = 'main.c' + + def tearDown(self): + # Destroy process before TestBase.tearDown() + self.dbg.GetSelectedTarget().GetProcess().Destroy() + + # Call super's tearDown(). + TestBase.tearDown(self) + + def test_attach_and_check_dsyms(self): + """Test attach to binary, see if the framework dSYM is found""" + exe = os.path.join(os.getcwd(), exe_name) + self.build() + popen = self.spawnSubprocess(exe) + self.addTearDownHook(self.cleanupSubprocesses) + + # Give the inferior time to start up, dlopen a bundle, remove the bundle it linked in + sleep(5) + + # Since the library that was dlopen()'ed is now removed, lldb will need to find the + # binary & dSYM via target.exec-search-paths + settings_str = "settings set target.exec-search-paths " + self.get_process_working_directory() + "/hide.app" + self.runCmd(settings_str) + + self.runCmd("process attach -p " + str(popen.pid)) + + target = self.dbg.GetSelectedTarget() + self.assertTrue(target.IsValid(), 'Should have a valid Target after attaching to process') + + setup_complete = target.FindFirstGlobalVariable("setup_is_complete") + self.assertTrue(setup_complete.GetValueAsUnsigned() == 1, 'Check that inferior process has completed setup') + + # Find the bundle module, see if we found the dSYM too (they're both in "hide.app") + i = 0 + found_module = False + while i < target.GetNumModules(): + mod = target.GetModuleAtIndex(i) + if mod.GetFileSpec().GetFilename() == 'MyFramework': + found_module = True + dsym_name = mod.GetSymbolFileSpec().GetFilename() + self.assertTrue (dsym_name == 'MyFramework', "Check that we found the dSYM for the bundle that was loaded") + i=i+1 + + self.assertTrue(found_module, "Check that we found the framework loaded in lldb's image list") + +if __name__ == '__main__': + unittest.main() Property changes on: vendor/lldb/dist/packages/Python/lldbsuite/test/macosx/find-dsym/deep-bundle/TestDeepBundle.py ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: vendor/lldb/dist/packages/Python/lldbsuite/test/macosx/find-dsym/deep-bundle/main.c =================================================================== --- vendor/lldb/dist/packages/Python/lldbsuite/test/macosx/find-dsym/deep-bundle/main.c (nonexistent) +++ vendor/lldb/dist/packages/Python/lldbsuite/test/macosx/find-dsym/deep-bundle/main.c (revision 320023) @@ -0,0 +1,22 @@ +#include +#include +#include + +int setup_is_complete = 0; + +int main() +{ + system ("/bin/rm -rf MyFramework MyFramework.framework MyFramework.framework.dSYM"); + + setup_is_complete = 1; + + // At this point we want lldb to attach to the process. If lldb attaches + // before we've removed the framework we're running against, it will be + // easy for lldb to find the binary & dSYM without using target.exec-search-paths, + // which is the point of this test. + + for (int loop_limiter = 0; loop_limiter < 100; loop_limiter++) + sleep (1); + + return foo(); +} Property changes on: vendor/lldb/dist/packages/Python/lldbsuite/test/macosx/find-dsym/deep-bundle/main.c ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: vendor/lldb/dist/packages/Python/lldbsuite/test/macosx/find-dsym/deep-bundle/myframework.c =================================================================== --- vendor/lldb/dist/packages/Python/lldbsuite/test/macosx/find-dsym/deep-bundle/myframework.c (nonexistent) +++ vendor/lldb/dist/packages/Python/lldbsuite/test/macosx/find-dsym/deep-bundle/myframework.c (revision 320023) @@ -0,0 +1,4 @@ +int foo () +{ + return 5; +} Property changes on: vendor/lldb/dist/packages/Python/lldbsuite/test/macosx/find-dsym/deep-bundle/myframework.c ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: vendor/lldb/dist/packages/Python/lldbsuite/test/tools/lldb-server/TestGdbRemoteSingleStep.py =================================================================== --- vendor/lldb/dist/packages/Python/lldbsuite/test/tools/lldb-server/TestGdbRemoteSingleStep.py (revision 320022) +++ vendor/lldb/dist/packages/Python/lldbsuite/test/tools/lldb-server/TestGdbRemoteSingleStep.py (revision 320023) @@ -1,39 +1,40 @@ from __future__ import print_function import gdbremote_testcase from lldbsuite.test.decorators import * from lldbsuite.test.lldbtest import * from lldbsuite.test import lldbutil class TestGdbRemoteSingleStep(gdbremote_testcase.GdbRemoteTestCaseBase): mydir = TestBase.compute_mydir(__file__) @debugserver_test def test_single_step_only_steps_one_instruction_with_s_debugserver(self): self.init_debugserver_test() self.build() self.set_inferior_startup_launch() self.single_step_only_steps_one_instruction( use_Hc_packet=True, step_instruction="s") @llgs_test @expectedFailureAndroid( bugnumber="llvm.org/pr24739", archs=[ "arm", "aarch64"]) @expectedFailureAll( oslist=["linux"], archs=[ "arm", "aarch64"], bugnumber="llvm.org/pr24739") + @skipIf(triple='^mips') def test_single_step_only_steps_one_instruction_with_s_llgs(self): self.init_llgs_test() self.build() self.set_inferior_startup_launch() self.single_step_only_steps_one_instruction( use_Hc_packet=True, step_instruction="s") Index: vendor/lldb/dist/packages/Python/lldbsuite/test/tools/lldb-server/TestGdbRemote_vCont.py =================================================================== --- vendor/lldb/dist/packages/Python/lldbsuite/test/tools/lldb-server/TestGdbRemote_vCont.py (revision 320022) +++ vendor/lldb/dist/packages/Python/lldbsuite/test/tools/lldb-server/TestGdbRemote_vCont.py (revision 320023) @@ -1,145 +1,147 @@ from __future__ import print_function import gdbremote_testcase from lldbsuite.test.decorators import * from lldbsuite.test.lldbtest import * from lldbsuite.test import lldbutil class TestGdbRemote_vCont(gdbremote_testcase.GdbRemoteTestCaseBase): mydir = TestBase.compute_mydir(__file__) def vCont_supports_mode(self, mode, inferior_args=None): # Setup the stub and set the gdb remote command stream. procs = self.prep_debug_monitor_and_inferior( inferior_args=inferior_args) self.add_vCont_query_packets() # Run the gdb remote command stream. context = self.expect_gdbremote_sequence() self.assertIsNotNone(context) # Pull out supported modes. supported_vCont_modes = self.parse_vCont_query_response(context) self.assertIsNotNone(supported_vCont_modes) # Verify we support the given mode. self.assertTrue(mode in supported_vCont_modes) def vCont_supports_c(self): self.vCont_supports_mode("c") def vCont_supports_C(self): self.vCont_supports_mode("C") def vCont_supports_s(self): self.vCont_supports_mode("s") def vCont_supports_S(self): self.vCont_supports_mode("S") @debugserver_test def test_vCont_supports_c_debugserver(self): self.init_debugserver_test() self.build() self.vCont_supports_c() @llgs_test def test_vCont_supports_c_llgs(self): self.init_llgs_test() self.build() self.vCont_supports_c() @debugserver_test def test_vCont_supports_C_debugserver(self): self.init_debugserver_test() self.build() self.vCont_supports_C() @llgs_test def test_vCont_supports_C_llgs(self): self.init_llgs_test() self.build() self.vCont_supports_C() @debugserver_test def test_vCont_supports_s_debugserver(self): self.init_debugserver_test() self.build() self.vCont_supports_s() @llgs_test def test_vCont_supports_s_llgs(self): self.init_llgs_test() self.build() self.vCont_supports_s() @debugserver_test def test_vCont_supports_S_debugserver(self): self.init_debugserver_test() self.build() self.vCont_supports_S() @llgs_test def test_vCont_supports_S_llgs(self): self.init_llgs_test() self.build() self.vCont_supports_S() @debugserver_test def test_single_step_only_steps_one_instruction_with_Hc_vCont_s_debugserver( self): self.init_debugserver_test() self.build() self.set_inferior_startup_launch() self.single_step_only_steps_one_instruction( use_Hc_packet=True, step_instruction="vCont;s") @llgs_test @expectedFailureAndroid( bugnumber="llvm.org/pr24739", archs=[ "arm", "aarch64"]) @expectedFailureAll( oslist=["linux"], archs=[ "arm", "aarch64"], bugnumber="llvm.org/pr24739") + @skipIf(triple='^mips') def test_single_step_only_steps_one_instruction_with_Hc_vCont_s_llgs(self): self.init_llgs_test() self.build() self.set_inferior_startup_launch() self.single_step_only_steps_one_instruction( use_Hc_packet=True, step_instruction="vCont;s") @debugserver_test def test_single_step_only_steps_one_instruction_with_vCont_s_thread_debugserver( self): self.init_debugserver_test() self.build() self.set_inferior_startup_launch() self.single_step_only_steps_one_instruction( use_Hc_packet=False, step_instruction="vCont;s:{thread}") @llgs_test @expectedFailureAndroid( bugnumber="llvm.org/pr24739", archs=[ "arm", "aarch64"]) @expectedFailureAll( oslist=["linux"], archs=[ "arm", "aarch64"], bugnumber="llvm.org/pr24739") + @skipIf(triple='^mips') def test_single_step_only_steps_one_instruction_with_vCont_s_thread_llgs( self): self.init_llgs_test() self.build() self.set_inferior_startup_launch() self.single_step_only_steps_one_instruction( use_Hc_packet=False, step_instruction="vCont;s:{thread}") Index: vendor/lldb/dist/scripts/lldb.swig =================================================================== --- vendor/lldb/dist/scripts/lldb.swig (revision 320022) +++ vendor/lldb/dist/scripts/lldb.swig (revision 320023) @@ -1,225 +1,231 @@ /* lldb.swig This is the input file for SWIG, to create the appropriate C++ wrappers and functions for various scripting languages, to enable them to call the liblldb Script Bridge functions. */ /* Define our module docstring. */ %define DOCSTRING "The lldb module contains the public APIs for Python binding. Some of the important classes are described here: o SBTarget: Represents the target program running under the debugger. o SBProcess: Represents the process associated with the target program. o SBThread: Represents a thread of execution. SBProcess contains SBThread(s). o SBFrame: Represents one of the stack frames associated with a thread. SBThread contains SBFrame(s). o SBSymbolContext: A container that stores various debugger related info. o SBValue: Represents the value of a variable, a register, or an expression. o SBModule: Represents an executable image and its associated object and symbol files. SBTarget contains SBModule(s). o SBBreakpoint: Represents a logical breakpoint and its associated settings. SBTarget contains SBBreakpoint(s). o SBSymbol: Represents the symbol possibly associated with a stack frame. o SBCompileUnit: Represents a compilation unit, or compiled source file. o SBFunction: Represents a generic function, which can be inlined or not. o SBBlock: Represents a lexical block. SBFunction contains SBBlock(s). o SBLineEntry: Specifies an association with a contiguous range of instructions and a source file location. SBCompileUnit contains SBLineEntry(s)." %enddef /* Since version 3.0.9, swig's logic for importing the native module has changed in a way that is incompatible with our usage of the python module as __init__.py (See swig bug #769). Fortunately, since version 3.0.11, swig provides a way for us to override the module import logic to suit our needs. This does that. Older swig versions will simply ignore this setting. */ %define MODULEIMPORT -"from . import $module" +"try: + # Try a relative import first + from . import $module +except ImportError: + # Maybe absolute import will work (if we're being loaded from lldb, it + # should). + import $module" %enddef // These versions will not generate working python modules, so error out early. #if SWIG_VERSION >= 0x030009 && SWIG_VERSION < 0x030011 #error Swig versions 3.0.9 and 3.0.10 are incompatible with lldb. #endif // The name of the module to be created. %module(docstring=DOCSTRING, moduleimport=MODULEIMPORT) lldb // Parameter types will be used in the autodoc string. %feature("autodoc", "1"); %pythoncode%{ import uuid import re import os import six %} %include "./Python/python-typemaps.swig" /* C++ headers to be included. */ %{ #include #include %} /* The liblldb header files to be included. */ %{ #include "lldb/lldb-public.h" #include "lldb/API/SBAddress.h" #include "lldb/API/SBAttachInfo.h" #include "lldb/API/SBBlock.h" #include "lldb/API/SBBreakpoint.h" #include "lldb/API/SBBreakpointLocation.h" #include "lldb/API/SBBroadcaster.h" #include "lldb/API/SBCommandInterpreter.h" #include "lldb/API/SBCommandReturnObject.h" #include "lldb/API/SBCommunication.h" #include "lldb/API/SBCompileUnit.h" #include "lldb/API/SBData.h" #include "lldb/API/SBDebugger.h" #include "lldb/API/SBDeclaration.h" #include "lldb/API/SBError.h" #include "lldb/API/SBEvent.h" #include "lldb/API/SBExecutionContext.h" #include "lldb/API/SBExpressionOptions.h" #include "lldb/API/SBFileSpec.h" #include "lldb/API/SBFileSpecList.h" #include "lldb/API/SBFrame.h" #include "lldb/API/SBFunction.h" #include "lldb/API/SBHostOS.h" #include "lldb/API/SBInstruction.h" #include "lldb/API/SBInstructionList.h" #include "lldb/API/SBLanguageRuntime.h" #include "lldb/API/SBLaunchInfo.h" #include "lldb/API/SBLineEntry.h" #include "lldb/API/SBListener.h" #include "lldb/API/SBMemoryRegionInfo.h" #include "lldb/API/SBMemoryRegionInfoList.h" #include "lldb/API/SBModule.h" #include "lldb/API/SBModuleSpec.h" #include "lldb/API/SBPlatform.h" #include "lldb/API/SBProcess.h" #include "lldb/API/SBQueue.h" #include "lldb/API/SBQueueItem.h" #include "lldb/API/SBSection.h" #include "lldb/API/SBSourceManager.h" #include "lldb/API/SBStream.h" #include "lldb/API/SBStringList.h" #include "lldb/API/SBStructuredData.h" #include "lldb/API/SBSymbol.h" #include "lldb/API/SBSymbolContext.h" #include "lldb/API/SBSymbolContextList.h" #include "lldb/API/SBTarget.h" #include "lldb/API/SBThread.h" #include "lldb/API/SBThreadCollection.h" #include "lldb/API/SBThreadPlan.h" #include "lldb/API/SBTrace.h" #include "lldb/API/SBTraceOptions.h" #include "lldb/API/SBType.h" #include "lldb/API/SBTypeCategory.h" #include "lldb/API/SBTypeEnumMember.h" #include "lldb/API/SBTypeFilter.h" #include "lldb/API/SBTypeFormat.h" #include "lldb/API/SBTypeNameSpecifier.h" #include "lldb/API/SBTypeSummary.h" #include "lldb/API/SBTypeSynthetic.h" #include "lldb/API/SBValue.h" #include "lldb/API/SBValueList.h" #include "lldb/API/SBVariablesOptions.h" #include "lldb/API/SBWatchpoint.h" #include "lldb/API/SBUnixSignals.h" #include "../source/Plugins/ScriptInterpreter/Python/PythonDataObjects.h" #include "../scripts/Python/python-swigsafecast.swig" %} /* Various liblldb typedefs that SWIG needs to know about. */ #define __extension__ /* Undefine GCC keyword to make Swig happy when processing glibc's stdint.h. */ /* The ISO C99 standard specifies that in C++ implementations limit macros such as INT32_MAX should only be defined if __STDC_LIMIT_MACROS is. */ #define __STDC_LIMIT_MACROS %include "stdint.i" %include "lldb/lldb-defines.h" %include "lldb/lldb-enumerations.h" %include "lldb/lldb-forward.h" %include "lldb/lldb-types.h" /* Forward declaration of SB classes. */ %include "lldb/API/SBDefines.h" /* Python interface files with docstrings. */ %include "./interface/SBAddress.i" %include "./interface/SBAttachInfo.i" %include "./interface/SBBlock.i" %include "./interface/SBBreakpoint.i" %include "./interface/SBBreakpointLocation.i" %include "./interface/SBBroadcaster.i" %include "./interface/SBCommandInterpreter.i" %include "./interface/SBCommandReturnObject.i" %include "./interface/SBCommunication.i" %include "./interface/SBCompileUnit.i" %include "./interface/SBData.i" %include "./interface/SBDebugger.i" %include "./interface/SBDeclaration.i" %include "./interface/SBError.i" %include "./interface/SBEvent.i" %include "./interface/SBExecutionContext.i" %include "./interface/SBExpressionOptions.i" %include "./interface/SBFileSpec.i" %include "./interface/SBFileSpecList.i" %include "./interface/SBFrame.i" %include "./interface/SBFunction.i" %include "./interface/SBHostOS.i" %include "./interface/SBInstruction.i" %include "./interface/SBInstructionList.i" %include "./interface/SBLanguageRuntime.i" %include "./interface/SBLaunchInfo.i" %include "./interface/SBLineEntry.i" %include "./interface/SBListener.i" %include "./interface/SBMemoryRegionInfo.i" %include "./interface/SBMemoryRegionInfoList.i" %include "./interface/SBModule.i" %include "./interface/SBModuleSpec.i" %include "./interface/SBPlatform.i" %include "./interface/SBProcess.i" %include "./interface/SBQueue.i" %include "./interface/SBQueueItem.i" %include "./interface/SBSection.i" %include "./interface/SBSourceManager.i" %include "./interface/SBStream.i" %include "./interface/SBStringList.i" %include "./interface/SBStructuredData.i" %include "./interface/SBSymbol.i" %include "./interface/SBSymbolContext.i" %include "./interface/SBSymbolContextList.i" %include "./interface/SBTarget.i" %include "./interface/SBThread.i" %include "./interface/SBThreadCollection.i" %include "./interface/SBThreadPlan.i" %include "./interface/SBTrace.i" %include "./interface/SBTraceOptions.i" %include "./interface/SBType.i" %include "./interface/SBTypeCategory.i" %include "./interface/SBTypeEnumMember.i" %include "./interface/SBTypeFilter.i" %include "./interface/SBTypeFormat.i" %include "./interface/SBTypeNameSpecifier.i" %include "./interface/SBTypeSummary.i" %include "./interface/SBTypeSynthetic.i" %include "./interface/SBValue.i" %include "./interface/SBValueList.i" %include "./interface/SBVariablesOptions.i" %include "./interface/SBWatchpoint.i" %include "./interface/SBUnixSignals.i" %include "./Python/python-extensions.swig" %include "./Python/python-wrapper.swig" Index: vendor/lldb/dist/source/Commands/CommandObjectThread.cpp =================================================================== --- vendor/lldb/dist/source/Commands/CommandObjectThread.cpp (revision 320022) +++ vendor/lldb/dist/source/Commands/CommandObjectThread.cpp (revision 320023) @@ -1,1988 +1,2097 @@ //===-- CommandObjectThread.cpp ---------------------------------*- C++ -*-===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// #include "CommandObjectThread.h" // C Includes // C++ Includes // Other libraries and framework includes // Project includes #include "lldb/Core/SourceManager.h" #include "lldb/Core/State.h" #include "lldb/Core/ValueObject.h" #include "lldb/Host/Host.h" #include "lldb/Host/OptionParser.h" #include "lldb/Host/StringConvert.h" #include "lldb/Interpreter/CommandInterpreter.h" #include "lldb/Interpreter/CommandReturnObject.h" #include "lldb/Interpreter/Options.h" #include "lldb/Symbol/CompileUnit.h" #include "lldb/Symbol/Function.h" #include "lldb/Symbol/LineEntry.h" #include "lldb/Symbol/LineTable.h" #include "lldb/Target/Process.h" #include "lldb/Target/RegisterContext.h" #include "lldb/Target/SystemRuntime.h" #include "lldb/Target/Target.h" #include "lldb/Target/Thread.h" #include "lldb/Target/ThreadPlan.h" #include "lldb/Target/ThreadPlanStepInRange.h" #include "lldb/Target/ThreadPlanStepInstruction.h" #include "lldb/Target/ThreadPlanStepOut.h" #include "lldb/Target/ThreadPlanStepRange.h" #include "lldb/lldb-private.h" using namespace lldb; using namespace lldb_private; //------------------------------------------------------------------------- -// CommandObjectThreadBacktrace +// CommandObjectIterateOverThreads //------------------------------------------------------------------------- class CommandObjectIterateOverThreads : public CommandObjectParsed { + + class UniqueStack { + + public: + UniqueStack(std::stack stack_frames, uint32_t thread_index_id) + : m_stack_frames(stack_frames) { + m_thread_index_ids.push_back(thread_index_id); + } + + void AddThread(uint32_t thread_index_id) const { + m_thread_index_ids.push_back(thread_index_id); + } + + const std::vector &GetUniqueThreadIndexIDs() const { + return m_thread_index_ids; + } + + lldb::tid_t GetRepresentativeThread() const { + return m_thread_index_ids.front(); + } + + friend bool inline operator<(const UniqueStack &lhs, + const UniqueStack &rhs) { + return lhs.m_stack_frames < rhs.m_stack_frames; + } + + protected: + // Mark the thread index as mutable, as we don't care about it from a const + // perspective, we only care about m_stack_frames so we keep our std::set + // sorted. + mutable std::vector m_thread_index_ids; + std::stack m_stack_frames; + }; + public: CommandObjectIterateOverThreads(CommandInterpreter &interpreter, const char *name, const char *help, const char *syntax, uint32_t flags) : CommandObjectParsed(interpreter, name, help, syntax, flags) {} ~CommandObjectIterateOverThreads() override = default; bool DoExecute(Args &command, CommandReturnObject &result) override { result.SetStatus(m_success_return); + bool all_threads = false; if (command.GetArgumentCount() == 0) { Thread *thread = m_exe_ctx.GetThreadPtr(); if (!HandleOneThread(thread->GetID(), result)) return false; return result.Succeeded(); + } else if (command.GetArgumentCount() == 1) { + all_threads = ::strcmp(command.GetArgumentAtIndex(0), "all") == 0; + m_unique_stacks = ::strcmp(command.GetArgumentAtIndex(0), "unique") == 0; } // Use tids instead of ThreadSPs to prevent deadlocking problems which // result from JIT-ing // code while iterating over the (locked) ThreadSP list. std::vector tids; - if (command.GetArgumentCount() == 1 && - ::strcmp(command.GetArgumentAtIndex(0), "all") == 0) { + if (all_threads || m_unique_stacks) { Process *process = m_exe_ctx.GetProcessPtr(); for (ThreadSP thread_sp : process->Threads()) tids.push_back(thread_sp->GetID()); } else { const size_t num_args = command.GetArgumentCount(); Process *process = m_exe_ctx.GetProcessPtr(); std::lock_guard guard( process->GetThreadList().GetMutex()); for (size_t i = 0; i < num_args; i++) { bool success; uint32_t thread_idx = StringConvert::ToUInt32( command.GetArgumentAtIndex(i), 0, 0, &success); if (!success) { result.AppendErrorWithFormat("invalid thread specification: \"%s\"\n", command.GetArgumentAtIndex(i)); result.SetStatus(eReturnStatusFailed); return false; } ThreadSP thread = process->GetThreadList().FindThreadByIndexID(thread_idx); if (!thread) { result.AppendErrorWithFormat("no thread with index: \"%s\"\n", command.GetArgumentAtIndex(i)); result.SetStatus(eReturnStatusFailed); return false; } tids.push_back(thread->GetID()); } } - uint32_t idx = 0; - for (const lldb::tid_t &tid : tids) { - if (idx != 0 && m_add_return) - result.AppendMessage(""); + if (m_unique_stacks) { + // Iterate over threads, finding unique stack buckets. + std::set unique_stacks; + for (const lldb::tid_t &tid : tids) { + if (!BucketThread(tid, unique_stacks, result)) { + return false; + } + } - if (!HandleOneThread(tid, result)) - return false; + // Write the thread id's and unique call stacks to the output stream + Stream &strm = result.GetOutputStream(); + Process *process = m_exe_ctx.GetProcessPtr(); + for (const UniqueStack &stack : unique_stacks) { + // List the common thread ID's + const std::vector &thread_index_ids = + stack.GetUniqueThreadIndexIDs(); + strm.Printf("%lu thread(s) ", thread_index_ids.size()); + for (const uint32_t &thread_index_id : thread_index_ids) { + strm.Printf("#%u ", thread_index_id); + } + strm.EOL(); - ++idx; + // List the shared call stack for this set of threads + uint32_t representative_thread_id = stack.GetRepresentativeThread(); + ThreadSP thread = process->GetThreadList().FindThreadByIndexID( + representative_thread_id); + if (!HandleOneThread(thread->GetID(), result)) { + return false; + } + } + } else { + uint32_t idx = 0; + for (const lldb::tid_t &tid : tids) { + if (idx != 0 && m_add_return) + result.AppendMessage(""); + + if (!HandleOneThread(tid, result)) + return false; + + ++idx; + } } return result.Succeeded(); } protected: // Override this to do whatever you need to do for one thread. // // If you return false, the iteration will stop, otherwise it will proceed. // The result is set to m_success_return (defaults to // eReturnStatusSuccessFinishResult) before the iteration, // so you only need to set the return status in HandleOneThread if you want to // indicate an error. // If m_add_return is true, a blank line will be inserted between each of the // listings (except the last one.) virtual bool HandleOneThread(lldb::tid_t, CommandReturnObject &result) = 0; + bool BucketThread(lldb::tid_t tid, std::set &unique_stacks, + CommandReturnObject &result) { + // Grab the corresponding thread for the given thread id. + Process *process = m_exe_ctx.GetProcessPtr(); + Thread *thread = process->GetThreadList().FindThreadByID(tid).get(); + if (thread == nullptr) { + result.AppendErrorWithFormat("Failed to process thread# %lu.\n", tid); + result.SetStatus(eReturnStatusFailed); + return false; + } + + // Collect the each frame's address for this call-stack + std::stack stack_frames; + const uint32_t frame_count = thread->GetStackFrameCount(); + for (uint32_t frame_index = 0; frame_index < frame_count; frame_index++) { + const lldb::StackFrameSP frame_sp = + thread->GetStackFrameAtIndex(frame_index); + const lldb::addr_t pc = frame_sp->GetStackID().GetPC(); + stack_frames.push(pc); + } + + uint32_t thread_index_id = thread->GetIndexID(); + UniqueStack new_unique_stack(stack_frames, thread_index_id); + + // Try to match the threads stack to and existing entry. + std::set::iterator matching_stack = + unique_stacks.find(new_unique_stack); + if (matching_stack != unique_stacks.end()) { + matching_stack->AddThread(thread_index_id); + } else { + unique_stacks.insert(new_unique_stack); + } + return true; + } + ReturnStatus m_success_return = eReturnStatusSuccessFinishResult; + bool m_unique_stacks = false; bool m_add_return = true; }; //------------------------------------------------------------------------- // CommandObjectThreadBacktrace //------------------------------------------------------------------------- static OptionDefinition g_thread_backtrace_options[] = { // clang-format off { LLDB_OPT_SET_1, false, "count", 'c', OptionParser::eRequiredArgument, nullptr, nullptr, 0, eArgTypeCount, "How many frames to display (-1 for all)" }, { LLDB_OPT_SET_1, false, "start", 's', OptionParser::eRequiredArgument, nullptr, nullptr, 0, eArgTypeFrameIndex, "Frame in which to start the backtrace" }, { LLDB_OPT_SET_1, false, "extended", 'e', OptionParser::eRequiredArgument, nullptr, nullptr, 0, eArgTypeBoolean, "Show the extended backtrace, if available" } // clang-format on }; class CommandObjectThreadBacktrace : public CommandObjectIterateOverThreads { public: class CommandOptions : public Options { public: CommandOptions() : Options() { // Keep default values of all options in one place: OptionParsingStarting // () OptionParsingStarting(nullptr); } ~CommandOptions() override = default; Status SetOptionValue(uint32_t option_idx, llvm::StringRef option_arg, ExecutionContext *execution_context) override { Status error; const int short_option = m_getopt_table[option_idx].val; switch (short_option) { case 'c': { int32_t input_count = 0; if (option_arg.getAsInteger(0, m_count)) { m_count = UINT32_MAX; error.SetErrorStringWithFormat( "invalid integer value for option '%c'", short_option); } else if (input_count < 0) m_count = UINT32_MAX; } break; case 's': if (option_arg.getAsInteger(0, m_start)) error.SetErrorStringWithFormat( "invalid integer value for option '%c'", short_option); break; case 'e': { bool success; m_extended_backtrace = Args::StringToBoolean(option_arg, false, &success); if (!success) error.SetErrorStringWithFormat( "invalid boolean value for option '%c'", short_option); } break; default: error.SetErrorStringWithFormat("invalid short option character '%c'", short_option); break; } return error; } void OptionParsingStarting(ExecutionContext *execution_context) override { m_count = UINT32_MAX; m_start = 0; m_extended_backtrace = false; } llvm::ArrayRef GetDefinitions() override { return llvm::makeArrayRef(g_thread_backtrace_options); } // Instance variables to hold the values for command options. uint32_t m_count; uint32_t m_start; bool m_extended_backtrace; }; CommandObjectThreadBacktrace(CommandInterpreter &interpreter) : CommandObjectIterateOverThreads( interpreter, "thread backtrace", "Show thread call stacks. Defaults to the current thread, thread " - "indexes can be specified as arguments. Use the thread-index " - "\"all\" " - "to see all threads.", + "indexes can be specified as arguments.\n" + "Use the thread-index \"all\" to see all threads.\n" + "Use the thread-index \"unique\" to see threads grouped by unique " + "call stacks.", nullptr, eCommandRequiresProcess | eCommandRequiresThread | eCommandTryTargetAPILock | eCommandProcessMustBeLaunched | eCommandProcessMustBePaused), m_options() {} ~CommandObjectThreadBacktrace() override = default; Options *GetOptions() override { return &m_options; } protected: void DoExtendedBacktrace(Thread *thread, CommandReturnObject &result) { SystemRuntime *runtime = thread->GetProcess()->GetSystemRuntime(); if (runtime) { Stream &strm = result.GetOutputStream(); const std::vector &types = runtime->GetExtendedBacktraceTypes(); for (auto type : types) { ThreadSP ext_thread_sp = runtime->GetExtendedBacktraceThread( thread->shared_from_this(), type); if (ext_thread_sp && ext_thread_sp->IsValid()) { const uint32_t num_frames_with_source = 0; const bool stop_format = false; if (ext_thread_sp->GetStatus(strm, m_options.m_start, m_options.m_count, num_frames_with_source, stop_format)) { DoExtendedBacktrace(ext_thread_sp.get(), result); } } } } } bool HandleOneThread(lldb::tid_t tid, CommandReturnObject &result) override { ThreadSP thread_sp = m_exe_ctx.GetProcessPtr()->GetThreadList().FindThreadByID(tid); if (!thread_sp) { result.AppendErrorWithFormat( "thread disappeared while computing backtraces: 0x%" PRIx64 "\n", tid); result.SetStatus(eReturnStatusFailed); return false; } Thread *thread = thread_sp.get(); Stream &strm = result.GetOutputStream(); + // Only dump stack info if we processing unique stacks. + const bool only_stacks = m_unique_stacks; + // Don't show source context when doing backtraces. const uint32_t num_frames_with_source = 0; const bool stop_format = true; if (!thread->GetStatus(strm, m_options.m_start, m_options.m_count, - num_frames_with_source, stop_format)) { + num_frames_with_source, stop_format, only_stacks)) { result.AppendErrorWithFormat( "error displaying backtrace for thread: \"0x%4.4x\"\n", thread->GetIndexID()); result.SetStatus(eReturnStatusFailed); return false; } if (m_options.m_extended_backtrace) { DoExtendedBacktrace(thread, result); } return true; } CommandOptions m_options; }; enum StepScope { eStepScopeSource, eStepScopeInstruction }; static OptionEnumValueElement g_tri_running_mode[] = { {eOnlyThisThread, "this-thread", "Run only this thread"}, {eAllThreads, "all-threads", "Run all threads"}, {eOnlyDuringStepping, "while-stepping", "Run only this thread while stepping"}, {0, nullptr, nullptr}}; static OptionEnumValueElement g_duo_running_mode[] = { {eOnlyThisThread, "this-thread", "Run only this thread"}, {eAllThreads, "all-threads", "Run all threads"}, {0, nullptr, nullptr}}; static OptionDefinition g_thread_step_scope_options[] = { // clang-format off { LLDB_OPT_SET_1, false, "step-in-avoids-no-debug", 'a', OptionParser::eRequiredArgument, nullptr, nullptr, 0, eArgTypeBoolean, "A boolean value that sets whether stepping into functions will step over functions with no debug information." }, { LLDB_OPT_SET_1, false, "step-out-avoids-no-debug", 'A', OptionParser::eRequiredArgument, nullptr, nullptr, 0, eArgTypeBoolean, "A boolean value, if true stepping out of functions will continue to step out till it hits a function with debug information." }, { LLDB_OPT_SET_1, false, "count", 'c', OptionParser::eRequiredArgument, nullptr, nullptr, 1, eArgTypeCount, "How many times to perform the stepping operation - currently only supported for step-inst and next-inst." }, { LLDB_OPT_SET_1, false, "end-linenumber", 'e', OptionParser::eRequiredArgument, nullptr, nullptr, 1, eArgTypeLineNum, "The line at which to stop stepping - defaults to the next line and only supported for step-in and step-over. You can also pass the string 'block' to step to the end of the current block. This is particularly useful in conjunction with --step-target to step through a complex calling sequence." }, { LLDB_OPT_SET_1, false, "run-mode", 'm', OptionParser::eRequiredArgument, nullptr, g_tri_running_mode, 0, eArgTypeRunMode, "Determine how to run other threads while stepping the current thread." }, { LLDB_OPT_SET_1, false, "step-over-regexp", 'r', OptionParser::eRequiredArgument, nullptr, nullptr, 0, eArgTypeRegularExpression, "A regular expression that defines function names to not to stop at when stepping in." }, { LLDB_OPT_SET_1, false, "step-in-target", 't', OptionParser::eRequiredArgument, nullptr, nullptr, 0, eArgTypeFunctionName, "The name of the directly called function step in should stop at when stepping into." }, { LLDB_OPT_SET_2, false, "python-class", 'C', OptionParser::eRequiredArgument, nullptr, nullptr, 0, eArgTypePythonClass, "The name of the class that will manage this step - only supported for Scripted Step." } // clang-format on }; class CommandObjectThreadStepWithTypeAndScope : public CommandObjectParsed { public: class CommandOptions : public Options { public: CommandOptions() : Options() { // Keep default values of all options in one place: OptionParsingStarting // () OptionParsingStarting(nullptr); } ~CommandOptions() override = default; Status SetOptionValue(uint32_t option_idx, llvm::StringRef option_arg, ExecutionContext *execution_context) override { Status error; const int short_option = m_getopt_table[option_idx].val; switch (short_option) { case 'a': { bool success; bool avoid_no_debug = Args::StringToBoolean(option_arg, true, &success); if (!success) error.SetErrorStringWithFormat( "invalid boolean value for option '%c'", short_option); else { m_step_in_avoid_no_debug = avoid_no_debug ? eLazyBoolYes : eLazyBoolNo; } } break; case 'A': { bool success; bool avoid_no_debug = Args::StringToBoolean(option_arg, true, &success); if (!success) error.SetErrorStringWithFormat( "invalid boolean value for option '%c'", short_option); else { m_step_out_avoid_no_debug = avoid_no_debug ? eLazyBoolYes : eLazyBoolNo; } } break; case 'c': if (option_arg.getAsInteger(0, m_step_count)) error.SetErrorStringWithFormat("invalid step count '%s'", option_arg.str().c_str()); break; case 'C': m_class_name.clear(); m_class_name.assign(option_arg); break; case 'm': { OptionEnumValueElement *enum_values = GetDefinitions()[option_idx].enum_values; m_run_mode = (lldb::RunMode)Args::StringToOptionEnum( option_arg, enum_values, eOnlyDuringStepping, error); } break; case 'e': if (option_arg == "block") { m_end_line_is_block_end = 1; break; } if (option_arg.getAsInteger(0, m_end_line)) error.SetErrorStringWithFormat("invalid end line number '%s'", option_arg.str().c_str()); break; case 'r': m_avoid_regexp.clear(); m_avoid_regexp.assign(option_arg); break; case 't': m_step_in_target.clear(); m_step_in_target.assign(option_arg); break; default: error.SetErrorStringWithFormat("invalid short option character '%c'", short_option); break; } return error; } void OptionParsingStarting(ExecutionContext *execution_context) override { m_step_in_avoid_no_debug = eLazyBoolCalculate; m_step_out_avoid_no_debug = eLazyBoolCalculate; m_run_mode = eOnlyDuringStepping; // Check if we are in Non-Stop mode TargetSP target_sp = execution_context ? execution_context->GetTargetSP() : TargetSP(); if (target_sp && target_sp->GetNonStopModeEnabled()) m_run_mode = eOnlyThisThread; m_avoid_regexp.clear(); m_step_in_target.clear(); m_class_name.clear(); m_step_count = 1; m_end_line = LLDB_INVALID_LINE_NUMBER; m_end_line_is_block_end = false; } llvm::ArrayRef GetDefinitions() override { return llvm::makeArrayRef(g_thread_step_scope_options); } // Instance variables to hold the values for command options. LazyBool m_step_in_avoid_no_debug; LazyBool m_step_out_avoid_no_debug; RunMode m_run_mode; std::string m_avoid_regexp; std::string m_step_in_target; std::string m_class_name; uint32_t m_step_count; uint32_t m_end_line; bool m_end_line_is_block_end; }; CommandObjectThreadStepWithTypeAndScope(CommandInterpreter &interpreter, const char *name, const char *help, const char *syntax, StepType step_type, StepScope step_scope) : CommandObjectParsed(interpreter, name, help, syntax, eCommandRequiresProcess | eCommandRequiresThread | eCommandTryTargetAPILock | eCommandProcessMustBeLaunched | eCommandProcessMustBePaused), m_step_type(step_type), m_step_scope(step_scope), m_options() { CommandArgumentEntry arg; CommandArgumentData thread_id_arg; // Define the first (and only) variant of this arg. thread_id_arg.arg_type = eArgTypeThreadID; thread_id_arg.arg_repetition = eArgRepeatOptional; // There is only one variant this argument could be; put it into the // argument entry. arg.push_back(thread_id_arg); // Push the data for the first argument into the m_arguments vector. m_arguments.push_back(arg); } ~CommandObjectThreadStepWithTypeAndScope() override = default; Options *GetOptions() override { return &m_options; } protected: bool DoExecute(Args &command, CommandReturnObject &result) override { Process *process = m_exe_ctx.GetProcessPtr(); bool synchronous_execution = m_interpreter.GetSynchronous(); const uint32_t num_threads = process->GetThreadList().GetSize(); Thread *thread = nullptr; if (command.GetArgumentCount() == 0) { thread = GetDefaultThread(); if (thread == nullptr) { result.AppendError("no selected thread in process"); result.SetStatus(eReturnStatusFailed); return false; } } else { const char *thread_idx_cstr = command.GetArgumentAtIndex(0); uint32_t step_thread_idx = StringConvert::ToUInt32(thread_idx_cstr, LLDB_INVALID_INDEX32); if (step_thread_idx == LLDB_INVALID_INDEX32) { result.AppendErrorWithFormat("invalid thread index '%s'.\n", thread_idx_cstr); result.SetStatus(eReturnStatusFailed); return false; } thread = process->GetThreadList().FindThreadByIndexID(step_thread_idx).get(); if (thread == nullptr) { result.AppendErrorWithFormat( "Thread index %u is out of range (valid values are 0 - %u).\n", step_thread_idx, num_threads); result.SetStatus(eReturnStatusFailed); return false; } } if (m_step_type == eStepTypeScripted) { if (m_options.m_class_name.empty()) { result.AppendErrorWithFormat("empty class name for scripted step."); result.SetStatus(eReturnStatusFailed); return false; } else if (!m_interpreter.GetScriptInterpreter()->CheckObjectExists( m_options.m_class_name.c_str())) { result.AppendErrorWithFormat( "class for scripted step: \"%s\" does not exist.", m_options.m_class_name.c_str()); result.SetStatus(eReturnStatusFailed); return false; } } if (m_options.m_end_line != LLDB_INVALID_LINE_NUMBER && m_step_type != eStepTypeInto) { result.AppendErrorWithFormat( "end line option is only valid for step into"); result.SetStatus(eReturnStatusFailed); return false; } const bool abort_other_plans = false; const lldb::RunMode stop_other_threads = m_options.m_run_mode; // This is a bit unfortunate, but not all the commands in this command // object support // only while stepping, so I use the bool for them. bool bool_stop_other_threads; if (m_options.m_run_mode == eAllThreads) bool_stop_other_threads = false; else if (m_options.m_run_mode == eOnlyDuringStepping) bool_stop_other_threads = (m_step_type != eStepTypeOut && m_step_type != eStepTypeScripted); else bool_stop_other_threads = true; ThreadPlanSP new_plan_sp; if (m_step_type == eStepTypeInto) { StackFrame *frame = thread->GetStackFrameAtIndex(0).get(); assert(frame != nullptr); if (frame->HasDebugInformation()) { AddressRange range; SymbolContext sc = frame->GetSymbolContext(eSymbolContextEverything); if (m_options.m_end_line != LLDB_INVALID_LINE_NUMBER) { Status error; if (!sc.GetAddressRangeFromHereToEndLine(m_options.m_end_line, range, error)) { result.AppendErrorWithFormat("invalid end-line option: %s.", error.AsCString()); result.SetStatus(eReturnStatusFailed); return false; } } else if (m_options.m_end_line_is_block_end) { Status error; Block *block = frame->GetSymbolContext(eSymbolContextBlock).block; if (!block) { result.AppendErrorWithFormat("Could not find the current block."); result.SetStatus(eReturnStatusFailed); return false; } AddressRange block_range; Address pc_address = frame->GetFrameCodeAddress(); block->GetRangeContainingAddress(pc_address, block_range); if (!block_range.GetBaseAddress().IsValid()) { result.AppendErrorWithFormat( "Could not find the current block address."); result.SetStatus(eReturnStatusFailed); return false; } lldb::addr_t pc_offset_in_block = pc_address.GetFileAddress() - block_range.GetBaseAddress().GetFileAddress(); lldb::addr_t range_length = block_range.GetByteSize() - pc_offset_in_block; range = AddressRange(pc_address, range_length); } else { range = sc.line_entry.range; } new_plan_sp = thread->QueueThreadPlanForStepInRange( abort_other_plans, range, frame->GetSymbolContext(eSymbolContextEverything), m_options.m_step_in_target.c_str(), stop_other_threads, m_options.m_step_in_avoid_no_debug, m_options.m_step_out_avoid_no_debug); if (new_plan_sp && !m_options.m_avoid_regexp.empty()) { ThreadPlanStepInRange *step_in_range_plan = static_cast(new_plan_sp.get()); step_in_range_plan->SetAvoidRegexp(m_options.m_avoid_regexp.c_str()); } } else new_plan_sp = thread->QueueThreadPlanForStepSingleInstruction( false, abort_other_plans, bool_stop_other_threads); } else if (m_step_type == eStepTypeOver) { StackFrame *frame = thread->GetStackFrameAtIndex(0).get(); if (frame->HasDebugInformation()) new_plan_sp = thread->QueueThreadPlanForStepOverRange( abort_other_plans, frame->GetSymbolContext(eSymbolContextEverything).line_entry, frame->GetSymbolContext(eSymbolContextEverything), stop_other_threads, m_options.m_step_out_avoid_no_debug); else new_plan_sp = thread->QueueThreadPlanForStepSingleInstruction( true, abort_other_plans, bool_stop_other_threads); } else if (m_step_type == eStepTypeTrace) { new_plan_sp = thread->QueueThreadPlanForStepSingleInstruction( false, abort_other_plans, bool_stop_other_threads); } else if (m_step_type == eStepTypeTraceOver) { new_plan_sp = thread->QueueThreadPlanForStepSingleInstruction( true, abort_other_plans, bool_stop_other_threads); } else if (m_step_type == eStepTypeOut) { new_plan_sp = thread->QueueThreadPlanForStepOut( abort_other_plans, nullptr, false, bool_stop_other_threads, eVoteYes, eVoteNoOpinion, thread->GetSelectedFrameIndex(), m_options.m_step_out_avoid_no_debug); } else if (m_step_type == eStepTypeScripted) { new_plan_sp = thread->QueueThreadPlanForStepScripted( abort_other_plans, m_options.m_class_name.c_str(), bool_stop_other_threads); } else { result.AppendError("step type is not supported"); result.SetStatus(eReturnStatusFailed); return false; } // If we got a new plan, then set it to be a master plan (User level Plans // should be master plans // so that they can be interruptible). Then resume the process. if (new_plan_sp) { new_plan_sp->SetIsMasterPlan(true); new_plan_sp->SetOkayToDiscard(false); if (m_options.m_step_count > 1) { if (!new_plan_sp->SetIterationCount(m_options.m_step_count)) { result.AppendWarning( "step operation does not support iteration count."); } } process->GetThreadList().SetSelectedThreadByID(thread->GetID()); const uint32_t iohandler_id = process->GetIOHandlerID(); StreamString stream; Status error; if (synchronous_execution) error = process->ResumeSynchronous(&stream); else error = process->Resume(); // There is a race condition where this thread will return up the call // stack to the main command handler // and show an (lldb) prompt before HandlePrivateEvent (from // PrivateStateThread) has // a chance to call PushProcessIOHandler(). process->SyncIOHandler(iohandler_id, 2000); if (synchronous_execution) { // If any state changed events had anything to say, add that to the // result if (stream.GetSize() > 0) result.AppendMessage(stream.GetString()); process->GetThreadList().SetSelectedThreadByID(thread->GetID()); result.SetDidChangeProcessState(true); result.SetStatus(eReturnStatusSuccessFinishNoResult); } else { result.SetStatus(eReturnStatusSuccessContinuingNoResult); } } else { result.AppendError("Couldn't find thread plan to implement step type."); result.SetStatus(eReturnStatusFailed); } return result.Succeeded(); } protected: StepType m_step_type; StepScope m_step_scope; CommandOptions m_options; }; //------------------------------------------------------------------------- // CommandObjectThreadContinue //------------------------------------------------------------------------- class CommandObjectThreadContinue : public CommandObjectParsed { public: CommandObjectThreadContinue(CommandInterpreter &interpreter) : CommandObjectParsed( interpreter, "thread continue", "Continue execution of the current target process. One " "or more threads may be specified, by default all " "threads continue.", nullptr, eCommandRequiresThread | eCommandTryTargetAPILock | eCommandProcessMustBeLaunched | eCommandProcessMustBePaused) { CommandArgumentEntry arg; CommandArgumentData thread_idx_arg; // Define the first (and only) variant of this arg. thread_idx_arg.arg_type = eArgTypeThreadIndex; thread_idx_arg.arg_repetition = eArgRepeatPlus; // There is only one variant this argument could be; put it into the // argument entry. arg.push_back(thread_idx_arg); // Push the data for the first argument into the m_arguments vector. m_arguments.push_back(arg); } ~CommandObjectThreadContinue() override = default; bool DoExecute(Args &command, CommandReturnObject &result) override { bool synchronous_execution = m_interpreter.GetSynchronous(); if (!m_interpreter.GetDebugger().GetSelectedTarget()) { result.AppendError("invalid target, create a debug target using the " "'target create' command"); result.SetStatus(eReturnStatusFailed); return false; } Process *process = m_exe_ctx.GetProcessPtr(); if (process == nullptr) { result.AppendError("no process exists. Cannot continue"); result.SetStatus(eReturnStatusFailed); return false; } StateType state = process->GetState(); if ((state == eStateCrashed) || (state == eStateStopped) || (state == eStateSuspended)) { const size_t argc = command.GetArgumentCount(); if (argc > 0) { // These two lines appear at the beginning of both blocks in // this if..else, but that is because we need to release the // lock before calling process->Resume below. std::lock_guard guard( process->GetThreadList().GetMutex()); const uint32_t num_threads = process->GetThreadList().GetSize(); std::vector resume_threads; for (auto &entry : command.entries()) { uint32_t thread_idx; if (entry.ref.getAsInteger(0, thread_idx)) { result.AppendErrorWithFormat( "invalid thread index argument: \"%s\".\n", entry.c_str()); result.SetStatus(eReturnStatusFailed); return false; } Thread *thread = process->GetThreadList().FindThreadByIndexID(thread_idx).get(); if (thread) { resume_threads.push_back(thread); } else { result.AppendErrorWithFormat("invalid thread index %u.\n", thread_idx); result.SetStatus(eReturnStatusFailed); return false; } } if (resume_threads.empty()) { result.AppendError("no valid thread indexes were specified"); result.SetStatus(eReturnStatusFailed); return false; } else { if (resume_threads.size() == 1) result.AppendMessageWithFormat("Resuming thread: "); else result.AppendMessageWithFormat("Resuming threads: "); for (uint32_t idx = 0; idx < num_threads; ++idx) { Thread *thread = process->GetThreadList().GetThreadAtIndex(idx).get(); std::vector::iterator this_thread_pos = find(resume_threads.begin(), resume_threads.end(), thread); if (this_thread_pos != resume_threads.end()) { resume_threads.erase(this_thread_pos); if (!resume_threads.empty()) result.AppendMessageWithFormat("%u, ", thread->GetIndexID()); else result.AppendMessageWithFormat("%u ", thread->GetIndexID()); const bool override_suspend = true; thread->SetResumeState(eStateRunning, override_suspend); } else { thread->SetResumeState(eStateSuspended); } } result.AppendMessageWithFormat("in process %" PRIu64 "\n", process->GetID()); } } else { // These two lines appear at the beginning of both blocks in // this if..else, but that is because we need to release the // lock before calling process->Resume below. std::lock_guard guard( process->GetThreadList().GetMutex()); const uint32_t num_threads = process->GetThreadList().GetSize(); Thread *current_thread = GetDefaultThread(); if (current_thread == nullptr) { result.AppendError("the process doesn't have a current thread"); result.SetStatus(eReturnStatusFailed); return false; } // Set the actions that the threads should each take when resuming for (uint32_t idx = 0; idx < num_threads; ++idx) { Thread *thread = process->GetThreadList().GetThreadAtIndex(idx).get(); if (thread == current_thread) { result.AppendMessageWithFormat("Resuming thread 0x%4.4" PRIx64 " in process %" PRIu64 "\n", thread->GetID(), process->GetID()); const bool override_suspend = true; thread->SetResumeState(eStateRunning, override_suspend); } else { thread->SetResumeState(eStateSuspended); } } } StreamString stream; Status error; if (synchronous_execution) error = process->ResumeSynchronous(&stream); else error = process->Resume(); // We should not be holding the thread list lock when we do this. if (error.Success()) { result.AppendMessageWithFormat("Process %" PRIu64 " resuming\n", process->GetID()); if (synchronous_execution) { // If any state changed events had anything to say, add that to the // result if (stream.GetSize() > 0) result.AppendMessage(stream.GetString()); result.SetDidChangeProcessState(true); result.SetStatus(eReturnStatusSuccessFinishNoResult); } else { result.SetStatus(eReturnStatusSuccessContinuingNoResult); } } else { result.AppendErrorWithFormat("Failed to resume process: %s\n", error.AsCString()); result.SetStatus(eReturnStatusFailed); } } else { result.AppendErrorWithFormat( "Process cannot be continued from its current state (%s).\n", StateAsCString(state)); result.SetStatus(eReturnStatusFailed); } return result.Succeeded(); } }; //------------------------------------------------------------------------- // CommandObjectThreadUntil //------------------------------------------------------------------------- static OptionDefinition g_thread_until_options[] = { // clang-format off { LLDB_OPT_SET_1, false, "frame", 'f', OptionParser::eRequiredArgument, nullptr, nullptr, 0, eArgTypeFrameIndex, "Frame index for until operation - defaults to 0" }, { LLDB_OPT_SET_1, false, "thread", 't', OptionParser::eRequiredArgument, nullptr, nullptr, 0, eArgTypeThreadIndex, "Thread index for the thread for until operation" }, { LLDB_OPT_SET_1, false, "run-mode",'m', OptionParser::eRequiredArgument, nullptr, g_duo_running_mode, 0, eArgTypeRunMode, "Determine how to run other threads while stepping this one" }, { LLDB_OPT_SET_1, false, "address", 'a', OptionParser::eRequiredArgument, nullptr, nullptr, 0, eArgTypeAddressOrExpression, "Run until we reach the specified address, or leave the function - can be specified multiple times." } // clang-format on }; class CommandObjectThreadUntil : public CommandObjectParsed { public: class CommandOptions : public Options { public: uint32_t m_thread_idx; uint32_t m_frame_idx; CommandOptions() : Options(), m_thread_idx(LLDB_INVALID_THREAD_ID), m_frame_idx(LLDB_INVALID_FRAME_ID) { // Keep default values of all options in one place: OptionParsingStarting // () OptionParsingStarting(nullptr); } ~CommandOptions() override = default; Status SetOptionValue(uint32_t option_idx, llvm::StringRef option_arg, ExecutionContext *execution_context) override { Status error; const int short_option = m_getopt_table[option_idx].val; switch (short_option) { case 'a': { lldb::addr_t tmp_addr = Args::StringToAddress( execution_context, option_arg, LLDB_INVALID_ADDRESS, &error); if (error.Success()) m_until_addrs.push_back(tmp_addr); } break; case 't': if (option_arg.getAsInteger(0, m_thread_idx)) { m_thread_idx = LLDB_INVALID_INDEX32; error.SetErrorStringWithFormat("invalid thread index '%s'", option_arg.str().c_str()); } break; case 'f': if (option_arg.getAsInteger(0, m_frame_idx)) { m_frame_idx = LLDB_INVALID_FRAME_ID; error.SetErrorStringWithFormat("invalid frame index '%s'", option_arg.str().c_str()); } break; case 'm': { OptionEnumValueElement *enum_values = GetDefinitions()[option_idx].enum_values; lldb::RunMode run_mode = (lldb::RunMode)Args::StringToOptionEnum( option_arg, enum_values, eOnlyDuringStepping, error); if (error.Success()) { if (run_mode == eAllThreads) m_stop_others = false; else m_stop_others = true; } } break; default: error.SetErrorStringWithFormat("invalid short option character '%c'", short_option); break; } return error; } void OptionParsingStarting(ExecutionContext *execution_context) override { m_thread_idx = LLDB_INVALID_THREAD_ID; m_frame_idx = 0; m_stop_others = false; m_until_addrs.clear(); } llvm::ArrayRef GetDefinitions() override { return llvm::makeArrayRef(g_thread_until_options); } uint32_t m_step_thread_idx; bool m_stop_others; std::vector m_until_addrs; // Instance variables to hold the values for command options. }; CommandObjectThreadUntil(CommandInterpreter &interpreter) : CommandObjectParsed( interpreter, "thread until", "Continue until a line number or address is reached by the " "current or specified thread. Stops when returning from " "the current function as a safety measure. " "The target line number(s) are given as arguments, and if more than one" " is provided, stepping will stop when the first one is hit.", nullptr, eCommandRequiresThread | eCommandTryTargetAPILock | eCommandProcessMustBeLaunched | eCommandProcessMustBePaused), m_options() { CommandArgumentEntry arg; CommandArgumentData line_num_arg; // Define the first (and only) variant of this arg. line_num_arg.arg_type = eArgTypeLineNum; line_num_arg.arg_repetition = eArgRepeatPlain; // There is only one variant this argument could be; put it into the // argument entry. arg.push_back(line_num_arg); // Push the data for the first argument into the m_arguments vector. m_arguments.push_back(arg); } ~CommandObjectThreadUntil() override = default; Options *GetOptions() override { return &m_options; } protected: bool DoExecute(Args &command, CommandReturnObject &result) override { bool synchronous_execution = m_interpreter.GetSynchronous(); Target *target = m_interpreter.GetDebugger().GetSelectedTarget().get(); if (target == nullptr) { result.AppendError("invalid target, create a debug target using the " "'target create' command"); result.SetStatus(eReturnStatusFailed); return false; } Process *process = m_exe_ctx.GetProcessPtr(); if (process == nullptr) { result.AppendError("need a valid process to step"); result.SetStatus(eReturnStatusFailed); } else { Thread *thread = nullptr; std::vector line_numbers; if (command.GetArgumentCount() >= 1) { size_t num_args = command.GetArgumentCount(); for (size_t i = 0; i < num_args; i++) { uint32_t line_number; line_number = StringConvert::ToUInt32(command.GetArgumentAtIndex(i), UINT32_MAX); if (line_number == UINT32_MAX) { result.AppendErrorWithFormat("invalid line number: '%s'.\n", command.GetArgumentAtIndex(i)); result.SetStatus(eReturnStatusFailed); return false; } else line_numbers.push_back(line_number); } } else if (m_options.m_until_addrs.empty()) { result.AppendErrorWithFormat("No line number or address provided:\n%s", GetSyntax().str().c_str()); result.SetStatus(eReturnStatusFailed); return false; } if (m_options.m_thread_idx == LLDB_INVALID_THREAD_ID) { thread = GetDefaultThread(); } else { thread = process->GetThreadList() .FindThreadByIndexID(m_options.m_thread_idx) .get(); } if (thread == nullptr) { const uint32_t num_threads = process->GetThreadList().GetSize(); result.AppendErrorWithFormat( "Thread index %u is out of range (valid values are 0 - %u).\n", m_options.m_thread_idx, num_threads); result.SetStatus(eReturnStatusFailed); return false; } const bool abort_other_plans = false; StackFrame *frame = thread->GetStackFrameAtIndex(m_options.m_frame_idx).get(); if (frame == nullptr) { result.AppendErrorWithFormat( "Frame index %u is out of range for thread %u.\n", m_options.m_frame_idx, m_options.m_thread_idx); result.SetStatus(eReturnStatusFailed); return false; } ThreadPlanSP new_plan_sp; if (frame->HasDebugInformation()) { // Finally we got here... Translate the given line number to a bunch of // addresses: SymbolContext sc(frame->GetSymbolContext(eSymbolContextCompUnit)); LineTable *line_table = nullptr; if (sc.comp_unit) line_table = sc.comp_unit->GetLineTable(); if (line_table == nullptr) { result.AppendErrorWithFormat("Failed to resolve the line table for " "frame %u of thread index %u.\n", m_options.m_frame_idx, m_options.m_thread_idx); result.SetStatus(eReturnStatusFailed); return false; } LineEntry function_start; uint32_t index_ptr = 0, end_ptr; std::vector address_list; // Find the beginning & end index of the AddressRange fun_addr_range = sc.function->GetAddressRange(); Address fun_start_addr = fun_addr_range.GetBaseAddress(); line_table->FindLineEntryByAddress(fun_start_addr, function_start, &index_ptr); Address fun_end_addr(fun_start_addr.GetSection(), fun_start_addr.GetOffset() + fun_addr_range.GetByteSize()); bool all_in_function = true; line_table->FindLineEntryByAddress(fun_end_addr, function_start, &end_ptr); for (uint32_t line_number : line_numbers) { uint32_t start_idx_ptr = index_ptr; while (start_idx_ptr <= end_ptr) { LineEntry line_entry; const bool exact = false; start_idx_ptr = sc.comp_unit->FindLineEntry( start_idx_ptr, line_number, sc.comp_unit, exact, &line_entry); if (start_idx_ptr == UINT32_MAX) break; addr_t address = line_entry.range.GetBaseAddress().GetLoadAddress(target); if (address != LLDB_INVALID_ADDRESS) { if (fun_addr_range.ContainsLoadAddress(address, target)) address_list.push_back(address); else all_in_function = false; } start_idx_ptr++; } } for (lldb::addr_t address : m_options.m_until_addrs) { if (fun_addr_range.ContainsLoadAddress(address, target)) address_list.push_back(address); else all_in_function = false; } if (address_list.empty()) { if (all_in_function) result.AppendErrorWithFormat( "No line entries matching until target.\n"); else result.AppendErrorWithFormat( "Until target outside of the current function.\n"); result.SetStatus(eReturnStatusFailed); return false; } new_plan_sp = thread->QueueThreadPlanForStepUntil( abort_other_plans, &address_list.front(), address_list.size(), m_options.m_stop_others, m_options.m_frame_idx); // User level plans should be master plans so they can be interrupted // (e.g. by hitting a breakpoint) // and other plans executed by the user (stepping around the breakpoint) // and then a "continue" // will resume the original plan. new_plan_sp->SetIsMasterPlan(true); new_plan_sp->SetOkayToDiscard(false); } else { result.AppendErrorWithFormat( "Frame index %u of thread %u has no debug information.\n", m_options.m_frame_idx, m_options.m_thread_idx); result.SetStatus(eReturnStatusFailed); return false; } process->GetThreadList().SetSelectedThreadByID(m_options.m_thread_idx); StreamString stream; Status error; if (synchronous_execution) error = process->ResumeSynchronous(&stream); else error = process->Resume(); if (error.Success()) { result.AppendMessageWithFormat("Process %" PRIu64 " resuming\n", process->GetID()); if (synchronous_execution) { // If any state changed events had anything to say, add that to the // result if (stream.GetSize() > 0) result.AppendMessage(stream.GetString()); result.SetDidChangeProcessState(true); result.SetStatus(eReturnStatusSuccessFinishNoResult); } else { result.SetStatus(eReturnStatusSuccessContinuingNoResult); } } else { result.AppendErrorWithFormat("Failed to resume process: %s.\n", error.AsCString()); result.SetStatus(eReturnStatusFailed); } } return result.Succeeded(); } CommandOptions m_options; }; //------------------------------------------------------------------------- // CommandObjectThreadSelect //------------------------------------------------------------------------- class CommandObjectThreadSelect : public CommandObjectParsed { public: CommandObjectThreadSelect(CommandInterpreter &interpreter) : CommandObjectParsed(interpreter, "thread select", "Change the currently selected thread.", nullptr, eCommandRequiresProcess | eCommandTryTargetAPILock | eCommandProcessMustBeLaunched | eCommandProcessMustBePaused) { CommandArgumentEntry arg; CommandArgumentData thread_idx_arg; // Define the first (and only) variant of this arg. thread_idx_arg.arg_type = eArgTypeThreadIndex; thread_idx_arg.arg_repetition = eArgRepeatPlain; // There is only one variant this argument could be; put it into the // argument entry. arg.push_back(thread_idx_arg); // Push the data for the first argument into the m_arguments vector. m_arguments.push_back(arg); } ~CommandObjectThreadSelect() override = default; protected: bool DoExecute(Args &command, CommandReturnObject &result) override { Process *process = m_exe_ctx.GetProcessPtr(); if (process == nullptr) { result.AppendError("no process"); result.SetStatus(eReturnStatusFailed); return false; } else if (command.GetArgumentCount() != 1) { result.AppendErrorWithFormat( "'%s' takes exactly one thread index argument:\nUsage: %s\n", m_cmd_name.c_str(), m_cmd_syntax.c_str()); result.SetStatus(eReturnStatusFailed); return false; } uint32_t index_id = StringConvert::ToUInt32(command.GetArgumentAtIndex(0), 0, 0); Thread *new_thread = process->GetThreadList().FindThreadByIndexID(index_id).get(); if (new_thread == nullptr) { result.AppendErrorWithFormat("invalid thread #%s.\n", command.GetArgumentAtIndex(0)); result.SetStatus(eReturnStatusFailed); return false; } process->GetThreadList().SetSelectedThreadByID(new_thread->GetID(), true); result.SetStatus(eReturnStatusSuccessFinishNoResult); return result.Succeeded(); } }; //------------------------------------------------------------------------- // CommandObjectThreadList //------------------------------------------------------------------------- class CommandObjectThreadList : public CommandObjectParsed { public: CommandObjectThreadList(CommandInterpreter &interpreter) : CommandObjectParsed( interpreter, "thread list", "Show a summary of each thread in the current target process.", "thread list", eCommandRequiresProcess | eCommandTryTargetAPILock | eCommandProcessMustBeLaunched | eCommandProcessMustBePaused) {} ~CommandObjectThreadList() override = default; protected: bool DoExecute(Args &command, CommandReturnObject &result) override { Stream &strm = result.GetOutputStream(); result.SetStatus(eReturnStatusSuccessFinishNoResult); Process *process = m_exe_ctx.GetProcessPtr(); const bool only_threads_with_stop_reason = false; const uint32_t start_frame = 0; const uint32_t num_frames = 0; const uint32_t num_frames_with_source = 0; process->GetStatus(strm); process->GetThreadStatus(strm, only_threads_with_stop_reason, start_frame, num_frames, num_frames_with_source, false); return result.Succeeded(); } }; //------------------------------------------------------------------------- // CommandObjectThreadInfo //------------------------------------------------------------------------- static OptionDefinition g_thread_info_options[] = { // clang-format off { LLDB_OPT_SET_ALL, false, "json", 'j', OptionParser::eNoArgument, nullptr, nullptr, 0, eArgTypeNone, "Display the thread info in JSON format." }, { LLDB_OPT_SET_ALL, false, "stop-info", 's', OptionParser::eNoArgument, nullptr, nullptr, 0, eArgTypeNone, "Display the extended stop info in JSON format." } // clang-format on }; class CommandObjectThreadInfo : public CommandObjectIterateOverThreads { public: class CommandOptions : public Options { public: CommandOptions() : Options() { OptionParsingStarting(nullptr); } ~CommandOptions() override = default; void OptionParsingStarting(ExecutionContext *execution_context) override { m_json_thread = false; m_json_stopinfo = false; } Status SetOptionValue(uint32_t option_idx, llvm::StringRef option_arg, ExecutionContext *execution_context) override { const int short_option = m_getopt_table[option_idx].val; Status error; switch (short_option) { case 'j': m_json_thread = true; break; case 's': m_json_stopinfo = true; break; default: return Status("invalid short option character '%c'", short_option); } return error; } llvm::ArrayRef GetDefinitions() override { return llvm::makeArrayRef(g_thread_info_options); } bool m_json_thread; bool m_json_stopinfo; }; CommandObjectThreadInfo(CommandInterpreter &interpreter) : CommandObjectIterateOverThreads( interpreter, "thread info", "Show an extended summary of one or " "more threads. Defaults to the " "current thread.", "thread info", eCommandRequiresProcess | eCommandTryTargetAPILock | eCommandProcessMustBeLaunched | eCommandProcessMustBePaused), m_options() { m_add_return = false; } ~CommandObjectThreadInfo() override = default; Options *GetOptions() override { return &m_options; } bool HandleOneThread(lldb::tid_t tid, CommandReturnObject &result) override { ThreadSP thread_sp = m_exe_ctx.GetProcessPtr()->GetThreadList().FindThreadByID(tid); if (!thread_sp) { result.AppendErrorWithFormat("thread no longer exists: 0x%" PRIx64 "\n", tid); result.SetStatus(eReturnStatusFailed); return false; } Thread *thread = thread_sp.get(); Stream &strm = result.GetOutputStream(); if (!thread->GetDescription(strm, eDescriptionLevelFull, m_options.m_json_thread, m_options.m_json_stopinfo)) { result.AppendErrorWithFormat("error displaying info for thread: \"%d\"\n", thread->GetIndexID()); result.SetStatus(eReturnStatusFailed); return false; } return true; } CommandOptions m_options; }; //------------------------------------------------------------------------- // CommandObjectThreadReturn //------------------------------------------------------------------------- static OptionDefinition g_thread_return_options[] = { // clang-format off { LLDB_OPT_SET_ALL, false, "from-expression", 'x', OptionParser::eNoArgument, nullptr, nullptr, 0, eArgTypeNone, "Return from the innermost expression evaluation." } // clang-format on }; class CommandObjectThreadReturn : public CommandObjectRaw { public: class CommandOptions : public Options { public: CommandOptions() : Options(), m_from_expression(false) { // Keep default values of all options in one place: OptionParsingStarting // () OptionParsingStarting(nullptr); } ~CommandOptions() override = default; Status SetOptionValue(uint32_t option_idx, llvm::StringRef option_arg, ExecutionContext *execution_context) override { Status error; const int short_option = m_getopt_table[option_idx].val; switch (short_option) { case 'x': { bool success; bool tmp_value = Args::StringToBoolean(option_arg, false, &success); if (success) m_from_expression = tmp_value; else { error.SetErrorStringWithFormat( "invalid boolean value '%s' for 'x' option", option_arg.str().c_str()); } } break; default: error.SetErrorStringWithFormat("invalid short option character '%c'", short_option); break; } return error; } void OptionParsingStarting(ExecutionContext *execution_context) override { m_from_expression = false; } llvm::ArrayRef GetDefinitions() override { return llvm::makeArrayRef(g_thread_return_options); } bool m_from_expression; // Instance variables to hold the values for command options. }; CommandObjectThreadReturn(CommandInterpreter &interpreter) : CommandObjectRaw(interpreter, "thread return", "Prematurely return from a stack frame, " "short-circuiting execution of newer frames " "and optionally yielding a specified value. Defaults " "to the exiting the current stack " "frame.", "thread return", eCommandRequiresFrame | eCommandTryTargetAPILock | eCommandProcessMustBeLaunched | eCommandProcessMustBePaused), m_options() { CommandArgumentEntry arg; CommandArgumentData expression_arg; // Define the first (and only) variant of this arg. expression_arg.arg_type = eArgTypeExpression; expression_arg.arg_repetition = eArgRepeatOptional; // There is only one variant this argument could be; put it into the // argument entry. arg.push_back(expression_arg); // Push the data for the first argument into the m_arguments vector. m_arguments.push_back(arg); } ~CommandObjectThreadReturn() override = default; Options *GetOptions() override { return &m_options; } protected: bool DoExecute(const char *command, CommandReturnObject &result) override { // I am going to handle this by hand, because I don't want you to have to // say: // "thread return -- -5". if (command[0] == '-' && command[1] == 'x') { if (command && command[2] != '\0') result.AppendWarning("Return values ignored when returning from user " "called expressions"); Thread *thread = m_exe_ctx.GetThreadPtr(); Status error; error = thread->UnwindInnermostExpression(); if (!error.Success()) { result.AppendErrorWithFormat("Unwinding expression failed - %s.", error.AsCString()); result.SetStatus(eReturnStatusFailed); } else { bool success = thread->SetSelectedFrameByIndexNoisily(0, result.GetOutputStream()); if (success) { m_exe_ctx.SetFrameSP(thread->GetSelectedFrame()); result.SetStatus(eReturnStatusSuccessFinishResult); } else { result.AppendErrorWithFormat( "Could not select 0th frame after unwinding expression."); result.SetStatus(eReturnStatusFailed); } } return result.Succeeded(); } ValueObjectSP return_valobj_sp; StackFrameSP frame_sp = m_exe_ctx.GetFrameSP(); uint32_t frame_idx = frame_sp->GetFrameIndex(); if (frame_sp->IsInlined()) { result.AppendError("Don't know how to return from inlined frames."); result.SetStatus(eReturnStatusFailed); return false; } if (command && command[0] != '\0') { Target *target = m_exe_ctx.GetTargetPtr(); EvaluateExpressionOptions options; options.SetUnwindOnError(true); options.SetUseDynamic(eNoDynamicValues); ExpressionResults exe_results = eExpressionSetupError; exe_results = target->EvaluateExpression(command, frame_sp.get(), return_valobj_sp, options); if (exe_results != eExpressionCompleted) { if (return_valobj_sp) result.AppendErrorWithFormat( "Error evaluating result expression: %s", return_valobj_sp->GetError().AsCString()); else result.AppendErrorWithFormat( "Unknown error evaluating result expression."); result.SetStatus(eReturnStatusFailed); return false; } } Status error; ThreadSP thread_sp = m_exe_ctx.GetThreadSP(); const bool broadcast = true; error = thread_sp->ReturnFromFrame(frame_sp, return_valobj_sp, broadcast); if (!error.Success()) { result.AppendErrorWithFormat( "Error returning from frame %d of thread %d: %s.", frame_idx, thread_sp->GetIndexID(), error.AsCString()); result.SetStatus(eReturnStatusFailed); return false; } result.SetStatus(eReturnStatusSuccessFinishResult); return true; } CommandOptions m_options; }; //------------------------------------------------------------------------- // CommandObjectThreadJump //------------------------------------------------------------------------- static OptionDefinition g_thread_jump_options[] = { // clang-format off { LLDB_OPT_SET_1, false, "file", 'f', OptionParser::eRequiredArgument, nullptr, nullptr, CommandCompletions::eSourceFileCompletion, eArgTypeFilename, "Specifies the source file to jump to." }, { LLDB_OPT_SET_1, true, "line", 'l', OptionParser::eRequiredArgument, nullptr, nullptr, 0, eArgTypeLineNum, "Specifies the line number to jump to." }, { LLDB_OPT_SET_2, true, "by", 'b', OptionParser::eRequiredArgument, nullptr, nullptr, 0, eArgTypeOffset, "Jumps by a relative line offset from the current line." }, { LLDB_OPT_SET_3, true, "address", 'a', OptionParser::eRequiredArgument, nullptr, nullptr, 0, eArgTypeAddressOrExpression, "Jumps to a specific address." }, { LLDB_OPT_SET_1 | LLDB_OPT_SET_2 | LLDB_OPT_SET_3, false, "force", 'r', OptionParser::eNoArgument, nullptr, nullptr, 0, eArgTypeNone, "Allows the PC to leave the current function." } // clang-format on }; class CommandObjectThreadJump : public CommandObjectParsed { public: class CommandOptions : public Options { public: CommandOptions() : Options() { OptionParsingStarting(nullptr); } ~CommandOptions() override = default; void OptionParsingStarting(ExecutionContext *execution_context) override { m_filenames.Clear(); m_line_num = 0; m_line_offset = 0; m_load_addr = LLDB_INVALID_ADDRESS; m_force = false; } Status SetOptionValue(uint32_t option_idx, llvm::StringRef option_arg, ExecutionContext *execution_context) override { const int short_option = m_getopt_table[option_idx].val; Status error; switch (short_option) { case 'f': m_filenames.AppendIfUnique(FileSpec(option_arg, false)); if (m_filenames.GetSize() > 1) return Status("only one source file expected."); break; case 'l': if (option_arg.getAsInteger(0, m_line_num)) return Status("invalid line number: '%s'.", option_arg.str().c_str()); break; case 'b': if (option_arg.getAsInteger(0, m_line_offset)) return Status("invalid line offset: '%s'.", option_arg.str().c_str()); break; case 'a': m_load_addr = Args::StringToAddress(execution_context, option_arg, LLDB_INVALID_ADDRESS, &error); break; case 'r': m_force = true; break; default: return Status("invalid short option character '%c'", short_option); } return error; } llvm::ArrayRef GetDefinitions() override { return llvm::makeArrayRef(g_thread_jump_options); } FileSpecList m_filenames; uint32_t m_line_num; int32_t m_line_offset; lldb::addr_t m_load_addr; bool m_force; }; CommandObjectThreadJump(CommandInterpreter &interpreter) : CommandObjectParsed( interpreter, "thread jump", "Sets the program counter to a new address.", "thread jump", eCommandRequiresFrame | eCommandTryTargetAPILock | eCommandProcessMustBeLaunched | eCommandProcessMustBePaused), m_options() {} ~CommandObjectThreadJump() override = default; Options *GetOptions() override { return &m_options; } protected: bool DoExecute(Args &args, CommandReturnObject &result) override { RegisterContext *reg_ctx = m_exe_ctx.GetRegisterContext(); StackFrame *frame = m_exe_ctx.GetFramePtr(); Thread *thread = m_exe_ctx.GetThreadPtr(); Target *target = m_exe_ctx.GetTargetPtr(); const SymbolContext &sym_ctx = frame->GetSymbolContext(eSymbolContextLineEntry); if (m_options.m_load_addr != LLDB_INVALID_ADDRESS) { // Use this address directly. Address dest = Address(m_options.m_load_addr); lldb::addr_t callAddr = dest.GetCallableLoadAddress(target); if (callAddr == LLDB_INVALID_ADDRESS) { result.AppendErrorWithFormat("Invalid destination address."); result.SetStatus(eReturnStatusFailed); return false; } if (!reg_ctx->SetPC(callAddr)) { result.AppendErrorWithFormat("Error changing PC value for thread %d.", thread->GetIndexID()); result.SetStatus(eReturnStatusFailed); return false; } } else { // Pick either the absolute line, or work out a relative one. int32_t line = (int32_t)m_options.m_line_num; if (line == 0) line = sym_ctx.line_entry.line + m_options.m_line_offset; // Try the current file, but override if asked. FileSpec file = sym_ctx.line_entry.file; if (m_options.m_filenames.GetSize() == 1) file = m_options.m_filenames.GetFileSpecAtIndex(0); if (!file) { result.AppendErrorWithFormat( "No source file available for the current location."); result.SetStatus(eReturnStatusFailed); return false; } std::string warnings; Status err = thread->JumpToLine(file, line, m_options.m_force, &warnings); if (err.Fail()) { result.SetError(err); return false; } if (!warnings.empty()) result.AppendWarning(warnings.c_str()); } result.SetStatus(eReturnStatusSuccessFinishResult); return true; } CommandOptions m_options; }; //------------------------------------------------------------------------- // Next are the subcommands of CommandObjectMultiwordThreadPlan //------------------------------------------------------------------------- //------------------------------------------------------------------------- // CommandObjectThreadPlanList //------------------------------------------------------------------------- static OptionDefinition g_thread_plan_list_options[] = { // clang-format off { LLDB_OPT_SET_1, false, "verbose", 'v', OptionParser::eNoArgument, nullptr, nullptr, 0, eArgTypeNone, "Display more information about the thread plans" }, { LLDB_OPT_SET_1, false, "internal", 'i', OptionParser::eNoArgument, nullptr, nullptr, 0, eArgTypeNone, "Display internal as well as user thread plans" } // clang-format on }; class CommandObjectThreadPlanList : public CommandObjectIterateOverThreads { public: class CommandOptions : public Options { public: CommandOptions() : Options() { // Keep default values of all options in one place: OptionParsingStarting // () OptionParsingStarting(nullptr); } ~CommandOptions() override = default; Status SetOptionValue(uint32_t option_idx, llvm::StringRef option_arg, ExecutionContext *execution_context) override { Status error; const int short_option = m_getopt_table[option_idx].val; switch (short_option) { case 'i': m_internal = true; break; case 'v': m_verbose = true; break; default: error.SetErrorStringWithFormat("invalid short option character '%c'", short_option); break; } return error; } void OptionParsingStarting(ExecutionContext *execution_context) override { m_verbose = false; m_internal = false; } llvm::ArrayRef GetDefinitions() override { return llvm::makeArrayRef(g_thread_plan_list_options); } // Instance variables to hold the values for command options. bool m_verbose; bool m_internal; }; CommandObjectThreadPlanList(CommandInterpreter &interpreter) : CommandObjectIterateOverThreads( interpreter, "thread plan list", "Show thread plans for one or more threads. If no threads are " "specified, show the " "current thread. Use the thread-index \"all\" to see all threads.", nullptr, eCommandRequiresProcess | eCommandRequiresThread | eCommandTryTargetAPILock | eCommandProcessMustBeLaunched | eCommandProcessMustBePaused), m_options() {} ~CommandObjectThreadPlanList() override = default; Options *GetOptions() override { return &m_options; } protected: bool HandleOneThread(lldb::tid_t tid, CommandReturnObject &result) override { ThreadSP thread_sp = m_exe_ctx.GetProcessPtr()->GetThreadList().FindThreadByID(tid); if (!thread_sp) { result.AppendErrorWithFormat("thread no longer exists: 0x%" PRIx64 "\n", tid); result.SetStatus(eReturnStatusFailed); return false; } Thread *thread = thread_sp.get(); Stream &strm = result.GetOutputStream(); DescriptionLevel desc_level = eDescriptionLevelFull; if (m_options.m_verbose) desc_level = eDescriptionLevelVerbose; thread->DumpThreadPlans(&strm, desc_level, m_options.m_internal, true); return true; } CommandOptions m_options; }; class CommandObjectThreadPlanDiscard : public CommandObjectParsed { public: CommandObjectThreadPlanDiscard(CommandInterpreter &interpreter) : CommandObjectParsed(interpreter, "thread plan discard", "Discards thread plans up to and including the " "specified index (see 'thread plan list'.) " "Only user visible plans can be discarded.", nullptr, eCommandRequiresProcess | eCommandRequiresThread | eCommandTryTargetAPILock | eCommandProcessMustBeLaunched | eCommandProcessMustBePaused) { CommandArgumentEntry arg; CommandArgumentData plan_index_arg; // Define the first (and only) variant of this arg. plan_index_arg.arg_type = eArgTypeUnsignedInteger; plan_index_arg.arg_repetition = eArgRepeatPlain; // There is only one variant this argument could be; put it into the // argument entry. arg.push_back(plan_index_arg); // Push the data for the first argument into the m_arguments vector. m_arguments.push_back(arg); } ~CommandObjectThreadPlanDiscard() override = default; bool DoExecute(Args &args, CommandReturnObject &result) override { Thread *thread = m_exe_ctx.GetThreadPtr(); if (args.GetArgumentCount() != 1) { result.AppendErrorWithFormat("Too many arguments, expected one - the " "thread plan index - but got %zu.", args.GetArgumentCount()); result.SetStatus(eReturnStatusFailed); return false; } bool success; uint32_t thread_plan_idx = StringConvert::ToUInt32(args.GetArgumentAtIndex(0), 0, 0, &success); if (!success) { result.AppendErrorWithFormat( "Invalid thread index: \"%s\" - should be unsigned int.", args.GetArgumentAtIndex(0)); result.SetStatus(eReturnStatusFailed); return false; } if (thread_plan_idx == 0) { result.AppendErrorWithFormat( "You wouldn't really want me to discard the base thread plan."); result.SetStatus(eReturnStatusFailed); return false; } if (thread->DiscardUserThreadPlansUpToIndex(thread_plan_idx)) { result.SetStatus(eReturnStatusSuccessFinishNoResult); return true; } else { result.AppendErrorWithFormat( "Could not find User thread plan with index %s.", args.GetArgumentAtIndex(0)); result.SetStatus(eReturnStatusFailed); return false; } } }; //------------------------------------------------------------------------- // CommandObjectMultiwordThreadPlan //------------------------------------------------------------------------- class CommandObjectMultiwordThreadPlan : public CommandObjectMultiword { public: CommandObjectMultiwordThreadPlan(CommandInterpreter &interpreter) : CommandObjectMultiword( interpreter, "plan", "Commands for managing thread plans that control execution.", "thread plan [ []") { LoadSubCommand("backtrace", CommandObjectSP(new CommandObjectThreadBacktrace( interpreter))); LoadSubCommand("continue", CommandObjectSP(new CommandObjectThreadContinue(interpreter))); LoadSubCommand("list", CommandObjectSP(new CommandObjectThreadList(interpreter))); LoadSubCommand("return", CommandObjectSP(new CommandObjectThreadReturn(interpreter))); LoadSubCommand("jump", CommandObjectSP(new CommandObjectThreadJump(interpreter))); LoadSubCommand("select", CommandObjectSP(new CommandObjectThreadSelect(interpreter))); LoadSubCommand("until", CommandObjectSP(new CommandObjectThreadUntil(interpreter))); LoadSubCommand("info", CommandObjectSP(new CommandObjectThreadInfo(interpreter))); LoadSubCommand("step-in", CommandObjectSP(new CommandObjectThreadStepWithTypeAndScope( interpreter, "thread step-in", "Source level single step, stepping into calls. Defaults " "to current thread unless specified.", nullptr, eStepTypeInto, eStepScopeSource))); LoadSubCommand("step-out", CommandObjectSP(new CommandObjectThreadStepWithTypeAndScope( interpreter, "thread step-out", "Finish executing the current stack frame and stop after " "returning. Defaults to current thread unless specified.", nullptr, eStepTypeOut, eStepScopeSource))); LoadSubCommand("step-over", CommandObjectSP(new CommandObjectThreadStepWithTypeAndScope( interpreter, "thread step-over", "Source level single step, stepping over calls. Defaults " "to current thread unless specified.", nullptr, eStepTypeOver, eStepScopeSource))); LoadSubCommand("step-inst", CommandObjectSP(new CommandObjectThreadStepWithTypeAndScope( interpreter, "thread step-inst", "Instruction level single step, stepping into calls. " "Defaults to current thread unless specified.", nullptr, eStepTypeTrace, eStepScopeInstruction))); LoadSubCommand("step-inst-over", CommandObjectSP(new CommandObjectThreadStepWithTypeAndScope( interpreter, "thread step-inst-over", "Instruction level single step, stepping over calls. " "Defaults to current thread unless specified.", nullptr, eStepTypeTraceOver, eStepScopeInstruction))); LoadSubCommand( "step-scripted", CommandObjectSP(new CommandObjectThreadStepWithTypeAndScope( interpreter, "thread step-scripted", "Step as instructed by the script class passed in the -C option.", nullptr, eStepTypeScripted, eStepScopeSource))); LoadSubCommand("plan", CommandObjectSP(new CommandObjectMultiwordThreadPlan( interpreter))); } CommandObjectMultiwordThread::~CommandObjectMultiwordThread() = default; Index: vendor/lldb/dist/source/Core/Debugger.cpp =================================================================== --- vendor/lldb/dist/source/Core/Debugger.cpp (revision 320022) +++ vendor/lldb/dist/source/Core/Debugger.cpp (revision 320023) @@ -1,1747 +1,1776 @@ //===-- Debugger.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/Core/Debugger.h" #include "lldb/Breakpoint/Breakpoint.h" // for Breakpoint, Brea... #include "lldb/Core/Event.h" // for Event, EventData... #include "lldb/Core/FormatEntity.h" #include "lldb/Core/Listener.h" // for Listener #include "lldb/Core/Mangled.h" // for Mangled #include "lldb/Core/PluginManager.h" #include "lldb/Core/State.h" #include "lldb/Core/StreamAsynchronousIO.h" #include "lldb/Core/StreamFile.h" #include "lldb/DataFormatters/DataVisualization.h" #include "lldb/Expression/REPL.h" #include "lldb/Host/File.h" // for File, File::kInv... #include "lldb/Host/HostInfo.h" #include "lldb/Host/Terminal.h" #include "lldb/Host/ThreadLauncher.h" #include "lldb/Interpreter/CommandInterpreter.h" #include "lldb/Interpreter/OptionValue.h" // for OptionValue, Opt... #include "lldb/Interpreter/OptionValueProperties.h" #include "lldb/Interpreter/OptionValueSInt64.h" #include "lldb/Interpreter/OptionValueString.h" #include "lldb/Interpreter/Property.h" // for PropertyDefinition #include "lldb/Interpreter/ScriptInterpreter.h" // for ScriptInterpreter #include "lldb/Symbol/Function.h" #include "lldb/Symbol/Symbol.h" #include "lldb/Symbol/SymbolContext.h" // for SymbolContext #include "lldb/Target/Language.h" #include "lldb/Target/Process.h" #include "lldb/Target/StructuredDataPlugin.h" #include "lldb/Target/Target.h" #include "lldb/Target/TargetList.h" #include "lldb/Target/Thread.h" #include "lldb/Target/ThreadList.h" // for ThreadList #include "lldb/Utility/AnsiTerminal.h" #include "lldb/Utility/Log.h" // for LLDB_LOG_OPTION_... #include "lldb/Utility/Stream.h" // for Stream #include "lldb/Utility/StreamCallback.h" #include "lldb/Utility/StreamString.h" #if defined(LLVM_ON_WIN32) #include "lldb/Host/windows/PosixApi.h" // for PATH_MAX #endif #include "llvm/ADT/None.h" // for None #include "llvm/ADT/STLExtras.h" // for make_unique #include "llvm/ADT/StringRef.h" #include "llvm/ADT/iterator.h" // for iterator_facade_... #include "llvm/Support/DynamicLibrary.h" #include "llvm/Support/FileSystem.h" #include "llvm/Support/Threading.h" #include "llvm/Support/raw_ostream.h" // for raw_fd_ostream #include // for list #include // for make_shared #include #include // for set #include // for size_t, NULL #include // for getenv #include // for strcmp #include // for string #include // for error_code namespace lldb_private { class Address; } using namespace lldb; using namespace lldb_private; static lldb::user_id_t g_unique_id = 1; static size_t g_debugger_event_thread_stack_bytes = 8 * 1024 * 1024; #pragma mark Static Functions typedef std::vector DebuggerList; static std::recursive_mutex *g_debugger_list_mutex_ptr = nullptr; // NOTE: intentional leak to avoid issues with C++ destructor chain static DebuggerList *g_debugger_list_ptr = nullptr; // NOTE: intentional leak to avoid issues with C++ destructor chain OptionEnumValueElement g_show_disassembly_enum_values[] = { {Debugger::eStopDisassemblyTypeNever, "never", "Never show disassembly when displaying a stop context."}, {Debugger::eStopDisassemblyTypeNoDebugInfo, "no-debuginfo", "Show disassembly when there is no debug information."}, {Debugger::eStopDisassemblyTypeNoSource, "no-source", "Show disassembly when there is no source information, or the source file " "is missing when displaying a stop context."}, {Debugger::eStopDisassemblyTypeAlways, "always", "Always show disassembly when displaying a stop context."}, {0, nullptr, nullptr}}; OptionEnumValueElement g_language_enumerators[] = { {eScriptLanguageNone, "none", "Disable scripting languages."}, {eScriptLanguagePython, "python", "Select python as the default scripting language."}, {eScriptLanguageDefault, "default", "Select the lldb default as the default scripting language."}, {0, nullptr, nullptr}}; #define MODULE_WITH_FUNC \ "{ " \ "${module.file.basename}{`${function.name-with-args}" \ "{${frame.no-debug}${function.pc-offset}}}}" + +#define MODULE_WITH_FUNC_NO_ARGS \ + "{ " \ + "${module.file.basename}{`${function.name-without-args}" \ + "{${frame.no-debug}${function.pc-offset}}}}" + #define FILE_AND_LINE "{ at ${line.file.basename}:${line.number}}" #define IS_OPTIMIZED "{${function.is-optimized} [opt]}" #define DEFAULT_THREAD_FORMAT \ "thread #${thread.index}: tid = ${thread.id%tid}" \ "{, ${frame.pc}}" MODULE_WITH_FUNC FILE_AND_LINE \ "{, name = '${thread.name}'}" \ "{, queue = '${thread.queue}'}" \ "{, activity = '${thread.info.activity.name}'}" \ "{, ${thread.info.trace_messages} messages}" \ "{, stop reason = ${thread.stop-reason}}" \ "{\\nReturn value: ${thread.return-value}}" \ "{\\nCompleted expression: ${thread.completed-expression}}" \ "\\n" #define DEFAULT_THREAD_STOP_FORMAT \ "thread #${thread.index}{, name = '${thread.name}'}" \ "{, queue = '${thread.queue}'}" \ "{, activity = '${thread.info.activity.name}'}" \ "{, ${thread.info.trace_messages} messages}" \ "{, stop reason = ${thread.stop-reason}}" \ "{\\nReturn value: ${thread.return-value}}" \ "{\\nCompleted expression: ${thread.completed-expression}}" \ "\\n" #define DEFAULT_FRAME_FORMAT \ "frame #${frame.index}: ${frame.pc}" MODULE_WITH_FUNC FILE_AND_LINE \ IS_OPTIMIZED "\\n" +#define DEFAULT_FRAME_FORMAT_NO_ARGS \ + "frame #${frame.index}: ${frame.pc}" MODULE_WITH_FUNC_NO_ARGS FILE_AND_LINE \ + IS_OPTIMIZED "\\n" + // Three parts to this disassembly format specification: // 1. If this is a new function/symbol (no previous symbol/function), print // dylib`funcname:\n // 2. If this is a symbol context change (different from previous // symbol/function), print // dylib`funcname:\n // 3. print // address <+offset>: #define DEFAULT_DISASSEMBLY_FORMAT \ "{${function.initial-function}{${module.file.basename}`}{${function.name-" \ "without-args}}:\n}{${function.changed}\n{${module.file.basename}`}{${" \ "function.name-without-args}}:\n}{${current-pc-arrow} " \ "}${addr-file-or-load}{ " \ "<${function.concrete-only-addr-offset-no-padding}>}: " // gdb's disassembly format can be emulated with // ${current-pc-arrow}${addr-file-or-load}{ // <${function.name-without-args}${function.concrete-only-addr-offset-no-padding}>}: // lldb's original format for disassembly would look like this format string - // {${function.initial-function}{${module.file.basename}`}{${function.name-without-args}}:\n}{${function.changed}\n{${module.file.basename}`}{${function.name-without-args}}:\n}{${current-pc-arrow} // }{${addr-file-or-load}}: #define DEFAULT_STOP_SHOW_COLUMN_ANSI_PREFIX "${ansi.underline}" #define DEFAULT_STOP_SHOW_COLUMN_ANSI_SUFFIX "${ansi.normal}" static OptionEnumValueElement s_stop_show_column_values[] = { {eStopShowColumnAnsiOrCaret, "ansi-or-caret", "Highlight the stop column with ANSI terminal codes when color/ANSI mode " "is enabled; otherwise, fall back to using a text-only caret (^) as if " "\"caret-only\" mode was selected."}, {eStopShowColumnAnsi, "ansi", "Highlight the stop column with ANSI " "terminal codes when running LLDB with " "color/ANSI enabled."}, {eStopShowColumnCaret, "caret", "Highlight the stop column with a caret character (^) underneath the stop " "column. This method introduces a new line in source listings that " "display thread stop locations."}, {eStopShowColumnNone, "none", "Do not highlight the stop column."}, {0, nullptr, nullptr}}; static PropertyDefinition g_properties[] = { {"auto-confirm", OptionValue::eTypeBoolean, true, false, nullptr, nullptr, "If true all confirmation prompts will receive their default reply."}, {"disassembly-format", OptionValue::eTypeFormatEntity, true, 0, - DEFAULT_DISASSEMBLY_FORMAT, nullptr, "The default disassembly format " - "string to use when disassembling " - "instruction sequences."}, + DEFAULT_DISASSEMBLY_FORMAT, nullptr, + "The default disassembly format " + "string to use when disassembling " + "instruction sequences."}, {"frame-format", OptionValue::eTypeFormatEntity, true, 0, - DEFAULT_FRAME_FORMAT, nullptr, "The default frame format string to use " - "when displaying stack frame information " - "for threads."}, + DEFAULT_FRAME_FORMAT, nullptr, + "The default frame format string to use " + "when displaying stack frame information " + "for threads."}, {"notify-void", OptionValue::eTypeBoolean, true, false, nullptr, nullptr, "Notify the user explicitly if an expression returns void (default: " "false)."}, {"prompt", OptionValue::eTypeString, true, OptionValueString::eOptionEncodeCharacterEscapeSequences, "(lldb) ", nullptr, "The debugger command line prompt displayed for the user."}, {"script-lang", OptionValue::eTypeEnum, true, eScriptLanguagePython, nullptr, g_language_enumerators, "The script language to be used for evaluating user-written scripts."}, {"stop-disassembly-count", OptionValue::eTypeSInt64, true, 4, nullptr, - nullptr, "The number of disassembly lines to show when displaying a " - "stopped context."}, + nullptr, + "The number of disassembly lines to show when displaying a " + "stopped context."}, {"stop-disassembly-display", OptionValue::eTypeEnum, true, Debugger::eStopDisassemblyTypeNoDebugInfo, nullptr, g_show_disassembly_enum_values, "Control when to display disassembly when displaying a stopped context."}, {"stop-line-count-after", OptionValue::eTypeSInt64, true, 3, nullptr, - nullptr, "The number of sources lines to display that come after the " - "current source line when displaying a stopped context."}, + nullptr, + "The number of sources lines to display that come after the " + "current source line when displaying a stopped context."}, {"stop-line-count-before", OptionValue::eTypeSInt64, true, 3, nullptr, - nullptr, "The number of sources lines to display that come before the " - "current source line when displaying a stopped context."}, + nullptr, + "The number of sources lines to display that come before the " + "current source line when displaying a stopped context."}, {"stop-show-column", OptionValue::eTypeEnum, false, eStopShowColumnAnsiOrCaret, nullptr, s_stop_show_column_values, "If true, LLDB will use the column information from the debug info to " "mark the current position when displaying a stopped context."}, {"stop-show-column-ansi-prefix", OptionValue::eTypeFormatEntity, true, 0, DEFAULT_STOP_SHOW_COLUMN_ANSI_PREFIX, nullptr, "When displaying the column marker in a color-enabled (i.e. ANSI) " "terminal, use the ANSI terminal code specified in this format at the " "immediately before the column to be marked."}, {"stop-show-column-ansi-suffix", OptionValue::eTypeFormatEntity, true, 0, DEFAULT_STOP_SHOW_COLUMN_ANSI_SUFFIX, nullptr, "When displaying the column marker in a color-enabled (i.e. ANSI) " "terminal, use the ANSI terminal code specified in this format " "immediately after the column to be marked."}, {"term-width", OptionValue::eTypeSInt64, true, 80, nullptr, nullptr, "The maximum number of columns to use for displaying text."}, {"thread-format", OptionValue::eTypeFormatEntity, true, 0, - DEFAULT_THREAD_FORMAT, nullptr, "The default thread format string to use " - "when displaying thread information."}, + DEFAULT_THREAD_FORMAT, nullptr, + "The default thread format string to use " + "when displaying thread information."}, {"thread-stop-format", OptionValue::eTypeFormatEntity, true, 0, - DEFAULT_THREAD_STOP_FORMAT, nullptr, "The default thread format " - "string to usewhen displaying thread " - "information as part of the stop display."}, + DEFAULT_THREAD_STOP_FORMAT, nullptr, + "The default thread format " + "string to use when displaying thread " + "information as part of the stop display."}, {"use-external-editor", OptionValue::eTypeBoolean, true, false, nullptr, nullptr, "Whether to use an external editor or not."}, {"use-color", OptionValue::eTypeBoolean, true, true, nullptr, nullptr, "Whether to use Ansi color codes or not."}, {"auto-one-line-summaries", OptionValue::eTypeBoolean, true, true, nullptr, - nullptr, "If true, LLDB will automatically display small structs in " - "one-liner format (default: true)."}, + nullptr, + "If true, LLDB will automatically display small structs in " + "one-liner format (default: true)."}, {"auto-indent", OptionValue::eTypeBoolean, true, true, nullptr, nullptr, "If true, LLDB will auto indent/outdent code. Currently only supported in " "the REPL (default: true)."}, {"print-decls", OptionValue::eTypeBoolean, true, true, nullptr, nullptr, "If true, LLDB will print the values of variables declared in an " "expression. Currently only supported in the REPL (default: true)."}, {"tab-size", OptionValue::eTypeUInt64, true, 4, nullptr, nullptr, "The tab size to use when indenting code in multi-line input mode " "(default: 4)."}, {"escape-non-printables", OptionValue::eTypeBoolean, true, true, nullptr, - nullptr, "If true, LLDB will automatically escape non-printable and " - "escape characters when formatting strings."}, + nullptr, + "If true, LLDB will automatically escape non-printable and " + "escape characters when formatting strings."}, + {"frame-format-unique", OptionValue::eTypeFormatEntity, true, 0, + DEFAULT_FRAME_FORMAT_NO_ARGS, nullptr, + "The default frame format string to use when displaying stack frame" + "information for threads from thread backtrace unique."}, {nullptr, OptionValue::eTypeInvalid, true, 0, nullptr, nullptr, nullptr}}; enum { ePropertyAutoConfirm = 0, ePropertyDisassemblyFormat, ePropertyFrameFormat, ePropertyNotiftVoid, ePropertyPrompt, ePropertyScriptLanguage, ePropertyStopDisassemblyCount, ePropertyStopDisassemblyDisplay, ePropertyStopLineCountAfter, ePropertyStopLineCountBefore, ePropertyStopShowColumn, ePropertyStopShowColumnAnsiPrefix, ePropertyStopShowColumnAnsiSuffix, ePropertyTerminalWidth, ePropertyThreadFormat, ePropertyThreadStopFormat, ePropertyUseExternalEditor, ePropertyUseColor, ePropertyAutoOneLineSummaries, ePropertyAutoIndent, ePropertyPrintDecls, ePropertyTabSize, - ePropertyEscapeNonPrintables + ePropertyEscapeNonPrintables, + ePropertyFrameFormatUnique, }; LoadPluginCallbackType Debugger::g_load_plugin_callback = nullptr; Status Debugger::SetPropertyValue(const ExecutionContext *exe_ctx, VarSetOperationType op, llvm::StringRef property_path, llvm::StringRef value) { bool is_load_script = (property_path == "target.load-script-from-symbol-file"); bool is_escape_non_printables = (property_path == "escape-non-printables"); TargetSP target_sp; LoadScriptFromSymFile load_script_old_value; if (is_load_script && exe_ctx->GetTargetSP()) { target_sp = exe_ctx->GetTargetSP(); load_script_old_value = target_sp->TargetProperties::GetLoadScriptFromSymbolFile(); } Status error(Properties::SetPropertyValue(exe_ctx, op, property_path, value)); if (error.Success()) { // FIXME it would be nice to have "on-change" callbacks for properties if (property_path == g_properties[ePropertyPrompt].name) { llvm::StringRef new_prompt = GetPrompt(); std::string str = lldb_utility::ansi::FormatAnsiTerminalCodes( new_prompt, GetUseColor()); if (str.length()) new_prompt = str; GetCommandInterpreter().UpdatePrompt(new_prompt); auto bytes = llvm::make_unique(new_prompt); auto prompt_change_event_sp = std::make_shared( CommandInterpreter::eBroadcastBitResetPrompt, bytes.release()); GetCommandInterpreter().BroadcastEvent(prompt_change_event_sp); } else if (property_path == g_properties[ePropertyUseColor].name) { // use-color changed. Ping the prompt so it can reset the ansi terminal // codes. SetPrompt(GetPrompt()); } else if (is_load_script && target_sp && load_script_old_value == eLoadScriptFromSymFileWarn) { if (target_sp->TargetProperties::GetLoadScriptFromSymbolFile() == eLoadScriptFromSymFileTrue) { std::list errors; StreamString feedback_stream; if (!target_sp->LoadScriptingResources(errors, &feedback_stream)) { StreamFileSP stream_sp(GetErrorFile()); if (stream_sp) { for (auto error : errors) { stream_sp->Printf("%s\n", error.AsCString()); } if (feedback_stream.GetSize()) stream_sp->PutCString(feedback_stream.GetString()); } } } } else if (is_escape_non_printables) { DataVisualization::ForceUpdate(); } } return error; } bool Debugger::GetAutoConfirm() const { const uint32_t idx = ePropertyAutoConfirm; return m_collection_sp->GetPropertyAtIndexAsBoolean( nullptr, idx, g_properties[idx].default_uint_value != 0); } const FormatEntity::Entry *Debugger::GetDisassemblyFormat() const { const uint32_t idx = ePropertyDisassemblyFormat; return m_collection_sp->GetPropertyAtIndexAsFormatEntity(nullptr, idx); } const FormatEntity::Entry *Debugger::GetFrameFormat() const { const uint32_t idx = ePropertyFrameFormat; + return m_collection_sp->GetPropertyAtIndexAsFormatEntity(nullptr, idx); +} + +const FormatEntity::Entry *Debugger::GetFrameFormatUnique() const { + const uint32_t idx = ePropertyFrameFormatUnique; return m_collection_sp->GetPropertyAtIndexAsFormatEntity(nullptr, idx); } bool Debugger::GetNotifyVoid() const { const uint32_t idx = ePropertyNotiftVoid; return m_collection_sp->GetPropertyAtIndexAsBoolean( nullptr, idx, g_properties[idx].default_uint_value != 0); } llvm::StringRef Debugger::GetPrompt() const { const uint32_t idx = ePropertyPrompt; return m_collection_sp->GetPropertyAtIndexAsString( nullptr, idx, g_properties[idx].default_cstr_value); } void Debugger::SetPrompt(llvm::StringRef p) { const uint32_t idx = ePropertyPrompt; m_collection_sp->SetPropertyAtIndexAsString(nullptr, idx, p); llvm::StringRef new_prompt = GetPrompt(); std::string str = lldb_utility::ansi::FormatAnsiTerminalCodes(new_prompt, GetUseColor()); if (str.length()) new_prompt = str; GetCommandInterpreter().UpdatePrompt(new_prompt); } const FormatEntity::Entry *Debugger::GetThreadFormat() const { const uint32_t idx = ePropertyThreadFormat; return m_collection_sp->GetPropertyAtIndexAsFormatEntity(nullptr, idx); } const FormatEntity::Entry *Debugger::GetThreadStopFormat() const { const uint32_t idx = ePropertyThreadStopFormat; return m_collection_sp->GetPropertyAtIndexAsFormatEntity(nullptr, idx); } lldb::ScriptLanguage Debugger::GetScriptLanguage() const { const uint32_t idx = ePropertyScriptLanguage; return (lldb::ScriptLanguage)m_collection_sp->GetPropertyAtIndexAsEnumeration( nullptr, idx, g_properties[idx].default_uint_value); } bool Debugger::SetScriptLanguage(lldb::ScriptLanguage script_lang) { const uint32_t idx = ePropertyScriptLanguage; return m_collection_sp->SetPropertyAtIndexAsEnumeration(nullptr, idx, script_lang); } uint32_t Debugger::GetTerminalWidth() const { const uint32_t idx = ePropertyTerminalWidth; return m_collection_sp->GetPropertyAtIndexAsSInt64( nullptr, idx, g_properties[idx].default_uint_value); } bool Debugger::SetTerminalWidth(uint32_t term_width) { const uint32_t idx = ePropertyTerminalWidth; return m_collection_sp->SetPropertyAtIndexAsSInt64(nullptr, idx, term_width); } bool Debugger::GetUseExternalEditor() const { const uint32_t idx = ePropertyUseExternalEditor; return m_collection_sp->GetPropertyAtIndexAsBoolean( nullptr, idx, g_properties[idx].default_uint_value != 0); } bool Debugger::SetUseExternalEditor(bool b) { const uint32_t idx = ePropertyUseExternalEditor; return m_collection_sp->SetPropertyAtIndexAsBoolean(nullptr, idx, b); } bool Debugger::GetUseColor() const { const uint32_t idx = ePropertyUseColor; return m_collection_sp->GetPropertyAtIndexAsBoolean( nullptr, idx, g_properties[idx].default_uint_value != 0); } bool Debugger::SetUseColor(bool b) { const uint32_t idx = ePropertyUseColor; bool ret = m_collection_sp->SetPropertyAtIndexAsBoolean(nullptr, idx, b); SetPrompt(GetPrompt()); return ret; } StopShowColumn Debugger::GetStopShowColumn() const { const uint32_t idx = ePropertyStopShowColumn; return (lldb::StopShowColumn)m_collection_sp->GetPropertyAtIndexAsEnumeration( nullptr, idx, g_properties[idx].default_uint_value); } const FormatEntity::Entry *Debugger::GetStopShowColumnAnsiPrefix() const { const uint32_t idx = ePropertyStopShowColumnAnsiPrefix; return m_collection_sp->GetPropertyAtIndexAsFormatEntity(nullptr, idx); } const FormatEntity::Entry *Debugger::GetStopShowColumnAnsiSuffix() const { const uint32_t idx = ePropertyStopShowColumnAnsiSuffix; return m_collection_sp->GetPropertyAtIndexAsFormatEntity(nullptr, idx); } uint32_t Debugger::GetStopSourceLineCount(bool before) const { const uint32_t idx = before ? ePropertyStopLineCountBefore : ePropertyStopLineCountAfter; return m_collection_sp->GetPropertyAtIndexAsSInt64( nullptr, idx, g_properties[idx].default_uint_value); } Debugger::StopDisassemblyType Debugger::GetStopDisassemblyDisplay() const { const uint32_t idx = ePropertyStopDisassemblyDisplay; return (Debugger::StopDisassemblyType) m_collection_sp->GetPropertyAtIndexAsEnumeration( nullptr, idx, g_properties[idx].default_uint_value); } uint32_t Debugger::GetDisassemblyLineCount() const { const uint32_t idx = ePropertyStopDisassemblyCount; return m_collection_sp->GetPropertyAtIndexAsSInt64( nullptr, idx, g_properties[idx].default_uint_value); } bool Debugger::GetAutoOneLineSummaries() const { const uint32_t idx = ePropertyAutoOneLineSummaries; return m_collection_sp->GetPropertyAtIndexAsBoolean(nullptr, idx, true); } bool Debugger::GetEscapeNonPrintables() const { const uint32_t idx = ePropertyEscapeNonPrintables; return m_collection_sp->GetPropertyAtIndexAsBoolean(nullptr, idx, true); } bool Debugger::GetAutoIndent() const { const uint32_t idx = ePropertyAutoIndent; return m_collection_sp->GetPropertyAtIndexAsBoolean(nullptr, idx, true); } bool Debugger::SetAutoIndent(bool b) { const uint32_t idx = ePropertyAutoIndent; return m_collection_sp->SetPropertyAtIndexAsBoolean(nullptr, idx, b); } bool Debugger::GetPrintDecls() const { const uint32_t idx = ePropertyPrintDecls; return m_collection_sp->GetPropertyAtIndexAsBoolean(nullptr, idx, true); } bool Debugger::SetPrintDecls(bool b) { const uint32_t idx = ePropertyPrintDecls; return m_collection_sp->SetPropertyAtIndexAsBoolean(nullptr, idx, b); } uint32_t Debugger::GetTabSize() const { const uint32_t idx = ePropertyTabSize; return m_collection_sp->GetPropertyAtIndexAsUInt64( nullptr, idx, g_properties[idx].default_uint_value); } bool Debugger::SetTabSize(uint32_t tab_size) { const uint32_t idx = ePropertyTabSize; return m_collection_sp->SetPropertyAtIndexAsUInt64(nullptr, idx, tab_size); } #pragma mark Debugger // const DebuggerPropertiesSP & // Debugger::GetSettings() const //{ // return m_properties_sp; //} // void Debugger::Initialize(LoadPluginCallbackType load_plugin_callback) { assert(g_debugger_list_ptr == nullptr && "Debugger::Initialize called more than once!"); g_debugger_list_mutex_ptr = new std::recursive_mutex(); g_debugger_list_ptr = new DebuggerList(); g_load_plugin_callback = load_plugin_callback; } void Debugger::Terminate() { assert(g_debugger_list_ptr && "Debugger::Terminate called without a matching Debugger::Initialize!"); if (g_debugger_list_ptr && g_debugger_list_mutex_ptr) { // Clear our master list of debugger objects { std::lock_guard guard(*g_debugger_list_mutex_ptr); for (const auto &debugger : *g_debugger_list_ptr) debugger->Clear(); g_debugger_list_ptr->clear(); } } } void Debugger::SettingsInitialize() { Target::SettingsInitialize(); } void Debugger::SettingsTerminate() { Target::SettingsTerminate(); } bool Debugger::LoadPlugin(const FileSpec &spec, Status &error) { if (g_load_plugin_callback) { llvm::sys::DynamicLibrary dynlib = g_load_plugin_callback(shared_from_this(), spec, error); if (dynlib.isValid()) { m_loaded_plugins.push_back(dynlib); return true; } } else { // The g_load_plugin_callback is registered in SBDebugger::Initialize() // and if the public API layer isn't available (code is linking against // all of the internal LLDB static libraries), then we can't load plugins error.SetErrorString("Public API layer is not available"); } return false; } static FileSpec::EnumerateDirectoryResult LoadPluginCallback(void *baton, llvm::sys::fs::file_type ft, const FileSpec &file_spec) { Status error; static ConstString g_dylibext("dylib"); static ConstString g_solibext("so"); if (!baton) return FileSpec::eEnumerateDirectoryResultQuit; Debugger *debugger = (Debugger *)baton; namespace fs = llvm::sys::fs; // If we have a regular file, a symbolic link or unknown file type, try // and process the file. We must handle unknown as sometimes the directory // enumeration might be enumerating a file system that doesn't have correct // file type information. if (ft == fs::file_type::regular_file || ft == fs::file_type::symlink_file || ft == fs::file_type::type_unknown) { FileSpec plugin_file_spec(file_spec); plugin_file_spec.ResolvePath(); if (plugin_file_spec.GetFileNameExtension() != g_dylibext && plugin_file_spec.GetFileNameExtension() != g_solibext) { return FileSpec::eEnumerateDirectoryResultNext; } Status plugin_load_error; debugger->LoadPlugin(plugin_file_spec, plugin_load_error); return FileSpec::eEnumerateDirectoryResultNext; } else if (ft == fs::file_type::directory_file || ft == fs::file_type::symlink_file || ft == fs::file_type::type_unknown) { // Try and recurse into anything that a directory or symbolic link. // We must also do this for unknown as sometimes the directory enumeration // might be enumerating a file system that doesn't have correct file type // information. return FileSpec::eEnumerateDirectoryResultEnter; } return FileSpec::eEnumerateDirectoryResultNext; } void Debugger::InstanceInitialize() { FileSpec dir_spec; const bool find_directories = true; const bool find_files = true; const bool find_other = true; char dir_path[PATH_MAX]; if (HostInfo::GetLLDBPath(ePathTypeLLDBSystemPlugins, dir_spec)) { if (dir_spec.Exists() && dir_spec.GetPath(dir_path, sizeof(dir_path))) { FileSpec::EnumerateDirectory(dir_path, find_directories, find_files, find_other, LoadPluginCallback, this); } } if (HostInfo::GetLLDBPath(ePathTypeLLDBUserPlugins, dir_spec)) { if (dir_spec.Exists() && dir_spec.GetPath(dir_path, sizeof(dir_path))) { FileSpec::EnumerateDirectory(dir_path, find_directories, find_files, find_other, LoadPluginCallback, this); } } PluginManager::DebuggerInitialize(*this); } DebuggerSP Debugger::CreateInstance(lldb::LogOutputCallback log_callback, void *baton) { DebuggerSP debugger_sp(new Debugger(log_callback, baton)); if (g_debugger_list_ptr && g_debugger_list_mutex_ptr) { std::lock_guard guard(*g_debugger_list_mutex_ptr); g_debugger_list_ptr->push_back(debugger_sp); } debugger_sp->InstanceInitialize(); return debugger_sp; } void Debugger::Destroy(DebuggerSP &debugger_sp) { if (!debugger_sp) return; debugger_sp->Clear(); if (g_debugger_list_ptr && g_debugger_list_mutex_ptr) { std::lock_guard guard(*g_debugger_list_mutex_ptr); DebuggerList::iterator pos, end = g_debugger_list_ptr->end(); for (pos = g_debugger_list_ptr->begin(); pos != end; ++pos) { if ((*pos).get() == debugger_sp.get()) { g_debugger_list_ptr->erase(pos); return; } } } } DebuggerSP Debugger::FindDebuggerWithInstanceName(const ConstString &instance_name) { DebuggerSP debugger_sp; if (g_debugger_list_ptr && g_debugger_list_mutex_ptr) { std::lock_guard guard(*g_debugger_list_mutex_ptr); DebuggerList::iterator pos, end = g_debugger_list_ptr->end(); for (pos = g_debugger_list_ptr->begin(); pos != end; ++pos) { if ((*pos)->m_instance_name == instance_name) { debugger_sp = *pos; break; } } } return debugger_sp; } TargetSP Debugger::FindTargetWithProcessID(lldb::pid_t pid) { TargetSP target_sp; if (g_debugger_list_ptr && g_debugger_list_mutex_ptr) { std::lock_guard guard(*g_debugger_list_mutex_ptr); DebuggerList::iterator pos, end = g_debugger_list_ptr->end(); for (pos = g_debugger_list_ptr->begin(); pos != end; ++pos) { target_sp = (*pos)->GetTargetList().FindTargetWithProcessID(pid); if (target_sp) break; } } return target_sp; } TargetSP Debugger::FindTargetWithProcess(Process *process) { TargetSP target_sp; if (g_debugger_list_ptr && g_debugger_list_mutex_ptr) { std::lock_guard guard(*g_debugger_list_mutex_ptr); DebuggerList::iterator pos, end = g_debugger_list_ptr->end(); for (pos = g_debugger_list_ptr->begin(); pos != end; ++pos) { target_sp = (*pos)->GetTargetList().FindTargetWithProcess(process); if (target_sp) break; } } return target_sp; } Debugger::Debugger(lldb::LogOutputCallback log_callback, void *baton) : UserID(g_unique_id++), Properties(std::make_shared()), m_input_file_sp(std::make_shared(stdin, false)), m_output_file_sp(std::make_shared(stdout, false)), m_error_file_sp(std::make_shared(stderr, false)), m_broadcaster_manager_sp(BroadcasterManager::MakeBroadcasterManager()), m_terminal_state(), m_target_list(*this), m_platform_list(), m_listener_sp(Listener::MakeListener("lldb.Debugger")), m_source_manager_ap(), m_source_file_cache(), m_command_interpreter_ap(llvm::make_unique( *this, eScriptLanguageDefault, false)), m_input_reader_stack(), m_instance_name(), m_loaded_plugins(), m_event_handler_thread(), m_io_handler_thread(), m_sync_broadcaster(nullptr, "lldb.debugger.sync"), m_forward_listener_sp(), m_clear_once() { char instance_cstr[256]; snprintf(instance_cstr, sizeof(instance_cstr), "debugger_%d", (int)GetID()); m_instance_name.SetCString(instance_cstr); if (log_callback) m_log_callback_stream_sp = std::make_shared(log_callback, baton); m_command_interpreter_ap->Initialize(); // Always add our default platform to the platform list PlatformSP default_platform_sp(Platform::GetHostPlatform()); assert(default_platform_sp); m_platform_list.Append(default_platform_sp, true); m_collection_sp->Initialize(g_properties); m_collection_sp->AppendProperty( ConstString("target"), ConstString("Settings specify to debugging targets."), true, Target::GetGlobalProperties()->GetValueProperties()); m_collection_sp->AppendProperty( ConstString("platform"), ConstString("Platform settings."), true, Platform::GetGlobalPlatformProperties()->GetValueProperties()); if (m_command_interpreter_ap) { m_collection_sp->AppendProperty( ConstString("interpreter"), ConstString("Settings specify to the debugger's command interpreter."), true, m_command_interpreter_ap->GetValueProperties()); } OptionValueSInt64 *term_width = m_collection_sp->GetPropertyAtIndexAsOptionValueSInt64( nullptr, ePropertyTerminalWidth); term_width->SetMinimumValue(10); term_width->SetMaximumValue(1024); // Turn off use-color if this is a dumb terminal. const char *term = getenv("TERM"); if (term && !strcmp(term, "dumb")) SetUseColor(false); } Debugger::~Debugger() { Clear(); } void Debugger::Clear() { //---------------------------------------------------------------------- // Make sure we call this function only once. With the C++ global // destructor chain having a list of debuggers and with code that can be // running on other threads, we need to ensure this doesn't happen // multiple times. // // The following functions call Debugger::Clear(): // Debugger::~Debugger(); // static void Debugger::Destroy(lldb::DebuggerSP &debugger_sp); // static void Debugger::Terminate(); //---------------------------------------------------------------------- llvm::call_once(m_clear_once, [this]() { ClearIOHandlers(); StopIOHandlerThread(); StopEventHandlerThread(); m_listener_sp->Clear(); int num_targets = m_target_list.GetNumTargets(); for (int i = 0; i < num_targets; i++) { TargetSP target_sp(m_target_list.GetTargetAtIndex(i)); if (target_sp) { ProcessSP process_sp(target_sp->GetProcessSP()); if (process_sp) process_sp->Finalize(); target_sp->Destroy(); } } m_broadcaster_manager_sp->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(); if (m_input_file_sp) m_input_file_sp->GetFile().Close(); m_command_interpreter_ap->Clear(); }); } bool Debugger::GetCloseInputOnEOF() const { // return m_input_comm.GetCloseOnEOF(); return false; } void Debugger::SetCloseInputOnEOF(bool b) { // m_input_comm.SetCloseOnEOF(b); } bool Debugger::GetAsyncExecution() { return !m_command_interpreter_ap->GetSynchronous(); } void Debugger::SetAsyncExecution(bool async_execution) { m_command_interpreter_ap->SetSynchronous(!async_execution); } void Debugger::SetInputFileHandle(FILE *fh, bool tranfer_ownership) { if (m_input_file_sp) m_input_file_sp->GetFile().SetStream(fh, tranfer_ownership); else m_input_file_sp = std::make_shared(fh, tranfer_ownership); File &in_file = m_input_file_sp->GetFile(); if (!in_file.IsValid()) in_file.SetStream(stdin, true); // Save away the terminal state if that is relevant, so that we can restore it // in RestoreInputState. SaveInputTerminalState(); } void Debugger::SetOutputFileHandle(FILE *fh, bool tranfer_ownership) { if (m_output_file_sp) m_output_file_sp->GetFile().SetStream(fh, tranfer_ownership); else m_output_file_sp = std::make_shared(fh, tranfer_ownership); File &out_file = m_output_file_sp->GetFile(); if (!out_file.IsValid()) out_file.SetStream(stdout, false); // do not create the ScriptInterpreter just for setting the output file handle // as the constructor will know how to do the right thing on its own const bool can_create = false; ScriptInterpreter *script_interpreter = GetCommandInterpreter().GetScriptInterpreter(can_create); if (script_interpreter) script_interpreter->ResetOutputFileHandle(fh); } void Debugger::SetErrorFileHandle(FILE *fh, bool tranfer_ownership) { if (m_error_file_sp) m_error_file_sp->GetFile().SetStream(fh, tranfer_ownership); else m_error_file_sp = std::make_shared(fh, tranfer_ownership); File &err_file = m_error_file_sp->GetFile(); if (!err_file.IsValid()) err_file.SetStream(stderr, false); } void Debugger::SaveInputTerminalState() { if (m_input_file_sp) { File &in_file = m_input_file_sp->GetFile(); if (in_file.GetDescriptor() != File::kInvalidDescriptor) m_terminal_state.Save(in_file.GetDescriptor(), true); } } void Debugger::RestoreInputTerminalState() { m_terminal_state.Restore(); } ExecutionContext Debugger::GetSelectedExecutionContext() { ExecutionContext exe_ctx; TargetSP target_sp(GetSelectedTarget()); exe_ctx.SetTargetSP(target_sp); if (target_sp) { ProcessSP process_sp(target_sp->GetProcessSP()); exe_ctx.SetProcessSP(process_sp); if (process_sp && !process_sp->IsRunning()) { ThreadSP thread_sp(process_sp->GetThreadList().GetSelectedThread()); if (thread_sp) { exe_ctx.SetThreadSP(thread_sp); exe_ctx.SetFrameSP(thread_sp->GetSelectedFrame()); if (exe_ctx.GetFramePtr() == nullptr) exe_ctx.SetFrameSP(thread_sp->GetStackFrameAtIndex(0)); } } } return exe_ctx; } void Debugger::DispatchInputInterrupt() { std::lock_guard guard(m_input_reader_stack.GetMutex()); IOHandlerSP reader_sp(m_input_reader_stack.Top()); if (reader_sp) reader_sp->Interrupt(); } void Debugger::DispatchInputEndOfFile() { std::lock_guard guard(m_input_reader_stack.GetMutex()); IOHandlerSP reader_sp(m_input_reader_stack.Top()); if (reader_sp) reader_sp->GotEOF(); } void Debugger::ClearIOHandlers() { // The bottom input reader should be the main debugger input reader. We do // not want to close that one here. std::lock_guard guard(m_input_reader_stack.GetMutex()); while (m_input_reader_stack.GetSize() > 1) { IOHandlerSP reader_sp(m_input_reader_stack.Top()); if (reader_sp) PopIOHandler(reader_sp); } } void Debugger::ExecuteIOHandlers() { while (true) { IOHandlerSP reader_sp(m_input_reader_stack.Top()); if (!reader_sp) break; reader_sp->Run(); // Remove all input readers that are done from the top of the stack while (true) { IOHandlerSP top_reader_sp = m_input_reader_stack.Top(); if (top_reader_sp && top_reader_sp->GetIsDone()) PopIOHandler(top_reader_sp); else break; } } ClearIOHandlers(); } bool Debugger::IsTopIOHandler(const lldb::IOHandlerSP &reader_sp) { return m_input_reader_stack.IsTop(reader_sp); } bool Debugger::CheckTopIOHandlerTypes(IOHandler::Type top_type, IOHandler::Type second_top_type) { return m_input_reader_stack.CheckTopIOHandlerTypes(top_type, second_top_type); } void Debugger::PrintAsync(const char *s, size_t len, bool is_stdout) { lldb::StreamFileSP stream = is_stdout ? GetOutputFile() : GetErrorFile(); m_input_reader_stack.PrintAsync(stream.get(), s, len); } ConstString Debugger::GetTopIOHandlerControlSequence(char ch) { return m_input_reader_stack.GetTopIOHandlerControlSequence(ch); } const char *Debugger::GetIOHandlerCommandPrefix() { return m_input_reader_stack.GetTopIOHandlerCommandPrefix(); } const char *Debugger::GetIOHandlerHelpPrologue() { return m_input_reader_stack.GetTopIOHandlerHelpPrologue(); } void Debugger::RunIOHandler(const IOHandlerSP &reader_sp) { PushIOHandler(reader_sp); IOHandlerSP top_reader_sp = reader_sp; while (top_reader_sp) { top_reader_sp->Run(); if (top_reader_sp.get() == reader_sp.get()) { if (PopIOHandler(reader_sp)) break; } while (true) { top_reader_sp = m_input_reader_stack.Top(); if (top_reader_sp && top_reader_sp->GetIsDone()) PopIOHandler(top_reader_sp); else break; } } } void Debugger::AdoptTopIOHandlerFilesIfInvalid(StreamFileSP &in, StreamFileSP &out, StreamFileSP &err) { // Before an IOHandler runs, it must have in/out/err streams. // This function is called when one ore more of the streams // are nullptr. We use the top input reader's in/out/err streams, // or fall back to the debugger file handles, or we fall back // onto stdin/stdout/stderr as a last resort. std::lock_guard guard(m_input_reader_stack.GetMutex()); IOHandlerSP top_reader_sp(m_input_reader_stack.Top()); // If no STDIN has been set, then set it appropriately if (!in) { if (top_reader_sp) in = top_reader_sp->GetInputStreamFile(); else in = GetInputFile(); // If there is nothing, use stdin if (!in) in = std::make_shared(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 = std::make_shared(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 = std::make_shared(stdout, false); } } void Debugger::PushIOHandler(const IOHandlerSP &reader_sp) { if (!reader_sp) return; std::lock_guard guard(m_input_reader_stack.GetMutex()); // Get the current top input reader... IOHandlerSP top_reader_sp(m_input_reader_stack.Top()); // Don't push the same IO handler twice... if (reader_sp == top_reader_sp) return; // Push our new input reader m_input_reader_stack.Push(reader_sp); reader_sp->Activate(); // Interrupt the top input reader to it will exit its Run() function // and let this new input reader take over if (top_reader_sp) { top_reader_sp->Deactivate(); top_reader_sp->Cancel(); } } bool Debugger::PopIOHandler(const IOHandlerSP &pop_reader_sp) { if (!pop_reader_sp) return false; std::lock_guard guard(m_input_reader_stack.GetMutex()); // The reader on the stop of the stack is done, so let the next // read on the stack refresh its prompt and if there is one... if (m_input_reader_stack.IsEmpty()) return false; IOHandlerSP reader_sp(m_input_reader_stack.Top()); if (pop_reader_sp != reader_sp) return false; reader_sp->Deactivate(); reader_sp->Cancel(); m_input_reader_stack.Pop(); reader_sp = m_input_reader_stack.Top(); if (reader_sp) reader_sp->Activate(); return true; } StreamSP Debugger::GetAsyncOutputStream() { return std::make_shared(*this, true); } StreamSP Debugger::GetAsyncErrorStream() { return std::make_shared(*this, false); } size_t Debugger::GetNumDebuggers() { if (g_debugger_list_ptr && g_debugger_list_mutex_ptr) { std::lock_guard guard(*g_debugger_list_mutex_ptr); return g_debugger_list_ptr->size(); } return 0; } lldb::DebuggerSP Debugger::GetDebuggerAtIndex(size_t index) { DebuggerSP debugger_sp; if (g_debugger_list_ptr && g_debugger_list_mutex_ptr) { std::lock_guard guard(*g_debugger_list_mutex_ptr); if (index < g_debugger_list_ptr->size()) debugger_sp = g_debugger_list_ptr->at(index); } return debugger_sp; } DebuggerSP Debugger::FindDebuggerWithID(lldb::user_id_t id) { DebuggerSP debugger_sp; if (g_debugger_list_ptr && g_debugger_list_mutex_ptr) { std::lock_guard guard(*g_debugger_list_mutex_ptr); DebuggerList::iterator pos, end = g_debugger_list_ptr->end(); for (pos = g_debugger_list_ptr->begin(); pos != end; ++pos) { if ((*pos)->GetID() == id) { debugger_sp = *pos; break; } } } return debugger_sp; } #if 0 static void TestPromptFormats (StackFrame *frame) { if (frame == nullptr) return; StreamString s; const char *prompt_format = "{addr = '${addr}'\n}" "{addr-file-or-load = '${addr-file-or-load}'\n}" "{current-pc-arrow = '${current-pc-arrow}'\n}" "{process.id = '${process.id}'\n}" "{process.name = '${process.name}'\n}" "{process.file.basename = '${process.file.basename}'\n}" "{process.file.fullpath = '${process.file.fullpath}'\n}" "{thread.id = '${thread.id}'\n}" "{thread.index = '${thread.index}'\n}" "{thread.name = '${thread.name}'\n}" "{thread.queue = '${thread.queue}'\n}" "{thread.stop-reason = '${thread.stop-reason}'\n}" "{target.arch = '${target.arch}'\n}" "{module.file.basename = '${module.file.basename}'\n}" "{module.file.fullpath = '${module.file.fullpath}'\n}" "{file.basename = '${file.basename}'\n}" "{file.fullpath = '${file.fullpath}'\n}" "{frame.index = '${frame.index}'\n}" "{frame.pc = '${frame.pc}'\n}" "{frame.sp = '${frame.sp}'\n}" "{frame.fp = '${frame.fp}'\n}" "{frame.flags = '${frame.flags}'\n}" "{frame.reg.rdi = '${frame.reg.rdi}'\n}" "{frame.reg.rip = '${frame.reg.rip}'\n}" "{frame.reg.rsp = '${frame.reg.rsp}'\n}" "{frame.reg.rbp = '${frame.reg.rbp}'\n}" "{frame.reg.rflags = '${frame.reg.rflags}'\n}" "{frame.reg.xmm0 = '${frame.reg.xmm0}'\n}" "{frame.reg.carp = '${frame.reg.carp}'\n}" "{function.id = '${function.id}'\n}" "{function.changed = '${function.changed}'\n}" "{function.initial-function = '${function.initial-function}'\n}" "{function.name = '${function.name}'\n}" "{function.name-without-args = '${function.name-without-args}'\n}" "{function.name-with-args = '${function.name-with-args}'\n}" "{function.addr-offset = '${function.addr-offset}'\n}" "{function.concrete-only-addr-offset-no-padding = '${function.concrete-only-addr-offset-no-padding}'\n}" "{function.line-offset = '${function.line-offset}'\n}" "{function.pc-offset = '${function.pc-offset}'\n}" "{line.file.basename = '${line.file.basename}'\n}" "{line.file.fullpath = '${line.file.fullpath}'\n}" "{line.number = '${line.number}'\n}" "{line.start-addr = '${line.start-addr}'\n}" "{line.end-addr = '${line.end-addr}'\n}" ; SymbolContext sc (frame->GetSymbolContext(eSymbolContextEverything)); ExecutionContext exe_ctx; frame->CalculateExecutionContext(exe_ctx); if (Debugger::FormatPrompt (prompt_format, &sc, &exe_ctx, &sc.line_entry.range.GetBaseAddress(), s)) { printf("%s\n", s.GetData()); } else { printf ("what we got: %s\n", s.GetData()); } } #endif bool Debugger::FormatDisassemblerAddress(const FormatEntity::Entry *format, const SymbolContext *sc, const SymbolContext *prev_sc, const ExecutionContext *exe_ctx, const Address *addr, Stream &s) { FormatEntity::Entry format_entry; if (format == nullptr) { if (exe_ctx != nullptr && exe_ctx->HasTargetScope()) format = exe_ctx->GetTargetRef().GetDebugger().GetDisassemblyFormat(); if (format == nullptr) { FormatEntity::Parse("${addr}: ", format_entry); format = &format_entry; } } bool function_changed = false; bool initial_function = false; if (prev_sc && (prev_sc->function || prev_sc->symbol)) { if (sc && (sc->function || sc->symbol)) { if (prev_sc->symbol && sc->symbol) { if (!sc->symbol->Compare(prev_sc->symbol->GetName(), prev_sc->symbol->GetType())) { function_changed = true; } } else if (prev_sc->function && sc->function) { if (prev_sc->function->GetMangled() != sc->function->GetMangled()) { function_changed = true; } } } } // The first context on a list of instructions will have a prev_sc that // has no Function or Symbol -- if SymbolContext had an IsValid() method, it // would return false. But we do get a prev_sc pointer. if ((sc && (sc->function || sc->symbol)) && prev_sc && (prev_sc->function == nullptr && prev_sc->symbol == nullptr)) { initial_function = true; } return FormatEntity::Format(*format, s, sc, exe_ctx, addr, nullptr, function_changed, initial_function); } void Debugger::SetLoggingCallback(lldb::LogOutputCallback log_callback, void *baton) { // For simplicity's sake, I am not going to deal with how to close down any // open logging streams, I just redirect everything from here on out to the // callback. m_log_callback_stream_sp = std::make_shared(log_callback, baton); } bool Debugger::EnableLog(llvm::StringRef channel, llvm::ArrayRef categories, llvm::StringRef log_file, uint32_t log_options, llvm::raw_ostream &error_stream) { const bool should_close = true; const bool unbuffered = true; std::shared_ptr log_stream_sp; if (m_log_callback_stream_sp) { log_stream_sp = m_log_callback_stream_sp; // For now when using the callback mode you always get thread & timestamp. log_options |= LLDB_LOG_OPTION_PREPEND_TIMESTAMP | LLDB_LOG_OPTION_PREPEND_THREAD_NAME; } else if (log_file.empty()) { log_stream_sp = std::make_shared( GetOutputFile()->GetFile().GetDescriptor(), !should_close, unbuffered); } else { auto pos = m_log_streams.find(log_file); if (pos != m_log_streams.end()) log_stream_sp = pos->second.lock(); if (!log_stream_sp) { llvm::sys::fs::OpenFlags flags = llvm::sys::fs::F_Text; if (log_options & LLDB_LOG_OPTION_APPEND) flags |= llvm::sys::fs::F_Append; int FD; if (std::error_code ec = llvm::sys::fs::openFileForWrite(log_file, FD, flags)) { error_stream << "Unable to open log file: " << ec.message(); return false; } log_stream_sp = std::make_shared(FD, should_close, unbuffered); m_log_streams[log_file] = log_stream_sp; } } assert(log_stream_sp); if (log_options == 0) log_options = LLDB_LOG_OPTION_PREPEND_THREAD_NAME | LLDB_LOG_OPTION_THREADSAFE; return Log::EnableLogChannel(log_stream_sp, log_options, channel, categories, error_stream); } SourceManager &Debugger::GetSourceManager() { if (!m_source_manager_ap) m_source_manager_ap = llvm::make_unique(shared_from_this()); return *m_source_manager_ap; } // 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); StreamSP output_sp(GetAsyncOutputStream()); if (output_sp) { output_sp->Printf("%d location%s added to breakpoint %d\n", num_new_locations, num_new_locations == 1 ? "" : "s", breakpoint->GetID()); output_sp->Flush(); } } } // else if (event_type & eBreakpointEventTypeLocationsRemoved) // { // // These locations just get disabled, not sure it is worth spamming // folks about this on the command line. // } // else if (event_type & eBreakpointEventTypeLocationsResolved) // { // // This might be an interesting thing to note, but I'm going to // leave it quiet for now, it just looked noisy. // } } size_t Debugger::GetProcessSTDOUT(Process *process, Stream *stream) { size_t total_bytes = 0; if (stream == nullptr) stream = GetOutputFile().get(); if (stream) { // The process has stuff waiting for stdout; get it and write it out to the // appropriate place. if (process == nullptr) { TargetSP target_sp = GetTargetList().GetSelectedTarget(); if (target_sp) process = target_sp->GetProcessSP().get(); } if (process) { Status error; size_t len; char stdio_buffer[1024]; while ((len = process->GetSTDOUT(stdio_buffer, sizeof(stdio_buffer), error)) > 0) { stream->Write(stdio_buffer, len); total_bytes += len; } } stream->Flush(); } return total_bytes; } size_t Debugger::GetProcessSTDERR(Process *process, Stream *stream) { size_t total_bytes = 0; if (stream == nullptr) stream = GetOutputFile().get(); if (stream) { // The process has stuff waiting for stderr; get it and write it out to the // appropriate place. if (process == nullptr) { TargetSP target_sp = GetTargetList().GetSelectedTarget(); if (target_sp) process = target_sp->GetProcessSP().get(); } if (process) { Status 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 = (event_type == Process::eBroadcastBitStructuredData) ? EventDataStructuredData::GetProcessFromEvent(event_sp.get()) : Process::ProcessEventData::GetProcessFromEvent(event_sp.get()); StreamSP output_stream_sp = GetAsyncOutputStream(); StreamSP error_stream_sp = GetAsyncErrorStream(); const bool gui_enabled = IsForwardingEvents(); if (!gui_enabled) { bool pop_process_io_handler = false; assert(process_sp); bool state_is_stopped = false; const bool got_state_changed = (event_type & Process::eBroadcastBitStateChanged) != 0; const bool got_stdout = (event_type & Process::eBroadcastBitSTDOUT) != 0; const bool got_stderr = (event_type & Process::eBroadcastBitSTDERR) != 0; const bool got_structured_data = (event_type & Process::eBroadcastBitStructuredData) != 0; if (got_state_changed) { StateType event_state = Process::ProcessEventData::GetStateFromEvent(event_sp.get()); state_is_stopped = StateIsStoppedState(event_state, false); } // Display running state changes first before any STDIO if (got_state_changed && !state_is_stopped) { Process::HandleProcessStateChangedEvent(event_sp, output_stream_sp.get(), pop_process_io_handler); } // Now display and STDOUT if (got_stdout || got_state_changed) { GetProcessSTDOUT(process_sp.get(), output_stream_sp.get()); } // Now display and STDERR if (got_stderr || got_state_changed) { GetProcessSTDERR(process_sp.get(), error_stream_sp.get()); } // Give structured data events an opportunity to display. if (got_structured_data) { StructuredDataPluginSP plugin_sp = EventDataStructuredData::GetPluginFromEvent(event_sp.get()); if (plugin_sp) { auto structured_data_sp = EventDataStructuredData::GetObjectFromEvent(event_sp.get()); if (output_stream_sp) { StreamString content_stream; Status error = plugin_sp->GetDescription(structured_data_sp, content_stream); if (error.Success()) { if (!content_stream.GetString().empty()) { // Add newline. content_stream.PutChar('\n'); content_stream.Flush(); // Print it. output_stream_sp->PutCString(content_stream.GetString()); } } else { error_stream_sp->Printf("Failed to print structured " "data with plugin %s: %s", plugin_sp->GetPluginName().AsCString(), error.AsCString()); } } } } // Now display any stopped state changes after any STDIO if (got_state_changed && state_is_stopped) { Process::HandleProcessStateChangedEvent(event_sp, output_stream_sp.get(), pop_process_io_handler); } output_stream_sp->Flush(); error_stream_sp->Flush(); if (pop_process_io_handler) process_sp->PopProcessIOHandler(); } } void Debugger::HandleThreadEvent(const EventSP &event_sp) { // At present the only thread event we handle is the Frame Changed event, // and all we do for that is just reprint the thread status for that thread. using namespace lldb; const uint32_t event_type = event_sp->GetType(); const bool stop_format = true; if (event_type == Thread::eBroadcastBitStackChanged || event_type == Thread::eBroadcastBitThreadSelected) { ThreadSP thread_sp( Thread::ThreadEventData::GetThreadFromEvent(event_sp.get())); if (thread_sp) { thread_sp->GetStatus(*GetAsyncOutputStream(), 0, 1, 1, stop_format); } } } bool Debugger::IsForwardingEvents() { return (bool)m_forward_listener_sp; } void Debugger::EnableForwardEvents(const ListenerSP &listener_sp) { m_forward_listener_sp = listener_sp; } void Debugger::CancelForwardEvents(const ListenerSP &listener_sp) { m_forward_listener_sp.reset(); } void Debugger::DefaultEventHandler() { ListenerSP listener_sp(GetListener()); ConstString broadcaster_class_target(Target::GetStaticBroadcasterClass()); ConstString broadcaster_class_process(Process::GetStaticBroadcasterClass()); ConstString broadcaster_class_thread(Thread::GetStaticBroadcasterClass()); BroadcastEventSpec target_event_spec(broadcaster_class_target, Target::eBroadcastBitBreakpointChanged); BroadcastEventSpec process_event_spec( broadcaster_class_process, Process::eBroadcastBitStateChanged | Process::eBroadcastBitSTDOUT | Process::eBroadcastBitSTDERR | Process::eBroadcastBitStructuredData); BroadcastEventSpec thread_event_spec(broadcaster_class_thread, Thread::eBroadcastBitStackChanged | Thread::eBroadcastBitThreadSelected); listener_sp->StartListeningForEventSpec(m_broadcaster_manager_sp, target_event_spec); listener_sp->StartListeningForEventSpec(m_broadcaster_manager_sp, process_event_spec); listener_sp->StartListeningForEventSpec(m_broadcaster_manager_sp, thread_event_spec); listener_sp->StartListeningForEvents( m_command_interpreter_ap.get(), CommandInterpreter::eBroadcastBitQuitCommandReceived | CommandInterpreter::eBroadcastBitAsynchronousOutputData | CommandInterpreter::eBroadcastBitAsynchronousErrorData); // Let the thread that spawned us know that we have started up and // that we are now listening to all required events so no events get missed m_sync_broadcaster.BroadcastEvent(eBroadcastBitEventThreadIsListening); bool done = false; while (!done) { EventSP event_sp; if (listener_sp->GetEvent(event_sp, llvm::None)) { 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( EventDataBytes::GetBytesFromEvent(event_sp.get())); if (data && data[0]) { StreamSP error_sp(GetAsyncErrorStream()); if (error_sp) { error_sp->PutCString(data); error_sp->Flush(); } } } else if (event_type & CommandInterpreter:: eBroadcastBitAsynchronousOutputData) { const char *data = reinterpret_cast( EventDataBytes::GetBytesFromEvent(event_sp.get())); if (data && data[0]) { StreamSP output_sp(GetAsyncOutputStream()); if (output_sp) { output_sp->PutCString(data); output_sp->Flush(); } } } } } 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 (!m_event_handler_thread.IsJoinable()) { // We must synchronize with the DefaultEventHandler() thread to ensure // it is up and running and listening to events before we return from // this function. We do this by listening to events for the // eBroadcastBitEventThreadIsListening from the m_sync_broadcaster ListenerSP listener_sp( Listener::MakeListener("lldb.debugger.event-handler")); listener_sp->StartListeningForEvents(&m_sync_broadcaster, eBroadcastBitEventThreadIsListening); // Use larger 8MB stack for this thread m_event_handler_thread = ThreadLauncher::LaunchThread( "lldb.debugger.event-handler", EventHandlerThread, this, nullptr, g_debugger_event_thread_stack_bytes); // Make sure DefaultEventHandler() is running and listening to events before // we return // from this function. We are only listening for events of type // eBroadcastBitEventThreadIsListening so we don't need to check the event, // we just need // to wait an infinite amount of time for it (nullptr timeout as the first // parameter) lldb::EventSP event_sp; listener_sp->GetEvent(event_sp, llvm::None); } return m_event_handler_thread.IsJoinable(); } void Debugger::StopEventHandlerThread() { if (m_event_handler_thread.IsJoinable()) { GetCommandInterpreter().BroadcastEvent( CommandInterpreter::eBroadcastBitQuitCommandReceived); m_event_handler_thread.Join(nullptr); } } lldb::thread_result_t Debugger::IOHandlerThread(lldb::thread_arg_t arg) { Debugger *debugger = (Debugger *)arg; debugger->ExecuteIOHandlers(); debugger->StopEventHandlerThread(); return NULL; } bool Debugger::HasIOHandlerThread() { return m_io_handler_thread.IsJoinable(); } bool Debugger::StartIOHandlerThread() { if (!m_io_handler_thread.IsJoinable()) m_io_handler_thread = ThreadLauncher::LaunchThread( "lldb.debugger.io-handler", IOHandlerThread, this, nullptr, 8 * 1024 * 1024); // Use larger 8MB stack for this thread return m_io_handler_thread.IsJoinable(); } void Debugger::StopIOHandlerThread() { if (m_io_handler_thread.IsJoinable()) { if (m_input_file_sp) m_input_file_sp->GetFile().Close(); m_io_handler_thread.Join(nullptr); } } void Debugger::JoinIOHandlerThread() { if (HasIOHandlerThread()) { thread_result_t result; m_io_handler_thread.Join(&result); m_io_handler_thread = LLDB_INVALID_HOST_THREAD; } } Target *Debugger::GetDummyTarget() { return m_target_list.GetDummyTarget(*this).get(); } Target *Debugger::GetSelectedOrDummyTarget(bool prefer_dummy) { Target *target = nullptr; if (!prefer_dummy) { target = m_target_list.GetSelectedTarget().get(); if (target) return target; } return GetDummyTarget(); } Status Debugger::RunREPL(LanguageType language, const char *repl_options) { Status err; FileSpec repl_executable; if (language == eLanguageTypeUnknown) { std::set repl_languages; Language::GetLanguagesSupportingREPLs(repl_languages); if (repl_languages.size() == 1) { language = *repl_languages.begin(); } else if (repl_languages.empty()) { err.SetErrorStringWithFormat( "LLDB isn't configured with REPL support for any languages."); return err; } else { err.SetErrorStringWithFormat( "Multiple possible REPL languages. Please specify a language."); return err; } } Target *const target = nullptr; // passing in an empty target means the REPL must create one REPLSP repl_sp(REPL::Create(err, language, this, target, repl_options)); if (!err.Success()) { return err; } if (!repl_sp) { err.SetErrorStringWithFormat("couldn't find a REPL for %s", Language::GetNameForLanguageType(language)); return err; } repl_sp->SetCompilerOptions(repl_options); repl_sp->RunLoop(); return err; } Index: vendor/lldb/dist/source/Host/common/Symbols.cpp =================================================================== --- vendor/lldb/dist/source/Host/common/Symbols.cpp (revision 320022) +++ vendor/lldb/dist/source/Host/common/Symbols.cpp (revision 320023) @@ -1,300 +1,316 @@ //===-- Symbols.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/Host/Symbols.h" #include "lldb/Core/ArchSpec.h" #include "lldb/Core/Module.h" #include "lldb/Core/ModuleSpec.h" #include "lldb/Core/Timer.h" #include "lldb/Symbol/ObjectFile.h" #include "lldb/Target/Target.h" #include "lldb/Utility/DataBuffer.h" #include "lldb/Utility/DataExtractor.h" #include "lldb/Utility/Log.h" #include "lldb/Utility/SafeMachO.h" #include "lldb/Utility/StreamString.h" #include "lldb/Utility/UUID.h" #include "llvm/Support/FileSystem.h" // From MacOSX system header "mach/machine.h" typedef int cpu_type_t; typedef int cpu_subtype_t; using namespace lldb; using namespace lldb_private; using namespace llvm::MachO; #if defined(__APPLE__) // Forward declaration of method defined in source/Host/macosx/Symbols.cpp int LocateMacOSXFilesUsingDebugSymbols(const ModuleSpec &module_spec, ModuleSpec &return_module_spec); #else int LocateMacOSXFilesUsingDebugSymbols(const ModuleSpec &module_spec, ModuleSpec &return_module_spec) { // Cannot find MacOSX files using debug symbols on non MacOSX. return 0; } #endif static bool FileAtPathContainsArchAndUUID(const FileSpec &file_fspec, const ArchSpec *arch, const lldb_private::UUID *uuid) { ModuleSpecList module_specs; if (ObjectFile::GetModuleSpecifications(file_fspec, 0, 0, module_specs)) { ModuleSpec spec; for (size_t i = 0; i < module_specs.GetSize(); ++i) { bool got_spec = module_specs.GetModuleSpecAtIndex(i, spec); UNUSED_IF_ASSERT_DISABLED(got_spec); assert(got_spec); if ((uuid == NULL || (spec.GetUUIDPtr() && spec.GetUUID() == *uuid)) && (arch == NULL || (spec.GetArchitecturePtr() && spec.GetArchitecture().IsCompatibleMatch(*arch)))) { return true; } } } return false; } static bool LocateDSYMInVincinityOfExecutable(const ModuleSpec &module_spec, FileSpec &dsym_fspec) { Log *log = lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_HOST); const FileSpec *exec_fspec = module_spec.GetFileSpecPtr(); if (exec_fspec) { char path[PATH_MAX]; if (exec_fspec->GetPath(path, sizeof(path))) { // Make sure the module isn't already just a dSYM file... if (strcasestr(path, ".dSYM/Contents/Resources/DWARF") == NULL) { if (log) { if (module_spec.GetUUIDPtr() && module_spec.GetUUIDPtr()->IsValid()) { log->Printf( "Searching for dSYM bundle next to executable %s, UUID %s", path, module_spec.GetUUIDPtr()->GetAsString().c_str()); } else { log->Printf("Searching for dSYM bundle next to executable %s", path); } } - size_t obj_file_path_length = strlen(path); ::strncat(path, ".dSYM/Contents/Resources/DWARF/", sizeof(path) - strlen(path) - 1); ::strncat(path, exec_fspec->GetFilename().AsCString(), sizeof(path) - strlen(path) - 1); dsym_fspec.SetFile(path, false); ModuleSpecList module_specs; ModuleSpec matched_module_spec; if (dsym_fspec.Exists() && FileAtPathContainsArchAndUUID(dsym_fspec, module_spec.GetArchitecturePtr(), module_spec.GetUUIDPtr())) { if (log) { log->Printf("dSYM with matching UUID & arch found at %s", path); } return true; } else { - path[obj_file_path_length] = '\0'; + FileSpec parent_dirs = exec_fspec; - char *last_dot = strrchr(path, '.'); - while (last_dot != NULL && last_dot[0]) { - char *next_slash = strchr(last_dot, '/'); - if (next_slash != NULL) { - *next_slash = '\0'; - ::strncat(path, ".dSYM/Contents/Resources/DWARF/", - sizeof(path) - strlen(path) - 1); - ::strncat(path, exec_fspec->GetFilename().AsCString(), - sizeof(path) - strlen(path) - 1); - dsym_fspec.SetFile(path, false); + // Remove the binary name from the FileSpec + parent_dirs.RemoveLastPathComponent(); + + // Add a ".dSYM" name to each directory component of the path, stripping + // off components. e.g. we may have a binary like + // /S/L/F/Foundation.framework/Versions/A/Foundation + // and + // /S/L/F/Foundation.framework.dSYM + // + // so we'll need to start with /S/L/F/Foundation.framework/Versions/A, + // add the .dSYM part to the "A", and if that doesn't exist, strip off + // the "A" and try it again with "Versions", etc., until we find a dSYM + // bundle or we've stripped off enough path components that there's no + // need to continue. + + for (int i = 0; i < 4; i++) { + // Does this part of the path have a "." character - could it be a bundle's + // top level directory? + const char *fn = parent_dirs.GetFilename().AsCString(); + if (fn == nullptr) + break; + if (::strchr (fn, '.') != nullptr) { + dsym_fspec = parent_dirs; + dsym_fspec.RemoveLastPathComponent(); + + // If the current directory name is "Foundation.framework", see if + // "Foundation.framework.dSYM/Contents/Resources/DWARF/Foundation" + // exists & has the right uuid. + std::string dsym_fn = fn; + dsym_fn += ".dSYM"; + dsym_fspec.AppendPathComponent(dsym_fn.c_str()); + dsym_fspec.AppendPathComponent("Contents"); + dsym_fspec.AppendPathComponent("Resources"); + dsym_fspec.AppendPathComponent("DWARF"); + dsym_fspec.AppendPathComponent(exec_fspec->GetFilename().AsCString()); if (dsym_fspec.Exists() && - FileAtPathContainsArchAndUUID( - dsym_fspec, module_spec.GetArchitecturePtr(), - module_spec.GetUUIDPtr())) { - if (log) { - log->Printf("dSYM with matching UUID & arch found at %s", - path); - } - return true; - } else { - *last_dot = '\0'; - char *prev_slash = strrchr(path, '/'); - if (prev_slash != NULL) - *prev_slash = '\0'; - else - break; + FileAtPathContainsArchAndUUID( + dsym_fspec, module_spec.GetArchitecturePtr(), + module_spec.GetUUIDPtr())) { + if (log) { + log->Printf("dSYM with matching UUID & arch found at %s", + dsym_fspec.GetPath().c_str()); + } + return true; } - } else { - break; } + parent_dirs.RemoveLastPathComponent(); } } } } } dsym_fspec.Clear(); return false; } FileSpec LocateExecutableSymbolFileDsym(const ModuleSpec &module_spec) { const FileSpec *exec_fspec = module_spec.GetFileSpecPtr(); const ArchSpec *arch = module_spec.GetArchitecturePtr(); const UUID *uuid = module_spec.GetUUIDPtr(); static Timer::Category func_cat(LLVM_PRETTY_FUNCTION); Timer scoped_timer( func_cat, "LocateExecutableSymbolFileDsym (file = %s, arch = %s, uuid = %p)", exec_fspec ? exec_fspec->GetFilename().AsCString("") : "", arch ? arch->GetArchitectureName() : "", (const void *)uuid); FileSpec symbol_fspec; ModuleSpec dsym_module_spec; // First try and find the dSYM in the same directory as the executable or in // an appropriate parent directory if (LocateDSYMInVincinityOfExecutable(module_spec, symbol_fspec) == false) { // We failed to easily find the dSYM above, so use DebugSymbols LocateMacOSXFilesUsingDebugSymbols(module_spec, dsym_module_spec); } else { dsym_module_spec.GetSymbolFileSpec() = symbol_fspec; } return dsym_module_spec.GetSymbolFileSpec(); } ModuleSpec Symbols::LocateExecutableObjectFile(const ModuleSpec &module_spec) { ModuleSpec result; const FileSpec *exec_fspec = module_spec.GetFileSpecPtr(); const ArchSpec *arch = module_spec.GetArchitecturePtr(); const UUID *uuid = module_spec.GetUUIDPtr(); static Timer::Category func_cat(LLVM_PRETTY_FUNCTION); Timer scoped_timer( func_cat, "LocateExecutableObjectFile (file = %s, arch = %s, uuid = %p)", exec_fspec ? exec_fspec->GetFilename().AsCString("") : "", arch ? arch->GetArchitectureName() : "", (const void *)uuid); ModuleSpecList module_specs; ModuleSpec matched_module_spec; if (exec_fspec && ObjectFile::GetModuleSpecifications(*exec_fspec, 0, 0, module_specs) && module_specs.FindMatchingModuleSpec(module_spec, matched_module_spec)) { result.GetFileSpec() = exec_fspec; } else { LocateMacOSXFilesUsingDebugSymbols(module_spec, result); } return result; } FileSpec Symbols::LocateExecutableSymbolFile(const ModuleSpec &module_spec) { FileSpec symbol_file_spec = module_spec.GetSymbolFileSpec(); if (symbol_file_spec.IsAbsolute() && symbol_file_spec.Exists()) return symbol_file_spec; const char *symbol_filename = symbol_file_spec.GetFilename().AsCString(); if (symbol_filename && symbol_filename[0]) { FileSpecList debug_file_search_paths( Target::GetDefaultDebugFileSearchPaths()); // Add module directory. const ConstString &file_dir = module_spec.GetFileSpec().GetDirectory(); debug_file_search_paths.AppendIfUnique( FileSpec(file_dir.AsCString("."), true)); // Add current working directory. debug_file_search_paths.AppendIfUnique(FileSpec(".", true)); #ifndef LLVM_ON_WIN32 #if defined(__NetBSD__) // Add /usr/libdata/debug directory. debug_file_search_paths.AppendIfUnique(FileSpec("/usr/libdata/debug", true)); #else // Add /usr/lib/debug directory. debug_file_search_paths.AppendIfUnique(FileSpec("/usr/lib/debug", true)); #endif #endif // LLVM_ON_WIN32 std::string uuid_str; const UUID &module_uuid = module_spec.GetUUID(); if (module_uuid.IsValid()) { // Some debug files are stored in the .build-id directory like this: // /usr/lib/debug/.build-id/ff/e7fe727889ad82bb153de2ad065b2189693315.debug uuid_str = module_uuid.GetAsString(""); uuid_str.insert(2, 1, '/'); uuid_str = uuid_str + ".debug"; } size_t num_directories = debug_file_search_paths.GetSize(); for (size_t idx = 0; idx < num_directories; ++idx) { FileSpec dirspec = debug_file_search_paths.GetFileSpecAtIndex(idx); dirspec.ResolvePath(); if (!llvm::sys::fs::is_directory(dirspec.GetPath())) continue; std::vector files; std::string dirname = dirspec.GetPath(); files.push_back(dirname + "/" + symbol_filename); files.push_back(dirname + "/.debug/" + symbol_filename); files.push_back(dirname + "/.build-id/" + uuid_str); // Some debug files may stored in the module directory like this: // /usr/lib/debug/usr/lib/library.so.debug if (!file_dir.IsEmpty()) files.push_back(dirname + file_dir.AsCString() + "/" + symbol_filename); const uint32_t num_files = files.size(); for (size_t idx_file = 0; idx_file < num_files; ++idx_file) { const std::string &filename = files[idx_file]; FileSpec file_spec(filename, true); if (llvm::sys::fs::equivalent(file_spec.GetPath(), module_spec.GetFileSpec().GetPath())) continue; if (file_spec.Exists()) { lldb_private::ModuleSpecList specs; const size_t num_specs = ObjectFile::GetModuleSpecifications(file_spec, 0, 0, specs); assert(num_specs <= 1 && "Symbol Vendor supports only a single architecture"); if (num_specs == 1) { ModuleSpec mspec; if (specs.GetModuleSpecAtIndex(0, mspec)) { if (mspec.GetUUID() == module_uuid) return file_spec; } } } } } } return LocateExecutableSymbolFileDsym(module_spec); } #if !defined(__APPLE__) FileSpec Symbols::FindSymbolFileInBundle(const FileSpec &symfile_bundle, const lldb_private::UUID *uuid, const ArchSpec *arch) { // FIXME return FileSpec(); } bool Symbols::DownloadObjectAndSymbolFile(ModuleSpec &module_spec, bool force_lookup) { // Fill in the module_spec.GetFileSpec() for the object file and/or the // module_spec.GetSymbolFileSpec() for the debug symbols file. return false; } #endif Index: vendor/lldb/dist/source/Target/StackFrame.cpp =================================================================== --- vendor/lldb/dist/source/Target/StackFrame.cpp (revision 320022) +++ vendor/lldb/dist/source/Target/StackFrame.cpp (revision 320023) @@ -1,1916 +1,1920 @@ //===-- StackFrame.cpp ------------------------------------------*- C++ -*-===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// // C Includes // C++ Includes // Other libraries and framework includes // Project includes #include "lldb/Target/StackFrame.h" #include "lldb/Core/Debugger.h" #include "lldb/Core/Disassembler.h" #include "lldb/Core/FormatEntity.h" #include "lldb/Core/Mangled.h" #include "lldb/Core/Module.h" #include "lldb/Core/RegisterValue.h" #include "lldb/Core/Value.h" #include "lldb/Core/ValueObjectConstResult.h" #include "lldb/Core/ValueObjectMemory.h" #include "lldb/Core/ValueObjectVariable.h" #include "lldb/Symbol/CompileUnit.h" #include "lldb/Symbol/Function.h" #include "lldb/Symbol/Symbol.h" #include "lldb/Symbol/SymbolContextScope.h" #include "lldb/Symbol/Type.h" #include "lldb/Symbol/VariableList.h" #include "lldb/Target/ABI.h" #include "lldb/Target/ExecutionContext.h" #include "lldb/Target/Process.h" #include "lldb/Target/RegisterContext.h" #include "lldb/Target/Target.h" #include "lldb/Target/Thread.h" using namespace lldb; using namespace lldb_private; // The first bits in the flags are reserved for the SymbolContext::Scope bits // so we know if we have tried to look up information in our internal symbol // context (m_sc) already. #define RESOLVED_FRAME_CODE_ADDR (uint32_t(eSymbolContextEverything + 1)) #define RESOLVED_FRAME_ID_SYMBOL_SCOPE (RESOLVED_FRAME_CODE_ADDR << 1) #define GOT_FRAME_BASE (RESOLVED_FRAME_ID_SYMBOL_SCOPE << 1) #define RESOLVED_VARIABLES (GOT_FRAME_BASE << 1) #define RESOLVED_GLOBAL_VARIABLES (RESOLVED_VARIABLES << 1) StackFrame::StackFrame(const ThreadSP &thread_sp, user_id_t frame_idx, user_id_t unwind_frame_index, addr_t cfa, bool cfa_is_valid, addr_t pc, uint32_t stop_id, bool stop_id_is_valid, bool is_history_frame, const SymbolContext *sc_ptr) : m_thread_wp(thread_sp), m_frame_index(frame_idx), m_concrete_frame_index(unwind_frame_index), m_reg_context_sp(), m_id(pc, cfa, nullptr), m_frame_code_addr(pc), m_sc(), m_flags(), m_frame_base(), m_frame_base_error(), m_cfa_is_valid(cfa_is_valid), m_stop_id(stop_id), m_stop_id_is_valid(stop_id_is_valid), m_is_history_frame(is_history_frame), m_variable_list_sp(), m_variable_list_value_objects(), m_disassembly(), m_mutex() { // If we don't have a CFA value, use the frame index for our StackID so that // recursive // functions properly aren't confused with one another on a history stack. if (m_is_history_frame && !m_cfa_is_valid) { m_id.SetCFA(m_frame_index); } if (sc_ptr != nullptr) { m_sc = *sc_ptr; m_flags.Set(m_sc.GetResolvedMask()); } } StackFrame::StackFrame(const ThreadSP &thread_sp, user_id_t frame_idx, user_id_t unwind_frame_index, const RegisterContextSP ®_context_sp, addr_t cfa, addr_t pc, const SymbolContext *sc_ptr) : m_thread_wp(thread_sp), m_frame_index(frame_idx), m_concrete_frame_index(unwind_frame_index), m_reg_context_sp(reg_context_sp), m_id(pc, cfa, nullptr), m_frame_code_addr(pc), m_sc(), m_flags(), m_frame_base(), m_frame_base_error(), m_cfa_is_valid(true), m_stop_id(0), m_stop_id_is_valid(false), m_is_history_frame(false), m_variable_list_sp(), m_variable_list_value_objects(), m_disassembly(), m_mutex() { if (sc_ptr != nullptr) { m_sc = *sc_ptr; m_flags.Set(m_sc.GetResolvedMask()); } if (reg_context_sp && !m_sc.target_sp) { m_sc.target_sp = reg_context_sp->CalculateTarget(); if (m_sc.target_sp) m_flags.Set(eSymbolContextTarget); } } StackFrame::StackFrame(const ThreadSP &thread_sp, user_id_t frame_idx, user_id_t unwind_frame_index, const RegisterContextSP ®_context_sp, addr_t cfa, const Address &pc_addr, const SymbolContext *sc_ptr) : m_thread_wp(thread_sp), m_frame_index(frame_idx), m_concrete_frame_index(unwind_frame_index), m_reg_context_sp(reg_context_sp), m_id(pc_addr.GetLoadAddress(thread_sp->CalculateTarget().get()), cfa, nullptr), m_frame_code_addr(pc_addr), m_sc(), m_flags(), m_frame_base(), m_frame_base_error(), m_cfa_is_valid(true), m_stop_id(0), m_stop_id_is_valid(false), m_is_history_frame(false), m_variable_list_sp(), m_variable_list_value_objects(), m_disassembly(), m_mutex() { if (sc_ptr != nullptr) { m_sc = *sc_ptr; m_flags.Set(m_sc.GetResolvedMask()); } if (!m_sc.target_sp && reg_context_sp) { m_sc.target_sp = reg_context_sp->CalculateTarget(); if (m_sc.target_sp) m_flags.Set(eSymbolContextTarget); } ModuleSP pc_module_sp(pc_addr.GetModule()); if (!m_sc.module_sp || m_sc.module_sp != pc_module_sp) { if (pc_module_sp) { m_sc.module_sp = pc_module_sp; m_flags.Set(eSymbolContextModule); } else { m_sc.module_sp.reset(); } } } StackFrame::~StackFrame() = default; StackID &StackFrame::GetStackID() { std::lock_guard guard(m_mutex); // Make sure we have resolved the StackID object's symbol context scope if // we already haven't looked it up. if (m_flags.IsClear(RESOLVED_FRAME_ID_SYMBOL_SCOPE)) { if (m_id.GetSymbolContextScope()) { // We already have a symbol context scope, we just don't have our // flag bit set. m_flags.Set(RESOLVED_FRAME_ID_SYMBOL_SCOPE); } else { // Calculate the frame block and use this for the stack ID symbol // context scope if we have one. SymbolContextScope *scope = GetFrameBlock(); if (scope == nullptr) { // We don't have a block, so use the symbol if (m_flags.IsClear(eSymbolContextSymbol)) GetSymbolContext(eSymbolContextSymbol); // It is ok if m_sc.symbol is nullptr here scope = m_sc.symbol; } // Set the symbol context scope (the accessor will set the // RESOLVED_FRAME_ID_SYMBOL_SCOPE bit in m_flags). SetSymbolContextScope(scope); } } return m_id; } uint32_t StackFrame::GetFrameIndex() const { ThreadSP thread_sp = GetThread(); if (thread_sp) return thread_sp->GetStackFrameList()->GetVisibleStackFrameIndex( m_frame_index); else return m_frame_index; } void StackFrame::SetSymbolContextScope(SymbolContextScope *symbol_scope) { std::lock_guard guard(m_mutex); m_flags.Set(RESOLVED_FRAME_ID_SYMBOL_SCOPE); m_id.SetSymbolContextScope(symbol_scope); } const Address &StackFrame::GetFrameCodeAddress() { std::lock_guard guard(m_mutex); if (m_flags.IsClear(RESOLVED_FRAME_CODE_ADDR) && !m_frame_code_addr.IsSectionOffset()) { m_flags.Set(RESOLVED_FRAME_CODE_ADDR); // Resolve the PC into a temporary address because if ResolveLoadAddress // fails to resolve the address, it will clear the address object... ThreadSP thread_sp(GetThread()); if (thread_sp) { TargetSP target_sp(thread_sp->CalculateTarget()); if (target_sp) { const bool allow_section_end = true; if (m_frame_code_addr.SetOpcodeLoadAddress( m_frame_code_addr.GetOffset(), target_sp.get(), eAddressClassCode, allow_section_end)) { ModuleSP module_sp(m_frame_code_addr.GetModule()); if (module_sp) { m_sc.module_sp = module_sp; m_flags.Set(eSymbolContextModule); } } } } } return m_frame_code_addr; } bool StackFrame::ChangePC(addr_t pc) { std::lock_guard guard(m_mutex); // We can't change the pc value of a history stack frame - it is immutable. if (m_is_history_frame) return false; m_frame_code_addr.SetRawAddress(pc); m_sc.Clear(false); m_flags.Reset(0); ThreadSP thread_sp(GetThread()); if (thread_sp) thread_sp->ClearStackFrames(); return true; } const char *StackFrame::Disassemble() { std::lock_guard guard(m_mutex); if (m_disassembly.Empty()) { ExecutionContext exe_ctx(shared_from_this()); Target *target = exe_ctx.GetTargetPtr(); if (target) { const char *plugin_name = nullptr; const char *flavor = nullptr; Disassembler::Disassemble(target->GetDebugger(), target->GetArchitecture(), plugin_name, flavor, exe_ctx, 0, false, 0, 0, m_disassembly); } if (m_disassembly.Empty()) return nullptr; } return m_disassembly.GetData(); } Block *StackFrame::GetFrameBlock() { if (m_sc.block == nullptr && m_flags.IsClear(eSymbolContextBlock)) GetSymbolContext(eSymbolContextBlock); if (m_sc.block) { Block *inline_block = m_sc.block->GetContainingInlinedBlock(); if (inline_block) { // Use the block with the inlined function info // as the frame block we want this frame to have only the variables // for the inlined function and its non-inlined block child blocks. return inline_block; } else { // This block is not contained within any inlined function blocks // with so we want to use the top most function block. return &m_sc.function->GetBlock(false); } } return nullptr; } //---------------------------------------------------------------------- // Get the symbol context if we already haven't done so by resolving the // PC address as much as possible. This way when we pass around a // StackFrame object, everyone will have as much information as // possible and no one will ever have to look things up manually. //---------------------------------------------------------------------- const SymbolContext &StackFrame::GetSymbolContext(uint32_t resolve_scope) { std::lock_guard guard(m_mutex); // Copy our internal symbol context into "sc". if ((m_flags.Get() & resolve_scope) != resolve_scope) { uint32_t resolved = 0; // If the target was requested add that: if (!m_sc.target_sp) { m_sc.target_sp = CalculateTarget(); if (m_sc.target_sp) resolved |= eSymbolContextTarget; } // Resolve our PC to section offset if we haven't already done so // and if we don't have a module. The resolved address section will // contain the module to which it belongs if (!m_sc.module_sp && m_flags.IsClear(RESOLVED_FRAME_CODE_ADDR)) GetFrameCodeAddress(); // If this is not frame zero, then we need to subtract 1 from the PC // value when doing address lookups since the PC will be on the // instruction following the function call instruction... Address lookup_addr(GetFrameCodeAddress()); if (m_frame_index > 0 && lookup_addr.IsValid()) { addr_t offset = lookup_addr.GetOffset(); if (offset > 0) { lookup_addr.SetOffset(offset - 1); } else { // lookup_addr is the start of a section. We need // do the math on the actual load address and re-compute // the section. We're working with a 'noreturn' function // at the end of a section. ThreadSP thread_sp(GetThread()); if (thread_sp) { TargetSP target_sp(thread_sp->CalculateTarget()); if (target_sp) { addr_t addr_minus_one = lookup_addr.GetLoadAddress(target_sp.get()) - 1; lookup_addr.SetLoadAddress(addr_minus_one, target_sp.get()); } else { lookup_addr.SetOffset(offset - 1); } } } } if (m_sc.module_sp) { // We have something in our stack frame symbol context, lets check // if we haven't already tried to lookup one of those things. If we // haven't then we will do the query. uint32_t actual_resolve_scope = 0; if (resolve_scope & eSymbolContextCompUnit) { if (m_flags.IsClear(eSymbolContextCompUnit)) { if (m_sc.comp_unit) resolved |= eSymbolContextCompUnit; else actual_resolve_scope |= eSymbolContextCompUnit; } } if (resolve_scope & eSymbolContextFunction) { if (m_flags.IsClear(eSymbolContextFunction)) { if (m_sc.function) resolved |= eSymbolContextFunction; else actual_resolve_scope |= eSymbolContextFunction; } } if (resolve_scope & eSymbolContextBlock) { if (m_flags.IsClear(eSymbolContextBlock)) { if (m_sc.block) resolved |= eSymbolContextBlock; else actual_resolve_scope |= eSymbolContextBlock; } } if (resolve_scope & eSymbolContextSymbol) { if (m_flags.IsClear(eSymbolContextSymbol)) { if (m_sc.symbol) resolved |= eSymbolContextSymbol; else actual_resolve_scope |= eSymbolContextSymbol; } } if (resolve_scope & eSymbolContextLineEntry) { if (m_flags.IsClear(eSymbolContextLineEntry)) { if (m_sc.line_entry.IsValid()) resolved |= eSymbolContextLineEntry; else actual_resolve_scope |= eSymbolContextLineEntry; } } if (actual_resolve_scope) { // We might be resolving less information than what is already // in our current symbol context so resolve into a temporary // symbol context "sc" so we don't clear out data we have // already found in "m_sc" SymbolContext sc; // Set flags that indicate what we have tried to resolve resolved |= m_sc.module_sp->ResolveSymbolContextForAddress( lookup_addr, actual_resolve_scope, sc); // Only replace what we didn't already have as we may have // information for an inlined function scope that won't match // what a standard lookup by address would match if ((resolved & eSymbolContextCompUnit) && m_sc.comp_unit == nullptr) m_sc.comp_unit = sc.comp_unit; if ((resolved & eSymbolContextFunction) && m_sc.function == nullptr) m_sc.function = sc.function; if ((resolved & eSymbolContextBlock) && m_sc.block == nullptr) m_sc.block = sc.block; if ((resolved & eSymbolContextSymbol) && m_sc.symbol == nullptr) m_sc.symbol = sc.symbol; if ((resolved & eSymbolContextLineEntry) && !m_sc.line_entry.IsValid()) { m_sc.line_entry = sc.line_entry; m_sc.line_entry.ApplyFileMappings(m_sc.target_sp); } } } else { // If we don't have a module, then we can't have the compile unit, // function, block, line entry or symbol, so we can safely call // ResolveSymbolContextForAddress with our symbol context member m_sc. if (m_sc.target_sp) { resolved |= m_sc.target_sp->GetImages().ResolveSymbolContextForAddress( lookup_addr, resolve_scope, m_sc); } } // Update our internal flags so we remember what we have tried to locate so // we don't have to keep trying when more calls to this function are made. // We might have dug up more information that was requested (for example // if we were asked to only get the block, we will have gotten the // compile unit, and function) so set any additional bits that we resolved m_flags.Set(resolve_scope | resolved); } // Return the symbol context with everything that was possible to resolve // resolved. return m_sc; } VariableList *StackFrame::GetVariableList(bool get_file_globals) { std::lock_guard guard(m_mutex); if (m_flags.IsClear(RESOLVED_VARIABLES)) { m_flags.Set(RESOLVED_VARIABLES); Block *frame_block = GetFrameBlock(); if (frame_block) { const bool get_child_variables = true; const bool can_create = true; const bool stop_if_child_block_is_inlined_function = true; m_variable_list_sp.reset(new VariableList()); frame_block->AppendBlockVariables(can_create, get_child_variables, stop_if_child_block_is_inlined_function, [](Variable *v) { return true; }, m_variable_list_sp.get()); } } if (m_flags.IsClear(RESOLVED_GLOBAL_VARIABLES) && get_file_globals) { m_flags.Set(RESOLVED_GLOBAL_VARIABLES); if (m_flags.IsClear(eSymbolContextCompUnit)) GetSymbolContext(eSymbolContextCompUnit); if (m_sc.comp_unit) { VariableListSP global_variable_list_sp( m_sc.comp_unit->GetVariableList(true)); if (m_variable_list_sp) m_variable_list_sp->AddVariables(global_variable_list_sp.get()); else m_variable_list_sp = global_variable_list_sp; } } return m_variable_list_sp.get(); } VariableListSP StackFrame::GetInScopeVariableList(bool get_file_globals, bool must_have_valid_location) { std::lock_guard guard(m_mutex); // We can't fetch variable information for a history stack frame. if (m_is_history_frame) return VariableListSP(); VariableListSP var_list_sp(new VariableList); GetSymbolContext(eSymbolContextCompUnit | eSymbolContextBlock); if (m_sc.block) { const bool can_create = true; const bool get_parent_variables = true; const bool stop_if_block_is_inlined_function = true; m_sc.block->AppendVariables( can_create, get_parent_variables, stop_if_block_is_inlined_function, [this, must_have_valid_location](Variable *v) { return v->IsInScope(this) && (!must_have_valid_location || v->LocationIsValidForFrame(this)); }, var_list_sp.get()); } if (m_sc.comp_unit && get_file_globals) { VariableListSP global_variable_list_sp( m_sc.comp_unit->GetVariableList(true)); if (global_variable_list_sp) var_list_sp->AddVariables(global_variable_list_sp.get()); } return var_list_sp; } ValueObjectSP StackFrame::GetValueForVariableExpressionPath( llvm::StringRef var_expr, DynamicValueType use_dynamic, uint32_t options, VariableSP &var_sp, Status &error) { llvm::StringRef original_var_expr = var_expr; // We can't fetch variable information for a history stack frame. if (m_is_history_frame) return ValueObjectSP(); if (var_expr.empty()) { error.SetErrorStringWithFormat("invalid variable path '%s'", var_expr.str().c_str()); return ValueObjectSP(); } const bool check_ptr_vs_member = (options & eExpressionPathOptionCheckPtrVsMember) != 0; const bool no_fragile_ivar = (options & eExpressionPathOptionsNoFragileObjcIvar) != 0; const bool no_synth_child = (options & eExpressionPathOptionsNoSyntheticChildren) != 0; // const bool no_synth_array = (options & // eExpressionPathOptionsNoSyntheticArrayRange) != 0; error.Clear(); bool deref = false; bool address_of = false; ValueObjectSP valobj_sp; const bool get_file_globals = true; // When looking up a variable for an expression, we need only consider the // variables that are in scope. VariableListSP var_list_sp(GetInScopeVariableList(get_file_globals)); VariableList *variable_list = var_list_sp.get(); if (!variable_list) return ValueObjectSP(); // If first character is a '*', then show pointer contents std::string var_expr_storage; if (var_expr[0] == '*') { deref = true; var_expr = var_expr.drop_front(); // Skip the '*' } else if (var_expr[0] == '&') { address_of = true; var_expr = var_expr.drop_front(); // Skip the '&' } size_t separator_idx = var_expr.find_first_of(".-[=+~|&^%#@!/?,<>{}"); StreamString var_expr_path_strm; ConstString name_const_string(var_expr.substr(0, separator_idx)); var_sp = variable_list->FindVariable(name_const_string, false); bool synthetically_added_instance_object = false; if (var_sp) { var_expr = var_expr.drop_front(name_const_string.GetLength()); } if (!var_sp && (options & eExpressionPathOptionsAllowDirectIVarAccess)) { // Check for direct ivars access which helps us with implicit // access to ivars with the "this->" or "self->" GetSymbolContext(eSymbolContextFunction | eSymbolContextBlock); lldb::LanguageType method_language = eLanguageTypeUnknown; bool is_instance_method = false; ConstString method_object_name; if (m_sc.GetFunctionMethodInfo(method_language, is_instance_method, method_object_name)) { if (is_instance_method && method_object_name) { var_sp = variable_list->FindVariable(method_object_name); if (var_sp) { separator_idx = 0; var_expr_storage = "->"; var_expr_storage += var_expr; var_expr = var_expr_storage; synthetically_added_instance_object = true; } } } } if (!var_sp && (options & eExpressionPathOptionsInspectAnonymousUnions)) { // Check if any anonymous unions are there which contain a variable with // the name we need for (size_t i = 0; i < variable_list->GetSize(); i++) { VariableSP variable_sp = variable_list->GetVariableAtIndex(i); if (!variable_sp) continue; if (!variable_sp->GetName().IsEmpty()) continue; Type *var_type = variable_sp->GetType(); if (!var_type) continue; if (!var_type->GetForwardCompilerType().IsAnonymousType()) continue; valobj_sp = GetValueObjectForFrameVariable(variable_sp, use_dynamic); if (!valobj_sp) return valobj_sp; valobj_sp = valobj_sp->GetChildMemberWithName(name_const_string, true); if (valobj_sp) break; } } if (var_sp && !valobj_sp) { valobj_sp = GetValueObjectForFrameVariable(var_sp, use_dynamic); if (!valobj_sp) return valobj_sp; } if (!valobj_sp) { error.SetErrorStringWithFormat("no variable named '%s' found in this frame", name_const_string.GetCString()); return ValueObjectSP(); } // We are dumping at least one child while (separator_idx != std::string::npos) { // Calculate the next separator index ahead of time ValueObjectSP child_valobj_sp; const char separator_type = var_expr[0]; bool expr_is_ptr = false; switch (separator_type) { case '-': expr_is_ptr = true; if (var_expr.size() >= 2 && var_expr[1] != '>') return ValueObjectSP(); if (no_fragile_ivar) { // Make sure we aren't trying to deref an objective // C ivar if this is not allowed const uint32_t pointer_type_flags = valobj_sp->GetCompilerType().GetTypeInfo(nullptr); if ((pointer_type_flags & eTypeIsObjC) && (pointer_type_flags & eTypeIsPointer)) { // This was an objective C object pointer and // it was requested we skip any fragile ivars // so return nothing here return ValueObjectSP(); } } // If we have a non pointer type with a sythetic value then lets check if // we have an sythetic dereference specified. if (!valobj_sp->IsPointerType() && valobj_sp->HasSyntheticValue()) { Status deref_error; if (valobj_sp->GetCompilerType().IsReferenceType()) { valobj_sp = valobj_sp->GetSyntheticValue()->Dereference(deref_error); if (error.Fail()) { error.SetErrorStringWithFormatv( "Failed to dereference reference type: %s", deref_error); return ValueObjectSP(); } } valobj_sp = valobj_sp->Dereference(deref_error); if (error.Fail()) { error.SetErrorStringWithFormatv( "Failed to dereference sythetic value: %s", deref_error); return ValueObjectSP(); } expr_is_ptr = false; } var_expr = var_expr.drop_front(); // Remove the '-' LLVM_FALLTHROUGH; case '.': { var_expr = var_expr.drop_front(); // Remove the '.' or '>' separator_idx = var_expr.find_first_of(".-["); ConstString child_name(var_expr.substr(0, var_expr.find_first_of(".-["))); if (check_ptr_vs_member) { // We either have a pointer type and need to verify // valobj_sp is a pointer, or we have a member of a // class/union/struct being accessed with the . syntax // and need to verify we don't have a pointer. const bool actual_is_ptr = valobj_sp->IsPointerType(); if (actual_is_ptr != expr_is_ptr) { // Incorrect use of "." with a pointer, or "->" with // a class/union/struct instance or reference. valobj_sp->GetExpressionPath(var_expr_path_strm, false); if (actual_is_ptr) error.SetErrorStringWithFormat( "\"%s\" is a pointer and . was used to attempt to access " "\"%s\". Did you mean \"%s->%s\"?", var_expr_path_strm.GetData(), child_name.GetCString(), var_expr_path_strm.GetData(), var_expr.str().c_str()); else error.SetErrorStringWithFormat( "\"%s\" is not a pointer and -> was used to attempt to " "access \"%s\". Did you mean \"%s.%s\"?", var_expr_path_strm.GetData(), child_name.GetCString(), var_expr_path_strm.GetData(), var_expr.str().c_str()); return ValueObjectSP(); } } child_valobj_sp = valobj_sp->GetChildMemberWithName(child_name, true); if (!child_valobj_sp) { if (!no_synth_child) { child_valobj_sp = valobj_sp->GetSyntheticValue(); if (child_valobj_sp) child_valobj_sp = child_valobj_sp->GetChildMemberWithName(child_name, true); } if (no_synth_child || !child_valobj_sp) { // No child member with name "child_name" if (synthetically_added_instance_object) { // We added a "this->" or "self->" to the beginning of the // expression // and this is the first pointer ivar access, so just return // the normal // error error.SetErrorStringWithFormat( "no variable or instance variable named '%s' found in " "this frame", name_const_string.GetCString()); } else { valobj_sp->GetExpressionPath(var_expr_path_strm, false); if (child_name) { error.SetErrorStringWithFormat( "\"%s\" is not a member of \"(%s) %s\"", child_name.GetCString(), valobj_sp->GetTypeName().AsCString(""), var_expr_path_strm.GetData()); } else { error.SetErrorStringWithFormat( "incomplete expression path after \"%s\" in \"%s\"", var_expr_path_strm.GetData(), original_var_expr.str().c_str()); } } return ValueObjectSP(); } } synthetically_added_instance_object = false; // Remove the child name from the path var_expr = var_expr.drop_front(child_name.GetLength()); if (use_dynamic != eNoDynamicValues) { ValueObjectSP dynamic_value_sp( child_valobj_sp->GetDynamicValue(use_dynamic)); if (dynamic_value_sp) child_valobj_sp = dynamic_value_sp; } } break; case '[': { // Array member access, or treating pointer as an array // Need at least two brackets and a number if (var_expr.size() <= 2) { error.SetErrorStringWithFormat( "invalid square bracket encountered after \"%s\" in \"%s\"", var_expr_path_strm.GetData(), var_expr.str().c_str()); return ValueObjectSP(); } // Drop the open brace. var_expr = var_expr.drop_front(); long child_index = 0; // If there's no closing brace, this is an invalid expression. size_t end_pos = var_expr.find_first_of(']'); if (end_pos == llvm::StringRef::npos) { error.SetErrorStringWithFormat( "missing closing square bracket in expression \"%s\"", var_expr_path_strm.GetData()); return ValueObjectSP(); } llvm::StringRef index_expr = var_expr.take_front(end_pos); llvm::StringRef original_index_expr = index_expr; // Drop all of "[index_expr]" var_expr = var_expr.drop_front(end_pos + 1); if (index_expr.consumeInteger(0, child_index)) { // If there was no integer anywhere in the index expression, this is // erroneous expression. error.SetErrorStringWithFormat("invalid index expression \"%s\"", index_expr.str().c_str()); return ValueObjectSP(); } if (index_expr.empty()) { // The entire index expression was a single integer. if (valobj_sp->GetCompilerType().IsPointerToScalarType() && deref) { // what we have is *ptr[low]. the most similar C++ syntax is to deref // ptr and extract bit low out of it. reading array item low would be // done by saying ptr[low], without a deref * sign Status error; ValueObjectSP temp(valobj_sp->Dereference(error)); if (error.Fail()) { valobj_sp->GetExpressionPath(var_expr_path_strm, false); error.SetErrorStringWithFormat( "could not dereference \"(%s) %s\"", valobj_sp->GetTypeName().AsCString(""), var_expr_path_strm.GetData()); return ValueObjectSP(); } valobj_sp = temp; deref = false; } else if (valobj_sp->GetCompilerType().IsArrayOfScalarType() && deref) { // what we have is *arr[low]. the most similar C++ syntax is // to get arr[0] // (an operation that is equivalent to deref-ing arr) // and extract bit low out of it. reading array item low // would be done by saying arr[low], without a deref * sign Status error; ValueObjectSP temp(valobj_sp->GetChildAtIndex(0, true)); if (error.Fail()) { valobj_sp->GetExpressionPath(var_expr_path_strm, false); error.SetErrorStringWithFormat( "could not get item 0 for \"(%s) %s\"", valobj_sp->GetTypeName().AsCString(""), var_expr_path_strm.GetData()); return ValueObjectSP(); } valobj_sp = temp; deref = false; } bool is_incomplete_array = false; if (valobj_sp->IsPointerType()) { bool is_objc_pointer = true; if (valobj_sp->GetCompilerType().GetMinimumLanguage() != eLanguageTypeObjC) is_objc_pointer = false; else if (!valobj_sp->GetCompilerType().IsPointerType()) is_objc_pointer = false; if (no_synth_child && is_objc_pointer) { error.SetErrorStringWithFormat( "\"(%s) %s\" is an Objective-C pointer, and cannot be " "subscripted", valobj_sp->GetTypeName().AsCString(""), var_expr_path_strm.GetData()); return ValueObjectSP(); } else if (is_objc_pointer) { // dereferencing ObjC variables is not valid.. so let's try // and recur to synthetic children ValueObjectSP synthetic = valobj_sp->GetSyntheticValue(); if (!synthetic /* no synthetic */ || synthetic == valobj_sp) /* synthetic is the same as the original object */ { valobj_sp->GetExpressionPath(var_expr_path_strm, false); error.SetErrorStringWithFormat( "\"(%s) %s\" is not an array type", valobj_sp->GetTypeName().AsCString(""), var_expr_path_strm.GetData()); } else if ( static_cast(child_index) >= synthetic ->GetNumChildren() /* synthetic does not have that many values */) { valobj_sp->GetExpressionPath(var_expr_path_strm, false); error.SetErrorStringWithFormat( "array index %ld is not valid for \"(%s) %s\"", child_index, valobj_sp->GetTypeName().AsCString(""), var_expr_path_strm.GetData()); } else { child_valobj_sp = synthetic->GetChildAtIndex(child_index, true); if (!child_valobj_sp) { valobj_sp->GetExpressionPath(var_expr_path_strm, false); error.SetErrorStringWithFormat( "array index %ld is not valid for \"(%s) %s\"", child_index, valobj_sp->GetTypeName().AsCString(""), var_expr_path_strm.GetData()); } } } else { child_valobj_sp = valobj_sp->GetSyntheticArrayMember(child_index, true); if (!child_valobj_sp) { valobj_sp->GetExpressionPath(var_expr_path_strm, false); error.SetErrorStringWithFormat( "failed to use pointer as array for index %ld for " "\"(%s) %s\"", child_index, valobj_sp->GetTypeName().AsCString(""), var_expr_path_strm.GetData()); } } } else if (valobj_sp->GetCompilerType().IsArrayType( nullptr, nullptr, &is_incomplete_array)) { // Pass false to dynamic_value here so we can tell the // difference between // no dynamic value and no member of this type... child_valobj_sp = valobj_sp->GetChildAtIndex(child_index, true); if (!child_valobj_sp && (is_incomplete_array || !no_synth_child)) child_valobj_sp = valobj_sp->GetSyntheticArrayMember(child_index, true); if (!child_valobj_sp) { valobj_sp->GetExpressionPath(var_expr_path_strm, false); error.SetErrorStringWithFormat( "array index %ld is not valid for \"(%s) %s\"", child_index, valobj_sp->GetTypeName().AsCString(""), var_expr_path_strm.GetData()); } } else if (valobj_sp->GetCompilerType().IsScalarType()) { // this is a bitfield asking to display just one bit child_valobj_sp = valobj_sp->GetSyntheticBitFieldChild( child_index, child_index, true); if (!child_valobj_sp) { valobj_sp->GetExpressionPath(var_expr_path_strm, false); error.SetErrorStringWithFormat( "bitfield range %ld-%ld is not valid for \"(%s) %s\"", child_index, child_index, valobj_sp->GetTypeName().AsCString(""), var_expr_path_strm.GetData()); } } else { ValueObjectSP synthetic = valobj_sp->GetSyntheticValue(); if (no_synth_child /* synthetic is forbidden */ || !synthetic /* no synthetic */ || synthetic == valobj_sp) /* synthetic is the same as the original object */ { valobj_sp->GetExpressionPath(var_expr_path_strm, false); error.SetErrorStringWithFormat( "\"(%s) %s\" is not an array type", valobj_sp->GetTypeName().AsCString(""), var_expr_path_strm.GetData()); } else if ( static_cast(child_index) >= synthetic ->GetNumChildren() /* synthetic does not have that many values */) { valobj_sp->GetExpressionPath(var_expr_path_strm, false); error.SetErrorStringWithFormat( "array index %ld is not valid for \"(%s) %s\"", child_index, valobj_sp->GetTypeName().AsCString(""), var_expr_path_strm.GetData()); } else { child_valobj_sp = synthetic->GetChildAtIndex(child_index, true); if (!child_valobj_sp) { valobj_sp->GetExpressionPath(var_expr_path_strm, false); error.SetErrorStringWithFormat( "array index %ld is not valid for \"(%s) %s\"", child_index, valobj_sp->GetTypeName().AsCString(""), var_expr_path_strm.GetData()); } } } if (!child_valobj_sp) { // Invalid array index... return ValueObjectSP(); } separator_idx = var_expr.find_first_of(".-["); if (use_dynamic != eNoDynamicValues) { ValueObjectSP dynamic_value_sp( child_valobj_sp->GetDynamicValue(use_dynamic)); if (dynamic_value_sp) child_valobj_sp = dynamic_value_sp; } // Break out early from the switch since we were able to find the child // member break; } // this is most probably a BitField, let's take a look if (index_expr.front() != '-') { error.SetErrorStringWithFormat("invalid range expression \"'%s'\"", original_index_expr.str().c_str()); return ValueObjectSP(); } index_expr = index_expr.drop_front(); long final_index = 0; if (index_expr.getAsInteger(0, final_index)) { error.SetErrorStringWithFormat("invalid range expression \"'%s'\"", original_index_expr.str().c_str()); return ValueObjectSP(); } // if the format given is [high-low], swap range if (child_index > final_index) { long temp = child_index; child_index = final_index; final_index = temp; } if (valobj_sp->GetCompilerType().IsPointerToScalarType() && deref) { // what we have is *ptr[low-high]. the most similar C++ syntax is to // deref ptr and extract bits low thru high out of it. reading array // items low thru high would be done by saying ptr[low-high], without // a deref * sign Status error; ValueObjectSP temp(valobj_sp->Dereference(error)); if (error.Fail()) { valobj_sp->GetExpressionPath(var_expr_path_strm, false); error.SetErrorStringWithFormat( "could not dereference \"(%s) %s\"", valobj_sp->GetTypeName().AsCString(""), var_expr_path_strm.GetData()); return ValueObjectSP(); } valobj_sp = temp; deref = false; } else if (valobj_sp->GetCompilerType().IsArrayOfScalarType() && deref) { // what we have is *arr[low-high]. the most similar C++ syntax is to get // arr[0] (an operation that is equivalent to deref-ing arr) and extract // bits low thru high out of it. reading array items low thru high would // be done by saying arr[low-high], without a deref * sign Status error; ValueObjectSP temp(valobj_sp->GetChildAtIndex(0, true)); if (error.Fail()) { valobj_sp->GetExpressionPath(var_expr_path_strm, false); error.SetErrorStringWithFormat( "could not get item 0 for \"(%s) %s\"", valobj_sp->GetTypeName().AsCString(""), var_expr_path_strm.GetData()); return ValueObjectSP(); } valobj_sp = temp; deref = false; } child_valobj_sp = valobj_sp->GetSyntheticBitFieldChild(child_index, final_index, true); if (!child_valobj_sp) { valobj_sp->GetExpressionPath(var_expr_path_strm, false); error.SetErrorStringWithFormat( "bitfield range %ld-%ld is not valid for \"(%s) %s\"", child_index, final_index, valobj_sp->GetTypeName().AsCString(""), var_expr_path_strm.GetData()); } if (!child_valobj_sp) { // Invalid bitfield range... return ValueObjectSP(); } separator_idx = var_expr.find_first_of(".-["); if (use_dynamic != eNoDynamicValues) { ValueObjectSP dynamic_value_sp( child_valobj_sp->GetDynamicValue(use_dynamic)); if (dynamic_value_sp) child_valobj_sp = dynamic_value_sp; } // Break out early from the switch since we were able to find the child // member break; } default: // Failure... { valobj_sp->GetExpressionPath(var_expr_path_strm, false); error.SetErrorStringWithFormat( "unexpected char '%c' encountered after \"%s\" in \"%s\"", separator_type, var_expr_path_strm.GetData(), var_expr.str().c_str()); return ValueObjectSP(); } } if (child_valobj_sp) valobj_sp = child_valobj_sp; if (var_expr.empty()) break; } if (valobj_sp) { if (deref) { ValueObjectSP deref_valobj_sp(valobj_sp->Dereference(error)); valobj_sp = deref_valobj_sp; } else if (address_of) { ValueObjectSP address_of_valobj_sp(valobj_sp->AddressOf(error)); valobj_sp = address_of_valobj_sp; } } return valobj_sp; } bool StackFrame::GetFrameBaseValue(Scalar &frame_base, Status *error_ptr) { std::lock_guard guard(m_mutex); if (!m_cfa_is_valid) { m_frame_base_error.SetErrorString( "No frame base available for this historical stack frame."); return false; } if (m_flags.IsClear(GOT_FRAME_BASE)) { if (m_sc.function) { m_frame_base.Clear(); m_frame_base_error.Clear(); m_flags.Set(GOT_FRAME_BASE); ExecutionContext exe_ctx(shared_from_this()); Value expr_value; addr_t loclist_base_addr = LLDB_INVALID_ADDRESS; if (m_sc.function->GetFrameBaseExpression().IsLocationList()) loclist_base_addr = m_sc.function->GetAddressRange().GetBaseAddress().GetLoadAddress( exe_ctx.GetTargetPtr()); if (m_sc.function->GetFrameBaseExpression().Evaluate( &exe_ctx, nullptr, nullptr, nullptr, loclist_base_addr, nullptr, nullptr, expr_value, &m_frame_base_error) == false) { // We should really have an error if evaluate returns, but in case // we don't, lets set the error to something at least. if (m_frame_base_error.Success()) m_frame_base_error.SetErrorString( "Evaluation of the frame base expression failed."); } else { m_frame_base = expr_value.ResolveValue(&exe_ctx); } } else { m_frame_base_error.SetErrorString("No function in symbol context."); } } if (m_frame_base_error.Success()) frame_base = m_frame_base; if (error_ptr) *error_ptr = m_frame_base_error; return m_frame_base_error.Success(); } DWARFExpression *StackFrame::GetFrameBaseExpression(Status *error_ptr) { if (!m_sc.function) { if (error_ptr) { error_ptr->SetErrorString("No function in symbol context."); } return nullptr; } return &m_sc.function->GetFrameBaseExpression(); } RegisterContextSP StackFrame::GetRegisterContext() { std::lock_guard guard(m_mutex); if (!m_reg_context_sp) { ThreadSP thread_sp(GetThread()); if (thread_sp) m_reg_context_sp = thread_sp->CreateRegisterContextForFrame(this); } return m_reg_context_sp; } bool StackFrame::HasDebugInformation() { GetSymbolContext(eSymbolContextLineEntry); return m_sc.line_entry.IsValid(); } ValueObjectSP StackFrame::GetValueObjectForFrameVariable(const VariableSP &variable_sp, DynamicValueType use_dynamic) { std::lock_guard guard(m_mutex); ValueObjectSP valobj_sp; if (m_is_history_frame) { return valobj_sp; } VariableList *var_list = GetVariableList(true); if (var_list) { // Make sure the variable is a frame variable const uint32_t var_idx = var_list->FindIndexForVariable(variable_sp.get()); const uint32_t num_variables = var_list->GetSize(); if (var_idx < num_variables) { valobj_sp = m_variable_list_value_objects.GetValueObjectAtIndex(var_idx); if (!valobj_sp) { if (m_variable_list_value_objects.GetSize() < num_variables) m_variable_list_value_objects.Resize(num_variables); valobj_sp = ValueObjectVariable::Create(this, variable_sp); m_variable_list_value_objects.SetValueObjectAtIndex(var_idx, valobj_sp); } } } if (use_dynamic != eNoDynamicValues && valobj_sp) { ValueObjectSP dynamic_sp = valobj_sp->GetDynamicValue(use_dynamic); if (dynamic_sp) return dynamic_sp; } return valobj_sp; } ValueObjectSP StackFrame::TrackGlobalVariable(const VariableSP &variable_sp, DynamicValueType use_dynamic) { std::lock_guard guard(m_mutex); if (m_is_history_frame) return ValueObjectSP(); // Check to make sure we aren't already tracking this variable? ValueObjectSP valobj_sp( GetValueObjectForFrameVariable(variable_sp, use_dynamic)); if (!valobj_sp) { // We aren't already tracking this global VariableList *var_list = GetVariableList(true); // If this frame has no variables, create a new list if (var_list == nullptr) m_variable_list_sp.reset(new VariableList()); // Add the global/static variable to this frame m_variable_list_sp->AddVariable(variable_sp); // Now make a value object for it so we can track its changes valobj_sp = GetValueObjectForFrameVariable(variable_sp, use_dynamic); } return valobj_sp; } bool StackFrame::IsInlined() { if (m_sc.block == nullptr) GetSymbolContext(eSymbolContextBlock); if (m_sc.block) return m_sc.block->GetContainingInlinedBlock() != nullptr; return false; } lldb::LanguageType StackFrame::GetLanguage() { CompileUnit *cu = GetSymbolContext(eSymbolContextCompUnit).comp_unit; if (cu) return cu->GetLanguage(); return lldb::eLanguageTypeUnknown; } lldb::LanguageType StackFrame::GuessLanguage() { LanguageType lang_type = GetLanguage(); if (lang_type == eLanguageTypeUnknown) { SymbolContext sc = GetSymbolContext(eSymbolContextFunction | eSymbolContextSymbol); if (sc.function) { lang_type = sc.function->GetMangled().GuessLanguage(); } else if (sc.symbol) { lang_type = sc.symbol->GetMangled().GuessLanguage(); } } return lang_type; } namespace { std::pair GetBaseExplainingValue(const Instruction::Operand &operand, RegisterContext ®ister_context, lldb::addr_t value) { switch (operand.m_type) { case Instruction::Operand::Type::Dereference: case Instruction::Operand::Type::Immediate: case Instruction::Operand::Type::Invalid: case Instruction::Operand::Type::Product: // These are not currently interesting return std::make_pair(nullptr, 0); case Instruction::Operand::Type::Sum: { const Instruction::Operand *immediate_child = nullptr; const Instruction::Operand *variable_child = nullptr; if (operand.m_children[0].m_type == Instruction::Operand::Type::Immediate) { immediate_child = &operand.m_children[0]; variable_child = &operand.m_children[1]; } else if (operand.m_children[1].m_type == Instruction::Operand::Type::Immediate) { immediate_child = &operand.m_children[1]; variable_child = &operand.m_children[0]; } if (!immediate_child) { return std::make_pair(nullptr, 0); } lldb::addr_t adjusted_value = value; if (immediate_child->m_negative) { adjusted_value += immediate_child->m_immediate; } else { adjusted_value -= immediate_child->m_immediate; } std::pair base_and_offset = GetBaseExplainingValue(*variable_child, register_context, adjusted_value); if (!base_and_offset.first) { return std::make_pair(nullptr, 0); } if (immediate_child->m_negative) { base_and_offset.second -= immediate_child->m_immediate; } else { base_and_offset.second += immediate_child->m_immediate; } return base_and_offset; } case Instruction::Operand::Type::Register: { const RegisterInfo *info = register_context.GetRegisterInfoByName(operand.m_register.AsCString()); if (!info) { return std::make_pair(nullptr, 0); } RegisterValue reg_value; if (!register_context.ReadRegister(info, reg_value)) { return std::make_pair(nullptr, 0); } if (reg_value.GetAsUInt64() == value) { return std::make_pair(&operand, 0); } else { return std::make_pair(nullptr, 0); } } } return std::make_pair(nullptr, 0); } std::pair GetBaseExplainingDereference(const Instruction::Operand &operand, RegisterContext ®ister_context, lldb::addr_t addr) { if (operand.m_type == Instruction::Operand::Type::Dereference) { return GetBaseExplainingValue(operand.m_children[0], register_context, addr); } return std::make_pair(nullptr, 0); } } lldb::ValueObjectSP StackFrame::GuessValueForAddress(lldb::addr_t addr) { TargetSP target_sp = CalculateTarget(); const ArchSpec &target_arch = target_sp->GetArchitecture(); AddressRange pc_range; pc_range.GetBaseAddress() = GetFrameCodeAddress(); pc_range.SetByteSize(target_arch.GetMaximumOpcodeByteSize()); ExecutionContext exe_ctx(shared_from_this()); const char *plugin_name = nullptr; const char *flavor = nullptr; const bool prefer_file_cache = false; DisassemblerSP disassembler_sp = Disassembler::DisassembleRange( target_arch, plugin_name, flavor, exe_ctx, pc_range, prefer_file_cache); if (!disassembler_sp || !disassembler_sp->GetInstructionList().GetSize()) { return ValueObjectSP(); } InstructionSP instruction_sp = disassembler_sp->GetInstructionList().GetInstructionAtIndex(0); llvm::SmallVector operands; if (!instruction_sp->ParseOperands(operands)) { return ValueObjectSP(); } RegisterContextSP register_context_sp = GetRegisterContext(); if (!register_context_sp) { return ValueObjectSP(); } for (const Instruction::Operand &operand : operands) { std::pair base_and_offset = GetBaseExplainingDereference(operand, *register_context_sp, addr); if (!base_and_offset.first) { continue; } switch (base_and_offset.first->m_type) { case Instruction::Operand::Type::Immediate: { lldb_private::Address addr; if (target_sp->ResolveLoadAddress(base_and_offset.first->m_immediate + base_and_offset.second, addr)) { TypeSystem *c_type_system = target_sp->GetScratchTypeSystemForLanguage(nullptr, eLanguageTypeC); if (!c_type_system) { return ValueObjectSP(); } else { CompilerType void_ptr_type = c_type_system ->GetBasicTypeFromAST(lldb::BasicType::eBasicTypeChar) .GetPointerType(); return ValueObjectMemory::Create(this, "", addr, void_ptr_type); } } else { return ValueObjectSP(); } break; } case Instruction::Operand::Type::Register: { return GuessValueForRegisterAndOffset(base_and_offset.first->m_register, base_and_offset.second); } default: return ValueObjectSP(); } } return ValueObjectSP(); } namespace { ValueObjectSP GetValueForOffset(StackFrame &frame, ValueObjectSP &parent, int64_t offset) { if (offset < 0 || uint64_t(offset) >= parent->GetByteSize()) { return ValueObjectSP(); } if (parent->IsPointerOrReferenceType()) { return parent; } for (int ci = 0, ce = parent->GetNumChildren(); ci != ce; ++ci) { const bool can_create = true; ValueObjectSP child_sp = parent->GetChildAtIndex(ci, can_create); if (!child_sp) { return ValueObjectSP(); } int64_t child_offset = child_sp->GetByteOffset(); int64_t child_size = child_sp->GetByteSize(); if (offset >= child_offset && offset < (child_offset + child_size)) { return GetValueForOffset(frame, child_sp, offset - child_offset); } } if (offset == 0) { return parent; } else { return ValueObjectSP(); } } ValueObjectSP GetValueForDereferincingOffset(StackFrame &frame, ValueObjectSP &base, int64_t offset) { // base is a pointer to something // offset is the thing to add to the pointer // We return the most sensible ValueObject for the result of *(base+offset) if (!base->IsPointerOrReferenceType()) { return ValueObjectSP(); } Status error; ValueObjectSP pointee = base->Dereference(error); if (!pointee) { return ValueObjectSP(); } if (offset >= 0 && uint64_t(offset) >= pointee->GetByteSize()) { int64_t index = offset / pointee->GetByteSize(); offset = offset % pointee->GetByteSize(); const bool can_create = true; pointee = base->GetSyntheticArrayMember(index, can_create); } if (!pointee || error.Fail()) { return ValueObjectSP(); } return GetValueForOffset(frame, pointee, offset); } //------------------------------------------------------------------ /// Attempt to reconstruct the ValueObject for the address contained in a /// given register plus an offset. /// /// @params [in] frame /// The current stack frame. /// /// @params [in] reg /// The register. /// /// @params [in] offset /// The offset from the register. /// /// @param [in] disassembler /// A disassembler containing instructions valid up to the current PC. /// /// @param [in] variables /// The variable list from the current frame, /// /// @param [in] pc /// The program counter for the instruction considered the 'user'. /// /// @return /// A string describing the base for the ExpressionPath. This could be a /// variable, a register value, an argument, or a function return value. /// The ValueObject if found. If valid, it has a valid ExpressionPath. //------------------------------------------------------------------ lldb::ValueObjectSP DoGuessValueAt(StackFrame &frame, ConstString reg, int64_t offset, Disassembler &disassembler, VariableList &variables, const Address &pc) { // Example of operation for Intel: // // +14: movq -0x8(%rbp), %rdi // +18: movq 0x8(%rdi), %rdi // +22: addl 0x4(%rdi), %eax // // f, a pointer to a struct, is known to be at -0x8(%rbp). // // DoGuessValueAt(frame, rdi, 4, dis, vars, 0x22) finds the instruction at +18 // that assigns to rdi, and calls itself recursively for that dereference // DoGuessValueAt(frame, rdi, 8, dis, vars, 0x18) finds the instruction at // +14 that assigns to rdi, and calls itself recursively for that // derefernece // DoGuessValueAt(frame, rbp, -8, dis, vars, 0x14) finds "f" in the // variable list. // Returns a ValueObject for f. (That's what was stored at rbp-8 at +14) // Returns a ValueObject for *(f+8) or f->b (That's what was stored at rdi+8 // at +18) // Returns a ValueObject for *(f->b+4) or f->b->a (That's what was stored at // rdi+4 at +22) // First, check the variable list to see if anything is at the specified // location. using namespace OperandMatchers; const RegisterInfo *reg_info = frame.GetRegisterContext()->GetRegisterInfoByName(reg.AsCString()); if (!reg_info) { return ValueObjectSP(); } Instruction::Operand op = offset ? Instruction::Operand::BuildDereference( Instruction::Operand::BuildSum( Instruction::Operand::BuildRegister(reg), Instruction::Operand::BuildImmediate(offset))) : Instruction::Operand::BuildDereference( Instruction::Operand::BuildRegister(reg)); for (size_t vi = 0, ve = variables.GetSize(); vi != ve; ++vi) { VariableSP var_sp = variables.GetVariableAtIndex(vi); if (var_sp->LocationExpression().MatchesOperand(frame, op)) { return frame.GetValueObjectForFrameVariable(var_sp, eNoDynamicValues); } } const uint32_t current_inst = disassembler.GetInstructionList().GetIndexOfInstructionAtAddress(pc); if (current_inst == UINT32_MAX) { return ValueObjectSP(); } for (uint32_t ii = current_inst - 1; ii != (uint32_t)-1; --ii) { // This is not an exact algorithm, and it sacrifices accuracy for // generality. Recognizing "mov" and "ld" instructions –– and which are // their source and destination operands -- is something the disassembler // should do for us. InstructionSP instruction_sp = disassembler.GetInstructionList().GetInstructionAtIndex(ii); if (instruction_sp->IsCall()) { ABISP abi_sp = frame.CalculateProcess()->GetABI(); if (!abi_sp) { continue; } const char *return_register_name; if (!abi_sp->GetPointerReturnRegister(return_register_name)) { continue; } const RegisterInfo *return_register_info = frame.GetRegisterContext()->GetRegisterInfoByName( return_register_name); if (!return_register_info) { continue; } int64_t offset = 0; if (!MatchUnaryOp(MatchOpType(Instruction::Operand::Type::Dereference), MatchRegOp(*return_register_info))(op) && !MatchUnaryOp( MatchOpType(Instruction::Operand::Type::Dereference), MatchBinaryOp(MatchOpType(Instruction::Operand::Type::Sum), MatchRegOp(*return_register_info), FetchImmOp(offset)))(op)) { continue; } llvm::SmallVector operands; if (!instruction_sp->ParseOperands(operands) || operands.size() != 1) { continue; } switch (operands[0].m_type) { default: break; case Instruction::Operand::Type::Immediate: { SymbolContext sc; Address load_address; if (!frame.CalculateTarget()->ResolveLoadAddress( operands[0].m_immediate, load_address)) { break; } frame.CalculateTarget()->GetImages().ResolveSymbolContextForAddress( load_address, eSymbolContextFunction, sc); if (!sc.function) { break; } CompilerType function_type = sc.function->GetCompilerType(); if (!function_type.IsFunctionType()) { break; } CompilerType return_type = function_type.GetFunctionReturnType(); RegisterValue return_value; if (!frame.GetRegisterContext()->ReadRegister(return_register_info, return_value)) { break; } std::string name_str( sc.function->GetName().AsCString("")); name_str.append("()"); Address return_value_address(return_value.GetAsUInt64()); ValueObjectSP return_value_sp = ValueObjectMemory::Create( &frame, name_str, return_value_address, return_type); return GetValueForDereferincingOffset(frame, return_value_sp, offset); } } continue; } llvm::SmallVector operands; if (!instruction_sp->ParseOperands(operands) || operands.size() != 2) { continue; } Instruction::Operand *origin_operand = nullptr; auto clobbered_reg_matcher = [reg_info](const Instruction::Operand &op) { return MatchRegOp(*reg_info)(op) && op.m_clobbered; }; if (clobbered_reg_matcher(operands[0])) { origin_operand = &operands[1]; } else if (clobbered_reg_matcher(operands[1])) { origin_operand = &operands[0]; } else { continue; } // We have an origin operand. Can we track its value down? ValueObjectSP source_path; ConstString origin_register; int64_t origin_offset = 0; if (FetchRegOp(origin_register)(*origin_operand)) { source_path = DoGuessValueAt(frame, origin_register, 0, disassembler, variables, instruction_sp->GetAddress()); } else if (MatchUnaryOp( MatchOpType(Instruction::Operand::Type::Dereference), FetchRegOp(origin_register))(*origin_operand) || MatchUnaryOp( MatchOpType(Instruction::Operand::Type::Dereference), MatchBinaryOp(MatchOpType(Instruction::Operand::Type::Sum), FetchRegOp(origin_register), FetchImmOp(origin_offset)))(*origin_operand)) { source_path = DoGuessValueAt(frame, origin_register, origin_offset, disassembler, variables, instruction_sp->GetAddress()); if (!source_path) { continue; } source_path = GetValueForDereferincingOffset(frame, source_path, offset); } if (source_path) { return source_path; } } return ValueObjectSP(); } } lldb::ValueObjectSP StackFrame::GuessValueForRegisterAndOffset(ConstString reg, int64_t offset) { TargetSP target_sp = CalculateTarget(); const ArchSpec &target_arch = target_sp->GetArchitecture(); Block *frame_block = GetFrameBlock(); if (!frame_block) { return ValueObjectSP(); } Function *function = frame_block->CalculateSymbolContextFunction(); if (!function) { return ValueObjectSP(); } AddressRange pc_range = function->GetAddressRange(); if (GetFrameCodeAddress().GetFileAddress() < pc_range.GetBaseAddress().GetFileAddress() || GetFrameCodeAddress().GetFileAddress() - pc_range.GetBaseAddress().GetFileAddress() >= pc_range.GetByteSize()) { return ValueObjectSP(); } ExecutionContext exe_ctx(shared_from_this()); const char *plugin_name = nullptr; const char *flavor = nullptr; const bool prefer_file_cache = false; DisassemblerSP disassembler_sp = Disassembler::DisassembleRange( target_arch, plugin_name, flavor, exe_ctx, pc_range, prefer_file_cache); if (!disassembler_sp || !disassembler_sp->GetInstructionList().GetSize()) { return ValueObjectSP(); } const bool get_file_globals = false; VariableList *variables = GetVariableList(get_file_globals); if (!variables) { return ValueObjectSP(); } return DoGuessValueAt(*this, reg, offset, *disassembler_sp, *variables, GetFrameCodeAddress()); } TargetSP StackFrame::CalculateTarget() { TargetSP target_sp; ThreadSP thread_sp(GetThread()); if (thread_sp) { ProcessSP process_sp(thread_sp->CalculateProcess()); if (process_sp) target_sp = process_sp->CalculateTarget(); } return target_sp; } ProcessSP StackFrame::CalculateProcess() { ProcessSP process_sp; ThreadSP thread_sp(GetThread()); if (thread_sp) process_sp = thread_sp->CalculateProcess(); return process_sp; } ThreadSP StackFrame::CalculateThread() { return GetThread(); } StackFrameSP StackFrame::CalculateStackFrame() { return shared_from_this(); } void StackFrame::CalculateExecutionContext(ExecutionContext &exe_ctx) { exe_ctx.SetContext(shared_from_this()); } -void StackFrame::DumpUsingSettingsFormat(Stream *strm, +void StackFrame::DumpUsingSettingsFormat(Stream *strm, bool show_unique, const char *frame_marker) { if (strm == nullptr) return; GetSymbolContext(eSymbolContextEverything); ExecutionContext exe_ctx(shared_from_this()); StreamString s; if (frame_marker) s.PutCString(frame_marker); const FormatEntity::Entry *frame_format = nullptr; Target *target = exe_ctx.GetTargetPtr(); - if (target) - frame_format = target->GetDebugger().GetFrameFormat(); + if (target) { + if (show_unique) { + frame_format = target->GetDebugger().GetFrameFormatUnique(); + } else { + frame_format = target->GetDebugger().GetFrameFormat(); + } + } if (frame_format && FormatEntity::Format(*frame_format, s, &m_sc, &exe_ctx, nullptr, nullptr, false, false)) { strm->PutCString(s.GetString()); } else { Dump(strm, true, false); strm->EOL(); } } void StackFrame::Dump(Stream *strm, bool show_frame_index, bool show_fullpaths) { if (strm == nullptr) return; if (show_frame_index) strm->Printf("frame #%u: ", m_frame_index); ExecutionContext exe_ctx(shared_from_this()); Target *target = exe_ctx.GetTargetPtr(); strm->Printf("0x%0*" PRIx64 " ", target ? (target->GetArchitecture().GetAddressByteSize() * 2) : 16, GetFrameCodeAddress().GetLoadAddress(target)); GetSymbolContext(eSymbolContextEverything); const bool show_module = true; const bool show_inline = true; const bool show_function_arguments = true; const bool show_function_name = true; m_sc.DumpStopContext(strm, exe_ctx.GetBestExecutionContextScope(), GetFrameCodeAddress(), show_fullpaths, show_module, show_inline, show_function_arguments, show_function_name); } void StackFrame::UpdateCurrentFrameFromPreviousFrame(StackFrame &prev_frame) { std::lock_guard guard(m_mutex); assert(GetStackID() == prev_frame.GetStackID()); // TODO: remove this after some testing m_variable_list_sp = prev_frame.m_variable_list_sp; m_variable_list_value_objects.Swap(prev_frame.m_variable_list_value_objects); if (!m_disassembly.GetString().empty()) { m_disassembly.Clear(); m_disassembly.PutCString(prev_frame.m_disassembly.GetString()); } } void StackFrame::UpdatePreviousFrameFromCurrentFrame(StackFrame &curr_frame) { std::lock_guard guard(m_mutex); assert(GetStackID() == curr_frame.GetStackID()); // TODO: remove this after some testing m_id.SetPC(curr_frame.m_id.GetPC()); // Update the Stack ID PC value assert(GetThread() == curr_frame.GetThread()); m_frame_index = curr_frame.m_frame_index; m_concrete_frame_index = curr_frame.m_concrete_frame_index; m_reg_context_sp = curr_frame.m_reg_context_sp; m_frame_code_addr = curr_frame.m_frame_code_addr; assert(!m_sc.target_sp || !curr_frame.m_sc.target_sp || m_sc.target_sp.get() == curr_frame.m_sc.target_sp.get()); assert(!m_sc.module_sp || !curr_frame.m_sc.module_sp || m_sc.module_sp.get() == curr_frame.m_sc.module_sp.get()); assert(m_sc.comp_unit == nullptr || curr_frame.m_sc.comp_unit == nullptr || m_sc.comp_unit == curr_frame.m_sc.comp_unit); assert(m_sc.function == nullptr || curr_frame.m_sc.function == nullptr || m_sc.function == curr_frame.m_sc.function); m_sc = curr_frame.m_sc; m_flags.Clear(GOT_FRAME_BASE | eSymbolContextEverything); m_flags.Set(m_sc.GetResolvedMask()); m_frame_base.Clear(); m_frame_base_error.Clear(); } bool StackFrame::HasCachedData() const { if (m_variable_list_sp) return true; if (m_variable_list_value_objects.GetSize() > 0) return true; if (!m_disassembly.GetString().empty()) return true; return false; } bool StackFrame::GetStatus(Stream &strm, bool show_frame_info, bool show_source, - const char *frame_marker) { - + bool show_unique, const char *frame_marker) { if (show_frame_info) { strm.Indent(); - DumpUsingSettingsFormat(&strm, frame_marker); + DumpUsingSettingsFormat(&strm, show_unique, frame_marker); } if (show_source) { ExecutionContext exe_ctx(shared_from_this()); bool have_source = false, have_debuginfo = false; Debugger::StopDisassemblyType disasm_display = Debugger::eStopDisassemblyTypeNever; Target *target = exe_ctx.GetTargetPtr(); if (target) { Debugger &debugger = target->GetDebugger(); const uint32_t source_lines_before = debugger.GetStopSourceLineCount(true); const uint32_t source_lines_after = debugger.GetStopSourceLineCount(false); disasm_display = debugger.GetStopDisassemblyDisplay(); GetSymbolContext(eSymbolContextCompUnit | eSymbolContextLineEntry); if (m_sc.comp_unit && m_sc.line_entry.IsValid()) { have_debuginfo = true; if (source_lines_before > 0 || source_lines_after > 0) { size_t num_lines = target->GetSourceManager().DisplaySourceLinesWithLineNumbers( m_sc.line_entry.file, m_sc.line_entry.line, m_sc.line_entry.column, source_lines_before, source_lines_after, "->", &strm); if (num_lines != 0) have_source = true; // TODO: Give here a one time warning if source file is missing. } } switch (disasm_display) { case Debugger::eStopDisassemblyTypeNever: break; case Debugger::eStopDisassemblyTypeNoDebugInfo: if (have_debuginfo) break; LLVM_FALLTHROUGH; case Debugger::eStopDisassemblyTypeNoSource: if (have_source) break; LLVM_FALLTHROUGH; case Debugger::eStopDisassemblyTypeAlways: if (target) { const uint32_t disasm_lines = debugger.GetDisassemblyLineCount(); if (disasm_lines > 0) { const ArchSpec &target_arch = target->GetArchitecture(); AddressRange pc_range; pc_range.GetBaseAddress() = GetFrameCodeAddress(); pc_range.SetByteSize(disasm_lines * target_arch.GetMaximumOpcodeByteSize()); const char *plugin_name = nullptr; const char *flavor = nullptr; const bool mixed_source_and_assembly = false; Disassembler::Disassemble( target->GetDebugger(), target_arch, plugin_name, flavor, exe_ctx, pc_range, disasm_lines, mixed_source_and_assembly, 0, Disassembler::eOptionMarkPCAddress, strm); } } break; } } } return true; } Index: vendor/lldb/dist/source/Target/StackFrameList.cpp =================================================================== --- vendor/lldb/dist/source/Target/StackFrameList.cpp (revision 320022) +++ vendor/lldb/dist/source/Target/StackFrameList.cpp (revision 320023) @@ -1,852 +1,853 @@ //===-- StackFrameList.cpp --------------------------------------*- C++ -*-===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// // C Includes // C++ Includes // Other libraries and framework includes // Project includes #include "lldb/Target/StackFrameList.h" #include "lldb/Breakpoint/Breakpoint.h" #include "lldb/Breakpoint/BreakpointLocation.h" #include "lldb/Core/SourceManager.h" #include "lldb/Core/StreamFile.h" #include "lldb/Symbol/Block.h" #include "lldb/Symbol/Function.h" #include "lldb/Symbol/Symbol.h" #include "lldb/Target/Process.h" #include "lldb/Target/RegisterContext.h" #include "lldb/Target/StackFrame.h" #include "lldb/Target/StopInfo.h" #include "lldb/Target/Target.h" #include "lldb/Target/Thread.h" #include "lldb/Target/Unwind.h" #include "lldb/Utility/Log.h" //#define DEBUG_STACK_FRAMES 1 using namespace lldb; using namespace lldb_private; //---------------------------------------------------------------------- // StackFrameList constructor //---------------------------------------------------------------------- StackFrameList::StackFrameList(Thread &thread, const lldb::StackFrameListSP &prev_frames_sp, bool show_inline_frames) : m_thread(thread), m_prev_frames_sp(prev_frames_sp), m_mutex(), m_frames(), m_selected_frame_idx(0), m_concrete_frames_fetched(0), m_current_inlined_depth(UINT32_MAX), m_current_inlined_pc(LLDB_INVALID_ADDRESS), m_show_inlined_frames(show_inline_frames) { if (prev_frames_sp) { m_current_inlined_depth = prev_frames_sp->m_current_inlined_depth; m_current_inlined_pc = prev_frames_sp->m_current_inlined_pc; } } StackFrameList::~StackFrameList() { // Call clear since this takes a lock and clears the stack frame list // in case another thread is currently using this stack frame list Clear(); } void StackFrameList::CalculateCurrentInlinedDepth() { uint32_t cur_inlined_depth = GetCurrentInlinedDepth(); if (cur_inlined_depth == UINT32_MAX) { ResetCurrentInlinedDepth(); } } uint32_t StackFrameList::GetCurrentInlinedDepth() { if (m_show_inlined_frames && m_current_inlined_pc != LLDB_INVALID_ADDRESS) { lldb::addr_t cur_pc = m_thread.GetRegisterContext()->GetPC(); if (cur_pc != m_current_inlined_pc) { m_current_inlined_pc = LLDB_INVALID_ADDRESS; m_current_inlined_depth = UINT32_MAX; Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_STEP)); if (log && log->GetVerbose()) log->Printf( "GetCurrentInlinedDepth: invalidating current inlined depth.\n"); } return m_current_inlined_depth; } else { return UINT32_MAX; } } void StackFrameList::ResetCurrentInlinedDepth() { std::lock_guard guard(m_mutex); if (m_show_inlined_frames) { GetFramesUpTo(0); if (m_frames.empty()) return; if (!m_frames[0]->IsInlined()) { m_current_inlined_depth = UINT32_MAX; m_current_inlined_pc = LLDB_INVALID_ADDRESS; Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_STEP)); if (log && log->GetVerbose()) log->Printf( "ResetCurrentInlinedDepth: Invalidating current inlined depth.\n"); } else { // We only need to do something special about inlined blocks when we // are at the beginning of an inlined function: // FIXME: We probably also have to do something special if the PC is at // the END // of an inlined function, which coincides with the end of either its // containing // function or another inlined function. lldb::addr_t curr_pc = m_thread.GetRegisterContext()->GetPC(); Block *block_ptr = m_frames[0]->GetFrameBlock(); if (block_ptr) { Address pc_as_address; pc_as_address.SetLoadAddress(curr_pc, &(m_thread.GetProcess()->GetTarget())); AddressRange containing_range; if (block_ptr->GetRangeContainingAddress(pc_as_address, containing_range)) { if (pc_as_address == containing_range.GetBaseAddress()) { // If we got here because of a breakpoint hit, then set the inlined // depth depending on where // the breakpoint was set. // If we got here because of a crash, then set the inlined depth to // the deepest most block. // Otherwise, we stopped here naturally as the result of a step, so // set ourselves in the // containing frame of the whole set of nested inlines, so the user // can then "virtually" // step into the frames one by one, or next over the whole mess. // Note: We don't have to handle being somewhere in the middle of // the stack here, since // ResetCurrentInlinedDepth doesn't get called if there is a valid // inlined depth set. StopInfoSP stop_info_sp = m_thread.GetStopInfo(); if (stop_info_sp) { switch (stop_info_sp->GetStopReason()) { case eStopReasonWatchpoint: case eStopReasonException: case eStopReasonExec: case eStopReasonSignal: // In all these cases we want to stop in the deepest most frame. m_current_inlined_pc = curr_pc; m_current_inlined_depth = 0; break; case eStopReasonBreakpoint: { // FIXME: Figure out what this break point is doing, and set the // inline depth // appropriately. Be careful to take into account breakpoints // that implement // step over prologue, since that should do the default // calculation. // For now, if the breakpoints corresponding to this hit are all // internal, // I set the stop location to the top of the inlined stack, // since that will make // things like stepping over prologues work right. But if there // are any non-internal // breakpoints I do to the bottom of the stack, since that was // the old behavior. uint32_t bp_site_id = stop_info_sp->GetValue(); BreakpointSiteSP bp_site_sp( m_thread.GetProcess()->GetBreakpointSiteList().FindByID( bp_site_id)); bool all_internal = true; if (bp_site_sp) { uint32_t num_owners = bp_site_sp->GetNumberOfOwners(); for (uint32_t i = 0; i < num_owners; i++) { Breakpoint &bp_ref = bp_site_sp->GetOwnerAtIndex(i)->GetBreakpoint(); if (!bp_ref.IsInternal()) { all_internal = false; } } } if (!all_internal) { m_current_inlined_pc = curr_pc; m_current_inlined_depth = 0; break; } } LLVM_FALLTHROUGH; default: { // Otherwise, we should set ourselves at the container of the // inlining, so that the // user can descend into them. // So first we check whether we have more than one inlined block // sharing this PC: int num_inlined_functions = 0; for (Block *container_ptr = block_ptr->GetInlinedParent(); container_ptr != nullptr; container_ptr = container_ptr->GetInlinedParent()) { if (!container_ptr->GetRangeContainingAddress( pc_as_address, containing_range)) break; if (pc_as_address != containing_range.GetBaseAddress()) break; num_inlined_functions++; } m_current_inlined_pc = curr_pc; m_current_inlined_depth = num_inlined_functions + 1; Log *log( lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_STEP)); if (log && log->GetVerbose()) log->Printf("ResetCurrentInlinedDepth: setting inlined " "depth: %d 0x%" PRIx64 ".\n", m_current_inlined_depth, curr_pc); } break; } } } } } } } } bool StackFrameList::DecrementCurrentInlinedDepth() { if (m_show_inlined_frames) { uint32_t current_inlined_depth = GetCurrentInlinedDepth(); if (current_inlined_depth != UINT32_MAX) { if (current_inlined_depth > 0) { m_current_inlined_depth--; return true; } } } return false; } void StackFrameList::SetCurrentInlinedDepth(uint32_t new_depth) { m_current_inlined_depth = new_depth; if (new_depth == UINT32_MAX) m_current_inlined_pc = LLDB_INVALID_ADDRESS; else m_current_inlined_pc = m_thread.GetRegisterContext()->GetPC(); } void StackFrameList::GetFramesUpTo(uint32_t end_idx) { // this makes sure we do not fetch frames for an invalid thread if (!m_thread.IsValid()) return; // We've already gotten more frames than asked for, or we've already finished // unwinding, return. if (m_frames.size() > end_idx || GetAllFramesFetched()) return; Unwind *unwinder = m_thread.GetUnwinder(); if (m_show_inlined_frames) { #if defined(DEBUG_STACK_FRAMES) StreamFile s(stdout, false); #endif // If we are hiding some frames from the outside world, we need to add those // onto the total count of // frames to fetch. However, we don't need to do that if end_idx is 0 since // in that case we always // get the first concrete frame and all the inlined frames below it... And // of course, if end_idx is // UINT32_MAX that means get all, so just do that... uint32_t inlined_depth = 0; if (end_idx > 0 && end_idx != UINT32_MAX) { inlined_depth = GetCurrentInlinedDepth(); if (inlined_depth != UINT32_MAX) { if (end_idx > 0) end_idx += inlined_depth; } } StackFrameSP unwind_frame_sp; do { uint32_t idx = m_concrete_frames_fetched++; lldb::addr_t pc = LLDB_INVALID_ADDRESS; lldb::addr_t cfa = LLDB_INVALID_ADDRESS; if (idx == 0) { // We might have already created frame zero, only create it // if we need to if (m_frames.empty()) { RegisterContextSP reg_ctx_sp(m_thread.GetRegisterContext()); if (reg_ctx_sp) { const bool success = unwinder && unwinder->GetFrameInfoAtIndex(idx, cfa, pc); // There shouldn't be any way not to get the frame info for frame 0. // But if the unwinder can't make one, lets make one by hand with // the // SP as the CFA and see if that gets any further. if (!success) { cfa = reg_ctx_sp->GetSP(); pc = reg_ctx_sp->GetPC(); } unwind_frame_sp.reset(new StackFrame(m_thread.shared_from_this(), m_frames.size(), idx, reg_ctx_sp, cfa, pc, nullptr)); m_frames.push_back(unwind_frame_sp); } } else { unwind_frame_sp = m_frames.front(); cfa = unwind_frame_sp->m_id.GetCallFrameAddress(); } } else { const bool success = unwinder && unwinder->GetFrameInfoAtIndex(idx, cfa, pc); if (!success) { // We've gotten to the end of the stack. SetAllFramesFetched(); break; } const bool cfa_is_valid = true; const bool stop_id_is_valid = false; const bool is_history_frame = false; unwind_frame_sp.reset(new StackFrame( m_thread.shared_from_this(), m_frames.size(), idx, cfa, cfa_is_valid, pc, 0, stop_id_is_valid, is_history_frame, nullptr)); m_frames.push_back(unwind_frame_sp); } assert(unwind_frame_sp); SymbolContext unwind_sc = unwind_frame_sp->GetSymbolContext( eSymbolContextBlock | eSymbolContextFunction); Block *unwind_block = unwind_sc.block; if (unwind_block) { Address curr_frame_address(unwind_frame_sp->GetFrameCodeAddress()); TargetSP target_sp = m_thread.CalculateTarget(); // Be sure to adjust the frame address to match the address // that was used to lookup the symbol context above. If we are // in the first concrete frame, then we lookup using the current // address, else we decrement the address by one to get the correct // location. if (idx > 0) { if (curr_frame_address.GetOffset() == 0) { // If curr_frame_address points to the first address in a section // then after // adjustment it will point to an other section. In that case // resolve the // address again to the correct section plus offset form. addr_t load_addr = curr_frame_address.GetOpcodeLoadAddress( target_sp.get(), eAddressClassCode); curr_frame_address.SetOpcodeLoadAddress( load_addr - 1, target_sp.get(), eAddressClassCode); } else { curr_frame_address.Slide(-1); } } SymbolContext next_frame_sc; Address next_frame_address; while (unwind_sc.GetParentOfInlinedScope( curr_frame_address, next_frame_sc, next_frame_address)) { next_frame_sc.line_entry.ApplyFileMappings(target_sp); StackFrameSP frame_sp( new StackFrame(m_thread.shared_from_this(), m_frames.size(), idx, unwind_frame_sp->GetRegisterContextSP(), cfa, next_frame_address, &next_frame_sc)); m_frames.push_back(frame_sp); unwind_sc = next_frame_sc; curr_frame_address = next_frame_address; } } } while (m_frames.size() - 1 < end_idx); // Don't try to merge till you've calculated all the frames in this stack. if (GetAllFramesFetched() && m_prev_frames_sp) { StackFrameList *prev_frames = m_prev_frames_sp.get(); StackFrameList *curr_frames = this; // curr_frames->m_current_inlined_depth = prev_frames->m_current_inlined_depth; // curr_frames->m_current_inlined_pc = prev_frames->m_current_inlined_pc; // printf ("GetFramesUpTo: Copying current inlined depth: %d 0x%" PRIx64 ".\n", // curr_frames->m_current_inlined_depth, curr_frames->m_current_inlined_pc); #if defined(DEBUG_STACK_FRAMES) s.PutCString("\nprev_frames:\n"); prev_frames->Dump(&s); s.PutCString("\ncurr_frames:\n"); curr_frames->Dump(&s); s.EOL(); #endif size_t curr_frame_num, prev_frame_num; for (curr_frame_num = curr_frames->m_frames.size(), prev_frame_num = prev_frames->m_frames.size(); curr_frame_num > 0 && prev_frame_num > 0; --curr_frame_num, --prev_frame_num) { const size_t curr_frame_idx = curr_frame_num - 1; const size_t prev_frame_idx = prev_frame_num - 1; StackFrameSP curr_frame_sp(curr_frames->m_frames[curr_frame_idx]); StackFrameSP prev_frame_sp(prev_frames->m_frames[prev_frame_idx]); #if defined(DEBUG_STACK_FRAMES) s.Printf("\n\nCurr frame #%u ", curr_frame_idx); if (curr_frame_sp) curr_frame_sp->Dump(&s, true, false); else s.PutCString("NULL"); s.Printf("\nPrev frame #%u ", prev_frame_idx); if (prev_frame_sp) prev_frame_sp->Dump(&s, true, false); else s.PutCString("NULL"); #endif StackFrame *curr_frame = curr_frame_sp.get(); StackFrame *prev_frame = prev_frame_sp.get(); if (curr_frame == nullptr || prev_frame == nullptr) break; // Check the stack ID to make sure they are equal if (curr_frame->GetStackID() != prev_frame->GetStackID()) break; prev_frame->UpdatePreviousFrameFromCurrentFrame(*curr_frame); // Now copy the fixed up previous frame into the current frames // so the pointer doesn't change m_frames[curr_frame_idx] = prev_frame_sp; // curr_frame->UpdateCurrentFrameFromPreviousFrame (*prev_frame); #if defined(DEBUG_STACK_FRAMES) s.Printf("\n Copying previous frame to current frame"); #endif } // We are done with the old stack frame list, we can release it now m_prev_frames_sp.reset(); } #if defined(DEBUG_STACK_FRAMES) s.PutCString("\n\nNew frames:\n"); Dump(&s); s.EOL(); #endif } else { if (end_idx < m_concrete_frames_fetched) return; if (unwinder) { uint32_t num_frames = unwinder->GetFramesUpTo(end_idx); if (num_frames <= end_idx + 1) { // Done unwinding. m_concrete_frames_fetched = UINT32_MAX; } m_frames.resize(num_frames); } } } uint32_t StackFrameList::GetNumFrames(bool can_create) { std::lock_guard guard(m_mutex); if (can_create) GetFramesUpTo(UINT32_MAX); uint32_t inlined_depth = GetCurrentInlinedDepth(); if (inlined_depth == UINT32_MAX) return m_frames.size(); else return m_frames.size() - inlined_depth; } void StackFrameList::Dump(Stream *s) { if (s == nullptr) return; std::lock_guard guard(m_mutex); const_iterator pos, begin = m_frames.begin(), end = m_frames.end(); for (pos = begin; pos != end; ++pos) { StackFrame *frame = (*pos).get(); s->Printf("%p: ", static_cast(frame)); if (frame) { frame->GetStackID().Dump(s); frame->DumpUsingSettingsFormat(s); } else s->Printf("frame #%u", (uint32_t)std::distance(begin, pos)); s->EOL(); } s->EOL(); } StackFrameSP StackFrameList::GetFrameAtIndex(uint32_t idx) { StackFrameSP frame_sp; std::lock_guard guard(m_mutex); uint32_t original_idx = idx; uint32_t inlined_depth = GetCurrentInlinedDepth(); if (inlined_depth != UINT32_MAX) idx += inlined_depth; if (idx < m_frames.size()) frame_sp = m_frames[idx]; if (frame_sp) return frame_sp; // GetFramesUpTo will fill m_frames with as many frames as you asked for, // if there are that many. If there weren't then you asked for too many // frames. GetFramesUpTo(idx); if (idx < m_frames.size()) { if (m_show_inlined_frames) { // When inline frames are enabled we actually create all the frames in // GetFramesUpTo. frame_sp = m_frames[idx]; } else { Unwind *unwinder = m_thread.GetUnwinder(); if (unwinder) { addr_t pc, cfa; if (unwinder->GetFrameInfoAtIndex(idx, cfa, pc)) { const bool cfa_is_valid = true; const bool stop_id_is_valid = false; const bool is_history_frame = false; frame_sp.reset(new StackFrame( m_thread.shared_from_this(), idx, idx, cfa, cfa_is_valid, pc, 0, stop_id_is_valid, is_history_frame, nullptr)); Function *function = frame_sp->GetSymbolContext(eSymbolContextFunction).function; if (function) { // When we aren't showing inline functions we always use // the top most function block as the scope. frame_sp->SetSymbolContextScope(&function->GetBlock(false)); } else { // Set the symbol scope from the symbol regardless if it is nullptr // or valid. frame_sp->SetSymbolContextScope( frame_sp->GetSymbolContext(eSymbolContextSymbol).symbol); } SetFrameAtIndex(idx, frame_sp); } } } } else if (original_idx == 0) { // There should ALWAYS be a frame at index 0. If something went wrong with // the CurrentInlinedDepth such that // there weren't as many frames as we thought taking that into account, then // reset the current inlined depth // and return the real zeroth frame. if (m_frames.empty()) { // Why do we have a thread with zero frames, that should not ever // happen... assert(!m_thread.IsValid() && "A valid thread has no frames."); } else { ResetCurrentInlinedDepth(); frame_sp = m_frames[original_idx]; } } return frame_sp; } StackFrameSP StackFrameList::GetFrameWithConcreteFrameIndex(uint32_t unwind_idx) { // First try assuming the unwind index is the same as the frame index. The // unwind index is always greater than or equal to the frame index, so it // is a good place to start. If we have inlined frames we might have 5 // concrete frames (frame unwind indexes go from 0-4), but we might have 15 // frames after we make all the inlined frames. Most of the time the unwind // frame index (or the concrete frame index) is the same as the frame index. uint32_t frame_idx = unwind_idx; StackFrameSP frame_sp(GetFrameAtIndex(frame_idx)); while (frame_sp) { if (frame_sp->GetFrameIndex() == unwind_idx) break; frame_sp = GetFrameAtIndex(++frame_idx); } return frame_sp; } static bool CompareStackID(const StackFrameSP &stack_sp, const StackID &stack_id) { return stack_sp->GetStackID() < stack_id; } StackFrameSP StackFrameList::GetFrameWithStackID(const StackID &stack_id) { StackFrameSP frame_sp; if (stack_id.IsValid()) { std::lock_guard guard(m_mutex); uint32_t frame_idx = 0; // Do a binary search in case the stack frame is already in our cache collection::const_iterator begin = m_frames.begin(); collection::const_iterator end = m_frames.end(); if (begin != end) { collection::const_iterator pos = std::lower_bound(begin, end, stack_id, CompareStackID); if (pos != end) { if ((*pos)->GetStackID() == stack_id) return *pos; } // if (m_frames.back()->GetStackID() < stack_id) // frame_idx = m_frames.size(); } do { frame_sp = GetFrameAtIndex(frame_idx); if (frame_sp && frame_sp->GetStackID() == stack_id) break; frame_idx++; } while (frame_sp); } return frame_sp; } bool StackFrameList::SetFrameAtIndex(uint32_t idx, StackFrameSP &frame_sp) { if (idx >= m_frames.size()) m_frames.resize(idx + 1); // Make sure allocation succeeded by checking bounds again if (idx < m_frames.size()) { m_frames[idx] = frame_sp; return true; } return false; // resize failed, out of memory? } uint32_t StackFrameList::GetSelectedFrameIndex() const { std::lock_guard guard(m_mutex); return m_selected_frame_idx; } uint32_t StackFrameList::SetSelectedFrame(lldb_private::StackFrame *frame) { std::lock_guard guard(m_mutex); const_iterator pos; const_iterator begin = m_frames.begin(); const_iterator end = m_frames.end(); m_selected_frame_idx = 0; for (pos = begin; pos != end; ++pos) { if (pos->get() == frame) { m_selected_frame_idx = std::distance(begin, pos); uint32_t inlined_depth = GetCurrentInlinedDepth(); if (inlined_depth != UINT32_MAX) m_selected_frame_idx -= inlined_depth; break; } } SetDefaultFileAndLineToSelectedFrame(); return m_selected_frame_idx; } // Mark a stack frame as the current frame using the frame index bool StackFrameList::SetSelectedFrameByIndex(uint32_t idx) { std::lock_guard guard(m_mutex); StackFrameSP frame_sp(GetFrameAtIndex(idx)); if (frame_sp) { SetSelectedFrame(frame_sp.get()); return true; } else return false; } void StackFrameList::SetDefaultFileAndLineToSelectedFrame() { if (m_thread.GetID() == m_thread.GetProcess()->GetThreadList().GetSelectedThread()->GetID()) { StackFrameSP frame_sp(GetFrameAtIndex(GetSelectedFrameIndex())); if (frame_sp) { SymbolContext sc = frame_sp->GetSymbolContext(eSymbolContextLineEntry); if (sc.line_entry.file) m_thread.CalculateTarget()->GetSourceManager().SetDefaultFileAndLine( sc.line_entry.file, sc.line_entry.line); } } } // The thread has been run, reset the number stack frames to zero so we can // determine how many frames we have lazily. void StackFrameList::Clear() { std::lock_guard guard(m_mutex); m_frames.clear(); m_concrete_frames_fetched = 0; } void StackFrameList::InvalidateFrames(uint32_t start_idx) { std::lock_guard guard(m_mutex); if (m_show_inlined_frames) { Clear(); } else { const size_t num_frames = m_frames.size(); while (start_idx < num_frames) { m_frames[start_idx].reset(); ++start_idx; } } } void StackFrameList::Merge(std::unique_ptr &curr_ap, lldb::StackFrameListSP &prev_sp) { std::unique_lock current_lock, previous_lock; if (curr_ap) current_lock = std::unique_lock(curr_ap->m_mutex); if (prev_sp) previous_lock = std::unique_lock(prev_sp->m_mutex); #if defined(DEBUG_STACK_FRAMES) StreamFile s(stdout, false); s.PutCString("\n\nStackFrameList::Merge():\nPrev:\n"); if (prev_sp) prev_sp->Dump(&s); else s.PutCString("NULL"); s.PutCString("\nCurr:\n"); if (curr_ap) curr_ap->Dump(&s); else s.PutCString("NULL"); s.EOL(); #endif if (!curr_ap || curr_ap->GetNumFrames(false) == 0) { #if defined(DEBUG_STACK_FRAMES) s.PutCString("No current frames, leave previous frames alone...\n"); #endif curr_ap.release(); return; } if (!prev_sp || prev_sp->GetNumFrames(false) == 0) { #if defined(DEBUG_STACK_FRAMES) s.PutCString("No previous frames, so use current frames...\n"); #endif // We either don't have any previous frames, or since we have more than // one current frames it means we have all the frames and can safely // replace our previous frames. prev_sp.reset(curr_ap.release()); return; } const uint32_t num_curr_frames = curr_ap->GetNumFrames(false); if (num_curr_frames > 1) { #if defined(DEBUG_STACK_FRAMES) s.PutCString( "We have more than one current frame, so use current frames...\n"); #endif // We have more than one current frames it means we have all the frames // and can safely replace our previous frames. prev_sp.reset(curr_ap.release()); #if defined(DEBUG_STACK_FRAMES) s.PutCString("\nMerged:\n"); prev_sp->Dump(&s); #endif return; } StackFrameSP prev_frame_zero_sp(prev_sp->GetFrameAtIndex(0)); StackFrameSP curr_frame_zero_sp(curr_ap->GetFrameAtIndex(0)); StackID curr_stack_id(curr_frame_zero_sp->GetStackID()); StackID prev_stack_id(prev_frame_zero_sp->GetStackID()); #if defined(DEBUG_STACK_FRAMES) const uint32_t num_prev_frames = prev_sp->GetNumFrames(false); s.Printf("\n%u previous frames with one current frame\n", num_prev_frames); #endif // We have only a single current frame // Our previous stack frames only had a single frame as well... if (curr_stack_id == prev_stack_id) { #if defined(DEBUG_STACK_FRAMES) s.Printf("\nPrevious frame #0 is same as current frame #0, merge the " "cached data\n"); #endif curr_frame_zero_sp->UpdateCurrentFrameFromPreviousFrame( *prev_frame_zero_sp); // prev_frame_zero_sp->UpdatePreviousFrameFromCurrentFrame // (*curr_frame_zero_sp); // prev_sp->SetFrameAtIndex (0, prev_frame_zero_sp); } else if (curr_stack_id < prev_stack_id) { #if defined(DEBUG_STACK_FRAMES) s.Printf("\nCurrent frame #0 has a stack ID that is less than the previous " "frame #0, insert current frame zero in front of previous\n"); #endif prev_sp->m_frames.insert(prev_sp->m_frames.begin(), curr_frame_zero_sp); } curr_ap.release(); #if defined(DEBUG_STACK_FRAMES) s.PutCString("\nMerged:\n"); prev_sp->Dump(&s); #endif } lldb::StackFrameSP StackFrameList::GetStackFrameSPForStackFramePtr(StackFrame *stack_frame_ptr) { const_iterator pos; const_iterator begin = m_frames.begin(); const_iterator end = m_frames.end(); lldb::StackFrameSP ret_sp; for (pos = begin; pos != end; ++pos) { if (pos->get() == stack_frame_ptr) { ret_sp = (*pos); break; } } return ret_sp; } size_t StackFrameList::GetStatus(Stream &strm, uint32_t first_frame, uint32_t num_frames, bool show_frame_info, uint32_t num_frames_with_source, + bool show_unique, const char *selected_frame_marker) { size_t num_frames_displayed = 0; if (num_frames == 0) return 0; StackFrameSP frame_sp; uint32_t frame_idx = 0; uint32_t last_frame; // Don't let the last frame wrap around... if (num_frames == UINT32_MAX) last_frame = UINT32_MAX; else last_frame = first_frame + num_frames; StackFrameSP selected_frame_sp = m_thread.GetSelectedFrame(); const char *unselected_marker = nullptr; std::string buffer; if (selected_frame_marker) { size_t len = strlen(selected_frame_marker); buffer.insert(buffer.begin(), len, ' '); unselected_marker = buffer.c_str(); } const char *marker = nullptr; for (frame_idx = first_frame; frame_idx < last_frame; ++frame_idx) { frame_sp = GetFrameAtIndex(frame_idx); if (!frame_sp) break; if (selected_frame_marker != nullptr) { if (frame_sp == selected_frame_sp) marker = selected_frame_marker; else marker = unselected_marker; } if (!frame_sp->GetStatus(strm, show_frame_info, num_frames_with_source > (first_frame - frame_idx), - marker)) + show_unique, marker)) break; ++num_frames_displayed; } strm.IndentLess(); return num_frames_displayed; } Index: vendor/lldb/dist/source/Target/Thread.cpp =================================================================== --- vendor/lldb/dist/source/Target/Thread.cpp (revision 320022) +++ vendor/lldb/dist/source/Target/Thread.cpp (revision 320023) @@ -1,2208 +1,2211 @@ //===-- Thread.cpp ----------------------------------------------*- C++ -*-===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// // C Includes // C++ Includes // Other libraries and framework includes // Project includes #include "lldb/Target/Thread.h" #include "Plugins/Process/Utility/UnwindLLDB.h" #include "Plugins/Process/Utility/UnwindMacOSXFrameBackchain.h" #include "lldb/Breakpoint/BreakpointLocation.h" #include "lldb/Core/Debugger.h" #include "lldb/Core/FormatEntity.h" #include "lldb/Core/Module.h" #include "lldb/Core/State.h" #include "lldb/Core/ValueObject.h" #include "lldb/Host/Host.h" #include "lldb/Interpreter/OptionValueFileSpecList.h" #include "lldb/Interpreter/OptionValueProperties.h" #include "lldb/Interpreter/Property.h" #include "lldb/Symbol/Function.h" #include "lldb/Target/ABI.h" #include "lldb/Target/DynamicLoader.h" #include "lldb/Target/ExecutionContext.h" #include "lldb/Target/Process.h" #include "lldb/Target/RegisterContext.h" #include "lldb/Target/StopInfo.h" #include "lldb/Target/SystemRuntime.h" #include "lldb/Target/Target.h" #include "lldb/Target/ThreadPlan.h" #include "lldb/Target/ThreadPlanBase.h" #include "lldb/Target/ThreadPlanCallFunction.h" #include "lldb/Target/ThreadPlanPython.h" #include "lldb/Target/ThreadPlanRunToAddress.h" #include "lldb/Target/ThreadPlanStepInRange.h" #include "lldb/Target/ThreadPlanStepInstruction.h" #include "lldb/Target/ThreadPlanStepOut.h" #include "lldb/Target/ThreadPlanStepOverBreakpoint.h" #include "lldb/Target/ThreadPlanStepOverRange.h" #include "lldb/Target/ThreadPlanStepThrough.h" #include "lldb/Target/ThreadPlanStepUntil.h" #include "lldb/Target/ThreadSpec.h" #include "lldb/Target/Unwind.h" #include "lldb/Utility/Log.h" #include "lldb/Utility/RegularExpression.h" #include "lldb/Utility/Stream.h" #include "lldb/Utility/StreamString.h" #include "lldb/lldb-enumerations.h" using namespace lldb; using namespace lldb_private; const ThreadPropertiesSP &Thread::GetGlobalProperties() { // NOTE: intentional leak so we don't crash if global destructor chain gets // called as other threads still use the result of this function static ThreadPropertiesSP *g_settings_sp_ptr = new ThreadPropertiesSP(new ThreadProperties(true)); return *g_settings_sp_ptr; } static PropertyDefinition g_properties[] = { {"step-in-avoid-nodebug", OptionValue::eTypeBoolean, true, true, nullptr, nullptr, "If true, step-in will not stop in functions with no debug information."}, {"step-out-avoid-nodebug", OptionValue::eTypeBoolean, true, false, nullptr, nullptr, "If true, when step-in/step-out/step-over leave the current " "frame, they will continue to step out till they come to a " "function with " "debug information. Passing a frame argument to step-out will " "override this option."}, {"step-avoid-regexp", OptionValue::eTypeRegex, true, 0, "^std::", nullptr, "A regular expression defining functions step-in won't stop in."}, {"step-avoid-libraries", OptionValue::eTypeFileSpecList, true, 0, nullptr, nullptr, "A list of libraries that source stepping won't stop in."}, {"trace-thread", OptionValue::eTypeBoolean, false, false, nullptr, nullptr, "If true, this thread will single-step and log execution."}, {nullptr, OptionValue::eTypeInvalid, false, 0, nullptr, nullptr, nullptr}}; enum { ePropertyStepInAvoidsNoDebug, ePropertyStepOutAvoidsNoDebug, ePropertyStepAvoidRegex, ePropertyStepAvoidLibraries, ePropertyEnableThreadTrace }; class ThreadOptionValueProperties : public OptionValueProperties { public: ThreadOptionValueProperties(const ConstString &name) : OptionValueProperties(name) {} // This constructor is used when creating ThreadOptionValueProperties when it // is part of a new lldb_private::Thread instance. It will copy all current // global property values as needed ThreadOptionValueProperties(ThreadProperties *global_properties) : OptionValueProperties(*global_properties->GetValueProperties()) {} const Property *GetPropertyAtIndex(const ExecutionContext *exe_ctx, bool will_modify, uint32_t idx) const override { // When getting the value for a key from the thread options, we will always // try and grab the setting from the current thread if there is one. Else we // just // use the one from this instance. if (exe_ctx) { Thread *thread = exe_ctx->GetThreadPtr(); if (thread) { ThreadOptionValueProperties *instance_properties = static_cast( thread->GetValueProperties().get()); if (this != instance_properties) return instance_properties->ProtectedGetPropertyAtIndex(idx); } } return ProtectedGetPropertyAtIndex(idx); } }; ThreadProperties::ThreadProperties(bool is_global) : Properties() { if (is_global) { m_collection_sp.reset( new ThreadOptionValueProperties(ConstString("thread"))); m_collection_sp->Initialize(g_properties); } else m_collection_sp.reset( new ThreadOptionValueProperties(Thread::GetGlobalProperties().get())); } ThreadProperties::~ThreadProperties() = default; const RegularExpression *ThreadProperties::GetSymbolsToAvoidRegexp() { const uint32_t idx = ePropertyStepAvoidRegex; return m_collection_sp->GetPropertyAtIndexAsOptionValueRegex(nullptr, idx); } FileSpecList &ThreadProperties::GetLibrariesToAvoid() const { const uint32_t idx = ePropertyStepAvoidLibraries; OptionValueFileSpecList *option_value = m_collection_sp->GetPropertyAtIndexAsOptionValueFileSpecList(nullptr, false, idx); assert(option_value); return option_value->GetCurrentValue(); } bool ThreadProperties::GetTraceEnabledState() const { const uint32_t idx = ePropertyEnableThreadTrace; return m_collection_sp->GetPropertyAtIndexAsBoolean( nullptr, idx, g_properties[idx].default_uint_value != 0); } bool ThreadProperties::GetStepInAvoidsNoDebug() const { const uint32_t idx = ePropertyStepInAvoidsNoDebug; return m_collection_sp->GetPropertyAtIndexAsBoolean( nullptr, idx, g_properties[idx].default_uint_value != 0); } bool ThreadProperties::GetStepOutAvoidsNoDebug() const { const uint32_t idx = ePropertyStepOutAvoidsNoDebug; return m_collection_sp->GetPropertyAtIndexAsBoolean( nullptr, idx, g_properties[idx].default_uint_value != 0); } //------------------------------------------------------------------ // Thread Event Data //------------------------------------------------------------------ const ConstString &Thread::ThreadEventData::GetFlavorString() { static ConstString g_flavor("Thread::ThreadEventData"); return g_flavor; } Thread::ThreadEventData::ThreadEventData(const lldb::ThreadSP thread_sp) : m_thread_sp(thread_sp), m_stack_id() {} Thread::ThreadEventData::ThreadEventData(const lldb::ThreadSP thread_sp, const StackID &stack_id) : m_thread_sp(thread_sp), m_stack_id(stack_id) {} Thread::ThreadEventData::ThreadEventData() : m_thread_sp(), m_stack_id() {} Thread::ThreadEventData::~ThreadEventData() = default; void Thread::ThreadEventData::Dump(Stream *s) const {} const Thread::ThreadEventData * Thread::ThreadEventData::GetEventDataFromEvent(const Event *event_ptr) { if (event_ptr) { const EventData *event_data = event_ptr->GetData(); if (event_data && event_data->GetFlavor() == ThreadEventData::GetFlavorString()) return static_cast(event_ptr->GetData()); } return nullptr; } ThreadSP Thread::ThreadEventData::GetThreadFromEvent(const Event *event_ptr) { ThreadSP thread_sp; const ThreadEventData *event_data = GetEventDataFromEvent(event_ptr); if (event_data) thread_sp = event_data->GetThread(); return thread_sp; } StackID Thread::ThreadEventData::GetStackIDFromEvent(const Event *event_ptr) { StackID stack_id; const ThreadEventData *event_data = GetEventDataFromEvent(event_ptr); if (event_data) stack_id = event_data->GetStackID(); return stack_id; } StackFrameSP Thread::ThreadEventData::GetStackFrameFromEvent(const Event *event_ptr) { const ThreadEventData *event_data = GetEventDataFromEvent(event_ptr); StackFrameSP frame_sp; if (event_data) { ThreadSP thread_sp = event_data->GetThread(); if (thread_sp) { frame_sp = thread_sp->GetStackFrameList()->GetFrameWithStackID( event_data->GetStackID()); } } return frame_sp; } //------------------------------------------------------------------ // Thread class //------------------------------------------------------------------ ConstString &Thread::GetStaticBroadcasterClass() { static ConstString class_name("lldb.thread"); return class_name; } Thread::Thread(Process &process, lldb::tid_t tid, bool use_invalid_index_id) : ThreadProperties(false), UserID(tid), Broadcaster(process.GetTarget().GetDebugger().GetBroadcasterManager(), Thread::GetStaticBroadcasterClass().AsCString()), m_process_wp(process.shared_from_this()), m_stop_info_sp(), m_stop_info_stop_id(0), m_stop_info_override_stop_id(0), m_index_id(use_invalid_index_id ? LLDB_INVALID_INDEX32 : process.GetNextThreadIndexID(tid)), m_reg_context_sp(), m_state(eStateUnloaded), m_state_mutex(), m_plan_stack(), m_completed_plan_stack(), m_frame_mutex(), m_curr_frames_sp(), m_prev_frames_sp(), m_resume_signal(LLDB_INVALID_SIGNAL_NUMBER), m_resume_state(eStateRunning), m_temporary_resume_state(eStateRunning), m_unwinder_ap(), m_destroy_called(false), m_override_should_notify(eLazyBoolCalculate), m_extended_info_fetched(false), m_extended_info() { Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_OBJECT)); if (log) log->Printf("%p Thread::Thread(tid = 0x%4.4" PRIx64 ")", static_cast(this), GetID()); CheckInWithManager(); QueueFundamentalPlan(true); } Thread::~Thread() { Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_OBJECT)); if (log) log->Printf("%p Thread::~Thread(tid = 0x%4.4" PRIx64 ")", static_cast(this), GetID()); /// If you hit this assert, it means your derived class forgot to call /// DoDestroy in its destructor. assert(m_destroy_called); } void Thread::DestroyThread() { // Tell any plans on the plan stacks that the thread is being destroyed since // any plans that have a thread go away in the middle of might need // to do cleanup, or in some cases NOT do cleanup... for (auto plan : m_plan_stack) plan->ThreadDestroyed(); for (auto plan : m_discarded_plan_stack) plan->ThreadDestroyed(); for (auto plan : m_completed_plan_stack) plan->ThreadDestroyed(); m_destroy_called = true; m_plan_stack.clear(); m_discarded_plan_stack.clear(); m_completed_plan_stack.clear(); // Push a ThreadPlanNull on the plan stack. That way we can continue assuming // that the // plan stack is never empty, but if somebody errantly asks questions of a // destroyed thread // without checking first whether it is destroyed, they won't crash. ThreadPlanSP null_plan_sp(new ThreadPlanNull(*this)); m_plan_stack.push_back(null_plan_sp); m_stop_info_sp.reset(); m_reg_context_sp.reset(); m_unwinder_ap.reset(); std::lock_guard guard(m_frame_mutex); m_curr_frames_sp.reset(); m_prev_frames_sp.reset(); } void Thread::BroadcastSelectedFrameChange(StackID &new_frame_id) { if (EventTypeHasListeners(eBroadcastBitSelectedFrameChanged)) BroadcastEvent(eBroadcastBitSelectedFrameChanged, new ThreadEventData(this->shared_from_this(), new_frame_id)); } lldb::StackFrameSP Thread::GetSelectedFrame() { StackFrameListSP stack_frame_list_sp(GetStackFrameList()); StackFrameSP frame_sp = stack_frame_list_sp->GetFrameAtIndex( stack_frame_list_sp->GetSelectedFrameIndex()); FunctionOptimizationWarning(frame_sp.get()); return frame_sp; } uint32_t Thread::SetSelectedFrame(lldb_private::StackFrame *frame, bool broadcast) { uint32_t ret_value = GetStackFrameList()->SetSelectedFrame(frame); if (broadcast) BroadcastSelectedFrameChange(frame->GetStackID()); FunctionOptimizationWarning(frame); return ret_value; } bool Thread::SetSelectedFrameByIndex(uint32_t frame_idx, bool broadcast) { StackFrameSP frame_sp(GetStackFrameList()->GetFrameAtIndex(frame_idx)); if (frame_sp) { GetStackFrameList()->SetSelectedFrame(frame_sp.get()); if (broadcast) BroadcastSelectedFrameChange(frame_sp->GetStackID()); FunctionOptimizationWarning(frame_sp.get()); return true; } else return false; } bool Thread::SetSelectedFrameByIndexNoisily(uint32_t frame_idx, Stream &output_stream) { const bool broadcast = true; bool success = SetSelectedFrameByIndex(frame_idx, broadcast); if (success) { StackFrameSP frame_sp = GetSelectedFrame(); if (frame_sp) { bool already_shown = false; SymbolContext frame_sc( frame_sp->GetSymbolContext(eSymbolContextLineEntry)); if (GetProcess()->GetTarget().GetDebugger().GetUseExternalEditor() && frame_sc.line_entry.file && frame_sc.line_entry.line != 0) { already_shown = Host::OpenFileInExternalEditor( frame_sc.line_entry.file, frame_sc.line_entry.line); } bool show_frame_info = true; bool show_source = !already_shown; FunctionOptimizationWarning(frame_sp.get()); return frame_sp->GetStatus(output_stream, show_frame_info, show_source); } return false; } else return false; } void Thread::FunctionOptimizationWarning(StackFrame *frame) { if (frame && frame->HasDebugInformation() && GetProcess()->GetWarningsOptimization()) { SymbolContext sc = frame->GetSymbolContext(eSymbolContextFunction | eSymbolContextModule); GetProcess()->PrintWarningOptimization(sc); } } lldb::StopInfoSP Thread::GetStopInfo() { if (m_destroy_called) return m_stop_info_sp; ThreadPlanSP completed_plan_sp(GetCompletedPlan()); ProcessSP process_sp(GetProcess()); const uint32_t stop_id = process_sp ? process_sp->GetStopID() : UINT32_MAX; // Here we select the stop info according to priorirty: // - m_stop_info_sp (if not trace) - preset value // - completed plan stop info - new value with plan from completed plan stack // - m_stop_info_sp (trace stop reason is OK now) // - ask GetPrivateStopInfo to set stop info bool have_valid_stop_info = m_stop_info_sp && m_stop_info_sp ->IsValid() && m_stop_info_stop_id == stop_id; bool have_valid_completed_plan = completed_plan_sp && completed_plan_sp->PlanSucceeded(); bool plan_overrides_trace = have_valid_stop_info && have_valid_completed_plan && (m_stop_info_sp->GetStopReason() == eStopReasonTrace); if (have_valid_stop_info && !plan_overrides_trace) { return m_stop_info_sp; } else if (have_valid_completed_plan) { return StopInfo::CreateStopReasonWithPlan( completed_plan_sp, GetReturnValueObject(), GetExpressionVariable()); } else { GetPrivateStopInfo(); return m_stop_info_sp; } } lldb::StopInfoSP Thread::GetPrivateStopInfo() { if (m_destroy_called) return m_stop_info_sp; ProcessSP process_sp(GetProcess()); if (process_sp) { const uint32_t process_stop_id = process_sp->GetStopID(); if (m_stop_info_stop_id != process_stop_id) { if (m_stop_info_sp) { if (m_stop_info_sp->IsValid() || IsStillAtLastBreakpointHit() || GetCurrentPlan()->IsVirtualStep()) SetStopInfo(m_stop_info_sp); else m_stop_info_sp.reset(); } if (!m_stop_info_sp) { if (!CalculateStopInfo()) SetStopInfo(StopInfoSP()); } } // The stop info can be manually set by calling Thread::SetStopInfo() // prior to this function ever getting called, so we can't rely on // "m_stop_info_stop_id != process_stop_id" as the condition for // the if statement below, we must also check the stop info to see // if we need to override it. See the header documentation in // Process::GetStopInfoOverrideCallback() for more information on // the stop info override callback. if (m_stop_info_override_stop_id != process_stop_id) { m_stop_info_override_stop_id = process_stop_id; if (m_stop_info_sp) { ArchSpec::StopInfoOverrideCallbackType callback = GetProcess()->GetStopInfoOverrideCallback(); if (callback) callback(*this); } } } return m_stop_info_sp; } lldb::StopReason Thread::GetStopReason() { lldb::StopInfoSP stop_info_sp(GetStopInfo()); if (stop_info_sp) return stop_info_sp->GetStopReason(); return eStopReasonNone; } bool Thread::StopInfoIsUpToDate() const { ProcessSP process_sp(GetProcess()); if (process_sp) return m_stop_info_stop_id == process_sp->GetStopID(); else return true; // Process is no longer around so stop info is always up to // date... } void Thread::ResetStopInfo() { if (m_stop_info_sp) { m_stop_info_sp.reset(); } } void Thread::SetStopInfo(const lldb::StopInfoSP &stop_info_sp) { m_stop_info_sp = stop_info_sp; if (m_stop_info_sp) { m_stop_info_sp->MakeStopInfoValid(); // If we are overriding the ShouldReportStop, do that here: if (m_override_should_notify != eLazyBoolCalculate) m_stop_info_sp->OverrideShouldNotify(m_override_should_notify == eLazyBoolYes); } ProcessSP process_sp(GetProcess()); if (process_sp) m_stop_info_stop_id = process_sp->GetStopID(); else m_stop_info_stop_id = UINT32_MAX; Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_THREAD)); if (log) log->Printf("%p: tid = 0x%" PRIx64 ": stop info = %s (stop_id = %u)", static_cast(this), GetID(), stop_info_sp ? stop_info_sp->GetDescription() : "", m_stop_info_stop_id); } void Thread::SetShouldReportStop(Vote vote) { if (vote == eVoteNoOpinion) return; else { m_override_should_notify = (vote == eVoteYes ? eLazyBoolYes : eLazyBoolNo); if (m_stop_info_sp) m_stop_info_sp->OverrideShouldNotify(m_override_should_notify == eLazyBoolYes); } } void Thread::SetStopInfoToNothing() { // Note, we can't just NULL out the private reason, or the native thread // implementation will try to // go calculate it again. For now, just set it to a Unix Signal with an // invalid signal number. SetStopInfo( StopInfo::CreateStopReasonWithSignal(*this, LLDB_INVALID_SIGNAL_NUMBER)); } bool Thread::ThreadStoppedForAReason(void) { return (bool)GetPrivateStopInfo(); } bool Thread::CheckpointThreadState(ThreadStateCheckpoint &saved_state) { saved_state.register_backup_sp.reset(); lldb::StackFrameSP frame_sp(GetStackFrameAtIndex(0)); if (frame_sp) { lldb::RegisterCheckpointSP reg_checkpoint_sp( new RegisterCheckpoint(RegisterCheckpoint::Reason::eExpression)); if (reg_checkpoint_sp) { lldb::RegisterContextSP reg_ctx_sp(frame_sp->GetRegisterContext()); if (reg_ctx_sp && reg_ctx_sp->ReadAllRegisterValues(*reg_checkpoint_sp)) saved_state.register_backup_sp = reg_checkpoint_sp; } } if (!saved_state.register_backup_sp) return false; saved_state.stop_info_sp = GetStopInfo(); ProcessSP process_sp(GetProcess()); if (process_sp) saved_state.orig_stop_id = process_sp->GetStopID(); saved_state.current_inlined_depth = GetCurrentInlinedDepth(); saved_state.m_completed_plan_stack = m_completed_plan_stack; return true; } bool Thread::RestoreRegisterStateFromCheckpoint( ThreadStateCheckpoint &saved_state) { if (saved_state.register_backup_sp) { lldb::StackFrameSP frame_sp(GetStackFrameAtIndex(0)); if (frame_sp) { lldb::RegisterContextSP reg_ctx_sp(frame_sp->GetRegisterContext()); if (reg_ctx_sp) { bool ret = reg_ctx_sp->WriteAllRegisterValues(*saved_state.register_backup_sp); // Clear out all stack frames as our world just changed. ClearStackFrames(); reg_ctx_sp->InvalidateIfNeeded(true); if (m_unwinder_ap.get()) m_unwinder_ap->Clear(); return ret; } } } return false; } bool Thread::RestoreThreadStateFromCheckpoint( ThreadStateCheckpoint &saved_state) { if (saved_state.stop_info_sp) saved_state.stop_info_sp->MakeStopInfoValid(); SetStopInfo(saved_state.stop_info_sp); GetStackFrameList()->SetCurrentInlinedDepth( saved_state.current_inlined_depth); m_completed_plan_stack = saved_state.m_completed_plan_stack; return true; } StateType Thread::GetState() const { // If any other threads access this we will need a mutex for it std::lock_guard guard(m_state_mutex); return m_state; } void Thread::SetState(StateType state) { std::lock_guard guard(m_state_mutex); m_state = state; } void Thread::WillStop() { ThreadPlan *current_plan = GetCurrentPlan(); // FIXME: I may decide to disallow threads with no plans. In which // case this should go to an assert. if (!current_plan) return; current_plan->WillStop(); } void Thread::SetupForResume() { if (GetResumeState() != eStateSuspended) { // If we're at a breakpoint push the step-over breakpoint plan. Do this // before // telling the current plan it will resume, since we might change what the // current // plan is. lldb::RegisterContextSP reg_ctx_sp(GetRegisterContext()); if (reg_ctx_sp) { const addr_t thread_pc = reg_ctx_sp->GetPC(); BreakpointSiteSP bp_site_sp = GetProcess()->GetBreakpointSiteList().FindByAddress(thread_pc); if (bp_site_sp) { // Note, don't assume there's a ThreadPlanStepOverBreakpoint, the target // may not require anything // special to step over a breakpoint. ThreadPlan *cur_plan = GetCurrentPlan(); bool push_step_over_bp_plan = false; if (cur_plan->GetKind() == ThreadPlan::eKindStepOverBreakpoint) { ThreadPlanStepOverBreakpoint *bp_plan = (ThreadPlanStepOverBreakpoint *)cur_plan; if (bp_plan->GetBreakpointLoadAddress() != thread_pc) push_step_over_bp_plan = true; } else push_step_over_bp_plan = true; if (push_step_over_bp_plan) { ThreadPlanSP step_bp_plan_sp(new ThreadPlanStepOverBreakpoint(*this)); if (step_bp_plan_sp) { step_bp_plan_sp->SetPrivate(true); if (GetCurrentPlan()->RunState() != eStateStepping) { ThreadPlanStepOverBreakpoint *step_bp_plan = static_cast( step_bp_plan_sp.get()); step_bp_plan->SetAutoContinue(true); } QueueThreadPlan(step_bp_plan_sp, false); } } } } } } bool Thread::ShouldResume(StateType resume_state) { // At this point clear the completed plan stack. m_completed_plan_stack.clear(); m_discarded_plan_stack.clear(); m_override_should_notify = eLazyBoolCalculate; StateType prev_resume_state = GetTemporaryResumeState(); SetTemporaryResumeState(resume_state); lldb::ThreadSP backing_thread_sp(GetBackingThread()); if (backing_thread_sp) backing_thread_sp->SetTemporaryResumeState(resume_state); // Make sure m_stop_info_sp is valid. Don't do this for threads we suspended // in the previous run. if (prev_resume_state != eStateSuspended) GetPrivateStopInfo(); // This is a little dubious, but we are trying to limit how often we actually // fetch stop info from // the target, 'cause that slows down single stepping. So assume that if we // got to the point where // we're about to resume, and we haven't yet had to fetch the stop reason, // then it doesn't need to know // about the fact that we are resuming... const uint32_t process_stop_id = GetProcess()->GetStopID(); if (m_stop_info_stop_id == process_stop_id && (m_stop_info_sp && m_stop_info_sp->IsValid())) { StopInfo *stop_info = GetPrivateStopInfo().get(); if (stop_info) stop_info->WillResume(resume_state); } // Tell all the plans that we are about to resume in case they need to clear // any state. // We distinguish between the plan on the top of the stack and the lower // plans in case a plan needs to do any special business before it runs. bool need_to_resume = false; ThreadPlan *plan_ptr = GetCurrentPlan(); if (plan_ptr) { need_to_resume = plan_ptr->WillResume(resume_state, true); while ((plan_ptr = GetPreviousPlan(plan_ptr)) != nullptr) { plan_ptr->WillResume(resume_state, false); } // If the WillResume for the plan says we are faking a resume, then it will // have set an appropriate stop info. // In that case, don't reset it here. if (need_to_resume && resume_state != eStateSuspended) { m_stop_info_sp.reset(); } } if (need_to_resume) { ClearStackFrames(); // Let Thread subclasses do any special work they need to prior to resuming WillResume(resume_state); } return need_to_resume; } void Thread::DidResume() { SetResumeSignal(LLDB_INVALID_SIGNAL_NUMBER); } void Thread::DidStop() { SetState(eStateStopped); } bool Thread::ShouldStop(Event *event_ptr) { ThreadPlan *current_plan = GetCurrentPlan(); bool should_stop = true; Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_STEP)); if (GetResumeState() == eStateSuspended) { if (log) log->Printf("Thread::%s for tid = 0x%4.4" PRIx64 " 0x%4.4" PRIx64 ", should_stop = 0 (ignore since thread was suspended)", __FUNCTION__, GetID(), GetProtocolID()); return false; } if (GetTemporaryResumeState() == eStateSuspended) { if (log) log->Printf("Thread::%s for tid = 0x%4.4" PRIx64 " 0x%4.4" PRIx64 ", should_stop = 0 (ignore since thread was suspended)", __FUNCTION__, GetID(), GetProtocolID()); return false; } // Based on the current thread plan and process stop info, check if this // thread caused the process to stop. NOTE: this must take place before // the plan is moved from the current plan stack to the completed plan // stack. if (!ThreadStoppedForAReason()) { if (log) log->Printf("Thread::%s for tid = 0x%4.4" PRIx64 " 0x%4.4" PRIx64 ", pc = 0x%16.16" PRIx64 ", should_stop = 0 (ignore since no stop reason)", __FUNCTION__, GetID(), GetProtocolID(), GetRegisterContext() ? GetRegisterContext()->GetPC() : LLDB_INVALID_ADDRESS); return false; } if (log) { log->Printf("Thread::%s(%p) for tid = 0x%4.4" PRIx64 " 0x%4.4" PRIx64 ", pc = 0x%16.16" PRIx64, __FUNCTION__, static_cast(this), GetID(), GetProtocolID(), GetRegisterContext() ? GetRegisterContext()->GetPC() : LLDB_INVALID_ADDRESS); log->Printf("^^^^^^^^ Thread::ShouldStop Begin ^^^^^^^^"); StreamString s; s.IndentMore(); DumpThreadPlans(&s); log->Printf("Plan stack initial state:\n%s", s.GetData()); } // The top most plan always gets to do the trace log... current_plan->DoTraceLog(); // First query the stop info's ShouldStopSynchronous. This handles // "synchronous" stop reasons, for example the breakpoint // command on internal breakpoints. If a synchronous stop reason says we // should not stop, then we don't have to // do any more work on this stop. StopInfoSP private_stop_info(GetPrivateStopInfo()); if (private_stop_info && !private_stop_info->ShouldStopSynchronous(event_ptr)) { if (log) log->Printf("StopInfo::ShouldStop async callback says we should not " "stop, returning ShouldStop of false."); return false; } // If we've already been restarted, don't query the plans since the state they // would examine is not current. if (Process::ProcessEventData::GetRestartedFromEvent(event_ptr)) return false; // Before the plans see the state of the world, calculate the current inlined // depth. GetStackFrameList()->CalculateCurrentInlinedDepth(); // If the base plan doesn't understand why we stopped, then we have to find a // plan that does. // If that plan is still working, then we don't need to do any more work. If // the plan that explains // the stop is done, then we should pop all the plans below it, and pop it, // and then let the plans above it decide // whether they still need to do more work. bool done_processing_current_plan = false; if (!current_plan->PlanExplainsStop(event_ptr)) { if (current_plan->TracerExplainsStop()) { done_processing_current_plan = true; should_stop = false; } else { // If the current plan doesn't explain the stop, then find one that // does and let it handle the situation. ThreadPlan *plan_ptr = current_plan; while ((plan_ptr = GetPreviousPlan(plan_ptr)) != nullptr) { if (plan_ptr->PlanExplainsStop(event_ptr)) { should_stop = plan_ptr->ShouldStop(event_ptr); // plan_ptr explains the stop, next check whether plan_ptr is done, if // so, then we should take it // and all the plans below it off the stack. if (plan_ptr->MischiefManaged()) { // We're going to pop the plans up to and including the plan that // explains the stop. ThreadPlan *prev_plan_ptr = GetPreviousPlan(plan_ptr); do { if (should_stop) current_plan->WillStop(); PopPlan(); } while ((current_plan = GetCurrentPlan()) != prev_plan_ptr); // Now, if the responsible plan was not "Okay to discard" then we're // done, // otherwise we forward this to the next plan in the stack below. done_processing_current_plan = (plan_ptr->IsMasterPlan() && !plan_ptr->OkayToDiscard()); } else done_processing_current_plan = true; break; } } } } if (!done_processing_current_plan) { bool over_ride_stop = current_plan->ShouldAutoContinue(event_ptr); if (log) log->Printf("Plan %s explains stop, auto-continue %i.", current_plan->GetName(), over_ride_stop); // We're starting from the base plan, so just let it decide; if (PlanIsBasePlan(current_plan)) { should_stop = current_plan->ShouldStop(event_ptr); if (log) log->Printf("Base plan says should stop: %i.", should_stop); } else { // Otherwise, don't let the base plan override what the other plans say to // do, since // presumably if there were other plans they would know what to do... while (1) { if (PlanIsBasePlan(current_plan)) break; should_stop = current_plan->ShouldStop(event_ptr); if (log) log->Printf("Plan %s should stop: %d.", current_plan->GetName(), should_stop); if (current_plan->MischiefManaged()) { if (should_stop) current_plan->WillStop(); // If a Master Plan wants to stop, and wants to stick on the stack, we // let it. // Otherwise, see if the plan's parent wants to stop. if (should_stop && current_plan->IsMasterPlan() && !current_plan->OkayToDiscard()) { PopPlan(); break; } else { PopPlan(); current_plan = GetCurrentPlan(); if (current_plan == nullptr) { break; } } } else { break; } } } if (over_ride_stop) should_stop = false; } // One other potential problem is that we set up a master plan, then stop in // before it is complete - for instance // by hitting a breakpoint during a step-over - then do some step/finish/etc // operations that wind up // past the end point condition of the initial plan. We don't want to strand // the original plan on the stack, // This code clears stale plans off the stack. if (should_stop) { ThreadPlan *plan_ptr = GetCurrentPlan(); // Discard the stale plans and all plans below them in the stack, // plus move the completed plans to the completed plan stack while (!PlanIsBasePlan(plan_ptr)) { bool stale = plan_ptr->IsPlanStale(); ThreadPlan *examined_plan = plan_ptr; plan_ptr = GetPreviousPlan(examined_plan); if (stale) { if (log) log->Printf( "Plan %s being discarded in cleanup, it says it is already done.", examined_plan->GetName()); while (GetCurrentPlan() != examined_plan) { DiscardPlan(); } if (examined_plan->IsPlanComplete()) { // plan is complete but does not explain the stop (example: step to a line // with breakpoint), let us move the plan to completed_plan_stack anyway PopPlan(); } else DiscardPlan(); } } } if (log) { StreamString s; s.IndentMore(); DumpThreadPlans(&s); log->Printf("Plan stack final state:\n%s", s.GetData()); log->Printf("vvvvvvvv Thread::ShouldStop End (returning %i) vvvvvvvv", should_stop); } return should_stop; } Vote Thread::ShouldReportStop(Event *event_ptr) { StateType thread_state = GetResumeState(); StateType temp_thread_state = GetTemporaryResumeState(); Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_STEP)); if (thread_state == eStateSuspended || thread_state == eStateInvalid) { if (log) log->Printf("Thread::ShouldReportStop() tid = 0x%4.4" PRIx64 ": returning vote %i (state was suspended or invalid)", GetID(), eVoteNoOpinion); return eVoteNoOpinion; } if (temp_thread_state == eStateSuspended || temp_thread_state == eStateInvalid) { if (log) log->Printf( "Thread::ShouldReportStop() tid = 0x%4.4" PRIx64 ": returning vote %i (temporary state was suspended or invalid)", GetID(), eVoteNoOpinion); return eVoteNoOpinion; } if (!ThreadStoppedForAReason()) { if (log) log->Printf("Thread::ShouldReportStop() tid = 0x%4.4" PRIx64 ": returning vote %i (thread didn't stop for a reason.)", GetID(), eVoteNoOpinion); return eVoteNoOpinion; } if (m_completed_plan_stack.size() > 0) { // Don't use GetCompletedPlan here, since that suppresses private plans. if (log) log->Printf("Thread::ShouldReportStop() tid = 0x%4.4" PRIx64 ": returning vote for complete stack's back plan", GetID()); return m_completed_plan_stack.back()->ShouldReportStop(event_ptr); } else { Vote thread_vote = eVoteNoOpinion; ThreadPlan *plan_ptr = GetCurrentPlan(); while (1) { if (plan_ptr->PlanExplainsStop(event_ptr)) { thread_vote = plan_ptr->ShouldReportStop(event_ptr); break; } if (PlanIsBasePlan(plan_ptr)) break; else plan_ptr = GetPreviousPlan(plan_ptr); } if (log) log->Printf("Thread::ShouldReportStop() tid = 0x%4.4" PRIx64 ": returning vote %i for current plan", GetID(), thread_vote); return thread_vote; } } Vote Thread::ShouldReportRun(Event *event_ptr) { StateType thread_state = GetResumeState(); if (thread_state == eStateSuspended || thread_state == eStateInvalid) { return eVoteNoOpinion; } Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_STEP)); if (m_completed_plan_stack.size() > 0) { // Don't use GetCompletedPlan here, since that suppresses private plans. if (log) log->Printf("Current Plan for thread %d(%p) (0x%4.4" PRIx64 ", %s): %s being asked whether we should report run.", GetIndexID(), static_cast(this), GetID(), StateAsCString(GetTemporaryResumeState()), m_completed_plan_stack.back()->GetName()); return m_completed_plan_stack.back()->ShouldReportRun(event_ptr); } else { if (log) log->Printf("Current Plan for thread %d(%p) (0x%4.4" PRIx64 ", %s): %s being asked whether we should report run.", GetIndexID(), static_cast(this), GetID(), StateAsCString(GetTemporaryResumeState()), GetCurrentPlan()->GetName()); return GetCurrentPlan()->ShouldReportRun(event_ptr); } } bool Thread::MatchesSpec(const ThreadSpec *spec) { return (spec == nullptr) ? true : spec->ThreadPassesBasicTests(*this); } void Thread::PushPlan(ThreadPlanSP &thread_plan_sp) { if (thread_plan_sp) { // If the thread plan doesn't already have a tracer, give it its parent's // tracer: if (!thread_plan_sp->GetThreadPlanTracer()) thread_plan_sp->SetThreadPlanTracer( m_plan_stack.back()->GetThreadPlanTracer()); m_plan_stack.push_back(thread_plan_sp); thread_plan_sp->DidPush(); Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_STEP)); if (log) { StreamString s; thread_plan_sp->GetDescription(&s, lldb::eDescriptionLevelFull); log->Printf("Thread::PushPlan(0x%p): \"%s\", tid = 0x%4.4" PRIx64 ".", static_cast(this), s.GetData(), thread_plan_sp->GetThread().GetID()); } } } void Thread::PopPlan() { Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_STEP)); if (m_plan_stack.size() <= 1) return; else { ThreadPlanSP &plan = m_plan_stack.back(); if (log) { log->Printf("Popping plan: \"%s\", tid = 0x%4.4" PRIx64 ".", plan->GetName(), plan->GetThread().GetID()); } m_completed_plan_stack.push_back(plan); plan->WillPop(); m_plan_stack.pop_back(); } } void Thread::DiscardPlan() { Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_STEP)); if (m_plan_stack.size() > 1) { ThreadPlanSP &plan = m_plan_stack.back(); if (log) log->Printf("Discarding plan: \"%s\", tid = 0x%4.4" PRIx64 ".", plan->GetName(), plan->GetThread().GetID()); m_discarded_plan_stack.push_back(plan); plan->WillPop(); m_plan_stack.pop_back(); } } ThreadPlan *Thread::GetCurrentPlan() { // There will always be at least the base plan. If somebody is mucking with a // thread with an empty plan stack, we should assert right away. return m_plan_stack.empty() ? nullptr : m_plan_stack.back().get(); } ThreadPlanSP Thread::GetCompletedPlan() { ThreadPlanSP empty_plan_sp; if (!m_completed_plan_stack.empty()) { for (int i = m_completed_plan_stack.size() - 1; i >= 0; i--) { ThreadPlanSP completed_plan_sp; completed_plan_sp = m_completed_plan_stack[i]; if (!completed_plan_sp->GetPrivate()) return completed_plan_sp; } } return empty_plan_sp; } ValueObjectSP Thread::GetReturnValueObject() { if (!m_completed_plan_stack.empty()) { for (int i = m_completed_plan_stack.size() - 1; i >= 0; i--) { ValueObjectSP return_valobj_sp; return_valobj_sp = m_completed_plan_stack[i]->GetReturnValueObject(); if (return_valobj_sp) return return_valobj_sp; } } return ValueObjectSP(); } ExpressionVariableSP Thread::GetExpressionVariable() { if (!m_completed_plan_stack.empty()) { for (int i = m_completed_plan_stack.size() - 1; i >= 0; i--) { ExpressionVariableSP expression_variable_sp; expression_variable_sp = m_completed_plan_stack[i]->GetExpressionVariable(); if (expression_variable_sp) return expression_variable_sp; } } return ExpressionVariableSP(); } bool Thread::IsThreadPlanDone(ThreadPlan *plan) { if (!m_completed_plan_stack.empty()) { for (int i = m_completed_plan_stack.size() - 1; i >= 0; i--) { if (m_completed_plan_stack[i].get() == plan) return true; } } return false; } bool Thread::WasThreadPlanDiscarded(ThreadPlan *plan) { if (!m_discarded_plan_stack.empty()) { for (int i = m_discarded_plan_stack.size() - 1; i >= 0; i--) { if (m_discarded_plan_stack[i].get() == plan) return true; } } return false; } bool Thread::CompletedPlanOverridesBreakpoint() { return (!m_completed_plan_stack.empty()) ; } ThreadPlan *Thread::GetPreviousPlan(ThreadPlan *current_plan) { if (current_plan == nullptr) return nullptr; int stack_size = m_completed_plan_stack.size(); for (int i = stack_size - 1; i > 0; i--) { if (current_plan == m_completed_plan_stack[i].get()) return m_completed_plan_stack[i - 1].get(); } if (stack_size > 0 && m_completed_plan_stack[0].get() == current_plan) { return GetCurrentPlan(); } stack_size = m_plan_stack.size(); for (int i = stack_size - 1; i > 0; i--) { if (current_plan == m_plan_stack[i].get()) return m_plan_stack[i - 1].get(); } return nullptr; } void Thread::QueueThreadPlan(ThreadPlanSP &thread_plan_sp, bool abort_other_plans) { if (abort_other_plans) DiscardThreadPlans(true); PushPlan(thread_plan_sp); } void Thread::EnableTracer(bool value, bool single_stepping) { int stack_size = m_plan_stack.size(); for (int i = 0; i < stack_size; i++) { if (m_plan_stack[i]->GetThreadPlanTracer()) { m_plan_stack[i]->GetThreadPlanTracer()->EnableTracing(value); m_plan_stack[i]->GetThreadPlanTracer()->EnableSingleStep(single_stepping); } } } void Thread::SetTracer(lldb::ThreadPlanTracerSP &tracer_sp) { int stack_size = m_plan_stack.size(); for (int i = 0; i < stack_size; i++) m_plan_stack[i]->SetThreadPlanTracer(tracer_sp); } bool Thread::DiscardUserThreadPlansUpToIndex(uint32_t thread_index) { // Count the user thread plans from the back end to get the number of the one // we want // to discard: uint32_t idx = 0; ThreadPlan *up_to_plan_ptr = nullptr; for (ThreadPlanSP plan_sp : m_plan_stack) { if (plan_sp->GetPrivate()) continue; if (idx == thread_index) { up_to_plan_ptr = plan_sp.get(); break; } else idx++; } if (up_to_plan_ptr == nullptr) return false; DiscardThreadPlansUpToPlan(up_to_plan_ptr); return true; } void Thread::DiscardThreadPlansUpToPlan(lldb::ThreadPlanSP &up_to_plan_sp) { DiscardThreadPlansUpToPlan(up_to_plan_sp.get()); } void Thread::DiscardThreadPlansUpToPlan(ThreadPlan *up_to_plan_ptr) { Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_STEP)); if (log) log->Printf("Discarding thread plans for thread tid = 0x%4.4" PRIx64 ", up to %p", GetID(), static_cast(up_to_plan_ptr)); int stack_size = m_plan_stack.size(); // If the input plan is nullptr, discard all plans. Otherwise make sure this // plan is in the // stack, and if so discard up to and including it. if (up_to_plan_ptr == nullptr) { for (int i = stack_size - 1; i > 0; i--) DiscardPlan(); } else { bool found_it = false; for (int i = stack_size - 1; i > 0; i--) { if (m_plan_stack[i].get() == up_to_plan_ptr) found_it = true; } if (found_it) { bool last_one = false; for (int i = stack_size - 1; i > 0 && !last_one; i--) { if (GetCurrentPlan() == up_to_plan_ptr) last_one = true; DiscardPlan(); } } } } void Thread::DiscardThreadPlans(bool force) { Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_STEP)); if (log) { log->Printf("Discarding thread plans for thread (tid = 0x%4.4" PRIx64 ", force %d)", GetID(), force); } if (force) { int stack_size = m_plan_stack.size(); for (int i = stack_size - 1; i > 0; i--) { DiscardPlan(); } return; } while (1) { int master_plan_idx; bool discard = true; // Find the first master plan, see if it wants discarding, and if yes // discard up to it. for (master_plan_idx = m_plan_stack.size() - 1; master_plan_idx >= 0; master_plan_idx--) { if (m_plan_stack[master_plan_idx]->IsMasterPlan()) { discard = m_plan_stack[master_plan_idx]->OkayToDiscard(); break; } } if (discard) { // First pop all the dependent plans: for (int i = m_plan_stack.size() - 1; i > master_plan_idx; i--) { // FIXME: Do we need a finalize here, or is the rule that // "PrepareForStop" // for the plan leaves it in a state that it is safe to pop the plan // with no more notice? DiscardPlan(); } // Now discard the master plan itself. // The bottom-most plan never gets discarded. "OkayToDiscard" for it // means // discard it's dependent plans, but not it... if (master_plan_idx > 0) { DiscardPlan(); } } else { // If the master plan doesn't want to get discarded, then we're done. break; } } } bool Thread::PlanIsBasePlan(ThreadPlan *plan_ptr) { if (plan_ptr->IsBasePlan()) return true; else if (m_plan_stack.size() == 0) return false; else return m_plan_stack[0].get() == plan_ptr; } Status Thread::UnwindInnermostExpression() { Status error; int stack_size = m_plan_stack.size(); // If the input plan is nullptr, discard all plans. Otherwise make sure this // plan is in the // stack, and if so discard up to and including it. for (int i = stack_size - 1; i > 0; i--) { if (m_plan_stack[i]->GetKind() == ThreadPlan::eKindCallFunction) { DiscardThreadPlansUpToPlan(m_plan_stack[i].get()); return error; } } error.SetErrorString("No expressions currently active on this thread"); return error; } ThreadPlanSP Thread::QueueFundamentalPlan(bool abort_other_plans) { ThreadPlanSP thread_plan_sp(new ThreadPlanBase(*this)); QueueThreadPlan(thread_plan_sp, abort_other_plans); return thread_plan_sp; } ThreadPlanSP Thread::QueueThreadPlanForStepSingleInstruction( bool step_over, bool abort_other_plans, bool stop_other_threads) { ThreadPlanSP thread_plan_sp(new ThreadPlanStepInstruction( *this, step_over, stop_other_threads, eVoteNoOpinion, eVoteNoOpinion)); QueueThreadPlan(thread_plan_sp, abort_other_plans); return thread_plan_sp; } ThreadPlanSP Thread::QueueThreadPlanForStepOverRange( bool abort_other_plans, const AddressRange &range, const SymbolContext &addr_context, lldb::RunMode stop_other_threads, LazyBool step_out_avoids_code_withoug_debug_info) { ThreadPlanSP thread_plan_sp; thread_plan_sp.reset(new ThreadPlanStepOverRange( *this, range, addr_context, stop_other_threads, step_out_avoids_code_withoug_debug_info)); QueueThreadPlan(thread_plan_sp, abort_other_plans); return thread_plan_sp; } // Call the QueueThreadPlanForStepOverRange method which takes an address range. ThreadPlanSP Thread::QueueThreadPlanForStepOverRange( bool abort_other_plans, const LineEntry &line_entry, const SymbolContext &addr_context, lldb::RunMode stop_other_threads, LazyBool step_out_avoids_code_withoug_debug_info) { return QueueThreadPlanForStepOverRange( abort_other_plans, line_entry.GetSameLineContiguousAddressRange(), addr_context, stop_other_threads, step_out_avoids_code_withoug_debug_info); } ThreadPlanSP Thread::QueueThreadPlanForStepInRange( bool abort_other_plans, const AddressRange &range, const SymbolContext &addr_context, const char *step_in_target, lldb::RunMode stop_other_threads, LazyBool step_in_avoids_code_without_debug_info, LazyBool step_out_avoids_code_without_debug_info) { ThreadPlanSP thread_plan_sp( new ThreadPlanStepInRange(*this, range, addr_context, stop_other_threads, step_in_avoids_code_without_debug_info, step_out_avoids_code_without_debug_info)); ThreadPlanStepInRange *plan = static_cast(thread_plan_sp.get()); if (step_in_target) plan->SetStepInTarget(step_in_target); QueueThreadPlan(thread_plan_sp, abort_other_plans); return thread_plan_sp; } // Call the QueueThreadPlanForStepInRange method which takes an address range. ThreadPlanSP Thread::QueueThreadPlanForStepInRange( bool abort_other_plans, const LineEntry &line_entry, const SymbolContext &addr_context, const char *step_in_target, lldb::RunMode stop_other_threads, LazyBool step_in_avoids_code_without_debug_info, LazyBool step_out_avoids_code_without_debug_info) { return QueueThreadPlanForStepInRange( abort_other_plans, line_entry.GetSameLineContiguousAddressRange(), addr_context, step_in_target, stop_other_threads, step_in_avoids_code_without_debug_info, step_out_avoids_code_without_debug_info); } ThreadPlanSP Thread::QueueThreadPlanForStepOut( bool abort_other_plans, SymbolContext *addr_context, bool first_insn, bool stop_other_threads, Vote stop_vote, Vote run_vote, uint32_t frame_idx, LazyBool step_out_avoids_code_without_debug_info) { ThreadPlanSP thread_plan_sp(new ThreadPlanStepOut( *this, addr_context, first_insn, stop_other_threads, stop_vote, run_vote, frame_idx, step_out_avoids_code_without_debug_info)); if (thread_plan_sp->ValidatePlan(nullptr)) { QueueThreadPlan(thread_plan_sp, abort_other_plans); return thread_plan_sp; } else { return ThreadPlanSP(); } } ThreadPlanSP Thread::QueueThreadPlanForStepOutNoShouldStop( bool abort_other_plans, SymbolContext *addr_context, bool first_insn, bool stop_other_threads, Vote stop_vote, Vote run_vote, uint32_t frame_idx, bool continue_to_next_branch) { const bool calculate_return_value = false; // No need to calculate the return value here. ThreadPlanSP thread_plan_sp(new ThreadPlanStepOut( *this, addr_context, first_insn, stop_other_threads, stop_vote, run_vote, frame_idx, eLazyBoolNo, continue_to_next_branch, calculate_return_value)); ThreadPlanStepOut *new_plan = static_cast(thread_plan_sp.get()); new_plan->ClearShouldStopHereCallbacks(); if (thread_plan_sp->ValidatePlan(nullptr)) { QueueThreadPlan(thread_plan_sp, abort_other_plans); return thread_plan_sp; } else { return ThreadPlanSP(); } } ThreadPlanSP Thread::QueueThreadPlanForStepThrough(StackID &return_stack_id, bool abort_other_plans, bool stop_other_threads) { ThreadPlanSP thread_plan_sp( new ThreadPlanStepThrough(*this, return_stack_id, stop_other_threads)); if (!thread_plan_sp || !thread_plan_sp->ValidatePlan(nullptr)) return ThreadPlanSP(); QueueThreadPlan(thread_plan_sp, abort_other_plans); return thread_plan_sp; } ThreadPlanSP Thread::QueueThreadPlanForRunToAddress(bool abort_other_plans, Address &target_addr, bool stop_other_threads) { ThreadPlanSP thread_plan_sp( new ThreadPlanRunToAddress(*this, target_addr, stop_other_threads)); QueueThreadPlan(thread_plan_sp, abort_other_plans); return thread_plan_sp; } ThreadPlanSP Thread::QueueThreadPlanForStepUntil(bool abort_other_plans, lldb::addr_t *address_list, size_t num_addresses, bool stop_other_threads, uint32_t frame_idx) { ThreadPlanSP thread_plan_sp(new ThreadPlanStepUntil( *this, address_list, num_addresses, stop_other_threads, frame_idx)); QueueThreadPlan(thread_plan_sp, abort_other_plans); return thread_plan_sp; } lldb::ThreadPlanSP Thread::QueueThreadPlanForStepScripted( bool abort_other_plans, const char *class_name, bool stop_other_threads) { ThreadPlanSP thread_plan_sp(new ThreadPlanPython(*this, class_name)); QueueThreadPlan(thread_plan_sp, abort_other_plans); // This seems a little funny, but I don't want to have to split up the // constructor and the // DidPush in the scripted plan, that seems annoying. // That means the constructor has to be in DidPush. // So I have to validate the plan AFTER pushing it, and then take it off // again... if (!thread_plan_sp->ValidatePlan(nullptr)) { DiscardThreadPlansUpToPlan(thread_plan_sp); return ThreadPlanSP(); } else return thread_plan_sp; } uint32_t Thread::GetIndexID() const { return m_index_id; } static void PrintPlanElement(Stream *s, const ThreadPlanSP &plan, lldb::DescriptionLevel desc_level, int32_t elem_idx) { s->IndentMore(); s->Indent(); s->Printf("Element %d: ", elem_idx); plan->GetDescription(s, desc_level); s->EOL(); s->IndentLess(); } static void PrintPlanStack(Stream *s, const std::vector &plan_stack, lldb::DescriptionLevel desc_level, bool include_internal) { int32_t print_idx = 0; for (ThreadPlanSP plan_sp : plan_stack) { if (include_internal || !plan_sp->GetPrivate()) { PrintPlanElement(s, plan_sp, desc_level, print_idx++); } } } void Thread::DumpThreadPlans(Stream *s, lldb::DescriptionLevel desc_level, bool include_internal, bool ignore_boring_threads) const { uint32_t stack_size; if (ignore_boring_threads) { uint32_t stack_size = m_plan_stack.size(); uint32_t completed_stack_size = m_completed_plan_stack.size(); uint32_t discarded_stack_size = m_discarded_plan_stack.size(); if (stack_size == 1 && completed_stack_size == 0 && discarded_stack_size == 0) { s->Printf("thread #%u: tid = 0x%4.4" PRIx64 "\n", GetIndexID(), GetID()); s->IndentMore(); s->Indent(); s->Printf("No active thread plans\n"); s->IndentLess(); return; } } s->Indent(); s->Printf("thread #%u: tid = 0x%4.4" PRIx64 ":\n", GetIndexID(), GetID()); s->IndentMore(); s->Indent(); s->Printf("Active plan stack:\n"); PrintPlanStack(s, m_plan_stack, desc_level, include_internal); stack_size = m_completed_plan_stack.size(); if (stack_size > 0) { s->Indent(); s->Printf("Completed Plan Stack:\n"); PrintPlanStack(s, m_completed_plan_stack, desc_level, include_internal); } stack_size = m_discarded_plan_stack.size(); if (stack_size > 0) { s->Indent(); s->Printf("Discarded Plan Stack:\n"); PrintPlanStack(s, m_discarded_plan_stack, desc_level, include_internal); } s->IndentLess(); } TargetSP Thread::CalculateTarget() { TargetSP target_sp; ProcessSP process_sp(GetProcess()); if (process_sp) target_sp = process_sp->CalculateTarget(); return target_sp; } ProcessSP Thread::CalculateProcess() { return GetProcess(); } ThreadSP Thread::CalculateThread() { return shared_from_this(); } StackFrameSP Thread::CalculateStackFrame() { return StackFrameSP(); } void Thread::CalculateExecutionContext(ExecutionContext &exe_ctx) { exe_ctx.SetContext(shared_from_this()); } StackFrameListSP Thread::GetStackFrameList() { StackFrameListSP frame_list_sp; std::lock_guard guard(m_frame_mutex); if (m_curr_frames_sp) { frame_list_sp = m_curr_frames_sp; } else { frame_list_sp.reset(new StackFrameList(*this, m_prev_frames_sp, true)); m_curr_frames_sp = frame_list_sp; } return frame_list_sp; } void Thread::ClearStackFrames() { std::lock_guard guard(m_frame_mutex); Unwind *unwinder = GetUnwinder(); if (unwinder) unwinder->Clear(); // Only store away the old "reference" StackFrameList if we got all its // frames: // FIXME: At some point we can try to splice in the frames we have fetched // into // the new frame as we make it, but let's not try that now. if (m_curr_frames_sp && m_curr_frames_sp->GetAllFramesFetched()) m_prev_frames_sp.swap(m_curr_frames_sp); m_curr_frames_sp.reset(); m_extended_info.reset(); m_extended_info_fetched = false; } lldb::StackFrameSP Thread::GetFrameWithConcreteFrameIndex(uint32_t unwind_idx) { return GetStackFrameList()->GetFrameWithConcreteFrameIndex(unwind_idx); } Status Thread::ReturnFromFrameWithIndex(uint32_t frame_idx, lldb::ValueObjectSP return_value_sp, bool broadcast) { StackFrameSP frame_sp = GetStackFrameAtIndex(frame_idx); Status return_error; if (!frame_sp) { return_error.SetErrorStringWithFormat( "Could not find frame with index %d in thread 0x%" PRIx64 ".", frame_idx, GetID()); } return ReturnFromFrame(frame_sp, return_value_sp, broadcast); } Status Thread::ReturnFromFrame(lldb::StackFrameSP frame_sp, lldb::ValueObjectSP return_value_sp, bool broadcast) { Status return_error; if (!frame_sp) { return_error.SetErrorString("Can't return to a null frame."); return return_error; } Thread *thread = frame_sp->GetThread().get(); uint32_t older_frame_idx = frame_sp->GetFrameIndex() + 1; StackFrameSP older_frame_sp = thread->GetStackFrameAtIndex(older_frame_idx); if (!older_frame_sp) { return_error.SetErrorString("No older frame to return to."); return return_error; } if (return_value_sp) { lldb::ABISP abi = thread->GetProcess()->GetABI(); if (!abi) { return_error.SetErrorString("Could not find ABI to set return value."); return return_error; } SymbolContext sc = frame_sp->GetSymbolContext(eSymbolContextFunction); // FIXME: ValueObject::Cast doesn't currently work correctly, at least not // for scalars. // Turn that back on when that works. if (/* DISABLES CODE */ (0) && sc.function != nullptr) { Type *function_type = sc.function->GetType(); if (function_type) { CompilerType return_type = sc.function->GetCompilerType().GetFunctionReturnType(); if (return_type) { StreamString s; return_type.DumpTypeDescription(&s); ValueObjectSP cast_value_sp = return_value_sp->Cast(return_type); if (cast_value_sp) { cast_value_sp->SetFormat(eFormatHex); return_value_sp = cast_value_sp; } } } } return_error = abi->SetReturnValueObject(older_frame_sp, return_value_sp); if (!return_error.Success()) return return_error; } // Now write the return registers for the chosen frame: // Note, we can't use ReadAllRegisterValues->WriteAllRegisterValues, since the // read & write // cook their data StackFrameSP youngest_frame_sp = thread->GetStackFrameAtIndex(0); if (youngest_frame_sp) { lldb::RegisterContextSP reg_ctx_sp(youngest_frame_sp->GetRegisterContext()); if (reg_ctx_sp) { bool copy_success = reg_ctx_sp->CopyFromRegisterContext( older_frame_sp->GetRegisterContext()); if (copy_success) { thread->DiscardThreadPlans(true); thread->ClearStackFrames(); if (broadcast && EventTypeHasListeners(eBroadcastBitStackChanged)) BroadcastEvent(eBroadcastBitStackChanged, new ThreadEventData(this->shared_from_this())); } else { return_error.SetErrorString("Could not reset register values."); } } else { return_error.SetErrorString("Frame has no register context."); } } else { return_error.SetErrorString("Returned past top frame."); } return return_error; } static void DumpAddressList(Stream &s, const std::vector
&list, ExecutionContextScope *exe_scope) { for (size_t n = 0; n < list.size(); n++) { s << "\t"; list[n].Dump(&s, exe_scope, Address::DumpStyleResolvedDescription, Address::DumpStyleSectionNameOffset); s << "\n"; } } Status Thread::JumpToLine(const FileSpec &file, uint32_t line, bool can_leave_function, std::string *warnings) { ExecutionContext exe_ctx(GetStackFrameAtIndex(0)); Target *target = exe_ctx.GetTargetPtr(); TargetSP target_sp = exe_ctx.GetTargetSP(); RegisterContext *reg_ctx = exe_ctx.GetRegisterContext(); StackFrame *frame = exe_ctx.GetFramePtr(); const SymbolContext &sc = frame->GetSymbolContext(eSymbolContextFunction); // Find candidate locations. std::vector
candidates, within_function, outside_function; target->GetImages().FindAddressesForLine(target_sp, file, line, sc.function, within_function, outside_function); // If possible, we try and stay within the current function. // Within a function, we accept multiple locations (optimized code may do // this, // there's no solution here so we do the best we can). // However if we're trying to leave the function, we don't know how to pick // the // right location, so if there's more than one then we bail. if (!within_function.empty()) candidates = within_function; else if (outside_function.size() == 1 && can_leave_function) candidates = outside_function; // Check if we got anything. if (candidates.empty()) { if (outside_function.empty()) { return Status("Cannot locate an address for %s:%i.", file.GetFilename().AsCString(), line); } else if (outside_function.size() == 1) { return Status("%s:%i is outside the current function.", file.GetFilename().AsCString(), line); } else { StreamString sstr; DumpAddressList(sstr, outside_function, target); return Status("%s:%i has multiple candidate locations:\n%s", file.GetFilename().AsCString(), line, sstr.GetData()); } } // Accept the first location, warn about any others. Address dest = candidates[0]; if (warnings && candidates.size() > 1) { StreamString sstr; sstr.Printf("%s:%i appears multiple times in this function, selecting the " "first location:\n", file.GetFilename().AsCString(), line); DumpAddressList(sstr, candidates, target); *warnings = sstr.GetString(); } if (!reg_ctx->SetPC(dest)) return Status("Cannot change PC to target address."); return Status(); } void Thread::DumpUsingSettingsFormat(Stream &strm, uint32_t frame_idx, bool stop_format) { ExecutionContext exe_ctx(shared_from_this()); Process *process = exe_ctx.GetProcessPtr(); if (process == nullptr) return; StackFrameSP frame_sp; SymbolContext frame_sc; if (frame_idx != LLDB_INVALID_FRAME_ID) { frame_sp = GetStackFrameAtIndex(frame_idx); if (frame_sp) { exe_ctx.SetFrameSP(frame_sp); frame_sc = frame_sp->GetSymbolContext(eSymbolContextEverything); } } const FormatEntity::Entry *thread_format; if (stop_format) thread_format = exe_ctx.GetTargetRef().GetDebugger().GetThreadStopFormat(); else thread_format = exe_ctx.GetTargetRef().GetDebugger().GetThreadFormat(); assert(thread_format); FormatEntity::Format(*thread_format, strm, frame_sp ? &frame_sc : nullptr, &exe_ctx, nullptr, nullptr, false, false); } void Thread::SettingsInitialize() {} void Thread::SettingsTerminate() {} lldb::addr_t Thread::GetThreadPointer() { return LLDB_INVALID_ADDRESS; } addr_t Thread::GetThreadLocalData(const ModuleSP module, lldb::addr_t tls_file_addr) { // The default implementation is to ask the dynamic loader for it. // This can be overridden for specific platforms. DynamicLoader *loader = GetProcess()->GetDynamicLoader(); if (loader) return loader->GetThreadLocalData(module, shared_from_this(), tls_file_addr); else return LLDB_INVALID_ADDRESS; } bool Thread::SafeToCallFunctions() { Process *process = GetProcess().get(); if (process) { SystemRuntime *runtime = process->GetSystemRuntime(); if (runtime) { return runtime->SafeToCallFunctionsOnThisThread(shared_from_this()); } } return true; } lldb::StackFrameSP Thread::GetStackFrameSPForStackFramePtr(StackFrame *stack_frame_ptr) { return GetStackFrameList()->GetStackFrameSPForStackFramePtr(stack_frame_ptr); } const char *Thread::StopReasonAsCString(lldb::StopReason reason) { switch (reason) { case eStopReasonInvalid: return "invalid"; case eStopReasonNone: return "none"; case eStopReasonTrace: return "trace"; case eStopReasonBreakpoint: return "breakpoint"; case eStopReasonWatchpoint: return "watchpoint"; case eStopReasonSignal: return "signal"; case eStopReasonException: return "exception"; case eStopReasonExec: return "exec"; case eStopReasonPlanComplete: return "plan complete"; case eStopReasonThreadExiting: return "thread exiting"; case eStopReasonInstrumentation: return "instrumentation break"; } static char unknown_state_string[64]; snprintf(unknown_state_string, sizeof(unknown_state_string), "StopReason = %i", reason); return unknown_state_string; } const char *Thread::RunModeAsCString(lldb::RunMode mode) { switch (mode) { case eOnlyThisThread: return "only this thread"; case eAllThreads: return "all threads"; case eOnlyDuringStepping: return "only during stepping"; } static char unknown_state_string[64]; snprintf(unknown_state_string, sizeof(unknown_state_string), "RunMode = %i", mode); return unknown_state_string; } size_t Thread::GetStatus(Stream &strm, uint32_t start_frame, uint32_t num_frames, uint32_t num_frames_with_source, - bool stop_format) { - ExecutionContext exe_ctx(shared_from_this()); - Target *target = exe_ctx.GetTargetPtr(); - Process *process = exe_ctx.GetProcessPtr(); - size_t num_frames_shown = 0; - strm.Indent(); - bool is_selected = false; - if (process) { - if (process->GetThreadList().GetSelectedThread().get() == this) - is_selected = true; - } - strm.Printf("%c ", is_selected ? '*' : ' '); - if (target && target->GetDebugger().GetUseExternalEditor()) { - StackFrameSP frame_sp = GetStackFrameAtIndex(start_frame); - if (frame_sp) { - SymbolContext frame_sc( - frame_sp->GetSymbolContext(eSymbolContextLineEntry)); - if (frame_sc.line_entry.line != 0 && frame_sc.line_entry.file) { - Host::OpenFileInExternalEditor(frame_sc.line_entry.file, - frame_sc.line_entry.line); + bool stop_format, bool only_stacks) { + + if (!only_stacks) { + ExecutionContext exe_ctx(shared_from_this()); + Target *target = exe_ctx.GetTargetPtr(); + Process *process = exe_ctx.GetProcessPtr(); + strm.Indent(); + bool is_selected = false; + if (process) { + if (process->GetThreadList().GetSelectedThread().get() == this) + is_selected = true; + } + strm.Printf("%c ", is_selected ? '*' : ' '); + if (target && target->GetDebugger().GetUseExternalEditor()) { + StackFrameSP frame_sp = GetStackFrameAtIndex(start_frame); + if (frame_sp) { + SymbolContext frame_sc( + frame_sp->GetSymbolContext(eSymbolContextLineEntry)); + if (frame_sc.line_entry.line != 0 && frame_sc.line_entry.file) { + Host::OpenFileInExternalEditor(frame_sc.line_entry.file, + frame_sc.line_entry.line); + } } } + + DumpUsingSettingsFormat(strm, start_frame, stop_format); } - DumpUsingSettingsFormat(strm, start_frame, stop_format); - + size_t num_frames_shown = 0; if (num_frames > 0) { strm.IndentMore(); const bool show_frame_info = true; - + const bool show_frame_unique = only_stacks; const char *selected_frame_marker = nullptr; - if (num_frames == 1 || + if (num_frames == 1 || only_stacks || (GetID() != GetProcess()->GetThreadList().GetSelectedThread()->GetID())) strm.IndentMore(); else selected_frame_marker = "* "; num_frames_shown = GetStackFrameList()->GetStatus( strm, start_frame, num_frames, show_frame_info, num_frames_with_source, - selected_frame_marker); + show_frame_unique, selected_frame_marker); if (num_frames == 1) strm.IndentLess(); strm.IndentLess(); } return num_frames_shown; } bool Thread::GetDescription(Stream &strm, lldb::DescriptionLevel level, bool print_json_thread, bool print_json_stopinfo) { const bool stop_format = false; DumpUsingSettingsFormat(strm, 0, stop_format); strm.Printf("\n"); StructuredData::ObjectSP thread_info = GetExtendedInfo(); if (print_json_thread || print_json_stopinfo) { if (thread_info && print_json_thread) { thread_info->Dump(strm); strm.Printf("\n"); } if (print_json_stopinfo && m_stop_info_sp) { StructuredData::ObjectSP stop_info = m_stop_info_sp->GetExtendedInfo(); if (stop_info) { stop_info->Dump(strm); strm.Printf("\n"); } } return true; } if (thread_info) { StructuredData::ObjectSP activity = thread_info->GetObjectForDotSeparatedPath("activity"); StructuredData::ObjectSP breadcrumb = thread_info->GetObjectForDotSeparatedPath("breadcrumb"); StructuredData::ObjectSP messages = thread_info->GetObjectForDotSeparatedPath("trace_messages"); bool printed_activity = false; if (activity && activity->GetType() == eStructuredDataTypeDictionary) { StructuredData::Dictionary *activity_dict = activity->GetAsDictionary(); StructuredData::ObjectSP id = activity_dict->GetValueForKey("id"); StructuredData::ObjectSP name = activity_dict->GetValueForKey("name"); if (name && name->GetType() == eStructuredDataTypeString && id && id->GetType() == eStructuredDataTypeInteger) { strm.Format(" Activity '{0}', {1:x}\n", name->GetAsString()->GetValue(), id->GetAsInteger()->GetValue()); } printed_activity = true; } bool printed_breadcrumb = false; if (breadcrumb && breadcrumb->GetType() == eStructuredDataTypeDictionary) { if (printed_activity) strm.Printf("\n"); StructuredData::Dictionary *breadcrumb_dict = breadcrumb->GetAsDictionary(); StructuredData::ObjectSP breadcrumb_text = breadcrumb_dict->GetValueForKey("name"); if (breadcrumb_text && breadcrumb_text->GetType() == eStructuredDataTypeString) { strm.Format(" Current Breadcrumb: {0}\n", breadcrumb_text->GetAsString()->GetValue()); } printed_breadcrumb = true; } if (messages && messages->GetType() == eStructuredDataTypeArray) { if (printed_breadcrumb) strm.Printf("\n"); StructuredData::Array *messages_array = messages->GetAsArray(); const size_t msg_count = messages_array->GetSize(); if (msg_count > 0) { strm.Printf(" %zu trace messages:\n", msg_count); for (size_t i = 0; i < msg_count; i++) { StructuredData::ObjectSP message = messages_array->GetItemAtIndex(i); if (message && message->GetType() == eStructuredDataTypeDictionary) { StructuredData::Dictionary *message_dict = message->GetAsDictionary(); StructuredData::ObjectSP message_text = message_dict->GetValueForKey("message"); if (message_text && message_text->GetType() == eStructuredDataTypeString) { strm.Format(" {0}\n", message_text->GetAsString()->GetValue()); } } } } } } return true; } size_t Thread::GetStackFrameStatus(Stream &strm, uint32_t first_frame, uint32_t num_frames, bool show_frame_info, uint32_t num_frames_with_source) { return GetStackFrameList()->GetStatus( strm, first_frame, num_frames, show_frame_info, num_frames_with_source); } Unwind *Thread::GetUnwinder() { if (!m_unwinder_ap) { const ArchSpec target_arch(CalculateTarget()->GetArchitecture()); const llvm::Triple::ArchType machine = target_arch.GetMachine(); switch (machine) { case llvm::Triple::x86_64: case llvm::Triple::x86: case llvm::Triple::arm: case llvm::Triple::aarch64: case llvm::Triple::thumb: case llvm::Triple::mips: case llvm::Triple::mipsel: case llvm::Triple::mips64: case llvm::Triple::mips64el: case llvm::Triple::ppc: case llvm::Triple::ppc64: case llvm::Triple::systemz: case llvm::Triple::hexagon: m_unwinder_ap.reset(new UnwindLLDB(*this)); break; default: if (target_arch.GetTriple().getVendor() == llvm::Triple::Apple) m_unwinder_ap.reset(new UnwindMacOSXFrameBackchain(*this)); break; } } return m_unwinder_ap.get(); } void Thread::Flush() { ClearStackFrames(); m_reg_context_sp.reset(); } bool Thread::IsStillAtLastBreakpointHit() { // If we are currently stopped at a breakpoint, always return that stopinfo // and don't reset it. // This allows threads to maintain their breakpoint stopinfo, such as when // thread-stepping in // multithreaded programs. if (m_stop_info_sp) { StopReason stop_reason = m_stop_info_sp->GetStopReason(); if (stop_reason == lldb::eStopReasonBreakpoint) { uint64_t value = m_stop_info_sp->GetValue(); lldb::RegisterContextSP reg_ctx_sp(GetRegisterContext()); if (reg_ctx_sp) { lldb::addr_t pc = reg_ctx_sp->GetPC(); BreakpointSiteSP bp_site_sp = GetProcess()->GetBreakpointSiteList().FindByAddress(pc); if (bp_site_sp && static_cast(value) == bp_site_sp->GetID()) return true; } } } return false; } Status Thread::StepIn(bool source_step, LazyBool step_in_avoids_code_without_debug_info, LazyBool step_out_avoids_code_without_debug_info) { Status error; Process *process = GetProcess().get(); if (StateIsStoppedState(process->GetState(), true)) { StackFrameSP frame_sp = GetStackFrameAtIndex(0); ThreadPlanSP new_plan_sp; const lldb::RunMode run_mode = eOnlyThisThread; const bool abort_other_plans = false; if (source_step && frame_sp && frame_sp->HasDebugInformation()) { SymbolContext sc(frame_sp->GetSymbolContext(eSymbolContextEverything)); new_plan_sp = QueueThreadPlanForStepInRange( abort_other_plans, sc.line_entry, sc, nullptr, run_mode, step_in_avoids_code_without_debug_info, step_out_avoids_code_without_debug_info); } else { new_plan_sp = QueueThreadPlanForStepSingleInstruction( false, abort_other_plans, run_mode); } new_plan_sp->SetIsMasterPlan(true); new_plan_sp->SetOkayToDiscard(false); // Why do we need to set the current thread by ID here??? process->GetThreadList().SetSelectedThreadByID(GetID()); error = process->Resume(); } else { error.SetErrorString("process not stopped"); } return error; } Status Thread::StepOver(bool source_step, LazyBool step_out_avoids_code_without_debug_info) { Status error; Process *process = GetProcess().get(); if (StateIsStoppedState(process->GetState(), true)) { StackFrameSP frame_sp = GetStackFrameAtIndex(0); ThreadPlanSP new_plan_sp; const lldb::RunMode run_mode = eOnlyThisThread; const bool abort_other_plans = false; if (source_step && frame_sp && frame_sp->HasDebugInformation()) { SymbolContext sc(frame_sp->GetSymbolContext(eSymbolContextEverything)); new_plan_sp = QueueThreadPlanForStepOverRange( abort_other_plans, sc.line_entry, sc, run_mode, step_out_avoids_code_without_debug_info); } else { new_plan_sp = QueueThreadPlanForStepSingleInstruction( true, abort_other_plans, run_mode); } new_plan_sp->SetIsMasterPlan(true); new_plan_sp->SetOkayToDiscard(false); // Why do we need to set the current thread by ID here??? process->GetThreadList().SetSelectedThreadByID(GetID()); error = process->Resume(); } else { error.SetErrorString("process not stopped"); } return error; } Status Thread::StepOut() { Status error; Process *process = GetProcess().get(); if (StateIsStoppedState(process->GetState(), true)) { const bool first_instruction = false; const bool stop_other_threads = false; const bool abort_other_plans = false; ThreadPlanSP new_plan_sp(QueueThreadPlanForStepOut( abort_other_plans, nullptr, first_instruction, stop_other_threads, eVoteYes, eVoteNoOpinion, 0)); new_plan_sp->SetIsMasterPlan(true); new_plan_sp->SetOkayToDiscard(false); // Why do we need to set the current thread by ID here??? process->GetThreadList().SetSelectedThreadByID(GetID()); error = process->Resume(); } else { error.SetErrorString("process not stopped"); } return error; } Index: vendor/lldb/dist/source/Utility/Status.cpp =================================================================== --- vendor/lldb/dist/source/Utility/Status.cpp (revision 320022) +++ vendor/lldb/dist/source/Utility/Status.cpp (revision 320023) @@ -1,304 +1,309 @@ //===-- Status.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/Utility/Status.h" #include "lldb/Utility/VASPrintf.h" #include "lldb/lldb-defines.h" // for LLDB_GENERIC_ERROR #include "lldb/lldb-enumerations.h" // for ErrorType, ErrorType::eErr... #include "llvm/ADT/SmallString.h" // for SmallString #include "llvm/ADT/StringRef.h" // for StringRef #include "llvm/Support/Errno.h" #include "llvm/Support/FormatProviders.h" // for format_provider #include #include #include // for string #include #ifdef __APPLE__ #include #endif #include // for uint32_t namespace llvm { class raw_ostream; } using namespace lldb; using namespace lldb_private; Status::Status() : m_code(0), m_type(eErrorTypeInvalid), m_string() {} Status::Status(ValueType err, ErrorType type) : m_code(err), m_type(type), m_string() {} Status::Status(std::error_code EC) : m_code(EC.value()), m_type(ErrorType::eErrorTypeGeneric), m_string(EC.message()) {} Status::Status(const Status &rhs) = default; Status::Status(const char *format, ...) : m_code(0), m_type(eErrorTypeInvalid), m_string() { va_list args; va_start(args, format); SetErrorToGenericError(); SetErrorStringWithVarArg(format, args); va_end(args); } -Status::Status(llvm::Error error) - : m_code(0), m_type(ErrorType::eErrorTypeGeneric) { - if (!error) - return; +const Status &Status::operator=(llvm::Error error) { + if (!error) { + Clear(); + return *this; + } // if the error happens to be a errno error, preserve the error code error = llvm::handleErrors( std::move(error), [&](std::unique_ptr e) -> llvm::Error { std::error_code ec = e->convertToErrorCode(); if (ec.category() == std::generic_category()) { m_code = ec.value(); m_type = ErrorType::eErrorTypePOSIX; return llvm::Error::success(); } return llvm::Error(std::move(e)); }); // Otherwise, just preserve the message - if (error) + if (error) { + SetErrorToGenericError(); SetErrorString(llvm::toString(std::move(error))); + } + + return *this; } llvm::Error Status::ToError() const { if (Success()) return llvm::Error::success(); if (m_type == ErrorType::eErrorTypePOSIX) return llvm::errorCodeToError(std::error_code(m_code, std::generic_category())); return llvm::make_error(AsCString(), llvm::inconvertibleErrorCode()); } //---------------------------------------------------------------------- // Assignment operator //---------------------------------------------------------------------- const Status &Status::operator=(const Status &rhs) { if (this != &rhs) { m_code = rhs.m_code; m_type = rhs.m_type; m_string = rhs.m_string; } return *this; } //---------------------------------------------------------------------- // Assignment operator //---------------------------------------------------------------------- const Status &Status::operator=(uint32_t err) { m_code = err; m_type = eErrorTypeMachKernel; m_string.clear(); return *this; } Status::~Status() = default; //---------------------------------------------------------------------- // Get the error value as a NULL C string. The error string will be // fetched and cached on demand. The cached error string value will // remain until the error value is changed or cleared. //---------------------------------------------------------------------- const char *Status::AsCString(const char *default_error_str) const { if (Success()) return nullptr; if (m_string.empty()) { switch (m_type) { case eErrorTypeMachKernel: #if defined(__APPLE__) if (const char *s = ::mach_error_string(m_code)) m_string.assign(s); #endif break; case eErrorTypePOSIX: m_string = llvm::sys::StrError(m_code); break; default: break; } } if (m_string.empty()) { if (default_error_str) m_string.assign(default_error_str); else return nullptr; // User wanted a nullptr string back... } return m_string.c_str(); } //---------------------------------------------------------------------- // Clear the error and any cached error string that it might contain. //---------------------------------------------------------------------- void Status::Clear() { m_code = 0; m_type = eErrorTypeInvalid; m_string.clear(); } //---------------------------------------------------------------------- // Access the error value. //---------------------------------------------------------------------- Status::ValueType Status::GetError() const { return m_code; } //---------------------------------------------------------------------- // Access the error type. //---------------------------------------------------------------------- ErrorType Status::GetType() const { return m_type; } //---------------------------------------------------------------------- // Returns true if this object contains a value that describes an // error or otherwise non-success result. //---------------------------------------------------------------------- bool Status::Fail() const { return m_code != 0; } //---------------------------------------------------------------------- // Set accesssor for the error value to "err" and the type to // "eErrorTypeMachKernel" //---------------------------------------------------------------------- void Status::SetMachError(uint32_t err) { m_code = err; m_type = eErrorTypeMachKernel; m_string.clear(); } void Status::SetExpressionError(lldb::ExpressionResults result, const char *mssg) { m_code = result; m_type = eErrorTypeExpression; m_string = mssg; } int Status::SetExpressionErrorWithFormat(lldb::ExpressionResults result, const char *format, ...) { int length = 0; if (format != nullptr && format[0]) { va_list args; va_start(args, format); length = SetErrorStringWithVarArg(format, args); va_end(args); } else { m_string.clear(); } m_code = result; m_type = eErrorTypeExpression; return length; } //---------------------------------------------------------------------- // Set accesssor for the error value and type. //---------------------------------------------------------------------- void Status::SetError(ValueType err, ErrorType type) { m_code = err; m_type = type; m_string.clear(); } //---------------------------------------------------------------------- // Update the error value to be "errno" and update the type to // be "POSIX". //---------------------------------------------------------------------- void Status::SetErrorToErrno() { m_code = errno; m_type = eErrorTypePOSIX; m_string.clear(); } //---------------------------------------------------------------------- // Update the error value to be LLDB_GENERIC_ERROR and update the type // to be "Generic". //---------------------------------------------------------------------- void Status::SetErrorToGenericError() { m_code = LLDB_GENERIC_ERROR; m_type = eErrorTypeGeneric; m_string.clear(); } //---------------------------------------------------------------------- // Set accessor for the error string value for a specific error. // This allows any string to be supplied as an error explanation. // The error string value will remain until the error value is // cleared or a new error value/type is assigned. //---------------------------------------------------------------------- void Status::SetErrorString(llvm::StringRef err_str) { if (!err_str.empty()) { // If we have an error string, we should always at least have an error // set to a generic value. if (Success()) SetErrorToGenericError(); } m_string = err_str; } //------------------------------------------------------------------ /// Set the current error string to a formatted error string. /// /// @param format /// A printf style format string //------------------------------------------------------------------ int Status::SetErrorStringWithFormat(const char *format, ...) { if (format != nullptr && format[0]) { va_list args; va_start(args, format); int length = SetErrorStringWithVarArg(format, args); va_end(args); return length; } else { m_string.clear(); } return 0; } int Status::SetErrorStringWithVarArg(const char *format, va_list args) { if (format != nullptr && format[0]) { // If we have an error string, we should always at least have // an error set to a generic value. if (Success()) SetErrorToGenericError(); llvm::SmallString<1024> buf; VASprintf(buf, format, args); m_string = buf.str(); return buf.size(); } else { m_string.clear(); } return 0; } //---------------------------------------------------------------------- // Returns true if the error code in this object is considered a // successful return value. //---------------------------------------------------------------------- bool Status::Success() const { return m_code == 0; } bool Status::WasInterrupted() const { return (m_type == eErrorTypePOSIX && m_code == EINTR); } void llvm::format_provider::format( const lldb_private::Status &error, llvm::raw_ostream &OS, llvm::StringRef Options) { llvm::format_provider::format(error.AsCString(), OS, Options); } Index: vendor/lldb/dist/unittests/Utility/StatusTest.cpp =================================================================== --- vendor/lldb/dist/unittests/Utility/StatusTest.cpp (revision 320022) +++ vendor/lldb/dist/unittests/Utility/StatusTest.cpp (revision 320023) @@ -1,50 +1,53 @@ //===-- StatusTest.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/Utility/Status.h" #include "gtest/gtest.h" using namespace lldb_private; using namespace lldb; TEST(StatusTest, Formatv) { EXPECT_EQ("", llvm::formatv("{0}", Status()).str()); EXPECT_EQ("Hello Status", llvm::formatv("{0}", Status("Hello Status")).str()); EXPECT_EQ("Hello", llvm::formatv("{0:5}", Status("Hello Error")).str()); } TEST(StatusTest, ErrorConstructor) { EXPECT_TRUE(Status(llvm::Error::success()).Success()); Status eagain( llvm::errorCodeToError(std::error_code(EAGAIN, std::generic_category()))); EXPECT_TRUE(eagain.Fail()); EXPECT_EQ(eErrorTypePOSIX, eagain.GetType()); EXPECT_EQ(Status::ValueType(EAGAIN), eagain.GetError()); Status foo(llvm::make_error( "foo", llvm::inconvertibleErrorCode())); EXPECT_TRUE(foo.Fail()); EXPECT_EQ(eErrorTypeGeneric, foo.GetType()); EXPECT_STREQ("foo", foo.AsCString()); + + foo = llvm::Error::success(); + EXPECT_TRUE(foo.Success()); } TEST(StatusTest, ErrorConversion) { EXPECT_FALSE(bool(Status().ToError())); llvm::Error eagain = Status(EAGAIN, ErrorType::eErrorTypePOSIX).ToError(); EXPECT_TRUE(bool(eagain)); std::error_code ec = llvm::errorToErrorCode(std::move(eagain)); EXPECT_EQ(EAGAIN, ec.value()); EXPECT_EQ(std::generic_category(), ec.category()); llvm::Error foo = Status("foo").ToError(); EXPECT_TRUE(bool(foo)); EXPECT_EQ("foo", llvm::toString(std::move(foo))); }