727 lines · cpp
1//===-- Variable.cpp ------------------------------------------------------===//2//3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.4// See https://llvm.org/LICENSE.txt for license information.5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception6//7//===----------------------------------------------------------------------===//8 9#include "lldb/Symbol/Variable.h"10 11#include "lldb/Core/Module.h"12#include "lldb/Symbol/Block.h"13#include "lldb/Symbol/CompileUnit.h"14#include "lldb/Symbol/CompilerDecl.h"15#include "lldb/Symbol/CompilerDeclContext.h"16#include "lldb/Symbol/Function.h"17#include "lldb/Symbol/SymbolContext.h"18#include "lldb/Symbol/SymbolFile.h"19#include "lldb/Symbol/Type.h"20#include "lldb/Symbol/TypeSystem.h"21#include "lldb/Symbol/VariableList.h"22#include "lldb/Target/ABI.h"23#include "lldb/Target/Process.h"24#include "lldb/Target/RegisterContext.h"25#include "lldb/Target/StackFrame.h"26#include "lldb/Target/Target.h"27#include "lldb/Target/Thread.h"28#include "lldb/Utility/LLDBLog.h"29#include "lldb/Utility/Log.h"30#include "lldb/Utility/RegularExpression.h"31#include "lldb/Utility/Stream.h"32#include "lldb/ValueObject/ValueObject.h"33#include "lldb/ValueObject/ValueObjectVariable.h"34 35#include "llvm/ADT/Twine.h"36 37using namespace lldb;38using namespace lldb_private;39 40Variable::Variable(lldb::user_id_t uid, const char *name, const char *mangled,41 const lldb::SymbolFileTypeSP &symfile_type_sp,42 ValueType scope, SymbolContextScope *context,43 const RangeList &scope_range, Declaration *decl_ptr,44 const DWARFExpressionList &location_list, bool external,45 bool artificial, bool location_is_constant_data,46 bool static_member)47 : UserID(uid), m_name(name), m_mangled(ConstString(mangled)),48 m_symfile_type_sp(symfile_type_sp), m_scope(scope),49 m_owner_scope(context), m_scope_range(scope_range),50 m_declaration(decl_ptr), m_location_list(location_list), m_external(external),51 m_artificial(artificial), m_loc_is_const_data(location_is_constant_data),52 m_static_member(static_member) {}53 54Variable::~Variable() = default;55 56lldb::LanguageType Variable::GetLanguage() const {57 lldb::LanguageType lang = m_mangled.GuessLanguage();58 if (lang != lldb::eLanguageTypeUnknown)59 return lang;60 61 if (auto *func = m_owner_scope->CalculateSymbolContextFunction()) {62 if ((lang = func->GetLanguage()) != lldb::eLanguageTypeUnknown)63 return lang;64 } else if (auto *comp_unit =65 m_owner_scope->CalculateSymbolContextCompileUnit()) {66 if ((lang = comp_unit->GetLanguage()) != lldb::eLanguageTypeUnknown)67 return lang;68 }69 70 return lldb::eLanguageTypeUnknown;71}72 73ConstString Variable::GetName() const {74 ConstString name = m_mangled.GetName();75 if (name)76 return name;77 return m_name;78}79 80ConstString Variable::GetUnqualifiedName() const { return m_name; }81 82bool Variable::NameMatches(ConstString name) const {83 if (m_name == name)84 return true;85 SymbolContext variable_sc;86 m_owner_scope->CalculateSymbolContext(&variable_sc);87 88 return m_mangled.NameMatches(name);89}90bool Variable::NameMatches(const RegularExpression ®ex) const {91 if (regex.Execute(m_name.AsCString()))92 return true;93 if (m_mangled)94 return m_mangled.NameMatches(regex);95 return false;96}97 98Type *Variable::GetType() {99 if (m_symfile_type_sp)100 return m_symfile_type_sp->GetType();101 return nullptr;102}103 104void Variable::Dump(Stream *s, bool show_context) const {105 s->Printf("%p: ", static_cast<const void *>(this));106 s->Indent();107 *s << "Variable" << (const UserID &)*this;108 109 if (m_name)110 *s << ", name = \"" << m_name << "\"";111 112 if (m_symfile_type_sp) {113 Type *type = m_symfile_type_sp->GetType();114 if (type) {115 s->Format(", type = {{{0:x-16}} {1} (", type->GetID(), type);116 type->DumpTypeName(s);117 s->PutChar(')');118 }119 }120 121 if (m_scope != eValueTypeInvalid) {122 s->PutCString(", scope = ");123 switch (m_scope) {124 case eValueTypeVariableGlobal:125 s->PutCString(m_external ? "global" : "static");126 break;127 case eValueTypeVariableArgument:128 s->PutCString("parameter");129 break;130 case eValueTypeVariableLocal:131 s->PutCString("local");132 break;133 case eValueTypeVariableThreadLocal:134 s->PutCString("thread local");135 break;136 default:137 s->AsRawOstream() << "??? (" << m_scope << ')';138 }139 }140 141 if (show_context && m_owner_scope != nullptr) {142 s->PutCString(", context = ( ");143 m_owner_scope->DumpSymbolContext(s);144 s->PutCString(" )");145 }146 147 bool show_fullpaths = false;148 m_declaration.Dump(s, show_fullpaths);149 150 if (m_location_list.IsValid()) {151 s->PutCString(", location = ");152 ABISP abi;153 if (m_owner_scope) {154 ModuleSP module_sp(m_owner_scope->CalculateSymbolContextModule());155 if (module_sp)156 abi = ABI::FindPlugin(ProcessSP(), module_sp->GetArchitecture());157 }158 m_location_list.GetDescription(s, lldb::eDescriptionLevelBrief, abi.get());159 }160 161 if (m_external)162 s->PutCString(", external");163 164 if (m_artificial)165 s->PutCString(", artificial");166 167 s->EOL();168}169 170bool Variable::DumpDeclaration(Stream *s, bool show_fullpaths,171 bool show_module) {172 bool dumped_declaration_info = false;173 if (m_owner_scope) {174 SymbolContext sc;175 m_owner_scope->CalculateSymbolContext(&sc);176 sc.block = nullptr;177 sc.line_entry.Clear();178 bool show_inlined_frames = false;179 const bool show_function_arguments = true;180 const bool show_function_name = true;181 182 dumped_declaration_info = sc.DumpStopContext(183 s, nullptr, Address(), show_fullpaths, show_module, show_inlined_frames,184 show_function_arguments, show_function_name);185 186 if (sc.function)187 s->PutChar(':');188 }189 if (m_declaration.DumpStopContext(s, false))190 dumped_declaration_info = true;191 return dumped_declaration_info;192}193 194size_t Variable::MemorySize() const { return sizeof(Variable); }195 196CompilerDeclContext Variable::GetDeclContext() {197 Type *type = GetType();198 if (type)199 return type->GetSymbolFile()->GetDeclContextContainingUID(GetID());200 return CompilerDeclContext();201}202 203CompilerDecl Variable::GetDecl() {204 Type *type = GetType();205 return type ? type->GetSymbolFile()->GetDeclForUID(GetID()) : CompilerDecl();206}207 208void Variable::CalculateSymbolContext(SymbolContext *sc) {209 if (m_owner_scope) {210 m_owner_scope->CalculateSymbolContext(sc);211 sc->variable = this;212 } else213 sc->Clear(false);214}215 216bool Variable::LocationIsValidForFrame(StackFrame *frame) {217 if (frame) {218 Function *function =219 frame->GetSymbolContext(eSymbolContextFunction).function;220 if (function) {221 TargetSP target_sp(frame->CalculateTarget());222 223 addr_t loclist_base_load_addr =224 function->GetAddress().GetLoadAddress(target_sp.get());225 if (loclist_base_load_addr == LLDB_INVALID_ADDRESS)226 return false;227 // It is a location list. We just need to tell if the location list228 // contains the current address when converted to a load address229 return m_location_list.ContainsAddress(230 loclist_base_load_addr,231 frame->GetFrameCodeAddressForSymbolication().GetLoadAddress(232 target_sp.get()));233 }234 }235 return false;236}237 238bool Variable::LocationIsValidForAddress(const Address &address) {239 // Be sure to resolve the address to section offset prior to calling this240 // function.241 if (address.IsSectionOffset()) {242 // We need to check if the address is valid for both scope range and value243 // range.244 // Empty scope range means block range.245 bool valid_in_scope_range =246 GetScopeRange().IsEmpty() || GetScopeRange().FindEntryThatContains(247 address.GetFileAddress()) != nullptr;248 if (!valid_in_scope_range)249 return false;250 SymbolContext sc;251 CalculateSymbolContext(&sc);252 if (sc.module_sp == address.GetModule()) {253 // Is the variable is described by a single location?254 if (m_location_list.IsAlwaysValidSingleExpr()) {255 // Yes it is, the location is valid.256 return true;257 }258 259 if (sc.function) {260 addr_t loclist_base_file_addr =261 sc.function->GetAddress().GetFileAddress();262 if (loclist_base_file_addr == LLDB_INVALID_ADDRESS)263 return false;264 // It is a location list. We just need to tell if the location list265 // contains the current address when converted to a load address266 return m_location_list.ContainsAddress(loclist_base_file_addr,267 address.GetFileAddress());268 }269 }270 }271 return false;272}273 274bool Variable::IsInScope(StackFrame *frame) {275 switch (m_scope) {276 case eValueTypeRegister:277 case eValueTypeRegisterSet:278 return frame != nullptr;279 280 case eValueTypeConstResult:281 case eValueTypeVariableGlobal:282 case eValueTypeVariableStatic:283 case eValueTypeVariableThreadLocal:284 return true;285 286 case eValueTypeVariableArgument:287 case eValueTypeVariableLocal:288 if (frame) {289 // We don't have a location list, we just need to see if the block that290 // this variable was defined in is currently291 Block *deepest_frame_block =292 frame->GetSymbolContext(eSymbolContextBlock).block;293 Address frame_addr = frame->GetFrameCodeAddress();294 if (deepest_frame_block)295 return IsInScope(*deepest_frame_block, frame_addr);296 }297 break;298 299 default:300 break;301 }302 return false;303}304 305bool Variable::IsInScope(const Block &block, const Address &addr) {306 SymbolContext variable_sc;307 CalculateSymbolContext(&variable_sc);308 309 // Check for static or global variable defined at the compile unit310 // level that wasn't defined in a block311 if (variable_sc.block == nullptr)312 return true;313 314 // Check if the variable is valid in the current block315 if (variable_sc.block != &block && !variable_sc.block->Contains(&block))316 return false;317 318 // If no scope range is specified then it means that the scope is the319 // same as the scope of the enclosing lexical block.320 if (m_scope_range.IsEmpty())321 return true;322 323 return m_scope_range.FindEntryThatContains(addr.GetFileAddress()) != nullptr;324}325 326Status Variable::GetValuesForVariableExpressionPath(327 llvm::StringRef variable_expr_path, ExecutionContextScope *scope,328 GetVariableCallback callback, void *baton, VariableList &variable_list,329 ValueObjectList &valobj_list) {330 Status error;331 if (!callback || variable_expr_path.empty()) {332 error = Status::FromErrorString("unknown error");333 return error;334 }335 336 switch (variable_expr_path.front()) {337 case '*':338 error = Variable::GetValuesForVariableExpressionPath(339 variable_expr_path.drop_front(), scope, callback, baton, variable_list,340 valobj_list);341 if (error.Fail()) {342 error = Status::FromErrorString("unknown error");343 return error;344 }345 for (uint32_t i = 0; i < valobj_list.GetSize();) {346 Status tmp_error;347 ValueObjectSP valobj_sp(348 valobj_list.GetValueObjectAtIndex(i)->Dereference(tmp_error));349 if (tmp_error.Fail()) {350 variable_list.RemoveVariableAtIndex(i);351 valobj_list.RemoveValueObjectAtIndex(i);352 } else {353 valobj_list.SetValueObjectAtIndex(i, valobj_sp);354 ++i;355 }356 }357 return error;358 case '&': {359 error = Variable::GetValuesForVariableExpressionPath(360 variable_expr_path.drop_front(), scope, callback, baton, variable_list,361 valobj_list);362 if (error.Success()) {363 for (uint32_t i = 0; i < valobj_list.GetSize();) {364 Status tmp_error;365 ValueObjectSP valobj_sp(366 valobj_list.GetValueObjectAtIndex(i)->AddressOf(tmp_error));367 if (tmp_error.Fail()) {368 variable_list.RemoveVariableAtIndex(i);369 valobj_list.RemoveValueObjectAtIndex(i);370 } else {371 valobj_list.SetValueObjectAtIndex(i, valobj_sp);372 ++i;373 }374 }375 } else {376 error = Status::FromErrorString("unknown error");377 }378 return error;379 } break;380 381 default: {382 static RegularExpression g_regex(383 llvm::StringRef("^([A-Za-z_:][A-Za-z_0-9:]*)(.*)"));384 llvm::SmallVector<llvm::StringRef, 2> matches;385 variable_list.Clear();386 if (!g_regex.Execute(variable_expr_path, &matches)) {387 error = Status::FromErrorStringWithFormatv(388 "unable to extract a variable name from '{0}'", variable_expr_path);389 return error;390 }391 std::string variable_name = matches[1].str();392 if (!callback(baton, variable_name.c_str(), variable_list)) {393 error = Status::FromErrorString("unknown error");394 return error;395 }396 uint32_t i = 0;397 while (i < variable_list.GetSize()) {398 VariableSP var_sp(variable_list.GetVariableAtIndex(i));399 ValueObjectSP valobj_sp;400 if (!var_sp) {401 variable_list.RemoveVariableAtIndex(i);402 continue;403 }404 ValueObjectSP variable_valobj_sp(405 ValueObjectVariable::Create(scope, var_sp));406 if (!variable_valobj_sp) {407 variable_list.RemoveVariableAtIndex(i);408 continue;409 }410 411 llvm::StringRef variable_sub_expr_path =412 variable_expr_path.drop_front(variable_name.size());413 if (!variable_sub_expr_path.empty()) {414 valobj_sp = variable_valobj_sp->GetValueForExpressionPath(415 variable_sub_expr_path);416 if (!valobj_sp) {417 error = Status::FromErrorStringWithFormatv(418 "invalid expression path '{0}' for variable '{1}'",419 variable_sub_expr_path, var_sp->GetName().GetCString());420 variable_list.RemoveVariableAtIndex(i);421 continue;422 }423 } else {424 // Just the name of a variable with no extras425 valobj_sp = variable_valobj_sp;426 }427 428 valobj_list.Append(valobj_sp);429 ++i;430 }431 432 if (variable_list.GetSize() > 0) {433 error.Clear();434 return error;435 }436 } break;437 }438 error = Status::FromErrorString("unknown error");439 return error;440}441 442bool Variable::DumpLocations(Stream *s, const Address &address) {443 SymbolContext sc;444 CalculateSymbolContext(&sc);445 ABISP abi;446 if (m_owner_scope) {447 ModuleSP module_sp(m_owner_scope->CalculateSymbolContextModule());448 if (module_sp)449 abi = ABI::FindPlugin(ProcessSP(), module_sp->GetArchitecture());450 }451 452 const addr_t file_addr = address.GetFileAddress();453 if (sc.function) {454 addr_t loclist_base_file_addr = sc.function->GetAddress().GetFileAddress();455 if (loclist_base_file_addr == LLDB_INVALID_ADDRESS)456 return false;457 return m_location_list.DumpLocations(s, eDescriptionLevelBrief,458 loclist_base_file_addr, file_addr,459 abi.get());460 }461 return false;462}463 464static void PrivateAutoComplete(465 StackFrame *frame, llvm::StringRef partial_path,466 const llvm::Twine467 &prefix_path, // Anything that has been resolved already will be in here468 const CompilerType &compiler_type, CompletionRequest &request);469 470static void PrivateAutoCompleteMembers(471 StackFrame *frame, const std::string &partial_member_name,472 llvm::StringRef partial_path,473 const llvm::Twine474 &prefix_path, // Anything that has been resolved already will be in here475 const CompilerType &compiler_type, CompletionRequest &request) {476 477 // We are in a type parsing child members478 const uint32_t num_bases = compiler_type.GetNumDirectBaseClasses();479 480 if (num_bases > 0) {481 for (uint32_t i = 0; i < num_bases; ++i) {482 CompilerType base_class_type =483 compiler_type.GetDirectBaseClassAtIndex(i, nullptr);484 485 PrivateAutoCompleteMembers(frame, partial_member_name, partial_path,486 prefix_path,487 base_class_type.GetCanonicalType(), request);488 }489 }490 491 const uint32_t num_vbases = compiler_type.GetNumVirtualBaseClasses();492 493 if (num_vbases > 0) {494 for (uint32_t i = 0; i < num_vbases; ++i) {495 CompilerType vbase_class_type =496 compiler_type.GetVirtualBaseClassAtIndex(i, nullptr);497 498 PrivateAutoCompleteMembers(frame, partial_member_name, partial_path,499 prefix_path,500 vbase_class_type.GetCanonicalType(), request);501 }502 }503 504 // We are in a type parsing child members505 const uint32_t num_fields = compiler_type.GetNumFields();506 507 if (num_fields > 0) {508 for (uint32_t i = 0; i < num_fields; ++i) {509 std::string member_name;510 511 CompilerType member_compiler_type = compiler_type.GetFieldAtIndex(512 i, member_name, nullptr, nullptr, nullptr);513 514 if (partial_member_name.empty()) {515 request.AddCompletion((prefix_path + member_name).str());516 } else if (llvm::StringRef(member_name)517 .starts_with(partial_member_name)) {518 if (member_name == partial_member_name) {519 PrivateAutoComplete(520 frame, partial_path,521 prefix_path + member_name, // Anything that has been resolved522 // already will be in here523 member_compiler_type.GetCanonicalType(), request);524 } else if (partial_path.empty()) {525 request.AddCompletion((prefix_path + member_name).str());526 }527 }528 }529 }530}531 532static void PrivateAutoComplete(533 StackFrame *frame, llvm::StringRef partial_path,534 const llvm::Twine535 &prefix_path, // Anything that has been resolved already will be in here536 const CompilerType &compiler_type, CompletionRequest &request) {537 // printf ("\nPrivateAutoComplete()\n\tprefix_path = '%s'\n\tpartial_path =538 // '%s'\n", prefix_path.c_str(), partial_path.c_str());539 std::string remaining_partial_path;540 541 const lldb::TypeClass type_class = compiler_type.GetTypeClass();542 if (partial_path.empty()) {543 if (compiler_type.IsValid()) {544 switch (type_class) {545 default:546 case eTypeClassArray:547 case eTypeClassBlockPointer:548 case eTypeClassBuiltin:549 case eTypeClassComplexFloat:550 case eTypeClassComplexInteger:551 case eTypeClassEnumeration:552 case eTypeClassFunction:553 case eTypeClassMemberPointer:554 case eTypeClassReference:555 case eTypeClassTypedef:556 case eTypeClassVector: {557 request.AddCompletion(prefix_path.str());558 } break;559 560 case eTypeClassClass:561 case eTypeClassStruct:562 case eTypeClassUnion:563 if (prefix_path.str().back() != '.')564 request.AddCompletion((prefix_path + ".").str());565 break;566 567 case eTypeClassObjCObject:568 case eTypeClassObjCInterface:569 break;570 case eTypeClassObjCObjectPointer:571 case eTypeClassPointer: {572 bool omit_empty_base_classes = true;573 if (llvm::expectedToStdOptional(574 compiler_type.GetNumChildren(omit_empty_base_classes, nullptr))575 .value_or(0))576 request.AddCompletion((prefix_path + "->").str());577 else {578 request.AddCompletion(prefix_path.str());579 }580 } break;581 }582 } else {583 if (frame) {584 const bool get_file_globals = true;585 586 VariableList *variable_list = frame->GetVariableList(get_file_globals,587 nullptr);588 589 if (variable_list) {590 for (const VariableSP &var_sp : *variable_list)591 request.AddCompletion(var_sp->GetName().AsCString());592 }593 }594 }595 } else {596 const char ch = partial_path[0];597 switch (ch) {598 case '*':599 if (prefix_path.str().empty()) {600 PrivateAutoComplete(frame, partial_path.substr(1), "*", compiler_type,601 request);602 }603 break;604 605 case '&':606 if (prefix_path.isTriviallyEmpty()) {607 PrivateAutoComplete(frame, partial_path.substr(1), std::string("&"),608 compiler_type, request);609 }610 break;611 612 case '-':613 if (partial_path.size() > 1 && partial_path[1] == '>' &&614 !prefix_path.str().empty()) {615 switch (type_class) {616 case lldb::eTypeClassPointer: {617 CompilerType pointee_type(compiler_type.GetPointeeType());618 if (partial_path.size() > 2 && partial_path[2]) {619 // If there is more after the "->", then search deeper620 PrivateAutoComplete(frame, partial_path.substr(2),621 prefix_path + "->",622 pointee_type.GetCanonicalType(), request);623 } else {624 // Nothing after the "->", so list all members625 PrivateAutoCompleteMembers(626 frame, std::string(), std::string(), prefix_path + "->",627 pointee_type.GetCanonicalType(), request);628 }629 } break;630 default:631 break;632 }633 }634 break;635 636 case '.':637 if (compiler_type.IsValid()) {638 switch (type_class) {639 case lldb::eTypeClassUnion:640 case lldb::eTypeClassStruct:641 case lldb::eTypeClassClass:642 if (partial_path.size() > 1 && partial_path[1]) {643 // If there is more after the ".", then search deeper644 PrivateAutoComplete(frame, partial_path.substr(1),645 prefix_path + ".", compiler_type, request);646 647 } else {648 // Nothing after the ".", so list all members649 PrivateAutoCompleteMembers(frame, std::string(), partial_path,650 prefix_path + ".", compiler_type,651 request);652 }653 break;654 default:655 break;656 }657 }658 break;659 default:660 if (isalpha(ch) || ch == '_' || ch == '$') {661 const size_t partial_path_len = partial_path.size();662 size_t pos = 1;663 while (pos < partial_path_len) {664 const char curr_ch = partial_path[pos];665 if (isalnum(curr_ch) || curr_ch == '_' || curr_ch == '$') {666 ++pos;667 continue;668 }669 break;670 }671 672 std::string token(std::string(partial_path), 0, pos);673 remaining_partial_path = std::string(partial_path.substr(pos));674 675 if (compiler_type.IsValid()) {676 PrivateAutoCompleteMembers(frame, token, remaining_partial_path,677 prefix_path, compiler_type, request);678 } else if (frame) {679 // We haven't found our variable yet680 const bool get_file_globals = true;681 682 VariableList *variable_list =683 frame->GetVariableList(get_file_globals, nullptr);684 685 if (!variable_list)686 break;687 688 for (VariableSP var_sp : *variable_list) {689 690 if (!var_sp)691 continue;692 693 llvm::StringRef variable_name = var_sp->GetName().GetStringRef();694 if (variable_name.starts_with(token)) {695 if (variable_name == token) {696 Type *variable_type = var_sp->GetType();697 if (variable_type) {698 CompilerType variable_compiler_type(699 variable_type->GetForwardCompilerType());700 PrivateAutoComplete(701 frame, remaining_partial_path,702 prefix_path + token, // Anything that has been resolved703 // already will be in here704 variable_compiler_type.GetCanonicalType(), request);705 } else {706 request.AddCompletion((prefix_path + variable_name).str());707 }708 } else if (remaining_partial_path.empty()) {709 request.AddCompletion((prefix_path + variable_name).str());710 }711 }712 }713 }714 }715 break;716 }717 }718}719 720void Variable::AutoComplete(const ExecutionContext &exe_ctx,721 CompletionRequest &request) {722 CompilerType compiler_type;723 724 PrivateAutoComplete(exe_ctx.GetFramePtr(), request.GetCursorArgumentPrefix(),725 "", compiler_type, request);726}727