blob: f408e27c76f67895395ce563fd22a7f27cb1989c [file] [log] [blame]
Stephan Massaltf84cf812019-12-31 14:14:50 -08001#include <iomanip>
2#include <iostream>
3
Austin Schuhb06f03b2021-02-17 22:00:37 -08004#include "aos/events/logging/log_reader.h"
Stephan Massaltf84cf812019-12-31 14:14:50 -08005#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
Stephan Massaltf84cf812019-12-31 14:14:50 -080011DEFINE_string(
12 name, "",
13 "Name to match for printing out channels. Empty means no name filter.");
14
Austin Schuh6f3babe2020-01-26 20:34:50 -080015DEFINE_string(node, "", "Node to print stats out for.");
16
milind upadhyay38fe3cd2021-03-27 15:30:53 -070017DEFINE_bool(excessive_size_only, false,
18 "Only print channels that have a set max message size that is more "
19 "than double of the max message size.");
20
Stephan Massaltf84cf812019-12-31 14:14:50 -080021// define struct to hold all information
22struct ChannelStats {
23 // pointer to the channel for which stats are collected
24 const aos::Channel *channel;
25 aos::realtime_clock::time_point channel_end_time =
26 aos::realtime_clock::min_time;
27 aos::monotonic_clock::time_point first_message_time =
28 // needs to be higher than time in the logfile!
29 aos::monotonic_clock::max_time;
30 aos::monotonic_clock::time_point current_message_time =
31 aos::monotonic_clock::min_time;
32 // channel stats to collect per channel
33 int total_num_messages = 0;
34 size_t max_message_size = 0;
35 size_t total_message_size = 0;
36 double avg_messages_sec = 0.0; // TODO in Lambda, now in stats overview.
37 double max_messages_sec = 0.0; // TODO in Lambda
38};
39
40struct LogfileStats {
41 // All relevant stats on to logfile level
42 size_t logfile_length = 0;
43 int total_log_messages = 0;
44 aos::realtime_clock::time_point logfile_end_time =
45 aos::realtime_clock::min_time;
46};
47
48int main(int argc, char **argv) {
49 gflags::SetUsageMessage(
Ravago Jones8bab1842020-12-12 17:36:39 -080050 "Usage: \n"
51 " log_stats [args] logfile1 logfile2 ...\n"
Stephan Massaltf84cf812019-12-31 14:14:50 -080052 "This program provides statistics on a given log file. Supported "
53 "statistics are:\n"
54 " - Logfile start time;\n"
55 " - Total messages per channel/type;\n"
56 " - Max message size per channel/type;\n"
57 " - Frequency of messages per second;\n"
58 " - Total logfile size and number of messages.\n"
59 "Use --logfile flag to select a logfile (path/filename) and use --name "
60 "flag to specify a channel to listen on.");
61
62 aos::InitGoogle(&argc, &argv);
63
Ravago Jones8bab1842020-12-12 17:36:39 -080064 if (argc < 2) {
65 LOG(FATAL) << "Expected at least 1 logfile as an argument.";
66 }
67
68 // find logfiles
69 std::vector<std::string> unsorted_logfiles =
70 aos::logger::FindLogs(argc, argv);
71
72 // sort logfiles
73 const std::vector<aos::logger::LogFile> logfiles =
74 aos::logger::SortParts(unsorted_logfiles);
75
76 // open logfiles
77 aos::logger::LogReader reader(logfiles);
78
Stephan Massaltf84cf812019-12-31 14:14:50 -080079 LogfileStats logfile_stats;
80 std::vector<ChannelStats> channel_stats;
81
Stephan Massaltf84cf812019-12-31 14:14:50 -080082 aos::SimulatedEventLoopFactory log_reader_factory(reader.configuration());
83 reader.Register(&log_reader_factory);
84
Austin Schuh6f3babe2020-01-26 20:34:50 -080085 const aos::Node *node = nullptr;
86
87 if (aos::configuration::MultiNode(reader.configuration())) {
88 if (FLAGS_node.empty()) {
89 LOG(INFO) << "Need a --node specified. The log file has:";
Austin Schuh07676622021-01-21 18:59:17 -080090 for (const aos::Node *node : reader.LoggedNodes()) {
Austin Schuh6f3babe2020-01-26 20:34:50 -080091 LOG(INFO) << " " << node->name()->string_view();
92 }
Austin Schuh8c7f14b2021-01-21 19:01:54 -080093 reader.Deregister();
Austin Schuh6f3babe2020-01-26 20:34:50 -080094 return 1;
95 } else {
96 node = aos::configuration::GetNode(reader.configuration(), FLAGS_node);
97 }
98 }
99
Stephan Massaltf84cf812019-12-31 14:14:50 -0800100 // Make an eventloop for retrieving stats
101 std::unique_ptr<aos::EventLoop> stats_event_loop =
Austin Schuh6f3babe2020-01-26 20:34:50 -0800102 log_reader_factory.MakeEventLoop("logstats", node);
Stephan Massaltf84cf812019-12-31 14:14:50 -0800103 stats_event_loop->SkipTimingReport();
Tyler Chatow67ddb032020-01-12 14:30:04 -0800104 stats_event_loop->SkipAosLog();
Stephan Massaltf84cf812019-12-31 14:14:50 -0800105
106 // Read channel info and store in vector
107 bool found_channel = false;
108 const flatbuffers::Vector<flatbuffers::Offset<aos::Channel>> *channels =
109 reader.configuration()->channels();
110
111 int it = 0; // iterate through the channel_stats
112 for (flatbuffers::uoffset_t i = 0; i < channels->size(); i++) {
113 const aos::Channel *channel = channels->Get(i);
Austin Schuh76db3fa2020-03-07 17:02:44 -0800114 if (!aos::configuration::ChannelIsReadableOnNode(
115 channel, stats_event_loop->node())) {
116 continue;
Stephan Massaltf84cf812019-12-31 14:14:50 -0800117 }
Austin Schuh76db3fa2020-03-07 17:02:44 -0800118
119 if (channel->name()->string_view().find(FLAGS_name) == std::string::npos) {
120 continue;
121 }
122
123 // Add a record to the stats vector.
124 channel_stats.push_back({channel});
125 // Lambda to read messages and parse for information
Brian Silvermanad1bce02020-03-05 14:23:01 -0800126 stats_event_loop->MakeRawNoArgWatcher(
127 channel,
128 [&logfile_stats, &channel_stats, it](const aos::Context &context) {
129 channel_stats[it].max_message_size =
130 std::max(channel_stats[it].max_message_size, context.size);
131 channel_stats[it].total_message_size += context.size;
132 channel_stats[it].total_num_messages++;
133 // asume messages are send in sequence per channel
134 channel_stats[it].channel_end_time = context.realtime_event_time;
135 channel_stats[it].first_message_time =
136 std::min(channel_stats[it].first_message_time,
137 context.monotonic_event_time);
138 channel_stats[it].current_message_time = context.monotonic_event_time;
139 // update the overall logfile statistics
140 logfile_stats.logfile_length += context.size;
141 });
Austin Schuh76db3fa2020-03-07 17:02:44 -0800142 it++;
143 // TODO (Stephan): Frequency of messages per second
144 // - Sliding window
145 // - Max / Deviation
146 found_channel = true;
Stephan Massaltf84cf812019-12-31 14:14:50 -0800147 }
148 if (!found_channel) {
149 LOG(FATAL) << "Could not find any channels";
150 }
151
152 log_reader_factory.Run();
153
milind upadhyay38fe3cd2021-03-27 15:30:53 -0700154 std::cout << std::endl;
Stephan Massaltf84cf812019-12-31 14:14:50 -0800155 // Print out the stats per channel and for the logfile
156 for (size_t i = 0; i != channel_stats.size(); i++) {
157 if (channel_stats[i].total_num_messages > 0) {
158 double sec_active =
159 aos::time::DurationInSeconds(channel_stats[i].current_message_time -
160 channel_stats[i].first_message_time);
161 channel_stats[i].avg_messages_sec =
162 (channel_stats[i].total_num_messages / sec_active);
163 logfile_stats.total_log_messages += channel_stats[i].total_num_messages;
164 logfile_stats.logfile_end_time = std::max(
165 logfile_stats.logfile_end_time, channel_stats[i].channel_end_time);
milind upadhyay38fe3cd2021-03-27 15:30:53 -0700166
167 if (!FLAGS_excessive_size_only ||
168 (channel_stats[i].max_message_size * 2) <
169 static_cast<unsigned long>(
170 channel_stats[i].channel->max_size())) {
171 std::cout << "Channel name: "
172 << channel_stats[i].channel->name()->string_view()
173 << "\tMsg type: "
174 << channel_stats[i].channel->type()->string_view() << "\n";
175 if (!FLAGS_excessive_size_only) {
176 std::cout << "Number of msg: " << channel_stats[i].total_num_messages
177 << std::setprecision(3) << std::fixed
178 << "\tAvg msg per sec: "
179 << channel_stats[i].avg_messages_sec
180 << "\tSet max msg frequency: "
181 << channel_stats[i].channel->frequency() << "\n";
182 }
183 std::cout << "Avg msg size: "
184 << (channel_stats[i].total_message_size /
185 channel_stats[i].total_num_messages)
186 << "\tMax msg size: " << channel_stats[i].max_message_size
187 << "\tSet max msg size: "
188 << channel_stats[i].channel->max_size() << "\n";
189 if (!FLAGS_excessive_size_only) {
190 std::cout << "First msg time: " << channel_stats[i].first_message_time
191 << "\tLast msg time: "
192 << channel_stats[i].current_message_time
193 << "\tSeconds active: " << sec_active << "sec\n";
194 }
195 std::cout << std::endl;
196 }
Stephan Massaltf84cf812019-12-31 14:14:50 -0800197 } else {
198 std::cout << "Channel name: "
199 << channel_stats[i].channel->name()->string_view() << "\t"
200 << "Msg type: "
201 << channel_stats[i].channel->type()->string_view() << "\n"
202 << "Set max msg frequency: "
203 << channel_stats[i].channel->frequency() << "\t"
204 << "Set max msg size: " << channel_stats[i].channel->max_size()
205 << "\n--- No messages in channel ---"
206 << "\n";
207 }
208 }
209 std::cout << std::setfill('-') << std::setw(80) << "-"
Ravago Jones8bab1842020-12-12 17:36:39 -0800210 << "\nLogfile statistics:\n"
Austin Schuh76db3fa2020-03-07 17:02:44 -0800211 << "Log starts at:\t" << reader.realtime_start_time(node) << "\n"
Stephan Massaltf84cf812019-12-31 14:14:50 -0800212 << "Log ends at:\t" << logfile_stats.logfile_end_time << "\n"
213 << "Log file size:\t" << logfile_stats.logfile_length << "\n"
214 << "Total messages:\t" << logfile_stats.total_log_messages << "\n";
215
216 // Cleanup the created processes
217 reader.Deregister();
Austin Schuhae87e312020-08-01 16:15:01 -0700218
Stephan Massaltf84cf812019-12-31 14:14:50 -0800219 return 0;
220}