blob: 2c4a8778a588e53635766d4365dd56828c0fb309 [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 */
Patrick Williamsa0c724d2024-08-16 15:21:54 -040064std::vector<std::string> sdjGetMessages(
65 const std::string& field, const std::string& fieldValue, unsigned int max)
Ben Tynerfaf33362022-02-16 14:04:51 -060066{
67 sd_journal* journal;
68 std::vector<std::string> messages;
69
70 if (0 == sd_journal_open(&journal, SD_JOURNAL_LOCAL_ONLY))
71 {
72 SD_JOURNAL_FOREACH_BACKWARDS(journal)
73 {
74 // Get input field
75 std::string value = sdjGetFieldValue(journal, field.c_str());
76
77 // Compare field value and read data
78 if (value == fieldValue)
79 {
80 // Get SYSLOG_IDENTIFIER field (process that logged message)
Patrick Williamsa0c724d2024-08-16 15:21:54 -040081 std::string syslog =
82 sdjGetFieldValue(journal, "SYSLOG_IDENTIFIER");
Ben Tynerfaf33362022-02-16 14:04:51 -060083
84 // Get _PID field
85 std::string pid = sdjGetFieldValue(journal, "_PID");
86
87 // Get MESSAGE field
88 std::string message = sdjGetFieldValue(journal, "MESSAGE");
89
90 // Get timestamp
91 uint64_t usec{0};
92 if (0 == sd_journal_get_realtime_usec(journal, &usec))
93 {
Ben Tynerfaf33362022-02-16 14:04:51 -060094 // Convert realtime microseconds to date format
95 char dateBuffer[80];
96 std::string date;
97 std::time_t timeInSecs = usec / 1000000;
98 strftime(dateBuffer, sizeof(dateBuffer), "%b %d %H:%M:%S",
99 std::localtime(&timeInSecs));
100 date = dateBuffer;
101
102 // Store value to messages
103 value = date + " " + syslog + "[" + pid + "]: " + message;
104 messages.insert(messages.begin(), value);
105 }
106 }
107
108 // limit maximum number of messages
109 if (messages.size() >= max)
110 {
111 break;
112 }
113 }
114
115 sd_journal_close(journal); // close journal when done
116 }
117
118 return messages;
119}
120/**
121 * @brief Create an FFDCFile object containing the specified lines of text data
122 *
123 * Throws an exception if an error occurs.
124 *
125 * @param lines - lines of text data to write to file
126 * @return FFDCFile object
127 */
128FFDCFile createFFDCTraceFile(const std::vector<std::string>& lines)
129{
130 // Create FFDC file of type Text
131 FFDCFile file{FFDCFormat::Text};
132 int fd = file.getFileDescriptor();
133
134 // Write FFDC lines to file
135 std::string buffer;
136 for (const std::string& line : lines)
137 {
138 // Copy line to buffer. Add newline if necessary.
139 buffer = line;
140 if (line.empty() || (line.back() != '\n'))
141 {
142 buffer += '\n';
143 }
144
145 // write buffer to file
146 size_t numBytes = write(fd, buffer.c_str(), buffer.size());
147 if (buffer.size() != numBytes)
148 {
149 trace::err("%s only %u of %u bytes written", file.getPath().c_str(),
150 numBytes, buffer.size());
151 }
152 }
153
154 // Seek to beginning of file so error logging system can read data
155 lseek(fd, 0, SEEK_SET);
156
157 return file;
158}
159
160/**
161 * Create FDDC files from journal messages of relevant executables
162 *
163 * Parse the system journal looking for log entries created by the executables
164 * of interest for logging. For each of these entries create a ffdc trace file
165 * that will be used to create ffdc log entries. These files will be pushed
166 * onto the stack of ffdc files.
167 *
168 * @param i_files - vector of ffdc files that will become log entries
169 */
170void createFFDCTraceFiles(std::vector<FFDCFile>& i_files)
171{
172 // Executables of interest
173 std::vector<std::string> executables{"openpower-hw-diags"};
174
175 for (const std::string& executable : executables)
176 {
177 try
178 {
179 // get journal messages
180 std::vector<std::string> messages =
181 sdjGetMessages("SYSLOG_IDENTIFIER", executable, 30);
182
183 // Create FFDC file containing the journal messages
184 if (!messages.empty())
185 {
186 i_files.emplace_back(createFFDCTraceFile(messages));
187 }
188 }
189 catch (const std::exception& e)
190 {
191 trace::inf("createFFDCTraceFiles exception");
192 trace::inf(e.what());
193 }
194 }
195}
196
197} // namespace util