1 #include <util/ffdc_file.hpp> 2 #include <util/trace.hpp> 3 4 #include <string> 5 #include <vector> 6 7 namespace 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 */ 20 std::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 */ 64 std::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 = sdjGetFieldValue(journal, 83 "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 // 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 */ 129 FFDCFile 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 */ 171 void 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 199