Automatic Null Checks : mitigate null pointers exceptions

As every modern language, C# exposes the null reference billion dollar mistake. On the other hand, native languages such as CUDA or C/C++ expose null pointers. To mitigate that, latest versions of compilers tend to introduce some form of automatic null checks.
For example, Typescript recently added a --strictNullChecks option, and C#, following a proposal, will expose similar features.

When it comes to CUDA, null pointers can pop out virtually anywhere. You can explicitely return nullptr in some cases, and forget to check against it at some point in your code. You can also allocate local memory by using malloc which returns null when no more memory is available.

A example

For example, have a look at this simple toy code:

unsafe class Program
    {
        struct MyStruct
        {
            public fixed double large[1000001]; // 8 extra bytes
        }

        [IntrinsicFunction("malloc")]
        static void* malloc(int s)
        {
            return (void*)Marshal.AllocHGlobal(s);
        }

        [IntrinsicFunction("free")]
        static void free(void* ptr)
        {
            Marshal.FreeHGlobal(new IntPtr(ptr));
        }

        [EntryPoint]
        public static void Run(double[] output, double[] input)
        {
            MyStruct* ptr = (MyStruct*) malloc(sizeof(MyStruct));
            for (int k = 0; k < 1000001; ++k) {
                ptr->large[k] = input[k];
            }

            free(ptr);
        }

        static void Main(string[] args)
        {
            cudaDeviceProp prop;
            cuda.GetDeviceProperties(out prop, 0);
            HybRunner runner = HybRunner.Cuda("NullChecks_CUDA.dll").SetDistrib(1, 1);
            dynamic wrapped = runner.Wrap(new Program());

            int threadCount = 1;
            double[] output = new double[threadCount];
            double[] input = new double[1000001];
            wrapped.Run(output, input);
        }
    }

Since we allocate 8000008 bytes per thread, we exceed the cudaLimitMallocHeapSize limit of 8MB, and malloc will return NULL.

Running the above code in Release mode yields the following output:


System.ApplicationException: CUDA error occured before deserialization (most probably during kernel call): an illegal memory access was encountered
at Hybridizer.Runtime.CUDAImports.CudaSerializationState.CUDADeserializer.InitialVisit(Object param)
at Hybridizer.Runtime.CUDAImports.NativeSerializerState.UpdateManagedData(Object param)

Leverage automatic null checks

As of version 1.0.5923.8928, Hybridizer proposes automatic null checks with various behaviors:

Print

The good old printf debugging! Just add -DHYBRIDIZER_NULL_CHECKS_PRINT to Hybridizer Jitter Options, recompile, and you’ll get:

null pointer at directory\Program.cs:31
System.ApplicationException: CUDA error occured before deserialization (most probably during kernel call): an illegal memory access was encountered

handy isn’t it?

Break

Add -DHYBRIDIZER_NULL_CHECKS_BREAK to Hybridizer Jitter Options. Then recompile and start CUDA debugging, and debugger will stop just before the illegal memory access:
automatic null checks


Tags: , ,