diff options
author | Jiri Moskovcak <jmoskovc@redhat.com> | 2010-02-07 16:03:50 +0100 |
---|---|---|
committer | Jiri Moskovcak <jmoskovc@redhat.com> | 2010-02-07 16:03:50 +0100 |
commit | 34fc227339ccd23b30973ea4684fcb6fb408635e (patch) | |
tree | c47ba01ecdc6e0e2ab5fc38c5eb07c4d6deeeedd | |
parent | 985908c6b086b83381eccd95a0a6508c8bf1d731 (diff) | |
parent | 1e76e071620e1f9bf110dacf3cf8caffccef324b (diff) | |
download | abrt-34fc227339ccd23b30973ea4684fcb6fb408635e.tar.gz abrt-34fc227339ccd23b30973ea4684fcb6fb408635e.tar.xz abrt-34fc227339ccd23b30973ea4684fcb6fb408635e.zip |
Merge branch 'master' of ssh://git.fedorahosted.org/git/abrt
-rw-r--r-- | lib/Plugins/CCpp.cpp | 34 | ||||
-rw-r--r-- | lib/Plugins/FileTransfer.conf | 2 | ||||
-rw-r--r-- | lib/Plugins/Firefox.cpp | 1 | ||||
-rwxr-xr-x | scripts/abrt-bz-stats | 35 | ||||
-rwxr-xr-x | src/Backtrace/abrt-bz-dupchecker | 7 | ||||
-rwxr-xr-x | src/Backtrace/abrt-bz-hashchecker | 2 | ||||
-rw-r--r-- | src/CLI/dbus.cpp | 28 | ||||
-rw-r--r-- | src/CLI/dbus.h | 14 | ||||
-rw-r--r-- | src/CLI/report.cpp | 55 | ||||
-rw-r--r-- | src/Daemon/Daemon.cpp | 58 | ||||
-rw-r--r-- | src/Daemon/MiddleWare.cpp | 22 | ||||
-rw-r--r-- | src/Daemon/MiddleWare.h | 9 |
12 files changed, 207 insertions, 60 deletions
diff --git a/lib/Plugins/CCpp.cpp b/lib/Plugins/CCpp.cpp index 14e1ee79..50423b7c 100644 --- a/lib/Plugins/CCpp.cpp +++ b/lib/Plugins/CCpp.cpp @@ -19,7 +19,6 @@ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ -#include <sys/wait.h> #include <fstream> #include <sstream> #include <set> @@ -198,10 +197,11 @@ static LineRating rate_line(const char *line) /* returns number of "stars" to show */ static int rate_backtrace(const char *backtrace) { - int i, j, len; + 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 @@ -212,15 +212,31 @@ static int rate_backtrace(const char *backtrace) len = 0; for (i = strlen(backtrace) - 1; i >= 0; i--) { - if (backtrace[i] == '#') /* this separates frames from each other */ - { - string s(backtrace + i + 1, len); - for (j=0; j<len; j++) /* replace tabs with spaces */ - if (s[j] == '\t') - s[j] = ' '; + 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 += rate_line(s.c_str()) * 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 diff --git a/lib/Plugins/FileTransfer.conf b/lib/Plugins/FileTransfer.conf index 5e231403..30769b18 100644 --- a/lib/Plugins/FileTransfer.conf +++ b/lib/Plugins/FileTransfer.conf @@ -15,7 +15,7 @@ Enabled = yes # At the time of the crash, the plugin stores a record of it # in its internal list. When specified time is reached, # the plugin iterates through its internal list and sends -# every recorded crash to the specified URL. +# every recorded crash to the specified URL. # After that, the internal list is cleared. diff --git a/lib/Plugins/Firefox.cpp b/lib/Plugins/Firefox.cpp index 98d892b7..7bee46dc 100644 --- a/lib/Plugins/Firefox.cpp +++ b/lib/Plugins/Firefox.cpp @@ -19,7 +19,6 @@ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ -#include <sys/wait.h> #include <fstream> #include <sstream> #include <set> diff --git a/scripts/abrt-bz-stats b/scripts/abrt-bz-stats index 5e253912..d84fd55e 100755 --- a/scripts/abrt-bz-stats +++ b/scripts/abrt-bz-stats @@ -29,6 +29,8 @@ parser.add_option("-w", "--weekly", help="Generate weekly report instead of mont # HTML output for blogs etc. 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", + action="store_true", default=False, dest="wiki") # Newest stats first parser.add_option("-r", "--reversed", help="Display the newest stats first", action="store_true", default=False, dest="reversed") @@ -139,7 +141,8 @@ class TimeSpan: return int(100 * self.closed_as_waste / self.closed()) def closed_as_other_percentage(self): - return int(100 * self.closed_as_other / self.closed()) + return 100 - self.closed_as_useful_percentage() \ + - self.closed_as_waste_percentage() def closed(self): return self.closed_as_useful + self.closed_as_waste + self.closed_as_other @@ -151,13 +154,16 @@ class TimeSpan: self.components[component] = 1 def add_resolution(self, resolution): - if resolution in ["CLOSED_NOTABUG", "CLOSED_WONTFIX", "CLOSED_DEFERRED", "CLOSED_WORKSFORME"]: - self.closed_as_other += 1 - elif resolution in ["CLOSED_CURRENTRELEASE", "CLOSED_RAWHIDE", "CLOSED_ERRATA", \ - "CLOSED_UPSTREAM", "CLOSED_NEXTRELEASE"]: + # Catches only resolutions starting with "CLOSED_" + 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", "CLOSED_INSUFFICIENT_DATA"]: + elif resolution in ["CLOSED_DUPLICATE", "CLOSED_CANTFIX", + "CLOSED_INSUFFICIENT_DATA"]: self.closed_as_waste += 1 + elif resolution in ["CLOSED_NOTABUG", "CLOSED_WONTFIX", + "CLOSED_DEFERRED", "CLOSED_WORKSFORME"]: + self.closed_as_other += 1 def __str__(self): def bug(count): @@ -195,6 +201,19 @@ class TimeSpan: top_crasher_item = " <li>%s: %s</li>\n" top_crashers_end = "</ol></li>\n" end = "</ul>\n" + elif options.wiki: + start = "" + bugs_reported = "* %s reported\n" + bugs_closed = "* %s closed\n" + bugs_cl_useful = "** %s (%d%%) as fixed, so ABRT was useful\n" + bugs_cl_notuseful = "** %s (%d%%) as duplicate, can't fix, insuf. data, so ABRT was not useful\n" + bugs_cl_other = "** %s (%d%%) as notabug, wontfix, worksforme\n" + bugs_closed_end = "" + top_crashers = "* top crashers:\n" + top_crasher_item = "*# %s: %s\n" + top_crashers_end = "" + end = "" + str = start str += bugs_reported % bug(self.bugs_reported()) @@ -273,6 +292,8 @@ if not options.weekly: m = monthly_stats[month] if options.html: print "<h2>Month %s</h2>" % month + elif options.wiki: + print "==Month %s==" % month else: print "MONTH %s" % month print m @@ -285,6 +306,8 @@ else: w = weekly_stats[week] if options.html: print "<h2>Week %s</h2>" % week + elif options.wiki: + print "==Week %s==" % week else: print "WEEK %s" % week print w diff --git a/src/Backtrace/abrt-bz-dupchecker b/src/Backtrace/abrt-bz-dupchecker index d7748c72..344d1326 100755 --- a/src/Backtrace/abrt-bz-dupchecker +++ b/src/Backtrace/abrt-bz-dupchecker @@ -130,7 +130,7 @@ dupcount = 0 for backtrace, components in database.items(): for component, bugitems in components.items(): if len(bugitems) > 1: - dupcount += len(value) - 1 + dupcount += len(bugitems) - 1 print "Total number of duplicate bugs detected: {0}".format(dupcount) print "------------------------------" @@ -140,5 +140,8 @@ for backtrace, components in database.items(): for component, bugitems in components.items(): if len(bugitems) > 1: print "Component: {0}".format(component) - print "Duplicates: {0}".format(map(lambda x: "{0} ({1})".format(x['id'],x['comments']), bugitems).join(", ")) + print "Duplicates: {0}".format( + 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/src/Backtrace/abrt-bz-hashchecker index 9c4a5ff3..ec7ce1a6 100755 --- a/src/Backtrace/abrt-bz-hashchecker +++ b/src/Backtrace/abrt-bz-hashchecker @@ -56,4 +56,4 @@ bz.logout() for hash, ids in hashes.items(): if len(ids) > 1: - print "Duplicates found: ", ids.join(", ") + print "Duplicates found: ", reduce(lambda x,y: str(x)+", "+str(y), ids) diff --git a/src/CLI/dbus.cpp b/src/CLI/dbus.cpp index ffd1157e..db45cd8b 100644 --- a/src/CLI/dbus.cpp +++ b/src/CLI/dbus.cpp @@ -142,12 +142,15 @@ map_crash_data_t call_CreateReport(const char* uuid) return argout; } -report_status_t call_Report(const map_crash_data_t& report) +report_status_t call_Report(const map_crash_data_t& report, + const map_map_string_t &plugins) { DBusMessage* msg = new_call_msg(__func__ + 5); DBusMessageIter out_iter; dbus_message_iter_init_append(msg, &out_iter); store_val(&out_iter, report); + if (!plugins.empty()) + store_val(&out_iter, plugins); DBusMessage *reply = send_get_reply_and_unref(msg); @@ -184,7 +187,6 @@ int32_t call_DeleteDebugDump(const char* uuid) return result; } -#ifdef UNUSED map_map_string_t call_GetPluginsInfo() { DBusMessage *msg = new_call_msg(__func__ + 5); @@ -201,7 +203,27 @@ map_map_string_t call_GetPluginsInfo() dbus_message_unref(reply); return argout; } -#endif + +map_plugin_settings_t call_GetPluginSettings(const char *name) +{ + DBusMessage *msg = new_call_msg(__func__ + 5); + dbus_message_append_args(msg, + DBUS_TYPE_STRING, &name, + DBUS_TYPE_INVALID); + + DBusMessage *reply = send_get_reply_and_unref(msg); + + DBusMessageIter in_iter; + dbus_message_iter_init(reply, &in_iter); + + map_string_t argout; + int r = load_val(&in_iter, argout); + if (r != ABRT_DBUS_LAST_FIELD) /* more values present, or bad type */ + error_msg_and_die("dbus call %s: return type mismatch", __func__ + 5); + + dbus_message_unref(reply); + return argout; +} void handle_dbus_err(bool error_flag, DBusError *err) { diff --git a/src/CLI/dbus.h b/src/CLI/dbus.h index c6fd7a4d..c6c61eb5 100644 --- a/src/CLI/dbus.h +++ b/src/CLI/dbus.h @@ -25,10 +25,19 @@ extern DBusConnection* s_dbus_conn; vector_map_crash_data_t call_GetCrashInfos(); map_crash_data_t call_CreateReport(const char *uuid); -report_status_t call_Report(const map_crash_data_t& report); + +/** Sends report using enabled Reporter plugins. + * @param plugins + * Optional settings for plugins, can be empty. + * Format: plugins["PluginName"]["SettingsKey"] = "SettingsValue" + * If it contains settings for some plugin, it must contain _all fields_ + * obtained by call_GetPluginSettings, otherwise the plugin might ignore + * the settings. + */ +report_status_t call_Report(const map_crash_data_t& report, + const map_map_string_t &plugins); int32_t call_DeleteDebugDump(const char* uuid); -#ifdef UNUSED /* Gets basic data about all installed plugins. */ map_map_string_t call_GetPluginsInfo(); @@ -38,7 +47,6 @@ map_map_string_t call_GetPluginsInfo(); * Corresponds to name obtained from call_GetPluginsInfo. */ map_plugin_settings_t call_GetPluginSettings(const char *name); -#endif void handle_dbus_err(bool error_flag, DBusError *err); diff --git a/src/CLI/report.cpp b/src/CLI/report.cpp index 2bcd52af..7ef33acf 100644 --- a/src/CLI/report.cpp +++ b/src/CLI/report.cpp @@ -412,10 +412,63 @@ int report(const char *uuid, bool always) } } + map_map_string_t pluginSettings; + if (!always) + { + // Get informations about all plugins. + map_map_string_t plugins = call_GetPluginsInfo(); + // Check the configuration of each enabled Reporter plugin. + map_map_string_t::iterator it, itend = plugins.end(); + for (it = plugins.begin(); it != itend; ++it) + { + // Skip disabled plugins. + if (0 != strcmp(it->second["Enabled"].c_str(), "yes")) + continue; + // Skip nonReporter plugins. + if (0 != strcmp(it->second["Type"].c_str(), "Reporter")) + continue; + + map_string_t settings = call_GetPluginSettings(it->first.c_str()); + // Login information is missing. + bool loginMissing = settings.find("Login") != settings.end() + && 0 == strcmp(settings["Login"].c_str(), ""); + bool passwordMissing = settings.find("Password") != settings.end() + && 0 == strcmp(settings["Password"].c_str(), ""); + if (!loginMissing && !passwordMissing) + continue; + + // Copy the received settings as defaults. + // Plugins won't work without it, if some value is missing + // they use their default values for all fields. + pluginSettings[it->first] = settings; + + printf(_("Wrong settings were detected for plugin %s.\n"), it->second["Name"].c_str()); + if (loginMissing) + { + printf(_("Enter your login: ")); + fflush(NULL); + char answer[64] = ""; + fgets(answer, sizeof(answer), stdin); + if (strlen(answer) > 0) + pluginSettings[it->first]["Login"] = answer; + } + if (passwordMissing) + { +// TODO: echo off, see http://fixunix.com/unix/84474-echo-off.html + printf(_("Enter your password: ")); + fflush(NULL); + char answer[64] = ""; + fgets(answer, sizeof(answer), stdin); + if (strlen(answer) > 0) + pluginSettings[it->first]["Password"] = answer; + } + } + } + int errors = 0; int plugins = 0; puts(_("Reporting...")); - report_status_t r = call_Report(cr); + report_status_t r = call_Report(cr, pluginSettings); report_status_t::iterator it = r.begin(); while (it != r.end()) { diff --git a/src/Daemon/Daemon.cpp b/src/Daemon/Daemon.cpp index 511d45d6..99214230 100644 --- a/src/Daemon/Daemon.cpp +++ b/src/Daemon/Daemon.cpp @@ -316,9 +316,12 @@ static void FindNewDumps(const char* pPath) } closedir(dp); - log("Checking for unsaved crashdumps (%u dirs to check)", (unsigned)dirs.size()); + unsigned size = dirs.size(); + if (size == 0) + return; + log("Checking for unsaved crashes (dirs to check:%u)", size); - /* Get potential unsaved debugdumps */ + /* Get potentially non-processed debugdumps */ vector_string_t::iterator itt = dirs.begin(); for (; itt != dirs.end(); ++itt) { @@ -332,19 +335,22 @@ static void FindNewDumps(const char* pPath) case MW_OK: /* Not VERB1: this is new, unprocessed crash dump. * Last abrtd somehow missed it - need to inform user */ - log("Non-processed crashdump in %s, saving into database", dir_name); - RunActionsAndReporters(get_crash_data_item_content(crashinfo, CD_DUMPDIR).c_str()); + log("Non-processed crash in %s, saving into database", dir_name); + /* Run automatic actions and reporters on it (if we have them configured) */ + RunActionsAndReporters(dir_name); break; case MW_IN_DB: + /* This debugdump was found in DB, nothing else was done + * by SaveDebugDump or needs to be done by us */ VERB1 log("%s is already saved in database", dir_name); break; - case MW_REPORTED: - case MW_OCCURED: - VERB1 log("Already saved crash %s, deleting", dir_name); + case MW_REPORTED: /* already reported dup */ + case MW_OCCURRED: /* not-yet-reported dup */ + VERB1 log("Duplicate crash %s, deleting", dir_name); delete_debug_dump_dir(dir_name); break; default: - log("Corrupted or bad crashdump %s (res:%d), deleting", dir_name, (int)res); + log("Corrupted or bad crash %s (res:%d), deleting", dir_name, (int)res); delete_debug_dump_dir(dir_name); break; } @@ -354,6 +360,7 @@ static void FindNewDumps(const char* pPath) error_msg("%s", e.what()); } } + log("Done checking for unsaved crashes"); } static int CreatePidFile() @@ -483,27 +490,37 @@ static gboolean handle_inotify_cb(GIOChannel *gio, GIOCondition condition, gpoin try { std::string fullname = concat_path_file(DEBUG_DUMPS_DIR, name); -//todo: rename SaveDebugDump to ???? it does not save crashinfo, it FETCHES crashinfo + /* Note: SaveDebugDump does not save crashinfo, it _fetches_ crashinfo */ map_crash_data_t crashinfo; mw_result_t res = SaveDebugDump(fullname.c_str(), crashinfo); switch (res) { case MW_OK: - log("New crash, saving"); - RunActionsAndReporters(get_crash_data_item_content(crashinfo, CD_DUMPDIR).c_str()); + log("New crash %s, processing", fullname.c_str()); + /* Run automatic actions and reporters on it (if we have them configured) */ + RunActionsAndReporters(fullname.c_str()); /* Fall through */ - case MW_REPORTED: - case MW_OCCURED: + + case MW_REPORTED: /* already reported dup */ + case MW_OCCURRED: /* not-yet-reported dup */ { if (res != MW_OK) - log("Already saved crash, just sending dbus signal"); + { + const char *first = get_crash_data_item_content(crashinfo, CD_DUMPDIR).c_str(); + log("Deleting crash %s (dup of %s), sending dbus signal", + strrchr(fullname.c_str(), '/') + 1, + strrchr(first, '/') + 1); + delete_debug_dump_dir(fullname.c_str()); + } +#define fullname fullname_should_not_be_used_here const char *analyzer = get_crash_data_item_content(crashinfo, FILENAME_ANALYZER).c_str(); const char *uid_str = get_crash_data_item_content(crashinfo, FILENAME_UID).c_str(); /* Autoreport it if configured to do so */ - if (analyzer_has_AutoReportUIDs(analyzer, uid_str)) - { + if (res != MW_REPORTED + && analyzer_has_AutoReportUIDs(analyzer, uid_str) + ) { VERB1 log("Reporting the crash automatically"); map_crash_data_t crash_report; mw_result_t crash_result = CreateCrashReport( @@ -531,15 +548,18 @@ static gboolean handle_inotify_cb(GIOChannel *gio, GIOCondition condition, gpoin uid_str = NULL; g_pCommLayer->Crash(get_crash_data_item_content(crashinfo, FILENAME_PACKAGE).c_str(), uid_str); break; +#undef fullname } + case MW_IN_DB: + log("Huh, this crash is already in db?! Nothing to do"); + break; case MW_BLACKLISTED: case MW_CORRUPTED: case MW_PACKAGE_ERROR: case MW_GPG_ERROR: - case MW_IN_DB: case MW_FILE_ERROR: default: - log("Corrupted or bad crash, deleting"); + log("Corrupted or bad crash %s (res:%d), deleting", fullname.c_str(), (int)res); delete_debug_dump_dir(fullname.c_str()); break; } @@ -859,7 +879,7 @@ int main(int argc, char** argv) { /* This may take a while, therefore we don't do it in init section */ FindNewDumps(DEBUG_DUMPS_DIR); - log("Running..."); + log("Init complete, entering main loop"); run_main_loop(pMainloop); } catch (CABRTException& e) diff --git a/src/Daemon/MiddleWare.cpp b/src/Daemon/MiddleWare.cpp index c3f9061a..ebd5c0fc 100644 --- a/src/Daemon/MiddleWare.cpp +++ b/src/Daemon/MiddleWare.cpp @@ -702,10 +702,6 @@ static mw_result_t SavePackageDescriptionToDebugDump( catch (CABRTException& e) { error_msg("%s", e.what()); - if (e.type() == EXCEP_DD_SAVE) - { - return MW_FILE_ERROR; - } return MW_ERROR; } @@ -832,15 +828,16 @@ static mw_result_t SaveDebugDumpToDatabase(const char *pUUID, mw_result_t res = FillCrashInfo(pUUID, pUID, pCrashData); if (res == MW_OK) { + const char *first = get_crash_data_item_content(pCrashData, CD_DUMPDIR).c_str(); if (row.m_sReported == "1") { - log("Crash is already reported"); + log("Crash is in database already (dup of %s) and is reported", first); return MW_REPORTED; } if (row.m_sCount != "1") { - log("Crash is in database already"); - return MW_OCCURED; + log("Crash is in database already (dup of %s)", first); + return MW_OCCURRED; } } return res; @@ -876,10 +873,6 @@ mw_result_t SaveDebugDump(const char *pDebugDumpDir, catch (CABRTException& e) { error_msg("%s", e.what()); - if (e.type() == EXCEP_DD_SAVE) - { - return MW_FILE_ERROR; - } return MW_ERROR; } @@ -898,6 +891,13 @@ mw_result_t SaveDebugDump(const char *pDebugDumpDir, const char *uid_str = analyzer_has_InformAllUsers(analyzer.c_str()) ? "-1" : UID.c_str(); + /* Loads pCrashData (from the *first debugdump dir* if this one is a dup) + * Returns: + * MW_REPORTED: "the crash is flagged as reported in DB" (which also means it's a dup) + * MW_OCCURRED: "crash count is != 1" (iow: it is > 1 - dup) + * MW_OK: "crash count is 1" (iow: this is a new crash, not a dup) + * else: an error code + */ return SaveDebugDumpToDatabase(lUUID.c_str(), uid_str, time.c_str(), pDebugDumpDir, pCrashData); } diff --git a/src/Daemon/MiddleWare.h b/src/Daemon/MiddleWare.h index 71d17f35..aa37e3ed 100644 --- a/src/Daemon/MiddleWare.h +++ b/src/Daemon/MiddleWare.h @@ -37,7 +37,7 @@ typedef enum { MW_PACKAGE_ERROR, /**< Cannot determine package name.*/ MW_GPG_ERROR, /**< Package is not signed properly.*/ MW_REPORTED, /**< Crash is already reported.*/ - MW_OCCURED, /**< Crash occurred in the past, but it is not reported yet.*/ + MW_OCCURRED, /**< Crash occurred in the past, but it is not reported yet.*/ MW_IN_DB, /**< Debugdump directory is already saved in a database.*/ MW_IN_DB_ERROR, /**< Error while working with a database.*/ MW_PLUGIN_ERROR, /**< plugin wasn't found or error within plugin*/ @@ -105,8 +105,11 @@ report_status_t Report(const map_crash_data_t& pCrashData, std::string getDebugDumpDir( const char *pUUID, const char *pUID); /** - * Saves debugdump into database. If saving is successful, - * it fills crash info. + * Adds package name and description to debugdump dir. + * Saves debugdump into database. + * Detects whether it's a duplicate crash. + * Fills crash info. + * Note that if it's a dup, loads _first crash_ info, not this one's. * @param pDebugDumpDir A debugdump directory. * @param pCrashData A crash info. * @return It return results of operation. See mw_result_t. |