D576 Buffer Monitoring (#19674)

This commit is contained in:
chendo 2021-07-23 13:53:00 +10:00 committed by Scott Lahteine
parent 416234f43a
commit 51d954a4fd
7 changed files with 366 additions and 213 deletions

View File

@ -4177,6 +4177,14 @@
// Enable Marlin dev mode which adds some special commands // Enable Marlin dev mode which adds some special commands
//#define MARLIN_DEV_MODE //#define MARLIN_DEV_MODE
#if ENABLED(MARLIN_DEV_MODE)
/**
* D576 - Buffer Monitoring
* To help diagnose print quality issues stemming from empty command buffers.
*/
//#define BUFFER_MONITORING
#endif
/** /**
* Postmortem Debugging captures misbehavior and outputs the CPU status and backtrace to serial. * Postmortem Debugging captures misbehavior and outputs the CPU status and backtrace to serial.
* When running in the debugger it will break for debugging. This is useful to help understand * When running in the debugger it will break for debugging. This is useful to help understand

View File

@ -868,6 +868,7 @@ void idle(bool no_stepper_sleep/*=false*/) {
TERN_(AUTO_REPORT_TEMPERATURES, thermalManager.auto_reporter.tick()); TERN_(AUTO_REPORT_TEMPERATURES, thermalManager.auto_reporter.tick());
TERN_(AUTO_REPORT_SD_STATUS, card.auto_reporter.tick()); TERN_(AUTO_REPORT_SD_STATUS, card.auto_reporter.tick());
TERN_(AUTO_REPORT_POSITION, position_auto_reporter.tick()); TERN_(AUTO_REPORT_POSITION, position_auto_reporter.tick());
TERN_(BUFFER_MONITORING, queue.auto_report_buffer_statistics());
} }
#endif #endif

View File

@ -242,6 +242,7 @@
* M553 - Get or set IP netmask. (Requires enabled Ethernet port) * M553 - Get or set IP netmask. (Requires enabled Ethernet port)
* M554 - Get or set IP gateway. (Requires enabled Ethernet port) * M554 - Get or set IP gateway. (Requires enabled Ethernet port)
* M569 - Enable stealthChop on an axis. (Requires at least one _DRIVER_TYPE to be TMC2130/2160/2208/2209/5130/5160) * M569 - Enable stealthChop on an axis. (Requires at least one _DRIVER_TYPE to be TMC2130/2160/2208/2209/5130/5160)
* M575 - Change the serial baud rate. (Requires BAUD_RATE_GCODE)
* M600 - Pause for filament change: "M600 X<pos> Y<pos> Z<raise> E<first_retract> L<later_retract>". (Requires ADVANCED_PAUSE_FEATURE) * M600 - Pause for filament change: "M600 X<pos> Y<pos> Z<raise> E<first_retract> L<later_retract>". (Requires ADVANCED_PAUSE_FEATURE)
* M603 - Configure filament change: "M603 T<tool> U<unload_length> L<load_length>". (Requires ADVANCED_PAUSE_FEATURE) * M603 - Configure filament change: "M603 T<tool> U<unload_length> L<load_length>". (Requires ADVANCED_PAUSE_FEATURE)
* M605 - Set Dual X-Carriage movement mode: "M605 S<mode> [X<x_offset>] [R<temp_offset>]". (Requires DUAL_X_CARRIAGE) * M605 - Set Dual X-Carriage movement mode: "M605 S<mode> [X<x_offset>] [R<temp_offset>]". (Requires DUAL_X_CARRIAGE)
@ -298,6 +299,7 @@
* M997 - Perform in-application firmware update * M997 - Perform in-application firmware update
* M999 - Restart after being stopped by error * M999 - Restart after being stopped by error
* D... - Custom Development G-code. Add hooks to 'gcode_D.cpp' for developers to test features. (Requires MARLIN_DEV_MODE) * D... - Custom Development G-code. Add hooks to 'gcode_D.cpp' for developers to test features. (Requires MARLIN_DEV_MODE)
* D576 - Set buffer monitoring options. (Requires BUFFER_MONITORING)
* *
* "T" Codes * "T" Codes
* *

View File

@ -23,25 +23,30 @@
#if ENABLED(MARLIN_DEV_MODE) #if ENABLED(MARLIN_DEV_MODE)
#include "gcode.h" #include "gcode.h"
#include "../module/settings.h"
#include "../module/temperature.h"
#include "../libs/hex_print.h"
#include "../HAL/shared/eeprom_if.h"
#include "../HAL/shared/Delay.h"
#include "../sd/cardreader.h"
#include "../MarlinCore.h" // for kill
extern void dump_delay_accuracy_check(); #if ENABLED(BUFFER_MONITORING)
#include "queue.h"
#endif
/** #include "../module/settings.h"
#include "../module/temperature.h"
#include "../libs/hex_print.h"
#include "../HAL/shared/eeprom_if.h"
#include "../HAL/shared/Delay.h"
#include "../sd/cardreader.h"
#include "../MarlinCore.h" // for kill
extern void dump_delay_accuracy_check();
/**
* Dn: G-code for development and testing * Dn: G-code for development and testing
* *
* See https://reprap.org/wiki/G-code#D:_Debug_codes * See https://reprap.org/wiki/G-code#D:_Debug_codes
* *
* Put whatever else you need here to test ongoing development. * Put whatever else you need here to test ongoing development.
*/ */
void GcodeSuite::D(const int16_t dcode) { void GcodeSuite::D(const int16_t dcode) {
switch (dcode) { switch (dcode) {
case -1: case -1:
@ -264,7 +269,33 @@
} }
#endif #endif
}
#if ENABLED(BUFFER_MONITORING)
/**
* D576: Return buffer stats or set the auto-report interval.
* Usage: D576 [S<seconds>]
*
* With no parameters emits the following output:
* "D576 P<nn> B<nn> PU<nn> PD<nn> BU<nn> BD<nn>"
* Where:
* P : Planner buffers free
* B : Command buffers free
* PU: Planner buffer underruns (since the last report)
* PD: Longest duration (ms) the planner buffer was empty (since the last report)
* BU: Command buffer underruns (since the last report)
* BD: Longest duration (ms) command buffer was empty (since the last report)
*/
case 576: {
if (parser.seenval('S'))
queue.set_auto_report_interval((uint8_t)parser.value_byte());
else
queue.report_buffer_statistics();
break;
} }
#endif #endif // BUFFER_MONITORING
}
}
#endif // MARLIN_DEV_MODE

View File

@ -67,6 +67,23 @@ GCodeQueue::RingBuffer GCodeQueue::ring_buffer = { 0 };
static millis_t last_command_time = 0; static millis_t last_command_time = 0;
#endif #endif
/**
* Track buffer underruns
*/
#if ENABLED(BUFFER_MONITORING)
uint32_t GCodeQueue::command_buffer_underruns = 0,
GCodeQueue::planner_buffer_underruns = 0;
bool GCodeQueue::command_buffer_empty = false,
GCodeQueue::planner_buffer_empty = false;
millis_t GCodeQueue::max_command_buffer_empty_duration = 0,
GCodeQueue::max_planner_buffer_empty_duration = 0,
GCodeQueue::command_buffer_empty_at = 0,
GCodeQueue::planner_buffer_empty_at = 0;
uint8_t GCodeQueue::auto_buffer_report_interval;
millis_t GCodeQueue::next_buffer_report_ms;
#endif
/** /**
* Serial command injection * Serial command injection
*/ */
@ -82,7 +99,6 @@ PGM_P GCodeQueue::injected_commands_P; // = nullptr
*/ */
char GCodeQueue::injected_commands[64]; // = { 0 } char GCodeQueue::injected_commands[64]; // = { 0 }
void GCodeQueue::RingBuffer::commit_command(bool skip_ok void GCodeQueue::RingBuffer::commit_command(bool skip_ok
OPTARG(HAS_MULTI_SERIAL, serial_index_t serial_ind/*=-1*/) OPTARG(HAS_MULTI_SERIAL, serial_index_t serial_ind/*=-1*/)
) { ) {
@ -621,7 +637,24 @@ void GCodeQueue::advance() {
if (process_injected_command_P() || process_injected_command()) return; if (process_injected_command_P() || process_injected_command()) return;
// Return if the G-code buffer is empty // Return if the G-code buffer is empty
if (ring_buffer.empty()) return; if (ring_buffer.empty()) {
#if ENABLED(BUFFER_MONITORING)
if (!command_buffer_empty) {
command_buffer_empty = true;
command_buffer_underruns++;
command_buffer_empty_at = millis();
}
#endif
return;
}
#if ENABLED(BUFFER_MONITORING)
if (command_buffer_empty) {
command_buffer_empty = false;
const millis_t command_buffer_empty_duration = millis() - command_buffer_empty_at;
NOLESS(max_command_buffer_empty_duration, command_buffer_empty_duration);
}
#endif
#if ENABLED(SDSUPPORT) #if ENABLED(SDSUPPORT)
@ -664,3 +697,41 @@ void GCodeQueue::advance() {
// The queue may be reset by a command handler or by code invoked by idle() within a handler // The queue may be reset by a command handler or by code invoked by idle() within a handler
ring_buffer.advance_pos(ring_buffer.index_r, -1); ring_buffer.advance_pos(ring_buffer.index_r, -1);
} }
#if ENABLED(BUFFER_MONITORING)
void GCodeQueue::report_buffer_statistics() {
SERIAL_ECHOLNPAIR("D576"
" P:", planner.moves_free(), " ", -queue.planner_buffer_underruns, " (", queue.max_planner_buffer_empty_duration, ")"
" B:", BUFSIZE - ring_buffer.length, " ", -queue.command_buffer_underruns, " (", queue.max_command_buffer_empty_duration, ")"
);
command_buffer_underruns = planner_buffer_underruns = 0;
max_command_buffer_empty_duration = max_planner_buffer_empty_duration = 0;
}
void GCodeQueue::auto_report_buffer_statistics() {
// Bit of a hack to try to catch planner buffer underruns without having logic
// running inside Stepper::block_phase_isr
const millis_t ms = millis();
if (planner.movesplanned() == 0) {
if (!planner_buffer_empty) { // the planner buffer wasn't empty, but now it is
planner_buffer_empty = true;
planner_buffer_underruns++;
planner_buffer_empty_at = ms;
}
}
else if (planner_buffer_empty) { // the planner buffer was empty, but now it's not
planner_buffer_empty = false;
const millis_t planner_buffer_empty_duration = ms - planner_buffer_empty_at;
NOLESS(max_planner_buffer_empty_duration, planner_buffer_empty_duration); // if it's longer than the currently tracked max duration, replace it
}
if (queue.auto_buffer_report_interval && ELAPSED(ms, queue.next_buffer_report_ms)) {
queue.next_buffer_report_ms = ms + 1000UL * queue.auto_buffer_report_interval;
PORT_REDIRECT(SERIAL_BOTH);
report_buffer_statistics();
PORT_RESTORE();
}
}
#endif // BUFFER_MONITORING

View File

@ -197,6 +197,46 @@ public:
*/ */
static inline void set_current_line_number(long n) { serial_state[ring_buffer.command_port().index].last_N = n; } static inline void set_current_line_number(long n) { serial_state[ring_buffer.command_port().index].last_N = n; }
#if ENABLED(BUFFER_MONITORING)
private:
/**
* Track buffer underruns
*/
static uint32_t command_buffer_underruns, planner_buffer_underruns;
static bool command_buffer_empty, planner_buffer_empty;
static millis_t max_command_buffer_empty_duration, max_planner_buffer_empty_duration,
command_buffer_empty_at, planner_buffer_empty_at;
/**
* Report buffer statistics to the host to be able to detect buffer underruns
*
* Returns "D576 " followed by:
* P<uint> Planner space remaining
* B<uint> Command buffer space remaining
* PU<uint> Number of planner buffer underruns since last report
* PD<uint> Max time in ms the planner buffer was empty since last report
* BU<uint> Number of command buffer underruns since last report
* BD<uint> Max time in ms the command buffer was empty since last report
*/
static void report_buffer_statistics();
static uint8_t auto_buffer_report_interval;
static millis_t next_buffer_report_ms;
public:
static void auto_report_buffer_statistics();
static inline void set_auto_report_interval(uint8_t v) {
NOMORE(v, 60);
auto_buffer_report_interval = v;
next_buffer_report_ms = millis() + 1000UL * v;
}
#endif // BUFFER_MONITORING
private: private:
static void get_serial_commands(); static void get_serial_commands();

View File

@ -10,7 +10,7 @@ set -e
# Build with configs included in the PR # Build with configs included in the PR
# #
use_example_configs "Creality/Ender-3 V2/CrealityUI" use_example_configs "Creality/Ender-3 V2/CrealityUI"
opt_enable MARLIN_DEV_MODE opt_enable MARLIN_DEV_MODE BUFFER_MONITORING
exec_test $1 $2 "Ender 3 v2 with CrealityUI" "$3" exec_test $1 $2 "Ender 3 v2 with CrealityUI" "$3"
use_example_configs "Creality/Ender-3 V2/CrealityUI" use_example_configs "Creality/Ender-3 V2/CrealityUI"