summaryrefslogtreecommitdiffstats
path: root/content/gpu/gpu_info_collector_linux.cc
diff options
context:
space:
mode:
Diffstat (limited to 'content/gpu/gpu_info_collector_linux.cc')
-rw-r--r--content/gpu/gpu_info_collector_linux.cc279
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