summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTom St Denis <tom.stdenis@amd.com>2019-02-25 10:49:43 -0500
committerTom St Denis <tom.stdenis@amd.com>2019-02-25 14:05:40 -0500
commit1f7f7395cccabef9fd342de7600dc61296eec2b0 (patch)
tree2bc6a4e12475743555117acfe5872c4203e5b526
parent9b6f9d1e56691a6c423ba5ca86eb74a6bc18f5f9 (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.rst91
-rw-r--r--doc/sphinx/source/libumr_api.rst1
-rw-r--r--src/app/profile.c4
-rw-r--r--src/lib/CMakeLists.txt2
-rw-r--r--src/lib/lowlevel/linux/CMakeLists.txt1
-rw-r--r--src/lib/lowlevel/linux/umr_shader_disasm.c106
-rw-r--r--src/lib/shader_disasm.c (renamed from src/lib/umr_llvm_disasm.c)175
-rw-r--r--src/umr.h5
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
diff --git a/src/umr.h b/src/umr.h
index d9620e8..d00e083 100644
--- a/src/umr.h
+++ b/src/umr.h
@@ -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);