blob: fd47d9a508c75a3878d66660282c338af716a132 [file] [log] [blame]
Ben Tynerfaf33362022-02-16 14:04:51 -06001#include <util/ffdc_file.hpp>
2#include <util/trace.hpp>
3
4#include <string>
5#include <vector>
6
7namespace util
8{
9
10/**
11 * Parse systemd journal message field
12 *
13 * Parse the journal looking for the specified field and return the journal
14 * data for that field.
15 *
16 * @param journal - The journal to parse
17 * @param field - Field containing the data to retrieve
18 * @return Data for the speciefied field
19 */
20std::string sdjGetFieldValue(sd_journal* journal, const char* field)
21{
22 const char* data{nullptr};
23 size_t length{0};
24
25 // get field value
26 if (0 == sd_journal_get_data(journal, field, (const void**)&data, &length))
27 {
28 size_t prefix{0};
29
30 // The data returned by sd_journal_get_data will be prefixed with the
31 // field name and "="
32 const void* eq = memchr(data, '=', length);
33 if (nullptr != eq)
34 {
35 // get just data following the "="
36 prefix = (const char*)eq - data + 1;
37 }
38 else
39 {
40 // all the data (should not happen)
41 prefix = 0;
42 std::string value{}; // empty string
43 }
44
45 return std::string{data + prefix, length - prefix};
46 }
47 else
48 {
49 return std::string{}; // empty string
50 }
51}
52
53/**
54 * Gather messages from the journal
55 *
56 * Fetch journal entry data for all entries with the specified field equal to
57 * the specified value.
58 *
59 * @param field - Field to search on
60 * @param fieldValue - Value to search for
61 * @param max - Maximum number of messages fetch
62 * @return Vector of journal entry data
63 */
64std::vector<std::string> sdjGetMessages(const std::string& field,
65 const std::string& fieldValue,
66 unsigned int max)
67{
68 sd_journal* journal;
69 std::vector<std::string> messages;
70
71 if (0 == sd_journal_open(&journal, SD_JOURNAL_LOCAL_ONLY))
72 {
73 SD_JOURNAL_FOREACH_BACKWARDS(journal)
74 {
75 // Get input field
76 std::string value = sdjGetFieldValue(journal, field.c_str());
77
78 // Compare field value and read data
79 if (value == fieldValue)
80 {
81 // Get SYSLOG_IDENTIFIER field (process that logged message)
82 std::string syslog =
83 sdjGetFieldValue(journal, "SYSLOG_IDENTIFIER");
84
85 // Get _PID field
86 std::string pid = sdjGetFieldValue(journal, "_PID");
87
88 // Get MESSAGE field
89 std::string message = sdjGetFieldValue(journal, "MESSAGE");
90
91 // Get timestamp
92 uint64_t usec{0};
93 if (0 == sd_journal_get_realtime_usec(journal, &usec))
94 {
95
96 // Convert realtime microseconds to date format
97 char dateBuffer[80];
98 std::string date;
99 std::time_t timeInSecs = usec / 1000000;
100 strftime(dateBuffer, sizeof(dateBuffer), "%b %d %H:%M:%S",
101 std::localtime(&timeInSecs));
102 date = dateBuffer;
103
104 // Store value to messages
105 value = date + " " + syslog + "[" + pid + "]: " + message;
106 messages.insert(messages.begin(), value);
107 }
108 }
109
110 // limit maximum number of messages
111 if (messages.size() >= max)
112 {
113 break;
114 }
115 }
116
117 sd_journal_close(journal); // close journal when done
118 }
119
120 return messages;
121}
122/**
123 * @brief Create an FFDCFile object containing the specified lines of text data
124 *
125 * Throws an exception if an error occurs.
126 *
127 * @param lines - lines of text data to write to file
128 * @return FFDCFile object
129 */
130FFDCFile createFFDCTraceFile(const std::vector<std::string>& lines)
131{
132 // Create FFDC file of type Text
133 FFDCFile file{FFDCFormat::Text};
134 int fd = file.getFileDescriptor();
135
136 // Write FFDC lines to file
137 std::string buffer;
138 for (const std::string& line : lines)
139 {
140 // Copy line to buffer. Add newline if necessary.
141 buffer = line;
142 if (line.empty() || (line.back() != '\n'))
143 {
144 buffer += '\n';
145 }
146
147 // write buffer to file
148 size_t numBytes = write(fd, buffer.c_str(), buffer.size());
149 if (buffer.size() != numBytes)
150 {
151 trace::err("%s only %u of %u bytes written", file.getPath().c_str(),
152 numBytes, buffer.size());
153 }
154 }
155
156 // Seek to beginning of file so error logging system can read data
157 lseek(fd, 0, SEEK_SET);
158
159 return file;
160}
161
162/**
163 * Create FDDC files from journal messages of relevant executables
164 *
165 * Parse the system journal looking for log entries created by the executables
166 * of interest for logging. For each of these entries create a ffdc trace file
167 * that will be used to create ffdc log entries. These files will be pushed
168 * onto the stack of ffdc files.
169 *
170 * @param i_files - vector of ffdc files that will become log entries
171 */
172void createFFDCTraceFiles(std::vector<FFDCFile>& i_files)
173{
174 // Executables of interest
175 std::vector<std::string> executables{"openpower-hw-diags"};
176
177 for (const std::string& executable : executables)
178 {
179 try
180 {
181 // get journal messages
182 std::vector<std::string> messages =
183 sdjGetMessages("SYSLOG_IDENTIFIER", executable, 30);
184
185 // Create FFDC file containing the journal messages
186 if (!messages.empty())
187 {
188 i_files.emplace_back(createFFDCTraceFile(messages));
189 }
190 }
191 catch (const std::exception& e)
192 {
193 trace::inf("createFFDCTraceFiles exception");
194 trace::inf(e.what());
195 }
196 }
197}
198
199} // namespace util