1 /** 2 * Copyright (c) 2007-2012, Timothy Stack 3 * 4 * All rights reserved. 5 * 6 * Redistribution and use in source and binary forms, with or without 7 * modification, are permitted provided that the following conditions are met: 8 * 9 * * Redistributions of source code must retain the above copyright notice, this 10 * list of conditions and the following disclaimer. 11 * * Redistributions in binary form must reproduce the above copyright notice, 12 * this list of conditions and the following disclaimer in the documentation 13 * and/or other materials provided with the distribution. 14 * * Neither the name of Timothy Stack nor the names of its contributors 15 * may be used to endorse or promote products derived from this software 16 * without specific prior written permission. 17 * 18 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ''AS IS'' AND ANY 19 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 20 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 21 * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY 22 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 23 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 24 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON 25 * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 26 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 27 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 * 29 * @file logfile_sub_source.hh 30 */ 31 32 #ifndef logfile_sub_source_hh 33 #define logfile_sub_source_hh 34 35 #include <limits.h> 36 37 #include <map> 38 #include <list> 39 #include <array> 40 #include <sstream> 41 #include <utility> 42 #include <vector> 43 44 #include "base/lnav_log.hh" 45 #include "base/time_util.hh" 46 #include "log_accel.hh" 47 #include "strong_int.hh" 48 #include "logfile.hh" 49 #include "bookmarks.hh" 50 #include "big_array.hh" 51 #include "textview_curses.hh" 52 #include "filter_observer.hh" 53 #include "log_format.hh" 54 55 STRONG_INT_TYPE(uint64_t, content_line); 56 57 struct sqlite3_stmt; 58 extern "C" { 59 int sqlite3_finalize(sqlite3_stmt *pStmt); 60 } 61 62 class logfile_sub_source; 63 64 class index_delegate { 65 public: 66 virtual ~index_delegate() = default; 67 index_start(logfile_sub_source & lss)68 virtual void index_start(logfile_sub_source &lss) { 69 70 }; 71 index_line(logfile_sub_source & lss,logfile * lf,logfile::iterator ll)72 virtual void index_line(logfile_sub_source &lss, logfile *lf, logfile::iterator ll) { 73 74 }; 75 index_complete(logfile_sub_source & lss)76 virtual void index_complete(logfile_sub_source &lss) { 77 78 }; 79 }; 80 81 class pcre_filter 82 : public text_filter { 83 public: pcre_filter(type_t type,const std::string & id,size_t index,pcre * code)84 pcre_filter(type_t type, const std::string& id, size_t index, pcre *code) 85 : text_filter(type, filter_lang_t::REGEX, id, index), 86 pf_pcre(code) { }; 87 88 ~pcre_filter() override = default; 89 matches(const logfile & lf,logfile::const_iterator ll,shared_buffer_ref & line)90 bool matches(const logfile &lf, logfile::const_iterator ll, shared_buffer_ref &line) override { 91 pcre_context_static<30> pc; 92 pcre_input pi(line.get_data(), 0, line.length()); 93 94 return this->pf_pcre.match(pc, pi); 95 }; 96 to_command()97 std::string to_command() override { 98 return (this->lf_type == text_filter::INCLUDE ? 99 "filter-in " : "filter-out ") + 100 this->lf_id; 101 }; 102 103 protected: 104 pcrepp pf_pcre; 105 }; 106 107 class sql_filter : public text_filter { 108 public: sql_filter(logfile_sub_source & lss,std::string stmt_str,sqlite3_stmt * stmt)109 sql_filter(logfile_sub_source& lss, std::string stmt_str, sqlite3_stmt *stmt) 110 : text_filter(EXCLUDE, filter_lang_t::SQL, std::move(stmt_str), 0), 111 sf_log_source(lss) { 112 this->sf_filter_stmt = stmt; 113 } 114 115 bool matches(const logfile &lf, logfile::const_iterator ll, shared_buffer_ref &line) override; 116 117 std::string to_command() override; 118 119 auto_mem<sqlite3_stmt> sf_filter_stmt{sqlite3_finalize}; 120 logfile_sub_source& sf_log_source; 121 }; 122 123 class log_location_history : public location_history { 124 public: log_location_history(logfile_sub_source & lss)125 explicit log_location_history(logfile_sub_source &lss) 126 : llh_history(std::begin(this->llh_backing), 127 std::end(this->llh_backing)), 128 llh_log_source(lss) { 129 } 130 131 ~log_location_history() override = default; 132 133 void loc_history_append(vis_line_t top) override; 134 135 nonstd::optional<vis_line_t> 136 loc_history_back(vis_line_t current_top) override; 137 138 nonstd::optional<vis_line_t> 139 loc_history_forward(vis_line_t current_top) override; 140 141 private: 142 nonstd::ring_span<content_line_t> llh_history; 143 logfile_sub_source &llh_log_source; 144 content_line_t llh_backing[MAX_SIZE]; 145 }; 146 147 /** 148 * Delegate class that merges the contents of multiple log files into a single 149 * source of data for a text view. 150 */ 151 class logfile_sub_source 152 : public text_sub_source, 153 public text_time_translator, 154 public list_input_delegate { 155 public: 156 157 static bookmark_type_t BM_ERRORS; 158 static bookmark_type_t BM_WARNINGS; 159 static bookmark_type_t BM_FILES; 160 161 virtual void text_filters_changed(); 162 163 logfile_sub_source(); 164 toggle_time_offset()165 void toggle_time_offset() { 166 this->lss_flags ^= F_TIME_OFFSET; 167 this->clear_line_size_cache(); 168 }; 169 increase_line_context()170 void increase_line_context() { 171 auto old_flags = this->lss_flags; 172 173 if (this->lss_flags & F_FILENAME) { 174 // Nothing to do 175 } else if (this->lss_flags & F_BASENAME) { 176 this->lss_flags &= ~F_NAME_MASK; 177 this->lss_flags |= F_FILENAME; 178 } else { 179 this->lss_flags |= F_BASENAME; 180 } 181 if (old_flags != this->lss_flags) { 182 this->clear_line_size_cache(); 183 } 184 }; 185 decrease_line_context()186 bool decrease_line_context() { 187 auto old_flags = this->lss_flags; 188 189 if (this->lss_flags & F_FILENAME) { 190 this->lss_flags &= ~F_NAME_MASK; 191 this->lss_flags |= F_BASENAME; 192 } else if (this->lss_flags & F_BASENAME) { 193 this->lss_flags &= ~F_NAME_MASK; 194 } 195 if (old_flags != this->lss_flags) { 196 this->clear_line_size_cache(); 197 198 return true; 199 } 200 201 return false; 202 }; 203 get_filename_offset() const204 size_t get_filename_offset() const { 205 if (this->lss_flags & F_FILENAME) { 206 return this->lss_filename_width; 207 } else if (this->lss_flags & F_BASENAME) { 208 return this->lss_basename_width; 209 } 210 211 return 0; 212 } 213 set_time_offset(bool enabled)214 void set_time_offset(bool enabled) { 215 if (enabled) 216 this->lss_flags |= F_TIME_OFFSET; 217 else 218 this->lss_flags &= ~F_TIME_OFFSET; 219 this->clear_line_size_cache(); 220 }; 221 is_time_offset_enabled() const222 bool is_time_offset_enabled() const { 223 return (bool) (this->lss_flags & F_TIME_OFFSET); 224 }; 225 is_filename_enabled() const226 bool is_filename_enabled() const { 227 return (bool) (this->lss_flags & F_FILENAME); 228 }; 229 is_basename_enabled() const230 bool is_basename_enabled() const { 231 return (bool) (this->lss_flags & F_BASENAME); 232 }; 233 get_min_log_level() const234 log_level_t get_min_log_level() const { 235 return this->lss_min_log_level; 236 }; 237 set_force_rebuild()238 void set_force_rebuild() { 239 this->lss_force_rebuild = true; 240 } 241 set_min_log_level(log_level_t level)242 void set_min_log_level(log_level_t level) { 243 if (this->lss_min_log_level != level) { 244 this->lss_min_log_level = level; 245 this->text_filters_changed(); 246 } 247 }; 248 get_min_log_time(struct timeval & tv_out) const249 bool get_min_log_time(struct timeval &tv_out) const { 250 tv_out = this->lss_min_log_time; 251 return (this->lss_min_log_time.tv_sec != 0 || 252 this->lss_min_log_time.tv_usec != 0); 253 }; 254 set_min_log_time(const struct timeval & tv)255 void set_min_log_time(const struct timeval &tv) { 256 if (this->lss_min_log_time != tv) { 257 this->lss_min_log_time = tv; 258 this->text_filters_changed(); 259 } 260 }; 261 get_max_log_time(struct timeval & tv_out) const262 bool get_max_log_time(struct timeval &tv_out) const { 263 tv_out = this->lss_max_log_time; 264 return (this->lss_max_log_time.tv_sec != std::numeric_limits<time_t>::max() || 265 this->lss_max_log_time.tv_usec != 0); 266 }; 267 set_max_log_time(struct timeval & tv)268 void set_max_log_time(struct timeval &tv) { 269 if (this->lss_max_log_time != tv) { 270 this->lss_max_log_time = tv; 271 this->text_filters_changed(); 272 } 273 }; 274 clear_min_max_log_times()275 void clear_min_max_log_times() { 276 if (this->lss_min_log_time.tv_sec != 0 || 277 this->lss_min_log_time.tv_usec != 0 || 278 this->lss_max_log_time.tv_sec != std::numeric_limits<time_t>::max() || 279 this->lss_max_log_time.tv_usec != 0) { 280 memset(&this->lss_min_log_time, 0, sizeof(this->lss_min_log_time)); 281 this->lss_max_log_time.tv_sec = std::numeric_limits<time_t>::max(); 282 this->lss_max_log_time.tv_usec = 0; 283 this->text_filters_changed(); 284 } 285 }; 286 287 bool list_input_handle_key(listview_curses &lv, int ch); 288 set_marked_only(bool val)289 void set_marked_only(bool val) { 290 if (this->lss_marked_only != val) { 291 this->lss_marked_only = val; 292 this->text_filters_changed(); 293 } 294 }; 295 get_marked_only()296 bool get_marked_only() { 297 return this->lss_marked_only; 298 }; 299 text_line_count()300 size_t text_line_count() 301 { 302 return this->lss_filtered_index.size(); 303 }; 304 text_line_width(textview_curses & curses)305 size_t text_line_width(textview_curses &curses) { 306 return this->lss_longest_line; 307 }; 308 file_count() const309 size_t file_count() const { 310 size_t retval = 0; 311 const_iterator iter; 312 313 for (iter = this->cbegin(); iter != this->cend(); ++iter) { 314 if (*iter != NULL && (*iter)->get_file() != NULL) { 315 retval += 1; 316 } 317 } 318 319 return retval; 320 } 321 empty() const322 bool empty() const { return this->lss_filtered_index.empty(); }; 323 324 void text_value_for_line(textview_curses &tc, 325 int row, 326 std::string &value_out, 327 line_flags_t flags); 328 329 void text_attrs_for_line(textview_curses &tc, 330 int row, 331 string_attrs_t &value_out); 332 text_size_for_line(textview_curses & tc,int row,line_flags_t flags)333 size_t text_size_for_line(textview_curses &tc, int row, line_flags_t flags) { 334 size_t index = row % LINE_SIZE_CACHE_SIZE; 335 336 if (this->lss_line_size_cache[index].first != row) { 337 std::string value; 338 339 this->text_value_for_line(tc, row, value, flags); 340 this->lss_line_size_cache[index].second = value.size(); 341 this->lss_line_size_cache[index].first = row; 342 } 343 return this->lss_line_size_cache[index].second; 344 }; 345 text_mark(bookmark_type_t * bm,vis_line_t line,bool added)346 void text_mark(bookmark_type_t *bm, vis_line_t line, bool added) 347 { 348 if (line >= (int) this->lss_index.size()) { 349 return; 350 } 351 352 content_line_t cl = this->at(line); 353 std::vector<content_line_t>::iterator lb; 354 355 if (bm == &textview_curses::BM_USER) { 356 logline *ll = this->find_line(cl); 357 358 ll->set_mark(added); 359 } 360 lb = std::lower_bound(this->lss_user_marks[bm].begin(), 361 this->lss_user_marks[bm].end(), 362 cl); 363 if (added) { 364 if (lb == this->lss_user_marks[bm].end() || *lb != cl) { 365 this->lss_user_marks[bm].insert(lb, cl); 366 } 367 } 368 else if (lb != this->lss_user_marks[bm].end() && *lb == cl) { 369 require(lb != this->lss_user_marks[bm].end()); 370 371 this->lss_user_marks[bm].erase(lb); 372 } 373 if (bm == &textview_curses::BM_META && 374 this->lss_meta_grepper.gps_proc != nullptr) { 375 this->tss_view->search_range(line, line + 1_vl); 376 this->tss_view->search_new_data(); 377 } 378 }; 379 text_clear_marks(bookmark_type_t * bm)380 void text_clear_marks(bookmark_type_t *bm) 381 { 382 std::vector<content_line_t>::iterator iter; 383 384 if (bm == &textview_curses::BM_USER) { 385 for (iter = this->lss_user_marks[bm].begin(); 386 iter != this->lss_user_marks[bm].end();) { 387 auto bm_iter = this->lss_user_mark_metadata.find(*iter); 388 if (bm_iter != this->lss_user_mark_metadata.end()) { 389 ++iter; 390 continue; 391 } 392 this->find_line(*iter)->set_mark(false); 393 iter = this->lss_user_marks[bm].erase(iter); 394 } 395 } else { 396 this->lss_user_marks[bm].clear(); 397 } 398 }; 399 400 bool insert_file(const std::shared_ptr<logfile>& lf); 401 remove_file(std::shared_ptr<logfile> lf)402 void remove_file(std::shared_ptr<logfile> lf) 403 { 404 iterator iter; 405 406 iter = std::find_if(this->lss_files.begin(), 407 this->lss_files.end(), 408 logfile_data_eq(lf)); 409 if (iter != this->lss_files.end()) { 410 bookmarks<content_line_t>::type::iterator mark_iter; 411 int file_index = iter - this->lss_files.begin(); 412 413 (*iter)->clear(); 414 for (mark_iter = this->lss_user_marks.begin(); 415 mark_iter != this->lss_user_marks.end(); 416 ++mark_iter) { 417 content_line_t mark_curr = content_line_t( 418 file_index * MAX_LINES_PER_FILE); 419 content_line_t mark_end = content_line_t( 420 (file_index + 1) * MAX_LINES_PER_FILE); 421 bookmark_vector<content_line_t>::iterator bv_iter; 422 bookmark_vector<content_line_t> & bv = 423 mark_iter->second; 424 425 while ((bv_iter = 426 std::lower_bound(bv.begin(), bv.end(), 427 mark_curr)) != bv.end()) { 428 if (*bv_iter >= mark_end) { 429 break; 430 } 431 mark_iter->second.erase(bv_iter); 432 } 433 } 434 435 this->lss_force_rebuild = true; 436 } 437 }; 438 439 enum class rebuild_result { 440 rr_no_change, 441 rr_appended_lines, 442 rr_partial_rebuild, 443 rr_full_rebuild, 444 }; 445 446 rebuild_result rebuild_index(nonstd::optional<ui_clock::time_point> deadline = nonstd::nullopt); 447 448 void text_update_marks(vis_bookmarks &bm); 449 set_user_mark(bookmark_type_t * bm,content_line_t cl)450 void set_user_mark(bookmark_type_t *bm, content_line_t cl) 451 { 452 this->lss_user_marks[bm].insert_once(cl); 453 }; 454 get_user_bookmarks()455 bookmarks<content_line_t>::type &get_user_bookmarks() 456 { 457 return this->lss_user_marks; 458 }; 459 get_user_bookmark_metadata()460 std::map<content_line_t, bookmark_metadata> &get_user_bookmark_metadata() { 461 return this->lss_user_mark_metadata; 462 }; 463 get_filtered_count() const464 int get_filtered_count() const { 465 return this->lss_index.size() - this->lss_filtered_index.size(); 466 }; 467 get_filtered_count_for(size_t filter_index) const468 int get_filtered_count_for(size_t filter_index) const { 469 int retval = 0; 470 471 for (auto& ld : this->lss_files) { 472 retval += ld->ld_filter_state.lfo_filter_state.tfs_filter_hits[filter_index]; 473 } 474 475 return retval; 476 } 477 478 Result<void, std::string> set_sql_filter(std::string stmt_str, sqlite3_stmt *stmt); 479 480 Result<void, std::string> set_sql_marker(std::string stmt_str, sqlite3_stmt *stmt); 481 482 Result<void, std::string> set_preview_sql_filter(sqlite3_stmt *stmt); 483 get_sql_filter_text()484 std::string get_sql_filter_text() { 485 auto filt = this->get_sql_filter(); 486 487 if (filt) { 488 return filt.value()->get_id(); 489 } 490 return ""; 491 } 492 get_sql_marker_text() const493 std::string get_sql_marker_text() const { 494 return this->lss_marker_stmt_text; 495 } 496 497 std::shared_ptr<logfile> find(const char *fn, content_line_t &line_base); 498 find(content_line_t & line) const499 std::shared_ptr<logfile> find(content_line_t &line) const 500 { 501 std::shared_ptr<logfile> retval; 502 503 retval = this->lss_files[line / MAX_LINES_PER_FILE]->get_file(); 504 line = content_line_t(line % MAX_LINES_PER_FILE); 505 506 return retval; 507 }; 508 find_file_ptr(content_line_t & line)509 logfile *find_file_ptr(content_line_t &line) 510 { 511 auto retval = this->lss_files[line / MAX_LINES_PER_FILE]->get_file_ptr(); 512 line = content_line_t(line % MAX_LINES_PER_FILE); 513 514 return retval; 515 }; 516 find_line(content_line_t line) const517 logline *find_line(content_line_t line) const 518 { 519 logline *retval = nullptr; 520 std::shared_ptr<logfile> lf = this->find(line); 521 522 if (lf != nullptr) { 523 auto ll_iter = lf->begin() + line; 524 525 retval = &(*ll_iter); 526 } 527 528 return retval; 529 }; 530 531 nonstd::optional<vis_line_t> find_from_time(const struct timeval &start) const; 532 find_from_time(time_t start) const533 nonstd::optional<vis_line_t> find_from_time(time_t start) const { 534 struct timeval tv = { start, 0 }; 535 536 return this->find_from_time(tv); 537 }; 538 find_from_time(const exttm & etm) const539 nonstd::optional<vis_line_t> find_from_time(const exttm &etm) const { 540 return this->find_from_time(etm.to_timeval()); 541 }; 542 find_from_content(content_line_t cl)543 nonstd::optional<vis_line_t> find_from_content(content_line_t cl) { 544 content_line_t line = cl; 545 std::shared_ptr<logfile> lf = this->find(line); 546 547 if (lf != nullptr) { 548 auto ll_iter = lf->begin() + line; 549 auto &ll = *ll_iter; 550 auto vis_start_opt = this->find_from_time(ll.get_timeval()); 551 552 if (!vis_start_opt) { 553 return nonstd::nullopt; 554 } 555 556 auto vis_start = *vis_start_opt; 557 558 while (vis_start < vis_line_t(this->text_line_count())) { 559 content_line_t guess_cl = this->at(vis_start); 560 561 if (cl == guess_cl) { 562 return vis_start; 563 } 564 565 auto guess_line = this->find_line(guess_cl); 566 567 if (!guess_line || ll < *guess_line) { 568 return nonstd::nullopt; 569 } 570 571 ++vis_start; 572 } 573 } 574 575 return nonstd::nullopt; 576 } 577 time_for_row(vis_line_t row)578 nonstd::optional<struct timeval> time_for_row(vis_line_t row) { 579 if (row < (ssize_t) this->text_line_count()) { 580 return this->find_line(this->at(row))->get_timeval(); 581 } 582 return nonstd::nullopt; 583 }; 584 row_for_time(struct timeval time_bucket)585 nonstd::optional<vis_line_t> row_for_time(struct timeval time_bucket) { 586 return this->find_from_time(time_bucket); 587 }; 588 at(vis_line_t vl)589 content_line_t at(vis_line_t vl) { 590 return this->lss_index[this->lss_filtered_index[vl]]; 591 }; 592 at_base(vis_line_t vl)593 content_line_t at_base(vis_line_t vl) { 594 while (this->find_line(this->at(vl))->get_sub_offset() != 0) { 595 --vl; 596 } 597 598 return this->at(vl); 599 }; 600 601 log_accel::direction_t get_line_accel_direction(vis_line_t vl); 602 603 /** 604 * Container for logfile references that keeps of how many lines in the 605 * logfile have been indexed. 606 */ 607 struct logfile_data { logfile_datalogfile_sub_source::logfile_data608 logfile_data(size_t index, filter_stack &fs, const std::shared_ptr<logfile> &lf) 609 : ld_file_index(index), 610 ld_filter_state(fs, lf), 611 ld_visible(lf->is_indexing()) { 612 lf->set_logline_observer(&this->ld_filter_state); 613 }; 614 clearlogfile_sub_source::logfile_data615 void clear() 616 { 617 this->ld_filter_state.lfo_filter_state.clear(); 618 }; 619 set_filelogfile_sub_source::logfile_data620 void set_file(const std::shared_ptr<logfile> &lf) { 621 this->ld_filter_state.lfo_filter_state.tfs_logfile = lf; 622 lf->set_logline_observer(&this->ld_filter_state); 623 }; 624 get_filelogfile_sub_source::logfile_data625 std::shared_ptr<logfile> get_file() const { 626 return this->ld_filter_state.lfo_filter_state.tfs_logfile; 627 }; 628 get_file_ptrlogfile_sub_source::logfile_data629 logfile *get_file_ptr() const { 630 return this->ld_filter_state.lfo_filter_state.tfs_logfile.get(); 631 }; 632 is_visiblelogfile_sub_source::logfile_data633 bool is_visible() const { 634 return this->ld_visible; 635 } 636 set_visibilitylogfile_sub_source::logfile_data637 void set_visibility(bool vis) { 638 this->ld_visible = vis; 639 } 640 641 size_t ld_file_index; 642 line_filter_observer ld_filter_state; 643 size_t ld_lines_indexed{0}; 644 bool ld_visible; 645 }; 646 647 typedef std::vector<std::unique_ptr<logfile_data>>::iterator iterator; 648 typedef std::vector<std::unique_ptr<logfile_data>>::const_iterator const_iterator; 649 begin()650 iterator begin() 651 { 652 return this->lss_files.begin(); 653 }; 654 end()655 iterator end() 656 { 657 return this->lss_files.end(); 658 }; 659 cbegin() const660 const_iterator cbegin() const 661 { 662 return this->lss_files.begin(); 663 }; 664 cend() const665 const_iterator cend() const 666 { 667 return this->lss_files.end(); 668 }; 669 find_data(content_line_t & line)670 iterator find_data(content_line_t &line) 671 { 672 auto retval = this->lss_files.begin(); 673 std::advance(retval, line / MAX_LINES_PER_FILE); 674 line = content_line_t(line % MAX_LINES_PER_FILE); 675 676 return retval; 677 }; 678 find_data(content_line_t line,uint64_t & offset_out)679 iterator find_data(content_line_t line, uint64_t &offset_out) 680 { 681 auto retval = this->lss_files.begin(); 682 std::advance(retval, line / MAX_LINES_PER_FILE); 683 offset_out = line % MAX_LINES_PER_FILE; 684 685 return retval; 686 }; 687 find_data(const std::shared_ptr<logfile> & lf)688 nonstd::optional<logfile_data *> find_data(const std::shared_ptr<logfile>& lf) { 689 for (auto& ld : *this) { 690 if (ld->ld_filter_state.lfo_filter_state.tfs_logfile == lf) { 691 return ld.get(); 692 } 693 } 694 return nonstd::nullopt; 695 } 696 find_data_i(const std::shared_ptr<const logfile> & lf)697 iterator find_data_i(const std::shared_ptr<const logfile>& lf) { 698 for (auto iter = this->begin(); iter != this->end(); ++iter) { 699 if ((*iter)->ld_filter_state.lfo_filter_state.tfs_logfile == lf) { 700 return iter; 701 } 702 } 703 704 return this->end(); 705 } 706 get_file_base_content_line(iterator iter)707 content_line_t get_file_base_content_line(iterator iter) { 708 ssize_t index = std::distance(this->begin(), iter); 709 710 return content_line_t(index * MAX_LINES_PER_FILE); 711 }; 712 set_index_delegate(index_delegate * id)713 void set_index_delegate(index_delegate *id) { 714 if (id != this->lss_index_delegate) { 715 this->lss_index_delegate = id; 716 this->reload_index_delegate(); 717 } 718 }; 719 get_index_delegate() const720 index_delegate *get_index_delegate() const { 721 return this->lss_index_delegate; 722 }; 723 reload_index_delegate()724 void reload_index_delegate() { 725 if (this->lss_index_delegate == nullptr) { 726 return; 727 } 728 729 this->lss_index_delegate->index_start(*this); 730 for (unsigned int index : this->lss_filtered_index) { 731 content_line_t cl = (content_line_t) this->lss_index[index]; 732 uint64_t line_number; 733 auto ld = this->find_data(cl, line_number); 734 std::shared_ptr<logfile> lf = (*ld)->get_file(); 735 736 this->lss_index_delegate->index_line(*this, lf.get(), lf->begin() + line_number); 737 } 738 this->lss_index_delegate->index_complete(*this); 739 }; 740 741 class meta_grepper 742 : public grep_proc_source<vis_line_t>, 743 public grep_proc_sink<vis_line_t> { 744 public: meta_grepper(logfile_sub_source & source)745 meta_grepper(logfile_sub_source &source) 746 : lmg_source(source) { 747 }; 748 grep_value_for_line(vis_line_t line,std::string & value_out)749 bool grep_value_for_line(vis_line_t line, std::string &value_out) override { 750 content_line_t cl = this->lmg_source.at(vis_line_t(line)); 751 std::map<content_line_t, bookmark_metadata> &user_mark_meta = 752 lmg_source.get_user_bookmark_metadata(); 753 auto meta_iter = user_mark_meta.find(cl); 754 755 if (meta_iter == user_mark_meta.end()) { 756 value_out.clear(); 757 } else { 758 bookmark_metadata &bm = meta_iter->second; 759 760 value_out.append(bm.bm_comment); 761 for (const auto &tag : bm.bm_tags) { 762 value_out.append(tag); 763 } 764 } 765 766 return !this->lmg_done; 767 }; 768 grep_initial_line(vis_line_t start,vis_line_t highest)769 vis_line_t grep_initial_line(vis_line_t start, vis_line_t highest) override { 770 vis_bookmarks &bm = this->lmg_source.tss_view->get_bookmarks(); 771 bookmark_vector<vis_line_t> &bv = bm[&textview_curses::BM_META]; 772 773 if (bv.empty()) { 774 return -1_vl; 775 } 776 return *bv.begin(); 777 }; 778 grep_next_line(vis_line_t & line)779 void grep_next_line(vis_line_t &line) override { 780 vis_bookmarks &bm = this->lmg_source.tss_view->get_bookmarks(); 781 bookmark_vector<vis_line_t> &bv = bm[&textview_curses::BM_META]; 782 783 line = bv.next(vis_line_t(line)); 784 if (line == -1) { 785 this->lmg_done = true; 786 } 787 }; 788 grep_begin(grep_proc<vis_line_t> & gp,vis_line_t start,vis_line_t stop)789 void grep_begin(grep_proc<vis_line_t> &gp, vis_line_t start, vis_line_t stop) override { 790 this->lmg_source.tss_view->grep_begin(gp, start, stop); 791 }; 792 grep_end(grep_proc<vis_line_t> & gp)793 void grep_end(grep_proc<vis_line_t> &gp) override { 794 this->lmg_source.tss_view->grep_end(gp); 795 }; 796 grep_match(grep_proc<vis_line_t> & gp,vis_line_t line,int start,int end)797 void grep_match(grep_proc<vis_line_t> &gp, 798 vis_line_t line, 799 int start, 800 int end) override { 801 this->lmg_source.tss_view->grep_match(gp, line, start, end); 802 }; 803 804 logfile_sub_source &lmg_source; 805 bool lmg_done{false}; 806 }; 807 808 nonstd::optional<std::pair<grep_proc_source<vis_line_t> *, grep_proc_sink<vis_line_t> *>> 809 get_grepper(); 810 get_location_history()811 nonstd::optional<location_history *> get_location_history() { 812 return &this->lss_location_history; 813 }; 814 815 Result<bool, std::string> eval_sql_filter( 816 sqlite3_stmt *stmt, iterator ld, logfile::const_iterator ll); 817 818 void invalidate_sql_filter(); 819 set_line_meta_changed()820 void set_line_meta_changed() { 821 this->lss_line_meta_changed = true; 822 } 823 is_line_meta_changed() const824 bool is_line_meta_changed() const { 825 return this->lss_line_meta_changed; 826 } 827 828 static const uint64_t MAX_CONTENT_LINES = (1ULL << 40) - 1; 829 static const uint64_t MAX_LINES_PER_FILE = 256 * 1024 * 1024; 830 static const uint64_t MAX_FILES = ( 831 MAX_CONTENT_LINES / MAX_LINES_PER_FILE); 832 833 std::function<void(logfile_sub_source&, file_off_t, file_size_t)> lss_sorting_observer; 834 835 private: 836 static const size_t LINE_SIZE_CACHE_SIZE = 512; 837 838 enum { 839 B_SCRUB, 840 B_TIME_OFFSET, 841 B_FILENAME, 842 B_BASENAME, 843 }; 844 845 enum { 846 F_SCRUB = (1UL << B_SCRUB), 847 F_TIME_OFFSET = (1UL << B_TIME_OFFSET), 848 F_FILENAME = (1UL << B_FILENAME), 849 F_BASENAME = (1UL << B_BASENAME), 850 851 F_NAME_MASK = (F_FILENAME | F_BASENAME), 852 }; 853 854 struct __attribute__((__packed__)) indexed_content { indexed_contentlogfile_sub_source::indexed_content855 indexed_content() { 856 857 }; 858 indexed_contentlogfile_sub_source::indexed_content859 indexed_content(content_line_t cl) : ic_value(cl) { 860 861 }; 862 operator content_line_tlogfile_sub_source::indexed_content863 operator content_line_t () const { 864 return content_line_t(this->ic_value); 865 }; 866 867 uint64_t ic_value : 40; 868 }; 869 870 struct logline_cmp { logline_cmplogfile_sub_source::logline_cmp871 logline_cmp(logfile_sub_source & lc) 872 : llss_controller(lc) { }; operator ()logfile_sub_source::logline_cmp873 bool operator()(const content_line_t &lhs, const content_line_t &rhs) const 874 { 875 logline *ll_lhs = this->llss_controller.find_line(lhs); 876 logline *ll_rhs = this->llss_controller.find_line(rhs); 877 878 return (*ll_lhs) < (*ll_rhs); 879 }; 880 operator ()logfile_sub_source::logline_cmp881 bool operator()(const uint32_t &lhs, const uint32_t &rhs) const 882 { 883 content_line_t cl_lhs = (content_line_t) 884 llss_controller.lss_index[lhs]; 885 content_line_t cl_rhs = (content_line_t) 886 llss_controller.lss_index[rhs]; 887 logline *ll_lhs = this->llss_controller.find_line(cl_lhs); 888 logline *ll_rhs = this->llss_controller.find_line(cl_rhs); 889 890 return (*ll_lhs) < (*ll_rhs); 891 }; 892 #if 0 893 bool operator()(const indexed_content &lhs, const indexed_content &rhs) 894 { 895 logline *ll_lhs = this->llss_controller.find_line(lhs.ic_value); 896 logline *ll_rhs = this->llss_controller.find_line(rhs.ic_value); 897 898 return (*ll_lhs) < (*ll_rhs); 899 }; 900 #endif operator ()logfile_sub_source::logline_cmp901 bool operator()(const content_line_t &lhs, const time_t &rhs) const 902 { 903 logline *ll_lhs = this->llss_controller.find_line(lhs); 904 905 return *ll_lhs < rhs; 906 }; operator ()logfile_sub_source::logline_cmp907 bool operator()(const content_line_t &lhs, const struct timeval &rhs) const 908 { 909 logline *ll_lhs = this->llss_controller.find_line(lhs); 910 911 return *ll_lhs < rhs; 912 }; 913 914 logfile_sub_source & llss_controller; 915 }; 916 917 struct filtered_logline_cmp { filtered_logline_cmplogfile_sub_source::filtered_logline_cmp918 filtered_logline_cmp(const logfile_sub_source & lc) 919 : llss_controller(lc) { }; operator ()logfile_sub_source::filtered_logline_cmp920 bool operator()(const uint32_t &lhs, const uint32_t &rhs) const 921 { 922 content_line_t cl_lhs = (content_line_t) 923 llss_controller.lss_index[lhs]; 924 content_line_t cl_rhs = (content_line_t) 925 llss_controller.lss_index[rhs]; 926 logline *ll_lhs = this->llss_controller.find_line(cl_lhs); 927 logline *ll_rhs = this->llss_controller.find_line(cl_rhs); 928 929 return (*ll_lhs) < (*ll_rhs); 930 }; 931 operator ()logfile_sub_source::filtered_logline_cmp932 bool operator()(const uint32_t &lhs, const struct timeval &rhs) const 933 { 934 content_line_t cl_lhs = (content_line_t) 935 llss_controller.lss_index[lhs]; 936 logline *ll_lhs = this->llss_controller.find_line(cl_lhs); 937 938 return (*ll_lhs) < rhs; 939 }; 940 941 const logfile_sub_source & llss_controller; 942 }; 943 944 /** 945 * Functor for comparing the ld_file field of the logfile_data struct. 946 */ 947 struct logfile_data_eq { logfile_data_eqlogfile_sub_source::logfile_data_eq948 explicit logfile_data_eq(std::shared_ptr<logfile> lf) : lde_file(std::move(lf)) { }; 949 operator ()logfile_sub_source::logfile_data_eq950 bool operator()(const std::unique_ptr<logfile_data>& ld) const 951 { 952 return this->lde_file == ld->get_file(); 953 } 954 955 std::shared_ptr<logfile> lde_file; 956 }; 957 clear_line_size_cache()958 void clear_line_size_cache() { 959 this->lss_line_size_cache.fill(std::make_pair(0, 0)); 960 this->lss_line_size_cache[0].first = -1; 961 }; 962 get_sql_filter()963 nonstd::optional<std::shared_ptr<text_filter>> get_sql_filter() { 964 auto iter = std::find_if(this->tss_filters.begin(), 965 this->tss_filters.end(), 966 [](const auto& filt) { 967 return filt->get_index() == 0; 968 }); 969 970 if (iter != this->tss_filters.end()) { 971 return *iter; 972 } 973 return nonstd::nullopt; 974 } 975 976 bool check_extra_filters(iterator ld, logfile::iterator ll); 977 978 size_t lss_basename_width = 0; 979 size_t lss_filename_width = 0; 980 unsigned long lss_flags{0}; 981 bool lss_force_rebuild{false}; 982 std::vector<std::unique_ptr<logfile_data>> lss_files; 983 984 big_array<indexed_content> lss_index; 985 std::vector<uint32_t> lss_filtered_index; 986 auto_mem<sqlite3_stmt> lss_preview_filter_stmt{sqlite3_finalize}; 987 988 bookmarks<content_line_t>::type lss_user_marks; 989 std::map<content_line_t, bookmark_metadata> lss_user_mark_metadata; 990 auto_mem<sqlite3_stmt> lss_marker_stmt{sqlite3_finalize}; 991 std::string lss_marker_stmt_text; 992 993 line_flags_t lss_token_flags{0}; 994 iterator lss_token_file_data; 995 std::shared_ptr<logfile> lss_token_file; 996 std::string lss_token_value; 997 string_attrs_t lss_token_attrs; 998 std::vector<logline_value> lss_token_values; 999 int lss_token_shift_start{0}; 1000 int lss_token_shift_size{0}; 1001 shared_buffer lss_share_manager; 1002 logfile::iterator lss_token_line; 1003 std::array<std::pair<int, size_t>, LINE_SIZE_CACHE_SIZE> lss_line_size_cache; 1004 log_level_t lss_min_log_level{LEVEL_UNKNOWN}; 1005 struct timeval lss_min_log_time{0, 0}; 1006 struct timeval lss_max_log_time{std::numeric_limits<time_t>::max(), 0}; 1007 bool lss_marked_only{false}; 1008 index_delegate *lss_index_delegate{nullptr}; 1009 size_t lss_longest_line{0}; 1010 meta_grepper lss_meta_grepper; 1011 log_location_history lss_location_history; 1012 1013 bool lss_in_value_for_line{false}; 1014 bool lss_line_meta_changed{false}; 1015 }; 1016 1017 #endif 1018