xref: /aosp_15_r20/external/mesa3d/src/imagination/common/pvr_dump.c (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 #include <inttypes.h>
25 #include <stdbool.h>
26 #include <stddef.h>
27 #include <stdint.h>
28 
29 #include "pvr_dump.h"
30 #include "pvr_util.h"
31 #include "util/u_math.h"
32 
33 const struct pvr_dump_ctx __pvr_dump_ctx_invalid = {
34    .active_child = &__pvr_dump_ctx_invalid,
35 };
36 
37 /*****************************************************************************
38    Hex dumps
39 *****************************************************************************/
40 
41 #define HEX_WORD_SIZE ((unsigned)sizeof(uint32_t))
42 #define HEX_BYTE_FMT "%02" PRIx8
43 
44 /* This must be even, and should probably always be a power of 2. */
45 #define HEX_LINE_SIZE (HEX_WORD_SIZE * 8)
46 
47 struct pvr_dump_hex_ctx {
48    struct pvr_dump_ctx base;
49 
50    const uint8_t *start_ptr;
51    const uint8_t *end_ptr;
52    uint64_t nr_bytes;
53    uint32_t offset_digits;
54 
55    /* User-modifiable values */
56    const uint8_t *line_ptr;
57 
58    uint32_t prev_non_zero_trailing_zero_bytes;
59    uint64_t prev_non_zero_leading_zero_lines;
60    const uint8_t *prev_non_zero_line;
61    uint64_t zero_lines;
62 };
63 
pvr_dump_hex_ctx_push(struct pvr_dump_hex_ctx * const ctx,struct pvr_dump_buffer_ctx * const parent_ctx,const uint64_t nr_bytes)64 static bool pvr_dump_hex_ctx_push(struct pvr_dump_hex_ctx *const ctx,
65                                   struct pvr_dump_buffer_ctx *const parent_ctx,
66                                   const uint64_t nr_bytes)
67 {
68    const uint64_t real_nr_bytes = nr_bytes ? nr_bytes
69                                            : parent_ctx->remaining_size;
70    bool ret;
71 
72    if (parent_ctx->remaining_size < nr_bytes)
73       return false;
74 
75    ret = pvr_dump_ctx_push(&ctx->base, &parent_ctx->base);
76    if (!ret)
77       return false;
78 
79    ctx->start_ptr = parent_ctx->ptr;
80    ctx->end_ptr = ctx->start_ptr + real_nr_bytes;
81    ctx->nr_bytes = real_nr_bytes;
82    ctx->offset_digits = u64_hex_digits(real_nr_bytes);
83 
84    ctx->line_ptr = ctx->start_ptr;
85 
86    ctx->prev_non_zero_trailing_zero_bytes = 0;
87    ctx->prev_non_zero_leading_zero_lines = 0;
88    ctx->prev_non_zero_line = NULL;
89    ctx->zero_lines = 0;
90 
91    return true;
92 }
93 
94 static struct pvr_dump_buffer_ctx *
pvr_dump_hex_ctx_pop(struct pvr_dump_hex_ctx * const ctx)95 pvr_dump_hex_ctx_pop(struct pvr_dump_hex_ctx *const ctx)
96 {
97    struct pvr_dump_buffer_ctx *parent;
98    struct pvr_dump_ctx *parent_base;
99 
100    if (ctx->line_ptr != ctx->end_ptr) {
101       ctx->base.ok = false;
102       return NULL;
103    }
104 
105    parent_base = pvr_dump_ctx_pop(&ctx->base);
106    if (!parent_base)
107       return NULL;
108 
109    parent = container_of(parent_base, struct pvr_dump_buffer_ctx, base);
110 
111    pvr_dump_buffer_advance(parent, ctx->nr_bytes);
112 
113    return parent;
114 }
115 
pvr_dump_hex_print_prefix(const struct pvr_dump_hex_ctx * ctx,const uint64_t offset)116 static inline void pvr_dump_hex_print_prefix(const struct pvr_dump_hex_ctx *ctx,
117                                              const uint64_t offset)
118 {
119    pvr_dump_printf(&ctx->base,
120                    PVR_DUMP_OFFSET_PREFIX,
121                    ctx->offset_digits,
122                    offset);
123 }
124 
125 #define pvr_dump_hex_println(ctx, offset, format, args...) \
126    pvr_dump_println(&(ctx)->base,                          \
127                     PVR_DUMP_OFFSET_PREFIX format,         \
128                     (ctx)->offset_digits,                  \
129                     offset,                                \
130                     ##args)
131 
132 #define pvr_dump_hex_println_no_prefix(ctx, format, args...) \
133    pvr_dump_println(&(ctx)->base,                            \
134                     "%*c" format,                            \
135                     (ctx)->offset_digits + 3,                \
136                     ' ',                                     \
137                     ##args)
138 
pvr_dump_hex_print_line(const struct pvr_dump_hex_ctx * ctx,const uint8_t * const line_ptr,const uint32_t truncate)139 static void pvr_dump_hex_print_line(const struct pvr_dump_hex_ctx *ctx,
140                                     const uint8_t *const line_ptr,
141                                     const uint32_t truncate)
142 {
143    const uint32_t nr_bytes =
144       MIN2(HEX_LINE_SIZE - truncate, ctx->end_ptr - line_ptr);
145 
146    pvr_dump_hex_print_prefix(ctx, line_ptr - ctx->start_ptr);
147 
148    for (uint32_t i = 0; i < nr_bytes; i++) {
149       if (i == HEX_LINE_SIZE / 2)
150          pvr_dump_printf_cont(&ctx->base, " ");
151 
152       if (i % HEX_WORD_SIZE == 0)
153          pvr_dump_printf_cont(&ctx->base, " ");
154 
155       if (line_ptr[i])
156          pvr_dump_printf_cont(&ctx->base, HEX_BYTE_FMT, line_ptr[i]);
157       else
158          pvr_dump_printf_cont(&ctx->base, "..");
159    }
160 
161    pvr_dump_print_eol(&ctx->base);
162 }
163 
164 static void
pvr_dump_hex_print_zero_lines(const struct pvr_dump_hex_ctx * const ctx,const uint64_t zero_lines)165 pvr_dump_hex_print_zero_lines(const struct pvr_dump_hex_ctx *const ctx,
166                               const uint64_t zero_lines)
167 {
168    const uint64_t zero_bytes = zero_lines * HEX_LINE_SIZE;
169 
170    if (!zero_lines)
171       return;
172 
173    /* If we've only buffered a single zero line, print it normally. We don't
174     * save any space by folding it, and it's more readable this way.
175     */
176    if (zero_lines == 1) {
177       pvr_dump_hex_print_line(ctx, ctx->prev_non_zero_line + HEX_LINE_SIZE, 0);
178       return;
179    }
180 
181    pvr_dump_hex_println_no_prefix(ctx,
182                                   "  + %" PRIu64 " zero line%s (%" PRIu64
183                                   "/0x%" PRIx64 " bytes)",
184                                   zero_lines,
185                                   zero_lines == 1 ? "" : "s",
186                                   zero_bytes,
187                                   zero_bytes);
188 }
189 
190 static void
pvr_dump_hex_print_trailing_zeroes(const struct pvr_dump_hex_ctx * const ctx)191 pvr_dump_hex_print_trailing_zeroes(const struct pvr_dump_hex_ctx *const ctx)
192 {
193    const uint64_t zero_bytes =
194       ctx->zero_lines * HEX_LINE_SIZE + ctx->prev_non_zero_trailing_zero_bytes;
195 
196    if (!ctx->prev_non_zero_trailing_zero_bytes)
197       return pvr_dump_hex_print_zero_lines(ctx, ctx->zero_lines);
198 
199    if (!ctx->zero_lines)
200       return;
201 
202    pvr_dump_hex_println_no_prefix(ctx,
203                                   "  + %" PRIu64 "+%" PRIu32
204                                   " zero lines (%" PRIu64 "/0x%" PRIx64
205                                   " bytes)",
206                                   ctx->zero_lines,
207                                   ctx->prev_non_zero_trailing_zero_bytes,
208                                   zero_bytes,
209                                   zero_bytes);
210 }
211 
pvr_dump_hex_process_line(struct pvr_dump_hex_ctx * const ctx,uint32_t truncate)212 static void pvr_dump_hex_process_line(struct pvr_dump_hex_ctx *const ctx,
213                                       uint32_t truncate)
214 {
215    const uint32_t max_bytes = HEX_LINE_SIZE - truncate;
216 
217    uint32_t trailing_zero_bytes = max_bytes;
218 
219    for (uint32_t i = max_bytes; i > 0; i--) {
220       if (ctx->line_ptr[i - 1]) {
221          trailing_zero_bytes = HEX_LINE_SIZE - i;
222          break;
223       }
224    }
225 
226    if (trailing_zero_bytes == max_bytes) {
227       /* No non-zero words were found in this line; mark it and move on. */
228       ctx->zero_lines++;
229       return;
230    }
231 
232    /* We have at least one non-zero word in this line. If we have a previous
233     * non-zero line stored, collapse and print any leading zero-only lines
234     * before it then print the stored line.
235     */
236    if (ctx->prev_non_zero_line) {
237       pvr_dump_hex_print_zero_lines(ctx, ctx->prev_non_zero_leading_zero_lines);
238       pvr_dump_hex_print_line(ctx, ctx->prev_non_zero_line, truncate);
239    }
240 
241    /* Now we store the current non-zero line for printing later. This way we
242     * can treat the last non-zero line specially.
243     */
244    ctx->prev_non_zero_line = ctx->line_ptr;
245    ctx->prev_non_zero_leading_zero_lines = ctx->zero_lines;
246    ctx->prev_non_zero_trailing_zero_bytes = trailing_zero_bytes;
247    ctx->zero_lines = 0;
248 }
249 
pvr_dump_hex(struct pvr_dump_hex_ctx * const ctx)250 static void pvr_dump_hex(struct pvr_dump_hex_ctx *const ctx)
251 {
252    while (ctx->line_ptr < (ctx->end_ptr - HEX_LINE_SIZE)) {
253       pvr_dump_hex_process_line(ctx, 0);
254       ctx->line_ptr += HEX_LINE_SIZE;
255    }
256 
257    pvr_dump_hex_process_line(ctx,
258                              HEX_LINE_SIZE - (ctx->end_ptr - ctx->line_ptr));
259    ctx->line_ptr = ctx->end_ptr;
260 
261    if (ctx->prev_non_zero_line) {
262       /* If we don't have any zero lines to collapse, print the trailing zeroes
263        * on the last line.
264        */
265       if (!ctx->zero_lines) {
266          pvr_dump_hex_print_line(ctx, ctx->prev_non_zero_line, 0);
267       } else {
268          pvr_dump_hex_print_zero_lines(ctx,
269                                        ctx->prev_non_zero_leading_zero_lines);
270 
271          pvr_dump_hex_print_line(ctx,
272                                  ctx->prev_non_zero_line,
273                                  ctx->prev_non_zero_trailing_zero_bytes);
274 
275          /* Collapse and print any trailing zeroes. */
276          pvr_dump_hex_print_trailing_zeroes(ctx);
277       }
278    } else {
279       /* We made it to the end of the buffer without ever encountering a
280        * non-zero word. Make this known.
281        */
282       pvr_dump_hex_println(ctx, UINT64_C(0), " <empty buffer>");
283    }
284 
285    pvr_dump_hex_println(ctx, ctx->nr_bytes, " <end of buffer>");
286 }
287 
pvr_dump_buffer_hex(struct pvr_dump_buffer_ctx * const ctx,const uint64_t nr_bytes)288 bool pvr_dump_buffer_hex(struct pvr_dump_buffer_ctx *const ctx,
289                          const uint64_t nr_bytes)
290 {
291    struct pvr_dump_hex_ctx hex_ctx;
292 
293    if (!pvr_dump_hex_ctx_push(&hex_ctx, ctx, nr_bytes))
294       return false;
295 
296    pvr_dump_hex(&hex_ctx);
297 
298    return !!pvr_dump_hex_ctx_pop(&hex_ctx);
299 }
300