2 * Copyright © 2018 Intel Corporation
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:
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
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
18 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
20 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
35 #include "util/list.h"
37 #include "aub_write.h"
38 #include "drm-uapi/i915_drm.h"
39 #include "intel_aub.h"
41 static void __attribute__ ((format(__printf__
, 2, 3)))
42 fail_if(int cond
, const char *format
, ...)
49 va_start(args
, format
);
50 vfprintf(stderr
, format
, args
);
56 #define fail(...) fail_if(true, __VA_ARGS__)
58 static int zlib_inflate(uint32_t **ptr
, int len
)
60 struct z_stream_s zstream
;
62 const uint32_t out_size
= 128*4096; /* approximate obj size */
64 memset(&zstream
, 0, sizeof(zstream
));
66 zstream
.next_in
= (unsigned char *)*ptr
;
67 zstream
.avail_in
= 4*len
;
69 if (inflateInit(&zstream
) != Z_OK
)
72 out
= malloc(out_size
);
73 zstream
.next_out
= out
;
74 zstream
.avail_out
= out_size
;
77 switch (inflate(&zstream
, Z_SYNC_FLUSH
)) {
87 if (zstream
.avail_out
)
90 out
= realloc(out
, 2*zstream
.total_out
);
96 zstream
.next_out
= (unsigned char *)out
+ zstream
.total_out
;
97 zstream
.avail_out
= zstream
.total_out
;
100 inflateEnd(&zstream
);
103 return zstream
.total_out
/ 4;
106 static int ascii85_decode(const char *in
, uint32_t **out
, bool inflate
)
108 int len
= 0, size
= 1024;
110 *out
= realloc(*out
, sizeof(uint32_t)*size
);
114 while (*in
>= '!' && *in
<= 'z') {
119 *out
= realloc(*out
, sizeof(uint32_t)*size
);
127 v
+= in
[0] - 33; v
*= 85;
128 v
+= in
[1] - 33; v
*= 85;
129 v
+= in
[2] - 33; v
*= 85;
130 v
+= in
[3] - 33; v
*= 85;
140 return zlib_inflate(out
, len
);
144 print_help(const char *progname
, FILE *file
)
147 "Usage: %s [OPTION]... [FILE]\n"
148 "Convert an Intel GPU i915 error state to an aub file.\n"
149 " -h, --help display this help and exit\n"
150 " -o, --output=FILE the output aub file (default FILE.aub)\n",
169 enum drm_i915_gem_engine_class engine_class
;
172 struct list_head link
;
176 find_or_create(struct list_head
*bo_list
, uint64_t addr
,
177 enum drm_i915_gem_engine_class engine_class
,
180 list_for_each_entry(struct bo
, bo_entry
, bo_list
, link
) {
181 if (bo_entry
->addr
== addr
&&
182 bo_entry
->engine_class
== engine_class
&&
183 bo_entry
->engine_instance
== engine_instance
)
187 struct bo
*new_bo
= calloc(1, sizeof(*new_bo
));
189 new_bo
->engine_class
= engine_class
;
190 new_bo
->engine_instance
= engine_instance
;
191 list_addtail(&new_bo
->link
, bo_list
);
197 engine_from_name(const char *engine_name
,
198 enum drm_i915_gem_engine_class
*engine_class
,
199 int *engine_instance
)
203 enum drm_i915_gem_engine_class engine_class
;
205 { "rcs", I915_ENGINE_CLASS_RENDER
},
206 { "vcs", I915_ENGINE_CLASS_VIDEO
},
207 { "vecs", I915_ENGINE_CLASS_VIDEO_ENHANCE
},
208 { "bcs", I915_ENGINE_CLASS_COPY
},
209 { NULL
, I915_ENGINE_CLASS_INVALID
},
212 for (r
= rings
; r
->match
; r
++) {
213 if (strncasecmp(engine_name
, r
->match
, strlen(r
->match
)) == 0) {
214 *engine_class
= r
->engine_class
;
215 *engine_instance
= strtol(engine_name
+ strlen(r
->match
), NULL
, 10);
220 fail("Unknown engine %s\n", engine_name
);
224 main(int argc
, char *argv
[])
228 char *out_filename
= NULL
, *in_filename
= NULL
;
229 const struct option aubinator_opts
[] = {
230 { "help", no_argument
, NULL
, 'h' },
231 { "output", required_argument
, NULL
, 'o' },
236 while ((c
= getopt_long(argc
, argv
, "ho:", aubinator_opts
, &i
)) != -1) {
242 out_filename
= strdup(optarg
);
250 in_filename
= argv
[optind
++];
252 if (help
|| argc
== 1 || !in_filename
) {
253 print_help(argv
[0], stderr
);
254 return in_filename
? EXIT_SUCCESS
: EXIT_FAILURE
;
257 if (out_filename
== NULL
) {
258 int out_filename_size
= strlen(in_filename
) + 5;
259 out_filename
= malloc(out_filename_size
);
260 snprintf(out_filename
, out_filename_size
, "%s.aub", in_filename
);
263 FILE *err_file
= fopen(in_filename
, "r");
264 fail_if(!err_file
, "Failed to open error file \"%s\": %m\n", in_filename
);
266 FILE *aub_file
= fopen(out_filename
, "w");
267 fail_if(!aub_file
, "Failed to open aub file \"%s\": %m\n", in_filename
);
269 struct aub_file aub
= {};
271 enum drm_i915_gem_engine_class active_engine_class
= I915_ENGINE_CLASS_INVALID
;
272 int active_engine_instance
= -1;
274 int num_ring_bos
= 0;
276 struct list_head bo_list
;
277 list_inithead(&bo_list
);
279 struct bo
*last_bo
= NULL
;
283 while (getline(&line
, &line_size
, err_file
) > 0) {
284 const char *pci_id_start
= strstr(line
, "PCI ID");
287 int matched
= sscanf(line
, "PCI ID: 0x%04x\n", &pci_id
);
288 fail_if(!matched
, "Invalid error state file!\n");
290 aub_file_init(&aub
, aub_file
,
291 NULL
, pci_id
, "error_state");
292 fail_if(!aub_use_execlists(&aub
),
293 "%s currently only works on gen8+\n", argv
[0]);
295 aub_write_default_setup(&aub
);
299 if (strstr(line
, " command stream:")) {
300 engine_from_name(line
, &active_engine_class
, &active_engine_instance
);
304 const char *active_start
= "Active (";
305 if (strncmp(line
, active_start
, strlen(active_start
)) == 0) {
306 char *ring
= line
+ strlen(active_start
);
308 engine_from_name(ring
, &active_engine_class
, &active_engine_instance
);
310 char *count
= strchr(ring
, '[');
311 fail_if(!count
|| sscanf(count
, "[%d]:", &num_ring_bos
) < 1,
312 "Failed to parse BO table header\n");
316 const char *global_start
= "Pinned (global) [";
317 if (strncmp(line
, global_start
, strlen(global_start
)) == 0) {
318 active_engine_class
= I915_ENGINE_CLASS_INVALID
;
319 active_engine_instance
= -1;
323 if (num_ring_bos
> 0) {
324 unsigned hi
, lo
, size
;
325 if (sscanf(line
, " %x_%x %d", &hi
, &lo
, &size
) == 3) {
326 assert(aub_use_execlists(&aub
));
327 struct bo
*bo_entry
= find_or_create(&bo_list
, ((uint64_t)hi
) << 32 | lo
,
328 active_engine_class
, active_engine_instance
);
329 bo_entry
->size
= size
;
332 fail("Not enough BO entries in the active table\n");
337 if (line
[0] == ':' || line
[0] == '~') {
338 if (!last_bo
|| last_bo
->type
== BO_TYPE_UNKNOWN
)
341 int count
= ascii85_decode(line
+1, (uint32_t **) &last_bo
->data
, line
[0] == ':');
342 fail_if(count
== 0, "ASCII85 decode failed.\n");
343 last_bo
->size
= count
* 4;
347 char *dashes
= strstr(line
, " --- ");
351 engine_from_name(line
, &active_engine_class
, &active_engine_instance
);
354 char *bo_address_str
= strchr(dashes
, '=');
355 if (!bo_address_str
|| sscanf(bo_address_str
, "= 0x%08x %08x\n", &hi
, &lo
) != 2)
358 last_bo
= find_or_create(&bo_list
, ((uint64_t) hi
) << 32 | lo
,
359 active_engine_class
, active_engine_instance
);
365 { "gtt_offset", BO_TYPE_BATCH
},
366 { "user", BO_TYPE_USER
},
367 { "HW context", BO_TYPE_CONTEXT
},
368 { "ringbuffer", BO_TYPE_RINGBUFFER
},
369 { "HW Status", BO_TYPE_STATUS
},
370 { "WA context", BO_TYPE_CONTEXT_WA
},
371 { "unknown", BO_TYPE_UNKNOWN
},
374 for (b
= bo_types
; b
->type
!= BO_TYPE_UNKNOWN
; b
++) {
375 if (strncasecmp(dashes
, b
->match
, strlen(b
->match
)) == 0)
379 /* The batch buffer will appear twice as gtt_offset and user. Only
380 * keep the batch type.
382 if (last_bo
->type
== BO_TYPE_UNKNOWN
) {
383 last_bo
->type
= b
->type
;
384 last_bo
->name
= b
->match
;
391 /* Add all the BOs to the aub file */
392 list_for_each_entry(struct bo
, bo_entry
, &bo_list
, link
) {
393 switch (bo_entry
->type
) {
395 aub_map_ppgtt(&aub
, bo_entry
->addr
, bo_entry
->size
);
396 aub_write_trace_block(&aub
, AUB_TRACE_TYPE_BATCH
,
397 bo_entry
->data
, bo_entry
->size
, bo_entry
->addr
);
400 aub_map_ppgtt(&aub
, bo_entry
->addr
, bo_entry
->size
);
401 aub_write_trace_block(&aub
, AUB_TRACE_TYPE_NOTYPE
,
402 bo_entry
->data
, bo_entry
->size
, bo_entry
->addr
);
408 /* Finally exec the batch BO */
409 bool batch_found
= false;
410 list_for_each_entry(struct bo
, bo_entry
, &bo_list
, link
) {
411 if (bo_entry
->type
== BO_TYPE_BATCH
) {
412 aub_write_exec(&aub
, bo_entry
->addr
, aub_gtt_size(&aub
), bo_entry
->engine_class
);
417 fail_if(!batch_found
, "Failed to find batch buffer.\n");
420 list_for_each_entry_safe(struct bo
, bo_entry
, &bo_list
, link
) {
421 list_del(&bo_entry
->link
);
422 free(bo_entry
->data
);
432 aub_file_finish(&aub
);
433 } else if(aub_file
) {
439 /* vim: set ts=8 sw=8 tw=0 cino=:0,(0 noet :*/