| //===-- StackFrameRecognizer.cpp ------------------------------------------===// | |
| // | |
| // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. | |
| // See https://llvm.org/LICENSE.txt for license information. | |
| // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception | |
| // | |
| //===----------------------------------------------------------------------===// | |
| #include "lldb/Target/StackFrameRecognizer.h" | |
| #include "lldb/Core/Architecture.h" | |
| #include "lldb/Core/Debugger.h" | |
| #include "lldb/Core/Module.h" | |
| #include "lldb/Interpreter/Interfaces/ScriptedStackFrameRecognizerInterface.h" | |
| #include "lldb/Interpreter/ScriptInterpreter.h" | |
| #include "lldb/Symbol/Function.h" | |
| #include "lldb/Symbol/Symbol.h" | |
| #include "lldb/Target/StackFrame.h" | |
| #include "lldb/Target/Target.h" | |
| #include "lldb/Utility/RegularExpression.h" | |
| #include "lldb/Utility/ScriptedMetadata.h" | |
| #include "llvm/Support/FormatVariadic.h" | |
| using namespace lldb; | |
| using namespace lldb_private; | |
| class ScriptedRecognizedStackFrame : public RecognizedStackFrame { | |
| public: | |
| ScriptedRecognizedStackFrame(ValueObjectListSP args, bool hidden, | |
| lldb::StackFrameSP most_relevant_frame, | |
| lldb::ValueObjectSP exception, | |
| std::string stop_desc, | |
| lldb::ThreadPlanSP step_through_plan_sp) | |
| : m_hidden(hidden), m_most_relevant_frame(std::move(most_relevant_frame)), | |
| m_exception(std::move(exception)), | |
| m_thread_plan_sp(std::move(step_through_plan_sp)) { | |
| m_arguments = std::move(args); | |
| m_stop_desc = std::move(stop_desc); | |
| } | |
| bool ShouldHide() override { return m_hidden; } | |
| lldb::StackFrameSP GetMostRelevantFrame() override { | |
| return m_most_relevant_frame; | |
| } | |
| lldb::ValueObjectSP GetExceptionObject() override { return m_exception; } | |
| lldb::ThreadPlanSP GetStepThroughPlan() override { return m_thread_plan_sp; } | |
| protected: | |
| bool m_hidden; | |
| lldb::StackFrameSP m_most_relevant_frame; | |
| lldb::ValueObjectSP m_exception; | |
| lldb::ThreadPlanSP m_thread_plan_sp; | |
| }; | |
| ScriptedStackFrameRecognizer::ScriptedStackFrameRecognizer( | |
| ScriptInterpreter *interpreter, const char *pclass) | |
| : m_python_class(pclass) { | |
| if (!interpreter) | |
| return; | |
| m_interface_sp = interpreter->CreateScriptedStackFrameRecognizerInterface(); | |
| if (!m_interface_sp) | |
| return; | |
| ScriptedMetadata scripted_metadata(m_python_class, nullptr); | |
| auto obj_or_err = m_interface_sp->CreatePluginObject(scripted_metadata); | |
| if (!obj_or_err) { | |
| // CreatePluginObject has no error-return channel back to the command | |
| // handler that requested this recognizer, so report the detailed error | |
| // (which may include a Python backtrace) via the diagnostic system. | |
| Debugger::ReportError( | |
| llvm::formatv("failed to create ScriptedStackFrameRecognizer: {0}", | |
| llvm::toString(obj_or_err.takeError())) | |
| .str(), | |
| interpreter->GetDebugger().GetID()); | |
| m_interface_sp.reset(); | |
| } | |
| } | |
| RecognizedStackFrameSP | |
| ScriptedStackFrameRecognizer::RecognizeFrame(lldb::StackFrameSP frame) { | |
| if (!m_interface_sp) | |
| return RecognizedStackFrameSP(); | |
| ValueObjectListSP args = m_interface_sp->GetRecognizedArguments(frame); | |
| auto args_synthesized = std::make_shared<ValueObjectList>(); | |
| if (args) { | |
| for (const auto &o : args->GetObjects()) | |
| args_synthesized->Append(ValueObjectRecognizerSynthesizedValue::Create( | |
| *o, eValueTypeVariableArgument)); | |
| } | |
| bool hidden = m_interface_sp->ShouldHide(frame); | |
| lldb::StackFrameSP most_relevant = | |
| m_interface_sp->SelectMostRelevantFrame(frame); | |
| lldb::ValueObjectSP exception = m_interface_sp->GetException(frame); | |
| std::string stop_desc = m_interface_sp->GetStopDescription(frame); | |
| // We only do step through if we're at the zeroth frame: | |
| lldb::ThreadPlanSP step_through_sp; | |
| if (frame->GetConcreteFrameIndex() == 0) | |
| step_through_sp = m_interface_sp->GetStepThroughPlan(frame->GetThread()); | |
| return RecognizedStackFrameSP(new ScriptedRecognizedStackFrame( | |
| args_synthesized, hidden, std::move(most_relevant), std::move(exception), | |
| std::move(stop_desc), std::move(step_through_sp))); | |
| } | |
| void StackFrameRecognizerManager::BumpGeneration() { | |
| uint32_t n = m_generation; | |
| n = (n + 1) & ((1 << 16) - 1); | |
| m_generation = n; | |
| } | |
| void StackFrameRecognizerManager::AddRecognizer( | |
| StackFrameRecognizerSP recognizer, std::string module, | |
| std::vector<ConstString> symbols, Mangled::NamePreference symbol_mangling, | |
| bool first_instruction_only) { | |
| m_recognizers.push_front({(uint32_t)m_recognizers.size(), recognizer, false, | |
| std::move(module), RegularExpressionSP(), | |
| std::move(symbols), RegularExpressionSP(), | |
| symbol_mangling, first_instruction_only, true}); | |
| BumpGeneration(); | |
| } | |
| void StackFrameRecognizerManager::AddRecognizer( | |
| StackFrameRecognizerSP recognizer, RegularExpressionSP module, | |
| RegularExpressionSP symbol, Mangled::NamePreference symbol_mangling, | |
| bool first_instruction_only) { | |
| m_recognizers.push_front({(uint32_t)m_recognizers.size(), recognizer, true, | |
| std::string(), module, std::vector<ConstString>(), | |
| symbol, symbol_mangling, first_instruction_only, | |
| true}); | |
| BumpGeneration(); | |
| } | |
| void StackFrameRecognizerManager::ForEach( | |
| const std::function<void( | |
| uint32_t, bool, std::string, std::string, llvm::ArrayRef<ConstString>, | |
| Mangled::NamePreference name_preference, bool)> &callback) { | |
| for (auto entry : m_recognizers) { | |
| if (entry.is_regexp) { | |
| std::string module_name; | |
| std::string symbol_name; | |
| if (entry.module_regexp) | |
| module_name = entry.module_regexp->GetText().str(); | |
| if (entry.symbol_regexp) | |
| symbol_name = entry.symbol_regexp->GetText().str(); | |
| callback(entry.recognizer_id, entry.enabled, entry.recognizer->GetName(), | |
| module_name, llvm::ArrayRef(ConstString(symbol_name)), | |
| entry.symbol_mangling, true); | |
| } else { | |
| callback(entry.recognizer_id, entry.enabled, entry.recognizer->GetName(), | |
| entry.module, entry.symbols, entry.symbol_mangling, false); | |
| } | |
| } | |
| } | |
| bool StackFrameRecognizerManager::SetEnabledForID(uint32_t recognizer_id, | |
| bool enabled) { | |
| auto found = | |
| llvm::find_if(m_recognizers, [recognizer_id](const RegisteredEntry &e) { | |
| return e.recognizer_id == recognizer_id; | |
| }); | |
| if (found == m_recognizers.end()) | |
| return false; | |
| found->enabled = enabled; | |
| BumpGeneration(); | |
| return true; | |
| } | |
| bool StackFrameRecognizerManager::RemoveRecognizerWithID( | |
| uint32_t recognizer_id) { | |
| auto found = | |
| llvm::find_if(m_recognizers, [recognizer_id](const RegisteredEntry &e) { | |
| return e.recognizer_id == recognizer_id; | |
| }); | |
| if (found == m_recognizers.end()) | |
| return false; | |
| m_recognizers.erase(found); | |
| BumpGeneration(); | |
| return true; | |
| } | |
| void StackFrameRecognizerManager::RemoveAllRecognizers() { | |
| BumpGeneration(); | |
| m_recognizers.clear(); | |
| } | |
| StackFrameRecognizerSP | |
| StackFrameRecognizerManager::GetRecognizerForFrame(StackFrameSP frame) { | |
| const SymbolContext &symctx = frame->GetSymbolContext( | |
| eSymbolContextModule | eSymbolContextFunction | eSymbolContextSymbol); | |
| ModuleSP module_sp = symctx.module_sp; | |
| if (!module_sp) | |
| return StackFrameRecognizerSP(); | |
| llvm::StringRef module_name = module_sp->GetFileSpec().GetFilename(); | |
| Address start_addr; | |
| if (symctx.symbol) | |
| start_addr = symctx.symbol->GetAddress(); | |
| else if (symctx.function) | |
| start_addr = symctx.function->GetAddress(); | |
| else | |
| return StackFrameRecognizerSP(); | |
| Address current_addr = frame->GetFrameCodeAddress(); | |
| // The symbol's start address may fall inside a non-executable function | |
| // header (as on WebAssembly), which no frame's PC can equal. Compare against | |
| // the first instruction instead. | |
| if (TargetSP target_sp = frame->CalculateTarget()) | |
| if (Architecture *arch = target_sp->GetArchitecturePlugin()) | |
| start_addr = arch->SkipFunctionHeader(start_addr); | |
| for (const auto &entry : m_recognizers) { | |
| if (!entry.enabled) | |
| continue; | |
| if (!entry.module.empty() && entry.module != module_name) | |
| continue; | |
| if (entry.module_regexp) | |
| if (!entry.module_regexp->Execute(module_name)) | |
| continue; | |
| ConstString function_name = symctx.GetFunctionName(entry.symbol_mangling); | |
| if (!entry.symbols.empty()) | |
| if (!llvm::is_contained(entry.symbols, function_name)) | |
| continue; | |
| if (entry.symbol_regexp) | |
| if (!entry.symbol_regexp->Execute(function_name.GetStringRef())) | |
| continue; | |
| if (entry.first_instruction_only) | |
| if (start_addr != current_addr) | |
| continue; | |
| return entry.recognizer; | |
| } | |
| return StackFrameRecognizerSP(); | |
| } | |
| RecognizedStackFrameSP | |
| StackFrameRecognizerManager::RecognizeFrame(StackFrameSP frame) { | |
| auto recognizer = GetRecognizerForFrame(frame); | |
| if (!recognizer) | |
| return RecognizedStackFrameSP(); | |
| return recognizer->RecognizeFrame(frame); | |
| } |