aboutsummaryrefslogtreecommitdiff
path: root/lldb/unittests/ValueObject/DynamicValueObjectLocalBuffer.cpp
blob: 0f3d2d2ba9d68da2f894a20b16ac85abd58128be (plain)
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
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
//===---DynamicValueObjectLocalBuffer.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 "Plugins/Platform/Linux/PlatformLinux.h"
#include "Plugins/ScriptInterpreter/None/ScriptInterpreterNone.h"
#include "Plugins/TypeSystem/Clang/TypeSystemClang.h"
#include "TestingSupport/SubsystemRAII.h"
#include "TestingSupport/Symbol/ClangTestUtils.h"
#include "lldb/Core/Debugger.h"
#include "lldb/Core/PluginManager.h"
#include "lldb/Target/Language.h"
#include "lldb/Target/LanguageRuntime.h"
#include "lldb/ValueObject/ValueObject.h"
#include "lldb/ValueObject/ValueObjectConstResult.h"

#include "gtest/gtest.h"

using namespace lldb;
using namespace lldb_private;
using namespace lldb_private::clang_utils;

// This entire class is boilerplate.
struct MockLanguage : public Language {

  llvm::StringRef GetPluginName() override { return "MockLanguage"; }
  lldb::LanguageType GetLanguageType() const override {
    return lldb::eLanguageTypeC_plus_plus;
  };

  static Language *CreateInstance(lldb::LanguageType language) {
    return new MockLanguage();
  }
  static void Initialize() {
    PluginManager::RegisterPlugin("MockLanguage", "Mock Language",
                                  CreateInstance);
  };

  static void Terminate() { PluginManager::UnregisterPlugin(CreateInstance); }
  bool IsSourceFile(llvm::StringRef file_path) const override { return true; }
};
LLDB_PLUGIN_DEFINE(MockLanguage)

struct MockLanguageRuntime : public LanguageRuntime {
  // This is the only method in this class that matters for this test.
  // This will unconditionally succeed and return a type with size 4,
  // a value_type of HostAddress, and a local buffer that points to the parent's
  // local buffer.
  // The tests will set that buffer to be either be larger or smaller than the
  // type we're returning.
  bool
  GetDynamicTypeAndAddress(ValueObject &in_value,
                           lldb::DynamicValueType use_dynamic,
                           TypeAndOrName &class_type_or_name, Address &address,
                           Value::ValueType &value_type,
                           llvm::ArrayRef<uint8_t> &local_buffer) override {
    auto ast = in_value.GetCompilerType().GetTypeSystem<TypeSystemClang>();

    auto int_type = createRecordWithField(
        *ast, "TypeWitInt", ast->GetBasicType(lldb::BasicType::eBasicTypeInt),
        "theIntField", LanguageType::eLanguageTypeC_plus_plus);
    class_type_or_name.SetCompilerType(int_type);
    local_buffer = in_value.GetLocalBuffer();
    value_type = Value::ValueType::HostAddress;
    return true;
  }

  // All of this is boilerplate.
  MockLanguageRuntime(Process *process) : LanguageRuntime(process) {}
  llvm::StringRef GetPluginName() override { return "MockLanguageRuntime"; }
  lldb::LanguageType GetLanguageType() const override {
    return lldb::eLanguageTypeC_plus_plus;
  }

  llvm::Error GetObjectDescription(Stream &str, ValueObject &object) override {
    return llvm::Error::success();
  }

  llvm::Error GetObjectDescription(Stream &str, Value &value,
                                   ExecutionContextScope *exe_scope) override {
    return llvm::Error::success();
  }

  bool CouldHaveDynamicValue(ValueObject &in_value) override { return true; }

  TypeAndOrName FixUpDynamicType(const TypeAndOrName &type_and_or_name,
                                 ValueObject &static_value) override {
    return type_and_or_name;
  }

  lldb::BreakpointResolverSP
  CreateExceptionResolver(const lldb::BreakpointSP &bkpt, bool catch_bp,
                          bool throw_bp) override {
    return lldb::BreakpointResolverSP();
  }

  lldb::ThreadPlanSP GetStepThroughTrampolinePlan(Thread &thread,
                                                  bool stop_others) override {
    return {};
  }

  static LanguageRuntime *CreateInstance(Process *process,
                                         LanguageType language) {
    return new MockLanguageRuntime(process);
  }

  static void Initialize() {
    PluginManager::RegisterPlugin(
        "MockLanguageRuntime", "MockLanguageRuntime", CreateInstance,
        [](CommandInterpreter &interpreter) -> lldb::CommandObjectSP {
          return {};
        },
        [](lldb::LanguageType language,
           bool throw_bp) -> BreakpointPreconditionSP { return {}; });
  }

  static void Terminate() { PluginManager::UnregisterPlugin(CreateInstance); }
};
LLDB_PLUGIN_DEFINE(MockLanguageRuntime)

// This entire class is boilerplate.
struct MockProcess : Process {
  MockProcess(lldb::TargetSP target_sp, lldb::ListenerSP listener_sp)
      : Process(target_sp, listener_sp) {}

  llvm::StringRef GetPluginName() override { return "mock process"; }

  bool CanDebug(lldb::TargetSP target, bool plugin_specified_by_name) override {
    return false;
  };

  Status DoDestroy() override { return {}; }

  void RefreshStateAfterStop() override {}

  bool DoUpdateThreadList(ThreadList &old_thread_list,
                          ThreadList &new_thread_list) override {
    return false;
  };

  size_t DoReadMemory(lldb::addr_t vm_addr, void *buf, size_t size,
                      Status &error) override {
    // No need to read memory in these tests.
    return size;
  }
};

class DynamicValueObjectLocalBufferTest : public ::testing::Test {
public:
  void SetUp() override {
    ArchSpec arch("i386-pc-linux");
    Platform::SetHostPlatform(
        platform_linux::PlatformLinux::CreateInstance(true, &arch));
    // std::call_once(TestUtilities::g_debugger_initialize_flag,
    //                []() { Debugger::Initialize(nullptr); });
    m_debugger_sp = Debugger::CreateInstance();
    ASSERT_TRUE(m_debugger_sp);
    m_debugger_sp->GetTargetList().CreateTarget(*m_debugger_sp, "", arch,
                                                eLoadDependentsNo,
                                                m_platform_sp, m_target_sp);
    ASSERT_TRUE(m_target_sp);
    ASSERT_TRUE(m_target_sp->GetArchitecture().IsValid());
    ASSERT_TRUE(m_platform_sp);
    m_listener_sp = Listener::MakeListener("dummy");
    m_process_sp = std::make_shared<MockProcess>(m_target_sp, m_listener_sp);
    ASSERT_TRUE(m_process_sp);
    m_exe_ctx = ExecutionContext(m_process_sp);

    m_holder = std::make_unique<clang_utils::TypeSystemClangHolder>("test");
    m_type_system = m_holder->GetAST();
    LLDB_PLUGIN_INITIALIZE(MockLanguage);
    LLDB_PLUGIN_INITIALIZE(MockLanguageRuntime);
  }
  void TearDown() override {
    LLDB_PLUGIN_TERMINATE(MockLanguage);
    LLDB_PLUGIN_TERMINATE(MockLanguageRuntime);
  }

  void TestValueObjectWithLocalBuffer(DataExtractor &data_extractor,
                                      bool should_succeed) {
    std::unique_ptr<TypeSystemClangHolder> holder =
        std::make_unique<TypeSystemClangHolder>("test ASTContext");
    TypeSystemClang *ast = holder->GetAST();
    auto char_type = createRecordWithField(
        *ast, "TypeWithChar",
        ast->GetBasicType(lldb::BasicType::eBasicTypeChar), "theField");

    ExecutionContextScope *exe_scope = m_exe_ctx.GetBestExecutionContextScope();
    ConstString var_name("test_var");
    auto valobj_sp = ValueObjectConstResult::Create(exe_scope, char_type,
                                                    var_name, data_extractor);
    auto dyn_valobj = valobj_sp->GetDynamicValue(lldb::eDynamicCanRunTarget);
    ASSERT_TRUE(dyn_valobj->GetValueIsValid() == should_succeed);
  }

  SubsystemRAII<FileSystem, HostInfo, platform_linux::PlatformLinux,
                ScriptInterpreterNone>
      m_subsystems;
  std::unique_ptr<clang_utils::TypeSystemClangHolder> m_holder;
  lldb::DebuggerSP m_debugger_sp;
  lldb::TargetSP m_target_sp;
  lldb::PlatformSP m_platform_sp;
  lldb::ListenerSP m_listener_sp;
  lldb::ProcessSP m_process_sp;
  ExecutionContext m_exe_ctx;
  TypeSystemClang *m_type_system;
};

TEST_F(DynamicValueObjectLocalBufferTest, BufferTooSmall) {
  /// Test that a value object with a buffer to small to fit the
  /// "dynamic" type will return an invalid dynamic value object.
  uint8_t value = 1;
  ByteOrder endian = endian::InlHostByteOrder();
  DataExtractor data_extractor{&value, sizeof(value), endian, 4};
  TestValueObjectWithLocalBuffer(data_extractor, false);
}

TEST_F(DynamicValueObjectLocalBufferTest, BufferTooBig) {
  /// Test that a value object with a buffer big enough fit the
  /// "dynamic" type will return a valid dynamic value object.
  uint64_t value = 1;
  ByteOrder endian = endian::InlHostByteOrder();
  DataExtractor data_extractor{&value, sizeof(value), endian, 4};
  TestValueObjectWithLocalBuffer(data_extractor, true);
}

TEST_F(DynamicValueObjectLocalBufferTest, BufferExactlyRight) {
  /// Test that a value object with a buffer exactly the size of the
  /// "dynamic" type will return a valid dynamic value object.
  uint32_t value = 1;
  ByteOrder endian = endian::InlHostByteOrder();
  DataExtractor data_extractor{&value, sizeof(value), endian, 4};
  TestValueObjectWithLocalBuffer(data_extractor, true);
}