1 //
2 // Copyright 2020 Serge Martin
3 //
4 // Permission is hereby granted, free of charge, to any person obtaining a
5 // copy of this software and associated documentation files (the "Software"),
6 // to deal in the Software without restriction, including without limitation
7 // the rights to use, copy, modify, merge, publish, distribute, sublicense,
8 // and/or sell copies of the Software, and to permit persons to whom the
9 // Software is furnished to do so, subject to the following conditions:
10 //
11 // The above copyright notice and this permission notice shall be included in
12 // all copies or substantial portions of the Software.
13 //
14 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
17 // THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
18 // OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
19 // ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
20 // OTHER DEALINGS IN THE SOFTWARE.
21 //
22
23 #include <cstring>
24 #include <cstdio>
25 #include <string>
26 #include <iostream>
27
28 #include "util/u_math.h"
29 #include "core/printf.hpp"
30
31 #include "util/u_printf.h"
32 using namespace clover;
33
34 namespace {
35
36 const cl_uint hdr_dwords = 2;
37 const cl_uint initial_buffer_offset = hdr_dwords * sizeof(cl_uint);
38
39 /* all valid chars that can appear in CL C printf string. */
40 const std::string clc_printf_whitelist = "%0123456789-+ #.AacdeEfFgGhilopsuvxX";
41
42 void
print_formatted(const std::vector<binary::printf_info> & formatters,bool _strings_in_buffer,const std::vector<char> & buffer)43 print_formatted(const std::vector<binary::printf_info> &formatters,
44 bool _strings_in_buffer,
45 const std::vector<char> &buffer) {
46
47 static std::atomic<unsigned> warn_count;
48 if (buffer.empty() && !warn_count++)
49 std::cerr << "Printf used but no printf occurred - may cause perfomance issue." << std::endl;
50
51 for (size_t buf_pos = 0; buf_pos < buffer.size(); ) {
52 cl_uint fmt_idx = *(cl_uint*)&buffer[buf_pos];
53 assert(fmt_idx > 0);
54 binary::printf_info fmt = formatters[fmt_idx-1];
55
56 std::string format = (char *)fmt.strings.data();
57 buf_pos += sizeof(cl_uint);
58
59 if (fmt.arg_sizes.empty()) {
60 printf("%s", format.c_str());
61
62 } else {
63 size_t fmt_last_pos = 0;
64 size_t fmt_pos = 0;
65 for (int arg_size : fmt.arg_sizes) {
66 const size_t spec_pos = util_printf_next_spec_pos(format, fmt_pos);
67 const size_t cur_tok = format.rfind('%', spec_pos);
68 const size_t next_spec = util_printf_next_spec_pos(format, spec_pos);
69 const size_t next_tok = next_spec == std::string::npos ? std::string::npos :
70 format.rfind('%', next_spec);
71
72 size_t vec_pos = format.find_first_of("v", cur_tok + 1);
73 size_t mod_pos = format.find_first_of("hl", cur_tok + 1);
74
75 // print the part before the format token
76 if (cur_tok != fmt_last_pos) {
77 std::string s = format.substr(fmt_last_pos,
78 cur_tok - fmt_last_pos);
79 printf("%s", s.c_str());
80 }
81
82 std::string print_str;
83 print_str = format.substr(cur_tok, spec_pos + 1 - cur_tok);
84
85 /* Never pass a 'n' spec to the host printf */
86 bool valid_str = print_str.find_first_not_of(clc_printf_whitelist) ==
87 std::string::npos;
88
89 // print the formated part
90 if (spec_pos != std::string::npos && valid_str) {
91 bool is_vector = vec_pos != std::string::npos &&
92 vec_pos + 1 < spec_pos;
93 bool is_string = format[spec_pos] == 's';
94 bool is_float = std::string("fFeEgGaA")
95 .find(format[spec_pos]) != std::string::npos;
96
97 if (is_string) {
98 if (_strings_in_buffer)
99 printf(print_str.c_str(), &buffer[buf_pos]);
100 else {
101 uint64_t idx;
102 memcpy(&idx, &buffer[buf_pos], 8);
103 printf(print_str.c_str(), &fmt.strings[idx]);
104 }
105 } else {
106 int component_count = 1;
107
108 if (is_vector) {
109 size_t l = std::min(mod_pos, spec_pos) - vec_pos - 1;
110 std::string s = format.substr(vec_pos + 1, l);
111 component_count = std::stoi(s);
112 if (mod_pos != std::string::npos) {
113 // CL C has hl specifier for 32-bit vectors, C doesn't have it
114 // just remove it.
115 std::string mod = format.substr(mod_pos, 2);
116 if (mod == "hl")
117 mod_pos = std::string::npos;
118 }
119 print_str.erase(vec_pos - cur_tok, std::min(mod_pos, spec_pos) - vec_pos);
120 print_str.push_back(',');
121 }
122
123 //in fact vec3 are vec4
124 int men_components =
125 component_count == 3 ? 4 : component_count;
126 size_t elmt_size = arg_size / men_components;
127
128 for (int i = 0; i < component_count; i++) {
129 size_t elmt_buf_pos = buf_pos + i * elmt_size;
130 if (is_vector && i + 1 == component_count)
131 print_str.pop_back();
132
133 if (is_float) {
134 switch (elmt_size) {
135 case 2:
136 cl_half h;
137 std::memcpy(&h, &buffer[elmt_buf_pos], elmt_size);
138 printf(print_str.c_str(), h);
139 break;
140 case 4:
141 cl_float f;
142 std::memcpy(&f, &buffer[elmt_buf_pos], elmt_size);
143 printf(print_str.c_str(), f);
144 break;
145 default:
146 cl_double d;
147 std::memcpy(&d, &buffer[elmt_buf_pos], elmt_size);
148 printf(print_str.c_str(), d);
149 }
150 } else {
151 cl_long l = 0;
152 std::memcpy(&l, &buffer[elmt_buf_pos], elmt_size);
153 printf(print_str.c_str(), l);
154 }
155 }
156 }
157 // print the remaining
158 if (next_tok != spec_pos) {
159 std::string s = format.substr(spec_pos + 1,
160 next_tok - spec_pos - 1);
161 printf("%s", s.c_str());
162 }
163 }
164
165 fmt_pos = spec_pos;
166 fmt_last_pos = next_tok;
167
168 buf_pos += arg_size;
169 buf_pos = ALIGN(buf_pos, 4);
170 }
171 }
172 }
173 }
174 }
175
176 std::unique_ptr<printf_handler>
create(const intrusive_ptr<command_queue> & q,const std::vector<binary::printf_info> & infos,bool strings_in_buffer,cl_uint size)177 printf_handler::create(const intrusive_ptr<command_queue> &q,
178 const std::vector<binary::printf_info> &infos,
179 bool strings_in_buffer,
180 cl_uint size) {
181 return std::unique_ptr<printf_handler>(
182 new printf_handler(q, infos, strings_in_buffer, size));
183 }
184
printf_handler(const intrusive_ptr<command_queue> & q,const std::vector<binary::printf_info> & infos,bool strings_in_buffer,cl_uint size)185 printf_handler::printf_handler(const intrusive_ptr<command_queue> &q,
186 const std::vector<binary::printf_info> &infos,
187 bool strings_in_buffer,
188 cl_uint size) :
189 _q(q), _formatters(infos), _strings_in_buffer(strings_in_buffer), _size(size), _buffer() {
190
191 if (_size) {
192 std::string data;
193 data.reserve(_size);
194 cl_uint header[2] = { 0 };
195
196 header[0] = initial_buffer_offset;
197 header[1] = _size;
198
199 data.append((char *)header, (char *)(header+hdr_dwords));
200 _buffer = std::unique_ptr<root_buffer>(new root_buffer(_q->context,
201 std::vector<cl_mem_properties>(),
202 CL_MEM_COPY_HOST_PTR,
203 _size, (char*)data.data()));
204 }
205 }
206
207 cl_mem
get_mem()208 printf_handler::get_mem() {
209 return (cl_mem)(_buffer.get());
210 }
211
212 void
print()213 printf_handler::print() {
214 if (!_buffer)
215 return;
216
217 mapping src = { *_q, _buffer->resource_in(*_q), CL_MAP_READ, true,
218 {{ 0 }}, {{ _size, 1, 1 }} };
219
220 cl_uint header[2] = { 0 };
221 std::memcpy(header,
222 static_cast<const char *>(src),
223 initial_buffer_offset);
224
225 cl_uint buffer_size = header[0];
226 buffer_size -= initial_buffer_offset;
227 std::vector<char> buf;
228 buf.resize(buffer_size);
229
230 std::memcpy(buf.data(),
231 static_cast<const char *>(src) + initial_buffer_offset,
232 buffer_size);
233
234 // mixed endian isn't going to work, sort it out if anyone cares later.
235 assert(_q->device().endianness() == PIPE_ENDIAN_NATIVE);
236 print_formatted(_formatters, _strings_in_buffer, buf);
237 }
238