| /* |
| * Copyright 2020 Advanced Micro Devices, Inc. |
| * Copyright © 2020 Valve Corporation |
| * |
| * 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 "ac_rgp.h" |
| |
| #include "util/macros.h" |
| #include "util/os_misc.h" |
| #include "util/os_time.h" |
| #include "util/u_process.h" |
| #include "util/u_math.h" |
| |
| #include "ac_spm.h" |
| #include "ac_sqtt.h" |
| #include "ac_gpu_info.h" |
| #include "amd_family.h" |
| |
| #include <stdbool.h> |
| #include <string.h> |
| |
| #define SQTT_FILE_MAGIC_NUMBER 0x50303042 |
| #define SQTT_FILE_VERSION_MAJOR 1 |
| #define SQTT_FILE_VERSION_MINOR 5 |
| |
| #define SQTT_GPU_NAME_MAX_SIZE 256 |
| #define SQTT_MAX_NUM_SE 32 |
| #define SQTT_SA_PER_SE 2 |
| |
| enum sqtt_version |
| { |
| SQTT_VERSION_NONE = 0x0, |
| SQTT_VERSION_2_2 = 0x5, /* GFX8 */ |
| SQTT_VERSION_2_3 = 0x6, /* GFX9 */ |
| SQTT_VERSION_2_4 = 0x7 /* GFX10+ */ |
| }; |
| |
| /** |
| * SQTT chunks. |
| */ |
| enum sqtt_file_chunk_type |
| { |
| SQTT_FILE_CHUNK_TYPE_ASIC_INFO, |
| SQTT_FILE_CHUNK_TYPE_SQTT_DESC, |
| SQTT_FILE_CHUNK_TYPE_SQTT_DATA, |
| SQTT_FILE_CHUNK_TYPE_API_INFO, |
| SQTT_FILE_CHUNK_TYPE_RESERVED, |
| SQTT_FILE_CHUNK_TYPE_QUEUE_EVENT_TIMINGS, |
| SQTT_FILE_CHUNK_TYPE_CLOCK_CALIBRATION, |
| SQTT_FILE_CHUNK_TYPE_CPU_INFO, |
| SQTT_FILE_CHUNK_TYPE_SPM_DB, |
| SQTT_FILE_CHUNK_TYPE_CODE_OBJECT_DATABASE, |
| SQTT_FILE_CHUNK_TYPE_CODE_OBJECT_LOADER_EVENTS, |
| SQTT_FILE_CHUNK_TYPE_PSO_CORRELATION, |
| SQTT_FILE_CHUNK_TYPE_INSTRUMENTATION_TABLE, |
| SQTT_FILE_CHUNK_TYPE_COUNT |
| }; |
| |
| struct sqtt_file_chunk_id { |
| enum sqtt_file_chunk_type type : 8; |
| int32_t index : 8; |
| int32_t reserved : 16; |
| }; |
| |
| struct sqtt_file_chunk_header { |
| struct sqtt_file_chunk_id chunk_id; |
| uint16_t minor_version; |
| uint16_t major_version; |
| int32_t size_in_bytes; |
| int32_t padding; |
| }; |
| |
| /** |
| * SQTT file header. |
| */ |
| struct sqtt_file_header_flags { |
| union { |
| struct { |
| int32_t is_semaphore_queue_timing_etw : 1; |
| int32_t no_queue_semaphore_timestamps : 1; |
| int32_t reserved : 30; |
| }; |
| |
| uint32_t value; |
| }; |
| }; |
| |
| struct sqtt_file_header { |
| uint32_t magic_number; |
| uint32_t version_major; |
| uint32_t version_minor; |
| struct sqtt_file_header_flags flags; |
| int32_t chunk_offset; |
| int32_t second; |
| int32_t minute; |
| int32_t hour; |
| int32_t day_in_month; |
| int32_t month; |
| int32_t year; |
| int32_t day_in_week; |
| int32_t day_in_year; |
| int32_t is_daylight_savings; |
| }; |
| |
| static_assert(sizeof(struct sqtt_file_header) == 56, "sqtt_file_header doesn't match RGP spec"); |
| |
| static void ac_sqtt_fill_header(struct sqtt_file_header *header) |
| { |
| struct tm *timep, result; |
| time_t raw_time; |
| |
| header->magic_number = SQTT_FILE_MAGIC_NUMBER; |
| header->version_major = SQTT_FILE_VERSION_MAJOR; |
| header->version_minor = SQTT_FILE_VERSION_MINOR; |
| header->flags.value = 0; |
| header->flags.is_semaphore_queue_timing_etw = 1; |
| header->flags.no_queue_semaphore_timestamps = 0; |
| header->chunk_offset = sizeof(*header); |
| |
| time(&raw_time); |
| timep = os_localtime(&raw_time, &result); |
| |
| header->second = timep->tm_sec; |
| header->minute = timep->tm_min; |
| header->hour = timep->tm_hour; |
| header->day_in_month = timep->tm_mday; |
| header->month = timep->tm_mon; |
| header->year = timep->tm_year; |
| header->day_in_week = timep->tm_wday; |
| header->day_in_year = timep->tm_yday; |
| header->is_daylight_savings = timep->tm_isdst; |
| } |
| |
| /** |
| * SQTT CPU info. |
| */ |
| struct sqtt_file_chunk_cpu_info { |
| struct sqtt_file_chunk_header header; |
| uint32_t vendor_id[4]; |
| uint32_t processor_brand[12]; |
| uint32_t reserved[2]; |
| uint64_t cpu_timestamp_freq; |
| uint32_t clock_speed; |
| uint32_t num_logical_cores; |
| uint32_t num_physical_cores; |
| uint32_t system_ram_size; |
| }; |
| |
| static_assert(sizeof(struct sqtt_file_chunk_cpu_info) == 112, |
| "sqtt_file_chunk_cpu_info doesn't match RGP spec"); |
| |
| static void ac_sqtt_fill_cpu_info(struct sqtt_file_chunk_cpu_info *chunk) |
| { |
| uint32_t cpu_clock_speed_total = 0; |
| uint64_t system_ram_size = 0; |
| char line[1024]; |
| FILE *f; |
| |
| chunk->header.chunk_id.type = SQTT_FILE_CHUNK_TYPE_CPU_INFO; |
| chunk->header.chunk_id.index = 0; |
| chunk->header.major_version = 0; |
| chunk->header.minor_version = 0; |
| chunk->header.size_in_bytes = sizeof(*chunk); |
| |
| chunk->cpu_timestamp_freq = 1000000000; /* tick set to 1ns */ |
| |
| strncpy((char *)chunk->vendor_id, "Unknown", sizeof(chunk->vendor_id)); |
| strncpy((char *)chunk->processor_brand, "Unknown", sizeof(chunk->processor_brand)); |
| chunk->clock_speed = 0; |
| chunk->num_logical_cores = 0; |
| chunk->num_physical_cores = 0; |
| chunk->system_ram_size = 0; |
| if (os_get_total_physical_memory(&system_ram_size)) |
| chunk->system_ram_size = system_ram_size / (1024 * 1024); |
| |
| /* Parse cpuinfo to get more detailled information. */ |
| f = fopen("/proc/cpuinfo", "r"); |
| if (!f) |
| return; |
| |
| while (fgets(line, sizeof(line), f)) { |
| char *str; |
| |
| /* Parse vendor name. */ |
| str = strstr(line, "vendor_id"); |
| if (str) { |
| char *ptr = (char *)chunk->vendor_id; |
| char *v = strtok(str, ":"); |
| v = strtok(NULL, ":"); |
| strncpy(ptr, v + 1, sizeof(chunk->vendor_id) - 1); |
| ptr[sizeof(chunk->vendor_id) - 1] = '\0'; |
| } |
| |
| /* Parse processor name. */ |
| str = strstr(line, "model name"); |
| if (str) { |
| char *ptr = (char *)chunk->processor_brand; |
| char *v = strtok(str, ":"); |
| v = strtok(NULL, ":"); |
| strncpy(ptr, v + 1, sizeof(chunk->processor_brand) - 1); |
| ptr[sizeof(chunk->processor_brand) - 1] = '\0'; |
| } |
| |
| /* Parse the current CPU clock speed for each cores. */ |
| str = strstr(line, "cpu MHz"); |
| if (str) { |
| uint32_t v = 0; |
| if (sscanf(str, "cpu MHz : %d", &v) == 1) |
| cpu_clock_speed_total += v; |
| } |
| |
| /* Parse the number of logical cores. */ |
| str = strstr(line, "siblings"); |
| if (str) { |
| uint32_t v = 0; |
| if (sscanf(str, "siblings : %d", &v) == 1) |
| chunk->num_logical_cores = v; |
| } |
| |
| /* Parse the number of physical cores. */ |
| str = strstr(line, "cpu cores"); |
| if (str) { |
| uint32_t v = 0; |
| if (sscanf(str, "cpu cores : %d", &v) == 1) |
| chunk->num_physical_cores = v; |
| } |
| } |
| |
| if (chunk->num_logical_cores) |
| chunk->clock_speed = cpu_clock_speed_total / chunk->num_logical_cores; |
| |
| fclose(f); |
| } |
| |
| /** |
| * SQTT ASIC info. |
| */ |
| enum sqtt_file_chunk_asic_info_flags |
| { |
| SQTT_FILE_CHUNK_ASIC_INFO_FLAG_SC_PACKER_NUMBERING = (1 << 0), |
| SQTT_FILE_CHUNK_ASIC_INFO_FLAG_PS1_EVENT_TOKENS_ENABLED = (1 << 1) |
| }; |
| |
| enum sqtt_gpu_type |
| { |
| SQTT_GPU_TYPE_UNKNOWN = 0x0, |
| SQTT_GPU_TYPE_INTEGRATED = 0x1, |
| SQTT_GPU_TYPE_DISCRETE = 0x2, |
| SQTT_GPU_TYPE_VIRTUAL = 0x3 |
| }; |
| |
| enum sqtt_gfxip_level |
| { |
| SQTT_GFXIP_LEVEL_NONE = 0x0, |
| SQTT_GFXIP_LEVEL_GFXIP_6 = 0x1, |
| SQTT_GFXIP_LEVEL_GFXIP_7 = 0x2, |
| SQTT_GFXIP_LEVEL_GFXIP_8 = 0x3, |
| SQTT_GFXIP_LEVEL_GFXIP_8_1 = 0x4, |
| SQTT_GFXIP_LEVEL_GFXIP_9 = 0x5, |
| SQTT_GFXIP_LEVEL_GFXIP_10_1 = 0x7, |
| SQTT_GFXIP_LEVEL_GFXIP_10_3 = 0x9, |
| }; |
| |
| enum sqtt_memory_type |
| { |
| SQTT_MEMORY_TYPE_UNKNOWN = 0x0, |
| SQTT_MEMORY_TYPE_DDR = 0x1, |
| SQTT_MEMORY_TYPE_DDR2 = 0x2, |
| SQTT_MEMORY_TYPE_DDR3 = 0x3, |
| SQTT_MEMORY_TYPE_DDR4 = 0x4, |
| SQTT_MEMORY_TYPE_DDR5 = 0x5, |
| SQTT_MEMORY_TYPE_GDDR3 = 0x10, |
| SQTT_MEMORY_TYPE_GDDR4 = 0x11, |
| SQTT_MEMORY_TYPE_GDDR5 = 0x12, |
| SQTT_MEMORY_TYPE_GDDR6 = 0x13, |
| SQTT_MEMORY_TYPE_HBM = 0x20, |
| SQTT_MEMORY_TYPE_HBM2 = 0x21, |
| SQTT_MEMORY_TYPE_HBM3 = 0x22, |
| SQTT_MEMORY_TYPE_LPDDR4 = 0x30, |
| SQTT_MEMORY_TYPE_LPDDR5 = 0x31, |
| }; |
| |
| struct sqtt_file_chunk_asic_info { |
| struct sqtt_file_chunk_header header; |
| uint64_t flags; |
| uint64_t trace_shader_core_clock; |
| uint64_t trace_memory_clock; |
| int32_t device_id; |
| int32_t device_revision_id; |
| int32_t vgprs_per_simd; |
| int32_t sgprs_per_simd; |
| int32_t shader_engines; |
| int32_t compute_unit_per_shader_engine; |
| int32_t simd_per_compute_unit; |
| int32_t wavefronts_per_simd; |
| int32_t minimum_vgpr_alloc; |
| int32_t vgpr_alloc_granularity; |
| int32_t minimum_sgpr_alloc; |
| int32_t sgpr_alloc_granularity; |
| int32_t hardware_contexts; |
| enum sqtt_gpu_type gpu_type; |
| enum sqtt_gfxip_level gfxip_level; |
| int32_t gpu_index; |
| int32_t gds_size; |
| int32_t gds_per_shader_engine; |
| int32_t ce_ram_size; |
| int32_t ce_ram_size_graphics; |
| int32_t ce_ram_size_compute; |
| int32_t max_number_of_dedicated_cus; |
| int64_t vram_size; |
| int32_t vram_bus_width; |
| int32_t l2_cache_size; |
| int32_t l1_cache_size; |
| int32_t lds_size; |
| char gpu_name[SQTT_GPU_NAME_MAX_SIZE]; |
| float alu_per_clock; |
| float texture_per_clock; |
| float prims_per_clock; |
| float pixels_per_clock; |
| uint64_t gpu_timestamp_frequency; |
| uint64_t max_shader_core_clock; |
| uint64_t max_memory_clock; |
| uint32_t memory_ops_per_clock; |
| enum sqtt_memory_type memory_chip_type; |
| uint32_t lds_granularity; |
| uint16_t cu_mask[SQTT_MAX_NUM_SE][SQTT_SA_PER_SE]; |
| char reserved1[128]; |
| char padding[4]; |
| }; |
| |
| static_assert(sizeof(struct sqtt_file_chunk_asic_info) == 720, |
| "sqtt_file_chunk_asic_info doesn't match RGP spec"); |
| |
| static enum sqtt_gfxip_level ac_gfx_level_to_sqtt_gfxip_level(enum amd_gfx_level gfx_level) |
| { |
| switch (gfx_level) { |
| case GFX8: |
| return SQTT_GFXIP_LEVEL_GFXIP_8; |
| case GFX9: |
| return SQTT_GFXIP_LEVEL_GFXIP_9; |
| case GFX10: |
| return SQTT_GFXIP_LEVEL_GFXIP_10_1; |
| case GFX10_3: |
| return SQTT_GFXIP_LEVEL_GFXIP_10_3; |
| default: |
| unreachable("Invalid gfx level"); |
| } |
| } |
| |
| static enum sqtt_memory_type ac_vram_type_to_sqtt_memory_type(uint32_t vram_type) |
| { |
| switch (vram_type) { |
| case AMD_VRAM_TYPE_UNKNOWN: |
| return SQTT_MEMORY_TYPE_UNKNOWN; |
| case AMD_VRAM_TYPE_DDR2: |
| return SQTT_MEMORY_TYPE_DDR2; |
| case AMD_VRAM_TYPE_DDR3: |
| return SQTT_MEMORY_TYPE_DDR3; |
| case AMD_VRAM_TYPE_DDR4: |
| return SQTT_MEMORY_TYPE_DDR4; |
| case AMD_VRAM_TYPE_DDR5: |
| return SQTT_MEMORY_TYPE_DDR5; |
| case AMD_VRAM_TYPE_GDDR3: |
| return SQTT_MEMORY_TYPE_GDDR3; |
| case AMD_VRAM_TYPE_GDDR4: |
| return SQTT_MEMORY_TYPE_GDDR4; |
| case AMD_VRAM_TYPE_GDDR5: |
| return SQTT_MEMORY_TYPE_GDDR5; |
| case AMD_VRAM_TYPE_GDDR6: |
| return SQTT_MEMORY_TYPE_GDDR6; |
| case AMD_VRAM_TYPE_HBM: |
| return SQTT_MEMORY_TYPE_HBM; |
| case AMD_VRAM_TYPE_LPDDR4: |
| return SQTT_MEMORY_TYPE_LPDDR4; |
| case AMD_VRAM_TYPE_LPDDR5: |
| return SQTT_MEMORY_TYPE_LPDDR5; |
| default: |
| unreachable("Invalid vram type"); |
| } |
| } |
| |
| static void ac_sqtt_fill_asic_info(struct radeon_info *rad_info, |
| struct sqtt_file_chunk_asic_info *chunk) |
| { |
| bool has_wave32 = rad_info->gfx_level >= GFX10; |
| |
| chunk->header.chunk_id.type = SQTT_FILE_CHUNK_TYPE_ASIC_INFO; |
| chunk->header.chunk_id.index = 0; |
| chunk->header.major_version = 0; |
| chunk->header.minor_version = 4; |
| chunk->header.size_in_bytes = sizeof(*chunk); |
| |
| chunk->flags = 0; |
| |
| /* All chips older than GFX9 are affected by the "SPI not |
| * differentiating pkr_id for newwave commands" bug. |
| */ |
| if (rad_info->gfx_level < GFX9) |
| chunk->flags |= SQTT_FILE_CHUNK_ASIC_INFO_FLAG_SC_PACKER_NUMBERING; |
| |
| /* Only GFX9+ support PS1 events. */ |
| if (rad_info->gfx_level >= GFX9) |
| chunk->flags |= SQTT_FILE_CHUNK_ASIC_INFO_FLAG_PS1_EVENT_TOKENS_ENABLED; |
| |
| chunk->trace_shader_core_clock = rad_info->max_gpu_freq_mhz * 1000000ull; |
| chunk->trace_memory_clock = rad_info->memory_freq_mhz * 1000000ull; |
| |
| /* RGP gets very confused if these clocks are 0. The numbers here are for profile_peak on |
| * VGH since that is the chips where we've seen the need for this workaround. */ |
| if (!chunk->trace_shader_core_clock) |
| chunk->trace_shader_core_clock = 1300000000; |
| if (!chunk->trace_memory_clock) |
| chunk->trace_memory_clock = 687000000; |
| |
| chunk->device_id = rad_info->pci_id; |
| chunk->device_revision_id = rad_info->pci_rev_id; |
| chunk->vgprs_per_simd = rad_info->num_physical_wave64_vgprs_per_simd * (has_wave32 ? 2 : 1); |
| chunk->sgprs_per_simd = rad_info->num_physical_sgprs_per_simd; |
| chunk->shader_engines = rad_info->max_se; |
| chunk->compute_unit_per_shader_engine = rad_info->min_good_cu_per_sa * rad_info->max_sa_per_se; |
| chunk->simd_per_compute_unit = rad_info->num_simd_per_compute_unit; |
| chunk->wavefronts_per_simd = rad_info->max_wave64_per_simd; |
| |
| chunk->minimum_vgpr_alloc = rad_info->min_wave64_vgpr_alloc; |
| chunk->vgpr_alloc_granularity = rad_info->wave64_vgpr_alloc_granularity * (has_wave32 ? 2 : 1); |
| chunk->minimum_sgpr_alloc = rad_info->min_sgpr_alloc; |
| chunk->sgpr_alloc_granularity = rad_info->sgpr_alloc_granularity; |
| |
| chunk->hardware_contexts = 8; |
| chunk->gpu_type = |
| rad_info->has_dedicated_vram ? SQTT_GPU_TYPE_DISCRETE : SQTT_GPU_TYPE_INTEGRATED; |
| chunk->gfxip_level = ac_gfx_level_to_sqtt_gfxip_level(rad_info->gfx_level); |
| chunk->gpu_index = 0; |
| |
| chunk->max_number_of_dedicated_cus = 0; |
| chunk->ce_ram_size = 0; |
| chunk->ce_ram_size_graphics = 0; |
| chunk->ce_ram_size_compute = 0; |
| |
| chunk->vram_bus_width = rad_info->memory_bus_width; |
| chunk->vram_size = (uint64_t)rad_info->vram_size_kb * 1024; |
| chunk->l2_cache_size = rad_info->l2_cache_size; |
| chunk->l1_cache_size = rad_info->l1_cache_size; |
| chunk->lds_size = rad_info->lds_size_per_workgroup; |
| if (rad_info->gfx_level >= GFX10) { |
| /* RGP expects the LDS size in CU mode. */ |
| chunk->lds_size /= 2; |
| } |
| |
| strncpy(chunk->gpu_name, rad_info->name, SQTT_GPU_NAME_MAX_SIZE - 1); |
| |
| chunk->alu_per_clock = 0.0; |
| chunk->texture_per_clock = 0.0; |
| chunk->prims_per_clock = rad_info->max_se; |
| if (rad_info->gfx_level == GFX10) |
| chunk->prims_per_clock *= 2; |
| chunk->pixels_per_clock = 0.0; |
| |
| chunk->gpu_timestamp_frequency = rad_info->clock_crystal_freq * 1000; |
| chunk->max_shader_core_clock = rad_info->max_gpu_freq_mhz * 1000000; |
| chunk->max_memory_clock = rad_info->memory_freq_mhz * 1000000; |
| chunk->memory_ops_per_clock = ac_memory_ops_per_clock(rad_info->vram_type); |
| chunk->memory_chip_type = ac_vram_type_to_sqtt_memory_type(rad_info->vram_type); |
| chunk->lds_granularity = rad_info->lds_encode_granularity; |
| |
| for (unsigned se = 0; se < AMD_MAX_SE; se++) { |
| for (unsigned sa = 0; sa < AMD_MAX_SA_PER_SE; sa++) { |
| chunk->cu_mask[se][sa] = rad_info->cu_mask[se][sa]; |
| } |
| } |
| } |
| |
| /** |
| * SQTT API info. |
| */ |
| enum sqtt_api_type |
| { |
| SQTT_API_TYPE_DIRECTX_12, |
| SQTT_API_TYPE_VULKAN, |
| SQTT_API_TYPE_GENERIC, |
| SQTT_API_TYPE_OPENCL |
| }; |
| |
| enum sqtt_instruction_trace_mode |
| { |
| SQTT_INSTRUCTION_TRACE_DISABLED = 0x0, |
| SQTT_INSTRUCTION_TRACE_FULL_FRAME = 0x1, |
| SQTT_INSTRUCTION_TRACE_API_PSO = 0x2, |
| }; |
| |
| enum sqtt_profiling_mode |
| { |
| SQTT_PROFILING_MODE_PRESENT = 0x0, |
| SQTT_PROFILING_MODE_USER_MARKERS = 0x1, |
| SQTT_PROFILING_MODE_INDEX = 0x2, |
| SQTT_PROFILING_MODE_TAG = 0x3, |
| }; |
| |
| union sqtt_profiling_mode_data { |
| struct { |
| char start[256]; |
| char end[256]; |
| } user_marker_profiling_data; |
| |
| struct { |
| uint32_t start; |
| uint32_t end; |
| } index_profiling_data; |
| |
| struct { |
| uint32_t begin_hi; |
| uint32_t begin_lo; |
| uint32_t end_hi; |
| uint32_t end_lo; |
| } tag_profiling_data; |
| }; |
| |
| union sqtt_instruction_trace_data { |
| struct { |
| uint64_t api_pso_filter; |
| } api_pso_data; |
| |
| struct { |
| char start[256]; |
| char end[256]; |
| } user_marker_data; |
| }; |
| |
| struct sqtt_file_chunk_api_info { |
| struct sqtt_file_chunk_header header; |
| enum sqtt_api_type api_type; |
| uint16_t major_version; |
| uint16_t minor_version; |
| enum sqtt_profiling_mode profiling_mode; |
| uint32_t reserved; |
| union sqtt_profiling_mode_data profiling_mode_data; |
| enum sqtt_instruction_trace_mode instruction_trace_mode; |
| uint32_t reserved2; |
| union sqtt_instruction_trace_data instruction_trace_data; |
| }; |
| |
| static_assert(sizeof(struct sqtt_file_chunk_api_info) == 1064, |
| "sqtt_file_chunk_api_info doesn't match RGP spec"); |
| |
| static void ac_sqtt_fill_api_info(struct sqtt_file_chunk_api_info *chunk) |
| { |
| chunk->header.chunk_id.type = SQTT_FILE_CHUNK_TYPE_API_INFO; |
| chunk->header.chunk_id.index = 0; |
| chunk->header.major_version = 0; |
| chunk->header.minor_version = 1; |
| chunk->header.size_in_bytes = sizeof(*chunk); |
| |
| chunk->api_type = SQTT_API_TYPE_VULKAN; |
| chunk->major_version = 0; |
| chunk->minor_version = 0; |
| chunk->profiling_mode = SQTT_PROFILING_MODE_PRESENT; |
| chunk->instruction_trace_mode = SQTT_INSTRUCTION_TRACE_DISABLED; |
| } |
| |
| struct sqtt_code_object_database_record { |
| uint32_t size; |
| }; |
| |
| struct sqtt_file_chunk_code_object_database { |
| struct sqtt_file_chunk_header header; |
| uint32_t offset; |
| uint32_t flags; |
| uint32_t size; |
| uint32_t record_count; |
| }; |
| |
| static void |
| ac_sqtt_fill_code_object(struct rgp_code_object *rgp_code_object, |
| struct sqtt_file_chunk_code_object_database *chunk, |
| size_t file_offset, uint32_t chunk_size) |
| { |
| chunk->header.chunk_id.type = SQTT_FILE_CHUNK_TYPE_CODE_OBJECT_DATABASE; |
| chunk->header.chunk_id.index = 0; |
| chunk->header.major_version = 0; |
| chunk->header.minor_version = 0; |
| chunk->header.size_in_bytes = chunk_size; |
| chunk->offset = file_offset; |
| chunk->flags = 0; |
| chunk->size = chunk_size; |
| chunk->record_count = rgp_code_object->record_count; |
| } |
| |
| struct sqtt_code_object_loader_events_record { |
| uint32_t loader_event_type; |
| uint32_t reserved; |
| uint64_t base_address; |
| uint64_t code_object_hash[2]; |
| uint64_t time_stamp; |
| }; |
| |
| struct sqtt_file_chunk_code_object_loader_events { |
| struct sqtt_file_chunk_header header; |
| uint32_t offset; |
| uint32_t flags; |
| uint32_t record_size; |
| uint32_t record_count; |
| }; |
| |
| static void |
| ac_sqtt_fill_loader_events(struct rgp_loader_events *rgp_loader_events, |
| struct sqtt_file_chunk_code_object_loader_events *chunk, |
| size_t file_offset) |
| { |
| chunk->header.chunk_id.type = |
| SQTT_FILE_CHUNK_TYPE_CODE_OBJECT_LOADER_EVENTS; |
| chunk->header.chunk_id.index = 0; |
| chunk->header.major_version = 1; |
| chunk->header.minor_version = 0; |
| chunk->header.size_in_bytes = (rgp_loader_events->record_count * |
| sizeof(struct sqtt_code_object_loader_events_record)) + |
| sizeof(*chunk); |
| chunk->offset = file_offset; |
| chunk->flags = 0; |
| chunk->record_size = sizeof(struct sqtt_code_object_loader_events_record); |
| chunk->record_count = rgp_loader_events->record_count; |
| } |
| struct sqtt_pso_correlation_record { |
| uint64_t api_pso_hash; |
| uint64_t pipeline_hash[2]; |
| char api_level_obj_name[64]; |
| }; |
| |
| struct sqtt_file_chunk_pso_correlation { |
| struct sqtt_file_chunk_header header; |
| uint32_t offset; |
| uint32_t flags; |
| uint32_t record_size; |
| uint32_t record_count; |
| }; |
| |
| static void |
| ac_sqtt_fill_pso_correlation(struct rgp_pso_correlation *rgp_pso_correlation, |
| struct sqtt_file_chunk_pso_correlation *chunk, |
| size_t file_offset) |
| { |
| chunk->header.chunk_id.type = SQTT_FILE_CHUNK_TYPE_PSO_CORRELATION; |
| chunk->header.chunk_id.index = 0; |
| chunk->header.major_version = 0; |
| chunk->header.minor_version = 0; |
| chunk->header.size_in_bytes = (rgp_pso_correlation->record_count * |
| sizeof(struct sqtt_pso_correlation_record)) + |
| sizeof(*chunk); |
| chunk->offset = file_offset; |
| chunk->flags = 0; |
| chunk->record_size = sizeof(struct sqtt_pso_correlation_record); |
| chunk->record_count = rgp_pso_correlation->record_count; |
| } |
| |
| /** |
| * SQTT desc info. |
| */ |
| struct sqtt_file_chunk_sqtt_desc { |
| struct sqtt_file_chunk_header header; |
| int32_t shader_engine_index; |
| enum sqtt_version sqtt_version; |
| union { |
| struct { |
| int32_t instrumentation_version; |
| } v0; |
| struct { |
| int16_t instrumentation_spec_version; |
| int16_t instrumentation_api_version; |
| int32_t compute_unit_index; |
| } v1; |
| }; |
| }; |
| |
| static_assert(sizeof(struct sqtt_file_chunk_sqtt_desc) == 32, |
| "sqtt_file_chunk_sqtt_desc doesn't match RGP spec"); |
| |
| static enum sqtt_version ac_gfx_level_to_sqtt_version(enum amd_gfx_level gfx_level) |
| { |
| switch (gfx_level) { |
| case GFX8: |
| return SQTT_VERSION_2_2; |
| case GFX9: |
| return SQTT_VERSION_2_3; |
| case GFX10: |
| return SQTT_VERSION_2_4; |
| case GFX10_3: |
| return SQTT_VERSION_2_4; |
| default: |
| unreachable("Invalid gfx level"); |
| } |
| } |
| |
| static void ac_sqtt_fill_sqtt_desc(struct radeon_info *info, |
| struct sqtt_file_chunk_sqtt_desc *chunk, int32_t chunk_index, |
| int32_t shader_engine_index, int32_t compute_unit_index) |
| { |
| chunk->header.chunk_id.type = SQTT_FILE_CHUNK_TYPE_SQTT_DESC; |
| chunk->header.chunk_id.index = chunk_index; |
| chunk->header.major_version = 0; |
| chunk->header.minor_version = 2; |
| chunk->header.size_in_bytes = sizeof(*chunk); |
| |
| chunk->sqtt_version = |
| ac_gfx_level_to_sqtt_version(info->gfx_level); |
| chunk->shader_engine_index = shader_engine_index; |
| chunk->v1.instrumentation_spec_version = 1; |
| chunk->v1.instrumentation_api_version = 0; |
| chunk->v1.compute_unit_index = compute_unit_index; |
| } |
| |
| /** |
| * SQTT data info. |
| */ |
| struct sqtt_file_chunk_sqtt_data { |
| struct sqtt_file_chunk_header header; |
| int32_t offset; /* in bytes */ |
| int32_t size; /* in bytes */ |
| }; |
| |
| static_assert(sizeof(struct sqtt_file_chunk_sqtt_data) == 24, |
| "sqtt_file_chunk_sqtt_data doesn't match RGP spec"); |
| |
| static void ac_sqtt_fill_sqtt_data(struct sqtt_file_chunk_sqtt_data *chunk, int32_t chunk_index, |
| int32_t offset, int32_t size) |
| { |
| chunk->header.chunk_id.type = SQTT_FILE_CHUNK_TYPE_SQTT_DATA; |
| chunk->header.chunk_id.index = chunk_index; |
| chunk->header.major_version = 0; |
| chunk->header.minor_version = 0; |
| chunk->header.size_in_bytes = sizeof(*chunk) + size; |
| |
| chunk->offset = sizeof(*chunk) + offset; |
| chunk->size = size; |
| } |
| |
| /** |
| * SQTT queue event timings info. |
| */ |
| struct sqtt_file_chunk_queue_event_timings { |
| struct sqtt_file_chunk_header header; |
| uint32_t queue_info_table_record_count; |
| uint32_t queue_info_table_size; |
| uint32_t queue_event_table_record_count; |
| uint32_t queue_event_table_size; |
| }; |
| |
| static_assert(sizeof(struct sqtt_file_chunk_queue_event_timings) == 32, |
| "sqtt_file_chunk_queue_event_timings doesn't match RGP spec"); |
| |
| struct sqtt_queue_info_record { |
| uint64_t queue_id; |
| uint64_t queue_context; |
| struct sqtt_queue_hardware_info hardware_info; |
| uint32_t reserved; |
| }; |
| |
| static_assert(sizeof(struct sqtt_queue_info_record) == 24, |
| "sqtt_queue_info_record doesn't match RGP spec"); |
| |
| struct sqtt_queue_event_record { |
| enum sqtt_queue_event_type event_type; |
| uint32_t sqtt_cb_id; |
| uint64_t frame_index; |
| uint32_t queue_info_index; |
| uint32_t submit_sub_index; |
| uint64_t api_id; |
| uint64_t cpu_timestamp; |
| uint64_t gpu_timestamps[2]; |
| }; |
| |
| static_assert(sizeof(struct sqtt_queue_event_record) == 56, |
| "sqtt_queue_event_record doesn't match RGP spec"); |
| |
| static void |
| ac_sqtt_fill_queue_event_timings(struct rgp_queue_info *rgp_queue_info, |
| struct rgp_queue_event *rgp_queue_event, |
| struct sqtt_file_chunk_queue_event_timings *chunk) |
| { |
| unsigned queue_info_size = |
| rgp_queue_info->record_count * sizeof(struct sqtt_queue_info_record); |
| unsigned queue_event_size = |
| rgp_queue_event->record_count * sizeof(struct sqtt_queue_event_record); |
| |
| chunk->header.chunk_id.type = SQTT_FILE_CHUNK_TYPE_QUEUE_EVENT_TIMINGS; |
| chunk->header.chunk_id.index = 0; |
| chunk->header.major_version = 1; |
| chunk->header.minor_version = 1; |
| chunk->header.size_in_bytes = queue_info_size + queue_event_size + |
| sizeof(*chunk); |
| |
| chunk->queue_info_table_record_count = rgp_queue_info->record_count; |
| chunk->queue_info_table_size = queue_info_size; |
| chunk->queue_event_table_record_count = rgp_queue_event->record_count; |
| chunk->queue_event_table_size = queue_event_size; |
| } |
| |
| /** |
| * SQTT clock calibration info. |
| */ |
| struct sqtt_file_chunk_clock_calibration { |
| struct sqtt_file_chunk_header header; |
| uint64_t cpu_timestamp; |
| uint64_t gpu_timestamp; |
| uint64_t reserved; |
| }; |
| |
| static_assert(sizeof(struct sqtt_file_chunk_clock_calibration) == 40, |
| "sqtt_file_chunk_clock_calibration doesn't match RGP spec"); |
| |
| static void |
| ac_sqtt_fill_clock_calibration(struct sqtt_file_chunk_clock_calibration *chunk, |
| int32_t chunk_index) |
| { |
| chunk->header.chunk_id.type = SQTT_FILE_CHUNK_TYPE_CLOCK_CALIBRATION; |
| chunk->header.chunk_id.index = chunk_index; |
| chunk->header.major_version = 0; |
| chunk->header.minor_version = 0; |
| chunk->header.size_in_bytes = sizeof(*chunk); |
| } |
| |
| /* Below values are from from llvm project |
| * llvm/include/llvm/BinaryFormat/ELF.h |
| */ |
| enum elf_gfxip_level |
| { |
| EF_AMDGPU_MACH_AMDGCN_GFX801 = 0x028, |
| EF_AMDGPU_MACH_AMDGCN_GFX900 = 0x02c, |
| EF_AMDGPU_MACH_AMDGCN_GFX1010 = 0x033, |
| EF_AMDGPU_MACH_AMDGCN_GFX1030 = 0x036, |
| }; |
| |
| static enum elf_gfxip_level ac_gfx_level_to_elf_gfxip_level(enum amd_gfx_level gfx_level) |
| { |
| switch (gfx_level) { |
| case GFX8: |
| return EF_AMDGPU_MACH_AMDGCN_GFX801; |
| case GFX9: |
| return EF_AMDGPU_MACH_AMDGCN_GFX900; |
| case GFX10: |
| return EF_AMDGPU_MACH_AMDGCN_GFX1010; |
| case GFX10_3: |
| return EF_AMDGPU_MACH_AMDGCN_GFX1030; |
| default: |
| unreachable("Invalid gfx level"); |
| } |
| } |
| |
| /** |
| * SQTT SPM DB info. |
| */ |
| struct sqtt_spm_counter_info { |
| enum ac_pc_gpu_block block; |
| uint32_t instance; |
| uint32_t data_offset; /* offset of counter from the beginning of the chunk */ |
| uint32_t event_index; /* index of counter within the block */ |
| }; |
| |
| struct sqtt_file_chunk_spm_db { |
| struct sqtt_file_chunk_header header; |
| uint32_t flags; |
| uint32_t num_timestamps; |
| uint32_t num_spm_counter_info; |
| uint32_t sample_interval; |
| }; |
| |
| static_assert(sizeof(struct sqtt_file_chunk_spm_db) == 32, |
| "sqtt_file_chunk_spm_db doesn't match RGP spec"); |
| |
| static void ac_sqtt_fill_spm_db(const struct ac_spm_trace_data *spm_trace, |
| struct sqtt_file_chunk_spm_db *chunk, |
| uint32_t num_samples, |
| uint32_t chunk_size) |
| { |
| chunk->header.chunk_id.type = SQTT_FILE_CHUNK_TYPE_SPM_DB; |
| chunk->header.chunk_id.index = 0; |
| chunk->header.major_version = 1; |
| chunk->header.minor_version = 3; |
| chunk->header.size_in_bytes = chunk_size; |
| |
| chunk->flags = 0; |
| chunk->num_timestamps = num_samples; |
| chunk->num_spm_counter_info = spm_trace->num_counters; |
| chunk->sample_interval = spm_trace->sample_interval; |
| } |
| |
| static void ac_sqtt_dump_spm(const struct ac_spm_trace_data *spm_trace, |
| size_t file_offset, |
| FILE *output) |
| { |
| uint32_t sample_size_in_bytes = ac_spm_get_sample_size(spm_trace); |
| uint32_t num_samples = ac_spm_get_num_samples(spm_trace); |
| uint8_t *spm_data_ptr = (uint8_t *)spm_trace->ptr; |
| struct sqtt_file_chunk_spm_db spm_db; |
| size_t file_spm_db_offset = file_offset; |
| |
| fseek(output, sizeof(struct sqtt_file_chunk_spm_db), SEEK_CUR); |
| file_offset += sizeof(struct sqtt_file_chunk_spm_db); |
| |
| /* Skip the reserved 32 bytes of data at beginning. */ |
| spm_data_ptr += 32; |
| |
| /* SPM timestamps. */ |
| uint32_t sample_size_in_qwords = sample_size_in_bytes / sizeof(uint64_t); |
| uint64_t *timestamp_ptr = (uint64_t *)spm_data_ptr; |
| |
| for (uint32_t s = 0; s < num_samples; s++) { |
| uint64_t index = s * sample_size_in_qwords; |
| uint64_t timestamp = timestamp_ptr[index]; |
| |
| file_offset += sizeof(timestamp); |
| fwrite(×tamp, sizeof(timestamp), 1, output); |
| } |
| |
| /* SPM counter info. */ |
| uint64_t counter_values_size = num_samples * sizeof(uint16_t); |
| uint64_t counter_values_offset = num_samples * sizeof(uint64_t) + |
| spm_trace->num_counters * sizeof(struct sqtt_spm_counter_info); |
| |
| for (uint32_t c = 0; c < spm_trace->num_counters; c++) { |
| struct sqtt_spm_counter_info cntr_info = { |
| .block = spm_trace->counters[c].gpu_block, |
| .instance = spm_trace->counters[c].instance, |
| .data_offset = counter_values_offset, |
| .event_index = spm_trace->counters[c].event_id, |
| }; |
| |
| file_offset += sizeof(cntr_info); |
| fwrite(&cntr_info, sizeof(cntr_info), 1, output); |
| |
| counter_values_offset += counter_values_size; |
| } |
| |
| /* SPM counter values. */ |
| uint32_t sample_size_in_hwords = sample_size_in_bytes / sizeof(uint16_t); |
| uint16_t *counter_values_ptr = (uint16_t *)spm_data_ptr; |
| |
| for (uint32_t c = 0; c < spm_trace->num_counters; c++) { |
| uint64_t offset = spm_trace->counters[c].offset; |
| |
| for (uint32_t s = 0; s < num_samples; s++) { |
| uint64_t index = offset + (s * sample_size_in_hwords); |
| uint16_t value = counter_values_ptr[index]; |
| |
| file_offset += sizeof(value); |
| fwrite(&value, sizeof(value), 1, output); |
| } |
| } |
| |
| /* SQTT SPM DB chunk. */ |
| ac_sqtt_fill_spm_db(spm_trace, &spm_db, num_samples, |
| file_offset - file_spm_db_offset); |
| fseek(output, file_spm_db_offset, SEEK_SET); |
| fwrite(&spm_db, sizeof(struct sqtt_file_chunk_spm_db), 1, output); |
| fseek(output, file_offset, SEEK_SET); |
| } |
| |
| #if defined(USE_LIBELF) |
| static void ac_sqtt_dump_data(struct radeon_info *rad_info, |
| struct ac_thread_trace *thread_trace, |
| const struct ac_spm_trace_data *spm_trace, |
| FILE *output) |
| { |
| struct ac_thread_trace_data *thread_trace_data = thread_trace->data; |
| struct sqtt_file_chunk_asic_info asic_info = {0}; |
| struct sqtt_file_chunk_cpu_info cpu_info = {0}; |
| struct sqtt_file_chunk_api_info api_info = {0}; |
| struct sqtt_file_header header = {0}; |
| size_t file_offset = 0; |
| struct rgp_code_object *rgp_code_object = |
| &thread_trace_data->rgp_code_object; |
| struct rgp_loader_events *rgp_loader_events = |
| &thread_trace_data->rgp_loader_events; |
| struct rgp_pso_correlation *rgp_pso_correlation = |
| &thread_trace_data->rgp_pso_correlation; |
| struct rgp_queue_info *rgp_queue_info = &thread_trace_data->rgp_queue_info; |
| struct rgp_queue_event *rgp_queue_event = &thread_trace_data->rgp_queue_event; |
| struct rgp_clock_calibration *rgp_clock_calibration = &thread_trace_data->rgp_clock_calibration; |
| |
| /* SQTT header file. */ |
| ac_sqtt_fill_header(&header); |
| file_offset += sizeof(header); |
| fwrite(&header, sizeof(header), 1, output); |
| |
| /* SQTT cpu chunk. */ |
| ac_sqtt_fill_cpu_info(&cpu_info); |
| file_offset += sizeof(cpu_info); |
| fwrite(&cpu_info, sizeof(cpu_info), 1, output); |
| |
| /* SQTT asic chunk. */ |
| ac_sqtt_fill_asic_info(rad_info, &asic_info); |
| file_offset += sizeof(asic_info); |
| fwrite(&asic_info, sizeof(asic_info), 1, output); |
| |
| /* SQTT api chunk. */ |
| ac_sqtt_fill_api_info(&api_info); |
| file_offset += sizeof(api_info); |
| fwrite(&api_info, sizeof(api_info), 1, output); |
| |
| /* SQTT code object database chunk. */ |
| if (rgp_code_object->record_count) { |
| size_t file_code_object_offset = file_offset; |
| struct sqtt_file_chunk_code_object_database code_object; |
| struct sqtt_code_object_database_record code_object_record; |
| uint32_t elf_size_calc = 0; |
| uint32_t flags = ac_gfx_level_to_elf_gfxip_level(rad_info->gfx_level); |
| |
| fseek(output, sizeof(struct sqtt_file_chunk_code_object_database), SEEK_CUR); |
| file_offset += sizeof(struct sqtt_file_chunk_code_object_database); |
| list_for_each_entry_safe(struct rgp_code_object_record, record, |
| &rgp_code_object->record, list) { |
| fseek(output, sizeof(struct sqtt_code_object_database_record), SEEK_CUR); |
| ac_rgp_file_write_elf_object(output, file_offset + |
| sizeof(struct sqtt_code_object_database_record), |
| record, &elf_size_calc, flags); |
| /* Align to 4 bytes per the RGP file spec. */ |
| code_object_record.size = ALIGN(elf_size_calc, 4); |
| fseek(output, file_offset, SEEK_SET); |
| fwrite(&code_object_record, sizeof(struct sqtt_code_object_database_record), |
| 1, output); |
| file_offset += (sizeof(struct sqtt_code_object_database_record) + |
| code_object_record.size); |
| fseek(output, file_offset, SEEK_SET); |
| } |
| ac_sqtt_fill_code_object(rgp_code_object, &code_object, |
| file_code_object_offset, |
| file_offset - file_code_object_offset); |
| fseek(output, file_code_object_offset, SEEK_SET); |
| fwrite(&code_object, sizeof(struct sqtt_file_chunk_code_object_database), 1, output); |
| fseek(output, file_offset, SEEK_SET); |
| } |
| |
| /* SQTT code object loader events chunk. */ |
| if (rgp_loader_events->record_count) { |
| struct sqtt_file_chunk_code_object_loader_events loader_events; |
| |
| ac_sqtt_fill_loader_events(rgp_loader_events, &loader_events, |
| file_offset); |
| fwrite(&loader_events, sizeof(struct sqtt_file_chunk_code_object_loader_events), |
| 1, output); |
| file_offset += sizeof(struct sqtt_file_chunk_code_object_loader_events); |
| list_for_each_entry_safe(struct rgp_loader_events_record, record, |
| &rgp_loader_events->record, list) { |
| fwrite(record, sizeof(struct sqtt_code_object_loader_events_record), 1, output); |
| } |
| file_offset += (rgp_loader_events->record_count * |
| sizeof(struct sqtt_code_object_loader_events_record)); |
| } |
| |
| /* SQTT pso correlation chunk. */ |
| if (rgp_pso_correlation->record_count) { |
| struct sqtt_file_chunk_pso_correlation pso_correlation; |
| |
| ac_sqtt_fill_pso_correlation(rgp_pso_correlation, |
| &pso_correlation, file_offset); |
| fwrite(&pso_correlation, sizeof(struct sqtt_file_chunk_pso_correlation), 1, |
| output); |
| file_offset += sizeof(struct sqtt_file_chunk_pso_correlation); |
| list_for_each_entry_safe(struct rgp_pso_correlation_record, record, |
| &rgp_pso_correlation->record, list) { |
| fwrite(record, sizeof(struct sqtt_pso_correlation_record), |
| 1, output); |
| } |
| file_offset += (rgp_pso_correlation->record_count * |
| sizeof(struct sqtt_pso_correlation_record)); |
| } |
| |
| /* SQTT queue event timings. */ |
| if (rgp_queue_info->record_count || rgp_queue_event->record_count) { |
| struct sqtt_file_chunk_queue_event_timings queue_event_timings; |
| |
| ac_sqtt_fill_queue_event_timings(rgp_queue_info, rgp_queue_event, |
| &queue_event_timings); |
| fwrite(&queue_event_timings, sizeof(struct sqtt_file_chunk_queue_event_timings), 1, |
| output); |
| file_offset += sizeof(struct sqtt_file_chunk_queue_event_timings); |
| |
| /* Queue info. */ |
| list_for_each_entry_safe(struct rgp_queue_info_record, record, |
| &rgp_queue_info->record, list) { |
| fwrite(record, sizeof(struct sqtt_queue_info_record), 1, output); |
| } |
| file_offset += (rgp_queue_info->record_count * |
| sizeof(struct sqtt_queue_info_record)); |
| |
| /* Queue event. */ |
| list_for_each_entry_safe(struct rgp_queue_event_record, record, |
| &rgp_queue_event->record, list) { |
| fwrite(record, sizeof(struct sqtt_queue_event_record), 1, output); |
| } |
| file_offset += (rgp_queue_event->record_count * |
| sizeof(struct sqtt_queue_event_record)); |
| } |
| |
| /* SQTT clock calibration. */ |
| if (rgp_clock_calibration->record_count) { |
| uint32_t num_records = 0; |
| |
| list_for_each_entry_safe(struct rgp_clock_calibration_record, record, |
| &rgp_clock_calibration->record, list) { |
| struct sqtt_file_chunk_clock_calibration clock_calibration; |
| memset(&clock_calibration, 0, sizeof(clock_calibration)); |
| |
| ac_sqtt_fill_clock_calibration(&clock_calibration, num_records); |
| |
| clock_calibration.cpu_timestamp = record->cpu_timestamp; |
| clock_calibration.gpu_timestamp = record->gpu_timestamp; |
| |
| fwrite(&clock_calibration, sizeof(struct sqtt_file_chunk_clock_calibration), 1, |
| output); |
| file_offset += sizeof(struct sqtt_file_chunk_clock_calibration); |
| |
| num_records++; |
| } |
| } |
| |
| if (thread_trace) { |
| for (unsigned i = 0; i < thread_trace->num_traces; i++) { |
| const struct ac_thread_trace_se *se = &thread_trace->traces[i]; |
| const struct ac_thread_trace_info *info = &se->info; |
| struct sqtt_file_chunk_sqtt_desc desc = {0}; |
| struct sqtt_file_chunk_sqtt_data data = {0}; |
| uint64_t size = info->cur_offset * 32; /* unit of 32 bytes */ |
| |
| /* SQTT desc chunk. */ |
| ac_sqtt_fill_sqtt_desc(rad_info, &desc, i, se->shader_engine, se->compute_unit); |
| file_offset += sizeof(desc); |
| fwrite(&desc, sizeof(desc), 1, output); |
| |
| /* SQTT data chunk. */ |
| ac_sqtt_fill_sqtt_data(&data, i, file_offset, size); |
| file_offset += sizeof(data); |
| fwrite(&data, sizeof(data), 1, output); |
| |
| /* Copy thread trace data generated by the hardware. */ |
| file_offset += size; |
| fwrite(se->data_ptr, size, 1, output); |
| } |
| } |
| |
| if (spm_trace) { |
| ac_sqtt_dump_spm(spm_trace, file_offset, output); |
| } |
| } |
| #endif |
| |
| int ac_dump_rgp_capture(struct radeon_info *info, |
| struct ac_thread_trace *thread_trace, |
| const struct ac_spm_trace_data *spm_trace) |
| { |
| #if !defined(USE_LIBELF) |
| return -1; |
| #else |
| char filename[2048]; |
| struct tm now; |
| time_t t; |
| FILE *f; |
| |
| t = time(NULL); |
| now = *localtime(&t); |
| |
| snprintf(filename, sizeof(filename), "/tmp/%s_%04d.%02d.%02d_%02d.%02d.%02d.rgp", |
| util_get_process_name(), 1900 + now.tm_year, now.tm_mon + 1, now.tm_mday, now.tm_hour, |
| now.tm_min, now.tm_sec); |
| |
| f = fopen(filename, "w+"); |
| if (!f) |
| return -1; |
| |
| ac_sqtt_dump_data(info, thread_trace, spm_trace, f); |
| |
| fprintf(stderr, "RGP capture saved to '%s'\n", filename); |
| |
| fclose(f); |
| return 0; |
| #endif |
| } |