#define _REENTRANT

#include <cuda.h>
#include <stdlib.h>
#include <stdio.h>
#include <assert.h>

#include "ezt_cuda.h"
#include "cuda_ev_codes.h"
#include "eztrace.h"

extern "C" {


  /**** CUDA runtime interface ****/


  cudaError_t cudaThreadSynchronize(){ // note: deprecated
    FUNCTION_ENTRY;
    EZT_CUDA_INITIALIZE;
    cudaError_t ret;
    int deviceId;
    ret = cudaGetDevice(&deviceId);
    assert(ret == cudaSuccess);

    EZTRACE_EVENT_PACKED_1(EZTRACE_CUDA_CUDATHREADSYNCHRONIZE_START, deviceId);
    ret = libcudaThreadSynchronize();
    EZTRACE_EVENT_PACKED_1(EZTRACE_CUDA_CUDATHREADSYNCHRONIZE_STOP, deviceId);
    return ret;
  }

  cudaError_t cudaDeviceSynchronize(){
    FUNCTION_ENTRY;
    EZT_CUDA_INITIALIZE;
    cudaError_t ret;
    int deviceId;
    ret = cudaGetDevice(&deviceId);
    assert(ret == cudaSuccess);

    EZTRACE_EVENT_PACKED_1(EZTRACE_CUDA_CUDADEVICESYNCHRONIZE_START, deviceId);
    ret = libcudaDeviceSynchronize();
    EZTRACE_EVENT_PACKED_1(EZTRACE_CUDA_CUDADEVICESYNCHRONIZE_STOP, deviceId);
    return ret;
  }


  /* Kernel management */

#if 0
  // todo: just before cudaLaunch, nvcc puts a call to cudaConfigureCall . We could intercept this function and
  // record the gridDim, blockDim, etc.
  cudaError_t cudaConfigureCall ( dim3  gridDim,
				  dim3  blockDim,
				  size_t  sharedMem,
				  cudaStream_t  stream) {
    fprintf(stderr, "cudaConfigureCall(gridDim(%d, %d, %d), blockDim(%d, %d, %d), sharedMem=%d, stream=%p)\n",
	    gridDim.x, gridDim.y, gridDim.z,
	    blockDim.x, blockDim.y, blockDim.z,
	    sharedMem, stream);
    cudaError_t ret;
    ret = libcudaConfigureCall(gridDim, blockDim, sharedMem, stream);
    return ret;
  }
#else
  cudaError_t cudaConfigureCall ( dim3  gridDim,
				  dim3  blockDim,
				  size_t  sharedMem,
				  cudaStream_t  stream) {
    return libcudaConfigureCall(gridDim, blockDim, sharedMem, stream);
  }
#endif

  cudaError_t cudaLaunch(const void *entry)
  {
    FUNCTION_ENTRY;
    EZT_CUDA_INITIALIZE;
    cudaError_t ret;
    static unsigned kernel_id = 0;
    unsigned current_id = kernel_id++;
    int deviceId;
    ret = cudaGetDevice(&deviceId);
    assert(ret == cudaSuccess);

    EZTRACE_EVENT_PACKED_2(EZTRACE_CUDA_CULAUNCHKERNEL_START, deviceId, current_id);
    ret = libcudaLaunch(entry);
    EZTRACE_EVENT_PACKED_2(EZTRACE_CUDA_CULAUNCHKERNEL_STOP, deviceId, current_id);
    return ret;
  }


  /* Memory management */
  cudaError_t cudaMalloc(void **  devPtr, size_t  size) {
    FUNCTION_ENTRY;
    EZT_CUDA_INITIALIZE;
    cudaError_t  ret;
    int deviceId;
    ret = cudaGetDevice(&deviceId);
    assert(ret == cudaSuccess);
    ezt_cuda_size_t ezt_size = size;

    // todo: use devptr ?
    EZTRACE_EVENT_PACKED_2(EZTRACE_CUDA_CUMEMALLOC_START, deviceId, ezt_size);
    ret = libcudaMalloc(devPtr, size);
    EZTRACE_EVENT_PACKED_3(EZTRACE_CUDA_CUMEMALLOC_STOP, deviceId, ezt_size, *devPtr);
    return ret;
  }

  cudaError_t cudaMallocPitch(void **devPtr, size_t *pitch, size_t width, size_t height) {
    FUNCTION_ENTRY;
    EZT_CUDA_INITIALIZE;
    cudaError_t ret;
    int deviceId;
    ret = cudaGetDevice(&deviceId);
    assert(ret == cudaSuccess);
    ezt_cuda_size_t ezt_size = width*height;

    EZTRACE_EVENT_PACKED_2(EZTRACE_CUDA_CUMEMALLOC_START, deviceId, ezt_size);
    ret = libcudaMallocPitch(devPtr, pitch, width, height);
    EZTRACE_EVENT_PACKED_3(EZTRACE_CUDA_CUMEMALLOC_STOP, deviceId, ezt_size, *devPtr);
    return ret;
  }

  cudaError_t cudaMallocArray(struct cudaArray **array, const struct cudaChannelFormatDesc *desc, size_t width, size_t height, unsigned int flags) {
    FUNCTION_ENTRY;
    EZT_CUDA_INITIALIZE;
    cudaError_t ret;
    ezt_cuda_size_t size = width*height;
    int deviceId;
    ret = cudaGetDevice(&deviceId);
    assert(ret == cudaSuccess);

    EZTRACE_EVENT_PACKED_2(EZTRACE_CUDA_CUMEMALLOC_START, deviceId, size);
    ret = libcudaMallocArray(array, desc, width, height, flags);
    EZTRACE_EVENT_PACKED_3(EZTRACE_CUDA_CUMEMALLOC_STOP, deviceId, size, *array);
    return ret;
  }

  cudaError_t cudaMalloc3D(struct cudaPitchedPtr* pitchedDevPtr, struct cudaExtent extent) {
    FUNCTION_ENTRY;
    EZT_CUDA_INITIALIZE;
    cudaError_t ret;
    int deviceId;
    ret = cudaGetDevice(&deviceId);
    assert(ret == cudaSuccess);

    ezt_cuda_size_t size = extent.width * extent.height * extent.depth;
    EZTRACE_EVENT_PACKED_2(EZTRACE_CUDA_CUMEMALLOC_START, deviceId, size);
    ret = libcudaMalloc3D(pitchedDevPtr, extent);
    EZTRACE_EVENT_PACKED_3(EZTRACE_CUDA_CUMEMALLOC_STOP, deviceId, size, pitchedDevPtr);
    return ret;
  }

  cudaError_t cudaMalloc3DArray(cudaArray_t *array, const struct cudaChannelFormatDesc* desc, struct cudaExtent extent, unsigned int flags) {
    FUNCTION_ENTRY;
    EZT_CUDA_INITIALIZE;
    cudaError_t ret;
    int deviceId;
    ret = cudaGetDevice(&deviceId);
    assert(ret == cudaSuccess);

    ezt_cuda_size_t size = extent.width * extent.height * extent.depth;
    EZTRACE_EVENT_PACKED_2(EZTRACE_CUDA_CUMEMALLOC_START, deviceId, size);
    ret = libcudaMalloc3DArray(array, desc, extent, flags);
    EZTRACE_EVENT_PACKED_3(EZTRACE_CUDA_CUMEMALLOC_STOP, deviceId, size, *array);
    return ret;
  }

  cudaError_t cudaMallocMipmappedArray(cudaMipmappedArray_t *mipmappedArray, const struct cudaChannelFormatDesc* desc, struct cudaExtent extent, unsigned int numLevels, unsigned int flags) {
    FUNCTION_ENTRY;
    EZT_CUDA_INITIALIZE;
    cudaError_t ret;
    int deviceId;
    ret = cudaGetDevice(&deviceId);
    assert(ret == cudaSuccess);

    ezt_cuda_size_t size = extent.width * extent.height * extent.depth;
    EZTRACE_EVENT_PACKED_2(EZTRACE_CUDA_CUMEMALLOC_START, deviceId, size);
    ret = libcudaMallocMipmappedArray(mipmappedArray, desc, extent, numLevels, flags);
    EZTRACE_EVENT_PACKED_3(EZTRACE_CUDA_CUMEMALLOC_STOP, deviceId, size, mipmappedArray);
    return ret;
  }


  cudaError_t cudaFree(void *devPtr) {
    FUNCTION_ENTRY;
    EZT_CUDA_INITIALIZE;
    cudaError_t  ret;
    int deviceId;
    ret = cudaGetDevice(&deviceId);
    assert(ret == cudaSuccess);

    if(devPtr)
      EZTRACE_EVENT_PACKED_2(EZTRACE_CUDA_CUDAFREE_START, deviceId, devPtr);

    ret = libcudaFree(devPtr);

    if(devPtr)
      EZTRACE_EVENT_PACKED_2(EZTRACE_CUDA_CUDAFREE_STOP, deviceId, devPtr);
    return ret;
  }

  cudaError_t cudaFreeArray(cudaArray_t array) {
    FUNCTION_ENTRY;
    EZT_CUDA_INITIALIZE;
    cudaError_t ret;
    int deviceId;
    ret = cudaGetDevice(&deviceId);
    assert(ret == cudaSuccess);

    if(array)
      EZTRACE_EVENT_PACKED_2(EZTRACE_CUDA_CUDAFREE_START, deviceId, array);
    ret = libcudaFreeArray(array);
    if(array)
      EZTRACE_EVENT_PACKED_2(EZTRACE_CUDA_CUDAFREE_STOP, deviceId, array);
    return ret;
  }

  cudaError_t cudaFreeMipmappedArray(cudaMipmappedArray_t mipmappedArray) {
    FUNCTION_ENTRY;
    EZT_CUDA_INITIALIZE;
    cudaError_t ret;
    int deviceId;
    ret = cudaGetDevice(&deviceId);
    assert(ret == cudaSuccess);

    if(mipmappedArray)
      EZTRACE_EVENT_PACKED_2(EZTRACE_CUDA_CUDAFREE_START, deviceId, mipmappedArray);
    ret = libcudaFreeMipmappedArray(mipmappedArray);
    if(mipmappedArray)
      EZTRACE_EVENT_PACKED_2(EZTRACE_CUDA_CUDAFREE_STOP, deviceId, mipmappedArray);
    return ret;
  }


  /* Memory Transfers */
  cudaError_t cudaMemcpy(void *dst,
			 const void *src,
			 size_t  count,
			 enum cudaMemcpyKind kind) {
    FUNCTION_ENTRY;
    EZT_CUDA_INITIALIZE;
    cudaError_t  ret;
    // todo: use devptr ?
    enum ezt_cudaMemcpyKind ezt_kind = CUDA_MEMCPY_KIND_TO_EZT(kind);

    int deviceId;
    ret = cudaGetDevice(&deviceId);
    assert(ret == cudaSuccess);
    ezt_cuda_size_t ezt_size = count;

    EZTRACE_EVENT_PACKED_3(EZTRACE_CUDA_CUMEMCPY_START, deviceId, ezt_size, ezt_kind);
    ret = libcudaMemcpy(dst, src, count, kind);
    EZTRACE_EVENT_PACKED_3(EZTRACE_CUDA_CUMEMCPY_STOP, deviceId, ezt_size, ezt_kind);
    return ret;
  }

  cudaError_t cudaMemcpy3D(const struct cudaMemcpy3DParms *p) {
    FUNCTION_ENTRY;
    EZT_CUDA_INITIALIZE;
    cudaError_t  ret;
    enum ezt_cudaMemcpyKind ezt_kind = CUDA_MEMCPY_KIND_TO_EZT(p->kind);
    ezt_cuda_size_t size = p->extent.width * p->extent.height *p->extent.depth; 
    int deviceId;
    ret = cudaGetDevice(&deviceId);
    assert(ret == cudaSuccess);

    EZTRACE_EVENT_PACKED_3(EZTRACE_CUDA_CUMEMCPY_START, deviceId, size, ezt_kind);
    ret = libcudaMemcpy3D(p);
    EZTRACE_EVENT_PACKED_3(EZTRACE_CUDA_CUMEMCPY_STOP, deviceId, size, ezt_kind);
    return ret;
  }

  cudaError_t cudaMemcpy3DAsync(const struct cudaMemcpy3DParms *p, cudaStream_t stream ) {
    FUNCTION_ENTRY;
    EZT_CUDA_INITIALIZE;
    cudaError_t  ret;
    enum ezt_cudaMemcpyKind ezt_kind = CUDA_MEMCPY_KIND_TO_EZT(p->kind);
    ezt_cuda_size_t size = p->extent.width * p->extent.height *p->extent.depth; 

    int deviceId;
    ret = cudaGetDevice(&deviceId);
    assert(ret == cudaSuccess);

    EZTRACE_EVENT_PACKED_3(EZTRACE_CUDA_CUMEMCPY_START, deviceId, size, ezt_kind);
    ret = libcudaMemcpy3DAsync(p, stream);
    EZTRACE_EVENT_PACKED_3(EZTRACE_CUDA_CUMEMCPY_STOP, deviceId, size, ezt_kind);
    return ret;
  }


  cudaError_t cudaMemcpyToArray(cudaArray_t dst, size_t wOffset, size_t hOffset, const void *src, size_t count, enum cudaMemcpyKind kind) {
    FUNCTION_ENTRY;
    EZT_CUDA_INITIALIZE;
    cudaError_t ret;
    enum ezt_cudaMemcpyKind ezt_kind = CUDA_MEMCPY_KIND_TO_EZT(kind);
    ezt_cuda_size_t size = count;
    int deviceId;
    ret = cudaGetDevice(&deviceId);
    assert(ret == cudaSuccess);

    EZTRACE_EVENT_PACKED_3(EZTRACE_CUDA_CUMEMCPY_START, deviceId, size, ezt_kind);
    ret = libcudaMemcpyToArray(dst, wOffset, hOffset, src, count, kind);
    EZTRACE_EVENT_PACKED_3(EZTRACE_CUDA_CUMEMCPY_STOP, deviceId, size, ezt_kind);
    return ret;
  }

  cudaError_t cudaMemcpyFromArray(void *dst, cudaArray_const_t src, size_t wOffset, size_t hOffset, size_t count, enum cudaMemcpyKind kind) {
    FUNCTION_ENTRY;
    EZT_CUDA_INITIALIZE;
    cudaError_t ret;
    enum ezt_cudaMemcpyKind ezt_kind = CUDA_MEMCPY_KIND_TO_EZT(kind);
    ezt_cuda_size_t size = count;

    int deviceId;
    ret = cudaGetDevice(&deviceId);
    assert(ret == cudaSuccess);

    EZTRACE_EVENT_PACKED_3(EZTRACE_CUDA_CUMEMCPY_START, deviceId, size, ezt_kind);
    ret = libcudaMemcpyFromArray(dst, src, wOffset, hOffset, count, kind);
    EZTRACE_EVENT_PACKED_3(EZTRACE_CUDA_CUMEMCPY_STOP, deviceId, size, ezt_kind);
    return ret;
  }

  cudaError_t cudaMemcpyArrayToArray(cudaArray_t dst, size_t wOffsetDst, size_t hOffsetDst, cudaArray_const_t src, size_t wOffsetSrc, size_t hOffsetSrc, size_t count, enum cudaMemcpyKind kind) {
    FUNCTION_ENTRY;
    EZT_CUDA_INITIALIZE;
    cudaError_t ret;
    enum ezt_cudaMemcpyKind ezt_kind = CUDA_MEMCPY_KIND_TO_EZT(kind);
    ezt_cuda_size_t size = count;

    int deviceId;
    ret = cudaGetDevice(&deviceId);
    assert(ret == cudaSuccess);

    EZTRACE_EVENT_PACKED_3(EZTRACE_CUDA_CUMEMCPY_START, deviceId, size, ezt_kind);
    ret = libcudaMemcpyArrayToArray(dst, wOffsetDst, hOffsetDst, src, wOffsetSrc, hOffsetSrc, count, kind);
    EZTRACE_EVENT_PACKED_3(EZTRACE_CUDA_CUMEMCPY_STOP, deviceId, size, ezt_kind);
    return ret;
  }

  cudaError_t cudaMemcpy2D(void *dst, size_t dpitch, const void *src, size_t spitch, size_t width, size_t height, enum cudaMemcpyKind kind) {
    FUNCTION_ENTRY;
    EZT_CUDA_INITIALIZE;
    cudaError_t ret;
    enum ezt_cudaMemcpyKind ezt_kind = CUDA_MEMCPY_KIND_TO_EZT(kind);
    ezt_cuda_size_t size = width*height;

    int deviceId;
    ret = cudaGetDevice(&deviceId);
    assert(ret == cudaSuccess);

    EZTRACE_EVENT_PACKED_3(EZTRACE_CUDA_CUMEMCPY_START, deviceId, size, ezt_kind);
    ret = libcudaMemcpy2D(dst, dpitch, src, spitch, width, height, kind);
    EZTRACE_EVENT_PACKED_3(EZTRACE_CUDA_CUMEMCPY_STOP, deviceId, size, ezt_kind);
    return ret;
  }

  cudaError_t cudaMemcpy2DToArray(cudaArray_t dst, size_t wOffset, size_t hOffset, const void *src, size_t spitch, size_t width, size_t height, enum cudaMemcpyKind kind) {
    FUNCTION_ENTRY;
    EZT_CUDA_INITIALIZE;
    cudaError_t ret;
    enum ezt_cudaMemcpyKind ezt_kind = CUDA_MEMCPY_KIND_TO_EZT(kind);
    ezt_cuda_size_t size = width*height;

    int deviceId;
    ret = cudaGetDevice(&deviceId);
    assert(ret == cudaSuccess);

    EZTRACE_EVENT_PACKED_3(EZTRACE_CUDA_CUMEMCPY_START, deviceId, size, ezt_kind);
    ret = libcudaMemcpy2DToArray(dst, wOffset, hOffset, src, spitch, width, height, kind);
    EZTRACE_EVENT_PACKED_3(EZTRACE_CUDA_CUMEMCPY_STOP, deviceId, size, ezt_kind);
    return ret;
  }

  cudaError_t cudaMemcpy2DFromArray(void *dst, size_t dpitch, cudaArray_const_t src, size_t wOffset, size_t hOffset, size_t width, size_t height, enum cudaMemcpyKind kind) {
    FUNCTION_ENTRY;
    EZT_CUDA_INITIALIZE;
    cudaError_t ret;
    enum ezt_cudaMemcpyKind ezt_kind = CUDA_MEMCPY_KIND_TO_EZT(kind);
    ezt_cuda_size_t size = width*height;

    int deviceId;
    ret = cudaGetDevice(&deviceId);
    assert(ret == cudaSuccess);

    EZTRACE_EVENT_PACKED_3(EZTRACE_CUDA_CUMEMCPY_START, deviceId, size, ezt_kind);
    ret = libcudaMemcpy2DFromArray(dst, dpitch, src, wOffset, hOffset, width, height, kind) ;
    EZTRACE_EVENT_PACKED_3(EZTRACE_CUDA_CUMEMCPY_STOP, deviceId, size, ezt_kind);
    return ret;
  }

  cudaError_t cudaMemcpy2DArrayToArray(cudaArray_t dst, size_t wOffsetDst, size_t hOffsetDst, cudaArray_const_t src, size_t wOffsetSrc, size_t hOffsetSrc, size_t width, size_t height, enum cudaMemcpyKind kind) {
    FUNCTION_ENTRY;
    EZT_CUDA_INITIALIZE;
    cudaError_t ret;
    enum ezt_cudaMemcpyKind ezt_kind = CUDA_MEMCPY_KIND_TO_EZT(kind);
    ezt_cuda_size_t size = width*height;

    int deviceId;
    ret = cudaGetDevice(&deviceId);
    assert(ret == cudaSuccess);

    EZTRACE_EVENT_PACKED_3(EZTRACE_CUDA_CUMEMCPY_START, deviceId, size, ezt_kind);
    ret = libcudaMemcpy2DArrayToArray(dst, wOffsetDst, hOffsetDst, src, wOffsetSrc, hOffsetSrc, width, height, kind);
    EZTRACE_EVENT_PACKED_3(EZTRACE_CUDA_CUMEMCPY_STOP, deviceId, size, ezt_kind);
    return ret;
  }

  cudaError_t cudaMemcpyToSymbol(const void *symbol, const void *src, size_t count, size_t offset , enum cudaMemcpyKind kind) {
    FUNCTION_ENTRY;
    EZT_CUDA_INITIALIZE;
    cudaError_t ret;
    enum ezt_cudaMemcpyKind ezt_kind = CUDA_MEMCPY_KIND_TO_EZT(kind);
    ezt_cuda_size_t size = count;

    int deviceId;
    ret = cudaGetDevice(&deviceId);
    assert(ret == cudaSuccess);

    EZTRACE_EVENT_PACKED_3(EZTRACE_CUDA_CUMEMCPY_START, deviceId, size, ezt_kind);
    ret = libcudaMemcpyToSymbol(symbol, src, count, offset , kind);
    EZTRACE_EVENT_PACKED_3(EZTRACE_CUDA_CUMEMCPY_STOP, deviceId, size, ezt_kind);
    return ret;
  }

  cudaError_t cudaMemcpyFromSymbol(void *dst, const void *symbol, size_t count, size_t offset , enum cudaMemcpyKind kind) {
    FUNCTION_ENTRY;
    EZT_CUDA_INITIALIZE;
    cudaError_t ret;
    enum ezt_cudaMemcpyKind ezt_kind = CUDA_MEMCPY_KIND_TO_EZT(kind);
    ezt_cuda_size_t size = count;

    int deviceId;
    ret = cudaGetDevice(&deviceId);
    assert(ret == cudaSuccess);

    EZTRACE_EVENT_PACKED_3(EZTRACE_CUDA_CUMEMCPY_START, deviceId, size, ezt_kind);
    ret = libcudaMemcpyFromSymbol(dst, symbol, count, offset , kind);
    EZTRACE_EVENT_PACKED_3(EZTRACE_CUDA_CUMEMCPY_STOP, deviceId, size, ezt_kind);
    return ret;
  }

  cudaError_t cudaMemcpyAsync(void *dst, const void *src, size_t count, enum cudaMemcpyKind kind, cudaStream_t stream ) {
    FUNCTION_ENTRY;
    EZT_CUDA_INITIALIZE;
    cudaError_t ret;
    enum ezt_cudaMemcpyKind ezt_kind = CUDA_MEMCPY_KIND_TO_EZT(kind);
    ezt_cuda_size_t size = count;
    int deviceId;

    ret = cudaGetDevice(&deviceId);
    assert(ret == cudaSuccess);

    EZTRACE_EVENT_PACKED_3(EZTRACE_CUDA_CUMEMCPY_START, deviceId, size, ezt_kind);
    ret = libcudaMemcpyAsync(dst, src, count, kind, stream );
    EZTRACE_EVENT_PACKED_3(EZTRACE_CUDA_CUMEMCPY_STOP, deviceId, size, ezt_kind);
    return ret;
  }

  cudaError_t cudaMemcpyToArrayAsync(cudaArray_t dst, size_t wOffset, size_t hOffset, const void *src, size_t count, enum cudaMemcpyKind kind, cudaStream_t stream ) {
    FUNCTION_ENTRY;
    EZT_CUDA_INITIALIZE;
    cudaError_t ret;
    enum ezt_cudaMemcpyKind ezt_kind = CUDA_MEMCPY_KIND_TO_EZT(kind);
    ezt_cuda_size_t size = count;

    int deviceId;
    ret = cudaGetDevice(&deviceId);
    assert(ret == cudaSuccess);

    EZTRACE_EVENT_PACKED_3(EZTRACE_CUDA_CUMEMCPY_START, deviceId, size, ezt_kind);
    ret = libcudaMemcpyToArrayAsync(dst, wOffset, hOffset, src, count, kind, stream);
    EZTRACE_EVENT_PACKED_3(EZTRACE_CUDA_CUMEMCPY_STOP, deviceId, size, ezt_kind);
    return ret;
  }

  cudaError_t cudaMemcpyFromArrayAsync(void *dst, cudaArray_const_t src, size_t wOffset, size_t hOffset, size_t count, enum cudaMemcpyKind kind, cudaStream_t stream ) {
    FUNCTION_ENTRY;
    EZT_CUDA_INITIALIZE;
    cudaError_t ret;
    enum ezt_cudaMemcpyKind ezt_kind = CUDA_MEMCPY_KIND_TO_EZT(kind);
    ezt_cuda_size_t size = count;

    int deviceId;
    ret = cudaGetDevice(&deviceId);
    assert(ret == cudaSuccess);

    EZTRACE_EVENT_PACKED_3(EZTRACE_CUDA_CUMEMCPY_START, deviceId, size, ezt_kind);
    ret = libcudaMemcpyFromArrayAsync(dst, src, wOffset, hOffset, count, kind, stream);
    EZTRACE_EVENT_PACKED_3(EZTRACE_CUDA_CUMEMCPY_STOP, deviceId, size, ezt_kind);
    return ret;
  }

  cudaError_t cudaMemcpy2DAsync(void *dst, size_t dpitch, const void *src, size_t spitch, size_t width, size_t height, enum cudaMemcpyKind kind, cudaStream_t stream ) {
    FUNCTION_ENTRY;
    EZT_CUDA_INITIALIZE;
    cudaError_t ret;
    enum ezt_cudaMemcpyKind ezt_kind = CUDA_MEMCPY_KIND_TO_EZT(kind);
    ezt_cuda_size_t size = width * height;

    int deviceId;
    ret = cudaGetDevice(&deviceId);
    assert(ret == cudaSuccess);

    EZTRACE_EVENT_PACKED_3(EZTRACE_CUDA_CUMEMCPY_START, deviceId, size, ezt_kind);
    ret = libcudaMemcpy2DAsync(dst, dpitch, src, spitch, width, height, kind, stream );
    EZTRACE_EVENT_PACKED_3(EZTRACE_CUDA_CUMEMCPY_STOP, deviceId, size, ezt_kind);
    return ret;
  }

  cudaError_t cudaMemcpy2DToArrayAsync(cudaArray_t dst, size_t wOffset, size_t hOffset, const void *src, size_t spitch, size_t width, size_t height, enum cudaMemcpyKind kind, cudaStream_t stream) {
    FUNCTION_ENTRY;
    EZT_CUDA_INITIALIZE;
    cudaError_t ret;
    enum ezt_cudaMemcpyKind ezt_kind = CUDA_MEMCPY_KIND_TO_EZT(kind);
    ezt_cuda_size_t size = width * height;

    int deviceId;
    ret = cudaGetDevice(&deviceId);
    assert(ret == cudaSuccess);

    EZTRACE_EVENT_PACKED_3(EZTRACE_CUDA_CUMEMCPY_START, deviceId, size, ezt_kind);
    ret = libcudaMemcpy2DToArrayAsync(dst, wOffset, hOffset, src, spitch, width, height, kind, stream );
    EZTRACE_EVENT_PACKED_3(EZTRACE_CUDA_CUMEMCPY_STOP, deviceId, size, ezt_kind);
    return ret;
  }

  cudaError_t cudaMemcpy2DFromArrayAsync(void *dst, size_t dpitch, cudaArray_const_t src, size_t wOffset, size_t hOffset, size_t width, size_t height, enum cudaMemcpyKind kind, cudaStream_t stream ) {
    FUNCTION_ENTRY;
    EZT_CUDA_INITIALIZE;
    cudaError_t ret;
    enum ezt_cudaMemcpyKind ezt_kind = CUDA_MEMCPY_KIND_TO_EZT(kind);
    ezt_cuda_size_t size = width * height;

    int deviceId;
    ret = cudaGetDevice(&deviceId);
    assert(ret == cudaSuccess);

    EZTRACE_EVENT_PACKED_3(EZTRACE_CUDA_CUMEMCPY_START, deviceId, size, ezt_kind);
    ret = libcudaMemcpy2DFromArrayAsync(dst, dpitch, src, wOffset, hOffset, width, height, kind, stream);
    EZTRACE_EVENT_PACKED_3(EZTRACE_CUDA_CUMEMCPY_STOP, deviceId, size, ezt_kind);
    return ret;
  }

  cudaError_t cudaMemcpyToSymbolAsync(const void *symbol, const void *src, size_t count, size_t offset, enum cudaMemcpyKind kind, cudaStream_t stream ) {
    FUNCTION_ENTRY;
    EZT_CUDA_INITIALIZE;
    cudaError_t ret;
    enum ezt_cudaMemcpyKind ezt_kind = CUDA_MEMCPY_KIND_TO_EZT(kind);
    ezt_cuda_size_t size = count;

    int deviceId;
    ret = cudaGetDevice(&deviceId);
    assert(ret == cudaSuccess);

    EZTRACE_EVENT_PACKED_3(EZTRACE_CUDA_CUMEMCPY_START, deviceId, size, ezt_kind);
    ret = libcudaMemcpyToSymbolAsync(symbol, src, count, offset, kind, stream );
    EZTRACE_EVENT_PACKED_3(EZTRACE_CUDA_CUMEMCPY_STOP, deviceId, size, ezt_kind);
    return ret;
  }

  cudaError_t cudaMemcpyFromSymbolAsync(void *dst, const void *symbol, size_t count, size_t offset, enum cudaMemcpyKind kind, cudaStream_t stream ) {
    FUNCTION_ENTRY;
    EZT_CUDA_INITIALIZE;
    cudaError_t ret;
    enum ezt_cudaMemcpyKind ezt_kind = CUDA_MEMCPY_KIND_TO_EZT(kind);
    ezt_cuda_size_t size = count;

    int deviceId;
    ret = cudaGetDevice(&deviceId);
    assert(ret == cudaSuccess);

    EZTRACE_EVENT_PACKED_3(EZTRACE_CUDA_CUMEMCPY_START, deviceId, size, ezt_kind);
    ret = libcudaMemcpyFromSymbolAsync(dst, symbol, count, offset, kind, stream);
    EZTRACE_EVENT_PACKED_3(EZTRACE_CUDA_CUMEMCPY_STOP, deviceId, size, ezt_kind);
    return ret;
  }


  /**** functions to implement ****/

  cudaError_t cudaMemset(void *devPtr, int value, size_t count) {
    return libcudaMemset(devPtr, value, count);
  }

  cudaError_t cudaMemset2D(void *devPtr, size_t pitch, int value, size_t width, size_t height) {
    return libcudaMemset2D(devPtr, pitch, value, width, height);
  }

  cudaError_t cudaMemset3D(struct cudaPitchedPtr pitchedDevPtr, int value, struct cudaExtent extent) {
    return libcudaMemset3D(pitchedDevPtr, value, extent);
  }

  cudaError_t cudaMemsetAsync(void *devPtr, int value, size_t count, cudaStream_t stream ) {
    return libcudaMemsetAsync(devPtr, value, count, stream );
  }

  cudaError_t cudaMemset2DAsync(void *devPtr, size_t pitch, int value, size_t width, size_t height, cudaStream_t stream ) {
    return libcudaMemset2DAsync(devPtr, pitch, value, width, height, stream );
  }

  cudaError_t cudaMemset3DAsync(struct cudaPitchedPtr pitchedDevPtr, int value, struct cudaExtent extent, cudaStream_t stream ) {
    return libcudaMemset3DAsync(pitchedDevPtr, value, extent, stream );
  }

 cudaError_t cudaMemcpy3DPeer(const struct cudaMemcpy3DPeerParms *p) {
    fprintf(stderr, "[EZTRACE] Warning: the instrumentation of function %s is not implemented!\n", __FUNCTION__);
    return libcudaMemcpy3DPeer(p);
  }

  cudaError_t cudaMemcpy3DPeerAsync(const struct cudaMemcpy3DPeerParms *p, cudaStream_t stream ) {
    fprintf(stderr, "[EZTRACE] Warning: the instrumentation of function %s is not implemented!\n", __FUNCTION__);
    return libcudaMemcpy3DPeerAsync(p, stream);
  }

  cudaError_t cudaMemcpyPeer(void *dst, int dstDevice, const void *src, int srcDevice, size_t count) {
    fprintf(stderr, "[EZTRACE] Warning: the instrumentation of function %s is not implemented!\n", __FUNCTION__);
    return libcudaMemcpyPeer(dst, dstDevice, src, srcDevice, count);
  }

  cudaError_t cudaMemcpyPeerAsync(void *dst, int dstDevice, const void *src, int srcDevice, size_t count, cudaStream_t stream ) {
    fprintf(stderr, "[EZTRACE] Warning: the instrumentation of function %s is not implemented!\n", __FUNCTION__);
    return libcudaMemcpyPeerAsync(dst, dstDevice, src, srcDevice, count, stream );
  }
}

  cudaError_t cudaMallocHost(void **ptr, size_t size) {
    // todo: record something or remove this function
    return libcudaMallocHost(ptr, size);
  }


  cudaError_t cudaFreeHost(void *ptr) {
    return libcudaFreeHost(ptr);
  }


#if 0
  cudaError_t cudaHostAlloc(void **pHost, size_t size, unsigned int flags);
  cudaError_t cudaHostRegister(void *ptr, size_t size, unsigned int flags);
  cudaError_t cudaHostUnregister(void *ptr);
#endif
