| //===-- SwiftREPLMaterializer.cpp -------------------------------*- C++ -*-===// |
| // |
| // This source file is part of the Swift.org open source project |
| // |
| // Copyright (c) 2014 - 2016 Apple Inc. and the Swift project authors |
| // Licensed under Apache License v2.0 with Runtime Library Exception |
| // |
| // See https://swift.org/LICENSE.txt for license information |
| // See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors |
| // |
| //===----------------------------------------------------------------------===// |
| |
| #include "SwiftREPLMaterializer.h" |
| #include "SwiftASTManipulator.h" |
| |
| #include "lldb/Core/DumpDataExtractor.h" |
| #include "lldb/Core/ValueObjectConstResult.h" |
| #include "lldb/Expression/IRExecutionUnit.h" |
| #include "lldb/Expression/IRMemoryMap.h" |
| #include "lldb/Target/Target.h" |
| #include "lldb/Utility/Log.h" |
| |
| #include "swift/Demangling/Demangle.h" |
| |
| using namespace lldb_private; |
| |
| static llvm::StringRef |
| GetNameOfDemangledVariable(swift::Demangle::NodePointer node_pointer) { |
| if (!node_pointer || |
| node_pointer->getKind() != swift::Demangle::Node::Kind::Global) |
| return llvm::StringRef(); |
| |
| swift::Demangle::NodePointer variable_pointer = |
| node_pointer->getFirstChild(); |
| |
| if (!variable_pointer || |
| variable_pointer->getKind() != swift::Demangle::Node::Kind::Variable) |
| return llvm::StringRef(); |
| |
| for (swift::Demangle::NodePointer child : *variable_pointer) { |
| if (child && |
| child->getKind() == swift::Demangle::Node::Kind::Identifier && |
| child->hasText()) { |
| return child->getText(); |
| } |
| } |
| return llvm::StringRef(); |
| } |
| |
| class EntityREPLResultVariable : public Materializer::Entity { |
| public: |
| EntityREPLResultVariable(const CompilerType &type, |
| swift::ValueDecl *swift_decl, |
| SwiftREPLMaterializer *parent, |
| Materializer::PersistentVariableDelegate *delegate) |
| : Entity(), m_type(type), m_parent(parent), m_swift_decl(swift_decl), |
| m_temporary_allocation(LLDB_INVALID_ADDRESS), |
| m_temporary_allocation_size(0), m_delegate(delegate) { |
| // Hard-coding to maximum size of a pointer since all results are |
| // materialized by reference |
| m_size = 8; |
| m_alignment = 8; |
| } |
| |
| void Materialize(lldb::StackFrameSP &frame_sp, IRMemoryMap &map, |
| lldb::addr_t process_address, Status &err) { |
| // no action required |
| } |
| |
| void MakeREPLResult(IRExecutionUnit &execution_unit, Status &err, |
| const IRExecutionUnit::JittedGlobalVariable *variable) { |
| err.Clear(); |
| |
| ExecutionContextScope *exe_scope = |
| execution_unit.GetBestExecutionContextScope(); |
| |
| if (!exe_scope) { |
| err.SetErrorString("Couldn't dematerialize a result variable: invalid " |
| "execution context scope"); |
| return; |
| } |
| |
| lldb::TargetSP target_sp = exe_scope->CalculateTarget(); |
| |
| if (!target_sp) { |
| err.SetErrorString("Couldn't dematerialize a result variable: no target"); |
| return; |
| } |
| |
| lldb::LanguageType lang = |
| (m_type.GetMinimumLanguage() == lldb::eLanguageTypeSwift) |
| ? lldb::eLanguageTypeSwift |
| : lldb::eLanguageTypeObjC_plus_plus; |
| |
| PersistentExpressionState *persistent_state = |
| target_sp->GetPersistentExpressionStateForLanguage(lang); |
| |
| if (!persistent_state) { |
| err.SetErrorString("Couldn't dematerialize a result variable: language " |
| "doesn't have persistent state"); |
| } |
| |
| ConstString name = |
| m_delegate ? m_delegate->GetName() |
| : persistent_state->GetNextPersistentVariableName(false); |
| |
| lldb::ExpressionVariableSP ret; |
| |
| ret = persistent_state |
| ->CreatePersistentVariable(exe_scope, name, m_type, |
| execution_unit.GetByteOrder(), |
| execution_unit.GetAddressByteSize()) |
| ->shared_from_this(); |
| |
| if (!ret) { |
| err.SetErrorStringWithFormat("couldn't dematerialize a result variable: " |
| "failed to make persistent variable %s", |
| name.AsCString()); |
| return; |
| } |
| |
| lldb::ProcessSP process_sp = |
| execution_unit.GetBestExecutionContextScope()->CalculateProcess(); |
| |
| ret->m_live_sp = ValueObjectConstResult::Create( |
| exe_scope, m_type, name, |
| variable ? variable->m_remote_addr : LLDB_INVALID_ADDRESS, |
| eAddressTypeLoad, execution_unit.GetAddressByteSize()); |
| |
| ret->ValueUpdated(); |
| |
| if (variable) { |
| const size_t pvar_byte_size = ret->GetByteSize(); |
| uint8_t *pvar_data = ret->GetValueBytes(); |
| |
| Status read_error; |
| |
| execution_unit.ReadMemory(pvar_data, variable->m_remote_addr, |
| pvar_byte_size, read_error); |
| |
| if (!read_error.Success()) { |
| err.SetErrorString("Couldn't dematerialize a result variable: couldn't " |
| "read its memory"); |
| return; |
| } |
| } |
| |
| if (m_delegate) { |
| m_delegate->DidDematerialize(ret); |
| } |
| |
| // Register the variable with the persistent decls under the assumed, |
| // just-generated name so it can be reused. |
| |
| if (m_swift_decl) { |
| llvm::cast<SwiftPersistentExpressionState>(persistent_state) |
| ->RegisterSwiftPersistentDeclAlias(m_swift_decl, name); |
| } |
| |
| return; |
| } |
| |
| void Dematerialize(lldb::StackFrameSP &frame_sp, IRMemoryMap &map, |
| lldb::addr_t process_address, lldb::addr_t frame_top, |
| lldb::addr_t frame_bottom, Status &err) { |
| IRExecutionUnit *execution_unit = |
| llvm::cast<SwiftREPLMaterializer>(m_parent)->GetExecutionUnit(); |
| |
| if (!execution_unit) { |
| return; |
| } |
| |
| swift::Demangle::Context demangle_ctx; |
| llvm::StringRef result_name = SwiftASTManipulator::GetResultName(); |
| |
| for (const IRExecutionUnit::JittedGlobalVariable &variable : |
| execution_unit->GetJittedGlobalVariables()) { |
| swift::Demangle::NodePointer node_pointer = |
| demangle_ctx.demangleSymbolAsNode(variable.m_name.GetStringRef()); |
| |
| llvm::StringRef variable_name = GetNameOfDemangledVariable(node_pointer); |
| if (variable_name == result_name) { |
| MakeREPLResult(*execution_unit, err, &variable); |
| return; |
| } |
| |
| demangle_ctx.clear(); |
| } |
| |
| if (SwiftASTContext::IsPossibleZeroSizeType(m_type)) { |
| MakeREPLResult(*execution_unit, err, nullptr); |
| return; |
| } |
| |
| err.SetErrorToGenericError(); |
| err.SetErrorStringWithFormat( |
| "Couldn't dematerialize result: corresponding symbol wasn't found"); |
| } |
| |
| void DumpToLog(IRMemoryMap &map, lldb::addr_t process_address, Log *log) { |
| StreamString dump_stream; |
| |
| const lldb::addr_t load_addr = process_address + m_offset; |
| |
| dump_stream.Printf("0x%" PRIx64 ": EntityResultVariable\n", load_addr); |
| |
| Status err; |
| |
| lldb::addr_t ptr = LLDB_INVALID_ADDRESS; |
| |
| { |
| dump_stream.Printf("Pointer:\n"); |
| |
| DataBufferHeap data(m_size, 0); |
| |
| map.ReadMemory(data.GetBytes(), load_addr, m_size, err); |
| |
| if (!err.Success()) { |
| dump_stream.Printf(" <could not be read>\n"); |
| } else { |
| DataExtractor extractor(data.GetBytes(), data.GetByteSize(), |
| map.GetByteOrder(), map.GetAddressByteSize()); |
| |
| DumpHexBytes(&dump_stream, data.GetBytes(), |
| data.GetByteSize(), 16, load_addr); |
| |
| lldb::offset_t offset; |
| |
| ptr = extractor.GetPointer(&offset); |
| |
| dump_stream.PutChar('\n'); |
| } |
| } |
| |
| if (m_temporary_allocation == LLDB_INVALID_ADDRESS) { |
| dump_stream.Printf("Points to process memory:\n"); |
| } else { |
| dump_stream.Printf("Temporary allocation:\n"); |
| } |
| |
| if (ptr == LLDB_INVALID_ADDRESS) { |
| dump_stream.Printf(" <could not be be found>\n"); |
| } else { |
| DataBufferHeap data(m_temporary_allocation_size, 0); |
| |
| map.ReadMemory(data.GetBytes(), m_temporary_allocation, |
| m_temporary_allocation_size, err); |
| |
| if (!err.Success()) { |
| dump_stream.Printf(" <could not be read>\n"); |
| } else { |
| DumpHexBytes(&dump_stream, data.GetBytes(), |
| data.GetByteSize(), 16, m_temporary_allocation); |
| |
| dump_stream.PutChar('\n'); |
| } |
| } |
| |
| log->PutCString(dump_stream.GetData()); |
| } |
| |
| void Wipe(IRMemoryMap &map, lldb::addr_t process_address) { |
| m_temporary_allocation = LLDB_INVALID_ADDRESS; |
| m_temporary_allocation_size = 0; |
| } |
| |
| private: |
| CompilerType m_type; |
| |
| SwiftREPLMaterializer *m_parent; |
| swift::ValueDecl *m_swift_decl; // only used for the REPL; nullptr otherwise |
| |
| lldb::addr_t m_temporary_allocation; |
| size_t m_temporary_allocation_size; |
| |
| Materializer::PersistentVariableDelegate *m_delegate; |
| }; |
| |
| uint32_t SwiftREPLMaterializer::AddREPLResultVariable( |
| const CompilerType &type, swift::ValueDecl *decl, |
| PersistentVariableDelegate *delegate, Status &err) { |
| EntityVector::iterator iter = m_entities.insert(m_entities.end(), EntityUP()); |
| |
| iter->reset(new EntityREPLResultVariable(type, decl, this, delegate)); |
| uint32_t ret = AddStructMember(**iter); |
| (*iter)->SetOffset(ret); |
| |
| return ret; |
| } |
| |
| class EntityREPLPersistentVariable : public Materializer::Entity { |
| public: |
| EntityREPLPersistentVariable( |
| lldb::ExpressionVariableSP &persistent_variable_sp, |
| SwiftREPLMaterializer *parent, |
| Materializer::PersistentVariableDelegate *delegate) |
| : Entity(), m_persistent_variable_sp(persistent_variable_sp), |
| m_parent(parent), m_delegate(delegate) { |
| // Hard-coding to maximum size of a pointer since persistent variables are |
| // materialized by reference |
| m_size = 8; |
| m_alignment = 8; |
| } |
| |
| void Materialize(lldb::StackFrameSP &frame_sp, IRMemoryMap &map, |
| lldb::addr_t process_address, Status &err) { |
| // no action required |
| } |
| |
| void Dematerialize(lldb::StackFrameSP &frame_sp, IRMemoryMap &map, |
| lldb::addr_t process_address, lldb::addr_t frame_top, |
| lldb::addr_t frame_bottom, Status &err) { |
| if (llvm::cast<SwiftExpressionVariable>(m_persistent_variable_sp.get()) |
| ->GetIsComputed()) |
| return; |
| |
| IRExecutionUnit *execution_unit = m_parent->GetExecutionUnit(); |
| |
| if (!execution_unit) { |
| return; |
| } |
| |
| swift::Demangle::Context demangle_ctx; |
| |
| for (const IRExecutionUnit::JittedGlobalVariable &variable : |
| execution_unit->GetJittedGlobalVariables()) { |
| // e.g. |
| // kind=Global |
| // kind=Variable |
| // kind=Module, text="lldb_expr_0" |
| // kind=Identifier, text="a" |
| |
| swift::Demangle::NodePointer node_pointer = |
| demangle_ctx.demangleSymbolAsNode(variable.m_name.GetStringRef()); |
| |
| llvm::StringRef last_component = GetNameOfDemangledVariable(node_pointer); |
| |
| if (last_component.empty()) |
| continue; |
| |
| if (m_persistent_variable_sp->GetName().GetStringRef().equals( |
| last_component)) { |
| ExecutionContextScope *exe_scope = |
| execution_unit->GetBestExecutionContextScope(); |
| |
| if (!exe_scope) { |
| err.SetErrorString("Couldn't dematerialize a persistent variable: " |
| "invalid execution context scope"); |
| return; |
| } |
| |
| m_persistent_variable_sp->m_live_sp = ValueObjectConstResult::Create( |
| exe_scope, m_persistent_variable_sp->GetCompilerType(), |
| m_persistent_variable_sp->GetName(), variable.m_remote_addr, |
| eAddressTypeLoad, execution_unit->GetAddressByteSize()); |
| |
| // Read the contents of the spare memory area |
| |
| m_persistent_variable_sp->ValueUpdated(); |
| |
| Status read_error; |
| |
| execution_unit->ReadMemory( |
| m_persistent_variable_sp->GetValueBytes(), variable.m_remote_addr, |
| m_persistent_variable_sp->GetByteSize(), read_error); |
| |
| if (!read_error.Success()) { |
| err.SetErrorStringWithFormat( |
| "couldn't read the contents of %s from memory: %s", |
| m_persistent_variable_sp->GetName().GetCString(), |
| read_error.AsCString()); |
| return; |
| } |
| |
| m_persistent_variable_sp->m_flags &= |
| ~ExpressionVariable::EVNeedsFreezeDry; |
| |
| return; |
| } |
| demangle_ctx.clear(); |
| } |
| |
| err.SetErrorToGenericError(); |
| err.SetErrorStringWithFormat( |
| "Couldn't dematerialize %s: corresponding symbol wasn't found", |
| m_persistent_variable_sp->GetName().GetCString()); |
| } |
| |
| void DumpToLog(IRMemoryMap &map, lldb::addr_t process_address, Log *log) { |
| StreamString dump_stream; |
| |
| Status err; |
| |
| const lldb::addr_t load_addr = process_address + m_offset; |
| |
| dump_stream.Printf("0x%" PRIx64 ": EntityPersistentVariable (%s)\n", |
| load_addr, |
| m_persistent_variable_sp->GetName().AsCString()); |
| |
| { |
| dump_stream.Printf("Pointer:\n"); |
| |
| DataBufferHeap data(m_size, 0); |
| |
| map.ReadMemory(data.GetBytes(), load_addr, m_size, err); |
| |
| if (!err.Success()) { |
| dump_stream.Printf(" <could not be read>\n"); |
| } else { |
| DumpHexBytes(&dump_stream, data.GetBytes(), |
| data.GetByteSize(), 16, load_addr); |
| |
| dump_stream.PutChar('\n'); |
| } |
| } |
| |
| { |
| dump_stream.Printf("Target:\n"); |
| |
| lldb::addr_t target_address; |
| |
| map.ReadPointerFromMemory(&target_address, load_addr, err); |
| |
| if (!err.Success()) { |
| dump_stream.Printf(" <could not be read>\n"); |
| } else { |
| DataBufferHeap data(m_persistent_variable_sp->GetByteSize(), 0); |
| |
| map.ReadMemory(data.GetBytes(), target_address, |
| m_persistent_variable_sp->GetByteSize(), err); |
| |
| if (!err.Success()) { |
| dump_stream.Printf(" <could not be read>\n"); |
| } else { |
| DumpHexBytes(&dump_stream, data.GetBytes(), |
| data.GetByteSize(), 16, target_address); |
| |
| dump_stream.PutChar('\n'); |
| } |
| } |
| } |
| |
| log->PutCString(dump_stream.GetData()); |
| } |
| |
| void Wipe(IRMemoryMap &map, lldb::addr_t process_address) {} |
| |
| private: |
| lldb::ExpressionVariableSP m_persistent_variable_sp; |
| SwiftREPLMaterializer *m_parent; |
| Materializer::PersistentVariableDelegate *m_delegate; |
| }; |
| |
| uint32_t SwiftREPLMaterializer::AddPersistentVariable( |
| lldb::ExpressionVariableSP &persistent_variable_sp, |
| PersistentVariableDelegate *delegate, Status &err) { |
| EntityVector::iterator iter = m_entities.insert(m_entities.end(), EntityUP()); |
| iter->reset( |
| new EntityREPLPersistentVariable(persistent_variable_sp, this, delegate)); |
| uint32_t ret = AddStructMember(**iter); |
| (*iter)->SetOffset(ret); |
| return ret; |
| } |