/*
* 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 ) , & timestruct ) ;
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 " , & timestruct ) ;
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 , " \n Thread %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 ) ;
}