|  | // Copyright 2018 The Fuchsia Authors. All rights reserved. | 
|  | // Use of this source code is governed by a BSD-style license that can be | 
|  | // found in the LICENSE file. | 
|  |  | 
|  | #include "src/developer/debug/zxdb/expr/eval_context_impl.h" | 
|  |  | 
|  | #include <lib/syslog/cpp/macros.h> | 
|  |  | 
|  | #include "src/developer/debug/shared/message_loop.h" | 
|  | #include "src/developer/debug/zxdb/common/adapters.h" | 
|  | #include "src/developer/debug/zxdb/common/err.h" | 
|  | #include "src/developer/debug/zxdb/expr/async_dwarf_expr_eval.h" | 
|  | #include "src/developer/debug/zxdb/expr/builtin_types.h" | 
|  | #include "src/developer/debug/zxdb/expr/expr_value.h" | 
|  | #include "src/developer/debug/zxdb/expr/find_name.h" | 
|  | #include "src/developer/debug/zxdb/expr/resolve_collection.h" | 
|  | #include "src/developer/debug/zxdb/expr/resolve_const_value.h" | 
|  | #include "src/developer/debug/zxdb/expr/resolve_ptr_ref.h" | 
|  | #include "src/developer/debug/zxdb/expr/resolve_type.h" | 
|  | #include "src/developer/debug/zxdb/symbols/array_type.h" | 
|  | #include "src/developer/debug/zxdb/symbols/base_type.h" | 
|  | #include "src/developer/debug/zxdb/symbols/code_block.h" | 
|  | #include "src/developer/debug/zxdb/symbols/compile_unit.h" | 
|  | #include "src/developer/debug/zxdb/symbols/data_member.h" | 
|  | #include "src/developer/debug/zxdb/symbols/dwarf_expr_eval.h" | 
|  | #include "src/developer/debug/zxdb/symbols/function.h" | 
|  | #include "src/developer/debug/zxdb/symbols/identifier.h" | 
|  | #include "src/developer/debug/zxdb/symbols/input_location.h" | 
|  | #include "src/developer/debug/zxdb/symbols/location.h" | 
|  | #include "src/developer/debug/zxdb/symbols/modified_type.h" | 
|  | #include "src/developer/debug/zxdb/symbols/process_symbols.h" | 
|  | #include "src/developer/debug/zxdb/symbols/symbol_data_provider.h" | 
|  | #include "src/developer/debug/zxdb/symbols/variable.h" | 
|  | #include "src/lib/fxl/strings/string_printf.h" | 
|  |  | 
|  | namespace zxdb { | 
|  | namespace { | 
|  |  | 
|  | using debug_ipc::RegisterID; | 
|  |  | 
|  | RegisterID GetRegisterID(const ParsedIdentifier& ident) { | 
|  | // Check for explicit register identifier annotation. | 
|  | if (ident.components().size() == 1 && | 
|  | ident.components()[0].special() == SpecialIdentifier::kRegister) { | 
|  | return debug_ipc::StringToRegisterID(ident.components()[0].name()); | 
|  | } | 
|  |  | 
|  | // Try to convert the identifier string to a register name. | 
|  | auto str = GetSingleComponentIdentifierName(ident); | 
|  | if (!str) | 
|  | return debug_ipc::RegisterID::kUnknown; | 
|  | return debug_ipc::StringToRegisterID(*str); | 
|  | } | 
|  |  | 
|  | Err GetUnavailableRegisterErr(RegisterID id) { | 
|  | return Err("Register %s unavailable in this context.", debug_ipc::RegisterIDToString(id)); | 
|  | } | 
|  |  | 
|  | ErrOrValue RegisterDataToValue(RegisterID id, VectorRegisterFormat vector_fmt, | 
|  | containers::array_view<uint8_t> data) { | 
|  | if (ShouldFormatRegisterAsVector(id)) | 
|  | return VectorRegisterToValue(id, vector_fmt, std::vector<uint8_t>(data.begin(), data.end())); | 
|  |  | 
|  | ExprValueSource source(id); | 
|  |  | 
|  | if (data.size() <= sizeof(uint64_t)) { | 
|  | uint64_t int_value = 0; | 
|  | memcpy(&int_value, &data[0], data.size()); | 
|  |  | 
|  | // Use the types defined by ExprValue for the unsigned number of the corresponding size. | 
|  | // Passing a null type will cause ExprValue to create one matching the input type. | 
|  | switch (data.size()) { | 
|  | case 1: | 
|  | return ExprValue(static_cast<uint8_t>(int_value), fxl::RefPtr<Type>(), source); | 
|  | case 2: | 
|  | return ExprValue(static_cast<uint16_t>(int_value), fxl::RefPtr<Type>(), source); | 
|  | case 4: | 
|  | return ExprValue(static_cast<uint32_t>(int_value), fxl::RefPtr<Type>(), source); | 
|  | case 8: | 
|  | return ExprValue(static_cast<uint64_t>(int_value), fxl::RefPtr<Type>(), source); | 
|  | } | 
|  | } | 
|  |  | 
|  | // Large and/or weird sized registers. | 
|  | const char* type_name; | 
|  | if (data.size() == sizeof(uint128_t)) | 
|  | type_name = "uint128_t"; | 
|  | else | 
|  | type_name = "(register data)"; | 
|  |  | 
|  | return ExprValue( | 
|  | fxl::MakeRefCounted<BaseType>(BaseType::kBaseTypeUnsigned, data.size(), type_name), | 
|  | std::vector<uint8_t>(data.begin(), data.end()), source); | 
|  | } | 
|  |  | 
|  | }  // namespace | 
|  |  | 
|  | EvalContextImpl::EvalContextImpl(fxl::WeakPtr<const ProcessSymbols> process_symbols, | 
|  | fxl::RefPtr<SymbolDataProvider> data_provider, | 
|  | ExprLanguage language, fxl::RefPtr<CodeBlock> code_block) | 
|  | : process_symbols_(std::move(process_symbols)), | 
|  | data_provider_(data_provider), | 
|  | block_(std::move(code_block)), | 
|  | language_(language), | 
|  | weak_factory_(this) {} | 
|  |  | 
|  | EvalContextImpl::EvalContextImpl(fxl::WeakPtr<const ProcessSymbols> process_symbols, | 
|  | fxl::RefPtr<SymbolDataProvider> data_provider, | 
|  | const Location& location, | 
|  | std::optional<ExprLanguage> force_language) | 
|  | : process_symbols_(std::move(process_symbols)), | 
|  | data_provider_(data_provider), | 
|  | weak_factory_(this) { | 
|  | const CodeBlock* function = nullptr; | 
|  | if (location.symbol()) | 
|  | function = location.symbol().Get()->AsCodeBlock(); | 
|  |  | 
|  | if (function) { | 
|  | block_ = | 
|  | RefPtrTo(function->GetMostSpecificChild(location.symbol_context(), location.address())); | 
|  | } | 
|  |  | 
|  | if (force_language) { | 
|  | language_ = *force_language; | 
|  | } else if (function) { | 
|  | // Extract the language for the code if possible. | 
|  | if (auto unit = function->GetCompileUnit()) | 
|  | language_ = DwarfLangToExprLanguage(unit->language()); | 
|  | } | 
|  | } | 
|  |  | 
|  | EvalContextImpl::~EvalContextImpl() = default; | 
|  |  | 
|  | ExprLanguage EvalContextImpl::GetLanguage() const { return language_; } | 
|  |  | 
|  | FindNameContext EvalContextImpl::GetFindNameContext() const { | 
|  | // The symbol context for the current location is passed to the FindNameContext to prioritize | 
|  | // the current module's values when searching for variables. If relative, this will be ignored. | 
|  | SymbolContext symbol_context = SymbolContext::ForRelativeAddresses(); | 
|  | if (block_ && process_symbols_) | 
|  | symbol_context = block_->GetSymbolContext(process_symbols_.get()); | 
|  | return FindNameContext(process_symbols_.get(), symbol_context, block_.get()); | 
|  | } | 
|  |  | 
|  | void EvalContextImpl::GetNamedValue(const ParsedIdentifier& identifier, EvalCallback cb) const { | 
|  | if (FoundName found = | 
|  | FindName(GetFindNameContext(), FindNameOptions(FindNameOptions::kAllKinds), identifier)) { | 
|  | switch (found.kind()) { | 
|  | case FoundName::kVariable: | 
|  | case FoundName::kMemberVariable: | 
|  | DoResolve(std::move(found), std::move(cb)); | 
|  | return; | 
|  | case FoundName::kNamespace: | 
|  | cb(Err("Can not evaluate a namespace.")); | 
|  | return; | 
|  | case FoundName::kTemplate: | 
|  | cb(Err("Can not evaluate a template with no parameters.")); | 
|  | return; | 
|  | case FoundName::kType: | 
|  | cb(Err("Can not evaluate a type.")); | 
|  | return; | 
|  | case FoundName::kFunction: | 
|  | case FoundName::kOtherSymbol: | 
|  | break;  // Function pointers not supported yet. | 
|  | case FoundName::kNone: | 
|  | break;  // Fall through to checking other stuff. | 
|  | } | 
|  | } | 
|  |  | 
|  | auto reg = GetRegisterID(identifier); | 
|  | if (reg == RegisterID::kUnknown || GetArchForRegisterID(reg) != data_provider_->GetArch()) | 
|  | return cb(Err("No variable '%s' found.", identifier.GetFullName().c_str())); | 
|  |  | 
|  | // Fall back to matching registers when no symbol is found. | 
|  | if (std::optional<containers::array_view<uint8_t>> opt_reg_data = | 
|  | data_provider_->GetRegister(reg)) { | 
|  | // Available synchronously. | 
|  | if (opt_reg_data->empty()) | 
|  | cb(GetUnavailableRegisterErr(reg)); | 
|  | else | 
|  | cb(RegisterDataToValue(reg, GetVectorRegisterFormat(), *opt_reg_data)); | 
|  | } else { | 
|  | data_provider_->GetRegisterAsync( | 
|  | reg, [reg, vector_fmt = GetVectorRegisterFormat(), cb = std::move(cb)]( | 
|  | const Err& err, std::vector<uint8_t> value) mutable { | 
|  | if (err.has_error()) { | 
|  | cb(err); | 
|  | } else if (value.empty()) { | 
|  | cb(GetUnavailableRegisterErr(reg)); | 
|  | } else { | 
|  | cb(RegisterDataToValue(reg, vector_fmt, value)); | 
|  | } | 
|  | }); | 
|  | } | 
|  | } | 
|  |  | 
|  | void EvalContextImpl::GetVariableValue(fxl::RefPtr<Value> input_val, EvalCallback cb) const { | 
|  | // Handle const values. | 
|  | if (input_val->const_value().has_value()) | 
|  | return cb(ResolveConstValue(RefPtrTo(this), input_val.get())); | 
|  |  | 
|  | fxl::RefPtr<Variable> var; | 
|  | if (input_val->is_external()) { | 
|  | // Convert extern Variables and DataMembers to the actual variable memory. | 
|  | if (Err err = ResolveExternValue(input_val, &var); err.has_error()) | 
|  | return cb(err); | 
|  | } else { | 
|  | // Everything else should be a variable. | 
|  | var = RefPtrTo(input_val->AsVariable()); | 
|  | FX_DCHECK(var); | 
|  | } | 
|  |  | 
|  | SymbolContext symbol_context = var->GetSymbolContext(process_symbols_.get()); | 
|  |  | 
|  | // Need to explicitly take a reference to the type. | 
|  | fxl::RefPtr<Type> type = RefPtrTo(var->type().Get()->AsType()); | 
|  | if (!type) | 
|  | return cb(Err("Missing type information.")); | 
|  |  | 
|  | std::optional<containers::array_view<uint8_t>> ip_data = | 
|  | data_provider_->GetRegister(debug_ipc::GetSpecialRegisterID( | 
|  | data_provider_->GetArch(), debug_ipc::SpecialRegisterType::kIP)); | 
|  | TargetPointer ip; | 
|  | if (!ip_data || ip_data->size() != sizeof(ip))  // The IP should never require an async call. | 
|  | return cb(Err("No location available.")); | 
|  | memcpy(&ip, &(*ip_data)[0], ip_data->size()); | 
|  |  | 
|  | const VariableLocation::Entry* loc_entry = var->location().EntryForIP(symbol_context, ip); | 
|  | if (!loc_entry) { | 
|  | // No DWARF location applies to the current instruction pointer. | 
|  | const char* err_str; | 
|  | if (var->location().is_null()) { | 
|  | // With no locations, this variable has been completely optimized out. | 
|  | err_str = "Optimized out"; | 
|  | } else { | 
|  | // There are locations but none of them match the current IP. | 
|  | err_str = "Unavailable"; | 
|  | } | 
|  | return cb(Err(ErrType::kOptimizedOut, err_str)); | 
|  | } | 
|  |  | 
|  | // Schedule the expression to be evaluated. | 
|  | auto evaluator = fxl::MakeRefCounted<AsyncDwarfExprEval>(std::move(cb), std::move(type)); | 
|  | evaluator->Eval(RefPtrTo(this), symbol_context, loc_entry->expression); | 
|  | } | 
|  |  | 
|  | const ProcessSymbols* EvalContextImpl::GetProcessSymbols() const { | 
|  | if (!process_symbols_) | 
|  | return nullptr; | 
|  | return process_symbols_.get(); | 
|  | } | 
|  |  | 
|  | fxl::RefPtr<SymbolDataProvider> EvalContextImpl::GetDataProvider() { return data_provider_; } | 
|  |  | 
|  | NameLookupCallback EvalContextImpl::GetSymbolNameLookupCallback() { | 
|  | // The contract for this function is that the callback must not be stored | 
|  | // so the callback can reference |this|. | 
|  | return [this](const ParsedIdentifier& ident, const FindNameOptions& opts) -> FoundName { | 
|  | // Look up the symbols in the symbol table if possible. | 
|  | FoundName result = FindName(GetFindNameContext(), opts, ident); | 
|  |  | 
|  | // Fall back on builtin types. | 
|  | if (result.kind() == FoundName::kNone && opts.find_types) { | 
|  | if (auto type = GetBuiltinType(language_, ident.GetFullName())) | 
|  | return FoundName(std::move(type)); | 
|  | } | 
|  | return result; | 
|  | }; | 
|  | } | 
|  |  | 
|  | Location EvalContextImpl::GetLocationForAddress(uint64_t address) const { | 
|  | if (!process_symbols_) | 
|  | return Location(Location::State::kAddress, address);  // Can't symbolize. | 
|  |  | 
|  | auto locations = process_symbols_->ResolveInputLocation(InputLocation(address)); | 
|  |  | 
|  | // Given an exact address, ResolveInputLocation() should only return one result. | 
|  | FX_DCHECK(locations.size() == 1u); | 
|  | return locations[0]; | 
|  | } | 
|  |  | 
|  | Err EvalContextImpl::ResolveExternValue(const fxl::RefPtr<Value>& input_value, | 
|  | fxl::RefPtr<Variable>* resolved) const { | 
|  | FX_DCHECK(input_value->is_external()); | 
|  |  | 
|  | FindNameOptions options(FindNameOptions::kNoKinds); | 
|  | options.find_vars = true; | 
|  |  | 
|  | // Passing a null block in the FindNameContext will bypass searching the current scope and | 
|  | // "this" object and instead only search global names. This is what we want since the extern | 
|  | // Value name will be fully qualified. | 
|  | FindNameContext context = GetFindNameContext(); | 
|  | context.block = nullptr; | 
|  |  | 
|  | FoundName found = FindName(context, options, ToParsedIdentifier(input_value->GetIdentifier())); | 
|  | if (!found || !found.variable()) | 
|  | return Err("Extern variable '%s' not found.", input_value->GetFullName().c_str()); | 
|  |  | 
|  | *resolved = found.variable_ref(); | 
|  | return Err(); | 
|  | } | 
|  |  | 
|  | void EvalContextImpl::DoResolve(FoundName found, EvalCallback cb) const { | 
|  | if (found.kind() == FoundName::kVariable) { | 
|  | // Simple variable resolution. | 
|  | GetVariableValue(found.variable_ref(), std::move(cb)); | 
|  | return; | 
|  | } | 
|  |  | 
|  | // Everything below here is an object variable resolution. | 
|  | FX_DCHECK(found.kind() == FoundName::kMemberVariable); | 
|  |  | 
|  | // Static ("external") data members don't require a "this" pointer. | 
|  | if (found.member().data_member()->is_external()) | 
|  | return GetVariableValue(RefPtrTo(found.member().data_member()), std::move(cb)); | 
|  |  | 
|  | // Get the value of of the |this| variable to resolve. | 
|  | GetVariableValue(found.object_ptr_ref(), [weak_this = weak_factory_.GetWeakPtr(), found, | 
|  | cb = std::move(cb)](ErrOrValue value) mutable { | 
|  | if (!weak_this) | 
|  | return;  // Don't issue callbacks if we've been destroyed. | 
|  |  | 
|  | if (value.has_error())  // |this| not available, probably optimized out. | 
|  | return cb(value); | 
|  |  | 
|  | // Got |this|, resolve |this-><DataMember>|. | 
|  | // | 
|  | // Here we do not support automatically converting a base class pointer to a derived class if | 
|  | // we can. First, that's more difficult to implement because it requires asynchronously | 
|  | // computing the derived class based on |this|'s vtable pointer. Second, it's not linguistically | 
|  | // in scope and it could be surprising, especially if it shadows another value. The user can | 
|  | // always do "this->foo" to expicitly request the conversion if enabled. | 
|  | ResolveMemberByPointer(fxl::RefPtr<EvalContextImpl>(weak_this.get()), value.value(), | 
|  | found.member(), | 
|  | [weak_this, found, cb = std::move(cb)](ErrOrValue value) mutable { | 
|  | if (weak_this) { | 
|  | // Only issue callbacks if we're still alive. | 
|  | cb(std::move(value)); | 
|  | } | 
|  | }); | 
|  | }); | 
|  | } | 
|  |  | 
|  | FoundName EvalContextImpl::DoTargetSymbolsNameLookup(const ParsedIdentifier& ident) { | 
|  | return FindName(GetFindNameContext(), FindNameOptions(FindNameOptions::kAllKinds), ident); | 
|  | } | 
|  |  | 
|  | }  // namespace zxdb |