[Offload] add 'olIterateSymbols' runtime function (#213036)

Summary:
This provides users with a way to check all symbols present in a given
program. This is useful for many cases where a power user wants to do
something dynamic with the objects present.
This commit is contained in:
Joseph Huber 2026-07-31 14:29:06 -05:00 committed by GitHub
parent 7ef0ca2b13
commit 14ec3c42ce
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
16 changed files with 350 additions and 29 deletions

View file

@ -32,6 +32,37 @@ def olGetSymbol : Function {
let returns = [];
}
def ol_symbol_iterate_cb_t : FptrTypedef {
let desc = "User-provided function to be used with `olIterateSymbols`";
let params = [
Param<"ol_symbol_handle_t", "Symbol", "the symbol handle of the current iteration", PARAM_IN>,
Param<"void*", "UserData", "optional user data", PARAM_IN_OPTIONAL>
];
let return = "bool";
}
def olIterateSymbols : Function {
let desc = "Iterates over all symbols of the given kind in the program, calling the callback for each symbol.";
let details = [
"If the user-provided callback returns `false`, the iteration is stopped.",
"Symbol handles are owned by the program and do not need to be manually destroyed."
];
let params = [
Param<"ol_program_handle_t", "Program", "handle of the program", PARAM_IN>,
Param<"ol_symbol_kind_t", "Kind", "symbol kind to iterate over", PARAM_IN>,
Param<"ol_symbol_iterate_cb_t", "Callback", "User-provided function called for each symbol", PARAM_IN>,
Param<"void*", "UserData", "Optional user data to pass to the callback", PARAM_IN_OPTIONAL>
];
let returns = [
Return<"OL_ERRC_UNSUPPORTED", [
"The platform could not enumerate symbols of the given kind."
]>,
Return<"OL_ERRC_INVALID_BINARY", [
"The program's image could not be parsed to enumerate its symbols."
]>
];
}
def ol_symbol_info_t : Enum {
let desc = "Supported symbol info.";
let is_typed = 1;
@ -39,6 +70,7 @@ def ol_symbol_info_t : Enum {
TaggedEtor<"KIND", "ol_symbol_kind_t", "The kind of this symbol.">,
TaggedEtor<"GLOBAL_VARIABLE_ADDRESS", "void *", "The address in memory for this global variable.">,
TaggedEtor<"GLOBAL_VARIABLE_SIZE", "size_t", "The size in bytes for this global variable.">,
TaggedEtor<"NAME", "char[]", "The name of this symbol.">,
];
}

View file

@ -199,10 +199,12 @@ struct ol_program_impl_t {
};
struct ol_symbol_impl_t {
ol_symbol_impl_t(const char *Name, GenericKernelTy *Kernel)
: PluginImpl(Kernel), Kind(OL_SYMBOL_KIND_KERNEL), Name(Name) {}
ol_symbol_impl_t(const char *Name, GlobalTy &&Global)
: PluginImpl(Global), Kind(OL_SYMBOL_KIND_GLOBAL_VARIABLE), Name(Name) {}
ol_symbol_impl_t(GenericKernelTy *Kernel)
: PluginImpl(Kernel), Kind(OL_SYMBOL_KIND_KERNEL),
Name(Kernel->getName()) {}
ol_symbol_impl_t(GlobalTy &&Global)
: PluginImpl(std::move(Global)), Kind(OL_SYMBOL_KIND_GLOBAL_VARIABLE),
Name(std::get<GlobalTy>(PluginImpl).getName()) {}
std::variant<GenericKernelTy *, GlobalTy> PluginImpl;
ol_symbol_kind_t Kind;
llvm::StringRef Name;
@ -1281,12 +1283,11 @@ Error olLaunchKernel_impl(ol_queue_handle_t Queue, ol_device_handle_t Device,
return Error::success();
}
Error olGetSymbol_impl(ol_program_handle_t Program, const char *Name,
ol_symbol_kind_t Kind, ol_symbol_handle_t *Symbol) {
Expected<ol_symbol_handle_t> getSymbolImplDetail(ol_program_handle_t Program,
StringRef Name,
ol_symbol_kind_t Kind) {
auto &Device = Program->Image->getDevice();
std::lock_guard<std::mutex> Lock(Program->SymbolListMutex);
switch (Kind) {
case OL_SYMBOL_KIND_KERNEL: {
auto &Kernel = Program->KernelSymbols[Name];
@ -1298,12 +1299,10 @@ Error olGetSymbol_impl(ol_program_handle_t Program, const char *Name,
if (auto Err = KernelImpl->init(Device, *Program->Image))
return Err;
Kernel = std::make_unique<ol_symbol_impl_t>(KernelImpl->getName(),
&*KernelImpl);
Kernel = std::make_unique<ol_symbol_impl_t>(&*KernelImpl);
}
*Symbol = Kernel.get();
return Error::success();
return Kernel.get();
}
case OL_SYMBOL_KIND_GLOBAL_VARIABLE: {
auto &Global = Program->GlobalSymbols[Name];
@ -1314,12 +1313,10 @@ Error olGetSymbol_impl(ol_program_handle_t Program, const char *Name,
Device, *Program->Image, GlobalObj))
return Res;
Global = std::make_unique<ol_symbol_impl_t>(GlobalObj.getName().c_str(),
std::move(GlobalObj));
Global = std::make_unique<ol_symbol_impl_t>(std::move(GlobalObj));
}
*Symbol = Global.get();
return Error::success();
return Global.get();
}
default:
return createOffloadError(ErrorCode::INVALID_ENUMERATION,
@ -1327,6 +1324,50 @@ Error olGetSymbol_impl(ol_program_handle_t Program, const char *Name,
}
}
Error olGetSymbol_impl(ol_program_handle_t Program, const char *Name,
ol_symbol_kind_t Kind, ol_symbol_handle_t *Symbol) {
std::lock_guard<std::mutex> Lock(Program->SymbolListMutex);
auto SymbolOrErr = getSymbolImplDetail(Program, Name, Kind);
if (!SymbolOrErr)
return SymbolOrErr.takeError();
*Symbol = *SymbolOrErr;
return Error::success();
}
Error olIterateSymbols_impl(ol_program_handle_t Program, ol_symbol_kind_t Kind,
ol_symbol_iterate_cb_t Callback, void *UserData) {
SymbolKindTy PluginKind;
switch (Kind) {
case OL_SYMBOL_KIND_KERNEL:
PluginKind = SymbolKindTy::Kernel;
break;
case OL_SYMBOL_KIND_GLOBAL_VARIABLE:
PluginKind = SymbolKindTy::GlobalVariable;
break;
default:
return createOffloadError(ErrorCode::INVALID_ENUMERATION,
"iterateSymbols kind enum '%i' is invalid", Kind);
}
auto &Device = Program->Image->getDevice();
std::lock_guard<std::mutex> Lock(Program->SymbolListMutex);
Error SymbolErr = Error::success();
Error IterateErr = Device.Plugin.getGlobalHandler().iterateSymbols(
*Program->Image, PluginKind, [&](StringRef Name) {
auto SymbolOrErr = getSymbolImplDetail(Program, Name, Kind);
if (!SymbolOrErr) {
SymbolErr = SymbolOrErr.takeError();
return false;
}
return Callback(*SymbolOrErr, UserData);
});
return joinErrors(std::move(IterateErr), std::move(SymbolErr));
}
Error olGetSymbolInfoImplDetail(ol_symbol_handle_t Symbol,
ol_symbol_info_t PropName, size_t PropSize,
void *PropValue, size_t *PropSizeRet) {
@ -1346,6 +1387,8 @@ Error olGetSymbolInfoImplDetail(ol_symbol_handle_t Symbol,
switch (PropName) {
case OL_SYMBOL_INFO_KIND:
return Info.write<ol_symbol_kind_t>(Symbol->Kind);
case OL_SYMBOL_INFO_NAME:
return Info.writeString(Symbol->Name);
case OL_SYMBOL_INFO_GLOBAL_VARIABLE_ADDRESS:
if (auto Err = CheckKind(OL_SYMBOL_KIND_GLOBAL_VARIABLE))
return Err;

View file

@ -586,7 +586,7 @@ private:
/// generic kernel class.
struct AMDGPUKernelTy : public GenericKernelTy {
/// Create an AMDGPU kernel with a name and an execution mode.
AMDGPUKernelTy(const char *Name) : GenericKernelTy(Name) {}
AMDGPUKernelTy(StringRef Name) : GenericKernelTy(Name) {}
/// Initialize the AMDGPU kernel.
Error initImpl(GenericDeviceTy &Device, DeviceImageTy &Image) override {
@ -2636,7 +2636,7 @@ struct AMDGPUDeviceTy : public GenericDeviceTy, AMDGenericDeviceTy {
}
/// Allocate and construct an AMDGPU kernel.
Expected<GenericKernelTy &> constructKernel(const char *Name) override {
Expected<GenericKernelTy &> constructKernel(StringRef Name) override {
// Allocate and construct the AMDGPU kernel.
AMDGPUKernelTy *AMDGPUKernel = Plugin.allocate<AMDGPUKernelTy>();
if (!AMDGPUKernel)
@ -3920,6 +3920,35 @@ struct AMDGPUGlobalHandlerTy final : public GenericGlobalHandlerTy {
return Plugin::success();
}
protected:
/// Kernels are represented by a kernel descriptor, which is an object named
/// after the function it describes with a '.kd' suffix.
std::optional<StringRef> matchSymbol(const ELFSymbolRef &Symbol,
StringRef Name,
SymbolKindTy Kind) override {
if (Symbol.getELFType() != ELF::STT_OBJECT)
return std::nullopt;
StringRef Function = Name;
bool IsDescriptor =
Function.consume_back(".kd") && isFunction(Symbol, Function);
if (IsDescriptor != (Kind == SymbolKindTy::Kernel))
return std::nullopt;
return IsDescriptor ? Function : Name;
}
private:
/// Returns whether \p Name is a function in the image containing \p Symbol.
static bool isFunction(const ELFSymbolRef &Symbol, StringRef Name) {
auto SymbolOrErr = utils::elf::getSymbol(*Symbol.getObject(), Name);
if (!SymbolOrErr) {
consumeError(SymbolOrErr.takeError());
return false;
}
return *SymbolOrErr && (*SymbolOrErr)->getELFType() == ELF::STT_FUNC;
}
};
/// Class implementing the AMDGPU-specific functionalities of the plugin.

View file

@ -13,9 +13,11 @@
#ifndef LLVM_OPENMP_LIBOMPTARGET_PLUGINS_NEXTGEN_COMMON_GLOBALHANDLER_H
#define LLVM_OPENMP_LIBOMPTARGET_PLUGINS_NEXTGEN_COMMON_GLOBALHANDLER_H
#include <optional>
#include <type_traits>
#include "llvm/ADT/DenseMap.h"
#include "llvm/ADT/STLFunctionalExtras.h"
#include "llvm/Object/ELFObjectFile.h"
#include "llvm/ProfileData/InstrProf.h"
#include "llvm/Support/Compiler.h"
@ -35,6 +37,9 @@ struct GenericDeviceTy;
using namespace llvm::object;
/// The kinds of symbols that can be enumerated in a device image.
enum class SymbolKindTy { Kernel, GlobalVariable };
/// Common abstraction for globals that live on the host and device.
/// It simply encapsulates the symbol name, symbol size, and symbol address
/// (which might be host or device depending on the context).
@ -48,7 +53,7 @@ class GlobalTy {
void *Ptr;
public:
GlobalTy(const std::string &Name, uint32_t Size = 0, void *Ptr = nullptr)
GlobalTy(StringRef Name, uint32_t Size = 0, void *Ptr = nullptr)
: Name(Name), Size(Size), Ptr(Ptr) {}
const std::string &getName() const { return Name; }
@ -216,6 +221,21 @@ public:
/// with profiling prefixes.
Expected<GPUProfGlobals> readProfilingGlobals(GenericDeviceTy &Device,
DeviceImageTy &Image);
/// Enumerate the names of the symbols of the given \p Kind in \p Image,
/// stopping early if \p Callback returns false.
virtual Error iterateSymbols(DeviceImageTy &Image, SymbolKindTy Kind,
function_ref<bool(StringRef)> Callback);
protected:
/// Returns whether a symbol with the given \p Flags is defined by and
/// exported from the image, and therefore usable by the runtime.
virtual bool isExportedSymbol(uint32_t Flags);
/// Returns the name \p Symbol is known by if it identifies a symbol of the
/// given \p Kind, otherwise std::nullopt.
virtual std::optional<StringRef>
matchSymbol(const ELFSymbolRef &Symbol, StringRef Name, SymbolKindTy Kind);
};
} // namespace plugin

View file

@ -429,7 +429,7 @@ public:
/// implement the necessary virtual function members.
struct GenericKernelTy {
/// Construct a kernel with a name and a execution mode.
GenericKernelTy(const char *Name)
GenericKernelTy(StringRef Name)
: Name(Name), PreferredNumThreads(0), MaxNumThreads(0) {}
virtual ~GenericKernelTy() {}
@ -1266,7 +1266,7 @@ struct GenericDeviceTy : public DeviceAllocatorTy {
}
/// Allocate and construct a kernel object.
virtual Expected<GenericKernelTy &> constructKernel(const char *Name) = 0;
virtual Expected<GenericKernelTy &> constructKernel(StringRef Name) = 0;
/// Reference to the underlying plugin that created this device.
GenericPluginTy &Plugin;

View file

@ -193,6 +193,55 @@ Error GenericGlobalHandlerTy::readGlobalFromImage(GenericDeviceTy &Device,
return Plugin::success();
}
bool GenericGlobalHandlerTy::isExportedSymbol(uint32_t Flags) {
uint32_t Ignored = SymbolRef::SF_Undefined | SymbolRef::SF_Hidden |
SymbolRef::SF_FormatSpecific;
return (Flags & SymbolRef::SF_Global) && !(Flags & Ignored);
}
std::optional<StringRef>
GenericGlobalHandlerTy::matchSymbol(const ELFSymbolRef &Symbol, StringRef Name,
SymbolKindTy Kind) {
uint8_t Type = Kind == SymbolKindTy::Kernel ? ELF::STT_FUNC : ELF::STT_OBJECT;
if (Symbol.getELFType() != Type)
return std::nullopt;
return Name;
}
Error GenericGlobalHandlerTy::iterateSymbols(
DeviceImageTy &Image, SymbolKindTy Kind,
function_ref<bool(StringRef)> Callback) {
auto ELFObjOrErr = getELFObjectFile(Image);
if (!ELFObjOrErr)
return ELFObjOrErr.takeError();
auto &ELFObj = cast<ELFObjectFileBase>(**ELFObjOrErr);
auto Symbols = ELFObj.symbols();
if (Symbols.empty())
Symbols = ELFObj.getDynamicSymbolIterators();
for (ELFSymbolRef Symbol : Symbols) {
auto FlagsOrErr = Symbol.getFlags();
if (!FlagsOrErr)
return Plugin::error(ErrorCode::INVALID_BINARY, FlagsOrErr.takeError(),
"error reading ELF symbol");
if (!isExportedSymbol(*FlagsOrErr))
continue;
auto NameOrErr = Symbol.getName();
if (!NameOrErr)
return Plugin::error(ErrorCode::INVALID_BINARY, NameOrErr.takeError(),
"error reading ELF symbol name");
if (auto Name = matchSymbol(Symbol, *NameOrErr, Kind))
if (!Callback(*Name))
break;
}
return Plugin::success();
}
Expected<GPUProfGlobals>
GenericGlobalHandlerTy::readProfilingGlobals(GenericDeviceTy &Device,
DeviceImageTy &Image) {

View file

@ -96,7 +96,7 @@ private:
/// generic kernel class.
struct CUDAKernelTy : public GenericKernelTy {
/// Create a CUDA kernel with a name and an execution mode.
CUDAKernelTy(const char *Name) : GenericKernelTy(Name), Func(nullptr) {}
CUDAKernelTy(StringRef Name) : GenericKernelTy(Name), Func(nullptr) {}
/// Initialize the CUDA kernel.
Error initImpl(GenericDeviceTy &GenericDevice,
@ -512,7 +512,7 @@ struct CUDADeviceTy : public GenericDeviceTy {
}
/// Allocate and construct a CUDA kernel.
Expected<GenericKernelTy &> constructKernel(const char *Name) override {
Expected<GenericKernelTy &> constructKernel(StringRef Name) override {
// Allocate and construct the CUDA kernel.
CUDAKernelTy *CUDAKernel = Plugin.allocate<CUDAKernelTy>();
if (!CUDAKernel)

View file

@ -56,7 +56,7 @@ using namespace error;
/// Class implementing kernel functionalities for GenELF64.
struct GenELF64KernelTy : public GenericKernelTy {
/// Construct the kernel with a name and an execution mode.
GenELF64KernelTy(const char *Name) : GenericKernelTy(Name), Func(nullptr) {}
GenELF64KernelTy(StringRef Name) : GenericKernelTy(Name), Func(nullptr) {}
/// Initialize the kernel.
Error initImpl(GenericDeviceTy &Device, DeviceImageTy &Image) override {
@ -165,7 +165,7 @@ struct GenELF64DeviceTy : public GenericDeviceTy {
std::string getComputeUnitKind() const override { return "generic-64bit"; }
/// Construct the kernel for a specific image on the device.
Expected<GenericKernelTy &> constructKernel(const char *Name) override {
Expected<GenericKernelTy &> constructKernel(StringRef Name) override {
// Allocate and construct the kernel.
GenELF64KernelTy *GenELF64Kernel = Plugin.allocate<GenELF64KernelTy>();
if (!GenELF64Kernel)

View file

@ -576,7 +576,7 @@ public:
V = 0;
return Plugin::success();
}
Expected<GenericKernelTy &> constructKernel(const char *Name) override;
Expected<GenericKernelTy &> constructKernel(StringRef Name) override;
Error callGlobalConstructors(GenericPluginTy &Plugin,
DeviceImageTy &Image) override;

View file

@ -67,7 +67,7 @@ class L0KernelTy : public GenericKernelTy {
public:
/// Create a L0 kernel with a name and an execution mode.
L0KernelTy(const char *Name) : GenericKernelTy(Name), zeKernel(nullptr) {}
L0KernelTy(StringRef Name) : GenericKernelTy(Name), zeKernel(nullptr) {}
~L0KernelTy() = default;
L0KernelTy(const L0KernelTy &) = delete;
L0KernelTy(L0KernelTy &&) = delete;

View file

@ -126,6 +126,9 @@ struct L0GlobalHandlerTy final : public GenericGlobalHandlerTy {
Error getGlobalMetadataFromDevice(GenericDeviceTy &Device,
DeviceImageTy &Image,
GlobalTy &DeviceGlobal) override;
protected:
bool isExportedSymbol(uint32_t Flags) override;
};
bool isValidOneOmpImage(StringRef Image, uint64_t &MajorVer,

View file

@ -554,7 +554,7 @@ Expected<InfoTreeNode> L0DeviceTy::obtainInfoImpl() {
return Info;
}
Expected<GenericKernelTy &> L0DeviceTy::constructKernel(const char *Name) {
Expected<GenericKernelTy &> L0DeviceTy::constructKernel(StringRef Name) {
// Allocate and construct the L0 kernel.
L0KernelTy *L0Kernel = getPlugin().allocate<L0KernelTy>();
if (!L0Kernel)

View file

@ -43,6 +43,14 @@ Error L0GlobalHandlerTy::getGlobalMetadataFromDevice(GenericDeviceTy &Device,
return Plugin::success();
}
bool L0GlobalHandlerTy::isExportedSymbol(uint32_t Flags) {
// Images returned by the Level Zero runtime do not correctly expose kernel
// functions as global symbols. Bypass the normal ELF handling.here.
uint32_t Ignored = SymbolRef::SF_Undefined | SymbolRef::SF_Hidden |
SymbolRef::SF_FormatSpecific;
return !(Flags & Ignored);
}
inline L0DeviceTy &L0ProgramTy::getL0Device() const {
return L0DeviceTy::makeL0Device(getDevice());
}

View file

@ -65,4 +65,5 @@ add_offload_unittest("queue"
add_offload_unittest("symbol"
symbol/olGetSymbol.cpp
symbol/olGetSymbolInfo.cpp
symbol/olGetSymbolInfoSize.cpp)
symbol/olGetSymbolInfoSize.cpp
symbol/olIterateSymbols.cpp)

View file

@ -30,6 +30,24 @@ TEST_P(olGetSymbolInfoGlobalTest, SuccessKind) {
ASSERT_EQ(RetrievedKind, OL_SYMBOL_KIND_GLOBAL_VARIABLE);
}
TEST_P(olGetSymbolInfoKernelTest, SuccessName) {
size_t Size = 0;
ASSERT_SUCCESS(olGetSymbolInfoSize(Kernel, OL_SYMBOL_INFO_NAME, &Size));
std::vector<char> Name(Size);
ASSERT_SUCCESS(
olGetSymbolInfo(Kernel, OL_SYMBOL_INFO_NAME, Size, Name.data()));
ASSERT_STREQ(Name.data(), "foo");
}
TEST_P(olGetSymbolInfoGlobalTest, SuccessName) {
size_t Size = 0;
ASSERT_SUCCESS(olGetSymbolInfoSize(Global, OL_SYMBOL_INFO_NAME, &Size));
std::vector<char> Name(Size);
ASSERT_SUCCESS(
olGetSymbolInfo(Global, OL_SYMBOL_INFO_NAME, Size, Name.data()));
ASSERT_STREQ(Name.data(), "global");
}
TEST_P(olGetSymbolInfoKernelTest, InvalidAddress) {
void *RetrievedAddr;
ASSERT_ERROR(OL_ERRC_SYMBOL_KIND,

View file

@ -0,0 +1,118 @@
//===------- Offload API tests - olIterateSymbols -------------------------===//
//
// 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 "../common/Fixtures.hpp"
#include <OffloadAPI.h>
#include <gtest/gtest.h>
// The 'global' program contains the 'read' and 'write' kernels as well as the
// 'global' global variable.
struct olIterateSymbolsTest : OffloadProgramTest {
void SetUp() override { SetUpWith("global"); }
};
OFFLOAD_TESTS_INSTANTIATE_DEVICE_FIXTURE(olIterateSymbolsTest);
using SymbolVecT = std::vector<ol_symbol_handle_t>;
static bool collectSymbol(ol_symbol_handle_t Symbol, void *Data) {
static_cast<SymbolVecT *>(Data)->push_back(Symbol);
return true;
}
static std::string getName(ol_symbol_handle_t Symbol) {
size_t Size = 0;
if (olGetSymbolInfoSize(Symbol, OL_SYMBOL_INFO_NAME, &Size))
return {};
std::vector<char> Name(Size);
if (olGetSymbolInfo(Symbol, OL_SYMBOL_INFO_NAME, Size, Name.data()))
return {};
return std::string{Name.data()};
}
static std::vector<std::string> getNames(const SymbolVecT &Symbols,
ol_symbol_kind_t Expected) {
std::vector<std::string> Names;
for (auto *Symbol : Symbols) {
ol_symbol_kind_t Kind;
if (olGetSymbolInfo(Symbol, OL_SYMBOL_INFO_KIND, sizeof(Kind), &Kind))
return {};
EXPECT_EQ(Kind, Expected);
Names.push_back(getName(Symbol));
}
return Names;
}
static bool contains(const std::vector<std::string> &Names,
const std::string &Name) {
return std::find(Names.begin(), Names.end(), Name) != Names.end();
}
TEST_P(olIterateSymbolsTest, SuccessKernels) {
SymbolVecT Symbols;
ASSERT_SUCCESS(olIterateSymbols(Program, OL_SYMBOL_KIND_KERNEL, collectSymbol,
&Symbols));
auto Names = getNames(Symbols, OL_SYMBOL_KIND_KERNEL);
ASSERT_TRUE(contains(Names, "read"));
ASSERT_TRUE(contains(Names, "write"));
}
TEST_P(olIterateSymbolsTest, SuccessGlobals) {
SymbolVecT Symbols;
ASSERT_SUCCESS_OR_UNSUPPORTED(olIterateSymbols(
Program, OL_SYMBOL_KIND_GLOBAL_VARIABLE, collectSymbol, &Symbols));
auto Names = getNames(Symbols, OL_SYMBOL_KIND_GLOBAL_VARIABLE);
ASSERT_TRUE(contains(Names, "global"));
}
TEST_P(olIterateSymbolsTest, SuccessSameSymbol) {
SymbolVecT Symbols;
ASSERT_SUCCESS(olIterateSymbols(Program, OL_SYMBOL_KIND_KERNEL, collectSymbol,
&Symbols));
ASSERT_FALSE(Symbols.empty());
for (auto *Symbol : Symbols) {
ol_symbol_handle_t Looked = nullptr;
ASSERT_SUCCESS(olGetSymbol(Program, getName(Symbol).c_str(),
OL_SYMBOL_KIND_KERNEL, &Looked));
ASSERT_EQ(Symbol, Looked);
}
}
TEST_P(olIterateSymbolsTest, SuccessStopIteration) {
size_t Count = 0;
ASSERT_SUCCESS(olIterateSymbols(
Program, OL_SYMBOL_KIND_KERNEL,
[](ol_symbol_handle_t, void *Data) {
(*static_cast<size_t *>(Data))++;
return false;
},
&Count));
ASSERT_EQ(Count, 1u);
}
TEST_P(olIterateSymbolsTest, InvalidNullProgram) {
SymbolVecT Symbols;
ASSERT_ERROR(OL_ERRC_INVALID_NULL_HANDLE,
olIterateSymbols(nullptr, OL_SYMBOL_KIND_KERNEL, collectSymbol,
&Symbols));
}
TEST_P(olIterateSymbolsTest, InvalidNullCallback) {
ASSERT_ERROR(
OL_ERRC_INVALID_NULL_POINTER,
olIterateSymbols(Program, OL_SYMBOL_KIND_KERNEL, nullptr, nullptr));
}
TEST_P(olIterateSymbolsTest, InvalidKind) {
SymbolVecT Symbols;
ASSERT_ERROR(OL_ERRC_INVALID_ENUMERATION,
olIterateSymbols(Program, OL_SYMBOL_KIND_FORCE_UINT32,
collectSymbol, &Symbols));
}