1 //
2 // Copyright (c) 2022 The Khronos Group Inc.
3 //
4 // Licensed under the Apache License, Version 2.0 (the "License");
5 // you may not use this file except in compliance with the License.
6 // You may obtain a copy of the License at
7 //
8 // http://www.apache.org/licenses/LICENSE-2.0
9 //
10 // Unless required by applicable law or agreed to in writing, software
11 // distributed under the License is distributed on an "AS IS" BASIS,
12 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 // See the License for the specific language governing permissions and
14 // limitations under the License.
15 //
16
17 #include <harness/os_helpers.h>
18
19 #include "basic_command_buffer.h"
20 #include "procs.h"
21
22 #if !defined(_WIN32)
23 #if defined(__APPLE__)
24 #include <sys/sysctl.h>
25 #endif
26 #include <unistd.h>
27 #define streamDup(fd1) dup(fd1)
28 #define streamDup2(fd1, fd2) dup2(fd1, fd2)
29 #endif
30 #include <limits.h>
31 #include <time.h>
32
33 #if defined(_WIN32)
34 #include <io.h>
35 #define streamDup(fd1) _dup(fd1)
36 #define streamDup2(fd1, fd2) _dup2(fd1, fd2)
37 #endif
38
39 #include <vector>
40 #include <list>
41 #include <map>
42 #include <fstream>
43 #include <stdio.h>
44
45 namespace {
46
47 ////////////////////////////////////////////////////////////////////////////////
48 // printf tests for cl_khr_command_buffer which handles below cases:
49 // -test cases for device side printf
50 // -test cases for device side printf with a simultaneous use command-buffer
51
52 template <bool simul_use>
53 struct CommandBufferPrintfTest : public BasicCommandBufferTest
54 {
CommandBufferPrintfTest__anonac20b4380111::CommandBufferPrintfTest55 CommandBufferPrintfTest(cl_device_id device, cl_context context,
56 cl_command_queue queue)
57 : BasicCommandBufferTest(device, context, queue),
58 trigger_event(nullptr), wait_event(nullptr), file_descriptor(0),
59 printf_use_support(false)
60 {
61 simultaneous_use_requested = simul_use;
62 if (simul_use)
63 {
64 buffer_size_multiplier = num_test_iters;
65 }
66 }
67
68 //--------------------------------------------------------------------------
ReleaseOutputStream__anonac20b4380111::CommandBufferPrintfTest69 void ReleaseOutputStream(int fd)
70 {
71 fflush(stdout);
72 streamDup2(fd, fileno(stdout));
73 close(fd);
74 }
75
76 //--------------------------------------------------------------------------
AcquireOutputStream__anonac20b4380111::CommandBufferPrintfTest77 int AcquireOutputStream(int* error)
78 {
79 int fd = streamDup(fileno(stdout));
80 *error = 0;
81 if (!freopen(temp_filename.c_str(), "wt", stdout))
82 {
83 ReleaseOutputStream(fd);
84 *error = -1;
85 }
86 return fd;
87 }
88
89 //--------------------------------------------------------------------------
GetAnalysisBuffer__anonac20b4380111::CommandBufferPrintfTest90 void GetAnalysisBuffer(std::stringstream& buffer)
91 {
92 std::ifstream fp(temp_filename, std::ios::in);
93 if (fp.is_open())
94 {
95 buffer << fp.rdbuf();
96 }
97 }
98
99 //--------------------------------------------------------------------------
PurgeTempFile__anonac20b4380111::CommandBufferPrintfTest100 void PurgeTempFile()
101 {
102 std::ofstream ofs(temp_filename,
103 std::ofstream::out | std::ofstream::trunc);
104 ofs.close();
105 }
106
107 //--------------------------------------------------------------------------
Skip__anonac20b4380111::CommandBufferPrintfTest108 bool Skip() override
109 {
110 // Query if device supports kernel printf use
111 cl_device_command_buffer_capabilities_khr capabilities;
112 cl_int error =
113 clGetDeviceInfo(device, CL_DEVICE_COMMAND_BUFFER_CAPABILITIES_KHR,
114 sizeof(capabilities), &capabilities, NULL);
115 test_error(error,
116 "Unable to query CL_DEVICE_COMMAND_BUFFER_CAPABILITIES_KHR");
117
118 printf_use_support =
119 (capabilities & CL_COMMAND_BUFFER_CAPABILITY_KERNEL_PRINTF_KHR)
120 != 0;
121
122 if (!printf_use_support) return true;
123 return BasicCommandBufferTest::Skip()
124 || (simultaneous_use_requested && !simultaneous_use_support);
125 }
126
127 //--------------------------------------------------------------------------
SetUpKernel__anonac20b4380111::CommandBufferPrintfTest128 cl_int SetUpKernel() override
129 {
130 cl_int error = CL_SUCCESS;
131
132 const char* kernel_str =
133 R"(
134 __kernel void print(__global char* in, __global char* out, __global int* offset)
135 {
136 size_t id = get_global_id(0);
137 int ind = offset[0] + offset[1] * id;
138 for(int i=0; i<offset[1]; i++) {
139 out[ind+i] = in[i];
140 printf("%c", in[i]);
141 }
142 })";
143
144 error = create_single_kernel_helper_create_program(context, &program, 1,
145 &kernel_str);
146 test_error(error, "Failed to create program with source");
147
148 error = clBuildProgram(program, 1, &device, nullptr, nullptr, nullptr);
149 test_error(error, "Failed to build program");
150
151 kernel = clCreateKernel(program, "print", &error);
152 test_error(error, "Failed to create print kernel");
153
154 return CL_SUCCESS;
155 }
156
157 //--------------------------------------------------------------------------
data_size__anonac20b4380111::CommandBufferPrintfTest158 size_t data_size() const override
159 {
160 return sizeof(cl_char) * num_elements * buffer_size_multiplier
161 * max_pattern_length;
162 }
163
164 //--------------------------------------------------------------------------
SetUpKernelArgs__anonac20b4380111::CommandBufferPrintfTest165 cl_int SetUpKernelArgs() override
166 {
167 cl_int error = CL_SUCCESS;
168
169 in_mem = clCreateBuffer(context, CL_MEM_READ_ONLY,
170 sizeof(cl_char) * (max_pattern_length + 1),
171 nullptr, &error);
172 test_error(error, "clCreateBuffer failed");
173
174 out_mem = clCreateBuffer(context, CL_MEM_WRITE_ONLY, data_size(),
175 nullptr, &error);
176 test_error(error, "clCreateBuffer failed");
177
178 cl_int offset[] = { 0, max_pattern_length };
179 off_mem =
180 clCreateBuffer(context, CL_MEM_READ_ONLY | CL_MEM_COPY_HOST_PTR,
181 sizeof(offset), offset, &error);
182 test_error(error, "clCreateBuffer failed");
183
184 error = clSetKernelArg(kernel, 0, sizeof(in_mem), &in_mem);
185 test_error(error, "clSetKernelArg failed");
186
187 error = clSetKernelArg(kernel, 1, sizeof(out_mem), &out_mem);
188 test_error(error, "clSetKernelArg failed");
189
190 error = clSetKernelArg(kernel, 2, sizeof(off_mem), &off_mem);
191 test_error(error, "clSetKernelArg failed");
192
193 return CL_SUCCESS;
194 }
195
196 //--------------------------------------------------------------------------
SetUp__anonac20b4380111::CommandBufferPrintfTest197 cl_int SetUp(int elements) override
198 {
199 auto pcFname = get_temp_filename();
200 temp_filename = pcFname;
201
202 if (pcFname != nullptr) free(pcFname);
203
204 if (temp_filename.empty())
205 {
206 log_error("get_temp_filename failed\n");
207 return -1;
208 }
209
210 return BasicCommandBufferTest::SetUp(elements);
211 }
212
213 //--------------------------------------------------------------------------
Run__anonac20b4380111::CommandBufferPrintfTest214 cl_int Run() override
215 {
216 cl_int error = CL_SUCCESS;
217
218 // record command buffer with primary queue
219 error = RecordCommandBuffer();
220 test_error(error, "RecordCommandBuffer failed");
221
222 if (simultaneous_use_support)
223 {
224 // enqueue simultaneous command-buffers with printf calls
225 error = RunSimultaneous();
226 test_error(error, "RunSimultaneous failed");
227 }
228 else
229 {
230 // enqueue single command-buffer with printf calls
231 error = RunSingle();
232 test_error(error, "RunSingle failed");
233 }
234
235 std::remove(temp_filename.c_str());
236
237 return CL_SUCCESS;
238 }
239
240 //--------------------------------------------------------------------------
RecordCommandBuffer__anonac20b4380111::CommandBufferPrintfTest241 cl_int RecordCommandBuffer()
242 {
243 cl_int error = CL_SUCCESS;
244
245 error = clCommandNDRangeKernelKHR(
246 command_buffer, nullptr, nullptr, kernel, 1, nullptr, &num_elements,
247 nullptr, 0, nullptr, nullptr, nullptr);
248 test_error(error, "clCommandNDRangeKernelKHR failed");
249
250 error = clFinalizeCommandBufferKHR(command_buffer);
251 test_error(error, "clFinalizeCommandBufferKHR failed");
252 return CL_SUCCESS;
253 }
254
255 //--------------------------------------------------------------------------
256 #define test_error_release_stdout(errCode, msg) \
257 { \
258 auto errCodeResult = errCode; \
259 if (errCodeResult != CL_SUCCESS) \
260 { \
261 ReleaseOutputStream(file_descriptor); \
262 print_error(errCodeResult, msg); \
263 return errCode; \
264 } \
265 }
266
267 //--------------------------------------------------------------------------
EnqueueSinglePass__anonac20b4380111::CommandBufferPrintfTest268 cl_int EnqueueSinglePass(const std::vector<cl_char>& pattern,
269 std::vector<cl_char>& output_data)
270 {
271 cl_int error = CL_SUCCESS;
272 auto in_mem_size = sizeof(cl_char) * pattern.size();
273 error = clEnqueueWriteBuffer(queue, in_mem, CL_TRUE, 0, in_mem_size,
274 &pattern[0], 0, nullptr, nullptr);
275 test_error(error, "clEnqueueWriteBuffer failed");
276
277 cl_int offset[] = { 0, pattern.size() - 1 };
278 error = clEnqueueWriteBuffer(queue, off_mem, CL_TRUE, 0, sizeof(offset),
279 offset, 0, nullptr, nullptr);
280 test_error(error, "clEnqueueWriteBuffer failed");
281
282 // redirect output stream to temporary file
283 file_descriptor = AcquireOutputStream(&error);
284 if (error != 0)
285 {
286 log_error("Error while redirection stdout to file");
287 return TEST_FAIL;
288 }
289
290 // enqueue command buffer with kernel containing printf command
291 error = clEnqueueCommandBufferKHR(0, nullptr, command_buffer, 0,
292 nullptr, &wait_event);
293 test_error_release_stdout(error, "clEnqueueCommandBufferKHR failed");
294
295 fflush(stdout);
296
297 // Wait until kernel finishes its execution and (thus) the output
298 // printed from the kernel is immediately printed
299 error = clWaitForEvents(1, &wait_event);
300 test_error(error, "clWaitForEvents failed");
301
302 // output buffer contains pattern to be compared with printout
303 error = clEnqueueReadBuffer(queue, out_mem, CL_FALSE, 0, data_size(),
304 output_data.data(), 0, nullptr, nullptr);
305 test_error_release_stdout(error, "clEnqueueReadBuffer failed");
306
307 error = clFinish(queue);
308 test_error_release_stdout(error, "clFinish failed");
309
310 ReleaseOutputStream(file_descriptor);
311
312 // copy content of temporary file into string stream
313 std::stringstream sstr;
314 GetAnalysisBuffer(sstr);
315 if (sstr.str().size() != num_elements * offset[1])
316 {
317 log_error("GetAnalysisBuffer failed\n");
318 return TEST_FAIL;
319 }
320
321 // verify the result - compare printout and output buffer
322 for (size_t i = 0; i < num_elements * offset[1]; i++)
323 {
324 CHECK_VERIFICATION_ERROR(sstr.str().at(i), output_data[i], i);
325 }
326
327 return CL_SUCCESS;
328 }
329
330 //--------------------------------------------------------------------------
RunSingle__anonac20b4380111::CommandBufferPrintfTest331 cl_int RunSingle()
332 {
333 cl_int error = CL_SUCCESS;
334 std::vector<cl_char> output_data(num_elements * max_pattern_length);
335
336 for (unsigned i = 0; i < num_test_iters; i++)
337 {
338 unsigned pattern_length =
339 std::max(min_pattern_length, rand() % max_pattern_length);
340 char pattern_character = 'a' + rand() % 26;
341 std::vector<cl_char> pattern(pattern_length + 1, pattern_character);
342 pattern[pattern_length] = '\0';
343 error = EnqueueSinglePass(pattern, output_data);
344 test_error(error, "EnqueueSinglePass failed");
345
346 output_data.assign(output_data.size(), 0);
347 PurgeTempFile();
348 }
349
350 return CL_SUCCESS;
351 }
352
353 //--------------------------------------------------------------------------
354 struct SimulPassData
355 {
356 // null terminated character buffer
357 std::vector<cl_char> pattern;
358 // 0-command buffer offset, 1-pattern offset
359 cl_int offset[2];
360 std::vector<cl_char> output_buffer;
361 };
362
363 //--------------------------------------------------------------------------
EnqueueSimultaneousPass__anonac20b4380111::CommandBufferPrintfTest364 cl_int EnqueueSimultaneousPass(SimulPassData& pd)
365 {
366 // write current pattern to device memory
367 auto in_mem_size = sizeof(cl_char) * pd.pattern.size();
368 cl_int error =
369 clEnqueueWriteBuffer(queue, in_mem, CL_FALSE, 0, in_mem_size,
370 &pd.pattern[0], 0, nullptr, nullptr);
371 test_error_release_stdout(error, "clEnqueueWriteBuffer failed");
372
373 // refresh offsets for current enqueuing
374 error =
375 clEnqueueWriteBuffer(queue, off_mem, CL_FALSE, 0, sizeof(pd.offset),
376 pd.offset, 0, nullptr, nullptr);
377 test_error_release_stdout(error, "clEnqueueWriteBuffer failed");
378
379 // create user event to block simultaneous command buffers
380 if (!trigger_event)
381 {
382 trigger_event = clCreateUserEvent(context, &error);
383 test_error_release_stdout(error, "clCreateUserEvent failed");
384 }
385
386 error = clEnqueueCommandBufferKHR(0, nullptr, command_buffer, 1,
387 &trigger_event, nullptr);
388 test_error_release_stdout(error, "clEnqueueCommandBufferKHR failed");
389
390 // output buffer contains pattern to be compared with printout
391 error = clEnqueueReadBuffer(
392 queue, out_mem, CL_FALSE, pd.offset[0] * sizeof(cl_char),
393 pd.output_buffer.size() * sizeof(cl_char), pd.output_buffer.data(),
394 0, nullptr, nullptr);
395 test_error_release_stdout(error, "clEnqueueReadBuffer failed");
396
397 return CL_SUCCESS;
398 }
399
400
401 //--------------------------------------------------------------------------
RunSimultaneous__anonac20b4380111::CommandBufferPrintfTest402 cl_int RunSimultaneous()
403 {
404 cl_int error = CL_SUCCESS;
405 cl_int offset = static_cast<cl_int>(num_elements * max_pattern_length);
406
407 std::vector<SimulPassData> simul_passes(num_test_iters);
408
409 const int pattern_chars_range = 26;
410 std::list<cl_char> pattern_chars;
411 for (size_t i = 0; i < pattern_chars_range; i++)
412 pattern_chars.push_back(cl_char('a' + i));
413
414 test_assert_error(pattern_chars.size() >= num_test_iters,
415 "Number of simultaneous launches must be lower than "
416 "size of characters container");
417
418 cl_int total_pattern_coverage = 0;
419 for (unsigned i = 0; i < num_test_iters; i++)
420 {
421 // random character pattern unique for each iteration
422 auto it = pattern_chars.begin();
423 std::advance(it, rand() % pattern_chars.size());
424 char pattern_character = *it;
425 unsigned pattern_length =
426 std::max(min_pattern_length, rand() % max_pattern_length);
427
428 std::vector<cl_char> pattern(pattern_length + 1, pattern_character);
429 pattern[pattern_length] = '\0';
430 simul_passes[i] = { pattern,
431 { cl_int(i * offset), cl_int(pattern_length) },
432 std::vector<cl_char>(num_elements
433 * pattern_length) };
434 total_pattern_coverage += simul_passes[i].output_buffer.size();
435 pattern_chars.erase(it);
436 };
437
438 // takeover stdout stream
439 file_descriptor = AcquireOutputStream(&error);
440 if (error != 0)
441 {
442 log_error("Error while redirection stdout to file");
443 return TEST_FAIL;
444 }
445
446 // enqueue read/write and command buffer operations
447 for (auto&& pass : simul_passes)
448 {
449 error = EnqueueSimultaneousPass(pass);
450 test_error_release_stdout(error, "EnqueueSimultaneousPass failed");
451 }
452
453 // execute command buffers
454 error = clSetUserEventStatus(trigger_event, CL_COMPLETE);
455 test_error_release_stdout(error, "clSetUserEventStatus failed");
456
457 // flush streams
458 fflush(stdout);
459
460 // finish command queue
461 error = clFinish(queue);
462 test_error_release_stdout(error, "clFinish failed\n");
463
464 ReleaseOutputStream(file_descriptor);
465
466 std::stringstream sstr;
467 GetAnalysisBuffer(sstr);
468 if (sstr.str().size() != total_pattern_coverage)
469 {
470 log_error("GetAnalysisBuffer failed\n");
471 return TEST_FAIL;
472 }
473
474 // verify the result - compare printout and output buffer
475 std::map<cl_char, size_t> counters_map;
476 for (int i = 0; i < total_pattern_coverage; i++)
477 counters_map[sstr.str().at(i)]++;
478
479 if (counters_map.size() != simul_passes.size())
480 {
481 log_error("printout inconsistent with input data\n");
482 return TEST_FAIL;
483 }
484
485 for (auto&& pass : simul_passes)
486 {
487 auto& res_data = pass.output_buffer;
488
489 if (res_data.empty()
490 || res_data.size() != counters_map[res_data.front()])
491 {
492 log_error("output buffer inconsistent with printout\n");
493 return TEST_FAIL;
494 }
495
496 // verify consistency of output buffer
497 for (size_t i = 0; i < res_data.size(); i++)
498 {
499 CHECK_VERIFICATION_ERROR(res_data.front(), res_data[i], i);
500 }
501 }
502
503 return CL_SUCCESS;
504 }
505
506 //--------------------------------------------------------------------------
507 clEventWrapper trigger_event = nullptr;
508 clEventWrapper wait_event = nullptr;
509
510 std::string temp_filename;
511 int file_descriptor;
512
513 bool printf_use_support;
514
515 // specifies max test length for printf pattern
516 const unsigned max_pattern_length = 6;
517 // specifies min test length for printf pattern
518 const unsigned min_pattern_length = 1;
519 // specifies number of command-buffer enqueue iterations
520 const unsigned num_test_iters = 3;
521 };
522
523 } // anonymous namespace
524
test_basic_printf(cl_device_id device,cl_context context,cl_command_queue queue,int num_elements)525 int test_basic_printf(cl_device_id device, cl_context context,
526 cl_command_queue queue, int num_elements)
527 {
528 return MakeAndRunTest<CommandBufferPrintfTest<false>>(device, context,
529 queue, num_elements);
530 }
531
test_simultaneous_printf(cl_device_id device,cl_context context,cl_command_queue queue,int num_elements)532 int test_simultaneous_printf(cl_device_id device, cl_context context,
533 cl_command_queue queue, int num_elements)
534 {
535 return MakeAndRunTest<CommandBufferPrintfTest<true>>(device, context, queue,
536 num_elements);
537 }
538