diff options
-rw-r--r-- | DEPS | 3 | ||||
-rw-r--r-- | build/linux/system.gyp | 66 | ||||
-rw-r--r-- | content/content_gpu.gypi | 21 | ||||
-rw-r--r-- | content/gpu/gpu_info_collector_linux.cc | 132 | ||||
-rwxr-xr-x | tools/generate_library_loader/generate_library_loader.py | 248 |
5 files changed, 339 insertions, 131 deletions
@@ -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()) |