You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
318 lines
12 KiB
318 lines
12 KiB
/* |
|
* 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 3 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, see <http://www.gnu.org/licenses/>. |
|
*/ |
|
|
|
#include "CrashWindow.h" |
|
#include "ui_CrashReportViewer.h" |
|
#include "CrashReporterSymbolSupplier.h" |
|
|
|
CrashWindow::CrashWindow(QWidget* parent) : QDialog(parent) |
|
{ |
|
mCrashReporterUi = new Ui::UniboardCrashreporter(); |
|
mCrashReporterUi->setupUi(this); |
|
mCrashReporterUi->crashReportResult->setFont(QFont("Monaco", 11)); |
|
|
|
connect(mCrashReporterUi->viewReportButton, SIGNAL(clicked(bool)), this, SLOT(showReport())); |
|
connect(mCrashReporterUi->dumpFileButton, SIGNAL(clicked(bool)), this, SLOT(chooseDumpFile())); |
|
connect(mCrashReporterUi->symFileButton, SIGNAL(clicked(bool)), this, SLOT(chooseSymboleFile())); |
|
} |
|
|
|
CrashWindow::~CrashWindow() |
|
{ |
|
// NOOP |
|
} |
|
|
|
void CrashWindow::setDumpFilePath(const QString &fileName) |
|
{ |
|
mCrashReporterUi->dumpFilePath->setText(fileName); |
|
} |
|
|
|
void CrashWindow::chooseDumpFile() |
|
{ |
|
QSettings settings("Mnemis", "CrashReporter"); |
|
QString dumpFileName = QDesktopServices::storageLocation(QDesktopServices::HomeLocation); |
|
if (settings.contains("DumpFileName")) |
|
{ |
|
dumpFileName = settings.value("DumpFileName").toString(); |
|
} |
|
QString fileName = QFileDialog::getOpenFileName(this, tr("Open File"), |
|
dumpFileName, |
|
tr("Dump file (*.dmp)")); |
|
settings.setValue("DumpFileName", QVariant(fileName)); |
|
setDumpFilePath(fileName); |
|
} |
|
|
|
void CrashWindow::chooseSymboleFile() |
|
{ |
|
QSettings settings("Mnemis", "CrashReporter"); |
|
QString symFileName = QDesktopServices::storageLocation(QDesktopServices::HomeLocation); |
|
if (settings.contains("SymFileName")) |
|
{ |
|
symFileName = settings.value("SymFileName").toString(); |
|
} |
|
QString fileName = QFileDialog::getOpenFileName(this, tr("Open File"), |
|
symFileName, |
|
tr("Symbol file (*.sym)")); |
|
settings.setValue("SymFileName", QVariant(fileName)); |
|
mCrashReporterUi->symFilePath->setText(fileName); |
|
} |
|
|
|
//============================================================================= |
|
int CrashWindow::PrintRegister(const char *name, u_int32_t value, int sequence) { |
|
if (sequence % 4 == 0) { |
|
mReport.append("\n"); |
|
} |
|
// string should not exceed 200 byte!!! |
|
size_t BufSize = 2000; |
|
char buf[BufSize]; |
|
|
|
snprintf(buf, BufSize, "%6s = 0x%08x ", name, value); |
|
QString str = QString::fromAscii(buf); |
|
mReport.append(str); |
|
return ++sequence; |
|
} |
|
|
|
//============================================================================= |
|
void CrashWindow::PrintStack(const CallStack* stack, const string &cpu) { |
|
int frame_count = stack->frames()->size(); |
|
char buffer[1024]; |
|
|
|
for (int frame_index = 0; frame_index < frame_count; ++frame_index) { |
|
const StackFrame *frame = stack->frames()->at(frame_index); |
|
const CodeModule *module = frame->module; |
|
// string should not exceed 200 byte!!! |
|
size_t BufSize = 2000; |
|
char buf[BufSize]; |
|
snprintf(buf, BufSize, "%2d ", frame_index); |
|
mReport.append(QString::fromAscii(buf)); |
|
|
|
if (module) { |
|
// Module name (20 chars max) |
|
strcpy(buffer, PathnameStripper::File(module->code_file()).c_str()); |
|
int maxStr = 20; |
|
buffer[maxStr] = 0; |
|
snprintf(buf, BufSize, "%-*s", maxStr, buffer); |
|
mReport.append(QString::fromAscii(buf)); |
|
|
|
|
|
strcpy(buffer, module->version().c_str()); |
|
buffer[maxStr] = 0; |
|
snprintf(buf, BufSize, "%-*s",maxStr, buffer); |
|
mReport.append(QString::fromAscii(buf)); |
|
|
|
u_int64_t instruction = frame->instruction; |
|
|
|
// PPC only: Adjust the instruction to match that of Crash reporter. The |
|
// instruction listed is actually the return address. See the detailed |
|
// comments in stackwalker_ppc.cc for more information. |
|
if (cpu == "ppc" && frame_index) |
|
instruction += 4; |
|
snprintf(buf, BufSize, " 0x%08llx ", instruction); |
|
mReport.append(QString::fromAscii(buf)); |
|
|
|
// Function name |
|
if (!frame->function_name.empty()) { |
|
snprintf(buf, BufSize, "%s", frame->function_name.c_str()); |
|
mReport.append(QString::fromAscii(buf)); |
|
if (!frame->source_file_name.empty()) { |
|
string source_file = PathnameStripper::File(frame->source_file_name); |
|
snprintf(buf, BufSize, " + 0x%llx (%s:%d)", |
|
instruction - frame->source_line_base, |
|
source_file.c_str(), frame->source_line); |
|
mReport.append(QString::fromAscii(buf)); |
|
} else { |
|
snprintf(buf, BufSize, " + 0x%llx", instruction - frame->function_base); |
|
mReport.append(QString::fromAscii(buf)); |
|
} |
|
} |
|
} |
|
mReport.append("\n"); |
|
} |
|
} |
|
|
|
//============================================================================= |
|
void CrashWindow::PrintRegisters(const CallStack *stack, const string &cpu) { |
|
int sequence = 0; |
|
const StackFrame *frame = stack->frames()->at(0); |
|
if (cpu == "x86") { |
|
const StackFrameX86 *frame_x86 = |
|
reinterpret_cast<const StackFrameX86*>(frame); |
|
|
|
if (frame_x86->context_validity & StackFrameX86::CONTEXT_VALID_EIP) |
|
sequence = PrintRegister("eip", frame_x86->context.eip, sequence); |
|
if (frame_x86->context_validity & StackFrameX86::CONTEXT_VALID_ESP) |
|
sequence = PrintRegister("esp", frame_x86->context.esp, sequence); |
|
if (frame_x86->context_validity & StackFrameX86::CONTEXT_VALID_EBP) |
|
sequence = PrintRegister("ebp", frame_x86->context.ebp, sequence); |
|
if (frame_x86->context_validity & StackFrameX86::CONTEXT_VALID_EBX) |
|
sequence = PrintRegister("ebx", frame_x86->context.ebx, sequence); |
|
if (frame_x86->context_validity & StackFrameX86::CONTEXT_VALID_ESI) |
|
sequence = PrintRegister("esi", frame_x86->context.esi, sequence); |
|
if (frame_x86->context_validity & StackFrameX86::CONTEXT_VALID_EDI) |
|
sequence = PrintRegister("edi", frame_x86->context.edi, sequence); |
|
if (frame_x86->context_validity == StackFrameX86::CONTEXT_VALID_ALL) { |
|
sequence = PrintRegister("eax", frame_x86->context.eax, sequence); |
|
sequence = PrintRegister("ecx", frame_x86->context.ecx, sequence); |
|
sequence = PrintRegister("edx", frame_x86->context.edx, sequence); |
|
sequence = PrintRegister("efl", frame_x86->context.eflags, sequence); |
|
} |
|
} else if (cpu == "ppc") { |
|
const StackFramePPC *frame_ppc = |
|
reinterpret_cast<const StackFramePPC*>(frame); |
|
|
|
if (frame_ppc->context_validity & StackFramePPC::CONTEXT_VALID_ALL == |
|
StackFramePPC::CONTEXT_VALID_ALL) { |
|
sequence = PrintRegister("srr0", frame_ppc->context.srr0, sequence); |
|
sequence = PrintRegister("srr1", frame_ppc->context.srr1, sequence); |
|
sequence = PrintRegister("cr", frame_ppc->context.cr, sequence); |
|
sequence = PrintRegister("xer", frame_ppc->context.xer, sequence); |
|
sequence = PrintRegister("lr", frame_ppc->context.lr, sequence); |
|
sequence = PrintRegister("ctr", frame_ppc->context.ctr, sequence); |
|
sequence = PrintRegister("mq", frame_ppc->context.mq, sequence); |
|
sequence = PrintRegister("vrsave", frame_ppc->context.vrsave, sequence); |
|
|
|
sequence = 0; |
|
char buffer[5]; |
|
for (int i = 0; i < MD_CONTEXT_PPC_GPR_COUNT; ++i) { |
|
sprintf(buffer, "r%d", i); |
|
sequence = PrintRegister(buffer, frame_ppc->context.gpr[i], sequence); |
|
} |
|
} else { |
|
if (frame_ppc->context_validity & StackFramePPC::CONTEXT_VALID_SRR0) |
|
sequence = PrintRegister("srr0", frame_ppc->context.srr0, sequence); |
|
if (frame_ppc->context_validity & StackFramePPC::CONTEXT_VALID_GPR1) |
|
sequence = PrintRegister("r1", frame_ppc->context.gpr[1], sequence); |
|
} |
|
} |
|
|
|
mReport.append("\n"); |
|
} |
|
|
|
void CrashWindow::PrintModules(const CodeModules *modules) { |
|
if (!modules) |
|
return; |
|
|
|
mReport.append("\n"); |
|
mReport.append("Loaded modules:\n"); |
|
|
|
u_int64_t main_address = 0; |
|
const CodeModule *main_module = modules->GetMainModule(); |
|
if (main_module) { |
|
main_address = main_module->base_address(); |
|
} |
|
|
|
unsigned int module_count = modules->module_count(); |
|
for (unsigned int module_sequence = 0; |
|
module_sequence < module_count; |
|
++module_sequence) { |
|
const CodeModule *module = modules->GetModuleAtSequence(module_sequence); |
|
assert(module); |
|
u_int64_t base_address = module->base_address(); |
|
// string should not exceed 200 byte!!! |
|
size_t BufSize = 2000; |
|
char buf[BufSize]; |
|
|
|
snprintf(buf, BufSize, "0x%08llx - 0x%08llx %s %s%s %s\n", |
|
base_address, base_address + module->size() - 1, |
|
PathnameStripper::File(module->code_file()).c_str(), |
|
module->version().empty() ? "???" : module->version().c_str(), |
|
main_module != NULL && base_address == main_address ? |
|
" (main)" : "", |
|
module->code_file().c_str()); |
|
mReport.append(QString::fromAscii(buf)); |
|
} |
|
} |
|
|
|
void CrashWindow::showReport() |
|
{ |
|
qDebug() << "compute report"; |
|
mReport.clear(); |
|
QString minidump_file = mCrashReporterUi->dumpFilePath->text(); |
|
BasicSourceLineResolver resolver; |
|
|
|
scoped_ptr<CrashReporterSymbolSupplier> symbol_supplier( |
|
|
|
new CrashReporterSymbolSupplier(mCrashReporterUi->symFilePath->text())); |
|
scoped_ptr<MinidumpProcessor> |
|
minidump_processor(new MinidumpProcessor(symbol_supplier.get(), &resolver)); |
|
ProcessState process_state; |
|
if (minidump_processor->Process(minidump_file.toStdString(), &process_state) != |
|
MinidumpProcessor::PROCESS_OK) { |
|
mReport += "MinidumpProcessor::Process failed\n"; |
|
return; |
|
} |
|
const SystemInfo *system_info = process_state.system_info(); |
|
string cpu = system_info->cpu; |
|
// Convert the time to a string |
|
u_int32_t time_date_stamp = process_state.time_date_stamp(); |
|
struct tm timestruct; |
|
gmtime_r(reinterpret_cast<time_t*>(&time_date_stamp), ×truct); |
|
char timestr[20]; |
|
// string should not exceed 200 byte!!! |
|
size_t BufSize = 2000; |
|
char buf[BufSize]; |
|
strftime(timestr, 20, "%Y-%m-%d %H:%M:%S", ×truct); |
|
snprintf(buf, BufSize, "Date: %s GMT\n", timestr); |
|
mReport.append(QString::fromAscii(buf)); |
|
snprintf(buf, BufSize, "Operating system: %s (%s)\n", system_info->os.c_str(), |
|
system_info->os_version.c_str()); |
|
mReport.append(QString::fromAscii(buf)); |
|
snprintf(buf, BufSize, "Architecture: %s\n", cpu.c_str()); |
|
mReport.append(QString::fromAscii(buf)); |
|
|
|
if (process_state.crashed()) { |
|
snprintf(buf, BufSize, "Crash reason: %s\n", process_state.crash_reason().c_str()); |
|
mReport.append(QString::fromAscii(buf)); |
|
snprintf(buf, BufSize, "Crash address: 0x%llx\n", process_state.crash_address()); |
|
mReport.append(QString::fromAscii(buf)); |
|
} else { |
|
mReport.append("No crash\n"); |
|
} |
|
|
|
int requesting_thread = process_state.requesting_thread(); |
|
if (requesting_thread != -1) { |
|
mReport.append("\n"); |
|
snprintf(buf, BufSize, "Thread %d (%s)\n", |
|
requesting_thread, |
|
process_state.crashed() ? "crashed" : |
|
"requested dump, did not crash"); |
|
mReport.append(QString::fromAscii(buf)); |
|
PrintStack(process_state.threads()->at(requesting_thread), cpu); |
|
} |
|
|
|
// Print all of the threads in the dump. |
|
int thread_count = process_state.threads()->size(); |
|
for (int thread_index = 0; thread_index < thread_count; ++thread_index) { |
|
if (thread_index != requesting_thread) { |
|
// Don't print the crash thread again, it was already printed. |
|
mReport.append("\n"); |
|
snprintf(buf, BufSize,"Thread %d\n", thread_index); |
|
mReport.append(QString::fromAscii(buf)); |
|
PrintStack(process_state.threads()->at(thread_index), cpu); |
|
} |
|
} |
|
|
|
// Print the crashed registers |
|
if (requesting_thread != -1) { |
|
snprintf(buf, BufSize,"\nThread %d:", requesting_thread); |
|
mReport.append(QString::fromAscii(buf)); |
|
PrintRegisters(process_state.threads()->at(requesting_thread), cpu); |
|
} |
|
|
|
// Print information about modules |
|
PrintModules(process_state.modules()); |
|
mCrashReporterUi->crashReportResult->setPlainText(mReport); |
|
} |
|
|
|
|
|
|