OpenCL: Segmentation fault at clCreateProgramWithSource

Viewed 108

I'm very new to OpenCL. I created a simple working code to perform A = AB + C, and used that to get to my actual task, which is to read in a raw image file and do debayering on it. But the modified code fails at clCreateProgramWithSource sometimes with error code -6. There's minimal changes, and I compared with a diff but for the life of me I can't figure out why I'm getting error in the failed code. So here's the working code:

#include <stdio.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <string.h>
#include <CL/cl.h>

#define LIST_SIZE   1024

int main()
{
    // We need an object to hold the program
    cl_program prog = NULL;
    // First step is to load the kernel in a local memory
    FILE *kfile;
    char *kfilename = "./src/mad_kernel.cl";

    kfile = fopen(kfilename, "r");
    fseek(kfile, 0L, SEEK_END);
    int kfilesize = ftell(kfile) + 1;
    rewind(kfile);
    char *kernel = malloc(kfilesize);
    memset(kernel, 0, kfilesize);
    fread(kernel, 1, kfilesize, kfile);
    fclose(kfile);
    // printf("%s", kernel); // Test passed
    
    // We want to get the platform ID
    cl_platform_id pid = NULL;
    // We need some variables for holding the returned values
    cl_uint num_platforms;
    cl_int ret = clGetPlatformIDs(1, &pid, &num_platforms);
    printf("Line %d err = %d\n", __LINE__, ret);
    
    // We need a cl device ID
    cl_device_id dev_id = NULL;
    cl_uint num_devices;
    ret = clGetDeviceIDs(pid, CL_DEVICE_TYPE_ALL, 1, &dev_id, &num_devices);
    printf("Line %d err = %d\n", __LINE__, ret);

    // Creating a queue to pass our tasks
    // ------------------------------------------------------------------------
    // We need a context for the cl application to run in
    cl_context context = clCreateContext(NULL, 1, &dev_id, NULL, NULL, &ret);
    printf("Line %d err = %d\n", __LINE__, ret);

    // We need a command queue to send our commands to
    cl_command_queue cmd_queue = clCreateCommandQueue(context, dev_id, 0, &ret);
    printf("Line %d err = %d\n", __LINE__, ret);
    // ------------------------------------------------------------------------

    // We need objects to hold the data
    // We are going to perform A = A*B + C
    int *A = malloc(LIST_SIZE * sizeof(int));
    int *B = malloc(LIST_SIZE * sizeof(int));
    int *C = malloc(LIST_SIZE * sizeof(int));

    for (int i = 0; i < LIST_SIZE; i++) {
        A[i] = 2;
        B[i] = 3;
        C[i] = 5;
    }
    
    cl_mem vec_A = clCreateBuffer(context, CL_MEM_READ_WRITE, LIST_SIZE*sizeof(int), 
                        NULL, &ret);
    printf("Line %d err = %d\n", __LINE__, ret);
    cl_mem vec_B = clCreateBuffer(context, CL_MEM_READ_ONLY, LIST_SIZE*sizeof(int), 
                        NULL, &ret); 
    printf("Line %d err = %d\n", __LINE__, ret);
    cl_mem vec_C = clCreateBuffer(context, CL_MEM_READ_ONLY, LIST_SIZE*sizeof(int), 
                        NULL, &ret);
    printf("Line %d err = %d\n", __LINE__, ret);


    // Pass A, B and C to the CL memory
    ret = clEnqueueWriteBuffer(cmd_queue, vec_A, CL_TRUE,
                0, LIST_SIZE*sizeof(int), A, 0, NULL, NULL);
    printf("Line %d err = %d\n", __LINE__, ret);
    ret = clEnqueueWriteBuffer(cmd_queue, vec_B, CL_TRUE,
                0, LIST_SIZE*sizeof(int), B, 0, NULL, NULL);
    printf("Line %d err = %d\n", __LINE__, ret);
    ret = clEnqueueWriteBuffer(cmd_queue, vec_C, CL_TRUE,
                0, LIST_SIZE*sizeof(int), C, 0, NULL, NULL);
    printf("Line %d err = %d\n", __LINE__, ret);

    // We're ready to create the kernel now
    prog = clCreateProgramWithSource(context, 1, (const char **)&kernel, 
                (const size_t *)&kfilesize, &ret);
    printf("Line %d err = %d\n", __LINE__, ret);
    ret = clBuildProgram(prog, 1, &dev_id, NULL, NULL, NULL);
    printf("Line %d err = %d\n", __LINE__, ret);
    cl_kernel kern = clCreateKernel(prog, "vector_mad", &ret);
    printf("Line %d err = %d\n", __LINE__, ret);

    // Set the created memory as arguments to the kernel
    ret = clSetKernelArg(kern, 0, sizeof(cl_mem), &vec_A);
    printf("Line %d err = %d\n", __LINE__, ret);
    ret = clSetKernelArg(kern, 1, sizeof(cl_mem), &vec_B);
    printf("Line %d err = %d\n", __LINE__, ret);
    ret = clSetKernelArg(kern, 2, sizeof(cl_mem), &vec_C);
    printf("Line %d err = %d\n", __LINE__, ret);

    // Tell the program size of the entire object
    size_t global_item_size = LIST_SIZE;
    // Each chunk size
    size_t local_item_size = 64;

    ret = clEnqueueNDRangeKernel(cmd_queue, kern, 1, NULL, &global_item_size, 
                &local_item_size, 0, NULL, NULL);
    printf("Line %d err = %d\n", __LINE__, ret);
    
    // Read memory back from CL
    ret = clEnqueueReadBuffer(cmd_queue, vec_A, CL_TRUE, 0, LIST_SIZE * sizeof(int), A, 
                0, NULL, NULL);
    printf("Line %d err = %d\n", __LINE__, ret);

    int flag = 1;
    for (int i = 0; i < LIST_SIZE; i++) {
        // printf("%d\n", A[i]);
        flag &= A[i] == 11;
    }

    if (flag) {
        printf("Pass\n");
    } else {
        printf("Fail\n");
    }

    // Clean up
    ret = clFlush(cmd_queue);
    ret = clFinish(cmd_queue);
    ret = clReleaseKernel(kern);
    ret = clReleaseProgram(prog);
    ret = clReleaseMemObject(vec_A);
    ret = clReleaseMemObject(vec_B);
    ret = clReleaseMemObject(vec_C);
    ret = clReleaseCommandQueue(cmd_queue);
    ret = clReleaseContext(context);
    free(A);
    free(B);
    free(C);
    return 0;
}

with the kernel:

__kernel void vector_mad(__global int *A, __global int *B, __global int *C)
{
    int i = get_global_id(0);

    A[i] = A[i] * B[i] + C[i];
}

Now, I wanted to read in a raw image in one of the buffer and for initial test, just output a constant value to the output RGBA right, so I modified this program to:

#include <stdio.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <string.h>
#include <CL/cl.h>

int main()
{
    // We need an object to hold the program
    cl_program prog = NULL;
    // First step is to load the kernel in a local memory
    FILE *kfile;
    char *kfilename = "./src/mad_kernel.cl";

    kfile = fopen(kfilename, "r");
    fseek(kfile, 0L, SEEK_END);
    int kfilesize = ftell(kfile) + 1;
    rewind(kfile);
    char *kernel = malloc(kfilesize);
    memset(kernel, 0, kfilesize);
    fread(kernel, 1, kfilesize, kfile);
    fclose(kfile);
    // printf("%s", kernel); // Test passed
    
    // We want to get the platform ID
    cl_platform_id pid = NULL;
    // We need some variables for holding the returned values
    cl_uint num_platforms;
    cl_int ret = clGetPlatformIDs(1, &pid, &num_platforms);
    printf("Line %d err = %d\n", __LINE__, ret);
    
    // We need a cl device ID
    cl_device_id dev_id = NULL;
    cl_uint num_devices;
    ret = clGetDeviceIDs(pid, CL_DEVICE_TYPE_ALL, 1, &dev_id, &num_devices);
    printf("Line %d err = %d\n", __LINE__, ret);

    // Creating a queue to pass our tasks
    // ------------------------------------------------------------------------
    // We need a context for the cl application to run in
    cl_context context = clCreateContext(NULL, 1, &dev_id, NULL, NULL, &ret);
    printf("Line %d err = %d\n", __LINE__, ret);

    // We need a command queue to send our commands to
    cl_command_queue cmd_queue = clCreateCommandQueue(context, dev_id, 0, &ret);
    printf("Line %d err = %d\n", __LINE__, ret);
    // ------------------------------------------------------------------------

    size_t imgsize = 16*16;
    // We need objects to hold the data
    // We are going to perform A = A*B + C
    int *A = malloc(imgsize * sizeof(int));
    int *B = malloc(imgsize * sizeof(int));

    //FILE *img = fopen("./data/testimage.raw", "rb");
    //fread(A, sizeof(cl_ushort), imgsize, img);
    
    for (int i = 0; i < 16; i++) {
        for (int j = 0; j < 16; j++) {
            printf("%x ", *(A + (16*i) + j));
        }
        printf("\n");
    }    
    
    cl_mem vec_A = clCreateBuffer(context, CL_MEM_READ_ONLY, imgsize*sizeof(int), 
                        NULL, &ret);
    printf("Line %d err = %d\n", __LINE__, ret);
    cl_mem vec_B = clCreateBuffer(context, CL_MEM_WRITE_ONLY, imgsize*sizeof(int), 
                        NULL, &ret); 
    printf("Line %d err = %d\n", __LINE__, ret);

    // Pass A, B and C to the CL memory
    ret = clEnqueueWriteBuffer(cmd_queue, vec_A, CL_TRUE,
                0, imgsize * sizeof(int), A, 0, NULL, NULL);
    printf("Line %d err = %d\n", __LINE__, ret);
    ret = clEnqueueWriteBuffer(cmd_queue, vec_B, CL_TRUE,
                0, imgsize * sizeof(int), B, 0, NULL, NULL);
    printf("Line %d err = %d\n", __LINE__, ret);

    // We're ready to create the kernel now
    prog = clCreateProgramWithSource(context, 1, (const char **)&kernel, 
                (const size_t *)&kfilesize, &ret);
    printf("Line %d err = %d\n", __LINE__, ret);
    ret = clBuildProgram(prog, 1, &dev_id, NULL, NULL, NULL);
    printf("Line %d err = %d\n", __LINE__, ret);
    cl_kernel kern = clCreateKernel(prog, "vector_mad", &ret);
    printf("Line %d err = %d\n", __LINE__, ret);

    // Set the created memory as arguments to the kernel
    ret = clSetKernelArg(kern, 0, sizeof(cl_mem), &vec_A);
    printf("Line %d err = %d\n", __LINE__, ret);
    ret = clSetKernelArg(kern, 1, sizeof(cl_mem), &vec_B);
    printf("Line %d err = %d\n", __LINE__, ret);

    // Tell the program size of the entire object
    size_t global_item_size = imgsize;
    // Each chunk size
    size_t local_item_size = 64;

    ret = clEnqueueNDRangeKernel(cmd_queue, kern, 1, NULL, &global_item_size, 
                &local_item_size, 0, NULL, NULL);
    printf("Line %d err = %d\n", __LINE__, ret);
    
    // Read memory back from CL
    cl_uint *C = malloc(imgsize * sizeof(int));
    ret = clEnqueueReadBuffer(cmd_queue, vec_B, CL_TRUE, 0, imgsize * sizeof(int), C, 
                0, NULL, NULL);
    printf("Line %d err = %d\n", __LINE__, ret);

    for (int i = 0; i < 16; i++) {
        for (int j = 0; j < 16; j++) {
            printf("%x", *(C + (16*i) + j));
        }
        printf("\n");
    }

    // Clean up
    ret = clFlush(cmd_queue);
    ret = clFinish(cmd_queue);
    ret = clReleaseKernel(kern);
    ret = clReleaseProgram(prog);
    ret = clReleaseMemObject(vec_A);
    ret = clReleaseMemObject(vec_B);
    ret = clReleaseCommandQueue(cmd_queue);
    ret = clReleaseContext(context);
    free(A);
    free(B);
    free(C);
    return 0;
}

But it did not work, so I even commented out the file read and the vector A now contains only garbage. But the code is failing at the line:

    prog = clCreateProgramWithSource(context, 1, (const char **)&kernel, 
                (const size_t *)&kfilesize, &ret);

I have an Intel CPU and NVidia GPU, so I've tried compiling on both platforms: On intel:

PS E:\OpenCL_SoftISP\image_proc\ocldemo> gcc -g2 -I"C:\Program Files (x86)\IntelSWTools\system_studio_2020\OpenCL\sdk\include" -L"C:\Program Files (x86)\IntelSWTools\system_studio_2020\OpenCL\sdk\lib\x64" -DCL_TARGET_OPENCL_VERSION=120 src/improc.c -o improc -lOpenCL
PS E:\OpenCL_SoftISP\image_proc\ocldemo> .\improc.exe

When I run with gdb it fails at:

Thread 1 hit Breakpoint 1, main () at src\improc.c:81
81          prog = clCreateProgramWithSource(context, 1, (const char **)&kernel,
(gdb) c
Continuing.

Thread 1 received signal SIGSEGV, Segmentation fault.
0x00007ffcd81699ca in nvopencl64!cuGetExportTable () from C:\Windows\System32\DriverStore\FileRepository\nvamui.inf_amd64_5f5d5675b52c555b\nvopencl64.dll

On Nvidia:

PS E:\OpenCL_SoftISP\image_proc\ocldemo> gcc -g2 -I"C:\Program Files\NVIDIA GPU Computing Toolkit\CUDA\v11.6\include" -L"C:\Program Files\NVIDIA GPU Computing Toolkit\CUDA\v11.6\lib\x64" -DCL_TARGET_OPENCL_VERSION=120 src/improc.c -o improc -lOpenCL
PS E:\OpenCL_SoftISP\image_proc\ocldemo> .\improc.exe

When I run with GDB, it breaks at:

Thread 1 hit Breakpoint 2, main () at src\improc.c:81
81          prog = clCreateProgramWithSource(context, 1, (const char **)&kernel,
(gdb) s
[New Thread 16688.0x4918]

Thread 1 received signal SIGSEGV, Segmentation fault.
0x00007ffcd81699ca in nvopencl64!cuGetExportTable () from C:\Windows\System32\DriverStore\FileRepository\nvamui.inf_amd64_5f5d5675b52c555b\nvopencl64.dll

Now the interesting part is, that if I actually do the file read:

    FILE *img = fopen("./data/testimage.raw", "rb");
    fread(A, sizeof(cl_ushort), imgsize, img);

I actually get a different output

Line 67 err = 0
Line 70 err = 0
Line 75 err = 0
Line 78 err = 0
Line 83 err = -6
Line 85 err = -44
Line 87 err = -44
Line 91 err = -48
Line 93 err = -48
Line 102 err = -48
Line 108 err = 0

Where the error -6 I can see is:

CL_OUT_OF_HOST_MEMORY                       -6

Which still points to the fact that some illegal memory access I must be doing. But I am going crazy because where is it happening?

1 Answers

the problem is that you're giving it the address of int kfilesize where the clCreateProgramWithSource() expects an address of size_t. It ends up reading an 8 byte integer from an address of a 4 byte integer. IOW it reads some garbage from the stacks, and thinks you're giving some (probably) billion-bytes long source code.

Related