| /* |
| * Copyright © 2020 Google, Inc. |
| * |
| * Permission is hereby granted, free of charge, to any person obtaining a |
| * copy of this software and associated documentation files (the "Software"), |
| * to deal in the Software without restriction, including without limitation |
| * the rights to use, copy, modify, merge, publish, distribute, sublicense, |
| * and/or sell copies of the Software, and to permit persons to whom the |
| * Software is furnished to do so, subject to the following conditions: |
| * |
| * The above copyright notice and this permission notice (including the next |
| * paragraph) shall be included in all copies or substantial portions of the |
| * Software. |
| * |
| * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
| * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
| * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL |
| * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
| * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, |
| * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE |
| * SOFTWARE. |
| */ |
| |
| #include <getopt.h> |
| #include <inttypes.h> |
| #include <locale.h> |
| #include <stdlib.h> |
| #include <xf86drm.h> |
| |
| #include "util/u_math.h" |
| |
| #include "perfcntrs/freedreno_perfcntr.h" |
| |
| #include "main.h" |
| |
| static void |
| dump_float(void *buf, int sz) |
| { |
| uint8_t *ptr = (uint8_t *)buf; |
| uint8_t *end = ptr + sz - 3; |
| int i = 0; |
| |
| while (ptr < end) { |
| uint32_t d = 0; |
| |
| printf((i % 8) ? " " : "\t"); |
| |
| d |= *(ptr++) << 0; |
| d |= *(ptr++) << 8; |
| d |= *(ptr++) << 16; |
| d |= *(ptr++) << 24; |
| |
| printf("%8f", uif(d)); |
| |
| if ((i % 8) == 7) { |
| printf("\n"); |
| } |
| |
| i++; |
| } |
| |
| if (i % 8) { |
| printf("\n"); |
| } |
| } |
| |
| static void |
| dump_hex(void *buf, int sz) |
| { |
| uint8_t *ptr = (uint8_t *)buf; |
| uint8_t *end = ptr + sz; |
| int i = 0; |
| |
| while (ptr < end) { |
| uint32_t d = 0; |
| |
| printf((i % 8) ? " " : "\t"); |
| |
| d |= *(ptr++) << 0; |
| d |= *(ptr++) << 8; |
| d |= *(ptr++) << 16; |
| d |= *(ptr++) << 24; |
| |
| printf("%08x", d); |
| |
| if ((i % 8) == 7) { |
| printf("\n"); |
| } |
| |
| i++; |
| } |
| |
| if (i % 8) { |
| printf("\n"); |
| } |
| } |
| |
| static const char *shortopts = "df:g:hp:"; |
| |
| static const struct option longopts[] = { |
| {"disasm", no_argument, 0, 'd'}, {"file", required_argument, 0, 'f'}, |
| {"groups", required_argument, 0, 'g'}, {"help", no_argument, 0, 'h'}, |
| {"perfcntr", required_argument, 0, 'p'}, {0, 0, 0, 0}}; |
| |
| static void |
| usage(const char *name) |
| { |
| printf( |
| "Usage: %s [-dfgh]\n" |
| "\n" |
| "options:\n" |
| " -d, --disasm print disassembled shader\n" |
| " -f, --file=FILE read shader from file (instead of stdin)\n" |
| " -g, --groups=X,Y,Z use specified group size\n" |
| " -h, --help show this message\n" |
| " -p, --perfcntr=LIST sample specified performance counters " |
| "(comma\n" |
| " separated list)\n", |
| name); |
| } |
| |
| /* performance counter description: */ |
| static unsigned num_groups; |
| static const struct fd_perfcntr_group *groups; |
| |
| /* Track enabled counters per group: */ |
| static unsigned *enabled_counters; |
| |
| static void |
| setup_counter(const char *name, struct perfcntr *c) |
| { |
| for (int i = 0; i < num_groups; i++) { |
| const struct fd_perfcntr_group *group = &groups[i]; |
| |
| for (int j = 0; j < group->num_countables; j++) { |
| const struct fd_perfcntr_countable *countable = &group->countables[j]; |
| |
| if (strcmp(name, countable->name) != 0) |
| continue; |
| |
| /* |
| * Allocate a counter to use to monitor the requested countable: |
| */ |
| if (enabled_counters[i] >= group->num_counters) { |
| errx(-1, "Too many counters selected in group: %s", group->name); |
| } |
| |
| unsigned idx = enabled_counters[i]++; |
| const struct fd_perfcntr_counter *counter = &group->counters[idx]; |
| |
| /* |
| * And initialize the perfcntr struct, pulling together the info |
| * about selected counter and countable, to simplify life for the |
| * backend: |
| */ |
| c->name = name; |
| c->select_reg = counter->select_reg; |
| c->counter_reg_lo = counter->counter_reg_lo; |
| c->counter_reg_hi = counter->counter_reg_hi; |
| c->selector = countable->selector; |
| |
| return; |
| } |
| } |
| |
| errx(-1, "could not find countable: %s", name); |
| } |
| |
| static struct perfcntr * |
| parse_perfcntrs(const struct fd_dev_id *dev_id, const char *perfcntrstr, |
| unsigned *num_perfcntrs) |
| { |
| struct perfcntr *counters = NULL; |
| char *cnames, *s; |
| unsigned cnt = 0; |
| |
| groups = fd_perfcntrs(dev_id, &num_groups); |
| enabled_counters = (uint32_t *) calloc(num_groups, sizeof(enabled_counters[0])); |
| |
| cnames = strdup(perfcntrstr); |
| while ((s = strstr(cnames, ","))) { |
| char *name = cnames; |
| s[0] = '\0'; |
| cnames = &s[1]; |
| |
| counters = |
| (struct perfcntr *)realloc(counters, ++cnt * sizeof(counters[0])); |
| setup_counter(name, &counters[cnt - 1]); |
| } |
| |
| char *name = cnames; |
| counters = (struct perfcntr *)realloc(counters, ++cnt * sizeof(counters[0])); |
| setup_counter(name, &counters[cnt - 1]); |
| |
| *num_perfcntrs = cnt; |
| |
| return counters; |
| } |
| |
| int |
| main(int argc, char **argv) |
| { |
| FILE *in = stdin; |
| const char *perfcntrstr = NULL; |
| struct perfcntr *perfcntrs = NULL; |
| unsigned num_perfcntrs = 0; |
| bool disasm = false; |
| uint32_t grid[3] = {0}; |
| int opt, ret; |
| |
| setlocale(LC_NUMERIC, "en_US.UTF-8"); |
| |
| while ((opt = getopt_long_only(argc, argv, shortopts, longopts, NULL)) != |
| -1) { |
| switch (opt) { |
| case 'd': |
| disasm = true; |
| break; |
| case 'f': |
| in = fopen(optarg, "r"); |
| if (!in) |
| err(1, "could not open '%s'", optarg); |
| break; |
| case 'g': |
| ret = sscanf(optarg, "%u,%u,%u", &grid[0], &grid[1], &grid[2]); |
| if (ret != 3) { |
| usage(argv[0]); |
| return -1; |
| } |
| break; |
| case 'h': |
| usage(argv[0]); |
| return -1; |
| case 'p': |
| perfcntrstr = optarg; |
| break; |
| default: |
| printf("unrecognized arg: %c\n", opt); |
| usage(argv[0]); |
| return -1; |
| } |
| } |
| |
| struct fd_device *dev = fd_device_open(); |
| struct fd_pipe *pipe = fd_pipe_new(dev, FD_PIPE_3D); |
| |
| const struct fd_dev_id *dev_id = fd_pipe_dev_id(pipe); |
| |
| printf("got gpu: %s\n", fd_dev_name(dev_id)); |
| |
| struct backend *backend; |
| switch (fd_dev_gen(dev_id)) { |
| case 4: |
| backend = a4xx_init(dev, dev_id); |
| break; |
| case 6: |
| backend = a6xx_init<A6XX>(dev, dev_id); |
| break; |
| case 7: |
| backend = a6xx_init<A7XX>(dev, dev_id); |
| break; |
| default: |
| err(1, "unsupported gpu generation: a%uxx", fd_dev_gen(dev_id)); |
| } |
| |
| struct kernel *kernel = backend->assemble(backend, in); |
| printf("localsize: %dx%dx%d\n", kernel->local_size[0], kernel->local_size[1], |
| kernel->local_size[2]); |
| for (int i = 0; i < kernel->num_bufs; i++) { |
| printf("buf[%d]: size=%u\n", i, kernel->buf_sizes[i]); |
| kernel->bufs[i] = fd_bo_new(dev, kernel->buf_sizes[i] * 4, 0, "buf[%d]", i); |
| |
| if (kernel->buf_init_data[i]) { |
| fd_bo_cpu_prep(kernel->bufs[i], pipe, FD_BO_PREP_WRITE); |
| void *map = fd_bo_map(kernel->bufs[i]); |
| memcpy(map, kernel->buf_init_data[i], kernel->buf_sizes[i] * 4); |
| free(kernel->buf_init_data[i]); |
| } |
| } |
| |
| if (disasm) |
| backend->disassemble(kernel, stdout); |
| |
| if (grid[0] == 0) |
| return 0; |
| |
| struct fd_submit *submit = fd_submit_new(pipe); |
| |
| if (perfcntrstr) { |
| if (!backend->set_perfcntrs) { |
| err(1, "performance counters not supported"); |
| } |
| perfcntrs = parse_perfcntrs(dev_id, perfcntrstr, &num_perfcntrs); |
| backend->set_perfcntrs(backend, perfcntrs, num_perfcntrs); |
| } |
| |
| backend->emit_grid(kernel, grid, submit); |
| |
| struct fd_fence *fence = fd_submit_flush(submit, -1, false); |
| |
| fd_fence_flush(fence); |
| fd_fence_del(fence); |
| |
| for (int i = 0; i < kernel->num_bufs; i++) { |
| fd_bo_cpu_prep(kernel->bufs[i], pipe, FD_BO_PREP_READ); |
| void *map = fd_bo_map(kernel->bufs[i]); |
| |
| printf("buf[%d]:\n", i); |
| dump_hex(map, kernel->buf_sizes[i] * 4); |
| dump_float(map, kernel->buf_sizes[i] * 4); |
| } |
| |
| if (perfcntrstr) { |
| uint64_t results[num_perfcntrs]; |
| backend->read_perfcntrs(backend, results); |
| |
| for (unsigned i = 0; i < num_perfcntrs; i++) { |
| printf("%s:\t%'" PRIu64 "\n", perfcntrs[i].name, results[i]); |
| } |
| } |
| |
| return 0; |
| } |