blob: 741691a63b4b19d9033e3acbfe961e41f950abbf [file] [log] [blame]
Brian Silvermanf7f267a2017-02-04 16:16:08 -08001/*----------------------------------------------------------------------------*/
2/* Copyright (c) FIRST 2008-2017. All Rights Reserved. */
3/* Open Source Software - may be modified and shared by FRC teams. The code */
4/* must be accompanied by the FIRST BSD license file in the root directory of */
5/* the project. */
6/*----------------------------------------------------------------------------*/
7
8#include "Utility.h"
9
10#include <cxxabi.h>
11#include <execinfo.h>
12
13#include <cstdio>
14#include <cstdlib>
15#include <cstring>
16#include <sstream>
17
18#include "ErrorBase.h"
19#include "HAL/DriverStation.h"
20#include "HAL/HAL.h"
21#include "llvm/SmallString.h"
22
23using namespace frc;
24
25/**
26 * Assert implementation.
27 * This allows breakpoints to be set on an assert.
28 * The users don't call this, but instead use the wpi_assert macros in
29 * Utility.h.
30 */
31bool wpi_assert_impl(bool conditionValue, llvm::StringRef conditionText,
32 llvm::StringRef message, llvm::StringRef fileName,
33 int lineNumber, llvm::StringRef funcName) {
34 if (!conditionValue) {
35 std::stringstream locStream;
36 locStream << funcName << " [";
37 llvm::SmallString<128> fileTemp;
38 locStream << basename(fileName.c_str(fileTemp)) << ":" << lineNumber << "]";
39
40 std::stringstream errorStream;
41
42 errorStream << "Assertion \"" << conditionText << "\" ";
43
44 if (message[0] != '\0') {
45 errorStream << "failed: " << message << std::endl;
46 } else {
47 errorStream << "failed." << std::endl;
48 }
49
50 std::string stack = GetStackTrace(2);
51 std::string location = locStream.str();
52 std::string error = errorStream.str();
53
54 // Print the error and send it to the DriverStation
55 HAL_SendError(1, 1, 0, error.c_str(), location.c_str(), stack.c_str(), 1);
56 }
57
58 return conditionValue;
59}
60
61/**
62 * Common error routines for wpi_assertEqual_impl and wpi_assertNotEqual_impl
63 * This should not be called directly; it should only be used by
64 * wpi_assertEqual_impl and wpi_assertNotEqual_impl.
65 */
66void wpi_assertEqual_common_impl(llvm::StringRef valueA, llvm::StringRef valueB,
67 llvm::StringRef equalityType,
68 llvm::StringRef message,
69 llvm::StringRef fileName, int lineNumber,
70 llvm::StringRef funcName) {
71 std::stringstream locStream;
72 locStream << funcName << " [";
73 llvm::SmallString<128> fileTemp;
74 locStream << basename(fileName.c_str(fileTemp)) << ":" << lineNumber << "]";
75
76 std::stringstream errorStream;
77
78 errorStream << "Assertion \"" << valueA << " " << equalityType << " "
79 << valueB << "\" ";
80
81 if (message[0] != '\0') {
82 errorStream << "failed: " << message << std::endl;
83 } else {
84 errorStream << "failed." << std::endl;
85 }
86
87 std::string trace = GetStackTrace(3);
88 std::string location = locStream.str();
89 std::string error = errorStream.str();
90
91 // Print the error and send it to the DriverStation
92 HAL_SendError(1, 1, 0, error.c_str(), location.c_str(), trace.c_str(), 1);
93}
94
95/**
96 * Assert equal implementation.
97 * This determines whether the two given integers are equal. If not,
98 * the value of each is printed along with an optional message string.
99 * The users don't call this, but instead use the wpi_assertEqual macros in
100 * Utility.h.
101 */
102bool wpi_assertEqual_impl(int valueA, int valueB, llvm::StringRef valueAString,
103 llvm::StringRef valueBString, llvm::StringRef message,
104 llvm::StringRef fileName, int lineNumber,
105 llvm::StringRef funcName) {
106 if (!(valueA == valueB)) {
107 wpi_assertEqual_common_impl(valueAString, valueBString, "==", message,
108 fileName, lineNumber, funcName);
109 }
110 return valueA == valueB;
111}
112
113/**
114 * Assert not equal implementation.
115 * This determines whether the two given integers are equal. If so,
116 * the value of each is printed along with an optional message string.
117 * The users don't call this, but instead use the wpi_assertNotEqual macros in
118 * Utility.h.
119 */
120bool wpi_assertNotEqual_impl(int valueA, int valueB,
121 llvm::StringRef valueAString,
122 llvm::StringRef valueBString,
123 llvm::StringRef message, llvm::StringRef fileName,
124 int lineNumber, llvm::StringRef funcName) {
125 if (!(valueA != valueB)) {
126 wpi_assertEqual_common_impl(valueAString, valueBString, "!=", message,
127 fileName, lineNumber, funcName);
128 }
129 return valueA != valueB;
130}
131
132namespace frc {
133
134/**
135 * Return the FPGA Version number.
136 *
137 * For now, expect this to be competition year.
138 * @return FPGA Version number.
139 */
140int GetFPGAVersion() {
141 int32_t status = 0;
142 int version = HAL_GetFPGAVersion(&status);
143 wpi_setGlobalErrorWithContext(status, HAL_GetErrorMessage(status));
144 return version;
145}
146
147/**
148 * Return the FPGA Revision number.
149 * The format of the revision is 3 numbers.
150 * The 12 most significant bits are the Major Revision.
151 * the next 8 bits are the Minor Revision.
152 * The 12 least significant bits are the Build Number.
153 * @return FPGA Revision number.
154 */
155int64_t GetFPGARevision() {
156 int32_t status = 0;
157 int64_t revision = HAL_GetFPGARevision(&status);
158 wpi_setGlobalErrorWithContext(status, HAL_GetErrorMessage(status));
159 return revision;
160}
161
162/**
163 * Read the microsecond-resolution timer on the FPGA.
164 *
165 * @return The current time in microseconds according to the FPGA (since FPGA
166 * reset).
167 */
168uint64_t GetFPGATime() {
169 int32_t status = 0;
170 uint64_t time = HAL_GetFPGATime(&status);
171 wpi_setGlobalErrorWithContext(status, HAL_GetErrorMessage(status));
172 return time;
173}
174
175/**
176 * Get the state of the "USER" button on the roboRIO.
177 *
178 * @return True if the button is currently pressed down
179 */
180bool GetUserButton() {
181 int32_t status = 0;
182
183 bool value = HAL_GetFPGAButton(&status);
184 wpi_setGlobalError(status);
185
186 return value;
187}
188
189/**
190 * Demangle a C++ symbol, used for printing stack traces.
191 */
192static std::string demangle(char const* mangledSymbol) {
193 char buffer[256];
194 size_t length;
195 int32_t status;
196
197 if (std::sscanf(mangledSymbol, "%*[^(]%*[(]%255[^)+]", buffer)) {
198 char* symbol = abi::__cxa_demangle(buffer, nullptr, &length, &status);
199 if (status == 0) {
200 return symbol;
201 } else {
202 // If the symbol couldn't be demangled, it's probably a C function,
203 // so just return it as-is.
204 return buffer;
205 }
206 }
207
208 // If everything else failed, just return the mangled symbol
209 return mangledSymbol;
210}
211
212/**
213 * Get a stack trace, ignoring the first "offset" symbols.
214 * @param offset The number of symbols at the top of the stack to ignore
215 */
216std::string GetStackTrace(int offset) {
217 void* stackTrace[128];
218 int stackSize = backtrace(stackTrace, 128);
219 char** mangledSymbols = backtrace_symbols(stackTrace, stackSize);
220 std::stringstream trace;
221
222 for (int i = offset; i < stackSize; i++) {
223 // Only print recursive functions once in a row.
224 if (i == 0 || stackTrace[i] != stackTrace[i - 1]) {
225 trace << "\tat " << demangle(mangledSymbols[i]) << std::endl;
226 }
227 }
228
229 std::free(mangledSymbols);
230
231 return trace.str();
232}
233
234} // namespace frc