[perf] improve initial indexing times

This commit is contained in:
Timothy Stack 2018-10-17 07:03:33 -07:00
parent 2e10ca09d0
commit 2589345e5c
27 changed files with 1550 additions and 256 deletions

View File

@ -56,23 +56,6 @@ AS_VAR_IF([enable_profiling], [yes],
AC_ARG_VAR(SFTP_TEST_URL) AC_ARG_VAR(SFTP_TEST_URL)
AC_ARG_ENABLE([profiling],
AS_HELP_STRING([--enable-profiling],
[Compile with gprof(1) profiling support]))
AC_MSG_CHECKING(gprof(4) profiling support)
AS_VAR_IF([enable_profiling], [yes],
[CFLAGS="$CFLAGS -pg -gstabs"
CXXFLAGS="$CXXFLAGS -pg -gstabs"
LDFLAGS="$LDFLAGS -pg"],
[enable_profiling=no]dnl
)
AC_MSG_RESULT($enable_profiling)
AC_SUBST(CFLAGS_PG)
AC_PROG_INSTALL AC_PROG_INSTALL
AC_PROG_RANLIB AC_PROG_RANLIB
AM_PROG_AR AM_PROG_AR
@ -134,7 +117,7 @@ AS_CASE(["$host_os"],
) )
) )
AC_CHECK_HEADERS(execinfo.h pty.h util.h zlib.h bzlib.h libutil.h sys/ttydefaults.h) AC_CHECK_HEADERS(execinfo.h pty.h util.h zlib.h bzlib.h libutil.h sys/ttydefaults.h x86intrin.h)
LNAV_WITH_JEMALLOC LNAV_WITH_JEMALLOC

View File

@ -19,6 +19,7 @@ set(diag_STAT_SRCS
hist_source.cc hist_source.cc
hotkeys.cc hotkeys.cc
intern_string.cc intern_string.cc
is_utf8.cc
json-extension-functions.cc json-extension-functions.cc
json_op.cc json_op.cc
json_ptr.cc json_ptr.cc
@ -116,6 +117,7 @@ set(diag_STAT_SRCS
hotkeys.hh hotkeys.hh
init-sql.hh init-sql.hh
intern_string.hh intern_string.hh
is_utf8.hh
k_merge_tree.h k_merge_tree.h
log_data_helper.hh log_data_helper.hh
log_data_table.hh log_data_table.hh
@ -136,6 +138,7 @@ set(diag_STAT_SRCS
relative_time.hh relative_time.hh
sequence_sink.hh sequence_sink.hh
shlex.hh shlex.hh
simdutf8check.h
spectro_source.hh spectro_source.hh
strong_int.hh strong_int.hh
sysclip.hh sysclip.hh

View File

@ -96,7 +96,7 @@ time_fmts.cc: ptimec
if HAVE_RE2C if HAVE_RE2C
%.cc: %.re %.cc: %.re
$(RE2C_V)$(RE2C_CMD) -8 -o $@ $< $(RE2C_V)$(RE2C_CMD) --tags -8 -o $@ $<
$(REC2_V)test $@ -ef $(srcdir)/$*.cc || cp $@ $(srcdir)/$*.cc $(REC2_V)test $@ -ef $(srcdir)/$*.cc || cp $@ $(srcdir)/$*.cc
endif endif
@ -173,6 +173,7 @@ noinst_HEADERS = \
init.sql \ init.sql \
init-sql.hh \ init-sql.hh \
intern_string.hh \ intern_string.hh \
is_utf8.hh \
json_op.hh \ json_op.hh \
json_ptr.hh \ json_ptr.hh \
k_merge_tree.h \ k_merge_tree.h \
@ -189,6 +190,7 @@ noinst_HEADERS = \
log_format.hh \ log_format.hh \
log_format_loader.hh \ log_format_loader.hh \
log_level.hh \ log_level.hh \
log_level_re.re \
log_search_table.hh \ log_search_table.hh \
logfile.hh \ logfile.hh \
logfile_sub_source.hh \ logfile_sub_source.hh \
@ -216,6 +218,7 @@ noinst_HEADERS = \
session_data.hh \ session_data.hh \
shared_buffer.hh \ shared_buffer.hh \
shlex.hh \ shlex.hh \
simdutf8check.h \
spectro_source.hh \ spectro_source.hh \
sql_util.hh \ sql_util.hh \
sqlite-extension-func.hh \ sqlite-extension-func.hh \
@ -284,6 +287,7 @@ libdiag_a_SOURCES = \
hist_source.cc \ hist_source.cc \
hotkeys.cc \ hotkeys.cc \
intern_string.cc \ intern_string.cc \
is_utf8.cc \
json-extension-functions.cc \ json-extension-functions.cc \
json_op.cc \ json_op.cc \
json_ptr.cc \ json_ptr.cc \
@ -297,6 +301,7 @@ libdiag_a_SOURCES = \
log_format.cc \ log_format.cc \
log_format_loader.cc \ log_format_loader.cc \
log_level.cc \ log_level.cc \
log_level_re.cc \
logfile.cc \ logfile.cc \
logfile_sub_source.cc \ logfile_sub_source.cc \
network-extension-functions.cc \ network-extension-functions.cc \
@ -389,11 +394,12 @@ ptimec_LDADD =
DISTCLEANFILES = \ DISTCLEANFILES = \
data_scanner_re.cc \ data_scanner_re.cc \
default-config-json.c \
default-log-formats-json.c \
dump-pid-sh.c \ dump-pid-sh.c \
help.c \ help.c \
init-sql.c \ init-sql.c \
default-log-formats-json.c \ log_level_re.cc \
default-config-json.c \
time_fmts.cc \ time_fmts.cc \
xterm-palette.c xterm-palette.c

View File

@ -433,7 +433,7 @@ private:
hist_value b_values[HT__MAX]; hist_value b_values[HT__MAX];
}; };
static const unsigned int BLOCK_SIZE = 100; static const int64_t BLOCK_SIZE = 100;
struct bucket_block { struct bucket_block {
bucket_block() : bb_used(0) { bucket_block() : bb_used(0) {
@ -445,7 +445,7 @@ private:
}; };
bucket_t &find_bucket(int64_t index) { bucket_t &find_bucket(int64_t index) {
struct bucket_block &bb = this->hs_blocks[index / this->BLOCK_SIZE]; struct bucket_block &bb = this->hs_blocks[index / BLOCK_SIZE];
unsigned int intra_block_index = index % BLOCK_SIZE; unsigned int intra_block_index = index % BLOCK_SIZE;
bb.bb_used = std::max(intra_block_index, bb.bb_used); bb.bb_used = std::max(intra_block_index, bb.bb_used);
this->hs_line_count = std::max(this->hs_line_count, index + 1); this->hs_line_count = std::max(this->hs_line_count, index + 1);

298
src/is_utf8.cc Normal file
View File

@ -0,0 +1,298 @@
/*
* is_utf8 is distributed under the following terms:
*
* Copyright (c) 2013 Palard Julien. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
#include "config.h"
#include "is_utf8.hh"
/*
Check if the given unsigned char * is a valid utf-8 sequence.
Return value :
If the string is valid utf-8, 0 is returned.
Else the position, starting from 1, is returned.
Source:
http://www.unicode.org/versions/Unicode7.0.0/UnicodeStandard-7.0.pdf
page 124, 3.9 "Unicode Encoding Forms", "UTF-8"
Table 3-7. Well-Formed UTF-8 Byte Sequences
-----------------------------------------------------------------------------
| Code Points | First Byte | Second Byte | Third Byte | Fourth Byte |
| U+0000..U+007F | 00..7F | | | |
| U+0080..U+07FF | C2..DF | 80..BF | | |
| U+0800..U+0FFF | E0 | A0..BF | 80..BF | |
| U+1000..U+CFFF | E1..EC | 80..BF | 80..BF | |
| U+D000..U+D7FF | ED | 80..9F | 80..BF | |
| U+E000..U+FFFF | EE..EF | 80..BF | 80..BF | |
| U+10000..U+3FFFF | F0 | 90..BF | 80..BF | 80..BF |
| U+40000..U+FFFFF | F1..F3 | 80..BF | 80..BF | 80..BF |
| U+100000..U+10FFFF | F4 | 80..8F | 80..BF | 80..BF |
-----------------------------------------------------------------------------
Returns the first erroneous byte position, and give in
`faulty_bytes` the number of actually existing bytes taking part in this error.
*/
ssize_t is_utf8(unsigned char *str, size_t len, const char **message, int *faulty_bytes)
{
size_t i = 0;
*message = nullptr;
*faulty_bytes = 0;
while (i < len)
{
if (str[i] == '\n') {
*message = nullptr;
return i;
}
if (str[i] <= 0x7F) /* 00..7F */
{
i += 1;
}
else if (str[i] >= 0xC2 && str[i] <= 0xDF) /* C2..DF 80..BF */
{
if (i + 1 < len) /* Expect a 2nd byte */
{
if (str[i + 1] < 0x80 || str[i + 1] > 0xBF)
{
*message = "After a first byte between C2 and DF, expecting a 2nd byte between 80 and BF";
*faulty_bytes = 2;
return i;
}
}
else
{
*message = "After a first byte between C2 and DF, expecting a 2nd byte.";
*faulty_bytes = 1;
return i;
}
i += 2;
}
else if (str[i] == 0xE0) /* E0 A0..BF 80..BF */
{
if (i + 2 < len) /* Expect a 2nd and 3rd byte */
{
if (str[i + 1] < 0xA0 || str[i + 1] > 0xBF)
{
*message = "After a first byte of E0, expecting a 2nd byte between A0 and BF.";
*faulty_bytes = 2;
return i;
}
if (str[i + 2] < 0x80 || str[i + 2] > 0xBF)
{
*message = "After a first byte of E0, expecting a 3nd byte between 80 and BF.";
*faulty_bytes = 3;
return i;
}
}
else
{
*message = "After a first byte of E0, expecting two following bytes.";
*faulty_bytes = 1;
return i;
}
i += 3;
}
else if (str[i] >= 0xE1 && str[i] <= 0xEC) /* E1..EC 80..BF 80..BF */
{
if (i + 2 < len) /* Expect a 2nd and 3rd byte */
{
if (str[i + 1] < 0x80 || str[i + 1] > 0xBF)
{
*message = "After a first byte between E1 and EC, expecting the 2nd byte between 80 and BF.";
*faulty_bytes = 2;
return i;
}
if (str[i + 2] < 0x80 || str[i + 2] > 0xBF)
{
*message = "After a first byte between E1 and EC, expecting the 3rd byte between 80 and BF.";
*faulty_bytes = 3;
return i;
}
}
else
{
*message = "After a first byte between E1 and EC, expecting two following bytes.";
*faulty_bytes = 1;
return i;
}
i += 3;
}
else if (str[i] == 0xED) /* ED 80..9F 80..BF */
{
if (i + 2 < len) /* Expect a 2nd and 3rd byte */
{
if (str[i + 1] < 0x80 || str[i + 1] > 0x9F)
{
*message = "After a first byte of ED, expecting 2nd byte between 80 and 9F.";
*faulty_bytes = 2;
return i;
}
if (str[i + 2] < 0x80 || str[i + 2] > 0xBF)
{
*message = "After a first byte of ED, expecting 3rd byte between 80 and BF.";
*faulty_bytes = 3;
return i;
}
}
else
{
*message = "After a first byte of ED, expecting two following bytes.";
*faulty_bytes = 1;
return i;
}
i += 3;
}
else if (str[i] >= 0xEE && str[i] <= 0xEF) /* EE..EF 80..BF 80..BF */
{
if (i + 2 < len) /* Expect a 2nd and 3rd byte */
{
if (str[i + 1] < 0x80 || str[i + 1] > 0xBF)
{
*message = "After a first byte between EE and EF, expecting 2nd byte between 80 and BF.";
*faulty_bytes = 2;
return i;
}
if (str[i + 2] < 0x80 || str[i + 2] > 0xBF)
{
*message = "After a first byte between EE and EF, expecting 3rd byte between 80 and BF.";
*faulty_bytes = 3;
return i;
}
}
else
{
*message = "After a first byte between EE and EF, two following bytes.";
*faulty_bytes = 1;
return i;
}
i += 3;
}
else if (str[i] == 0xF0) /* F0 90..BF 80..BF 80..BF */
{
if (i + 3 < len) /* Expect a 2nd, 3rd 3th byte */
{
if (str[i + 1] < 0x90 || str[i + 1] > 0xBF)
{
*message = "After a first byte of F0, expecting 2nd byte between 90 and BF.";
*faulty_bytes = 2;
return i;
}
if (str[i + 2] < 0x80 || str[i + 2] > 0xBF)
{
*message = "After a first byte of F0, expecting 3rd byte between 80 and BF.";
*faulty_bytes = 3;
return i;
}
if (str[i + 3] < 0x80 || str[i + 3] > 0xBF)
{
*message = "After a first byte of F0, expecting 4th byte between 80 and BF.";
*faulty_bytes = 4;
return i;
}
}
else
{
*message = "After a first byte of F0, expecting three following bytes.";
*faulty_bytes = 1;
return i;
}
i += 4;
}
else if (str[i] >= 0xF1 && str[i] <= 0xF3) /* F1..F3 80..BF 80..BF 80..BF */
{
if (i + 3 < len) /* Expect a 2nd, 3rd 3th byte */
{
if (str[i + 1] < 0x80 || str[i + 1] > 0xBF)
{
*message = "After a first byte of F1, F2, or F3, expecting a 2nd byte between 80 and BF.";
*faulty_bytes = 2;
return i;
}
if (str[i + 2] < 0x80 || str[i + 2] > 0xBF)
{
*message = "After a first byte of F1, F2, or F3, expecting a 3rd byte between 80 and BF.";
*faulty_bytes = 3;
return i;
}
if (str[i + 3] < 0x80 || str[i + 3] > 0xBF)
{
*message = "After a first byte of F1, F2, or F3, expecting a 4th byte between 80 and BF.";
*faulty_bytes = 4;
return i;
}
}
else
{
*message = "After a first byte of F1, F2, or F3, expecting three following bytes.";
*faulty_bytes = 1;
return i;
}
i += 4;
}
else if (str[i] == 0xF4) /* F4 80..8F 80..BF 80..BF */
{
if (i + 3 < len) /* Expect a 2nd, 3rd 3th byte */
{
if (str[i + 1] < 0x80 || str[i + 1] > 0x8F)
{
*message = "After a first byte of F4, expecting 2nd byte between 80 and 8F.";
*faulty_bytes = 2;
return i;
}
if (str[i + 2] < 0x80 || str[i + 2] > 0xBF)
{
*message = "After a first byte of F4, expecting 3rd byte between 80 and BF.";
*faulty_bytes = 3;
return i;
}
if (str[i + 3] < 0x80 || str[i + 3] > 0xBF)
{
*message = "After a first byte of F4, expecting 4th byte between 80 and BF.";
*faulty_bytes = 4;
return i;
}
}
else
{
*message = "After a first byte of F4, expecting three following bytes.";
*faulty_bytes = 1;
return i;
}
i += 4;
}
else
{
*message = "Expecting bytes in the following ranges: 00..7F C2..F4.";
*faulty_bytes = 1;
return i;
}
}
return -1;
}

36
src/is_utf8.hh Normal file
View File

@ -0,0 +1,36 @@
/*
* is_utf8 is distributed under the following terms:
*
* Copyright (c) 2013 Palard Julien. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
#ifndef _IS_UTF8_H
#define _IS_UTF8_H
#include <sys/types.h>
#include <stdlib.h>
ssize_t is_utf8(unsigned char *str, size_t len, const char **message, int *faulty_bytes);
#endif /* _IS_UTF8_H */

View File

@ -43,6 +43,11 @@
#include <set> #include <set>
#ifdef HAVE_X86INTRIN_H
#include "simdutf8check.h"
#endif
#include "is_utf8.hh"
#include "lnav_util.hh" #include "lnav_util.hh"
#include "line_buffer.hh" #include "line_buffer.hh"
@ -497,6 +502,7 @@ bool line_buffer::read_line(off_t &offset, line_value &lv, bool include_delim)
lv.lv_len = 0; lv.lv_len = 0;
lv.lv_partial = false; lv.lv_partial = false;
lv.lv_valid_utf = true;
while (!retval) { while (!retval) {
char *line_start, *lf; char *line_start, *lf;
@ -505,7 +511,30 @@ bool line_buffer::read_line(off_t &offset, line_value &lv, bool include_delim)
/* Find the data in the cache and */ /* Find the data in the cache and */
line_start = this->get_range(offset, lv.lv_len); line_start = this->get_range(offset, lv.lv_len);
/* ... look for the end-of-line or end-of-file. */ /* ... look for the end-of-line or end-of-file. */
if (((lf = (char *)memchr(line_start, '\n', lv.lv_len)) != NULL) || ssize_t utf8_end = -1;
#ifdef HAVE_X86INTRIN_H
if (!validate_utf8_fast(line_start, lv.lv_len, &utf8_end)) {
lv.lv_valid_utf = false;
}
#else
{
const char *msg;
int faulty_bytes;
utf8_end = is_utf8(line_start, lv.lv_len, &msg, &faulty_bytes);
if (msg != nullptr) {
lv.lv_valid_utf = false;
}
}
#endif
if (utf8_end >= 0) {
lf = line_start + utf8_end;
} else {
lf = nullptr;
}
if (lf != nullptr ||
(lv.lv_len >= MAX_LINE_BUFFER_SIZE) || (lv.lv_len >= MAX_LINE_BUFFER_SIZE) ||
(request_size == MAX_LINE_BUFFER_SIZE) || (request_size == MAX_LINE_BUFFER_SIZE) ||
((request_size > lv.lv_len) && lv.lv_len > 0)) { ((request_size > lv.lv_len) && lv.lv_len > 0)) {
@ -604,6 +633,22 @@ bool line_buffer::read_line(off_t &offset_inout, shared_buffer_ref &sbr, line_va
sbr.disown(); sbr.disown();
if ((retval = this->read_line(offset_inout, *lv))) { if ((retval = this->read_line(offset_inout, *lv))) {
sbr.share(this->lb_share_manager, lv->lv_start, lv->lv_len); sbr.share(this->lb_share_manager, lv->lv_start, lv->lv_len);
if (!lv->lv_valid_utf) {
auto *bits = (unsigned char *) sbr.get_writable_data();
const char *msg;
int faulty_bytes;
while (true) {
ssize_t utf8_end = is_utf8(bits, sbr.length(), &msg, &faulty_bytes);
if (msg == nullptr) {
break;
}
for (int lpc = 0; lpc < faulty_bytes; lpc++) {
bits[utf8_end + lpc] = '?';
}
}
}
} }
return retval; return retval;

View File

@ -48,6 +48,7 @@ struct line_value {
char *lv_start; char *lv_start;
size_t lv_len; size_t lv_len;
bool lv_partial; bool lv_partial;
bool lv_valid_utf;
void terminate() { void terminate() {
this->lv_start[this->lv_len] = '\0'; this->lv_start[this->lv_len] = '\0';

View File

@ -365,15 +365,15 @@ bool setup_logline_table(exec_context &ec)
iter.second->get_foreign_keys(db_key_names); iter.second->get_foreign_keys(db_key_names);
} }
db_key_names.push_back("device"); db_key_names.emplace_back("device");
db_key_names.push_back("inode"); db_key_names.emplace_back("inode");
db_key_names.push_back("rowid"); db_key_names.emplace_back("rowid");
db_key_names.push_back("st_dev"); db_key_names.emplace_back("st_dev");
db_key_names.push_back("st_ino"); db_key_names.emplace_back("st_ino");
db_key_names.push_back("st_mode"); db_key_names.emplace_back("st_mode");
db_key_names.push_back("st_rdev"); db_key_names.emplace_back("st_rdev");
db_key_names.push_back("st_uid"); db_key_names.emplace_back("st_uid");
db_key_names.push_back("st_gid"); db_key_names.emplace_back("st_gid");
stable_sort(db_key_names.begin(), db_key_names.end()); stable_sort(db_key_names.begin(), db_key_names.end());
@ -490,7 +490,7 @@ class textfile_callback {
public: public:
textfile_callback() : force(false), front_file(NULL), front_top(-1) { }; textfile_callback() : force(false), front_file(NULL), front_top(-1) { };
void closed_file(shared_ptr<logfile> lf) { void closed_file(const shared_ptr<logfile> &lf) {
log_info("closed text file: %s", lf->get_filename().c_str()); log_info("closed text file: %s", lf->get_filename().c_str());
if (!lf->is_valid_filename()) { if (!lf->is_valid_filename()) {
lnav_data.ld_file_names.erase(lf->get_filename()); lnav_data.ld_file_names.erase(lf->get_filename());
@ -503,7 +503,7 @@ public:
regenerate_unique_file_names(); regenerate_unique_file_names();
}; };
void promote_file(shared_ptr<logfile> lf) { void promote_file(const shared_ptr<logfile> &lf) {
if (lnav_data.ld_log_source.insert_file(lf)) { if (lnav_data.ld_log_source.insert_file(lf)) {
force = true; force = true;
@ -523,7 +523,7 @@ public:
} }
}; };
void scanned_file(shared_ptr<logfile> lf) { void scanned_file(const shared_ptr<logfile> &lf) {
if (!lnav_data.ld_files_to_front.empty() && if (!lnav_data.ld_files_to_front.empty() &&
lnav_data.ld_files_to_front.front().first == lnav_data.ld_files_to_front.front().first ==
lf->get_filename()) { lf->get_filename()) {

View File

@ -179,11 +179,11 @@ const char *log_format::log_scanf(const char *line,
va_start(args, tv_out); va_start(args, tv_out);
pi.reset(line, 0, len); pi.reset(line, 0, len);
if (!fmt[curr_fmt].pcre.match(pc, pi)) { if (!fmt[curr_fmt].pcre.match(pc, pi, PCRE_NO_UTF8_CHECK)) {
retval = NULL; retval = NULL;
} }
else { else {
pcre_context::capture_t *ts = pc["timestamp"]; pcre_context::capture_t *ts = pc[fmt[curr_fmt].pf_timestamp_index];
for (auto &iter : pc) { for (auto &iter : pc) {
pcre_context::capture_t *cap = va_arg( pcre_context::capture_t *cap = va_arg(

View File

@ -790,13 +790,14 @@ protected:
struct pcre_format { struct pcre_format {
pcre_format(const char *regex) : name(regex), pcre(regex) { pcre_format(const char *regex) : name(regex), pcre(regex) {
this->pf_timestamp_index = this->pcre.name_index("timestamp");
}; };
pcre_format() : name(NULL), pcre("") { }; pcre_format() : name(NULL), pcre("") { };
const char *name; const char *name;
pcrepp pcre; pcrepp pcre;
int pf_timestamp_index{-1};
}; };
static bool next_format(pcre_format *fmt, int &index, int &locked_index); static bool next_format(pcre_format *fmt, int &index, int &locked_index);
@ -1140,7 +1141,7 @@ public:
log_level_t convert_level(const pcre_input &pi, pcre_context::capture_t *level_cap) const { log_level_t convert_level(const pcre_input &pi, pcre_context::capture_t *level_cap) const {
log_level_t retval = LEVEL_INFO; log_level_t retval = LEVEL_INFO;
if (level_cap != NULL && level_cap->is_valid()) { if (level_cap != nullptr && level_cap->is_valid()) {
pcre_context_static<128> pc_level; pcre_context_static<128> pc_level;
pcre_input pi_level(pi.get_substr_start(level_cap), pcre_input pi_level(pi.get_substr_start(level_cap),
0, 0,
@ -1149,11 +1150,9 @@ public:
if (this->elf_level_patterns.empty()) { if (this->elf_level_patterns.empty()) {
retval = string2level(pi_level.get_string(), level_cap->length()); retval = string2level(pi_level.get_string(), level_cap->length());
} else { } else {
for (auto iter = this->elf_level_patterns.begin(); for (const auto &elf_level_pattern : this->elf_level_patterns) {
iter != this->elf_level_patterns.end(); if (elf_level_pattern.second.lp_pcre->match(pc_level, pi_level)) {
++iter) { retval = elf_level_pattern.first;
if (iter->second.lp_pcre->match(pc_level, pi_level)) {
retval = iter->first;
break; break;
} }
} }

View File

@ -165,7 +165,7 @@ class generic_log_format : public log_format {
this->check_for_new_year(dst, log_time, log_tv); this->check_for_new_year(dst, log_time, log_tv);
} }
dst.push_back(logline(offset, log_tv, level_val)); dst.emplace_back(offset, log_tv, level_val);
return SCAN_MATCH; return SCAN_MATCH;
} }

View File

@ -50,36 +50,6 @@ const char *level_names[LEVEL__MAX + 1] = {
NULL NULL
}; };
static pcrepp LEVEL_RE(
"(?i)(TRACE|DEBUG\\d*|INFO|NOTICE|STATS|WARN(?:ING)?|ERR(?:OR)?|CRITICAL|SEVERE|FATAL)");
log_level_t string2level(const char *levelstr, ssize_t len, bool exact)
{
log_level_t retval = LEVEL_UNKNOWN;
if (len == (ssize_t)-1) {
len = strlen(levelstr);
}
if (((len == 1) || ((len > 1) && (levelstr[1] == ' '))) &&
(retval = abbrev2level(levelstr, 1)) != LEVEL_UNKNOWN) {
return retval;
}
pcre_input pi(levelstr, 0, len);
pcre_context_static<10> pc;
if (LEVEL_RE.match(pc, pi)) {
auto iter = pc.begin();
if (!exact || pc[0]->c_begin == 0) {
retval = abbrev2level(pi.get_substr_start(iter),
pi.get_substr_len(iter));
}
}
return retval;
}
log_level_t abbrev2level(const char *levelstr, ssize_t len) log_level_t abbrev2level(const char *levelstr, ssize_t len)
{ {
if (len == 0 || levelstr[0] == '\0') { if (len == 0 || levelstr[0] == '\0') {

590
src/log_level_re.cc Normal file
View File

@ -0,0 +1,590 @@
/* Generated by re2c 1.1.1 on Tue Oct 16 06:58:50 2018 */
#line 1 "../../lnav2/src/log_level_re.re"
/**
* Copyright (c) 2018, Timothy Stack
*
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
* * Neither the name of Timothy Stack nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ''AS IS'' AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "config.h"
#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include "log_level.hh"
log_level_t string2level(const char *levelstr, ssize_t len, bool exact)
{
log_level_t retval = LEVEL_UNKNOWN;
if (len == (ssize_t)-1) {
len = strlen(levelstr);
}
if (((len == 1) || ((len > 1) && (levelstr[1] == ' '))) &&
(retval = abbrev2level(levelstr, 1)) != LEVEL_UNKNOWN) {
return retval;
}
# define YYCTYPE unsigned char
# define RET(tok) { \
return tok; \
}
const YYCTYPE *YYCURSOR = (const unsigned char *) levelstr;
const YYCTYPE *YYLIMIT = (const unsigned char *) levelstr + len;
const YYCTYPE *YYMARKER = YYCURSOR;
const YYCTYPE *debug_level = nullptr;
# define YYPEEK() (YYCURSOR < YYLIMIT ? *YYCURSOR : 0)
# define YYSKIP() ++YYCURSOR
# define YYBACKUP() YYMARKER = YYCURSOR
# define YYRESTORE() YYCURSOR = YYMARKER
# define YYSTAGP(x) x = YYCURSOR - 1
const unsigned char *yyt1;
loop:
#line 73 "log_level_re.cc"
{
YYCTYPE yych;
unsigned int yyaccept = 0;
yych = YYPEEK ();
switch (yych) {
case 0x00: goto yy2;
case 'C':
case 'c': goto yy6;
case 'D':
case 'd': goto yy7;
case 'E':
case 'e': goto yy8;
case 'F':
case 'f': goto yy9;
case 'I':
case 'i': goto yy10;
case 'N':
case 'n': goto yy11;
case 'S':
case 's': goto yy12;
case 'T':
case 't': goto yy13;
case 'W':
case 'w': goto yy14;
default: goto yy4;
}
yy2:
YYSKIP ();
#line 75 "../../lnav2/src/log_level_re.re"
{ RET(LEVEL_UNKNOWN); }
#line 104 "log_level_re.cc"
yy4:
YYSKIP ();
yy5:
#line 102 "../../lnav2/src/log_level_re.re"
{ goto loop; }
#line 110 "log_level_re.cc"
yy6:
yyaccept = 0;
YYSKIP ();
YYBACKUP ();
yych = YYPEEK ();
switch (yych) {
case 'R':
case 'r': goto yy15;
default: goto yy5;
}
yy7:
yyaccept = 0;
YYSKIP ();
YYBACKUP ();
yych = YYPEEK ();
switch (yych) {
case 'E':
case 'e': goto yy17;
default: goto yy5;
}
yy8:
yyaccept = 0;
YYSKIP ();
YYBACKUP ();
yych = YYPEEK ();
switch (yych) {
case 'R':
case 'r': goto yy18;
default: goto yy5;
}
yy9:
yyaccept = 0;
YYSKIP ();
YYBACKUP ();
yych = YYPEEK ();
switch (yych) {
case 'A':
case 'a': goto yy19;
default: goto yy5;
}
yy10:
yyaccept = 0;
YYSKIP ();
YYBACKUP ();
yych = YYPEEK ();
switch (yych) {
case 'N':
case 'n': goto yy20;
default: goto yy5;
}
yy11:
yyaccept = 0;
YYSKIP ();
YYBACKUP ();
yych = YYPEEK ();
switch (yych) {
case 'O':
case 'o': goto yy21;
default: goto yy5;
}
yy12:
yyaccept = 0;
YYSKIP ();
YYBACKUP ();
yych = YYPEEK ();
switch (yych) {
case 'E':
case 'e': goto yy22;
case 'T':
case 't': goto yy23;
default: goto yy5;
}
yy13:
yyaccept = 0;
YYSKIP ();
YYBACKUP ();
yych = YYPEEK ();
switch (yych) {
case 'R':
case 'r': goto yy24;
default: goto yy5;
}
yy14:
yyaccept = 0;
YYSKIP ();
YYBACKUP ();
yych = YYPEEK ();
switch (yych) {
case 'A':
case 'a': goto yy25;
default: goto yy5;
}
yy15:
YYSKIP ();
yych = YYPEEK ();
switch (yych) {
case 'I':
case 'i': goto yy26;
default: goto yy16;
}
yy16:
YYRESTORE ();
switch (yyaccept) {
case 0: goto yy5;
case 1: goto yy29;
default: goto yy48;
}
yy17:
YYSKIP ();
yych = YYPEEK ();
switch (yych) {
case 'B':
case 'b': goto yy27;
default: goto yy16;
}
yy18:
YYSKIP ();
yych = YYPEEK ();
switch (yych) {
case 'R':
case 'r': goto yy28;
default: goto yy16;
}
yy19:
YYSKIP ();
yych = YYPEEK ();
switch (yych) {
case 'T':
case 't': goto yy30;
default: goto yy16;
}
yy20:
YYSKIP ();
yych = YYPEEK ();
switch (yych) {
case 'F':
case 'f': goto yy31;
default: goto yy16;
}
yy21:
YYSKIP ();
yych = YYPEEK ();
switch (yych) {
case 'T':
case 't': goto yy32;
default: goto yy16;
}
yy22:
YYSKIP ();
yych = YYPEEK ();
switch (yych) {
case 'V':
case 'v': goto yy33;
default: goto yy16;
}
yy23:
YYSKIP ();
yych = YYPEEK ();
switch (yych) {
case 'A':
case 'a': goto yy34;
default: goto yy16;
}
yy24:
YYSKIP ();
yych = YYPEEK ();
switch (yych) {
case 'A':
case 'a': goto yy35;
default: goto yy16;
}
yy25:
YYSKIP ();
yych = YYPEEK ();
switch (yych) {
case 'R':
case 'r': goto yy36;
default: goto yy16;
}
yy26:
YYSKIP ();
yych = YYPEEK ();
switch (yych) {
case 'T':
case 't': goto yy37;
default: goto yy16;
}
yy27:
YYSKIP ();
yych = YYPEEK ();
switch (yych) {
case 'U':
case 'u': goto yy38;
default: goto yy16;
}
yy28:
yyaccept = 1;
YYSKIP ();
YYBACKUP ();
yych = YYPEEK ();
switch (yych) {
case 'O':
case 'o': goto yy39;
default: goto yy29;
}
yy29:
#line 98 "../../lnav2/src/log_level_re.re"
{ RET(LEVEL_ERROR); }
#line 319 "log_level_re.cc"
yy30:
YYSKIP ();
yych = YYPEEK ();
switch (yych) {
case 'A':
case 'a': goto yy40;
default: goto yy16;
}
yy31:
YYSKIP ();
yych = YYPEEK ();
switch (yych) {
case 'O':
case 'o': goto yy41;
default: goto yy16;
}
yy32:
YYSKIP ();
yych = YYPEEK ();
switch (yych) {
case 'I':
case 'i': goto yy43;
default: goto yy16;
}
yy33:
YYSKIP ();
yych = YYPEEK ();
switch (yych) {
case 'E':
case 'e': goto yy44;
default: goto yy16;
}
yy34:
YYSKIP ();
yych = YYPEEK ();
switch (yych) {
case 'T':
case 't': goto yy45;
default: goto yy16;
}
yy35:
YYSKIP ();
yych = YYPEEK ();
switch (yych) {
case 'C':
case 'c': goto yy46;
default: goto yy16;
}
yy36:
YYSKIP ();
yych = YYPEEK ();
switch (yych) {
case 'N':
case 'n': goto yy47;
default: goto yy16;
}
yy37:
YYSKIP ();
yych = YYPEEK ();
switch (yych) {
case 'I':
case 'i': goto yy49;
default: goto yy16;
}
yy38:
YYSKIP ();
yych = YYPEEK ();
switch (yych) {
case 'G':
case 'g': goto yy50;
default: goto yy16;
}
yy39:
YYSKIP ();
yych = YYPEEK ();
switch (yych) {
case 'R':
case 'r': goto yy52;
default: goto yy16;
}
yy40:
YYSKIP ();
yych = YYPEEK ();
switch (yych) {
case 'L':
case 'l': goto yy53;
default: goto yy16;
}
yy41:
YYSKIP ();
#line 94 "../../lnav2/src/log_level_re.re"
{ RET(LEVEL_INFO); }
#line 412 "log_level_re.cc"
yy43:
YYSKIP ();
yych = YYPEEK ();
switch (yych) {
case 'C':
case 'c': goto yy55;
default: goto yy16;
}
yy44:
YYSKIP ();
yych = YYPEEK ();
switch (yych) {
case 'R':
case 'r': goto yy56;
default: goto yy16;
}
yy45:
YYSKIP ();
yych = YYPEEK ();
switch (yych) {
case 'S':
case 's': goto yy57;
default: goto yy16;
}
yy46:
YYSKIP ();
yych = YYPEEK ();
switch (yych) {
case 'E':
case 'e': goto yy59;
default: goto yy16;
}
yy47:
yyaccept = 2;
YYSKIP ();
YYBACKUP ();
yych = YYPEEK ();
switch (yych) {
case 'I':
case 'i': goto yy61;
default: goto yy48;
}
yy48:
#line 97 "../../lnav2/src/log_level_re.re"
{ RET(LEVEL_WARNING); }
#line 458 "log_level_re.cc"
yy49:
YYSKIP ();
yych = YYPEEK ();
switch (yych) {
case 'C':
case 'c': goto yy62;
default: goto yy16;
}
yy50:
YYSKIP ();
yych = YYPEEK ();
switch (yych) {
case '2':
case '3':
case '4':
case '5': goto yy63;
default:
YYSTAGP (yyt1);
goto yy51;
}
yy51:
debug_level = yyt1;
#line 77 "../../lnav2/src/log_level_re.re"
{
if (debug_level == nullptr) {
RET(LEVEL_DEBUG);
}
switch (*debug_level) {
case '2':
RET(LEVEL_DEBUG2);
case '3':
RET(LEVEL_DEBUG3);
case '4':
RET(LEVEL_DEBUG4);
case '5':
RET(LEVEL_DEBUG5);
default:
RET(LEVEL_DEBUG);
}
}
#line 499 "log_level_re.cc"
yy52:
YYSKIP ();
goto yy29;
yy53:
YYSKIP ();
#line 101 "../../lnav2/src/log_level_re.re"
{ RET(LEVEL_FATAL); }
#line 507 "log_level_re.cc"
yy55:
YYSKIP ();
yych = YYPEEK ();
switch (yych) {
case 'E':
case 'e': goto yy64;
default: goto yy16;
}
yy56:
YYSKIP ();
yych = YYPEEK ();
switch (yych) {
case 'E':
case 'e': goto yy66;
default: goto yy16;
}
yy57:
YYSKIP ();
#line 96 "../../lnav2/src/log_level_re.re"
{ RET(LEVEL_STATS); }
#line 528 "log_level_re.cc"
yy59:
YYSKIP ();
#line 76 "../../lnav2/src/log_level_re.re"
{ RET(LEVEL_TRACE); }
#line 533 "log_level_re.cc"
yy61:
YYSKIP ();
yych = YYPEEK ();
switch (yych) {
case 'N':
case 'n': goto yy68;
default: goto yy16;
}
yy62:
YYSKIP ();
yych = YYPEEK ();
switch (yych) {
case 'A':
case 'a': goto yy69;
default: goto yy16;
}
yy63:
YYSKIP ();
YYSTAGP (yyt1);
goto yy51;
yy64:
YYSKIP ();
#line 95 "../../lnav2/src/log_level_re.re"
{ RET(LEVEL_INFO); }
#line 558 "log_level_re.cc"
yy66:
YYSKIP ();
#line 100 "../../lnav2/src/log_level_re.re"
{ RET(LEVEL_CRITICAL); }
#line 563 "log_level_re.cc"
yy68:
YYSKIP ();
yych = YYPEEK ();
switch (yych) {
case 'G':
case 'g': goto yy70;
default: goto yy16;
}
yy69:
YYSKIP ();
yych = YYPEEK ();
switch (yych) {
case 'L':
case 'l': goto yy71;
default: goto yy16;
}
yy70:
YYSKIP ();
goto yy48;
yy71:
YYSKIP ();
#line 99 "../../lnav2/src/log_level_re.re"
{ RET(LEVEL_CRITICAL); }
#line 587 "log_level_re.cc"
}
#line 104 "../../lnav2/src/log_level_re.re"
}

105
src/log_level_re.re Normal file
View File

@ -0,0 +1,105 @@
/**
* Copyright (c) 2018, Timothy Stack
*
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
* * Neither the name of Timothy Stack nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ''AS IS'' AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "config.h"
#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include "log_level.hh"
log_level_t string2level(const char *levelstr, ssize_t len, bool exact)
{
log_level_t retval = LEVEL_UNKNOWN;
if (len == (ssize_t)-1) {
len = strlen(levelstr);
}
if (((len == 1) || ((len > 1) && (levelstr[1] == ' '))) &&
(retval = abbrev2level(levelstr, 1)) != LEVEL_UNKNOWN) {
return retval;
}
# define YYCTYPE unsigned char
# define RET(tok) { \
return tok; \
}
const YYCTYPE *YYCURSOR = (const unsigned char *) levelstr;
const YYCTYPE *YYLIMIT = (const unsigned char *) levelstr + len;
const YYCTYPE *YYMARKER = YYCURSOR;
const YYCTYPE *debug_level = nullptr;
# define YYPEEK() (YYCURSOR < YYLIMIT ? *YYCURSOR : 0)
# define YYSKIP() ++YYCURSOR
# define YYBACKUP() YYMARKER = YYCURSOR
# define YYRESTORE() YYCURSOR = YYMARKER
# define YYSTAGP(x) x = YYCURSOR - 1
/*!stags:re2c format = 'const unsigned char *@@;'; */
loop:
/*!re2c
re2c:yyfill:enable = 0;
re2c:flags:input = custom;
EOF = "\x00";
EOF { RET(LEVEL_UNKNOWN); }
'trace' { RET(LEVEL_TRACE); }
'debug' [2-5]? @debug_level {
if (debug_level == nullptr) {
RET(LEVEL_DEBUG);
}
switch (*debug_level) {
case '2':
RET(LEVEL_DEBUG2);
case '3':
RET(LEVEL_DEBUG3);
case '4':
RET(LEVEL_DEBUG4);
case '5':
RET(LEVEL_DEBUG5);
default:
RET(LEVEL_DEBUG);
}
}
'info' { RET(LEVEL_INFO); }
'notice' { RET(LEVEL_INFO); }
'stats' { RET(LEVEL_STATS); }
'warn'|'warning' { RET(LEVEL_WARNING); }
'err'|'error' { RET(LEVEL_ERROR); }
'critical' { RET(LEVEL_CRITICAL); }
'severe' { RET(LEVEL_CRITICAL); }
'fatal' { RET(LEVEL_FATAL); }
* { goto loop; }
*/
}

View File

@ -56,7 +56,7 @@ static const size_t INDEX_RESERVE_INCREMENT = 1024;
logfile::logfile(const string &filename, logfile_open_options &loo) logfile::logfile(const string &filename, logfile_open_options &loo)
: lf_filename(filename) : lf_filename(filename)
{ {
require(filename.size() > 0); require(!filename.empty());
memset(&this->lf_stat, 0, sizeof(this->lf_stat)); memset(&this->lf_stat, 0, sizeof(this->lf_stat));
if (loo.loo_fd == -1) { if (loo.loo_fd == -1) {
@ -107,7 +107,7 @@ logfile::~logfile()
{ {
} }
bool logfile::exists(void) const bool logfile::exists() const
{ {
struct stat st; struct stat st;
@ -210,14 +210,7 @@ bool logfile::process_prefix(off_t offset, shared_buffer_ref &sbr)
if (latest < second_to_last) { if (latest < second_to_last) {
if (this->lf_format->lf_time_ordered) { if (this->lf_format->lf_time_ordered) {
log_debug( this->lf_out_of_time_order_count += 1;
"%s:%d: out-of-time-order line detected %d.%03d < %d.%03d",
this->lf_filename.c_str(),
prescan_size,
latest.get_time(),
latest.get_millis(),
second_to_last.get_time(),
second_to_last.get_millis());
for (size_t lpc = prescan_size; for (size_t lpc = prescan_size;
lpc < this->lf_index.size(); lpc++) { lpc < this->lf_index.size(); lpc++) {
logline &line_to_update = this->lf_index[lpc]; logline &line_to_update = this->lf_index[lpc];
@ -416,6 +409,13 @@ logfile::rebuild_result_t logfile::rebuild_index()
this->lf_index_time = st.st_mtime; this->lf_index_time = st.st_mtime;
} }
if (this->lf_out_of_time_order_count) {
log_info("Detected %d out-of-time-order lines in file: %s",
this->lf_out_of_time_order_count,
this->lf_filename.c_str());
this->lf_out_of_time_order_count = 0;
}
return retval; return retval;
} }

View File

@ -1,3 +1,5 @@
#include <utility>
/** /**
* Copyright (c) 2007-2012, Timothy Stack * Copyright (c) 2007-2012, Timothy Stack
* *
@ -109,8 +111,8 @@ public:
class error { class error {
public: public:
error(const std::string &filename, int err) error(std::string filename, int err)
: e_filename(filename), : e_filename(std::move(filename)),
e_err(err) { }; e_err(err) { };
std::string e_filename; std::string e_filename;
@ -205,26 +207,24 @@ public:
else { else {
timeradd(&old_time, &tv, &this->lf_time_offset); timeradd(&old_time, &tv, &this->lf_time_offset);
} }
for (iterator iter = this->begin(); for (auto &iter : *this) {
iter != this->end();
++iter) {
struct timeval curr, diff, new_time; struct timeval curr, diff, new_time;
curr = iter->get_timeval(); curr = iter.get_timeval();
timersub(&curr, &old_time, &diff); timersub(&curr, &old_time, &diff);
timeradd(&diff, &this->lf_time_offset, &new_time); timeradd(&diff, &this->lf_time_offset, &new_time);
iter->set_time(new_time); iter.set_time(new_time);
} }
this->lf_sort_needed = true; this->lf_sort_needed = true;
}; };
void clear_time_offset(void) { void clear_time_offset() {
struct timeval tv = { 0, 0 }; struct timeval tv = { 0, 0 };
this->adjust_content_time(-1, tv); this->adjust_content_time(-1, tv);
}; };
bool is_time_adjusted(void) const { bool is_time_adjusted() const {
return (this->lf_time_offset.tv_sec != 0 || return (this->lf_time_offset.tv_sec != 0 ||
this->lf_time_offset.tv_usec != 0); this->lf_time_offset.tv_usec != 0);
} }
@ -392,9 +392,9 @@ public:
}; };
/** Check the invariants for this object. */ /** Check the invariants for this object. */
bool invariant(void) bool invariant()
{ {
require(this->lf_filename.size() > 0); require(!this->lf_filename.empty());
return true; return true;
} }
@ -435,6 +435,7 @@ protected:
logfile_observer *lf_logfile_observer{nullptr}; logfile_observer *lf_logfile_observer{nullptr};
size_t lf_longest_line{0}; size_t lf_longest_line{0};
text_format_t lf_text_format{TF_UNKNOWN}; text_format_t lf_text_format{TF_UNKNOWN};
uint32_t lf_out_of_time_order_count{0};
}; };
class logline_observer { class logline_observer {

View File

@ -541,7 +541,7 @@ bool logfile_sub_source::rebuild_index(bool force)
{ {
iterator iter; iterator iter;
size_t total_lines = 0; size_t total_lines = 0;
bool retval, full_sort = false; bool retval, full_sort = false, new_order = false;
int file_count = 0; int file_count = 0;
force = force || this->lss_force_rebuild; force = force || this->lss_force_rebuild;
@ -584,6 +584,7 @@ bool logfile_sub_source::rebuild_index(bool force)
case logfile::RR_NEW_ORDER: case logfile::RR_NEW_ORDER:
retval = true; retval = true;
force = true; force = true;
new_order = true;
break; break;
} }
file_count += 1; file_count += 1;
@ -644,7 +645,10 @@ bool logfile_sub_source::rebuild_index(bool force)
} }
} }
sort(this->lss_index.begin(), this->lss_index.end(), line_cmper); if (new_order || (this->lss_files.size() > 1)) {
sort(this->lss_index.begin(), this->lss_index.end(),
line_cmper);
}
} else { } else {
kmerge_tree_c<logline, logfile_data, logfile::iterator> merge( kmerge_tree_c<logline, logfile_data, logfile::iterator> merge(
file_count); file_count);

View File

@ -85,7 +85,7 @@ void pcrepp::find_captures(const char *pattern)
in_class = true; in_class = true;
break; break;
case '(': case '(':
cap_in_progress.push_back(pcre_context::capture(lpc, lpc)); cap_in_progress.emplace_back(lpc, lpc);
break; break;
case ')': { case ')': {
if (!cap_in_progress.empty()) { if (!cap_in_progress.empty()) {

View File

@ -373,8 +373,12 @@ public:
const char *errptr; const char *errptr;
int eoff; int eoff;
if (!(options & PCRE_NEVER_UTF)) {
options |= PCRE_UTF8;
}
if ((this->p_code = pcre_compile(pattern, if ((this->p_code = pcre_compile(pattern,
options | PCRE_UTF8, options,
&errptr, &errptr,
&eoff, &eoff,
NULL)) == NULL) { NULL)) == NULL) {
@ -421,16 +425,15 @@ public:
}; };
pcre_named_capture::iterator named_begin() const { pcre_named_capture::iterator named_begin() const {
return pcre_named_capture::iterator(this->p_named_entries, return {this->p_named_entries, static_cast<size_t>(this->p_name_len)};
this->p_name_len);
}; };
pcre_named_capture::iterator named_end() const { pcre_named_capture::iterator named_end() const {
char *ptr = (char *)this->p_named_entries; char *ptr = (char *)this->p_named_entries;
ptr += this->p_named_count * this->p_name_len; ptr += this->p_named_count * this->p_name_len;
return pcre_named_capture::iterator((pcre_named_capture *)ptr, return {(pcre_named_capture *)ptr,
this->p_name_len); static_cast<size_t>(this->p_name_len)};
}; };
const std::vector<pcre_context::capture> &captures() const { const std::vector<pcre_context::capture> &captures() const {
@ -565,6 +568,7 @@ public:
return length; return length;
}; };
// #undef PCRE_STUDY_JIT_COMPILE
#ifdef PCRE_STUDY_JIT_COMPILE #ifdef PCRE_STUDY_JIT_COMPILE
static pcre_jit_stack *jit_stack(void); static pcre_jit_stack *jit_stack(void);

237
src/simdutf8check.h Normal file
View File

@ -0,0 +1,237 @@
/**
* https://github.com/lemire/fastvalidate-utf-8
*/
#ifndef SIMDUTF8CHECK_H
#define SIMDUTF8CHECK_H
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
#include <x86intrin.h>
#include "lnav_log.hh"
#ifdef __cplusplus
extern "C" {
#endif
/*
* legal utf-8 byte sequence
* http://www.unicode.org/versions/Unicode6.0.0/ch03.pdf - page 94
*
* Code Points 1st 2s 3s 4s
* U+0000..U+007F 00..7F
* U+0080..U+07FF C2..DF 80..BF
* U+0800..U+0FFF E0 A0..BF 80..BF
* U+1000..U+CFFF E1..EC 80..BF 80..BF
* U+D000..U+D7FF ED 80..9F 80..BF
* U+E000..U+FFFF EE..EF 80..BF 80..BF
* U+10000..U+3FFFF F0 90..BF 80..BF 80..BF
* U+40000..U+FFFFF F1..F3 80..BF 80..BF 80..BF
* U+100000..U+10FFFF F4 80..8F 80..BF 80..BF
*
*/
// all byte values must be no larger than 0xF4
static inline void checkSmallerThan0xF4(__m128i current_bytes,
__m128i *has_error)
{
// unsigned, saturates to 0 below max
*has_error = _mm_or_si128(*has_error,
_mm_subs_epu8(current_bytes,
_mm_set1_epi8(0xF4)));
}
static inline __m128i continuationLengths(__m128i high_nibbles)
{
return _mm_shuffle_epi8(
_mm_setr_epi8(1, 1, 1, 1, 1, 1, 1, 1, // 0xxx (ASCII)
0, 0, 0, 0, // 10xx (continuation)
2, 2, // 110x
3, // 1110
4), // 1111, next should be 0 (not checked here)
high_nibbles);
}
static inline __m128i carryContinuations(__m128i initial_lengths,
__m128i previous_carries)
{
__m128i right1 = _mm_subs_epu8(
_mm_alignr_epi8(initial_lengths, previous_carries, 16 - 1),
_mm_set1_epi8(1));
__m128i sum = _mm_add_epi8(initial_lengths, right1);
__m128i right2 = _mm_subs_epu8(
_mm_alignr_epi8(sum, previous_carries, 16 - 2),
_mm_set1_epi8(2));
return _mm_add_epi8(sum, right2);
}
static inline void checkContinuations(__m128i initial_lengths,
__m128i carries,
__m128i *has_error)
{
// overlap || underlap
// carry > length && length > 0 || !(carry > length) && !(length > 0)
// (carries > length) == (lengths > 0)
__m128i overunder = _mm_cmpeq_epi8(
_mm_cmpgt_epi8(carries, initial_lengths),
_mm_cmpgt_epi8(initial_lengths, _mm_setzero_si128()));
*has_error = _mm_or_si128(*has_error, overunder);
}
// when 0xED is found, next byte must be no larger than 0x9F
// when 0xF4 is found, next byte must be no larger than 0x8F
// next byte must be continuation, ie sign bit is set, so signed < is ok
static inline void checkFirstContinuationMax(__m128i current_bytes,
__m128i off1_current_bytes,
__m128i *has_error)
{
__m128i maskED = _mm_cmpeq_epi8(off1_current_bytes, _mm_set1_epi8(0xED));
__m128i maskF4 = _mm_cmpeq_epi8(off1_current_bytes, _mm_set1_epi8(0xF4));
__m128i badfollowED = _mm_and_si128(
_mm_cmpgt_epi8(current_bytes, _mm_set1_epi8(0x9F)),
maskED);
__m128i badfollowF4 = _mm_and_si128(
_mm_cmpgt_epi8(current_bytes, _mm_set1_epi8(0x8F)),
maskF4);
*has_error = _mm_or_si128(*has_error,
_mm_or_si128(badfollowED, badfollowF4));
}
// map off1_hibits => error condition
// hibits off1 cur
// C => < C2 && true
// E => < E1 && < A0
// F => < F1 && < 90
// else false && false
static inline void checkOverlong(__m128i current_bytes,
__m128i off1_current_bytes,
__m128i hibits,
__m128i previous_hibits,
__m128i *has_error)
{
__m128i off1_hibits = _mm_alignr_epi8(hibits, previous_hibits, 16 - 1);
__m128i initial_mins = _mm_shuffle_epi8(
_mm_setr_epi8(-128, -128, -128, -128, -128, -128, -128, -128,
-128, -128, -128, -128, // 10xx => false
0xC2, -128, // 110x
0xE1, // 1110
0xF1),
off1_hibits);
__m128i initial_under = _mm_cmpgt_epi8(initial_mins, off1_current_bytes);
__m128i second_mins = _mm_shuffle_epi8(
_mm_setr_epi8(-128, -128, -128, -128, -128, -128, -128, -128,
-128, -128, -128, -128, // 10xx => false
127, 127, // 110x => true
0xA0, // 1110
0x90),
off1_hibits);
__m128i second_under = _mm_cmpgt_epi8(second_mins, current_bytes);
*has_error = _mm_or_si128(*has_error,
_mm_and_si128(initial_under, second_under));
}
struct processed_utf_bytes {
__m128i rawbytes;
__m128i high_nibbles;
__m128i carried_continuations;
};
static inline void count_nibbles(__m128i bytes,
struct processed_utf_bytes *answer)
{
answer->rawbytes = bytes;
answer->high_nibbles = _mm_and_si128(_mm_srli_epi16(bytes, 4),
_mm_set1_epi8(0x0F));
}
// check whether the current bytes are valid UTF-8
// at the end of the function, previous gets updated
static struct processed_utf_bytes
checkUTF8Bytes(__m128i current_bytes, struct processed_utf_bytes *previous,
__m128i *has_error)
{
struct processed_utf_bytes pb;
count_nibbles(current_bytes, &pb);
checkSmallerThan0xF4(current_bytes, has_error);
__m128i initial_lengths = continuationLengths(pb.high_nibbles);
pb.carried_continuations = carryContinuations(
initial_lengths,
previous->carried_continuations);
checkContinuations(initial_lengths, pb.carried_continuations, has_error);
__m128i off1_current_bytes =
_mm_alignr_epi8(pb.rawbytes, previous->rawbytes, 16 - 1);
checkFirstContinuationMax(current_bytes, off1_current_bytes,
has_error);
checkOverlong(current_bytes, off1_current_bytes,
pb.high_nibbles, previous->high_nibbles, has_error);
return pb;
}
static bool validate_utf8_fast(const char *src, size_t len, ssize_t *len_out)
{
size_t i = 0, orig_len = len;
__m128i has_error = _mm_setzero_si128();
__m128i lfchars = _mm_set1_epi8('\n');
__m128i lfresult = _mm_setzero_si128();
struct processed_utf_bytes previous = {.rawbytes = _mm_setzero_si128(),
.high_nibbles = _mm_setzero_si128(),
.carried_continuations = _mm_setzero_si128()};
if (len >= 16) {
for (; i <= len - 16; i += 16) {
__m128i current_bytes = _mm_loadu_si128(
(const __m128i *) (src + i));
previous = checkUTF8Bytes(current_bytes, &previous, &has_error);
lfresult = _mm_cmpeq_epi8(current_bytes, lfchars);
if (_mm_movemask_epi8(lfresult)) {
for (; src[i] != '\n'; i++) {
}
len = i;
break;
}
}
}
//last part
if (i < len) {
char buffer[16];
memset(buffer, 0, 16);
memcpy(buffer, src + i, len - i);
__m128i current_bytes = _mm_loadu_si128((const __m128i *) (buffer));
previous = checkUTF8Bytes(current_bytes, &previous, &has_error);
for (; i < len && src[i] != '\n'; i++) {
}
} else {
has_error = _mm_or_si128(_mm_cmpgt_epi8(previous.carried_continuations,
_mm_setr_epi8(9, 9, 9, 9, 9, 9,
9, 9, 9, 9, 9, 9,
9, 9, 9, 1)),
has_error);
}
if (i < orig_len && src[i] == '\n') {
*len_out = i;
}
return _mm_testz_si128(has_error, has_error);
}
#ifdef __cplusplus
}
#endif
#endif

View File

@ -335,6 +335,7 @@ dist_noinst_DATA = \
textfile_json_one_line.0 \ textfile_json_one_line.0 \
textfile_quoted_json.0 \ textfile_quoted_json.0 \
toplevel.lnav \ toplevel.lnav \
UTF-8-test.txt \
view_colors_output.0 \ view_colors_output.0 \
vt52_curses_input.0 \ vt52_curses_input.0 \
vt52_curses_input.1 \ vt52_curses_input.1 \

BIN
test/UTF-8-test.txt Normal file

Binary file not shown.

View File

@ -52,136 +52,139 @@ using namespace std;
int main(int argc, char *argv[]) int main(int argc, char *argv[])
{ {
int c, rnd_iters = 5, retval = EXIT_SUCCESS; int c, rnd_iters = 5, retval = EXIT_SUCCESS;
vector<pair<int, int> > index; vector<pair<int, int> > index;
auto_fd fd = STDIN_FILENO; auto_fd fd = STDIN_FILENO;
int offseti = 0; int offseti = 0;
off_t offset = 0; off_t offset = 0;
struct stat st; int count = 1000;
struct stat st;
while ((c = getopt(argc, argv, "o:i:n:")) != -1) {
switch (c) {
case 'o':
if (sscanf(optarg, "%d", &offseti) != 1) {
fprintf(stderr,
"error: offset is not an integer -- %s\n",
optarg);
retval = EXIT_FAILURE;
} else {
offset = offseti;
}
break;
case 'n':
if (sscanf(optarg, "%d", &rnd_iters) != 1) {
fprintf(stderr,
"error: offset is not an integer -- %s\n",
optarg);
retval = EXIT_FAILURE;
}
break;
case 'i':
{
FILE *file;
if ((file = fopen(optarg, "r")) == NULL) { while ((c = getopt(argc, argv, "o:i:n:c:")) != -1) {
perror("open"); switch (c) {
retval = EXIT_FAILURE; case 'o':
if (sscanf(optarg, "%d", &offseti) != 1) {
fprintf(stderr,
"error: offset is not an integer -- %s\n",
optarg);
retval = EXIT_FAILURE;
} else {
offset = offseti;
}
break;
case 'n':
if (sscanf(optarg, "%d", &rnd_iters) != 1) {
fprintf(stderr,
"error: offset is not an integer -- %s\n",
optarg);
retval = EXIT_FAILURE;
}
break;
case 'c':
if (sscanf(optarg, "%d", &count) != 1) {
fprintf(stderr,
"error: count is not an integer -- %s\n",
optarg);
retval = EXIT_FAILURE;
}
break;
case 'i': {
FILE *file;
if ((file = fopen(optarg, "r")) == NULL) {
perror("open");
retval = EXIT_FAILURE;
} else {
int line_number = 1, line_offset;
while (fscanf(file, "%d", &line_offset) == 1) {
index.push_back(
make_pair(line_number, line_offset));
line_number += 1;
}
fclose(file);
file = NULL;
}
}
break;
default:
retval = EXIT_FAILURE;
break;
} }
else {
int line_number = 1, line_offset;
while (fscanf(file, "%d", &line_offset) == 1) {
index.push_back(
make_pair(line_number, line_offset));
line_number += 1;
}
fclose(file);
file = NULL;
}
}
break;
default:
retval = EXIT_FAILURE;
break;
} }
}
argc -= optind; argc -= optind;
argv += optind; argv += optind;
if (retval != EXIT_SUCCESS) { if (retval != EXIT_SUCCESS) {
} } else if ((argc == 0) && (index.size() > 0)) {
else if ((argc == 0) && (index.size() > 0)) { fprintf(stderr, "error: cannot randomize stdin\n");
fprintf(stderr, "error: cannot randomize stdin\n");
retval = EXIT_FAILURE;
}
else if ((argc > 0) && (fd = open(argv[0], O_RDONLY)) == -1) {
perror("open");
retval = EXIT_FAILURE;
}
else if ((argc > 0) && (fstat(fd, &st) == -1)) {
perror("fstat");
retval = EXIT_FAILURE;
}
else {
try {
off_t last_offset = offset;
line_buffer lb;
line_value lv;
char *maddr;
lb.set_fd(fd);
if (index.size() == 0) {
while (lb.read_line(offset, lv)) {
lv.terminate();
printf("%s", lv.lv_start);
if ((off_t)(last_offset + lv.lv_len) < offset)
printf("\n");
last_offset = offset;
}
}
else if ((maddr = (char *)mmap(NULL,
st.st_size,
PROT_READ,
MAP_FILE | MAP_PRIVATE,
lb.get_fd(),
0)) == MAP_FAILED) {
perror("mmap");
retval = EXIT_FAILURE; retval = EXIT_FAILURE;
} } else if ((argc > 0) && (fd = open(argv[0], O_RDONLY)) == -1) {
else { perror("open");
off_t seq_offset = 0; retval = EXIT_FAILURE;
} else if ((argc > 0) && (fstat(fd, &st) == -1)) {
perror("fstat");
retval = EXIT_FAILURE;
} else {
try {
off_t last_offset = offset;
line_buffer lb;
line_value lv;
char *maddr;
while (lb.read_line(seq_offset, lv)) { } lb.set_fd(fd);
do { if (index.size() == 0) {
bool ret; shared_buffer_ref sbr;
size_t lpc;
random_shuffle(index.begin(), index.end()); while (count && lb.read_line(offset, sbr, &lv)) {
for (lpc = 0; lpc < index.size(); lpc++) { printf("%.*s", (int) sbr.length(), sbr.get_data());
if ((off_t) (last_offset + lv.lv_len) < offset)
printf("\n");
last_offset = offset;
count -= 1;
}
} else if ((maddr = (char *) mmap(NULL,
st.st_size,
PROT_READ,
MAP_FILE | MAP_PRIVATE,
lb.get_fd(),
0)) == MAP_FAILED) {
perror("mmap");
retval = EXIT_FAILURE;
} else {
off_t seq_offset = 0;
offset = index[lpc].second; while (lb.read_line(seq_offset, lv)) {}
ret = lb.read_line(offset, lv); do {
bool ret;
size_t lpc;
assert(ret); random_shuffle(index.begin(), index.end());
assert(offset >= 0); for (lpc = 0; lpc < index.size(); lpc++) {
assert(offset <= st.st_size);
assert(memcmp(lv.lv_start,
&maddr[index[lpc].second],
lv.lv_len) == 0);
}
rnd_iters -= 1; offset = index[lpc].second;
} while (rnd_iters); ret = lb.read_line(offset, lv);
printf("All done\n"); assert(ret);
} assert(offset >= 0);
assert(offset <= st.st_size);
assert(memcmp(lv.lv_start,
&maddr[index[lpc].second],
lv.lv_len) == 0);
}
rnd_iters -= 1;
} while (rnd_iters);
printf("All done\n");
}
}
catch (line_buffer::error &e) {
fprintf(stderr, "error: %s\n", strerror(e.e_err));
retval = EXIT_FAILURE;
}
} }
catch (line_buffer::error &e) {
fprintf(stderr, "error: %s\n", strerror(e.e_err)); return retval;
retval = EXIT_FAILURE;
}
}
return retval;
} }

View File

@ -161,7 +161,9 @@ int main(int argc, char *argv[]) {
break; break;
case MODE_LEVELS: case MODE_LEVELS:
for (logfile::iterator iter = lf.begin(); iter != lf.end(); ++iter) { for (logfile::iterator iter = lf.begin(); iter != lf.end(); ++iter) {
printf("0x%02x\n", iter->get_level_and_flags()); log_level_t level = iter->get_level_and_flags();
printf("%s 0x%x\n", level_names[level & ~LEVEL__FLAGS],
level & LEVEL__FLAGS);
} }
break; break;
} }

View File

@ -42,6 +42,12 @@ check_output "Seeking in the line buffer doesn't work?" <<EOF
5 5
EOF EOF
run_test ./drive_line_buffer -o 4424 -c 1 ${srcdir}/UTF-8-test.txt
check_output "Invalid UTF is not scrubbed?" <<EOF
2.1.5 5 bytes (U-00200000): "?????" |
EOF
cat "${top_srcdir}/src/"*.hh "${top_srcdir}/src/"*.cc > lb-2.dat cat "${top_srcdir}/src/"*.hh "${top_srcdir}/src/"*.cc > lb-2.dat
grep -b '$' lb-2.dat | cut -f 1 -d : > lb.index grep -b '$' lb-2.dat | cut -f 1 -d : > lb.index
line_count=`wc -l lb-2.dat` line_count=`wc -l lb-2.dat`

View File

@ -222,41 +222,41 @@ EOF
run_test ./drive_logfile -v -f syslog_log ${srcdir}/logfile_syslog.0 run_test ./drive_logfile -v -f syslog_log ${srcdir}/logfile_syslog.0
check_output "Syslog level interpreted incorrectly?" <<EOF check_output "Syslog level interpreted incorrectly?" <<EOF
0x0a error 0x0
0x07 info 0x0
0x0a error 0x0
0x07 info 0x0
EOF EOF
run_test ./drive_logfile -v -f tcsh_history ${srcdir}/logfile_tcsh_history.0 run_test ./drive_logfile -v -f tcsh_history ${srcdir}/logfile_tcsh_history.0
check_output "TCSH level interpreted incorrectly?" <<EOF check_output "TCSH level interpreted incorrectly?" <<EOF
0x07 info 0x0
0x87 info 0x80
0x07 info 0x0
0x87 info 0x80
EOF EOF
run_test ./drive_logfile -v -f access_log ${srcdir}/logfile_access_log.0 run_test ./drive_logfile -v -f access_log ${srcdir}/logfile_access_log.0
check_output "access_log level interpreted incorrectly?" <<EOF check_output "access_log level interpreted incorrectly?" <<EOF
0x07 info 0x0
0x0a error 0x0
0x07 info 0x0
EOF EOF
run_test ./drive_logfile -v -f strace_log ${srcdir}/logfile_strace_log.0 run_test ./drive_logfile -v -f strace_log ${srcdir}/logfile_strace_log.0
check_output "strace_log level interpreted incorrectly?" <<EOF check_output "strace_log level interpreted incorrectly?" <<EOF
0x07 info 0x0
0x07 info 0x0
0x07 info 0x0
0x0a error 0x0
0x07 info 0x0
0x0a error 0x0
0x07 info 0x0
0x07 info 0x0
0x07 info 0x0
EOF EOF
run_test ./drive_logfile -t -f generic_log ${srcdir}/logfile_generic.0 run_test ./drive_logfile -t -f generic_log ${srcdir}/logfile_generic.0
@ -269,22 +269,22 @@ EOF
run_test ./drive_logfile -v -f generic_log ${srcdir}/logfile_generic.0 run_test ./drive_logfile -v -f generic_log ${srcdir}/logfile_generic.0
check_output "generic_log level interpreted incorrectly?" <<EOF check_output "generic_log level interpreted incorrectly?" <<EOF
0x06 debug 0x0
0x09 warning 0x0
EOF EOF
run_test ./drive_logfile -v -f generic_log ${srcdir}/logfile_generic.1 run_test ./drive_logfile -v -f generic_log ${srcdir}/logfile_generic.1
check_output "generic_log (1) level interpreted incorrectly?" <<EOF check_output "generic_log (1) level interpreted incorrectly?" <<EOF
0x07 info 0x0
0x0a error 0x0
EOF EOF
run_test ./drive_logfile -v -f generic_log ${srcdir}/logfile_generic.2 run_test ./drive_logfile -v -f generic_log ${srcdir}/logfile_generic.2
check_output "generic_log (2) level interpreted incorrectly?" <<EOF check_output "generic_log (2) level interpreted incorrectly?" <<EOF
0x0a error 0x0
0x0a error 0x0
EOF EOF
touch -t 200711030923 ${srcdir}/logfile_glog.0 touch -t 200711030923 ${srcdir}/logfile_glog.0
@ -303,13 +303,13 @@ EOF
run_test ./drive_logfile -v -f glog_log ${srcdir}/logfile_glog.0 run_test ./drive_logfile -v -f glog_log ${srcdir}/logfile_glog.0
check_output "glog_log level interpreted incorrectly?" <<EOF check_output "glog_log level interpreted incorrectly?" <<EOF
0x0a error 0x0
0x07 info 0x0
0x07 info 0x0
0x09 warning 0x0
0x07 info 0x0
0x07 info 0x0
0x0a error 0x0
EOF EOF
cp ${srcdir}/logfile_syslog.0 truncfile.0 cp ${srcdir}/logfile_syslog.0 truncfile.0