summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.gitignore7
-rw-r--r--Makefile.am38
-rw-r--r--configure.ac3
-rw-r--r--inc/CrashTypes.h5
-rw-r--r--inc/abrtlib.h50
-rw-r--r--lib/Plugins/CCpp.cpp443
-rw-r--r--lib/Utils/Makefile.am9
-rw-r--r--lib/Utils/backtrace.c846
-rw-r--r--lib/Utils/backtrace.h (renamed from src/Backtrace/backtrace.h)74
-rw-r--r--lib/Utils/backtrace_parser.y (renamed from src/Backtrace/parser.y)388
-rw-r--r--lib/Utils/strbuf.c130
-rw-r--r--lib/Utils/strbuf.h (renamed from src/Backtrace/strbuf.h)14
-rw-r--r--lib/Utils/xfuncs.cpp8
-rw-r--r--lib/Utils/xfuncs.h104
-rwxr-xr-xscripts/abrt-bz-downloader (renamed from src/Backtrace/abrt-bz-downloader)2
-rwxr-xr-xscripts/abrt-bz-dupchecker (renamed from src/Backtrace/abrt-bz-dupchecker)38
-rwxr-xr-xscripts/abrt-bz-hashchecker (renamed from src/Backtrace/abrt-bz-hashchecker)2
-rwxr-xr-xscripts/abrt-bz-ratingfixer4
-rwxr-xr-xscripts/abrt-bz-stats24
-rw-r--r--scripts/abrt-rate-backtrace.c183
-rwxr-xr-xscripts/check-bt-parsability (renamed from src/Backtrace/check-bt-parsability)2
-rw-r--r--src/Backtrace/Makefile.am24
-rw-r--r--src/Backtrace/backtrace.c533
-rw-r--r--src/Backtrace/fallback.c174
-rw-r--r--src/Backtrace/fallback.h32
-rw-r--r--src/Backtrace/main.c361
-rw-r--r--src/Backtrace/strbuf.c112
-rw-r--r--src/CLI/dbus.h2
-rw-r--r--src/Makefile.am2
-rw-r--r--src/utils/Makefile.am11
-rw-r--r--src/utils/abrt-backtrace.1 (renamed from src/Backtrace/abrt-backtrace.1)0
-rw-r--r--src/utils/abrt-backtrace.c318
32 files changed, 1942 insertions, 2001 deletions
diff --git a/.gitignore b/.gitignore
index fa420738..dd253bfc 100644
--- a/.gitignore
+++ b/.gitignore
@@ -47,9 +47,9 @@ py-compile
scripts/*.bt
src/Applet/abrt-applet
src/CLI/abrt-cli
-src/Backtrace/abrt-backtrace
-src/Backtrace/parser.c
-src/Backtrace/parser.output
+src/utils/abrt-backtrace
+lib/Utils/backtrace_parser.c
+lib/Utils/backtrace_parser.output
src/Backtrace/*.bt
src/Daemon/abrtd
src/Gui/abrt.desktop
@@ -57,6 +57,7 @@ src/Hooks/abrt_exception_handler.py
src/Hooks/dumpoops
src/Hooks/abrt-hook-ccpp
src/Hooks/abrt-hook-python
+scripts/abrt-rate-backtrace
stamp-h1
x86_64/
*.pyc
diff --git a/Makefile.am b/Makefile.am
index 61f4e290..d448da75 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -2,39 +2,25 @@ SUBDIRS = lib src inc po icons
EXTRA_DIST = doc/CodingStyle abrt.spec
+RPM_DIRS = --define "_sourcedir `pwd`" \
+ --define "_rpmdir `pwd`" \
+ --define "_specdir `pwd`" \
+ --define "_builddir `pwd`" \
+ --define "_srcrpmdir `pwd`"
+
rpm: dist
- rpmbuild --define "_sourcedir `pwd`" \
- --define "_rpmdir `pwd`" \
- --define "_specdir `pwd`" \
- --define "_builddir `pwd`" \
- --define "_srcrpmdir `pwd`" \
- -ba abrt.spec
+ rpmbuild $(RPM_DIRS) -ba abrt.spec
srpm: dist
- rpmbuild --define "_sourcedir `pwd`" \
- --define "_rpmdir `pwd`" \
- --define "_specdir `pwd`" \
- --define "_builddir `pwd`" \
- --define "_srcrpmdir `pwd`" \
- -bs abrt.spec
+ rpmbuild $(RPM_DIRS) -bs abrt.spec
test-rpm: dist
- rpmbuild --define "_sourcedir `pwd`" \
- --define "_rpmdir `pwd`" \
- --define "_specdir `pwd`" \
- --define "_builddir `pwd`" \
- --define "_srcrpmdir `pwd`" \
- --define "_buildid `date +%G%m%e%k%M%S`" \
- -ba abrt.spec
+ rpmbuild --define "_buildid `date +%G%m%e%k%M%S`" \
+ $(RPM_DIRS) -ba abrt.spec
test-srpm: dist
- rpmbuild --define "_sourcedir `pwd`" \
- --define "_rpmdir `pwd`" \
- --define "_specdir `pwd`" \
- --define "_builddir `pwd`" \
- --define "_srcrpmdir `pwd`" \
- --define "_buildid `date +%G%m%e%k%M%S`" \
- -bs abrt.spec
+ rpmbuild --define "_buildid `date +%G%m%e%k%M%S`" \
+ $(RPM_DIRS) -bs abrt.spec
scratch-build: srpm
koji build --scratch dist-f12 $(PACKAGE)-$(VERSION)-1.fc12.src.rpm
diff --git a/configure.ac b/configure.ac
index c3925eec..ccaf9793 100644
--- a/configure.ac
+++ b/configure.ac
@@ -9,6 +9,7 @@ AC_ARG_ENABLE(debug,
AC_DISABLE_STATIC
AC_PROG_LIBTOOL
+AC_PROG_CC
AC_PROG_CXX
AC_SYS_LARGEFILE
@@ -103,7 +104,7 @@ AC_CONFIG_FILES([
src/Applet/Makefile
src/Gui/Makefile
src/CLI/Makefile
- src/Backtrace/Makefile
+ src/utils/Makefile
inc/Makefile
po/Makefile.in
icons/Makefile
diff --git a/inc/CrashTypes.h b/inc/CrashTypes.h
index 8fa9ab1b..87119ae0 100644
--- a/inc/CrashTypes.h
+++ b/inc/CrashTypes.h
@@ -30,6 +30,11 @@
#define FILENAME_COREDUMP "coredump"
#define FILENAME_BACKTRACE "backtrace"
#define FILENAME_MEMORYMAP "memorymap"
+// Used by CCpp analyzer to cache GetGlobalUUID() calls.
+#define FILENAME_GLOBAL_UUID "global_uuid"
+// Name of the function where the application crashed.
+// Optional.
+#define FILENAME_CRASH_FUNCTION "crash_function"
// filled by CDebugDump::Create() (which also fills CD_UID):
#define FILENAME_ARCHITECTURE "architecture"
#define FILENAME_KERNEL "kernel"
diff --git a/inc/abrtlib.h b/inc/abrtlib.h
index 7b00fe35..7cafc99c 100644
--- a/inc/abrtlib.h
+++ b/inc/abrtlib.h
@@ -41,6 +41,7 @@
#include <string>
#include "abrt_types.h"
+#include "xfuncs.h"
/* Some libc's forget to declare these, do it ourself */
extern char **environ;
@@ -95,13 +96,6 @@ extern int g_verbose;
#define VERB3 if (g_verbose >= 3)
/* there is no level > 3 */
-
-void* xmalloc(size_t size);
-void* xrealloc(void *ptr, size_t size);
-void* xzalloc(size_t size);
-char* xstrdup(const char *s);
-char* xstrndup(const char *s, int n);
-
char* skip_whitespace(const char *s);
char* skip_non_whitespace(const char *s);
@@ -123,38 +117,6 @@ extern ssize_t safe_write(int fd, const void *buf, size_t count);
// NB: will return short write on error, not -1,
// if some data was written before error occurred
extern ssize_t full_write(int fd, const void *buf, size_t count);
-extern void xwrite(int fd, const void *buf, size_t count);
-extern void xwrite_str(int fd, const char *str);
-
-void xpipe(int filedes[2]);
-void xdup(int from);
-void xdup2(int from, int to);
-off_t xlseek(int fd, off_t offset, int whence);
-void xsetenv(const char *key, const char *value);
-int xsocket(int domain, int type, int protocol);
-void xbind(int sockfd, struct sockaddr *my_addr, socklen_t addrlen);
-void xlisten(int s, int backlog);
-ssize_t xsendto(int s, const void *buf, size_t len, const struct sockaddr *to, socklen_t tolen);
-void xchdir(const char *path);
-void xstat(const char *name, struct stat *stat_buf);
-/* Just testing dent->d_type == DT_REG is wrong: some filesystems
- * do not report the type, they report DT_UNKNOWN for every dirent
- * (and this is not a bug in filesystem, this is allowed by standards).
- * This function handles this case. Note: it returns 0 on symlinks
- * even if they point to regular files.
- */
-int is_regular_file(struct dirent *dent, const char *dirname);
-
-void xmove_fd(int from, int to);
-int ndelay_on(int fd);
-int ndelay_off(int fd);
-int close_on_exec_on(int fd);
-char* xasprintf(const char *format, ...);
-char* xvasprintf(const char *format, va_list p);
-
-int xopen(const char *pathname, int flags);
-int xopen3(const char *pathname, int flags, int mode);
-void xunlink(const char *pathname);
/* copyfd_XX print read/write errors and return -1 if they occur */
off_t copyfd_eof(int src_fd, int dst_fd);
@@ -162,9 +124,6 @@ off_t copyfd_size(int src_fd, int dst_fd, off_t size);
void copyfd_exact_size(int src_fd, int dst_fd, off_t size);
off_t copy_file(const char *src_name, const char *dst_name, int mode);
-
-void xsetreuid(uid_t ruid, uid_t euid);
-void xsetregid(gid_t rgid, gid_t egid);
enum {
EXECFLG_INPUT = 1 << 0,
EXECFLG_OUTPUT = 1 << 1,
@@ -249,15 +208,8 @@ int daemon_is_ok();
/* Returns malloc'ed block */
char *encode_base64(const void *src, int length);
-bool dot_or_dotdot(const char *filename);
-char *last_char_is(const char *s, int c);
-bool string_to_bool(const char *s);
/* C++ style stuff */
-
-std::string ssprintf(const char *format, ...);
-std::string get_home_dir(uid_t uid);
-std::string concat_path_file(const char *path, const char *filename);
double get_dirsize(const char *pPath);
double get_dirsize_find_largest_dir(
const char *pPath,
diff --git a/lib/Plugins/CCpp.cpp b/lib/Plugins/CCpp.cpp
index 47378690..77cc0eda 100644
--- a/lib/Plugins/CCpp.cpp
+++ b/lib/Plugins/CCpp.cpp
@@ -23,12 +23,13 @@
//#include <nss.h>
//#include <sechash.h>
#include "abrtlib.h"
+#include "strbuf.h"
#include "CCpp.h"
#include "ABRTException.h"
#include "DebugDump.h"
#include "CommLayerInner.h"
#include "Polkit.h"
-
+#include "backtrace.h"
#include "CCpp_sha1.h"
using namespace std;
@@ -40,7 +41,7 @@ using namespace std;
0 - means unlimited, but the it's not guaranteed that /proc/<pid> of crashing
process might not be available for dump_helper
4 - means that 4 dump_helpers can run at the same time, which should be enough
-for ABRT, we can miss some crashes, but what are the odds that more processes
+for ABRT, we can miss some crashes, but what are the odds that more processes
crash at the same time? This value has been recommended by nhorman
*/
#define CORE_PIPE_LIMIT "4"
@@ -54,7 +55,7 @@ CAnalyzerCCpp::CAnalyzerCCpp() :
m_nDebugInfoCacheMB(4000)
{}
-static string CreateHash(const char *pInput)
+static string create_hash(const char *pInput)
{
unsigned int len;
@@ -163,111 +164,6 @@ static int ExecVP(char **pArgs, uid_t uid, int redirect_stderr, string& pOutput)
return status;
}
-enum LineRating
-{
- // RATING EXAMPLE
- MissingEverything = 0, // #0 0x0000dead in ?? ()
- MissingFunction = 1, // #0 0x0000dead in ?? () from /usr/lib/libfoobar.so.4
- MissingLibrary = 2, // #0 0x0000dead in foobar()
- MissingSourceFile = 3, // #0 0x0000dead in FooBar::FooBar () from /usr/lib/libfoobar.so.4
- Good = 4, // #0 0x0000dead in FooBar::crash (this=0x0) at /home/user/foobar.cpp:204
- BestRating = Good,
-};
-
-static LineRating rate_line(const char *line)
-{
-#define FOUND(x) (strstr(line, x) != NULL)
- /* see the "enum LineRating" comments for possible combinations */
- if (FOUND(" at "))
- return Good;
- const char *function = strstr(line, " in ");
- if (function)
- {
- if (function[4] == '?') /* " in ??" does not count */
- {
- function = NULL;
- }
- }
- bool library = FOUND(" from ");
- if (function && library)
- return MissingSourceFile;
- if (function)
- return MissingLibrary;
- if (library)
- return MissingFunction;
-
- return MissingEverything;
-#undef FOUND
-}
-
-/* returns number of "stars" to show */
-static int rate_backtrace(const char *backtrace)
-{
- int i, len;
- int multiplier = 0;
- int rating = 0;
- int best_possible_rating = 0;
- char last_lvl = 0;
-
- /* We look at the frames in reversed order, since:
- * - rate_line() checks starting from the first line of the frame
- * (note: it may need to look at more than one line!)
- * - we increase weight (multiplier) for every frame,
- * so that topmost frames end up most important
- */
- len = 0;
- for (i = strlen(backtrace) - 1; i >= 0; i--)
- {
- if (backtrace[i] == '#'
- && (backtrace[i+1] >= '0' && backtrace[i+1] <= '9') /* #N */
- && (i == 0 || backtrace[i-1] == '\n') /* it's at line start */
- ) {
- /* For one, "#0 xxx" always repeats, skip repeats */
- if (backtrace[i+1] == last_lvl)
- continue;
- last_lvl = backtrace[i+1];
-
- char *s = xstrndup(backtrace + i + 1, len);
- /* Replace tabs with spaces, rate_line() does not expect tabs.
- * Actually, even newlines may be there. Example of multiline frame
- * where " at SRCFILE" is on 2nd line:
- * #3 0x0040b35d in __libc_message (do_abort=<value optimized out>,
- * fmt=<value optimized out>) at ../sysdeps/unix/sysv/linux/libc_fatal.c:186
- */
- for (char *p = s; *p; p++)
- if (*p == '\t' || *p == '\n')
- *p = ' ';
- int lrate = rate_line(s);
- multiplier++;
- rating += lrate * multiplier;
- best_possible_rating += BestRating * multiplier;
- //log("lrate:%d rating:%d best_possible_rating:%d s:'%-.40s'", lrate, rating, best_possible_rating, s);
- free(s);
- len = 0; /* starting new line */
- }
- else
- {
- len++;
- }
- }
-
- /* Bogus 'backtrace' with zero frames? */
- if (best_possible_rating == 0)
- return 0;
-
- /* Returning number of "stars" to show */
- if (rating*10 >= best_possible_rating*8) /* >= 0.8 */
- return 4;
- if (rating*10 >= best_possible_rating*6)
- return 3;
- if (rating*10 >= best_possible_rating*4)
- return 2;
- if (rating*10 >= best_possible_rating*2)
- return 1;
-
- return 0;
-}
-
static void GetBacktrace(const char *pDebugDumpDir,
const char *pDebugInfoDirs,
string& pBacktrace)
@@ -629,121 +525,16 @@ string CAnalyzerCCpp::GetLocalUUID(const char *pDebugDumpDir)
}
string hash_str = trimmed_package + executable + independentBuildIdPC;
free(trimmed_package);
- return CreateHash(hash_str.c_str());
+ return create_hash(hash_str.c_str());
}
string CAnalyzerCCpp::GetGlobalUUID(const char *pDebugDumpDir)
{
- log(_("Getting global universal unique identification..."));
-
-//TODO: convert to fork_execv_on_steroids(), nuke static concat_str_vector
-
- string backtrace_path = concat_path_file(pDebugDumpDir, FILENAME_BACKTRACE);
- string executable;
- string package;
- string uid_str;
- {
- CDebugDump dd;
- dd.Open(pDebugDumpDir);
- dd.LoadText(FILENAME_EXECUTABLE, executable);
- dd.LoadText(FILENAME_PACKAGE, package);
- if (m_bBacktrace)
- dd.LoadText(CD_UID, uid_str);
- }
-
- string independent_backtrace;
- if (m_bBacktrace)
- {
- /* Run abrt-backtrace to get independent backtrace suitable
- to UUID calculation. */
- char *args[7];
- args[0] = (char*)"abrt-backtrace";
- args[1] = (char*)"--single-thread";
- args[2] = (char*)"--remove-exit-handlers";
- args[3] = (char*)"--frame-depth=5";
- args[4] = (char*)"--remove-noncrash-frames";
- args[5] = (char*)backtrace_path.c_str();
- args[6] = NULL;
-
- int pipeout[2];
- xpipe(pipeout); /* stdout of abrt-backtrace */
-
- fflush(NULL);
- pid_t child = fork();
- if (child == -1)
- perror_msg_and_die("fork");
- if (child == 0)
- {
- VERB1 log("Executing: %s", concat_str_vector(args).c_str());
-
- xmove_fd(pipeout[1], STDOUT_FILENO);
- close(pipeout[0]); /* read side of the pipe */
-
- /* abrt-backtrace is executed under the user's uid and gid. */
- uid_t uid = xatoi_u(uid_str.c_str());
- struct passwd* pw = getpwuid(uid);
- gid_t gid = pw ? pw->pw_gid : uid;
- setgroups(1, &gid);
- xsetregid(gid, gid);
- xsetreuid(uid, uid);
-
- execvp(args[0], args);
- VERB1 perror_msg("Can't execute '%s'", args[0]);
- exit(1);
- }
-
- close(pipeout[1]); /* write side of the pipe */
-
- /* Read the result from abrt-backtrace. */
- int r;
- char buff[1024];
- while ((r = safe_read(pipeout[0], buff, sizeof(buff) - 1)) > 0)
- {
- buff[r] = '\0';
- independent_backtrace += buff;
- }
- close(pipeout[0]);
-
- /* Wait until it exits, and check the exit status. */
- errno = 0;
- int status;
- waitpid(child, &status, 0);
- if (!WIFEXITED(status))
- {
- perror_msg("abrt-backtrace not executed properly, "
- "status: %x signal: %d", status, WIFSIGNALED(status));
- }
- else
- {
- int exit_status = WEXITSTATUS(status);
- if (exit_status == 79) /* EX_PARSINGFAILED */
- {
- /* abrt-backtrace returns alternative backtrace
- representation in this case, so everything will work
- as expected except worse duplication detection */
- log_msg("abrt-backtrace failed to parse the backtrace");
- }
- else if (exit_status == 80) /* EX_THREADDETECTIONFAILED */
- {
- /* abrt-backtrace returns backtrace with all threads
- in this case, so everything will work as expected
- except worse duplication detection */
- log_msg("abrt-backtrace failed to determine crash frame");
- }
- else if (exit_status != 0)
- {
- /* this is unexpected problem and it should be investigated */
- error_msg("abrt-backtrace run failed, exit value: %d",
- exit_status);
- }
- }
-
- /*VERB1 log("abrt-backtrace result: %s", independent_backtrace.c_str());*/
- }
- /* else: no backtrace, independent_backtrace == "" */
-
- string hash_base = package + executable + independent_backtrace;
- return CreateHash(hash_base.c_str());
+ CDebugDump dd;
+ dd.Open(pDebugDumpDir);
+ string uuid;
+ dd.LoadText(FILENAME_GLOBAL_UUID, uuid);
+ return uuid;
}
static bool DebuginfoCheckPolkit(uid_t uid)
@@ -777,27 +568,23 @@ static bool DebuginfoCheckPolkit(uid_t uid)
void CAnalyzerCCpp::CreateReport(const char *pDebugDumpDir, int force)
{
- string package;
- string UID;
+ string package, executable, UID;
CDebugDump dd;
dd.Open(pDebugDumpDir);
if (!m_bBacktrace)
- {
return;
- }
if (!force)
{
bool bt_exists = dd.Exist(FILENAME_BACKTRACE);
if (bt_exists)
- {
return; /* backtrace already exists */
- }
}
dd.LoadText(FILENAME_PACKAGE, package);
+ dd.LoadText(FILENAME_EXECUTABLE, executable);
dd.LoadText(CD_UID, UID);
dd.Close(); /* do not keep dir locked longer than needed */
@@ -805,28 +592,106 @@ void CAnalyzerCCpp::CreateReport(const char *pDebugDumpDir, int force)
if (m_bInstallDebugInfo && DebuginfoCheckPolkit(xatoi_u(UID.c_str())))
{
if (m_nDebugInfoCacheMB > 0)
- {
trim_debuginfo_cache(m_nDebugInfoCacheMB);
- }
InstallDebugInfos(pDebugDumpDir, m_sDebugInfoDirs.c_str(), build_ids);
}
else
- {
VERB1 log(_("Skipping debuginfo installation"));
- }
-
- string backtrace;
- GetBacktrace(pDebugDumpDir, m_sDebugInfoDirs.c_str(), backtrace);
+ /* Create and store backtrace. */
+ string backtrace_str;
+ GetBacktrace(pDebugDumpDir, m_sDebugInfoDirs.c_str(), backtrace_str);
dd.Open(pDebugDumpDir);
- dd.SaveText(FILENAME_BACKTRACE, (backtrace + build_ids).c_str());
+ dd.SaveText(FILENAME_BACKTRACE, (backtrace_str + build_ids).c_str());
+
if (m_bMemoryMap)
- {
dd.SaveText(FILENAME_MEMORYMAP, "memory map of the crashed C/C++ application, not implemented yet");
+
+ /* Compute and store UUID from the backtrace. */
+ char *backtrace_cpy = xstrdup(backtrace_str.c_str());
+ struct backtrace *backtrace = backtrace_parse(backtrace_cpy, false, false);
+ free(backtrace_cpy);
+ if (backtrace)
+ {
+ /* Get the quality of the full backtrace. */
+ float q1 = backtrace_quality(backtrace);
+
+ /* Remove all the other threads except the crash thread. */
+ struct thread *crash_thread = backtrace_find_crash_thread(backtrace);
+ if (crash_thread)
+ backtrace_remove_threads_except_one(backtrace, crash_thread);
+ else
+ log_msg("Detection of crash thread failed.\n");
+
+ /* Get the quality of the crash thread. */
+ float q2 = backtrace_quality(backtrace);
+
+ backtrace_remove_noncrash_frames(backtrace);
+
+ /* Do the frame removal now. */
+ backtrace_limit_frame_depth(backtrace, 5);
+ /* Frame removal can be done before removing exit handlers. */
+ backtrace_remove_exit_handlers(backtrace);
+
+ /* Get the quality of frames around the crash. */
+ float q3 = backtrace_quality(backtrace);
+
+ /* Compute UUID. */
+ struct strbuf *bt = backtrace_tree_as_str(backtrace, false);
+ strbuf_prepend_str(bt, executable.c_str());
+ strbuf_prepend_str(bt, package.c_str());
+ dd.SaveText(FILENAME_GLOBAL_UUID, create_hash(bt->buf).c_str());
+ strbuf_free(bt);
+
+ /* Compute and store backtrace rating. */
+ /* Compute and store backtrace rating. The crash frame
+ is more important that the others. The frames around
+ the crash are more important than the rest. */
+ float qtot = 0.25f * q1 + 0.35f * q2 + 0.4f * q3;
+
+ /* Turn the quality to rating. */
+ const char *rating;
+ if (qtot < 0.6f) rating = "0";
+ else if (qtot < 0.7f) rating = "1";
+ else if (qtot < 0.8f) rating = "2";
+ else if (qtot < 0.9f) rating = "3";
+ else rating = "4";
+ dd.SaveText(FILENAME_RATING, rating);
+
+ /* Get the function name from the crash frame. */
+ if (crash_thread)
+ {
+ struct frame *abort_frame = thread_find_abort_frame(crash_thread);
+ if (abort_frame)
+ {
+ struct frame *crash_frame = abort_frame->next;
+ if (crash_frame && crash_frame->function && 0 != strcmp(crash_frame->function, "??"))
+ dd.SaveText(FILENAME_CRASH_FUNCTION, crash_frame->function);
+ }
+ }
+
+ backtrace_free(backtrace);
}
- dd.SaveText(FILENAME_RATING, to_string(rate_backtrace(backtrace.c_str())).c_str());
+ else
+ {
+ /* If the parser failed fall back to the independent backtrace. */
+ /* If we write and use a hand-written parser instead of the bison one,
+ the parser never fails, and it will be possible to get rid of
+ the independent_backtrace and backtrace_rate_old. */
+ struct strbuf *ibt = independent_backtrace(backtrace_str.c_str());
+ strbuf_prepend_str(ibt, executable.c_str());
+ strbuf_prepend_str(ibt, package.c_str());
+ dd.SaveText(FILENAME_GLOBAL_UUID, create_hash(ibt->buf).c_str());
+ strbuf_free(ibt);
+
+ /* Compute and store backtrace rating. */
+ /* Crash frame is not known so store nothing. */
+ dd.SaveText(FILENAME_RATING, to_string(backtrace_rate_old(backtrace_str.c_str())).c_str());
+ }
+
dd.Close();
}
+
/*
this is just a workaround until kernel changes it's behavior
when handling pipes in core_pattern
@@ -835,66 +700,66 @@ void CAnalyzerCCpp::CreateReport(const char *pDebugDumpDir, int force)
#define CORE_SIZE_PATTERN "Max core file size=1:unlimited"
static int isdigit_str(char *str)
{
- do {
- if (*str < '0' || *str > '9')
- return 0;
- } while (*++str);
- return 1;
+ do {
+ if (*str < '0' || *str > '9')
+ return 0;
+ } while (*++str);
+ return 1;
}
static int set_limits()
{
- DIR *dir = opendir("/proc");
- if (!dir) {
- /* this shouldn't fail, but to be safe.. */
- return 1;
- }
-
- struct dirent *ent;
- while ((ent = readdir(dir)) != NULL) {
- if (!isdigit_str(ent->d_name))
- continue;
-
- char limits_name[sizeof("/proc/%s/limits") + sizeof(int)];
- snprintf(limits_name, sizeof(limits_name), "/proc/%s/limits", ent->d_name);
- FILE *limits_fp = fopen(limits_name, "r");
- if (!limits_fp) {
- break;
- }
-
- char line[128];
- char *ulimit_c = NULL;
- while (1) {
- if (fgets(line, sizeof(line)-1, limits_fp) == NULL)
- break;
- if (strncmp(line, "Max core file size", sizeof("Max core file size")-1) == 0) {
- ulimit_c = skip_whitespace(line + sizeof("Max core file size")-1);
- skip_non_whitespace(ulimit_c)[0] = '\0';
- break;
- }
- }
- fclose(limits_fp);
- if (!ulimit_c || ulimit_c[0] != '0' || ulimit_c[1] != '\0') {
- /*process has nonzero ulimit -c, so need to modify it*/
- continue;
- }
- /* echo -n 'Max core file size=1:unlimited' >/proc/PID/limits */
- int fd = open(limits_name, O_WRONLY);
- if (fd >= 0) {
- errno = 0;
- /*full_*/
- ssize_t n = write(fd, CORE_SIZE_PATTERN, sizeof(CORE_SIZE_PATTERN)-1);
- if (n < sizeof(CORE_SIZE_PATTERN)-1)
- log("warning: can't write core_size limit to: %s", limits_name);
- close(fd);
- }
+ DIR *dir = opendir("/proc");
+ if (!dir) {
+ /* this shouldn't fail, but to be safe.. */
+ return 1;
+ }
+
+ struct dirent *ent;
+ while ((ent = readdir(dir)) != NULL) {
+ if (!isdigit_str(ent->d_name))
+ continue;
+
+ char limits_name[sizeof("/proc/%s/limits") + sizeof(int)];
+ snprintf(limits_name, sizeof(limits_name), "/proc/%s/limits", ent->d_name);
+ FILE *limits_fp = fopen(limits_name, "r");
+ if (!limits_fp) {
+ break;
+ }
+
+ char line[128];
+ char *ulimit_c = NULL;
+ while (1) {
+ if (fgets(line, sizeof(line)-1, limits_fp) == NULL)
+ break;
+ if (strncmp(line, "Max core file size", sizeof("Max core file size")-1) == 0) {
+ ulimit_c = skip_whitespace(line + sizeof("Max core file size")-1);
+ skip_non_whitespace(ulimit_c)[0] = '\0';
+ break;
+ }
+ }
+ fclose(limits_fp);
+ if (!ulimit_c || ulimit_c[0] != '0' || ulimit_c[1] != '\0') {
+ /*process has nonzero ulimit -c, so need to modify it*/
+ continue;
+ }
+ /* echo -n 'Max core file size=1:unlimited' >/proc/PID/limits */
+ int fd = open(limits_name, O_WRONLY);
+ if (fd >= 0) {
+ errno = 0;
+ /*full_*/
+ ssize_t n = write(fd, CORE_SIZE_PATTERN, sizeof(CORE_SIZE_PATTERN)-1);
+ if (n < sizeof(CORE_SIZE_PATTERN)-1)
+ log("warning: can't write core_size limit to: %s", limits_name);
+ close(fd);
+ }
else
{
log("warning: can't open %s for writing", limits_name);
}
- }
- closedir(dir);
- return 0;
+ }
+ closedir(dir);
+ return 0;
}
#endif /* HOSTILE_KERNEL */
@@ -937,7 +802,7 @@ void CAnalyzerCCpp::Init()
fputs(CORE_PATTERN, fp);
fclose(fp);
}
-
+
/* read the core_pipe_limit and change it if it's == 0
otherwise the abrt-hook-ccpp won't be able to read /proc/<pid>
of the crashing process
@@ -972,7 +837,7 @@ void CAnalyzerCCpp::Init()
void CAnalyzerCCpp::DeInit()
{
- /* no need to restore the core_pipe_limit, because it's only used
+ /* no need to restore the core_pipe_limit, because it's only used
when there is s pipe in core_pattern
*/
FILE *fp = fopen(CORE_PATTERN_IFACE, "w");
diff --git a/lib/Utils/Makefile.am b/lib/Utils/Makefile.am
index 2667b18a..d3c7a255 100644
--- a/lib/Utils/Makefile.am
+++ b/lib/Utils/Makefile.am
@@ -2,12 +2,14 @@
# ABRTdUtils has much more. It is used by daemon and plugins only
lib_LTLIBRARIES = libABRTUtils.la libABRTdUtils.la
+AM_YFLAGS = --verbose
+
# Not used just yet:
# time.cpp
# xconnect.cpp
libABRTUtils_la_SOURCES = \
- xfuncs.cpp \
+ xfuncs.h xfuncs.cpp \
encbase64.cpp \
read_write.cpp \
logging.cpp \
@@ -21,7 +23,10 @@ libABRTUtils_la_SOURCES = \
DebugDump.h DebugDump.cpp \
abrt_dbus.h abrt_dbus.cpp \
CrashTypes.cpp \
- ABRTException.cpp
+ ABRTException.cpp \
+ backtrace.h backtrace.c \
+ backtrace_parser.y \
+ strbuf.h strbuf.c
libABRTUtils_la_CPPFLAGS = \
-Wall -Werror \
-I$(srcdir)/../../inc \
diff --git a/lib/Utils/backtrace.c b/lib/Utils/backtrace.c
new file mode 100644
index 00000000..3708eb17
--- /dev/null
+++ b/lib/Utils/backtrace.c
@@ -0,0 +1,846 @@
+/* -*-mode:c;c-file-style:"bsd";c-basic-offset:4;indent-tabs-mode:nil-*-
+ Copyright (C) 2009 RedHat inc.
+
+ This program 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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; if not, write to the Free Software Foundation, Inc.,
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+*/
+#include "backtrace.h"
+#include "strbuf.h"
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include "xfuncs.h"
+
+struct frame *frame_new()
+{
+ struct frame *f = malloc(sizeof(struct frame));
+ if (!f)
+ {
+ puts("Error while allocating memory for backtrace frame.");
+ exit(5);
+ }
+
+ f->function = NULL;
+ f->number = 0;
+ f->sourcefile = NULL;
+ f->signal_handler_called = false;
+ f->next = NULL;
+ return f;
+}
+
+void frame_free(struct frame *f)
+{
+ if (f->function)
+ free(f->function);
+ if (f->sourcefile)
+ free(f->sourcefile);
+ free(f);
+}
+
+struct frame *frame_add_sibling(struct frame *a, struct frame *b)
+{
+ struct frame *aa = a;
+ while (aa->next)
+ aa = aa->next;
+
+ aa->next = b;
+ return a;
+}
+
+/* Appends a string representation of 'frame' to the 'str'. */
+static void frame_append_str(struct frame *frame, struct strbuf *str, bool verbose)
+{
+ if (verbose)
+ strbuf_append_strf(str, " #%d", frame->number);
+ else
+ strbuf_append_str(str, " ");
+
+ if (frame->function)
+ strbuf_append_strf(str, " %s", frame->function);
+ if (verbose && frame->sourcefile)
+ {
+ if (frame->function)
+ strbuf_append_str(str, " at");
+ strbuf_append_strf(str, " %s", frame->sourcefile);
+ }
+
+ if (frame->signal_handler_called)
+ strbuf_append_str(str, " <signal handler called>");
+
+ strbuf_append_str(str, "\n");
+}
+
+static bool frame_is_exit_handler(struct frame *frame)
+{
+ return (frame->function
+ && frame->sourcefile
+ && 0 == strcmp(frame->function, "__run_exit_handlers")
+ && NULL != strstr(frame->sourcefile, "exit.c"));
+}
+
+/* Checks if a frame contains abort function used
+ * by operating system to exit application.
+ * E.g. in C it's called "abort" or "raise".
+ */
+static bool frame_is_abort_frame(struct frame *frame)
+{
+ if (!frame->function || !frame->sourcefile)
+ return false;
+
+ if (0 == strcmp(frame->function, "raise")
+ && (NULL != strstr(frame->sourcefile, "pt-raise.c")
+ || NULL != strstr(frame->sourcefile, "/libc.so.6")))
+ return true;
+ else if (0 == strcmp(frame->function, "exit")
+ && NULL != strstr(frame->sourcefile, "exit.c"))
+ return true;
+ else if (0 == strcmp(frame->function, "abort")
+ && (NULL != strstr(frame->sourcefile, "abort.c")
+ || NULL != strstr(frame->sourcefile, "/libc.so.6")))
+ return true;
+ else if (frame_is_exit_handler(frame))
+ return true;
+
+ return false;
+}
+
+static bool frame_is_noncrash_frame(struct frame *frame)
+{
+ /* Abort frames. */
+ if (frame_is_abort_frame(frame))
+ return true;
+
+ if (!frame->function)
+ return false;
+
+ if (0 == strcmp(frame->function, "__kernel_vsyscall"))
+ return true;
+
+ if (0 == strcmp(frame->function, "__assert_fail"))
+ return true;
+
+ if (!frame->sourcefile)
+ return false;
+
+ /* GDK */
+ if (0 == strcmp(frame->function, "gdk_x_error")
+ && 0 == strcmp(frame->sourcefile, "gdkmain-x11.c"))
+ return true;
+
+ /* X.org */
+ if (0 == strcmp(frame->function, "_XReply")
+ && 0 == strcmp(frame->sourcefile, "xcb_io.c"))
+ return true;
+ if (0 == strcmp(frame->function, "_XError")
+ && 0 == strcmp(frame->sourcefile, "XlibInt.c"))
+ return true;
+ if (0 == strcmp(frame->function, "XSync")
+ && 0 == strcmp(frame->sourcefile, "Sync.c"))
+ return true;
+ if (0 == strcmp(frame->function, "process_responses")
+ && 0 == strcmp(frame->sourcefile, "xcb_io.c"))
+ return true;
+
+ /* glib */
+ if (0 == strcmp(frame->function, "IA__g_log")
+ && 0 == strcmp(frame->sourcefile, "gmessages.c"))
+ return true;
+ if (0 == strcmp(frame->function, "IA__g_logv")
+ && 0 == strcmp(frame->sourcefile, "gmessages.c"))
+ return true;
+ if (0 == strcmp(frame->function, "IA__g_assertion_message")
+ && 0 == strcmp(frame->sourcefile, "gtestutils.c"))
+ return true;
+ if (0 == strcmp(frame->function, "IA__g_assertion_message_expr")
+ && 0 == strcmp(frame->sourcefile, "gtestutils.c"))
+ return true;
+
+ /* DBus */
+ if (0 == strcmp(frame->function, "gerror_to_dbus_error_message")
+ && 0 == strcmp(frame->sourcefile, "dbus-gobject.c"))
+ return true;
+ if (0 == strcmp(frame->function, "dbus_g_method_return_error")
+ && 0 == strcmp(frame->sourcefile, "dbus-gobject.c"))
+ return true;
+
+ /* libstdc++ */
+ if (0 == strcmp(frame->function, "__gnu_cxx::__verbose_terminate_handler")
+ && NULL != strstr(frame->sourcefile, "/vterminate.cc"))
+ return true;
+ if (0 == strcmp(frame->function, "__cxxabiv1::__terminate")
+ && NULL != strstr(frame->sourcefile, "/eh_terminate.cc"))
+ return true;
+ if (0 == strcmp(frame->function, "std::terminate")
+ && NULL != strstr(frame->sourcefile, "/eh_terminate.cc"))
+ return true;
+ if (0 == strcmp(frame->function, "__cxxabiv1::__cxa_throw")
+ && NULL != strstr(frame->sourcefile, "/eh_throw.cc"))
+ return true;
+
+ return false;
+}
+
+struct thread *thread_new()
+{
+ struct thread *t = malloc(sizeof(struct thread));
+ if (!t)
+ {
+ puts("Error while allocating memory for backtrace thread.");
+ exit(5);
+ }
+
+ t->number = 0;
+ t->frames = NULL;
+ t->next = NULL;
+ return t;
+}
+
+void thread_free(struct thread *t)
+{
+ while (t->frames)
+ {
+ struct frame *rm = t->frames;
+ t->frames = rm->next;
+ frame_free(rm);
+ }
+
+ free(t);
+}
+
+struct thread *thread_add_sibling(struct thread *a, struct thread *b)
+{
+ struct thread *aa = a;
+ while (aa->next)
+ aa = aa->next;
+
+ aa->next = b;
+ return a;
+}
+
+static int thread_get_frame_count(struct thread *thread)
+{
+ struct frame *f = thread->frames;
+ int count = 0;
+ while (f)
+ {
+ f = f->next;
+ ++count;
+ }
+ return count;
+}
+
+/* Appends string representation of 'thread' to the 'str'. */
+static void thread_append_str(struct thread *thread, struct strbuf *str, bool verbose)
+{
+ int framecount = thread_get_frame_count(thread);
+ if (verbose)
+ strbuf_append_strf(str, "Thread no. %d (%d frames)\n", thread->number, framecount);
+ else
+ strbuf_append_str(str, "Thread\n");
+ struct frame *frame = thread->frames;
+ while (frame)
+ {
+ frame_append_str(frame, str, verbose);
+ frame = frame->next;
+ }
+}
+
+/*
+ * Checks whether the thread it contains some known "abort" function.
+ * If a frame with the function is found, it is returned.
+ * If there are multiple frames with abort function, the lowest
+ * one is returned.
+ * Nonrecursive.
+ */
+struct frame *thread_find_abort_frame(struct thread *thread)
+{
+ struct frame *frame = thread->frames;
+ struct frame *result = NULL;
+ while (frame)
+ {
+ if (frame_is_abort_frame(frame))
+ result = frame;
+
+ frame = frame->next;
+ }
+
+ return result;
+}
+
+static void thread_remove_exit_handlers(struct thread *thread)
+{
+ struct frame *frame = thread->frames;
+ while (frame)
+ {
+ if (frame_is_exit_handler(frame))
+ {
+ /* Delete all frames from the beginning to this frame. */
+ while (thread->frames != frame)
+ {
+ struct frame *rm = thread->frames;
+ thread->frames = thread->frames->next;
+ frame_free(rm);
+ }
+ return;
+ }
+
+ frame = frame->next;
+ }
+}
+
+static void thread_remove_noncrash_frames(struct thread *thread)
+{
+ struct frame *prev = NULL;
+ struct frame *cur = thread->frames;
+ while (cur)
+ {
+ if (frame_is_noncrash_frame(cur))
+ {
+ /* This frame must be skipped, because it will
+ be deleted. */
+ if (prev)
+ prev->next = cur->next;
+ else
+ thread->frames = cur->next;
+
+ frame_free(cur);
+
+ /* Set cur to be valid, as it will be used to
+ advance to next item. */
+ if (prev)
+ cur = prev;
+ else
+ {
+ cur = thread->frames;
+ continue;
+ }
+ }
+
+ prev = cur;
+ cur = cur->next;
+ }
+}
+
+/* Counts the number of quality frames and the number of all frames
+ * in a thread.
+ * @param ok_count
+ * @param all_count
+ * Not zeroed. This function just adds the numbers to
+ * ok_count and all_count.
+ */
+static void thread_rating(struct thread *thread, int *ok_count, int *all_count)
+{
+ struct frame *frame = thread->frames;
+ while (frame)
+ {
+ *all_count += 1;
+ if (frame->signal_handler_called ||
+ (frame->function && 0 != strcmp(frame->function, "??")))
+ {
+ *ok_count += 1;
+ }
+ frame = frame->next;
+ }
+}
+
+struct backtrace *backtrace_new()
+{
+ struct backtrace *bt = malloc(sizeof(struct backtrace));
+ if (!bt)
+ {
+ puts("Error while allocating memory for backtrace.");
+ exit(5);
+ }
+
+ bt->threads = NULL;
+ bt->crash = NULL;
+ return bt;
+}
+
+void backtrace_free(struct backtrace *bt)
+{
+ while (bt->threads)
+ {
+ struct thread *rm = bt->threads;
+ bt->threads = rm->next;
+ thread_free(rm);
+ }
+
+ if (bt->crash)
+ frame_free(bt->crash);
+
+ free(bt);
+}
+
+static int backtrace_get_thread_count(struct backtrace *bt)
+{
+ struct thread *t = bt->threads;
+ int count = 0;
+ while (t)
+ {
+ t = t->next;
+ ++count;
+ }
+ return count;
+}
+
+void backtrace_print_tree(struct backtrace *backtrace, bool verbose)
+{
+ struct strbuf *strbuf = backtrace_tree_as_str(backtrace, verbose);
+ puts(strbuf->buf);
+ strbuf_free(strbuf);
+}
+
+struct strbuf *backtrace_tree_as_str(struct backtrace *backtrace, bool verbose)
+{
+ struct strbuf *str = strbuf_new();
+ if (verbose)
+ strbuf_append_strf(str, "Thread count: %d\n", backtrace_get_thread_count(backtrace));
+
+ if (backtrace->crash && verbose)
+ {
+ strbuf_append_str(str, "Crash frame: ");
+ frame_append_str(backtrace->crash, str, verbose);
+ }
+
+ struct thread *thread = backtrace->threads;
+ while (thread)
+ {
+ thread_append_str(thread, str, verbose);
+ thread = thread->next;
+ }
+
+ return str;
+}
+
+void backtrace_remove_threads_except_one(struct backtrace *backtrace,
+ struct thread *one)
+{
+ while (backtrace->threads)
+ {
+ struct thread *rm = backtrace->threads;
+ backtrace->threads = rm->next;
+ if (rm != one)
+ thread_free(rm);
+ }
+
+ one->next = NULL;
+ backtrace->threads = one;
+}
+
+/*
+ * Loop through all threads and if a single one contains the crash frame on the top,
+ * return it. Otherwise, return NULL.
+ *
+ * If require_abort is true, it is also required that the thread containing
+ * the crash frame contains some known "abort" function. In this case there can be
+ * multiple threads with the crash frame on the top, but only one of them might
+ * contain the abort function to succeed.
+ */
+static struct thread *backtrace_find_crash_thread_from_crash_frame(struct backtrace *backtrace,
+ bool require_abort)
+{
+ /*
+ * This code can be extended to compare something else when the function
+ * name is not available.
+ */
+ if (!backtrace->threads || !backtrace->crash || !backtrace->crash->function)
+ return NULL;
+
+ struct thread *result = NULL;
+ struct thread *thread = backtrace->threads;
+ while (thread)
+ {
+ if (thread->frames
+ && thread->frames->function
+ && 0 == strcmp(thread->frames->function, backtrace->crash->function)
+ && (!require_abort || thread_find_abort_frame(thread)))
+ {
+ if (result == NULL)
+ result = thread;
+ else
+ {
+ /* Second frame with the same function. Failure. */
+ return NULL;
+ }
+ }
+
+ thread = thread->next;
+ }
+
+ return result;
+}
+
+struct thread *backtrace_find_crash_thread(struct backtrace *backtrace)
+{
+ /* If there is no thread, be silent and report NULL. */
+ if (!backtrace->threads)
+ return NULL;
+
+ /* If there is just one thread, it is simple. */
+ if (!backtrace->threads->next)
+ return backtrace->threads;
+
+ /* If we have a crash frame *and* there is just one thread which has
+ * this frame on the top, it is also simple.
+ */
+ struct thread *thread;
+ thread = backtrace_find_crash_thread_from_crash_frame(backtrace, false);
+ if (thread)
+ return thread;
+
+ /* There are multiple threads with a frame indistinguishable from
+ * the crash frame on the top of stack.
+ * Try to search for known abort functions.
+ */
+ thread = backtrace_find_crash_thread_from_crash_frame(backtrace, true);
+
+ return thread; /* result or null */
+}
+
+void backtrace_limit_frame_depth(struct backtrace *backtrace, int depth)
+{
+ if (depth <= 0)
+ return;
+
+ struct thread *thread = backtrace->threads;
+ while (thread)
+ {
+ struct frame *frame = thread_find_abort_frame(thread);
+ if (frame)
+ frame = frame->next; /* Start counting from the frame following the abort fr. */
+ else
+ frame = thread->frames; /* Start counting from the first frame. */
+
+ /* Skip some frames to get the required stack depth. */
+ int i = depth;
+ struct frame *last_frame = NULL;
+ while (frame && i)
+ {
+ last_frame = frame;
+ frame = frame->next;
+ --i;
+ }
+
+ /* Delete the remaining frames. */
+ if (last_frame)
+ last_frame->next = NULL;
+
+ while (frame)
+ {
+ struct frame *rm = frame;
+ frame = frame->next;
+ frame_free(rm);
+ }
+
+ thread = thread->next;
+ }
+}
+
+void backtrace_remove_exit_handlers(struct backtrace *backtrace)
+{
+ struct thread *thread = backtrace->threads;
+ while (thread)
+ {
+ thread_remove_exit_handlers(thread);
+ thread = thread->next;
+ }
+}
+
+void backtrace_remove_noncrash_frames(struct backtrace *backtrace)
+{
+ struct thread *thread = backtrace->threads;
+ while (thread)
+ {
+ thread_remove_noncrash_frames(thread);
+ thread = thread->next;
+ }
+}
+
+/* Belongs to independent_backtrace(). */
+struct header
+{
+ struct strbuf *text;
+ struct header *next;
+};
+
+/* Belongs to independent_backtrace(). */
+static struct header *header_new()
+{
+ struct header *head = malloc(sizeof(struct header));
+ if (!head)
+ {
+ puts("Error while allocating memory for backtrace header.");
+ exit(5);
+ }
+ head->text = NULL;
+ head->next = NULL;
+ return head;
+}
+
+/* Recursively frees siblings. */
+/* Belongs to independent_backtrace(). */
+static void header_free(struct header *head)
+{
+ if (head->text)
+ strbuf_free(head->text);
+ if (head->next)
+ header_free(head->next);
+ free(head);
+}
+
+/* Inserts new header to array if it is not already there. */
+/* Belongs to independent_backtrace(). */
+static void header_set_insert(struct header *cur, struct strbuf *new)
+{
+ /* Duplicate found case. */
+ if (strcmp(cur->text->buf, new->buf) == 0)
+ return;
+
+ /* Last item case, insert new header here. */
+ if (cur->next == NULL)
+ {
+ cur->next = header_new();
+ cur->next->text = new;
+ return;
+ }
+
+ /* Move to next item in array case. */
+ header_set_insert(cur->next, new);
+}
+
+struct strbuf *independent_backtrace(const char *input)
+{
+ struct strbuf *header = strbuf_new();
+ bool in_bracket = false;
+ bool in_quote = false;
+ bool in_header = false;
+ bool in_digit = false;
+ bool has_at = false;
+ bool has_filename = false;
+ bool has_bracket = false;
+ struct header *headers = NULL;
+
+ const char *bk = input;
+ while (*bk)
+ {
+ if (bk[0] == '#'
+ && bk[1] >= '0' && bk[1] <= '7'
+ && bk[2] == ' ' /* take only #0...#7 (8 last stack frames) */
+ && !in_quote)
+ {
+ if (in_header && !has_filename)
+ strbuf_clear(header);
+ in_header = true;
+ }
+
+ if (!in_header)
+ {
+ ++bk;
+ continue;
+ }
+
+ if (isdigit(*bk) && !in_quote && !has_at)
+ in_digit = true;
+ else if (bk[0] == '\\' && bk[1] == '\"')
+ bk++;
+ else if (*bk == '\"')
+ in_quote = in_quote == true ? false : true;
+ else if (*bk == '(' && !in_quote)
+ {
+ in_bracket = true;
+ in_digit = false;
+ strbuf_append_char(header, '(');
+ }
+ else if (*bk == ')' && !in_quote)
+ {
+ in_bracket = false;
+ has_bracket = true;
+ in_digit = false;
+ strbuf_append_char(header, '(');
+ }
+ else if (*bk == '\n' && has_filename)
+ {
+ if (headers == NULL)
+ {
+ headers = header_new();
+ headers->text = header;
+ }
+ else
+ header_set_insert(headers, header);
+
+ header = strbuf_new();
+ in_bracket = false;
+ in_quote = false;
+ in_header = false;
+ in_digit = false;
+ has_at = false;
+ has_filename = false;
+ has_bracket = false;
+ }
+ else if (*bk == ',' && !in_quote)
+ in_digit = false;
+ else if (isspace(*bk) && !in_quote)
+ in_digit = false;
+ else if (bk[0] == 'a' && bk[1] == 't' && has_bracket && !in_quote)
+ {
+ has_at = true;
+ strbuf_append_char(header, 'a');
+ }
+ else if (bk[0] == ':' && has_at && isdigit(bk[1]) && !in_quote)
+ has_filename = true;
+ else if (in_header && !in_digit && !in_quote && !in_bracket)
+ strbuf_append_char(header, *bk);
+
+ bk++;
+ }
+
+ strbuf_free(header);
+
+ struct strbuf *result = strbuf_new();
+ struct header *loop = headers;
+ while (loop)
+ {
+ strbuf_append_str(result, loop->text->buf);
+ strbuf_append_char(result, '\n');
+ loop = loop->next;
+ }
+
+ if (headers)
+ header_free(headers); /* recursive */
+
+ return result;
+}
+
+/* Belongs to backtrace_rate_old(). */
+enum line_rating
+{
+ // RATING EXAMPLE
+ MissingEverything = 0, // #0 0x0000dead in ?? ()
+ MissingFunction = 1, // #0 0x0000dead in ?? () from /usr/lib/libfoobar.so.4
+ MissingLibrary = 2, // #0 0x0000dead in foobar()
+ MissingSourceFile = 3, // #0 0x0000dead in FooBar::FooBar () from /usr/lib/libfoobar.so.4
+ Good = 4, // #0 0x0000dead in FooBar::crash (this=0x0) at /home/user/foobar.cpp:204
+ BestRating = Good,
+};
+
+/* Belongs to backtrace_rate_old(). */
+static enum line_rating rate_line(const char *line)
+{
+#define FOUND(x) (strstr(line, x) != NULL)
+ /* see the comments at enum line_rating for possible combinations */
+ if (FOUND(" at "))
+ return Good;
+ const char *function = strstr(line, " in ");
+ if (function && function[4] == '?') /* " in ??" does not count */
+ function = NULL;
+ bool library = FOUND(" from ");
+ if (function && library)
+ return MissingSourceFile;
+ if (function)
+ return MissingLibrary;
+ if (library)
+ return MissingFunction;
+
+ return MissingEverything;
+#undef FOUND
+}
+
+/* just a fallback function, to be removed one day */
+int backtrace_rate_old(const char *backtrace)
+{
+ int i, len;
+ int multiplier = 0;
+ int rating = 0;
+ int best_possible_rating = 0;
+ char last_lvl = 0;
+
+ /* We look at the frames in reversed order, since:
+ * - rate_line() checks starting from the first line of the frame
+ * (note: it may need to look at more than one line!)
+ * - we increase weight (multiplier) for every frame,
+ * so that topmost frames end up most important
+ */
+ len = 0;
+ for (i = strlen(backtrace) - 1; i >= 0; i--)
+ {
+ if (backtrace[i] == '#'
+ && (backtrace[i+1] >= '0' && backtrace[i+1] <= '9') /* #N */
+ && (i == 0 || backtrace[i-1] == '\n')) /* it's at line start */
+ {
+ /* For one, "#0 xxx" always repeats, skip repeats */
+ if (backtrace[i+1] == last_lvl)
+ continue;
+ last_lvl = backtrace[i+1];
+
+ char *s = xstrndup(backtrace + i + 1, len);
+ /* Replace tabs with spaces, rate_line() does not expect tabs.
+ * Actually, even newlines may be there. Example of multiline frame
+ * where " at SRCFILE" is on 2nd line:
+ * #3 0x0040b35d in __libc_message (do_abort=<value optimized out>,
+ * fmt=<value optimized out>) at ../sysdeps/unix/sysv/linux/libc_fatal.c:186
+ */
+ char *p;
+ for (p = s; *p; p++)
+ {
+ if (*p == '\t' || *p == '\n')
+ *p = ' ';
+ }
+ int lrate = rate_line(s);
+ multiplier++;
+ rating += lrate * multiplier;
+ best_possible_rating += BestRating * multiplier;
+ //log("lrate:%d rating:%d best_possible_rating:%d s:'%-.40s'",
+ // lrate, rating, best_possible_rating, s);
+ free(s);
+ len = 0; /* starting new line */
+ }
+ else
+ {
+ len++;
+ }
+ }
+
+ /* Bogus 'backtrace' with zero frames? */
+ if (best_possible_rating == 0)
+ return 0;
+
+ /* Returning number of "stars" to show */
+ if (rating*10 >= best_possible_rating*8) /* >= 0.8 */
+ return 4;
+ if (rating*10 >= best_possible_rating*6)
+ return 3;
+ if (rating*10 >= best_possible_rating*4)
+ return 2;
+ if (rating*10 >= best_possible_rating*2)
+ return 1;
+
+ return 0;
+}
+
+float backtrace_quality(struct backtrace *backtrace)
+{
+ int ok_count = 0;
+ int all_count = 0;
+ struct thread *thread = backtrace->threads;
+ while (thread)
+ {
+ thread_rating(thread, &ok_count, &all_count);
+ thread = thread->next;
+ }
+
+ if (all_count == 0)
+ return 0;
+ return ok_count / (float)all_count;
+}
diff --git a/src/Backtrace/backtrace.h b/lib/Utils/backtrace.h
index 93897e93..df5def56 100644
--- a/src/Backtrace/backtrace.h
+++ b/lib/Utils/backtrace.h
@@ -1,5 +1,13 @@
/*
- Copyright (C) 2009 RedHat inc.
+ Backtrace parsing and processing.
+
+ If we transform analyzer plugins to separate applications one day,
+ this functionality should be moved to CCpp analyzer, which will
+ then easily provide what abrt-backtrace utility provides now. Currently
+ the code is used by abrt-backtrace, so it is shared in the utils
+ library.
+
+ Copyright (C) 2009, 2010 RedHat inc.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -18,6 +26,10 @@
#ifndef BACKTRACE_H
#define BACKTRACE_H
+#ifdef __cplusplus
+extern "C" {
+#endif
+
#include <stdio.h>
#include <stdbool.h>
@@ -45,8 +57,8 @@ struct thread
struct backtrace
{
struct thread *threads;
- /*
- * The frame where the crash happened according to GDB.
+ /*
+ * The frame where the crash happened according to GDB.
* It might be that we can not tell to which thread this frame belongs,
* because all threads end with mutually indistinguishable frames.
*/
@@ -60,6 +72,7 @@ extern struct frame *frame_add_sibling(struct frame *a, struct frame *b);
extern struct thread *thread_new();
extern void thread_free(struct thread *t);
extern struct thread *thread_add_sibling(struct thread *a, struct thread *b);
+extern struct frame *thread_find_abort_frame(struct thread *thread);
extern struct backtrace *backtrace_new();
extern void backtrace_free(struct backtrace *bt);
@@ -67,16 +80,19 @@ extern void backtrace_free(struct backtrace *bt);
/* Prints how internal backtrace representation looks to stdout. */
extern void backtrace_print_tree(struct backtrace *backtrace, bool verbose);
-/*
- * Frees all threads except the one provided as parameters.
+/* Returns the backtrace tree string representation. */
+extern struct strbuf *backtrace_tree_as_str(struct backtrace *backtrace, bool verbose);
+
+/*
+ * Frees all threads except the one provided as parameters.
* It does not check whether one is a member of backtrace.
* Caller must know that.
*/
-extern void backtrace_remove_threads_except_one(struct backtrace *backtrace,
+extern void backtrace_remove_threads_except_one(struct backtrace *backtrace,
struct thread *one);
-/*
- * Search all threads and tries to find the one that caused the crash.
+/*
+ * Search all threads and tries to find the one that caused the crash.
* It might return NULL if the thread cannot be determined.
*/
extern struct thread *backtrace_find_crash_thread(struct backtrace *backtrace);
@@ -89,12 +105,48 @@ extern void backtrace_limit_frame_depth(struct backtrace *backtrace, int depth);
extern void backtrace_remove_exit_handlers(struct backtrace *backtrace);
/*
- * Removes frames known as not causing crash, but that are often
+ * Removes frames known as not causing crash, but that are often
* a part of a backtrace.
*/
extern void backtrace_remove_noncrash_frames(struct backtrace *backtrace);
-/* Defined in parser.y. */
-extern struct backtrace *do_parse(char *input, bool debug_parser, bool debug_scanner);
+/* Parses the backtrace and stores it to a structure.
+ * @returns
+ * Returns the backtrace struct representation, or NULL if the parser failed.
+ * Caller of this function is responsible for backtrace_free()ing the returned value.
+ * Defined in backtrace_parser.y.
+ */
+extern struct backtrace *backtrace_parse(char *input, bool debug_parser, bool debug_scanner);
+
+/* Reads the input file and calculates "independent" backtrace from it. "Independent" means
+ * that the memory addresses that differ from run to run are removed from the backtrace, and
+ * also variable names and values are removed.
+ *
+ * This function can be called when backtrace_parse() call fails. It provides a shorter
+ * version of backtrace, with a chance that hash calculated from the returned value can be used
+ * to detect duplicates. However, this kind of duplicate detection is very low-quality.
+ * @returns
+ * The independent backtrace. Caller is responsible for calling
+ * strbuf_free() on it.
+ */
+extern struct strbuf *independent_backtrace(const char *input);
+
+/* Get the quality of backtrace, as a number of "stars".
+ * @returns
+ * Value 0 to 4.
+ */
+extern int backtrace_rate_old(const char *backtrace);
+
+/* Evaluates the quality of the backtrace, meaning the ratio of frames
+ * with function name fully known to all frames.
+ * @returns
+ * A number between 0 and 1. 0 means the lowest quality,
+ * 1 means full backtrace is known.
+ */
+extern float backtrace_quality(struct backtrace *backtrace);
+
+#ifdef __cplusplus
+}
+#endif
#endif
diff --git a/src/Backtrace/parser.y b/lib/Utils/backtrace_parser.y
index 42ac2206..8e2fc2b1 100644
--- a/src/Backtrace/parser.y
+++ b/lib/Utils/backtrace_parser.y
@@ -33,7 +33,7 @@ void yyerror(char const *s)
int yylex();
%}
-
+
/* This defines the type of yylval */
%union {
struct backtrace *backtrace;
@@ -50,35 +50,35 @@ int yylex();
%token END 0 "end of file"
%type <backtrace> backtrace
-%type <thread> threads
+%type <thread> threads
thread
-%type <frame> frames
- frame
- frame_head
- frame_head_1
- frame_head_2
- frame_head_3
- frame_head_4
+%type <frame> frames
+ frame
+ frame_head
+ frame_head_1
+ frame_head_2
+ frame_head_3
+ frame_head_4
frame_head_5
-%type <strbuf> identifier
- hexadecimal_digit_sequence
- hexadecimal_number
- file_name
- file_location
- function_call
- function_name
- digit_sequence
+%type <strbuf> identifier
+ hexadecimal_digit_sequence
+ hexadecimal_number
+ file_name
+ file_location
+ function_call
+ function_name
+ digit_sequence
frame_address_in_function
identifier_braces
identifier_braces_inside
identifier_template
identifier_template_inside
-%type <c> nondigit
- digit
- hexadecimal_digit
- file_name_char
- identifier_char
- identifier_char_no_templates
+%type <c> nondigit
+ digit
+ hexadecimal_digit
+ file_name_char
+ identifier_char
+ identifier_char_no_templates
identifier_first_char
identifier_braces_inside_char
identifier_template_inside_char
@@ -87,12 +87,12 @@ int yylex();
ws
ws_nonl
'(' ')' '+' '-' '/' '.' '_' '~' '[' ']' '\r' '?' '{' '}'
- 'a' 'b' 'c' 'd' 'e' 'f' 'g' 'h' 'i' 'j' 'k' 'l'
- 'm' 'n' 'o' 'p' 'q' 'r' 's' 't' 'u' 'v' 'w' 'x' 'y' 'z'
- 'A' 'B' 'C' 'D' 'E' 'F' 'G' 'H' 'I' 'J' 'K' 'L' 'M' 'N'
- 'O' 'P' 'Q' 'R' 'S' 'T' 'U' 'V' 'W' 'X' 'Y' 'Z'
+ 'a' 'b' 'c' 'd' 'e' 'f' 'g' 'h' 'i' 'j' 'k' 'l'
+ 'm' 'n' 'o' 'p' 'q' 'r' 's' 't' 'u' 'v' 'w' 'x' 'y' 'z'
+ 'A' 'B' 'C' 'D' 'E' 'F' 'G' 'H' 'I' 'J' 'K' 'L' 'M' 'N'
+ 'O' 'P' 'Q' 'R' 'S' 'T' 'U' 'V' 'W' 'X' 'Y' 'Z'
'0' '1' '2' '3' '4' '5' '6' '7' '8' '9'
- '\'' '`' ',' '#' '@' '<' '>' '=' ':' '"' ';' ' '
+ '\'' '`' ',' '#' '@' '<' '>' '=' ':' '"' ';' ' '
'\n' '\t' '\\' '!' '*' '%' '|' '^' '&' '$'
%type <num> frame_start
@@ -107,23 +107,23 @@ int yylex();
%% /* The grammar follows. */
-backtrace : /* empty */ %dprec 1
+backtrace : /* empty */ %dprec 1
{ $$ = g_backtrace = backtrace_new(); }
| threads wsa %dprec 2
- {
- $$ = g_backtrace = backtrace_new();
- $$->threads = $1;
+ {
+ $$ = g_backtrace = backtrace_new();
+ $$->threads = $1;
}
| frame_head wss threads wsa %dprec 4
- {
- $$ = g_backtrace = backtrace_new();
- $$->threads = $3;
+ {
+ $$ = g_backtrace = backtrace_new();
+ $$->threads = $3;
$$->crash = $1;
}
| frame wss threads wsa %dprec 3
- {
- $$ = g_backtrace = backtrace_new();
- $$->threads = $3;
+ {
+ $$ = g_backtrace = backtrace_new();
+ $$->threads = $3;
$$->crash = $1;
}
;
@@ -132,11 +132,11 @@ threads : thread
| threads '\n' thread { $$ = thread_add_sibling($1, $3); }
;
-thread : keyword_thread wss digit_sequence wsa '(' keyword_thread wss digit_sequence wsa ')' ':' wsa frames
- {
- $$ = thread_new();
- $$->frames = $13;
-
+thread : keyword_thread wss digit_sequence wsa '(' keyword_thread wss digit_sequence wsa ')' ':' wsa frames
+ {
+ $$ = thread_new();
+ $$->frames = $13;
+
if (sscanf($3->buf, "%d", &$$->number) != 1)
{
printf("Error while parsing thread number '%s'", $3->buf);
@@ -153,110 +153,110 @@ frames : frame { $$ = $1; }
frame : frame_head_1 wss variables %dprec 3
| frame_head_2 wss variables %dprec 4
- | frame_head_3 wss variables %dprec 5
- | frame_head_4 wss variables %dprec 2
+ | frame_head_3 wss variables %dprec 5
+ | frame_head_4 wss variables %dprec 2
| frame_head_5 wss variables %dprec 1
;
-frame_head : frame_head_1 %dprec 3
- | frame_head_2 %dprec 4
- | frame_head_3 %dprec 5
- | frame_head_4 %dprec 2
+frame_head : frame_head_1 %dprec 3
+ | frame_head_2 %dprec 4
+ | frame_head_3 %dprec 5
+ | frame_head_4 %dprec 2
| frame_head_5 %dprec 1
;
frame_head_1 : frame_start wss function_call wsa keyword_at wss file_location
- {
- $$ = frame_new();
- $$->number = $1;
- $$->function = $3->buf;
- strbuf_free_nobuf($3);
+ {
+ $$ = frame_new();
+ $$->number = $1;
+ $$->function = $3->buf;
+ strbuf_free_nobuf($3);
$$->sourcefile = $7->buf;
- strbuf_free_nobuf($7);
+ strbuf_free_nobuf($7);
}
;
frame_head_2 : frame_start wss frame_address_in_function wss keyword_at wss file_location
- {
- $$ = frame_new();
- $$->number = $1;
- $$->function = $3->buf;
- strbuf_free_nobuf($3);
+ {
+ $$ = frame_new();
+ $$->number = $1;
+ $$->function = $3->buf;
+ strbuf_free_nobuf($3);
$$->sourcefile = $7->buf;
- strbuf_free_nobuf($7);
+ strbuf_free_nobuf($7);
}
;
frame_head_3 : frame_start wss frame_address_in_function wss keyword_from wss file_location
- {
- $$ = frame_new();
- $$->number = $1;
- $$->function = $3->buf;
- strbuf_free_nobuf($3);
+ {
+ $$ = frame_new();
+ $$->number = $1;
+ $$->function = $3->buf;
+ strbuf_free_nobuf($3);
$$->sourcefile = $7->buf;
- strbuf_free_nobuf($7);
+ strbuf_free_nobuf($7);
}
;
frame_head_4 : frame_start wss frame_address_in_function
- {
- $$ = frame_new();
- $$->number = $1;
- $$->function = $3->buf;
- strbuf_free_nobuf($3);
+ {
+ $$ = frame_new();
+ $$->number = $1;
+ $$->function = $3->buf;
+ strbuf_free_nobuf($3);
}
;
frame_head_5 : frame_start wss keyword_sighandler
- {
- $$ = frame_new();
- $$->number = $1;
+ {
+ $$ = frame_new();
+ $$->number = $1;
$$->signal_handler_called = true;
}
-frame_start: '#' digit_sequence
- {
+frame_start: '#' digit_sequence
+ {
if (sscanf($2->buf, "%d", &$$) != 1)
{
printf("Error while parsing frame number '%s'.\n", $2->buf);
exit(5);
}
- strbuf_free($2);
+ strbuf_free($2);
}
;
-frame_address_in_function : hexadecimal_number wss keyword_in wss function_call
+frame_address_in_function : hexadecimal_number wss keyword_in wss function_call
{
- strbuf_free($1);
- $$ = $5;
+ strbuf_free($1);
+ $$ = $5;
}
| hexadecimal_number wss keyword_in wss keyword_vtable wss keyword_for wss function_call
{
- strbuf_free($1);
- $$ = $9;
+ strbuf_free($1);
+ $$ = $9;
}
;
-file_location : file_name ':' digit_sequence
+file_location : file_name ':' digit_sequence
{
- $$ = $1;
+ $$ = $1;
strbuf_free($3); /* line number not needed for now */
}
| file_name
;
variables : variables_line '\n'
- | variables_line END
- | variables_line wss_nonl '\n'
- | variables_line wss_nonl END
- | variables variables_line '\n'
- | variables variables_line END
- | variables variables_line wss_nonl '\n'
- | variables variables_line wss_nonl END
+ | variables_line END
+ | variables_line wss_nonl '\n'
+ | variables_line wss_nonl END
+ | variables variables_line '\n'
+ | variables variables_line END
+ | variables variables_line wss_nonl '\n'
+ | variables variables_line wss_nonl END
| variables wss_nonl variables_line '\n'
- | variables wss_nonl variables_line END
- | variables wss_nonl variables_line wss_nonl '\n'
- | variables wss_nonl variables_line wss_nonl END
+ | variables wss_nonl variables_line END
+ | variables wss_nonl variables_line wss_nonl '\n'
+ | variables wss_nonl variables_line wss_nonl END
;
variables_line : variables_char_no_framestart
@@ -269,10 +269,10 @@ variables_char : '#' | variables_char_no_framestart
/* Manually synchronized with function_args_char_base, except the first line. */
variables_char_no_framestart : digit | nondigit | '"' | '(' | ')' | '\\'
- | '+' | '-' | '<' | '>' | '/' | '.'
- | '[' | ']' | '?' | '\'' | '`' | ','
+ | '+' | '-' | '<' | '>' | '/' | '.'
+ | '[' | ']' | '?' | '\'' | '`' | ','
| '=' | '{' | '}' | '^' | '&' | '$'
- | ':' | ';' | '!' | '@' | '*'
+ | ':' | ';' | '!' | '@' | '*'
| '%' | '|' | '~'
;
@@ -287,10 +287,10 @@ return_type : identifier { strbuf_free($1); }
;
function_name : identifier
- | '?' '?'
- {
- $$ = strbuf_new();
- strbuf_append_str($$, "??");
+ | '?' '?'
+ {
+ $$ = strbuf_new();
+ strbuf_append_str($$, "??");
}
;
@@ -311,13 +311,13 @@ function_args_string : '"' wsa function_args_string_sequence wsa '"'
;
/* Manually synchronized with variables_char_no_framestart,
- * except the first line.
+ * except the first line.
*/
function_args_char_base : digit | nondigit | '#'
- | '+' | '-' | '<' | '>' | '/' | '.'
- | '[' | ']' | '?' | '\'' | '`' | ','
+ | '+' | '-' | '<' | '>' | '/' | '.'
+ | '[' | ']' | '?' | '\'' | '`' | ','
| '=' | '{' | '}' | '^' | '&' | '$'
- | ':' | ';' | '!' | '@' | '*'
+ | ':' | ';' | '!' | '@' | '*'
| '%' | '|' | '~'
;
function_args_escaped_char : '\\' function_args_char_base
@@ -344,42 +344,42 @@ file_name : file_name_char { $$ = strbuf_new(); strbuf_append_char($$, $1); }
file_name_char : digit | nondigit | '-' | '+' | '/' | '.'
;
- /* Function name, sometimes mangled.
+ /* Function name, sometimes mangled.
* Example: something@GLIB_2_2
* CClass::operator=
*/
identifier : identifier_first_char %dprec 1
{
- $$ = strbuf_new();
- strbuf_append_char($$, $1);
+ $$ = strbuf_new();
+ strbuf_append_char($$, $1);
}
| identifier_braces %dprec 1 /* e.g. (anonymous namespace)::WorkerThread */
| identifier identifier_char %dprec 1
{ $$ = strbuf_append_char($1, $2); }
| identifier identifier_braces %dprec 1
- {
- $$ = strbuf_append_str($1, $2->buf);
+ {
+ $$ = strbuf_append_str($1, $2->buf);
strbuf_free($2);
}
| identifier identifier_template %dprec 2
- {
- $$ = strbuf_append_str($1, $2->buf);
+ {
+ $$ = strbuf_append_str($1, $2->buf);
strbuf_free($2);
}
;
-identifier_first_char: nondigit
+identifier_first_char: nondigit
| '~' /* destructor */
| '*'
;
-identifier_char_no_templates : digit | nondigit | '@' | '.' | ':' | '='
+identifier_char_no_templates : digit | nondigit | '@' | '.' | ':' | '='
| '!' | '*' | '+' | '-' | '[' | ']'
| '~' | '&' | '/' | '%' | '^'
| '|' | ','
;
-/* Most of the special characters are required to support C++
+/* Most of the special characters are required to support C++
* operator overloading.
*/
identifier_char : identifier_char_no_templates | '<'| '>'
@@ -391,7 +391,7 @@ identifier_braces : '(' ')'
strbuf_append_char($$, $1);
strbuf_append_char($$, $2);
}
- | '(' identifier_braces_inside ')'
+ | '(' identifier_braces_inside ')'
{
$$ = strbuf_new();
strbuf_append_char($$, $1);
@@ -403,28 +403,28 @@ identifier_braces : '(' ')'
identifier_braces_inside : identifier_braces_inside_char %dprec 1
{
- $$ = strbuf_new();
- strbuf_append_char($$, $1);
- }
+ $$ = strbuf_new();
+ strbuf_append_char($$, $1);
+ }
| identifier_braces_inside identifier_braces_inside_char %dprec 1
{ $$ = strbuf_append_char($1, $2); }
| identifier_braces_inside '(' identifier_braces_inside ')' %dprec 1
- {
- $$ = strbuf_append_char($1, $2);
- $$ = strbuf_append_str($1, $3->buf);
+ {
+ $$ = strbuf_append_char($1, $2);
+ $$ = strbuf_append_str($1, $3->buf);
strbuf_free($3);
- $$ = strbuf_append_char($1, $4);
+ $$ = strbuf_append_char($1, $4);
}
| identifier_braces_inside '(' ')' %dprec 1
- {
- $$ = strbuf_append_char($1, $2);
- $$ = strbuf_append_char($1, $3);
+ {
+ $$ = strbuf_append_char($1, $2);
+ $$ = strbuf_append_char($1, $3);
}
| identifier_braces_inside identifier_template %dprec 2
{
- $$ = strbuf_append_str($1, $2->buf);
+ $$ = strbuf_append_str($1, $2->buf);
strbuf_free($2);
- }
+ }
;
identifier_braces_inside_char : identifier_char | ws_nonl
@@ -442,23 +442,23 @@ identifier_template : '<' identifier_template_inside '>'
identifier_template_inside : identifier_template_inside_char
{
- $$ = strbuf_new();
- strbuf_append_char($$, $1);
- }
+ $$ = strbuf_new();
+ strbuf_append_char($$, $1);
+ }
| identifier_template_inside identifier_template_inside_char
{ $$ = strbuf_append_char($1, $2); }
| identifier_template_inside '<' identifier_template_inside '>'
- {
- $$ = strbuf_append_char($1, $2);
- $$ = strbuf_append_str($1, $3->buf);
+ {
+ $$ = strbuf_append_char($1, $2);
+ $$ = strbuf_append_str($1, $3->buf);
strbuf_free($3);
- $$ = strbuf_append_char($1, $4);
+ $$ = strbuf_append_char($1, $4);
}
| identifier_template_inside identifier_braces
{
- $$ = strbuf_append_str($1, $2->buf);
+ $$ = strbuf_append_str($1, $2->buf);
strbuf_free($2);
- }
+ }
;
identifier_template_inside_char : identifier_char_no_templates | ws_nonl
@@ -468,24 +468,24 @@ digit_sequence : digit { $$ = strbuf_new(); strbuf_append_char($$, $1); }
| digit_sequence digit { $$ = strbuf_append_char($1, $2); }
;
-hexadecimal_number : '0' 'x' hexadecimal_digit_sequence
- {
+hexadecimal_number : '0' 'x' hexadecimal_digit_sequence
+ {
$$ = $3;
- strbuf_prepend_str($$, "0x");
+ strbuf_prepend_str($$, "0x");
}
- | '0' 'X' hexadecimal_digit_sequence
- {
+ | '0' 'X' hexadecimal_digit_sequence
+ {
$$ = $3;
- strbuf_prepend_str($$, "0X");
+ strbuf_prepend_str($$, "0X");
}
;
-hexadecimal_digit_sequence : hexadecimal_digit
- {
- $$ = strbuf_new();
- strbuf_append_char($$, $1);
+hexadecimal_digit_sequence : hexadecimal_digit
+ {
+ $$ = strbuf_new();
+ strbuf_append_char($$, $1);
}
- | hexadecimal_digit_sequence hexadecimal_digit
+ | hexadecimal_digit_sequence hexadecimal_digit
{ $$ = strbuf_append_char($1, $2); }
;
@@ -499,7 +499,7 @@ digit : '0' | '1' | '2' | '3' | '4' | '5' | '6' | '7' | '8' | '9'
nondigit : 'a' | 'b' | 'c' | 'd' | 'e' | 'f' | 'g' | 'h' | 'i' | 'j' | 'k'
| 'l' | 'm' | 'n' | 'o' | 'p' | 'q' | 'r' | 's' | 't' | 'u' | 'v' | 'w'
- | 'x' | 'y' | 'z'
+ | 'x' | 'y' | 'z'
| 'A' | 'B' | 'C' | 'D' | 'E' | 'F' | 'G' | 'H' | 'I' | 'J' | 'K'
| 'L' | 'M' | 'N' | 'O' | 'P' | 'Q' | 'R' | 'S' | 'T' | 'U' | 'V' | 'W'
| 'X' | 'Y' | 'Z'
@@ -523,9 +523,9 @@ wss_nonl : ws_nonl
wss : ws
| wss ws
;
-
+
/* whitespace sequence allowed */
-wsa :
+wsa :
| wss
;
@@ -572,17 +572,91 @@ int yylex()
return c;
}
-/* This is the function that is actually called from outside.
+/* This is the function that is actually called from outside.
* @returns
* Backtrace structure. Caller is responsible for calling
* backtrace_free() on this.
* Returns NULL when parsing failed.
*/
-struct backtrace *do_parse(char *input, bool debug_parser, bool debug_scanner)
+struct backtrace *backtrace_parse(char *input, bool debug_parser, bool debug_scanner)
{
+ /* Skip the backtrace header information. */
+ char *btnoheader_a = strstr(input, "\nThread ");
+ char *btnoheader_b = strstr(input, "\n#");
+ char *btnoheader = input;
+ if (btnoheader_a)
+ {
+ if (btnoheader_b && btnoheader_b < btnoheader_a)
+ btnoheader = btnoheader_b + 1;
+ else
+ btnoheader = btnoheader_a + 1;
+ }
+ else if (btnoheader_b)
+ btnoheader = btnoheader_b + 1;
+
+ /* Bug fixing hack for broken backtraces.
+ * Sometimes the empty line is missing before new Thread section.
+ * This is against rules, but a bug (now fixed) in Linux kernel caused
+ * this.
+ */
+ char *thread_fixer = btnoheader + 1;
+ while ((thread_fixer = strstr(thread_fixer, "\nThread")) != NULL)
+ {
+ if (thread_fixer[-1] != '\n')
+ thread_fixer[-1] = '\n';
+
+ ++thread_fixer;
+ }
+
+ /* Bug fixing hack for GDB - remove wrongly placed newlines from the backtrace.
+ * Sometimes there is a newline in the local variable section.
+ * This is caused by some GDB hooks.
+ * Example: rhbz#538440
+ * #1 0x0000000000420939 in sync_deletions (mse=0x0, mfld=0x1b85020)
+ * at mail-stub-exchange.c:1119
+ * status = <value optimized out>
+ * iter = 0x1af38d0
+ * known_messages = 0x1b5c460Traceback (most recent call last):
+ * File "/usr/share/glib-2.0/gdb/glib.py", line 98, in next
+ * if long (node["key_hash"]) >= 2:
+ * RuntimeError: Cannot access memory at address 0x11
+ *
+ * __PRETTY_FUNCTION__ = "sync_deletions"
+ * #2 0x0000000000423e6b in refresh_folder (stub=0x1b77f10 [MailStubExchange],
+ * ...
+ *
+ * The code removes every empty line (also those containing only spaces),
+ * which is not followed by a new Thread section.
+ *
+ * rhbz#555251 contains empty lines with spaces
+ */
+ char *empty_line = btnoheader;
+ char *c = btnoheader;
+ while (*c)
+ {
+ if (*c == '\n')
+ {
+ char *cend = c + 1;
+ while (*cend == ' ' || *cend == '\t')
+ ++cend;
+ if (*cend == '\n' && 0 != strncmp(cend, "\nThread", strlen("\nThread")))
+ memmove(c, cend, strlen(cend) + 1);
+ }
+ ++c;
+ }
+ while ((empty_line = strstr(empty_line, "\n\n")) != NULL)
+ {
+ if (0 != strncmp(empty_line, "\n\nThread", strlen("\n\nThread")))
+ {
+ /* Remove the empty line by converting the first newline to char. */
+ empty_line[0] = 'X';
+ }
+ ++empty_line;
+ }
+
/* Prepare for running parser. */
g_backtrace = 0;
- yyin = input;
+ yyin = btnoheader;
#if YYDEBUG == 1
if (debug_parser)
yydebug = 1;
@@ -591,10 +665,10 @@ struct backtrace *do_parse(char *input, bool debug_parser, bool debug_scanner)
/* Parse. */
int failure = yyparse();
-
+
/* Separate debugging output. */
if (scanner_echo)
- putchar('\n');
+ putchar('\n');
if (failure)
{
diff --git a/lib/Utils/strbuf.c b/lib/Utils/strbuf.c
new file mode 100644
index 00000000..ab872543
--- /dev/null
+++ b/lib/Utils/strbuf.c
@@ -0,0 +1,130 @@
+/* -*-mode:c;c-file-style:"bsd";c-basic-offset:4;indent-tabs-mode:nil-*-
+ String buffer implementation
+
+ Copyright (C) 2009 RedHat inc.
+
+ This program 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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; if not, write to the Free Software Foundation, Inc.,
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+*/
+#include "strbuf.h"
+#include <stdlib.h>
+#include <stdio.h>
+#include <assert.h>
+#include <string.h>
+#include <stdarg.h>
+#include "xfuncs.h"
+
+struct strbuf *strbuf_new()
+{
+ struct strbuf *buf = xmalloc(sizeof(struct strbuf));
+ buf->alloc = 8;
+ buf->len = 0;
+ buf->buf = xmalloc(buf->alloc);
+ buf->buf[buf->len] = '\0';
+ return buf;
+}
+
+void strbuf_free(struct strbuf *strbuf)
+{
+ free(strbuf->buf);
+ free(strbuf);
+}
+
+void strbuf_free_nobuf(struct strbuf *strbuf)
+{
+ free(strbuf);
+}
+
+
+void strbuf_clear(struct strbuf *strbuf)
+{
+ assert(strbuf->alloc > 0);
+ strbuf->len = 0;
+ strbuf->buf[0] = '\0';
+}
+
+/* Ensures that the buffer can be extended by num characters
+ * without touching malloc/realloc.
+ */
+static void strbuf_grow(struct strbuf *strbuf, int num)
+{
+ if (strbuf->len + num + 1 > strbuf->alloc)
+ {
+ while (strbuf->len + num + 1 > strbuf->alloc)
+ strbuf->alloc *= 2; /* huge grow = infinite loop */
+
+ strbuf->buf = realloc(strbuf->buf, strbuf->alloc);
+ if (!strbuf->buf)
+ {
+ puts("Error while allocating memory for string buffer.");
+ exit(5);
+ }
+ }
+}
+
+struct strbuf *strbuf_append_char(struct strbuf *strbuf, char c)
+{
+ strbuf_grow(strbuf, 1);
+ strbuf->buf[strbuf->len++] = c;
+ strbuf->buf[strbuf->len] = '\0';
+ return strbuf;
+}
+
+struct strbuf *strbuf_append_str(struct strbuf *strbuf, const char *str)
+{
+ int len = strlen(str);
+ strbuf_grow(strbuf, len);
+ assert(strbuf->len + len < strbuf->alloc);
+ strcpy(strbuf->buf + strbuf->len, str);
+ strbuf->len += len;
+ return strbuf;
+}
+
+struct strbuf *strbuf_prepend_str(struct strbuf *strbuf, const char *str)
+{
+ int len = strlen(str);
+ strbuf_grow(strbuf, len);
+ assert(strbuf->len + len < strbuf->alloc);
+ memmove(strbuf->buf + len, strbuf->buf, strbuf->len + 1);
+ memcpy(strbuf->buf, str, len);
+ return strbuf;
+}
+
+struct strbuf *strbuf_append_strf(struct strbuf *strbuf, const char *format, ...)
+{
+ va_list p;
+ char *string_ptr;
+
+ va_start(p, format);
+ string_ptr = xvasprintf(format, p);
+ va_end(p);
+
+ strbuf_append_str(strbuf, string_ptr);
+ free(string_ptr);
+ return strbuf;
+}
+
+struct strbuf *strbuf_prepend_strf(struct strbuf *strbuf, const char *format, ...)
+{
+ va_list p;
+ char *string_ptr;
+
+ va_start(p, format);
+ string_ptr = xvasprintf(format, p);
+ va_end(p);
+
+ strbuf_prepend_str(strbuf, string_ptr);
+ free(string_ptr);
+ return strbuf;
+}
diff --git a/src/Backtrace/strbuf.h b/lib/Utils/strbuf.h
index f2e4459e..d0c9c981 100644
--- a/src/Backtrace/strbuf.h
+++ b/lib/Utils/strbuf.h
@@ -20,6 +20,10 @@
#ifndef STRBUF_H
#define STRBUF_H
+#ifdef __cplusplus
+extern "C" {
+#endif
+
struct strbuf {
int alloc;
int len;
@@ -32,7 +36,13 @@ extern void strbuf_free(struct strbuf *strbuf);
extern void strbuf_free_nobuf(struct strbuf *strbuf);
extern void strbuf_clear(struct strbuf *strbuf);
extern struct strbuf *strbuf_append_char(struct strbuf *strbuf, char c);
-extern struct strbuf *strbuf_append_str(struct strbuf *strbuf, char *str);
-extern struct strbuf *strbuf_prepend_str(struct strbuf *strbuf, char *str);
+extern struct strbuf *strbuf_append_str(struct strbuf *strbuf, const char *str);
+extern struct strbuf *strbuf_prepend_str(struct strbuf *strbuf, const char *str);
+extern struct strbuf *strbuf_append_strf(struct strbuf *strbuf, const char *format, ...);
+extern struct strbuf *strbuf_prepend_strf(struct strbuf *strbuf, const char *format, ...);
+
+#ifdef __cplusplus
+}
+#endif
#endif
diff --git a/lib/Utils/xfuncs.cpp b/lib/Utils/xfuncs.cpp
index 74fdac76..fd29b704 100644
--- a/lib/Utils/xfuncs.cpp
+++ b/lib/Utils/xfuncs.cpp
@@ -389,12 +389,12 @@ bool string_to_bool(const char *s)
void xsetreuid(uid_t ruid, uid_t euid)
{
- if (setreuid(ruid, euid) != 0)
- perror_msg_and_die("can't set %cid %lu", 'u', (long)ruid);
+ if (setreuid(ruid, euid) != 0)
+ perror_msg_and_die("can't set %cid %lu", 'u', (long)ruid);
}
void xsetregid(gid_t rgid, gid_t egid)
{
- if (setregid(rgid, egid) != 0)
- perror_msg_and_die("can't set %cid %lu", 'g', (long)rgid);
+ if (setregid(rgid, egid) != 0)
+ perror_msg_and_die("can't set %cid %lu", 'g', (long)rgid);
}
diff --git a/lib/Utils/xfuncs.h b/lib/Utils/xfuncs.h
new file mode 100644
index 00000000..327d9c9a
--- /dev/null
+++ b/lib/Utils/xfuncs.h
@@ -0,0 +1,104 @@
+/*
+ Copyright (C) 2010 ABRT team
+ Copyright (C) 2010 RedHat Inc
+
+ This program 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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; if not, write to the Free Software Foundation, Inc.,
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+*/
+#ifndef ABRT_XFUNCS_H
+#define ABRT_XFUNCS_H
+
+#include <sys/socket.h>
+#include <sys/stat.h>
+#include <dirent.h>
+#include <stdbool.h>
+
+int ndelay_on(int fd);
+int ndelay_off(int fd);
+int close_on_exec_on(int fd);
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+void* xmalloc(size_t size);
+void* xrealloc(void *ptr, size_t size);
+void* xzalloc(size_t size);
+char* xstrdup(const char *s);
+char* xstrndup(const char *s, int n);
+
+#ifdef __cplusplus
+}
+#endif
+
+void xpipe(int filedes[2]);
+void xdup(int from);
+void xdup2(int from, int to);
+void xmove_fd(int from, int to);
+
+extern void xwrite(int fd, const void *buf, size_t count);
+extern void xwrite_str(int fd, const char *str);
+
+off_t xlseek(int fd, off_t offset, int whence);
+
+void xchdir(const char *path);
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+char* xvasprintf(const char *format, va_list p);
+char* xasprintf(const char *format, ...);
+
+#ifdef __cplusplus
+}
+#endif
+
+#ifdef __cplusplus
+std::string ssprintf(const char *format, ...);
+#endif
+
+void xsetenv(const char *key, const char *value);
+int xsocket(int domain, int type, int protocol);
+void xbind(int sockfd, struct sockaddr *my_addr, socklen_t addrlen);
+void xlisten(int s, int backlog);
+ssize_t xsendto(int s, const void *buf, size_t len, const struct sockaddr *to, socklen_t tolen);
+void xstat(const char *name, struct stat *stat_buf);
+
+#ifdef __cplusplus
+std::string get_home_dir(uid_t uid);
+#endif
+
+int xopen3(const char *pathname, int flags, int mode);
+int xopen(const char *pathname, int flags);
+void xunlink(const char *pathname);
+
+/* Just testing dent->d_type == DT_REG is wrong: some filesystems
+ * do not report the type, they report DT_UNKNOWN for every dirent
+ * (and this is not a bug in filesystem, this is allowed by standards).
+ * This function handles this case. Note: it returns 0 on symlinks
+ * even if they point to regular files.
+ */
+int is_regular_file(struct dirent *dent, const char *dirname);
+bool dot_or_dotdot(const char *filename);
+char *last_char_is(const char *s, int c);
+#ifdef __cplusplus
+std::string concat_path_file(const char *path, const char *filename);
+#endif
+bool string_to_bool(const char *s);
+
+void xsetreuid(uid_t ruid, uid_t euid);
+void xsetregid(gid_t rgid, gid_t egid);
+
+#endif
diff --git a/src/Backtrace/abrt-bz-downloader b/scripts/abrt-bz-downloader
index 7f294257..d7f5a719 100755
--- a/src/Backtrace/abrt-bz-downloader
+++ b/scripts/abrt-bz-downloader
@@ -3,7 +3,7 @@
# ABRT Bugzilla Backtrace Downloader
# Downloads all backtraces reported by ABRT from Bugzilla.
#
-# Please do not run this script unless it's neccessary to do so.
+# Please do not run this script unless it's neccessary to do so.
# It forces Bugzilla to send data related to thousands of bug reports.
from bugzilla import RHBugzilla
diff --git a/src/Backtrace/abrt-bz-dupchecker b/scripts/abrt-bz-dupchecker
index 654a3702..89e13281 100755
--- a/src/Backtrace/abrt-bz-dupchecker
+++ b/scripts/abrt-bz-dupchecker
@@ -2,13 +2,13 @@
# -*- mode:python -*-
# ABRT Bugzilla Duplication Checker
# Downloads all backtraces reported by ABRT from Bugzilla,
-# and search for duplicates using the newest ABRT duplication
+# and search for duplicates using the newest ABRT duplication
# checker.
#
# Some bugs in Bugzilla were reported by older ABRT
# versions, which had poor duplication detection.
#
-# Please do not run this script unless it's neccessary to do so.
+# Please do not run this script unless it's neccessary to do so.
# It forces Bugzilla to send data related to thousands of bug reports.
#
#
@@ -37,9 +37,9 @@ parser.add_option("-b", "--bugzilla", dest="bugzilla", default="https://bugzilla
help="Bugzilla URL (defaults to Red Hat Bugzilla)", metavar="URL")
parser.add_option("-v", "--verbose", dest="verbose",
help="Detailed output")
-parser.add_option("-c", "--close", help="Close some of the bugs in Bugzilla (DANGEROUS)",
+parser.add_option("-c", "--close", help="Close some of the bugs in Bugzilla (DANGEROUS)",
action="store_true", default=False, dest="close")
-parser.add_option("-i", "--wiki", help="Generate output in wiki syntax",
+parser.add_option("-i", "--wiki", help="Generate output in wiki syntax",
action="store_true", default=False, dest="wiki")
(options, args) = parser.parse_args()
@@ -60,7 +60,7 @@ print "{0} bugs found.".format(len(buginfos))
#
# Load cache from previous run. Speeds up the case Bugzilla closes connection.
-# The cache should be manually removed after a day or so, because the data in it
+# The cache should be manually removed after a day or so, because the data in it
# are no longer valid.
#
database = {}
@@ -77,7 +77,7 @@ def save_to_cache():
f = open(CACHE_FILE, 'w')
cPickle.dump(database, f, 2)
cPickle.dump(ids, f, 2)
- f.close()
+ f.close()
count = 0
for buginfo in buginfos:
@@ -121,7 +121,7 @@ for buginfo in buginfos:
# abrt_hash, but it does not copy the attachment.
if not downloaded:
continue
-
+
command = ["./abrt-backtrace"]
command.append(filename)
command.append("--single-thread")
@@ -161,12 +161,12 @@ dupclosecount = 0
for backtrace, components in database.items():
for component, bugitems in components.items():
dupcount += len(bugitems) - 1
- dupclosecount += min(len(filter(lambda x: x <= 2,
- map(lambda x: x["comments"],
+ dupclosecount += min(len(filter(lambda x: x <= 2,
+ map(lambda x: x["comments"],
bugitems))),
len(bugitems) - 1)
-# Get the component owner.
+# Get the component owner.
# Sort the duplicates by the component owner, and
# filter out those which should not be printed.
dups = []
@@ -185,7 +185,7 @@ for backtrace, components in database.items():
owner = component_f12[0]["owner"]
except KeyError:
pass
-
+
dups.append((component, owner, bugitems, backtrace))
print "."
@@ -196,12 +196,12 @@ if options.close:
for (component, owner, bugitems, backtrace) in dups:
# Find the master bug item
# Its the one with the most comments.
-
+
# Sort function sorting by comment count.
def commentCmp(x, y):
if x['comments'] < y['comments']:
return 1
- elif x['comments'] == y['comments']:
+ elif x['comments'] == y['comments']:
# Sort by bug id, older bugs should became the master bug
if x['id'] > y['id']:
return 1
@@ -225,7 +225,7 @@ if options.close:
continue
print "Closing bug #{0} with {1} comments as a duplicate of #{2}.".format(item['id'], item['comments'], master['id'])
- bug.close("DUPLICATE", int(master['id']), "",
+ bug.close("DUPLICATE", int(master['id']), "",
("This bug appears to have been filled using a buggy version of ABRT, because\n" +
"it contains a backtrace which is a duplicate of backtrace from bug #{0}.\n\n" +
"Sorry for the inconvenience.").format(master['id']))
@@ -233,7 +233,7 @@ if options.close:
counter += 1
if counter > LIMIT:
sys.exit(0)
-
+
bz.logout()
print
@@ -257,8 +257,8 @@ for (component, owner, bugitems, backtrace) in sorted(dups, cmp):
print "----"
print "* component: '''{0}''' ({1})".format(component, owner)
print "* duplicates: {0}".format(
- reduce(lambda x,y: x+", "+y,
- map(lambda x: "#[https://bugzilla.redhat.com/show_bug.cgi?id={0} {0}] ({1} comments)".format(x['id'],x['comments']),
+ reduce(lambda x,y: x+", "+y,
+ map(lambda x: "#[https://bugzilla.redhat.com/show_bug.cgi?id={0} {0}] ({1} comments)".format(x['id'],x['comments']),
bugitems)))
print "* backtrace:"
for line in backtrace.replace("Thread\n", "").splitlines():
@@ -266,7 +266,7 @@ for (component, owner, bugitems, backtrace) in sorted(dups, cmp):
else:
print "Component: {0} ({1})".format(component, owner)
print "Duplicates: {0}".format(
- reduce(lambda x,y: x+", "+y,
- map(lambda x: "{0} ({1})".format(x['id'],x['comments']),
+ reduce(lambda x,y: x+", "+y,
+ map(lambda x: "{0} ({1})".format(x['id'],x['comments']),
bugitems)))
print "Backtrace: {0}".format(backtrace)
diff --git a/src/Backtrace/abrt-bz-hashchecker b/scripts/abrt-bz-hashchecker
index ec7ce1a6..bb66c7d4 100755
--- a/src/Backtrace/abrt-bz-hashchecker
+++ b/scripts/abrt-bz-hashchecker
@@ -2,7 +2,7 @@
# -*- mode:python -*-
# Checks how many bugs in Bugzilla have the same hash.
#
-# Please do not run this script unless it's neccessary to do so.
+# Please do not run this script unless it's neccessary to do so.
# It forces Bugzilla to send data related to thousands of bug reports.
from bugzilla import RHBugzilla
diff --git a/scripts/abrt-bz-ratingfixer b/scripts/abrt-bz-ratingfixer
index 9bdb1276..f774081d 100755
--- a/scripts/abrt-bz-ratingfixer
+++ b/scripts/abrt-bz-ratingfixer
@@ -118,7 +118,7 @@ for buginfo in buginfos:
continue
# Rate the backtrace using external program.
- command = ["./abrt-rate-backtrace"]
+ command = ["abrt-backtrace", "--rate"]
command.append(filename)
helper = subprocess.Popen(command, stdout=subprocess.PIPE)
rating, err = helper.communicate()
@@ -166,7 +166,7 @@ for id, bug in ids.items():
# Get the component owner
owner = "Failed to get component owner"
try:
- component_info = json.load(urllib.urlopen("https://admin.fedoraproject.org/pkgdb/packages/name/{0}?tg_format=json".format(bug['component'])))
+ component_info = json.load(urllib.urlopen("https://admin.fedoraproject.org/pkgdb/acls/name/{0}?tg_format=json".format(bug['component'])))
component_packages = component_info['packageListings']
component_f12 = filter(lambda x:x["collection"]["version"]=="12", component_packages)
if len(component_f12) == 1:
diff --git a/scripts/abrt-bz-stats b/scripts/abrt-bz-stats
index d84fd55e..ea44f5bd 100755
--- a/scripts/abrt-bz-stats
+++ b/scripts/abrt-bz-stats
@@ -2,7 +2,7 @@
# -*- mode:python -*-
# ABRT Bugzilla Statistics script
#
-# Please do not run this script unless it's neccessary to do so.
+# Please do not run this script unless it's neccessary to do so.
# It forces Bugzilla to send info about thousands of bug reports.
from bugzilla import RHBugzilla
@@ -24,15 +24,15 @@ parser.add_option("-p", "--password", dest="password",
parser.add_option("-b", "--bugzilla", dest="bugzilla",
help="Bugzilla URL (defaults to Red Hat Bugzilla)", metavar="URL")
# Weekly stats shows the impact of changes in ABRT early.
-parser.add_option("-w", "--weekly", help="Generate weekly report instead of monthly",
+parser.add_option("-w", "--weekly", help="Generate weekly report instead of monthly",
action="store_true", default=False, dest="weekly")
# HTML output for blogs etc.
-parser.add_option("-t", "--html", help="Generate HTML output",
+parser.add_option("-t", "--html", help="Generate HTML output",
action="store_true", default=False, dest="html")
-parser.add_option("-i", "--wiki", help="Generate output in wiki syntax",
+parser.add_option("-i", "--wiki", help="Generate output in wiki syntax",
action="store_true", default=False, dest="wiki")
# Newest stats first
-parser.add_option("-r", "--reversed", help="Display the newest stats first",
+parser.add_option("-r", "--reversed", help="Display the newest stats first",
action="store_true", default=False, dest="reversed")
(options, args) = parser.parse_args()
if not options.user or len(options.user) == 0:
@@ -67,7 +67,7 @@ def save_to_cache():
global buginfos_loaded
f = open(CACHE_FILE, 'w')
pickle.dump(buginfos_loaded, f, 2)
- f.close()
+ f.close()
#
# Load data from Bugzilla
@@ -84,16 +84,16 @@ for buginfo in buginfos:
continue
# creation date, format YEAR-MONTH-DAY
- created = buginfo.creation_ts[0:10].replace(".", "-")
+ created = buginfo.creation_ts[0:10].replace(".", "-")
# last change to bug, format YEAR-MONTH-DAY
lastchange = buginfo.delta_ts[0:10].replace(".", "-")
status = buginfo.bug_status # status during the last change
if buginfo.resolution != "":
status += "_" + buginfo.resolution
buginfos_loaded[buginfo.bug_id] = {
- 'created':created,
- 'lastchange':lastchange,
- 'status':status,
+ 'created':created,
+ 'lastchange':lastchange,
+ 'status':status,
'component':buginfo.component}
bz.logout()
@@ -158,10 +158,10 @@ class TimeSpan:
if resolution in ["CLOSED_CURRENTRELEASE", "CLOSED_RAWHIDE", "CLOSED_ERRATA",
"CLOSED_UPSTREAM", "CLOSED_NEXTRELEASE"]:
self.closed_as_useful += 1
- elif resolution in ["CLOSED_DUPLICATE", "CLOSED_CANTFIX",
+ elif resolution in ["CLOSED_DUPLICATE", "CLOSED_CANTFIX",
"CLOSED_INSUFFICIENT_DATA"]:
self.closed_as_waste += 1
- elif resolution in ["CLOSED_NOTABUG", "CLOSED_WONTFIX",
+ elif resolution in ["CLOSED_NOTABUG", "CLOSED_WONTFIX",
"CLOSED_DEFERRED", "CLOSED_WORKSFORME"]:
self.closed_as_other += 1
diff --git a/scripts/abrt-rate-backtrace.c b/scripts/abrt-rate-backtrace.c
deleted file mode 100644
index 07248233..00000000
--- a/scripts/abrt-rate-backtrace.c
+++ /dev/null
@@ -1,183 +0,0 @@
-/* -*-mode:c++;c-file-style:"bsd";c-basic-offset:4;indent-tabs-mode:nil-*-
- * Returns rating 0-4 of backtrace file on stdout.
- * 4 - backtrace with complete or almost-complete debuginfos
- * 0 - useless backtrace with no debuginfos
- * Compile:
- * gcc abrt-rate-backtrace.c -std=c99 -o abrt-rate-backtrace
- */
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <stdbool.h>
-
-// Die if we can't allocate n+1 bytes (space for the null terminator) and copy
-// the (possibly truncated to length n) string into it.
-static char* xstrndup(const char *s, int n)
-{
- int m;
- char *t;
-
- /* We can just xmalloc(n+1) and strncpy into it, */
- /* but think about xstrndup("abc", 10000) wastage! */
- m = n;
- t = (char*) s;
- while (m) {
- if (!*t) break;
- m--;
- t++;
- }
- n -= m;
- t = (char*) malloc(n + 1);
- t[n] = '\0';
-
- return (char*) memcpy(t, s, n);
-}
-
-enum LineRating
-{
- // RATING EXAMPLE
- MissingEverything = 0, // #0 0x0000dead in ?? ()
- MissingFunction = 1, // #0 0x0000dead in ?? () from /usr/lib/libfoobar.so.4
- MissingLibrary = 2, // #0 0x0000dead in foobar()
- MissingSourceFile = 3, // #0 0x0000dead in FooBar::FooBar () from /usr/lib/libfoobar.so.4
- Good = 4, // #0 0x0000dead in FooBar::crash (this=0x0) at /home/user/foobar.cpp:204
- BestRating = Good,
-};
-
-static enum LineRating rate_line(const char *line)
-{
-#define FOUND(x) (strstr(line, x) != NULL)
- /* see the "enum LineRating" comments for possible combinations */
- if (FOUND(" at "))
- return Good;
- const char *function = strstr(line, " in ");
- if (function)
- {
- if (function[4] == '?') /* " in ??" does not count */
- {
- function = NULL;
- }
- }
- bool library = FOUND(" from ");
- if (function && library)
- return MissingSourceFile;
- if (function)
- return MissingLibrary;
- if (library)
- return MissingFunction;
-
- return MissingEverything;
-#undef FOUND
-}
-
-/* returns number of "stars" to show */
-static int rate_backtrace(const char *backtrace)
-{
- int i, len;
- int multiplier = 0;
- int rating = 0;
- int best_possible_rating = 0;
- char last_lvl = 0;
-
- /* We look at the frames in reversed order, since:
- * - rate_line() checks starting from the first line of the frame
- * (note: it may need to look at more than one line!)
- * - we increase weight (multiplier) for every frame,
- * so that topmost frames end up most important
- */
- len = 0;
- for (i = strlen(backtrace) - 1; i >= 0; i--)
- {
- if (backtrace[i] == '#'
- && (backtrace[i+1] >= '0' && backtrace[i+1] <= '9') /* #N */
- && (i == 0 || backtrace[i-1] == '\n') /* it's at line start */
- ) {
- /* For one, "#0 xxx" always repeats, skip repeats */
- if (backtrace[i+1] == last_lvl)
- continue;
- last_lvl = backtrace[i+1];
-
- char *s = xstrndup(backtrace + i + 1, len);
- /* Replace tabs with spaces, rate_line() does not expect tabs.
- * Actually, even newlines may be there. Example of multiline frame
- * where " at SRCFILE" is on 2nd line:
- * #3 0x0040b35d in __libc_message (do_abort=<value optimized out>,
- * fmt=<value optimized out>) at ../sysdeps/unix/sysv/linux/libc_fatal.c:186
- */
- for (char *p = s; *p; p++)
- if (*p == '\t' || *p == '\n')
- *p = ' ';
- int lrate = rate_line(s);
- multiplier++;
- rating += lrate * multiplier;
- best_possible_rating += BestRating * multiplier;
- //log("lrate:%d rating:%d best_possible_rating:%d s:'%-.40s'", lrate, rating, best_possible_rating, s);
- free(s);
- len = 0; /* starting new line */
- }
- else
- {
- len++;
- }
- }
-
- /* Bogus 'backtrace' with zero frames? */
- if (best_possible_rating == 0)
- return 0;
-
- /* Returning number of "stars" to show */
- if (rating*10 >= best_possible_rating*8) /* >= 0.8 */
- return 4;
- if (rating*10 >= best_possible_rating*6)
- return 3;
- if (rating*10 >= best_possible_rating*4)
- return 2;
- if (rating*10 >= best_possible_rating*2)
- return 1;
-
- return 0;
-}
-
-int main(int argc, char **argv)
-{
- if (argc != 2)
- {
- fprintf(stderr, "Usage: %s BACKTRACE_FILE\n", argv[0]);
- return 1;
- }
-
- FILE *fp = fopen(argv[1], "r");
- if (!fp)
- {
- fprintf(stderr, "Cannot open the input file.\n");
- return 2;
- }
- fseek(fp, 0, SEEK_END);
- size_t size = ftell(fp);
- fseek(fp, 0, SEEK_SET);
-
- char *file = malloc(size + 1);
- if (!file)
- {
- fprintf(stderr, "Malloc error.\n");
- return 3;
- }
- size_t read = fread(file, size, 1, fp);
- if (read != 1)
- {
- fprintf(stderr, "Error while reading file.\n", argv[0]);
- return 4;
- }
- fclose(fp);
- file[size] = '\0';
-
- int rating = 4;
- /* Do not rate Python backtraces. */
- if (NULL == strstr(file, "Local variables in innermost frame:\n"))
- rating = rate_backtrace(file);
-
- free(file);
- fprintf(stdout, "%d", rating);
- return 0;
-}
diff --git a/src/Backtrace/check-bt-parsability b/scripts/check-bt-parsability
index a5018bfa..b154ad88 100755
--- a/src/Backtrace/check-bt-parsability
+++ b/scripts/check-bt-parsability
@@ -15,6 +15,6 @@ do
echo "-$file"
FAIL=$(($FAIL+1))
fi
-done
+done
echo ""
echo "Passed $PASS and failed $FAIL."
diff --git a/src/Backtrace/Makefile.am b/src/Backtrace/Makefile.am
deleted file mode 100644
index c39575ca..00000000
--- a/src/Backtrace/Makefile.am
+++ /dev/null
@@ -1,24 +0,0 @@
-bin_PROGRAMS = abrt-backtrace
-
-#BUILT_SOURCES = parser.h
-#AM_YFLAGS = -d
-AM_YFLAGS = --verbose
-
-abrt_backtrace_CFLAGS = -Wall
-
-abrt_backtrace_SOURCES = \
- main.c \
- backtrace.h backtrace.c \
- strbuf.h strbuf.c \
- fallback.h fallback.c \
- parser.y
-
-#abrt_backtrace_CPPFLAGS = \
-# -I$(srcdir)/../../inc \
-# -I$(srcdir)/../../lib/Utils
-
-#abrt_backtrace_LDADD = \
-# ../../lib/Utils/libABRTUtils.la
-
-man_MANS = abrt-backtrace.1
-EXTRA_DIST = $(man_MANS)
diff --git a/src/Backtrace/backtrace.c b/src/Backtrace/backtrace.c
deleted file mode 100644
index 9b697da1..00000000
--- a/src/Backtrace/backtrace.c
+++ /dev/null
@@ -1,533 +0,0 @@
-/* -*-mode:c++;c-file-style:"bsd";c-basic-offset:2;indent-tabs-mode:nil-*-
- Copyright (C) 2009 RedHat inc.
-
- This program 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; either version 2 of the License, or
- (at your option) any later version.
-
- 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; if not, write to the Free Software Foundation, Inc.,
- 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
-*/
-#include "backtrace.h"
-#include <stdlib.h>
-#include <string.h>
-
-struct frame *frame_new()
-{
- struct frame *f = malloc(sizeof(struct frame));
- if (!f)
- {
- puts("Error while allocating memory for backtrace frame.");
- exit(5);
- }
-
- f->function = NULL;
- f->number = 0;
- f->sourcefile = NULL;
- f->signal_handler_called = false;
- f->next = NULL;
- return f;
-}
-
-void frame_free(struct frame *f)
-{
- if (f->function)
- free(f->function);
- if (f->sourcefile)
- free(f->sourcefile);
- free(f);
-}
-
-struct frame *frame_add_sibling(struct frame *a, struct frame *b)
-{
- struct frame *aa = a;
- while (aa->next)
- aa = aa->next;
-
- aa->next = b;
- return a;
-}
-
-static void frame_print_tree(struct frame *frame, bool verbose)
-{
- if (verbose)
- printf(" #%d", frame->number);
- else
- printf(" ");
-
- if (frame->function)
- printf(" %s", frame->function);
- if (frame->sourcefile)
- {
- if (frame->function)
- printf(" at");
- printf(" %s", frame->sourcefile);
- }
-
- if (frame->signal_handler_called)
- printf(" <signal handler called>");
-
- puts(""); /* newline */
-}
-
-static bool frame_is_exit_handler(struct frame *frame)
-{
- return (frame->function
- && frame->sourcefile
- && 0 == strcmp(frame->function, "__run_exit_handlers")
- && NULL != strstr(frame->sourcefile, "exit.c"));
-}
-
-/* Checks if a frame contains abort function used
- * by operating system to exit application.
- * E.g. in C it's called "abort" or "raise".
- */
-static bool frame_is_abort_frame(struct frame *frame)
-{
- if (!frame->function || !frame->sourcefile)
- return false;
-
- if (0 == strcmp(frame->function, "raise")
- && (NULL != strstr(frame->sourcefile, "pt-raise.c")
- || NULL != strstr(frame->sourcefile, "/libc.so.6")))
- return true;
- else if (0 == strcmp(frame->function, "exit")
- && NULL != strstr(frame->sourcefile, "exit.c"))
- return true;
- else if (0 == strcmp(frame->function, "abort")
- && (NULL != strstr(frame->sourcefile, "abort.c")
- || NULL != strstr(frame->sourcefile, "/libc.so.6")))
- return true;
- else if (frame_is_exit_handler(frame))
- return true;
-
- return false;
-}
-
-static bool frame_is_noncrash_frame(struct frame *frame)
-{
- /* Abort frames. */
- if (frame_is_abort_frame(frame))
- return true;
-
- if (!frame->function)
- return false;
-
- if (0 == strcmp(frame->function, "__kernel_vsyscall"))
- return true;
-
- if (0 == strcmp(frame->function, "__assert_fail"))
- return true;
-
- if (!frame->sourcefile)
- return false;
-
- /* GDK */
- if (0 == strcmp(frame->function, "gdk_x_error")
- && 0 == strcmp(frame->sourcefile, "gdkmain-x11.c"))
- return true;
-
- /* X.org */
- if (0 == strcmp(frame->function, "_XReply")
- && 0 == strcmp(frame->sourcefile, "xcb_io.c"))
- return true;
- if (0 == strcmp(frame->function, "_XError")
- && 0 == strcmp(frame->sourcefile, "XlibInt.c"))
- return true;
- if (0 == strcmp(frame->function, "XSync")
- && 0 == strcmp(frame->sourcefile, "Sync.c"))
- return true;
- if (0 == strcmp(frame->function, "process_responses")
- && 0 == strcmp(frame->sourcefile, "xcb_io.c"))
- return true;
-
- /* glib */
- if (0 == strcmp(frame->function, "IA__g_log")
- && 0 == strcmp(frame->sourcefile, "gmessages.c"))
- return true;
- if (0 == strcmp(frame->function, "IA__g_logv")
- && 0 == strcmp(frame->sourcefile, "gmessages.c"))
- return true;
- if (0 == strcmp(frame->function, "IA__g_assertion_message")
- && 0 == strcmp(frame->sourcefile, "gtestutils.c"))
- return true;
- if (0 == strcmp(frame->function, "IA__g_assertion_message_expr")
- && 0 == strcmp(frame->sourcefile, "gtestutils.c"))
- return true;
-
- /* DBus */
- if (0 == strcmp(frame->function, "gerror_to_dbus_error_message")
- && 0 == strcmp(frame->sourcefile, "dbus-gobject.c"))
- return true;
- if (0 == strcmp(frame->function, "dbus_g_method_return_error")
- && 0 == strcmp(frame->sourcefile, "dbus-gobject.c"))
- return true;
-
- /* libstdc++ */
- if (0 == strcmp(frame->function, "__gnu_cxx::__verbose_terminate_handler")
- && NULL != strstr(frame->sourcefile, "/vterminate.cc"))
- return true;
- if (0 == strcmp(frame->function, "__cxxabiv1::__terminate")
- && NULL != strstr(frame->sourcefile, "/eh_terminate.cc"))
- return true;
- if (0 == strcmp(frame->function, "std::terminate")
- && NULL != strstr(frame->sourcefile, "/eh_terminate.cc"))
- return true;
- if (0 == strcmp(frame->function, "__cxxabiv1::__cxa_throw")
- && NULL != strstr(frame->sourcefile, "/eh_throw.cc"))
- return true;
-
- return false;
-}
-
-struct thread *thread_new()
-{
- struct thread *t = malloc(sizeof(struct thread));
- if (!t)
- {
- puts("Error while allocating memory for backtrace thread.");
- exit(5);
- }
-
- t->number = 0;
- t->frames = NULL;
- t->next = NULL;
- return t;
-}
-
-void thread_free(struct thread *t)
-{
- while (t->frames)
- {
- struct frame *rm = t->frames;
- t->frames = rm->next;
- frame_free(rm);
- }
-
- free(t);
-}
-
-struct thread *thread_add_sibling(struct thread *a, struct thread *b)
-{
- struct thread *aa = a;
- while (aa->next)
- aa = aa->next;
-
- aa->next = b;
- return a;
-}
-
-static int thread_get_frame_count(struct thread *thread)
-{
- struct frame *f = thread->frames;
- int count = 0;
- while (f)
- {
- f = f->next;
- ++count;
- }
- return count;
-}
-
-static void thread_print_tree(struct thread *thread, bool verbose)
-{
- int framecount = thread_get_frame_count(thread);
- if (verbose)
- printf("Thread no. %d (%d frames)\n", thread->number, framecount);
- else
- printf("Thread\n");
- struct frame *frame = thread->frames;
- while (frame)
- {
- frame_print_tree(frame, verbose);
- frame = frame->next;
- }
-}
-
-/*
- * Checks whether the thread it contains some known "abort" function.
- * If a frame with the function is found, it is returned.
- * If there are multiple frames with abort function, the lowest
- * one is returned.
- * Nonrecursive.
- */
-static struct frame *thread_find_abort_frame(struct thread *thread)
-{
- struct frame *frame = thread->frames;
- struct frame *result = NULL;
- while (frame)
- {
- if (frame_is_abort_frame(frame))
- result = frame;
-
- frame = frame->next;
- }
-
- return result;
-}
-
-static void thread_remove_exit_handlers(struct thread *thread)
-{
- struct frame *frame = thread->frames;
- while (frame)
- {
- if (frame_is_exit_handler(frame))
- {
- /* Delete all frames from the beginning to this frame. */
- while (thread->frames != frame)
- {
- struct frame *rm = thread->frames;
- thread->frames = thread->frames->next;
- frame_free(rm);
- }
- return;
- }
-
- frame = frame->next;
- }
-}
-
-void thread_remove_noncrash_frames(struct thread *thread)
-{
- struct frame *prev = NULL;
- struct frame *cur = thread->frames;
- while (cur)
- {
- if (frame_is_noncrash_frame(cur))
- {
- /* This frame must be skipped, because it will
- be deleted. */
- if (prev)
- prev->next = cur->next;
- else
- thread->frames = cur->next;
-
- frame_free(cur);
-
- /* Set cur to be valid, as it will be used to
- advance to next item. */
- if (prev)
- cur = prev;
- else
- {
- cur = thread->frames;
- continue;
- }
- }
-
- prev = cur;
- cur = cur->next;
- }
-}
-
-struct backtrace *backtrace_new()
-{
- struct backtrace *bt = malloc(sizeof(struct backtrace));
- if (!bt)
- {
- puts("Error while allocating memory for backtrace.");
- exit(5);
- }
-
- bt->threads = NULL;
- bt->crash = NULL;
- return bt;
-}
-
-void backtrace_free(struct backtrace *bt)
-{
- while (bt->threads)
- {
- struct thread *rm = bt->threads;
- bt->threads = rm->next;
- thread_free(rm);
- }
-
- if (bt->crash)
- frame_free(bt->crash);
-
- free(bt);
-}
-
-static int backtrace_get_thread_count(struct backtrace *bt)
-{
- struct thread *t = bt->threads;
- int count = 0;
- while (t)
- {
- t = t->next;
- ++count;
- }
- return count;
-}
-
-void backtrace_print_tree(struct backtrace *backtrace, bool verbose)
-{
- if (verbose)
- printf("Thread count: %d\n", backtrace_get_thread_count(backtrace));
-
- if (backtrace->crash && verbose)
- {
- printf("Crash frame: ");
- frame_print_tree(backtrace->crash, verbose);
- }
-
- struct thread *thread = backtrace->threads;
- while (thread)
- {
- thread_print_tree(thread, verbose);
- thread = thread->next;
- }
-}
-
-void backtrace_remove_threads_except_one(struct backtrace *backtrace,
- struct thread *one)
-{
- while (backtrace->threads)
- {
- struct thread *rm = backtrace->threads;
- backtrace->threads = rm->next;
- if (rm != one)
- thread_free(rm);
- }
-
- one->next = NULL;
- backtrace->threads = one;
-}
-
-/*
- * Loop through all threads and if a single one contains the crash frame on the top,
- * return it. Otherwise, return NULL.
- *
- * If require_abort is true, it is also required that the thread containing
- * the crash frame contains some known "abort" function. In this case there can be
- * multiple threads with the crash frame on the top, but only one of them might
- * contain the abort function to succeed.
- */
-static struct thread *backtrace_find_crash_thread_from_crash_frame(struct backtrace *backtrace,
- bool require_abort)
-{
- /*
- * This code can be extended to compare something else when the function
- * name is not available.
- */
- if (!backtrace->threads || !backtrace->crash || !backtrace->crash->function)
- return NULL;
-
- struct thread *result = NULL;
- struct thread *thread = backtrace->threads;
- while (thread)
- {
- if (thread->frames
- && thread->frames->function
- && 0 == strcmp(thread->frames->function, backtrace->crash->function)
- && (!require_abort || thread_find_abort_frame(thread)))
- {
- if (result == NULL)
- result = thread;
- else
- {
- /* Second frame with the same function. Failure. */
- return NULL;
- }
- }
-
- thread = thread->next;
- }
-
- return result;
-}
-
-struct thread *backtrace_find_crash_thread(struct backtrace *backtrace)
-{
- /* If there is no thread, be silent and report NULL. */
- if (!backtrace->threads)
- return NULL;
-
- /* If there is just one thread, it is simple. */
- if (!backtrace->threads->next)
- return backtrace->threads;
-
- /* If we have a crash frame *and* there is just one thread which has
- * this frame on the top, it is also simple.
- */
- struct thread *thread;
- thread = backtrace_find_crash_thread_from_crash_frame(backtrace, false);
- if (thread)
- return thread;
-
- /* There are multiple threads with a frame indistinguishable from
- * the crash frame on the top of stack.
- * Try to search for known abort functions.
- */
- thread = backtrace_find_crash_thread_from_crash_frame(backtrace, true);
-
- return thread; /* result or null */
-}
-
-void backtrace_limit_frame_depth(struct backtrace *backtrace, int depth)
-{
- if (depth <= 0)
- return;
-
- struct thread *thread = backtrace->threads;
- while (thread)
- {
- struct frame *frame = thread_find_abort_frame(thread);
- if (frame)
- frame = frame->next; /* Start counting from the frame following the abort fr. */
- else
- frame = thread->frames; /* Start counting from the first frame. */
-
- /* Skip some frames to get the required stack depth. */
- int i = depth;
- struct frame *last_frame = NULL;
- while (frame && i)
- {
- last_frame = frame;
- frame = frame->next;
- --i;
- }
-
- /* Delete the remaining frames. */
- if (last_frame)
- last_frame->next = NULL;
-
- while (frame)
- {
- struct frame *rm = frame;
- frame = frame->next;
- frame_free(rm);
- }
-
- thread = thread->next;
- }
-}
-
-void backtrace_remove_exit_handlers(struct backtrace *backtrace)
-{
- struct thread *thread = backtrace->threads;
- while (thread)
- {
- thread_remove_exit_handlers(thread);
- thread = thread->next;
- }
-}
-
-void backtrace_remove_noncrash_frames(struct backtrace *backtrace)
-{
- struct thread *thread = backtrace->threads;
- while (thread)
- {
- thread_remove_noncrash_frames(thread);
- thread = thread->next;
- }
-}
-
diff --git a/src/Backtrace/fallback.c b/src/Backtrace/fallback.c
deleted file mode 100644
index 77a16283..00000000
--- a/src/Backtrace/fallback.c
+++ /dev/null
@@ -1,174 +0,0 @@
-/*
- Copyright (C) 2009 RedHat inc.
-
- This program 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; either version 2 of the License, or
- (at your option) any later version.
-
- 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; if not, write to the Free Software Foundation, Inc.,
- 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
-*/
-#include "fallback.h"
-#include <stdlib.h>
-#include <stdbool.h>
-#include <stdio.h>
-#include <string.h>
-#include <ctype.h>
-
-struct header
-{
- struct strbuf *text;
- struct header *next;
-};
-
-static struct header *header_new()
-{
- struct header *head = malloc(sizeof(struct header));
- if (!head)
- {
- puts("Error while allocating memory for backtrace header.");
- exit(5);
- }
- head->text = NULL;
- head->next = NULL;
- return head;
-}
-
-/* Recursively frees siblings. */
-static void header_free(struct header *head)
-{
- if (head->text)
- strbuf_free(head->text);
- if (head->next)
- header_free(head->next);
- free(head);
-}
-
-/* Inserts new header to array if it is not already there. */
-static void header_set_insert(struct header *cur, struct strbuf *new)
-{
- /* Duplicate found case. */
- if (strcmp(cur->text->buf, new->buf) == 0)
- return;
-
- /* Last item case, insert new header here. */
- if (cur->next == NULL)
- {
- cur->next = header_new();
- cur->next->text = new;
- return;
- }
-
- /* Move to next item in array case. */
- header_set_insert(cur->next, new);
-}
-
-struct strbuf *independent_backtrace(char *input)
-{
- struct strbuf *header = strbuf_new();
- bool in_bracket = false;
- bool in_quote = false;
- bool in_header = false;
- bool in_digit = false;
- bool has_at = false;
- bool has_filename = false;
- bool has_bracket = false;
- struct header *headers = NULL;
-
- const char *bk = input;
- while (*bk)
- {
- if (bk[0] == '#'
- && bk[1] >= '0' && bk[1] <= '7'
- && bk[2] == ' ' /* take only #0...#7 (8 last stack frames) */
- && !in_quote)
- {
- if (in_header && !has_filename)
- strbuf_clear(header);
- in_header = true;
- }
-
- if (!in_header)
- {
- ++bk;
- continue;
- }
-
- if (isdigit(*bk) && !in_quote && !has_at)
- in_digit = true;
- else if (bk[0] == '\\' && bk[1] == '\"')
- bk++;
- else if (*bk == '\"')
- in_quote = in_quote == true ? false : true;
- else if (*bk == '(' && !in_quote)
- {
- in_bracket = true;
- in_digit = false;
- strbuf_append_char(header, '(');
- }
- else if (*bk == ')' && !in_quote)
- {
- in_bracket = false;
- has_bracket = true;
- in_digit = false;
- strbuf_append_char(header, '(');
- }
- else if (*bk == '\n' && has_filename)
- {
- if (headers == NULL)
- {
- headers = header_new();
- headers->text = header;
- }
- else
- header_set_insert(headers, header);
-
- header = strbuf_new();
- in_bracket = false;
- in_quote = false;
- in_header = false;
- in_digit = false;
- has_at = false;
- has_filename = false;
- has_bracket = false;
- }
- else if (*bk == ',' && !in_quote)
- in_digit = false;
- else if (isspace(*bk) && !in_quote)
- in_digit = false;
- else if (bk[0] == 'a' && bk[1] == 't' && has_bracket && !in_quote)
- {
- has_at = true;
- strbuf_append_char(header, 'a');
- }
- else if (bk[0] == ':' && has_at && isdigit(bk[1]) && !in_quote)
- has_filename = true;
- else if (in_header && !in_digit && !in_quote && !in_bracket)
- strbuf_append_char(header, *bk);
-
- bk++;
- }
-
- strbuf_free(header);
-
- struct strbuf *result = strbuf_new();
- struct header *loop = headers;
- while (loop)
- {
- strbuf_append_str(result, loop->text->buf);
- strbuf_append_char(result, '\n');
- loop = loop->next;
- }
-
- if (headers)
- header_free(headers); /* recursive */
-
- return result;
-}
diff --git a/src/Backtrace/fallback.h b/src/Backtrace/fallback.h
deleted file mode 100644
index 85b06320..00000000
--- a/src/Backtrace/fallback.h
+++ /dev/null
@@ -1,32 +0,0 @@
-/*
- Copyright (C) 2009 RedHat inc.
-
- This program 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; either version 2 of the License, or
- (at your option) any later version.
-
- 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; if not, write to the Free Software Foundation, Inc.,
- 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
-*/
-#ifndef FALLBACK_H
-#define FALLBACK_H
-
-#include "strbuf.h"
-#include <stdio.h>
-
-/*
- * Reads the input file and calculates independent backtrace from it.
- * @returns
- * The independent backtrace. Caller is responsible for calling
- * strbuf_free() on it.
- */
-extern struct strbuf *independent_backtrace(char *input);
-
-#endif
diff --git a/src/Backtrace/main.c b/src/Backtrace/main.c
deleted file mode 100644
index cddf8979..00000000
--- a/src/Backtrace/main.c
+++ /dev/null
@@ -1,361 +0,0 @@
-/* -*-mode:c++;c-file-style:"bsd";c-basic-offset:2;indent-tabs-mode:nil-*-
- main.cpp - parses command line arguments
-
- Copyright (C) 2009 RedHat inc.
-
- This program 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; either version 2 of the License, or
- (at your option) any later version.
-
- 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; if not, write to the Free Software Foundation, Inc.,
- 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
-*/
-#include <argp.h>
-#include <stdlib.h>
-#include <sysexits.h>
-#include <string.h>
-#include "config.h"
-#include "backtrace.h"
-#include "fallback.h"
-
-/* Too large files are trimmed. */
-#define FILE_SIZE_LIMIT 20000000 /* ~ 20 MB */
-
-#define EX_PARSINGFAILED EX__MAX + 1 /* = 79 */
-#define EX_THREADDETECTIONFAILED EX__MAX + 2 /* = 80 */
-
-const char *argp_program_version = "abrt-backtrace " VERSION;
-const char *argp_program_bug_address = "<crash-catcher@lists.fedorahosted.org>";
-
-static char doc[] = "abrt-backtrace -- backtrace analyzer";
-
-/* A description of the arguments we accept. */
-static char args_doc[] = "FILE";
-
-static struct argp_option options[] = {
- {"independent" , 'i', 0 , 0, "Prints independent backtrace (fallback)"},
- {"single-thread" , 'n', 0 , 0, "Display the crash thread only in the backtrace"},
- {"frame-depth" , 'd', "N", 0, "Display only top N frames under the crash frame"},
- {"remove-exit-handlers" , 'r', 0 , 0, "Removes exit handler frames from the displayed backtrace"},
- {"remove-noncrash-frames", 'm', 0 , 0, "Removes common frames known as not causing crash"},
- {"debug-parser" , 'p', 0 , 0, "Prints parser debug information"},
- {"debug-scanner" , 's', 0 , 0, "Prints scanner debug information"},
- {"verbose" , 'v', 0 , 0, "Print human-friendly superfluous output."},
- { 0 }
-};
-
-struct arguments
-{
- bool independent;
- bool single_thread;
- int frame_depth; /* negative == do not limit the depth */
- bool remove_exit_handlers;
- bool remove_noncrash_frames;
- bool debug_parser;
- bool debug_scanner;
- bool verbose;
- char *filename;
-};
-
-static error_t
-parse_opt (int key, char *arg, struct argp_state *state)
-{
- /* Get the input argument from argp_parse, which we
- know is a pointer to our arguments structure. */
- struct arguments *arguments = (struct arguments*)state->input;
-
- switch (key)
- {
- case 'i': arguments->independent = true; break;
- case 'n': arguments->single_thread = true; break;
- case 'd':
- if (1 != sscanf(arg, "%d", &arguments->frame_depth))
- {
- /* Must be a number. */
- argp_usage(state);
- exit(EX_USAGE); /* Invalid argument */
- }
- break;
- case 'r': arguments->remove_exit_handlers = true; break;
- case 'm': arguments->remove_noncrash_frames = true; break;
- case 'p': arguments->debug_parser = true; break;
- case 's': arguments->debug_scanner = true; break;
- case 'v': arguments->verbose = true; break;
-
- case ARGP_KEY_ARG:
- if (arguments->filename)
- {
- /* Too many arguments. */
- argp_usage(state);
- exit(EX_USAGE); /* Invalid argument */
- }
- arguments->filename = arg;
- break;
-
- case ARGP_KEY_END:
- break;
-
- default:
- return ARGP_ERR_UNKNOWN;
- }
- return 0;
-}
-
-/* Our argp parser. */
-static struct argp argp = { options, parse_opt, args_doc, doc };
-
-#define PYTHON_BACKTRACE_ID1 "\n\nTraceback (most recent call last):\n"
-#define PYTHON_BACKTRACE_ID2 "\n\nLocal variables in innermost frame:\n"
-
-int main(int argc, char **argv)
-{
- /* Set options default values and parse program command line. */
- struct arguments arguments;
- arguments.independent = false;
- arguments.frame_depth = -1;
- arguments.single_thread = false;
- arguments.remove_exit_handlers = false;
- arguments.remove_noncrash_frames = false;
- arguments.debug_parser = false;
- arguments.debug_scanner = false;
- arguments.verbose = false;
- arguments.filename = 0;
- argp_parse(&argp, argc, argv, 0, 0, &arguments);
-
- char *bttext = NULL;
-
- /* If a filename was provided, read input from file.
- Otherwise read from stdin. */
- if (arguments.filename)
- {
- /* Open input file, and parse it. */
- FILE *fp = fopen(arguments.filename, "r");
- if (!fp)
- {
- fprintf(stderr, "Unable to open '%s'.\n", arguments.filename);
- exit(EX_NOINPUT); /* No such file or directory */
- }
-
- /* Header and footer of the backtrace is stripped to simplify the parser.
- * A drawback is that the backtrace must be loaded to memory.
- */
- fseek(fp, 0, SEEK_END);
- size_t size = ftell(fp);
- fseek(fp, 0, SEEK_SET);
-
- if (size > FILE_SIZE_LIMIT)
- {
- fprintf(stderr, "Input file too big (%zd). Maximum size is %d.\n",
- size, FILE_SIZE_LIMIT);
- exit(EX_IOERR);
- }
-
- /* Handle the case that the input file is empty.
- * The code is not designed to support completely empty backtrace.
- * Silently exit indicating success.
- */
- if (size == 0)
- {
- fclose(fp);
- exit(0);
- }
-
- bttext = malloc(size + 1);
- if (!bttext)
- {
- fclose(fp);
- fputs("malloc failed", stderr);
- exit(EX_OSERR);
- }
-
- if (1 != fread(bttext, size, 1, fp))
- {
- fclose(fp);
- fprintf(stderr, "Unable to read from '%s'.\n", arguments.filename);
- exit(EX_IOERR); /* IO Error */
- }
-
- bttext[size] = '\0';
- fclose(fp);
- }
- else
- {
- struct strbuf *btin = strbuf_new();
- int c;
- while ((c = getchar()) != EOF && c != '\0')
- strbuf_append_char(btin, (char)c);
-
- strbuf_append_char(btin, '\0');
- bttext = btin->buf;
- strbuf_free_nobuf(btin); /* free btin, but not its internal buffer */
- }
-
- /* Detect Python backtraces. If it is a Python backtrace,
- * silently exit for now.
- */
- if (strstr(bttext, PYTHON_BACKTRACE_ID1) != NULL
- && strstr(bttext, PYTHON_BACKTRACE_ID2) != NULL)
- {
- exit(0);
- }
-
- /* Print independent backtrace and exit. */
- if (arguments.independent)
- {
- struct strbuf *ibt = independent_backtrace(bttext);
- puts(ibt->buf);
- strbuf_free(ibt);
- free(bttext);
- return 0; /* OK */
- }
-
- /* Skip the backtrace header information. */
- char *btnoheader_a = strstr(bttext, "\nThread ");
- char *btnoheader_b = strstr(bttext, "\n#");
- char *btnoheader = bttext;
- if (btnoheader_a)
- {
- if (btnoheader_b && btnoheader_b < btnoheader_a)
- btnoheader = btnoheader_b + 1;
- else
- btnoheader = btnoheader_a + 1;
- }
- else if (btnoheader_b)
- btnoheader = btnoheader_b + 1;
-
- /* Bug fixing hack for broken backtraces.
- * Sometimes the empty line is missing before new Thread section.
- * This is against rules, but a bug (now fixed) in Linux kernel caused
- * this.
- */
- char *thread_fixer = btnoheader + 1;
- while ((thread_fixer = strstr(thread_fixer, "\nThread")) != NULL)
- {
- if (thread_fixer[-1] != '\n')
- thread_fixer[-1] = '\n';
-
- ++thread_fixer;
- }
-
- /* Bug fixing hack for GDB - remove wrongly placed newlines from the backtrace.
- * Sometimes there is a newline in the local variable section.
- * This is caused by some GDB hooks.
- * Example: rhbz#538440
- * #1 0x0000000000420939 in sync_deletions (mse=0x0, mfld=0x1b85020)
- * at mail-stub-exchange.c:1119
- * status = <value optimized out>
- * iter = 0x1af38d0
- * known_messages = 0x1b5c460Traceback (most recent call last):
- * File "/usr/share/glib-2.0/gdb/glib.py", line 98, in next
- * if long (node["key_hash"]) >= 2:
- * RuntimeError: Cannot access memory at address 0x11
- *
- * __PRETTY_FUNCTION__ = "sync_deletions"
- * #2 0x0000000000423e6b in refresh_folder (stub=0x1b77f10 [MailStubExchange],
- * ...
- *
- * The code removes every empty line (also those containing only spaces),
- * which is not followed by a new Thread section.
- *
- * rhbz#555251 contains empty lines with spaces
- */
- char *empty_line = btnoheader;
- char *c = btnoheader;
- while (*c)
- {
- if (*c == '\n')
- {
- char *cend = c + 1;
- while (*cend == ' ' || *cend == '\t')
- ++cend;
- if (*cend == '\n' && 0 != strncmp(cend, "\nThread", strlen("\nThread")))
- memmove(c, cend, strlen(cend) + 1);
- }
- ++c;
- }
- while ((empty_line = strstr(empty_line, "\n\n")) != NULL)
- {
- if (0 != strncmp(empty_line, "\n\nThread", strlen("\n\nThread")))
- {
- /* Remove the empty line by converting the first newline to char. */
- empty_line[0] = 'X';
- }
- ++empty_line;
- }
-
- /* Cut the backtrace footer.
- * Footer: lines not starting with # or "Thread", and separated from
- * the backtrace body by a newline.
- */
- /* It is not necessary for now, because of the bug fixing hack for GDB.
- int i;
- for (i = size - 1; i > 0; --i)
- {
- if (bttext[i] != '\n')
- continue;
- if (strncmp(bttext + i + 1, "Thread", strlen("Thread")) == 0)
- break;
- if (bttext[i + 1] == '#')
- break;
- if (bttext[i - 1] == '\n')
- {
- bttext[i] = '\0';
- break;
- }
- }*/
-
- /* Try to parse the backtrace. */
- struct backtrace *backtrace;
- backtrace = do_parse(btnoheader, arguments.debug_parser, arguments.debug_scanner);
-
- /* If the parser failed print independent backtrace. */
- if (!backtrace)
- {
- struct strbuf *ibt = independent_backtrace(bttext);
- puts(ibt->buf);
- strbuf_free(ibt);
- free(bttext);
- /* Parsing failed, but the output can be used. */
- return EX_PARSINGFAILED;
- }
-
- free(bttext);
-
- /* If a single thread is requested, remove all other threads. */
- int retval = 0;
- struct thread *crash_thread = NULL;
- if (arguments.single_thread)
- {
- crash_thread = backtrace_find_crash_thread(backtrace);
- if (crash_thread)
- backtrace_remove_threads_except_one(backtrace, crash_thread);
- else
- {
- fprintf(stderr, "Detection of crash thread failed.\n");
- /* THREAD DETECTION FAILED, BUT THE OUTPUT CAN BE USED */
- retval = EX_THREADDETECTIONFAILED;
- }
- }
-
- if (arguments.remove_noncrash_frames)
- backtrace_remove_noncrash_frames(backtrace);
-
- /* If a frame removal is requested, do it now. */
- if (arguments.frame_depth > 0)
- backtrace_limit_frame_depth(backtrace, arguments.frame_depth);
-
- /* Frame removal can be done before removing exit handlers */
- if (arguments.remove_exit_handlers > 0)
- backtrace_remove_exit_handlers(backtrace);
-
- backtrace_print_tree(backtrace, arguments.verbose);
- backtrace_free(backtrace);
- return retval;
-}
diff --git a/src/Backtrace/strbuf.c b/src/Backtrace/strbuf.c
deleted file mode 100644
index 4c6d886b..00000000
--- a/src/Backtrace/strbuf.c
+++ /dev/null
@@ -1,112 +0,0 @@
-/*
- strbuf.c - string buffer implementation
-
- Copyright (C) 2009 RedHat inc.
-
- This program 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; either version 2 of the License, or
- (at your option) any later version.
-
- 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; if not, write to the Free Software Foundation, Inc.,
- 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
-*/
-#include "strbuf.h"
-#include <stdlib.h>
-#include <stdio.h>
-#include <assert.h>
-#include <string.h>
-
-struct strbuf *strbuf_new()
-{
- struct strbuf *buf = malloc(sizeof(struct strbuf));
- if (!buf)
- {
- puts("Error while allocating memory for string buffer.");
- exit(5);
- }
-
- buf->alloc = 8;
- buf->len = 0;
- buf->buf = malloc(buf->alloc);
- if (!buf->buf)
- {
- puts("Error while allocating memory for string buffer.");
- exit(5);
- }
-
- buf->buf[buf->len] = '\0';
- return buf;
-}
-
-void strbuf_free(struct strbuf *strbuf)
-{
- free(strbuf->buf);
- free(strbuf);
-}
-
-void strbuf_free_nobuf(struct strbuf *strbuf)
-{
- free(strbuf);
-}
-
-
-void strbuf_clear(struct strbuf *strbuf)
-{
- assert(strbuf->alloc > 0);
- strbuf->len = 0;
- strbuf->buf[0] = '\0';
-}
-
-/* Ensures that the buffer can be extended by num characters
- * without touching malloc/realloc.
- */
-static void strbuf_grow(struct strbuf *strbuf, int num)
-{
- if (strbuf->len + num + 1 > strbuf->alloc)
- {
- while (strbuf->len + num + 1 > strbuf->alloc)
- strbuf->alloc *= 2; /* huge grow = infinite loop */
-
- strbuf->buf = realloc(strbuf->buf, strbuf->alloc);
- if (!strbuf->buf)
- {
- puts("Error while allocating memory for string buffer.");
- exit(5);
- }
- }
-}
-
-struct strbuf *strbuf_append_char(struct strbuf *strbuf, char c)
-{
- strbuf_grow(strbuf, 1);
- strbuf->buf[strbuf->len++] = c;
- strbuf->buf[strbuf->len] = '\0';
- return strbuf;
-}
-
-struct strbuf *strbuf_append_str(struct strbuf *strbuf, char *str)
-{
- int len = strlen(str);
- strbuf_grow(strbuf, len);
- assert(strbuf->len + len < strbuf->alloc);
- strcpy(strbuf->buf + strbuf->len, str);
- strbuf->len += len;
- return strbuf;
-}
-
-struct strbuf *strbuf_prepend_str(struct strbuf *strbuf, char *str)
-{
- int len = strlen(str);
- strbuf_grow(strbuf, len);
- assert(strbuf->len + len < strbuf->alloc);
- memmove(strbuf->buf + len, strbuf->buf, strbuf->len + 1);
- memcpy(strbuf->buf, str, len);
- return strbuf;
-}
diff --git a/src/CLI/dbus.h b/src/CLI/dbus.h
index 4aaa4e96..6461ccf5 100644
--- a/src/CLI/dbus.h
+++ b/src/CLI/dbus.h
@@ -57,7 +57,7 @@ map_map_string_t call_GetPluginsInfo();
*/
map_plugin_settings_t call_GetPluginSettings(const char *name);
-/** Gets global daemon settings.
+/** Gets global daemon settings.
* @todo
* Return more semantically structured output - maybe a struct instead of a map.
*/
diff --git a/src/Makefile.am b/src/Makefile.am
index dbd7ebbc..42266b31 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -1 +1 @@
-SUBDIRS = Hooks Daemon Applet Gui CLI Backtrace
+SUBDIRS = Hooks Daemon Applet Gui CLI utils
diff --git a/src/utils/Makefile.am b/src/utils/Makefile.am
new file mode 100644
index 00000000..0cecf26a
--- /dev/null
+++ b/src/utils/Makefile.am
@@ -0,0 +1,11 @@
+bin_PROGRAMS = abrt-backtrace
+abrt_backtrace_CFLAGS = -Wall
+abrt_backtrace_SOURCES = abrt-backtrace.c
+abrt_backtrace_CPPFLAGS = \
+ -I$(srcdir)/../../inc \
+ -I$(srcdir)/../../lib/Utils
+abrt_backtrace_LDADD = \
+ ../../lib/Utils/libABRTUtils.la
+
+man_MANS = abrt-backtrace.1
+EXTRA_DIST = $(man_MANS)
diff --git a/src/Backtrace/abrt-backtrace.1 b/src/utils/abrt-backtrace.1
index ff7c2be2..ff7c2be2 100644
--- a/src/Backtrace/abrt-backtrace.1
+++ b/src/utils/abrt-backtrace.1
diff --git a/src/utils/abrt-backtrace.c b/src/utils/abrt-backtrace.c
new file mode 100644
index 00000000..e568ed1c
--- /dev/null
+++ b/src/utils/abrt-backtrace.c
@@ -0,0 +1,318 @@
+/* -*-mode:c++;c-file-style:"bsd";c-basic-offset:4;indent-tabs-mode:nil-*-
+ main.cpp - parses command line arguments
+
+ Copyright (C) 2009 RedHat inc.
+
+ This program 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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; if not, write to the Free Software Foundation, Inc.,
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+*/
+#include <argp.h>
+#include <stdlib.h>
+#include <sysexits.h>
+#include <string.h>
+#include "config.h"
+#include "backtrace.h"
+#include "strbuf.h"
+
+/* Too large files are trimmed. */
+#define FILE_SIZE_LIMIT 20000000 /* ~ 20 MB */
+
+#define EX_PARSINGFAILED EX__MAX + 1 /* = 79 */
+#define EX_THREADDETECTIONFAILED EX__MAX + 2 /* = 80 */
+
+const char *argp_program_version = "abrt-backtrace " VERSION;
+const char *argp_program_bug_address = "<crash-catcher@lists.fedorahosted.org>";
+
+static char doc[] = "abrt-backtrace -- backtrace analyzer";
+
+/* A description of the arguments we accept. */
+static char args_doc[] = "FILE";
+
+static struct argp_option options[] = {
+ {"independent" , 'i', 0 , 0, "Prints independent backtrace (fallback)"},
+ {"single-thread" , 'n', 0 , 0, "Display the crash thread only in the backtrace"},
+ {"frame-depth" , 'd', "N", 0, "Display only top N frames under the crash frame"},
+ {"remove-exit-handlers" , 'r', 0 , 0, "Removes exit handler frames from the displayed backtrace"},
+ {"remove-noncrash-frames", 'm', 0 , 0, "Removes common frames known as not causing crash"},
+ {"rate" , 'a', 0 , 0, "Prints the backtrace rating from 0 to 4"},
+ {"debug-parser" , 'p', 0 , 0, "Prints parser debug information"},
+ {"debug-scanner" , 's', 0 , 0, "Prints scanner debug information"},
+ {"verbose" , 'v', 0 , 0, "Print human-friendly superfluous output."},
+ { 0 }
+};
+
+struct arguments
+{
+ bool independent;
+ bool single_thread;
+ int frame_depth; /* negative == do not limit the depth */
+ bool remove_exit_handlers;
+ bool remove_noncrash_frames;
+ bool debug_parser;
+ bool debug_scanner;
+ bool verbose;
+ bool rate;
+ char *filename;
+};
+
+static error_t
+parse_opt (int key, char *arg, struct argp_state *state)
+{
+ /* Get the input argument from argp_parse, which we
+ know is a pointer to our arguments structure. */
+ struct arguments *arguments = (struct arguments*)state->input;
+
+ switch (key)
+ {
+ case 'i': arguments->independent = true; break;
+ case 'n': arguments->single_thread = true; break;
+ case 'd':
+ if (1 != sscanf(arg, "%d", &arguments->frame_depth))
+ {
+ /* Must be a number. */
+ argp_usage(state);
+ exit(EX_USAGE); /* Invalid argument */
+ }
+ break;
+ case 'r': arguments->remove_exit_handlers = true; break;
+ case 'm': arguments->remove_noncrash_frames = true; break;
+ case 'p': arguments->debug_parser = true; break;
+ case 's': arguments->debug_scanner = true; break;
+ case 'v': arguments->verbose = true; break;
+ case 'a': arguments->rate = true; break;
+
+ case ARGP_KEY_ARG:
+ if (arguments->filename)
+ {
+ /* Too many arguments. */
+ argp_usage(state);
+ exit(EX_USAGE); /* Invalid argument */
+ }
+ arguments->filename = arg;
+ break;
+
+ case ARGP_KEY_END:
+ break;
+
+ default:
+ return ARGP_ERR_UNKNOWN;
+ }
+ return 0;
+}
+
+/* Our argp parser. */
+static struct argp argp = { options, parse_opt, args_doc, doc };
+
+#define PYTHON_BACKTRACE_ID1 "\n\nTraceback (most recent call last):\n"
+#define PYTHON_BACKTRACE_ID2 "\n\nLocal variables in innermost frame:\n"
+
+int main(int argc, char **argv)
+{
+ /* Set options default values and parse program command line. */
+ struct arguments arguments;
+ arguments.independent = false;
+ arguments.frame_depth = -1;
+ arguments.single_thread = false;
+ arguments.remove_exit_handlers = false;
+ arguments.remove_noncrash_frames = false;
+ arguments.debug_parser = false;
+ arguments.debug_scanner = false;
+ arguments.verbose = false;
+ arguments.filename = 0;
+ arguments.rate = false;
+ argp_parse(&argp, argc, argv, 0, 0, &arguments);
+
+ /* If we are about to rate a backtrace, the other values must be set accordingly,
+ no matter what the user set on the command line. */
+ if (arguments.rate)
+ {
+ arguments.independent = false;
+ arguments.frame_depth = 5;
+ arguments.single_thread = true;
+ arguments.remove_exit_handlers = true;
+ arguments.remove_noncrash_frames = true;
+ }
+
+ char *bttext = NULL;
+
+ /* If a filename was provided, read input from file.
+ Otherwise read from stdin. */
+ if (arguments.filename)
+ {
+ /* Open input file, and parse it. */
+ FILE *fp = fopen(arguments.filename, "r");
+ if (!fp)
+ {
+ fprintf(stderr, "Unable to open '%s'.\n", arguments.filename);
+ exit(EX_NOINPUT); /* No such file or directory */
+ }
+
+ /* Header and footer of the backtrace is stripped to simplify the parser.
+ * A drawback is that the backtrace must be loaded to memory.
+ */
+ fseek(fp, 0, SEEK_END);
+ size_t size = ftell(fp);
+ fseek(fp, 0, SEEK_SET);
+
+ if (size > FILE_SIZE_LIMIT)
+ {
+ fprintf(stderr, "Input file too big (%zd). Maximum size is %d.\n",
+ size, FILE_SIZE_LIMIT);
+ exit(EX_IOERR);
+ }
+
+ /* Handle the case that the input file is empty.
+ * The code is not designed to support completely empty backtrace.
+ * Silently exit indicating success.
+ */
+ if (size == 0)
+ {
+ fclose(fp);
+ exit(0);
+ }
+
+ bttext = malloc(size + 1);
+ if (!bttext)
+ {
+ fclose(fp);
+ fputs("malloc failed", stderr);
+ exit(EX_OSERR);
+ }
+
+ if (1 != fread(bttext, size, 1, fp))
+ {
+ fclose(fp);
+ fprintf(stderr, "Unable to read from '%s'.\n", arguments.filename);
+ exit(EX_IOERR); /* IO Error */
+ }
+
+ bttext[size] = '\0';
+ fclose(fp);
+ }
+ else
+ {
+ struct strbuf *btin = strbuf_new();
+ int c;
+ while ((c = getchar()) != EOF && c != '\0')
+ strbuf_append_char(btin, (char)c);
+
+ strbuf_append_char(btin, '\0');
+ bttext = btin->buf;
+ strbuf_free_nobuf(btin); /* free btin, but not its internal buffer */
+ }
+
+ /* Detect Python backtraces. If it is a Python backtrace,
+ * silently exit for now.
+ */
+ if (strstr(bttext, PYTHON_BACKTRACE_ID1) != NULL
+ && strstr(bttext, PYTHON_BACKTRACE_ID2) != NULL)
+ {
+ if (arguments.rate)
+ puts("4");
+ exit(0);
+ }
+
+ /* Print independent backtrace and exit. */
+ if (arguments.independent)
+ {
+ struct strbuf *ibt = independent_backtrace(bttext);
+ puts(ibt->buf);
+ strbuf_free(ibt);
+ free(bttext);
+ return 0; /* OK */
+ }
+
+ /* Try to parse the backtrace. */
+ struct backtrace *backtrace;
+ backtrace = backtrace_parse(bttext, arguments.debug_parser, arguments.debug_scanner);
+
+ /* If the parser failed print independent backtrace. */
+ if (!backtrace)
+ {
+ if (arguments.rate)
+ {
+ free(bttext);
+ puts("0");
+ /* Parsing failed, but the output can be used. */
+ return EX_PARSINGFAILED;
+ }
+ struct strbuf *ibt = independent_backtrace(bttext);
+ puts(ibt->buf);
+ strbuf_free(ibt);
+ free(bttext);
+ /* Parsing failed, but the output can be used. */
+ return EX_PARSINGFAILED;
+ }
+
+ free(bttext);
+
+ /* [--rate] Get the quality of the full backtrace. */
+ float q1 = backtrace_quality(backtrace);
+
+ /* If a single thread is requested, remove all other threads. */
+ int retval = 0;
+ struct thread *crash_thread = NULL;
+ if (arguments.single_thread)
+ {
+ crash_thread = backtrace_find_crash_thread(backtrace);
+ if (crash_thread)
+ backtrace_remove_threads_except_one(backtrace, crash_thread);
+ else
+ {
+ fprintf(stderr, "Detection of crash thread failed.\n");
+ /* THREAD DETECTION FAILED, BUT THE OUTPUT CAN BE USED */
+ retval = EX_THREADDETECTIONFAILED;
+ }
+ }
+
+ /* [--rate] Get the quality of the crash thread. */
+ float q2 = backtrace_quality(backtrace);
+
+ if (arguments.remove_noncrash_frames)
+ backtrace_remove_noncrash_frames(backtrace);
+
+ /* If a frame removal is requested, do it now. */
+ if (arguments.frame_depth > 0)
+ backtrace_limit_frame_depth(backtrace, arguments.frame_depth);
+
+ /* Frame removal can be done before removing exit handlers */
+ if (arguments.remove_exit_handlers > 0)
+ backtrace_remove_exit_handlers(backtrace);
+
+ /* [--rate] Get the quality of frames around the crash. */
+ float q3 = backtrace_quality(backtrace);
+
+ if (arguments.rate)
+ {
+ /* Compute and store backtrace rating. */
+ /* Compute and store backtrace rating. The crash frame
+ is more important that the others. The frames around
+ the crash are more important than the rest. */
+ float qtot = 0.25f * q1 + 0.35f * q2 + 0.4f * q3;
+
+ /* Turn the quality to rating. */
+ const char *rating;
+ if (qtot < 0.6f) rating = "0";
+ else if (qtot < 0.7f) rating = "1";
+ else if (qtot < 0.8f) rating = "2";
+ else if (qtot < 0.9f) rating = "3";
+ else rating = "4";
+ puts(rating);
+ }
+ else
+ backtrace_print_tree(backtrace, arguments.verbose);
+
+ backtrace_free(backtrace);
+ return retval;
+}