summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--DEPS3
-rw-r--r--build/linux/system.gyp66
-rw-r--r--content/content_gpu.gypi21
-rw-r--r--content/gpu/gpu_info_collector_linux.cc132
-rwxr-xr-xtools/generate_library_loader/generate_library_loader.py248
5 files changed, 339 insertions, 131 deletions
diff --git a/DEPS b/DEPS
index aba2c7f..ac73fe5 100644
--- a/DEPS
+++ b/DEPS
@@ -560,6 +560,9 @@ include_rules = [
"+googleurl",
"+ipc",
+ # Everybody can use headers generated by tools/generate_library_loader.
+ "+library_loaders",
+
# For now, we allow ICU to be included by specifying "unicode/...", although
# this should probably change.
"+unicode",
diff --git a/build/linux/system.gyp b/build/linux/system.gyp
index 2eacbdb..23f2689 100644
--- a/build/linux/system.gyp
+++ b/build/linux/system.gyp
@@ -11,6 +11,8 @@
'pkg-config': 'pkg-config'
}]
],
+
+ 'linux_link_libpci%': 0,
},
'conditions': [
[ 'os_posix==1 and OS!="mac"', {
@@ -275,6 +277,70 @@
],
},
{
+ 'target_name': 'libpci',
+ 'type': 'static_library',
+ 'cflags': [
+ '<!@(<(pkg-config) --cflags libpci)',
+ ],
+ 'include_dirs': [
+ '../..',
+ ],
+ 'direct_dependent_settings': {
+ 'include_dirs': [
+ '<(SHARED_INTERMEDIATE_DIR)',
+ ],
+ 'conditions': [
+ ['linux_link_libpci==1', {
+ 'link_settings': {
+ 'ldflags': [
+ '<!@(<(pkg-config) --libs-only-L --libs-only-other libpci)',
+ ],
+ 'libraries': [
+ '<!@(<(pkg-config) --libs-only-l libpci)',
+ ],
+ }
+ }],
+ ],
+ },
+ 'hard_dependency': 1,
+ 'actions': [
+ {
+ 'variables': {
+ 'output_h': '<(SHARED_INTERMEDIATE_DIR)/library_loaders/libpci.h',
+ 'output_cc': '<(INTERMEDIATE_DIR)/libpci_loader.cc',
+ 'generator': '../../tools/generate_library_loader/generate_library_loader.py',
+ },
+ 'action_name': 'generate_libpci_loader',
+ 'inputs': [
+ '<(generator)',
+ ],
+ 'outputs': [
+ '<(output_h)',
+ '<(output_cc)',
+ ],
+ 'action': ['python',
+ '<(generator)',
+ '--name', 'LibPciLoader',
+ '--output-h', '<(output_h)',
+ '--output-cc', '<(output_cc)',
+ '--header', '<pci/pci.h>',
+ # TODO(phajdan.jr): Report problem to pciutils project
+ # and get it fixed so that we don't need --use-extern-c.
+ '--use-extern-c',
+ '--link-directly=<(linux_link_libpci)',
+ 'pci_alloc',
+ 'pci_init',
+ 'pci_cleanup',
+ 'pci_scan_bus',
+ 'pci_fill_info',
+ 'pci_lookup_name',
+ ],
+ 'message': 'Generating libpci library loader.',
+ 'process_outputs_as_sources': 1,
+ },
+ ],
+ },
+ {
'target_name': 'x11',
'type': 'none',
'toolsets': ['host', 'target'],
diff --git a/content/content_gpu.gypi b/content/content_gpu.gypi
index 6b05cde..66cb3c6 100644
--- a/content/content_gpu.gypi
+++ b/content/content_gpu.gypi
@@ -3,9 +3,6 @@
# found in the LICENSE file.
{
- 'variables': {
- 'linux_link_libpci%': 0,
- },
'dependencies': [
'../base/base.gyp:base',
'../skia/skia.gyp:skia',
@@ -100,6 +97,7 @@
}],
['OS=="linux"', {
'dependencies': [
+ '../build/linux/system.gyp:libpci',
'../third_party/libXNVCtrl/libXNVCtrl.gyp:libXNVCtrl',
],
}],
@@ -113,22 +111,5 @@
'<(DEPTH)/third_party/libva',
],
}],
- ['linux_link_libpci==0', {
- 'defines': [
- 'DLOPEN_LIBPCI',
- ],
- }, { # linux_link_libpci==1
- 'cflags': [
- '<!@(pkg-config --cflags libpci)',
- ],
- 'link_settings': {
- 'ldflags': [
- '<!@(pkg-config --libs-only-L --libs-only-other libpci)',
- ],
- 'libraries': [
- '<!@(pkg-config --libs-only-l libpci)',
- ],
- }
- }],
],
}
diff --git a/content/gpu/gpu_info_collector_linux.cc b/content/gpu/gpu_info_collector_linux.cc
index c6f2c2b..d849cab 100644
--- a/content/gpu/gpu_info_collector_linux.cc
+++ b/content/gpu/gpu_info_collector_linux.cc
@@ -4,15 +4,9 @@
#include "content/gpu/gpu_info_collector.h"
-#include <dlfcn.h>
#include <X11/Xlib.h>
#include <vector>
-// TODO(phajdan.jr): Report problem upstream and make pci.h handle this.
-extern "C" {
-#include <pci/pci.h>
-}
-
#include "base/command_line.h"
#include "base/debug/trace_event.h"
#include "base/file_util.h"
@@ -23,6 +17,7 @@ extern "C" {
#include "base/string_split.h"
#include "base/string_tokenizer.h"
#include "base/string_util.h"
+#include "library_loaders/libpci.h"
#include "third_party/libXNVCtrl/NVCtrl.h"
#include "third_party/libXNVCtrl/NVCtrlLib.h"
#include "ui/gl/gl_bindings.h"
@@ -33,28 +28,6 @@ extern "C" {
namespace {
-// Define function types.
-typedef pci_access* (*FT_pci_alloc)();
-typedef void (*FT_pci_init)(pci_access*);
-typedef void (*FT_pci_cleanup)(pci_access*);
-typedef void (*FT_pci_scan_bus)(pci_access*);
-typedef void (*FT_pci_scan_bus)(pci_access*);
-typedef int (*FT_pci_fill_info)(pci_dev*, int);
-typedef char* (*FT_pci_lookup_name)(pci_access*, 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() {
@@ -64,68 +37,6 @@ bool IsPciSupported() {
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) {
- scoped_ptr<PciInterface> interface(new PciInterface);
-#if defined(DLOPEN_LIBPCI)
- void* handle = dlopen(lib_name, RTLD_LAZY);
- if (handle == NULL) {
- VLOG(1) << "Failed to dlopen " << lib_name;
- return NULL;
- }
- 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) {
- VLOG(1) << "Missing required function(s) from " << lib_name;
- dlclose(handle);
- return NULL;
- }
-#else // !defined(DLOPEN_LIBPCI)
- interface->lib_handle = NULL;
- interface->pci_alloc = reinterpret_cast<FT_pci_alloc>(
- &pci_alloc);
- interface->pci_init = reinterpret_cast<FT_pci_init>(
- &pci_init);
- interface->pci_cleanup = reinterpret_cast<FT_pci_cleanup>(
- &pci_cleanup);
- interface->pci_scan_bus = reinterpret_cast<FT_pci_scan_bus>(
- &pci_scan_bus);
- interface->pci_fill_info = reinterpret_cast<FT_pci_fill_info>(
- &pci_fill_info);
- interface->pci_lookup_name = reinterpret_cast<FT_pci_lookup_name>(
- &pci_lookup_name);
-#endif // !defined(DLOPEN_LIBPCI)
- return interface.release();
-}
-
-// This close the dynamically opened libpci and delete the interface.
-void FinalizeLibPci(PciInterface** interface) {
-#if defined(DLOPEN_LIBPCI)
- DCHECK(interface && *interface && (*interface)->lib_handle);
- dlclose((*interface)->lib_handle);
-#endif // defined(DLOPEN_LIBPCI)
- delete (*interface);
- *interface = NULL;
-}
-
// Scan /etc/ati/amdpcsdb.default for "ReleaseVersion".
// Return empty string on failing.
std::string CollectDriverVersionATI() {
@@ -263,22 +174,22 @@ bool CollectVideoCardInfo(content::GPUInfo* gpu_info) {
}
// TODO(zmo): be more flexible about library name.
- PciInterface* interface = InitializeLibPci("libpci.so.3");
- if (interface == NULL)
- interface = InitializeLibPci("libpci.so");
- if (interface == NULL) {
+ LibPciLoader libpci_loader;
+ if (!libpci_loader.Load("libpci.so.3") &&
+ !libpci_loader.Load("libpci.so")) {
VLOG(1) << "Failed to locate libpci";
return false;
}
- pci_access* access = (interface->pci_alloc)();
+ pci_access* access = (libpci_loader.pci_alloc)();
DCHECK(access != NULL);
- (interface->pci_init)(access);
- (interface->pci_scan_bus)(access);
+ (libpci_loader.pci_init)(access);
+ (libpci_loader.pci_scan_bus)(access);
bool primary_gpu_identified = false;
for (pci_dev* device = access->devices;
device != NULL; device = device->next) {
- (interface->pci_fill_info)(device, 33); // Fill the IDs and class fields.
+ // Fill the IDs and class fields.
+ (libpci_loader.pci_fill_info)(device, 33);
// TODO(zmo): there might be other classes that qualify as display devices.
if (device->device_class != 0x0300) // Device class is DISPLAY_VGA.
continue;
@@ -292,19 +203,19 @@ bool CollectVideoCardInfo(content::GPUInfo* gpu_info) {
// 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,
- device->vendor_id) == buffer.get()) {
+ if ((libpci_loader.pci_lookup_name)(access,
+ buffer.get(),
+ buffer_size,
+ 1,
+ device->vendor_id) == buffer.get()) {
gpu.vendor_string = buffer.get();
}
- if ((interface->pci_lookup_name)(access,
- buffer.get(),
- buffer_size,
- 2,
- device->vendor_id,
- device->device_id) == buffer.get()) {
+ if ((libpci_loader.pci_lookup_name)(access,
+ buffer.get(),
+ buffer_size,
+ 2,
+ device->vendor_id,
+ device->device_id) == buffer.get()) {
std::string device_string = buffer.get();
size_t begin = device_string.find_first_of('[');
size_t end = device_string.find_last_of(']');
@@ -341,8 +252,7 @@ bool CollectVideoCardInfo(content::GPUInfo* gpu_info) {
gpu_info->amd_switchable = true;
}
- (interface->pci_cleanup)(access);
- FinalizeLibPci(&interface);
+ (libpci_loader.pci_cleanup)(access);
return (primary_gpu_identified);
}
diff --git a/tools/generate_library_loader/generate_library_loader.py b/tools/generate_library_loader/generate_library_loader.py
new file mode 100755
index 0000000..edf0706
--- /dev/null
+++ b/tools/generate_library_loader/generate_library_loader.py
@@ -0,0 +1,248 @@
+#!/usr/bin/env python
+# Copyright (c) 2012 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.
+
+"""
+Creates a library loader (a header and implementation file),
+which is a wrapper for dlopen or direct linking with given library.
+
+The loader makes it possible to have the same client code for both cases,
+and also makes it easier to write code using dlopen (and also provides
+a standard way to do so, and limits the ugliness just to generated files).
+
+For more info refer to http://crbug.com/162733 .
+"""
+
+
+import optparse
+import os.path
+import re
+import sys
+
+
+HEADER_TEMPLATE = """// This is generated file. Do not modify directly.
+// Path to the code generator: %(generator_path)s .
+
+#ifndef %(unique_prefix)s
+#define %(unique_prefix)s
+
+%(wrapped_header_include)s
+
+#include <string>
+
+#include "base/basictypes.h"
+#include "base/compiler_specific.h"
+#if defined(%(unique_prefix)s_DLOPEN)
+#include "base/native_library.h"
+#endif
+
+class %(class_name)s {
+ public:
+ %(class_name)s();
+ ~%(class_name)s();
+
+ bool Load(const std::string& library_name) WARN_UNUSED_RESULT;
+
+%(member_decls)s
+
+ private:
+ void CleanUp(bool unload);
+
+#if defined(%(unique_prefix)s_DLOPEN)
+ base::NativeLibrary library_;
+#endif
+
+ bool loaded_;
+
+ DISALLOW_COPY_AND_ASSIGN(%(class_name)s);
+};
+
+#endif // %(unique_prefix)s
+"""
+
+
+HEADER_MEMBER_TEMPLATE = """ typeof(&::%(function_name)s) %(function_name)s;
+"""
+
+
+IMPL_TEMPLATE = """// This is generated file. Do not modify directly.
+// Path to the code generator: %(generator_path)s .
+
+#include "%(generated_header_name)s"
+
+// Put these sanity checks here so that they fire at most once
+// (to avoid cluttering the build output).
+#if !defined(%(unique_prefix)s_DLOPEN) && !defined(%(unique_prefix)s_DT_NEEDED)
+#error neither %(unique_prefix)s_DLOPEN nor %(unique_prefix)s_DT_NEEDED defined
+#endif
+#if defined(%(unique_prefix)s_DLOPEN) && defined(%(unique_prefix)s_DT_NEEDED)
+#error both %(unique_prefix)s_DLOPEN and %(unique_prefix)s_DT_NEEDED defined
+#endif
+
+#include "base/file_path.h"
+#include "base/logging.h"
+
+%(class_name)s::%(class_name)s() : loaded_(false) {
+}
+
+%(class_name)s::~%(class_name)s() {
+ CleanUp(loaded_);
+}
+
+bool %(class_name)s::Load(const std::string& library_name) {
+ if (loaded_) {
+ NOTREACHED();
+ return false;
+ }
+
+#if defined(%(unique_prefix)s_DLOPEN)
+ library_ = base::LoadNativeLibrary(FilePath(library_name), NULL);
+ if (!library_)
+ return false;
+#endif
+
+%(member_init)s
+
+ loaded_ = true;
+ return true;
+}
+
+void %(class_name)s::CleanUp(bool unload) {
+#if defined(%(unique_prefix)s_DLOPEN)
+ if (unload) {
+ base::UnloadNativeLibrary(library_);
+ library_ = NULL;
+ }
+#endif
+ loaded_ = false;
+%(member_cleanup)s
+}
+"""
+
+IMPL_MEMBER_INIT_TEMPLATE = """
+#if defined(%(unique_prefix)s_DLOPEN)
+ %(function_name)s =
+ reinterpret_cast<typeof(this->%(function_name)s)>(
+ base::GetFunctionPointerFromNativeLibrary(
+ library_, "%(function_name)s"));
+#endif
+#if defined(%(unique_prefix)s_DT_NEEDED)
+ %(function_name)s = &::%(function_name)s;
+#endif
+ if (!%(function_name)s) {
+ CleanUp(true);
+ return false;
+ }
+"""
+
+IMPL_MEMBER_CLEANUP_TEMPLATE = """ %(function_name)s = NULL;
+"""
+
+def main():
+ parser = optparse.OptionParser()
+ parser.add_option('--name')
+ parser.add_option('--output-cc')
+ parser.add_option('--output-h')
+ parser.add_option('--header')
+
+ parser.add_option('--use-extern-c', action='store_true', default=False)
+ parser.add_option('--link-directly', type=int, default=0)
+
+ options, args = parser.parse_args()
+
+ if not options.name:
+ parser.error('Missing --name parameter')
+ if not options.output_cc:
+ parser.error('Missing --output-cc parameter')
+ if not options.output_h:
+ parser.error('Missing --output-h parameter')
+ if not options.header:
+ parser.error('Missing --header paramater')
+ if not args:
+ parser.error('No function names specified')
+
+ # Make sure we are always dealing with an absolute path
+ # to avoid issues caused by different relative path roots.
+ options.output_cc = os.path.abspath(options.output_cc)
+ options.output_h = os.path.abspath(options.output_h)
+
+ # Create a unique prefix, e.g. for header guards.
+ # Stick a known string at the beginning to ensure this doesn't begin
+ # with an underscore, which is reserved for the C++ implementation.
+ unique_prefix = ('LIBRARY_LOADER_' +
+ re.sub(r'[\W]', '_', options.output_h).upper())
+
+ member_decls = []
+ member_init = []
+ member_cleanup = []
+ for fn in args:
+ member_decls.append(HEADER_MEMBER_TEMPLATE % {
+ 'function_name': fn,
+ 'unique_prefix': unique_prefix
+ })
+ member_init.append(IMPL_MEMBER_INIT_TEMPLATE % {
+ 'function_name': fn,
+ 'unique_prefix': unique_prefix
+ })
+ member_cleanup.append(IMPL_MEMBER_CLEANUP_TEMPLATE % {
+ 'function_name': fn,
+ 'unique_prefix': unique_prefix
+ })
+
+ wrapped_header_include = '#include %s' % options.header
+
+ # Some libraries (e.g. libpci) have headers that cannot be included
+ # without extern "C", otherwise they cause the link to fail.
+ # TODO(phajdan.jr): This is a workaround for broken headers. Remove it.
+ if options.use_extern_c:
+ wrapped_header_include = 'extern "C" {\n%s\n}\n' % wrapped_header_include
+
+ # It seems cleaner just to have a single #define here and #ifdefs in bunch
+ # of places, rather than having a different set of templates, duplicating
+ # or complicating more code.
+ if options.link_directly == 0:
+ wrapped_header_include += '#define %s_DLOPEN\n' % unique_prefix
+ elif options.link_directly == 1:
+ wrapped_header_include += '#define %s_DT_NEEDED\n' % unique_prefix
+ else:
+ parser.error('Invalid value for --link-directly. Should be 0 or 1.')
+
+ # Make it easier for people to find the code generator just in case.
+ # Doing it this way is more maintainable, because it's going to work
+ # even if file gets moved without updating the contents.
+ generator_path = os.path.abspath(__file__)
+
+ header_contents = HEADER_TEMPLATE % {
+ 'generator_path': generator_path,
+ 'unique_prefix': unique_prefix,
+ 'wrapped_header_include': wrapped_header_include,
+ 'class_name': options.name,
+ 'member_decls': ''.join(member_decls),
+ }
+
+ impl_contents = IMPL_TEMPLATE % {
+ 'generator_path': generator_path,
+ 'unique_prefix': unique_prefix,
+ 'generated_header_name': options.output_h,
+ 'class_name': options.name,
+ 'member_init': ''.join(member_init),
+ 'member_cleanup': ''.join(member_cleanup),
+ }
+
+ header_file = open(options.output_h, 'w')
+ try:
+ header_file.write(header_contents)
+ finally:
+ header_file.close()
+
+ impl_file = open(options.output_cc, 'w')
+ try:
+ impl_file.write(impl_contents)
+ finally:
+ impl_file.close()
+
+ return 0
+
+if __name__ == '__main__':
+ sys.exit(main())