diff options
author | Tom St Denis <tom.stdenis@amd.com> | 2019-02-25 10:49:43 -0500 |
---|---|---|
committer | Tom St Denis <tom.stdenis@amd.com> | 2019-02-25 14:05:40 -0500 |
commit | 1f7f7395cccabef9fd342de7600dc61296eec2b0 (patch) | |
tree | 2bc6a4e12475743555117acfe5872c4203e5b526 | |
parent | 9b6f9d1e56691a6c423ba5ca86eb74a6bc18f5f9 (diff) |
refactor shader disasm
Move llvm calls to lowlevel directory, followed by renaming
various functions and then adding a higher level "to_str" version
of the shader disasm call so that the formatted output could be displayed
in other UI configurations.
The functions were also modified to use a "char ***" pointers to be
consistent.
Signed-off-by: Tom St Denis <tom.stdenis@amd.com>
-rw-r--r-- | doc/sphinx/source/libshader_disasm.rst | 91 | ||||
-rw-r--r-- | doc/sphinx/source/libumr_api.rst | 1 | ||||
-rw-r--r-- | src/app/profile.c | 4 | ||||
-rw-r--r-- | src/lib/CMakeLists.txt | 2 | ||||
-rw-r--r-- | src/lib/lowlevel/linux/CMakeLists.txt | 1 | ||||
-rw-r--r-- | src/lib/lowlevel/linux/umr_shader_disasm.c | 106 | ||||
-rw-r--r-- | src/lib/shader_disasm.c (renamed from src/lib/umr_llvm_disasm.c) | 175 | ||||
-rw-r--r-- | src/umr.h | 5 |
8 files changed, 262 insertions, 123 deletions
diff --git a/doc/sphinx/source/libshader_disasm.rst b/doc/sphinx/source/libshader_disasm.rst new file mode 100644 index 0000000..52505b6 --- /dev/null +++ b/doc/sphinx/source/libshader_disasm.rst @@ -0,0 +1,91 @@ +Shader Disassembly +================== + +UMR has functions to disassemble and pretty print shader text. This requires +low level access to a shader disassembler such as provided by +llvm-dev. + +The lowest level function from the API is to decode a block of +shader program text (code) into printable strings: + +:: + + int umr_shader_disasm(struct umr_asic *asic, + uint8_t *inst, unsigned inst_bytes, + uint64_t PC, + char ***disasm_text); + +This will disassemble shader program text from 'inst' of 'inst_bytes' in +length. The program counter address (if any) can be specified as 'PC' to +hint to the decoder the address where the shader text comes from. + +The output (if any) is stored in an array of pointers which must be freed +by the caller for instance + +:: + + int x; + + for (x = 0; x < inst_bytes/4; x++) { + printf("%s\n", disasm_text[x]); + free(disasm_text[x]); + } + free(disasm_text); + +To create more useful formatted output the following function can be +used: + +:: + + int umr_vm_disasm_to_str(struct umr_asic *asic, + unsigned vmid, uint64_t addr, + uint64_t PC, uint32_t size, + uint32_t start_offset, char ***out); + +This function will fetch a shader from an address specified by the 'vmid' and +'addr' indicated. The program counter can be passed (if there is one) as 'PC'. +The size of the shader in bytes is passed by 'size'. The output is stored in +an array of char pointers that the function itself allocates and must be +freed by the caller as well. For a given shader the decoding can be offset +by a variable amount with 'start_offset' which can be useful for only +decoding a snapshot of a shader. + +The format of the output of this function includes information about the +opcodes being printed. For instance, + +:: + + pgm[2@0x800101289000 + 0x858 ] = 0xd1c10001 v_mad_f32 v1, s0, v6, v1 + pgm[2@0x800101289000 + 0x85c ] = 0x04060c00 ;; + pgm[2@0x800101289000 + 0x860 ] = 0xd1c10002 v_mad_f32 v2, s0, v7, v2 + pgm[2@0x800101289000 + 0x864 ] = 0x040a0e00 ;; + pgm[2@0x800101289000 + 0x868 ] = 0xd2960000 v_cvt_pkrtz_f16_f32 v0, v0, v1 + pgm[2@0x800101289000 + 0x86c ] = 0x00020300 ;; + pgm[2@0x800101289000 + 0x870 ] = 0xd2960001 v_cvt_pkrtz_f16_f32 v1, v2, v3 + pgm[2@0x800101289000 + 0x874 ] = 0x00020702 ;; + * pgm[2@0x800101289000 + 0x878 ] = 0xc4001c0f exp mrt0 v0, v0, v1, v1 done compr vm + pgm[2@0x800101289000 + 0x87c ] = 0x00000100 ;; + +The line with " * " indicates the address the 'PC' program counter points to. + +To compute the size of a shader this function can be used: + +:: + + uint32_t umr_compute_shader_size(struct umr_asic *asic, + struct umr_shaders_pgm *shader) + +The shader must be terminated at some point with an S_ENDPGM opcode or ideally +with an S_ENDINV opcode (as is the case with mesa based libraries). In the +case the shader does not use an S_ENDINV opcode the function will keep reading +shader text until a page fault occurs and then use the last S_ENDPGM +as the size. + +If the option 'disasm_early_term' is specified, e.g.,: + +:: + + asic->options.disasm_early_term = 1; + +then the function will stop on the first S_ENDPGM which is usually +correct for most shaders. diff --git a/doc/sphinx/source/libumr_api.rst b/doc/sphinx/source/libumr_api.rst index 891110e..c69f467 100644 --- a/doc/sphinx/source/libumr_api.rst +++ b/doc/sphinx/source/libumr_api.rst @@ -13,5 +13,6 @@ libumrcore.a: API Documentation libvm_access libhalt_waves libwave_status + libshader_disasm libpm4_stream libsdma_stream diff --git a/src/app/profile.c b/src/app/profile.c index 46dd430..1dce89e 100644 --- a/src/app/profile.c +++ b/src/app/profile.c @@ -309,9 +309,9 @@ throw_back: total_hits_by_type[texts->type] += shaders[x].total_cnt; // disasm shader - strs = calloc(texts->size/4, sizeof(strs[0])); + strs = NULL; data = texts->text; - umr_llvm_disasm(asic, (uint8_t *)data, texts->size, 0xFFFFFFFF, strs); + umr_shader_disasm(asic, (uint8_t *)data, texts->size, 0xFFFFFFFF, &strs); for (z = 0; z < shaders[x].hits[0].data.shader_size; z += 4) { unsigned cnt=0, pct; diff --git a/src/lib/CMakeLists.txt b/src/lib/CMakeLists.txt index 016997e..514145a 100644 --- a/src/lib/CMakeLists.txt +++ b/src/lib/CMakeLists.txt @@ -19,10 +19,10 @@ add_library(umrcore STATIC ring_decode.c scan_config.c scan_waves.c + shader_disasm.c sq_cmd_halt_waves.c transfer_soc15.c umr_apply_bank_address.c - umr_llvm_disasm.c umr_read_ring_data.c umr_read_pm4_stream.c umr_read_sdma_stream.c diff --git a/src/lib/lowlevel/linux/CMakeLists.txt b/src/lib/lowlevel/linux/CMakeLists.txt index d350699..daf97d9 100644 --- a/src/lib/lowlevel/linux/CMakeLists.txt +++ b/src/lib/lowlevel/linux/CMakeLists.txt @@ -7,6 +7,7 @@ add_library(umrlow STATIC mem.c wave_status.c umr_free_asic.c + umr_shader_disasm.c ) target_link_libraries(umrlow ${REQUIRED_EXTERNAL_LIBS}) diff --git a/src/lib/lowlevel/linux/umr_shader_disasm.c b/src/lib/lowlevel/linux/umr_shader_disasm.c new file mode 100644 index 0000000..82db478 --- /dev/null +++ b/src/lib/lowlevel/linux/umr_shader_disasm.c @@ -0,0 +1,106 @@ +/* + * Copyright 2019 Advanced Micro Devices, 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 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 COPYRIGHT HOLDER(S) OR AUTHOR(S) 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. + * + * Authors: Tom St Denis <tom.stdenis@amd.com> + * + */ +#include "umr.h" +#include <llvm-c/Disassembler.h> +#include <llvm-c/Target.h> + +/** + * umr_shader_disasm - Diassemble a shader + * + * @inst: Shader program + * @inst_bytes: number of bytes in shader + * @PC: Shader address in virtual memory + * @disasm_text: array of pointers that are assigned pointers + * to disassembled shader. + */ +int umr_shader_disasm(struct umr_asic *asic, + uint8_t *inst, unsigned inst_bytes, + uint64_t PC, + char ***disasm_text) +{ + LLVMDisasmContextRef disasm_ref; + unsigned x, z, i; + size_t n; + char tmp[256], *cpuname; + + *disasm_text = calloc(inst_bytes/4, sizeof(**disasm_text)); + + if (asic->options.no_disasm) { + for (x = 0; x < inst_bytes; x += 4) { + (*disasm_text)[x/4] = strdup("..."); + } + return 0; + } + + // initialize LLVM + LLVMInitializeAllTargetInfos(); + LLVMInitializeAllTargetMCs(); + LLVMInitializeAllDisassemblers(); + + // cpuname based on mesa usage + cpuname = asic->asicname; + if (asic->family == FAMILY_RV) + cpuname = "gfx902"; + else if (asic->family > FAMILY_VI) + cpuname = "gfx900"; + else if (!strcmp(cpuname, "polaris12") || !strcmp(cpuname, "vegam")) + cpuname = "polaris11"; + else if (!strcmp(cpuname, "vega12")) + cpuname = "gfx902"; + + disasm_ref = LLVMCreateDisasmCPU( + "amdgcn-mesa-mesa3d", cpuname, + NULL, 0, NULL, NULL); + + if (!disasm_ref) { + fprintf(stderr, "[ERROR]: Could not create disassembler context\n"); + free(*disasm_text); + return -1; + } + + for (i = x = 0; x < inst_bytes; x += n) { + n = LLVMDisasmInstruction( + disasm_ref, + inst + x, inst_bytes - x, + PC + x, + tmp, sizeof(tmp)); + if (!n) { + // invalid instruction, skip 4 bytes + n = 4; + (*disasm_text)[i++] = strdup("..."); + } else { + // valid instruction + + // if the instruction is longer than 4 bytes + // then add ';;' to all but the first line + (*disasm_text)[i++] = strdup(tmp); + for (z = 4; z < n; z += 4) + (*disasm_text)[i++] = strdup(";;"); + } + } + + LLVMDisasmDispose(disasm_ref); + return 0; +} diff --git a/src/lib/umr_llvm_disasm.c b/src/lib/shader_disasm.c index 0591003..d703e81 100644 --- a/src/lib/umr_llvm_disasm.c +++ b/src/lib/shader_disasm.c @@ -23,84 +23,6 @@ * */ #include "umr.h" -#include <llvm-c/Disassembler.h> -#include <llvm-c/Target.h> - -/** - * umr_llvm_disasm - Diassemble a shader - * - * @inst: Shader program - * @inst_bytes: number of bytes in shader - * @PC: Shader address in virtual memory - * @disasm_text: array of pointers that are assigned pointers - * to disassembled shader. - */ -int umr_llvm_disasm(struct umr_asic *asic, - uint8_t *inst, unsigned inst_bytes, - uint64_t PC, - char **disasm_text) -{ - LLVMDisasmContextRef disasm_ref; - unsigned x, z, i; - size_t n; - char tmp[256], *cpuname; - - if (asic->options.no_disasm) { - for (x = 0; x < inst_bytes; x += 4) { - disasm_text[x/4] = strdup("..."); - } - return 0; - } - - // initialize LLVM - LLVMInitializeAllTargetInfos(); - LLVMInitializeAllTargetMCs(); - LLVMInitializeAllDisassemblers(); - - // cpuname based on mesa usage - cpuname = asic->asicname; - if (asic->family == FAMILY_RV) - cpuname = "gfx902"; - else if (asic->family > FAMILY_VI) - cpuname = "gfx900"; - else if (!strcmp(cpuname, "polaris12") || !strcmp(cpuname, "vegam")) - cpuname = "polaris11"; - else if (!strcmp(cpuname, "vega12")) - cpuname = "gfx902"; - - disasm_ref = LLVMCreateDisasmCPU( - "amdgcn-mesa-mesa3d", cpuname, - NULL, 0, NULL, NULL); - - if (!disasm_ref) { - fprintf(stderr, "[ERROR]: Could not create disassembler context\n"); - return -1; - } - - for (i = x = 0; x < inst_bytes; x += n) { - n = LLVMDisasmInstruction( - disasm_ref, - inst + x, inst_bytes - x, - PC + x, - tmp, sizeof(tmp)); - if (!n) { - // invalid instruction, skip 4 bytes - n = 4; - disasm_text[i++] = strdup("..."); - } else { - // valid instruction - - // if the instruction is longer than 4 bytes - // then add ';;' to all but the first line - disasm_text[i++] = strdup(tmp); - for (z = 4; z < n; z += 4) - disasm_text[i++] = strdup(";;"); - } - } - - LLVMDisasmDispose(disasm_ref); - return 0; -} /** * find_wave - Find a wave by VMID@PC inside an array of wave data @@ -123,7 +45,7 @@ static struct umr_wave_data *find_wave(struct umr_wave_data *wd, unsigned vmid, } /** - * umr_vm_disasm - Disassemble shader programs in GPU mapped memory + * umr_vm_disasm_to_str - Disassemble shader programs in GPU mapped memory to an array of strings * * @vmid: VMID of shader * @addr: Address of shader @@ -131,32 +53,19 @@ static struct umr_wave_data *find_wave(struct umr_wave_data *wd, unsigned vmid, * @size: Shader size in bytes * @start_offset: Offset of disassembly starting address from @addr * @wd: Wave scan data (or NULL) used to track activity in this shader + * @out: array of strings containing formatted output */ -int umr_vm_disasm(struct umr_asic *asic, unsigned vmid, uint64_t addr, uint64_t PC, uint32_t size, uint32_t start_offset, struct umr_wave_data *wd) +int umr_vm_disasm_to_str(struct umr_asic *asic, unsigned vmid, uint64_t addr, uint64_t PC, uint32_t size, uint32_t start_offset, char ***out) { - uint32_t *opcodes = NULL, x, y, nwave, wavehits; - char **opcode_strs = NULL; - struct umr_wave_data *pwd; + uint32_t *opcodes = NULL, x, y; + char **opcode_strs; int r = 0; - - // count captured halted and valid waves so we can display - // relative counts - wavehits = nwave = 0; - pwd = wd; - while (pwd) { - ++nwave; - pwd = pwd->next; - } + char linebuf[512]; opcodes = calloc(size/4, sizeof(*opcodes)); - if (!opcodes) { - fprintf(stderr, "[ERROR]: Out of memory\n"); - r = -1; - goto error; - } + *out = calloc(size/4 + 1, sizeof(*out)); - opcode_strs = calloc(size/4, sizeof(opcode_strs[0])); - if (!opcode_strs) { + if (!*out || !opcode_strs || !opcodes) { fprintf(stderr, "[ERROR]: Out of memory\n"); r = -1; goto error; @@ -167,29 +76,61 @@ int umr_vm_disasm(struct umr_asic *asic, unsigned vmid, uint64_t addr, uint64_t if (umr_read_vram(asic, vmid, addr + start_offset, size, (void*)opcodes)) goto error; - if (asic->options.verbose) { - fflush(stdout); - fprintf(stderr, "pgm[] = { "); - for (x = 0; x < size/4; x++) - fprintf(stderr, "0x%08" PRIx32 ", ", opcodes[x]); - fprintf(stderr, "}\n"); - fflush(stderr); - } - - umr_llvm_disasm(asic, (uint8_t *)opcodes, size, addr + start_offset, &opcode_strs[0]); + umr_shader_disasm(asic, (uint8_t *)opcodes, size, addr + start_offset, &opcode_strs); for (y = 0, x = start_offset / 4; x < (start_offset + size)/4; x++, y++) { - if (addr + 4 * x == PC) - printf(" * "); - else - printf(" "); - printf("pgm[%s%u%s@%s0x%" PRIx64 "%s + %s0x%-4x%s] = %s0x%08" PRIx32 "%s\t%s%-60s%s\t", + snprintf(linebuf, sizeof(linebuf) - 1, "%s pgm[%s%u%s@%s0x%" PRIx64 "%s + %s0x%-4x%s] = %s0x%08" PRIx32 "%s\t%s%-60s%s\t", + (addr + 4 * x == PC) ? " * " : " ", BLUE, (unsigned)vmid, RST, YELLOW, addr, RST, YELLOW, (unsigned)x * 4, RST, BLUE, opcodes[y], RST, GREEN, opcode_strs[y], RST); free(opcode_strs[y]); + (*out)[y] = strdup(linebuf); + } + free(opcode_strs); + free(opcodes); + return 0; +error: + free(opcode_strs); + free(opcodes); + free(*out); + return r; +} + +/** + * umr_vm_disasm - Disassemble shader programs in GPU mapped memory + * + * @vmid: VMID of shader + * @addr: Address of shader + * @PC: Known wave PC address if any + * @size: Shader size in bytes + * @start_offset: Offset of disassembly starting address from @addr + * @wd: Wave scan data (or NULL) used to track activity in this shader + */ +int umr_vm_disasm(struct umr_asic *asic, unsigned vmid, uint64_t addr, uint64_t PC, uint32_t size, uint32_t start_offset, struct umr_wave_data *wd) +{ + uint32_t *opcodes = NULL, x, y, nwave, wavehits; + struct umr_wave_data *pwd; + int r = 0; + char **outstrs; + + // count captured halted and valid waves so we can display + // relative counts + wavehits = nwave = 0; + pwd = wd; + while (pwd) { + ++nwave; + pwd = pwd->next; + } + + r = umr_vm_disasm_to_str(asic, vmid, addr, PC, size, start_offset, &outstrs); + if (r) + return r; + for (y = 0, x = start_offset / 4; x < (start_offset + size)/4; x++, y++) { + printf("%s", outstrs[y]); + free(outstrs[y]); // if we have wave data see if we can find a wave at this // PC and then print out the stats for it @@ -218,9 +159,7 @@ int umr_vm_disasm(struct umr_asic *asic, unsigned vmid, uint64_t addr, uint64_t if (wd && wavehits) printf("\t%u waves in this shader (out of %u active waves)\n", wavehits, nwave); -error: - free(opcode_strs); - free(opcodes); + free(outstrs); return r; } @@ -237,7 +176,7 @@ error: * run out. */ uint32_t umr_compute_shader_size(struct umr_asic *asic, - struct umr_shaders_pgm *shader) + struct umr_shaders_pgm *shader) { uint64_t addr; uint32_t buf[256/4]; // read 256 byte pages at a time @@ -874,10 +874,11 @@ void umr_print_decode(struct umr_asic *asic, struct umr_ring_decoder *decoder, u void umr_dump_ib(struct umr_asic *asic, struct umr_ring_decoder *decoder); void umr_dump_shaders(struct umr_asic *asic, struct umr_ring_decoder *decoder, struct umr_wave_data *wd); -int umr_llvm_disasm(struct umr_asic *asic, +int umr_shader_disasm(struct umr_asic *asic, uint8_t *inst, unsigned inst_bytes, uint64_t PC, - char **disasm_text); + char ***disasm_text); +int umr_vm_disasm_to_str(struct umr_asic *asic, unsigned vmid, uint64_t addr, uint64_t PC, uint32_t size, uint32_t start_offset, char ***out); int umr_vm_disasm(struct umr_asic *asic, unsigned vmid, uint64_t addr, uint64_t PC, uint32_t size, uint32_t start_offset, struct umr_wave_data *wd); uint32_t umr_compute_shader_size(struct umr_asic *asic, struct umr_shaders_pgm *shader); |