diff --git a/hal/src/main/native/athena/HAL.cpp b/hal/src/main/native/athena/HAL.cpp index fcee49897f..eefe34c9a2 100644 --- a/hal/src/main/native/athena/HAL.cpp +++ b/hal/src/main/native/athena/HAL.cpp @@ -233,6 +233,8 @@ const char* HAL_GetErrorMessage(int32_t code) { return HAL_CAN_BUFFER_OVERRUN_MESSAGE; case HAL_LED_CHANNEL_ERROR: return HAL_LED_CHANNEL_ERROR_MESSAGE; + case HAL_USE_LAST_ERROR: + return HAL_USE_LAST_ERROR_MESSAGE; default: return "Unknown error status"; } diff --git a/hal/src/main/native/athena/HALInternal.h b/hal/src/main/native/athena/HALInternal.h index b9f284ff6a..f104bfe7d5 100644 --- a/hal/src/main/native/athena/HALInternal.h +++ b/hal/src/main/native/athena/HALInternal.h @@ -6,7 +6,9 @@ #include +#include + namespace hal { void ReleaseFPGAInterrupt(int32_t interruptNumber); - +void SetLastError(int32_t status, const wpi::Twine& value); } // namespace hal diff --git a/hal/src/main/native/cpp/ErrorHandling.cpp b/hal/src/main/native/cpp/ErrorHandling.cpp new file mode 100644 index 0000000000..9f10853b52 --- /dev/null +++ b/hal/src/main/native/cpp/ErrorHandling.cpp @@ -0,0 +1,41 @@ +// Copyright (c) FIRST and other WPILib contributors. +// Open Source Software; you can modify and/or share it under the terms of +// the WPILib BSD license file in the root directory of this project. + +#include +#include + +#include "hal/Errors.h" +#include "hal/HALBase.h" + +namespace { +struct LastErrorStorage { + int32_t status; + wpi::SmallString<512> message; +}; +} // namespace + +static LastErrorStorage& GetThreadLastError() { + thread_local LastErrorStorage lastError; + return lastError; +} + +namespace hal { +void SetLastError(int32_t status, const wpi::Twine& value) { + LastErrorStorage& lastError = GetThreadLastError(); + lastError.message.clear(); + value.toVector(lastError.message); + lastError.status = status; +} +} // namespace hal + +extern "C" { +const char* HAL_GetLastError(int32_t* status) { + if (*status == HAL_USE_LAST_ERROR) { + LastErrorStorage& lastError = GetThreadLastError(); + *status = lastError.status; + return lastError.message.c_str(); + } + return HAL_GetErrorMessage(*status); +} +} // extern "C" diff --git a/hal/src/main/native/cpp/jni/HALUtil.cpp b/hal/src/main/native/cpp/jni/HALUtil.cpp index fc37e9dd56..cbeb9c97ee 100644 --- a/hal/src/main/native/cpp/jni/HALUtil.cpp +++ b/hal/src/main/native/cpp/jni/HALUtil.cpp @@ -85,7 +85,7 @@ void ThrowUncleanStatusException(JNIEnv* env, wpi::StringRef msg, void ThrowAllocationException(JNIEnv* env, int32_t minRange, int32_t maxRange, int32_t requestedValue, int32_t status) { - const char* message = HAL_GetErrorMessage(status); + const char* message = HAL_GetLastError(&status); wpi::SmallString<1024> buf; wpi::raw_svector_ostream oss(buf); oss << " Code: " << status << ". " << message @@ -96,7 +96,7 @@ void ThrowAllocationException(JNIEnv* env, int32_t minRange, int32_t maxRange, } void ThrowHalHandleException(JNIEnv* env, int32_t status) { - const char* message = HAL_GetErrorMessage(status); + const char* message = HAL_GetLastError(&status); wpi::SmallString<1024> buf; wpi::raw_svector_ostream oss(buf); oss << " Code: " << status << ". " << message; @@ -110,7 +110,7 @@ void ReportError(JNIEnv* env, int32_t status, bool doThrow) { if (status == HAL_HANDLE_ERROR) { ThrowHalHandleException(env, status); } - const char* message = HAL_GetErrorMessage(status); + const char* message = HAL_GetLastError(&status); if (doThrow && status < 0) { wpi::SmallString<1024> buf; wpi::raw_svector_ostream oss(buf); @@ -119,7 +119,11 @@ void ReportError(JNIEnv* env, int32_t status, bool doThrow) { } else { std::string func; auto stack = GetJavaStackTrace(env, &func, "edu.wpi.first"); - HAL_SendError(1, status, 0, message, func.c_str(), stack.c_str(), 1); + // Make a copy of message for safety, calling back into the HAL might + // invalidate the string. + wpi::SmallString<256> lastMessage{wpi::StringRef{message}}; + HAL_SendError(1, status, 0, lastMessage.c_str(), func.c_str(), + stack.c_str(), 1); } } diff --git a/hal/src/main/native/include/hal/Errors.h b/hal/src/main/native/include/hal/Errors.h index b2e4b081b0..1e87f54cb2 100644 --- a/hal/src/main/native/include/hal/Errors.h +++ b/hal/src/main/native/include/hal/Errors.h @@ -125,6 +125,10 @@ #define HAL_SIM_NOT_SUPPORTED -1155 #define HAL_SIM_NOT_SUPPORTED_MESSAGE "HAL: Method not supported in sim" +#define HAL_USE_LAST_ERROR -1156 +#define HAL_USE_LAST_ERROR_MESSAGE \ + "HAL: Use HAL_GetLastError(status) to get last error" + #define HAL_CAN_BUFFER_OVERRUN -35007 #define HAL_CAN_BUFFER_OVERRUN_MESSAGE \ "HAL: CAN Output Buffer Full. Ensure a device is attached" diff --git a/hal/src/main/native/include/hal/HALBase.h b/hal/src/main/native/include/hal/HALBase.h index dbbf15d803..ce2a420711 100644 --- a/hal/src/main/native/include/hal/HALBase.h +++ b/hal/src/main/native/include/hal/HALBase.h @@ -22,6 +22,19 @@ HAL_ENUM(HAL_RuntimeType) { HAL_Athena, HAL_Mock }; extern "C" { #endif +/** + * Gets the last error set on this thread, or the message for the status code. + * + * If passed HAL_USE_LAST_ERROR, the last error set on the thread will be + * returned. + * + * @param code the status code, set to the error status code if input is + * HAL_USE_LAST_ERROR + * @return the error message for the code. This does not need to be freed, + * but can be overwritten by another hal call on the same thread. + */ +const char* HAL_GetLastError(int32_t* status); + /** * Gets the error message for a specific status code. * diff --git a/hal/src/main/native/sim/HAL.cpp b/hal/src/main/native/sim/HAL.cpp index 97ad1b9bba..09409bc1fb 100644 --- a/hal/src/main/native/sim/HAL.cpp +++ b/hal/src/main/native/sim/HAL.cpp @@ -250,6 +250,8 @@ const char* HAL_GetErrorMessage(int32_t code) { return HAL_CAN_BUFFER_OVERRUN_MESSAGE; case HAL_LED_CHANNEL_ERROR: return HAL_LED_CHANNEL_ERROR_MESSAGE; + case HAL_USE_LAST_ERROR: + return HAL_USE_LAST_ERROR_MESSAGE; default: return "Unknown error status"; } diff --git a/wpilibc/src/main/native/cpp/Errors.cpp b/wpilibc/src/main/native/cpp/Errors.cpp index 56c55922a4..b432968845 100644 --- a/wpilibc/src/main/native/cpp/Errors.cpp +++ b/wpilibc/src/main/native/cpp/Errors.cpp @@ -36,10 +36,10 @@ void RuntimeError::Report() const { m_data->stack.c_str(), 1); } -const char* frc::GetErrorMessage(int32_t code) { +const char* frc::GetErrorMessage(int32_t* code) { using namespace err; using namespace warn; - switch (code) { + switch (*code) { #define S(label, offset, message) \ case label: \ return message; @@ -47,7 +47,7 @@ const char* frc::GetErrorMessage(int32_t code) { #include "frc/WPIWarnings.mac" #undef S default: - return HAL_GetErrorMessage(code); + return HAL_GetLastError(code); } } @@ -57,7 +57,7 @@ void frc::ReportError(int32_t status, const wpi::Twine& message, if (status == 0) { return; } - const char* statusMessage = GetErrorMessage(status); + const char* statusMessage = GetErrorMessage(&status); auto stack = wpi::GetStackTrace(2); wpi::SmallString<128> buf; HAL_SendError(status < 0, status, 0, @@ -70,7 +70,7 @@ void frc::ReportError(int32_t status, const wpi::Twine& message, RuntimeError frc::MakeError(int32_t status, const wpi::Twine& message, const char* fileName, int lineNumber, const char* funcName) { - const char* statusMessage = GetErrorMessage(status); + const char* statusMessage = GetErrorMessage(&status); auto stack = wpi::GetStackTrace(2); return RuntimeError{status, statusMessage + wpi::Twine{": "} + message, fileName, lineNumber, diff --git a/wpilibc/src/main/native/include/frc/Errors.h b/wpilibc/src/main/native/include/frc/Errors.h index 9b16072298..64b965f3c0 100644 --- a/wpilibc/src/main/native/include/frc/Errors.h +++ b/wpilibc/src/main/native/include/frc/Errors.h @@ -45,7 +45,7 @@ class RuntimeError : public std::runtime_error { /** * Gets error message string for an error code. */ -const char* GetErrorMessage(int32_t code); +const char* GetErrorMessage(int32_t* code); /** * Reports an error to the driver station (using HAL_SendError). diff --git a/wpilibcExamples/src/main/cpp/examples/HAL/c/Robot.c b/wpilibcExamples/src/main/cpp/examples/HAL/c/Robot.c index 29de08fb7b..b7c0826bad 100644 --- a/wpilibcExamples/src/main/cpp/examples/HAL/c/Robot.c +++ b/wpilibcExamples/src/main/cpp/examples/HAL/c/Robot.c @@ -65,7 +65,7 @@ int main(void) { HAL_DigitalHandle pwmPort = HAL_InitializePWMPort(HAL_GetPort(2), &status); if (status != 0) { - const char* message = HAL_GetErrorMessage(status); + const char* message = HAL_GetLastError(&status); printf("%s\n", message); return 1; } @@ -78,7 +78,7 @@ int main(void) { HAL_DigitalHandle dio = HAL_InitializeDIOPort(HAL_GetPort(2), 1, &status); if (status != 0) { - const char* message = HAL_GetErrorMessage(status); + const char* message = HAL_GetLastError(&status); printf("%s\n", message); status = 0; HAL_FreePWMPort(pwmPort, &status);