diff --git a/src/emc/motion/command.c b/src/emc/motion/command.c index b8094e03e9b..0ba29b8c4fd 100644 --- a/src/emc/motion/command.c +++ b/src/emc/motion/command.c @@ -664,8 +664,7 @@ void emcmotCommandHandler_locked(void *arg, long servo_period) Called by task using [KINS]JOINTS= which is typically the same value as the motmod num_joints= parameter */ - rtapi_print_msg(RTAPI_MSG_DBG, "SET_NUM_JOINTS"); - rtapi_print_msg(RTAPI_MSG_DBG, " %d", emcmotCommand->joint); + rtapi_print_msg(RTAPI_MSG_DBG, "SET_NUM_JOINTS %d", emcmotCommand->joint); if (( emcmotCommand->joint <= 0 ) || ( emcmotCommand->joint > EMCMOT_MAX_JOINTS )) { break; @@ -679,9 +678,8 @@ void emcmotCommandHandler_locked(void *arg, long servo_period) the number of spindles configured for the motion module (motion_num_spindles) */ - rtapi_print_msg(RTAPI_MSG_DBG, "SET_NUM_SPINDLES"); + rtapi_print_msg(RTAPI_MSG_DBG, "SET_NUM_SPINDLES %d", emcmotCommand->spindle); emcmot_config_change(); - rtapi_print_msg(RTAPI_MSG_DBG, " %d", emcmotCommand->spindle); if ( emcmotCommand->spindle > motion_num_spindles || emcmotCommand->spindle <= 0 || emcmotCommand->spindle > EMCMOT_MAX_SPINDLES @@ -706,8 +704,7 @@ void emcmotCommandHandler_locked(void *arg, long servo_period) break; case EMCMOT_SET_JOINT_HOMING_PARAMS: - rtapi_print_msg(RTAPI_MSG_DBG, "SET_JOINT_HOMING_PARAMS"); - rtapi_print_msg(RTAPI_MSG_DBG, " %d", joint_num); + rtapi_print_msg(RTAPI_MSG_DBG, "SET_JOINT_HOMING_PARAMS %d", joint_num); emcmot_config_change(); if (joint == 0) { break; @@ -727,8 +724,7 @@ void emcmotCommandHandler_locked(void *arg, long servo_period) break; case EMCMOT_UPDATE_JOINT_HOMING_PARAMS: - rtapi_print_msg(RTAPI_MSG_DBG, "UPDATE_JOINT_HOMING_PARAMS"); - rtapi_print_msg(RTAPI_MSG_DBG, " %d", joint_num); + rtapi_print_msg(RTAPI_MSG_DBG, "UPDATE_JOINT_HOMING_PARAMS %d", joint_num); emcmot_config_change(); if (joint == 0) { break; @@ -744,8 +740,7 @@ void emcmotCommandHandler_locked(void *arg, long servo_period) /* this command can be issued with joint < 0 to re-enable limits, but they are automatically re-enabled at the end of the next jog */ - rtapi_print_msg(RTAPI_MSG_DBG, "OVERRIDE_LIMITS"); - rtapi_print_msg(RTAPI_MSG_DBG, " %d", joint_num); + rtapi_print_msg(RTAPI_MSG_DBG, "OVERRIDE_LIMITS %d", joint_num); if (joint_num < 0) { /* don't override limits */ rtapi_print_msg(RTAPI_MSG_DBG, "override off"); @@ -775,8 +770,7 @@ void emcmotCommandHandler_locked(void *arg, long servo_period) break; case EMCMOT_SET_JOINT_MOTOR_OFFSET: - rtapi_print_msg(RTAPI_MSG_DBG, "SET_JOINT_MOTOR_OFFSET"); - rtapi_print_msg(RTAPI_MSG_DBG, " %d", joint_num); + rtapi_print_msg(RTAPI_MSG_DBG, "SET_JOINT_MOTOR_OFFSET %d", joint_num); if(joint == 0) { break; } @@ -786,8 +780,7 @@ void emcmotCommandHandler_locked(void *arg, long servo_period) case EMCMOT_SET_JOINT_POSITION_LIMITS: /* set the position limits for the joint */ /* can be done at any time */ - rtapi_print_msg(RTAPI_MSG_DBG, "SET_JOINT_POSITION_LIMITS"); - rtapi_print_msg(RTAPI_MSG_DBG, " %d", joint_num); + rtapi_print_msg(RTAPI_MSG_DBG, "SET_JOINT_POSITION_LIMITS %d", joint_num); emcmot_config_change(); if (joint == 0) { break; @@ -799,8 +792,7 @@ void emcmotCommandHandler_locked(void *arg, long servo_period) case EMCMOT_SET_JOINT_BACKLASH: /* set the backlash for the joint */ /* can be done at any time */ - rtapi_print_msg(RTAPI_MSG_DBG, "SET_JOINT_BACKLASH"); - rtapi_print_msg(RTAPI_MSG_DBG, " %d", joint_num); + rtapi_print_msg(RTAPI_MSG_DBG, "SET_JOINT_BACKLASH %d", joint_num); emcmot_config_change(); if (joint == 0) { break; @@ -815,8 +807,7 @@ void emcmotCommandHandler_locked(void *arg, long servo_period) minFerror then OK else if ferror < limiting ferror then OK else ERROR */ case EMCMOT_SET_JOINT_MAX_FERROR: - rtapi_print_msg(RTAPI_MSG_DBG, "SET_JOINT_MAX_FERROR"); - rtapi_print_msg(RTAPI_MSG_DBG, " %d", joint_num); + rtapi_print_msg(RTAPI_MSG_DBG, "SET_JOINT_MAX_FERROR %d", joint_num); emcmot_config_change(); if (joint == 0 || emcmotCommand->maxFerror < 0.0) { break; @@ -825,8 +816,7 @@ void emcmotCommandHandler_locked(void *arg, long servo_period) break; case EMCMOT_SET_JOINT_MIN_FERROR: - rtapi_print_msg(RTAPI_MSG_DBG, "SET_JOINT_MIN_FERROR"); - rtapi_print_msg(RTAPI_MSG_DBG, " %d", joint_num); + rtapi_print_msg(RTAPI_MSG_DBG, "SET_JOINT_MIN_FERROR %d", joint_num); emcmot_config_change(); if (joint == 0 || emcmotCommand->minFerror < 0.0) { break; @@ -838,8 +828,7 @@ void emcmotCommandHandler_locked(void *arg, long servo_period) /* do a continuous jog, implemented as an incremental jog to the limit. When the user lets go of the button an abort will stop the jog. */ - rtapi_print_msg(RTAPI_MSG_DBG, "JOG_CONT"); - rtapi_print_msg(RTAPI_MSG_DBG, " %d", joint_num); + rtapi_print_msg(RTAPI_MSG_DBG, "JOG_CONT %d", joint_num); if (!GET_MOTION_ENABLE_FLAG()) { reportError(_("Can't jog joint when not enabled.")); SET_JOINT_ERROR_FLAG(joint, 1); @@ -907,8 +896,7 @@ void emcmotCommandHandler_locked(void *arg, long servo_period) case EMCMOT_JOG_INCR: /* do an incremental jog */ - rtapi_print_msg(RTAPI_MSG_DBG, "JOG_INCR"); - rtapi_print_msg(RTAPI_MSG_DBG, " %d", joint_num); + rtapi_print_msg(RTAPI_MSG_DBG, "JOG_INCR %d", joint_num); if (!GET_MOTION_ENABLE_FLAG()) { reportError(_("Can't jog joint when not enabled.")); SET_JOINT_ERROR_FLAG(joint, 1); @@ -984,8 +972,7 @@ void emcmotCommandHandler_locked(void *arg, long servo_period) case EMCMOT_JOG_ABS: /* do an absolute jog */ - rtapi_print_msg(RTAPI_MSG_DBG, "JOG_ABS"); - rtapi_print_msg(RTAPI_MSG_DBG, " %d", joint_num); + rtapi_print_msg(RTAPI_MSG_DBG, "JOG_ABS %d", joint_num); if (joint == 0) { break; } @@ -1208,8 +1195,7 @@ void emcmotCommandHandler_locked(void *arg, long servo_period) case EMCMOT_SET_JOINT_VEL_LIMIT: /* set joint max velocity */ /* can do it at any time */ - rtapi_print_msg(RTAPI_MSG_DBG, "SET_JOINT_VEL_LIMIT"); - rtapi_print_msg(RTAPI_MSG_DBG, " %d", joint_num); + rtapi_print_msg(RTAPI_MSG_DBG, "SET_JOINT_VEL_LIMIT %d", joint_num); emcmot_config_change(); /* check joint range */ if (joint == 0) { @@ -1221,8 +1207,7 @@ void emcmotCommandHandler_locked(void *arg, long servo_period) case EMCMOT_SET_JOINT_ACC_LIMIT: /* set joint max acceleration */ /* can do it at any time */ - rtapi_print_msg(RTAPI_MSG_DBG, "SET_JOINT_ACC_LIMIT"); - rtapi_print_msg(RTAPI_MSG_DBG, " %d", joint_num); + rtapi_print_msg(RTAPI_MSG_DBG, "SET_JOINT_ACC_LIMIT %d", joint_num); emcmot_config_change(); /* check joint range */ if (joint == 0) { @@ -1234,8 +1219,7 @@ void emcmotCommandHandler_locked(void *arg, long servo_period) case EMCMOT_SET_JOINT_JERK_LIMIT: /* set joint max jerk */ /* can do it at any time */ - rtapi_print_msg(RTAPI_MSG_DBG, "SET_JOINT_JERK_LIMIT"); - rtapi_print_msg(RTAPI_MSG_DBG, " j(%d) jerk(%f)", joint_num, emcmotCommand->jerk); + rtapi_print_msg(RTAPI_MSG_DBG, "SET_JOINT_JERK_LIMIT j(%d) jerk(%f)", joint_num, emcmotCommand->jerk); emcmot_config_change(); if (joint == 0) { break; @@ -1483,8 +1467,7 @@ void emcmotCommandHandler_locked(void *arg, long servo_period) /* make joint active, so that amps will be enabled when system is enabled or disabled */ /* can be done at any time */ - rtapi_print_msg(RTAPI_MSG_DBG, "JOINT_ACTIVATE"); - rtapi_print_msg(RTAPI_MSG_DBG, " %d", joint_num); + rtapi_print_msg(RTAPI_MSG_DBG, "JOINT_ACTIVATE %d", joint_num); if (joint == 0) { break; } @@ -1495,8 +1478,7 @@ void emcmotCommandHandler_locked(void *arg, long servo_period) /* make joint inactive, so that amps won't be affected when system is enabled or disabled */ /* can be done at any time */ - rtapi_print_msg(RTAPI_MSG_DBG, "JOINT_DEACTIVATE"); - rtapi_print_msg(RTAPI_MSG_DBG, " %d", joint_num); + rtapi_print_msg(RTAPI_MSG_DBG, "JOINT_DEACTIVATE %d", joint_num); if (joint == 0) { break; } @@ -1508,8 +1490,7 @@ void emcmotCommandHandler_locked(void *arg, long servo_period) /* need to be in free mode, enable on */ /* this just sets the initial state, then the state machine in homing.c does the rest */ - rtapi_print_msg(RTAPI_MSG_DBG, "JOINT_HOME"); - rtapi_print_msg(RTAPI_MSG_DBG, " %d", joint_num); + rtapi_print_msg(RTAPI_MSG_DBG, "JOINT_HOME %d", joint_num); /* Normally homing requires free (joint) mode. The queued G28.2 path * (emctaskmain EMC_TASK_EXEC::WAITING_FOR_HOMING) dips motion into @@ -1550,8 +1531,7 @@ void emcmotCommandHandler_locked(void *arg, long servo_period) case EMCMOT_JOINT_UNHOME: /* unhome the specified joint, or all joints if -1, or volatile joints if -2 */ - rtapi_print_msg(RTAPI_MSG_DBG, "JOINT_UNHOME"); - rtapi_print_msg(RTAPI_MSG_DBG, " %d", joint_num); + rtapi_print_msg(RTAPI_MSG_DBG, "JOINT_UNHOME %d", joint_num); if ( (emcmotStatus->motion_state != EMCMOT_MOTION_FREE) && (emcmotStatus->motion_state != EMCMOT_MOTION_DISABLED)) { @@ -2043,8 +2023,7 @@ void emcmotCommandHandler_locked(void *arg, long servo_period) case EMCMOT_SET_AXIS_POSITION_LIMITS: /* set the position limits for axis */ /* can be done at any time */ - rtapi_print_msg(RTAPI_MSG_DBG, "SET_AXIS_POSITION_LIMITS"); - rtapi_print_msg(RTAPI_MSG_DBG, " %d", emcmotCommand->axis); + rtapi_print_msg(RTAPI_MSG_DBG, "SET_AXIS_POSITION_LIMITS %d", emcmotCommand->axis); emcmot_config_change(); if ((emcmotCommand->axis < 0) || (emcmotCommand->axis >= EMCMOT_MAX_AXIS)) { break; @@ -2056,8 +2035,7 @@ void emcmotCommandHandler_locked(void *arg, long servo_period) case EMCMOT_SET_AXIS_VEL_LIMIT: /* set the max axis vel */ /* can be done at any time */ - rtapi_print_msg(RTAPI_MSG_DBG, "SET_AXIS_VEL_LIMITS"); - rtapi_print_msg(RTAPI_MSG_DBG, " %d", emcmotCommand->axis); + rtapi_print_msg(RTAPI_MSG_DBG, "SET_AXIS_VEL_LIMITS %d", emcmotCommand->axis); emcmot_config_change(); if ((emcmotCommand->axis < 0) || (emcmotCommand->axis >= EMCMOT_MAX_AXIS)) { break; @@ -2069,8 +2047,7 @@ void emcmotCommandHandler_locked(void *arg, long servo_period) case EMCMOT_SET_AXIS_ACC_LIMIT: /* set the max axis acc */ /* can be done at any time */ - rtapi_print_msg(RTAPI_MSG_DBG, "SET_AXIS_ACC_LIMITS"); - rtapi_print_msg(RTAPI_MSG_DBG, " %d", emcmotCommand->axis); + rtapi_print_msg(RTAPI_MSG_DBG, "SET_AXIS_ACC_LIMITS %d", emcmotCommand->axis); emcmot_config_change(); if ((emcmotCommand->axis < 0) || (emcmotCommand->axis >= EMCMOT_MAX_AXIS)) { break; @@ -2082,8 +2059,7 @@ void emcmotCommandHandler_locked(void *arg, long servo_period) case EMCMOT_SET_AXIS_JERK_LIMIT: /* set the max axis jerk */ /* can be done at any time */ - rtapi_print_msg(RTAPI_MSG_DBG, "SET_AXIS_JERK_LIMITS"); - rtapi_print_msg(RTAPI_MSG_DBG, " %d jerk(%f)", emcmotCommand->axis, emcmotCommand->jerk); + rtapi_print_msg(RTAPI_MSG_DBG, "SET_AXIS_JERK_LIMITS %d jerk(%f)", emcmotCommand->axis, emcmotCommand->jerk); emcmot_config_change(); if ((emcmotCommand->axis < 0) || (emcmotCommand->axis >= EMCMOT_MAX_AXIS)) { break; @@ -2092,8 +2068,7 @@ void emcmotCommandHandler_locked(void *arg, long servo_period) break; case EMCMOT_SET_AXIS_LOCKING_JOINT: - rtapi_print_msg(RTAPI_MSG_DBG, "SET_AXIS_ACC_LOCKING_JOINT"); - rtapi_print_msg(RTAPI_MSG_DBG, " %d", emcmotCommand->axis); + rtapi_print_msg(RTAPI_MSG_DBG, "SET_AXIS_ACC_LOCKING_JOINT %d", emcmotCommand->axis); emcmot_config_change(); if ((emcmotCommand->axis < 0) || (emcmotCommand->axis >= EMCMOT_MAX_AXIS)) { break; @@ -2139,7 +2114,6 @@ void emcmotCommandHandler_locked(void *arg, long servo_period) rtapi_print_msg(RTAPI_MSG_DBG, "ERROR: %d", emcmotStatus->commandStatus); } - rtapi_print_msg(RTAPI_MSG_DBG, "\n"); /* synch tail count */ rtapi_seqlock_write_end(&emcmotStatus->tail, &emcmotStatus->head); rtapi_seqlock_write_end(&emcmotConfig->tail, &emcmotConfig->head); diff --git a/src/emc/motion/motion.c b/src/emc/motion/motion.c index 9370b8803be..4a096507d64 100644 --- a/src/emc/motion/motion.c +++ b/src/emc/motion/motion.c @@ -224,6 +224,9 @@ void reportError(const char *fmt, ...) char fmt_tmp[EMCMOT_ERROR_LEN]; size_t fmt_len = strlen(fmt); //Manually check string length and truncate if it is to long + if(fmt_len > 0 && fmt[fmt_len-1] == '\n'){ + fmt_len--; + } if(fmt_len < EMCMOT_ERROR_LEN-1){ memcpy(fmt_tmp, fmt, fmt_len); fmt_tmp[fmt_len+0] = '\n'; diff --git a/src/emc/task/emctaskmain.cc b/src/emc/task/emctaskmain.cc index 72afc74b7bb..049c640f1af 100644 --- a/src/emc/task/emctaskmain.cc +++ b/src/emc/task/emctaskmain.cc @@ -222,6 +222,11 @@ static int emcOperatorErrorV(bool echo, const char *fmt, va_list ap) // force a NULL at the end for safety error_msg.error[LINELEN - 1] = 0; + // an error is one line: the GUI and the echo below end it + size_t n = strlen(error_msg.error); + while (n > 0 && error_msg.error[n - 1] == '\n') + error_msg.error[--n] = 0; + // write it if (echo) { log_info("{}\n", error_msg.error); diff --git a/src/hal/drivers/hal_ppmc.c b/src/hal/drivers/hal_ppmc.c index 83e65900f2e..9e8a553198b 100644 --- a/src/hal/drivers/hal_ppmc.c +++ b/src/hal/drivers/hal_ppmc.c @@ -566,8 +566,7 @@ int rtapi_app_main(void) slot = &(bus->slot_data[slotnum]); /* rv1 is used to flag errors that fail one bus */ rv1 = 0; - rtapi_print_msg(RTAPI_MSG_INFO, "PPMC: slot %d: ", slotnum); - + /* check slot */ idcode = SelRead(slot->slot_base+SLOT_ID_OFFSET, slot->port_addr); if ((idcode == 0)||(idcode == 0xFF)||((idcode&0x0f) == 0x0f)) { @@ -575,13 +574,13 @@ int rtapi_app_main(void) // being left on the bus by a non-implemented device slot slot->id = 0; slot->ver = 0; - rtapi_print_msg(RTAPI_MSG_INFO, "nothing detected at addr %x reads %x\n", - slotnum,idcode); + rtapi_print_msg(RTAPI_MSG_INFO, "PPMC: slot %d: nothing detected at addr %x reads %x\n", + slotnum,slotnum,idcode); ClrTimeout(slot->port_addr); /* skip to next slot */ continue; } - rtapi_print_msg(RTAPI_MSG_INFO, "ID code: %02X ", idcode); + rtapi_print_msg(RTAPI_MSG_INFO, "PPMC: slot %d: ID code: %02X\n", slotnum, idcode); slot->id = id = idcode & 0xF0; slot->ver = ver = idcode & 0x0F; slot->use_timestamp = 0; /* default is no timestamp */ diff --git a/src/hal/drivers/mesa-hostmot2/hm2_modbus.c b/src/hal/drivers/mesa-hostmot2/hm2_modbus.c index 01b982cc5e9..a562a3818a2 100644 --- a/src/hal/drivers/mesa-hostmot2/hm2_modbus.c +++ b/src/hal/drivers/mesa-hostmot2/hm2_modbus.c @@ -151,6 +151,20 @@ static inline unsigned mtypesize(unsigned mtype) { #define MSG_WARN(fmt...) do { rtapi_print_msg(RTAPI_MSG_WARN, fmt); } while(0) #define MSG_PRINT(fmt...) do { rtapi_print(fmt); } while(0) +#ifdef DEBUG +// Debug dump of a frame, one message for every 16 bytes +static void dump_frame(const char *what, unsigned cmdidx, const rtapi_u8 *data, unsigned len) +{ + for(unsigned i = 0; i < len; i += 16) { + char hex[16 * 3 + 1] = ""; + unsigned n = 0; + for(unsigned j = i; j < len && j < i + 16; j++) + n += rtapi_snprintf(hex + n, sizeof(hex) - n, " %02x", data[j]); + MSG_DBG("%s cmd=%u [%u]:%s\n", what, cmdidx, i, hex); + } +} +#endif + // State-machine states enum { STATE_START = 0, @@ -495,9 +509,8 @@ static int send_modbus_pkt(hm2_modbus_inst_t *inst) cc->data[cc->datalen++] = (checksum >> 8) & 0xff; #ifdef DEBUG - MSG_DBG("Sending cmd=%u to '%s' %i bytes", inst->cmdidx, inst->uart, cc->datalen); - for(int i = 0; i < cc->datalen; i++) MSG_DBG(" 0x%02x", cc->data[i]); - MSG_DBG("\n"); + MSG_DBG("Sending cmd=%u to '%s' %i bytes\n", inst->cmdidx, inst->uart, cc->datalen); + dump_frame("Sending", inst->cmdidx, cc->data, cc->datalen); #endif rtapi_u8 frames = 1; @@ -1719,13 +1732,13 @@ static int parse_data_frame(hm2_modbus_inst_t *inst) return -1; } - MSG_DBG("Return PDU cmd=%u is", inst->cmdidx); for(unsigned i = 0; i < rxcount; i++) { bytes[i] = (data[w] >> b) & 0xFF; - MSG_DBG(" %02x", bytes[i]); if((b += 8) >= 32) { b = 0; w++; } } - MSG_DBG("\n"); +#ifdef DEBUG + dump_frame("Return PDU", inst->cmdidx, bytes, rxcount); +#endif if(bytes[0] != cc->cmd.mbid) { MSG_ERR("%s: error: Modbus device address mismatch: got 0x%02x, expected 0x%02x\n", inst->name, bytes[0], cc->cmd.mbid); diff --git a/src/hal/utils/halcmd.c b/src/hal/utils/halcmd.c index 542cb19b127..694801c8d0a 100644 --- a/src/hal/utils/halcmd.c +++ b/src/hal/utils/halcmd.c @@ -212,14 +212,17 @@ pid_t hal_systemv_nowait(const char *const argv[]) { if ( pid == 0 ) { /* child process */ /* print debugging info if "very verbose" (-V) */ + char cmdline[1024] = ""; for(n=0; argv[n] != NULL; n++) { - rtapi_print_msg(RTAPI_MSG_DBG, "%s ", argv[n] ); + if (n) + strncat(cmdline, " ", sizeof(cmdline) - strlen(cmdline) - 1); + strncat(cmdline, argv[n], sizeof(cmdline) - strlen(cmdline) - 1); } if (n == 0) { halcmd_error("hal_systemv_nowait: empty argv array passed in\n"); exit(1); } - rtapi_print_msg(RTAPI_MSG_DBG, "\n" ); + rtapi_print_msg(RTAPI_MSG_DBG, "%s\n", cmdline); /* call execv() to invoke command */ execvp(argv[0], (char * const *)argv); /* should never get here */ diff --git a/src/rtapi/rtai_rtapi.c b/src/rtapi/rtai_rtapi.c index 3eb1bae6101..b019fda81f0 100644 --- a/src/rtapi/rtai_rtapi.c +++ b/src/rtapi/rtai_rtapi.c @@ -433,10 +433,19 @@ int rtapi_snprintf(char *buf, unsigned long int size, const char *fmt, ...) #define BUFFERLEN 1024 +/* Every rtapi_print_msg() message is one line: end one that lacks the + newline. rtapi_print() (RTAPI_MSG_ALL) prints like printf. */ void default_rtapi_msg_handler(msg_level_t level, const char *fmt, va_list ap) { char buf[BUFFERLEN]; - rtapi_vsnprintf(buf, BUFFERLEN, fmt, ap); - rt_printk(buf); + size_t n; + /* leave room for the newline */ + rtapi_vsnprintf(buf, sizeof(buf) - 1, fmt, ap); + n = strlen(buf); + if (level != RTAPI_MSG_ALL && n > 0 && buf[n - 1] != '\n') { + buf[n] = '\n'; + buf[n + 1] = '\0'; + } + rt_printk("%s", buf); } static rtapi_msg_handler_t rtapi_msg_handler = default_rtapi_msg_handler; diff --git a/src/rtapi/rtai_ulapi.c b/src/rtapi/rtai_ulapi.c index cc73ead56b2..47f2eccbc5f 100644 --- a/src/rtapi/rtai_ulapi.c +++ b/src/rtapi/rtai_ulapi.c @@ -269,9 +269,18 @@ void rtapi_print_msg(msg_level_t level, const char *fmt, ...) va_list args; if ((level <= msg_level) && (msg_level != RTAPI_MSG_NONE)) { + /* one message is one line: end one that lacks the newline */ + char *msg; + int n; va_start(args, fmt); - vfprintf(stderr, fmt, args); + n = vasprintf(&msg, fmt, args); va_end(args); + if (n < 0) + return; + fputs(msg, stderr); + if (level != RTAPI_MSG_ALL && n > 0 && msg[n - 1] != '\n') + fputc('\n', stderr); + free(msg); } } diff --git a/src/rtapi/uspace_common.h b/src/rtapi/uspace_common.h index 4032e7eb2fa..c3f22399b00 100644 --- a/src/rtapi/uspace_common.h +++ b/src/rtapi/uspace_common.h @@ -230,6 +230,15 @@ int rtapi_shmem_delete(int handle, int module_id) void default_rtapi_msg_handler(msg_level_t level, const char *fmt, va_list ap); +/* Every rtapi_print_msg() message is one line, and the default handlers + end one that lacks the newline. rtapi_print() (RTAPI_MSG_ALL) prints + like printf. */ +static inline int rtapi_msg_needs_newline(msg_level_t level, const char *msg) +{ + size_t n = strlen(msg); + return level != RTAPI_MSG_ALL && n > 0 && msg[n - 1] != '\n'; +} + static rtapi_msg_handler_t rtapi_msg_handler = default_rtapi_msg_handler; rtapi_msg_handler_t rtapi_get_msg_handler(void) { diff --git a/src/rtapi/uspace_rtapi_app.cc b/src/rtapi/uspace_rtapi_app.cc index 2b4bcb0f9a6..93e3e2014b8 100644 --- a/src/rtapi/uspace_rtapi_app.cc +++ b/src/rtapi/uspace_rtapi_app.cc @@ -22,6 +22,7 @@ #include #include #include +#include std::atomic_int WithRoot::level; uid_t WithRoot::ruid, WithRoot::euid; @@ -243,13 +244,13 @@ int RtapiApp::find_rt_cpu_number() { parse_cpu_list(isolated_file, &cpuset); //Print list - rtapi_print_msg(RTAPI_MSG_INFO, "cpuset isolated "); + std::string list; for (int i = 0; i < CPU_SETSIZE; i++) { if (CPU_ISSET(i, &cpuset)) { - rtapi_print_msg(RTAPI_MSG_INFO, "%i ", i); + list += " " + std::to_string(i); } } - rtapi_print_msg(RTAPI_MSG_INFO, "\n"); + rtapi_print_msg(RTAPI_MSG_INFO, "cpuset isolated%s", list.c_str()); int top = -1; for (int i = 0; i < CPU_SETSIZE; i++) { diff --git a/src/rtapi/uspace_rtapi_main.cc b/src/rtapi/uspace_rtapi_main.cc index 9946f3d16d1..337bce75d90 100644 --- a/src/rtapi/uspace_rtapi_main.cc +++ b/src/rtapi/uspace_rtapi_main.cc @@ -1708,9 +1708,23 @@ void default_rtapi_msg_handler(msg_level_t level, const char *fmt, va_list ap) { message_t m; m.level = level; vsnprintf(m.msg, sizeof(m.msg), fmt, ap); + if (rtapi_msg_needs_newline(level, m.msg)) { + size_t n = strlen(m.msg); + if (n == sizeof(m.msg) - 1) + n--; /* truncated: the newline replaces the last character */ + m.msg[n] = '\n'; + m.msg[n + 1] = '\0'; + } rtapi_msg_queue.push(m); } else { - vfprintf(level == RTAPI_MSG_ALL ? stdout : stderr, fmt, ap); + FILE *f = level == RTAPI_MSG_ALL ? stdout : stderr; + char *msg; + if (vasprintf(&msg, fmt, ap) < 0) + return; + fputs(msg, f); + if (rtapi_msg_needs_newline(level, msg)) + fputc('\n', f); + free(msg); } } diff --git a/src/rtapi/uspace_ulapi.c b/src/rtapi/uspace_ulapi.c index 0bec4b70257..65509783d3b 100644 --- a/src/rtapi/uspace_ulapi.c +++ b/src/rtapi/uspace_ulapi.c @@ -75,11 +75,13 @@ void rtapi_delay(long ns) { } void default_rtapi_msg_handler(msg_level_t level, const char *fmt, va_list ap) { - if(level == RTAPI_MSG_ALL) { - vfprintf(stdout, fmt, ap); - fflush(stdout); - } else { - vfprintf(stderr, fmt, ap); - fflush(stderr); - } + FILE *f = level == RTAPI_MSG_ALL ? stdout : stderr; + char *msg; + if (vasprintf(&msg, fmt, ap) < 0) + return; + fputs(msg, f); + if (rtapi_msg_needs_newline(level, msg)) + fputc('\n', f); + fflush(f); + free(msg); } diff --git a/tests/rtapi-msg-newline/.gitignore b/tests/rtapi-msg-newline/.gitignore new file mode 100644 index 00000000000..284bd9713dd --- /dev/null +++ b/tests/rtapi-msg-newline/.gitignore @@ -0,0 +1 @@ +halrun.out diff --git a/tests/rtapi-msg-newline/README b/tests/rtapi-msg-newline/README new file mode 100644 index 00000000000..d79375e4caf --- /dev/null +++ b/tests/rtapi-msg-newline/README @@ -0,0 +1,10 @@ +Every rtapi_print_msg() message is a line of its own, also when its +text lacks the newline (#4689). + +msgline prints two errors without a newline from its setup, which runs +in the main thread of rtapi_app, and an error, a warning and an info +message without a newline from its function, whose messages rtapi_app +queues and prints from another thread. Each must be a line of its own +in the output, and an error that ends in a newline must not get a +second one. rtapi_print() prints like printf: a line printed with it +in two parts must stay one line. diff --git a/tests/rtapi-msg-newline/expected b/tests/rtapi-msg-newline/expected new file mode 100644 index 00000000000..fd4e5f72093 --- /dev/null +++ b/tests/rtapi-msg-newline/expected @@ -0,0 +1,7 @@ +msgline: error at load +msgline: error at load +msgline: error in the thread +msgline: warning in the thread +msgline: info in the thread +msgline: error with its newline +msgline: print in parts diff --git a/tests/rtapi-msg-newline/halrun.err b/tests/rtapi-msg-newline/halrun.err new file mode 100644 index 00000000000..ac064432a8d --- /dev/null +++ b/tests/rtapi-msg-newline/halrun.err @@ -0,0 +1,14 @@ +Note: realtime scheduling unavailable (sched_setscheduler SCHED_FIFO: Operation not permitted). + Process capabilities: cap_sys_nice=no cap_ipc_lock=no. + Falling back to POSIX non-realtime. + Fix: 'sudo make setcap' (preferred) or 'sudo make setuid' on rtapi_app. + Override (testing only): set LINUXCNC_FORCE_REALTIME=1. +Note: Using POSIX non-realtime +No isolated CPU's found, expect some latency or set RTAPI_CPU_NUMBER to select CPU +msgline: error at load +msgline: error at load +msgline: error in the thread +msgline: warning in the thread +msgline: info in the thread +msgline: error with its newline +THREADS: hal_exit 7 0 0 diff --git a/tests/rtapi-msg-newline/msgline.comp b/tests/rtapi-msg-newline/msgline.comp new file mode 100644 index 00000000000..976bf82dd60 --- /dev/null +++ b/tests/rtapi-msg-newline/msgline.comp @@ -0,0 +1,31 @@ +component msgline "Test component for tests/rtapi-msg-newline: prints messages that lack a newline"; + +pin io bool print "set to print the messages from the realtime thread once; cleared by the function"; + +function _; +option extra_setup; +option period no; +license "GPL"; +author "Luca Toniolo"; +;; +EXTRA_SETUP() { + (void)__comp_inst; + (void)prefix; + (void)extra_arg; + rtapi_print_msg(RTAPI_MSG_ERR, "msgline: error at load"); + rtapi_print_msg(RTAPI_MSG_ERR, "msgline: error at load"); + return 0; +} + +FUNCTION(_) { + if (!print) + return; + print_set(0); + rtapi_print_msg(RTAPI_MSG_ERR, "msgline: error in the thread"); + rtapi_print_msg(RTAPI_MSG_WARN, "msgline: warning in the thread"); + rtapi_print_msg(RTAPI_MSG_INFO, "msgline: info in the thread"); + rtapi_print_msg(RTAPI_MSG_ERR, "msgline: error with its newline\n"); + /* rtapi_print() prints like printf */ + rtapi_print("msgline: print "); + rtapi_print("in parts\n"); +} diff --git a/tests/rtapi-msg-newline/msgline.hal b/tests/rtapi-msg-newline/msgline.hal new file mode 100644 index 00000000000..b985977d858 --- /dev/null +++ b/tests/rtapi-msg-newline/msgline.hal @@ -0,0 +1,6 @@ +loadrt threads name1=t period1=1000000 +loadrt msgline +addf msgline.0 t +debug 3 +start +loadusr -w ./print.py diff --git a/tests/rtapi-msg-newline/print.py b/tests/rtapi-msg-newline/print.py new file mode 100755 index 00000000000..3cd8457a458 --- /dev/null +++ b/tests/rtapi-msg-newline/print.py @@ -0,0 +1,15 @@ +#!/usr/bin/env python3 +# Make msgline print from the realtime thread and wait until it has. +# A wait that takes more than 10 s of wall time means the machine is +# dying; the test then aborts. + +import hal +import sys +import time + +hal.set_p("msgline.0.print", "1") +deadline = time.monotonic() + 10 +while hal.get_p("msgline.0.print"): + if time.monotonic() > deadline: + sys.exit("FAIL: the thread did not run msgline.0 in 10 s") + time.sleep(0.01) diff --git a/tests/rtapi-msg-newline/skip b/tests/rtapi-msg-newline/skip new file mode 100755 index 00000000000..14f65ebbd51 --- /dev/null +++ b/tests/rtapi-msg-newline/skip @@ -0,0 +1,4 @@ +#!/bin/sh +# Builds a realtime component with halcompile, which needs the build +# tree. Skip when testing installed packages. +[ -z "$SYSTEM_BUILD" ] diff --git a/tests/rtapi-msg-newline/test.sh b/tests/rtapi-msg-newline/test.sh new file mode 100755 index 00000000000..ba8d80bd391 --- /dev/null +++ b/tests/rtapi-msg-newline/test.sh @@ -0,0 +1,7 @@ +#!/bin/bash +set -e +halcompile --install msgline.comp >/dev/null +timeout -k 5 60 halrun -f msgline.hal > halrun.out 2> halrun.err +# the messages of msgline, each on a line of its own, then rtapi_print() +grep '^msgline:' halrun.err +grep '^msgline:' halrun.out