1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213
|
//===-- StackID.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/StackID.h"
#include "lldb/Symbol/Block.h"
#include "lldb/Symbol/Symbol.h"
#include "lldb/Symbol/SymbolContext.h"
#include "lldb/Target/MemoryRegionInfo.h"
#include "lldb/Target/Process.h"
#include "lldb/Target/Thread.h"
#include "lldb/Utility/LLDBLog.h"
#include "lldb/Target/Process.h"
#include "lldb/Utility/Stream.h"
using namespace lldb_private;
bool StackID::IsCFAOnStack(Process &process) const {
if (m_cfa_on_stack == eLazyBoolCalculate) {
// Conservatively assume stack memory
m_cfa_on_stack = eLazyBoolYes;
if (m_cfa != LLDB_INVALID_ADDRESS) {
MemoryRegionInfo mem_info;
if (process.GetMemoryRegionInfo(m_cfa, mem_info).Success())
if (mem_info.IsStackMemory() == MemoryRegionInfo::eNo)
m_cfa_on_stack = eLazyBoolNo;
}
}
return m_cfa_on_stack == eLazyBoolYes;
}
StackID::StackID(lldb::addr_t pc, lldb::addr_t cfa,
SymbolContextScope *symbol_scope, Process *process)
: m_pc(pc), m_cfa(cfa), m_cfa_with_metadata(cfa),
m_symbol_scope(symbol_scope) {
if (process) {
m_pc = process->FixCodeAddress(m_pc);
m_cfa = process->FixDataAddress(m_cfa);
}
}
void StackID::SetPC(lldb::addr_t pc, Process *process) {
m_pc = process ? process->FixCodeAddress(pc) : pc;
}
void StackID::SetCFA(lldb::addr_t cfa, Process *process) {
m_cfa_with_metadata = cfa;
m_cfa = process ? process->FixDataAddress(cfa) : cfa;
}
void StackID::Dump(Stream *s) {
s->Printf("StackID (pc = 0x%16.16" PRIx64 ", cfa = 0x%16.16" PRIx64
", cfa_on_stack = %d, symbol_scope = %p",
m_pc, m_cfa, m_cfa_on_stack, static_cast<void *>(m_symbol_scope));
if (m_symbol_scope) {
SymbolContext sc;
m_symbol_scope->CalculateSymbolContext(&sc);
if (sc.block)
s->Printf(" (Block {0x%8.8" PRIx64 "})", sc.block->GetID());
else if (sc.symbol)
s->Printf(" (Symbol{0x%8.8x})", sc.symbol->GetID());
}
s->PutCString(") ");
}
bool lldb_private::operator==(const StackID &lhs, const StackID &rhs) {
if (lhs.GetCallFrameAddressWithoutMetadata() !=
rhs.GetCallFrameAddressWithoutMetadata())
return false;
SymbolContextScope *lhs_scope = lhs.GetSymbolContextScope();
SymbolContextScope *rhs_scope = rhs.GetSymbolContextScope();
// Only compare the PC values if both symbol context scopes are nullptr
if (lhs_scope == nullptr && rhs_scope == nullptr)
return lhs.GetPC() == rhs.GetPC();
return lhs_scope == rhs_scope;
}
bool lldb_private::operator!=(const StackID &lhs, const StackID &rhs) {
return !(lhs == rhs);
}
// BEGIN SWIFT
/// Given two async contexts, source and maybe_parent, chase continuation
/// pointers to check if maybe_parent can be reached from source. The search
/// stops when it hits the end of the chain (parent_ctx == 0) or a safety limit
/// in case of an invalid continuation chain.
static llvm::Expected<bool> IsReachableParent(lldb::addr_t source,
lldb::addr_t maybe_parent,
Process &process) {
maybe_parent = process.FixDataAddress(maybe_parent);
auto max_num_frames = 512;
for (lldb::addr_t parent_ctx = source; parent_ctx && max_num_frames;
max_num_frames--) {
Status error;
lldb::addr_t old_parent_ctx = parent_ctx;
// The continuation's context is the first field of an async context.
parent_ctx = process.ReadPointerFromMemory(old_parent_ctx, error);
if (error.Fail())
return llvm::createStringError(llvm::formatv(
"Failed to read parent async context of: {0:x}. Error: {1}",
old_parent_ctx, error.AsCString()));
if (process.FixDataAddress(parent_ctx) == maybe_parent)
return true;
}
if (max_num_frames == 0)
return llvm::createStringError(
llvm::formatv("Failed to read continuation chain from {0:x} to "
"possible parent {1:x}. Reached limit of frames.",
source, maybe_parent));
return false;
}
enum class HeapCFAComparisonResult { Younger, Older, NoOpinion };
/// If at least one of the stack IDs (lhs, rhs) is a heap CFA, perform the
/// swift-specific async frame comparison. Otherwise, returns NoOpinion.
static HeapCFAComparisonResult
CompareHeapCFAs(const StackID &lhs, const StackID &rhs, Process &process) {
const bool lhs_cfa_on_stack = lhs.IsCFAOnStack(process);
const bool rhs_cfa_on_stack = rhs.IsCFAOnStack(process);
if (lhs_cfa_on_stack && rhs_cfa_on_stack)
return HeapCFAComparisonResult::NoOpinion;
// If one of the frames has a CFA on the stack and the other doesn't, we are
// at the boundary between an asynchronous and a synchronous function.
// Synchronous functions cannot call asynchronous functions, therefore the
// synchronous frame is always younger.
if (lhs_cfa_on_stack && !rhs_cfa_on_stack)
return HeapCFAComparisonResult::Younger;
if (!lhs_cfa_on_stack && rhs_cfa_on_stack)
return HeapCFAComparisonResult::Older;
const lldb::addr_t lhs_cfa = lhs.GetCallFrameAddressWithoutMetadata();
const lldb::addr_t rhs_cfa = rhs.GetCallFrameAddressWithoutMetadata();
// If the cfas are the same, fallback to the usual scope comparison.
if (lhs_cfa == rhs_cfa)
return HeapCFAComparisonResult::NoOpinion;
// Both CFAs are on the heap and they are distinct.
// LHS is younger if and only if its continuation async context is (directly
// or indirectly) RHS.
llvm::Expected<bool> lhs_younger =
IsReachableParent(lhs_cfa, rhs_cfa, process);
if (auto E = lhs_younger.takeError())
LLDB_LOG_ERROR(GetLog(LLDBLog::Unwind), std::move(E), "{0}");
else if (*lhs_younger)
return HeapCFAComparisonResult::Younger;
llvm::Expected<bool> lhs_older = IsReachableParent(rhs_cfa, lhs_cfa, process);
if (auto E = lhs_older.takeError())
LLDB_LOG_ERROR(GetLog(LLDBLog::Unwind), std::move(E), "{0}");
else if (*lhs_older)
return HeapCFAComparisonResult::Older;
return HeapCFAComparisonResult::NoOpinion;
}
// END SWIFT
bool StackID::IsYounger(const StackID &lhs, const StackID &rhs,
Process &process) {
// BEGIN SWIFT
switch (CompareHeapCFAs(lhs, rhs, process)) {
case HeapCFAComparisonResult::Younger:
return true;
case HeapCFAComparisonResult::Older:
return false;
case HeapCFAComparisonResult::NoOpinion:
break;
}
// END SWIFT
//
const lldb::addr_t lhs_cfa = lhs.GetCallFrameAddressWithoutMetadata();
const lldb::addr_t rhs_cfa = rhs.GetCallFrameAddressWithoutMetadata();
// FIXME: We are assuming that the stacks grow downward in memory. That's not
// necessary, but true on
// all the machines we care about at present. If this changes, we'll have to
// deal with that. The ABI is the agent who knows this ordering, but the
// StackID has no access to the ABI. The most straightforward way to handle
// this is to add a "m_grows_downward" bool to the StackID, and set it in the
// constructor. But I'm not going to waste a bool per StackID on this till we
// need it.
if (lhs_cfa != rhs_cfa)
return lhs_cfa < rhs_cfa;
SymbolContextScope *lhs_scope = lhs.GetSymbolContextScope();
SymbolContextScope *rhs_scope = rhs.GetSymbolContextScope();
if (lhs_scope != nullptr && rhs_scope != nullptr) {
// Same exact scope, lhs is not less than (younger than rhs)
if (lhs_scope == rhs_scope)
return false;
SymbolContext lhs_sc;
SymbolContext rhs_sc;
lhs_scope->CalculateSymbolContext(&lhs_sc);
rhs_scope->CalculateSymbolContext(&rhs_sc);
// Items with the same function can only be compared
if (lhs_sc.function == rhs_sc.function && lhs_sc.function != nullptr &&
lhs_sc.block != nullptr && rhs_sc.function != nullptr &&
rhs_sc.block != nullptr) {
return rhs_sc.block->Contains(lhs_sc.block);
}
}
return false;
}
|