blob: 97c02def4952d54578ae1c6963a80bf8f6952f35 [file] [log] [blame]
Brian Silvermanf7f267a2017-02-04 16:16:08 -08001/*----------------------------------------------------------------------------*/
2/* Copyright (c) FIRST 2016-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#ifndef _WIN32
11#include <cxxabi.h>
12#include <execinfo.h>
13#endif
14
15#include <cstdio>
16#include <cstdlib>
17#include <iostream>
18#include <sstream>
19
20#include "Timer.h"
21#include "llvm/SmallString.h"
22#include "simulation/simTime.h"
23
24using namespace frc;
25
26static bool stackTraceEnabled = false;
27static bool suspendOnAssertEnabled = false;
28
29/**
30 * Enable Stack trace after asserts.
31 */
32void wpi_stackTraceOnAssertEnable(bool enabled) { stackTraceEnabled = enabled; }
33
34/**
35 * Enable suspend on asssert.
36 *
37 * If enabled, the user task will be suspended whenever an assert fails. This
38 * will allow the user to attach to the task with the debugger and examine
39 * variables around the failure.
40 */
41void wpi_suspendOnAssertEnabled(bool enabled) {
42 suspendOnAssertEnabled = enabled;
43}
44
45static void wpi_handleTracing() {
46 // if (stackTraceEnabled)
47 // {
48 // std::printf("\n-----------<Stack Trace>----------------\n");
49 // printCurrentStackTrace();
50 // }
51 std::printf("\n");
52}
53
54/**
55 * Assert implementation.
56 * This allows breakpoints to be set on an assert.
57 * The users don't call this, but instead use the wpi_assert macros in
58 * Utility.h.
59 */
60bool wpi_assert_impl(bool conditionValue, llvm::StringRef conditionText,
61 llvm::StringRef message, llvm::StringRef fileName,
62 int lineNumber, llvm::StringRef funcName) {
63 if (!conditionValue) {
64 std::stringstream errorStream;
65
66 errorStream << "Assertion \"" << conditionText << "\" ";
67 errorStream << "on line " << lineNumber << " ";
68
69 llvm::SmallString<128> fileTemp;
70 errorStream << "of " << basename(fileName.c_str(fileTemp)) << " ";
71
72 if (message[0] != '\0') {
73 errorStream << "failed: " << message << std::endl;
74 } else {
75 errorStream << "failed." << std::endl;
76 }
77
78 // Print to console and send to remote dashboard
79 std::cout << "\n\n>>>>" << errorStream.str();
80 wpi_handleTracing();
81 }
82
83 return conditionValue;
84}
85
86/**
87 * Common error routines for wpi_assertEqual_impl and wpi_assertNotEqual_impl
88 * This should not be called directly; it should only be used by
89 * wpi_assertEqual_impl and wpi_assertNotEqual_impl.
90 */
91void wpi_assertEqual_common_impl(int valueA, int valueB,
92 llvm::StringRef equalityType,
93 llvm::StringRef message,
94 llvm::StringRef fileName, int lineNumber,
95 llvm::StringRef funcName) {
96 // Error string buffer
97 std::stringstream error;
98
99 // If an error message was specified, include it
100 // Build error string
101 if (message.size() > 0) {
102 error << "Assertion failed: \"" << message << "\", \"" << valueA << "\" "
103 << equalityType << " \"" << valueB << "\" in " << funcName << "() in "
104 << fileName << " at line " << lineNumber << "\n";
105 } else {
106 error << "Assertion failed: \"" << valueA << "\" " << equalityType << " \""
107 << valueB << "\" in " << funcName << "() in " << fileName
108 << " at line " << lineNumber << "\n";
109 }
110
111 // Print to console and send to remote dashboard
112 std::cout << "\n\n>>>>" << error.str();
113
114 wpi_handleTracing();
115}
116
117/**
118 * Assert equal implementation.
119 * This determines whether the two given integers are equal. If not,
120 * the value of each is printed along with an optional message string.
121 * The users don't call this, but instead use the wpi_assertEqual macros in
122 * Utility.h.
123 */
124bool wpi_assertEqual_impl(int valueA, int valueB, llvm::StringRef message,
125 llvm::StringRef fileName, int lineNumber,
126 llvm::StringRef funcName) {
127 if (!(valueA == valueB)) {
128 wpi_assertEqual_common_impl(valueA, valueB, "!=", message, fileName,
129 lineNumber, funcName);
130 }
131 return valueA == valueB;
132}
133
134/**
135 * Assert not equal implementation.
136 * This determines whether the two given integers are equal. If so,
137 * the value of each is printed along with an optional message string.
138 * The users don't call this, but instead use the wpi_assertNotEqual macros in
139 * Utility.h.
140 */
141bool wpi_assertNotEqual_impl(int valueA, int valueB, llvm::StringRef message,
142 llvm::StringRef fileName, int lineNumber,
143 llvm::StringRef funcName) {
144 if (!(valueA != valueB)) {
145 wpi_assertEqual_common_impl(valueA, valueB, "==", message, fileName,
146 lineNumber, funcName);
147 }
148 return valueA != valueB;
149}
150
151namespace frc {
152
153/**
154 * Read the microsecond-resolution timer on the FPGA.
155 *
156 * @return The current time in microseconds according to the FPGA (since FPGA
157 * reset).
158 */
159uint64_t GetFPGATime() { return wpilib::internal::simTime * 1e6; }
160
161// TODO: implement symbol demangling and backtrace on windows
162#if not defined(_WIN32)
163
164/**
165 * Demangle a C++ symbol, used for printing stack traces.
166 */
167static std::string demangle(char const* mangledSymbol) {
168 char buffer[256];
169 size_t length;
170 int32_t status;
171
172 if (std::sscanf(mangledSymbol, "%*[^(]%*[^_]%255[^)+]", buffer)) {
173 char* symbol = abi::__cxa_demangle(buffer, nullptr, &length, &status);
174
175 if (status == 0) {
176 return symbol;
177 } else {
178 // If the symbol couldn't be demangled, it's probably a C function,
179 // so just return it as-is.
180 return buffer;
181 }
182 }
183
184 // If everything else failed, just return the mangled symbol
185 return mangledSymbol;
186}
187
188/**
189 * Get a stack trace, ignoring the first "offset" symbols.
190 */
191std::string GetStackTrace(int offset) {
192 void* stackTrace[128];
193 int stackSize = backtrace(stackTrace, 128);
194 char** mangledSymbols = backtrace_symbols(stackTrace, stackSize);
195 std::stringstream trace;
196
197 for (int i = offset; i < stackSize; i++) {
198 // Only print recursive functions once in a row.
199 if (i == 0 || stackTrace[i] != stackTrace[i - 1]) {
200 trace << "\tat " << demangle(mangledSymbols[i]) << std::endl;
201 }
202 }
203
204 std::free(mangledSymbols);
205
206 return trace.str();
207}
208
209#else
210static std::string demangle(char const* mangledSymbol) {
211 return "no demangling on windows";
212}
213std::string GetStackTrace(int offset) { return "no stack trace on windows"; }
214#endif
215
216} // namespace frc