コード例 #1
0
ファイル: frame.C プロジェクト: cuviper/dyninst
void Frame::setNameValue() const {
  if (name_val_set == nv_set || name_val_set == nv_err)
    return;
  
  if (!walker) {
    setLastError(err_nosymlookup, "No Walker object was associated with this frame");
    sw_printf("[%s:%u] - Error, No walker found.\n", FILE__, __LINE__);
    name_val_set = nv_err;
    return;
  }
  
  SymbolLookup *lookup = walker->getSymbolLookup();
  if (!lookup) {
    setLastError(err_nosymlookup, "No SymbolLookup object was associated with the Walker");
    sw_printf("[%s:%u] - Error, No symbol lookup found.\n", FILE__, __LINE__);
    name_val_set = nv_err;
    return;
  }
  
  bool result = lookup->lookupAtAddr(getRA(), sym_name, sym_value);
  if (!result) {
    sw_printf("[%s:%u] - Error, returned by lookupAtAddr().\n", FILE__, __LINE__);
    name_val_set = nv_err;
  }
  
  sw_printf("[%s:%u] - Successfully looked up symbol for frame %p\n",
	    FILE__, __LINE__, this);
  
  name_val_set = nv_set;
}
コード例 #2
0
LastErrorTLS::LastErrorTLS()
{
    Symbols.Initialize();

    // Must be at end of function
    PushDestroyCallbacks();
}
コード例 #3
0
void OVRError::SetCurrentValues()
{
    OVRTime = Timer::GetSeconds(); // It would be better if we called ovr_GetTimeInSeconds, but that doesn't have a constant header to use.

    ClockTime = std::chrono::system_clock::now();

    #if defined(OVR_ERROR_ENABLE_BACKTRACES)
        if (Symbols.IsInitialized())
        {
            void* addressArray[32];
            size_t n = Symbols.GetBacktrace(addressArray, OVR_ARRAY_COUNT(addressArray), 2,
                                            nullptr, OVR_THREADSYSID_INVALID);
            Backtrace.Clear();
            Backtrace.Append(addressArray, n);
        }
    #endif
}
コード例 #4
0
ファイル: frame.C プロジェクト: dyninst/dyninst
void Frame::setNameValue() const {
  if (name_val_set == nv_set || name_val_set == nv_err)
    return;
  
  if (!walker) {
    setLastError(err_nosymlookup, "No Walker object was associated with this frame");
    sw_printf("[%s:%u] - Error, No walker found.\n", FILE__, __LINE__);
    name_val_set = nv_err;
    return;
  }
  
  SymbolLookup *lookup = walker->getSymbolLookup();
  if (!lookup) {
    setLastError(err_nosymlookup, "No SymbolLookup object was associated with the Walker");
    sw_printf("[%s:%u] - Error, No symbol lookup found.\n", FILE__, __LINE__);
    name_val_set = nv_err;
    return;
  }
  // Here we lookup return address minus 1 to handle the following special case:
  //
  // Suppose A calls B and B is a non-returnning function, and the call to B in A
  // is the last instruction of A. Then the compiler may generate code where 
  // another function C is immediately after A. In such case, the return address
  // will be the entry address of C. And if we look up function by the return
  // address, we will get C rather than A. 
  bool result = lookup->lookupAtAddr(getRA() - 1, sym_name, sym_value);
  if (!result) {
    sw_printf("[%s:%u] - Error, returned by lookupAtAddr().\n", FILE__, __LINE__);
    name_val_set = nv_err;
  }
  
  sw_printf("[%s:%u] - Successfully looked up symbol for frame %p\n",
	    FILE__, __LINE__, this);
  
  name_val_set = nv_set;
}
コード例 #5
0
int WatchDogObserver::Run()
{
    OVR_DEBUG_LOG(("[WatchDogObserver] Starting"));

    SetThreadName("WatchDog");

    while (!TerminationEvent.Wait(WakeupInterval))
    {
        Lock::Locker locker(&ListLock);

        const uint32_t t1 = GetFastMsTime();

        const int count = DogList.GetSizeI();
        for (int i = 0; i < count; ++i)
        {
            WatchDog* dog = DogList[i];

            const int threshold = dog->ThreshholdMilliseconds;
            const uint32_t t0 = dog->WhenLastFedMilliseconds;

            // If threshold exceeded, assume there is thread deadlock of some sort.
            int delta = (int)(t1 - t0);
            if (delta > threshold)
            {
                // Expected behavior:
                // SingleProcessDebug, SingleProcessRelease, Debug: This is only ever done for internal testing, so we don't want it to trigger the deadlock termination.
                // Release: This is our release configuration where we want it to terminate itself.

                // Print a stack trace of all threads if there's no debugger present.
                const bool debuggerPresent = OVRIsDebuggerPresent();

                LogError("{ERR-027} [WatchDogObserver] Deadlock detected: %s", dog->ThreadName.ToCStr());
                if (!debuggerPresent) // We don't print threads if a debugger is present because otherwise every time the developer paused the app to debug, it would spit out a long thread trace upon resuming.
                {
                    if (SymbolLookup::Initialize())
                    {
                        // symbolLookup is static here to avoid putting 32 KB on the stack
                        // and potentially overflowing the stack.  This function is only ever
                        // run by one thread so it should be safe.
                        static SymbolLookup symbolLookup;
                        String threadListOutput, moduleListOutput;
                        symbolLookup.ReportThreadCallstacks(threadListOutput);
                        symbolLookup.ReportModuleInformation(moduleListOutput);
                        LogError("---DEADLOCK STATE---\n\n%s\n\n%s\n---END OF DEADLOCK STATE---", threadListOutput.ToCStr(), moduleListOutput.ToCStr());
                    }

                    if (IsReporting)
                    {
                        ExceptionHandler::ReportDeadlock(DogList[i]->ThreadName, OrganizationName , ApplicationName);

                        // Disable reporting after the first deadlock report.
                        IsReporting = false;
                    }
                }

                if (IsExitingOnDeadlock())
                {
                    OVR_ASSERT_M(false, "Watchdog detected a deadlock. Exiting the process."); // This won't have an effect unless asserts are enabled in release builds.
                    OVR::ExitProcess(-1);
                }
            }
        }
    }

    OVR_DEBUG_LOG(("[WatchDogObserver] Good night"));

    return 0;
}
コード例 #6
0
LastErrorTLS::~LastErrorTLS()
{
    Symbols.Shutdown();
}
コード例 #7
0
namespace OVR {


//-----------------------------------------------------------------------------
// LastErrorTLS

static SymbolLookup Symbols;

LastErrorTLS::LastErrorTLS()
{
    Symbols.Initialize();

    // Must be at end of function
    PushDestroyCallbacks();
}

LastErrorTLS::~LastErrorTLS()
{
    Symbols.Shutdown();
}

void LastErrorTLS::OnSystemDestroy()
{
    delete this;
}

// This is an accessor which auto-allocates and initializes the return value if needed.
OVRError& LastErrorTLS::LastError()
{
    Lock::Locker autoLock(&TheLock);

    ThreadId threadId = GetCurrentThreadId();
    auto i = TLSDictionary.Find(threadId);

    if (i == TLSDictionary.End())
    {
        TLSDictionary.Add(threadId, OVRError::Success());
        i = TLSDictionary.Find(threadId);
    }

    return (*i).Second;
}




// ****** OVRFormatDateTime
//
// Prints a date/time like so:
//     Y-M-d H:M:S [ms:us:ns]
// Example output:
//     2016-12-25 8:15:01 [392:445:23]
//
// SysClockTime is of type std::chrono::time_point<std::chrono::system_clock>.
//
// To consider: Move SysClockTime and OVRFormatDateTime to OVRKernel.
//
static void OVRFormatDateTime(SysClockTime sysClockTime, OVR::String& dateTimeString)
{
    // Get the basic Date and HMS time.
    char buffer[128];
    struct tm tmResult;
    const time_t cTime = std::chrono::system_clock::to_time_t(sysClockTime);

    #if defined(_MSC_VER)
        localtime_s(&tmResult, &cTime);
    #else
        localtime_r(&cTime, &tmResult);
    #endif

    strftime(buffer, sizeof(buffer), "%Y-%m-%d %H:%M:%S", &tmResult);

    // Append milli:micro:nano time.
    std::chrono::system_clock::duration timeSinceEpoch = sysClockTime.time_since_epoch();

    std::chrono::seconds       s = std::chrono::duration_cast<std::chrono::seconds>(timeSinceEpoch);
    timeSinceEpoch -= s;

    std::chrono::milliseconds ms = std::chrono::duration_cast<std::chrono::milliseconds>(timeSinceEpoch);
    timeSinceEpoch -= ms;

    std::chrono::microseconds us = std::chrono::duration_cast<std::chrono::microseconds>(timeSinceEpoch);
    timeSinceEpoch -= us;

    std::chrono::nanoseconds  ns = std::chrono::duration_cast<std::chrono::nanoseconds>(timeSinceEpoch);

    char buffer2[384];
    sprintf(buffer2, "%s [%d:%d:%d]", buffer, (int)ms.count(), (int)us.count(), (int)ns.count());

    dateTimeString = buffer2;
}


static void OVRRemoveTrailingNewlines(String& s)
{
    while(!s.IsEmpty() && ((s.Back() == '\n') || (s.Back() == '\r')))
        s.PopBack();
}


OVR_SELECTANY const int64_t OVRError::kLogLineUnset;


OVRError::OVRError()
{
    Reset();
}


OVRError::OVRError(ovrResult code)
    : OVRError()
{
    Code = code;
}


OVRError::OVRError(ovrResult code, const char* pFormat, ...)
    : OVRError(code)
{
    va_list argList;
    va_start(argList, pFormat);
    StringBuffer strbuff;
    strbuff.AppendFormatV(pFormat, argList);
    SetDescription(strbuff.ToCStr());
    va_end(argList);
}

OVRError::OVRError(const OVRError& ovrError)
{
    operator=(ovrError);
}


OVRError::OVRError(OVRError&& ovrError)
{
    operator=(std::move(ovrError));
}


OVRError::~OVRError()
{
    // Empty
}


OVRError& OVRError::operator=(const OVRError& ovrError)
{
    Code             = ovrError.Code;
    SysCode          = ovrError.SysCode;
    Description      = ovrError.Description;
    Context          = ovrError.Context;
    OVRTime          = ovrError.OVRTime;
    ClockTime        = ovrError.ClockTime;
    LogLine          = ovrError.LogLine;
    SourceFilePath   = ovrError.SourceFilePath;
    SourceFileLine   = ovrError.SourceFileLine;
    Backtrace        = ovrError.Backtrace;
    AlreadyLogged    = ovrError.AlreadyLogged;

    return *this;
}


OVRError& OVRError::operator=(OVRError&& ovrError)
{
    Code             = ovrError.Code;
    SysCode          = ovrError.SysCode;
    Description      = std::move(ovrError.Description);
    Context          = std::move(ovrError.Context);
    OVRTime          = ovrError.OVRTime;
    ClockTime        = ovrError.ClockTime;
    LogLine          = ovrError.LogLine;
    SourceFilePath   = std::move(ovrError.SourceFilePath);
    SourceFileLine   = ovrError.SourceFileLine;
    Backtrace        = std::move(ovrError.Backtrace);
    AlreadyLogged    = ovrError.AlreadyLogged;

    return *this;
}


void OVRError::SetCurrentValues()
{
    OVRTime = Timer::GetSeconds(); // It would be better if we called ovr_GetTimeInSeconds, but that doesn't have a constant header to use.

    ClockTime = std::chrono::system_clock::now();

    #if defined(OVR_ERROR_ENABLE_BACKTRACES)
        if (Symbols.IsInitialized())
        {
            void* addressArray[32];
            size_t n = Symbols.GetBacktrace(addressArray, OVR_ARRAY_COUNT(addressArray), 2,
                                            nullptr, OVR_THREADSYSID_INVALID);
            Backtrace.Clear();
            Backtrace.Append(addressArray, n);
        }
    #endif
}


void OVRError::Reset()
{
    Code = ovrSuccess;
    SysCode = ovrSysErrorCodeSuccess;
    Description.Clear();
    Context.Clear();
    OVRTime = 0;
    ClockTime = SysClockTime();
    LogLine = kLogLineUnset;
    SourceFilePath.Clear();
    SourceFileLine = 0;
    Backtrace.ClearAndRelease();
    AlreadyLogged = false;
}


String OVRError::GetErrorString() const
{
    StringBuffer stringBuffer("OVR Error:\n");

    // Code
    OVR::String errorCodeString;
    GetErrorCodeString(Code, false, errorCodeString);
    stringBuffer.AppendFormat("  Code: %d -- %s\n", Code, errorCodeString.ToCStr());

    // SysCode
    if (SysCode != ovrSysErrorCodeSuccess)
    {
        OVR::String sysErrorString;
        GetSysErrorCodeString(SysCode, false, sysErrorString);
        OVRRemoveTrailingNewlines(sysErrorString);
        stringBuffer.AppendFormat("  System error: %d (%x) -- %s\n", (int)SysCode, (int)SysCode, sysErrorString.ToCStr());
    }

    // Description
    if (Description.GetLength())
    {
        stringBuffer.AppendFormat("  Description: %s\n", Description.ToCStr());
    }

    // OVRTime
    stringBuffer.AppendFormat("  OVRTime: %f\n", OVRTime);

    // SysClockTime
    OVR::String sysClockTimeString;
    OVRFormatDateTime(ClockTime, sysClockTimeString);
    stringBuffer.AppendFormat("  Time: %s\n", sysClockTimeString.ToCStr());

    // Context
    if (Context.GetLength())
    {
        stringBuffer.AppendFormat("  Context: %s\n", Context.ToCStr());
    }

    // If LogLine is set,
    if (LogLine != kLogLineUnset)
    {
        stringBuffer.AppendFormat("  LogLine: %lld\n", LogLine);
    }

    // FILE/LINE
    if (SourceFilePath.GetLength())
    {
        stringBuffer.AppendFormat("  File/Line: %s:%d\n", SourceFilePath.ToCStr(), SourceFileLine);
    }

    // Backtrace
    if (Backtrace.GetSize())
    {
        // We can trace symbols in a debug build here or we can trace just addresses. See other code for
        // examples of how to trace symbols.
        stringBuffer.AppendFormat("  Backtrace: ");
        for (size_t i = 0, iEnd = Backtrace.GetSize(); i != iEnd; ++i)
            stringBuffer.AppendFormat(" %p", Backtrace[i]);
        stringBuffer.AppendChar('\n');
    }

    return OVR::String(stringBuffer.ToCStr(), stringBuffer.GetSize());
}


void OVRError::SetCode(ovrResult code)
{
    Code = code;
}


ovrResult OVRError::GetCode() const
{
    return Code;
}


void OVRError::SetSysCode(ovrSysErrorCode sysCode)
{
    SysCode = sysCode;
}


ovrSysErrorCode OVRError::GetSysCode() const
{
    return SysCode;
}


void OVRError::SetDescription(const char* pDescription)
{
    if (pDescription)
    {
        Description = pDescription;
        OVRRemoveTrailingNewlines(Description); // Users sometimes send text with trailing newlines, which have no purpose in the error report.
    }
    else
        Description.Clear();
}


String OVRError::GetDescription() const
{
    return Description;
}


void OVRError::SetContext(const char* pContext)
{
    if (pContext)
    {
        Context = pContext;
        OVRRemoveTrailingNewlines(Context);
    }
    else
        Context.Clear();
}


String OVRError::GetContext() const
{
    return Context;
}


void OVRError::SetOVRTime(double ovrTime)
{
    OVRTime = ovrTime;
}


double OVRError::GetOVRTime() const
{
    return OVRTime;
}


void OVRError::SetSysClockTime(const SysClockTime& clockTime)
{
    ClockTime = clockTime;
}


SysClockTime OVRError::GetSysClockTime() const
{
    return ClockTime;
}


void OVRError::SetLogLine(int64_t logLine)
{
    LogLine = logLine;
}


int64_t OVRError::GetLogLine() const
{
    return LogLine;
}


void OVRError::SetSource(const char* pSourceFilePath, int sourceFileLine)
{
    if (pSourceFilePath)
        SourceFilePath = pSourceFilePath;
    else
        SourceFilePath.Clear();
    SourceFileLine = sourceFileLine;
}


std::pair<OVR::String, int> OVRError::GetSource() const
{
    return std::make_pair(SourceFilePath, SourceFileLine);
}


OVRError::AddressArray OVRError::GetBacktrace() const
{
    return Backtrace;
}

void LogError(OVRError& ovrError)
{
    // If not already logged,
    if (!ovrError.IsAlreadyLogged())
    {
        Logger.LogError(ovrError.GetErrorString());

        ovrError.SetAlreadyLogged();
    }
}

void SetError(OVRError& ovrError)
{
    // Record that the current thread's last error is this error. If we wanted to support
    // chaining of errors such that multiple OVRErrors could be concurrent in a thread
    // (e.g. one that occurred deep in the call chain and a higher level version of it higher
    // in the call chain), we could handle that here.
    LastErrorTLS::GetInstance()->LastError() = ovrError;
}

static OVRErrorCallback ErrorCallback;

void SetErrorCallback(OVRErrorCallback callback)
{
    ErrorCallback = callback;
}

OVRError MakeError(ovrResult errorCode, ovrSysErrorCode sysCode, const char* pSourceFile,
                    int sourceLine, bool logError, bool assertError, const char* pContext, const char* pDescriptionFormat, ...)
{
    OVRError ovrError(errorCode);

    ovrError.SetCurrentValues(); // Sets the current time, etc.

    ovrError.SetSysCode(sysCode);

    va_list argList;
    va_start(argList, pDescriptionFormat);
    StringBuffer strbuff;
    strbuff.AppendFormatV(pDescriptionFormat, argList);
    va_end(argList);
    ovrError.SetDescription(strbuff.ToCStr());

    ovrError.SetContext(pContext);

    ovrError.SetSource(pSourceFile, sourceLine);

    // Set the TLS last error.
    LastErrorTLS::GetInstance()->LastError() = ovrError;

    int silencerOptions = ovrlog::ErrorSilencer::GetSilenceOptions();
    if (silencerOptions & ovrlog::ErrorSilencer::CompletelySilenceLogs)
    {
        logError = false;
    }
    if (silencerOptions & ovrlog::ErrorSilencer::PreventErrorAsserts)
    {
        assertError = false;
    }

    // If logging the error:
    if (logError)
    {
        Logger.LogError(ovrError.GetDescription().ToCStr());
    }

    // If asserting the error:
    if (assertError)
    {
        // Assert in debug mode to alert unit tester/developer of the error as it occurs.
        OVR_FAIL_M(ovrError.GetDescription().ToCStr());
    }

    if (ErrorCallback)
    {
        const bool quiet = !logError && !assertError;
        ErrorCallback(ovrError, quiet);
    }

    return ovrError;
}

typedef std::unordered_map<ovrResult, const char*> ResultNameMap;

static ResultNameMap& GetResultNameMap()
{
    static ResultNameMap resultNameMap;
    return resultNameMap;
}

const char* GetErrorCodeName(ovrResult errorCode)
{
    if (errorCode == ovrSuccess)
    {
        return "ovrSuccess"; // Speed up a common case
    }
    ResultNameMap& resultNameMap = GetResultNameMap();
    const char* str = resultNameMap[errorCode];
    if (!str)
    {
        OVR_FAIL_M("Unknown result code. It should have been registered");
        return "UnknownResultCode";
    }
    return str;
}

void OVRError::RegisterResultCodeName(ovrResult number, const char* name)
{
    ResultNameMap& resultNameMap = GetResultNameMap();
    if (resultNameMap.find(number) != resultNameMap.end())
    {
        OVR_FAIL_M("Duplicate result code registered");
        return;
    }
    resultNameMap[number] = name;
}

bool GetErrorCodeString(ovrResult resultIn, bool prefixErrorCode, OVR::String& sResult)
{
    char codeBuffer[256];

    const char* errorCodeName = GetErrorCodeName(resultIn);

    if (prefixErrorCode)
    {
        snprintf(codeBuffer, OVR_ARRAY_COUNT(codeBuffer), "0x%llx (%lld) %s", (uint64_t)resultIn, (int64_t)resultIn, errorCodeName);
    }
    else
    {
        snprintf(codeBuffer, OVR_ARRAY_COUNT(codeBuffer), "%s", errorCodeName);
    }

    sResult = codeBuffer;

    return true;
}


#if defined(OVR_OS_WIN32)
    static const wchar_t* OVR_DXGetErrorStringW(HRESULT dwDXGIErrorCode)
    {
        switch (dwDXGIErrorCode)
        {
            case DXGI_ERROR_DEVICE_HUNG:                    return L"DXGI_ERROR_DEVICE_HUNG";                  // The application's device failed due to badly formed commands sent by the application. This is an design-time issue that should be investigated and fixed.
            case DXGI_ERROR_DEVICE_REMOVED:                 return L"DXGI_ERROR_DEVICE_REMOVED";               // The video card has been physically removed from the system, or a driver upgrade for the video card has occurred. The application should destroy and recreate the device. For help debugging the problem, call ID3D10Device::GetDeviceRemovedReason.
            case DXGI_ERROR_DEVICE_RESET:                   return L"DXGI_ERROR_DEVICE_RESET";                 // The device failed due to a badly formed command. This is a run-time issue; The application should destroy and recreate the device.
            case DXGI_ERROR_DRIVER_INTERNAL_ERROR:          return L"DXGI_ERROR_DRIVER_INTERNAL_ERROR";        // The driver encountered a problem and was put into the device removed state.
            case DXGI_ERROR_FRAME_STATISTICS_DISJOINT:      return L"DXGI_ERROR_FRAME_STATISTICS_DISJOINT";    // An event (for example, a power cycle) interrupted the gathering of presentation statistics.
            case DXGI_ERROR_GRAPHICS_VIDPN_SOURCE_IN_USE:   return L"DXGI_ERROR_GRAPHICS_VIDPN_SOURCE_IN_USE"; // The application attempted to acquire exclusive ownership of an output, but failed because some other application (or device within the application) already acquired ownership.
            case DXGI_ERROR_INVALID_CALL:                   return L"DXGI_ERROR_INVALID_CALL";                 // The application provided invalid parameter data; this must be debugged and fixed before the application is released.
            case DXGI_ERROR_MORE_DATA:                      return L"DXGI_ERROR_MORE_DATA";                    // The buffer supplied by the application is not big enough to hold the requested data.
            case DXGI_ERROR_NONEXCLUSIVE:                   return L"DXGI_ERROR_NONEXCLUSIVE";                 // A global counter resource is in use, and the Direct3D device can't currently use the counter resource.
            case DXGI_ERROR_NOT_CURRENTLY_AVAILABLE:        return L"DXGI_ERROR_NOT_CURRENTLY_AVAILABLE";      // The resource or request is not currently available, but it might become available later.
            case DXGI_ERROR_NOT_FOUND:                      return L"DXGI_ERROR_NOT_FOUND";                    // When calling IDXGIObject::GetPrivateData, the GUID passed in is not recognized as one previously passed to IDXGIObject::SetPrivateData or IDXGIObject::SetPrivateDataInterface. When calling IDXGIFactory::EnumAdapters or IDXGIAdapter::EnumOutputs, the enumerated ordinal is out of range.
            case DXGI_ERROR_REMOTE_CLIENT_DISCONNECTED:     return L"DXGI_ERROR_REMOTE_CLIENT_DISCONNECTED";   // Reserved
            case DXGI_ERROR_REMOTE_OUTOFMEMORY:             return L"DXGI_ERROR_REMOTE_OUTOFMEMORY";           // Reserved
            case DXGI_ERROR_WAS_STILL_DRAWING:              return L"DXGI_ERROR_WAS_STILL_DRAWING";            // The GPU was busy at the moment when a call was made to perform an operation, and did not execute or schedule the operation.
            case DXGI_ERROR_UNSUPPORTED:                    return L"DXGI_ERROR_UNSUPPORTED";                  // The requested functionality is not supported by the device or the driver.
            case DXGI_ERROR_ACCESS_LOST:                    return L"DXGI_ERROR_ACCESS_LOST";                  // The desktop duplication interface is invalid. The desktop duplication interface typically becomes invalid when a different type of image is displayed on the desktop.
            case DXGI_ERROR_WAIT_TIMEOUT:                   return L"DXGI_ERROR_WAIT_TIMEOUT";                 // The time-out interval elapsed before the next desktop frame was available.
            case DXGI_ERROR_SESSION_DISCONNECTED:           return L"DXGI_ERROR_SESSION_DISCONNECTED";         // The Remote Desktop Services session is currently disconnected.
            case DXGI_ERROR_RESTRICT_TO_OUTPUT_STALE:       return L"DXGI_ERROR_RESTRICT_TO_OUTPUT_STALE";     // The DXGI output (monitor) to which the swap chain content was restricted is now disconnected or changed.
            case DXGI_ERROR_CANNOT_PROTECT_CONTENT:         return L"DXGI_ERROR_CANNOT_PROTECT_CONTENT";       // DXGI can't provide content protection on the swap chain. This error is typically caused by an older driver, or when you use a swap chain that is incompatible with content protection.
            case DXGI_ERROR_ACCESS_DENIED:                  return L"DXGI_ERROR_ACCESS_DENIED";                // You tried to use a resource to which you did not have the required access privileges. This error is most typically caused when you write to a shared resource with read-only access.
            case DXGI_ERROR_NAME_ALREADY_EXISTS:            return L"DXGI_ERROR_NAME_ALREADY_EXISTS";          // The supplied name of a resource in a call to IDXGIResource1::CreateSharedHandle is already associated with some other resource.
            case DXGI_ERROR_SDK_COMPONENT_MISSING:          return L"DXGI_ERROR_SDK_COMPONENT_MISSING";        // The operation depends on an SDK component that is missing or mismatched.
        }

        return nullptr;
    }
#endif



bool GetSysErrorCodeString(ovrSysErrorCode sysErrorCode, bool prefixErrorCode, OVR::String& sResult)
{
    char errorBuffer[1024];
    errorBuffer[0] = '\0';

    if (prefixErrorCode)
    {
        char prefixBuffer[64];
        snprintf(prefixBuffer, OVR_ARRAY_COUNT(prefixBuffer), "0x%llx (%lld): ", (uint64_t)sysErrorCode, (int64_t)sysErrorCode);
        sResult = prefixBuffer;
    }
    else
    {
        sResult.Clear();
    }

    #if defined(OVR_OS_WIN32)
        // Note: It may be useful to use FORMAT_MESSAGE_FROM_HMODULE here to get a module-specific error string if our source of errors
        // ends up including more than just system-native errors. For example, a third party module with custom errors defined in it.

        WCHAR errorBufferW[1024];
        DWORD errorBufferWCapacity = OVR_ARRAY_COUNT(errorBufferW);
        DWORD length = FormatMessageW(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, nullptr, (DWORD)sysErrorCode, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), errorBufferW, errorBufferWCapacity, nullptr);

        if (!length) // If failed...
        {
            if (HRESULT_FACILITY(sysErrorCode) == _FACDXGI) // If it is a DXGI error...
            {
                // This situation occurs on Windows 7. You can't use FORMAT_MESSAGE_FROM_HMODULE to solve it either. We can only use DXGetErrorString or manually handle it.
                const wchar_t* pStr = OVR_DXGetErrorStringW(sysErrorCode);

                if (pStr)
                {
                    wcscpy_s(errorBufferW, OVR_ARRAY_COUNT(errorBufferW), pStr);
                    length = (DWORD)wcslen(errorBufferW);
                }
            }
        }

        if (length) // If errorBufferW contains what we are looking for...
        {
            // Need to convert WCHAR errorBuffer to UTF8 char sResult;
            const auto requiredUTF8Length = OVR::UTF8Util::Strlcpy(errorBuffer, OVR_ARRAY_COUNT(errorBuffer), errorBufferW);
            if (requiredUTF8Length >= OVR_ARRAY_COUNT(errorBuffer)) // Zero out if too big (XXX truncate instead?)
                errorBuffer[0] = '\0';
            // Else fall through
        } // Else fall through
    #else
        #if (((_POSIX_C_SOURCE >= 200112L) || (_XOPEN_SOURCE >= 600)) && !_GNU_SOURCE) || defined(__APPLE__) || defined(__BSD__)
            const int result = strerror_r((int)sysErrorCode, errorBuffer, OVR_ARRAY_COUNT(errorBuffer));

            if (result != 0)        // If failed... [result is 0 upon success; result will be EINVAL if the code is not recognized; ERANGE if buffer didn't have enough capacity.]
                errorBuffer[0] = '\0';  // re-null-terminate, in case strerror_r left it in an invalid state.
        #else
            const char* result = strerror_r((int)sysErrorCode, errorBuffer, OVR_ARRAY_COUNT(errorBuffer));

            if (result == nullptr)  // Implementations in practice seem to always return a pointer, though the behavior isn't formally standardized.
                errorBuffer[0] = '\0';  // re-null-terminate, in case strerror_r left it in an invalid state.
        #endif
    #endif

    // Fall through functionality of simply printing the value as an integer.
    if (errorBuffer[0]) // If errorBuffer was successfully written above...
    {
        sResult += errorBuffer;
        return true;
    }

    sResult += "(unknown)"; // This is not localized. Question: Is there a way to get the error formatting functions above to print this themselves in a localized way?
    return false;
}


}  // namespace OVR