Fork of the espurna firmware for `mhsw` switches
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

360 lines
9.3 KiB

7 years ago
6 years ago
6 years ago
7 years ago
7 years ago
6 years ago
7 years ago
  1. /*
  2. DEBUG MODULE
  3. Copyright (C) 2016-2019 by Xose Pérez <xose dot perez at gmail dot com>
  4. */
  5. #if DEBUG_SUPPORT
  6. #include <limits>
  7. #include <type_traits>
  8. #include <vector>
  9. #include "debug.h"
  10. #include "telnet.h"
  11. #include "ws.h"
  12. #if DEBUG_UDP_SUPPORT
  13. #include <WiFiUdp.h>
  14. WiFiUDP _udp_debug;
  15. #if DEBUG_UDP_PORT == 514
  16. char _udp_syslog_header[40] = {0};
  17. #endif
  18. #endif
  19. bool _debug_enabled = false;
  20. // -----------------------------------------------------------------------------
  21. // printf-like debug methods
  22. // -----------------------------------------------------------------------------
  23. constexpr const int DEBUG_SEND_STRING_BUFFER_SIZE = 128;
  24. void _debugSendInternal(const char * message, bool add_timestamp = DEBUG_ADD_TIMESTAMP);
  25. // TODO: switch to newlib vsnprintf for latest Cores to support PROGMEM args
  26. void _debugSend(const char * format, va_list args) {
  27. char temp[DEBUG_SEND_STRING_BUFFER_SIZE];
  28. int len = ets_vsnprintf(temp, sizeof(temp), format, args);
  29. // strlen(...) + '\0' already in temp buffer, avoid using malloc when possible
  30. if (len < DEBUG_SEND_STRING_BUFFER_SIZE) {
  31. _debugSendInternal(temp);
  32. return;
  33. }
  34. len += 1;
  35. auto* buffer = static_cast<char*>(malloc(len));
  36. if (!buffer) {
  37. return;
  38. }
  39. ets_vsnprintf(buffer, len, format, args);
  40. _debugSendInternal(buffer);
  41. free(buffer);
  42. }
  43. void debugSend(const char* format, ...) {
  44. if (!_debug_enabled) return;
  45. va_list args;
  46. va_start(args, format);
  47. _debugSend(format, args);
  48. va_end(args);
  49. }
  50. void debugSend_P(const char* format_P, ...) {
  51. if (!_debug_enabled) return;
  52. char format[strlen_P(format_P) + 1];
  53. memcpy_P(format, format_P, sizeof(format));
  54. va_list args;
  55. va_start(args, format_P);
  56. _debugSend(format, args);
  57. va_end(args);
  58. }
  59. // -----------------------------------------------------------------------------
  60. // specific debug targets
  61. // -----------------------------------------------------------------------------
  62. #if DEBUG_SERIAL_SUPPORT
  63. void _debugSendSerial(const char* prefix, const char* data) {
  64. if (prefix && (prefix[0] != '\0')) {
  65. DEBUG_PORT.print(prefix);
  66. }
  67. DEBUG_PORT.print(data);
  68. }
  69. #endif // DEBUG_SERIAL_SUPPORT
  70. #if DEBUG_LOG_BUFFER_SUPPORT
  71. std::vector<char> _debug_log_buffer;
  72. bool _debug_log_buffer_enabled = false;
  73. void _debugLogBuffer(const char* prefix, const char* data) {
  74. if (!_debug_log_buffer_enabled) return;
  75. const auto prefix_len = strlen(prefix);
  76. const auto data_len = strlen(data);
  77. const auto total_len = prefix_len + data_len;
  78. if (total_len >= std::numeric_limits<uint16_t>::max()) {
  79. return;
  80. }
  81. if ((_debug_log_buffer.capacity() - _debug_log_buffer.size()) <= (total_len + 3)) {
  82. _debug_log_buffer_enabled = false;
  83. return;
  84. }
  85. _debug_log_buffer.push_back(total_len >> 8);
  86. _debug_log_buffer.push_back(total_len & 0xff);
  87. if (prefix && (prefix[0] != '\0')) {
  88. _debug_log_buffer.insert(_debug_log_buffer.end(), prefix, prefix + prefix_len);
  89. }
  90. _debug_log_buffer.insert(_debug_log_buffer.end(), data, data + data_len);
  91. }
  92. void _debugLogBufferDump() {
  93. size_t index = 0;
  94. do {
  95. if (index >= _debug_log_buffer.size()) {
  96. break;
  97. }
  98. size_t len = _debug_log_buffer[index] << 8;
  99. len = len | _debug_log_buffer[index + 1];
  100. index += 2;
  101. auto value = _debug_log_buffer[index + len];
  102. _debug_log_buffer[index + len] = '\0';
  103. _debugSendInternal(_debug_log_buffer.data() + index, false);
  104. _debug_log_buffer[index + len] = value;
  105. index += len;
  106. } while (true);
  107. _debug_log_buffer.clear();
  108. _debug_log_buffer.shrink_to_fit();
  109. }
  110. bool debugLogBuffer() {
  111. return _debug_log_buffer_enabled;
  112. }
  113. #endif // DEBUG_LOG_BUFFER_SUPPORT
  114. // -----------------------------------------------------------------------------
  115. void _debugSendInternal(const char * message, bool add_timestamp) {
  116. const size_t msg_len = strlen(message);
  117. bool pause = false;
  118. char timestamp[10] = {0};
  119. #if DEBUG_ADD_TIMESTAMP
  120. static bool continue_timestamp = true;
  121. if (add_timestamp && continue_timestamp) {
  122. snprintf(timestamp, sizeof(timestamp), "[%06lu] ", millis() % 1000000);
  123. }
  124. continue_timestamp = add_timestamp || (message[msg_len - 1] == 10) || (message[msg_len - 1] == 13);
  125. #endif
  126. #if DEBUG_SERIAL_SUPPORT
  127. _debugSendSerial(timestamp, message);
  128. #endif
  129. #if DEBUG_UDP_SUPPORT
  130. #if SYSTEM_CHECK_ENABLED
  131. if (systemCheck()) {
  132. #endif
  133. _udp_debug.beginPacket(DEBUG_UDP_IP, DEBUG_UDP_PORT);
  134. #if DEBUG_UDP_PORT == 514
  135. _udp_debug.write(_udp_syslog_header);
  136. #endif
  137. _udp_debug.write(message);
  138. pause = _udp_debug.endPacket() > 0;
  139. #if SYSTEM_CHECK_ENABLED
  140. }
  141. #endif
  142. #endif
  143. #if DEBUG_TELNET_SUPPORT
  144. pause = telnetDebugSend(timestamp, message) || pause;
  145. #endif
  146. #if DEBUG_WEB_SUPPORT
  147. pause = wsDebugSend(timestamp, message) || pause;
  148. #endif
  149. #if DEBUG_LOG_BUFFER_SUPPORT
  150. _debugLogBuffer(timestamp, message);
  151. #endif
  152. if (pause) {
  153. optimistic_yield(1000);
  154. }
  155. }
  156. // -----------------------------------------------------------------------------
  157. #if DEBUG_WEB_SUPPORT
  158. void _debugWebSocketOnAction(uint32_t client_id, const char * action, JsonObject& data) {
  159. #if TERMINAL_SUPPORT
  160. if (strcmp(action, "dbgcmd") == 0) {
  161. if (!data.containsKey("command") || !data["command"].is<const char*>()) return;
  162. const char* command = data["command"];
  163. if (command && strlen(command)) {
  164. auto command = data.get<const char*>("command");
  165. terminalInject((void*) command, strlen(command));
  166. terminalInject('\n');
  167. }
  168. }
  169. #endif
  170. }
  171. void debugWebSetup() {
  172. wsRegister()
  173. .onVisible([](JsonObject& root) { root["dbgVisible"] = 1; })
  174. .onAction(_debugWebSocketOnAction);
  175. // TODO: if hostname string changes, need to update header too
  176. #if DEBUG_UDP_SUPPORT
  177. #if DEBUG_UDP_PORT == 514
  178. snprintf_P(_udp_syslog_header, sizeof(_udp_syslog_header), PSTR("<%u>%s ESPurna[0]: "), DEBUG_UDP_FAC_PRI, getSetting("hostname").c_str());
  179. #endif
  180. #endif
  181. }
  182. #endif // DEBUG_WEB_SUPPORT
  183. // -----------------------------------------------------------------------------
  184. void debugSetup() {
  185. #if DEBUG_SERIAL_SUPPORT
  186. DEBUG_PORT.begin(SERIAL_BAUDRATE);
  187. #endif
  188. #if TERMINAL_SUPPORT
  189. #if DEBUG_LOG_BUFFER_SUPPORT
  190. terminalRegisterCommand(F("DEBUG.BUFFER"), [](Embedis* e) {
  191. _debug_log_buffer_enabled = false;
  192. if (!_debug_log_buffer.size()) {
  193. DEBUG_MSG_P(PSTR("[DEBUG] Buffer is empty\n"));
  194. return;
  195. }
  196. DEBUG_MSG_P(PSTR("[DEBUG] Buffer size: %u / %u bytes\n"),
  197. _debug_log_buffer.size(),
  198. _debug_log_buffer.capacity()
  199. );
  200. _debugLogBufferDump();
  201. });
  202. #endif // DEBUG_LOG_BUFFER_SUPPORT
  203. #endif // TERMINAL_SUPPORT
  204. }
  205. String _debugLogModeSerialize(DebugLogMode value) {
  206. switch (value) {
  207. case DebugLogMode::Disabled:
  208. return "0";
  209. case DebugLogMode::SkipBoot:
  210. return "2";
  211. default:
  212. case DebugLogMode::Enabled:
  213. return "1";
  214. }
  215. }
  216. DebugLogMode _debugLogModeDeserialize(const String& value) {
  217. switch (value.toInt()) {
  218. case 0:
  219. return DebugLogMode::Disabled;
  220. case 2:
  221. return DebugLogMode::SkipBoot;
  222. case 1:
  223. default:
  224. return DebugLogMode::Enabled;
  225. }
  226. }
  227. void debugConfigureBoot() {
  228. static_assert(
  229. std::is_same<int, std::underlying_type<DebugLogMode>::type>::value,
  230. "should be able to match DebugLogMode with int"
  231. );
  232. const auto mode = getSetting<DebugLogMode, _debugLogModeDeserialize>("dbgLogMode", DEBUG_LOG_MODE);
  233. switch (mode) {
  234. case DebugLogMode::SkipBoot:
  235. schedule_function([]() {
  236. _debug_enabled = true;
  237. });
  238. // fall through
  239. case DebugLogMode::Disabled:
  240. _debug_enabled = false;
  241. break;
  242. case DebugLogMode::Enabled:
  243. _debug_enabled = true;
  244. break;
  245. }
  246. debugConfigure();
  247. }
  248. void debugConfigure() {
  249. // HardwareSerial::begin() will automatically enable this when
  250. // `#if defined(DEBUG_ESP_PORT) && !defined(NDEBUG)`
  251. // Core debugging also depends on various DEBUG_ESP_... being defined
  252. {
  253. #if defined(DEBUG_ESP_PORT)
  254. #if not defined(NDEBUG)
  255. constexpr const bool debug_sdk = true;
  256. #endif // !defined(NDEBUG)
  257. #else
  258. constexpr const bool debug_sdk = false;
  259. #endif // defined(DEBUG_ESP_PORT)
  260. DEBUG_PORT.setDebugOutput(getSetting("dbgSDK", debug_sdk));
  261. }
  262. #if DEBUG_LOG_BUFFER_SUPPORT
  263. {
  264. const auto enabled = getSetting("dbgBufEnabled", 1 == DEBUG_LOG_BUFFER_ENABLED);
  265. const auto size = getSetting("dbgBufSize", DEBUG_LOG_BUFFER_SIZE);
  266. if (enabled) {
  267. _debug_log_buffer_enabled = true;
  268. _debug_log_buffer.reserve(size);
  269. }
  270. }
  271. #endif // DEBUG_LOG_BUFFER
  272. }
  273. #endif // DEBUG_SUPPORT