Skip to content

Commit

Permalink
Tweak implementation of cl_arm_printf and introduce dedicated test fi…
Browse files Browse the repository at this point in the history
…xture (#717)

* Tweak implementation of cl_arm_printf and introduce dedicated test fixture

Change-Id: Ib27b60fb043e47227f519f34e04316d04f47b55d

* build fix

Change-Id: Ic0a395c202c7f8e59249db8d443faf72a17399ff
  • Loading branch information
kpet authored Aug 31, 2024
1 parent 353375a commit 7552c9e
Show file tree
Hide file tree
Showing 3 changed files with 46 additions and 147 deletions.
20 changes: 12 additions & 8 deletions src/context.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ struct cvk_context : public _cl_context,

cvk_context(cvk_device* device, const cl_context_properties* props,
void* user_data)
: m_device(device) {
: m_device(device), m_user_data(user_data) {

if (props) {
while (*props) {
Expand All @@ -49,23 +49,21 @@ struct cvk_context : public _cl_context,
}
// Get printf buffer size from extension.
auto buff_size_prop_index =
get_property_index(CL_PRINTF_BUFFERSIZE_ARM);
get_property_value_index(CL_PRINTF_BUFFERSIZE_ARM);
if (buff_size_prop_index != -1 && !config.printf_buffer_size.set) {
m_printf_buffersize = m_properties[buff_size_prop_index];
} else {
m_printf_buffersize = config.printf_buffer_size;
m_printf_buffersize = 0;
}

// Get printf callback from extension
auto printf_callback_prop_index =
get_property_index(CL_PRINTF_CALLBACK_ARM);
get_property_value_index(CL_PRINTF_CALLBACK_ARM);
if (printf_callback_prop_index != -1) {
m_printf_callback =
(cvk_printf_callback_t)m_properties[printf_callback_prop_index];
m_user_data = user_data;
} else {
m_printf_callback = nullptr;
m_user_data = nullptr;
}
}

Expand Down Expand Up @@ -99,7 +97,7 @@ struct cvk_context : public _cl_context,
return size <= m_device->max_mem_alloc_size();
}

int get_property_index(const int prop) {
int get_property_value_index(const int prop) {
for (unsigned i = 0; i < m_properties.size(); i += 2) {
if (m_properties[i] == prop) {
return i + 1;
Expand All @@ -108,7 +106,13 @@ struct cvk_context : public _cl_context,
return -1;
}

size_t get_printf_buffersize() { return m_printf_buffersize; }
size_t get_printf_buffersize() {
if (m_printf_buffersize) {
return m_printf_buffersize;
} else {
return config.printf_buffer_size;
}
}
cvk_printf_callback_t get_printf_callback() { return m_printf_callback; }
void* get_printf_userdata() { return m_user_data; }

Expand Down
144 changes: 12 additions & 132 deletions tests/api/printf.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -18,105 +18,7 @@
#include "unit.hpp"
#include "utils.hpp"

#include <filesystem>

#ifdef __APPLE__
#include <unistd.h>
#endif

#ifdef WIN32
#include <Windows.h>
#include <io.h>
#endif

void printf_callback(const char* buffer, size_t len, size_t complete,
void* user_data) {
std::string* user_buffer = (std::string*)user_data;
*user_buffer += std::string(buffer);
}

static std::string stdoutFileName;

#define BUFFER_SIZE 1024
static char stdoutBuffer[BUFFER_SIZE];

static void releaseStdout(int fd) {
fflush(stdout);
dup2(fd, fileno(stdout));
close(fd);
}

static bool getStdout(int& fd) {
fd = dup(fileno(stdout));
if (!freopen(stdoutFileName.c_str(), "w", stdout)) {
fprintf(stderr, "ERROR!\n");
releaseStdout(fd);
return false;
}
return true;
}

static char* getStdoutContent() {
FILE* f;
memset(stdoutBuffer, 0, BUFFER_SIZE);
fflush(stdout);
f = fopen(stdoutFileName.c_str(), "r");
if (f == nullptr)
return nullptr;

char* ptr = stdoutBuffer;
do {
ptr += strlen(ptr);
ptr = fgets(ptr, BUFFER_SIZE, f);
} while (ptr != nullptr);
fclose(f);

return stdoutBuffer;
}

struct temp_folder_deletion {
~temp_folder_deletion() {
if (!m_path.empty())
std::filesystem::remove_all(m_path.c_str());
}
void set_path(std::string path) { m_path = path; }

private:
std::string m_path;
};

static char* mkdtemp(char* tmpl, size_t size) {
#ifdef WIN32
if (_mktemp_s(tmpl, size + 1) != 0) {
return nullptr;
}

if (!CreateDirectory(tmpl, nullptr)) {
return nullptr;
}

return tmpl;
#else
return mkdtemp(tmpl);
#endif
}

static std::string getStdoutFileName(temp_folder_deletion& temp) {
char template_tmp_dir[] = "clvk-XXXXXX";
std::filesystem::path prefix(
mkdtemp(template_tmp_dir, sizeof(template_tmp_dir)));
std::filesystem::path suffix("stdout_buffer");
temp.set_path(prefix.string());
return (prefix / suffix).string();
}

TEST_F(WithCommandQueue, SimplePrintf) {
temp_folder_deletion temp;
stdoutFileName = getStdoutFileName(temp);

int fd;
ASSERT_TRUE(getStdout(fd));

TEST_F(WithCommandQueueAndPrintf, SimplePrintf) {
const char message[] = "Hello World!";
char source[512];
sprintf(source, "kernel void test_printf() { printf(\"%s\");}", message);
Expand All @@ -127,22 +29,14 @@ TEST_F(WithCommandQueue, SimplePrintf) {
EnqueueNDRangeKernel(kernel, 1, nullptr, &gws, &lws, 0, nullptr, nullptr);
Finish();

releaseStdout(fd);
auto printf_buffer = getStdoutContent();
ASSERT_NE(printf_buffer, nullptr);

ASSERT_STREQ(printf_buffer, message);
ASSERT_STREQ(m_printf_output.c_str(), message);
}

TEST_F(WithCommandQueueNoSetUp, TooLongPrintf) {
std::string buffer = "";
TEST_F(WithCommandQueueAndPrintf, TooLongPrintf) {
// each print takes 12 bytes (4 for the printf_id, and 2*4 for the 2 integer
// to print) + 4 for the byte written counter
cl_context_properties properties[4] = {
CL_PRINTF_CALLBACK_ARM, (cl_context_properties)printf_callback,
CL_PRINTF_BUFFERSIZE_ARM, (cl_context_properties)28};
WithCommandQueue::SetUpWithContextProperties(
properties, reinterpret_cast<void*>(&buffer));
auto cfg1 =
CLVK_CONFIG_SCOPED_OVERRIDE(printf_buffer_size, uint32_t, 28, true);

// We only get the first 2 prints because the buffer is too small to get
// the last one.
Expand All @@ -162,19 +56,15 @@ TEST_F(WithCommandQueueNoSetUp, TooLongPrintf) {
EnqueueNDRangeKernel(kernel, 1, nullptr, &gws, &lws, 0, nullptr, nullptr);
Finish();

ASSERT_STREQ(buffer.c_str(), message);
ASSERT_STREQ(m_printf_output.c_str(), message);
}

TEST_F(WithCommandQueueNoSetUp, TooLongPrintf2) {
std::string buffer = "";
TEST_F(WithCommandQueueAndPrintf, TooLongPrintf2) {
// each print takes 12 bytes (4 for the printf_id, and 2*4 for the 2 integer
// to print) + 4 for the byte written counter + 8 which are not enough for
// the third print, but should not cause any issue in clvk
cl_context_properties properties[4] = {
CL_PRINTF_CALLBACK_ARM, (cl_context_properties)printf_callback,
CL_PRINTF_BUFFERSIZE_ARM, (cl_context_properties)36};
WithCommandQueue::SetUpWithContextProperties(
properties, reinterpret_cast<void*>(&buffer));
auto cfg1 =
CLVK_CONFIG_SCOPED_OVERRIDE(printf_buffer_size, uint32_t, 36, true);

// We only get the first 2 prints because the buffer is too small to get
// the last one.
Expand All @@ -194,16 +84,10 @@ TEST_F(WithCommandQueueNoSetUp, TooLongPrintf2) {
EnqueueNDRangeKernel(kernel, 1, nullptr, &gws, &lws, 0, nullptr, nullptr);
Finish();

ASSERT_STREQ(buffer.c_str(), message);
ASSERT_STREQ(m_printf_output.c_str(), message);
}

TEST_F(WithCommandQueue, PrintfMissingLengthModifier) {
temp_folder_deletion temp;
stdoutFileName = getStdoutFileName(temp);

int fd;
ASSERT_TRUE(getStdout(fd));

TEST_F(WithCommandQueueAndPrintf, PrintfMissingLengthModifier) {
const char message[] = "1,2,3,4";
char source[512];
sprintf(source,
Expand All @@ -216,11 +100,7 @@ TEST_F(WithCommandQueue, PrintfMissingLengthModifier) {
EnqueueNDRangeKernel(kernel, 1, nullptr, &gws, &lws, 0, nullptr, nullptr);
Finish();

releaseStdout(fd);
auto printf_buffer = getStdoutContent();
ASSERT_NE(printf_buffer, nullptr);

ASSERT_STREQ(printf_buffer, message);
ASSERT_STREQ(m_printf_output.c_str(), message);
}

#endif
29 changes: 22 additions & 7 deletions tests/api/testcl.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@
#define CL_USE_DEPRECATED_OPENCL_2_1_APIS
#define CL_USE_DEPRECATED_OPENCL_2_2_APIS
#include "CL/cl.h"
#include "CL/cl_ext.h"

#ifdef CLVK_UNIT_TESTING_ENABLED
#include "unit.hpp"
Expand Down Expand Up @@ -211,11 +212,12 @@ class WithContext : public ::testing::Test {
void SetUpWithContextProperties(const cl_context_properties* properties,
void* user_data) {
cl_int err;
m_context = clCreateContext(
properties, 1, &gDevice,
[](const char* errinfo, const void* private_info, size_t cb,
void* user_data) {},
user_data, &err);
// TODO instrument calls to the context callback and validate that it's
// called in some tests
auto ctx_cb = [](const char* errinfo, const void* private_info,
size_t cb, void* user_data) {};
m_context =
clCreateContext(properties, 1, &gDevice, ctx_cb, user_data, &err);
ASSERT_CL_SUCCESS(err);
}

Expand Down Expand Up @@ -782,7 +784,20 @@ class WithProfiledCommandQueue : public WithCommandQueue {
}
};

class WithCommandQueueNoSetUp : public WithCommandQueue {
static void printf_callback(const char* buffer, size_t len, size_t complete,
void* user_data) {
std::string* user_buffer = (std::string*)user_data;
*user_buffer += std::string(buffer);
}

class WithCommandQueueAndPrintf : public WithCommandQueue {
protected:
void SetUp() override{};
void SetUp() override {

cl_context_properties properties[2] = {
CL_PRINTF_CALLBACK_ARM, (cl_context_properties)printf_callback};
WithCommandQueue::SetUpWithContextProperties(
properties, reinterpret_cast<void*>(&m_printf_output));
};
std::string m_printf_output{};
};

0 comments on commit 7552c9e

Please sign in to comment.