You can not select more than 25 topics
			Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
		
		
		
		
			
				
					148 lines
				
				4.9 KiB
			
		
		
			
		
	
	
					148 lines
				
				4.9 KiB
			| 
											4 years ago
										 | #include <sys/stat.h>
 | ||
|  | 
 | ||
| 
											4 years ago
										 | #include <climits>
 | ||
| 
											4 years ago
										 | #include <condition_variable>
 | ||
| 
											4 years ago
										 | #include <sstream>
 | ||
| 
											4 years ago
										 | #include <thread>
 | ||
| 
											3 years ago
										 | #include <utility>
 | ||
| 
											4 years ago
										 | 
 | ||
|  | #include "catch2/catch.hpp"
 | ||
|  | #include "cereal/messaging/messaging.h"
 | ||
| 
											3 years ago
										 | #include "common/util.h"
 | ||
| 
											3 years ago
										 | #include "system/loggerd/logger.h"
 | ||
| 
											3 years ago
										 | #include "tools/replay/util.h"
 | ||
| 
											4 years ago
										 | 
 | ||
|  | typedef cereal::Sentinel::SentinelType SentinelType;
 | ||
|  | 
 | ||
|  | void verify_segment(const std::string &route_path, int segment, int max_segment, int required_event_cnt) {
 | ||
|  |   const std::string segment_path = route_path + "--" + std::to_string(segment);
 | ||
|  |   SentinelType begin_sentinel = segment == 0 ? SentinelType::START_OF_ROUTE : SentinelType::START_OF_SEGMENT;
 | ||
|  |   SentinelType end_sentinel = segment == max_segment - 1 ? SentinelType::END_OF_ROUTE : SentinelType::END_OF_SEGMENT;
 | ||
|  | 
 | ||
| 
											4 years ago
										 |   REQUIRE(!util::file_exists(segment_path + "/rlog.lock"));
 | ||
|  |   for (const char *fn : {"/rlog", "/qlog"}) {
 | ||
| 
											4 years ago
										 |     const std::string log_file = segment_path + fn;
 | ||
| 
											4 years ago
										 |     std::string log = util::read_file(log_file);
 | ||
| 
											4 years ago
										 |     REQUIRE(!log.empty());
 | ||
| 
											4 years ago
										 |     int event_cnt = 0, i = 0;
 | ||
|  |     kj::ArrayPtr<const capnp::word> words((capnp::word *)log.data(), log.size() / sizeof(capnp::word));
 | ||
|  |     while (words.size() > 0) {
 | ||
|  |       try {
 | ||
|  |         capnp::FlatArrayMessageReader reader(words);
 | ||
|  |         auto event = reader.getRoot<cereal::Event>();
 | ||
|  |         words = kj::arrayPtr(reader.getEnd(), words.end());
 | ||
|  |         if (i == 0) {
 | ||
|  |           REQUIRE(event.which() == cereal::Event::INIT_DATA);
 | ||
|  |         } else if (i == 1) {
 | ||
|  |           REQUIRE(event.which() == cereal::Event::SENTINEL);
 | ||
|  |           REQUIRE(event.getSentinel().getType() == begin_sentinel);
 | ||
|  |           REQUIRE(event.getSentinel().getSignal() == 0);
 | ||
|  |         } else if (words.size() > 0) {
 | ||
|  |           REQUIRE(event.which() == cereal::Event::CLOCKS);
 | ||
|  |           ++event_cnt;
 | ||
|  |         } else {
 | ||
|  |           // the last event must be SENTINEL
 | ||
|  |           REQUIRE(event.which() == cereal::Event::SENTINEL);
 | ||
|  |           REQUIRE(event.getSentinel().getType() == end_sentinel);
 | ||
|  |           REQUIRE(event.getSentinel().getSignal() == (end_sentinel == SentinelType::END_OF_ROUTE ? 1 : 0));
 | ||
|  |         }
 | ||
|  |         ++i;
 | ||
|  |       } catch (const kj::Exception &ex) {
 | ||
| 
											3 years ago
										 |         INFO("failed parse " << i << " exception :" << ex.getDescription());
 | ||
| 
											4 years ago
										 |         REQUIRE(0);
 | ||
|  |         break;
 | ||
|  |       }
 | ||
|  |     }
 | ||
|  |     REQUIRE(event_cnt == required_event_cnt);
 | ||
|  |   }
 | ||
|  | }
 | ||
|  | 
 | ||
|  | void write_msg(LoggerHandle *logger) {
 | ||
|  |   MessageBuilder msg;
 | ||
|  |   msg.initEvent().initClocks();
 | ||
|  |   auto bytes = msg.toBytes();
 | ||
|  |   lh_log(logger, bytes.begin(), bytes.size(), true);
 | ||
|  | }
 | ||
|  | 
 | ||
|  | TEST_CASE("logger") {
 | ||
|  |   const std::string log_root = "/tmp/test_logger";
 | ||
|  |   system(("rm " + log_root + " -rf").c_str());
 | ||
| 
											4 years ago
										 | 
 | ||
| 
											4 years ago
										 |   ExitHandler do_exit;
 | ||
|  | 
 | ||
|  |   LoggerState logger = {};
 | ||
| 
											4 years ago
										 |   logger_init(&logger, true);
 | ||
| 
											4 years ago
										 |   char segment_path[PATH_MAX] = {};
 | ||
|  |   int segment = -1;
 | ||
|  | 
 | ||
|  |   SECTION("single thread logging & rotation(100 segments, one thread)") {
 | ||
|  |     const int segment_cnt = 100;
 | ||
|  |     for (int i = 0; i < segment_cnt; ++i) {
 | ||
|  |       REQUIRE(logger_next(&logger, log_root.c_str(), segment_path, sizeof(segment_path), &segment) == 0);
 | ||
| 
											4 years ago
										 |       REQUIRE(util::file_exists(std::string(segment_path) + "/rlog.lock"));
 | ||
| 
											4 years ago
										 |       REQUIRE(segment == i);
 | ||
|  |       write_msg(logger.cur_handle);
 | ||
|  |     }
 | ||
|  |     do_exit = true;
 | ||
|  |     do_exit.signal = 1;
 | ||
|  |     logger_close(&logger, &do_exit);
 | ||
|  |     for (int i = 0; i < segment_cnt; ++i) {
 | ||
|  |       verify_segment(log_root + "/" + logger.route_name, i, segment_cnt, 1);
 | ||
|  |     }
 | ||
|  |   }
 | ||
|  |   SECTION("multiple threads logging & rotation(100 segments, 10 threads") {
 | ||
|  |     const int segment_cnt = 100, thread_cnt = 10;
 | ||
|  |     std::atomic<int> event_cnt[segment_cnt] = {};
 | ||
|  |     std::atomic<int> main_segment = -1;
 | ||
|  | 
 | ||
|  |     auto logging_thread = [&]() -> void {
 | ||
|  |       LoggerHandle *lh = logger_get_handle(&logger);
 | ||
|  |       REQUIRE(lh != nullptr);
 | ||
|  |       int segment = main_segment;
 | ||
|  |       int delayed_cnt = 0;
 | ||
|  |       while (!do_exit) {
 | ||
|  |         // write 2 more messages in the current segment and then rotate to the new segment.
 | ||
|  |         if (main_segment > segment && ++delayed_cnt == 2) {
 | ||
|  |           lh_close(lh);
 | ||
|  |           lh = logger_get_handle(&logger);
 | ||
|  |           segment = main_segment;
 | ||
|  |           delayed_cnt = 0;
 | ||
|  |         }
 | ||
|  |         write_msg(lh);
 | ||
|  |         event_cnt[segment] += 1;
 | ||
|  |         usleep(1);
 | ||
|  |       }
 | ||
|  |       lh_close(lh);
 | ||
|  |     };
 | ||
|  | 
 | ||
|  |     // start logging
 | ||
|  |     std::vector<std::thread> threads;
 | ||
|  |     for (int i = 0; i < segment_cnt; ++i) {
 | ||
|  |       REQUIRE(logger_next(&logger, log_root.c_str(), segment_path, sizeof(segment_path), &segment) == 0);
 | ||
|  |       REQUIRE(segment == i);
 | ||
|  |       main_segment = segment;
 | ||
|  |       if (i == 0) {
 | ||
| 
											4 years ago
										 |         for (int j = 0; j < thread_cnt; ++j) {
 | ||
| 
											4 years ago
										 |           threads.push_back(std::thread(logging_thread));
 | ||
|  |         }
 | ||
|  |       }
 | ||
| 
											4 years ago
										 |       for (int j = 0; j < 100; ++j) {
 | ||
| 
											4 years ago
										 |         write_msg(logger.cur_handle);
 | ||
|  |         usleep(1);
 | ||
|  |       }
 | ||
|  |       event_cnt[segment] += 100;
 | ||
|  |     }
 | ||
|  | 
 | ||
|  |     // end logging
 | ||
|  |     for (auto &t : threads) t.join();
 | ||
|  |     do_exit = true;
 | ||
|  |     do_exit.signal = 1;
 | ||
|  |     logger_close(&logger, &do_exit);
 | ||
|  |     REQUIRE(logger.cur_handle->refcnt == 0);
 | ||
|  | 
 | ||
|  |     for (int i = 0; i < segment_cnt; ++i) {
 | ||
|  |       verify_segment(log_root + "/" + logger.route_name, i, segment_cnt, event_cnt[i]);
 | ||
|  |     }
 | ||
|  |   }
 | ||
|  | }
 |