xref: /aosp_15_r20/external/mesa3d/src/imagination/common/pvr_dump.h (revision 6104692788411f58d303aa86923a9ff6ecaded22)
1 /*
2  * Copyright © 2022 Imagination Technologies Ltd.
3  *
4  * Permission is hereby granted, free of charge, to any person obtaining a copy
5  * of this software and associated documentation files (the "Software"), to deal
6  * in the Software without restriction, including without limitation the rights
7  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8  * copies of the Software, and to permit persons to whom the Software is
9  * furnished to do so, subject to the following conditions:
10  *
11  * The above copyright notice and this permission notice (including the next
12  * paragraph) shall be included in all copies or substantial portions of the
13  * Software.
14  *
15  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21  * SOFTWARE.
22  */
23 
24 #ifndef PVR_DUMP_H
25 #define PVR_DUMP_H
26 
27 #include <inttypes.h>
28 #include <stdarg.h>
29 #include <stdbool.h>
30 #include <stdint.h>
31 #include <stdio.h>
32 
33 #include "pvr_types.h"
34 #include "pvr_util.h"
35 #include "util/macros.h"
36 #include "util/u_math.h"
37 
38 /** BASIC PRINTING **/
39 
40 #define PVR_DUMP_OFFSET_PREFIX "[%0*" PRIx64 "] "
41 
42 /** CONTEXTS **/
43 
44 #define PVR_DUMP_INDENT_SIZE 2U
45 #define PVR_DUMP_FIELD_COLUMN_WIDTH 36U
46 
47 /* This is an invalid context used to permanently mark popped contexts as
48  * unusable. All operations on a context check that it's the "top" context
49  * by ensuring it has no active child. The only way to remove the active child
50  * of a context is by popping the active child directly. Assigning an invalid
51  * context as the active child of a context therefore makes it impossible to
52  * use.
53  */
54 extern const struct pvr_dump_ctx __pvr_dump_ctx_invalid;
55 
56 struct pvr_dump_ctx {
57    /* This is const because only the "top" context should ever be modified. It's
58     * fine to extract information from the parent context, but not to modify it.
59     * There is *one* exception: pvr_dump_ctx_pop() must cast away the const to
60     * return the parent context as the new "top" context. This is considered
61     * sound because the parent context was not const when assigned here in
62     * pvr_dump_ctx_push().
63     */
64    const struct pvr_dump_ctx *parent;
65 
66    /* This is const because it's not meant to be used for access - it's just a
67     * way of checking if this context is the "top" context (see the comment on
68     * __pvr_dump_ctx_invalid for more details). Unlike parent, the const
69     * qualifier here should never be cast away.
70     */
71    const struct pvr_dump_ctx *active_child;
72 
73    FILE *file;
74    const char *name;
75 
76    uint32_t allowed_child_depth;
77    uint32_t parent_indent;
78 
79    /* User-modifiable values */
80    uint32_t indent;
81    bool ok;
82 };
83 
84 static inline uint32_t
__pvr_dump_ctx_get_indent(const struct pvr_dump_ctx * const ctx)85 __pvr_dump_ctx_get_indent(const struct pvr_dump_ctx *const ctx)
86 {
87    return (ctx->parent_indent + ctx->indent) * PVR_DUMP_INDENT_SIZE;
88 }
89 
90 struct pvr_dump_buffer_ctx {
91    struct pvr_dump_ctx base;
92 
93    const void *initial_ptr;
94    uint64_t capacity;
95 
96    /* User-modifiable values */
97    const void *ptr;
98    uint64_t remaining_size;
99 };
100 
101 #define pvr_dump_printf(ctx, format, args...)           \
102    pvr_dump_printf_cont(ctx,                            \
103                         "%*s" format,                   \
104                         __pvr_dump_ctx_get_indent(ctx), \
105                         "",                             \
106                         ##args)
107 
108 /* Same as pvr_dump_printf(), but with no indent.
109  * Intended for continuation lines.
110  */
111 #define pvr_dump_printf_cont(ctx, format, args...) \
112    fprintf((ctx)->file, format, ##args)
113 
114 #define pvr_dump_println(ctx, format, args...) \
115    pvr_dump_printf(ctx, format "\n", ##args)
116 
117 #define pvr_dump_println_cont(ctx, format, args...) \
118    pvr_dump_printf_cont(ctx, format "\n", ##args)
119 
120 #define pvr_dump_print_eol(ctx) fprintf((ctx)->file, "\n")
121 
122 #define pvr_dump_mark_section(ctx, format, args...)                \
123    do {                                                            \
124       pvr_dump_print_eol(ctx);                                     \
125       pvr_dump_println(ctx, "------- " format " -------", ##args); \
126    } while (0)
127 
128 #define pvr_dump_buffer_print_header_prefix(ctx)                            \
129    do {                                                                     \
130       struct pvr_dump_buffer_ctx *_prefix_ctx = (ctx);                      \
131       pvr_dump_printf(&_prefix_ctx->base,                                   \
132                       PVR_DUMP_OFFSET_PREFIX,                               \
133                       u64_dec_digits(_prefix_ctx->capacity),                \
134                       _prefix_ctx->capacity - _prefix_ctx->remaining_size); \
135    } while (0)
136 
137 #define pvr_dump_buffer_print_header_line(ctx, format, args...) \
138    do {                                                         \
139       struct pvr_dump_buffer_ctx *_ctx = (ctx);                 \
140       pvr_dump_buffer_print_header_prefix(_ctx);                \
141       pvr_dump_printf_cont(&_ctx->base, format "\n", ##args);   \
142    } while (0)
143 
144 #define pvr_dump_msg(ctx, prefix, ret, format, args...)            \
145    ({                                                              \
146       bool _ret = (ret);                                           \
147       struct pvr_dump_ctx *_ctx = (ctx);                           \
148       pvr_dump_println(_ctx, "<!" prefix "! " format ">", ##args); \
149       if (!_ret)                                                   \
150          _ctx->ok = _ret;                                          \
151       _ret;                                                        \
152    })
153 
154 #define pvr_dump_error(ctx, format, args...) \
155    pvr_dump_msg(ctx, "ERROR", false, format, ##args)
156 
157 #define pvr_dump_warn(ctx, format, args...) \
158    pvr_dump_msg(ctx, "WARN", true, format, ##args)
159 
pvr_dump_ctx_require_top(struct pvr_dump_ctx * const ctx)160 static inline bool pvr_dump_ctx_require_top(struct pvr_dump_ctx *const ctx)
161 {
162    if (ctx->active_child != NULL)
163       return pvr_dump_error(ctx, "use of non-top context");
164 
165    return true;
166 }
167 
pvr_dump_indent(struct pvr_dump_ctx * const ctx)168 static inline void pvr_dump_indent(struct pvr_dump_ctx *const ctx)
169 {
170    ctx->indent++;
171 }
172 
pvr_dump_dedent(struct pvr_dump_ctx * const ctx)173 static inline void pvr_dump_dedent(struct pvr_dump_ctx *const ctx)
174 {
175    if (ctx->indent)
176       ctx->indent--;
177 }
178 
__pvr_dump_ctx_init(struct pvr_dump_ctx * const ctx,const struct pvr_dump_ctx * const parent,FILE * const file,const char * const name,const uint32_t allowed_child_depth,const uint32_t parent_indent)179 static inline void __pvr_dump_ctx_init(struct pvr_dump_ctx *const ctx,
180                                        const struct pvr_dump_ctx *const parent,
181                                        FILE *const file,
182                                        const char *const name,
183                                        const uint32_t allowed_child_depth,
184                                        const uint32_t parent_indent)
185 {
186    ctx->parent = parent;
187    ctx->active_child = NULL;
188 
189    ctx->file = file;
190    ctx->name = name;
191 
192    ctx->allowed_child_depth = allowed_child_depth;
193    ctx->parent_indent = parent_indent;
194    ctx->indent = 0;
195    ctx->ok = true;
196 }
197 
__pvr_dump_ctx_mark_popped(struct pvr_dump_ctx * const ctx)198 static inline void __pvr_dump_ctx_mark_popped(struct pvr_dump_ctx *const ctx)
199 {
200    ctx->active_child = &__pvr_dump_ctx_invalid;
201 }
202 
pvr_dump_begin(struct pvr_dump_ctx * const root_ctx,FILE * const file,const char * const name,const uint32_t max_depth)203 static inline void pvr_dump_begin(struct pvr_dump_ctx *const root_ctx,
204                                   FILE *const file,
205                                   const char *const name,
206                                   const uint32_t max_depth)
207 {
208    __pvr_dump_ctx_init(root_ctx, NULL, file, name, max_depth, 0);
209 
210    flockfile(file);
211    pvr_dump_println(root_ctx, "======= BEGIN %s =======", name);
212 }
213 
pvr_dump_end(struct pvr_dump_ctx * const root_ctx)214 static inline bool pvr_dump_end(struct pvr_dump_ctx *const root_ctx)
215 {
216    /* In order to end a dump, we must be in a root context (no parent) and have
217     * no active child context.
218     */
219    if (!pvr_dump_ctx_require_top(root_ctx))
220       return false;
221 
222    if (root_ctx->parent)
223       return pvr_dump_error(root_ctx, "ending non-root context");
224 
225    pvr_dump_println(root_ctx, "======= END %s =======", root_ctx->name);
226    funlockfile(root_ctx->file);
227 
228    __pvr_dump_ctx_mark_popped(root_ctx);
229 
230    return true;
231 }
232 
pvr_dump_ctx_push(struct pvr_dump_ctx * const ctx,struct pvr_dump_ctx * const parent_ctx)233 static inline bool pvr_dump_ctx_push(struct pvr_dump_ctx *const ctx,
234                                      struct pvr_dump_ctx *const parent_ctx)
235 {
236    if (!parent_ctx->ok)
237       return false;
238 
239    if (!parent_ctx->allowed_child_depth)
240       return pvr_dump_error(parent_ctx, "context stack depth limit reached");
241 
242    __pvr_dump_ctx_init(ctx,
243                        parent_ctx,
244                        parent_ctx->file,
245                        parent_ctx->name,
246                        parent_ctx->allowed_child_depth - 1,
247                        parent_ctx->parent_indent + parent_ctx->indent);
248 
249    parent_ctx->active_child = ctx;
250 
251    return true;
252 }
253 
254 static inline struct pvr_dump_ctx *
pvr_dump_ctx_pop(struct pvr_dump_ctx * const ctx)255 pvr_dump_ctx_pop(struct pvr_dump_ctx *const ctx)
256 {
257    struct pvr_dump_ctx *const parent = (struct pvr_dump_ctx *)ctx->parent;
258 
259    if (!pvr_dump_ctx_require_top(ctx))
260       return NULL;
261 
262    if (!parent) {
263       pvr_dump_error(ctx, "popped root context");
264       return NULL;
265    }
266 
267    parent->active_child = NULL;
268 
269    __pvr_dump_ctx_mark_popped(ctx);
270 
271    return parent;
272 }
273 
274 static inline bool
pvr_dump_buffer_ctx_push(struct pvr_dump_buffer_ctx * const ctx,struct pvr_dump_ctx * const parent_ctx,const void * const initial_ptr,const uint64_t size)275 pvr_dump_buffer_ctx_push(struct pvr_dump_buffer_ctx *const ctx,
276                          struct pvr_dump_ctx *const parent_ctx,
277                          const void *const initial_ptr,
278                          const uint64_t size)
279 {
280    if (!pvr_dump_ctx_push(&ctx->base, parent_ctx))
281       return false;
282 
283    ctx->initial_ptr = initial_ptr;
284    ctx->capacity = size;
285 
286    ctx->ptr = initial_ptr;
287    ctx->remaining_size = size;
288 
289    return true;
290 }
291 
292 static inline struct pvr_dump_ctx *
pvr_dump_buffer_ctx_pop(struct pvr_dump_buffer_ctx * const ctx)293 pvr_dump_buffer_ctx_pop(struct pvr_dump_buffer_ctx *const ctx)
294 {
295    return pvr_dump_ctx_pop(&ctx->base);
296 }
297 
298 bool pvr_dump_buffer_hex(struct pvr_dump_buffer_ctx *ctx, uint64_t nr_bytes);
299 
__pvr_dump_buffer_advance(struct pvr_dump_buffer_ctx * ctx,const uint64_t nr_bytes)300 static inline void __pvr_dump_buffer_advance(struct pvr_dump_buffer_ctx *ctx,
301                                              const uint64_t nr_bytes)
302 {
303    ctx->ptr = (uint8_t *)ctx->ptr + nr_bytes;
304    ctx->remaining_size -= nr_bytes;
305 }
306 
pvr_dump_buffer_advance(struct pvr_dump_buffer_ctx * ctx,const uint64_t nr_bytes)307 static inline bool pvr_dump_buffer_advance(struct pvr_dump_buffer_ctx *ctx,
308                                            const uint64_t nr_bytes)
309 {
310    if (!ctx->base.ok || !pvr_dump_ctx_require_top(&ctx->base))
311       return false;
312 
313    if (nr_bytes > ctx->remaining_size)
314       return pvr_dump_error(&ctx->base, "advanced past end of context buffer");
315 
316    __pvr_dump_buffer_advance(ctx, nr_bytes);
317 
318    return true;
319 }
320 
__pvr_dump_buffer_rewind(struct pvr_dump_buffer_ctx * ctx,const uint32_t nr_bytes)321 static inline void __pvr_dump_buffer_rewind(struct pvr_dump_buffer_ctx *ctx,
322                                             const uint32_t nr_bytes)
323 {
324    ctx->ptr = (uint8_t *)ctx->ptr - nr_bytes;
325    ctx->remaining_size += nr_bytes;
326 }
327 
pvr_dump_buffer_rewind(struct pvr_dump_buffer_ctx * ctx,const uint32_t nr_bytes)328 static inline bool pvr_dump_buffer_rewind(struct pvr_dump_buffer_ctx *ctx,
329                                           const uint32_t nr_bytes)
330 {
331    if (!ctx->base.ok || !pvr_dump_ctx_require_top(&ctx->base))
332       return false;
333 
334    if (nr_bytes > ctx->capacity - ctx->remaining_size)
335       return pvr_dump_error(&ctx->base, "rewound past start of context buffer");
336 
337    __pvr_dump_buffer_rewind(ctx, nr_bytes);
338 
339    return true;
340 }
341 
pvr_dump_buffer_truncate(struct pvr_dump_buffer_ctx * ctx,const uint64_t remaining_size)342 static inline bool pvr_dump_buffer_truncate(struct pvr_dump_buffer_ctx *ctx,
343                                             const uint64_t remaining_size)
344 {
345    if (!ctx->base.ok || !pvr_dump_ctx_require_top(&ctx->base))
346       return false;
347 
348    if (remaining_size > ctx->remaining_size)
349       return pvr_dump_error(&ctx->base, "truncated to larger size");
350 
351    ctx->remaining_size = remaining_size;
352 
353    return true;
354 }
355 
356 static inline const void *restrict
pvr_dump_buffer_peek(struct pvr_dump_buffer_ctx * const restrict ctx,const uint64_t nr_bytes)357 pvr_dump_buffer_peek(struct pvr_dump_buffer_ctx *const restrict ctx,
358                      const uint64_t nr_bytes)
359 {
360    if (!ctx->base.ok || !pvr_dump_ctx_require_top(&ctx->base))
361       return NULL;
362 
363    if (nr_bytes > ctx->remaining_size) {
364       pvr_dump_error(&ctx->base, "peeked past end of context buffer");
365       return NULL;
366    }
367 
368    return ctx->ptr;
369 }
370 
371 static inline const void *restrict
pvr_dump_buffer_take(struct pvr_dump_buffer_ctx * const restrict ctx,const uint64_t nr_bytes)372 pvr_dump_buffer_take(struct pvr_dump_buffer_ctx *const restrict ctx,
373                      const uint64_t nr_bytes)
374 {
375    const void *const ptr = pvr_dump_buffer_peek(ctx, nr_bytes);
376 
377    if (ptr)
378       __pvr_dump_buffer_advance(ctx, nr_bytes);
379 
380    return ptr;
381 }
382 
383 static inline void
pvr_dump_buffer_restart(struct pvr_dump_buffer_ctx * const ctx)384 pvr_dump_buffer_restart(struct pvr_dump_buffer_ctx *const ctx)
385 {
386    ctx->ptr = ctx->initial_ptr;
387    ctx->remaining_size = ctx->capacity;
388 }
389 
390 /*****************************************************************************
391    Field printers
392 *****************************************************************************/
393 
394 #define pvr_dump_field(ctx, name, format, args...)     \
395    pvr_dump_println(ctx,                               \
396                     "%-*s : " format,                  \
397                     PVR_DUMP_FIELD_COLUMN_WIDTH -      \
398                        __pvr_dump_ctx_get_indent(ctx), \
399                     name,                              \
400                     ##args)
401 
402 #define pvr_dump_field_computed(ctx, name, format, raw_format, args...) \
403    pvr_dump_field(ctx, name, format " (" raw_format ")", ##args)
404 
405 #define pvr_dump_field_error(ctx, format, args...)               \
406    ({                                                            \
407       struct pvr_dump_ctx *_ctx = (ctx);                         \
408       pvr_dump_field(_ctx, "<!ERROR!>", "<" format ">", ##args); \
409       _ctx->ok = false;                                          \
410       false;                                                     \
411    })
412 
413 /*****************************************************************************
414    Field printers: integers
415 *****************************************************************************/
416 
pvr_dump_field_u32(struct pvr_dump_ctx * const ctx,const char * const name,const uint32_t value)417 static inline void pvr_dump_field_u32(struct pvr_dump_ctx *const ctx,
418                                       const char *const name,
419                                       const uint32_t value)
420 {
421    pvr_dump_field(ctx, name, "%" PRIu32, value);
422 }
423 
pvr_dump_field_u32_units(struct pvr_dump_ctx * const ctx,const char * const name,const uint32_t value,const char * const units)424 static inline void pvr_dump_field_u32_units(struct pvr_dump_ctx *const ctx,
425                                             const char *const name,
426                                             const uint32_t value,
427                                             const char *const units)
428 {
429    pvr_dump_field(ctx, name, "%" PRIu32 " %s", value, units);
430 }
431 
pvr_dump_field_u32_offset(struct pvr_dump_ctx * const ctx,const char * const name,const uint32_t value,const uint32_t offset)432 static inline void pvr_dump_field_u32_offset(struct pvr_dump_ctx *const ctx,
433                                              const char *const name,
434                                              const uint32_t value,
435                                              const uint32_t offset)
436 {
437    pvr_dump_field_computed(ctx,
438                            name,
439                            "%" PRIu32,
440                            "%" PRIu32 " + %" PRIu32,
441                            value + offset,
442                            value,
443                            offset);
444 }
445 
pvr_dump_field_u32_scaled(struct pvr_dump_ctx * const ctx,const char * const name,const uint32_t value,const uint32_t scale)446 static inline void pvr_dump_field_u32_scaled(struct pvr_dump_ctx *const ctx,
447                                              const char *const name,
448                                              const uint32_t value,
449                                              const uint32_t scale)
450 {
451    pvr_dump_field_computed(ctx,
452                            name,
453                            "%" PRIu32,
454                            "%" PRIu32 " x %" PRIu32,
455                            value * scale,
456                            value,
457                            scale);
458 }
459 
460 static inline void
pvr_dump_field_u32_scaled_units(struct pvr_dump_ctx * const ctx,const char * const name,const uint32_t value,const uint32_t scale,const char * const units)461 pvr_dump_field_u32_scaled_units(struct pvr_dump_ctx *const ctx,
462                                 const char *const name,
463                                 const uint32_t value,
464                                 const uint32_t scale,
465                                 const char *const units)
466 {
467    pvr_dump_field_computed(ctx,
468                            name,
469                            "%" PRIu32 " %s",
470                            "%" PRIu32 " x %" PRIu32 " %s",
471                            value * scale,
472                            units,
473                            value,
474                            scale,
475                            units);
476 }
477 
pvr_dump_field_u32_zero(struct pvr_dump_ctx * const ctx,const char * const name,const uint32_t value,const uint32_t zero_value)478 static inline void pvr_dump_field_u32_zero(struct pvr_dump_ctx *const ctx,
479                                            const char *const name,
480                                            const uint32_t value,
481                                            const uint32_t zero_value)
482 {
483    if (value)
484       pvr_dump_field_u32(ctx, name, value);
485    else
486       pvr_dump_field_computed(ctx, name, "%" PRIu32, "0", zero_value);
487 }
488 
pvr_dump_field_x32(struct pvr_dump_ctx * const ctx,const char * const name,const uint32_t value,const uint32_t chars)489 static inline void pvr_dump_field_x32(struct pvr_dump_ctx *const ctx,
490                                       const char *const name,
491                                       const uint32_t value,
492                                       const uint32_t chars)
493 {
494    pvr_dump_field(ctx,
495                   name,
496                   "0x%0*" PRIx32,
497                   chars,
498                   value & BITFIELD_MASK(chars * 4));
499 }
500 
pvr_dump_field_u64(struct pvr_dump_ctx * const ctx,const char * const name,const uint64_t value)501 static inline void pvr_dump_field_u64(struct pvr_dump_ctx *const ctx,
502                                       const char *const name,
503                                       const uint64_t value)
504 {
505    pvr_dump_field(ctx, name, "%" PRIu64, value);
506 }
507 
pvr_dump_field_u64_units(struct pvr_dump_ctx * const ctx,const char * const name,const uint64_t value,const char * const units)508 static inline void pvr_dump_field_u64_units(struct pvr_dump_ctx *const ctx,
509                                             const char *const name,
510                                             const uint64_t value,
511                                             const char *const units)
512 {
513    pvr_dump_field(ctx, name, "%" PRIu64 " %s", value, units);
514 }
515 
516 /*****************************************************************************
517    Field printers: floating point
518 *****************************************************************************/
519 
pvr_dump_field_f32(struct pvr_dump_ctx * const ctx,const char * const name,const float value)520 static inline void pvr_dump_field_f32(struct pvr_dump_ctx *const ctx,
521                                       const char *const name,
522                                       const float value)
523 {
524    pvr_dump_field_computed(ctx, name, "%f", "0x%08" PRIx32, value, fui(value));
525 }
526 
527 /*****************************************************************************
528    Field printers: fixed point
529 *****************************************************************************/
530 
531 /* clang-format off */
532 static const char *const __fixed_frac_str_table_4[1 << 4] = {
533    "0", "0625", "125", "1875", "25", "3125", "375", "4375",
534    "5", "5625", "625", "6875", "75", "8125", "875", "9375",
535 };
536 /* clang-format on */
537 
pvr_dump_field_uq4_4(struct pvr_dump_ctx * const ctx,const char * const name,const uint32_t raw_value)538 static inline void pvr_dump_field_uq4_4(struct pvr_dump_ctx *const ctx,
539                                         const char *const name,
540                                         const uint32_t raw_value)
541 {
542    const uint32_t int_part = (raw_value & BITFIELD_RANGE(4, 4)) >> 4;
543    const uint32_t frac_part = raw_value & BITFIELD_MASK(4);
544 
545    pvr_dump_field_computed(ctx,
546                            name,
547                            "%" PRIu32 ".%s",
548                            "0x%02" PRIx32, /* Or %0*x where *=(nr_bits+3)/4 */
549                            int_part,
550                            __fixed_frac_str_table_4[frac_part],
551                            raw_value & BITFIELD_MASK(8));
552 }
553 
pvr_dump_field_uq4_4_offset(struct pvr_dump_ctx * const ctx,const char * const name,const uint32_t raw_value,const uint32_t raw_offset)554 static inline void pvr_dump_field_uq4_4_offset(struct pvr_dump_ctx *const ctx,
555                                                const char *const name,
556                                                const uint32_t raw_value,
557                                                const uint32_t raw_offset)
558 {
559    const uint32_t raw_offset_value = raw_value + raw_offset;
560 
561    const uint32_t int_part = (raw_offset_value & BITFIELD_RANGE(4, 4)) >> 4;
562    const uint32_t frac_part = raw_offset_value & BITFIELD_MASK(4);
563 
564    pvr_dump_field_computed(ctx,
565                            name,
566                            "%" PRIu32 ".%s",
567                            "0x%02" PRIx32 " + 0x%02" PRIx32,
568                            int_part,
569                            __fixed_frac_str_table_4[frac_part],
570                            raw_value & BITFIELD_MASK(8),
571                            raw_offset);
572 }
573 
574 /*****************************************************************************
575    Field printers: device address
576 *****************************************************************************/
577 
pvr_dump_field_addr_non_null(struct pvr_dump_ctx * const ctx,const char * const name,const pvr_dev_addr_t value)578 static inline void pvr_dump_field_addr_non_null(struct pvr_dump_ctx *const ctx,
579                                                 const char *const name,
580                                                 const pvr_dev_addr_t value)
581 {
582    pvr_dump_field(ctx, name, PVR_DEV_ADDR_FMT, value.addr);
583 }
584 
pvr_dump_field_addr(struct pvr_dump_ctx * const ctx,const char * const name,const pvr_dev_addr_t value)585 static inline void pvr_dump_field_addr(struct pvr_dump_ctx *const ctx,
586                                        const char *const name,
587                                        const pvr_dev_addr_t value)
588 {
589    if (value.addr)
590       pvr_dump_field_addr_non_null(ctx, name, value);
591    else
592       pvr_dump_field(ctx, name, "<null>");
593 }
594 
pvr_dump_field_addr_split(struct pvr_dump_ctx * const ctx,const char * const name,const pvr_dev_addr_t msb,const pvr_dev_addr_t lsb)595 static inline void pvr_dump_field_addr_split(struct pvr_dump_ctx *const ctx,
596                                              const char *const name,
597                                              const pvr_dev_addr_t msb,
598                                              const pvr_dev_addr_t lsb)
599 {
600    pvr_dump_field_addr(ctx, name, PVR_DEV_ADDR(msb.addr | lsb.addr));
601 
602    pvr_dump_indent(ctx);
603    pvr_dump_field_addr_non_null(ctx, "msb", msb);
604    pvr_dump_field_addr_non_null(ctx, "lsb", lsb);
605    pvr_dump_dedent(ctx);
606 }
607 
pvr_dump_field_addr_offset(struct pvr_dump_ctx * const ctx,const char * const name,const pvr_dev_addr_t value,const pvr_dev_addr_t base)608 static inline void pvr_dump_field_addr_offset(struct pvr_dump_ctx *const ctx,
609                                               const char *const name,
610                                               const pvr_dev_addr_t value,
611                                               const pvr_dev_addr_t base)
612 {
613    pvr_dump_field_computed(ctx,
614                            name,
615                            PVR_DEV_ADDR_FMT,
616                            PVR_DEV_ADDR_FMT " + " PVR_DEV_ADDR_FMT,
617                            PVR_DEV_ADDR_OFFSET(base, value.addr).addr,
618                            base.addr,
619                            value.addr);
620 }
621 
622 /*****************************************************************************
623    Field printers: enums
624 *****************************************************************************/
625 
626 #define pvr_dump_field_enum(ctx, name, value, to_str)               \
627    do {                                                             \
628       __typeof__(value) _value = (value);                           \
629       const char *_str = to_str(_value);                            \
630       if (!_str)                                                    \
631          _str = "<unknown>";                                        \
632       pvr_dump_field_computed(ctx, name, "%s", "%u", _str, _value); \
633    } while (0)
634 
__bool_to_str(const bool b)635 static inline const char *__bool_to_str(const bool b)
636 {
637    return b ? "yes" : "no";
638 }
639 
640 /* A bool is just an enum with two values. */
pvr_dump_field_bool(struct pvr_dump_ctx * const ctx,const char * const name,const bool value)641 static inline void pvr_dump_field_bool(struct pvr_dump_ctx *const ctx,
642                                        const char *const name,
643                                        const bool value)
644 {
645    pvr_dump_field_enum(ctx, name, value, __bool_to_str);
646 }
647 
648 /*****************************************************************************
649    Field printers: string
650 *****************************************************************************/
651 
pvr_dump_field_string(struct pvr_dump_ctx * const ctx,const char * const name,const char * const value)652 static inline void pvr_dump_field_string(struct pvr_dump_ctx *const ctx,
653                                          const char *const name,
654                                          const char *const value)
655 {
656    pvr_dump_field(ctx, name, "%s", value);
657 }
658 
659 /*****************************************************************************
660    Field printers: not present
661 *****************************************************************************/
662 
pvr_dump_field_no_fields(struct pvr_dump_ctx * const ctx)663 static inline void pvr_dump_field_no_fields(struct pvr_dump_ctx *const ctx)
664 {
665    pvr_dump_println(ctx, "<no fields>");
666 }
667 
pvr_dump_field_not_present(struct pvr_dump_ctx * const ctx,const char * const name)668 static inline void pvr_dump_field_not_present(struct pvr_dump_ctx *const ctx,
669                                               const char *const name)
670 {
671    pvr_dump_field(ctx, name, "<not present>");
672 }
673 
674 /*****************************************************************************
675    Field printers: helpers for members
676 *****************************************************************************/
677 
678 /* clang-format off */
679 
680 #define pvr_dump_field_member_u32(ctx, compound, member) \
681    pvr_dump_field_u32(ctx, #member, (compound)->member)
682 
683 #define pvr_dump_field_member_u32_units(ctx, compound, member, units) \
684    pvr_dump_field_u32_units(ctx, #member, (compound)->member, units)
685 
686 #define pvr_dump_field_member_u32_offset(ctx, compound, member, offset) \
687    pvr_dump_field_u32_offset(ctx, #member, (compound)->member, offset)
688 
689 #define pvr_dump_field_member_u32_scaled(ctx, compound, member, scale) \
690    pvr_dump_field_u32_scaled(ctx, #member, (compound)->member, scale)
691 
692 #define pvr_dump_field_member_u32_scaled_units(ctx, compound, member, scale, units) \
693    pvr_dump_field_u32_scaled_units(ctx, #member, (compound)->member, scale, units)
694 
695 #define pvr_dump_field_member_u32_zero(ctx, compound, member, zero_value) \
696    pvr_dump_field_u32_zero(ctx, #member, (compound)->member, zero_value)
697 
698 #define pvr_dump_field_member_x32(ctx, compound, member, chars) \
699    pvr_dump_field_x32(ctx, #member, (compound)->member, chars)
700 
701 #define pvr_dump_field_member_u64(ctx, compound, member) \
702    pvr_dump_field_u64(ctx, #member, (compound)->member)
703 
704 #define pvr_dump_field_member_u64_units(ctx, compound, member, units) \
705    pvr_dump_field_u64_units(ctx, #member, (compound)->member, units)
706 
707 #define pvr_dump_field_member_f32(ctx, compound, member) \
708    pvr_dump_field_f32(ctx, #member, (compound)->member)
709 
710 #define pvr_dump_field_member_uq4_4(ctx, compound, member) \
711    pvr_dump_field_uq4_4(ctx, #member, (compound)->member)
712 
713 #define pvr_dump_field_member_uq4_4_offset(ctx, compound, member, raw_offset) \
714    pvr_dump_field_uq4_4_offset(ctx, #member, (compound)->member, raw_offset)
715 
716 #define pvr_dump_field_member_addr(ctx, compound, member) \
717    pvr_dump_field_addr(ctx, #member, (compound)->member)
718 
719 #define pvr_dump_field_member_addr_offset(ctx, compound, member, base) \
720    pvr_dump_field_addr_offset(ctx, #member, (compound)->member, base)
721 
722 #define pvr_dump_field_member_enum(ctx, compound, member, to_str) \
723    pvr_dump_field_enum(ctx, #member, (compound)->member, to_str)
724 
725 #define pvr_dump_field_member_bool(ctx, compound, member) \
726    pvr_dump_field_bool(ctx, #member, (compound)->member)
727 
728 #define pvr_dump_field_member_string(ctx, compound, member) \
729    pvr_dump_field_string(ctx, #member, (compound)->member)
730 
731 /* clang-format on */
732 
733 #define pvr_dump_field_member_not_present(ctx, compound, member) \
734    do {                                                          \
735       (void)&(compound)->member;                                 \
736       pvr_dump_field_not_present(ctx, #member);                  \
737    } while (0)
738 
739 #endif /* PVR_DUMP_H */
740