lib: optimize sprint_[long_]canframe()

Inspired by the SLCAN rework from Andre Naujoks this patch replaces the
extensive use of sprintf() with simple and efficient ASCII helpers.

valgrind/kcachegrind reported a cyle estimation of about 5.3 million cycles
before and about 1.5 million cycles after the rework (factor 3.55) for the
logging of the exact same CAN content.

Signed-off-by: Oliver Hartkopp <socketcan@hartkopp.net>
pull/1/head
Oliver Hartkopp 2014-05-05 22:13:38 +02:00
parent 31ccf45dc4
commit 4a68366bac
1 changed files with 67 additions and 25 deletions

92
lib.c
View File

@ -54,6 +54,29 @@
#define CANID_DELIM '#' #define CANID_DELIM '#'
#define DATA_SEPERATOR '.' #define DATA_SEPERATOR '.'
const char hex_asc_upper[] = "0123456789ABCDEF";
#define hex_asc_upper_lo(x) hex_asc_upper[((x) & 0x0F)]
#define hex_asc_upper_hi(x) hex_asc_upper[((x) & 0xF0) >> 4]
static inline void put_hex_byte(char *buf, __u8 byte)
{
buf[0] = hex_asc_upper_hi(byte);
buf[1] = hex_asc_upper_lo(byte);
}
static inline void _put_id(char *buf, int end_offset, canid_t id)
{
/* build 3 (SFF) or 8 (EFF) digit CAN identifier */
while (end_offset >= 0) {
buf[end_offset--] = hex_asc_upper[id & 0xF];
id >>= 4;
}
}
#define put_sff_id(buf, id) _put_id(buf, 2, id)
#define put_eff_id(buf, id) _put_id(buf, 7, id)
/* CAN DLC to real data length conversion helpers */ /* CAN DLC to real data length conversion helpers */
static const unsigned char dlc2len[] = {0, 1, 2, 3, 4, 5, 6, 7, static const unsigned char dlc2len[] = {0, 1, 2, 3, 4, 5, 6, 7,
@ -229,42 +252,46 @@ void sprint_canframe(char *buf , struct canfd_frame *cf, int sep, int maxdlen) {
int len = (cf->len > maxdlen) ? maxdlen : cf->len; int len = (cf->len > maxdlen) ? maxdlen : cf->len;
if (cf->can_id & CAN_ERR_FLAG) { if (cf->can_id & CAN_ERR_FLAG) {
sprintf(buf, "%08X#", cf->can_id & (CAN_ERR_MASK|CAN_ERR_FLAG)); put_eff_id(buf, cf->can_id & (CAN_ERR_MASK|CAN_ERR_FLAG));
buf[8] = '#';
offset = 9; offset = 9;
} else if (cf->can_id & CAN_EFF_FLAG) { } else if (cf->can_id & CAN_EFF_FLAG) {
sprintf(buf, "%08X#", cf->can_id & CAN_EFF_MASK); put_eff_id(buf, cf->can_id & CAN_EFF_MASK);
buf[8] = '#';
offset = 9; offset = 9;
} else { } else {
sprintf(buf, "%03X#", cf->can_id & CAN_SFF_MASK); put_sff_id(buf, cf->can_id & CAN_SFF_MASK);
buf[3] = '#';
offset = 4; offset = 4;
} }
/* standard CAN frames may have RTR enabled. There are no ERR frames with RTR */ /* standard CAN frames may have RTR enabled. There are no ERR frames with RTR */
if (maxdlen == CAN_MAX_DLEN && cf->can_id & CAN_RTR_FLAG) { if (maxdlen == CAN_MAX_DLEN && cf->can_id & CAN_RTR_FLAG) {
buf[offset++] = 'R';
/* print a given CAN 2.0B DLC if it's not zero */ /* print a given CAN 2.0B DLC if it's not zero */
if (cf->len && cf->len <= CAN_MAX_DLC) if (cf->len && cf->len <= CAN_MAX_DLC)
sprintf(buf+offset, "R%d", cf->len); buf[offset++] = hex_asc_upper[cf->len & 0xF];
else
sprintf(buf+offset, "R");
buf[offset] = 0;
return; return;
} }
if (maxdlen == CANFD_MAX_DLEN) { if (maxdlen == CANFD_MAX_DLEN) {
/* add CAN FD specific escape char and flags */ /* add CAN FD specific escape char and flags */
sprintf(buf+offset, "#%X", cf->flags & 0xF); buf[offset++] = '#';
offset += 2; buf[offset++] = hex_asc_upper[cf->flags & 0xF];
if (sep && len) if (sep && len)
sprintf(buf+offset++, "."); buf[offset++] = '.';
} }
for (i = 0; i < len; i++) { for (i = 0; i < len; i++) {
sprintf(buf+offset, "%02X", cf->data[i]); put_hex_byte(buf + offset, cf->data[i]);
offset += 2; offset += 2;
if (sep && (i+1 < len)) if (sep && (i+1 < len))
sprintf(buf+offset++, "."); buf[offset++] = '.';
} }
buf[offset] = 0;
} }
void fprint_long_canframe(FILE *stream , struct canfd_frame *cf, char *eol, int view, int maxdlen) { void fprint_long_canframe(FILE *stream , struct canfd_frame *cf, char *eol, int view, int maxdlen) {
@ -288,31 +315,41 @@ void sprint_long_canframe(char *buf , struct canfd_frame *cf, int view, int maxd
int i, j, dlen, offset; int i, j, dlen, offset;
int len = (cf->len > maxdlen)? maxdlen : cf->len; int len = (cf->len > maxdlen)? maxdlen : cf->len;
/* initialize space for CAN-ID and length information */
memset(buf, ' ', 15);
if (cf->can_id & CAN_ERR_FLAG) { if (cf->can_id & CAN_ERR_FLAG) {
sprintf(buf, "%08X ", cf->can_id & (CAN_ERR_MASK|CAN_ERR_FLAG)); put_eff_id(buf, cf->can_id & (CAN_ERR_MASK|CAN_ERR_FLAG));
offset = 10; offset = 10;
} else if (cf->can_id & CAN_EFF_FLAG) { } else if (cf->can_id & CAN_EFF_FLAG) {
sprintf(buf, "%08X ", cf->can_id & CAN_EFF_MASK); put_eff_id(buf, cf->can_id & CAN_EFF_MASK);
offset = 10; offset = 10;
} else { } else {
if (view & CANLIB_VIEW_INDENT_SFF) { if (view & CANLIB_VIEW_INDENT_SFF) {
sprintf(buf, " %03X ", cf->can_id & CAN_SFF_MASK); put_sff_id(buf + 5, cf->can_id & CAN_SFF_MASK);
offset = 10; offset = 10;
} else { } else {
sprintf(buf, "%03X ", cf->can_id & CAN_SFF_MASK); put_sff_id(buf, cf->can_id & CAN_SFF_MASK);
offset = 5; offset = 5;
} }
} }
/* The len value is sanitized by maxdlen (see above) */
if (maxdlen == CAN_MAX_DLEN) { if (maxdlen == CAN_MAX_DLEN) {
sprintf(buf+offset, " [%d] ", len); buf[offset + 1] = '[';
buf[offset + 2] = len + '0';
buf[offset + 3] = ']';
/* standard CAN frames may have RTR enabled */ /* standard CAN frames may have RTR enabled */
if (cf->can_id & CAN_RTR_FLAG) { if (cf->can_id & CAN_RTR_FLAG) {
sprintf(buf+offset+5, " remote request"); sprintf(buf+offset+5, " remote request");
return; return;
} }
} else { } else {
sprintf(buf+offset, "[%02d] ", len); buf[offset] = '[';
buf[offset + 1] = (len/10) + '0';
buf[offset + 2] = (len%10) + '0';
buf[offset + 3] = ']';
} }
offset += 5; offset += 5;
@ -331,24 +368,29 @@ void sprint_long_canframe(char *buf , struct canfd_frame *cf, int view, int maxd
buf[offset++] = (1<<j & cf->data[i])?'1':'0'; buf[offset++] = (1<<j & cf->data[i])?'1':'0';
} }
} }
buf[offset] = 0; /* terminate string */
} else { } else {
dlen = 3; /* _AA */ dlen = 3; /* _AA */
if (view & CANLIB_VIEW_SWAP) { if (view & CANLIB_VIEW_SWAP) {
for (i = len - 1; i >= 0; i--) { for (i = len - 1; i >= 0; i--) {
sprintf(buf+offset, "%c%02X", if (i == len-1)
(i == len-1)?' ':SWAP_DELIMITER, buf[offset++] = ' ';
cf->data[i]); else
offset += dlen; buf[offset++] = SWAP_DELIMITER;
put_hex_byte(buf + offset, cf->data[i]);
offset += 2;
} }
} else { } else {
for (i = 0; i < len; i++) { for (i = 0; i < len; i++) {
sprintf(buf+offset, " %02X", cf->data[i]); buf[offset++] = ' ';
offset += dlen; put_hex_byte(buf + offset, cf->data[i]);
offset += 2;
} }
} }
} }
buf[offset] = 0; /* terminate string */
/* /*
* The ASCII & ERRORFRAME output is put at a fixed len behind the data. * The ASCII & ERRORFRAME output is put at a fixed len behind the data.
* For now we support ASCII output only for payload length up to 8 bytes. * For now we support ASCII output only for payload length up to 8 bytes.