| /* Copyright (c) 2026 Valve Corporation |
| * Copyright (c) 2026 LunarG, Inc. |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| #include "gpu_dump.h" |
| #include <cstdint> |
| #include "chassis/layer_object_id.h" |
| #include "generated/dispatch_functions.h" |
| #include "state_tracker/buffer_state.h" |
| #include "state_tracker/device_memory_state.h" |
| |
| namespace gpudump { |
| |
| GpuDump::GpuDump(vvl::DispatchDevice* dev, gpudump::Instance* instance_vo) |
| : vvl::DeviceProxy(dev, instance_vo, LayerObjectTypeGpuDump) {} |
| |
| GpuDump::~GpuDump() {} |
| |
| std::vector<uint8_t> GpuDump::CopyDataFromMemory(VkDeviceAddress memory_addresss, VkDeviceSize copy_size) { |
| std::vector<uint8_t> result; |
| vvl::span<vvl::Buffer* const> buffer_list = device_state->GetBuffersByAddress(memory_addresss); |
| if (buffer_list.empty()) { |
| return result; |
| } |
| |
| auto buffer_state = *buffer_list.begin(); |
| const vvl::DeviceMemory& memory_state = *buffer_state->MemoryState(); |
| |
| // Prevent copying OOB of a buffer |
| if ((memory_addresss + copy_size) > buffer_state->DeviceAddressRange().end) { |
| return result; |
| } |
| |
| VkDeviceSize offset = memory_addresss - buffer_state->DeviceAddressRange().begin; |
| if (memory_state.mappable) { |
| uint8_t* data_ptr = static_cast<uint8_t*>(memory_state.p_driver_data); |
| |
| if (!memory_state.p_driver_data) { |
| // Just use WHOLE_SIZE to avoid issues with partial mappings |
| // Example: |
| // The |memory_address| is 0x1001 and |copy_size| is 4, the driver (or at least TestICD) will return back something |
| // aligned to a value like 64, so if the buffer is only 64 bytes, you will now be accessing data over it |
| DispatchMapMemory(device, memory_state.VkHandle(), offset, VK_WHOLE_SIZE, 0, (void**)&data_ptr); |
| |
| // Skip checking if coherent memory or not |
| VkMappedMemoryRange memory_range = vku::InitStructHelper(); |
| memory_range.memory = memory_state.VkHandle(); |
| memory_range.offset = offset; |
| memory_range.size = copy_size; |
| DispatchInvalidateMappedMemoryRanges(device, 1, &memory_range); |
| } |
| |
| data_ptr += offset; |
| result.resize(static_cast<uint32_t>(copy_size)); |
| memcpy(result.data(), data_ptr, static_cast<uint32_t>(copy_size)); |
| |
| if (!memory_state.p_driver_data) { |
| DispatchUnmapMemory(device, memory_state.VkHandle()); |
| } |
| } else { |
| // TODO - Handle non-host visible memory |
| // When we add, we need to guard against if trying to read non-aligned data |
| // (which should have a warning already) |
| } |
| |
| return result; |
| } |
| |
| // return for warning if no buffer found |
| bool GpuDump::ListBuffers(std::ostringstream& ss, VkDeviceAddress address, uint32_t indents, bool new_line_start) { |
| if (new_line_start) { |
| ss << "\n"; |
| } |
| |
| auto buffer_states = GetBuffersByAddress(address); |
| for (uint32_t i = 0; i < indents; i++) { |
| ss << " "; |
| } |
| |
| for (uint32_t i = 0; i < buffer_states.size(); i++) { |
| if (i != 0) { |
| ss << '\n'; |
| for (uint32_t j = 0; j < indents; j++) { |
| ss << " "; |
| } |
| } |
| auto& buffer_state = buffer_states[i]; |
| ss << "- " << buffer_state->Describe(*this); |
| } |
| |
| if (buffer_states.empty()) { |
| ss << "- [WARNING] No VkBuffer found at 0x" << std::hex << address; |
| } |
| |
| if (!new_line_start) { |
| ss << "\n"; |
| } |
| |
| return buffer_states.empty(); |
| } |
| |
| } // namespace gpudump |