Cleanup debug, refactor libdummycl
[ocl-icd] / ocl_icd_loader.c
1 /**
2 Copyright (c) 2012, Brice Videau <brice.videau@imag.fr>
3 Copyright (c) 2012, Vincent Danjean <Vincent.Danjean@ens-lyon.org>
4 All rights reserved.
5       
6 Redistribution and use in source and binary forms, with or without
7 modification, are permitted provided that the following conditions are met:
8     
9 1. Redistributions of source code must retain the above copyright notice, this
10    list of conditions and the following disclaimer.
11 2. Redistributions in binary form must reproduce the above copyright notice,
12    this list of conditions and the following disclaimer in the documentation
13    and/or other materials provided with the distribution.
14         
15 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
16 ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
17 WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
18 DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
19 ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
20 (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
21 LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
22 ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
24 SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25 */
26
27 #include <dirent.h>
28 #include <stdio.h>
29 #include <stdlib.h>
30 #include <string.h>
31 #include <dlfcn.h>
32 #define CL_USE_DEPRECATED_OPENCL_1_1_APIS
33 #include <CL/opencl.h>
34
35 #pragma GCC visibility push(hidden)
36
37 #include "ocl_icd_loader.h"
38 #include "ocl_icd_debug.h"
39
40 #define ETC_OPENCL_VENDORS "/etc/OpenCL/vendors/"
41
42 int debug_ocl_icd_mask=0;
43
44 typedef __typeof__(clGetExtensionFunctionAddress) *clGetExtensionFunctionAddress_fn;
45 typedef __typeof__(clGetPlatformInfo) *clGetPlatformInfo_fn;
46
47
48 struct vendor_icd {
49   cl_uint       num_platforms;
50   cl_uint       first_platform;
51   void *        dl_handle;
52   clGetExtensionFunctionAddress_fn ext_fn_ptr;
53 };
54
55 struct platform_icd {
56   char *         extension_suffix;
57   char *         version;
58   struct vendor_icd *vicd;
59   cl_platform_id pid;
60 };
61
62 struct vendor_icd *_icds=NULL;
63 struct platform_icd *_picds=NULL;
64 static cl_uint _num_icds = 0;
65 static cl_uint _num_picds = 0;
66
67 static cl_uint _initialized = 0;
68
69 #if DEBUG_OCL_ICD
70 #  define _clS(x) [-x] = #x
71 #  define MAX_CL_ERRORS CL_INVALID_DEVICE_PARTITION_COUNT
72 static char const * const clErrorStr[-MAX_CL_ERRORS+1] = {
73   _clS(CL_SUCCESS),
74   _clS(CL_DEVICE_NOT_FOUND),
75   _clS(CL_DEVICE_NOT_AVAILABLE),
76   _clS(CL_COMPILER_NOT_AVAILABLE),
77   _clS(CL_MEM_OBJECT_ALLOCATION_FAILURE),
78   _clS(CL_OUT_OF_RESOURCES),
79   _clS(CL_OUT_OF_HOST_MEMORY),
80   _clS(CL_PROFILING_INFO_NOT_AVAILABLE),
81   _clS(CL_MEM_COPY_OVERLAP),
82   _clS(CL_IMAGE_FORMAT_MISMATCH),
83   _clS(CL_IMAGE_FORMAT_NOT_SUPPORTED),
84   _clS(CL_BUILD_PROGRAM_FAILURE),
85   _clS(CL_MAP_FAILURE),
86   _clS(CL_MISALIGNED_SUB_BUFFER_OFFSET),
87   _clS(CL_EXEC_STATUS_ERROR_FOR_EVENTS_IN_WAIT_LIST),
88   _clS(CL_COMPILE_PROGRAM_FAILURE),
89   _clS(CL_LINKER_NOT_AVAILABLE),
90   _clS(CL_LINK_PROGRAM_FAILURE),
91   _clS(CL_DEVICE_PARTITION_FAILED),
92   _clS(CL_KERNEL_ARG_INFO_NOT_AVAILABLE),
93   _clS(CL_INVALID_VALUE),
94   _clS(CL_INVALID_DEVICE_TYPE),
95   _clS(CL_INVALID_PLATFORM),
96   _clS(CL_INVALID_DEVICE),
97   _clS(CL_INVALID_CONTEXT),
98   _clS(CL_INVALID_QUEUE_PROPERTIES),
99   _clS(CL_INVALID_COMMAND_QUEUE),
100   _clS(CL_INVALID_HOST_PTR),
101   _clS(CL_INVALID_MEM_OBJECT),
102   _clS(CL_INVALID_IMAGE_FORMAT_DESCRIPTOR),
103   _clS(CL_INVALID_IMAGE_SIZE),
104   _clS(CL_INVALID_SAMPLER),
105   _clS(CL_INVALID_BINARY),
106   _clS(CL_INVALID_BUILD_OPTIONS),
107   _clS(CL_INVALID_PROGRAM),
108   _clS(CL_INVALID_PROGRAM_EXECUTABLE),
109   _clS(CL_INVALID_KERNEL_NAME),
110   _clS(CL_INVALID_KERNEL_DEFINITION),
111   _clS(CL_INVALID_KERNEL),
112   _clS(CL_INVALID_ARG_INDEX),
113   _clS(CL_INVALID_ARG_VALUE),
114   _clS(CL_INVALID_ARG_SIZE),
115   _clS(CL_INVALID_KERNEL_ARGS),
116   _clS(CL_INVALID_WORK_DIMENSION),
117   _clS(CL_INVALID_WORK_GROUP_SIZE),
118   _clS(CL_INVALID_WORK_ITEM_SIZE),
119   _clS(CL_INVALID_GLOBAL_OFFSET),
120   _clS(CL_INVALID_EVENT_WAIT_LIST),
121   _clS(CL_INVALID_EVENT),
122   _clS(CL_INVALID_OPERATION),
123   _clS(CL_INVALID_GL_OBJECT),
124   _clS(CL_INVALID_BUFFER_SIZE),
125   _clS(CL_INVALID_MIP_LEVEL),
126   _clS(CL_INVALID_GLOBAL_WORK_SIZE),
127   _clS(CL_INVALID_PROPERTY),
128   _clS(CL_INVALID_IMAGE_DESCRIPTOR),
129   _clS(CL_INVALID_COMPILER_OPTIONS),
130   _clS(CL_INVALID_LINKER_OPTIONS),
131   _clS(CL_INVALID_DEVICE_PARTITION_COUNT)
132 };
133 #undef _clS
134 #endif
135
136 static char* _clerror2string (cl_int error) __attribute__((unused));
137 static char* _clerror2string (cl_int error) {
138 #if DEBUG_OCL_ICD
139   if (-error > MAX_CL_ERRORS || error > 0) {
140     debug(D_WARN, "Unknown error code %d", error);
141     RETURN_STR("OpenCL Error");
142   }
143   const char *ret=clErrorStr[-error];
144   if (ret == NULL) {
145     debug(D_WARN, "Unknown error code %d", error);
146     RETURN_STR("OpenCL Error");
147   }
148   RETURN_STR(ret);
149 #else
150   static char number[15];
151   if (error==0) {
152     RETURN_STR("CL_SUCCESS");
153   }
154   snprintf(number, 15, "%i", error);
155   RETURN_STR(number);
156 #endif
157 }
158
159 static inline cl_uint _find_num_icds(DIR *dir) {
160   cl_uint num_icds = 0;
161   struct dirent *ent;
162   while( (ent=readdir(dir)) != NULL ){
163     if( strcmp(ent->d_name,".") == 0 || strcmp(ent->d_name,"..") == 0 )
164       continue;
165     cl_uint d_name_len = strlen(ent->d_name);
166     if( d_name_len<5 || strcmp(ent->d_name + d_name_len - 4, ".icd" ) != 0 )
167       continue;
168     num_icds++;
169   }
170   rewinddir(dir);
171   RETURN(num_icds);
172 }
173
174 static inline cl_uint _open_drivers(DIR *dir, const char* dir_path) {
175   cl_uint num_icds = 0;
176   struct dirent *ent;
177   while( (ent=readdir(dir)) != NULL ){
178     if( strcmp(ent->d_name,".") == 0 || strcmp(ent->d_name,"..") == 0 )
179       continue;
180     cl_uint d_name_len = strlen(ent->d_name);
181     if( d_name_len<5 || strcmp(ent->d_name + d_name_len - 4, ".icd" ) != 0 )
182       continue;
183     char * lib_path;
184     char * err;
185     unsigned int lib_path_length = strlen(dir_path) + strlen(ent->d_name) + 1;
186     lib_path = malloc(lib_path_length*sizeof(char));
187     sprintf(lib_path,"%s%s", dir_path, ent->d_name);
188     debug(D_LOG, "Considering file '%s'", lib_path);
189     FILE *f = fopen(lib_path,"r");
190     free(lib_path);
191
192     fseek(f, 0, SEEK_END);
193     lib_path_length = ftell(f)+1;
194     fseek(f, 0, SEEK_SET);
195     if(lib_path_length == 1) {
196       fclose(f);
197       continue;
198     }
199     lib_path = malloc(lib_path_length*sizeof(char));
200     err = fgets(lib_path, lib_path_length, f);
201     fclose(f);
202     if( err == NULL ) {
203       free(lib_path);
204       continue;
205     }
206
207     lib_path_length = strlen(lib_path);
208     
209     if( lib_path[lib_path_length-1] == '\n' )
210       lib_path[lib_path_length-1] = '\0';
211
212     _icds[num_icds].dl_handle = dlopen(lib_path, RTLD_LAZY|RTLD_LOCAL);//|RTLD_DEEPBIND);
213     if(_icds[num_icds].dl_handle != NULL) {
214       debug(D_LOG, "ICD[%i] loaded", num_icds);
215       num_icds++;
216     }
217     free(lib_path);
218   }
219   RETURN(num_icds);
220 }
221
222 static void* _get_function_addr(void* dlh, clGetExtensionFunctionAddress_fn fn, const char*name) {
223   void *addr1;
224   addr1=dlsym(dlh, name);
225   if (addr1 == NULL) {
226     debug(D_WARN, "Missing global symbol '%s' in ICD, should be skipped", name);
227   }
228   void* addr2=NULL;
229   if (fn) {
230     addr2=(*fn)(name);
231     if (addr2 == NULL) {
232       debug(D_WARN, "Missing function '%s' in ICD, should be skipped", name);
233     }
234 #if DEBUG_OCL_ICD
235     if (addr1 && addr2 && addr1!=addr2) {
236       debug(D_WARN, "Function and symbol '%s' have different addresses!", name);
237     }
238 #endif
239   }
240   if (!addr2) addr2=addr1;
241   RETURN(addr2);
242 }
243
244 static int _allocate_platforms(int req) {
245   static cl_uint allocated=0;
246   debug(D_LOG,"Requesting allocation for %d platforms",req);
247   if (allocated - _num_picds < req) {
248     if (allocated==0) {
249       _picds=(struct platform_icd*)malloc(req*sizeof(struct platform_icd));
250     } else {
251       req = req - (allocated - _num_picds);
252       _picds=(struct platform_icd*)realloc(_picds, (allocated+req)*sizeof(struct platform_icd));
253     }
254     allocated += req;
255   }
256   RETURN(allocated - _num_picds);
257 }
258
259 static char* _malloc_clGetPlatformInfo(clGetPlatformInfo_fn plt_info_ptr,
260                  cl_platform_id pid, cl_platform_info cname, char* sname) {
261   cl_int error;
262   size_t param_value_size_ret;
263   error = plt_info_ptr(pid, cname, 0, NULL, &param_value_size_ret);
264   if (error != CL_SUCCESS) {
265     debug(D_WARN, "Error %s while requesting %s in platform %p",
266           _clerror2string(error), sname, pid);
267     return NULL;
268   }
269   char *param_value = (char *)malloc(sizeof(char)*param_value_size_ret);
270   if (param_value == NULL) {
271     debug(D_WARN, "Error in malloc while requesting %s in platform %p",
272           sname, pid);
273     return NULL;
274   }
275   error = plt_info_ptr(pid, cname, param_value_size_ret, param_value, NULL);
276   if (error != CL_SUCCESS){
277     free(param_value);
278     debug(D_WARN, "Error %s while requesting %s in platform %p",
279           _clerror2string(error), sname, pid);
280     return NULL;
281   }
282   RETURN_STR(param_value);
283 }
284
285 static inline void _find_and_check_platforms(cl_uint num_icds) {
286   cl_uint i;
287   _num_icds = 0;
288   for( i=0; i<num_icds; i++){
289     debug(D_LOG, "Checking ICD %i", i);
290     struct vendor_icd *picd = &_icds[_num_icds];
291     void* dlh = _icds[i].dl_handle;
292     picd->ext_fn_ptr = _get_function_addr(dlh, NULL, "clGetExtensionFunctionAddress");
293     clIcdGetPlatformIDsKHR_fn plt_fn_ptr = 
294       _get_function_addr(dlh, picd->ext_fn_ptr, "clIcdGetPlatformIDsKHR");
295     clGetPlatformInfo_fn plt_info_ptr = 
296       _get_function_addr(dlh, picd->ext_fn_ptr, "clGetPlatformInfo");
297     if( picd->ext_fn_ptr == NULL
298         || plt_fn_ptr == NULL
299         || plt_info_ptr == NULL) {
300       debug(D_WARN, "Missing symbols in ICD, skipping it");
301       continue;
302     }
303     cl_uint num_platforms=0;
304     cl_int error;
305     error = (*plt_fn_ptr)(0, NULL, &num_platforms);
306     if( error != CL_SUCCESS || num_platforms == 0) {
307       debug(D_LOG, "No platform in ICD, skipping it");
308       continue;
309     }
310     cl_platform_id *platforms = (cl_platform_id *) malloc( sizeof(cl_platform_id) * num_platforms);
311     error = (*plt_fn_ptr)(num_platforms, platforms, NULL);
312     if( error != CL_SUCCESS ){
313       free(platforms);
314       debug(D_WARN, "Error in loading ICD platforms, skipping ICD");
315       continue;
316     }
317     cl_uint num_valid_platforms=0;
318     cl_uint j;
319     debug(D_LOG, "Try to load %d plateforms", num_platforms);
320     if (_allocate_platforms(num_platforms) < num_platforms) {
321       free(platforms);
322       debug(D_WARN, "Not enought platform allocated. Skipping ICD");
323       continue;
324     }
325     for(j=0; j<num_platforms; j++) {
326       debug(D_LOG, "Checking platform %i", j);
327       struct platform_icd *p=&_picds[_num_picds];
328       p->extension_suffix=NULL;
329       p->vicd=&_icds[i];
330       p->pid=platforms[j];
331 #if DEBUG_OCL_ICD
332       if (debug_ocl_icd_mask & D_DUMP) {
333         dump_platform(p->pid);
334       }
335 #endif
336       char *param_value=_malloc_clGetPlatformInfo(plt_info_ptr, p->pid, CL_PLATFORM_EXTENSIONS, "extensions");
337       if (param_value == NULL){
338         debug(D_WARN, "Skipping platform %i", j);
339         continue;
340       }
341       debug(D_DUMP, "Supported extensions: %s", param_value);
342       if( strstr(param_value, "cl_khr_icd") == NULL){
343         free(param_value);
344         debug(D_WARN, "Missing khr extension in platform %i, skipping it", j);
345         continue;
346       }
347       free(param_value);
348       param_value=_malloc_clGetPlatformInfo(plt_info_ptr, p->pid, CL_PLATFORM_ICD_SUFFIX_KHR, "suffix");
349       if (param_value == NULL){
350         debug(D_WARN, "Skipping platform %i", j);
351         continue;
352       }
353       p->extension_suffix = param_value;
354       debug(D_DUMP|D_LOG, "Extension suffix: %s", param_value);
355 #if DEBUG_OCL_ICD
356       param_value=_malloc_clGetPlatformInfo(plt_info_ptr, p->pid, CL_PLATFORM_PROFILE, "profile");
357       if (param_value != NULL){
358         debug(D_DUMP, "Profile: %s", param_value);
359         free(param_value);
360       }
361 #endif
362       param_value=_malloc_clGetPlatformInfo(plt_info_ptr, p->pid, CL_PLATFORM_VERSION, "version");
363       p->version = param_value;
364       if (param_value != NULL){
365         debug(D_DUMP, "Version: %s", param_value);
366         free(param_value);
367       }
368 #if DEBUG_OCL_ICD
369       param_value=_malloc_clGetPlatformInfo(plt_info_ptr, p->pid, CL_PLATFORM_NAME, "name");
370       if (param_value != NULL){
371         debug(D_DUMP, "Name: %s", param_value);
372         free(param_value);
373       }
374       param_value=_malloc_clGetPlatformInfo(plt_info_ptr, p->pid, CL_PLATFORM_VENDOR, "vendor");
375       if (param_value != NULL){
376         debug(D_DUMP, "Vendor: %s", param_value);
377         free(param_value);
378       }
379 #endif
380       num_valid_platforms++;
381       _num_picds++;
382     }
383     if( num_valid_platforms != 0 ) {
384       if ( _num_icds != i ) {
385         picd->dl_handle = dlh;
386       }
387       _num_icds++;
388       picd->num_platforms = num_valid_platforms;
389       _icds[i].first_platform = _num_picds - num_valid_platforms;
390     } else {
391       dlclose(dlh);
392     }
393     free(platforms);
394   }
395 }
396
397 static void _initClIcd( void ) {
398   if( _initialized )
399     return;
400   debug_init();
401   cl_uint num_icds = 0;
402   DIR *dir;
403   const char* dir_path=getenv("OCL_ICD_VENDORS");
404   if (! dir_path || dir_path[0]==0) {
405     dir_path=ETC_OPENCL_VENDORS;
406   }
407   debug(D_LOG,"Reading icd list from '%s'", dir_path);
408   dir = opendir(dir_path);
409   if(dir == NULL) {
410     goto abort;
411   }
412
413   num_icds = _find_num_icds(dir);
414   if(num_icds == 0) {
415     goto abort;
416   }
417
418   _icds = (struct vendor_icd*)malloc(num_icds * sizeof(struct vendor_icd));
419   if (_icds == NULL) {
420     goto abort;
421   }
422   
423   num_icds = _open_drivers(dir, dir_path);
424   if(num_icds == 0) {
425     goto abort;
426   }
427
428   _find_and_check_platforms(num_icds);
429   if(_num_icds == 0){
430     goto abort;
431   }
432
433   if (_num_icds < num_icds) {
434     _icds = (struct vendor_icd*)realloc(_icds, _num_icds * sizeof(struct vendor_icd));
435   }
436   debug(D_WARN, "%d valid vendor(s)!", _num_icds);
437   _initialized = 1;
438   return;
439 abort:
440   _num_icds = 0;
441   _initialized = 1;
442   if (_icds) {
443     free(_icds);
444     _icds = NULL;
445   }
446   return;
447 }
448
449 #pragma GCC visibility pop
450
451 CL_API_ENTRY void * CL_API_CALL clGetExtensionFunctionAddress(const char * func_name) CL_API_SUFFIX__VERSION_1_0 {
452   if( !_initialized )
453     _initClIcd();
454   if( func_name == NULL )
455     return NULL;
456   cl_uint suffix_length;
457   cl_uint i;
458   void * return_value=NULL;
459   struct func_desc const * fn=&function_description[0];
460   while (fn->name != NULL) {
461     if (strcmp(func_name, fn->name)==0)
462       return fn->addr;
463     fn++;
464   }
465   for(i=0; i<_num_picds; i++) {
466     suffix_length = strlen(_picds[i].extension_suffix);
467     if( suffix_length > strlen(func_name) )
468       continue;
469     if(strcmp(_picds[i].extension_suffix, &func_name[strlen(func_name)-suffix_length]) == 0)
470       return (*_picds[i].vicd->ext_fn_ptr)(func_name);
471   }
472   return return_value;
473 }
474 typeof(clGetExtensionFunctionAddress) clGetExtensionFunctionAddress_hid __attribute__ ((alias ("clGetExtensionFunctionAddress"), visibility("hidden")));
475
476 CL_API_ENTRY cl_int CL_API_CALL
477 clGetPlatformIDs(cl_uint          num_entries,
478                  cl_platform_id * platforms,
479                  cl_uint *        num_platforms) CL_API_SUFFIX__VERSION_1_0 {
480   if( !_initialized )
481     _initClIcd();
482   if( platforms == NULL && num_platforms == NULL )
483     return CL_INVALID_VALUE;
484   if( num_entries == 0 && platforms != NULL )
485     return CL_INVALID_VALUE;
486   if( _num_icds == 0)
487     return CL_PLATFORM_NOT_FOUND_KHR;
488
489   cl_uint i;
490   if( num_platforms != NULL ){
491     *num_platforms = _num_picds;
492   }
493   if( platforms != NULL ) {
494     cl_uint n_platforms = _num_picds < num_entries ? _num_picds : num_entries;
495     for( i=0; i<n_platforms; i++) {
496       *(platforms++) = _picds[i].pid;
497     }
498   }
499   return CL_SUCCESS;
500 }
501 typeof(clGetPlatformIDs) clGetPlatformIDs_hid __attribute__ ((alias ("clGetPlatformIDs"), visibility("hidden")));
502