blob: 88bf15cfa458116b5bdf98096309afbec6434c82 [file] [log] [blame]
Stephan Massaltf84cf812019-12-31 14:14:50 -08001#include <iomanip>
2#include <iostream>
3
4#include "aos/events/logging/logger.h"
5#include "aos/events/simulated_event_loop.h"
6#include "aos/init.h"
7#include "aos/json_to_flatbuffer.h"
8#include "aos/time/time.h"
9#include "gflags/gflags.h"
10
11DEFINE_string(logfile, "/tmp/logfile.bfbs",
12 "Name and path of the logfile to read from.");
13DEFINE_string(
14 name, "",
15 "Name to match for printing out channels. Empty means no name filter.");
16
Austin Schuh6f3babe2020-01-26 20:34:50 -080017DEFINE_string(node, "", "Node to print stats out for.");
18
Stephan Massaltf84cf812019-12-31 14:14:50 -080019// define struct to hold all information
20struct ChannelStats {
21 // pointer to the channel for which stats are collected
22 const aos::Channel *channel;
23 aos::realtime_clock::time_point channel_end_time =
24 aos::realtime_clock::min_time;
25 aos::monotonic_clock::time_point first_message_time =
26 // needs to be higher than time in the logfile!
27 aos::monotonic_clock::max_time;
28 aos::monotonic_clock::time_point current_message_time =
29 aos::monotonic_clock::min_time;
30 // channel stats to collect per channel
31 int total_num_messages = 0;
32 size_t max_message_size = 0;
33 size_t total_message_size = 0;
34 double avg_messages_sec = 0.0; // TODO in Lambda, now in stats overview.
35 double max_messages_sec = 0.0; // TODO in Lambda
36};
37
38struct LogfileStats {
39 // All relevant stats on to logfile level
40 size_t logfile_length = 0;
41 int total_log_messages = 0;
42 aos::realtime_clock::time_point logfile_end_time =
43 aos::realtime_clock::min_time;
44};
45
46int main(int argc, char **argv) {
47 gflags::SetUsageMessage(
48 "This program provides statistics on a given log file. Supported "
49 "statistics are:\n"
50 " - Logfile start time;\n"
51 " - Total messages per channel/type;\n"
52 " - Max message size per channel/type;\n"
53 " - Frequency of messages per second;\n"
54 " - Total logfile size and number of messages.\n"
55 "Use --logfile flag to select a logfile (path/filename) and use --name "
56 "flag to specify a channel to listen on.");
57
58 aos::InitGoogle(&argc, &argv);
59
60 LogfileStats logfile_stats;
61 std::vector<ChannelStats> channel_stats;
62
63 // Open LogFile
64 aos::logger::LogReader reader(FLAGS_logfile);
65 aos::SimulatedEventLoopFactory log_reader_factory(reader.configuration());
66 reader.Register(&log_reader_factory);
67
Austin Schuh6f3babe2020-01-26 20:34:50 -080068 const aos::Node *node = nullptr;
69
70 if (aos::configuration::MultiNode(reader.configuration())) {
71 if (FLAGS_node.empty()) {
72 LOG(INFO) << "Need a --node specified. The log file has:";
73 for (const aos::Node *node : reader.Nodes()) {
74 LOG(INFO) << " " << node->name()->string_view();
75 }
76 return 1;
77 } else {
78 node = aos::configuration::GetNode(reader.configuration(), FLAGS_node);
79 }
80 }
81
Stephan Massaltf84cf812019-12-31 14:14:50 -080082 // Make an eventloop for retrieving stats
83 std::unique_ptr<aos::EventLoop> stats_event_loop =
Austin Schuh6f3babe2020-01-26 20:34:50 -080084 log_reader_factory.MakeEventLoop("logstats", node);
Stephan Massaltf84cf812019-12-31 14:14:50 -080085 stats_event_loop->SkipTimingReport();
Tyler Chatow67ddb032020-01-12 14:30:04 -080086 stats_event_loop->SkipAosLog();
Stephan Massaltf84cf812019-12-31 14:14:50 -080087
88 // Read channel info and store in vector
89 bool found_channel = false;
90 const flatbuffers::Vector<flatbuffers::Offset<aos::Channel>> *channels =
91 reader.configuration()->channels();
92
93 int it = 0; // iterate through the channel_stats
94 for (flatbuffers::uoffset_t i = 0; i < channels->size(); i++) {
95 const aos::Channel *channel = channels->Get(i);
96 if (channel->name()->string_view().find(FLAGS_name) != std::string::npos) {
97 // Add a record to the stats vector.
98 channel_stats.push_back({channel});
Stephan Massaltf84cf812019-12-31 14:14:50 -080099 // Lambda to read messages and parse for information
100 stats_event_loop->MakeRawWatcher(
101 channel,
102 [&logfile_stats, &channel_stats, it](const aos::Context &context,
103 const void * /* message */) {
104 channel_stats[it].max_message_size =
105 std::max(channel_stats[it].max_message_size, context.size);
106 channel_stats[it].total_message_size += context.size;
107 channel_stats[it].total_num_messages++;
108 // asume messages are send in sequence per channel
109 channel_stats[it].channel_end_time = context.realtime_event_time;
110 channel_stats[it].first_message_time =
111 std::min(channel_stats[it].first_message_time,
112 context.monotonic_event_time);
113 channel_stats[it].current_message_time =
114 context.monotonic_event_time;
115 // update the overall logfile statistics
116 logfile_stats.logfile_length += context.size;
117 });
Stephan Massalt8e8052e2020-01-11 15:41:55 -0800118 it++;
Stephan Massaltf84cf812019-12-31 14:14:50 -0800119 // TODO (Stephan): Frequency of messages per second
120 // - Sliding window
121 // - Max / Deviation
122 found_channel = true;
123 }
124 }
125 if (!found_channel) {
126 LOG(FATAL) << "Could not find any channels";
127 }
128
129 log_reader_factory.Run();
130
131 // Print out the stats per channel and for the logfile
132 for (size_t i = 0; i != channel_stats.size(); i++) {
133 if (channel_stats[i].total_num_messages > 0) {
134 double sec_active =
135 aos::time::DurationInSeconds(channel_stats[i].current_message_time -
136 channel_stats[i].first_message_time);
137 channel_stats[i].avg_messages_sec =
138 (channel_stats[i].total_num_messages / sec_active);
139 logfile_stats.total_log_messages += channel_stats[i].total_num_messages;
140 logfile_stats.logfile_end_time = std::max(
141 logfile_stats.logfile_end_time, channel_stats[i].channel_end_time);
142 std::cout << "Channel name: "
143 << channel_stats[i].channel->name()->string_view()
144 << "\tMsg type: "
145 << channel_stats[i].channel->type()->string_view() << "\n"
146 << "Number of msg: " << channel_stats[i].total_num_messages
147 << std::setprecision(3) << std::fixed
148 << "\tAvg msg per sec: " << channel_stats[i].avg_messages_sec
149 << "\tSet max msg frequency: "
150 << channel_stats[i].channel->frequency() << "\n"
151 << "Avg msg size: "
152 << (channel_stats[i].total_message_size /
153 channel_stats[i].total_num_messages)
154 << "\tMax msg size: " << channel_stats[i].max_message_size
155 << "\tSet max msg size: "
156 << channel_stats[i].channel->max_size() << "\n"
157 << "First msg time: " << channel_stats[i].first_message_time
158 << "\tLast msg time: " << channel_stats[i].current_message_time
159 << "\tSeconds active: " << sec_active << "sec\n";
160 } else {
161 std::cout << "Channel name: "
162 << channel_stats[i].channel->name()->string_view() << "\t"
163 << "Msg type: "
164 << channel_stats[i].channel->type()->string_view() << "\n"
165 << "Set max msg frequency: "
166 << channel_stats[i].channel->frequency() << "\t"
167 << "Set max msg size: " << channel_stats[i].channel->max_size()
168 << "\n--- No messages in channel ---"
169 << "\n";
170 }
171 }
172 std::cout << std::setfill('-') << std::setw(80) << "-"
173 << "\nLogfile statistics for: " << FLAGS_logfile << "\n"
174 << "Log starts at:\t" << reader.realtime_start_time() << "\n"
175 << "Log ends at:\t" << logfile_stats.logfile_end_time << "\n"
176 << "Log file size:\t" << logfile_stats.logfile_length << "\n"
177 << "Total messages:\t" << logfile_stats.total_log_messages << "\n";
178
179 // Cleanup the created processes
180 reader.Deregister();
181 aos::Cleanup();
182 return 0;
183}