File: StackID.cpp

package info (click to toggle)
swiftlang 6.2.3-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 2,856,264 kB
  • sloc: cpp: 9,995,718; ansic: 2,234,019; asm: 1,092,167; python: 313,940; objc: 82,726; f90: 80,126; lisp: 38,373; pascal: 25,580; sh: 20,378; ml: 5,058; perl: 4,751; makefile: 4,725; awk: 3,535; javascript: 3,018; xml: 918; fortran: 664; cs: 573; ruby: 396
file content (213 lines) | stat: -rw-r--r-- 8,238 bytes parent folder | download
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;
}