62 changes: 62 additions & 0 deletions libc/src/__support/time/linux/clock_gettime.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
//===--- clock_gettime linux implementation ---------------------*- C++ -*-===//
//
// 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 "src/__support/time/linux/clock_gettime.h"
#include "hdr/types/clockid_t.h"
#include "hdr/types/struct_timespec.h"
#include "src/__support/OSUtil/linux/vdso.h"
#include "src/__support/OSUtil/syscall.h"
#include "src/__support/common.h"
#include "src/__support/error_or.h"
#include "src/__support/macros/config.h"
#include <sys/syscall.h>

#if defined(SYS_clock_gettime64)
#include <linux/time_types.h>
#endif

namespace LIBC_NAMESPACE_DECL {
namespace internal {
ErrorOr<int> clock_gettime(clockid_t clockid, timespec *ts) {
using namespace vdso;
int ret;
#if defined(SYS_clock_gettime)
TypedSymbol<VDSOSym::ClockGetTime> clock_gettime;
if (LIBC_LIKELY(clock_gettime != nullptr))
ret = clock_gettime(clockid, ts);
else
ret = LIBC_NAMESPACE::syscall_impl<int>(SYS_clock_gettime,
static_cast<long>(clockid),
reinterpret_cast<long>(ts));
#elif defined(SYS_clock_gettime64)
static_assert(
sizeof(time_t) == sizeof(int64_t),
"SYS_clock_gettime64 requires struct timespec with 64-bit members.");

TypedSymbol<VDSOSym::ClockGetTime64> clock_gettime64;
__kernel_timespec ts64{};
if (LIBC_LIKELY(clock_gettime64 != nullptr))
ret = clock_gettime64(clockid, &ts64);
else
ret = LIBC_NAMESPACE::syscall_impl<int>(SYS_clock_gettime64,
static_cast<long>(clockid),
reinterpret_cast<long>(&ts64));
if (ret == 0) {
ts->tv_sec = static_cast<decltype(ts->tv_sec)>(ts64.tv_sec);
ts->tv_nsec = static_cast<decltype(ts->tv_nsec)>(ts64.tv_nsec);
}
#else
#error "SYS_clock_gettime and SYS_clock_gettime64 syscalls not available."
#endif
if (ret < 0)
return Error(-ret);
return ret;
}

} // namespace internal
} // namespace LIBC_NAMESPACE_DECL
42 changes: 1 addition & 41 deletions libc/src/__support/time/linux/clock_gettime.h
Original file line number Diff line number Diff line change
Expand Up @@ -11,55 +11,15 @@

#include "hdr/types/clockid_t.h"
#include "hdr/types/struct_timespec.h"
#include "src/__support/OSUtil/linux/vdso.h"
#include "src/__support/OSUtil/syscall.h"
#include "src/__support/common.h"
#include "src/__support/error_or.h"
#include "src/__support/macros/config.h"
#include <sys/syscall.h>

#if defined(SYS_clock_gettime64)
#include <linux/time_types.h>
#endif

namespace LIBC_NAMESPACE_DECL {
namespace internal {
LIBC_INLINE ErrorOr<int> clock_gettime(clockid_t clockid, timespec *ts) {
using namespace vdso;
int ret;
#if defined(SYS_clock_gettime)
TypedSymbol<VDSOSym::ClockGetTime> clock_gettime;
if (LIBC_LIKELY(clock_gettime != nullptr))
ret = clock_gettime(clockid, ts);
else
ret = LIBC_NAMESPACE::syscall_impl<int>(SYS_clock_gettime,
static_cast<long>(clockid),
reinterpret_cast<long>(ts));
#elif defined(SYS_clock_gettime64)
static_assert(
sizeof(time_t) == sizeof(int64_t),
"SYS_clock_gettime64 requires struct timespec with 64-bit members.");

TypedSymbol<VDSOSym::ClockGetTime64> clock_gettime64;
__kernel_timespec ts64{};
if (LIBC_LIKELY(clock_gettime64 != nullptr))
ret = clock_gettime64(clockid, &ts64);
else
ret = LIBC_NAMESPACE::syscall_impl<int>(SYS_clock_gettime64,
static_cast<long>(clockid),
reinterpret_cast<long>(&ts64));
if (ret == 0) {
ts->tv_sec = static_cast<decltype(ts->tv_sec)>(ts64.tv_sec);
ts->tv_nsec = static_cast<decltype(ts->tv_nsec)>(ts64.tv_nsec);
}
#else
#error "SYS_clock_gettime and SYS_clock_gettime64 syscalls not available."
#endif
if (ret < 0)
return Error(-ret);
return ret;
}

ErrorOr<int> clock_gettime(clockid_t clockid, timespec *ts);
} // namespace internal
} // namespace LIBC_NAMESPACE_DECL

Expand Down
4 changes: 2 additions & 2 deletions libc/src/stdlib/freelist_malloc.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,8 @@

namespace LIBC_NAMESPACE_DECL {

static LIBC_CONSTINIT FreeListHeap<> freelist_heap_symbols;
FreeListHeap<> *freelist_heap = &freelist_heap_symbols;
static LIBC_CONSTINIT FreeListHeap freelist_heap_symbols;
FreeListHeap *freelist_heap = &freelist_heap_symbols;

LLVM_LIBC_FUNCTION(void *, malloc, (size_t size)) {
return freelist_heap->allocate(size);
Expand Down
27 changes: 27 additions & 0 deletions libc/test/src/__support/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,34 @@ if(NOT LIBC_TARGET_OS_IS_GPU)
DEPENDS
libc.src.__support.CPP.array
libc.src.__support.CPP.span
libc.src.__support.block
libc.src.__support.freelist
)

add_libc_test(
freetrie_test
SUITE
libc-support-tests
SRCS
freetrie_test.cpp
DEPENDS
libc.src.__support.CPP.optional
libc.src.__support.block
libc.src.__support.freetrie
)

add_libc_test(
freestore_test
SUITE
libc-support-tests
SRCS
freestore_test.cpp
DEPENDS
libc.src.__support.CPP.optional
libc.src.__support.block
libc.src.__support.freelist
libc.src.__support.freestore
libc.src.__support.freetrie
)
endif()

Expand Down
14 changes: 0 additions & 14 deletions libc/test/src/__support/block_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -238,20 +238,6 @@ TEST_FOR_EACH_BLOCK_TYPE(CannotMakeSecondBlockLargerInSplit) {
ASSERT_FALSE(result.has_value());
}

TEST_FOR_EACH_BLOCK_TYPE(CannotMakeZeroSizeFirstBlock) {
// This block doesn't support splitting with zero payload size, since the
// prev_ field of the next block is always available.
constexpr size_t kN = 1024;

alignas(BlockType::ALIGNMENT) array<byte, kN> bytes;
auto result = BlockType::init(bytes);
ASSERT_TRUE(result.has_value());
BlockType *block = *result;

result = block->split(0);
EXPECT_FALSE(result.has_value());
}

TEST_FOR_EACH_BLOCK_TYPE(CanMakeMinimalSizeFirstBlock) {
// This block does support splitting with minimal payload size.
constexpr size_t kN = 1024;
Expand Down
53 changes: 25 additions & 28 deletions libc/test/src/__support/freelist_heap_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,12 @@
#include "src/string/memcpy.h"
#include "test/UnitTest/Test.h"

namespace LIBC_NAMESPACE_DECL {

using LIBC_NAMESPACE::Block;
using LIBC_NAMESPACE::freelist_heap;
using LIBC_NAMESPACE::FreeListHeap;
using LIBC_NAMESPACE::FreeListHeapBuffer;
using LIBC_NAMESPACE::cpp::byte;
using LIBC_NAMESPACE::cpp::span;

// Similar to `LlvmLibcBlockTest` in block_test.cpp, we'd like to run the same
// tests independently for different parameters. In this case, we'd like to test
Expand All @@ -28,23 +31,23 @@ using LIBC_NAMESPACE::freelist_heap;
// made in tests leak and aren't free'd. This is fine for the purposes of this
// test file.
#define TEST_FOR_EACH_ALLOCATOR(TestCase, BufferSize) \
class LlvmLibcFreeListHeapTest##TestCase : public testing::Test { \
class LlvmLibcFreeListHeapTest##TestCase \
: public LIBC_NAMESPACE::testing::Test { \
public: \
FreeListHeapBuffer<BufferSize> fake_global_buffer; \
void SetUp() override { \
freelist_heap = \
new (&fake_global_buffer) FreeListHeapBuffer<BufferSize>; \
} \
void RunTest(FreeListHeap<> &allocator, [[maybe_unused]] size_t N); \
void RunTest(FreeListHeap &allocator, [[maybe_unused]] size_t N); \
}; \
TEST_F(LlvmLibcFreeListHeapTest##TestCase, TestCase) { \
alignas(FreeListHeap<>::BlockType) \
cpp::byte buf[BufferSize] = {cpp::byte(0)}; \
FreeListHeap<> allocator(buf); \
alignas(Block<>) byte buf[BufferSize] = {byte(0)}; \
FreeListHeap allocator(buf); \
RunTest(allocator, BufferSize); \
RunTest(*freelist_heap, freelist_heap->region().size()); \
} \
void LlvmLibcFreeListHeapTest##TestCase::RunTest(FreeListHeap<> &allocator, \
void LlvmLibcFreeListHeapTest##TestCase::RunTest(FreeListHeap &allocator, \
size_t N)

TEST_FOR_EACH_ALLOCATOR(CanAllocate, 2048) {
Expand Down Expand Up @@ -92,14 +95,13 @@ TEST_FOR_EACH_ALLOCATOR(ReturnsNullWhenAllocationTooLarge, 2048) {
// is used for other test cases and we don't explicitly free them.
TEST(LlvmLibcFreeListHeap, ReturnsNullWhenFull) {
constexpr size_t N = 2048;
alignas(FreeListHeap<>::BlockType) cpp::byte buf[N] = {cpp::byte(0)};
alignas(Block<>) byte buf[N] = {byte(0)};

FreeListHeap<> allocator(buf);
FreeListHeap allocator(buf);

// Use aligned_allocate so we don't need to worry about ensuring the `buf`
// being aligned to max_align_t.
EXPECT_NE(allocator.aligned_allocate(
1, N - 2 * FreeListHeap<>::BlockType::BLOCK_OVERHEAD),
EXPECT_NE(allocator.aligned_allocate(1, N - 2 * Block<>::BLOCK_OVERHEAD),
static_cast<void *>(nullptr));
EXPECT_EQ(allocator.allocate(1), static_cast<void *>(nullptr));
}
Expand Down Expand Up @@ -134,9 +136,9 @@ TEST_FOR_EACH_ALLOCATOR(ReallocHasSameContent, 2048) {
constexpr size_t ALLOC_SIZE = sizeof(int);
constexpr size_t kNewAllocSize = sizeof(int) * 2;
// Data inside the allocated block.
cpp::byte data1[ALLOC_SIZE];
byte data1[ALLOC_SIZE];
// Data inside the reallocated block.
cpp::byte data2[ALLOC_SIZE];
byte data2[ALLOC_SIZE];

int *ptr1 = reinterpret_cast<int *>(allocator.allocate(ALLOC_SIZE));
*ptr1 = 42;
Expand Down Expand Up @@ -188,10 +190,9 @@ TEST_FOR_EACH_ALLOCATOR(CanCalloc, 2048) {
constexpr size_t ALLOC_SIZE = 128;
constexpr size_t NUM = 4;
constexpr int size = NUM * ALLOC_SIZE;
constexpr cpp::byte zero{0};
constexpr byte zero{0};

cpp::byte *ptr1 =
reinterpret_cast<cpp::byte *>(allocator.calloc(NUM, ALLOC_SIZE));
byte *ptr1 = reinterpret_cast<byte *>(allocator.calloc(NUM, ALLOC_SIZE));

// calloc'd content is zero.
for (int i = 0; i < size; i++) {
Expand All @@ -203,10 +204,9 @@ TEST_FOR_EACH_ALLOCATOR(CanCallocWeirdSize, 2048) {
constexpr size_t ALLOC_SIZE = 143;
constexpr size_t NUM = 3;
constexpr int size = NUM * ALLOC_SIZE;
constexpr cpp::byte zero{0};
constexpr byte zero{0};

cpp::byte *ptr1 =
reinterpret_cast<cpp::byte *>(allocator.calloc(NUM, ALLOC_SIZE));
byte *ptr1 = reinterpret_cast<byte *>(allocator.calloc(NUM, ALLOC_SIZE));

// calloc'd content is zero.
for (int i = 0; i < size; i++) {
Expand Down Expand Up @@ -241,17 +241,16 @@ TEST_FOR_EACH_ALLOCATOR(AlignedAlloc, 2048) {

// This test is not part of the TEST_FOR_EACH_ALLOCATOR since we want to
// explicitly ensure that the buffer can still return aligned allocations even
// if the underlying buffer is at most aligned to the BlockType alignment. This
// if the underlying buffer is at most aligned to the Block<> alignment. This
// is so we can check that we can still get aligned allocations even if the
// underlying buffer is not aligned to the alignments we request.
TEST(LlvmLibcFreeListHeap, AlignedAllocOnlyBlockTypeAligned) {
TEST(LlvmLibcFreeListHeap, AlignedAllocOnlyBlockAligned) {
constexpr size_t BUFFER_SIZE = 4096;
constexpr size_t BUFFER_ALIGNMENT = alignof(FreeListHeap<>::BlockType) * 2;
alignas(BUFFER_ALIGNMENT) cpp::byte buf[BUFFER_SIZE] = {cpp::byte(0)};
constexpr size_t BUFFER_ALIGNMENT = alignof(Block<>) * 2;
alignas(BUFFER_ALIGNMENT) byte buf[BUFFER_SIZE] = {byte(0)};

// Ensure the underlying buffer is at most aligned to the block type.
FreeListHeap<> allocator(
span<cpp::byte>(buf).subspan(alignof(FreeListHeap<>::BlockType)));
FreeListHeap allocator(span<byte>(buf).subspan(alignof(Block<>)));

constexpr size_t ALIGNMENTS[] = {1, 2, 4, 8, 16, 32, 64, 128, 256};
constexpr size_t SIZE_SCALES[] = {1, 2, 3, 4, 5};
Expand Down Expand Up @@ -289,5 +288,3 @@ TEST_FOR_EACH_ALLOCATOR(InvalidAlignedAllocAlignment, 2048) {
ptr = allocator.aligned_allocate(0, 8);
EXPECT_EQ(ptr, static_cast<void *>(nullptr));
}

} // namespace LIBC_NAMESPACE_DECL
11 changes: 5 additions & 6 deletions libc/test/src/__support/freelist_malloc_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
#include "src/stdlib/malloc.h"
#include "test/UnitTest/Test.h"

using LIBC_NAMESPACE::Block;
using LIBC_NAMESPACE::freelist_heap;
using LIBC_NAMESPACE::FreeListHeap;
using LIBC_NAMESPACE::FreeListHeapBuffer;
Expand All @@ -22,15 +23,13 @@ TEST(LlvmLibcFreeListMalloc, Malloc) {
constexpr size_t kCallocNum = 4;
constexpr size_t kCallocSize = 64;

typedef FreeListHeap<>::BlockType Block;

void *ptr1 = LIBC_NAMESPACE::malloc(kAllocSize);
auto *block = Block::from_usable_space(ptr1);
auto *block = Block<>::from_usable_space(ptr1);
EXPECT_GE(block->inner_size(), kAllocSize);

LIBC_NAMESPACE::free(ptr1);
ASSERT_NE(block->next(), static_cast<Block *>(nullptr));
ASSERT_EQ(block->next()->next(), static_cast<Block *>(nullptr));
ASSERT_NE(block->next(), static_cast<Block<> *>(nullptr));
ASSERT_EQ(block->next()->next(), static_cast<Block<> *>(nullptr));
size_t heap_size = block->inner_size();

void *ptr2 = LIBC_NAMESPACE::calloc(kCallocNum, kCallocSize);
Expand All @@ -47,7 +46,7 @@ TEST(LlvmLibcFreeListMalloc, Malloc) {
void *ptr3 = LIBC_NAMESPACE::aligned_alloc(ALIGN, kAllocSize);
EXPECT_NE(ptr3, static_cast<void *>(nullptr));
EXPECT_EQ(reinterpret_cast<uintptr_t>(ptr3) % ALIGN, size_t(0));
auto *aligned_block = reinterpret_cast<Block *>(ptr3);
auto *aligned_block = reinterpret_cast<Block<> *>(ptr3);
EXPECT_GE(aligned_block->inner_size(), kAllocSize);

LIBC_NAMESPACE::free(ptr3);
Expand Down
187 changes: 37 additions & 150 deletions libc/test/src/__support/freelist_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -8,159 +8,46 @@

#include <stddef.h>

#include "src/__support/CPP/array.h"
#include "src/__support/CPP/span.h"
#include "src/__support/freelist.h"
#include "test/UnitTest/Test.h"

using LIBC_NAMESPACE::Block;
using LIBC_NAMESPACE::FreeList;
using LIBC_NAMESPACE::cpp::array;
using LIBC_NAMESPACE::cpp::byte;
using LIBC_NAMESPACE::cpp::span;

static constexpr size_t SIZE = 8;
static constexpr array<size_t, SIZE> example_sizes = {64, 128, 256, 512,
1024, 2048, 4096, 8192};

TEST(LlvmLibcFreeList, EmptyListHasNoMembers) {
FreeList<SIZE> list(example_sizes);

auto item = list.find_chunk(4);
EXPECT_EQ(item.size(), static_cast<size_t>(0));
item = list.find_chunk(128);
EXPECT_EQ(item.size(), static_cast<size_t>(0));
}

TEST(LlvmLibcFreeList, CanRetrieveAddedMember) {
FreeList<SIZE> list(example_sizes);
constexpr size_t N = 512;

byte data[N] = {byte(0)};

bool ok = list.add_chunk(span<byte>(data, N));
EXPECT_TRUE(ok);

auto item = list.find_chunk(N);
EXPECT_EQ(item.size(), N);
EXPECT_EQ(item.data(), data);
}

TEST(LlvmLibcFreeList, CanRetrieveAddedMemberForSmallerSize) {
FreeList<SIZE> list(example_sizes);
constexpr size_t N = 512;

byte data[N] = {byte(0)};

ASSERT_TRUE(list.add_chunk(span<byte>(data, N)));
auto item = list.find_chunk(N / 2);
EXPECT_EQ(item.size(), N);
EXPECT_EQ(item.data(), data);
}

TEST(LlvmLibcFreeList, CanRemoveItem) {
FreeList<SIZE> list(example_sizes);
constexpr size_t N = 512;

byte data[N] = {byte(0)};

ASSERT_TRUE(list.add_chunk(span<byte>(data, N)));
EXPECT_TRUE(list.remove_chunk(span<byte>(data, N)));

auto item = list.find_chunk(N);
EXPECT_EQ(item.size(), static_cast<size_t>(0));
}

TEST(LlvmLibcFreeList, FindReturnsSmallestChunk) {
FreeList<SIZE> list(example_sizes);
constexpr size_t kN1 = 512;
constexpr size_t kN2 = 1024;

byte data1[kN1] = {byte(0)};
byte data2[kN2] = {byte(0)};

ASSERT_TRUE(list.add_chunk(span<byte>(data1, kN1)));
ASSERT_TRUE(list.add_chunk(span<byte>(data2, kN2)));

auto chunk = list.find_chunk(kN1 / 2);
EXPECT_EQ(chunk.size(), kN1);
EXPECT_EQ(chunk.data(), data1);

chunk = list.find_chunk(kN1);
EXPECT_EQ(chunk.size(), kN1);
EXPECT_EQ(chunk.data(), data1);

chunk = list.find_chunk(kN1 + 1);
EXPECT_EQ(chunk.size(), kN2);
EXPECT_EQ(chunk.data(), data2);
}

TEST(LlvmLibcFreeList, FindReturnsCorrectChunkInSameBucket) {
// If we have two values in the same bucket, ensure that the allocation will
// pick an appropriately sized one.
FreeList<SIZE> list(example_sizes);
constexpr size_t kN1 = 512;
constexpr size_t kN2 = 257;

byte data1[kN1] = {byte(0)};
byte data2[kN2] = {byte(0)};

// List should now be 257 -> 512 -> NULL
ASSERT_TRUE(list.add_chunk(span<byte>(data1, kN1)));
ASSERT_TRUE(list.add_chunk(span<byte>(data2, kN2)));

auto chunk = list.find_chunk(kN2 + 1);
EXPECT_EQ(chunk.size(), kN1);
}

TEST(LlvmLibcFreeList, FindCanMoveUpThroughBuckets) {
// Ensure that finding a chunk will move up through buckets if no appropriate
// chunks were found in a given bucket
FreeList<SIZE> list(example_sizes);
constexpr size_t kN1 = 257;
constexpr size_t kN2 = 513;

byte data1[kN1] = {byte(0)};
byte data2[kN2] = {byte(0)};

// List should now be:
// bkt[3] (257 bytes up to 512 bytes) -> 257 -> NULL
// bkt[4] (513 bytes up to 1024 bytes) -> 513 -> NULL
ASSERT_TRUE(list.add_chunk(span<byte>(data1, kN1)));
ASSERT_TRUE(list.add_chunk(span<byte>(data2, kN2)));

// Request a 300 byte chunk. This should return the 513 byte one
auto chunk = list.find_chunk(kN1 + 1);
EXPECT_EQ(chunk.size(), kN2);
}

TEST(LlvmLibcFreeList, RemoveUnknownChunkReturnsNotFound) {
FreeList<SIZE> list(example_sizes);
constexpr size_t N = 512;

byte data[N] = {byte(0)};
byte data2[N] = {byte(0)};

ASSERT_TRUE(list.add_chunk(span<byte>(data, N)));
EXPECT_FALSE(list.remove_chunk(span<byte>(data2, N)));
}

TEST(LlvmLibcFreeList, CanStoreMultipleChunksPerBucket) {
FreeList<SIZE> list(example_sizes);
constexpr size_t N = 512;

byte data1[N] = {byte(0)};
byte data2[N] = {byte(0)};

ASSERT_TRUE(list.add_chunk(span<byte>(data1, N)));
ASSERT_TRUE(list.add_chunk(span<byte>(data2, N)));

auto chunk1 = list.find_chunk(N);
ASSERT_TRUE(list.remove_chunk(chunk1));
auto chunk2 = list.find_chunk(N);
ASSERT_TRUE(list.remove_chunk(chunk2));

// Ordering of the chunks doesn't matter
EXPECT_TRUE(chunk1.data() != chunk2.data());
EXPECT_TRUE(chunk1.data() == data1 || chunk1.data() == data2);
EXPECT_TRUE(chunk2.data() == data1 || chunk2.data() == data2);
using LIBC_NAMESPACE::cpp::optional;

TEST(LlvmLibcFreeList, FreeList) {
byte mem[1024];
optional<Block<> *> maybeBlock = Block<>::init(mem);
ASSERT_TRUE(maybeBlock.has_value());
Block<> *block1 = *maybeBlock;

maybeBlock = block1->split(128);
ASSERT_TRUE(maybeBlock.has_value());
Block<> *block2 = *maybeBlock;

maybeBlock = block2->split(128);
ASSERT_TRUE(maybeBlock.has_value());

FreeList list;
list.push(block1);
ASSERT_FALSE(list.empty());
EXPECT_EQ(list.front(), block1);

list.push(block2);
EXPECT_EQ(list.front(), block1);

list.pop();
ASSERT_FALSE(list.empty());
EXPECT_EQ(list.front(), block2);

list.pop();
ASSERT_TRUE(list.empty());

list.push(block1);
list.push(block2);
list.remove(reinterpret_cast<FreeList::Node *>(block2->usable_space()));
EXPECT_EQ(list.front(), block1);
list.pop();
ASSERT_TRUE(list.empty());
}
109 changes: 109 additions & 0 deletions libc/test/src/__support/freestore_test.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
//===-- Unittests for a freestore -------------------------------*- C++ -*-===//
//
// 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 <stddef.h>

#include "src/__support/freestore.h"
#include "test/UnitTest/Test.h"

using LIBC_NAMESPACE::Block;
using LIBC_NAMESPACE::FreeList;
using LIBC_NAMESPACE::FreeStore;
using LIBC_NAMESPACE::FreeTrie;
using LIBC_NAMESPACE::cpp::byte;
using LIBC_NAMESPACE::cpp::optional;

// Inserting or removing blocks too small to be tracked does nothing.
TEST(LlvmLibcFreeStore, TooSmall) {
byte mem[1024];
optional<Block<> *> maybeBlock = Block<>::init(mem);
ASSERT_TRUE(maybeBlock.has_value());
Block<> *too_small = *maybeBlock;
maybeBlock = too_small->split(sizeof(Block<>::offset_type));
ASSERT_TRUE(maybeBlock.has_value());
Block<> *remainder = *maybeBlock;

FreeStore store;
store.set_range({0, 4096});
store.insert(too_small);
store.insert(remainder);

EXPECT_EQ(store.remove_best_fit(too_small->inner_size()), remainder);
store.remove(too_small);
}

TEST(LlvmLibcFreeStore, RemoveBestFit) {
byte mem[1024];
optional<Block<> *> maybeBlock = Block<>::init(mem);
ASSERT_TRUE(maybeBlock.has_value());

Block<> *smallest = *maybeBlock;
maybeBlock =
smallest->split(sizeof(FreeList::Node) + sizeof(Block<>::offset_type));
ASSERT_TRUE(maybeBlock.has_value());

Block<> *largest_small = *maybeBlock;
maybeBlock =
largest_small->split(sizeof(FreeTrie::Node) +
sizeof(Block<>::offset_type) - alignof(max_align_t));
ASSERT_TRUE(maybeBlock.has_value());
if (largest_small->inner_size() == smallest->inner_size())
largest_small = smallest;
ASSERT_GE(largest_small->inner_size(), smallest->inner_size());

Block<> *remainder = *maybeBlock;

FreeStore store;
store.set_range({0, 4096});
store.insert(smallest);
if (largest_small != smallest)
store.insert(largest_small);
store.insert(remainder);

// Find exact match for smallest.
ASSERT_EQ(store.remove_best_fit(smallest->inner_size()), smallest);
store.insert(smallest);

// Find exact match for largest.
ASSERT_EQ(store.remove_best_fit(largest_small->inner_size()), largest_small);
store.insert(largest_small);

// Search small list for best fit.
Block<> *next_smallest =
largest_small == smallest ? remainder : largest_small;
ASSERT_EQ(store.remove_best_fit(smallest->inner_size() + 1), next_smallest);
store.insert(next_smallest);

// Continue search for best fit to large blocks.
EXPECT_EQ(store.remove_best_fit(largest_small->inner_size() + 1), remainder);
}

TEST(LlvmLibcFreeStore, Remove) {
byte mem[1024];
optional<Block<> *> maybeBlock = Block<>::init(mem);
ASSERT_TRUE(maybeBlock.has_value());

Block<> *small = *maybeBlock;
maybeBlock =
small->split(sizeof(FreeList::Node) + sizeof(Block<>::offset_type));
ASSERT_TRUE(maybeBlock.has_value());

Block<> *remainder = *maybeBlock;

FreeStore store;
store.set_range({0, 4096});
store.insert(small);
store.insert(remainder);

store.remove(remainder);
ASSERT_EQ(store.remove_best_fit(remainder->inner_size()),
static_cast<Block<> *>(nullptr));
store.remove(small);
ASSERT_EQ(store.remove_best_fit(small->inner_size()),
static_cast<Block<> *>(nullptr));
}
125 changes: 125 additions & 0 deletions libc/test/src/__support/freetrie_test.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
//===-- Unittests for a freetrie --------------------------------*- C++ -*-===//
//
// 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 <stddef.h>

#include "src/__support/freetrie.h"
#include "test/UnitTest/Test.h"

using LIBC_NAMESPACE::Block;
using LIBC_NAMESPACE::FreeTrie;
using LIBC_NAMESPACE::cpp::byte;
using LIBC_NAMESPACE::cpp::optional;

TEST(LlvmLibcFreeTrie, FindBestFitRoot) {
FreeTrie trie({0, 4096});
EXPECT_EQ(trie.find_best_fit(123), static_cast<FreeTrie::Node *>(nullptr));

byte mem[1024];
optional<Block<> *> maybeBlock = Block<>::init(mem);
ASSERT_TRUE(maybeBlock.has_value());
Block<> *block = *maybeBlock;
trie.push(block);

FreeTrie::Node *root = trie.find_best_fit(0);
ASSERT_EQ(root->block(), block);
EXPECT_EQ(trie.find_best_fit(block->inner_size() - 1), root);
EXPECT_EQ(trie.find_best_fit(block->inner_size()), root);
EXPECT_EQ(trie.find_best_fit(block->inner_size() + 1),
static_cast<FreeTrie::Node *>(nullptr));
EXPECT_EQ(trie.find_best_fit(4095), static_cast<FreeTrie::Node *>(nullptr));
}

TEST(LlvmLibcFreeTrie, FindBestFitLower) {
byte mem[4096];
optional<Block<> *> maybeBlock = Block<>::init(mem);
ASSERT_TRUE(maybeBlock.has_value());
Block<> *lower = *maybeBlock;
maybeBlock = lower->split(512);
ASSERT_TRUE(maybeBlock.has_value());
Block<> *root = *maybeBlock;

FreeTrie trie({0, 4096});
trie.push(root);
trie.push(lower);

EXPECT_EQ(trie.find_best_fit(0)->block(), lower);
}

TEST(LlvmLibcFreeTrie, FindBestFitUpper) {
byte mem[4096];
optional<Block<> *> maybeBlock = Block<>::init(mem);
ASSERT_TRUE(maybeBlock.has_value());
Block<> *root = *maybeBlock;
maybeBlock = root->split(512);
ASSERT_TRUE(maybeBlock.has_value());
Block<> *upper = *maybeBlock;

FreeTrie trie({0, 4096});
trie.push(root);
trie.push(upper);

EXPECT_EQ(trie.find_best_fit(root->inner_size() + 1)->block(), upper);
// The upper subtrie should be skipped if it could not contain a better fit.
EXPECT_EQ(trie.find_best_fit(root->inner_size() - 1)->block(), root);
}

TEST(LlvmLibcFreeTrie, FindBestFitLowerAndUpper) {
byte mem[4096];
optional<Block<> *> maybeBlock = Block<>::init(mem);
ASSERT_TRUE(maybeBlock.has_value());
Block<> *root = *maybeBlock;
maybeBlock = root->split(1024);
ASSERT_TRUE(maybeBlock.has_value());
Block<> *lower = *maybeBlock;
maybeBlock = lower->split(128);
ASSERT_TRUE(maybeBlock.has_value());
Block<> *upper = *maybeBlock;

FreeTrie trie({0, 4096});
trie.push(root);
trie.push(lower);
trie.push(upper);

// The lower subtrie is examined first.
EXPECT_EQ(trie.find_best_fit(0)->block(), lower);
// The upper subtrie is examined if there are no fits found in the upper
// subtrie.
EXPECT_EQ(trie.find_best_fit(2048)->block(), upper);
}

TEST(LlvmLibcFreeTrie, Remove) {
byte mem[4096];
optional<Block<> *> maybeBlock = Block<>::init(mem);
ASSERT_TRUE(maybeBlock.has_value());
Block<> *small1 = *maybeBlock;
maybeBlock = small1->split(512);
ASSERT_TRUE(maybeBlock.has_value());
Block<> *small2 = *maybeBlock;
maybeBlock = small2->split(512);
ASSERT_TRUE(maybeBlock.has_value());
ASSERT_EQ(small1->inner_size(), small2->inner_size());
Block<> *large = *maybeBlock;

// Removing the root empties the trie.
FreeTrie trie({0, 4096});
trie.push(large);
FreeTrie::Node *large_node = trie.find_best_fit(0);
ASSERT_EQ(large_node->block(), large);
trie.remove(large_node);
ASSERT_TRUE(trie.empty());

// Removing the head of a trie list preserves the trie structure.
trie.push(small1);
trie.push(small2);
trie.push(large);
trie.remove(trie.find_best_fit(small1->inner_size()));
EXPECT_EQ(trie.find_best_fit(large->inner_size())->block(), large);
trie.remove(trie.find_best_fit(small1->inner_size()));
EXPECT_EQ(trie.find_best_fit(large->inner_size())->block(), large);
}
16 changes: 9 additions & 7 deletions libc/utils/gpu/loader/amdgpu/amdhsa-loader.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -371,8 +371,9 @@ int load(int argc, const char **argv, const char **envp, void *image,
handle_error(err);

// Load the code object's ISA information and executable data segments.
hsa_code_object_t object;
if (hsa_status_t err = hsa_code_object_deserialize(image, size, "", &object))
hsa_code_object_reader_t reader;
if (hsa_status_t err =
hsa_code_object_reader_create_from_memory(image, size, &reader))
handle_error(err);

hsa_executable_t executable;
Expand All @@ -381,8 +382,9 @@ int load(int argc, const char **argv, const char **envp, void *image,
&executable))
handle_error(err);

if (hsa_status_t err =
hsa_executable_load_code_object(executable, dev_agent, object, ""))
hsa_loaded_code_object_t object;
if (hsa_status_t err = hsa_executable_load_agent_code_object(
executable, dev_agent, reader, "", &object))
handle_error(err);

// No modifications to the executable are allowed after this point.
Expand All @@ -397,6 +399,9 @@ int load(int argc, const char **argv, const char **envp, void *image,
if (result)
handle_error(HSA_STATUS_ERROR);

if (hsa_status_t err = hsa_code_object_reader_destroy(reader))
handle_error(err);

// Obtain memory pools to exchange data between the host and the device. The
// fine-grained pool acts as pinned memory on the host for DMA transfers to
// the device, the coarse-grained pool is for allocations directly on the
Expand Down Expand Up @@ -617,9 +622,6 @@ int load(int argc, const char **argv, const char **envp, void *image,
if (hsa_status_t err = hsa_executable_destroy(executable))
handle_error(err);

if (hsa_status_t err = hsa_code_object_destroy(object))
handle_error(err);

if (hsa_status_t err = hsa_shut_down())
handle_error(err);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,9 @@
// TODO Investigate why this fails
// UNSUPPORTED: windows

// This test times out under msan
// UNSUPPORTED: msan

// to_chars requires functions in the dylib that have not been introduced in older
// versions of the dylib on macOS.
// XFAIL: availability-fp_to_chars-missing
Expand Down
35 changes: 9 additions & 26 deletions lld/ELF/Arch/ARM.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1330,22 +1330,6 @@ void elf::processArmCmseSymbols(Ctx &ctx) {
});
}

class elf::ArmCmseSGVeneer {
public:
ArmCmseSGVeneer(Symbol *sym, Symbol *acleSeSym,
std::optional<uint64_t> addr = std::nullopt)
: sym(sym), acleSeSym(acleSeSym), entAddr{addr} {}
static const size_t size{ACLESESYM_SIZE};
const std::optional<uint64_t> getAddr() const { return entAddr; };

Symbol *sym;
Symbol *acleSeSym;
uint64_t offset = 0;

private:
const std::optional<uint64_t> entAddr;
};

ArmCmseSGSection::ArmCmseSGSection(Ctx &ctx)
: SyntheticSection(ctx, llvm::ELF::SHF_ALLOC | llvm::ELF::SHF_EXECINSTR,
llvm::ELF::SHT_PROGBITS,
Expand Down Expand Up @@ -1389,19 +1373,19 @@ void ArmCmseSGSection::addSGVeneer(Symbol *acleSeSym, Symbol *sym) {
return;
// Only secure symbols with values equal to that of it's non-secure
// counterpart needs to be in the .gnu.sgstubs section.
ArmCmseSGVeneer *ss = nullptr;
std::unique_ptr<ArmCmseSGVeneer> ss;
if (ctx.symtab->cmseImportLib.count(sym->getName())) {
Defined *impSym = ctx.symtab->cmseImportLib[sym->getName()];
ss = make<ArmCmseSGVeneer>(sym, acleSeSym, impSym->value);
ss = std::make_unique<ArmCmseSGVeneer>(sym, acleSeSym, impSym->value);
} else {
ss = make<ArmCmseSGVeneer>(sym, acleSeSym);
ss = std::make_unique<ArmCmseSGVeneer>(sym, acleSeSym);
++newEntries;
}
sgVeneers.emplace_back(ss);
sgVeneers.emplace_back(std::move(ss));
}

void ArmCmseSGSection::writeTo(uint8_t *buf) {
for (ArmCmseSGVeneer *s : sgVeneers) {
for (std::unique_ptr<ArmCmseSGVeneer> &s : sgVeneers) {
uint8_t *p = buf + s->offset;
write16(ctx, p + 0, 0xe97f); // SG
write16(ctx, p + 2, 0xe97f);
Expand Down Expand Up @@ -1430,8 +1414,8 @@ void ArmCmseSGSection::finalizeContents() {

auto it =
std::stable_partition(sgVeneers.begin(), sgVeneers.end(),
[](auto *i) { return i->getAddr().has_value(); });
std::sort(sgVeneers.begin(), it, [](auto *a, auto *b) {
[](auto &i) { return i->getAddr().has_value(); });
std::sort(sgVeneers.begin(), it, [](auto &a, auto &b) {
return a->getAddr().value() < b->getAddr().value();
});
// This is the partition of the veneers with fixed addresses.
Expand All @@ -1441,13 +1425,12 @@ void ArmCmseSGSection::finalizeContents() {
// Check if the start address of '.gnu.sgstubs' correspond to the
// linker-synthesized veneer with the lowest address.
if ((getVA() & ~1) != (addr & ~1)) {
ErrAlways(ctx)
Err(ctx)
<< "start address of '.gnu.sgstubs' is different from previous link";
return;
}

for (size_t i = 0; i < sgVeneers.size(); ++i) {
ArmCmseSGVeneer *s = sgVeneers[i];
for (auto [i, s] : enumerate(sgVeneers)) {
s->offset = i * s->size;
Defined(ctx, file, StringRef(), s->sym->binding, s->sym->stOther,
s->sym->type, s->offset | 1, s->size, this)
Expand Down
18 changes: 16 additions & 2 deletions lld/ELF/SyntheticSections.h
Original file line number Diff line number Diff line change
Expand Up @@ -1300,7 +1300,21 @@ class ThunkSection final : public SyntheticSection {
const char ACLESESYM_PREFIX[] = "__acle_se_";
const int ACLESESYM_SIZE = 8;

class ArmCmseSGVeneer;
class ArmCmseSGVeneer {
public:
ArmCmseSGVeneer(Symbol *sym, Symbol *acleSeSym,
std::optional<uint64_t> addr = std::nullopt)
: sym(sym), acleSeSym(acleSeSym), entAddr{addr} {}
static const size_t size{ACLESESYM_SIZE};
const std::optional<uint64_t> getAddr() const { return entAddr; };

Symbol *sym;
Symbol *acleSeSym;
uint64_t offset = 0;

private:
const std::optional<uint64_t> entAddr;
};

class ArmCmseSGSection final : public SyntheticSection {
public:
Expand All @@ -1316,7 +1330,7 @@ class ArmCmseSGSection final : public SyntheticSection {

private:
SmallVector<std::pair<Symbol *, Symbol *>, 0> entries;
SmallVector<ArmCmseSGVeneer *, 0> sgVeneers;
SmallVector<std::unique_ptr<ArmCmseSGVeneer>, 0> sgVeneers;
uint64_t newEntries = 0;
};

Expand Down
2 changes: 1 addition & 1 deletion lld/MinGW/Options.td
Original file line number Diff line number Diff line change
Expand Up @@ -212,7 +212,7 @@ defm guard_longjmp : B<"guard-longjmp",
"Do not enable Control Flow Guard long jump hardening">;
defm error_limit:
EqLong<"error-limit", "Maximum number of errors to emit before stopping (0 = no limit)">;
def build_id: J<"build-id=">, HelpText<"Generate build ID note (pass none to disable)">,
def build_id: J<"build-id=">, HelpText<"Generate build ID note (pass none to disable)">,
MetaVarName<"<arg>">;
def : F<"build-id">, Alias<build_id>, HelpText<"Alias for --build-id=">;
def functionpadmin: J<"functionpadmin=">, HelpText<"Prepares an image for hotpatching">,
Expand Down
6 changes: 3 additions & 3 deletions lld/test/ELF/lto/riscv-attributes.ll
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,10 @@
; CHECK: BuildAttributes {
; CHECK-NEXT: FormatVersion: 0x41
; CHECK-NEXT: Section 1 {
; CHECK-NEXT: SectionLength: 79
; CHECK-NEXT: SectionLength: 98
; CHECK-NEXT: Vendor: riscv
; CHECK-NEXT: Tag: Tag_File (0x1)
; CHECK-NEXT: Size: 69
; CHECK-NEXT: Size: 88
; CHECK-NEXT: FileAttributes {
; CHECK-NEXT: Attribute {
; CHECK-NEXT: Tag: 4
Expand All @@ -30,7 +30,7 @@
; CHECK-NEXT: Attribute {
; CHECK-NEXT: Tag: 5
; CHECK-NEXT: TagName: arch
; CHECK-NEXT: Value: rv32i2p1_m2p0_a2p1_f2p2_d2p2_c2p0_zicsr2p0_zmmul1p0_zbb1p0{{$}}
; CHECK-NEXT: Value: rv32i2p1_m2p0_a2p1_f2p2_d2p2_c2p0_zicsr2p0_zmmul1p0_zaamo1p0_zalrsc1p0_zbb1p0{{$}}
; CHECK-NEXT: }
; CHECK-NEXT: }
; CHECK-NEXT: }
Expand Down
16 changes: 8 additions & 8 deletions lld/test/ELF/riscv-attributes.s
Original file line number Diff line number Diff line change
Expand Up @@ -104,20 +104,20 @@
# UNKNOWN22: warning: unknown22a.o:(.riscv.attributes): invalid tag 0x16 at offset 0x10

# HDR: Name Type Address Off Size ES Flg Lk Inf Al
# HDR: .riscv.attributes RISCV_ATTRIBUTES 0000000000000000 000158 000047 00 0 0 1{{$}}
# HDR: .riscv.attributes RISCV_ATTRIBUTES 0000000000000000 000158 00005a 00 0 0 1{{$}}

# HDR: Type Offset VirtAddr PhysAddr FileSiz MemSiz Flg Align
# HDR: LOAD 0x000000 0x0000000000010000 0x0000000000010000 0x000158 0x000158 R 0x1000
# HDR-NEXT: GNU_STACK 0x000000 0x0000000000000000 0x0000000000000000 0x000000 0x000000 RW 0
# HDR-NEXT: ATTRIBUTES 0x000158 0x0000000000000000 0x0000000000000000 0x000047 0x000047 R 0x1{{$}}
# HDR-NEXT: ATTRIBUTES 0x000158 0x0000000000000000 0x0000000000000000 0x00005a 0x00005a R 0x1{{$}}

# CHECK: BuildAttributes {
# CHECK-NEXT: FormatVersion: 0x41
# CHECK-NEXT: Section 1 {
# CHECK-NEXT: SectionLength: 70
# CHECK-NEXT: SectionLength: 89
# CHECK-NEXT: Vendor: riscv
# CHECK-NEXT: Tag: Tag_File (0x1)
# CHECK-NEXT: Size: 60
# CHECK-NEXT: Size: 79
# CHECK-NEXT: FileAttributes {
# CHECK-NEXT: Attribute {
# CHECK-NEXT: Tag: 4
Expand All @@ -128,7 +128,7 @@
# CHECK-NEXT: Attribute {
# CHECK-NEXT: Tag: 5
# CHECK-NEXT: TagName: arch
# CHECK-NEXT: Value: rv64i2p1_m2p0_a2p1_f2p2_d2p2_c2p0_zicsr2p0_zmmul1p0{{$}}
# CHECK-NEXT: Value: rv64i2p1_m2p0_a2p1_f2p2_d2p2_c2p0_zicsr2p0_zmmul1p0_zaamo1p0_zalrsc1p0{{$}}
# CHECK-NEXT: }
# CHECK-NEXT: }
# CHECK-NEXT: }
Expand All @@ -137,10 +137,10 @@
# CHECK2: BuildAttributes {
# CHECK2-NEXT: FormatVersion: 0x41
# CHECK2-NEXT: Section 1 {
# CHECK2-NEXT: SectionLength: 113
# CHECK2-NEXT: SectionLength: 132
# CHECK2-NEXT: Vendor: riscv
# CHECK2-NEXT: Tag: Tag_File (0x1)
# CHECK2-NEXT: Size: 103
# CHECK2-NEXT: Size: 122
# CHECK2-NEXT: FileAttributes {
# CHECK2-NEXT: Attribute {
# CHECK2-NEXT: Tag: 4
Expand All @@ -167,7 +167,7 @@
# CHECK2-NEXT: Attribute {
# CHECK2-NEXT: Tag: 5
# CHECK2-NEXT: TagName: arch
# CHECK2-NEXT: Value: rv64i2p1_m2p0_a2p1_f2p2_d2p2_c2p0_zicsr2p0_zmmul1p0_zkt1p0_zve32f1p0_zve32x1p0_zvl32b1p0{{$}}
# CHECK2-NEXT: Value: rv64i2p1_m2p0_a2p1_f2p2_d2p2_c2p0_zicsr2p0_zmmul1p0_zaamo1p0_zalrsc1p0_zkt1p0_zve32f1p0_zve32x1p0_zvl32b1p0{{$}}
# CHECK2-NEXT: }
# CHECK2-NEXT: }
# CHECK2-NEXT: }
Expand Down
10 changes: 4 additions & 6 deletions lldb/include/lldb/Expression/UserExpression.h
Original file line number Diff line number Diff line change
Expand Up @@ -240,11 +240,9 @@ class UserExpression : public Expression {
/// definitions to be included when the expression is parsed.
///
/// \param[in,out] result_valobj_sp
/// If execution is successful, the result valobj is placed here.
///
/// \param[out] error
/// Filled in with an error in case the expression evaluation
/// fails to parse, run, or evaluated.
/// If execution is successful, the result valobj is placed
/// here. Otherwise its Error will contain an ExpressionError
/// with details about the failure mode.
///
/// \param[out] fixed_expression
/// If non-nullptr, the fixed expression is copied into the provided
Expand All @@ -266,7 +264,7 @@ class UserExpression : public Expression {
static lldb::ExpressionResults
Evaluate(ExecutionContext &exe_ctx, const EvaluateExpressionOptions &options,
llvm::StringRef expr_cstr, llvm::StringRef expr_prefix,
lldb::ValueObjectSP &result_valobj_sp, Status &error,
lldb::ValueObjectSP &result_valobj_sp,
std::string *fixed_expression = nullptr,
ValueObject *ctx_obj = nullptr);

Expand Down
6 changes: 4 additions & 2 deletions lldb/source/API/SBValue.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -380,8 +380,10 @@ const char *SBValue::GetObjectDescription() {
return nullptr;

llvm::Expected<std::string> str = value_sp->GetObjectDescription();
if (!str)
return ConstString("error: " + toString(str.takeError())).AsCString();
if (!str) {
llvm::consumeError(str.takeError());
return nullptr;
}
return ConstString(*str).AsCString();
}

Expand Down
38 changes: 19 additions & 19 deletions lldb/source/Core/FormatEntity.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -410,31 +410,31 @@ static bool DumpAddressAndContent(Stream &s, const SymbolContext *sc,
const Address &addr,
bool print_file_addr_or_load_addr) {
Target *target = Target::GetTargetFromContexts(exe_ctx, sc);

addr_t vaddr = LLDB_INVALID_ADDRESS;
if (exe_ctx && !target->GetSectionLoadList().IsEmpty())
if (target && !target->GetSectionLoadList().IsEmpty())
vaddr = addr.GetLoadAddress(target);
if (vaddr == LLDB_INVALID_ADDRESS)
vaddr = addr.GetFileAddress();
if (vaddr == LLDB_INVALID_ADDRESS)
return false;

if (vaddr != LLDB_INVALID_ADDRESS) {
int addr_width = 0;
if (exe_ctx && target) {
addr_width = target->GetArchitecture().GetAddressByteSize() * 2;
}
if (addr_width == 0)
addr_width = 16;
if (print_file_addr_or_load_addr) {
ExecutionContextScope *exe_scope = nullptr;
if (exe_ctx)
exe_scope = exe_ctx->GetBestExecutionContextScope();
addr.Dump(&s, exe_scope, Address::DumpStyleLoadAddress,
Address::DumpStyleModuleWithFileAddress, 0);
} else {
s.Printf("0x%*.*" PRIx64, addr_width, addr_width, vaddr);
}
return true;
int addr_width = 0;
if (target)
addr_width = target->GetArchitecture().GetAddressByteSize() * 2;
if (addr_width == 0)
addr_width = 16;

if (print_file_addr_or_load_addr) {
ExecutionContextScope *exe_scope =
exe_ctx ? exe_ctx->GetBestExecutionContextScope() : nullptr;
addr.Dump(&s, exe_scope, Address::DumpStyleLoadAddress,
Address::DumpStyleModuleWithFileAddress, 0);
} else {
s.Printf("0x%*.*" PRIx64, addr_width, addr_width, vaddr);
}
return false;

return true;
}

static bool DumpAddressOffsetFromFunction(Stream &s, const SymbolContext *sc,
Expand Down
7 changes: 2 additions & 5 deletions lldb/source/Expression/REPL.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -339,12 +339,9 @@ void REPL::IOHandlerInputComplete(IOHandler &io_handler, std::string &code) {

const char *expr_prefix = nullptr;
lldb::ValueObjectSP result_valobj_sp;
lldb::ExpressionResults execution_results = UserExpression::Evaluate(
exe_ctx, expr_options, code.c_str(), expr_prefix, result_valobj_sp);
Status error;
lldb::ExpressionResults execution_results =
UserExpression::Evaluate(exe_ctx, expr_options, code.c_str(),
expr_prefix, result_valobj_sp, error,
nullptr); // fixed expression

if (llvm::Error err = OnExpressionEvaluated(exe_ctx, code, expr_options,
execution_results,
result_valobj_sp, error)) {
Expand Down
64 changes: 29 additions & 35 deletions lldb/source/Expression/UserExpression.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -144,9 +144,13 @@ lldb::ExpressionResults
UserExpression::Evaluate(ExecutionContext &exe_ctx,
const EvaluateExpressionOptions &options,
llvm::StringRef expr, llvm::StringRef prefix,
lldb::ValueObjectSP &result_valobj_sp, Status &error,
lldb::ValueObjectSP &result_valobj_sp,
std::string *fixed_expression, ValueObject *ctx_obj) {
Log *log(GetLog(LLDBLog::Expressions | LLDBLog::Step));
auto set_error = [&](Status error) {
result_valobj_sp = ValueObjectConstResult::Create(
exe_ctx.GetBestExecutionContextScope(), std::move(error));
};

if (ctx_obj) {
static unsigned const ctx_type_mask = lldb::TypeFlags::eTypeIsClass |
Expand All @@ -155,8 +159,7 @@ UserExpression::Evaluate(ExecutionContext &exe_ctx,
if (!(ctx_obj->GetTypeInfo() & ctx_type_mask)) {
LLDB_LOG(log, "== [UserExpression::Evaluate] Passed a context object of "
"an invalid type, can't run expressions.");
error =
Status::FromErrorString("a context object of an invalid type passed");
set_error(Status("a context object of an invalid type passed"));
return lldb::eExpressionSetupError;
}
}
Expand All @@ -168,8 +171,8 @@ UserExpression::Evaluate(ExecutionContext &exe_ctx,
LLDB_LOG(log, "== [UserExpression::Evaluate] Passed a context object of "
"a reference type that can't be dereferenced, can't run "
"expressions.");
error = Status::FromErrorString(
"passed context object of an reference type cannot be deferenced");
set_error(Status(
"passed context object of an reference type cannot be deferenced"));
return lldb::eExpressionSetupError;
}

Expand All @@ -181,37 +184,34 @@ UserExpression::Evaluate(ExecutionContext &exe_ctx,
const ResultType desired_type = options.DoesCoerceToId()
? UserExpression::eResultTypeId
: UserExpression::eResultTypeAny;
lldb::ExpressionResults execution_results = lldb::eExpressionSetupError;

Target *target = exe_ctx.GetTargetPtr();
if (!target) {
LLDB_LOG(log, "== [UserExpression::Evaluate] Passed a NULL target, can't "
"run expressions.");
error = Status::FromErrorString("expression passed a null target");
set_error(Status("expression passed a null target"));
return lldb::eExpressionSetupError;
}

Process *process = exe_ctx.GetProcessPtr();

if (process == nullptr && execution_policy == eExecutionPolicyAlways) {
if (!process && execution_policy == eExecutionPolicyAlways) {
LLDB_LOG(log, "== [UserExpression::Evaluate] No process, but the policy is "
"eExecutionPolicyAlways");

error = Status::FromErrorString(
"expression needed to run but couldn't: no process");
set_error(Status("expression needed to run but couldn't: no process"));

return execution_results;
return lldb::eExpressionSetupError;
}

// Since we might need to allocate memory, we need to be stopped to run
// an expression.
if (process != nullptr && process->GetState() != lldb::eStateStopped) {
error = Status::FromErrorStringWithFormatv(
if (process && process->GetState() != lldb::eStateStopped) {
set_error(Status::FromErrorStringWithFormatv(
"unable to evaluate expression while the process is {0}: the process "
"must be stopped because the expression might require allocating "
"memory.",
StateAsCString(process->GetState()));
return execution_results;
StateAsCString(process->GetState())));
return lldb::eExpressionSetupError;
}

// Explicitly force the IR interpreter to evaluate the expression when the
Expand Down Expand Up @@ -251,13 +251,14 @@ UserExpression::Evaluate(ExecutionContext &exe_ctx,
language = frame->GetLanguage();
}

Status error;
lldb::UserExpressionSP user_expression_sp(
target->GetUserExpressionForLanguage(expr, full_prefix, language,
desired_type, options, ctx_obj,
error));
target->GetUserExpressionForLanguage(
expr, full_prefix, language, desired_type, options, ctx_obj, error));
if (error.Fail() || !user_expression_sp) {
LLDB_LOG(log, "== [UserExpression::Evaluate] Getting expression: {0} ==",
error.AsCString());
set_error(std::move(error));
return lldb::eExpressionSetupError;
}

Expand All @@ -268,10 +269,7 @@ UserExpression::Evaluate(ExecutionContext &exe_ctx,
const bool generate_debug_info = options.GetGenerateDebugInfo();

if (options.InvokeCancelCallback(lldb::eExpressionEvaluationParse)) {
Status error = Status::FromErrorString(
"expression interrupted by callback before parse");
result_valobj_sp = ValueObjectConstResult::Create(
exe_ctx.GetBestExecutionContextScope(), std::move(error));
set_error(Status("expression interrupted by callback before parse"));
return lldb::eExpressionInterrupted;
}

Expand All @@ -287,6 +285,7 @@ UserExpression::Evaluate(ExecutionContext &exe_ctx,
fixed_expression = &tmp_fixed_expression;

*fixed_expression = user_expression_sp->GetFixedText().str();
lldb::ExpressionResults execution_results = lldb::eExpressionSetupError;

// If there is a fixed expression, try to parse it:
if (!parse_success) {
Expand Down Expand Up @@ -358,15 +357,13 @@ UserExpression::Evaluate(ExecutionContext &exe_ctx,
lldb::eExpressionSetupError,
"expression needed to run but couldn't"));
} else if (execution_policy == eExecutionPolicyTopLevel) {
error = Status(UserExpression::kNoResult, lldb::eErrorTypeGeneric);
set_error(Status(UserExpression::kNoResult, lldb::eErrorTypeGeneric));
return lldb::eExpressionCompleted;
} else {
if (options.InvokeCancelCallback(lldb::eExpressionEvaluationExecution)) {
error = Status::FromError(llvm::make_error<ExpressionError>(
set_error(Status::FromError(llvm::make_error<ExpressionError>(
lldb::eExpressionInterrupted,
"expression interrupted by callback before execution"));
result_valobj_sp = ValueObjectConstResult::Create(
exe_ctx.GetBestExecutionContextScope(), std::move(error));
"expression interrupted by callback before execution")));
return lldb::eExpressionInterrupted;
}

Expand Down Expand Up @@ -410,17 +407,14 @@ UserExpression::Evaluate(ExecutionContext &exe_ctx,
}

if (options.InvokeCancelCallback(lldb::eExpressionEvaluationComplete)) {
error = Status::FromError(llvm::make_error<ExpressionError>(
set_error(Status::FromError(llvm::make_error<ExpressionError>(
lldb::eExpressionInterrupted,
"expression interrupted by callback after complete"));
"expression interrupted by callback after complete")));
return lldb::eExpressionInterrupted;
}

if (result_valobj_sp.get() == nullptr) {
result_valobj_sp = ValueObjectConstResult::Create(
exe_ctx.GetBestExecutionContextScope(), std::move(error));
}

if (error.Fail())
set_error(std::move(error));
return execution_results;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -323,15 +323,15 @@ StructuredData::ObjectSP InstrumentationRuntimeTSan::RetrieveReportData(

ValueObjectSP main_value;
ExecutionContext exe_ctx;
Status eval_error;
frame_sp->CalculateExecutionContext(exe_ctx);
ExpressionResults result = UserExpression::Evaluate(
exe_ctx, options, thread_sanitizer_retrieve_report_data_command, "",
main_value, eval_error);
main_value);
if (result != eExpressionCompleted) {
StreamString ss;
ss << "cannot evaluate ThreadSanitizer expression:\n";
ss << eval_error.AsCString();
if (main_value)
ss << main_value->GetError().AsCString();
Debugger::ReportWarning(ss.GetString().str(),
process_sp->GetTarget().GetDebugger().GetID());
return StructuredData::ObjectSP();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -130,15 +130,15 @@ StructuredData::ObjectSP InstrumentationRuntimeUBSan::RetrieveReportData(

ValueObjectSP main_value;
ExecutionContext exe_ctx;
Status eval_error;
frame_sp->CalculateExecutionContext(exe_ctx);
ExpressionResults result = UserExpression::Evaluate(
exe_ctx, options, ub_sanitizer_retrieve_report_data_command, "",
main_value, eval_error);
main_value);
if (result != eExpressionCompleted) {
StreamString ss;
ss << "cannot evaluate UndefinedBehaviorSanitizer expression:\n";
ss << eval_error.AsCString();
if (main_value)
ss << main_value->GetError().AsCString();
Debugger::ReportWarning(ss.GetString().str(),
process_sp->GetTarget().GetDebugger().GetID());
return StructuredData::ObjectSP();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -84,15 +84,15 @@ ReportRetriever::RetrieveReportData(const ProcessSP process_sp) {

ValueObjectSP return_value_sp;
ExecutionContext exe_ctx;
Status eval_error;
frame_sp->CalculateExecutionContext(exe_ctx);
ExpressionResults result = UserExpression::Evaluate(
exe_ctx, options, address_sanitizer_retrieve_report_data_command, "",
return_value_sp, eval_error);
return_value_sp);
if (result != eExpressionCompleted) {
StreamString ss;
ss << "cannot evaluate AddressSanitizer expression:\n";
ss << eval_error.AsCString();
if (return_value_sp)
ss << return_value_sp->GetError().AsCString();
Debugger::ReportWarning(ss.GetString().str(),
process_sp->GetTarget().GetDebugger().GetID());
return StructuredData::ObjectSP();
Expand Down
6 changes: 3 additions & 3 deletions lldb/source/Plugins/MemoryHistory/asan/MemoryHistoryASan.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -162,7 +162,6 @@ HistoryThreads MemoryHistoryASan::GetHistoryThreads(lldb::addr_t address) {
ExecutionContext exe_ctx(frame_sp);
ValueObjectSP return_value_sp;
StreamString expr;
Status eval_error;
expr.Printf(memory_history_asan_command_format, address, address);

EvaluateExpressionOptions options;
Expand All @@ -176,11 +175,12 @@ HistoryThreads MemoryHistoryASan::GetHistoryThreads(lldb::addr_t address) {
options.SetLanguage(eLanguageTypeObjC_plus_plus);

ExpressionResults expr_result = UserExpression::Evaluate(
exe_ctx, options, expr.GetString(), "", return_value_sp, eval_error);
exe_ctx, options, expr.GetString(), "", return_value_sp);
if (expr_result != eExpressionCompleted) {
StreamString ss;
ss << "cannot evaluate AddressSanitizer expression:\n";
ss << eval_error.AsCString();
if (return_value_sp)
ss << return_value_sp->GetError().AsCString();
Debugger::ReportWarning(ss.GetString().str(),
process_sp->GetTarget().GetDebugger().GetID());
return result;
Expand Down
63 changes: 0 additions & 63 deletions lldb/source/Plugins/ObjectFile/Mach-O/ObjectFileMachO.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3768,7 +3768,6 @@ void ObjectFileMachO::ParseSymtab(Symtab &symtab) {

SymbolType type = eSymbolTypeInvalid;
SectionSP symbol_section;
lldb::addr_t symbol_byte_size = 0;
bool add_nlist = true;
bool is_gsym = false;
bool demangled_is_synthesized = false;
Expand Down Expand Up @@ -4354,47 +4353,6 @@ void ObjectFileMachO::ParseSymtab(Symtab &symtab) {

if (symbol_section) {
const addr_t section_file_addr = symbol_section->GetFileAddress();
if (symbol_byte_size == 0 && function_starts_count > 0) {
addr_t symbol_lookup_file_addr = nlist.n_value;
// Do an exact address match for non-ARM addresses, else get the
// closest since the symbol might be a thumb symbol which has an
// address with bit zero set.
FunctionStarts::Entry *func_start_entry =
function_starts.FindEntry(symbol_lookup_file_addr, !is_arm);
if (is_arm && func_start_entry) {
// Verify that the function start address is the symbol address
// (ARM) or the symbol address + 1 (thumb).
if (func_start_entry->addr != symbol_lookup_file_addr &&
func_start_entry->addr != (symbol_lookup_file_addr + 1)) {
// Not the right entry, NULL it out...
func_start_entry = nullptr;
}
}
if (func_start_entry) {
func_start_entry->data = true;

addr_t symbol_file_addr = func_start_entry->addr;
if (is_arm)
symbol_file_addr &= THUMB_ADDRESS_BIT_MASK;

const FunctionStarts::Entry *next_func_start_entry =
function_starts.FindNextEntry(func_start_entry);
const addr_t section_end_file_addr =
section_file_addr + symbol_section->GetByteSize();
if (next_func_start_entry) {
addr_t next_symbol_file_addr = next_func_start_entry->addr;
// Be sure the clear the Thumb address bit when we calculate the
// size from the current and next address
if (is_arm)
next_symbol_file_addr &= THUMB_ADDRESS_BIT_MASK;
symbol_byte_size = std::min<lldb::addr_t>(
next_symbol_file_addr - symbol_file_addr,
section_end_file_addr - symbol_file_addr);
} else {
symbol_byte_size = section_end_file_addr - symbol_file_addr;
}
}
}
symbol_value -= section_file_addr;
}

Expand Down Expand Up @@ -4501,9 +4459,6 @@ void ObjectFileMachO::ParseSymtab(Symtab &symtab) {
if (nlist.n_desc & N_WEAK_REF)
sym[sym_idx].SetIsWeak(true);

if (symbol_byte_size > 0)
sym[sym_idx].SetByteSize(symbol_byte_size);

if (demangled_is_synthesized)
sym[sym_idx].SetDemangledNameIsSynthesized(true);

Expand Down Expand Up @@ -4622,23 +4577,7 @@ void ObjectFileMachO::ParseSymtab(Symtab &symtab) {
Address symbol_addr;
if (module_sp->ResolveFileAddress(symbol_file_addr, symbol_addr)) {
SectionSP symbol_section(symbol_addr.GetSection());
uint32_t symbol_byte_size = 0;
if (symbol_section) {
const addr_t section_file_addr = symbol_section->GetFileAddress();
const FunctionStarts::Entry *next_func_start_entry =
function_starts.FindNextEntry(func_start_entry);
const addr_t section_end_file_addr =
section_file_addr + symbol_section->GetByteSize();
if (next_func_start_entry) {
addr_t next_symbol_file_addr = next_func_start_entry->addr;
if (is_arm)
next_symbol_file_addr &= THUMB_ADDRESS_BIT_MASK;
symbol_byte_size = std::min<lldb::addr_t>(
next_symbol_file_addr - symbol_file_addr,
section_end_file_addr - symbol_file_addr);
} else {
symbol_byte_size = section_end_file_addr - symbol_file_addr;
}
sym[sym_idx].SetID(synthetic_sym_id++);
// Don't set the name for any synthetic symbols, the Symbol
// object will generate one if needed when the name is accessed
Expand All @@ -4650,8 +4589,6 @@ void ObjectFileMachO::ParseSymtab(Symtab &symtab) {
add_symbol_addr(symbol_addr.GetFileAddress());
if (symbol_flags)
sym[sym_idx].SetFlags(symbol_flags);
if (symbol_byte_size)
sym[sym_idx].SetByteSize(symbol_byte_size);
++sym_idx;
}
}
Expand Down
9 changes: 4 additions & 5 deletions lldb/source/Plugins/Platform/POSIX/PlatformPOSIX.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -536,12 +536,11 @@ Status PlatformPOSIX::EvaluateLibdlExpression(
// don't do the work to trap them.
expr_options.SetTimeout(process->GetUtilityExpressionTimeout());

Status expr_error;
ExpressionResults result =
UserExpression::Evaluate(exe_ctx, expr_options, expr_cstr, expr_prefix,
result_valobj_sp, expr_error);
ExpressionResults result = UserExpression::Evaluate(
exe_ctx, expr_options, expr_cstr, expr_prefix, result_valobj_sp);
if (result != eExpressionCompleted)
return expr_error;
return result_valobj_sp ? result_valobj_sp->GetError().Clone()
: Status("unknown error");

if (result_valobj_sp->GetError().Fail())
return result_valobj_sp->GetError().Clone();
Expand Down
7 changes: 3 additions & 4 deletions lldb/source/Plugins/Platform/Windows/PlatformWindows.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -798,13 +798,12 @@ extern "C" {
options.SetTrapExceptions(false);
options.SetTimeout(process->GetUtilityExpressionTimeout());

Status error;
ExpressionResults result = UserExpression::Evaluate(
context, options, expression, kLoaderDecls, value, error);
context, options, expression, kLoaderDecls, value);
if (result != eExpressionCompleted)
return error;
return value ? value->GetError().Clone() : Status("unknown error");

if (value->GetError().Fail())
if (value && value->GetError().Fail())
return value->GetError().Clone();

return Status();
Expand Down
40 changes: 25 additions & 15 deletions lldb/source/Plugins/Process/elf-core/ProcessElfCore.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -224,15 +224,15 @@ Status ProcessElfCore::DoLoadCore() {
ArchSpec core_arch(m_core_module_sp->GetArchitecture());
target_arch.MergeFrom(core_arch);
GetTarget().SetArchitecture(target_arch);

SetUnixSignals(UnixSignals::Create(GetArchitecture()));

// Ensure we found at least one thread that was stopped on a signal.
bool siginfo_signal_found = false;
bool prstatus_signal_found = false;
// Check we found a signal in a SIGINFO note.
for (const auto &thread_data : m_thread_data) {
if (thread_data.signo != 0)
if (thread_data.siginfo.si_signo != 0)
siginfo_signal_found = true;
if (thread_data.prstatus_sig != 0)
prstatus_signal_found = true;
Expand All @@ -242,10 +242,10 @@ Status ProcessElfCore::DoLoadCore() {
// PRSTATUS note.
if (prstatus_signal_found) {
for (auto &thread_data : m_thread_data)
thread_data.signo = thread_data.prstatus_sig;
thread_data.siginfo.si_signo = thread_data.prstatus_sig;
} else if (m_thread_data.size() > 0) {
// If all else fails force the first thread to be SIGSTOP
m_thread_data.begin()->signo =
m_thread_data.begin()->siginfo.si_signo =
GetUnixSignals()->GetSignalNumberFromName("SIGSTOP");
}
}
Expand Down Expand Up @@ -276,8 +276,13 @@ Status ProcessElfCore::DoLoadCore() {
}

void ProcessElfCore::UpdateBuildIdForNTFileEntries() {
Log *log = GetLog(LLDBLog::Process);
for (NT_FILE_Entry &entry : m_nt_file_entries) {
entry.uuid = FindBuidIdInCoreMemory(entry.start);
if (log && entry.uuid.IsValid())
LLDB_LOGF(log, "%s found UUID @ %16.16" PRIx64 ": %s \"%s\"",
__FUNCTION__, entry.start, entry.uuid.GetAsString().c_str(),
entry.path.c_str());
}
}

Expand Down Expand Up @@ -493,7 +498,7 @@ static void ParseFreeBSDPrStatus(ThreadData &thread_data,
else
offset += 16;

thread_data.signo = data.GetU32(&offset); // pr_cursig
thread_data.siginfo.si_signo = data.GetU32(&offset); // pr_cursig
thread_data.tid = data.GetU32(&offset); // pr_pid
if (lp64)
offset += 4;
Expand Down Expand Up @@ -576,7 +581,7 @@ static void ParseOpenBSDProcInfo(ThreadData &thread_data,
return;

offset += 4;
thread_data.signo = data.GetU32(&offset);
thread_data.siginfo.si_signo = data.GetU32(&offset);
}

llvm::Expected<std::vector<CoreNote>>
Expand Down Expand Up @@ -814,15 +819,15 @@ llvm::Error ProcessElfCore::parseNetBSDNotes(llvm::ArrayRef<CoreNote> notes) {
// Signal targeted at the whole process.
if (siglwp == 0) {
for (auto &data : m_thread_data)
data.signo = signo;
data.siginfo.si_signo = signo;
}
// Signal destined for a particular LWP.
else {
bool passed = false;

for (auto &data : m_thread_data) {
if (data.tid == siglwp) {
data.signo = signo;
data.siginfo.si_signo = signo;
passed = true;
break;
}
Expand Down Expand Up @@ -875,7 +880,7 @@ llvm::Error ProcessElfCore::parseOpenBSDNotes(llvm::ArrayRef<CoreNote> notes) {
/// - NT_SIGINFO - Information about the signal that terminated the process
/// - NT_AUXV - Process auxiliary vector
/// - NT_FILE - Files mapped into memory
///
///
/// Additionally, for each thread in the process the core file will contain at
/// least the NT_PRSTATUS note, containing the thread id and general purpose
/// registers. It may include additional notes for other register sets (floating
Expand Down Expand Up @@ -925,12 +930,12 @@ llvm::Error ProcessElfCore::parseLinuxNotes(llvm::ArrayRef<CoreNote> notes) {
break;
}
case ELF::NT_SIGINFO: {
const lldb_private::UnixSignals &unix_signals = *GetUnixSignals();
ELFLinuxSigInfo siginfo;
Status status = siginfo.Parse(note.data, arch);
Status status = siginfo.Parse(note.data, arch, unix_signals);
if (status.Fail())
return status.ToError();
thread_data.signo = siginfo.si_signo;
thread_data.code = siginfo.si_code;
thread_data.siginfo = siginfo;
break;
}
case ELF::NT_FILE: {
Expand Down Expand Up @@ -1034,15 +1039,20 @@ UUID ProcessElfCore::FindBuidIdInCoreMemory(lldb::addr_t address) {
std::vector<uint8_t> note_bytes;
note_bytes.resize(program_header.p_memsz);

byte_read = ReadMemory(program_header.p_vaddr, note_bytes.data(),
program_header.p_memsz, error);
// We need to slide the address of the p_vaddr as these values don't get
// relocated in memory.
const lldb::addr_t vaddr = program_header.p_vaddr + address;
byte_read =
ReadMemory(vaddr, note_bytes.data(), program_header.p_memsz, error);
if (byte_read != program_header.p_memsz)
continue;
DataExtractor segment_data(note_bytes.data(), note_bytes.size(),
GetByteOrder(), addr_size);
auto notes_or_error = parseSegment(segment_data);
if (!notes_or_error)
if (!notes_or_error) {
llvm::consumeError(notes_or_error.takeError());
return invalid_uuid;
}
for (const CoreNote &note : *notes_or_error) {
if (note.info.n_namesz == 4 &&
note.info.n_type == llvm::ELF::NT_GNU_BUILD_ID &&
Expand Down
67 changes: 62 additions & 5 deletions lldb/source/Plugins/Process/elf-core/ThreadElfCore.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
#include "lldb/Target/RegisterContext.h"
#include "lldb/Target/StopInfo.h"
#include "lldb/Target/Target.h"
#include "lldb/Target/UnixSignals.h"
#include "lldb/Target/Unwind.h"
#include "lldb/Utility/DataExtractor.h"
#include "lldb/Utility/LLDBLog.h"
Expand Down Expand Up @@ -50,8 +51,8 @@ using namespace lldb_private;
// Construct a Thread object with given data
ThreadElfCore::ThreadElfCore(Process &process, const ThreadData &td)
: Thread(process, td.tid), m_thread_name(td.name), m_thread_reg_ctx_sp(),
m_signo(td.signo), m_code(td.code), m_gpregset_data(td.gpregset),
m_notes(td.notes) {}
m_gpregset_data(td.gpregset), m_notes(td.notes),
m_siginfo(std::move(td.siginfo)) {}

ThreadElfCore::~ThreadElfCore() { DestroyThread(); }

Expand Down Expand Up @@ -246,8 +247,21 @@ bool ThreadElfCore::CalculateStopInfo() {
if (!process_sp)
return false;

lldb::UnixSignalsSP unix_signals_sp(process_sp->GetUnixSignals());
if (!unix_signals_sp)
return false;

const char *sig_description;
std::string description = m_siginfo.GetDescription(*unix_signals_sp);
if (description.empty())
sig_description = nullptr;
else
sig_description = description.c_str();

SetStopInfo(StopInfo::CreateStopReasonWithSignal(
*this, m_signo, /*description=*/nullptr, m_code));
*this, m_siginfo.si_signo, sig_description, m_siginfo.si_code));

SetStopInfo(m_stop_info_sp);
return true;
}

Expand Down Expand Up @@ -547,21 +561,64 @@ size_t ELFLinuxSigInfo::GetSize(const lldb_private::ArchSpec &arch) {
}
}

Status ELFLinuxSigInfo::Parse(const DataExtractor &data, const ArchSpec &arch) {
Status ELFLinuxSigInfo::Parse(const DataExtractor &data, const ArchSpec &arch,
const lldb_private::UnixSignals &unix_signals) {
Status error;
if (GetSize(arch) > data.GetByteSize()) {
uint64_t size = GetSize(arch);
if (size > data.GetByteSize()) {
error = Status::FromErrorStringWithFormat(
"NT_SIGINFO size should be %zu, but the remaining bytes are: %" PRIu64,
GetSize(arch), data.GetByteSize());
return error;
}

// Set that we've parsed the siginfo from a SIGINFO note.
note_type = eNT_SIGINFO;
// Parsing from a 32 bit ELF core file, and populating/reusing the structure
// properly, because the struct is for the 64 bit version
offset_t offset = 0;
si_signo = data.GetU32(&offset);
si_errno = data.GetU32(&offset);
si_code = data.GetU32(&offset);
// 64b ELF have a 4 byte pad.
if (data.GetAddressByteSize() == 8)
offset += 4;
// Not every stop signal has a valid address, but that will get resolved in
// the unix_signals.GetSignalDescription() call below.
if (unix_signals.GetShouldStop(si_signo)) {
// Instead of memcpy we call all these individually as the extractor will
// handle endianness for us.
sigfault.si_addr = data.GetAddress(&offset);
sigfault.si_addr_lsb = data.GetU16(&offset);
if (data.GetByteSize() - offset >= sizeof(sigfault.bounds)) {
sigfault.bounds._addr_bnd._lower = data.GetAddress(&offset);
sigfault.bounds._addr_bnd._upper = data.GetAddress(&offset);
sigfault.bounds._pkey = data.GetU32(&offset);
} else {
// Set these to 0 so we don't use bogus data for the description.
sigfault.bounds._addr_bnd._lower = 0;
sigfault.bounds._addr_bnd._upper = 0;
sigfault.bounds._pkey = 0;
}
}

return error;
}

std::string ELFLinuxSigInfo::GetDescription(
const lldb_private::UnixSignals &unix_signals) const {
if (unix_signals.GetShouldStop(si_signo) && note_type == eNT_SIGINFO) {
if (sigfault.bounds._addr_bnd._upper != 0)
return unix_signals.GetSignalDescription(
si_signo, si_code, sigfault.si_addr, sigfault.bounds._addr_bnd._lower,
sigfault.bounds._addr_bnd._upper);
else
return unix_signals.GetSignalDescription(si_signo, si_code,
sigfault.si_addr);
}

// This looks weird, but there is an existing pattern where we don't pass a
// description to keep up with that, we return empty here, and then the above
// function will set the description whether or not this is empty.
return std::string();
}
44 changes: 34 additions & 10 deletions lldb/source/Plugins/Process/elf-core/ThreadElfCore.h
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,8 @@ class ProcessInstanceInfo;
#undef si_signo
#undef si_code
#undef si_errno
#undef si_addr
#undef si_addr_lsb

struct ELFLinuxPrStatus {
int32_t si_signo;
Expand Down Expand Up @@ -76,14 +78,36 @@ static_assert(sizeof(ELFLinuxPrStatus) == 112,
"sizeof ELFLinuxPrStatus is not correct!");

struct ELFLinuxSigInfo {
int32_t si_signo;
int32_t si_code;

int32_t si_signo; // Order matters for the first 3.
int32_t si_errno;
int32_t si_code;
// Copied from siginfo_t so we don't have to include signal.h on non 'Nix
// builds.
struct {
lldb::addr_t si_addr; /* faulting insn/memory ref. */
short int si_addr_lsb; /* Valid LSB of the reported address. */
union {
/* used when si_code=SEGV_BNDERR */
struct {
lldb::addr_t _lower;
lldb::addr_t _upper;
} _addr_bnd;
/* used when si_code=SEGV_PKUERR */
uint32_t _pkey;
} bounds;
} sigfault;

enum { eUnspecified, eNT_SIGINFO } note_type;

ELFLinuxSigInfo();

lldb_private::Status Parse(const lldb_private::DataExtractor &data,
const lldb_private::ArchSpec &arch);
const lldb_private::ArchSpec &arch,
const lldb_private::UnixSignals &unix_signals);

std::string
GetDescription(const lldb_private::UnixSignals &unix_signals) const;

// Return the bytesize of the structure
// 64 bit - just sizeof
Expand All @@ -93,7 +117,7 @@ struct ELFLinuxSigInfo {
static size_t GetSize(const lldb_private::ArchSpec &arch);
};

static_assert(sizeof(ELFLinuxSigInfo) == 12,
static_assert(sizeof(ELFLinuxSigInfo) == 56,
"sizeof ELFLinuxSigInfo is not correct!");

// PRPSINFO structure's size differs based on architecture.
Expand Down Expand Up @@ -142,10 +166,9 @@ struct ThreadData {
lldb_private::DataExtractor gpregset;
std::vector<lldb_private::CoreNote> notes;
lldb::tid_t tid;
int signo = 0;
int code = 0;
int prstatus_sig = 0;
std::string name;
ELFLinuxSigInfo siginfo;
int prstatus_sig = 0;
};

class ThreadElfCore : public lldb_private::Thread {
Expand Down Expand Up @@ -176,16 +199,17 @@ class ThreadElfCore : public lldb_private::Thread {
m_thread_name.clear();
}

void CreateStopFromSigInfo(const ELFLinuxSigInfo &siginfo,
const lldb_private::UnixSignals &unix_signals);

protected:
// Member variables.
std::string m_thread_name;
lldb::RegisterContextSP m_thread_reg_ctx_sp;

int m_signo;
int m_code;

lldb_private::DataExtractor m_gpregset_data;
std::vector<lldb_private::CoreNote> m_notes;
ELFLinuxSigInfo m_siginfo;

bool CalculateStopInfo() override;
};
Expand Down
21 changes: 7 additions & 14 deletions lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserClang.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,6 @@
#include "clang/AST/Type.h"
#include "clang/Basic/Specifiers.h"
#include "llvm/ADT/StringExtras.h"
#include "llvm/DebugInfo/DWARF/DWARFTypePrinter.h"
#include "llvm/Demangle/Demangle.h"

#include <map>
Expand Down Expand Up @@ -827,11 +826,11 @@ std::string DWARFASTParserClang::GetDIEClassTemplateParams(DWARFDIE die) {
if (llvm::StringRef(die.GetName()).contains("<"))
return {};

std::string name;
llvm::raw_string_ostream os(name);
llvm::DWARFTypePrinter<DWARFDIE> type_printer(os);
type_printer.appendAndTerminateTemplateParameters(die);
return name;
TypeSystemClang::TemplateParameterInfos template_param_infos;
if (ParseTemplateParameterInfos(die, template_param_infos))
return m_ast.PrintTemplateParams(template_param_infos);

return {};
}

void DWARFASTParserClang::MapDeclDIEToDefDIE(
Expand Down Expand Up @@ -1619,9 +1618,9 @@ void DWARFASTParserClang::GetUniqueTypeNameAndDeclaration(
case DW_TAG_structure_type:
case DW_TAG_union_type: {
if (const char *class_union_struct_name = parent_decl_ctx_die.GetName()) {
qualified_name.insert(
0, GetDIEClassTemplateParams(parent_decl_ctx_die));
qualified_name.insert(0, "::");
qualified_name.insert(0,
GetDIEClassTemplateParams(parent_decl_ctx_die));
qualified_name.insert(0, class_union_struct_name);
}
parent_decl_ctx_die = parent_decl_ctx_die.GetParentDeclContextDIE();
Expand Down Expand Up @@ -1674,12 +1673,6 @@ DWARFASTParserClang::ParseStructureLikeDIE(const SymbolContext &sc,
if (attrs.name) {
GetUniqueTypeNameAndDeclaration(die, cu_language, unique_typename,
unique_decl);
if (log) {
dwarf->GetObjectFile()->GetModule()->LogMessage(
log, "SymbolFileDWARF({0:p}) - {1:x16}: {2} has unique name: {3} ",
static_cast<void *>(this), die.GetID(), DW_TAG_value_to_name(tag),
unique_typename.AsCString());
}
if (UniqueDWARFASTType *unique_ast_entry_type =
dwarf->GetUniqueDWARFASTTypeMap().Find(
unique_typename, die, unique_decl, byte_size,
Expand Down
8 changes: 0 additions & 8 deletions lldb/source/Plugins/SymbolFile/DWARF/DWARFBaseDIE.h
Original file line number Diff line number Diff line change
Expand Up @@ -24,11 +24,9 @@ class DWARFUnit;
class DWARFDebugInfoEntry;
class DWARFDeclContext;
class SymbolFileDWARF;
class DWARFFormValue;

class DWARFBaseDIE {
public:
using DWARFFormValue = dwarf::DWARFFormValue;
DWARFBaseDIE() = default;

DWARFBaseDIE(DWARFUnit *cu, DWARFDebugInfoEntry *die)
Expand Down Expand Up @@ -119,12 +117,6 @@ class DWARFBaseDIE {
enum class Recurse : bool { no, yes };
DWARFAttributes GetAttributes(Recurse recurse = Recurse::yes) const;

// The following methods use LLVM naming convension in order to be are used by
// LLVM libraries.
dw_tag_t getTag() const { return Tag(); }

const char *getShortName() const { return GetName(); }

protected:
DWARFUnit *m_cu = nullptr;
DWARFDebugInfoEntry *m_die = nullptr;
Expand Down
37 changes: 0 additions & 37 deletions lldb/source/Plugins/SymbolFile/DWARF/DWARFDIE.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -572,43 +572,6 @@ bool DWARFDIE::GetDIENamesAndRanges(
return false;
}

// The following methods use LLVM naming convension in order to be are used by
// LLVM libraries.
llvm::iterator_range<DWARFDIE::child_iterator> DWARFDIE::children() const {
return llvm::make_range(child_iterator(*this), child_iterator());
}

DWARFDIE::child_iterator DWARFDIE::begin() const {
return child_iterator(*this);
}

DWARFDIE::child_iterator DWARFDIE::end() const { return child_iterator(); }

std::optional<DWARFFormValue> DWARFDIE::find(const dw_attr_t attr) const {
DWARFFormValue form_value;
if (m_die->GetAttributeValue(m_cu, attr, form_value, nullptr, false))
return form_value;
return std::nullopt;
}

std::optional<uint64_t> DWARFDIE::getLanguage() const {
if (IsValid())
return m_cu->GetDWARFLanguageType();
return std::nullopt;
}

DWARFDIE DWARFDIE::resolveReferencedType(dw_attr_t attr) const {
return GetReferencedDIE(attr);
}

DWARFDIE DWARFDIE::resolveReferencedType(DWARFFormValue v) const {
if (IsValid())
return v.Reference();
return {};
}

DWARFDIE DWARFDIE::resolveTypeUnitReference() const {
if (DWARFDIE reference = GetReferencedDIE(DW_AT_signature))
return reference;
return *this;
}
17 changes: 0 additions & 17 deletions lldb/source/Plugins/SymbolFile/DWARF/DWARFDIE.h
Original file line number Diff line number Diff line change
Expand Up @@ -103,25 +103,8 @@ class DWARFDIE : public DWARFBaseDIE {
std::optional<int> &call_line, std::optional<int> &call_column,
DWARFExpressionList *frame_base) const;

// The following methods use LLVM naming convension in order to be are used by
// LLVM libraries.
std::optional<uint64_t> getLanguage() const;

DWARFDIE getParent() const { return GetParent(); }

DWARFDIE resolveReferencedType(dw_attr_t attr) const;

DWARFDIE resolveReferencedType(DWARFFormValue v) const;

DWARFDIE resolveTypeUnitReference() const;

std::optional<DWARFFormValue> find(const dw_attr_t attr) const;

/// The range of all the children of this DIE.
llvm::iterator_range<child_iterator> children() const;

child_iterator begin() const;
child_iterator end() const;
};

class DWARFDIE::child_iterator
Expand Down
25 changes: 0 additions & 25 deletions lldb/source/Plugins/SymbolFile/DWARF/DWARFFormValue.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -574,31 +574,6 @@ uint64_t DWARFFormValue::Reference(dw_offset_t base_offset) const {
}
}

std::optional<uint64_t> DWARFFormValue::getAsUnsignedConstant() const {
if ((!IsDataForm(m_form)) || m_form == lldb_private::dwarf::DW_FORM_sdata)
return std::nullopt;
return m_value.uval;
}

std::optional<int64_t> DWARFFormValue::getAsSignedConstant() const {
if ((!IsDataForm(m_form)) ||
(m_form == lldb_private::dwarf::DW_FORM_udata &&
uint64_t(std::numeric_limits<int64_t>::max()) < m_value.uval))
return std::nullopt;
switch (m_form) {
case lldb_private::dwarf::DW_FORM_data4:
return int32_t(m_value.uval);
case lldb_private::dwarf::DW_FORM_data2:
return int16_t(m_value.uval);
case lldb_private::dwarf::DW_FORM_data1:
return int8_t(m_value.uval);
case lldb_private::dwarf::DW_FORM_sdata:
case lldb_private::dwarf::DW_FORM_data8:
default:
return m_value.sval;
}
}

const uint8_t *DWARFFormValue::BlockData() const { return m_value.data; }

bool DWARFFormValue::IsBlockForm(const dw_form_t form) {
Expand Down
6 changes: 0 additions & 6 deletions lldb/source/Plugins/SymbolFile/DWARF/DWARFFormValue.h
Original file line number Diff line number Diff line change
Expand Up @@ -76,12 +76,6 @@ class DWARFFormValue {
void Clear();
static bool FormIsSupported(dw_form_t form);

// The following methods use LLVM naming convension in order to be are used by
// LLVM libraries.
std::optional<uint64_t> getAsUnsignedConstant() const;
std::optional<int64_t> getAsSignedConstant() const;
const char *getAsCString() const { return AsCString(); }

protected:
// Compile unit where m_value was located.
// It may be different from compile unit where m_value refers to.
Expand Down
36 changes: 27 additions & 9 deletions lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@
#include "SymbolFileDWARF.h"

#include "llvm/DebugInfo/DWARF/DWARFDebugLoc.h"
#include "llvm/DebugInfo/DWARF/DWARFTypePrinter.h"
#include "llvm/Support/Casting.h"
#include "llvm/Support/FileUtilities.h"
#include "llvm/Support/Format.h"
Expand Down Expand Up @@ -2811,14 +2810,33 @@ void SymbolFileDWARF::FindTypes(const TypeQuery &query, TypeResults &results) {
return true; // Keep iterating over index types, language mismatch.
}

std::string qualified_name;
llvm::raw_string_ostream os(qualified_name);
llvm::DWARFTypePrinter<DWARFDIE> type_printer(os);
type_printer.appendQualifiedName(die);
TypeQuery die_query(qualified_name, e_exact_match);
if (query.ContextMatches(die_query.GetContextRef()))
if (Type *matching_type = ResolveType(die, true, true))
results.InsertUnique(matching_type->shared_from_this());
// Check the context matches
std::vector<lldb_private::CompilerContext> die_context;
if (query.GetModuleSearch())
die_context = die.GetDeclContext();
else
die_context = die.GetTypeLookupContext();
assert(!die_context.empty());
if (!query_simple.ContextMatches(die_context))
return true; // Keep iterating over index types, context mismatch.

// Try to resolve the type.
if (Type *matching_type = ResolveType(die, true, true)) {
ConstString name = matching_type->GetQualifiedName();
// We have found a type that still might not match due to template
// parameters. If we create a new TypeQuery that uses the new type's
// fully qualified name, we can find out if this type matches at all
// context levels. We can't use just the "match_simple" context
// because all template parameters were stripped off. The fully
// qualified name of the type will have the template parameters and
// will allow us to make sure it matches correctly.
TypeQuery die_query(name.GetStringRef(),
TypeQueryOptions::e_exact_match);
if (!query.ContextMatches(die_query.GetContextRef()))
return true; // Keep iterating over index types, context mismatch.

results.InsertUnique(matching_type->shared_from_this());
}
return !results.Done(query); // Keep iterating if we aren't done.
});
if (results.Done(query)) {
Expand Down
20 changes: 20 additions & 0 deletions lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1403,6 +1403,26 @@ static TemplateParameterList *CreateTemplateParameterList(
return template_param_list;
}

std::string TypeSystemClang::PrintTemplateParams(
const TemplateParameterInfos &template_param_infos) {
llvm::SmallVector<NamedDecl *, 8> ignore;
clang::TemplateParameterList *template_param_list =
CreateTemplateParameterList(getASTContext(), template_param_infos,
ignore);
llvm::SmallVector<clang::TemplateArgument, 2> args(
template_param_infos.GetArgs());
if (template_param_infos.hasParameterPack()) {
llvm::ArrayRef<TemplateArgument> pack_args =
template_param_infos.GetParameterPackArgs();
args.append(pack_args.begin(), pack_args.end());
}
std::string str;
llvm::raw_string_ostream os(str);
clang::printTemplateArgumentList(os, args, GetTypePrintingPolicy(),
template_param_list);
return str;
}

clang::FunctionTemplateDecl *TypeSystemClang::CreateFunctionTemplateDecl(
clang::DeclContext *decl_ctx, OptionalClangModuleID owning_module,
clang::FunctionDecl *func_decl,
Expand Down
4 changes: 4 additions & 0 deletions lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.h
Original file line number Diff line number Diff line change
Expand Up @@ -1148,6 +1148,10 @@ class TypeSystemClang : public TypeSystem {

bool SetDeclIsForcefullyCompleted(const clang::TagDecl *td);

/// Return the template parameters (including surrounding <>) in string form.
std::string
PrintTemplateParams(const TemplateParameterInfos &template_param_infos);

private:
/// Returns the PrintingPolicy used when generating the internal type names.
/// These type names are mostly used for the formatter selection.
Expand Down
19 changes: 10 additions & 9 deletions lldb/source/Symbol/Block.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -252,19 +252,20 @@ bool Block::GetRangeContainingAddress(const Address &addr,
Function *function = CalculateSymbolContextFunction();
if (function) {
const AddressRange &func_range = function->GetAddressRange();
if (addr.GetSection() == func_range.GetBaseAddress().GetSection()) {
const addr_t addr_offset = addr.GetOffset();
const addr_t func_offset = func_range.GetBaseAddress().GetOffset();
if (addr_offset >= func_offset &&
addr_offset < func_offset + func_range.GetByteSize()) {
addr_t offset = addr_offset - func_offset;
if (addr.GetModule() == func_range.GetBaseAddress().GetModule()) {
const addr_t file_addr = addr.GetFileAddress();
const addr_t func_file_addr =
func_range.GetBaseAddress().GetFileAddress();
if (file_addr >= func_file_addr &&
file_addr < func_file_addr + func_range.GetByteSize()) {
addr_t offset = file_addr - func_file_addr;

const Range *range_ptr = m_ranges.FindEntryThatContains(offset);

if (range_ptr) {
range.GetBaseAddress() = func_range.GetBaseAddress();
range.GetBaseAddress().SetOffset(func_offset +
range_ptr->GetRangeBase());
range.GetBaseAddress() =
Address(func_file_addr + range_ptr->GetRangeBase(),
addr.GetModule()->GetSectionList());
range.SetByteSize(range_ptr->GetByteSize());
return true;
}
Expand Down
10 changes: 6 additions & 4 deletions lldb/source/Symbol/SymbolContext.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -102,10 +102,11 @@ bool SymbolContext::DumpStopContext(
s->PutCStringColorHighlighted(name.GetStringRef(), settings);
}

if (addr.IsValid()) {
if (addr_t file_addr = addr.GetFileAddress();
file_addr != LLDB_INVALID_ADDRESS) {
const addr_t function_offset =
addr.GetOffset() -
function->GetAddressRange().GetBaseAddress().GetOffset();
file_addr -
function->GetAddressRange().GetBaseAddress().GetFileAddress();
if (!show_function_name) {
// Print +offset even if offset is 0
dumped_something = true;
Expand All @@ -126,7 +127,8 @@ bool SymbolContext::DumpStopContext(
lldb_private::AddressRange block_range;
if (inlined_block->GetRangeContainingAddress(addr, block_range)) {
const addr_t inlined_function_offset =
addr.GetOffset() - block_range.GetBaseAddress().GetOffset();
addr.GetFileAddress() -
block_range.GetBaseAddress().GetFileAddress();
if (inlined_function_offset) {
s->Printf(" + %" PRIu64, inlined_function_offset);
}
Expand Down
8 changes: 5 additions & 3 deletions lldb/source/Target/StopInfo.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -932,10 +932,9 @@ class StopInfoWatchpoint : public StopInfo {
expr_options.SetUnwindOnError(true);
expr_options.SetIgnoreBreakpoints(true);
ValueObjectSP result_value_sp;
Status error;
result_code = UserExpression::Evaluate(
exe_ctx, expr_options, wp_sp->GetConditionText(),
llvm::StringRef(), result_value_sp, error);
llvm::StringRef(), result_value_sp);

if (result_code == eExpressionCompleted) {
if (result_value_sp) {
Expand All @@ -959,7 +958,10 @@ class StopInfoWatchpoint : public StopInfo {
}
}
} else {
const char *err_str = error.AsCString("<unknown error>");
const char *err_str = "<unknown error>";
if (result_value_sp)
err_str = result_value_sp->GetError().AsCString();

LLDB_LOGF(log, "Error evaluating condition: \"%s\"\n", err_str);

StreamString strm;
Expand Down
11 changes: 3 additions & 8 deletions lldb/source/Target/Target.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2842,14 +2842,9 @@ ExpressionResults Target::EvaluateExpression(
execution_results = eExpressionCompleted;
} else {
llvm::StringRef prefix = GetExpressionPrefixContents();
Status error;
execution_results = UserExpression::Evaluate(exe_ctx, options, expr, prefix,
result_valobj_sp, error,
fixed_expression, ctx_obj);
// Pass up the error by wrapping it inside an error result.
if (error.Fail() && !result_valobj_sp)
result_valobj_sp = ValueObjectConstResult::Create(
exe_ctx.GetBestExecutionContextScope(), std::move(error));
execution_results =
UserExpression::Evaluate(exe_ctx, options, expr, prefix,
result_valobj_sp, fixed_expression, ctx_obj);
}

if (execution_results == eExpressionCompleted)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -195,6 +195,10 @@ def test_error_type(self):
value = frame.EvaluateExpression('#error("I am error.")')
error = value.GetError()
self.assertEqual(error.GetType(), lldb.eErrorTypeExpression)
value = frame.FindVariable("f")
self.assertTrue(value.IsValid())
desc = value.GetObjectDescription()
self.assertEqual(desc, None)

def test_command_expr_sbdata(self):
"""Test the structured diagnostics data"""
Expand Down
2 changes: 1 addition & 1 deletion lldb/test/API/commands/expression/fixits/TestFixIts.py
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ def test_with_target(self):
expr = "struct MyTy { int m; }; MyTy x; MyTy *ptr = &x; int m = ptr.m;"
value = frame.EvaluateExpression(expr, top_level_options)
# A successfully parsed top-level expression will yield an
# unknown error . If a parsing error would have happened we
# unknown error. If a parsing error would have happened we
# would get a different error kind, so let's check the error
# kind here.
self.assertEqual(value.GetError().GetCString(), "unknown error")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@
Test that memory tagging features work with Linux core files.
"""


import lldb
from lldbsuite.test.decorators import *
from lldbsuite.test.lldbtest import *
Expand Down Expand Up @@ -216,8 +215,7 @@ def test_mte_tag_fault_reason(self):
self.expect(
"bt",
substrs=[
"* thread #1, name = 'a.out.mte', stop reason = signal SIGSEGV: "
"sync tag check fault"
"* thread #1, name = 'a.out.mte', stop reason = SIGSEGV: sync tag check fault (fault address: 0xffff82c74010)"
],
)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@
API level it won't if we don't remove them there also.
"""


import lldb
from lldbsuite.test.decorators import *
from lldbsuite.test.lldbtest import *
Expand Down Expand Up @@ -199,7 +198,13 @@ def test_non_address_bit_memory_caching(self):
def test_non_address_bit_memory_corefile(self):
self.runCmd("target create --core corefile")

self.expect("thread list", substrs=["stopped", "stop reason = signal SIGSEGV"])
self.expect(
"thread list",
substrs=[
"stopped",
"stop reason = SIGSEGV: address not mapped to object (fault address: 0x0)",
],
)

# No caching (the program/corefile are the cache) and no writing
# to memory. So just check that tagged/untagged addresses read
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# RUN: %lldb -b -s %s -c %p/Inputs/x86-32-linux-multithread.core | FileCheck %s

thread list
# CHECK: * thread #1: tid = 330633, 0x080492d2, name = 'a.out', stop reason = signal SIGSEGV
# CHECK: * thread #1: tid = 330633, 0x080492d2, name = 'a.out', stop reason = SIGSEGV: address not mapped to object (fault address: 0x0)
# CHECK-NEXT: thread #2: tid = 330634, 0x080492dd, stop reason = signal 0
# CHECK-NEXT: thread #3: tid = 330635, 0x080492dd, stop reason = signal 0
# CHECK-NEXT: thread #4: tid = 330632, 0xf7f59549, stop reason = signal 0
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# RUN: %lldb -b -s %s -c %p/Inputs/x86-64-linux-multithread.core | FileCheck %s

thread list
# CHECK: * thread #1: tid = 329384, 0x0000000000401262, name = 'a.out', stop reason = signal SIGSEGV
# CHECK: * thread #1: tid = 329384, 0x0000000000401262, name = 'a.out', stop reason = SIGSEGV: address not mapped to object (fault address: 0x0)
# CHECK-NEXT: thread #2: tid = 329385, 0x000000000040126d, stop reason = signal 0
# CHECK-NEXT: thread #3: tid = 329386, 0x000000000040126d, stop reason = signal 0
# CHECK-NEXT: thread #4: tid = 329383, 0x00007fcf5582f762, stop reason = signal 0
Expand Down
167 changes: 167 additions & 0 deletions lldb/test/Shell/SymbolFile/DWARF/x86/discontinuous-inline-function.s
Original file line number Diff line number Diff line change
@@ -0,0 +1,167 @@
## Test that inline function resolution works when the function has been split
## into multiple discontinuous parts (and those parts are placed in different
## sections)

# RUN: llvm-mc -triple x86_64-pc-linux -filetype=obj %s -o %t
# RUN: %lldb %t -o "image lookup -v -n look_me_up" -o exit | FileCheck %s

# CHECK: 1 match found in {{.*}}
# CHECK: Summary: {{.*}}`foo + 6 [inlined] foo_inl + 1
# CHECK-NEXT: {{.*}}`foo + 5
# CHECK: Blocks: id = {{.*}}, ranges = [0x00000000-0x00000003)[0x00000004-0x00000008)
# CHECK-NEXT: id = {{.*}}, ranges = [0x00000001-0x00000002)[0x00000005-0x00000007), name = "foo_inl"

.text

.type foo,@function
foo:
nop
.Lfoo_inl:
nop
.Lfoo_inl_end:
nop
.Lfoo_end:
.size foo, .Lfoo_end-foo

bar:
nop
.Lbar_end:
.size bar, .Lbar_end-bar

.section .text.__part1,"ax",@progbits
foo.__part.1:
nop
.Lfoo_inl.__part.1:
nop
.type look_me_up,@function
look_me_up:
nop
.Lfoo_inl.__part.1_end:
nop
.Lfoo.__part.1_end:
.size foo.__part.1, .Lfoo.__part.1_end-foo.__part.1


.section .debug_abbrev,"",@progbits
.byte 1 # Abbreviation Code
.byte 17 # DW_TAG_compile_unit
.byte 1 # DW_CHILDREN_yes
.byte 37 # DW_AT_producer
.byte 8 # DW_FORM_string
.byte 19 # DW_AT_language
.byte 5 # DW_FORM_data2
.byte 17 # DW_AT_low_pc
.byte 1 # DW_FORM_addr
.byte 85 # DW_AT_ranges
.byte 35 # DW_FORM_rnglistx
.byte 116 # DW_AT_rnglists_base
.byte 23 # DW_FORM_sec_offset
.byte 0 # EOM(1)
.byte 0 # EOM(2)
.byte 2 # Abbreviation Code
.byte 46 # DW_TAG_subprogram
.byte 0 # DW_CHILDREN_no
.byte 3 # DW_AT_name
.byte 8 # DW_FORM_string
.byte 0 # EOM(1)
.byte 0 # EOM(2)
.byte 3 # Abbreviation Code
.byte 46 # DW_TAG_subprogram
.byte 1 # DW_CHILDREN_yes
.byte 85 # DW_AT_ranges
.byte 35 # DW_FORM_rnglistx
.byte 3 # DW_AT_name
.byte 8 # DW_FORM_string
.byte 0 # EOM(1)
.byte 0 # EOM(2)
.byte 4 # Abbreviation Code
.byte 29 # DW_TAG_inlined_subroutine
.byte 0 # DW_CHILDREN_no
.byte 85 # DW_AT_ranges
.byte 35 # DW_FORM_rnglistx
.byte 49 # DW_AT_abstract_origin
.byte 19 # DW_FORM_ref4
.byte 0 # EOM(1)
.byte 0 # EOM(2)
.byte 0 # EOM(3)

.section .debug_info,"",@progbits
.Lcu_begin0:
.long .Ldebug_info_end0-.Ldebug_info_start0 # Length of Unit
.Ldebug_info_start0:
.short 5 # DWARF version number
.byte 1 # DWARF Unit Type
.byte 8 # Address Size (in bytes)
.long .debug_abbrev # Offset Into Abbrev. Section
.byte 1 # Abbrev DW_TAG_compile_unit
.asciz "Hand-written DWARF" # DW_AT_producer
.short 29 # DW_AT_language
.quad 0 # DW_AT_low_pc
.byte 1 # DW_AT_ranges
.long .Lrnglists_table_base0 # DW_AT_rnglists_base

.byte 3 # Abbrev DW_TAG_subprogram
.byte 2 # DW_AT_ranges
.asciz "bar" # DW_AT_name
.byte 0 # End Of Children Mark

.Lfoo_inl_die:
.byte 2 # Abbrev DW_TAG_subprogram
.asciz "foo_inl" # DW_AT_name

.byte 3 # Abbrev DW_TAG_subprogram
.byte 0 # DW_AT_ranges
.asciz "foo" # DW_AT_name
.byte 4 # Abbrev DW_TAG_inlined_subroutine
.byte 3 # DW_AT_ranges
.long .Lfoo_inl_die-.Lcu_begin0 # DW_AT_abstract_origin
.byte 0 # End Of Children Mark

.byte 0 # End Of Children Mark
.Ldebug_info_end0:

.section .debug_rnglists,"",@progbits
.long .Ldebug_list_header_end0-.Ldebug_list_header_start0 # Length
.Ldebug_list_header_start0:
.short 5 # Version
.byte 8 # Address size
.byte 0 # Segment selector size
.long 4 # Offset entry count
.Lrnglists_table_base0:
.long .Ldebug_ranges0-.Lrnglists_table_base0
.long .Ldebug_ranges1-.Lrnglists_table_base0
.long .Ldebug_ranges2-.Lrnglists_table_base0
.long .Ldebug_ranges3-.Lrnglists_table_base0
.Ldebug_ranges0:
.byte 6 # DW_RLE_start_end
.quad foo
.quad .Lfoo_end
.byte 6 # DW_RLE_start_end
.quad foo.__part.1
.quad .Lfoo.__part.1_end
.byte 0 # DW_RLE_end_of_list
.Ldebug_ranges1:
.byte 6 # DW_RLE_start_end
.quad bar
.quad .Lbar_end
.byte 6 # DW_RLE_start_end
.quad foo.__part.1
.quad .Lfoo.__part.1_end
.byte 6 # DW_RLE_start_end
.quad foo
.quad .Lfoo_end
.byte 0 # DW_RLE_end_of_list
.Ldebug_ranges2:
.byte 6 # DW_RLE_start_end
.quad bar
.quad .Lbar_end
.byte 0 # DW_RLE_end_of_list
.Ldebug_ranges3:
.byte 6 # DW_RLE_start_end
.quad .Lfoo_inl
.quad .Lfoo_inl_end
.byte 6 # DW_RLE_start_end
.quad .Lfoo_inl.__part.1
.quad .Lfoo_inl.__part.1_end
.byte 0 # DW_RLE_end_of_list
.Ldebug_list_header_end0:
36 changes: 0 additions & 36 deletions lldb/test/Shell/SymbolFile/DWARF/x86/simplified-template-names.cpp

This file was deleted.

Loading