1 | /******************************************************************* |
2 | * reportinterface.cpp |
3 | * Copyright 2009,2010, 2011 Dario Andres Rodriguez <andresbajotierra@gmail.com> |
4 | * Copyright 2009 George Kiagiadakis <gkiagia@users.sourceforge.net> |
5 | * |
6 | * This program is free software; you can redistribute it and/or |
7 | * modify it under the terms of the GNU General Public License as |
8 | * published by the Free Software Foundation; either version 2 of |
9 | * the License, or (at your option) any later version. |
10 | * |
11 | * This program is distributed in the hope that it will be useful, |
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
14 | * GNU General Public License for more details. |
15 | * |
16 | * You should have received a copy of the GNU General Public License |
17 | * along with this program. If not, see <http://www.gnu.org/licenses/>. |
18 | * |
19 | ******************************************************************/ |
20 | |
21 | #include "reportinterface.h" |
22 | |
23 | #include <KDebug> |
24 | #include <KLocalizedString> |
25 | |
26 | #include "drkonqi.h" |
27 | #include "bugzillalib.h" |
28 | #include "productmapping.h" |
29 | #include "systeminformation.h" |
30 | #include "crashedapplication.h" |
31 | #include "debuggermanager.h" |
32 | #include "parser/backtraceparser.h" |
33 | #include "backtracegenerator.h" |
34 | #include "applicationdetailsexamples.h" |
35 | |
36 | ReportInterface::ReportInterface(QObject *parent) |
37 | : QObject(parent), |
38 | m_duplicate(0) |
39 | { |
40 | m_bugzillaManager = new BugzillaManager(KDE_BUGZILLA_URL, this); |
41 | |
42 | m_productMapping = new ProductMapping(DrKonqi::crashedApplication(), m_bugzillaManager, this); |
43 | |
44 | m_appDetailsExamples = new ApplicationDetailsExamples(this); |
45 | |
46 | //Information the user can provide about the crash |
47 | m_userRememberCrashSituation = false; |
48 | m_reproducible = ReproducibleUnsure; |
49 | m_provideActionsApplicationDesktop = false; |
50 | m_provideUnusualBehavior = false; |
51 | m_provideApplicationConfigurationDetails = false; |
52 | |
53 | //Do not attach the bug report to any other existent report (create a new one) |
54 | m_attachToBugNumber = 0; |
55 | } |
56 | |
57 | void ReportInterface::setBugAwarenessPageData(bool rememberSituation, |
58 | Reproducible reproducible, bool actions, |
59 | bool unusual, bool configuration) |
60 | { |
61 | //Save the information the user can provide about the crash from the assistant page |
62 | m_userRememberCrashSituation = rememberSituation; |
63 | m_reproducible = reproducible; |
64 | m_provideActionsApplicationDesktop = actions; |
65 | m_provideUnusualBehavior = unusual; |
66 | m_provideApplicationConfigurationDetails = configuration; |
67 | } |
68 | |
69 | bool ReportInterface::isBugAwarenessPageDataUseful() const |
70 | { |
71 | //Determine if the assistant should proceed, considering the amount of information |
72 | //the user can provide |
73 | int rating = selectedOptionsRating(); |
74 | |
75 | //Minimum information required even for a good backtrace. |
76 | bool useful = m_userRememberCrashSituation && |
77 | (rating >= 2 || (m_reproducible==ReproducibleSometimes || |
78 | m_reproducible==ReproducibleEverytime)); |
79 | return useful; |
80 | } |
81 | |
82 | int ReportInterface::selectedOptionsRating() const |
83 | { |
84 | //Check how many information the user can provide and generate a rating |
85 | int rating = 0; |
86 | if (m_provideActionsApplicationDesktop) { |
87 | rating += 3; |
88 | } |
89 | if (m_provideApplicationConfigurationDetails) { |
90 | rating += 2; |
91 | } |
92 | if (m_provideUnusualBehavior) { |
93 | rating += 1; |
94 | } |
95 | return rating; |
96 | } |
97 | |
98 | QString ReportInterface::backtrace() const |
99 | { |
100 | return m_backtrace; |
101 | } |
102 | |
103 | void ReportInterface::setBacktrace(const QString & backtrace) |
104 | { |
105 | m_backtrace = backtrace; |
106 | } |
107 | |
108 | QStringList ReportInterface::firstBacktraceFunctions() const |
109 | { |
110 | return m_firstBacktraceFunctions; |
111 | } |
112 | |
113 | void ReportInterface::setFirstBacktraceFunctions(const QStringList & functions) |
114 | { |
115 | m_firstBacktraceFunctions = functions; |
116 | } |
117 | |
118 | QString ReportInterface::title() const |
119 | { |
120 | return m_reportTitle; |
121 | } |
122 | |
123 | void ReportInterface::setTitle(const QString & text) |
124 | { |
125 | m_reportTitle = text; |
126 | } |
127 | |
128 | void ReportInterface::setDetailText(const QString & text) |
129 | { |
130 | m_reportDetailText = text; |
131 | } |
132 | |
133 | void ReportInterface::setPossibleDuplicates(const QStringList & list) |
134 | { |
135 | m_possibleDuplicates = list; |
136 | } |
137 | |
138 | QString ReportInterface::generateReportFullText(bool drKonqiStamp) const |
139 | { |
140 | //Note: no translations should be done in this function's strings |
141 | |
142 | const CrashedApplication * crashedApp = DrKonqi::crashedApplication(); |
143 | const SystemInformation * sysInfo = DrKonqi::systemInformation(); |
144 | |
145 | QString report; |
146 | |
147 | //Program name and versions |
148 | report.append(QString("Application: %1 (%2)\n" ).arg(crashedApp->fakeExecutableBaseName(), |
149 | crashedApp->version())); |
150 | report.append(QString("KDE Platform Version: %1" ).arg(sysInfo->kdeVersion())); |
151 | if ( sysInfo->compiledSources() ) { |
152 | report.append(QString(" (Compiled from sources)\n" )); |
153 | } else { |
154 | report.append(QString("\n" )); |
155 | } |
156 | report.append(QString("Qt Version: %1\n" ).arg(sysInfo->qtVersion())); |
157 | report.append(QString("Operating System: %1\n" ).arg(sysInfo->operatingSystem())); |
158 | |
159 | //LSB output or manually selected distro |
160 | if ( !sysInfo->lsbRelease().isEmpty() ) { |
161 | report.append(QString("Distribution: %1\n" ).arg(sysInfo->lsbRelease())); |
162 | } else if ( !sysInfo->bugzillaPlatform().isEmpty() && |
163 | sysInfo->bugzillaPlatform() != QLatin1String("unspecified" )) { |
164 | report.append(QString("Distribution (Platform): %1\n" ).arg( |
165 | sysInfo->bugzillaPlatform())); |
166 | } |
167 | report.append(QLatin1String("\n" )); |
168 | |
169 | //Details of the crash situation |
170 | if (isBugAwarenessPageDataUseful()) { |
171 | report.append(QString("-- Information about the crash:\n" )); |
172 | if (!m_reportDetailText.isEmpty()) { |
173 | report.append(m_reportDetailText.trimmed()); |
174 | } else { |
175 | //If the user manual reports this crash, he/she should know what to put in here. |
176 | //This message is the only one translated in this function |
177 | report.append(i18nc("@info/plain" ,"<placeholder>In detail, tell us what you were doing " |
178 | " when the application crashed.</placeholder>" )); |
179 | } |
180 | report.append(QLatin1String("\n\n" )); |
181 | } |
182 | |
183 | //Crash reproducibility (only if useful) |
184 | if (m_reproducible != ReproducibleUnsure) { |
185 | if (m_reproducible == ReproducibleEverytime) { |
186 | report.append(QString("The crash can be reproduced every time.\n\n" )); |
187 | } else if (m_reproducible == ReproducibleSometimes) { |
188 | report.append(QString("The crash can be reproduced sometimes.\n\n" )); |
189 | } else if (m_reproducible == ReproducibleNever) { |
190 | report.append(QString("The crash does not seem to be reproducible.\n\n" )); |
191 | } |
192 | } |
193 | |
194 | //Backtrace |
195 | report.append(QString("-- Backtrace:\n" )); |
196 | if (!m_backtrace.isEmpty()) { |
197 | QString formattedBacktrace = m_backtrace.trimmed(); |
198 | report.append(formattedBacktrace + QLatin1String("\n" )); |
199 | } else { |
200 | report.append(QString("A useful backtrace could not be generated\n" )); |
201 | } |
202 | |
203 | //Possible duplicates (selected by the user) |
204 | if (!m_possibleDuplicates.isEmpty()) { |
205 | report.append(QLatin1String("\n" )); |
206 | QString duplicatesString; |
207 | Q_FOREACH(const QString & dupe, m_possibleDuplicates) { |
208 | duplicatesString += QLatin1String("bug " ) + dupe + QLatin1String(", " ); |
209 | } |
210 | duplicatesString = duplicatesString.left(duplicatesString.length()-2) + '.'; |
211 | report.append(QString("The reporter indicates this bug may be a duplicate of or related to %1\n" ) |
212 | .arg(duplicatesString)); |
213 | } |
214 | |
215 | //Several possible duplicates (by bugzilla query) |
216 | if (!m_allPossibleDuplicatesByQuery.isEmpty()) { |
217 | report.append(QLatin1String("\n" )); |
218 | QString duplicatesString; |
219 | int count = m_allPossibleDuplicatesByQuery.count(); |
220 | for(int i=0; i < count && i < 5; i++) { |
221 | duplicatesString += QLatin1String("bug " ) + m_allPossibleDuplicatesByQuery.at(i) + |
222 | QLatin1String(", " ); |
223 | } |
224 | duplicatesString = duplicatesString.left(duplicatesString.length()-2) + '.'; |
225 | report.append(QString("Possible duplicates by query: %1\n" ).arg(duplicatesString)); |
226 | } |
227 | |
228 | if (drKonqiStamp) { |
229 | report.append(QLatin1String("\nReported using DrKonqi" )); |
230 | } |
231 | |
232 | return report; |
233 | } |
234 | |
235 | QString ReportInterface::() const |
236 | { |
237 | //Note: no translations should be done in this function's strings |
238 | |
239 | const CrashedApplication * crashedApp = DrKonqi::crashedApplication(); |
240 | const SystemInformation * sysInfo = DrKonqi::systemInformation(); |
241 | |
242 | QString ; |
243 | |
244 | //Program name and versions |
245 | comment.append(QString("%1 (%2) on KDE Platform %3 using Qt %4\n\n" ) |
246 | .arg(crashedApp->fakeExecutableBaseName()) |
247 | .arg(crashedApp->version()) |
248 | .arg(sysInfo->kdeVersion()) |
249 | .arg(sysInfo->qtVersion())); |
250 | |
251 | //Details of the crash situation |
252 | if (isBugAwarenessPageDataUseful()) { |
253 | comment.append(QString("%1\n\n" ).arg(m_reportDetailText.trimmed())); |
254 | } |
255 | |
256 | //Backtrace (only 6 lines) |
257 | comment.append(QString("-- Backtrace (Reduced):\n" )); |
258 | QString reducedBacktrace = |
259 | DrKonqi::debuggerManager()->backtraceGenerator()->parser()->simplifiedBacktrace(); |
260 | comment.append(reducedBacktrace.trimmed()); |
261 | |
262 | return comment; |
263 | } |
264 | |
265 | BugReport ReportInterface::newBugReportTemplate() const |
266 | { |
267 | //Generate a new bug report template with some values on it |
268 | BugReport report; |
269 | |
270 | const SystemInformation * sysInfo = DrKonqi::systemInformation(); |
271 | |
272 | report.setProduct(m_productMapping->bugzillaProduct()); |
273 | report.setComponent(m_productMapping->bugzillaComponent()); |
274 | report.setVersion(m_productMapping->bugzillaVersion()); |
275 | report.setOperatingSystem(sysInfo->bugzillaOperatingSystem()); |
276 | if (sysInfo->compiledSources()) { |
277 | report.setPlatform(QLatin1String("Compiled Sources" )); |
278 | } else { |
279 | report.setPlatform(sysInfo->bugzillaPlatform()); |
280 | } |
281 | report.setKeywords(QStringList() << "drkonqi" ); |
282 | report.setPriority(QLatin1String("NOR" )); |
283 | report.setBugSeverity(QLatin1String("crash" )); |
284 | |
285 | /* |
286 | Disable the backtrace functions on title for RELEASE. |
287 | It also needs a bit of polishment |
288 | |
289 | QString title = m_reportTitle; |
290 | |
291 | //If there are not too much possible duplicates by query then there are more possibilities |
292 | //that this report is unique. Let's add the backtrace functions to the title |
293 | if (m_allPossibleDuplicatesByQuery.count() <= 2) { |
294 | if (!m_firstBacktraceFunctions.isEmpty()) { |
295 | title += (QLatin1String(" [") + m_firstBacktraceFunctions.join(", ").trimmed() |
296 | + QLatin1String("]")); |
297 | } |
298 | } |
299 | */ |
300 | |
301 | report.setShortDescription(m_reportTitle); |
302 | return report; |
303 | } |
304 | |
305 | void ReportInterface::sendBugReport() const |
306 | { |
307 | if (m_attachToBugNumber > 0) |
308 | { |
309 | //We are going to attach the report to an existent one |
310 | connect(m_bugzillaManager, SIGNAL(addMeToCCFinished(int)), this, SLOT(addedToCC())); |
311 | connect(m_bugzillaManager, SIGNAL(addMeToCCError(QString,QString)), |
312 | this, SIGNAL(sendReportError(QString,QString))); |
313 | //First add the user to the CC list, then attach |
314 | m_bugzillaManager->addMeToCC(m_attachToBugNumber); |
315 | } else { |
316 | //Creating a new bug report |
317 | BugReport report = newBugReportTemplate(); |
318 | report.setDescription(generateReportFullText(true)); |
319 | report.setValid(true); |
320 | |
321 | connect(m_bugzillaManager, SIGNAL(sendReportErrorInvalidValues()), this, SLOT(sendUsingDefaultProduct())); |
322 | connect(m_bugzillaManager, SIGNAL(reportSent(int)), this, SIGNAL(reportSent(int))); |
323 | connect(m_bugzillaManager, SIGNAL(sendReportError(QString,QString)), |
324 | this, SIGNAL(sendReportError(QString,QString))); |
325 | m_bugzillaManager->sendReport(report); |
326 | } |
327 | } |
328 | |
329 | void ReportInterface::sendUsingDefaultProduct() const |
330 | { |
331 | //Fallback function: if some of the custom values fail, we need to reset all the fields to the default |
332 | //(and valid) bugzilla values; and try to resend |
333 | BugReport report = newBugReportTemplate(); |
334 | report.setProduct(QLatin1String("kde" )); |
335 | report.setComponent(QLatin1String("general" )); |
336 | report.setPlatform(QLatin1String("unspecified" )); |
337 | report.setDescription(generateReportFullText(true)); |
338 | report.setValid(true); |
339 | m_bugzillaManager->sendReport(report); |
340 | } |
341 | |
342 | void ReportInterface::addedToCC() |
343 | { |
344 | //The user was added to the CC list, proceed with the attachment |
345 | connect(m_bugzillaManager, SIGNAL(attachToReportSent(int)), this, SLOT(attachSent(int))); |
346 | connect(m_bugzillaManager, SIGNAL(attachToReportError(QString,QString)), |
347 | this, SIGNAL(sendReportError(QString,QString))); |
348 | |
349 | QString reportText = generateReportFullText(true); |
350 | QString = generateAttachmentComment(); |
351 | QString filename = getSuggestedKCrashFilename(DrKonqi::crashedApplication()); |
352 | QLatin1String summary("New crash information added by DrKonqi" ); |
353 | |
354 | //Attach the report. The comment of the attachment also includes the bug description |
355 | m_bugzillaManager->attachTextToReport(reportText, filename, summary, |
356 | m_attachToBugNumber, comment); |
357 | } |
358 | |
359 | void ReportInterface::attachSent(int attachId) |
360 | { |
361 | Q_UNUSED(attachId); |
362 | |
363 | //The bug was attached, consider it "sent" |
364 | emit reportSent(m_attachToBugNumber); |
365 | } |
366 | |
367 | QStringList ReportInterface::relatedBugzillaProducts() const |
368 | { |
369 | return m_productMapping->relatedBugzillaProducts(); |
370 | } |
371 | |
372 | bool ReportInterface::isWorthReporting() const |
373 | { |
374 | //Evaluate if the provided information is useful enough to enable the automatic report |
375 | bool needToReport = false; |
376 | |
377 | if (!m_userRememberCrashSituation) { |
378 | //This should never happen... but... |
379 | return false; |
380 | } |
381 | |
382 | int rating = selectedOptionsRating(); |
383 | |
384 | BacktraceParser::Usefulness use = |
385 | DrKonqi::debuggerManager()->backtraceGenerator()->parser()->backtraceUsefulness(); |
386 | |
387 | switch (use) { |
388 | case BacktraceParser::ReallyUseful: { |
389 | //Perfect backtrace: require at least one option or a 100%-50% reproducible crash |
390 | needToReport = (rating >=2) || |
391 | (m_reproducible == ReproducibleEverytime || m_reproducible == ReproducibleSometimes); |
392 | break; |
393 | } |
394 | case BacktraceParser::MayBeUseful: { |
395 | //Not perfect backtrace: require at least two options or a 100% reproducible crash |
396 | needToReport = (rating >=3) || (m_reproducible == ReproducibleEverytime); |
397 | break; |
398 | } |
399 | case BacktraceParser::ProbablyUseless: |
400 | //Bad backtrace: require at least two options and always reproducible (strict) |
401 | needToReport = (rating >=5) && (m_reproducible == ReproducibleEverytime); |
402 | break; |
403 | case BacktraceParser::Useless: |
404 | case BacktraceParser::InvalidUsefulness: { |
405 | needToReport = false; |
406 | } |
407 | } |
408 | |
409 | return needToReport; |
410 | } |
411 | |
412 | void ReportInterface::setAttachToBugNumber(uint bugNumber) |
413 | { |
414 | //If bugNumber>0, the report is going to be attached to bugNumber |
415 | m_attachToBugNumber = bugNumber; |
416 | } |
417 | |
418 | uint ReportInterface::attachToBugNumber() const |
419 | { |
420 | return m_attachToBugNumber; |
421 | } |
422 | |
423 | void ReportInterface::setDuplicateId(uint duplicate) |
424 | { |
425 | m_duplicate = duplicate; |
426 | } |
427 | |
428 | uint ReportInterface::duplicateId() const |
429 | { |
430 | return m_duplicate; |
431 | } |
432 | |
433 | void ReportInterface::setPossibleDuplicatesByQuery(const QStringList & list) |
434 | { |
435 | m_allPossibleDuplicatesByQuery = list; |
436 | } |
437 | |
438 | BugzillaManager * ReportInterface::bugzillaManager() const |
439 | { |
440 | return m_bugzillaManager; |
441 | } |
442 | |
443 | ApplicationDetailsExamples * ReportInterface::appDetailsExamples() const |
444 | { |
445 | return m_appDetailsExamples; |
446 | } |
447 | |
448 | #include "reportinterface.moc" |
449 | |