-
Notifications
You must be signed in to change notification settings - Fork 98
Add the option of hi-res (millisecond) timestamps #329
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
4a79052
48d7d79
a3d9e53
aa6bc9f
6bfba48
e53201d
c1c7550
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -53,7 +53,7 @@ _blackbox_reload(int32_t target) | |
| */ | ||
| static void | ||
| _blackbox_vlogger(int32_t target, | ||
| struct qb_log_callsite *cs, time_t timestamp, va_list ap) | ||
| struct qb_log_callsite *cs, struct timespec *timestamp, va_list ap) | ||
| { | ||
| size_t max_size; | ||
| size_t actual_size; | ||
|
|
@@ -69,7 +69,7 @@ _blackbox_vlogger(int32_t target, | |
|
|
||
| fn_size = strlen(cs->function) + 1; | ||
|
|
||
| actual_size = 4 * sizeof(uint32_t) + sizeof(uint8_t) + fn_size + sizeof(time_t); | ||
| actual_size = 4 * sizeof(uint32_t) + sizeof(uint8_t) + fn_size + sizeof(struct timespec); | ||
| max_size = actual_size + t->max_line_length; | ||
|
|
||
| chunk = qb_rb_chunk_alloc(t->instance, max_size); | ||
|
|
@@ -102,8 +102,8 @@ _blackbox_vlogger(int32_t target, | |
| chunk += fn_size; | ||
|
|
||
| /* timestamp */ | ||
| memcpy(chunk, ×tamp, sizeof(time_t)); | ||
| chunk += sizeof(time_t); | ||
| memcpy(chunk, timestamp, sizeof(struct timespec)); | ||
| chunk += sizeof(struct timespec); | ||
|
|
||
| /* log message length */ | ||
| msg_len_pt = chunk; | ||
|
|
@@ -161,19 +161,54 @@ qb_log_blackbox_open(struct qb_log_target *t) | |
| return 0; | ||
| } | ||
|
|
||
| /* | ||
| * This is designed to look as much like the ringbuffer header | ||
| * as possible so that we can distinguish an old RB dump | ||
| * from a new one with this header. | ||
| */ | ||
|
|
||
| struct _blackbox_file_header { | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Because this structure is used directly in fwrite it should be marked as a Yet another crazy idea (please ignore it if you feel like I had already too many of them :) ), what about storing it in network order so blackbox is "portable" between different endian systems (so it would be possible to share blackbox)?
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm not convinced it's worth the effort TBH.
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Understood (and kind of agree, we can always ask for text dump). |
||
| uint32_t word_size; | ||
| uint32_t read_pt; | ||
| uint32_t write_pt; | ||
| uint32_t version; | ||
| uint32_t hash; | ||
| } __attribute__((packed)); | ||
|
|
||
| /* Values we expect for a 'new' header */ | ||
| #define QB_BLACKBOX_HEADER_WORDSIZE 0 | ||
| #define QB_BLACKBOX_HEADER_READPT 0xCCBBCCBB | ||
| #define QB_BLACKBOX_HEADER_WRITEPT 0xBBCCBBCC | ||
| #define QB_BLACKBOX_HEADER_VERSION 2 | ||
| #define QB_BLACKBOX_HEADER_HASH 0 | ||
|
|
||
| ssize_t | ||
| qb_log_blackbox_write_to_file(const char *filename) | ||
| { | ||
| ssize_t written_size = 0; | ||
| struct qb_log_target *t; | ||
| struct _blackbox_file_header header; | ||
| int fd = open(filename, O_CREAT | O_RDWR, 0700); | ||
|
|
||
| if (fd < 0) { | ||
| return -errno; | ||
| } | ||
|
|
||
| /* Write header, so we know this is a 'new' format blackbox */ | ||
| header.word_size = QB_BLACKBOX_HEADER_WORDSIZE; | ||
| header.read_pt = QB_BLACKBOX_HEADER_READPT; | ||
| header.write_pt = QB_BLACKBOX_HEADER_WRITEPT; | ||
| header.version = QB_BLACKBOX_HEADER_VERSION; | ||
| header.hash = QB_BLACKBOX_HEADER_HASH; | ||
| written_size = write(fd, &header, sizeof(header)); | ||
| if (written_size < sizeof(header)) { | ||
| close(fd); | ||
| return written_size; | ||
| } | ||
|
|
||
| t = qb_log_target_get(QB_LOG_BLACKBOX); | ||
| if (t->instance) { | ||
| written_size = qb_rb_write_to_file(t->instance, fd); | ||
| written_size += qb_rb_write_to_file(t->instance, fd); | ||
| } else { | ||
| written_size = -ENOENT; | ||
| } | ||
|
|
@@ -182,25 +217,51 @@ qb_log_blackbox_write_to_file(const char *filename) | |
| return written_size; | ||
| } | ||
|
|
||
| void | ||
| int | ||
| qb_log_blackbox_print_from_file(const char *bb_filename) | ||
| { | ||
| qb_ringbuffer_t *instance; | ||
| ssize_t bytes_read; | ||
| int max_size = 2 * QB_LOG_MAX_LEN; | ||
| char *chunk; | ||
| int fd; | ||
| int err = 0; | ||
| int saved_errno; | ||
| struct _blackbox_file_header header; | ||
| int have_timespecs = 0; | ||
| char time_buf[64]; | ||
|
|
||
| fd = open(bb_filename, 0); | ||
| if (fd < 0) { | ||
| saved_errno = errno; | ||
| qb_util_perror(LOG_ERR, "qb_log_blackbox_print_from_file"); | ||
| return; | ||
| return -saved_errno; | ||
| } | ||
|
|
||
| /* Read the header. If it looks like one of ours then | ||
| we know we have hi-res timestamps */ | ||
| err = read(fd, &header, sizeof(header)); | ||
| if (err < sizeof(header)) { | ||
| saved_errno = errno; | ||
| close(fd); | ||
| return -saved_errno; | ||
| } | ||
|
|
||
| if (header.word_size == QB_BLACKBOX_HEADER_WORDSIZE && | ||
| header.read_pt == QB_BLACKBOX_HEADER_READPT && | ||
| header.write_pt == QB_BLACKBOX_HEADER_WRITEPT && | ||
| header.version == QB_BLACKBOX_HEADER_VERSION && | ||
| header.hash == QB_BLACKBOX_HEADER_HASH) { | ||
| have_timespecs = 1; | ||
| } else { | ||
| lseek(fd, 0, SEEK_SET); | ||
| } | ||
|
|
||
|
|
||
| instance = qb_rb_create_from_file(fd, 0); | ||
| close(fd); | ||
| if (instance == NULL) { | ||
| return; | ||
| return -EIO; | ||
| } | ||
| chunk = malloc(max_size); | ||
|
|
||
|
|
@@ -212,7 +273,8 @@ qb_log_blackbox_print_from_file(const char *bb_filename) | |
| uint32_t fn_size; | ||
| char *function; | ||
| uint32_t len; | ||
| time_t timestamp; | ||
| struct timespec timestamp; | ||
| time_t time_sec; | ||
| uint32_t msg_len; | ||
| struct tm *tm; | ||
| char message[QB_LOG_MAX_LEN]; | ||
|
|
@@ -221,10 +283,12 @@ qb_log_blackbox_print_from_file(const char *bb_filename) | |
|
|
||
| if (bytes_read >= 0 && bytes_read < BB_MIN_ENTRY_SIZE) { | ||
| printf("ERROR Corrupt file: blackbox header too small.\n"); | ||
| err = -1; | ||
| goto cleanup; | ||
| } else if (bytes_read < 0) { | ||
| errno = -bytes_read; | ||
| perror("ERROR: qb_rb_chunk_read failed"); | ||
| err = -EIO; | ||
| goto cleanup; | ||
| } | ||
| ptr = chunk; | ||
|
|
@@ -246,12 +310,14 @@ qb_log_blackbox_print_from_file(const char *bb_filename) | |
| if ((fn_size + BB_MIN_ENTRY_SIZE) > bytes_read) { | ||
| #ifndef S_SPLINT_S | ||
| printf("ERROR Corrupt file: fn_size way too big %" PRIu32 "\n", fn_size); | ||
| err = -EIO; | ||
| #endif /* S_SPLINT_S */ | ||
| goto cleanup; | ||
| } | ||
| if (fn_size <= 0) { | ||
| #ifndef S_SPLINT_S | ||
| printf("ERROR Corrupt file: fn_size negative %" PRIu32 "\n", fn_size); | ||
| err = -EIO; | ||
| #endif /* S_SPLINT_S */ | ||
| goto cleanup; | ||
| } | ||
|
|
@@ -261,22 +327,32 @@ qb_log_blackbox_print_from_file(const char *bb_filename) | |
| ptr += fn_size; | ||
|
|
||
| /* timestamp size & content */ | ||
| memcpy(×tamp, ptr, sizeof(time_t)); | ||
| ptr += sizeof(time_t); | ||
| tm = localtime(×tamp); | ||
| if (have_timespecs) { | ||
| memcpy(×tamp, ptr, sizeof(struct timespec)); | ||
| ptr += sizeof(struct timespec); | ||
| time_sec = timestamp.tv_sec; | ||
| } else { | ||
| memcpy(&time_sec, ptr, sizeof(time_t)); | ||
| ptr += sizeof(time_t); | ||
| timestamp.tv_nsec = 0LL; | ||
| } | ||
|
|
||
| tm = localtime(&time_sec); | ||
| if (tm) { | ||
| (void)strftime(time_buf, | ||
| sizeof(time_buf), "%b %d %T", | ||
| tm); | ||
| int slen = strftime(time_buf, | ||
| sizeof(time_buf), "%b %d %T", | ||
| tm); | ||
| snprintf(time_buf+slen, sizeof(time_buf - slen), ".%03lld", timestamp.tv_nsec/QB_TIME_NS_IN_MSEC); | ||
| } else { | ||
| snprintf(time_buf, sizeof(time_buf), "%ld", | ||
| (long int)timestamp); | ||
| (long int)time_sec); | ||
| } | ||
| /* message length */ | ||
| memcpy(&msg_len, ptr, sizeof(uint32_t)); | ||
| if (msg_len > QB_LOG_MAX_LEN || msg_len <= 0) { | ||
| #ifndef S_SPLINT_S | ||
| printf("ERROR Corrupt file: msg_len out of bounds %" PRIu32 "\n", msg_len); | ||
| err = -EIO; | ||
| #endif /* S_SPLINT_S */ | ||
| goto cleanup; | ||
| } | ||
|
|
@@ -302,4 +378,5 @@ qb_log_blackbox_print_from_file(const char *bb_filename) | |
| cleanup: | ||
| qb_rb_close(instance); | ||
| free(chunk); | ||
| return err; | ||
| } | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actually pacemaker uses these to implement its PCMK_trace_blackbox option (which dumps a blackbox when a message at a specified function+line is logged). Would it be possible to add these as new methods, and somehow wrap the old ones around the new ones? I can use the new constant to know which signature to use going forward, but it would be nice if newer libqb could be used with existing pacemaker versions.
Awesome that we're getting the capability, btw.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'll have a look into that. The callback hooks quite low into the libqb code but it's doable I think.
It probably mean 2 API calls for registering a custom logger and expanding the target struct to contain 2 logger functions. Or I could overload vlogger if I'm feeling evil.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thinking closely about this, I don't think there's any point in doing it. Partly because this is for libqb 2.0, but mainly because we've already remove the linker-section based logging callsites, so that means anything linked against libqb1 is not going to work anyway.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I wasn't thinking of already linked executables so much as someone upgrading libqb and rebuilding but not upgrading pacemaker, but that case may not be worth a lot of effort.