blob: cc78c2b1dca6feffdf2016381519c41c748ebbee [file] [log] [blame]
Austin Schuhb618a9b2022-01-27 18:03:21 -08001#include <iostream>
2#include <string>
3#include <vector>
4
Philipp Schrader790cb542023-07-05 21:06:52 -07005#include "gflags/gflags.h"
6
Austin Schuhb618a9b2022-01-27 18:03:21 -08007#include "aos/events/logging/logfile_sorting.h"
8#include "aos/events/logging/logfile_utils.h"
9#include "aos/init.h"
10#include "aos/network/multinode_timestamp_filter.h"
Austin Schuhb618a9b2022-01-27 18:03:21 -080011
12// This is a simple application to match up data with timestamps for a node in a
13// log. It doesn't solve the timestamp problem, but is still quite useful for
14// debugging what happened in a log.
15
16DEFINE_string(node, "", "The node to dump sorted messages for");
17
18namespace aos::logger {
19
20namespace chrono = std::chrono;
21
22std::string LogFileVectorToString(std::vector<logger::LogFile> log_files) {
23 std::stringstream ss;
24 for (const auto &f : log_files) {
25 ss << f << "\n";
26 }
27 return ss.str();
28}
29
30int Main(int argc, char **argv) {
31 const std::vector<std::string> unsorted_logfiles = FindLogs(argc, argv);
32 const std::vector<LogFile> log_files = SortParts(unsorted_logfiles);
33
34 CHECK_GT(log_files.size(), 0u);
35 // Validate that we have the same config everwhere. This will be true if
36 // all the parts were sorted together and the configs match.
37 const Configuration *config = nullptr;
38 for (const LogFile &log_file : log_files) {
39 VLOG(1) << log_file;
40 if (config == nullptr) {
41 config = log_file.config.get();
42 } else {
43 CHECK_EQ(config, log_file.config.get());
44 }
45 }
46
47 // Haven't tested this on a single node log, and don't really see a need to
48 // right now. The higher layers just work.
49 CHECK(configuration::MultiNode(config));
50
51 // Now, build up all the TimestampMapper classes to read and sort the data.
52 std::vector<std::unique_ptr<TimestampMapper>> mappers;
53 TimestampMapper *node_mapper = nullptr;
54
55 for (const Node *node : configuration::GetNodes(config)) {
56 std::vector<LogParts> filtered_parts =
57 FilterPartsForNode(log_files, node->name()->string_view());
58
59 // Confirm that all the parts are from the same boot if there are enough
60 // parts to not be from the same boot.
61 if (!filtered_parts.empty()) {
62 // Filter the parts relevant to each node when building the mapper.
63 mappers.emplace_back(
64 std::make_unique<TimestampMapper>(std::move(filtered_parts)));
65 if (node->name()->string_view() == FLAGS_node) {
66 node_mapper = mappers.back().get();
67 }
68 } else {
69 mappers.emplace_back(nullptr);
70 }
71 }
72
73 CHECK(node_mapper != nullptr) << ": Failed to find node " << FLAGS_node;
74
75 // Hook the peers up so data gets matched.
76 for (std::unique_ptr<TimestampMapper> &mapper1 : mappers) {
77 for (std::unique_ptr<TimestampMapper> &mapper2 : mappers) {
78 if (mapper1.get() != mapper2.get() && mapper1 && mapper2) {
79 mapper1->AddPeer(mapper2.get());
80 }
81 }
82 }
83
84 // Now, read all the timestamps for each node. This is simpler than the
85 // logger on purpose. It loads in *all* the timestamps in 1 go per node,
86 // ignoring memory usage.
87 const Node *node = configuration::GetNode(config, FLAGS_node);
88
89 LOG(INFO) << "Reading all data for " << node->name()->string_view();
90 const size_t node_index = configuration::GetNodeIndex(config, node);
91 TimestampMapper *timestamp_mapper = mappers[node_index].get();
92 CHECK(timestamp_mapper != nullptr);
93 CHECK_EQ(timestamp_mapper, node_mapper);
94
95 while (true) {
96 TimestampedMessage *m = timestamp_mapper->Front();
97 if (m == nullptr) {
98 break;
99 }
100 std::cout << config->nodes()
101 ->Get(configuration::GetNodeIndex(
102 config, config->channels()
103 ->Get(m->channel_index)
104 ->source_node()
105 ->string_view()))
106 ->name()
107 ->string_view()
108 << " "
109 << configuration::StrippedChannelToString(
110 config->channels()->Get(m->channel_index))
111 << " " << *m << "\n";
112 timestamp_mapper->PopFront();
113 }
114
115 return 0;
116}
117
118} // namespace aos::logger
119
120int main(int argc, char **argv) {
121 aos::InitGoogle(&argc, &argv);
122
123 return aos::logger::Main(argc, argv);
124}