summaryrefslogtreecommitdiffstats
path: root/lib/Plugins/KerneloopsSysLog.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'lib/Plugins/KerneloopsSysLog.cpp')
-rw-r--r--lib/Plugins/KerneloopsSysLog.cpp423
1 files changed, 423 insertions, 0 deletions
diff --git a/lib/Plugins/KerneloopsSysLog.cpp b/lib/Plugins/KerneloopsSysLog.cpp
new file mode 100644
index 0000000..5f5de48
--- /dev/null
+++ b/lib/Plugins/KerneloopsSysLog.cpp
@@ -0,0 +1,423 @@
+/*
+ * Copyright 2007, Intel Corporation
+ * Copyright 2009, Red Hat Inc.
+ *
+ * This file is part of Abrt.
+ *
+ * This program file is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program in a file named COPYING; if not, write to the
+ * Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301 USA
+ *
+ * Authors:
+ * Anton Arapov <anton@redhat.com>
+ * Arjan van de Ven <arjan@linux.intel.com>
+ */
+
+#include "KerneloopsSysLog.h"
+
+#include <list>
+#include <stdlib.h>
+#include <string.h>
+
+/*
+ * This limits the number of oopses we'll submit per session;
+ * it's important that this is bounded to avoid feedback loops
+ * for the scenario where submitting an oopses causes a warning/oops
+ */
+#define MAX_OOPS 16
+
+static char **linepointer;
+
+static char *linelevel;
+static int linecount;
+
+CSysLog::CSysLog() :
+ m_nFoundOopses(0)
+{}
+
+void CSysLog::QueueOops(char *data, char *version)
+{
+ COops m_NewOops;
+
+ if (m_nFoundOopses > MAX_OOPS)
+ return;
+
+ m_NewOops.m_sData = strdup(data);
+ m_NewOops.m_sVersion = strdup(version);
+
+ m_OopsQueue.push_back(m_NewOops);
+ m_nFoundOopses++;
+}
+
+void CSysLog::ClearOopsList()
+{
+ m_OopsQueue.clear();
+}
+
+std::list<COops> CSysLog::GetOopsList()
+{
+ return m_OopsQueue;
+}
+
+/*
+ * This function splits the dmesg buffer data into lines
+ * (null terminated). The linepointer array is assumed to be
+ * allocated already.
+ */
+void CSysLog::FillLinePointers(char *buffer, int remove_syslog)
+{
+ char *c;
+ linecount = 0;
+ c = buffer;
+ while (c) {
+ int len = 0;
+ char *c9;
+
+ c9 = strchr(c, '\n');
+ if (c9)
+ len = c9 - c;
+
+ /* in /var/log/messages, we need to strip the first part off, upto the 3rd ':' */
+ if (remove_syslog) {
+ char *c2;
+
+ /* skip non-kernel lines */
+ c2 = (char*)memmem(c, len, "kernel:", 7);
+ if (!c2)
+ c2 = (char*)memmem(c, len, "abrt:", 5);
+ if (!c2) {
+ c2 = c9;
+ if (c2) {
+ c = c2 + 1;
+ continue;
+ } else
+ break;
+ }
+ c = strchr(c, ':');
+ if (!c)
+ break;
+ c++;
+ c = strchr(c, ':');
+ if (!c)
+ break;
+ c++;
+ c = strchr(c, ':');
+ if (!c)
+ break;
+ c++;
+ if (*c)
+ c++;
+ }
+
+ linepointer[linecount] = c;
+ linelevel[linecount] = 0;
+ /* store and remove kernel log level */
+ if (*c == '<' && *(c+2) == '>') {
+ linelevel[linecount] = *(c+1);
+ c = c + 3;
+ linepointer[linecount] = c;
+ }
+ /* remove jiffies time stamp counter if present */
+ if (*c == '[') {
+ char *c2, *c3;
+ c2 = strchr(c, '.');
+ c3 = strchr(c, ']');
+ if (c2 && c3 && (c2 < c3) && (c3-c) < 14 && (c2-c) < 8) {
+ c = c3+1;
+ if (*c == ' ')
+ c++;
+ linepointer[linecount] = c;
+ }
+ }
+
+ c = strchr(c, '\n'); /* turn the \n into a string termination */
+ if (c) {
+ *c = 0;
+ c = c+1;
+ }
+
+ /* if we see our own marker, we know we submitted everything upto here already */
+ if (strstr(linepointer[linecount], "Abrt")) {
+ linecount = 0;
+ linepointer[0] = NULL;
+ }
+ linecount++;
+ }
+}
+
+/*
+ * extract_version tries to find the kernel version in given data
+ */
+int CSysLog::ExtractVersion(char *linepointer, char *version)
+{
+ int ret;
+
+ ret = 0;
+ if ((strstr(linepointer, "Pid") != NULL) ||
+ (strstr(linepointer, "comm") != NULL) ||
+ (strstr(linepointer, "CPU") != NULL) ||
+ (strstr(linepointer, "REGS") != NULL) ||
+ (strstr(linepointer, "EFLAGS") != NULL))
+ {
+ char* start;
+ char* end;
+
+ start = strstr(linepointer, "2.6.");
+ if (start) {
+ end = index(start, 0x20);
+ strncpy(version, start, end-start);
+ ret = 1;
+ }
+ }
+
+ if (!ret)
+ strncpy(version, "undefined", 9);
+
+ return ret;
+}
+
+/*
+ * extract_oops tries to find oops signatures in a log
+ */
+int CSysLog::ExtractOops(char *buffer, size_t buflen, int remove_syslog)
+{
+ int i;
+ char prevlevel = 0;
+ int oopsstart = -1;
+ int oopsend;
+ int inbacktrace = 0;
+ int oopsesfound = 0;
+
+ linepointer = (char**)calloc(buflen+1, sizeof(char*));
+ if (!linepointer)
+ return 0;
+ linelevel = (char*)calloc(buflen+1, sizeof(char));
+ if (!linelevel) {
+ free(linepointer);
+ linepointer = NULL;
+ return 0;
+ }
+
+ FillLinePointers(buffer, remove_syslog);
+
+ oopsend = linecount;
+
+ i = 0;
+ while (i < linecount) {
+ char *c = linepointer[i];
+
+ if (c == NULL) {
+ i++;
+ continue;
+ }
+ if (oopsstart < 0) {
+ /* find start-of-oops markers */
+ if (strstr(c, "general protection fault:"))
+ oopsstart = i;
+ if (strstr(c, "BUG:"))
+ oopsstart = i;
+ if (strstr(c, "kernel BUG at"))
+ oopsstart = i;
+ if (strstr(c, "do_IRQ: stack overflow:"))
+ oopsstart = i;
+ if (strstr(c, "RTNL: assertion failed"))
+ oopsstart = i;
+ if (strstr(c, "Eeek! page_mapcount(page) went negative!"))
+ oopsstart = i;
+ if (strstr(c, "near stack overflow (cur:"))
+ oopsstart = i;
+ if (strstr(c, "double fault:"))
+ oopsstart = i;
+ if (strstr(c, "Badness at"))
+ oopsstart = i;
+ if (strstr(c, "NETDEV WATCHDOG"))
+ oopsstart = i;
+ if (strstr(c, "WARNING:") &&
+ !strstr(c, "appears to be on the same physical disk"))
+ oopsstart = i;
+ if (strstr(c, "Unable to handle kernel"))
+ oopsstart = i;
+ if (strstr(c, "sysctl table check failed"))
+ oopsstart = i;
+ if (strstr(c, "------------[ cut here ]------------"))
+ oopsstart = i;
+ if (strstr(c, "list_del corruption."))
+ oopsstart = i;
+ if (strstr(c, "list_add corruption."))
+ oopsstart = i;
+ if (strstr(c, "Oops:") && i >= 3)
+ oopsstart = i-3;
+#if DEBUG
+ /* debug information */
+ if (oopsstart >= 0) {
+ printf("Found start of oops at line %i\n", oopsstart);
+ printf(" start line is -%s-\n", linepointer[oopsstart]);
+ if (oopsstart != i)
+ printf(" trigger line is -%s-\n", c);
+ }
+#endif
+ /* try to find the end marker */
+ if (oopsstart >= 0) {
+ int i2;
+ i2 = i+1;
+ while (i2 < linecount && i2 < (i+50)) {
+ if (strstr(linepointer[i2], "---[ end trace")) {
+ inbacktrace = 1;
+ i = i2;
+ break;
+ }
+ i2++;
+ }
+ }
+ }
+
+ /* a calltrace starts with "Call Trace:" or with the " [<.......>] function+0xFF/0xAA" pattern */
+ if (oopsstart >= 0 && strstr(linepointer[i], "Call Trace:"))
+ inbacktrace = 1;
+
+ else if (oopsstart >= 0 && inbacktrace == 0 && strlen(linepointer[i]) > 8) {
+ char *c1, *c2, *c3;
+ c1 = strstr(linepointer[i], ">]");
+ c2 = strstr(linepointer[i], "+0x");
+ c3 = strstr(linepointer[i], "/0x");
+ if (linepointer[i][0] == ' ' && linepointer[i][1] == '[' && linepointer[i][2] == '<' && c1 && c2 && c3)
+ inbacktrace = 1;
+ } else
+
+ /* try to see if we're at the end of an oops */
+ if (oopsstart >= 0 && inbacktrace > 0) {
+ char c2, c3;
+ c2 = linepointer[i][0];
+ c3 = linepointer[i][1];
+
+ /* line needs to start with " [" or have "] ["*/
+ if ((c2 != ' ' || c3 != '[') &&
+ strstr(linepointer[i], "] [") == NULL &&
+ strstr(linepointer[i], "--- Exception") == NULL &&
+ strstr(linepointer[i], " LR =") == NULL &&
+ strstr(linepointer[i], "<#DF>") == NULL &&
+ strstr(linepointer[i], "<IRQ>") == NULL &&
+ strstr(linepointer[i], "<EOI>") == NULL &&
+ strstr(linepointer[i], "<<EOE>>") == NULL)
+ oopsend = i-1;
+
+ /* oops lines are always more than 8 long */
+ if (strlen(linepointer[i]) < 8)
+ oopsend = i-1;
+ /* single oopses are of the same loglevel */
+ if (linelevel[i] != prevlevel)
+ oopsend = i-1;
+ /* The Code: line means we're done with the backtrace */
+ if (strstr(linepointer[i], "Code:") != NULL)
+ oopsend = i;
+ if (strstr(linepointer[i], "Instruction dump::") != NULL)
+ oopsend = i;
+ /* if a new oops starts, this one has ended */
+ if (strstr(linepointer[i], "WARNING:") != NULL && oopsstart != i)
+ oopsend = i-1;
+ if (strstr(linepointer[i], "Unable to handle") != NULL && oopsstart != i)
+ oopsend = i-1;
+ /* kernel end-of-oops marker */
+ if (strstr(linepointer[i], "---[ end trace") != NULL)
+ oopsend = i;
+
+ if (oopsend <= i) {
+ int q;
+ int len;
+ int is_version;
+ char *oops;
+ char *version;
+
+ len = 2;
+ for (q = oopsstart; q <= oopsend; q++)
+ len += strlen(linepointer[q])+1;
+
+ oops = (char*)calloc(len, 1);
+ version = (char*)calloc(len, 1);
+
+ is_version = 0;
+ for (q = oopsstart; q <= oopsend; q++) {
+ if (!is_version)
+ is_version = ExtractVersion(linepointer[q], version);
+ strcat(oops, linepointer[q]);
+ strcat(oops, "\n");
+ }
+ /* too short oopses are invalid */
+ if (strlen(oops) > 100) {
+ QueueOops(oops, version);
+ oopsesfound++;
+ }
+ oopsstart = -1;
+ inbacktrace = 0;
+ oopsend = linecount;
+ free(oops);
+ free(version);
+ }
+ }
+ prevlevel = linelevel[i];
+ i++;
+ if (oopsstart > 0 && i-oopsstart > 50) {
+ oopsstart = -1;
+ inbacktrace = 0;
+ oopsend = linecount;
+ }
+ if (oopsstart > 0 && !inbacktrace && i-oopsstart > 30) {
+ oopsstart = -1;
+ inbacktrace = 0;
+ oopsend = linecount;
+ }
+ }
+ if (oopsstart >= 0) {
+ int q;
+ int len;
+ int is_version;
+ char *oops;
+ char *version;
+
+ oopsend = i-1;
+
+ len = 2;
+ while (oopsend > 0 && linepointer[oopsend] == NULL)
+ oopsend--;
+ for (q = oopsstart; q <= oopsend; q++)
+ len += strlen(linepointer[q])+1;
+
+ oops = (char*)calloc(len, 1);
+ version = (char*)calloc(len, 1);
+
+ is_version = 0;
+ for (q = oopsstart; q <= oopsend; q++) {
+ if (!is_version)
+ is_version = ExtractVersion(linepointer[q], version);
+ strcat(oops, linepointer[q]);
+ strcat(oops, "\n");
+ }
+ /* too short oopses are invalid */
+ if (strlen(oops) > 100) {
+ QueueOops(oops, version);
+ oopsesfound++;
+ }
+ oopsstart = -1;
+ inbacktrace = 0;
+ oopsend = linecount;
+ free(oops);
+ free(version);
+ }
+ free(linepointer);
+ free(linelevel);
+ linepointer = NULL;
+ linelevel = NULL;
+
+ return oopsesfound;
+}