blob: e77012fd4bf5e6744e5ae628a70643189e1e628f [file] [log] [blame]
#include <util/ffdc_file.hpp>
#include <util/trace.hpp>
#include <string>
#include <vector>
namespace util
{
/**
* Parse systemd journal message field
*
* Parse the journal looking for the specified field and return the journal
* data for that field.
*
* @param journal - The journal to parse
* @param field - Field containing the data to retrieve
* @return Data for the speciefied field
*/
std::string sdjGetFieldValue(sd_journal* journal, const char* field)
{
const char* data{nullptr};
size_t length{0};
// get field value
if (0 == sd_journal_get_data(journal, field, (const void**)&data, &length))
{
size_t prefix{0};
// The data returned by sd_journal_get_data will be prefixed with the
// field name and "="
const void* eq = memchr(data, '=', length);
if (nullptr != eq)
{
// get just data following the "="
prefix = (const char*)eq - data + 1;
}
else
{
// all the data (should not happen)
prefix = 0;
std::string value{}; // empty string
}
return std::string{data + prefix, length - prefix};
}
else
{
return std::string{}; // empty string
}
}
/**
* Gather messages from the journal
*
* Fetch journal entry data for all entries with the specified field equal to
* the specified value.
*
* @param field - Field to search on
* @param fieldValue - Value to search for
* @param max - Maximum number of messages fetch
* @return Vector of journal entry data
*/
std::vector<std::string> sdjGetMessages(const std::string& field,
const std::string& fieldValue,
unsigned int max)
{
sd_journal* journal;
std::vector<std::string> messages;
if (0 == sd_journal_open(&journal, SD_JOURNAL_LOCAL_ONLY))
{
SD_JOURNAL_FOREACH_BACKWARDS(journal)
{
// Get input field
std::string value = sdjGetFieldValue(journal, field.c_str());
// Compare field value and read data
if (value == fieldValue)
{
// Get SYSLOG_IDENTIFIER field (process that logged message)
std::string syslog =
sdjGetFieldValue(journal, "SYSLOG_IDENTIFIER");
// Get _PID field
std::string pid = sdjGetFieldValue(journal, "_PID");
// Get MESSAGE field
std::string message = sdjGetFieldValue(journal, "MESSAGE");
// Get timestamp
uint64_t usec{0};
if (0 == sd_journal_get_realtime_usec(journal, &usec))
{
// Convert realtime microseconds to date format
char dateBuffer[80];
std::string date;
std::time_t timeInSecs = usec / 1000000;
strftime(dateBuffer, sizeof(dateBuffer), "%b %d %H:%M:%S",
std::localtime(&timeInSecs));
date = dateBuffer;
// Store value to messages
value = date + " " + syslog + "[" + pid + "]: " + message;
messages.insert(messages.begin(), value);
}
}
// limit maximum number of messages
if (messages.size() >= max)
{
break;
}
}
sd_journal_close(journal); // close journal when done
}
return messages;
}
/**
* @brief Create an FFDCFile object containing the specified lines of text data
*
* Throws an exception if an error occurs.
*
* @param lines - lines of text data to write to file
* @return FFDCFile object
*/
FFDCFile createFFDCTraceFile(const std::vector<std::string>& lines)
{
// Create FFDC file of type Text
FFDCFile file{FFDCFormat::Text};
int fd = file.getFileDescriptor();
// Write FFDC lines to file
std::string buffer;
for (const std::string& line : lines)
{
// Copy line to buffer. Add newline if necessary.
buffer = line;
if (line.empty() || (line.back() != '\n'))
{
buffer += '\n';
}
// write buffer to file
size_t numBytes = write(fd, buffer.c_str(), buffer.size());
if (buffer.size() != numBytes)
{
trace::err("%s only %u of %u bytes written", file.getPath().c_str(),
numBytes, buffer.size());
}
}
// Seek to beginning of file so error logging system can read data
lseek(fd, 0, SEEK_SET);
return file;
}
/**
* Create FDDC files from journal messages of relevant executables
*
* Parse the system journal looking for log entries created by the executables
* of interest for logging. For each of these entries create a ffdc trace file
* that will be used to create ffdc log entries. These files will be pushed
* onto the stack of ffdc files.
*
* @param i_files - vector of ffdc files that will become log entries
*/
void createFFDCTraceFiles(std::vector<FFDCFile>& i_files)
{
// Executables of interest
std::vector<std::string> executables{"openpower-hw-diags"};
for (const std::string& executable : executables)
{
try
{
// get journal messages
std::vector<std::string> messages =
sdjGetMessages("SYSLOG_IDENTIFIER", executable, 30);
// Create FFDC file containing the journal messages
if (!messages.empty())
{
i_files.emplace_back(createFFDCTraceFile(messages));
}
}
catch (const std::exception& e)
{
trace::inf("createFFDCTraceFiles exception");
trace::inf(e.what());
}
}
}
} // namespace util