| OLD | NEW |
| (Empty) |
| 1 // Copyright (c) 2006-2010 The Chromium Authors. All rights reserved. | |
| 2 // Use of this source code is governed by a BSD-style license that can be | |
| 3 // found in the LICENSE file. | |
| 4 | |
| 5 #include "chrome/gpu/gpu_info_collector.h" | |
| 6 | |
| 7 #include <dlfcn.h> | |
| 8 #include <vector> | |
| 9 | |
| 10 #include "app/gfx/gl/gl_bindings.h" | |
| 11 #include "app/gfx/gl/gl_context.h" | |
| 12 #include "app/gfx/gl/gl_implementation.h" | |
| 13 #include "base/file_util.h" | |
| 14 #include "base/logging.h" | |
| 15 #include "base/scoped_ptr.h" | |
| 16 #include "base/string_piece.h" | |
| 17 #include "base/string_split.h" | |
| 18 #include "base/string_util.h" | |
| 19 | |
| 20 namespace { | |
| 21 | |
| 22 // PciDevice and PciAccess are defined to access libpci functions. Their | |
| 23 // members match the corresponding structures defined by libpci in size up to | |
| 24 // fields we may access. For those members we don't use, their names are | |
| 25 // defined as "fieldX", etc., or, left out if they are declared after the | |
| 26 // members we care about in libpci. | |
| 27 | |
| 28 struct PciDevice { | |
| 29 PciDevice* next; | |
| 30 | |
| 31 uint16 field0; | |
| 32 uint8 field1; | |
| 33 uint8 field2; | |
| 34 uint8 field3; | |
| 35 int field4; | |
| 36 | |
| 37 uint16 vendor_id; | |
| 38 uint16 device_id; | |
| 39 uint16 device_class; | |
| 40 }; | |
| 41 | |
| 42 struct PciAccess { | |
| 43 unsigned int field0; | |
| 44 int field1; | |
| 45 int field2; | |
| 46 char* field3; | |
| 47 int field4; | |
| 48 int field5; | |
| 49 unsigned int field6; | |
| 50 int field7; | |
| 51 | |
| 52 void (*function0)(); | |
| 53 void (*function1)(); | |
| 54 void (*function2)(); | |
| 55 | |
| 56 PciDevice* device_list; | |
| 57 }; | |
| 58 | |
| 59 // Define function types. | |
| 60 typedef PciAccess* (*FT_pci_alloc)(); | |
| 61 typedef void (*FT_pci_init)(PciAccess*); | |
| 62 typedef void (*FT_pci_cleanup)(PciAccess*); | |
| 63 typedef void (*FT_pci_scan_bus)(PciAccess*); | |
| 64 typedef void (*FT_pci_scan_bus)(PciAccess*); | |
| 65 typedef int (*FT_pci_fill_info)(PciDevice*, int); | |
| 66 typedef char* (*FT_pci_lookup_name)(PciAccess*, char*, int, int, ...); | |
| 67 | |
| 68 // This includes dynamically linked library handle and functions pointers from | |
| 69 // libpci. | |
| 70 struct PciInterface { | |
| 71 void* lib_handle; | |
| 72 | |
| 73 FT_pci_alloc pci_alloc; | |
| 74 FT_pci_init pci_init; | |
| 75 FT_pci_cleanup pci_cleanup; | |
| 76 FT_pci_scan_bus pci_scan_bus; | |
| 77 FT_pci_fill_info pci_fill_info; | |
| 78 FT_pci_lookup_name pci_lookup_name; | |
| 79 }; | |
| 80 | |
| 81 // This checks if a system supports PCI bus. | |
| 82 // We check the existence of /sys/bus/pci or /sys/bug/pci_express. | |
| 83 bool IsPciSupported() { | |
| 84 const FilePath pci_path("/sys/bus/pci/"); | |
| 85 const FilePath pcie_path("/sys/bus/pci_express/"); | |
| 86 return (file_util::PathExists(pci_path) || | |
| 87 file_util::PathExists(pcie_path)); | |
| 88 } | |
| 89 | |
| 90 // This dynamically opens libpci and get function pointers we need. Return | |
| 91 // NULL if library fails to open or any functions can not be located. | |
| 92 // Returned interface (if not NULL) should be deleted in FinalizeLibPci. | |
| 93 PciInterface* InitializeLibPci(const char* lib_name) { | |
| 94 void* handle = dlopen(lib_name, RTLD_LAZY); | |
| 95 if (handle == NULL) { | |
| 96 LOG(INFO) << "Failed to dlopen " << lib_name; | |
| 97 return NULL; | |
| 98 } | |
| 99 PciInterface* interface = new struct PciInterface; | |
| 100 interface->lib_handle = handle; | |
| 101 interface->pci_alloc = reinterpret_cast<FT_pci_alloc>( | |
| 102 dlsym(handle, "pci_alloc")); | |
| 103 interface->pci_init = reinterpret_cast<FT_pci_init>( | |
| 104 dlsym(handle, "pci_init")); | |
| 105 interface->pci_cleanup = reinterpret_cast<FT_pci_cleanup>( | |
| 106 dlsym(handle, "pci_cleanup")); | |
| 107 interface->pci_scan_bus = reinterpret_cast<FT_pci_scan_bus>( | |
| 108 dlsym(handle, "pci_scan_bus")); | |
| 109 interface->pci_fill_info = reinterpret_cast<FT_pci_fill_info>( | |
| 110 dlsym(handle, "pci_fill_info")); | |
| 111 interface->pci_lookup_name = reinterpret_cast<FT_pci_lookup_name>( | |
| 112 dlsym(handle, "pci_lookup_name")); | |
| 113 if (interface->pci_alloc == NULL || | |
| 114 interface->pci_init == NULL || | |
| 115 interface->pci_cleanup == NULL || | |
| 116 interface->pci_scan_bus == NULL || | |
| 117 interface->pci_fill_info == NULL || | |
| 118 interface->pci_lookup_name == NULL) { | |
| 119 LOG(ERROR) << "Missing required function(s) from " << lib_name; | |
| 120 dlclose(handle); | |
| 121 delete interface; | |
| 122 return NULL; | |
| 123 } | |
| 124 return interface; | |
| 125 } | |
| 126 | |
| 127 // This close the dynamically opened libpci and delete the interface. | |
| 128 void FinalizeLibPci(PciInterface** interface) { | |
| 129 DCHECK(interface && *interface && (*interface)->lib_handle); | |
| 130 dlclose((*interface)->lib_handle); | |
| 131 delete (*interface); | |
| 132 *interface = NULL; | |
| 133 } | |
| 134 | |
| 135 } // namespace anonymous | |
| 136 | |
| 137 namespace gpu_info_collector { | |
| 138 | |
| 139 bool CollectGraphicsInfo(GPUInfo* gpu_info) { | |
| 140 DCHECK(gpu_info); | |
| 141 | |
| 142 // TODO(zmo): need to consider the case where we are running on top of | |
| 143 // desktop GL and GL_ARB_robustness extension is available. | |
| 144 gpu_info->can_lose_context = | |
| 145 (gfx::GetGLImplementation() == gfx::kGLImplementationEGLGLES2); | |
| 146 gpu_info->level = GPUInfo::kComplete; | |
| 147 return CollectGraphicsInfoGL(gpu_info); | |
| 148 } | |
| 149 | |
| 150 bool CollectPreliminaryGraphicsInfo(GPUInfo* gpu_info) { | |
| 151 DCHECK(gpu_info); | |
| 152 | |
| 153 gpu_info->level = GPUInfo::kPartial; | |
| 154 | |
| 155 bool rt = true; | |
| 156 if (!CollectVideoCardInfo(gpu_info)) | |
| 157 rt = false; | |
| 158 | |
| 159 // TODO(zmo): if vendor is ATI, consider passing /etc/ati/amdpcsdb.default | |
| 160 // for driver information. | |
| 161 | |
| 162 return rt; | |
| 163 } | |
| 164 | |
| 165 bool CollectVideoCardInfo(GPUInfo* gpu_info) { | |
| 166 DCHECK(gpu_info); | |
| 167 | |
| 168 if (IsPciSupported() == false) { | |
| 169 LOG(INFO) << "PCI bus scanning is not supported"; | |
| 170 return false; | |
| 171 } | |
| 172 | |
| 173 // TODO(zmo): be more flexible about library name. | |
| 174 PciInterface* interface = InitializeLibPci("libpci.so.3"); | |
| 175 if (interface == NULL) | |
| 176 interface = InitializeLibPci("libpci.so"); | |
| 177 if (interface == NULL) { | |
| 178 LOG(ERROR) << "Failed to locate libpci"; | |
| 179 return false; | |
| 180 } | |
| 181 | |
| 182 PciAccess* access = (interface->pci_alloc)(); | |
| 183 DCHECK(access != NULL); | |
| 184 (interface->pci_init)(access); | |
| 185 (interface->pci_scan_bus)(access); | |
| 186 std::vector<PciDevice*> gpu_list; | |
| 187 PciDevice* gpu_active = NULL; | |
| 188 for (PciDevice* device = access->device_list; | |
| 189 device != NULL; device = device->next) { | |
| 190 (interface->pci_fill_info)(device, 33); // Fill the IDs and class fields. | |
| 191 // TODO(zmo): there might be other classes that qualify as display devices. | |
| 192 if (device->device_class == 0x0300) { // Device class is DISPLAY_VGA. | |
| 193 gpu_list.push_back(device); | |
| 194 } | |
| 195 } | |
| 196 if (gpu_list.size() == 1) { | |
| 197 gpu_active = gpu_list[0]; | |
| 198 } else { | |
| 199 // If more than one graphics card are identified, find the one that matches | |
| 200 // gl VENDOR and RENDERER info. | |
| 201 std::string gl_vendor_string = gpu_info->gl_vendor; | |
| 202 std::string gl_renderer_string = gpu_info->gl_renderer; | |
| 203 const int buffer_size = 255; | |
| 204 scoped_array<char> buffer(new char[buffer_size]); | |
| 205 std::vector<PciDevice*> candidates; | |
| 206 for (size_t i = 0; i < gpu_list.size(); ++i) { | |
| 207 PciDevice* gpu = gpu_list[i]; | |
| 208 // The current implementation of pci_lookup_name returns the same pointer | |
| 209 // as the passed in upon success, and a different one (NULL or a pointer | |
| 210 // to an error message) upon failure. | |
| 211 if ((interface->pci_lookup_name)(access, | |
| 212 buffer.get(), | |
| 213 buffer_size, | |
| 214 1, | |
| 215 gpu->vendor_id) != buffer.get()) | |
| 216 continue; | |
| 217 std::string vendor_string = buffer.get(); | |
| 218 const bool kCaseSensitive = false; | |
| 219 if (!StartsWithASCII(gl_vendor_string, vendor_string, kCaseSensitive)) | |
| 220 continue; | |
| 221 if ((interface->pci_lookup_name)(access, | |
| 222 buffer.get(), | |
| 223 buffer_size, | |
| 224 2, | |
| 225 gpu->vendor_id, | |
| 226 gpu->device_id) != buffer.get()) | |
| 227 continue; | |
| 228 std::string device_string = buffer.get(); | |
| 229 size_t begin = device_string.find_first_of('['); | |
| 230 size_t end = device_string.find_last_of(']'); | |
| 231 if (begin != std::string::npos && end != std::string::npos && | |
| 232 begin < end) { | |
| 233 device_string = device_string.substr(begin + 1, end - begin - 1); | |
| 234 } | |
| 235 if (StartsWithASCII(gl_renderer_string, device_string, kCaseSensitive)) { | |
| 236 gpu_active = gpu; | |
| 237 break; | |
| 238 } | |
| 239 // If a device's vendor matches gl VENDOR string, we want to consider the | |
| 240 // possibility that libpci may not return the exact same name as gl | |
| 241 // RENDERER string. | |
| 242 candidates.push_back(gpu); | |
| 243 } | |
| 244 if (gpu_active == NULL && candidates.size() == 1) | |
| 245 gpu_active = candidates[0]; | |
| 246 } | |
| 247 if (gpu_active != NULL) { | |
| 248 gpu_info->vendor_id = gpu_active->vendor_id; | |
| 249 gpu_info->device_id = gpu_active->device_id; | |
| 250 } | |
| 251 (interface->pci_cleanup)(access); | |
| 252 FinalizeLibPci(&interface); | |
| 253 return (gpu_active != NULL); | |
| 254 } | |
| 255 | |
| 256 bool CollectDriverInfoGL(GPUInfo* gpu_info) { | |
| 257 DCHECK(gpu_info); | |
| 258 | |
| 259 std::string gl_version_string = gpu_info->gl_version_string; | |
| 260 std::vector<std::string> pieces; | |
| 261 base::SplitStringAlongWhitespace(gl_version_string, &pieces); | |
| 262 // In linux, the gl version string might be in the format of | |
| 263 // GLVersion DriverVendor DriverVersion | |
| 264 if (pieces.size() < 3) | |
| 265 return false; | |
| 266 | |
| 267 std::string driver_version = pieces[2]; | |
| 268 size_t pos = driver_version.find_first_not_of("0123456789."); | |
| 269 if (pos == 0) | |
| 270 return false; | |
| 271 if (pos != std::string::npos) | |
| 272 driver_version = driver_version.substr(0, pos); | |
| 273 | |
| 274 gpu_info->driver_vendor = pieces[1]; | |
| 275 gpu_info->driver_version = driver_version; | |
| 276 return true; | |
| 277 } | |
| 278 | |
| 279 } // namespace gpu_info_collector | |
| OLD | NEW |