#include "gpu/command_buffer/service/command_buffer_service.h"
#include <stddef.h>
#include <stdint.h>
#include <limits>
#include <memory>
#include "base/logging.h"
#include "base/memory/page_size.h"
#include "base/trace_event/memory_dump_manager.h"
#include "base/trace_event/memory_dump_provider.h"
#include "base/trace_event/memory_dump_request_args.h"
#include "base/trace_event/trace_event.h"
#include "build/build_config.h"
#include "gpu/command_buffer/common/cmd_buffer_common.h"
#include "gpu/command_buffer/common/command_buffer_shared.h"
#include "gpu/command_buffer/service/transfer_buffer_manager.h"
#include "gpu/config/gpu_finch_features.h"
#if BUILDFLAG(IS_MAC)
#include <mach/mach_vm.h>
#include <mach/vm_purgable.h>
#include <mach/vm_statistics.h>
#include "base/no_destructor.h"
#include "base/process/process_metrics.h"
#include "base/trace_event/process_memory_dump.h"
#endif
namespace gpu {
#if BUILDFLAG(IS_MAC)
namespace {
class AppleGpuMemoryDumpProvider
: public base::trace_event::MemoryDumpProvider {
public:
AppleGpuMemoryDumpProvider();
bool OnMemoryDump(const base::trace_event::MemoryDumpArgs& args,
base::trace_event::ProcessMemoryDump* pmd) override;
private:
~AppleGpuMemoryDumpProvider() override = default;
};
AppleGpuMemoryDumpProvider::AppleGpuMemoryDumpProvider() {
base::trace_event::MemoryDumpManager::GetInstance()->RegisterDumpProvider(
this, "CommandBuffer", nullptr);
}
bool AppleGpuMemoryDumpProvider::OnMemoryDump(
const base::trace_event::MemoryDumpArgs& args,
base::trace_event::ProcessMemoryDump* pmd) {
size_t surface_virtual_size = 0;
size_t surface_resident_size = 0;
size_t surface_swapped_out_size = 0;
size_t surface_dirty_size = 0;
size_t surface_nonpurgeable_size = 0;
size_t surface_purgeable_size = 0;
size_t accelerator_virtual_size = 0;
size_t accelerator_resident_size = 0;
size_t accelerator_swapped_out_size = 0;
size_t accelerator_dirty_size = 0;
size_t accelerator_nonpurgeable_size = 0;
size_t accelerator_purgeable_size = 0;
task_t task = mach_task_self();
mach_vm_address_t address = 0;
mach_vm_size_t size = 0;
while (true) {
address += size;
vm_region_basic_info_64 basic_info;
base::MachVMRegionResult result =
base::GetBasicInfo(task, &size, &address, &basic_info);
if (result == base::MachVMRegionResult::Finished) {
break;
} else if (result == base::MachVMRegionResult::Error) {
return false;
}
const vm_prot_t rw = VM_PROT_READ | VM_PROT_WRITE;
if (basic_info.protection != rw || basic_info.max_protection != rw)
continue;
vm_region_extended_info_data_t info;
mach_port_t object_name;
mach_msg_type_number_t count;
count = VM_REGION_EXTENDED_INFO_COUNT;
kern_return_t ret = mach_vm_region(
task, &address, &size, VM_REGION_EXTENDED_INFO,
reinterpret_cast<vm_region_info_t>(&info), &count, &object_name);
if (ret == KERN_INVALID_ADDRESS)
break;
if (ret != KERN_SUCCESS)
return false;
if (info.user_tag != VM_MEMORY_IOSURFACE &&
info.user_tag != VM_MEMORY_IOACCELERATOR) {
continue;
}
int purgeable_state = 0;
ret = mach_vm_purgable_control(task, address, VM_PURGABLE_GET_STATE,
&purgeable_state);
purgeable_state = purgeable_state & VM_PURGABLE_STATE_MASK;
switch (info.user_tag) {
case VM_MEMORY_IOSURFACE:
surface_virtual_size += size;
surface_resident_size += info.pages_resident * base::GetPageSize();
surface_swapped_out_size +=
info.pages_swapped_out * base::GetPageSize();
surface_dirty_size += info.pages_dirtied * base::GetPageSize();
if (purgeable_state == VM_PURGABLE_VOLATILE ||
purgeable_state == VM_PURGABLE_EMPTY) {
surface_purgeable_size += size;
} else {
surface_nonpurgeable_size += size;
}
break;
case VM_MEMORY_IOACCELERATOR:
accelerator_virtual_size += size;
accelerator_resident_size += info.pages_resident * base::GetPageSize();
accelerator_swapped_out_size +=
info.pages_swapped_out * base::GetPageSize();
accelerator_dirty_size += info.pages_dirtied * base::GetPageSize();
if (purgeable_state == VM_PURGABLE_VOLATILE ||
purgeable_state == VM_PURGABLE_EMPTY) {
accelerator_purgeable_size += size;
} else {
accelerator_nonpurgeable_size += size;
}
break;
}
}
auto* dump = pmd->CreateAllocatorDump("iosurface");
dump->AddScalar("virtual_size", "bytes", surface_virtual_size);
dump->AddScalar("resident_size", "bytes", surface_resident_size);
dump->AddScalar("swapped_out_size", "bytes", surface_swapped_out_size);
dump->AddScalar("dirty_size", "bytes", surface_dirty_size);
dump->AddScalar("size", "bytes", surface_virtual_size);
dump->AddScalar("resident_swapped", "bytes",
surface_resident_size + surface_swapped_out_size);
dump->AddScalar("nonpurgeable_size", "bytes", surface_nonpurgeable_size);
dump->AddScalar("purgeable_size", "bytes", surface_purgeable_size);
dump = pmd->CreateAllocatorDump("ioaccelerator");
dump->AddScalar("virtual_size", "bytes", accelerator_virtual_size);
dump->AddScalar("resident_size", "bytes", accelerator_resident_size);
dump->AddScalar("swapped_out_size", "bytes", accelerator_swapped_out_size);
dump->AddScalar("dirty_size", "bytes", accelerator_dirty_size);
dump->AddScalar("size", "bytes", accelerator_virtual_size);
dump->AddScalar("resident_swapped", "bytes",
accelerator_resident_size + accelerator_swapped_out_size);
dump->AddScalar("nonpurgeable_size", "bytes", accelerator_nonpurgeable_size);
dump->AddScalar("purgeable_size", "bytes", accelerator_purgeable_size);
return true;
}
}
#endif
int GetCommandBufferSliceSize() { … }
CommandBufferService::CommandBufferService(CommandBufferServiceClient* client,
MemoryTracker* memory_tracker)
: … { … }
CommandBufferService::~CommandBufferService() = default;
void CommandBufferService::UpdateState() { … }
void CommandBufferService::Flush(int32_t put_offset,
AsyncAPIInterface* handler) { … }
void CommandBufferService::SetGetBuffer(int32_t transfer_buffer_id) { … }
void CommandBufferService::SetSharedStateBuffer(
std::unique_ptr<BufferBacking> shared_state_buffer) { … }
CommandBuffer::State CommandBufferService::GetState() { … }
void CommandBufferService::SetReleaseCount(uint64_t release_count) { … }
scoped_refptr<Buffer> CommandBufferService::CreateTransferBuffer(
uint32_t size,
int32_t* id,
uint32_t alignment) { … }
void CommandBufferService::DestroyTransferBuffer(int32_t id) { … }
scoped_refptr<Buffer> CommandBufferService::GetTransferBuffer(int32_t id) { … }
bool CommandBufferService::RegisterTransferBuffer(
int32_t id,
scoped_refptr<Buffer> buffer) { … }
scoped_refptr<Buffer> CommandBufferService::CreateTransferBufferWithId(
uint32_t size,
int32_t id,
uint32_t alignment) { … }
void CommandBufferService::SetToken(int32_t token) { … }
void CommandBufferService::SetParseError(error::Error error) { … }
void CommandBufferService::SetContextLostReason(
error::ContextLostReason reason) { … }
bool CommandBufferService::ShouldYield() { … }
void CommandBufferService::SetScheduled(bool scheduled) { … }
size_t CommandBufferService::GetSharedMemoryBytesAllocated() const { … }
}