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.

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