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: