1 /*
2 * Copyright © 2016 Intel Corporation
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 (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
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
21 * IN THE SOFTWARE.
22 */
23
24 #include <stdio.h>
25 #include <stdlib.h>
26 #include <stdint.h>
27 #include <stdbool.h>
28 #include <getopt.h>
29
30 #include <unistd.h>
31 #include <fcntl.h>
32 #include <string.h>
33 #include <signal.h>
34 #include <errno.h>
35 #include <inttypes.h>
36 #include <sys/types.h>
37 #include <sys/stat.h>
38 #include <sys/wait.h>
39 #include <sys/mman.h>
40
41 #include "util/macros.h"
42
43 #include "aub_read.h"
44 #include "aub_mem.h"
45
46 #define CSI "\e["
47 #define GREEN_HEADER CSI "1;42m"
48 #define NORMAL CSI "0m"
49
50 /* options */
51
52 static int option_full_decode = true;
53 static int option_print_offsets = true;
54 static int max_vbo_lines = -1;
55 static enum { COLOR_AUTO, COLOR_ALWAYS, COLOR_NEVER } option_color;
56
57 /* state */
58
59 uint16_t pci_id = 0;
60 char *input_file = NULL, *xml_path = NULL;
61 struct intel_device_info devinfo;
62 struct intel_batch_decode_ctx batch_ctx;
63 struct aub_mem mem;
64
65 FILE *outfile;
66
67 struct brw_instruction;
68
69 static void
aubinator_error(void * user_data,const void * aub_data,const char * msg)70 aubinator_error(void *user_data, const void *aub_data, const char *msg)
71 {
72 fprintf(stderr, "%s", msg);
73 }
74
75 static void
aubinator_init(void * user_data,int aub_pci_id,const char * app_name)76 aubinator_init(void *user_data, int aub_pci_id, const char *app_name)
77 {
78 pci_id = aub_pci_id;
79
80 if (!intel_get_device_info_from_pci_id(pci_id, &devinfo)) {
81 fprintf(stderr, "can't find device information: pci_id=0x%x\n", pci_id);
82 exit(EXIT_FAILURE);
83 }
84
85 enum intel_batch_decode_flags batch_flags = 0;
86 if (option_color == COLOR_ALWAYS)
87 batch_flags |= INTEL_BATCH_DECODE_IN_COLOR;
88 if (option_full_decode)
89 batch_flags |= INTEL_BATCH_DECODE_FULL;
90 if (option_print_offsets)
91 batch_flags |= INTEL_BATCH_DECODE_OFFSETS;
92 batch_flags |= INTEL_BATCH_DECODE_FLOATS;
93
94 intel_batch_decode_ctx_init(&batch_ctx, &devinfo, outfile, batch_flags,
95 xml_path, NULL, NULL, NULL);
96
97 /* Check for valid spec instance, if wrong xml_path is passed then spec
98 * instance is not initialized properly
99 */
100 if (!batch_ctx.spec) {
101 fprintf(stderr, "Failed to initialize intel_batch_decode_ctx "
102 "spec instance\n");
103 free(xml_path);
104 intel_batch_decode_ctx_finish(&batch_ctx);
105 exit(EXIT_FAILURE);
106 }
107
108 batch_ctx.max_vbo_decoded_lines = max_vbo_lines;
109
110 char *color = GREEN_HEADER, *reset_color = NORMAL;
111 if (option_color == COLOR_NEVER)
112 color = reset_color = "";
113
114 fprintf(outfile, "%sAubinator: Intel AUB file decoder.%-80s%s\n",
115 color, "", reset_color);
116
117 if (input_file)
118 fprintf(outfile, "File name: %s\n", input_file);
119
120 if (aub_pci_id)
121 fprintf(outfile, "PCI ID: 0x%x\n", aub_pci_id);
122
123 fprintf(outfile, "Application name: %s\n", app_name);
124
125 fprintf(outfile, "Decoding as: %s\n", devinfo.name);
126
127 /* Throw in a new line before the first batch */
128 fprintf(outfile, "\n");
129 }
130
131 static struct intel_batch_decode_bo
get_bo(void * user_data,bool ppgtt,uint64_t addr)132 get_bo(void *user_data, bool ppgtt, uint64_t addr)
133 {
134 if (ppgtt)
135 return aub_mem_get_ppgtt_bo(user_data, addr);
136 else
137 return aub_mem_get_ggtt_bo(user_data, addr);
138 }
139
140 static void
handle_execlist_write(void * user_data,enum drm_i915_gem_engine_class engine,uint64_t context_descriptor)141 handle_execlist_write(void *user_data, enum drm_i915_gem_engine_class engine, uint64_t context_descriptor)
142 {
143 const uint32_t pphwsp_size = 4096;
144 uint32_t pphwsp_addr = context_descriptor & 0xfffff000;
145 struct intel_batch_decode_bo pphwsp_bo = aub_mem_get_ggtt_bo(&mem, pphwsp_addr);
146 uint32_t *context = (uint32_t *)((uint8_t *)pphwsp_bo.map +
147 (pphwsp_addr - pphwsp_bo.addr) +
148 pphwsp_size);
149
150 uint32_t ring_buffer_head = context[5];
151 uint32_t ring_buffer_tail = context[7];
152 uint32_t ring_buffer_start = context[9];
153 uint32_t ring_buffer_length = (context[11] & 0x1ff000) + 4096;
154
155 mem.pml4 = (uint64_t)context[49] << 32 | context[51];
156 batch_ctx.user_data = &mem;
157
158 struct intel_batch_decode_bo ring_bo = aub_mem_get_ggtt_bo(&mem,
159 ring_buffer_start);
160 assert(ring_bo.size > 0);
161 void *commands = (uint8_t *)ring_bo.map + (ring_buffer_start - ring_bo.addr) + ring_buffer_head;
162
163 batch_ctx.get_bo = get_bo;
164
165 batch_ctx.engine = engine;
166 intel_print_batch(&batch_ctx, commands,
167 MIN2(ring_buffer_tail - ring_buffer_head, ring_buffer_length),
168 ring_bo.addr + ring_buffer_head, true);
169 aub_mem_clear_bo_maps(&mem);
170 }
171
172 static struct intel_batch_decode_bo
get_legacy_bo(void * user_data,bool ppgtt,uint64_t addr)173 get_legacy_bo(void *user_data, bool ppgtt, uint64_t addr)
174 {
175 return aub_mem_get_ggtt_bo(user_data, addr);
176 }
177
178 static void
handle_ring_write(void * user_data,enum drm_i915_gem_engine_class engine,const void * data,uint32_t data_len)179 handle_ring_write(void *user_data, enum drm_i915_gem_engine_class engine,
180 const void *data, uint32_t data_len)
181 {
182 batch_ctx.user_data = &mem;
183 batch_ctx.get_bo = get_legacy_bo;
184
185 batch_ctx.engine = engine;
186 intel_print_batch(&batch_ctx, data, data_len, 0, false);
187
188 aub_mem_clear_bo_maps(&mem);
189 }
190
191 struct aub_file {
192 FILE *stream;
193
194 void *map, *end, *cursor;
195 };
196
197 static struct aub_file *
aub_file_open(const char * filename)198 aub_file_open(const char *filename)
199 {
200 struct aub_file *file;
201 struct stat sb;
202 int fd;
203
204 file = calloc(1, sizeof *file);
205 if (file == NULL)
206 return NULL;
207
208 fd = open(filename, O_RDONLY);
209 if (fd == -1) {
210 fprintf(stderr, "open %s failed: %s\n", filename, strerror(errno));
211 free(file);
212 exit(EXIT_FAILURE);
213 }
214
215 if (fstat(fd, &sb) == -1) {
216 fprintf(stderr, "stat failed: %s\n", strerror(errno));
217 free(file);
218 exit(EXIT_FAILURE);
219 }
220
221 file->map = mmap(NULL, sb.st_size,
222 PROT_READ, MAP_SHARED, fd, 0);
223 if (file->map == MAP_FAILED) {
224 fprintf(stderr, "mmap failed: %s\n", strerror(errno));
225 free(file);
226 exit(EXIT_FAILURE);
227 }
228
229 close(fd);
230
231 file->cursor = file->map;
232 file->end = file->map + sb.st_size;
233
234 return file;
235 }
236
237 static int
aub_file_more_stuff(struct aub_file * file)238 aub_file_more_stuff(struct aub_file *file)
239 {
240 return file->cursor < file->end || (file->stream && !feof(file->stream));
241 }
242
243 static void
setup_pager(void)244 setup_pager(void)
245 {
246 int fds[2];
247 pid_t pid;
248
249 if (!isatty(1))
250 return;
251
252 if (pipe(fds) == -1)
253 return;
254
255 pid = fork();
256 if (pid == -1)
257 return;
258
259 if (pid == 0) {
260 close(fds[1]);
261 dup2(fds[0], 0);
262 execlp("less", "less", "-FRSi", NULL);
263 }
264
265 close(fds[0]);
266 dup2(fds[1], 1);
267 close(fds[1]);
268 }
269
270 static void
print_help(const char * progname,FILE * file)271 print_help(const char *progname, FILE *file)
272 {
273 fprintf(file,
274 "Usage: %s [OPTION]... FILE\n"
275 "Decode aub file contents from FILE.\n\n"
276 " --help display this help and exit\n"
277 " --gen=platform decode for given platform (3 letter platform name)\n"
278 " --headers decode only command headers\n"
279 " --color[=WHEN] colorize the output; WHEN can be 'auto' (default\n"
280 " if omitted), 'always', or 'never'\n"
281 " --max-vbo-lines=N limit the number of decoded VBO lines\n"
282 " --no-pager don't launch pager\n"
283 " --no-offsets don't print instruction offsets\n"
284 " --xml=DIR load hardware xml description from directory DIR\n",
285 progname);
286 }
287
main(int argc,char * argv[])288 int main(int argc, char *argv[])
289 {
290 struct aub_file *file;
291 int c, i;
292 bool help = false, pager = true;
293 const struct option aubinator_opts[] = {
294 { "help", no_argument, (int *) &help, true },
295 { "no-pager", no_argument, (int *) &pager, false },
296 { "no-offsets", no_argument, (int *) &option_print_offsets, false },
297 { "gen", required_argument, NULL, 'g' },
298 { "headers", no_argument, (int *) &option_full_decode, false },
299 { "color", optional_argument, NULL, 'c' },
300 { "xml", required_argument, NULL, 'x' },
301 { "max-vbo-lines", required_argument, NULL, 'v' },
302 { NULL, 0, NULL, 0 }
303 };
304
305 outfile = stdout;
306
307 i = 0;
308 while ((c = getopt_long(argc, argv, "", aubinator_opts, &i)) != -1) {
309 switch (c) {
310 case 'g': {
311 const int id = intel_device_name_to_pci_device_id(optarg);
312 if (id < 0) {
313 fprintf(stderr, "can't parse gen: '%s', expected brw, g4x, ilk, "
314 "snb, ivb, hsw, byt, bdw, chv, skl, bxt, kbl, "
315 "aml, glk, cfl, whl, cnl, icl", optarg);
316 exit(EXIT_FAILURE);
317 } else {
318 pci_id = id;
319 }
320 break;
321 }
322 case 'c':
323 if (optarg == NULL || strcmp(optarg, "always") == 0)
324 option_color = COLOR_ALWAYS;
325 else if (strcmp(optarg, "never") == 0)
326 option_color = COLOR_NEVER;
327 else if (strcmp(optarg, "auto") == 0)
328 option_color = COLOR_AUTO;
329 else {
330 fprintf(stderr, "invalid value for --color: %s", optarg);
331 exit(EXIT_FAILURE);
332 }
333 break;
334 case 'x':
335 xml_path = strdup(optarg);
336 break;
337 case 'v':
338 max_vbo_lines = atoi(optarg);
339 break;
340 default:
341 break;
342 }
343 }
344
345 if (optind < argc)
346 input_file = argv[optind];
347
348 if (help || !input_file) {
349 print_help(argv[0], stderr);
350 exit(0);
351 }
352
353 /* Do this before we redirect stdout to pager. */
354 if (option_color == COLOR_AUTO)
355 option_color = isatty(1) ? COLOR_ALWAYS : COLOR_NEVER;
356
357 if (isatty(1) && pager)
358 setup_pager();
359
360 if (!aub_mem_init(&mem)) {
361 fprintf(stderr, "Unable to create GTT\n");
362 exit(EXIT_FAILURE);
363 }
364
365 file = aub_file_open(input_file);
366 if (!file) {
367 fprintf(stderr, "Unable to allocate buffer to open aub file\n");
368 free(xml_path);
369 exit(EXIT_FAILURE);
370 }
371
372 struct aub_read aub_read = {
373 .user_data = &mem,
374 .error = aubinator_error,
375 .info = aubinator_init,
376
377 .local_write = aub_mem_local_write,
378 .phys_write = aub_mem_phys_write,
379 .ggtt_write = aub_mem_ggtt_write,
380 .ggtt_entry_write = aub_mem_ggtt_entry_write,
381
382 .execlist_write = handle_execlist_write,
383 .ring_write = handle_ring_write,
384 };
385 int consumed;
386 while (aub_file_more_stuff(file) &&
387 (consumed = aub_read_command(&aub_read, file->cursor,
388 file->end - file->cursor)) > 0) {
389 file->cursor += consumed;
390 }
391
392 aub_mem_fini(&mem);
393
394 fflush(stdout);
395 /* close the stdout which is opened to write the output */
396 close(1);
397 free(file);
398 free(xml_path);
399
400 wait(NULL);
401 intel_batch_decode_ctx_finish(&batch_ctx);
402
403 return EXIT_SUCCESS;
404 }
405