packages feed

cuda-0.13.0.0: src/Foreign/CUDA/Driver/Error.chs

{-# LANGUAGE BangPatterns             #-}
{-# LANGUAGE DeriveDataTypeable       #-}
{-# LANGUAGE ForeignFunctionInterface #-}
--------------------------------------------------------------------------------
-- |
-- Module    : Foreign.CUDA.Driver.Error
-- Copyright : [2009..2023] Trevor L. McDonell
-- License   : BSD
--
-- Error handling
--
--------------------------------------------------------------------------------

module Foreign.CUDA.Driver.Error (

  -- * CUDA Errors
  Status(..), CUDAException(..),
  describe,
  cudaError, cudaErrorIO, requireSDK,
  resultIfOk, nothingIfOk, checkStatus,

) where

-- Friends
import Foreign.CUDA.Internal.C2HS
import Foreign.CUDA.Internal.Describe

-- System
import Control.Exception
import Control.Monad
import Data.Typeable
import Foreign.C
import Foreign.Marshal
import Foreign.Ptr
import Foreign.Storable
import Language.Haskell.TH
import System.IO.Unsafe
import Text.Printf


#include "cbits/stubs.h"
{# context lib="cuda" #}


--------------------------------------------------------------------------------
-- Return Status
--------------------------------------------------------------------------------

--
-- Error Codes
--
{# enum CUresult as Status
    { underscoreToCase
    , CUDA_SUCCESS                      as Success
    , CUDA_ERROR_NO_BINARY_FOR_GPU      as NoBinaryForGPU
    , CUDA_ERROR_INVALID_PTX            as InvalidPTX
    , CUDA_ERROR_INVALID_PC             as InvalidPC
    }
    with prefix="CUDA_ERROR" deriving (Eq, Show) #}


-- |
-- Return a descriptive error string associated with a particular error code
--
instance Describe Status where
#if CUDA_VERSION >= 6000
  describe status =
    case cuGetErrorString status of
      (Success, msg) -> msg
      (err, _)       -> throw (ExitCode err)

{# fun pure unsafe cuGetErrorString
    { cFromEnum `Status'
    , alloca-   `String' ppeek* } -> `Status' cToEnum #}
    where
      ppeek = peek >=> peekCString
#else
  describe Success                        = "no error"
  describe InvalidValue                   = "invalid argument"
  describe OutOfMemory                    = "out of memory"
  describe NotInitialized                 = "driver not initialised"
  describe Deinitialized                  = "driver deinitialised"
  describe NoDevice                       = "no CUDA-capable device is available"
  describe InvalidDevice                  = "invalid device ordinal"
  describe InvalidImage                   = "invalid kernel image"
  describe InvalidContext                 = "invalid context handle"
  describe ContextAlreadyCurrent          = "context already current"
  describe MapFailed                      = "map failed"
  describe UnmapFailed                    = "unmap failed"
  describe ArrayIsMapped                  = "array is mapped"
  describe AlreadyMapped                  = "already mapped"
  describe NoBinaryForGPU                 = "no binary available for this GPU"
  describe AlreadyAcquired                = "resource already acquired"
  describe NotMapped                      = "not mapped"
  describe InvalidSource                  = "invalid source"
  describe FileNotFound                   = "file not found"
  describe InvalidHandle                  = "invalid handle"
  describe NotFound                       = "not found"
  describe NotReady                       = "device not ready"
  describe LaunchFailed                   = "unspecified launch failure"
  describe LaunchOutOfResources           = "too many resources requested for launch"
  describe LaunchTimeout                  = "the launch timed out and was terminated"
  describe LaunchIncompatibleTexturing    = "launch with incompatible texturing"
#if CUDA_VERSION >= 3000
  describe NotMappedAsArray               = "mapped resource not available for access as an array"
  describe NotMappedAsPointer             = "mapped resource not available for access as a pointer"
  describe EccUncorrectable               = "uncorrectable ECC error detected"
#endif
#if CUDA_VERSION >= 3000 && CUDA_VERSION < 3020
  describe PointerIs64bit                 = "attempt to retrieve a 64-bit pointer via a 32-bit API function"
  describe SizeIs64bit                    = "attempt to retrieve 64-bit size via a 32-bit API function"
#endif
#if CUDA_VERSION >= 3010
  describe UnsupportedLimit               = "limits not supported by device"
  describe SharedObjectSymbolNotFound     = "link to a shared object failed to resolve"
  describe SharedObjectInitFailed         = "shared object initialisation failed"
#endif
#if CUDA_VERSION >= 3020
  describe OperatingSystem                = "operating system call failed"
#endif
#if CUDA_VERSION >= 4000
  describe ProfilerDisabled               = "profiling APIs disabled: application running with visual profiler"
  describe ProfilerNotInitialized         = "profiler not initialised"
  describe ProfilerAlreadyStarted         = "profiler already started"
  describe ProfilerAlreadyStopped         = "profiler already stopped"
  describe ContextAlreadyInUse            = "context is already bound to a thread and in use"
  describe PeerAccessAlreadyEnabled       = "peer access already enabled"
  describe PeerAccessNotEnabled           = "peer access has not been enabled"
  describe PrimaryContextActive           = "primary context for this device has already been initialised"
  describe ContextIsDestroyed             = "context already destroyed"
#endif
#if CUDA_VERSION >= 4010
  describe Assert                         = "device-side assert triggered"
  describe TooManyPeers                   = "peer mapping resources exhausted"
  describe HostMemoryAlreadyRegistered    = "part or all of the requested memory range is already mapped"
  describe HostMemoryNotRegistered        = "pointer does not correspond to a registered memory region"
#endif
#if CUDA_VERSION >= 5000
  describe PeerAccessUnsupported          = "peer access is not supported across the given devices"
  describe NotPermitted                   = "not permitted"
  describe NotSupported                   = "not supported"
#endif
  describe Unknown                        = "unknown error"
#endif


--------------------------------------------------------------------------------
-- Exceptions
--------------------------------------------------------------------------------

data CUDAException
  = ExitCode Status
  | UserError String
  deriving Typeable

instance Exception CUDAException

instance Show CUDAException where
  showsPrec _ (ExitCode  s) = showString ("CUDA Exception: " ++ describe s)
  showsPrec _ (UserError s) = showString ("CUDA Exception: " ++ s)


-- |
-- Raise a CUDAException. Exceptions can be thrown from pure code, but can only
-- be caught in the 'IO' monad.
--
{-# RULES "cudaError/IO" cudaError = cudaErrorIO #-}
{-# NOINLINE [1] cudaError #-}
cudaError :: String -> a
cudaError s = throw (UserError s)

-- |
-- Raise a CUDAException in the IO Monad
--
cudaErrorIO :: String -> IO a
cudaErrorIO s = throwIO (UserError s)

-- |
-- A specially formatted error message
--
requireSDK :: Name -> Double -> a
requireSDK n v = cudaError $ printf "'%s' requires at least cuda-%3.1f\n" (show n) v


--------------------------------------------------------------------------------
-- Helper Functions
--------------------------------------------------------------------------------


-- |
-- Return the results of a function on successful execution, otherwise throw an
-- exception with an error string associated with the return code
--
{-# INLINE resultIfOk #-}
resultIfOk :: (Status, a) -> IO a
resultIfOk (status, !result) =
    case status of
        Success -> return  result
        _       -> throwIO (ExitCode status)


-- |
-- Throw an exception with an error string associated with an unsuccessful
-- return code, otherwise return unit.
--
{-# INLINE nothingIfOk #-}
nothingIfOk :: Status -> IO ()
nothingIfOk status =
    case status of
        Success -> return  ()
        _       -> throwIO (ExitCode status)

{-# INLINE checkStatus #-}
checkStatus :: CInt -> IO ()
checkStatus = nothingIfOk . cToEnum