blob: e249434b9e480a48e7420a51653eb896f7f6887e [file] [log] [blame]
// Copyright 2017 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include <memory>
#include <string>
#include "instruction_decoder.h"
#include "msd_intel_device.h"
#include "registers.h"
namespace {
constexpr uint8_t to_uint8(uint32_t val) {
DASSERT((val & ~0xFF) == 0);
return val & 0xFF;
}
} // namespace
void MsdIntelDevice::Dump(DumpState* dump_out) {
dump_out->render_cs.sequence_number =
render_engine_cs()->hardware_status_page()->read_sequence_number();
dump_out->render_cs.active_head_pointer = render_engine_cs_->GetActiveHeadPointer();
dump_out->render_cs.inflight_batches = render_engine_cs_->GetInflightBatches();
dump_out->video_cs.sequence_number =
video_command_streamer()->hardware_status_page()->read_sequence_number();
dump_out->video_cs.active_head_pointer = video_command_streamer_->GetActiveHeadPointer();
DumpFault(
dump_out,
registers::AllEngineFault::GetAddr(device_id_).ReadFrom(register_io_.get()).reg_value());
dump_out->fault_gpu_address = kInvalidGpuAddr;
dump_out->global = false;
if (dump_out->fault_present)
DumpFaultAddress(dump_out, register_io_.get());
}
// static
void MsdIntelDevice::DumpFault(DumpState* dump_out, uint32_t fault) {
constexpr uint32_t kFakeAddr = 0; // can't read/write registers in this static method
auto fault_reg = hwreg::RegisterAddr<registers::AllEngineFault>(kFakeAddr).FromValue(fault);
dump_out->fault_present = fault_reg.valid();
dump_out->fault_engine = to_uint8(fault_reg.engine());
dump_out->fault_src = to_uint8(fault_reg.src());
dump_out->fault_type = to_uint8(fault_reg.type());
}
// static
void MsdIntelDevice::DumpFaultAddress(DumpState* dump_out, MsdIntelRegisterIo* register_io) {
uint64_t val = registers::FaultTlbReadData::read(register_io);
dump_out->fault_gpu_address = registers::FaultTlbReadData::addr(val);
dump_out->global = registers::FaultTlbReadData::is_ggtt(val);
}
void MsdIntelDevice::DumpToString(std::vector<std::string>& dump_out) {
DumpState dump_state;
Dump(&dump_state);
FormatDump(dump_state, dump_out);
}
void MsdIntelDevice::FormatDump(DumpState& dump_state, std::vector<std::string>& dump_out) {
dump_out.clear();
const char* build = magma::kDebug ? "DEBUG" : "RELEASE";
const char* fmt =
"---- GPU dump begin ----\n"
"%s build\n"
"Device id: 0x%x Revision: 0x%x\n"
"RENDER_COMMAND_STREAMER "
"sequence_number 0x%x "
"active head pointer: 0x%llx\n"
"VIDEO_COMMAND_STREAMER "
"sequence_number 0x%x "
"active head pointer: 0x%llx";
int size =
std::snprintf(nullptr, 0, fmt, build, device_id(), revision(),
dump_state.render_cs.sequence_number, dump_state.render_cs.active_head_pointer,
dump_state.video_cs.sequence_number, dump_state.video_cs.active_head_pointer);
std::vector<char> buf(size + 1);
std::snprintf(&buf[0], buf.size(), fmt, build, device_id(), revision(),
dump_state.render_cs.sequence_number, dump_state.render_cs.active_head_pointer,
dump_state.video_cs.sequence_number, dump_state.video_cs.active_head_pointer);
dump_out.push_back(&buf[0]);
if (dump_state.fault_present) {
fmt =
"ENGINE FAULT DETECTED\n"
"engine 0x%x src 0x%x type 0x%x gpu_address 0x%lx global %d";
size = std::snprintf(nullptr, 0, fmt, dump_state.fault_engine, dump_state.fault_src,
dump_state.fault_type, dump_state.fault_gpu_address, dump_state.global);
std::vector<char> buf(size + 1);
std::snprintf(&buf[0], buf.size(), fmt, dump_state.fault_engine, dump_state.fault_src,
dump_state.fault_type, dump_state.fault_gpu_address, dump_state.global);
dump_out.push_back(&buf[0]);
} else {
dump_out.push_back("No engine faults detected.");
}
bool is_mapped = false;
std::vector<GpuMappingView*> mappings;
GpuMappingView* fault_mapping;
GpuMappingView* closest_mapping;
const GpuMappingView* faulted_batch_mapping = nullptr;
uint64_t closest_mapping_distance = UINT64_MAX;
if (!dump_state.render_cs.inflight_batches.empty()) {
dump_out.push_back("Inflight Batches:");
for (auto batch : dump_state.render_cs.inflight_batches) {
fmt = " Batch %p, context %p, connection client_id %lu";
auto context = batch->GetContext().lock().get();
auto connection = context ? context->connection().lock() : nullptr;
size =
std::snprintf(nullptr, 0, fmt, batch, context, connection ? connection->client_id() : 0u);
std::vector<char> buf(size + 1);
std::snprintf(&buf[0], buf.size(), fmt, batch, context,
connection ? connection->client_id() : 0u);
dump_out.push_back(&buf[0]);
auto batch_mapping = batch->GetBatchMapping();
if (!batch_mapping)
continue;
if (dump_state.render_cs.active_head_pointer >= batch_mapping->gpu_addr() &&
dump_state.render_cs.active_head_pointer <
batch_mapping->gpu_addr() + batch_mapping->length()) {
dump_out.push_back(" FAULTING BATCH (active head ptr within this batch)");
faulted_batch_mapping = batch_mapping;
}
if (batch->GetType() != MappedBatch::BatchType::COMMAND_BUFFER)
continue;
auto cmd_buf = static_cast<CommandBuffer*>(batch);
cmd_buf->GetMappings(&mappings);
for (const auto& mapping : mappings) {
fmt =
" Mapping %p, buffer 0x%lx, gpu addr range [0x%lx, 0x%lx), "
"offset 0x%lx, mapping length 0x%lx";
gpu_addr_t mapping_start = mapping->gpu_addr();
gpu_addr_t mapping_end = mapping->gpu_addr() + mapping->length();
size = std::snprintf(nullptr, 0, fmt, mapping, mapping->BufferId(), mapping_start,
mapping_end, mapping->offset(), mapping->length());
std::vector<char> buf(size + 1);
std::snprintf(&buf[0], buf.size(), fmt, mapping, mapping->BufferId(), mapping_start,
mapping_end, mapping->offset(), mapping->length());
dump_out.push_back(&buf[0]);
if (dump_state.fault_gpu_address >= mapping_start &&
dump_state.fault_gpu_address < mapping_end) {
is_mapped = true;
fault_mapping = mapping;
} else if (dump_state.fault_gpu_address > mapping_end &&
dump_state.fault_gpu_address - mapping_end < closest_mapping_distance) {
closest_mapping_distance = dump_state.fault_gpu_address - mapping_end;
closest_mapping = mapping;
}
}
}
}
if (is_mapped) {
fmt = "Fault address appears to be within mapping %p addr [0x%lx, 0x%lx)";
size = std::snprintf(nullptr, 0, fmt, fault_mapping, fault_mapping->gpu_addr(),
fault_mapping->gpu_addr() + fault_mapping->length());
std::vector<char> buf(size + 1);
std::snprintf(&buf[0], buf.size(), fmt, fault_mapping, fault_mapping->gpu_addr(),
fault_mapping->gpu_addr() + fault_mapping->length());
dump_out.push_back(&buf[0]);
} else {
dump_out.push_back("Fault address does not appear to be mapped for any outstanding batch");
if (closest_mapping_distance < UINT64_MAX) {
fmt =
"Fault address is 0x%lx past the end of mapping %p addr [0x%08lx, 0x%08lx), size "
"0x%lx, buffer size 0x%lx";
size = std::snprintf(nullptr, 0, fmt, closest_mapping_distance, closest_mapping,
closest_mapping->gpu_addr(),
closest_mapping->gpu_addr() + closest_mapping->length(),
closest_mapping->length(), closest_mapping->BufferSize());
std::vector<char> buf(size + 1);
std::snprintf(&buf[0], buf.size(), fmt, closest_mapping_distance, closest_mapping,
closest_mapping->gpu_addr(),
closest_mapping->gpu_addr() + closest_mapping->length(),
closest_mapping->length(), closest_mapping->BufferSize());
dump_out.push_back(&buf[0]);
}
}
if (faulted_batch_mapping) {
dump_out.push_back("Batch instructions immediately surrounding the active head:");
std::vector<uint32_t> batch_data;
// dont early out because we always want to print the "dump end" line
if (faulted_batch_mapping->Copy(&batch_data)) {
uint64_t active_head_offset =
dump_state.render_cs.active_head_pointer - faulted_batch_mapping->gpu_addr();
DASSERT(active_head_offset <= faulted_batch_mapping->length());
DASSERT(active_head_offset % sizeof(uint32_t) == 0);
uint64_t total_dwords = faulted_batch_mapping->length() / sizeof(uint32_t);
uint64_t active_head_dword = active_head_offset / sizeof(uint32_t);
uint64_t start_dword = faulted_batch_mapping->offset();
uint64_t end_dword = total_dwords - 1;
uint32_t dwords_remaining = 0;
bool end_of_batch = false;
for (uint64_t i = start_dword; i < end_dword; i++) {
if (dwords_remaining == 0) {
InstructionDecoder::Id id;
bool decoded = InstructionDecoder::Decode(batch_data[i], &id, &dwords_remaining);
if (decoded) {
fmt = "%s: ";
size = std::snprintf(nullptr, 0, fmt, InstructionDecoder::name(id));
buf = std::vector<char>(size + 1);
std::snprintf(&buf[0], buf.size(), fmt, InstructionDecoder::name(id));
dump_out.push_back(&buf[0]);
end_of_batch = id == InstructionDecoder::Id::MI_BATCH_BUFFER_END;
}
}
const char* prefix = "";
const char* suffix = ",";
if (i == active_head_dword) {
prefix = "===>";
suffix = "<===,";
}
if (dwords_remaining)
--dwords_remaining;
fmt = "%s0x%08lx%s";
size = std::snprintf(nullptr, 0, fmt, prefix, batch_data[i], suffix);
std::vector<char> buf(size + 1);
std::snprintf(&buf[0], buf.size(), fmt, prefix, batch_data[i], suffix);
dump_out.push_back(&buf[0]);
if (end_of_batch)
break;
}
} else {
dump_out.push_back("Failed to map batch data");
}
}
dump_out.push_back("---- GPU dump end ----");
}