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