• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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