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.c_str(), fmt_pos); 67 const size_t cur_tok = format.rfind('%', spec_pos); 68 const size_t next_spec = util_printf_next_spec_pos(format.c_str(), 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> 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 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 208 printf_handler::get_mem() { 209 return (cl_mem)(_buffer.get()); 210 } 211 212 void 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