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