From ef7d641617c523c2690f4bf97c5c6139ec5b5b75 Mon Sep 17 00:00:00 2001 From: Jason Molenda Date: Thu, 6 Aug 2015 03:27:10 +0000 Subject: [PATCH] Second part of indicating when the user is stopped in optimized code. The first part was in r243508 -- the extent of the UI changes in that patchset was to add "[opt]" to the frame-format when a stack frame was built with optimized code. In this change, when a stack frame built with optimization is selected, a message will be printed to the async output channel -- opt1.c was compiled with optimization - stepping may behave oddly; variables may not be available. The warning will be only be printed once per source file in a debug session. These warnings may be disabled by settings set target.process.optimization-warnings false Internally, a new Process::PrintWarning() method has been added for warnings that we want to print only once to the user. It takes a type of warning (currently only eWarningsOptimization) and an object pointer (CompileUnit*) - the warning will only be printed once for a given object pointer value. This is a bit of a prototype of this change - I think we will be tweaking it more in the future. But I wanted to land this and see how it goes. Advanced users will find these warnings unnecessary noise and will quickly disable them - but anyone who maintains a debugger knows that debugging optimized code, without realizing it, is a constant source of confusion and frustation for more typical debugger users. I imagine there will be more of these "warn once per whatever" style warnings that we will want to add in the future and we'll need to come up with a better way for enabling/disabling them. But I'm not srue what form that warning settings should take and I didn't want to code up something that we regret later, so for now I just added another process setting for this one warning. llvm-svn: 244190 --- lldb/include/lldb/Target/Process.h | 42 ++++++++++++++++++++++++++++ lldb/include/lldb/Target/Thread.h | 9 +++--- lldb/source/Target/Process.cpp | 56 +++++++++++++++++++++++++++++++++++++- lldb/source/Target/Thread.cpp | 28 +++++++++++++++++++ 4 files changed, 129 insertions(+), 6 deletions(-) diff --git a/lldb/include/lldb/Target/Process.h b/lldb/include/lldb/Target/Process.h index b9c93bb..fb981f1 100644 --- a/lldb/include/lldb/Target/Process.h +++ b/lldb/include/lldb/Target/Process.h @@ -19,6 +19,7 @@ #include #include #include +#include // Other libraries and framework includes // Project includes @@ -103,6 +104,9 @@ public: void SetDetachKeepsStopped (bool keep_stopped); + bool + GetWarningsOptimization () const; + protected: static void @@ -766,6 +770,14 @@ public: eBroadcastInternalStateControlPause = (1<<1), eBroadcastInternalStateControlResume = (1<<2) }; + + //------------------------------------------------------------------ + /// Process warning types. + //------------------------------------------------------------------ + enum Warnings + { + eWarningsOptimization = 1 + }; typedef Range LoadRange; // We use a read/write lock to allow on or more clients to @@ -1939,6 +1951,33 @@ public: return StructuredData::ObjectSP(); } + //------------------------------------------------------------------ + /// Print a user-visible warning one time per Process + /// + /// A facility for printing a warning to the user once per repeat_key. + /// + /// warning_type is from the Process::Warnings enums. + /// repeat_key is a pointer value that will be used to ensure that the + /// warning message is not printed multiple times. For instance, with a + /// warning about a function being optimized, you can pass the CompileUnit + /// pointer to have the warning issued for only the first function in a + /// CU, or the Function pointer to have it issued once for every function, + /// or a Module pointer to have it issued once per Module. + /// + /// @param [in] warning_type + /// One of the types defined in Process::Warnings. + /// + /// @param [in] repeat_key + /// A pointer value used to ensure that the warning is only printed once. + /// May be nullptr, indicating that the warning is printed unconditionally + /// every time. + /// + /// @param [in] fmt + /// printf style format string + //------------------------------------------------------------------ + void + PrintWarning (uint64_t warning_type, void *repeat_key, const char *fmt, ...) __attribute__((format(printf, 4, 5))); + protected: void @@ -3243,6 +3282,8 @@ protected: // Type definitions //------------------------------------------------------------------ typedef std::map LanguageRuntimeCollection; + typedef std::unordered_set WarningsPointerSet; + typedef std::map WarningsCollection; struct PreResumeCallbackAndBaton { @@ -3322,6 +3363,7 @@ protected: std::map m_resolved_indirect_addresses; bool m_destroy_in_process; bool m_can_interpret_function_calls; // Some targets, e.g the OSX kernel, don't support the ability to modify the stack. + WarningsCollection m_warnings_issued; // A set of object pointers which have already had warnings printed enum { eCanJITDontKnow= 0, diff --git a/lldb/include/lldb/Target/Thread.h b/lldb/include/lldb/Target/Thread.h index fa2a851..1b20b83 100644 --- a/lldb/include/lldb/Target/Thread.h +++ b/lldb/include/lldb/Target/Thread.h @@ -495,11 +495,7 @@ public: } lldb::StackFrameSP - GetSelectedFrame () - { - lldb::StackFrameListSP stack_frame_list_sp(GetStackFrameList()); - return stack_frame_list_sp->GetFrameAtIndex (stack_frame_list_sp->GetSelectedFrameIndex()); - } + GetSelectedFrame (); uint32_t SetSelectedFrame (lldb_private::StackFrame *frame, bool broadcast = false); @@ -1328,6 +1324,9 @@ protected: GetStackFrameList (); + void + FunctionOptimizationWarning (lldb_private::StackFrame *frame); + //------------------------------------------------------------------ // Classes that inherit from Process can see and modify these //------------------------------------------------------------------ diff --git a/lldb/source/Target/Process.cpp b/lldb/source/Target/Process.cpp index 9594a8a..adc393f 100644 --- a/lldb/source/Target/Process.cpp +++ b/lldb/source/Target/Process.cpp @@ -115,6 +115,7 @@ g_properties[] = { "stop-on-sharedlibrary-events" , OptionValue::eTypeBoolean, true, false, NULL, NULL, "If true, stop when a shared library is loaded or unloaded." }, { "detach-keeps-stopped" , OptionValue::eTypeBoolean, true, false, NULL, NULL, "If true, detach will attempt to keep the process stopped." }, { "memory-cache-line-size" , OptionValue::eTypeUInt64, false, 512, NULL, NULL, "The memory cache line size" }, + { "optimization-warnings" , OptionValue::eTypeBoolean, false, true, NULL, NULL, "If true, warn when stopped in code that is optimized where stepping and variable availability may not behave as expected." }, { NULL , OptionValue::eTypeInvalid, false, 0, NULL, NULL, NULL } }; @@ -126,7 +127,8 @@ enum { ePropertyPythonOSPluginPath, ePropertyStopOnSharedLibraryEvents, ePropertyDetachKeepsStopped, - ePropertyMemCacheLineSize + ePropertyMemCacheLineSize, + ePropertyWarningOptimization }; ProcessProperties::ProcessProperties (lldb_private::Process *process) : @@ -263,6 +265,13 @@ ProcessProperties::SetDetachKeepsStopped (bool stop) m_collection_sp->SetPropertyAtIndexAsBoolean(NULL, idx, stop); } +bool +ProcessProperties::GetWarningsOptimization () const +{ + const uint32_t idx = ePropertyWarningOptimization; + return m_collection_sp->GetPropertyAtIndexAsBoolean (NULL, idx, g_properties[idx].default_uint_value != 0); +} + void ProcessInstanceInfo::Dump (Stream &s, Platform *platform) const { @@ -755,6 +764,7 @@ Process::Process(Target &target, Listener &listener, const UnixSignalsSP &unix_s m_last_broadcast_state (eStateInvalid), m_destroy_in_process (false), m_can_interpret_function_calls(false), + m_warnings_issued (), m_can_jit(eCanJITDontKnow) { CheckInWithManager (); @@ -6542,6 +6552,50 @@ Process::ModulesDidLoad (ModuleList &module_list) } } +void +Process::PrintWarning (uint64_t warning_type, void *repeat_key, const char *fmt, ...) +{ + bool print_warning = true; + + StreamSP stream_sp = GetTarget().GetDebugger().GetAsyncOutputStream(); + if (stream_sp.get() == nullptr) + return; + if (warning_type == eWarningsOptimization + && GetWarningsOptimization() == false) + { + return; + } + + if (repeat_key != nullptr) + { + WarningsCollection::iterator it = m_warnings_issued.find (warning_type); + if (it == m_warnings_issued.end()) + { + m_warnings_issued[warning_type] = WarningsPointerSet(); + m_warnings_issued[warning_type].insert (repeat_key); + } + else + { + if (it->second.find (repeat_key) != it->second.end()) + { + print_warning = false; + } + else + { + it->second.insert (repeat_key); + } + } + } + + if (print_warning) + { + va_list args; + va_start (args, fmt); + stream_sp->PrintfVarArg (fmt, args); + va_end (args); + } +} + ThreadCollectionSP Process::GetHistoryThreads(lldb::addr_t addr) { diff --git a/lldb/source/Target/Thread.cpp b/lldb/source/Target/Thread.cpp index b939aeb..0327fe7 100644 --- a/lldb/source/Target/Thread.cpp +++ b/lldb/source/Target/Thread.cpp @@ -360,12 +360,22 @@ Thread::BroadcastSelectedFrameChange(StackID &new_frame_id) 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; } @@ -378,6 +388,7 @@ Thread::SetSelectedFrameByIndex (uint32_t frame_idx, bool broadcast) GetStackFrameList()->SetSelectedFrame(frame_sp.get()); if (broadcast) BroadcastSelectedFrameChange(frame_sp->GetStackID()); + FunctionOptimizationWarning (frame_sp.get()); return true; } else @@ -403,6 +414,7 @@ Thread::SetSelectedFrameByIndexNoisily (uint32_t frame_idx, Stream &output_strea 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; @@ -411,6 +423,22 @@ Thread::SetSelectedFrameByIndexNoisily (uint32_t frame_idx, Stream &output_strea return false; } +void +Thread::FunctionOptimizationWarning (StackFrame *frame) +{ + if (frame && frame->HasDebugInformation()) + { + SymbolContext sc = frame->GetSymbolContext (eSymbolContextFunction | eSymbolContextCompUnit); + if (sc.function && sc.function->GetIsOptimized() == true && sc.comp_unit) + { + if (sc.line_entry.file.GetFilename().IsEmpty() == false) + { + GetProcess()->PrintWarning (Process::Warnings::eWarningsOptimization, sc.comp_unit, "%s was compiled with optimization - stepping may behave oddly; variables may not be available.\n", sc.line_entry.file.GetFilename().GetCString()); + } + } + } +} + lldb::StopInfoSP Thread::GetStopInfo () -- 2.7.4