Support cl_intel_required_subgroup_size
[clinfo] / src / clinfo.c
1 /* Collect all available information on all available devices
2  * on all available OpenCL platforms present in the system
3  */
4
5 #include <time.h>
6 #include <string.h>
7 #include <dlfcn.h>
8
9 #ifndef RTLD_DEFAULT
10 #define RTLD_DEFAULT ((void*)0)
11 #endif
12
13 /* ISO C forbids assignments between function pointers and void pointers,
14  * but POSIX allows it. To compile without warnings even in -pedantic mode,
15  * we use this horrible trick to get a function address from
16  * clGetExtensionFunctionAddress
17  */
18 #define PTR_FUNC_PTR *(void**)&
19
20 /* Load STDC format macros (PRI*), or define them
21  * for those crappy, non-standard compilers
22  */
23 #include "fmtmacros.h"
24
25 // Support for the horrible MS C compiler
26 #ifdef _MSC_VER
27 #include "ms_support.h"
28 #endif
29
30 #include "ext.h"
31 #include "error.h"
32 #include "memory.h"
33 #include "strbuf.h"
34
35 #define ARRAY_SIZE(ar) (sizeof(ar)/sizeof(*ar))
36 #define UNUSED __attribute__((unused))
37
38 struct platform_data {
39         char *pname; /* CL_PLATFORM_NAME */
40         char *sname; /* CL_PLATFORM_ICD_SUFFIX_KHR or surrogate */
41         cl_uint ndevs; /* number of devices */
42         cl_bool has_amd_offline; /* has cl_amd_offline_devices extension */
43 };
44
45 struct platform_info_checks {
46         int has_khr_icd;
47         cl_uint plat_version;
48 };
49
50 cl_uint num_platforms;
51 cl_platform_id *platform;
52 /* highest version exposed by any platform: if the OpenCL library (the ICD loader)
53  * has a lower version, problems may arise (such as API calls causing segfaults
54  * or any other unexpected behavior
55  */
56 cl_uint max_plat_version;
57 /* auto-detected OpenCL version support for the ICD loader */
58 cl_uint icdl_ocl_version_found = 10;
59 /* OpenCL version support declared by the ICD loader */
60 cl_uint icdl_ocl_version;
61
62 struct platform_data *pdata;
63 /* maximum length of a platform's sname */
64 size_t platform_sname_maxlen;
65 /* maximum number of devices */
66 cl_uint maxdevs;
67 /* line prefix, used to identify the platform/device for each
68  * device property in RAW output mode */
69 char *line_pfx;
70 int line_pfx_len;
71
72 cl_uint num_devs_all;
73
74 cl_device_id *all_devices;
75
76 enum output_modes {
77         CLINFO_HUMAN = 1, /* more human readable */
78         CLINFO_RAW = 2, /* property-by-property */
79         CLINFO_BOTH = CLINFO_HUMAN | CLINFO_RAW
80 };
81
82 enum output_modes output_mode = CLINFO_HUMAN;
83
84 /* Specify if we should only be listing the platform and devices;
85  * can be done in both human and raw mode, and only the platform
86  * and device names (and number) will be shown
87  * TODO check if terminal supports UTF-8 and use Unicode line-drawing
88  * for the tree in list mode
89  */
90 cl_bool list_only = CL_FALSE;
91
92 static const char unk[] = "Unknown";
93 static const char none[] = "None";
94 static const char none_raw[] = "CL_NONE";
95 static const char na[] = "n/a"; // not available
96 static const char core[] = "core"; // not available
97
98 static const char bytes_str[] = " bytes";
99 static const char pixels_str[] = " pixels";
100 static const char images_str[] = " images";
101
102 static const char* bool_str[] = { "No", "Yes" };
103 static const char* bool_raw_str[] = { "CL_FALSE", "CL_TRUE" };
104
105 static const char* endian_str[] = { "Big-Endian", "Little-Endian" };
106
107 static const cl_device_type devtype[] = { 0,
108         CL_DEVICE_TYPE_DEFAULT, CL_DEVICE_TYPE_CPU, CL_DEVICE_TYPE_GPU,
109         CL_DEVICE_TYPE_ACCELERATOR, CL_DEVICE_TYPE_CUSTOM, CL_DEVICE_TYPE_ALL };
110
111 const size_t devtype_count = ARRAY_SIZE(devtype);
112
113 static const char* device_type_str[] = { unk, "Default", "CPU", "GPU", "Accelerator", "Custom", "All" };
114 static const char* device_type_raw_str[] = { unk,
115         "CL_DEVICE_TYPE_DEFAULT", "CL_DEVICE_TYPE_CPU", "CL_DEVICE_TYPE_GPU",
116         "CL_DEVICE_TYPE_ACCELERATOR", "CL_DEVICE_TYPE_CUSTOM", "CL_DEVICE_TYPE_ALL"
117 };
118
119 static const char* partition_type_str[] = {
120         "none specified", none, "equally", "by counts", "by affinity domain", "by names (Intel)"
121 };
122 static const char* partition_type_raw_str[] = {
123         "NONE SPECIFIED",
124         none_raw,
125         "CL_DEVICE_PARTITION_EQUALLY_EXT",
126         "CL_DEVICE_PARTITION_BY_COUNTS_EXT",
127         "CL_DEVICE_PARTITION_BY_AFFINITY_DOMAIN_EXT",
128         "CL_DEVICE_PARTITION_BY_NAMES_INTEL_EXT"
129 };
130
131 static const char numa[] = "NUMA";
132 static const char l1cache[] = "L1 cache";
133 static const char l2cache[] = "L2 cache";
134 static const char l3cache[] = "L3 cache";
135 static const char l4cache[] = "L4 cache";
136
137 static const char* affinity_domain_str[] = {
138         numa, l4cache, l3cache, l2cache, l1cache, "next partitionable"
139 };
140
141 static const char* affinity_domain_ext_str[] = {
142         numa, l4cache, l3cache, l2cache, l1cache, "next fissionable"
143 };
144
145 static const char* affinity_domain_raw_str[] = {
146         "CL_DEVICE_AFFINITY_DOMAIN_NUMA",
147         "CL_DEVICE_AFFINITY_DOMAIN_L4_CACHE",
148         "CL_DEVICE_AFFINITY_DOMAIN_L3_CACHE",
149         "CL_DEVICE_AFFINITY_DOMAIN_L2_CACHE",
150         "CL_DEVICE_AFFINITY_DOMAIN_L1_CACHE",
151         "CL_DEVICE_AFFINITY_DOMAIN_NEXT_PARTITIONABLE"
152 };
153
154 static const char* affinity_domain_raw_ext_str[] = {
155         "CL_AFFINITY_DOMAIN_NUMA_EXT",
156         "CL_AFFINITY_DOMAIN_L4_CACHE_EXT",
157         "CL_AFFINITY_DOMAIN_L3_CACHE_EXT",
158         "CL_AFFINITY_DOMAIN_L2_CACHE_EXT",
159         "CL_AFFINITY_DOMAIN_L1_CACHE_EXT",
160         "CL_AFFINITY_DOMAIN_NEXT_FISSIONABLE_EXT"
161 };
162
163 const size_t affinity_domain_count = ARRAY_SIZE(affinity_domain_str);
164
165 static const char* fp_conf_str[] = {
166         "Denormals", "Infinity and NANs", "Round to nearest", "Round to zero",
167         "Round to infinity", "IEEE754-2008 fused multiply-add",
168         "Support is emulated in software",
169         "Correctly-rounded divide and sqrt operations"
170 };
171
172 static const char* fp_conf_raw_str[] = {
173         "CL_FP_DENORM",
174         "CL_FP_INF_NAN",
175         "CL_FP_ROUND_TO_NEAREST",
176         "CL_FP_ROUND_TO_ZERO",
177         "CL_FP_ROUND_TO_INF",
178         "CL_FP_FMA",
179         "CL_FP_SOFT_FLOAT",
180         "CL_FP_CORRECTLY_ROUNDED_DIVIDE_SQRT"
181 };
182
183 const size_t fp_conf_count = ARRAY_SIZE(fp_conf_str);
184
185 static const char* svm_cap_str[] = {
186         "Coarse-grained buffer sharing",
187         "Fine-grained buffer sharing",
188         "Fine-grained system sharing",
189         "Atomics"
190 };
191
192 static const char* svm_cap_raw_str[] = {
193         "CL_DEVICE_SVM_COARSE_GRAIN_BUFFER",
194         "CL_DEVICE_SVM_FINE_GRAIN_BUFFER",
195         "CL_DEVICE_SVM_FINE_GRAIN_SYSTEM",
196         "CL_DEVICE_SVM_ATOMICS",
197 };
198
199 const size_t svm_cap_count = ARRAY_SIZE(svm_cap_str);
200
201 static const char* memsfx[] = {
202         "B", "KiB", "MiB", "GiB", "TiB"
203 };
204
205 const size_t memsfx_count = ARRAY_SIZE(memsfx);
206
207 static const char* lmem_type_str[] = { none, "Local", "Global" };
208 static const char* lmem_type_raw_str[] = { none_raw, "CL_LOCAL", "CL_GLOBAL" };
209 static const char* cache_type_str[] = { none, "Read-Only", "Read/Write" };
210 static const char* cache_type_raw_str[] = { none_raw, "CL_READ_ONLY_CACHE", "CL_READ_WRITE_CACHE" };
211
212 static const char* queue_prop_str[] = { "Out-of-order execution", "Profiling" };
213 static const char* queue_prop_raw_str[] = {
214         "CL_QUEUE_OUT_OF_ORDER_EXEC_MODE_ENABLE",
215         "CL_QUEUE_PROFILING_ENABLE"
216 };
217
218 const size_t queue_prop_count = ARRAY_SIZE(queue_prop_str);
219
220 static const char* execap_str[] = { "Run OpenCL kernels", "Run native kernels" };
221 static const char* execap_raw_str[] = {
222         "CL_EXEC_KERNEL",
223         "CL_EXEC_NATIVE_KERNEL"
224 };
225
226 const size_t execap_count = ARRAY_SIZE(execap_str);
227
228 static const char* sources[] = {
229         "#define GWO(type) global type* restrict\n",
230         "#define GRO(type) global const type* restrict\n",
231         "#define BODY int i = get_global_id(0); out[i] = in1[i] + in2[i]\n",
232         "#define _KRN(T, N) void kernel sum##N(GWO(T##N) out, GRO(T##N) in1, GRO(T##N) in2) { BODY; }\n",
233         "#define KRN(N) _KRN(float, N)\n",
234         "KRN()\n/* KRN(2)\nKRN(4)\nKRN(8)\nKRN(16) */\n",
235 };
236
237 const char *no_plat(void)
238 {
239         return output_mode == CLINFO_HUMAN ?
240                 "No platform" :
241                 "CL_INVALID_PLATFORM";
242 }
243
244 const char *no_dev(void)
245 {
246         return output_mode == CLINFO_HUMAN ?
247                 "No devices found in platform" :
248                 "CL_DEVICE_NOT_FOUND";
249 }
250
251 const char *no_dev_avail(void)
252 {
253         return output_mode == CLINFO_HUMAN ?
254                 "No devices available in platform" :
255                 "CL_DEVICE_NOT_AVAILABLE";
256 }
257
258
259 /* preferred workgroup size multiple for each kernel
260  * have not found a platform where the WG multiple changes,
261  * but keep this flexible (this can grow up to 5)
262  */
263 #define NUM_KERNELS 1
264 size_t wgm[NUM_KERNELS];
265
266 #define INDENT "  "
267 #define I0_STR "%-48s  "
268 #define I1_STR "  %-46s  "
269 #define I2_STR "    %-44s  "
270
271 static const char empty_str[] = "";
272 static const char spc_str[] = " ";
273 static const char times_str[] = "x";
274 static const char comma_str[] = ", ";
275 static const char vbar_str[] = " | ";
276
277 int had_error = 0;
278 const char *cur_sfx = empty_str;
279
280 /* parse a CL_DEVICE_VERSION or CL_PLATFORM_VERSION info to determine the OpenCL version.
281  * Returns an unsigned integer in the form major*10 + minor
282  */
283 cl_uint
284 getOpenCLVersion(const char *version)
285 {
286         cl_uint ret = 10;
287         long parse = 0;
288         const char *from = version;
289         char *next = NULL;
290         parse = strtol(from, &next, 10);
291
292         if (next != from) {
293                 ret = parse*10;
294                 // skip the dot TODO should we actually check for the dot?
295                 from = ++next;
296                 parse = strtol(from, &next, 10);
297                 if (next != from)
298                         ret += parse;
299         }
300         return ret;
301 }
302
303
304 /* print strbuf, prefixed by pname, skipping leading whitespace if skip is nonzero,
305  * affixing cur_sfx */
306 static inline
307 void show_strbuf(const char *pname, int skip)
308 {
309         printf("%s" I1_STR "%s%s\n",
310                 line_pfx, pname,
311                 (skip ? skip_leading_ws(strbuf) : strbuf),
312                 had_error ? empty_str : cur_sfx);
313 }
314
315 int
316 platform_info_str(cl_platform_id pid, cl_platform_info param, const char* pname, const struct platform_info_checks * chk UNUSED)
317 {
318         error = clGetPlatformInfo(pid, param, 0, NULL, &nusz);
319         if (nusz > bufsz) {
320                 REALLOC(strbuf, nusz, current_param);
321                 bufsz = nusz;
322         }
323         had_error = REPORT_ERROR2("get %s size");
324         if (!had_error) {
325                 error = clGetPlatformInfo(pid, param, bufsz, strbuf, NULL);
326                 had_error = REPORT_ERROR2("get %s");
327         }
328         /* when only listing, do not print anything we're just gathering
329          * information
330          */
331         if (!list_only)
332                 show_strbuf(pname, 1);
333         return had_error;
334 }
335
336 int
337 platform_info_ulong(cl_platform_id pid, cl_platform_info param, const char* pname, const struct platform_info_checks * chk UNUSED)
338 {
339         cl_ulong val = 0;
340
341         error = clGetPlatformInfo(pid, param, sizeof(val), &val, NULL);
342         had_error = REPORT_ERROR2("get %s");
343         /* when only listing, do not print anything we're just gathering
344          * information
345          */
346         if (!list_only) {
347                 if (had_error)
348                         show_strbuf(pname, 0);
349                 else
350                         printf("%s" I1_STR "%" PRIu64 "%s\n", line_pfx, pname, val, cur_sfx);
351         }
352         return had_error;
353 }
354
355 struct platform_info_traits {
356         cl_platform_info param; // CL_PLATFORM_*
357         const char *sname; // "CL_PLATFORM_*"
358         const char *pname; // "Platform *"
359         const char *sfx; // suffix for the output in non-raw mode
360         /* pointer to function that shows the parameter */
361         int (*show_func)(cl_platform_id pid, cl_platform_info param, const char *pname, const struct platform_info_checks *);
362         /* pointer to function that checks if the parameter should be checked */
363         int (*check_func)(const struct platform_info_checks *);
364 };
365
366 int khr_icd_p(const struct platform_info_checks *chk)
367 {
368         return chk->has_khr_icd;
369 }
370
371 int plat_is_21(const struct platform_info_checks *chk)
372 {
373         return !(chk->plat_version < 21);
374 }
375
376 #define PINFO_COND(symbol, name, sfx, typ, funcptr) { symbol, #symbol, "Platform " name, sfx, &platform_info_##typ, &funcptr }
377 #define PINFO(symbol, name, sfx, typ) { symbol, #symbol, "Platform " name, sfx, &platform_info_##typ, NULL }
378 struct platform_info_traits pinfo_traits[] = {
379         PINFO(CL_PLATFORM_NAME, "Name", NULL, str),
380         PINFO(CL_PLATFORM_VENDOR, "Vendor", NULL, str),
381         PINFO(CL_PLATFORM_VERSION, "Version", NULL, str),
382         PINFO(CL_PLATFORM_PROFILE, "Profile", NULL, str),
383         PINFO(CL_PLATFORM_EXTENSIONS, "Extensions", NULL, str),
384         PINFO_COND(CL_PLATFORM_HOST_TIMER_RESOLUTION, "Host timer resolution", "ns", ulong, plat_is_21),
385         PINFO_COND(CL_PLATFORM_ICD_SUFFIX_KHR, "Extensions function suffix", NULL, str, khr_icd_p)
386 };
387
388 /* Print platform info and prepare arrays for device info */
389 void
390 printPlatformInfo(cl_uint p)
391 {
392         cl_platform_id pid = platform[p];
393         size_t len = 0;
394
395         struct platform_info_checks pinfo_checks = { 0, 10 };
396
397         current_function = __func__;
398
399         for (current_line = 0; current_line < ARRAY_SIZE(pinfo_traits); ++current_line) {
400                 const struct platform_info_traits *traits = pinfo_traits + current_line;
401                 const char *pname = (output_mode == CLINFO_HUMAN ?
402                         traits->pname : traits->sname);
403
404                 current_param = traits->sname;
405
406                 if (traits->check_func && !traits->check_func(&pinfo_checks))
407                         continue;
408
409                 cur_sfx = (output_mode == CLINFO_HUMAN && traits->sfx) ? traits->sfx : empty_str;
410
411                 had_error = traits->show_func(pid, traits->param,
412                         pname, &pinfo_checks);
413
414                 if (had_error)
415                         continue;
416
417                 /* post-processing */
418
419                 switch (traits->param) {
420                 case CL_PLATFORM_NAME:
421                         /* Store name for future reference */
422                         len = strlen(strbuf);
423                         ALLOC(pdata[p].pname, len+1, "platform name copy");
424                         /* memcpy instead of strncpy since we already have the len
425                          * and memcpy is possibly more optimized */
426                         memcpy(pdata[p].pname, strbuf, len);
427                         pdata[p].pname[len] = '\0';
428                         break;
429                 case CL_PLATFORM_VERSION:
430                         /* compute numeric value for OpenCL version */
431                         pinfo_checks.plat_version = getOpenCLVersion(strbuf + 7);
432                         break;
433                 case CL_PLATFORM_EXTENSIONS:
434                         pinfo_checks.has_khr_icd = !!strstr(strbuf, "cl_khr_icd");
435                         pdata[p].has_amd_offline = !!strstr(strbuf, "cl_amd_offline_devices");
436                         break;
437                 case CL_PLATFORM_ICD_SUFFIX_KHR:
438                         /* Store ICD suffix for future reference */
439                         len = strlen(strbuf);
440                         ALLOC(pdata[p].sname, len+1, "platform ICD suffix copy");
441                         /* memcpy instead of strncpy since we already have the len
442                          * and memcpy is possibly more optimized */
443                         memcpy(pdata[p].sname, strbuf, len);
444                         pdata[p].sname[len] = '\0';
445                 default:
446                         /* do nothing */
447                         break;
448                 }
449
450         }
451
452         if (pinfo_checks.plat_version > max_plat_version)
453                 max_plat_version = pinfo_checks.plat_version;
454
455         /* if no CL_PLATFORM_ICD_SUFFIX_KHR, use P### as short/symbolic name */
456         if (!pdata[p].sname) {
457 #define SNAME_MAX 32
458                 ALLOC(pdata[p].sname, SNAME_MAX, "platform symbolic name");
459                 snprintf(pdata[p].sname, SNAME_MAX, "P%u", p);
460         }
461
462         len = strlen(pdata[p].sname);
463         if (len > platform_sname_maxlen)
464                 platform_sname_maxlen = len;
465
466         error = clGetDeviceIDs(pid, CL_DEVICE_TYPE_ALL, 0, NULL, &(pdata[p].ndevs));
467         if (error == CL_DEVICE_NOT_FOUND)
468                 pdata[p].ndevs = 0;
469         else
470                 CHECK_ERROR("number of devices");
471
472         num_devs_all += pdata[p].ndevs;
473
474         if (pdata[p].ndevs > maxdevs)
475                 maxdevs = pdata[p].ndevs;
476 }
477
478 int
479 getWGsizes(cl_platform_id pid, cl_device_id dev)
480 {
481         int ret = 0;
482
483 #define RR_ERROR(what) do { \
484         had_error = REPORT_ERROR(what); \
485         if (had_error) { \
486                 ret = error; \
487                 goto out; \
488         } \
489 } while(0)
490
491
492         cl_context_properties ctxpft[] = {
493                 CL_CONTEXT_PLATFORM, (cl_context_properties)pid,
494                 0, 0 };
495         cl_uint cursor = 0;
496         cl_context ctx = NULL;
497         cl_program prg = NULL;
498         cl_kernel krn = NULL;
499
500         ctx = clCreateContext(ctxpft, 1, &dev, NULL, NULL, &error);
501         RR_ERROR("create context");
502         prg = clCreateProgramWithSource(ctx, ARRAY_SIZE(sources), sources, NULL, &error);
503         RR_ERROR("create program");
504         error = clBuildProgram(prg, 1, &dev, NULL, NULL, NULL);
505         had_error = REPORT_ERROR("build program");
506         if (had_error)
507                 ret = error;
508
509         /* for a program build failure, dump the log to stderr before bailing */
510         if (error == CL_BUILD_PROGRAM_FAILURE) {
511                 GET_STRING(clGetProgramBuildInfo, CL_PROGRAM_BUILD_LOG, "CL_PROGRAM_BUILD_LOG", prg, dev);
512                 if (error == CL_SUCCESS) {
513                         fputs("=== CL_PROGRAM_BUILD_LOG ===\n", stderr);
514                         fputs(strbuf, stderr);
515                 }
516         }
517         if (had_error)
518                 goto out;
519
520         for (cursor = 0; cursor < NUM_KERNELS; ++cursor) {
521                 snprintf(strbuf, bufsz, "sum%u", 1<<cursor);
522                 if (cursor == 0)
523                         strbuf[3] = 0; // scalar kernel is called 'sum'
524                 krn = clCreateKernel(prg, strbuf, &error);
525                 RR_ERROR("create kernel");
526                 error = clGetKernelWorkGroupInfo(krn, dev, CL_KERNEL_PREFERRED_WORK_GROUP_SIZE_MULTIPLE,
527                         sizeof(*wgm), wgm + cursor, NULL);
528                 RR_ERROR("get kernel info");
529                 clReleaseKernel(krn);
530                 krn = NULL;
531         }
532
533 out:
534         if (krn)
535                 clReleaseKernel(krn);
536         if (prg)
537                 clReleaseProgram(prg);
538         if (ctx)
539                 clReleaseContext(ctx);
540         return ret;
541 }
542
543 /*
544  * Device properties/extensions used in traits checks, and relevant functions
545  */
546
547 struct device_info_checks {
548         cl_device_type devtype;
549         cl_device_mem_cache_type cachetype;
550         cl_device_local_mem_type lmemtype;
551         cl_bool image_support;
552         cl_bool compiler_available;
553         char has_half[12];
554         char has_double[24];
555         char has_nv[29];
556         char has_amd[30];
557         char has_svm_ext[11];
558         char has_fission[22];
559         char has_atomic_counters[26];
560         char has_image2d_buffer[27];
561         char has_intel_local_thread[30];
562         char has_intel_AME[36];
563         char has_intel_required_subgroup_size[32];
564         char has_altera_dev_temp[29];
565         char has_spir[12];
566         char has_qcom_ext_host_ptr[21];
567         char has_simultaneous_sharing[30];
568         cl_uint dev_version;
569 };
570
571 #define DEFINE_EXT_CHECK(ext) int dev_has_##ext(const struct device_info_checks *chk) \
572 { \
573         return !!(chk->has_##ext[0]); \
574 }
575
576 DEFINE_EXT_CHECK(half)
577 DEFINE_EXT_CHECK(double)
578 DEFINE_EXT_CHECK(nv)
579 DEFINE_EXT_CHECK(amd)
580 DEFINE_EXT_CHECK(svm_ext)
581 DEFINE_EXT_CHECK(fission)
582 DEFINE_EXT_CHECK(atomic_counters)
583 DEFINE_EXT_CHECK(image2d_buffer)
584 DEFINE_EXT_CHECK(intel_local_thread)
585 DEFINE_EXT_CHECK(intel_AME)
586 DEFINE_EXT_CHECK(intel_required_subgroup_size)
587 DEFINE_EXT_CHECK(altera_dev_temp)
588 DEFINE_EXT_CHECK(spir)
589 DEFINE_EXT_CHECK(qcom_ext_host_ptr)
590 DEFINE_EXT_CHECK(simultaneous_sharing)
591
592 /* In the version checks we negate the opposite conditions
593  * instead of double-negating the actual condition
594  */
595
596 // device supports 1.2
597 int dev_is_12(const struct device_info_checks *chk)
598 {
599         return !(chk->dev_version < 12);
600 }
601
602 // device supports 2.0
603 int dev_is_20(const struct device_info_checks *chk)
604 {
605         return !(chk->dev_version < 20);
606 }
607
608 // device supports 2.1
609 int dev_is_21(const struct device_info_checks *chk)
610 {
611         return !(chk->dev_version < 21);
612 }
613
614 // device does not support 2.0
615 int dev_not_20(const struct device_info_checks *chk)
616 {
617         return !(chk->dev_version >= 20);
618 }
619
620
621 int dev_is_gpu(const struct device_info_checks *chk)
622 {
623         return !!(chk->devtype & CL_DEVICE_TYPE_GPU);
624 }
625
626 int dev_is_gpu_amd(const struct device_info_checks *chk)
627 {
628         return dev_is_gpu(chk) && dev_has_amd(chk);
629 }
630
631 int dev_has_svm(const struct device_info_checks *chk)
632 {
633         return dev_is_20(chk) || dev_has_svm_ext(chk);
634 }
635
636 int dev_has_partition(const struct device_info_checks *chk)
637 {
638         return dev_is_12(chk) || dev_has_fission(chk);
639 }
640
641 int dev_has_cache(const struct device_info_checks *chk)
642 {
643         return chk->cachetype != CL_NONE;
644 }
645
646 int dev_has_lmem(const struct device_info_checks *chk)
647 {
648         return chk->lmemtype != CL_NONE;
649 }
650
651 int dev_has_images(const struct device_info_checks *chk)
652 {
653         return chk->image_support;
654 }
655
656 int dev_has_images_12(const struct device_info_checks *chk)
657 {
658         return dev_has_images(chk) && dev_is_12(chk);
659 }
660
661 int dev_has_images_20(const struct device_info_checks *chk)
662 {
663         return dev_has_images(chk) && dev_is_20(chk);
664 }
665
666 int dev_has_compiler(const struct device_info_checks *chk)
667 {
668         return chk->compiler_available;
669 }
670
671
672 void identify_device_extensions(const char *extensions, struct device_info_checks *chk)
673 {
674 #define _HAS_EXT(ext) (strstr(extensions, ext))
675 #define HAS_EXT(ext) _HAS_EXT(#ext)
676 #define CPY_EXT(what, ext) do { \
677         strncpy(chk->has_##what, has, sizeof(ext)); \
678         chk->has_##what[sizeof(ext)-1] = '\0'; \
679 } while (0)
680 #define CHECK_EXT(what, ext) do { \
681         has = _HAS_EXT(#ext); \
682         if (has) CPY_EXT(what, #ext); \
683 } while(0)
684
685         char *has;
686         CHECK_EXT(half, cl_khr_fp16);
687         CHECK_EXT(spir, cl_khr_spir);
688         CHECK_EXT(double, cl_khr_fp64);
689         if (!dev_has_double(chk))
690                 CHECK_EXT(double, cl_amd_fp64);
691         if (!dev_has_double(chk))
692                 CHECK_EXT(double, cl_APPLE_fp64_basic_ops);
693         CHECK_EXT(nv, cl_nv_device_attribute_query);
694         CHECK_EXT(amd, cl_amd_device_attribute_query);
695         CHECK_EXT(svm_ext, cl_amd_svm);
696         CHECK_EXT(fission, cl_ext_device_fission);
697         CHECK_EXT(atomic_counters, cl_ext_atomic_counters_64);
698         if (dev_has_atomic_counters(chk))
699                 CHECK_EXT(atomic_counters, cl_ext_atomic_counters_32);
700         CHECK_EXT(image2d_buffer, cl_khr_image2d_from_buffer);
701         CHECK_EXT(intel_local_thread, cl_intel_exec_by_local_thread);
702         CHECK_EXT(intel_AME, cl_intel_advanced_motion_estimation);
703         CHECK_EXT(intel_required_subgroup_size, cl_intel_required_subgroup_size);
704         CHECK_EXT(altera_dev_temp, cl_altera_device_temperature);
705         CHECK_EXT(qcom_ext_host_ptr, cl_qcom_ext_host_ptr);
706         CHECK_EXT(simultaneous_sharing, cl_intel_simultaneous_sharing);
707 }
708
709
710
711 /*
712  * Device info print functions
713  */
714
715 #define _GET_VAL \
716         error = clGetDeviceInfo(dev, param, sizeof(val), &val, NULL); \
717         had_error = REPORT_ERROR2("get %s");
718
719 #define _GET_VAL_ARRAY \
720         error = clGetDeviceInfo(dev, param, 0, NULL, &szval); \
721         had_error = REPORT_ERROR2("get number of %s"); \
722         numval = szval/sizeof(val); \
723         if (!had_error) { \
724                 REALLOC(val, numval, current_param); \
725                 error = clGetDeviceInfo(dev, param, szval, val, NULL); \
726                 had_error = REPORT_ERROR("get %s"); \
727         }
728
729 #define GET_VAL do { \
730         _GET_VAL \
731 } while (0)
732
733 #define GET_VAL_ARRAY do { \
734         _GET_VAL_ARRAY \
735 } while (0)
736
737 #define _FMT_VAL(fmt) \
738         if (had_error) \
739                 show_strbuf(pname, 0); \
740         else \
741                 printf("%s" I1_STR fmt "%s\n", line_pfx, pname, val, cur_sfx);
742
743 #define FMT_VAL(fmt) do { \
744         _FMT_VAL(fmt) \
745 } while (0)
746
747 #define SHOW_VAL(fmt) do { \
748         _GET_VAL \
749         _FMT_VAL(fmt) \
750 } while (0)
751
752 #define DEFINE_DEVINFO_SHOW(how, type, fmt) \
753 int device_info_##how(cl_device_id dev, cl_device_info param, const char *pname, \
754         const struct device_info_checks *chk UNUSED) \
755 { \
756         type val = 0; \
757         SHOW_VAL(fmt); \
758         return had_error; \
759 }
760
761 /* Get string-type info without showing it */
762 int device_info_str_get(cl_device_id dev, cl_device_info param, const char *pname,
763         const struct device_info_checks *chk UNUSED)
764 {
765         current_param = pname;
766         error = clGetDeviceInfo(dev, param, 0, NULL, &nusz);
767         if (nusz > bufsz) {
768                 REALLOC(strbuf, nusz, current_param);
769                 bufsz = nusz;
770         }
771         had_error = REPORT_ERROR2("get %s size");
772         if (!had_error) {
773                 error = clGetDeviceInfo(dev, param, bufsz, strbuf, NULL);
774                 had_error = REPORT_ERROR2("get %s");
775         }
776         return had_error;
777 }
778
779 int device_info_str(cl_device_id dev, cl_device_info param, const char *pname,
780         const struct device_info_checks *chk)
781 {
782         had_error = device_info_str_get(dev, param, pname, chk);
783         show_strbuf(pname, 1);
784         return had_error;
785 }
786
787 DEFINE_DEVINFO_SHOW(int, cl_uint, "%u")
788 DEFINE_DEVINFO_SHOW(hex, cl_uint, "0x%x")
789 DEFINE_DEVINFO_SHOW(long, cl_ulong, "%" PRIu64)
790 DEFINE_DEVINFO_SHOW(sz, size_t, "%" PRIuS)
791
792 int device_info_bool(cl_device_id dev, cl_device_info param, const char *pname,
793         const struct device_info_checks *chk UNUSED)
794 {
795         cl_bool val = 0;
796         const char * const * str = (output_mode == CLINFO_HUMAN ?
797                 bool_str : bool_raw_str);
798         GET_VAL;
799         if (had_error)
800                 show_strbuf(pname, 0);
801         else {
802                 printf("%s" I1_STR "%s%s\n", line_pfx, pname, str[val], cur_sfx);
803                 /* abuse strbuf to pass the bool value up to the caller,
804                  * this is used e.g. by CL_DEVICE_IMAGE_SUPPORT
805                  */
806                 memcpy(strbuf, &val, sizeof(val));
807         }
808         return had_error;
809 }
810
811 int device_info_bits(cl_device_id dev, cl_device_info param, const char *pname,
812         const struct device_info_checks *chk UNUSED)
813 {
814         cl_uint val;
815         GET_VAL;
816         if (!had_error)
817                 sprintf(strbuf, "%u bits (%u bytes)", val, val/8);
818         show_strbuf(pname, 0);
819         return had_error;
820 }
821
822
823 size_t strbuf_mem(cl_ulong val, size_t szval)
824 {
825         double dbl = val;
826         size_t sfx = 0;
827         while (dbl > 1024 && sfx < memsfx_count) {
828                 dbl /= 1024;
829                 ++sfx;
830         }
831         return sprintf(strbuf + szval, " (%.4lg%s)",
832                 dbl, memsfx[sfx]);
833 }
834
835 int device_info_mem(cl_device_id dev, cl_device_info param, const char *pname,
836         const struct device_info_checks *chk UNUSED)
837 {
838         cl_ulong val = 0;
839         size_t szval = 0;
840         GET_VAL;
841         if (!had_error) {
842                 szval += sprintf(strbuf, "%" PRIu64, val);
843                 if (output_mode == CLINFO_HUMAN && val > 1024)
844                         strbuf_mem(val, szval);
845         }
846         show_strbuf(pname, 0);
847         return had_error;
848 }
849
850 int device_info_mem_int(cl_device_id dev, cl_device_info param, const char *pname,
851         const struct device_info_checks *chk UNUSED)
852 {
853         cl_uint val = 0;
854         size_t szval = 0;
855         GET_VAL;
856         if (!had_error) {
857                 szval += sprintf(strbuf, "%u", val);
858                 if (output_mode == CLINFO_HUMAN && val > 1024)
859                         strbuf_mem(val, szval);
860         }
861         show_strbuf(pname, 0);
862         return had_error;
863 }
864
865 int device_info_free_mem_amd(cl_device_id dev, cl_device_info param, const char *pname,
866         const struct device_info_checks *chk UNUSED)
867 {
868         size_t *val = NULL;
869         size_t szval = 0, numval = 0;
870         GET_VAL_ARRAY;
871         if (!had_error) {
872                 size_t cursor = 0;
873                 szval = 0;
874                 for (cursor = 0; cursor < numval; ++cursor) {
875                         if (szval > 0) {
876                                 strbuf[szval] = ' ';
877                                 ++szval;
878                         }
879                         szval += sprintf(strbuf + szval, "%" PRIuS, val[cursor]);
880                         if (output_mode == CLINFO_HUMAN)
881                                 szval += strbuf_mem(val[cursor]*UINT64_C(1024), szval);
882                 }
883         }
884         show_strbuf(pname, 0);
885         free(val);
886         return had_error;
887 }
888
889 int device_info_time_offset(cl_device_id dev, cl_device_info param, const char *pname,
890         const struct device_info_checks *chk UNUSED)
891 {
892         cl_ulong val = 0;
893         GET_VAL;
894         if (!had_error) {
895                 size_t szval = 0;
896                 time_t time = val/UINT64_C(1000000000);
897                 szval += snprintf(strbuf, bufsz, "%" PRIu64 "ns (", val);
898                 szval += bufcpy(szval, ctime(&time));
899                 /* overwrite ctime's newline with the closing parenthesis */
900                 if (szval < bufsz)
901                         strbuf[szval - 1] = ')';
902         }
903         show_strbuf(pname, 0);
904         return had_error;
905 }
906
907 int device_info_szptr(cl_device_id dev, cl_device_info param, const char *pname,
908         const struct device_info_checks *chk UNUSED)
909 {
910         size_t *val = NULL;
911         size_t szval = 0, numval = 0;
912         GET_VAL_ARRAY;
913         if (!had_error) {
914                 size_t counter = 0;
915                 set_separator(output_mode == CLINFO_HUMAN ? times_str : spc_str);
916                 szval = 0;
917                 for (counter = 0; counter < numval; ++counter) {
918                         add_separator(&szval);
919                         szval += snprintf(strbuf + szval, bufsz - szval - 1, "%" PRIuS, val[counter]);
920                         if (szval >= bufsz) {
921                                 trunc_strbuf();
922                                 break;
923                         }
924                 }
925         }
926         show_strbuf(pname, 0);
927         free(val);
928         return had_error;
929 }
930
931 int device_info_wg(cl_device_id dev, cl_device_info param UNUSED, const char *pname,
932         const struct device_info_checks *chk UNUSED)
933 {
934         cl_platform_id val = NULL;
935         {
936                 /* shadow */
937                 cl_device_info param = CL_DEVICE_PLATFORM;
938                 current_param = "CL_DEVICE_PLATFORM";
939                 GET_VAL;
940         }
941         current_param = pname;
942         if (!had_error)
943                 had_error = getWGsizes(val, dev);
944         if (!had_error) {
945                 sprintf(strbuf, "%" PRIuS, wgm[0]);
946         }
947         show_strbuf(pname, 0);
948         return had_error;
949 }
950
951 int device_info_img_sz_2d(cl_device_id dev, cl_device_info param, const char *pname,
952         const struct device_info_checks *chk UNUSED)
953 {
954         size_t width = 0, height = 0, val = 0;
955         GET_VAL; /* HEIGHT */
956         if (!had_error) {
957                 height = val;
958                 param = CL_DEVICE_IMAGE2D_MAX_WIDTH;
959                 current_param = "CL_DEVICE_IMAGE2D_MAX_WIDTH";
960                 GET_VAL;
961                 if (!had_error) {
962                         width = val;
963                         sprintf(strbuf, "%" PRIuS "x%" PRIuS, width, height);
964                 }
965         }
966         show_strbuf(pname, 0);
967         return had_error;
968 }
969
970 int device_info_img_sz_3d(cl_device_id dev, cl_device_info param, const char *pname,
971         const struct device_info_checks *chk UNUSED)
972 {
973         size_t width = 0, height = 0, depth = 0, val = 0;
974         GET_VAL; /* HEIGHT */
975         if (!had_error) {
976                 height = val;
977                 param = CL_DEVICE_IMAGE3D_MAX_WIDTH;
978                 current_param = "CL_DEVICE_IMAGE3D_MAX_WIDTH";
979                 GET_VAL;
980                 if (!had_error) {
981                         width = val;
982                         param = CL_DEVICE_IMAGE3D_MAX_DEPTH;
983                         current_param = "CL_DEVICE_IMAGE3D_MAX_DEPTH";
984                         GET_VAL;
985                         if (!had_error) {
986                                 depth = val;
987                                 sprintf(strbuf, "%" PRIuS "x%" PRIuS "x%" PRIuS,
988                                         width, height, depth);
989                         }
990                 }
991         }
992         show_strbuf(pname, 0);
993         return had_error;
994 }
995
996
997 int device_info_devtype(cl_device_id dev, cl_device_info param, const char *pname,
998         const struct device_info_checks *chk UNUSED)
999 {
1000         cl_device_type val = 0;
1001         GET_VAL;
1002         if (!had_error) {
1003                 /* iterate over device type strings, appending their textual form
1004                  * to strbuf.
1005                  * TODO: check for extra bits/no bits
1006                  */
1007                 cl_uint i = devtype_count - 1; /* skip CL_DEVICE_TYPE_ALL */
1008                 const char * const *devstr = (output_mode == CLINFO_HUMAN ?
1009                         device_type_str : device_type_raw_str);
1010                 size_t szval = 0;
1011                 strbuf[szval] = '\0';
1012                 set_separator(output_mode == CLINFO_HUMAN ? comma_str : vbar_str);
1013                 for (; i > 0; --i) {
1014                         /* assemble CL_DEVICE_TYPE_* from index i */
1015                         cl_device_type cur = (cl_device_type)(1) << (i-1);
1016                         if (val & cur) {
1017                                 /* match: add separator if not first match */
1018                                 add_separator(&szval);
1019                                 szval += bufcpy(szval, devstr[i]);
1020                         }
1021                 }
1022         }
1023         show_strbuf(pname, 0);
1024         /* we abuse global strbuf to pass the device type over to the caller */
1025         if (!had_error)
1026                 memcpy(strbuf, &val, sizeof(val));
1027         return had_error;
1028 }
1029
1030 int device_info_cachetype(cl_device_id dev, cl_device_info param, const char *pname,
1031         const struct device_info_checks *chk UNUSED)
1032 {
1033         cl_device_mem_cache_type val = 0;
1034         GET_VAL;
1035         if (!had_error) {
1036                 const char * const *ar = (output_mode == CLINFO_HUMAN ?
1037                         cache_type_str : cache_type_raw_str);
1038                 bufcpy(0, ar[val]);
1039         }
1040         show_strbuf(pname, 0);
1041         /* we abuse global strbuf to pass the cache type over to the caller */
1042         if (!had_error)
1043                 memcpy(strbuf, &val, sizeof(val));
1044         return had_error;
1045 }
1046
1047 int device_info_lmemtype(cl_device_id dev, cl_device_info param, const char *pname,
1048         const struct device_info_checks *chk UNUSED)
1049 {
1050         cl_device_local_mem_type val = 0;
1051         GET_VAL;
1052         if (!had_error) {
1053                 const char * const *ar = (output_mode == CLINFO_HUMAN ?
1054                         lmem_type_str : lmem_type_raw_str);
1055                 bufcpy(0, ar[val]);
1056         }
1057         show_strbuf(pname, 0);
1058         /* we abuse global strbuf to pass the lmem type over to the caller */
1059         if (!had_error)
1060                 memcpy(strbuf, &val, sizeof(val));
1061         return had_error;
1062 }
1063
1064 /* stringify a cl_device_topology_amd */
1065 void devtopo_str(const cl_device_topology_amd *devtopo)
1066 {
1067         switch (devtopo->raw.type) {
1068         case 0:
1069                 if (output_mode == CLINFO_HUMAN)
1070                         sprintf(strbuf, "(%s)", na);
1071                 else
1072                         sprintf(strbuf, none_raw);
1073                 break;
1074         case CL_DEVICE_TOPOLOGY_TYPE_PCIE_AMD:
1075                 sprintf(strbuf, "PCI-E, %02x:%02x.%u",
1076                         (cl_uchar)(devtopo->pcie.bus),
1077                         devtopo->pcie.device, devtopo->pcie.function);
1078                 break;
1079         default:
1080                 sprintf(strbuf, "<unknown (%u): %u %u %u %u %u>",
1081                         devtopo->raw.type,
1082                         devtopo->raw.data[0], devtopo->raw.data[1],
1083                         devtopo->raw.data[2],
1084                         devtopo->raw.data[3], devtopo->raw.data[4]);
1085         }
1086 }
1087
1088 int device_info_devtopo_amd(cl_device_id dev, cl_device_info param, const char *pname,
1089         const struct device_info_checks *chk UNUSED)
1090 {
1091         cl_device_topology_amd val;
1092         GET_VAL;
1093         /* TODO how to do this in CLINFO_RAW mode */
1094         if (!had_error) {
1095                 devtopo_str(&val);
1096         }
1097         show_strbuf(pname, 0);
1098         return had_error;
1099 }
1100
1101 /* we assemble a cl_device_topology_amd struct from the NVIDIA info */
1102 int device_info_devtopo_nv(cl_device_id dev, cl_device_info param, const char *pname,
1103         const struct device_info_checks *chk UNUSED)
1104 {
1105         cl_device_topology_amd devtopo;
1106         cl_uint val = 0;
1107
1108         devtopo.raw.type = CL_DEVICE_TOPOLOGY_TYPE_PCIE_AMD;
1109
1110         GET_VAL; /* CL_DEVICE_PCI_BUS_ID_NV */
1111
1112         if (!had_error) {
1113                 devtopo.pcie.bus = val & 0xff;
1114
1115                 param = CL_DEVICE_PCI_SLOT_ID_NV;
1116                 current_param = "CL_DEVICE_PCI_SLOT_ID_NV";
1117
1118                 GET_VAL;
1119
1120                 if (!had_error) {
1121                         devtopo.pcie.device = val >> 3;
1122                         devtopo.pcie.function = val & 7;
1123                         devtopo_str(&devtopo);
1124                 }
1125         }
1126
1127         show_strbuf(pname, 0);
1128         return had_error;
1129 }
1130
1131 /* NVIDIA Compute Capability */
1132 int device_info_cc_nv(cl_device_id dev, cl_device_info param, const char *pname,
1133         const struct device_info_checks *chk UNUSED)
1134 {
1135         cl_uint major = 0, val = 0;
1136         GET_VAL; /* MAJOR */
1137         if (!had_error) {
1138                 major = val;
1139                 param = CL_DEVICE_COMPUTE_CAPABILITY_MINOR_NV;
1140                 current_param = "CL_DEVICE_COMPUTE_CAPABILITY_MINOR_NV";
1141                 GET_VAL;
1142                 if (!had_error)
1143                         snprintf(strbuf, bufsz, "%u.%u", major, val);
1144         }
1145
1146         show_strbuf(pname, 0);
1147         return had_error;
1148 }
1149
1150 /* AMD GFXIP */
1151 int device_info_gfxip_amd(cl_device_id dev, cl_device_info param, const char *pname,
1152         const struct device_info_checks *chk UNUSED)
1153 {
1154         cl_uint major = 0, val = 0;
1155         GET_VAL; /* MAJOR */
1156         if (!had_error) {
1157                 major = val;
1158                 param = CL_DEVICE_GFXIP_MINOR_AMD;
1159                 current_param = "CL_DEVICE_GFXIP_MINOR_AMD";
1160                 GET_VAL;
1161                 if (!had_error)
1162                         snprintf(strbuf, bufsz, "%u.%u", major, val);
1163         }
1164
1165         show_strbuf(pname, 0);
1166         return had_error;
1167 }
1168
1169
1170 /* Device Partition, CLINFO_HUMAN header */
1171 int device_info_partition_header(cl_device_id dev UNUSED, cl_device_info param UNUSED,
1172         const char *pname, const struct device_info_checks *chk)
1173 {
1174         int is_12 = dev_is_12(chk);
1175         int has_fission = dev_has_fission(chk);
1176         size_t szval = snprintf(strbuf, bufsz, "(%s%s%s)",
1177                 (is_12 ? core : empty_str),
1178                 (is_12 && has_fission ? comma_str : empty_str),
1179                 chk->has_fission);
1180         if (szval >= bufsz)
1181                 trunc_strbuf();
1182
1183         show_strbuf(pname, 0);
1184         had_error = CL_SUCCESS;
1185         return had_error;
1186 }
1187
1188 /* Device partition properties */
1189 int device_info_partition_types(cl_device_id dev, cl_device_info param, const char *pname,
1190         const struct device_info_checks *chk UNUSED)
1191 {
1192         size_t numval = 0, szval = 0, cursor = 0, slen = 0;
1193         cl_device_partition_property *val = NULL;
1194         const char * const *ptstr = (output_mode == CLINFO_HUMAN ?
1195                 partition_type_str : partition_type_raw_str);
1196
1197         set_separator(output_mode == CLINFO_HUMAN ? comma_str : vbar_str);
1198
1199         GET_VAL_ARRAY;
1200
1201         szval = 0;
1202         if (!had_error) {
1203                 for (cursor = 0; cursor < numval; ++cursor) {
1204                         int str_idx = -1;
1205
1206                         /* add separator for values past the first */
1207                         add_separator(&szval);
1208
1209                         switch (val[cursor]) {
1210                         case 0: str_idx = 1; break;
1211                         case CL_DEVICE_PARTITION_EQUALLY: str_idx = 2; break;
1212                         case CL_DEVICE_PARTITION_BY_COUNTS: str_idx = 3; break;
1213                         case CL_DEVICE_PARTITION_BY_AFFINITY_DOMAIN: str_idx = 4; break;
1214                         case CL_DEVICE_PARTITION_BY_NAMES_INTEL: str_idx = 5; break;
1215                         default:
1216                                 szval += snprintf(strbuf + szval, bufsz - szval - 1, "by <unknown> (0x%" PRIXPTR ")", val[cursor]);
1217                                 break;
1218                         }
1219                         if (str_idx > 0) {
1220                                 /* string length, minus _EXT */
1221                                 slen = strlen(ptstr[str_idx]);
1222                                 if (output_mode == CLINFO_RAW && str_idx > 1)
1223                                         slen -= 4;
1224                                 szval += bufcpy_len(szval, ptstr[str_idx], slen);
1225                         }
1226                         if (szval >= bufsz) {
1227                                 trunc_strbuf();
1228                                 break;
1229                         }
1230                 }
1231                 if (szval == 0) {
1232                         bufcpy(szval, ptstr[0]);
1233                 } else if (szval < bufsz)
1234                         strbuf[szval] = '\0';
1235         }
1236
1237         show_strbuf(pname, 0);
1238
1239         free(val);
1240         return had_error;
1241 }
1242
1243 int device_info_partition_types_ext(cl_device_id dev, cl_device_info param, const char *pname,
1244         const struct device_info_checks *chk UNUSED)
1245 {
1246         size_t numval = 0, szval = 0, cursor = 0, slen = 0;
1247         cl_device_partition_property_ext *val = NULL;
1248         const char * const *ptstr = (output_mode == CLINFO_HUMAN ?
1249                 partition_type_str : partition_type_raw_str);
1250
1251         set_separator(output_mode == CLINFO_HUMAN ? comma_str : vbar_str);
1252
1253         GET_VAL_ARRAY;
1254
1255         szval = 0;
1256         if (!had_error) {
1257                 for (cursor = 0; cursor < numval; ++cursor) {
1258                         int str_idx = -1;
1259
1260                         /* add separator for values past the first */
1261                         add_separator(&szval);
1262
1263                         switch (val[cursor]) {
1264                         case 0: str_idx = 1; break;
1265                         case CL_DEVICE_PARTITION_EQUALLY_EXT: str_idx = 2; break;
1266                         case CL_DEVICE_PARTITION_BY_COUNTS_EXT: str_idx = 3; break;
1267                         case CL_DEVICE_PARTITION_BY_AFFINITY_DOMAIN_EXT: str_idx = 4; break;
1268                         case CL_DEVICE_PARTITION_BY_NAMES_EXT: str_idx = 5; break;
1269                         default:
1270                                 szval += snprintf(strbuf + szval, bufsz - szval - 1, "by <unknown> (0x%" PRIX64 ")", val[cursor]);
1271                                 break;
1272                         }
1273                         if (str_idx > 0) {
1274                                 /* string length */
1275                                 slen = strlen(ptstr[str_idx]);
1276                                 strncpy(strbuf + szval, ptstr[str_idx], slen);
1277                                 szval += slen;
1278                         }
1279                         if (szval >= bufsz) {
1280                                 trunc_strbuf();
1281                                 break;
1282                         }
1283                 }
1284                 if (szval == 0) {
1285                         slen = strlen(ptstr[0]);
1286                         memcpy(strbuf, ptstr[0], slen);
1287                         szval += slen;
1288                 }
1289                 if (szval < bufsz)
1290                         strbuf[szval] = '\0';
1291         }
1292
1293         show_strbuf(pname, 0);
1294
1295         free(val);
1296         return had_error;
1297 }
1298
1299
1300 /* Device partition affinity domains */
1301 int device_info_partition_affinities(cl_device_id dev, cl_device_info param, const char *pname,
1302         const struct device_info_checks *chk UNUSED)
1303 {
1304         cl_device_affinity_domain val;
1305         GET_VAL;
1306         if (!had_error && val) {
1307                 /* iterate over affinity domain strings appending their textual form
1308                  * to strbuf
1309                  * TODO: check for extra bits/no bits
1310                  */
1311                 size_t szval = 0;
1312                 cl_uint i = 0;
1313                 const char * const *affstr = (output_mode == CLINFO_HUMAN ?
1314                         affinity_domain_str : affinity_domain_raw_str);
1315                 set_separator(output_mode == CLINFO_HUMAN ? comma_str : vbar_str);
1316                 for (i = 0; i < affinity_domain_count; ++i) {
1317                         cl_device_affinity_domain cur = (cl_device_affinity_domain)(1) << i;
1318                         if (val & cur) {
1319                                 /* match: add separator if not first match */
1320                                 add_separator(&szval);
1321                                 szval += bufcpy(szval, affstr[i]);
1322                         }
1323                         if (szval >= bufsz)
1324                                 break;
1325                 }
1326         }
1327         if (val || had_error)
1328                 show_strbuf(pname, 0);
1329         return had_error;
1330 }
1331
1332 int device_info_partition_affinities_ext(cl_device_id dev, cl_device_info param, const char *pname,
1333         const struct device_info_checks *chk UNUSED)
1334 {
1335         size_t numval = 0, szval = 0, cursor = 0, slen = 0;
1336         cl_device_partition_property_ext *val = NULL;
1337         const char * const *ptstr = (output_mode == CLINFO_HUMAN ?
1338                 affinity_domain_ext_str : affinity_domain_raw_ext_str);
1339
1340         set_separator(output_mode == CLINFO_HUMAN ? comma_str : vbar_str);
1341
1342         GET_VAL_ARRAY;
1343
1344         szval = 0;
1345         if (!had_error) {
1346                 for (cursor = 0; cursor < numval; ++cursor) {
1347                         int str_idx = -1;
1348
1349                         /* add separator for values past the first */
1350                         add_separator(&szval);
1351
1352                         switch (val[cursor]) {
1353                         case CL_AFFINITY_DOMAIN_NUMA_EXT: str_idx = 0; break;
1354                         case CL_AFFINITY_DOMAIN_L4_CACHE_EXT: str_idx = 1; break;
1355                         case CL_AFFINITY_DOMAIN_L3_CACHE_EXT: str_idx = 2; break;
1356                         case CL_AFFINITY_DOMAIN_L2_CACHE_EXT: str_idx = 3; break;
1357                         case CL_AFFINITY_DOMAIN_L1_CACHE_EXT: str_idx = 4; break;
1358                         case CL_AFFINITY_DOMAIN_NEXT_FISSIONABLE_EXT: str_idx = 5; break;
1359                         default:
1360                                 szval += snprintf(strbuf + szval, bufsz - szval - 1, "<unknown> (0x%" PRIX64 ")", val[cursor]);
1361                                 break;
1362                         }
1363                         if (str_idx >= 0) {
1364                                 /* string length */
1365                                 const char *str = ptstr[str_idx];
1366                                 slen = strlen(str);
1367                                 strncpy(strbuf + szval, str, slen);
1368                                 szval += slen;
1369                         }
1370                         if (szval >= bufsz) {
1371                                 trunc_strbuf();
1372                                 break;
1373                         }
1374                 }
1375                 strbuf[szval] = '\0';
1376         }
1377
1378         show_strbuf(pname, 0);
1379
1380         free(val);
1381         return had_error;
1382 }
1383
1384 /* Preferred / native vector widths */
1385 int device_info_vecwidth(cl_device_id dev, cl_device_info param, const char *pname,
1386         const struct device_info_checks *chk)
1387 {
1388         cl_uint preferred = 0, val = 0;
1389         GET_VAL;
1390         if (!had_error) {
1391                 preferred = val;
1392
1393                 /* we get called with PREFERRED, NATIVE is at +0x30 offset, except for HALF,
1394                  * which is at +0x08 */
1395                 param += (param == CL_DEVICE_PREFERRED_VECTOR_WIDTH_HALF ? 0x08 : 0x30);
1396                 /* TODO update current_param */
1397                 GET_VAL;
1398
1399                 if (!had_error) {
1400                         size_t szval = 0;
1401                         const char *ext = (param == CL_DEVICE_NATIVE_VECTOR_WIDTH_HALF ?
1402                                 chk->has_half : (param == CL_DEVICE_NATIVE_VECTOR_WIDTH_DOUBLE ?
1403                                 chk->has_double : NULL));
1404                         szval = sprintf(strbuf, "%8u / %-8u", preferred, val);
1405                         if (ext)
1406                                 sprintf(strbuf + szval, " (%s)", *ext ? ext : na);
1407                 }
1408         }
1409         show_strbuf(pname, 0);
1410         return had_error;
1411 }
1412
1413 /* Floating-point configurations */
1414 int device_info_fpconf(cl_device_id dev, cl_device_info param, const char *pname,
1415         const struct device_info_checks *chk)
1416 {
1417         cl_device_fp_config val = 0;
1418         int get_it = (
1419                 (param == CL_DEVICE_SINGLE_FP_CONFIG) ||
1420                 (param == CL_DEVICE_HALF_FP_CONFIG && dev_has_half(chk)) ||
1421                 (param == CL_DEVICE_DOUBLE_FP_CONFIG && dev_has_double(chk)));
1422         if (get_it)
1423                 GET_VAL;
1424         else
1425                 had_error = CL_SUCCESS;
1426
1427         if (!had_error) {
1428                 size_t szval = 0;
1429                 cl_uint i = 0;
1430                 const char * const *fpstr = (output_mode == CLINFO_HUMAN ?
1431                         fp_conf_str : fp_conf_raw_str);
1432                 set_separator(vbar_str);
1433                 if (output_mode == CLINFO_HUMAN) {
1434                         const char *why = na;
1435                         switch (param) {
1436                         case CL_DEVICE_HALF_FP_CONFIG:
1437                                 if (get_it)
1438                                         why = chk->has_half;
1439                                 break;
1440                         case CL_DEVICE_SINGLE_FP_CONFIG:
1441                                 why = core;
1442                                 break;
1443                         case CL_DEVICE_DOUBLE_FP_CONFIG:
1444                                 if (get_it)
1445                                         why = chk->has_double;
1446                                 break;
1447                         default:
1448                                 /* "this can't happen" (unless OpenCL starts supporting _other_ floating-point formats, maybe) */
1449                                 fprintf(stderr, "unsupported floating-point configuration parameter %s\n", pname);
1450
1451                         }
1452                         /* show 'why' it's being shown */
1453                         szval += sprintf(strbuf, "(%s)", why);
1454                 }
1455                 if (get_it) {
1456                         for (i = 0; i < fp_conf_count; ++i) {
1457                                 cl_device_fp_config cur = (cl_device_fp_config)(1) << i;
1458                                 if (output_mode == CLINFO_HUMAN) {
1459                                         szval += sprintf(strbuf + szval, "\n%s" I2_STR "%s",
1460                                                 line_pfx, fpstr[i], bool_str[!!(val & cur)]);
1461                                 } else if (val & cur) {
1462                                         add_separator(&szval);
1463                                         szval += bufcpy(szval, fpstr[i]);
1464                                 }
1465                         }
1466                 }
1467         }
1468
1469         /* only print this for HUMAN output or if we actually got the value */
1470         if (output_mode == CLINFO_HUMAN || get_it)
1471                 show_strbuf(pname, 0);
1472         return had_error;
1473 }
1474
1475 /* Queue properties */
1476 int device_info_qprop(cl_device_id dev, cl_device_info param, const char *pname,
1477         const struct device_info_checks *chk)
1478 {
1479         cl_command_queue_properties val = 0;
1480         GET_VAL;
1481         if (!had_error) {
1482                 size_t szval = 0;
1483                 cl_uint i = 0;
1484                 const char * const *qpstr = (output_mode == CLINFO_HUMAN ?
1485                         queue_prop_str : queue_prop_raw_str);
1486                 set_separator(vbar_str);
1487                 for (i = 0; i < queue_prop_count; ++i) {
1488                         cl_command_queue_properties cur = (cl_command_queue_properties)(1) << i;
1489                         if (output_mode == CLINFO_HUMAN) {
1490                                 szval += sprintf(strbuf + szval, "\n%s" I2_STR "%s",
1491                                         line_pfx, qpstr[i], bool_str[!!(val & cur)]);
1492                         } else if (val & cur) {
1493                                 add_separator(&szval);
1494                                 szval += bufcpy(szval, qpstr[i]);
1495                         }
1496                 }
1497                 if (output_mode == CLINFO_HUMAN && param == CL_DEVICE_QUEUE_PROPERTIES &&
1498                         dev_has_intel_local_thread(chk))
1499                         sprintf(strbuf + szval, "\n%s" I2_STR "%s",
1500                                 line_pfx, "Local thread execution (Intel)", bool_str[CL_TRUE]);
1501         }
1502         show_strbuf(pname, 0);
1503         return had_error;
1504 }
1505
1506 /* Execution capbilities */
1507 int device_info_execap(cl_device_id dev, cl_device_info param, const char *pname,
1508         const struct device_info_checks *chk UNUSED)
1509 {
1510         cl_device_exec_capabilities val = 0;
1511         GET_VAL;
1512         if (!had_error) {
1513                 size_t szval = 0;
1514                 cl_uint i = 0;
1515                 const char * const *qpstr = (output_mode == CLINFO_HUMAN ?
1516                         execap_str : execap_raw_str);
1517                 set_separator(vbar_str);
1518                 for (i = 0; i < execap_count; ++i) {
1519                         cl_device_exec_capabilities cur = (cl_device_exec_capabilities)(1) << i;
1520                         if (output_mode == CLINFO_HUMAN) {
1521                                 szval += sprintf(strbuf + szval, "\n%s" I2_STR "%s",
1522                                         line_pfx, qpstr[i], bool_str[!!(val & cur)]);
1523                         } else if (val & cur) {
1524                                 add_separator(&szval);
1525                                 szval += bufcpy(szval, qpstr[i]);
1526                         }
1527                 }
1528         }
1529         show_strbuf(pname, 0);
1530         return had_error;
1531 }
1532
1533 /* Arch bits and endianness (HUMAN) */
1534 int device_info_arch(cl_device_id dev, cl_device_info param, const char *pname,
1535         const struct device_info_checks *chk UNUSED)
1536 {
1537         cl_uint bits = 0;
1538         {
1539                 cl_uint val = 0;
1540                 GET_VAL;
1541                 if (!had_error)
1542                         bits = val;
1543         }
1544         if (!had_error) {
1545                 cl_bool val = 0;
1546                 param = CL_DEVICE_ENDIAN_LITTLE;
1547                 current_param = "CL_DEVICE_ENDIAN_LITTLE";
1548                 GET_VAL;
1549                 if (!had_error)
1550                         sprintf(strbuf, "%u, %s", bits, endian_str[val]);
1551         }
1552         show_strbuf(pname, 0);
1553         return had_error;
1554 }
1555
1556 /* SVM capabilities */
1557 int device_info_svm_cap(cl_device_id dev, cl_device_info param, const char *pname,
1558         const struct device_info_checks *chk)
1559 {
1560         cl_device_svm_capabilities val = 0;
1561         int is_20 = dev_is_20(chk);
1562         int has_svm_ext = dev_has_svm_ext(chk);
1563
1564         GET_VAL;
1565
1566         if (!had_error) {
1567                 size_t szval = 0;
1568                 cl_uint i = 0;
1569                 const char * const *scstr = (output_mode == CLINFO_HUMAN ?
1570                         svm_cap_str : svm_cap_raw_str);
1571                 set_separator(vbar_str);
1572                 if (output_mode == CLINFO_HUMAN) {
1573                         /* show 'why' it's being shown */
1574                         szval += sprintf(strbuf, "(%s%s%s)",
1575                                 (is_20 ? core : empty_str),
1576                                 (is_20 && has_svm_ext ? comma_str : empty_str),
1577                                 chk->has_svm_ext);
1578                 }
1579                 for (i = 0; i < svm_cap_count; ++i) {
1580                         cl_device_svm_capabilities cur = (cl_device_svm_capabilities)(1) << i;
1581                         if (output_mode == CLINFO_HUMAN) {
1582                                 szval += sprintf(strbuf + szval, "\n%s" I2_STR "%s",
1583                                         line_pfx, scstr[i], bool_str[!!(val & cur)]);
1584                         } else if (val & cur) {
1585                                 add_separator(&szval);
1586                                 szval += bufcpy(szval, scstr[i]);
1587                         }
1588                 }
1589         }
1590
1591         show_strbuf(pname, 0);
1592         return had_error;
1593 }
1594
1595 /*
1596  * Device info traits
1597  */
1598
1599 /* A CL_FALSE param means "just print pname" */
1600
1601 struct device_info_traits {
1602         enum output_modes output_mode;
1603         cl_device_info param; // CL_DEVICE_*
1604         const char *sname; // "CL_DEVICE_*"
1605         const char *pname; // "Device *"
1606         const char *sfx; // suffix for the output in non-raw mode
1607         /* pointer to function that shows the parameter */
1608         int (*show_func)(cl_device_id dev, cl_device_info param, const char *pname, const struct device_info_checks *);
1609         /* pointer to function that checks if the parameter should be checked */
1610         int (*check_func)(const struct device_info_checks *);
1611 };
1612
1613 #define DINFO_SFX(symbol, name, sfx, typ) symbol, #symbol, name, sfx, device_info_##typ
1614 #define DINFO(symbol, name, typ) symbol, #symbol, name, NULL, device_info_##typ
1615
1616 struct device_info_traits dinfo_traits[] = {
1617         { CLINFO_BOTH, DINFO(CL_DEVICE_NAME, "Device Name", str), NULL },
1618         { CLINFO_BOTH, DINFO(CL_DEVICE_VENDOR, "Device Vendor", str), NULL },
1619         { CLINFO_BOTH, DINFO(CL_DEVICE_VENDOR_ID, "Device Vendor ID", hex), NULL },
1620         { CLINFO_BOTH, DINFO(CL_DEVICE_VERSION, "Device Version", str), NULL },
1621         { CLINFO_BOTH, DINFO(CL_DRIVER_VERSION, "Driver Version", str), NULL },
1622         { CLINFO_BOTH, DINFO(CL_DEVICE_OPENCL_C_VERSION, "Device OpenCL C Version", str), NULL },
1623         { CLINFO_BOTH, DINFO(CL_DEVICE_EXTENSIONS, "Device Extensions", str_get), NULL },
1624         { CLINFO_BOTH, DINFO(CL_DEVICE_TYPE, "Device Type", devtype), NULL },
1625
1626         { CLINFO_BOTH, DINFO(CL_DEVICE_AVAILABLE, "Device Available", bool), NULL },
1627
1628         { CLINFO_BOTH, DINFO(CL_DEVICE_PROFILE, "Device Profile", str), NULL },
1629         { CLINFO_BOTH, DINFO(CL_DEVICE_BOARD_NAME_AMD, "Device Board Name (AMD)", str), dev_has_amd },
1630         { CLINFO_BOTH, DINFO(CL_DEVICE_TOPOLOGY_AMD, "Device Topology (AMD)", devtopo_amd), dev_has_amd },
1631
1632         /* Device Topology (NV) is multipart, so different for HUMAN and RAW */
1633         { CLINFO_HUMAN, DINFO(CL_DEVICE_PCI_BUS_ID_NV, "Device Topology (NV)", devtopo_nv), dev_has_nv },
1634         { CLINFO_RAW, DINFO(CL_DEVICE_PCI_BUS_ID_NV, "Device PCI bus (NV)", int), dev_has_nv },
1635         { CLINFO_RAW, DINFO(CL_DEVICE_PCI_SLOT_ID_NV, "Device PCI slot (NV)", int), dev_has_nv },
1636
1637         { CLINFO_BOTH, DINFO(CL_DEVICE_MAX_COMPUTE_UNITS, "Max compute units", int), NULL },
1638         { CLINFO_BOTH, DINFO(CL_DEVICE_SIMD_PER_COMPUTE_UNIT_AMD, "SIMD per compute unit (AMD)", int), dev_is_gpu_amd },
1639         { CLINFO_BOTH, DINFO(CL_DEVICE_SIMD_WIDTH_AMD, "SIMD width (AMD)", int), dev_is_gpu_amd },
1640         { CLINFO_BOTH, DINFO(CL_DEVICE_SIMD_INSTRUCTION_WIDTH_AMD, "SIMD instruction width (AMD)", int), dev_is_gpu_amd },
1641         { CLINFO_BOTH, DINFO_SFX(CL_DEVICE_MAX_CLOCK_FREQUENCY, "Max clock frequency", "MHz", int), NULL },
1642
1643         /* Device Compute Capability (NV) is multipart, so different for HUMAN and RAW */
1644         { CLINFO_HUMAN, DINFO(CL_DEVICE_COMPUTE_CAPABILITY_MAJOR_NV, "Compute Capability (NV)", cc_nv), dev_has_nv },
1645         { CLINFO_RAW, DINFO(CL_DEVICE_COMPUTE_CAPABILITY_MAJOR_NV, INDENT "Compute Capability Major (NV)", int), dev_has_nv },
1646         { CLINFO_RAW, DINFO(CL_DEVICE_COMPUTE_CAPABILITY_MINOR_NV, INDENT "Compute Capability Minor (NV)", int), dev_has_nv },
1647
1648         /* GFXIP (AMD) is multipart, so different for HUMAN and RAW */
1649         /* TODO: find a better human-friendly name than GFXIP; v3 of the cl_amd_device_attribute_query
1650          * extension specification calls it “core engine GFXIP”, which honestly is not better than
1651          * our name choice. */
1652         { CLINFO_HUMAN, DINFO(CL_DEVICE_GFXIP_MAJOR_AMD, "Graphics IP (AMD)", gfxip_amd), dev_is_gpu_amd },
1653         { CLINFO_RAW, DINFO(CL_DEVICE_GFXIP_MAJOR_AMD, INDENT "Graphics IP MAJOR (AMD)", int), dev_is_gpu_amd },
1654         { CLINFO_RAW, DINFO(CL_DEVICE_GFXIP_MINOR_AMD, INDENT "Graphics IP MINOR (AMD)", int), dev_is_gpu_amd },
1655
1656         { CLINFO_BOTH, DINFO_SFX(CL_DEVICE_CORE_TEMPERATURE_ALTERA, "Core Temperature (Altera)", " C", int), dev_has_altera_dev_temp },
1657
1658         /* Device partition support: summary is only presented in HUMAN case */
1659         { CLINFO_HUMAN, DINFO(CL_DEVICE_PARTITION_MAX_SUB_DEVICES, "Device Partition", partition_header), dev_has_partition },
1660         { CLINFO_BOTH, DINFO(CL_DEVICE_PARTITION_MAX_SUB_DEVICES, INDENT "Max number of sub-devices", int), dev_is_12 },
1661         { CLINFO_BOTH, DINFO(CL_DEVICE_PARTITION_PROPERTIES, INDENT "Supported partition types", partition_types), dev_is_12 },
1662         { CLINFO_BOTH, DINFO(CL_DEVICE_PARTITION_AFFINITY_DOMAIN, INDENT "Supported affinity domains", partition_affinities), dev_is_12 },
1663         { CLINFO_BOTH, DINFO(CL_DEVICE_PARTITION_TYPES_EXT, INDENT "Supported partition types (ext)", partition_types_ext), dev_has_fission },
1664         { CLINFO_BOTH, DINFO(CL_DEVICE_AFFINITY_DOMAINS_EXT, INDENT "Supported affinity domains (ext)", partition_affinities_ext), dev_has_fission },
1665
1666         { CLINFO_BOTH, DINFO(CL_DEVICE_MAX_WORK_ITEM_DIMENSIONS, "Max work item dimensions", int), NULL },
1667         { CLINFO_BOTH, DINFO(CL_DEVICE_MAX_WORK_ITEM_SIZES, "Max work item sizes", szptr), NULL },
1668         { CLINFO_BOTH, DINFO(CL_DEVICE_MAX_WORK_GROUP_SIZE, "Max work group size", sz), NULL },
1669
1670         { CLINFO_BOTH, DINFO(CL_DEVICE_COMPILER_AVAILABLE, "Compiler Available", bool), NULL },
1671         { CLINFO_BOTH, DINFO(CL_DEVICE_LINKER_AVAILABLE, "Linker Available", bool), dev_is_12 },
1672         { CLINFO_BOTH, DINFO(CL_KERNEL_PREFERRED_WORK_GROUP_SIZE_MULTIPLE, "Preferred work group size multiple", wg), dev_has_compiler },
1673         { CLINFO_BOTH, DINFO(CL_DEVICE_WARP_SIZE_NV, "Warp size (NV)", int), dev_has_nv },
1674         { CLINFO_BOTH, DINFO(CL_DEVICE_WAVEFRONT_WIDTH_AMD, "Wavefront width (AMD)", int), dev_is_gpu_amd },
1675         { CLINFO_BOTH, DINFO(CL_DEVICE_MAX_NUM_SUB_GROUPS, "Max sub-groups per work group", int), dev_is_21 },
1676         { CLINFO_BOTH, DINFO(CL_DEVICE_SUB_GROUP_SIZES_INTEL, "Sub-group sizes (Intel)", szptr), dev_has_intel_required_subgroup_size },
1677
1678         /* Preferred/native vector widths: header is only presented in HUMAN case, that also pairs
1679          * PREFERRED and NATIVE in a single line */
1680 #define DINFO_VECWIDTH(Type, type) \
1681         { CLINFO_HUMAN, DINFO(CL_DEVICE_PREFERRED_VECTOR_WIDTH_##Type, INDENT #type, vecwidth), NULL }, \
1682         { CLINFO_RAW, DINFO(CL_DEVICE_PREFERRED_VECTOR_WIDTH_##Type, INDENT #type, int), NULL }, \
1683         { CLINFO_RAW, DINFO(CL_DEVICE_NATIVE_VECTOR_WIDTH_##Type, INDENT #type, int), NULL }
1684
1685         { CLINFO_HUMAN, DINFO(CL_FALSE, "Preferred / native vector sizes", str), NULL },
1686         DINFO_VECWIDTH(CHAR, char),
1687         DINFO_VECWIDTH(SHORT, short),
1688         DINFO_VECWIDTH(INT, int),
1689         DINFO_VECWIDTH(LONG, long),
1690         DINFO_VECWIDTH(HALF, half),
1691         DINFO_VECWIDTH(FLOAT, float),
1692         DINFO_VECWIDTH(DOUBLE, double),
1693
1694         /* Floating point configurations */
1695 #define DINFO_FPCONF(Type, type, cond) \
1696         { CLINFO_BOTH, DINFO(CL_DEVICE_##Type##_FP_CONFIG, #type "-precision Floating-point support", fpconf), NULL }
1697
1698         DINFO_FPCONF(HALF, Half, dev_has_half),
1699         DINFO_FPCONF(SINGLE, Single, NULL),
1700         DINFO_FPCONF(DOUBLE, Double, dev_has_double),
1701
1702         /* Address bits and endianness are written together for HUMAN, separate for RAW */
1703         { CLINFO_HUMAN, DINFO(CL_DEVICE_ADDRESS_BITS, "Address bits", arch), NULL },
1704         { CLINFO_RAW, DINFO(CL_DEVICE_ADDRESS_BITS, "Address bits", int), NULL },
1705         { CLINFO_RAW, DINFO(CL_DEVICE_ENDIAN_LITTLE, "Little Endian", bool), NULL },
1706
1707         /* Global memory */
1708         { CLINFO_BOTH, DINFO(CL_DEVICE_GLOBAL_MEM_SIZE, "Global memory size", mem), NULL },
1709         { CLINFO_BOTH, DINFO(CL_DEVICE_GLOBAL_FREE_MEMORY_AMD, "Global free memory (AMD)", free_mem_amd), dev_is_gpu_amd },
1710         { CLINFO_BOTH, DINFO(CL_DEVICE_GLOBAL_MEM_CHANNELS_AMD, "Global memory channels (AMD)", int), dev_is_gpu_amd },
1711         { CLINFO_BOTH, DINFO(CL_DEVICE_GLOBAL_MEM_CHANNEL_BANKS_AMD, "Global memory banks per channel (AMD)", int), dev_is_gpu_amd },
1712         { CLINFO_BOTH, DINFO_SFX(CL_DEVICE_GLOBAL_MEM_CHANNEL_BANK_WIDTH_AMD, "Global memory bank width (AMD)", bytes_str, int), dev_is_gpu_amd },
1713         { CLINFO_BOTH, DINFO(CL_DEVICE_ERROR_CORRECTION_SUPPORT, "Error Correction support", bool), NULL },
1714         { CLINFO_BOTH, DINFO(CL_DEVICE_MAX_MEM_ALLOC_SIZE, "Max memory allocation", mem), NULL },
1715         { CLINFO_BOTH, DINFO(CL_DEVICE_HOST_UNIFIED_MEMORY, "Unified memory for Host and Device", bool), NULL },
1716         { CLINFO_BOTH, DINFO(CL_DEVICE_INTEGRATED_MEMORY_NV, "Integrated memory (NV)", bool), dev_has_nv },
1717
1718         { CLINFO_BOTH, DINFO(CL_DEVICE_SVM_CAPABILITIES, "Shared Virtual Memory (SVM) capabilities", svm_cap), dev_has_svm },
1719
1720         /* Alignment */
1721         { CLINFO_BOTH, DINFO_SFX(CL_DEVICE_MIN_DATA_TYPE_ALIGN_SIZE, "Minimum alignment for any data type", bytes_str, int), NULL },
1722         { CLINFO_HUMAN, DINFO(CL_DEVICE_MEM_BASE_ADDR_ALIGN, "Alignment of base address", bits), NULL },
1723         { CLINFO_RAW, DINFO(CL_DEVICE_MEM_BASE_ADDR_ALIGN, "Alignment of base address", int), NULL },
1724
1725         { CLINFO_BOTH, DINFO_SFX(CL_DEVICE_PAGE_SIZE_QCOM, "Page size (QCOM)", bytes_str, sz), dev_has_qcom_ext_host_ptr },
1726         { CLINFO_BOTH, DINFO_SFX(CL_DEVICE_EXT_MEM_PADDING_IN_BYTES_QCOM, "Externa memory padding (QCOM)", bytes_str, sz), dev_has_qcom_ext_host_ptr },
1727
1728         /* Atomics alignment, with HUMAN-only header */
1729         { CLINFO_HUMAN, DINFO(CL_FALSE, "Preferred alignment for atomics", str), dev_is_20 },
1730         { CLINFO_BOTH, DINFO_SFX(CL_DEVICE_PREFERRED_PLATFORM_ATOMIC_ALIGNMENT, INDENT "SVM", bytes_str, int), dev_is_20 },
1731         { CLINFO_BOTH, DINFO_SFX(CL_DEVICE_PREFERRED_GLOBAL_ATOMIC_ALIGNMENT, INDENT "Global", bytes_str, int), dev_is_20 },
1732         { CLINFO_BOTH, DINFO_SFX(CL_DEVICE_PREFERRED_LOCAL_ATOMIC_ALIGNMENT, INDENT "Local", bytes_str, int), dev_is_20 },
1733
1734         /* Global variables. TODO some 1.2 devices respond to this too */
1735         { CLINFO_BOTH, DINFO(CL_DEVICE_MAX_GLOBAL_VARIABLE_SIZE, "Max size for global variable", mem), dev_is_20 },
1736         { CLINFO_BOTH, DINFO(CL_DEVICE_GLOBAL_VARIABLE_PREFERRED_TOTAL_SIZE, "Preferred total size of global vars", mem), dev_is_20 },
1737
1738         /* Global memory cache */
1739         { CLINFO_BOTH, DINFO(CL_DEVICE_GLOBAL_MEM_CACHE_TYPE, "Global Memory cache type", cachetype), NULL },
1740         { CLINFO_BOTH, DINFO(CL_DEVICE_GLOBAL_MEM_CACHE_SIZE, "Global Memory cache size", sz), dev_has_cache },
1741         { CLINFO_BOTH, DINFO_SFX(CL_DEVICE_GLOBAL_MEM_CACHELINE_SIZE, "Global Memory cache line", " bytes", int), dev_has_cache },
1742
1743         /* Image support */
1744         { CLINFO_BOTH, DINFO(CL_DEVICE_IMAGE_SUPPORT, "Image support", bool), NULL },
1745         { CLINFO_BOTH, DINFO(CL_DEVICE_MAX_SAMPLERS, INDENT "Max number of samplers per kernel", int), dev_has_images },
1746         { CLINFO_BOTH, DINFO_SFX(CL_DEVICE_IMAGE_MAX_BUFFER_SIZE, INDENT "Max size for 1D images from buffer", pixels_str, sz), dev_has_images_12 },
1747         { CLINFO_BOTH, DINFO_SFX(CL_DEVICE_IMAGE_MAX_ARRAY_SIZE, INDENT "Max 1D or 2D image array size", images_str, sz), dev_has_images_12 },
1748         { CLINFO_BOTH, DINFO_SFX(CL_DEVICE_IMAGE_BASE_ADDRESS_ALIGNMENT, INDENT "Base address alignment for 2D image buffers", bytes_str, sz), dev_has_image2d_buffer },
1749         { CLINFO_BOTH, DINFO_SFX(CL_DEVICE_IMAGE_PITCH_ALIGNMENT, INDENT "Pitch alignment for 2D image buffers", bytes_str, sz), dev_has_image2d_buffer },
1750
1751         /* Image dimensions are split for RAW, combined for HUMAN */
1752         { CLINFO_HUMAN, DINFO_SFX(CL_DEVICE_IMAGE2D_MAX_HEIGHT, INDENT "Max 2D image size",  pixels_str, img_sz_2d), dev_has_images },
1753         { CLINFO_RAW, DINFO(CL_DEVICE_IMAGE2D_MAX_HEIGHT, INDENT "Max 2D image height",  sz), dev_has_images },
1754         { CLINFO_RAW, DINFO(CL_DEVICE_IMAGE2D_MAX_WIDTH, INDENT "Max 2D image width",  sz), dev_has_images },
1755         { CLINFO_HUMAN, DINFO_SFX(CL_DEVICE_IMAGE3D_MAX_HEIGHT, INDENT "Max 3D image size",  pixels_str, img_sz_3d), dev_has_images },
1756         { CLINFO_RAW, DINFO(CL_DEVICE_IMAGE3D_MAX_HEIGHT, INDENT "Max 3D image height",  sz), dev_has_images },
1757         { CLINFO_RAW, DINFO(CL_DEVICE_IMAGE3D_MAX_WIDTH, INDENT "Max 3D image width",  sz), dev_has_images },
1758         { CLINFO_RAW, DINFO(CL_DEVICE_IMAGE3D_MAX_DEPTH, INDENT "Max 3D image depth",  sz), dev_has_images },
1759
1760         { CLINFO_BOTH, DINFO(CL_DEVICE_MAX_READ_IMAGE_ARGS, INDENT "Max number of read image args", int), dev_has_images },
1761         { CLINFO_BOTH, DINFO(CL_DEVICE_MAX_WRITE_IMAGE_ARGS, INDENT "Max number of write image args", int), dev_has_images },
1762         { CLINFO_BOTH, DINFO(CL_DEVICE_MAX_READ_WRITE_IMAGE_ARGS, INDENT "Max number of read/write image args", int), dev_has_images_20 },
1763
1764         /* Pipes */
1765         { CLINFO_BOTH, DINFO(CL_DEVICE_MAX_PIPE_ARGS, "Max number of pipe args", int), dev_is_20 },
1766         { CLINFO_BOTH, DINFO(CL_DEVICE_PIPE_MAX_ACTIVE_RESERVATIONS, "Max active pipe reservations", int), dev_is_20 },
1767         { CLINFO_BOTH, DINFO(CL_DEVICE_PIPE_MAX_PACKET_SIZE, "Max pipe packet size", mem_int), dev_is_20 },
1768
1769         /* Local memory */
1770         { CLINFO_BOTH, DINFO(CL_DEVICE_LOCAL_MEM_TYPE, "Local memory type", lmemtype), NULL },
1771         { CLINFO_BOTH, DINFO(CL_DEVICE_LOCAL_MEM_SIZE, "Local memory size", mem), dev_has_lmem },
1772         { CLINFO_BOTH, DINFO(CL_DEVICE_LOCAL_MEM_SIZE_PER_COMPUTE_UNIT_AMD, "Local memory syze per CU (AMD)", mem), dev_is_gpu_amd },
1773         { CLINFO_BOTH, DINFO(CL_DEVICE_LOCAL_MEM_BANKS_AMD, "Local memory banks (AMD)", int), dev_is_gpu_amd },
1774         { CLINFO_BOTH, DINFO(CL_DEVICE_REGISTERS_PER_BLOCK_NV, "Registers per block (NV)", int), dev_has_nv },
1775
1776         /* Constant memory */
1777         { CLINFO_BOTH, DINFO(CL_DEVICE_MAX_CONSTANT_BUFFER_SIZE, "Max constant buffer size", mem), NULL },
1778         { CLINFO_BOTH, DINFO(CL_DEVICE_MAX_CONSTANT_ARGS, "Max number of constant args", int), NULL },
1779
1780         { CLINFO_BOTH, DINFO(CL_DEVICE_MAX_PARAMETER_SIZE, "Max size of kernel argument", mem), NULL },
1781         { CLINFO_BOTH, DINFO(CL_DEVICE_MAX_ATOMIC_COUNTERS_EXT, "Max number of atomic counters", sz), dev_has_atomic_counters },
1782
1783         /* Queue properties */
1784         { CLINFO_BOTH, DINFO(CL_DEVICE_QUEUE_PROPERTIES, "Queue properties", qprop), dev_not_20 },
1785         { CLINFO_BOTH, DINFO(CL_DEVICE_QUEUE_PROPERTIES, "Queue properties (on host)", qprop), dev_is_20 },
1786         { CLINFO_BOTH, DINFO(CL_DEVICE_QUEUE_ON_DEVICE_PROPERTIES, "Queue properties (on device)", qprop), dev_is_20 },
1787         { CLINFO_BOTH, DINFO(CL_DEVICE_QUEUE_ON_DEVICE_PREFERRED_SIZE, INDENT "Preferred size", mem), dev_is_20 },
1788         { CLINFO_BOTH, DINFO(CL_DEVICE_QUEUE_ON_DEVICE_MAX_SIZE, INDENT "Max size", mem), dev_is_20 },
1789         { CLINFO_BOTH, DINFO(CL_DEVICE_MAX_ON_DEVICE_QUEUES, "Max queues on device", int), dev_is_20 },
1790         { CLINFO_BOTH, DINFO(CL_DEVICE_MAX_ON_DEVICE_EVENTS, "Max events on device", int), dev_is_20 },
1791
1792         /* Interop */
1793         { CLINFO_BOTH, DINFO(CL_DEVICE_PREFERRED_INTEROP_USER_SYNC, "Prefer user sync for interop", bool), dev_is_12 },
1794         { CLINFO_BOTH, DINFO(CL_DEVICE_NUM_SIMULTANEOUS_INTEROPS_INTEL, "Number of simulataneous interops (Intel)", int), dev_has_simultaneous_sharing },
1795         /* TODO: this needs defines for the possible values of the context interops,
1796         { CLINFO_BOTH, DINFO(CL_DEVICE_SIMULTANEOUS_INTEROPS_INTEL, "Simulataneous interops", interop_list), dev_has_simultaneous_sharing },
1797          */
1798
1799         /* Profiling resolution */
1800         { CLINFO_BOTH, DINFO_SFX(CL_DEVICE_PROFILING_TIMER_RESOLUTION, "Profiling timer resolution", "ns", long), NULL },
1801         { CLINFO_HUMAN, DINFO(CL_DEVICE_PROFILING_TIMER_OFFSET_AMD, "Profiling timer offset since Epoch (AMD)", time_offset), dev_has_amd },
1802         { CLINFO_RAW, DINFO(CL_DEVICE_PROFILING_TIMER_OFFSET_AMD, "Profiling timer offset since Epoch (AMD)", long), dev_has_amd },
1803
1804         /* Kernel execution capabilities */
1805         { CLINFO_BOTH, DINFO(CL_DEVICE_EXECUTION_CAPABILITIES, "Execution capabilities", execap), NULL },
1806         { CLINFO_BOTH, DINFO(CL_DEVICE_SUB_GROUP_INDEPENDENT_FORWARD_PROGRESS, INDENT "Sub-group independent forward progress", bool), dev_is_21 },
1807         { CLINFO_BOTH, DINFO(CL_DEVICE_THREAD_TRACE_SUPPORTED_AMD, INDENT "Thread trace supported (AMD)", bool), dev_is_gpu_amd },
1808         { CLINFO_BOTH, DINFO(CL_DEVICE_KERNEL_EXEC_TIMEOUT_NV, INDENT "Kernel execution timeout (NV)", bool), dev_has_nv },
1809         { CLINFO_BOTH, DINFO(CL_DEVICE_GPU_OVERLAP_NV, "Concurrent copy and kernel execution (NV)", bool), dev_has_nv },
1810         { CLINFO_BOTH, DINFO(CL_DEVICE_ATTRIBUTE_ASYNC_ENGINE_COUNT_NV, INDENT "Number of async copy engines", int), dev_has_nv },
1811         /* TODO FIXME Current drivers don't seem to respond to this, should probably be queried based on driver version,
1812          * or maybe it depends on some other device property?
1813         { CLINFO_BOTH, DINFO(CL_DEVICE_AVAILABLE_ASYNC_QUEUES_AMD, INDENT "Number of async queues (AMD)", int), dev_is_gpu_amd },
1814          */
1815         { CLINFO_BOTH, DINFO(CL_DEVICE_IL_VERSION, INDENT "IL version", str), dev_is_21, },
1816         { CLINFO_BOTH, DINFO(CL_DEVICE_SPIR_VERSIONS, INDENT "SPIR versions", str), dev_has_spir },
1817         { CLINFO_BOTH, DINFO(CL_DEVICE_PRINTF_BUFFER_SIZE, "printf() buffer size", mem), dev_is_12 },
1818         { CLINFO_BOTH, DINFO(CL_DEVICE_BUILT_IN_KERNELS, "Built-in kernels", str), dev_is_12 },
1819         { CLINFO_BOTH, DINFO(CL_DEVICE_ME_VERSION_INTEL, "Motion Estimation accelerator version (Intel)", int), dev_has_intel_AME },
1820 };
1821
1822 /* Process all the device info in the traits, except if param_whitelist is not NULL,
1823  * in which case only those in the whitelist will be processed.
1824  * If present, the whitelist should be sorted in the order of appearance of the parameters
1825  * in the traits table, and terminated by the value CL_FALSE
1826  */
1827
1828 void
1829 printDeviceInfo(const cl_device_id *device, cl_uint d,
1830         const cl_device_info *param_whitelist) /* list of device info to process, or NULL */
1831 {
1832         cl_device_id dev = device[d];
1833
1834         char *extensions = NULL;
1835
1836         /* pointer to the traits for CL_DEVICE_EXTENSIONS */
1837         const struct device_info_traits *extensions_traits = NULL;
1838
1839         struct device_info_checks chk;
1840         memset(&chk, 0, sizeof(chk));
1841         chk.dev_version = 10;
1842
1843         current_function = __func__;
1844
1845         for (current_line = 0; current_line < ARRAY_SIZE(dinfo_traits); ++current_line) {
1846
1847                 const struct device_info_traits *traits = dinfo_traits + current_line;
1848                 const char *pname = (output_mode == CLINFO_HUMAN ?
1849                         traits->pname : traits->sname);
1850
1851                 current_param = traits->sname;
1852
1853                 /* Whitelist check: finish if done traversing the list,
1854                  * skip current param if it's not the right one
1855                  */
1856                 if (param_whitelist) {
1857                         if (*param_whitelist == CL_FALSE)
1858                                 break;
1859                         if (traits->param != *param_whitelist)
1860                                 continue;
1861                         ++param_whitelist;
1862                 }
1863
1864                 /* skip if it's not for this output mode */
1865                 if (!(output_mode & traits->output_mode))
1866                         continue;
1867
1868                 if (traits->check_func && !traits->check_func(&chk))
1869                         continue;
1870
1871                 cur_sfx = (output_mode == CLINFO_HUMAN && traits->sfx) ? traits->sfx : empty_str;
1872
1873                 /* Handle headers */
1874                 if (traits->param == CL_FALSE) {
1875                         strbuf[0] = '\0';
1876                         show_strbuf(pname, 0);
1877                         had_error = CL_FALSE;
1878                         continue;
1879                 }
1880
1881                 had_error = traits->show_func(dev, traits->param,
1882                         pname, &chk);
1883
1884                 if (traits->param == CL_DEVICE_EXTENSIONS) {
1885                         /* make a backup of the extensions string, regardless of
1886                          * errors */
1887                         size_t len = strlen(strbuf);
1888                         extensions_traits = traits;
1889                         ALLOC(extensions, len+1, "extensions");
1890                         memcpy(extensions, strbuf, len);
1891                         extensions[len] = '\0';
1892                 }
1893
1894                 if (had_error)
1895                         continue;
1896
1897                 switch (traits->param) {
1898                 case CL_DEVICE_VERSION:
1899                         /* compute numeric value for OpenCL version */
1900                         chk.dev_version = getOpenCLVersion(strbuf + 7);
1901                         break;
1902                 case CL_DEVICE_EXTENSIONS:
1903                         identify_device_extensions(extensions, &chk);
1904                         break;
1905                 case CL_DEVICE_TYPE:
1906                         /* strbuf was abused to give us the dev type */
1907                         memcpy(&(chk.devtype), strbuf, sizeof(chk.devtype));
1908                         break;
1909                 case CL_DEVICE_GLOBAL_MEM_CACHE_TYPE:
1910                         /* strbuf was abused to give us the cache type */
1911                         memcpy(&(chk.cachetype), strbuf, sizeof(chk.cachetype));
1912                         break;
1913                 case CL_DEVICE_LOCAL_MEM_TYPE:
1914                         /* strbuf was abused to give us the lmem type */
1915                         memcpy(&(chk.lmemtype), strbuf, sizeof(chk.lmemtype));
1916                         break;
1917                 case CL_DEVICE_IMAGE_SUPPORT:
1918                         /* strbuf was abused to give us boolean value */
1919                         memcpy(&(chk.image_support), strbuf, sizeof(chk.image_support));
1920                         break;
1921                 case CL_DEVICE_COMPILER_AVAILABLE:
1922                         /* strbuf was abused to give us boolean value */
1923                         memcpy(&(chk.compiler_available), strbuf, sizeof(chk.compiler_available));
1924                         break;
1925                 default:
1926                         /* do nothing */
1927                         break;
1928                 }
1929         }
1930
1931         // and finally the extensions, if we retrieved them
1932         if (extensions)
1933                 printf("%s" I1_STR "%s\n", line_pfx, (output_mode == CLINFO_HUMAN ?
1934                                 extensions_traits->pname :
1935                                 extensions_traits->sname), extensions);
1936         free(extensions);
1937         extensions = NULL;
1938 }
1939
1940 /* list of allowed properties for AMD offline devices */
1941 /* everything else seems to be set to 0, and all the other string properties
1942  * actually segfault the driver */
1943
1944 static const cl_device_info amd_offline_info_whitelist[] = {
1945         CL_DEVICE_NAME,
1946         /* These are present, but all the same, so just skip them:
1947         CL_DEVICE_VENDOR,
1948         CL_DEVICE_VENDOR_ID,
1949         CL_DEVICE_VERSION,
1950         CL_DRIVER_VERSION,
1951         CL_DEVICE_OPENCL_C_VERSION,
1952         */
1953         CL_DEVICE_EXTENSIONS,
1954         CL_DEVICE_TYPE,
1955         CL_DEVICE_MAX_WORK_GROUP_SIZE,
1956         CL_DEVICE_AVAILABLE
1957 };
1958
1959 /* process offline devices from the cl_amd_offline_devices extension */
1960 int processOfflineDevicesAMD(cl_uint p)
1961 {
1962         int ret = 0;
1963
1964         cl_platform_id pid = platform[p];
1965         cl_device_id *device = NULL;
1966         cl_int num_devs, d;
1967
1968         cl_context_properties ctxpft[] = {
1969                 CL_CONTEXT_PLATFORM, (cl_context_properties)pid,
1970                 CL_CONTEXT_OFFLINE_DEVICES_AMD, (cl_context_properties)CL_TRUE,
1971                 0
1972         };
1973
1974         cl_context ctx = NULL;
1975
1976         if (!list_only)
1977                 printf("%s" I0_STR, line_pfx,
1978                         (output_mode == CLINFO_HUMAN ?
1979                          "Number of offline devices (AMD)" : "#OFFDEVICES"));
1980
1981         ctx = clCreateContextFromType(ctxpft, CL_DEVICE_TYPE_ALL, NULL, NULL, &error);
1982         RR_ERROR("create context");
1983
1984         error = clGetContextInfo(ctx, CL_CONTEXT_NUM_DEVICES, sizeof(num_devs), &num_devs, NULL);
1985         RR_ERROR("get num devs");
1986
1987         ALLOC(device, num_devs, "offline devices");
1988
1989         error = clGetContextInfo(ctx, CL_CONTEXT_DEVICES, num_devs*sizeof(*device), device, NULL);
1990         RR_ERROR("get devs");
1991
1992         if (!list_only)
1993                 printf("%d\n", num_devs);
1994
1995         for (d = 0; d < num_devs; ++d) {
1996                 if (list_only) {
1997                         /*
1998                         if (output_mode == CLINFO_HUMAN)
1999                                 puts(" |");
2000                         */
2001                         if (d == num_devs - 1 && output_mode != CLINFO_RAW)
2002                                 line_pfx[1] = '`';
2003                         had_error = device_info_str_get(device[d], CL_DEVICE_NAME, "CL_DEVICE_NAME", NULL);
2004                         printf("%s%u: %s\n", line_pfx, d, strbuf);
2005                 } else {
2006                         if (line_pfx_len > 0) {
2007                                 sprintf(strbuf, "[%s/%u]", pdata[p].sname, -d);
2008                                 sprintf(line_pfx, "%*s", -line_pfx_len, strbuf);
2009                         }
2010                         printDeviceInfo(device, d, amd_offline_info_whitelist);
2011                         if (d < num_devs - 1)
2012                                 puts("");
2013                 }
2014                 fflush(stdout);
2015                 fflush(stderr);
2016         }
2017
2018         had_error = CL_FALSE;
2019 out:
2020         free(device);
2021         if (ctx)
2022                 clReleaseContext(ctx);
2023         return ret;
2024
2025 }
2026
2027 void listPlatformsAndDevices(cl_bool show_offline)
2028 {
2029         cl_uint p, d;
2030         cl_device_id *device;
2031
2032         if (output_mode == CLINFO_RAW)
2033                 sprintf(strbuf, "%u", num_platforms);
2034         else
2035                 sprintf(strbuf, " +-- %sDevice #", (show_offline ? "Offline" : ""));
2036
2037         line_pfx_len = strlen(strbuf) + 1;
2038         REALLOC(line_pfx, line_pfx_len, "line prefix");
2039
2040         for (p = 0, device = all_devices; p < num_platforms; device += pdata[p++].ndevs) {
2041                 printf("%s%u: %s\n",
2042                         (output_mode == CLINFO_HUMAN ? "Platform #" : ""),
2043                         p, pdata[p].pname);
2044                 if (output_mode == CLINFO_RAW)
2045                         sprintf(line_pfx, "%u:", p);
2046                 else
2047                         sprintf(line_pfx, " +-- Device #");
2048
2049                 if (pdata[p].ndevs > 0) {
2050                         error = clGetDeviceIDs(platform[p], CL_DEVICE_TYPE_ALL, pdata[p].ndevs, device, NULL);
2051                         CHECK_ERROR("device IDs");
2052                         for (d = 0; d < pdata[p].ndevs; ++d) {
2053                                 /*
2054                                 if (output_mode == CLINFO_HUMAN)
2055                                         puts(" |");
2056                                 */
2057                                 cl_bool last_device = (d == pdata[p].ndevs - 1 && output_mode != CLINFO_RAW &&
2058                                         (!show_offline || !pdata[p].has_amd_offline));
2059                                 if (last_device)
2060                                         line_pfx[1] = '`';
2061                                 had_error = device_info_str_get(device[d], CL_DEVICE_NAME, "CL_DEVICE_NAME", NULL);
2062                                 printf("%s%u: %s\n", line_pfx, d, strbuf);
2063                                 fflush(stdout);
2064                                 fflush(stderr);
2065                         }
2066                 }
2067
2068                 if (show_offline && pdata[p].has_amd_offline) {
2069                         if (output_mode == CLINFO_RAW)
2070                                 sprintf(line_pfx, "%u*", p);
2071                         else
2072                                 sprintf(line_pfx, " +-- Offline Device #");
2073                         had_error = processOfflineDevicesAMD(p);
2074                         if (had_error)
2075                                 puts(strbuf);
2076                 }
2077         }
2078 }
2079
2080 void showDevices(cl_bool show_offline)
2081 {
2082         cl_uint p, d;
2083         cl_device_id *device;
2084
2085         /* TODO consider enabling this for both output modes */
2086         if (output_mode == CLINFO_RAW) {
2087                 sprintf(strbuf, "%u", maxdevs);
2088                 line_pfx_len = platform_sname_maxlen + strlen(strbuf) + 4;
2089                 REALLOC(line_pfx, line_pfx_len, "line prefix");
2090         }
2091
2092         for (p = 0, device = all_devices; p < num_platforms; device += pdata[p++].ndevs) {
2093                 if (line_pfx_len > 0) {
2094                         sprintf(strbuf, "[%s/*]", pdata[p].sname);
2095                         sprintf(line_pfx, "%*s", -line_pfx_len, strbuf);
2096                 }
2097                 printf("%s" I1_STR "%s\n",
2098                         line_pfx,
2099                         (output_mode == CLINFO_HUMAN ?
2100                          pinfo_traits[0].pname : pinfo_traits[0].sname),
2101                         pdata[p].pname);
2102                 printf("%s" I0_STR "%u\n",
2103                         line_pfx,
2104                         (output_mode == CLINFO_HUMAN ?
2105                          "Number of devices" : "#DEVICES"),
2106                         pdata[p].ndevs);
2107
2108                 if (pdata[p].ndevs > 0) {
2109                         error = clGetDeviceIDs(platform[p], CL_DEVICE_TYPE_ALL, pdata[p].ndevs, device, NULL);
2110                         CHECK_ERROR("device IDs");
2111                 }
2112                 for (d = 0; d < pdata[p].ndevs; ++d) {
2113                         if (line_pfx_len > 0) {
2114                                 sprintf(strbuf, "[%s/%u]", pdata[p].sname, d);
2115                                 sprintf(line_pfx, "%*s", -line_pfx_len, strbuf);
2116                         }
2117                         printDeviceInfo(device, d, NULL);
2118                         if (d < pdata[p].ndevs - 1)
2119                                 puts("");
2120                         fflush(stdout);
2121                         fflush(stderr);
2122                 }
2123                 if (show_offline && pdata[p].has_amd_offline) {
2124                         puts("");
2125                         had_error = processOfflineDevicesAMD(p);
2126                         if (had_error)
2127                                 puts(strbuf);
2128                 }
2129                 puts("");
2130         }
2131 }
2132
2133 /* check the behavior of clGetPlatformInfo() when given a NULL platform ID */
2134 void checkNullGetPlatformName(void)
2135 {
2136         current_param = "CL_PLATFORM_NAME";
2137
2138         error = clGetPlatformInfo(NULL, CL_PLATFORM_NAME, bufsz, strbuf, NULL);
2139         if (error == CL_INVALID_PLATFORM) {
2140                 bufcpy(0, no_plat());
2141         } else {
2142                 current_line = __LINE__+1;
2143                 had_error = REPORT_ERROR2("get %s");
2144         }
2145         printf(I1_STR "%s\n",
2146                 "clGetPlatformInfo(NULL, CL_PLATFORM_NAME, ...)", strbuf);
2147 }
2148
2149 /* check the behavior of clGetDeviceIDs() when given a NULL platform ID;
2150  * return the index of the default platform in our array of platform IDs,
2151  * or num_platforms (which is an invalid platform index) in case of errors
2152  * or no platform or device found.
2153  */
2154 cl_uint checkNullGetDevices(void)
2155 {
2156         cl_uint i = 0; /* generic iterator */
2157         cl_device_id dev = NULL; /* sample device */
2158         cl_platform_id plat = NULL; /* detected platform */
2159
2160         cl_uint found = 0; /* number of platforms found */
2161         cl_uint pidx = num_platforms; /* index of the platform found */
2162         cl_uint numdevs = 0;
2163
2164         current_function = __func__;
2165         current_param = "device IDs";
2166
2167         error = clGetDeviceIDs(NULL, CL_DEVICE_TYPE_ALL, 0, NULL, &numdevs);
2168         /* TODO we should check other CL_DEVICE_TYPE_* combinations, since a smart
2169          * implementation might give you a different default platform for GPUs
2170          * and for CPUs.
2171          * Of course the “no devices” case would then need to be handled differently.
2172          * The logic might be maintained similarly, provided we also gather
2173          * the number of devices of each type for each platform, although it's
2174          * obviously more likely to have multiple platforms with no devices
2175          * of a given type.
2176          */
2177
2178         switch (error) {
2179         case CL_INVALID_PLATFORM:
2180                 bufcpy(0, no_plat());
2181                 break;
2182         case CL_DEVICE_NOT_FOUND:
2183                  /* No devices were found, see if there are platforms with
2184                   * no devices, and if there's only one, assume this is the
2185                   * one being used as default by the ICD loader */
2186                 for (i = 0; i < num_platforms; ++i) {
2187                         if (pdata[i].ndevs == 0) {
2188                                 ++found;
2189                                 if (found > 1)
2190                                         break;
2191                                 else {
2192                                         plat = platform[i];
2193                                         pidx = i;
2194                                 }
2195                         }
2196                 }
2197
2198                 switch (found) {
2199                 case 0:
2200                         bufcpy(0, (output_mode == CLINFO_HUMAN ?
2201                                 "<error: 0 devices, no matching platform!>" :
2202                                 "CL_DEVICE_NOT_FOUND | CL_INVALID_PLATFORM"));
2203                         break;
2204                 case 1:
2205                         bufcpy(0, (output_mode == CLINFO_HUMAN ?
2206                                 pdata[pidx].pname :
2207                                 pdata[pidx].sname));
2208                         break;
2209                 default: /* found > 1 */
2210                         bufcpy(0, (output_mode == CLINFO_HUMAN ?
2211                                 "<error: 0 devices, multiple matching platforms!>" :
2212                                 "CL_DEVICE_NOT_FOUND | ????"));
2213                         break;
2214                 }
2215                 break;
2216         default:
2217                 current_line = __LINE__+1;
2218                 had_error = REPORT_ERROR2("get number of %s");
2219                 if (had_error)
2220                         break;
2221
2222                 /* Determine platform by looking at the CL_DEVICE_PLATFORM of
2223                  * one of the devices */
2224                 error = clGetDeviceIDs(NULL, CL_DEVICE_TYPE_ALL, 1, &dev, NULL);
2225                 current_line = __LINE__+1;
2226                 had_error = REPORT_ERROR2("get %s");
2227                 if (had_error)
2228                         break;
2229
2230                 current_param = "CL_DEVICE_PLATFORM";
2231                 error = clGetDeviceInfo(dev, CL_DEVICE_PLATFORM,
2232                         sizeof(plat), &plat, NULL);
2233                 current_line = __LINE__+1;
2234                 had_error = REPORT_ERROR2("get %s");
2235                 if (had_error)
2236                         break;
2237
2238                 for (i = 0; i < num_platforms; ++i) {
2239                         if (platform[i] == plat) {
2240                                 pidx = i;
2241                                 sprintf(strbuf, "%s [%s]",
2242                                         (output_mode == CLINFO_HUMAN ? "Success" : "CL_SUCCESS"),
2243                                         pdata[i].sname);
2244                                 break;
2245                         }
2246                 }
2247                 if (i == num_platforms) {
2248                         sprintf(strbuf, "<error: platform 0x%p not found>", (void*)plat);
2249                 }
2250         }
2251         printf(I1_STR "%s\n",
2252                 "clGetDeviceIDs(NULL, CL_DEVICE_TYPE_ALL, ...)", strbuf);
2253         return pidx;
2254 }
2255
2256 void checkNullCtx(cl_uint pidx, const cl_device_id *dev, const char *which)
2257 {
2258         cl_context ctx = clCreateContext(NULL, 1, dev, NULL, NULL, &error);
2259
2260         current_function = __func__;
2261         current_param = which;
2262         current_line = __LINE__+2;
2263
2264         had_error = REPORT_ERROR2("create context with device from %s platform");
2265         if (!had_error)
2266                 sprintf(strbuf, "%s [%s]",
2267                         (output_mode == CLINFO_HUMAN ? "Success" : "CL_SUCCESS"),
2268                         pdata[pidx].sname);
2269         if (ctx) {
2270                 clReleaseContext(ctx);
2271                 ctx = NULL;
2272         }
2273 }
2274
2275 /* check behavior of clCreateContextFromType() with NULL cl_context_properties */
2276 void checkNullCtxFromType(void)
2277 {
2278         size_t t; /* type iterator */
2279         size_t i; /* generic iterator */
2280         char def[1024];
2281         cl_context ctx = NULL;
2282
2283         size_t ndevs = 8;
2284         size_t szval = 0;
2285         size_t cursz = ndevs*sizeof(cl_device_id);
2286         cl_platform_id plat = NULL;
2287         cl_device_id *devs = NULL;
2288
2289         const char *platname_prop = (output_mode == CLINFO_HUMAN ?
2290                 pinfo_traits[0].pname :
2291                 pinfo_traits[0].sname);
2292
2293         const char *devname_prop = (output_mode == CLINFO_HUMAN ?
2294                 dinfo_traits[0].pname :
2295                 dinfo_traits[0].sname);
2296
2297         ALLOC(devs, ndevs, "context devices");
2298
2299         current_function = __func__;
2300         for (t = 2; t < devtype_count; ++t) { /* we skip 0 and _DEFAULT */
2301                 current_param = device_type_raw_str[t];
2302
2303                 sprintf(strbuf, "clCreateContextFromType(NULL, %s)", current_param);
2304                 sprintf(def, I1_STR, strbuf);
2305
2306                 current_line = __LINE__+1;
2307                 ctx = clCreateContextFromType(NULL, devtype[t], NULL, NULL, &error);
2308
2309                 switch (error) {
2310                 case CL_INVALID_PLATFORM:
2311                         bufcpy(0, no_plat()); break;
2312                 case CL_DEVICE_NOT_FOUND:
2313                 case CL_INVALID_DEVICE_TYPE: /* e.g. _CUSTOM device on 1.1 platform */
2314                         bufcpy(0, no_dev()); break;
2315                 case CL_DEVICE_NOT_AVAILABLE:
2316                         bufcpy(0, no_dev_avail()); break;
2317                 default:
2318                         had_error = REPORT_ERROR2("create context from type %s");
2319                         if (had_error)
2320                                 break;
2321
2322                         /* get the devices */
2323                         current_param = "CL_CONTEXT_DEVICES";
2324                         current_line = __LINE__+2;
2325
2326                         error = clGetContextInfo(ctx, CL_CONTEXT_DEVICES, 0, NULL, &szval);
2327                         had_error = REPORT_ERROR2("get %s size");
2328                         if (had_error)
2329                                 break;
2330                         if (szval > cursz) {
2331                                 REALLOC(devs, szval, "context devices");
2332                                 cursz = szval;
2333                         }
2334
2335                         current_line = __LINE__+1;
2336                         error = clGetContextInfo(ctx, CL_CONTEXT_DEVICES, cursz, devs, NULL);
2337                         had_error = REPORT_ERROR2("get %s");
2338                         if (had_error)
2339                                 break;
2340                         ndevs = szval/sizeof(cl_device_id);
2341                         if (ndevs < 1) {
2342                                 bufcpy(0, "<error: context created with no devices>");
2343                         }
2344
2345                         /* get the platform from the first device */
2346                         current_param = "CL_DEVICE_PLATFORM";
2347                         current_line = __LINE__+1;
2348                         error = clGetDeviceInfo(*devs, CL_DEVICE_PLATFORM, sizeof(plat), &plat, NULL);
2349                         had_error = REPORT_ERROR2("get %s");
2350                         if (had_error)
2351                                 break;
2352
2353                         szval = 0;
2354                         for (i = 0; i < num_platforms; ++i) {
2355                                 if (platform[i] == plat)
2356                                         break;
2357                         }
2358                         if (i == num_platforms) {
2359                                 sprintf(strbuf, "<error: platform 0x%p not found>", (void*)plat);
2360                                 break;
2361                         } else {
2362                                 szval += sprintf(strbuf, "%s (%" PRIuS ")",
2363                                         (output_mode == CLINFO_HUMAN ? "Success" : "CL_SUCCESS"),
2364                                         ndevs);
2365                                 szval += snprintf(strbuf + szval, bufsz - szval, "\n" I2_STR "%s",
2366                                         platname_prop, pdata[i].pname);
2367                         }
2368                         for (i = 0; i < ndevs; ++i) {
2369                                 size_t szname = 0;
2370                                 /* for each device, show the device name */
2371                                 /* TODO some other unique ID too, e.g. PCI address, if available? */
2372
2373                                 szval += snprintf(strbuf + szval, bufsz - szval, "\n" I2_STR, devname_prop);
2374                                 if (szval >= bufsz) {
2375                                         trunc_strbuf();
2376                                         break;
2377                                 }
2378
2379                                 current_param = "CL_DEVICE_NAME";
2380                                 current_line = __LINE__+1;
2381                                 error = clGetDeviceInfo(devs[i], CL_DEVICE_NAME, bufsz - szval, strbuf + szval, &szname);
2382                                 had_error = REPORT_ERROR2("get %s");
2383                                 if (had_error)
2384                                         break;
2385                                 szval += szname - 1;
2386
2387
2388                         }
2389                         if (i != ndevs)
2390                                 break; /* had an error earlier, bail */
2391
2392                 }
2393
2394                 if (ctx) {
2395                         clReleaseContext(ctx);
2396                         ctx = NULL;
2397                 }
2398                 printf("%s%s\n", def, strbuf);
2399         }
2400         free(devs);
2401 }
2402
2403 /* check the behavior of NULL platform in clGetDeviceIDs (see checkNullGetDevices)
2404  * and in clCreateContext() */
2405 void checkNullBehavior(void)
2406 {
2407         cl_device_id *dev = NULL;
2408         cl_uint p = 0;
2409         cl_uint pidx;
2410
2411         printf("NULL platform behavior\n");
2412
2413         checkNullGetPlatformName();
2414
2415         pidx = checkNullGetDevices();
2416
2417         /* If there's a default platform, and it has devices, try
2418          * creating a context with its first device and see if it works */
2419
2420         if (pidx == num_platforms) {
2421                 bufcpy(0, no_plat());
2422         } else if (pdata[pidx].ndevs == 0) {
2423                 bufcpy(0, no_dev());
2424         } else {
2425                 p = 0;
2426                 dev = all_devices;
2427                 while (p < num_platforms && p != pidx) {
2428                         dev += pdata[p++].ndevs;
2429                 }
2430                 if (p < num_platforms) {
2431                         checkNullCtx(pidx, dev, "default");
2432                 } else {
2433                         /* this shouldn't happen, but still ... */
2434                         bufcpy(0, "<error: overflow in default platform scan>");
2435                 }
2436         }
2437         printf(I1_STR "%s\n", "clCreateContext(NULL, ...) [default]", strbuf);
2438
2439         /* Look for a device from a non-default platform, if there are any */
2440         if (pidx == num_platforms || num_platforms > 1) {
2441                 p = 0;
2442                 dev = all_devices;
2443                 while (p < num_platforms && (p == pidx || pdata[p].ndevs == 0)) {
2444                         dev += pdata[p++].ndevs;
2445                 }
2446                 if (p < num_platforms) {
2447                         checkNullCtx(p, dev, "non-default");
2448                 } else {
2449                         bufcpy(0, "<error: no devices in non-default plaforms>");
2450                 }
2451                 printf(I1_STR "%s\n", "clCreateContext(NULL, ...) [other]", strbuf);
2452         }
2453
2454         checkNullCtxFromType();
2455
2456 }
2457
2458
2459 /* Get properties of the ocl-icd loader, if available */
2460 /* All properties are currently char[] */
2461 typedef enum {
2462         CL_ICDL_OCL_VERSION=1,
2463         CL_ICDL_VERSION=2,
2464         CL_ICDL_NAME=3,
2465         CL_ICDL_VENDOR=4,
2466 } cl_icdl_info;
2467
2468 /* Function pointer to the ICD loader info function */
2469 cl_int (*clGetICDLoaderInfoOCLICD)(cl_icdl_info, size_t, void*, size_t*);
2470
2471 /* We want to auto-detect the OpenCL version supported by the ICD loader.
2472  * To do this, we will progressively find symbols introduced in new APIs,
2473  * until a NULL symbol is found.
2474  */
2475
2476 struct icd_loader_test {
2477         cl_uint version;
2478         const char *symbol;
2479 } icd_loader_tests[] = {
2480         { 11, "clCreateSubBuffer" },
2481         { 12, "clCreateImage" },
2482         { 20, "clSVMAlloc" },
2483         { 21, "clGetHostTimer" },
2484         { 0, NULL }
2485 };
2486
2487 int
2488 icdl_info_str(cl_icdl_info param, const char* pname)
2489 {
2490         error = clGetICDLoaderInfoOCLICD(param, 0, NULL, &nusz);
2491         if (nusz > bufsz) {
2492                 REALLOC(strbuf, nusz, current_param);
2493                 bufsz = nusz;
2494         }
2495         had_error = REPORT_ERROR2("get %s size");
2496         if (!had_error) {
2497                 error = clGetICDLoaderInfoOCLICD(param, bufsz, strbuf, NULL);
2498                 had_error = REPORT_ERROR2("get %s");
2499         }
2500         show_strbuf(pname, 1);
2501         return had_error;
2502 }
2503
2504 struct icdl_info_traits {
2505         cl_icdl_info param; // CL_ICDL_*
2506         const char *sname; // "CL_ICDL_*"
2507         const char *pname; // "ICD loader *"
2508 };
2509
2510 static const char * const oclicdl_pfx = "OCLICD";
2511
2512 #define LINFO(symbol, name) { symbol, #symbol, "ICD loader " name }
2513 struct icdl_info_traits linfo_traits[] = {
2514         LINFO(CL_ICDL_NAME, "Name"),
2515         LINFO(CL_ICDL_VENDOR, "Vendor"),
2516         LINFO(CL_ICDL_VERSION, "Version"),
2517         LINFO(CL_ICDL_OCL_VERSION, "Profile")
2518 };
2519
2520 /* GCC < 4.6 does not support the diagnostic push _inside_ the function,
2521  * so we have to put it outside
2522  */
2523 #if defined __GNUC__ && ((__GNUC__*10 + __GNUC_MINOR__) < 46)
2524 #pragma GCC diagnostic ignored "-Wdeprecated-declarations"
2525 #endif
2526
2527 void oclIcdProps(void)
2528 {
2529         /* First of all, we try to auto-detect the supported ICD loader version */
2530         int i = 0;
2531
2532         do {
2533                 struct icd_loader_test check = icd_loader_tests[i];
2534                 if (check.symbol == NULL)
2535                         break;
2536                 if (dlsym(RTLD_DEFAULT, check.symbol) == NULL)
2537                         break;
2538                 icdl_ocl_version_found = check.version;
2539                 ++i;
2540         } while (1);
2541
2542
2543         /* We find the clGetICDLoaderInfoOCLICD extension address, and use it to query
2544          * the ICD loader properties. It should be noted however that
2545          * clGetExtensionFunctionAddress is marked deprecated as of OpenCL 1.2, so
2546          * to use it and compile cleanly we need disable the relevant warning.
2547          * It should be noted that in this specific case we cannot replace the
2548          * call to clGetExtensionFunctionAddress with a call to the superseding function
2549          * clGetExtensionFunctionAddressForPlatform because the extension is in the
2550          * loader itself, not in a specific platform.
2551          */
2552
2553 #ifdef _MSC_VER
2554 #pragma warning(push)
2555 #pragma warning(disable : 4996)
2556 #elif defined __GNUC__ && ((__GNUC__*10 + __GNUC_MINOR__) >= 46)
2557 #pragma GCC diagnostic push
2558 #pragma GCC diagnostic ignored "-Wdeprecated-declarations"
2559 #endif
2560
2561         PTR_FUNC_PTR clGetICDLoaderInfoOCLICD = clGetExtensionFunctionAddress("clGetICDLoaderInfoOCLICD");
2562
2563 #ifdef _MSC_VER
2564 #pragma warning(pop)
2565 #elif defined __GNUC__ && ((__GNUC__*10 + __GNUC_MINOR__) >= 46)
2566 #pragma GCC diagnostic pop
2567 #endif
2568
2569         if (clGetICDLoaderInfoOCLICD != NULL) {
2570                 /* TODO think of a sensible header in CLINFO_RAW */
2571                 if (output_mode != CLINFO_RAW)
2572                         puts("\nICD loader properties");
2573                 current_function = __func__;
2574
2575                 if (output_mode == CLINFO_RAW) {
2576                         line_pfx_len = strlen(oclicdl_pfx) + 5;
2577                         REALLOC(line_pfx, line_pfx_len, "line prefix OCL ICD");
2578                         sprintf(strbuf, "[%s/*]", oclicdl_pfx);
2579                         sprintf(line_pfx, "%*s", -line_pfx_len, strbuf);
2580                 }
2581
2582                 for (current_line = 0; current_line < ARRAY_SIZE(linfo_traits); ++current_line) {
2583                         const struct icdl_info_traits *traits = linfo_traits + current_line;
2584                         current_param = traits->sname;
2585
2586                         had_error = icdl_info_str(traits->param,
2587                                 output_mode == CLINFO_HUMAN ?
2588                                 traits->pname : traits->sname);
2589
2590                         if (!had_error && traits->param == CL_ICDL_OCL_VERSION) {
2591                                 icdl_ocl_version = getOpenCLVersion(strbuf + 7);
2592                         }
2593                 }
2594         }
2595
2596         if (output_mode == CLINFO_HUMAN) {
2597                 if (icdl_ocl_version &&
2598                         icdl_ocl_version != icdl_ocl_version_found) {
2599                         printf( "\tNOTE:\tyour OpenCL library declares to support OpenCL %u.%u,\n"
2600                                 "\t\tbut it seems to support up to OpenCL %u.%u %s.\n",
2601                                 icdl_ocl_version / 10, icdl_ocl_version % 10,
2602                                 icdl_ocl_version_found / 10, icdl_ocl_version_found % 10,
2603                                 icdl_ocl_version_found < icdl_ocl_version  ?
2604                                 "only" : "too");
2605                 }
2606                 if (icdl_ocl_version_found < max_plat_version) {
2607                         printf( "\tNOTE:\tyour OpenCL library only supports OpenCL %u.%u,\n"
2608                                 "\t\tbut some installed platforms support OpenCL %u.%u.\n"
2609                                 "\t\tPrograms using %u.%u features may crash\n"
2610                                 "\t\tor behave unexepectedly\n",
2611                                 icdl_ocl_version_found / 10, icdl_ocl_version_found % 10,
2612                                 max_plat_version / 10, max_plat_version % 10,
2613                                 max_plat_version / 10, max_plat_version % 10);
2614                 }
2615         }
2616 }
2617
2618 #if defined __GNUC__ && ((__GNUC__*10 + __GNUC_MINOR__) < 46)
2619 #pragma GCC diagnostic warning "-Wdeprecated-declarations"
2620 #endif
2621
2622 void version(void)
2623 {
2624         puts("clinfo version 2.1.16.01.12");
2625 }
2626
2627 void usage(void)
2628 {
2629         version();
2630         puts("Display properties of all available OpenCL platforms and devices");
2631         puts("Usage: clinfo [options ...]\n");
2632         puts("Options:");
2633         puts("\t--human\t\thuman-friendly output (default)");
2634         puts("\t--raw\t\traw output");
2635         puts("\t--offline\talso show offline devices");
2636         puts("\t--list, -l\tonly list the platforms and devices by name");
2637         puts("\t-h, -?\t\tshow usage");
2638         puts("\t--version, -v\tshow version\n");
2639         puts("Defaults to raw mode if invoked with");
2640         puts("a name that contains the string \"raw\"");
2641 }
2642
2643 int main(int argc, char *argv[])
2644 {
2645         cl_uint p;
2646         int a = 0;
2647
2648         cl_bool show_offline = CL_FALSE;
2649
2650         /* if there's a 'raw' in the program name, switch to raw output mode */
2651         if (strstr(argv[0], "raw"))
2652                 output_mode = CLINFO_RAW;
2653
2654         /* process command-line arguments */
2655         while (++a < argc) {
2656                 if (!strcmp(argv[a], "--raw"))
2657                         output_mode = CLINFO_RAW;
2658                 else if (!strcmp(argv[a], "--human"))
2659                         output_mode = CLINFO_HUMAN;
2660                 else if (!strcmp(argv[a], "--offline"))
2661                         show_offline = CL_TRUE;
2662                 else if (!strcmp(argv[a], "-l") || !strcmp(argv[a], "--list"))
2663                         list_only = CL_TRUE;
2664                 else if (!strcmp(argv[a], "-?") || !strcmp(argv[a], "-h")) {
2665                         usage();
2666                         return 0;
2667                 } else if (!strcmp(argv[a], "--version") || !strcmp(argv[a], "-v")) {
2668                         version();
2669                         return 0;
2670                 } else {
2671                         fprintf(stderr, "ignoring unknown command-line parameter %s\n", argv[a]);
2672                 }
2673         }
2674
2675
2676         ALLOC(strbuf, 1024, "general string buffer");
2677         bufsz = 1024;
2678
2679         error = clGetPlatformIDs(0, NULL, &num_platforms);
2680         if (error != CL_PLATFORM_NOT_FOUND_KHR)
2681                 CHECK_ERROR("number of platforms");
2682
2683         if (!list_only)
2684                 printf(I0_STR "%u\n",
2685                         (output_mode == CLINFO_HUMAN ?
2686                          "Number of platforms" : "#PLATFORMS"),
2687                         num_platforms);
2688         if (!num_platforms)
2689                 return 0;
2690
2691         ALLOC(platform, num_platforms, "platform IDs");
2692         error = clGetPlatformIDs(num_platforms, platform, NULL);
2693         CHECK_ERROR("platform IDs");
2694
2695         ALLOC(pdata, num_platforms, "platform data");
2696         ALLOC(line_pfx, 1, "line prefix");
2697
2698         for (p = 0; p < num_platforms; ++p) {
2699                 printPlatformInfo(p);
2700                 if (!list_only)
2701                         puts("");
2702         }
2703
2704         if (num_devs_all > 0) {
2705                 ALLOC(all_devices, num_devs_all, "device IDs");
2706         }
2707
2708         if (list_only) {
2709                 listPlatformsAndDevices(show_offline);
2710         } else {
2711                 showDevices(show_offline);
2712                 if (output_mode != CLINFO_RAW)
2713                         checkNullBehavior();
2714                 oclIcdProps();
2715         }
2716
2717         return 0;
2718 }