Coverage Report

Created: 2023-09-30 09:22

/Users/buildslave/jenkins/workspace/coverage/llvm-project/lldb/source/Plugins/Language/CPlusPlus/Coroutines.cpp
Line
Count
Source (jump to first uncovered line)
1
//===-- Coroutines.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-exception
6
//
7
//===----------------------------------------------------------------------===//
8
9
#include "Coroutines.h"
10
11
#include "Plugins/TypeSystem/Clang/TypeSystemClang.h"
12
#include "lldb/Symbol/Function.h"
13
#include "lldb/Symbol/VariableList.h"
14
15
using namespace lldb;
16
using namespace lldb_private;
17
using namespace lldb_private::formatters;
18
19
68
static lldb::addr_t GetCoroFramePtrFromHandle(ValueObjectSP valobj_sp) {
20
68
  if (!valobj_sp)
21
0
    return LLDB_INVALID_ADDRESS;
22
23
  // We expect a single pointer in the `coroutine_handle` class.
24
  // We don't care about its name.
25
68
  if (valobj_sp->GetNumChildren() != 1)
26
0
    return LLDB_INVALID_ADDRESS;
27
68
  ValueObjectSP ptr_sp(valobj_sp->GetChildAtIndex(0));
28
68
  if (!ptr_sp)
29
0
    return LLDB_INVALID_ADDRESS;
30
68
  if (!ptr_sp->GetCompilerType().IsPointerType())
31
0
    return LLDB_INVALID_ADDRESS;
32
33
68
  AddressType addr_type;
34
68
  lldb::addr_t frame_ptr_addr = ptr_sp->GetPointerValue(&addr_type);
35
68
  if (!frame_ptr_addr || frame_ptr_addr == LLDB_INVALID_ADDRESS)
36
0
    return LLDB_INVALID_ADDRESS;
37
68
  lldbassert(addr_type == AddressType::eAddressTypeLoad);
38
68
  if (addr_type != AddressType::eAddressTypeLoad)
39
0
    return LLDB_INVALID_ADDRESS;
40
41
68
  return frame_ptr_addr;
42
68
}
43
44
static Function *ExtractDestroyFunction(lldb::TargetSP target_sp,
45
8
                                        lldb::addr_t frame_ptr_addr) {
46
8
  lldb::ProcessSP process_sp = target_sp->GetProcessSP();
47
8
  auto ptr_size = process_sp->GetAddressByteSize();
48
49
8
  Status error;
50
8
  auto destroy_func_ptr_addr = frame_ptr_addr + ptr_size;
51
8
  lldb::addr_t destroy_func_addr =
52
8
      process_sp->ReadPointerFromMemory(destroy_func_ptr_addr, error);
53
8
  if (error.Fail())
54
0
    return nullptr;
55
56
8
  Address destroy_func_address;
57
8
  if (!target_sp->ResolveLoadAddress(destroy_func_addr, destroy_func_address))
58
0
    return nullptr;
59
60
8
  return destroy_func_address.CalculateSymbolContextFunction();
61
8
}
62
63
8
static CompilerType InferPromiseType(Function &destroy_func) {
64
8
  Block &block = destroy_func.GetBlock(true);
65
8
  auto variable_list = block.GetBlockVariableList(true);
66
67
  // clang generates an artificial `__promise` variable inside the
68
  // `destroy` function. Look for it.
69
8
  auto promise_var = variable_list->FindVariable(ConstString("__promise"));
70
8
  if (!promise_var)
71
0
    return {};
72
8
  if (!promise_var->IsArtificial())
73
0
    return {};
74
75
8
  Type *promise_type = promise_var->GetType();
76
8
  if (!promise_type)
77
0
    return {};
78
8
  return promise_type->GetForwardCompilerType();
79
8
}
80
81
bool lldb_private::formatters::StdlibCoroutineHandleSummaryProvider(
82
44
    ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) {
83
44
  lldb::addr_t frame_ptr_addr =
84
44
      GetCoroFramePtrFromHandle(valobj.GetNonSyntheticValue());
85
44
  if (frame_ptr_addr == LLDB_INVALID_ADDRESS)
86
0
    return false;
87
88
44
  if (frame_ptr_addr == 0) {
89
0
    stream << "nullptr";
90
44
  } else {
91
44
    stream.Printf("coro frame = 0x%" PRIx64, frame_ptr_addr);
92
44
  }
93
94
44
  return true;
95
44
}
96
97
lldb_private::formatters::StdlibCoroutineHandleSyntheticFrontEnd::
98
    StdlibCoroutineHandleSyntheticFrontEnd(lldb::ValueObjectSP valobj_sp)
99
12
    : SyntheticChildrenFrontEnd(*valobj_sp) {
100
12
  if (valobj_sp)
101
12
    Update();
102
12
}
103
104
lldb_private::formatters::StdlibCoroutineHandleSyntheticFrontEnd::
105
12
    ~StdlibCoroutineHandleSyntheticFrontEnd() = default;
106
107
size_t lldb_private::formatters::StdlibCoroutineHandleSyntheticFrontEnd::
108
12
    CalculateNumChildren() {
109
12
  if (!m_resume_ptr_sp || !m_destroy_ptr_sp)
110
0
    return 0;
111
112
12
  return m_promise_ptr_sp ? 3 : 
20
;
113
12
}
114
115
lldb::ValueObjectSP lldb_private::formatters::
116
36
    StdlibCoroutineHandleSyntheticFrontEnd::GetChildAtIndex(size_t idx) {
117
36
  switch (idx) {
118
12
  case 0:
119
12
    return m_resume_ptr_sp;
120
12
  case 1:
121
12
    return m_destroy_ptr_sp;
122
12
  case 2:
123
12
    return m_promise_ptr_sp;
124
36
  }
125
0
  return lldb::ValueObjectSP();
126
36
}
127
128
bool lldb_private::formatters::StdlibCoroutineHandleSyntheticFrontEnd::
129
24
    Update() {
130
24
  m_resume_ptr_sp.reset();
131
24
  m_destroy_ptr_sp.reset();
132
24
  m_promise_ptr_sp.reset();
133
134
24
  ValueObjectSP valobj_sp = m_backend.GetNonSyntheticValue();
135
24
  if (!valobj_sp)
136
0
    return false;
137
138
24
  lldb::addr_t frame_ptr_addr = GetCoroFramePtrFromHandle(valobj_sp);
139
24
  if (frame_ptr_addr == 0 || frame_ptr_addr == LLDB_INVALID_ADDRESS)
140
0
    return false;
141
142
24
  auto ts = valobj_sp->GetCompilerType().GetTypeSystem();
143
24
  auto ast_ctx = ts.dyn_cast_or_null<TypeSystemClang>();
144
24
  if (!ast_ctx)
145
0
    return false;
146
147
  // Create the `resume` and `destroy` children.
148
24
  lldb::TargetSP target_sp = m_backend.GetTargetSP();
149
24
  auto &exe_ctx = m_backend.GetExecutionContextRef();
150
24
  lldb::ProcessSP process_sp = target_sp->GetProcessSP();
151
24
  auto ptr_size = process_sp->GetAddressByteSize();
152
24
  CompilerType void_type = ast_ctx->GetBasicType(lldb::eBasicTypeVoid);
153
24
  CompilerType coro_func_type = ast_ctx->CreateFunctionType(
154
24
      /*result_type=*/void_type, /*args=*/&void_type, /*num_args=*/1,
155
24
      /*is_variadic=*/false, /*qualifiers=*/0);
156
24
  CompilerType coro_func_ptr_type = coro_func_type.GetPointerType();
157
24
  m_resume_ptr_sp = CreateValueObjectFromAddress(
158
24
      "resume", frame_ptr_addr + 0 * ptr_size, exe_ctx, coro_func_ptr_type);
159
24
  lldbassert(m_resume_ptr_sp);
160
24
  m_destroy_ptr_sp = CreateValueObjectFromAddress(
161
24
      "destroy", frame_ptr_addr + 1 * ptr_size, exe_ctx, coro_func_ptr_type);
162
24
  lldbassert(m_destroy_ptr_sp);
163
164
  // Get the `promise_type` from the template argument
165
24
  CompilerType promise_type(
166
24
      valobj_sp->GetCompilerType().GetTypeTemplateArgument(0));
167
24
  if (!promise_type)
168
0
    return false;
169
170
  // Try to infer the promise_type if it was type-erased
171
24
  if (promise_type.IsVoidType()) {
172
8
    if (Function *destroy_func =
173
8
            ExtractDestroyFunction(target_sp, frame_ptr_addr)) {
174
8
      if (CompilerType inferred_type = InferPromiseType(*destroy_func)) {
175
8
        promise_type = inferred_type;
176
8
      }
177
8
    }
178
8
  }
179
180
  // If we don't know the promise type, we don't display the `promise` member.
181
  // `CreateValueObjectFromAddress` below would fail for `void` types.
182
24
  if (promise_type.IsVoidType()) {
183
0
    return false;
184
0
  }
185
186
  // Add the `promise` member. We intentionally add `promise` as a pointer type
187
  // instead of a value type, and don't automatically dereference this pointer.
188
  // We do so to avoid potential very deep recursion in case there is a cycle
189
  // formed between `std::coroutine_handle`s and their promises.
190
24
  lldb::ValueObjectSP promise = CreateValueObjectFromAddress(
191
24
      "promise", frame_ptr_addr + 2 * ptr_size, exe_ctx, promise_type);
192
24
  Status error;
193
24
  lldb::ValueObjectSP promisePtr = promise->AddressOf(error);
194
24
  if (error.Success())
195
24
    m_promise_ptr_sp = promisePtr->Clone(ConstString("promise"));
196
197
24
  return false;
198
24
}
199
200
bool lldb_private::formatters::StdlibCoroutineHandleSyntheticFrontEnd::
201
0
    MightHaveChildren() {
202
0
  return true;
203
0
}
204
205
size_t StdlibCoroutineHandleSyntheticFrontEnd::GetIndexOfChildWithName(
206
0
    ConstString name) {
207
0
  if (!m_resume_ptr_sp || !m_destroy_ptr_sp)
208
0
    return UINT32_MAX;
209
210
0
  if (name == ConstString("resume"))
211
0
    return 0;
212
0
  if (name == ConstString("destroy"))
213
0
    return 1;
214
0
  if (name == ConstString("promise_ptr") && m_promise_ptr_sp)
215
0
    return 2;
216
217
0
  return UINT32_MAX;
218
0
}
219
220
SyntheticChildrenFrontEnd *
221
lldb_private::formatters::StdlibCoroutineHandleSyntheticFrontEndCreator(
222
12
    CXXSyntheticChildren *, lldb::ValueObjectSP valobj_sp) {
223
12
  return (valobj_sp ? new StdlibCoroutineHandleSyntheticFrontEnd(valobj_sp)
224
12
                    : 
nullptr0
);
225
12
}