• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 // Extract from Serge's printf clover code by airlied.
23 
24 #include <assert.h>
25 #include <stdarg.h>
26 #include <stdbool.h>
27 #include <stdlib.h>
28 #include <string.h>
29 
30 #include "blob.h"
31 #include "hash_table.h"
32 #include "macros.h"
33 #include "ralloc.h"
34 #include "simple_mtx.h"
35 #include "strndup.h"
36 #include "u_math.h"
37 #include "u_printf.h"
38 
39 #define XXH_INLINE_ALL
40 #include "util/xxhash.h"
41 
42 /* Some versions of MinGW are missing _vscprintf's declaration, although they
43  * still provide the symbol in the import library. */
44 #ifdef __MINGW32__
45 _CRTIMP int _vscprintf(const char *format, va_list argptr);
46 #endif
47 
48 const char*
util_printf_prev_tok(const char * str)49 util_printf_prev_tok(const char *str)
50 {
51    while (*str != '%')
52       str--;
53    return str;
54 }
55 
util_printf_next_spec_pos(const char * str,size_t pos)56 size_t util_printf_next_spec_pos(const char *str, size_t pos)
57 {
58    if (str == NULL)
59       return -1;
60 
61    const char *str_found = str + pos;
62    do {
63       str_found = strchr(str_found, '%');
64       if (str_found == NULL)
65          return -1;
66 
67       ++str_found;
68       if (*str_found == '%') {
69          ++str_found;
70          continue;
71       }
72 
73       char *spec_pos = strpbrk(str_found, "cdieEfFgGaAosuxXp%");
74       if (spec_pos == NULL) {
75          return -1;
76       } else if (*spec_pos == '%') {
77          str_found = spec_pos;
78       } else {
79          return spec_pos - str;
80       }
81    } while (1);
82 }
83 
u_printf_length(const char * fmt,va_list untouched_args)84 size_t u_printf_length(const char *fmt, va_list untouched_args)
85 {
86    int size;
87    char junk;
88 
89    /* Make a copy of the va_list so the original caller can still use it */
90    va_list args;
91    va_copy(args, untouched_args);
92 
93 #ifdef _WIN32
94    /* We need to use _vcsprintf to calculate the size as vsnprintf returns -1
95     * if the number of characters to write is greater than count.
96     */
97    size = _vscprintf(fmt, args);
98    (void)junk;
99 #else
100    size = vsnprintf(&junk, 1, fmt, args);
101 #endif
102    assert(size >= 0);
103 
104    va_end(args);
105 
106    return size;
107 }
108 
109 /**
110  * Used to print plain format strings without arguments as some post-processing
111  * will be required:
112  *  - %% needs to be printed as %
113  */
114 static void
u_printf_plain_sized(FILE * out,const char * format,size_t len)115 u_printf_plain_sized(FILE *out, const char* format, size_t len)
116 {
117    bool found = false;
118    size_t last = 0;
119 
120    for (size_t i = 0; i < len; i++) {
121       if (!found && format[i] == '%') {
122          found = true;
123       } else if (found && format[i] == '%') {
124          /* print one character less so we only print a single % */
125          fwrite(format + last, i - last - 1, 1, out);
126 
127          last = i;
128          found = false;
129       } else {
130          /* We should never end up here with an actual format token */
131          assert(!found);
132          found = false;
133       }
134    }
135 
136    fwrite(format + last, len - last, 1, out);
137 }
138 
139 static void
u_printf_plain(FILE * out,const char * format)140 u_printf_plain(FILE *out, const char* format)
141 {
142    u_printf_plain_sized(out, format, strlen(format));
143 }
144 
145 static void
u_printf_impl(FILE * out,const char * buffer,size_t buffer_size,const u_printf_info * info,const u_printf_info ** info_ptr,unsigned info_size)146 u_printf_impl(FILE *out, const char *buffer, size_t buffer_size,
147               const u_printf_info *info,
148               const u_printf_info **info_ptr,
149               unsigned info_size)
150 {
151    bool use_singleton = info == NULL && info_ptr == NULL;
152    for (size_t buf_pos = 0; buf_pos < buffer_size;) {
153       uint32_t fmt_idx = *(uint32_t*)&buffer[buf_pos];
154 
155       /* Don't die on invalid printf buffers due to aborted shaders. */
156       if (fmt_idx == 0)
157          break;
158 
159       /* the idx is 1 based, and hashes are nonzero */
160       assert(fmt_idx > 0);
161 
162       const u_printf_info *fmt;
163       if (use_singleton) {
164          fmt = u_printf_singleton_search(fmt_idx /* hash */);
165          if (!fmt)
166             return;
167       } else {
168          fmt_idx -= 1;
169 
170          if (fmt_idx >= info_size)
171             return;
172 
173          fmt = info != NULL ?  &info[fmt_idx] : info_ptr[fmt_idx];
174       }
175 
176       const char *format = fmt->strings;
177       buf_pos += sizeof(fmt_idx);
178 
179       if (!fmt->num_args) {
180          u_printf_plain(out, format);
181          continue;
182       }
183 
184       for (int i = 0; i < fmt->num_args; i++) {
185          int arg_size = fmt->arg_sizes[i];
186          size_t spec_pos = util_printf_next_spec_pos(format, 0);
187 
188          /* If we hit an unused argument we skip all remaining ones */
189          if (spec_pos == -1)
190             break;
191 
192          const char *token = util_printf_prev_tok(&format[spec_pos]);
193          const char *next_format = &format[spec_pos + 1];
194 
195          /* print the part before the format token */
196          if (token != format)
197             u_printf_plain_sized(out, format, token - format);
198 
199          char *print_str = strndup(token, next_format - token);
200          /* rebase spec_pos so we can use it with print_str */
201          spec_pos += format - token;
202 
203          /* print the formatted part */
204          if (print_str[spec_pos] == 's') {
205             uint64_t idx;
206             memcpy(&idx, &buffer[buf_pos], 8);
207             fprintf(out, print_str, &fmt->strings[idx]);
208 
209          /* Never pass a 'n' spec to the host printf */
210          } else if (print_str[spec_pos] != 'n') {
211             char *vec_pos = strchr(print_str, 'v');
212             char *mod_pos = strpbrk(print_str, "hl");
213 
214             int component_count = 1;
215             if (vec_pos != NULL) {
216                /* non vector part of the format */
217                size_t base = mod_pos ? mod_pos - print_str : spec_pos;
218                size_t l = base - (vec_pos - print_str) - 1;
219                char *vec = strndup(&vec_pos[1], l);
220                component_count = atoi(vec);
221                free(vec);
222 
223                /* remove the vector and precision stuff */
224                memmove(&print_str[vec_pos - print_str], &print_str[spec_pos], 2);
225             }
226 
227             /* in fact vec3 are vec4 */
228             int men_components = component_count == 3 ? 4 : component_count;
229             size_t elmt_size = arg_size / men_components;
230             bool is_float = strpbrk(print_str, "fFeEgGaA") != NULL;
231 
232             for (int i = 0; i < component_count; i++) {
233                size_t elmt_buf_pos = buf_pos + i * elmt_size;
234                switch (elmt_size) {
235                case 1: {
236                   uint8_t v;
237                   memcpy(&v, &buffer[elmt_buf_pos], elmt_size);
238                   fprintf(out, print_str, v);
239                   break;
240                }
241                case 2: {
242                   uint16_t v;
243                   memcpy(&v, &buffer[elmt_buf_pos], elmt_size);
244                   fprintf(out, print_str, v);
245                   break;
246                }
247                case 4: {
248                   if (is_float) {
249                      float v;
250                      memcpy(&v, &buffer[elmt_buf_pos], elmt_size);
251                      fprintf(out, print_str, v);
252                   } else {
253                      uint32_t v;
254                      memcpy(&v, &buffer[elmt_buf_pos], elmt_size);
255                      fprintf(out, print_str, v);
256                   }
257                   break;
258                }
259                case 8: {
260                   if (is_float) {
261                      double v;
262                      memcpy(&v, &buffer[elmt_buf_pos], elmt_size);
263                      fprintf(out, print_str, v);
264                   } else {
265                      uint64_t v;
266                      memcpy(&v, &buffer[elmt_buf_pos], elmt_size);
267                      fprintf(out, print_str, v);
268                   }
269                   break;
270                }
271                default:
272                   assert(false);
273                   break;
274                }
275 
276                if (i < component_count - 1)
277                   fprintf(out, ",");
278             }
279          }
280 
281          /* rebase format */
282          format = next_format;
283          free(print_str);
284 
285          buf_pos += arg_size;
286          buf_pos = align_uintptr(buf_pos, 4);
287       }
288 
289       /* print remaining */
290       u_printf_plain(out, format);
291    }
292 }
293 
u_printf(FILE * out,const char * buffer,size_t buffer_size,const u_printf_info * info,unsigned info_size)294 void u_printf(FILE *out, const char *buffer, size_t buffer_size,
295               const u_printf_info *info, unsigned info_size)
296 {
297    u_printf_impl(out, buffer, buffer_size, info, NULL, info_size);
298 }
299 
u_printf_ptr(FILE * out,const char * buffer,size_t buffer_size,const u_printf_info ** info,unsigned info_size)300 void u_printf_ptr(FILE *out, const char *buffer, size_t buffer_size,
301                   const u_printf_info **info, unsigned info_size)
302 {
303    u_printf_impl(out, buffer, buffer_size, NULL, info, info_size);
304 }
305 
306 void
u_printf_serialize_info(struct blob * blob,const u_printf_info * printf_info,unsigned printf_info_count)307 u_printf_serialize_info(struct blob *blob,
308                         const u_printf_info *printf_info,
309                         unsigned printf_info_count)
310 {
311    blob_write_uint32(blob, printf_info_count);
312    for (int i = 0; i < printf_info_count; i++) {
313       const u_printf_info *info = &printf_info[i];
314       blob_write_uint32(blob, info->num_args);
315       blob_write_uint32(blob, info->string_size);
316       blob_write_bytes(blob, info->arg_sizes,
317                        info->num_args * sizeof(info->arg_sizes[0]));
318       /* we can't use blob_write_string, because it contains multiple NULL
319        * terminated strings */
320       blob_write_bytes(blob, info->strings, info->string_size);
321    }
322 }
323 
324 u_printf_info *
u_printf_deserialize_info(void * mem_ctx,struct blob_reader * blob,unsigned * printf_info_count)325 u_printf_deserialize_info(void *mem_ctx,
326                           struct blob_reader *blob,
327                           unsigned *printf_info_count)
328 {
329    *printf_info_count = blob_read_uint32(blob);
330 
331    u_printf_info *printf_info =
332       ralloc_array(mem_ctx, u_printf_info, *printf_info_count);
333 
334    for (int i = 0; i < *printf_info_count; i++) {
335       u_printf_info *info = &printf_info[i];
336       info->num_args = blob_read_uint32(blob);
337       info->string_size = blob_read_uint32(blob);
338       info->arg_sizes = ralloc_array(mem_ctx, unsigned, info->num_args);
339       blob_copy_bytes(blob, info->arg_sizes,
340                       info->num_args * sizeof(info->arg_sizes[0]));
341       info->strings = ralloc_array(mem_ctx, char, info->string_size);
342       blob_copy_bytes(blob, info->strings, info->string_size);
343    }
344 
345    return printf_info;
346 }
347 
348 /*
349  * Hash the format string, allowing the driver to pool format strings.
350  *
351  * Post-condition: hash is nonzero. This is convenient.
352  */
353 uint32_t
u_printf_hash(const u_printf_info * info)354 u_printf_hash(const u_printf_info *info)
355 {
356    struct blob blob;
357    blob_init(&blob);
358    u_printf_serialize_info(&blob, info, 1);
359    uint32_t hash = XXH32(blob.data, blob.size, 0);
360    blob_finish(&blob);
361 
362    /* Force things away from zero. This weakens the hash only slightly, as
363     * there's only a 2^-31 probability of hashing to either hash=0 or hash=1.
364     */
365    if (hash == 0) {
366       hash = 1;
367    }
368 
369    assert(hash != 0);
370    return hash;
371 }
372 
373 static struct {
374    uint32_t users;
375    struct hash_table_u64 *ht;
376 } u_printf_cache = {0};
377 
378 static simple_mtx_t u_printf_lock = SIMPLE_MTX_INITIALIZER;
379 
380 void
u_printf_singleton_init_or_ref(void)381 u_printf_singleton_init_or_ref(void)
382 {
383    simple_mtx_lock(&u_printf_lock);
384 
385    if ((u_printf_cache.users++) == 0) {
386       u_printf_cache.ht = _mesa_hash_table_u64_create(NULL);
387    }
388 
389    simple_mtx_unlock(&u_printf_lock);
390 }
391 
392 void
u_printf_singleton_decref()393 u_printf_singleton_decref()
394 {
395    simple_mtx_lock(&u_printf_lock);
396    assert(u_printf_cache.users > 0);
397 
398    if ((--u_printf_cache.users) == 0) {
399       ralloc_free(u_printf_cache.ht);
400       memset(&u_printf_cache, 0, sizeof(u_printf_cache));
401    }
402 
403    simple_mtx_unlock(&u_printf_lock);
404 }
405 
406 static void
assert_singleton_exists_and_is_locked()407 assert_singleton_exists_and_is_locked()
408 {
409    simple_mtx_assert_locked(&u_printf_lock);
410    assert(u_printf_cache.users > 0);
411 }
412 
413 static const u_printf_info *
u_printf_singleton_search_locked(uint32_t hash)414 u_printf_singleton_search_locked(uint32_t hash)
415 {
416    assert_singleton_exists_and_is_locked();
417 
418    return _mesa_hash_table_u64_search(u_printf_cache.ht, hash);
419 }
420 
421 static void
u_printf_singleton_add_locked(const u_printf_info * info)422 u_printf_singleton_add_locked(const u_printf_info *info)
423 {
424    assert_singleton_exists_and_is_locked();
425 
426    /* If the format string is already known, do nothing. */
427    uint32_t hash = u_printf_hash(info);
428    const u_printf_info *cached = u_printf_singleton_search_locked(hash);
429    if (cached != NULL) {
430       assert(u_printf_hash(cached) == hash && "hash table invariant");
431       assert(!strcmp(cached->strings, info->strings) && "assume no collisions");
432       return;
433    }
434 
435    /* Otherwise, we need to add the string to the table. Doing so requires
436     * a deep-clone, so the singleton will probably outlive our parameter.
437     */
438    u_printf_info *clone = rzalloc(u_printf_cache.ht, u_printf_info);
439    clone->num_args = info->num_args;
440    clone->string_size = info->string_size;
441    clone->arg_sizes = ralloc_memdup(u_printf_cache.ht, info->arg_sizes,
442                                     sizeof(info->arg_sizes[0]) * info->num_args);
443    clone->strings = ralloc_memdup(u_printf_cache.ht, info->strings,
444                                   info->string_size);
445 
446    assert(_mesa_hash_table_u64_search(u_printf_cache.ht, hash) == NULL &&
447           "no duplicates at this point");
448 
449    _mesa_hash_table_u64_insert(u_printf_cache.ht, hash, clone);
450 }
451 
452 const u_printf_info *
u_printf_singleton_search(uint32_t hash)453 u_printf_singleton_search(uint32_t hash)
454 {
455    simple_mtx_lock(&u_printf_lock);
456    const u_printf_info *info = u_printf_singleton_search_locked(hash);
457    simple_mtx_unlock(&u_printf_lock);
458    return info;
459 }
460 
461 void
u_printf_singleton_add(const u_printf_info * info,unsigned count)462 u_printf_singleton_add(const u_printf_info *info, unsigned count)
463 {
464    simple_mtx_lock(&u_printf_lock);
465    for (unsigned i = 0; i < count; ++i) {
466       u_printf_singleton_add_locked(&info[i]);
467    }
468    simple_mtx_unlock(&u_printf_lock);
469 }
470 
471 void
u_printf_singleton_add_serialized(const void * data,size_t data_size)472 u_printf_singleton_add_serialized(const void *data, size_t data_size)
473 {
474    struct blob_reader blob;
475    blob_reader_init(&blob, data, data_size);
476 
477    unsigned count = 0;
478    u_printf_info *info = u_printf_deserialize_info(NULL, &blob, &count);
479    u_printf_singleton_add(info, count);
480    ralloc_free(info);
481 }
482