blob: 2819a0b25b7d0610b4e675463b3ceb0e4675aca8 [file] [log] [blame]
Stephan Massaltf84cf812019-12-31 14:14:50 -08001#include <iomanip>
2#include <iostream>
James Kuszmaulc7bb1652022-06-22 11:18:49 -07003#include <queue>
Stephan Massaltf84cf812019-12-31 14:14:50 -08004
Austin Schuhc99e1392021-03-30 22:59:24 -07005#include "absl/strings/str_format.h"
Philipp Schrader790cb542023-07-05 21:06:52 -07006#include "gflags/gflags.h"
7
Austin Schuhb06f03b2021-02-17 22:00:37 -08008#include "aos/events/logging/log_reader.h"
Stephan Massaltf84cf812019-12-31 14:14:50 -08009#include "aos/events/simulated_event_loop.h"
10#include "aos/init.h"
11#include "aos/json_to_flatbuffer.h"
12#include "aos/time/time.h"
Stephan Massaltf84cf812019-12-31 14:14:50 -080013
Stephan Massaltf84cf812019-12-31 14:14:50 -080014DEFINE_string(
15 name, "",
16 "Name to match for printing out channels. Empty means no name filter.");
17
Austin Schuh6f3babe2020-01-26 20:34:50 -080018DEFINE_string(node, "", "Node to print stats out for.");
19
milind upadhyay38fe3cd2021-03-27 15:30:53 -070020DEFINE_bool(excessive_size_only, false,
21 "Only print channels that have a set max message size that is more "
22 "than double of the max message size.");
23
Philipp Schradera1214ef2023-08-10 11:50:22 -070024DEFINE_double(
25 run_for, 0.0,
26 "If set to a positive value, only process the log for this many seconds. "
27 "Otherwise, process the log until the end of the log.");
28
Philipp Schrader8283e362023-08-10 11:57:24 -070029DEFINE_bool(
30 print_repack_size_diffs, false,
31 "Analyze how many bytes could be saved in each message when converted to "
32 "JSON and back. This can be helpful to identify code that is generating "
33 "inefficiently packed flatbuffer messages.");
34
Milind Upadhyay3d13a1a2022-08-04 10:58:32 -070035// This class implements a histogram for tracking message period
36// percentiles.
Austin Schuhc99e1392021-03-30 22:59:24 -070037class Histogram {
38 public:
39 Histogram(size_t buckets = 1024)
40 : max_value_bucket_(0.01), values_(buckets, 0.0), counts_(buckets, 0) {}
41
42 // Adds a new sample to the histogram, potentially downsampling the existing
43 // data.
44 void Add(double value) {
45 if (value < max_value_bucket_) {
46 const ssize_t bucket = static_cast<size_t>(
47 std::floor(value * values_.size() / max_value_bucket_));
48 CHECK_GE(bucket, 0);
49 CHECK_LT(bucket, static_cast<ssize_t>(values_.size()));
50 values_[bucket] += value;
51 if (all_counts_ == 0 || value > max_value_) {
52 max_value_ = value;
53 }
54 if (all_counts_ == 0 || value < min_value_) {
55 min_value_ = value;
56 }
57 ++counts_[bucket];
58 ++all_counts_;
59 } else {
60 // Double all the bucket sizes by merging adjacent buckets and doubling
61 // the max value. If this isn't enough, we'll recurse inside Add and
62 // do it again until it fits.
63 max_value_bucket_ *= 2.0;
64 for (size_t bucket = 0; bucket < values_.size() / 2; ++bucket) {
65 values_[bucket] = values_[bucket * 2] + values_[bucket * 2 + 1];
66 counts_[bucket] = counts_[bucket * 2] + counts_[bucket * 2 + 1];
67 }
68 for (size_t bucket = values_.size() / 2; bucket < values_.size();
69 ++bucket) {
70 values_[bucket] = 0.0;
71 counts_[bucket] = 0;
72 }
73 Add(value);
74 }
75 }
76
77 // Prints out the percentiles for a couple of critical numbers.
78 std::string Percentile() const {
79 const size_t percentile5 = all_counts_ / 20;
80 double percentile5_value = 0.0;
81 const size_t percentile50 = all_counts_ / 2;
82 double percentile50_value = 0.0;
83 const size_t percentile95 = all_counts_ - percentile5;
84 double percentile95_value = 0.0;
85
86 size_t count = 0;
87 for (size_t i = 0; i < values_.size(); ++i) {
88 if (count < percentile5 && count + counts_[i] >= percentile5) {
89 percentile5_value = values_[i] / counts_[i];
90 }
91 if (count < percentile50 && count + counts_[i] >= percentile50) {
92 percentile50_value = values_[i] / counts_[i];
93 }
94 if (count < percentile95 && count + counts_[i] >= percentile95) {
95 percentile95_value = values_[i] / counts_[i];
96 }
97 count += counts_[i];
98 }
99
100 // Assume here that these are periods in seconds. Convert to ms for
101 // readability. This isn't super generic, but that's fine for now.
102 return absl::StrFormat(
103 "[max %.3fms 95%%:%.3fms 50%%:%.3fms 5%%:%.3fms min %.3fms]",
104 max_value_ * 1000., percentile95_value * 1000.,
105 percentile50_value * 1000., percentile5_value * 1000.,
106 min_value_ * 1000.);
107 }
108
109 private:
110 // The size of the largest bucket. Used to figure out which bucket something
111 // goes into.
112 double max_value_bucket_;
113 // Max and min values overall we have seen.
114 double max_value_ = 0;
115 double min_value_ = 0;
116 // A list of the sum of values and counts for those per bucket.
117 std::vector<double> values_;
118 std::vector<size_t> counts_;
119 // Total number of samples.
120 size_t all_counts_ = 0;
121};
122
123class ChannelStats {
124 public:
Milind Upadhyay3d13a1a2022-08-04 10:58:32 -0700125 ChannelStats(const aos::Channel *channel, const aos::Node *destination_node,
Philipp Schrader8283e362023-08-10 11:57:24 -0700126 aos::SimulatedEventLoopFactory *factory,
127 const reflection::Schema *schema)
Milind Upadhyay3d13a1a2022-08-04 10:58:32 -0700128 : channel_(channel),
129 config_(factory->configuration()),
130 factory_(factory),
Philipp Schrader8283e362023-08-10 11:57:24 -0700131 schema_(CHECK_NOTNULL(schema)),
132 destination_node_(destination_node),
133 flatbuffer_type_(schema) {
Milind Upadhyay3d13a1a2022-08-04 10:58:32 -0700134 // Multi-node channel
135 if (channel_->has_source_node() && channel_->has_destination_nodes() &&
136 channel_->destination_nodes()->size() > 0) {
137 CHECK(destination_node_)
138 << "Should have destination node for forwarded channel: "
139 << channel_->name()->string_view();
140 source_node_ = aos::configuration::GetNode(
141 config_, channel_->source_node()->string_view());
142 CHECK(source_node_) << "Node not in config: "
143 << channel_->source_node()->string_view();
144 }
145 }
Austin Schuhc99e1392021-03-30 22:59:24 -0700146
147 // Adds a sample to the statistics.
148 void Add(const aos::Context &context) {
Philipp Schrader8283e362023-08-10 11:57:24 -0700149 if (context.size > 0 && context.data != nullptr) {
150 // Perform a very naive repacking of the message. Ideally, we'd use
151 // something like RecursiveCopyFlatBuffer that works with schemas.
152 // For now, this is good enough.
153 const std::string json = aos::FlatbufferToJson(
154 schema_, static_cast<const uint8_t *>(context.data));
155 flatbuffers::DetachedBuffer buffer =
156 aos::JsonToFlatbuffer(json, flatbuffer_type_);
157
158 const ssize_t packed_size_reduction = static_cast<ssize_t>(context.size) -
159 static_cast<ssize_t>(buffer.size());
160 max_packed_size_reduction_ =
161 std::max(max_packed_size_reduction_, packed_size_reduction);
162 total_packed_size_reduction_ += packed_size_reduction;
163 }
164
Austin Schuhc99e1392021-03-30 22:59:24 -0700165 max_message_size_ = std::max(max_message_size_, context.size);
166 total_message_size_ += context.size;
167 total_num_messages_++;
168 channel_end_time_ = context.realtime_event_time;
169 first_message_time_ =
170 std::min(first_message_time_, context.monotonic_event_time);
171 if (current_message_time_ != aos::monotonic_clock::min_time) {
172 histogram_.Add(std::chrono::duration<double>(
173 context.monotonic_event_time - current_message_time_)
174 .count());
175 }
176 current_message_time_ = context.monotonic_event_time;
James Kuszmaulc7bb1652022-06-22 11:18:49 -0700177 channel_storage_duration_messages_.push(current_message_time_);
178 while (channel_storage_duration_messages_.front() +
Austin Schuhfff9c3a2023-06-16 18:48:23 -0700179 aos::configuration::ChannelStorageDuration(config_, channel_) <=
James Kuszmaulc7bb1652022-06-22 11:18:49 -0700180 current_message_time_) {
181 channel_storage_duration_messages_.pop();
182 }
183 max_messages_per_period_ = std::max(
184 max_messages_per_period_, channel_storage_duration_messages_.size());
Milind Upadhyay3d13a1a2022-08-04 10:58:32 -0700185
186 // Only count latency if this message is forwarded and the remote time was
187 // filled
188 if (source_node_ != nullptr &&
189 context.monotonic_remote_time != context.monotonic_event_time) {
190 // Convert times to distributed clock so they can be compared across nodes
191 const aos::distributed_clock::time_point remote_time =
192 factory_->GetNodeEventLoopFactory(source_node_)
193 ->ToDistributedClock(context.monotonic_remote_time);
194
195 const aos::distributed_clock::time_point event_time =
196 factory_->GetNodeEventLoopFactory(destination_node_)
197 ->ToDistributedClock(context.monotonic_event_time);
198 // Add the current latency to the sum
199 total_latency_ += event_time - remote_time;
200
201 num_messages_with_remote_++;
202 }
Austin Schuhc99e1392021-03-30 22:59:24 -0700203 }
204
205 std::string Percentile() const { return histogram_.Percentile(); }
206
207 double SecondsActive() const {
208 return aos::time::DurationInSeconds(current_message_time_ -
209 first_message_time_);
210 }
211
212 size_t max_message_size() const { return max_message_size_; }
213 size_t total_num_messages() const { return total_num_messages_; }
214
Philipp Schrader8283e362023-08-10 11:57:24 -0700215 ssize_t max_packed_size_reduction() const {
216 return max_packed_size_reduction_;
217 }
218
Austin Schuhc99e1392021-03-30 22:59:24 -0700219 double avg_messages_per_sec() const {
220 return total_num_messages_ / SecondsActive();
221 }
James Kuszmaulc7bb1652022-06-22 11:18:49 -0700222 double max_messages_per_sec() const {
223 return max_messages_per_period_ /
224 std::min(SecondsActive(),
Austin Schuhfff9c3a2023-06-16 18:48:23 -0700225 std::chrono::duration<double>(
226 aos::configuration::ChannelStorageDuration(config_,
227 channel_))
228 .count());
James Kuszmaulc7bb1652022-06-22 11:18:49 -0700229 }
Austin Schuhc99e1392021-03-30 22:59:24 -0700230 size_t avg_message_size() const {
231 return total_message_size_ / total_num_messages_;
232 }
James Kuszmaul6abc7442021-10-24 13:46:04 -0700233 size_t avg_message_bandwidth() const {
234 return total_message_size_ / SecondsActive();
235 }
Austin Schuhc99e1392021-03-30 22:59:24 -0700236
Philipp Schrader8283e362023-08-10 11:57:24 -0700237 ssize_t avg_packed_size_reduction() const {
238 return total_packed_size_reduction_ / total_num_messages_;
239 }
240
Austin Schuhc99e1392021-03-30 22:59:24 -0700241 aos::realtime_clock::time_point channel_end_time() const {
242 return channel_end_time_;
243 }
244
245 const aos::Channel *channel() const { return channel_; }
246
Milind Upadhyay3d13a1a2022-08-04 10:58:32 -0700247 std::string AvgLatency() {
248 if (num_messages_with_remote_ == 0) {
249 return "";
250 }
251
252 std::stringstream ss;
253 ss << std::setprecision(3);
254
255 const double avg_latency =
256 std::chrono::duration<double, std::milli>(total_latency_).count() /
257 num_messages_with_remote_;
258 ss << '[' << source_node_->name()->string_view() << "->"
259 << destination_node_->name()->string_view() << " " << avg_latency
260 << "ms latency avg]";
261
262 return ss.str();
263 }
264
Austin Schuhc99e1392021-03-30 22:59:24 -0700265 private:
Stephan Massaltf84cf812019-12-31 14:14:50 -0800266 // pointer to the channel for which stats are collected
Austin Schuhc99e1392021-03-30 22:59:24 -0700267 const aos::Channel *channel_;
James Kuszmaulc7bb1652022-06-22 11:18:49 -0700268 const aos::Configuration *config_;
Milind Upadhyay3d13a1a2022-08-04 10:58:32 -0700269 aos::SimulatedEventLoopFactory *factory_;
Philipp Schrader8283e362023-08-10 11:57:24 -0700270 const reflection::Schema *const schema_;
Austin Schuhc99e1392021-03-30 22:59:24 -0700271 aos::realtime_clock::time_point channel_end_time_ =
Stephan Massaltf84cf812019-12-31 14:14:50 -0800272 aos::realtime_clock::min_time;
Austin Schuhc99e1392021-03-30 22:59:24 -0700273 aos::monotonic_clock::time_point first_message_time_ =
Stephan Massaltf84cf812019-12-31 14:14:50 -0800274 // needs to be higher than time in the logfile!
275 aos::monotonic_clock::max_time;
Austin Schuhc99e1392021-03-30 22:59:24 -0700276 aos::monotonic_clock::time_point current_message_time_ =
Stephan Massaltf84cf812019-12-31 14:14:50 -0800277 aos::monotonic_clock::min_time;
Austin Schuhc99e1392021-03-30 22:59:24 -0700278
James Kuszmaulc7bb1652022-06-22 11:18:49 -0700279 // Buffer of the last N seconds of messages, for N = channel_storage_duration.
280 std::queue<aos::monotonic_clock::time_point>
281 channel_storage_duration_messages_;
282 size_t max_messages_per_period_ = 0;
283
Stephan Massaltf84cf812019-12-31 14:14:50 -0800284 // channel stats to collect per channel
Austin Schuhc99e1392021-03-30 22:59:24 -0700285 int total_num_messages_ = 0;
286 size_t max_message_size_ = 0;
287 size_t total_message_size_ = 0;
288
Philipp Schrader8283e362023-08-10 11:57:24 -0700289 // The size reduction (in bytes) after a naive repacking. A negative number
290 // indicates that the repacking generated a _bigger_ message than the
291 // original message.
292 ssize_t max_packed_size_reduction_ = 0;
293 ssize_t total_packed_size_reduction_ = 0;
294
Milind Upadhyay3d13a1a2022-08-04 10:58:32 -0700295 // Count of messages which had remote timestamps
296 size_t num_messages_with_remote_ = 0;
297 // Sum of latencies in all messages sent on this channel if multinode
298 aos::distributed_clock::duration total_latency_;
299
Austin Schuhc99e1392021-03-30 22:59:24 -0700300 Histogram histogram_;
Milind Upadhyay3d13a1a2022-08-04 10:58:32 -0700301
302 const aos::Node *source_node_ = nullptr;
303 const aos::Node *destination_node_;
Philipp Schrader8283e362023-08-10 11:57:24 -0700304
305 const aos::FlatbufferType flatbuffer_type_;
Stephan Massaltf84cf812019-12-31 14:14:50 -0800306};
307
308struct LogfileStats {
309 // All relevant stats on to logfile level
310 size_t logfile_length = 0;
311 int total_log_messages = 0;
312 aos::realtime_clock::time_point logfile_end_time =
313 aos::realtime_clock::min_time;
314};
315
316int main(int argc, char **argv) {
317 gflags::SetUsageMessage(
Ravago Jones8bab1842020-12-12 17:36:39 -0800318 "Usage: \n"
319 " log_stats [args] logfile1 logfile2 ...\n"
Stephan Massaltf84cf812019-12-31 14:14:50 -0800320 "This program provides statistics on a given log file. Supported "
321 "statistics are:\n"
322 " - Logfile start time;\n"
323 " - Total messages per channel/type;\n"
324 " - Max message size per channel/type;\n"
325 " - Frequency of messages per second;\n"
Milind Upadhyay3d13a1a2022-08-04 10:58:32 -0700326 " - Total logfile size and number of messages;\n"
327 " - Average latency per forwarded channel/type.\n"
328 "Pass a logfile (path/filename) and use --name "
Stephan Massaltf84cf812019-12-31 14:14:50 -0800329 "flag to specify a channel to listen on.");
330
331 aos::InitGoogle(&argc, &argv);
332
Ravago Jones8bab1842020-12-12 17:36:39 -0800333 if (argc < 2) {
334 LOG(FATAL) << "Expected at least 1 logfile as an argument.";
335 }
336
Austin Schuh95460cc2023-06-26 11:53:10 -0700337 aos::logger::LogReader reader(
338 aos::logger::SortParts(aos::logger::FindLogs(argc, argv)));
Ravago Jones8bab1842020-12-12 17:36:39 -0800339
Stephan Massaltf84cf812019-12-31 14:14:50 -0800340 LogfileStats logfile_stats;
341 std::vector<ChannelStats> channel_stats;
342
Stephan Massaltf84cf812019-12-31 14:14:50 -0800343 aos::SimulatedEventLoopFactory log_reader_factory(reader.configuration());
344 reader.Register(&log_reader_factory);
345
Austin Schuh6f3babe2020-01-26 20:34:50 -0800346 const aos::Node *node = nullptr;
347
348 if (aos::configuration::MultiNode(reader.configuration())) {
349 if (FLAGS_node.empty()) {
350 LOG(INFO) << "Need a --node specified. The log file has:";
Austin Schuh07676622021-01-21 18:59:17 -0800351 for (const aos::Node *node : reader.LoggedNodes()) {
Austin Schuh6f3babe2020-01-26 20:34:50 -0800352 LOG(INFO) << " " << node->name()->string_view();
353 }
Austin Schuh8c7f14b2021-01-21 19:01:54 -0800354 reader.Deregister();
Austin Schuh6f3babe2020-01-26 20:34:50 -0800355 return 1;
356 } else {
357 node = aos::configuration::GetNode(reader.configuration(), FLAGS_node);
358 }
359 }
360
Stephan Massaltf84cf812019-12-31 14:14:50 -0800361 // Make an eventloop for retrieving stats
362 std::unique_ptr<aos::EventLoop> stats_event_loop =
Austin Schuh6f3babe2020-01-26 20:34:50 -0800363 log_reader_factory.MakeEventLoop("logstats", node);
Stephan Massaltf84cf812019-12-31 14:14:50 -0800364 stats_event_loop->SkipTimingReport();
Tyler Chatow67ddb032020-01-12 14:30:04 -0800365 stats_event_loop->SkipAosLog();
Stephan Massaltf84cf812019-12-31 14:14:50 -0800366
367 // Read channel info and store in vector
368 bool found_channel = false;
369 const flatbuffers::Vector<flatbuffers::Offset<aos::Channel>> *channels =
370 reader.configuration()->channels();
371
372 int it = 0; // iterate through the channel_stats
373 for (flatbuffers::uoffset_t i = 0; i < channels->size(); i++) {
374 const aos::Channel *channel = channels->Get(i);
Austin Schuh76db3fa2020-03-07 17:02:44 -0800375 if (!aos::configuration::ChannelIsReadableOnNode(
376 channel, stats_event_loop->node())) {
377 continue;
Stephan Massaltf84cf812019-12-31 14:14:50 -0800378 }
Austin Schuh76db3fa2020-03-07 17:02:44 -0800379
380 if (channel->name()->string_view().find(FLAGS_name) == std::string::npos) {
381 continue;
382 }
383
384 // Add a record to the stats vector.
Philipp Schrader8283e362023-08-10 11:57:24 -0700385 channel_stats.push_back(ChannelStats{
386 channel, node, &log_reader_factory,
387 aos::configuration::GetSchema(reader.configuration(),
388 channel->type()->string_view())});
Austin Schuh76db3fa2020-03-07 17:02:44 -0800389 // Lambda to read messages and parse for information
Philipp Schrader8283e362023-08-10 11:57:24 -0700390 auto watcher = [&logfile_stats, &channel_stats,
391 it](const aos::Context &context) {
392 channel_stats[it].Add(context);
Austin Schuhc99e1392021-03-30 22:59:24 -0700393
Philipp Schrader8283e362023-08-10 11:57:24 -0700394 // Update the overall logfile statistics
395 logfile_stats.logfile_length += context.size;
396 };
397 if (FLAGS_print_repack_size_diffs) {
398 stats_event_loop->MakeRawWatcher(
399 channel, std::bind(watcher, ::std::placeholders::_1));
400 } else {
401 stats_event_loop->MakeRawNoArgWatcher(channel, watcher);
402 }
Austin Schuh76db3fa2020-03-07 17:02:44 -0800403 it++;
404 // TODO (Stephan): Frequency of messages per second
405 // - Sliding window
406 // - Max / Deviation
407 found_channel = true;
Stephan Massaltf84cf812019-12-31 14:14:50 -0800408 }
409 if (!found_channel) {
410 LOG(FATAL) << "Could not find any channels";
411 }
412
Philipp Schradera1214ef2023-08-10 11:50:22 -0700413 if (FLAGS_run_for > 0.0) {
414 log_reader_factory.RunFor(
415 std::chrono::duration_cast<std::chrono::nanoseconds>(
416 std::chrono::duration<double>(FLAGS_run_for)));
417 } else {
418 log_reader_factory.Run();
419 }
Stephan Massaltf84cf812019-12-31 14:14:50 -0800420
milind upadhyay38fe3cd2021-03-27 15:30:53 -0700421 std::cout << std::endl;
Austin Schuhc99e1392021-03-30 22:59:24 -0700422
Stephan Massaltf84cf812019-12-31 14:14:50 -0800423 // Print out the stats per channel and for the logfile
424 for (size_t i = 0; i != channel_stats.size(); i++) {
Austin Schuhc99e1392021-03-30 22:59:24 -0700425 if (!FLAGS_excessive_size_only ||
426 (channel_stats[i].max_message_size() * 2) <
427 static_cast<size_t>(channel_stats[i].channel()->max_size())) {
428 if (channel_stats[i].total_num_messages() > 0) {
429 std::cout << channel_stats[i].channel()->name()->string_view() << " "
430 << channel_stats[i].channel()->type()->string_view() << "\n";
milind upadhyay38fe3cd2021-03-27 15:30:53 -0700431
Austin Schuhc99e1392021-03-30 22:59:24 -0700432 logfile_stats.total_log_messages +=
433 channel_stats[i].total_num_messages();
434 logfile_stats.logfile_end_time =
435 std::max(logfile_stats.logfile_end_time,
436 channel_stats[i].channel_end_time());
437
milind upadhyay38fe3cd2021-03-27 15:30:53 -0700438 if (!FLAGS_excessive_size_only) {
Austin Schuhc99e1392021-03-30 22:59:24 -0700439 std::cout << " " << channel_stats[i].total_num_messages()
440 << " msgs, " << channel_stats[i].avg_messages_per_sec()
James Kuszmaulc7bb1652022-06-22 11:18:49 -0700441 << "hz avg, " << channel_stats[i].max_messages_per_sec()
442 << "hz max, " << channel_stats[i].channel()->frequency()
443 << "hz configured max";
milind upadhyay38fe3cd2021-03-27 15:30:53 -0700444 }
Austin Schuhc99e1392021-03-30 22:59:24 -0700445 std::cout << " " << channel_stats[i].avg_message_size()
Austin Schuh60e77942022-05-16 17:48:24 -0700446 << " bytes avg, " << channel_stats[i].avg_message_bandwidth()
James Kuszmaul6abc7442021-10-24 13:46:04 -0700447 << " bytes/sec avg, " << channel_stats[i].max_message_size()
Austin Schuhc99e1392021-03-30 22:59:24 -0700448 << " bytes max / " << channel_stats[i].channel()->max_size()
Milind Upadhyay3d13a1a2022-08-04 10:58:32 -0700449 << "bytes, " << channel_stats[i].Percentile() << ", "
450 << channel_stats[i].AvgLatency();
milind upadhyay38fe3cd2021-03-27 15:30:53 -0700451 std::cout << std::endl;
Philipp Schrader8283e362023-08-10 11:57:24 -0700452 if (FLAGS_print_repack_size_diffs) {
453 std::cout << " " << channel_stats[i].avg_packed_size_reduction()
454 << " bytes packed reduction avg, "
455 << channel_stats[i].max_packed_size_reduction()
456 << " bytes packed reduction max";
457 std::cout << std::endl;
458 }
milind upadhyay38fe3cd2021-03-27 15:30:53 -0700459 }
Stephan Massaltf84cf812019-12-31 14:14:50 -0800460 }
461 }
462 std::cout << std::setfill('-') << std::setw(80) << "-"
Ravago Jones8bab1842020-12-12 17:36:39 -0800463 << "\nLogfile statistics:\n"
Austin Schuh76db3fa2020-03-07 17:02:44 -0800464 << "Log starts at:\t" << reader.realtime_start_time(node) << "\n"
Stephan Massaltf84cf812019-12-31 14:14:50 -0800465 << "Log ends at:\t" << logfile_stats.logfile_end_time << "\n"
466 << "Log file size:\t" << logfile_stats.logfile_length << "\n"
467 << "Total messages:\t" << logfile_stats.total_log_messages << "\n";
468
469 // Cleanup the created processes
470 reader.Deregister();
Austin Schuhae87e312020-08-01 16:15:01 -0700471
Stephan Massaltf84cf812019-12-31 14:14:50 -0800472 return 0;
473}