test-multi-thread-logs.cpp
Go to the documentation of this file.
1 // License: Apache 2.0. See LICENSE file in root directory.
2 // Copyright(c) 2020 Intel Corporation. All Rights Reserved.
3 
4 //#cmake:add-file log-common.h
5 #include "log-common.h"
6 #include <atomic>
7 
8 
9 #ifdef EASYLOGGINGPP_ASYNC
10 
11 std::atomic<int> atomic_integer(0);
12 std::chrono::milliseconds max_time = (std::chrono::milliseconds)0;
13 std::chrono::milliseconds min_time = (std::chrono::milliseconds)50;
14 std::chrono::milliseconds avg_time = (std::chrono::milliseconds)0;
15 int max_time_iteration = -1;
16 const int number_of_iterations = 10000;
17 const int number_of_threads = 10;
18 std::mutex stats_mutex;
19 //thresholds
20 const int checked_log_write_time = 10; //ms
21 const int required_log_write_time = 50; //ms
22 const int checked_lag_time = 15; //ms
23 const int required_lag_time = 50; //ms
24 //----------------------- HELPER METHODS START -------------------------
25 // input line example:
26 // 14/01 16:53:19,866 DEBUG [884] (rs.cpp:2579) atomic integer = 7
27 int get_time_from_line(std::string line)
28 {
29  auto time_word = line.substr(7, 12);
30  auto hours = stoi(time_word.substr(0, 2));
31  auto minutes = stoi(time_word.substr(3, 5));
32  auto seconds = stoi(time_word.substr(6, 8));
33  auto ms = stoi(time_word.substr(9, 12));
34  //std::cout << "h:m:s:ms = " << hours << ":" << minutes << ":" << seconds << ":" << ms << std::endl;
35  return (ms + seconds * 1000 + minutes * 1000 * 60 + hours * 1000 * 60 * 60);
36 }
37 
38 
39 std::string get_last_line(std::ifstream& in)
40 {
42  for (int i = 0; i < (number_of_iterations * number_of_threads) - 1; ++i)
43  {
44  getline(in, line);
45  }
46  return line;
47 }
48 
49 // inputs are use as 2 points that define a line:
50 // first_time_ms, first_value : x1, y1
51 // second_time_ms, second_value : x2, y2
52 // value_in_log : y3
53 // return value will be x3
54 int get_log_ideal_time(int first_time_ms, int first_value, int second_time_ms, int second_value, int value_in_log)
55 {
56  int ideal_time = value_in_log * (second_time_ms - first_time_ms) / (second_value - first_value) +
57  (first_time_ms * (second_value / (second_value - first_value))) - second_time_ms * (first_value / (second_value - first_value));
58 
59  return ideal_time;
60 }
61 
62 std::pair<unsigned int, int> get_next_50_lines_time_val(std::ifstream& ifs)
63 {
64  unsigned int avg_time_ms = -1;
65  int avg_value = -1;
66  for (int i = 0; i < 50; ++i) {
68  getline(ifs, line);
69  avg_time_ms += get_time_from_line(line);
70  avg_value += stoi(line.substr(line.find_last_of(" ") + 1));
71  }
72  avg_time_ms /= 50;
73  avg_value /= 50;
74  return std::make_pair(avg_time_ms, avg_value);
75 }
76 
77 //----------------------- HELPER METHODS END ---------------------------
78 
79 TEST_CASE("async logger", "[log][async_log]")
80 {
81  size_t n_callbacks = 0;
82  auto callback = [&](rs2_log_severity severity, rs2::log_message const& msg)
83  {
84  ++n_callbacks;
85  std::cout << msg.raw();
86  };
87 
88  auto func = [](int required_value) {
89  int iterations = 0;
90 
91  while (iterations < number_of_iterations)
92  {
93  std::stringstream ss;
94  ss << "atomic integer = " << ++atomic_integer;
95  auto start_ms = std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::system_clock::now().time_since_epoch());
96  rs2::log(RS2_LOG_SEVERITY_DEBUG, ss.str().c_str());
97  std::chrono::milliseconds ms = std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::system_clock::now().time_since_epoch());
98  std::chrono::milliseconds delta_ms = ms - start_ms;
99  {
100  std::lock_guard<std::mutex> lock(stats_mutex);
101  if (delta_ms > max_time) max_time = delta_ms;
102  if (delta_ms < min_time) min_time = delta_ms;
103  avg_time += delta_ms;
104  }
105  ++iterations;
106  }
107  };
108  const char* log_file_path = ".//multi-thread-logs.log";
109  try
110  {
111  remove(log_file_path);
112  }
113  catch (...) {}
115 
116  std::vector<std::thread> threads;
117 
118  for (int i = 0; i < number_of_threads; ++i)
119  {
120  threads.push_back(std::thread(func, i + 1));
121  }
122 
123  for (auto&&t : threads)
124  {
125  if (t.joinable()) t.join();
126  }
127 
128  std::this_thread::sleep_for(std::chrono::milliseconds(5000));
129  std::stringstream ss;
130  CAPTURE(max_time);
131  CAPTURE(min_time);
132  CAPTURE(avg_time);
133  // The threshold in the CHECK_NOFAIL statement should be filled when running in "normal PC"
134  // The threshold in the REQUIRE statement should be filled when running CI tests (less cores are available there)
135  CHECK_NOFAIL(max_time.count() < checked_log_write_time);
136  REQUIRE(max_time.count() < required_log_write_time);
137 
138  // checking logs correctness - that all the integer's values have been logged
139  {
140  // preparing array of number of iterations * number of threads
141  std::array<char, number_of_iterations * number_of_threads> check_table;
142  // setting all cells to 0
143  memset(&check_table[0], 0, check_table.size());
144  // marking cells at the position of each log integer value to 1
145  std::ifstream check_file(log_file_path, std::ios::in);
146  if (check_file.good())
147  {
149  while (std::getline(check_file, line))
150  {
151  auto value_in_log = stoi(line.substr(line.find_last_of(" ")));
152  REQUIRE(value_in_log > 0);
153  REQUIRE((value_in_log - 1) < (number_of_iterations* number_of_threads));
154  check_table.at(value_in_log - 1) = 1;
155  }
156  // checking that all cells have value 1, which means that all logs have been sent and received
157  std::for_each(check_table.cbegin(), check_table.cend(), [](const char c) {
158  REQUIRE(c == 1);
159  });
160  check_file.close();
161  }
162  }
163 
164  // checking logs order in means of logging time
165  {
166  std::ifstream check_file(log_file_path, std::ios::in);
167  int overall_ms = 0;
168  int overall_logs = 0;
169  if (check_file.good())
170  {
171  // get data to calculate "ideal" line
172  // using average from 50 first logs as one point,
173  // and average of 50 last points as second point
174  auto first_50_pair = get_next_50_lines_time_val(check_file);
175  unsigned int first_50_avg_time_ms = first_50_pair.first;
176  int first_50_avg_value = first_50_pair.second;
177 
179  // the aim of this loop is to get the file's cursor before the 50 last lines,
180  // considering it is already after the 50 first lines
181  for (int i = 0; i < (number_of_iterations * number_of_threads) - 1 - 50 - 50; ++i)
182  getline(check_file, line);
183 
184  auto last_50_pair = get_next_50_lines_time_val(check_file);
185  unsigned int last_50_avg_time_ms = last_50_pair.first;
186  int last_50_avg_value = last_50_pair.second;
187 
188  // clear and return to start of the file
189  check_file.clear();
190  check_file.seekg(0);
191 
192  // go over each line and check that its logging time is not "far away" in means of time than the "ideal" time
193  int max_delta = 0;
194  while (getline(check_file, line))
195  {
196  auto value_in_log = stoi(line.substr(line.find_last_of(" ")));
197  auto log_time_ms = get_time_from_line(line);
198  auto log_ideal_time_ms = get_log_ideal_time(first_50_avg_time_ms, first_50_avg_value, last_50_avg_time_ms, last_50_avg_value, value_in_log);
199  auto delta = std::abs(log_ideal_time_ms - log_time_ms);
200  if (delta > max_delta) max_delta = delta;
201  }
202  // The threshold in the CHECK_NOFAIL statement should be filled when running in "normal PC"
203  // The threshold in the REQUIRE statement should be filled when running CI tests (less cores are available there)
204  CHECK_NOFAIL(max_delta < checked_lag_time);
205  REQUIRE(max_delta < required_lag_time);
206  }
207  check_file.close();
208  }
209 
210  // checking number of logs throughput per ms
211  {
212  std::ifstream check_file(log_file_path, std::ios::in);
213  int overall_ms = 0;
214  int overall_logs = 0;
215  if (check_file.good())
216  {
218  while (std::getline(check_file, line))
219  {
220  auto previous_time = get_time_from_line(line);
221  int logs_counter_in_one_ms = 1;
222  while (std::getline(check_file, line))
223  {
224  auto current_time = get_time_from_line(line);
225  if (current_time != previous_time)
226  {
227  overall_logs += logs_counter_in_one_ms;
228  ++overall_ms;
229  break;
230  }
231  else
232  ++logs_counter_in_one_ms;
233  }
234  }
235  }
236  check_file.close();
237  float average_logs_in_one_ms = (float)overall_logs / overall_ms;
238  REQUIRE(average_logs_in_one_ms >= 20.0F);
239  }
240 }
241 #endif
242 
243 
static const textual_icon lock
Definition: model-views.h:218
GLenum GLuint GLenum severity
#define TEST_CASE(...)
Definition: catch.hpp:17434
long long current_time()
void log(rs2_log_severity severity, const char *message)
Definition: rs.hpp:149
GLsizei const GLchar *const * string
GLdouble t
std::ostream & cout()
const GLubyte * c
Definition: glext.h:12690
#define CAPTURE(...)
Definition: catch.hpp:17432
REQUIRE(n_callbacks==1)
def callback(frame)
Definition: t265_stereo.py:91
void log_to_file(rs2_log_severity min_severity, const char *file_path=nullptr)
Definition: rs.hpp:26
#define CHECK_NOFAIL(...)
Definition: catch.hpp:17413
GLenum func
GLuint in
Definition: glext.h:8859
int stoi(const std::string &value)
int i
rs2_log_severity
Severity of the librealsense logger.
Definition: rs_types.h:153


librealsense2
Author(s): Sergey Dorodnicov , Doron Hirshberg , Mark Horn , Reagan Lopez , Itay Carpis
autogenerated on Mon May 3 2021 02:50:11