diff options
Diffstat (limited to 'content/gpu/gpu_info_collector_linux.cc')
-rw-r--r-- | content/gpu/gpu_info_collector_linux.cc | 279 |
1 files changed, 279 insertions, 0 deletions
diff --git a/content/gpu/gpu_info_collector_linux.cc b/content/gpu/gpu_info_collector_linux.cc new file mode 100644 index 0000000..08b1f9c --- /dev/null +++ b/content/gpu/gpu_info_collector_linux.cc @@ -0,0 +1,279 @@ +// Copyright (c) 2011 The Chromium 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 "content/gpu/gpu_info_collector.h" + +#include <dlfcn.h> +#include <vector> + +#include "app/gfx/gl/gl_bindings.h" +#include "app/gfx/gl/gl_context.h" +#include "app/gfx/gl/gl_implementation.h" +#include "base/file_util.h" +#include "base/logging.h" +#include "base/scoped_ptr.h" +#include "base/string_piece.h" +#include "base/string_split.h" +#include "base/string_util.h" + +namespace { + +// PciDevice and PciAccess are defined to access libpci functions. Their +// members match the corresponding structures defined by libpci in size up to +// fields we may access. For those members we don't use, their names are +// defined as "fieldX", etc., or, left out if they are declared after the +// members we care about in libpci. + +struct PciDevice { + PciDevice* next; + + uint16 field0; + uint8 field1; + uint8 field2; + uint8 field3; + int field4; + + uint16 vendor_id; + uint16 device_id; + uint16 device_class; +}; + +struct PciAccess { + unsigned int field0; + int field1; + int field2; + char* field3; + int field4; + int field5; + unsigned int field6; + int field7; + + void (*function0)(); + void (*function1)(); + void (*function2)(); + + PciDevice* device_list; +}; + +// Define function types. +typedef PciAccess* (*FT_pci_alloc)(); +typedef void (*FT_pci_init)(PciAccess*); +typedef void (*FT_pci_cleanup)(PciAccess*); +typedef void (*FT_pci_scan_bus)(PciAccess*); +typedef void (*FT_pci_scan_bus)(PciAccess*); +typedef int (*FT_pci_fill_info)(PciDevice*, int); +typedef char* (*FT_pci_lookup_name)(PciAccess*, char*, int, int, ...); + +// This includes dynamically linked library handle and functions pointers from +// libpci. +struct PciInterface { + void* lib_handle; + + FT_pci_alloc pci_alloc; + FT_pci_init pci_init; + FT_pci_cleanup pci_cleanup; + FT_pci_scan_bus pci_scan_bus; + FT_pci_fill_info pci_fill_info; + FT_pci_lookup_name pci_lookup_name; +}; + +// This checks if a system supports PCI bus. +// We check the existence of /sys/bus/pci or /sys/bug/pci_express. +bool IsPciSupported() { + const FilePath pci_path("/sys/bus/pci/"); + const FilePath pcie_path("/sys/bus/pci_express/"); + return (file_util::PathExists(pci_path) || + file_util::PathExists(pcie_path)); +} + +// This dynamically opens libpci and get function pointers we need. Return +// NULL if library fails to open or any functions can not be located. +// Returned interface (if not NULL) should be deleted in FinalizeLibPci. +PciInterface* InitializeLibPci(const char* lib_name) { + void* handle = dlopen(lib_name, RTLD_LAZY); + if (handle == NULL) { + LOG(INFO) << "Failed to dlopen " << lib_name; + return NULL; + } + PciInterface* interface = new struct PciInterface; + interface->lib_handle = handle; + interface->pci_alloc = reinterpret_cast<FT_pci_alloc>( + dlsym(handle, "pci_alloc")); + interface->pci_init = reinterpret_cast<FT_pci_init>( + dlsym(handle, "pci_init")); + interface->pci_cleanup = reinterpret_cast<FT_pci_cleanup>( + dlsym(handle, "pci_cleanup")); + interface->pci_scan_bus = reinterpret_cast<FT_pci_scan_bus>( + dlsym(handle, "pci_scan_bus")); + interface->pci_fill_info = reinterpret_cast<FT_pci_fill_info>( + dlsym(handle, "pci_fill_info")); + interface->pci_lookup_name = reinterpret_cast<FT_pci_lookup_name>( + dlsym(handle, "pci_lookup_name")); + if (interface->pci_alloc == NULL || + interface->pci_init == NULL || + interface->pci_cleanup == NULL || + interface->pci_scan_bus == NULL || + interface->pci_fill_info == NULL || + interface->pci_lookup_name == NULL) { + LOG(ERROR) << "Missing required function(s) from " << lib_name; + dlclose(handle); + delete interface; + return NULL; + } + return interface; +} + +// This close the dynamically opened libpci and delete the interface. +void FinalizeLibPci(PciInterface** interface) { + DCHECK(interface && *interface && (*interface)->lib_handle); + dlclose((*interface)->lib_handle); + delete (*interface); + *interface = NULL; +} + +} // namespace anonymous + +namespace gpu_info_collector { + +bool CollectGraphicsInfo(GPUInfo* gpu_info) { + DCHECK(gpu_info); + + // TODO(zmo): need to consider the case where we are running on top of + // desktop GL and GL_ARB_robustness extension is available. + gpu_info->can_lose_context = + (gfx::GetGLImplementation() == gfx::kGLImplementationEGLGLES2); + gpu_info->level = GPUInfo::kComplete; + return CollectGraphicsInfoGL(gpu_info); +} + +bool CollectPreliminaryGraphicsInfo(GPUInfo* gpu_info) { + DCHECK(gpu_info); + + gpu_info->level = GPUInfo::kPartial; + + bool rt = true; + if (!CollectVideoCardInfo(gpu_info)) + rt = false; + + // TODO(zmo): if vendor is ATI, consider passing /etc/ati/amdpcsdb.default + // for driver information. + + return rt; +} + +bool CollectVideoCardInfo(GPUInfo* gpu_info) { + DCHECK(gpu_info); + + if (IsPciSupported() == false) { + LOG(INFO) << "PCI bus scanning is not supported"; + return false; + } + + // TODO(zmo): be more flexible about library name. + PciInterface* interface = InitializeLibPci("libpci.so.3"); + if (interface == NULL) + interface = InitializeLibPci("libpci.so"); + if (interface == NULL) { + LOG(ERROR) << "Failed to locate libpci"; + return false; + } + + PciAccess* access = (interface->pci_alloc)(); + DCHECK(access != NULL); + (interface->pci_init)(access); + (interface->pci_scan_bus)(access); + std::vector<PciDevice*> gpu_list; + PciDevice* gpu_active = NULL; + for (PciDevice* device = access->device_list; + device != NULL; device = device->next) { + (interface->pci_fill_info)(device, 33); // Fill the IDs and class fields. + // TODO(zmo): there might be other classes that qualify as display devices. + if (device->device_class == 0x0300) { // Device class is DISPLAY_VGA. + gpu_list.push_back(device); + } + } + if (gpu_list.size() == 1) { + gpu_active = gpu_list[0]; + } else { + // If more than one graphics card are identified, find the one that matches + // gl VENDOR and RENDERER info. + std::string gl_vendor_string = gpu_info->gl_vendor; + std::string gl_renderer_string = gpu_info->gl_renderer; + const int buffer_size = 255; + scoped_array<char> buffer(new char[buffer_size]); + std::vector<PciDevice*> candidates; + for (size_t i = 0; i < gpu_list.size(); ++i) { + PciDevice* gpu = gpu_list[i]; + // The current implementation of pci_lookup_name returns the same pointer + // as the passed in upon success, and a different one (NULL or a pointer + // to an error message) upon failure. + if ((interface->pci_lookup_name)(access, + buffer.get(), + buffer_size, + 1, + gpu->vendor_id) != buffer.get()) + continue; + std::string vendor_string = buffer.get(); + const bool kCaseSensitive = false; + if (!StartsWithASCII(gl_vendor_string, vendor_string, kCaseSensitive)) + continue; + if ((interface->pci_lookup_name)(access, + buffer.get(), + buffer_size, + 2, + gpu->vendor_id, + gpu->device_id) != buffer.get()) + continue; + std::string device_string = buffer.get(); + size_t begin = device_string.find_first_of('['); + size_t end = device_string.find_last_of(']'); + if (begin != std::string::npos && end != std::string::npos && + begin < end) { + device_string = device_string.substr(begin + 1, end - begin - 1); + } + if (StartsWithASCII(gl_renderer_string, device_string, kCaseSensitive)) { + gpu_active = gpu; + break; + } + // If a device's vendor matches gl VENDOR string, we want to consider the + // possibility that libpci may not return the exact same name as gl + // RENDERER string. + candidates.push_back(gpu); + } + if (gpu_active == NULL && candidates.size() == 1) + gpu_active = candidates[0]; + } + if (gpu_active != NULL) { + gpu_info->vendor_id = gpu_active->vendor_id; + gpu_info->device_id = gpu_active->device_id; + } + (interface->pci_cleanup)(access); + FinalizeLibPci(&interface); + return (gpu_active != NULL); +} + +bool CollectDriverInfoGL(GPUInfo* gpu_info) { + DCHECK(gpu_info); + + std::string gl_version_string = gpu_info->gl_version_string; + std::vector<std::string> pieces; + base::SplitStringAlongWhitespace(gl_version_string, &pieces); + // In linux, the gl version string might be in the format of + // GLVersion DriverVendor DriverVersion + if (pieces.size() < 3) + return false; + + std::string driver_version = pieces[2]; + size_t pos = driver_version.find_first_not_of("0123456789."); + if (pos == 0) + return false; + if (pos != std::string::npos) + driver_version = driver_version.substr(0, pos); + + gpu_info->driver_vendor = pieces[1]; + gpu_info->driver_version = driver_version; + return true; +} + +} // namespace gpu_info_collector |