diff --git a/docs/rh-log.txt b/docs/rh-log.txt index 41a1836da0..1165f3c5dc 100644 --- a/docs/rh-log.txt +++ b/docs/rh-log.txt @@ -1,3 +1,13 @@ +June 23, 2006 +- Changed the archive format for crash reports from tar.gz to zip. +- Went back to a single process model for crash reporting. It appears there + are some machines that don't like having another process collect the + information and report it. I still feel that having another process do + it _should_ be more reliable, because that process can execute in a known + good state. Ah well. I haven't yet seen anything that left the main process + in a totally unrecoverable state, so it's probably okay. Unlike before, this + single-process version is still GCC-compatible. + June 22, 2006 (Changes by Graf Zahl) - Fixed: The resurrect cheat didn't reset the player's damage type so if he died by being frozen the blue blend didn't disappear. diff --git a/src/win32/gccseh.h b/src/win32/gccseh.h deleted file mode 100644 index 9813e26584..0000000000 --- a/src/win32/gccseh.h +++ /dev/null @@ -1,31 +0,0 @@ -// GCC does not support Windows' Structured Exception Handling. :-( -// So I have to fake it using inline assembly - -#ifdef __GNUC__ - -#define __try try -#define __except(a) catch(...) - -#define EH_NONCONTINUABLE 0x01 -#define EH_UNWINDING 0x02 -#define EH_EXIT_UNWIND 0x04 -#define EH_STACK_INVALID 0x08 -#define EH_NESTED_CALL 0x10 - -typedef enum { - ExceptionContinueExecution, - ExceptionContinueSearch, - ExceptionNestedException, - ExceptionCollidedUnwind -} EXCEPTION_DISPOSITION; - -typedef EXCEPTION_DISPOSITION (*PEXCEPTION_HANDLER) - (struct _EXCEPTION_RECORD*, void*, struct _CONTEXT*, void*); - -typedef struct _EXCEPTION_REGISTRATION -{ - struct _EXCEPTION_REGISTRATION* Prev; - PEXCEPTION_HANDLER Handler; -} EXCEPTION_REGISTRATION, *PEXCEPTION_REGISTRATION; - -#endif diff --git a/src/win32/i_crash.cpp b/src/win32/i_crash.cpp index a8cdbea174..071e9edb18 100644 --- a/src/win32/i_crash.cpp +++ b/src/win32/i_crash.cpp @@ -32,6 +32,8 @@ ** */ +// HEADER FILES ------------------------------------------------------------ + #define WIN32_LEAN_AND_MEAN #define _WIN32_WINNT 0x0501 #include @@ -51,6 +53,7 @@ #include #include "resource.h" #include "version.h" +#include "m_swap.h" #include #include @@ -58,16 +61,7 @@ #include #include -// Damn Microsoft for doing Get/SetWindowLongPtr half-assed. Instead of -// giving them proper prototypes under Win32, they are just macros for -// Get/SetWindowLong, meaning they take LONGs and not LONG_PTRs. -#ifdef _WIN64 -typedef LONG_PTR WLONG_PTR; -#else -typedef LONG WLONG_PTR; -#endif - -HRESULT (__stdcall *pEnableThemeDialogTexture) (HWND hwnd, DWORD dwFlags); +// MACROS ------------------------------------------------------------------ #define BUGS_FORUM_URL "http://forum.zdoom.org/index.php?c=3" @@ -80,11 +74,143 @@ HRESULT (__stdcall *pEnableThemeDialogTexture) (HWND hwnd, DWORD dwFlags); // If you are working on your own modified version of ZDoom, change // the last part of the UPLOAD_AGENT (between parentheses) to your // own program's name. e.g. (Skulltag) or (ZDaemon) or (ZDoomFu) -#define UPLOAD_AGENT "ZDoom/" DOTVERSIONSTR " (ZDoom the Original)" +#define UPLOAD_AGENT "ZDoom/" DOTVERSIONSTR " (" GAMESIG ")" // Time, in milliseconds, to wait for a send() or recv() to complete. #define TIMEOUT 60000 +// Maximum number of files that might appear in a crash report. +#define MAX_FILES 5 + +#ifndef WORDS_BIGENDIAN +#define MAKE_ID(a,b,c,d) ((a)|((b)<<8)|((c)<<16)|((d)<<24)) +#else +#define MAKE_ID(a,b,c,d) ((d)|((c)<<8)|((b)<<16)|((a)<<24)) +#endif + +#define ZIP_LOCALFILE MAKE_ID('P','K',3,4) +#define ZIP_CENTRALFILE MAKE_ID('P','K',1,2) +#define ZIP_ENDOFDIR MAKE_ID('P','K',5,6) + +// TYPES ------------------------------------------------------------------- + +// Damn Microsoft for doing Get/SetWindowLongPtr half-assed. Instead of +// giving them proper prototypes under Win32, they are just macros for +// Get/SetWindowLong, meaning they take LONGs and not LONG_PTRs. +#ifdef _WIN64 +typedef LONG_PTR WLONG_PTR; +#else +typedef LONG WLONG_PTR; +#endif + +namespace zip +{ +#pragma pack(push,1) + struct LocalFileHeader + { + DWORD Magic; // 0 + BYTE VersionToExtract[2]; // 4 + WORD Flags; // 6 + WORD Method; // 8 + WORD ModTime; // 10 + WORD ModDate; // 12 + DWORD CRC32; // 14 + DWORD CompressedSize; // 18 + DWORD UncompressedSize; // 22 + WORD NameLength; // 26 + WORD ExtraLength; // 28 + }; + + struct CentralDirectoryEntry + { + DWORD Magic; + BYTE VersionMadeBy[2]; + BYTE VersionToExtract[2]; + WORD Flags; + WORD Method; + WORD ModTime; + WORD ModDate; + DWORD CRC32; + DWORD CompressedSize; + DWORD UncompressedSize; + WORD NameLength; + WORD ExtraLength; + WORD CommentLength; + WORD StartingDiskNumber; + WORD InternalAttributes; + DWORD ExternalAttributes; + DWORD LocalHeaderOffset; + }; + + struct EndOfCentralDirectory + { + DWORD Magic; + WORD DiskNumber; + WORD FirstDisk; + WORD NumEntries; + WORD NumEntriesOnAllDisks; + DWORD DirectorySize; + DWORD DirectoryOffset; + WORD ZipCommentLength; + }; +#pragma pack(pop) +} + +struct TarFile +{ + HANDLE File; + const char *Filename; + int ZipOffset; + DWORD UncompressedSize; + DWORD CompressedSize; + DWORD CRC32; + bool Deflated; +}; + +typedef BOOL (WINAPI *THREADWALK) (HANDLE, LPTHREADENTRY32); +typedef BOOL (WINAPI *MODULEWALK) (HANDLE, LPMODULEENTRY32); +typedef HANDLE (WINAPI *CREATESNAPSHOT) (DWORD, DWORD); +typedef BOOL (WINAPI *WRITEDUMP) (HANDLE, DWORD, HANDLE, MINIDUMP_TYPE, + PMINIDUMP_EXCEPTION_INFORMATION, + PMINIDUMP_USER_STREAM_INFORMATION, + PMINIDUMP_CALLBACK_INFORMATION); + +// EXTERNAL FUNCTION PROTOTYPES -------------------------------------------- + +// PUBLIC FUNCTION PROTOTYPES ---------------------------------------------- + +// PRIVATE FUNCTION PROTOTYPES --------------------------------------------- + +static void AddFile (HANDLE file, const char *filename); +static void CloseTarFiles (); +static HANDLE MakeZip (); +static void AddZipFile (HANDLE ziphandle, TarFile *whichfile, short dosdate, short dostime); +static HANDLE CreateTempFile (); + +static void DumpBytes (HANDLE file, BYTE *address); +static void AddStackInfo (HANDLE file, void *dumpaddress); +static void StackWalk (HANDLE file, void *dumpaddress, DWORD *topOfStack); +static void AddToolHelp (HANDLE file); + +static HANDLE WriteTextReport (); + +static INT_PTR CALLBACK DetailsDlgProc (HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam); +static void SetEditControl (HWND control, HWND sizedisplay, int filenum); + +static BOOL UploadReport (HANDLE report, bool gziped); + +// EXTERNAL DATA DECLARATIONS ---------------------------------------------- + +extern HINSTANCE g_hInst; + +// PUBLIC DATA DEFINITIONS ------------------------------------------------- + +EXCEPTION_POINTERS CrashPointers; + +// PRIVATE DATA DEFINITIONS ------------------------------------------------ + +static HRESULT (__stdcall *pEnableThemeDialogTexture) (HWND hwnd, DWORD dwFlags); + static const char PostHeader[] = "POST " UPLOAD_URI " HTTP/1.1\r\n" "Host: " REMOTE_HOST "\r\n" @@ -132,70 +258,10 @@ static const char DbgHelpRequest[] = "User-Agent: " UPLOAD_AGENT "\r\n" "\r\n"; - -namespace tar -{ - /* - * Standard Archive Format - Standard TAR - USTAR - */ - enum - { - RECORDSIZE = 512, - NAMSIZE = 100, - TUNMLEN = 32, - TGNMLEN = 32 - }; - - union record { - unsigned char charptr[RECORDSIZE]; - struct { - char name[NAMSIZE]; - char mode[8]; - char uid[8]; - char gid[8]; - char size[12]; - char mtime[12]; - char chksum[8]; - char linkflag; - char linkname[NAMSIZE]; - char magic[8]; - char uname[TUNMLEN]; - char gname[TGNMLEN]; - char devmajor[8]; - char devminor[8]; - } header; - }; -} - -#define MAX_FILES 5 - -struct TarFile -{ - HANDLE File; - const char *Filename; -}; static TarFile TarFiles[MAX_FILES]; + static int NumFiles; -static void AddFile (HANDLE file, const char *filename); -static void CloseTarFiles (); -static HANDLE MakeTar (bool &gzip); -static HANDLE CreateTempFile (); - -typedef BOOL (WINAPI *THREADWALK) (HANDLE, LPTHREADENTRY32); -typedef BOOL (WINAPI *MODULEWALK) (HANDLE, LPMODULEENTRY32); -typedef HANDLE (WINAPI *CREATESNAPSHOT) (DWORD, DWORD); -typedef BOOL (WINAPI *WRITEDUMP) (HANDLE, DWORD, HANDLE, MINIDUMP_TYPE, - PMINIDUMP_EXCEPTION_INFORMATION, - PMINIDUMP_USER_STREAM_INFORMATION, - PMINIDUMP_CALLBACK_INFORMATION); - -extern HINSTANCE g_hInst; - -EXCEPTION_POINTERS CrashPointers; -EXCEPTION_RECORD MyExceptionRecord; -CONTEXT MyContextRecord; - static HANDLE DbgProcess; static DWORD DbgProcessID; static DWORD DbgThreadID; @@ -204,17 +270,48 @@ static PVOID CrashAddress; static bool NeedDbgHelp; static char CrashSummary[256]; -static void DumpBytes (HANDLE file, BYTE *address); -static void AddStackInfo (HANDLE file, void *dumpaddress); -static void StackWalk (HANDLE file, void *dumpaddress, DWORD *topOfStack); -static void AddToolHelp (HANDLE file); +static WNDPROC StdStaticProc; -static HANDLE WriteTextReport (); +// CODE -------------------------------------------------------------------- -static INT_PTR CALLBACK DetailsDlgProc (HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam); -static void SetEditControl (HWND control, HWND sizedisplay, int filenum); +//========================================================================== +// +// SafeReadMemory +// +// Returns true if the memory was succussfully read and false if not. +// +//========================================================================== -static BOOL UploadReport (HANDLE report, bool gziped); +static bool SafeReadMemory (const void *base, void *buffer, size_t len) +{ + // Non-GCC version: Use SEH to catch any bad reads immediately when + // they happen instead of trying to catch them beforehand. + // + // GCC version: Test the memory beforehand and hope it doesn't become + // unreadable while we read it. GCC is the only Windows compiler (that + // I know of) that can't do SEH. +#ifdef __GNUC__ + if (IsBadReadPtr (base, len) != 0) + { + return false; + } +#endif + bool success = false; + +#ifndef __GNUC__ + __try + { +#endif + memcpy (buffer, base, len); + success = true; +#ifndef __GNUC__ + } + __except(EXCEPTION_EXECUTE_HANDLER) + { + } +#endif + return success; +} //========================================================================== // @@ -321,7 +418,7 @@ void __cdecl Writef (HANDLE file, const char *format, ...) // //========================================================================== -void CreateCrashLog (HANDLE process, DWORD processID, DWORD threadID, LPEXCEPTION_POINTERS exceptionRecord, char *custominfo, DWORD customsize) +void CreateCrashLog (char *custominfo, DWORD customsize) { // Do not collect information more than once. if (NumFiles != 0) @@ -329,59 +426,46 @@ void CreateCrashLog (HANDLE process, DWORD processID, DWORD threadID, LPEXCEPTIO return; } - DbgThreadID = threadID; - DbgProcessID = processID; - DbgProcess = process; - if (!ReadProcessMemory (DbgProcess, exceptionRecord, &CrashPointers, sizeof(CrashPointers), NULL) || - !ReadProcessMemory (DbgProcess, CrashPointers.ExceptionRecord, &MyExceptionRecord, sizeof(EXCEPTION_RECORD), NULL) || - !ReadProcessMemory (DbgProcess, CrashPointers.ContextRecord, &MyContextRecord, sizeof(CONTEXT), NULL)) + DbgThreadID = GetCurrentThreadId(); + DbgProcessID = GetCurrentProcessId(); + DbgProcess = GetCurrentProcess(); + + CrashCode = CrashPointers.ExceptionRecord->ExceptionCode; + CrashAddress = CrashPointers.ExceptionRecord->ExceptionAddress; + + AddFile (WriteTextReport(), "report.txt"); + AddFile (WriteMyMiniDump(), "minidump.mdmp"); + + // Copy app-specific information out of the crashing process's memory + // and into a new temporary file. + if (customsize != 0) { HANDLE file = CreateTempFile(); + DWORD wrote; - Writef (file, "Could not read process memory\n"); - AddFile (file, "failed.txt"); - } - else - { - CrashPointers.ExceptionRecord = &MyExceptionRecord; - CrashPointers.ContextRecord = &MyContextRecord; - CrashCode = CrashPointers.ExceptionRecord->ExceptionCode; - CrashAddress = CrashPointers.ExceptionRecord->ExceptionAddress; - - AddFile (WriteTextReport(), "report.txt"); - AddFile (WriteMyMiniDump(), "minidump.mdmp"); - - // Copy app-specific information out of the crashing process's memory - // and into a new temporary file. - if (customsize != 0) + if (file != INVALID_HANDLE_VALUE) { - HANDLE file = CreateTempFile(); - DWORD wrote; + BYTE buffer[512]; + DWORD left; - if (file != INVALID_HANDLE_VALUE) + for (;; customsize -= left, custominfo += left) { - BYTE buffer[512]; - DWORD left; - - for (;; customsize -= left, custominfo += left) + left = customsize > 512 ? 512 : customsize; + if (left == 0) { - left = customsize > 512 ? 512 : customsize; - if (left == 0) - { - break; - } - if (ReadProcessMemory (DbgProcess, custominfo, buffer, left, NULL)) - { - WriteFile (file, buffer, left, &wrote, NULL); - } - else - { - Writef (file, "Failed reading remaining %lu bytes\r\n", customsize); - break; - } + break; + } + if (SafeReadMemory (custominfo, buffer, left)) + { + WriteFile (file, buffer, left, &wrote, NULL); + } + else + { + Writef (file, "Failed reading remaining %lu bytes\r\n", customsize); + break; } - AddFile (file, "local.txt"); } + AddFile (file, "local.txt"); } } CloseHandle (DbgProcess); @@ -675,7 +759,7 @@ static void AddStackInfo (HANDLE file, void *dumpaddress) Writef (file, "%p:", scan); for (i = 0; i < max; ++i) { - if (!ReadProcessMemory (DbgProcess, &scan[i], &peekd, 4, NULL)) + if (!SafeReadMemory (&scan[i], &peekd, 4)) { break; } @@ -688,7 +772,7 @@ static void AddStackInfo (HANDLE file, void *dumpaddress) Writef (file, " "); for (i = 0; i < max*4; ++i) { - if (!ReadProcessMemory (DbgProcess, (BYTE *)scan + i, &peekb, 1, NULL)) + if (!SafeReadMemory ((BYTE *)scan + i, &peekb, 1)) { break; } @@ -725,7 +809,7 @@ static void StackWalk (HANDLE file, void *dumpaddress, DWORD *topOfStack) { DWORD_PTR code; - if (ReadProcessMemory (DbgProcess, scan, &code, sizeof(code), NULL) && + if (SafeReadMemory (scan, &code, sizeof(code)) && code >= codeStart && code < codeEnd) { static const char regNames[][4] = @@ -737,18 +821,18 @@ static void StackWalk (HANDLE file, void *dumpaddress, DWORD *topOfStack) const BYTE *bytep = (BYTE *)code; BYTE peekb; -#define chkbyte(x,m,v) ReadProcessMemory(DbgProcess, x, &peekb, 1, NULL) && ((peekb & m) == v) +#define chkbyte(x,m,v) SafeReadMemory(x, &peekb, 1) && ((peekb & m) == v) if (chkbyte(bytep - 5, 0xFF, 0xE8) || chkbyte(bytep - 5, 0xFF, 0xE9)) { DWORD peekd; - if (ReadProcessMemory (DbgProcess, bytep - 4, &peekd, 4, NULL)) + if (SafeReadMemory (bytep - 4, &peekd, 4)) { DWORD_PTR jumpaddr = peekd + code; Writef (file, "\r\n %p %s %p", code - 5, peekb == 0xE9 ? "jmp " : "call", jumpaddr); if (chkbyte((LPCVOID)jumpaddr, 0xFF, 0xE9) && - ReadProcessMemory (DbgProcess, (LPCVOID)(jumpaddr + 1), &peekd, 4, NULL)) + SafeReadMemory ((LPCVOID)(jumpaddr + 1), &peekd, 4)) { Writef (file," => jmp %p", peekd + jumpaddr + 5); } @@ -785,7 +869,7 @@ static void StackWalk (HANDLE file, void *dumpaddress, DWORD *topOfStack) Writef (file, "\r\n %08x", bytep - i); bytep -= i - 1; - ReadProcessMemory (DbgProcess, bytep, &peekb, 1, NULL); + SafeReadMemory (bytep, &peekb, 1); mod = peekb >> 6; rm = peekb & 7; bytep++; @@ -798,7 +882,7 @@ static void StackWalk (HANDLE file, void *dumpaddress, DWORD *topOfStack) { int index, base; - ReadProcessMemory (DbgProcess, bytep, &peekb, 1, NULL); + SafeReadMemory (bytep, &peekb, 1); scale = 1 << (peekb >> 6); index = (peekb >> 3) & 7; base = peekb & 7; @@ -832,12 +916,12 @@ static void StackWalk (HANDLE file, void *dumpaddress, DWORD *topOfStack) if (mod == 1) { signed char miniofs; - ReadProcessMemory (DbgProcess, bytep++, &miniofs, 1, NULL); + SafeReadMemory (bytep++, &miniofs, 1); offset = miniofs; } else { - ReadProcessMemory (DbgProcess, bytep, &offset, 4, NULL); + SafeReadMemory (bytep, &offset, 4); bytep += 4; } if ((DWORD_PTR)bytep == code) @@ -905,7 +989,7 @@ static void DumpBytes (HANDLE file, BYTE *address) { line_p += sprintf (line_p, "\r\n%p:", address); } - if (ReadProcessMemory (DbgProcess, address, &peek, 1, NULL)) + if (SafeReadMemory (address, &peek, 1)) { line_p += sprintf (line_p, " %02x", *address); } @@ -989,40 +1073,44 @@ static void CloseTarFiles () // WriteBlock // // This is a wrapper around WriteFile. If stream is non-NULL, then the data -// is compressed before writing it to the file. outbuf must be 512 bytes. +// is compressed before writing it to the file. outbuf must be 1024 bytes. // //========================================================================== -static void WriteBlock (HANDLE file, LPCVOID buffer, DWORD bytes, z_stream *stream, Bytef *outbuf, DWORD *crc) +static DWORD WriteBlock (HANDLE file, LPCVOID buffer, DWORD bytes, z_stream *stream, Bytef *outbuf) { if (stream == NULL) { WriteFile (file, buffer, bytes, &bytes, NULL); + return bytes; } else { + DWORD wrote = 0; + stream->next_in = (Bytef *)buffer; stream->avail_in = bytes; - *crc = crc32 (*crc, (const Bytef *)buffer, bytes); while (stream->avail_in != 0) { if (stream->avail_out == 0) { stream->next_out = outbuf; - stream->avail_out = sizeof(tar::record); - WriteFile (file, outbuf, sizeof(tar::record), &bytes, NULL); + stream->avail_out = 1024; + wrote += 1024; + WriteFile (file, outbuf, 1024, &bytes, NULL); } deflate (stream, Z_NO_FLUSH); } + return wrote; } } //========================================================================== // -// WriteTar +// WriteZip // -// Writes a .tar.gz file. If deflateInit fails, then ".gz" the gzip +// Writes a .zip/pk3 file. If deflateInit fails, then ".gz" the gzip // argument will be false, otherwise it will be set true. A HANDLE to the // file is returned. This file is delete-on-close. // @@ -1031,27 +1119,120 @@ static void WriteBlock (HANDLE file, LPCVOID buffer, DWORD bytes, z_stream *stre // //========================================================================== -static HANDLE MakeTar (bool &gzip) +static HANDLE MakeZip () { - DWORD len; - unsigned char gzipheader[10]; - tar::record record; - Bytef outbuf[sizeof(record)]; + zip::CentralDirectoryEntry central = { ZIP_CENTRALFILE, { 20, 0 }, { 20, 0 }, }; + zip::EndOfCentralDirectory dirend = { ZIP_ENDOFDIR, }; + short dosdate, dostime; time_t now; - int i, j, sum; + struct tm *nowtm; + int i, numfiles; HANDLE file; - DWORD filesize, k; - DWORD totalsize; - z_stream stream; - int err; - DWORD crc; + DWORD len, dirsize; if (NumFiles == 0) { return INVALID_HANDLE_VALUE; } + // Open the zip file. + file = CreateTempFile (); + if (file == INVALID_HANDLE_VALUE) + { + return file; + } + time (&now); + nowtm = localtime (&now); + + if (nowtm == NULL || nowtm->tm_year < 80) + { + dosdate = dostime = 0; + } + else + { + dosdate = (nowtm->tm_year - 80) * 512 + (nowtm->tm_mon + 1) * 32 + nowtm->tm_mday; + dostime = nowtm->tm_hour * 2048 + nowtm->tm_min * 32 + nowtm->tm_sec / 2; + dosdate = LittleShort(dosdate); + dostime = LittleShort(dostime); + } + + // Write out the zip archive, one file at a time + for (i = 0; i < NumFiles; ++i) + { + AddZipFile (file, &TarFiles[i], dosdate, dostime); + } + + // Write the central directory + central.ModTime = dostime; + central.ModDate = dosdate; + central.InternalAttributes = LittleShort(1); + + dirend.DirectoryOffset = LittleLong(SetFilePointer (file, 0, NULL, FILE_CURRENT)); + + for (i = 0, numfiles = 0, dirsize = 0; i < NumFiles; ++i) + { + // Skip empty files + if (TarFiles[i].UncompressedSize == 0) + { + continue; + } + numfiles++; + if (TarFiles[i].Deflated) + { + central.Flags = LittleShort(2); + central.Method = LittleShort(8); + } + else + { + central.Flags = 0; + central.Method = 0; + } + central.CRC32 = LittleLong(TarFiles[i].CRC32); + central.CompressedSize = LittleLong(TarFiles[i].CompressedSize); + central.UncompressedSize = LittleLong(TarFiles[i].UncompressedSize); + central.NameLength = LittleShort((WORD)strlen(TarFiles[i].Filename)); + central.LocalHeaderOffset = LittleLong(TarFiles[i].ZipOffset); + WriteFile (file, ¢ral, sizeof(central), &len, NULL); + WriteFile (file, TarFiles[i].Filename, (DWORD)strlen(TarFiles[i].Filename), &len, NULL); + dirsize += sizeof(central) + len; + } + + // Write the directory terminator + dirend.NumEntriesOnAllDisks = dirend.NumEntries = LittleShort(numfiles); + dirend.DirectorySize = LittleLong(dirsize); + WriteFile (file, &dirend, sizeof(dirend), &len, NULL); + + return file; +} + +//========================================================================== +// +// AddZipFile +// +// Adds a file to the opened zip. +// +//========================================================================== + +static void AddZipFile (HANDLE ziphandle, TarFile *whichfile, short dosdate, short dostime) +{ + zip::LocalFileHeader local = { ZIP_LOCALFILE, }; + Bytef outbuf[1024], inbuf[1024]; + z_stream stream; + DWORD wrote, len, k; + int err; + bool gzip; + + whichfile->UncompressedSize = GetFileSize (whichfile->File, NULL); + whichfile->CompressedSize = 0; + whichfile->ZipOffset = 0; + whichfile->Deflated = false; + + // Skip empty files. + if (whichfile->UncompressedSize == 0) + { + return; + } stream.next_in = Z_NULL; stream.avail_in = 0; @@ -1061,89 +1242,42 @@ static HANDLE MakeTar (bool &gzip) 8, Z_DEFAULT_STRATEGY); gzip = err == Z_OK; - // Open the tar file. - file = CreateTempFile (); - if (file == INVALID_HANDLE_VALUE) + if (gzip) { - if (gzip) - { - deflateEnd (&stream); - } - return file; - } - - if (gzip = (err == Z_OK)) - { - // Write GZIP header - memcpy (gzipheader, "\x1F\x8B\x08\x00 \x02\x0B", 10); - memcpy (gzipheader+4, &now, 4); - WriteFile (file, gzipheader, 10, &len, NULL); - crc = crc32 (0, NULL, 0); + local.Method = LittleShort(8); + whichfile->Deflated = true; stream.next_out = outbuf; stream.avail_out = sizeof(outbuf); } - // Write out the tar archive, one file at a time - for (i = 0, totalsize = 0; i < NumFiles; ++i) + // Write out the header and filename. + local.VersionToExtract[0] = 20; + local.Flags = gzip ? LittleShort(2) : 0; + local.ModTime = dostime; + local.ModDate = dosdate; + local.UncompressedSize = LittleLong(whichfile->UncompressedSize); + local.NameLength = LittleShort((WORD)strlen(whichfile->Filename)); + + whichfile->ZipOffset = SetFilePointer (ziphandle, 0, NULL, FILE_CURRENT); + WriteFile (ziphandle, &local, sizeof(local), &wrote, NULL); + WriteFile (ziphandle, whichfile->Filename, (DWORD)strlen(whichfile->Filename), &wrote, NULL); + + // Write the file itself and calculate its CRC. + SetFilePointer (whichfile->File, 0, NULL, FILE_BEGIN); + for (k = 0; k < whichfile->UncompressedSize; ) { - // Fill in header information - filesize = GetFileSize (TarFiles[i].File, NULL); - - // Skip empty files - if (filesize == 0) + len = whichfile->UncompressedSize - k; + if (len > 1024) { - continue; - } - - memset (&record, 0, sizeof(record)); - strcpy (record.header.name, TarFiles[i].Filename); - strcpy (record.header.mode, "000666 "); - strcpy (record.header.uid, " 0 "); - strcpy (record.header.gid, " 0 "); - strcpy (record.header.magic, "ustar "); - strcpy (record.header.uname, "zdoom"); - strcpy (record.header.gname, "games"); - sprintf(record.header.size, "%11lo ", filesize); - sprintf(record.header.mtime, "%11lo ", now); - record.header.linkflag = '0'; - - // Calculate checksum - memcpy (record.header.chksum, " ", 8); - for (j = 0, sum = 0; j < (int)sizeof(record); ++j) - { - sum += record.charptr[j]; - } - sprintf (record.header.chksum, "%6o", sum); - - // Write out the header and then the file (padded to 512 bytes) - WriteBlock (file, &record, sizeof(record), gzip ? &stream : NULL, outbuf, &crc); - totalsize += sizeof(record); - - SetFilePointer (TarFiles[i].File, 0, NULL, FILE_BEGIN); - for (k = 0; k < filesize; k += 512) - { - len = filesize - k; - if (len > sizeof(record)) - { - len = sizeof(record); - } - else - { - memset (&record.charptr[len], 0, sizeof(record)-len); - } - ReadFile (TarFiles[i].File, &record, len, &len, NULL); - WriteBlock (file, &record, sizeof(record), gzip ? &stream : NULL, outbuf, &crc); - totalsize += sizeof(record); + len = 1024; } + k += len; + ReadFile (whichfile->File, &inbuf, len, &len, NULL); + whichfile->CRC32 = crc32 (whichfile->CRC32, inbuf, len); + whichfile->CompressedSize += WriteBlock (ziphandle, inbuf, len, gzip ? &stream : NULL, outbuf); } - // EOT is two zeroed blocks, according to GNU tar. - memset (&record, 0, sizeof(record)); - WriteBlock (file, &record, sizeof(record), gzip ? &stream : NULL, outbuf, &crc); - WriteBlock (file, &record, sizeof(record), gzip ? &stream : NULL, outbuf, &crc); - totalsize += 2*sizeof(record); - - // Flush the zlib stream buffer and write the gzip epilogue + // Flush the zlib stream buffer. if (gzip) { for (bool done = false;;) @@ -1151,7 +1285,8 @@ static HANDLE MakeTar (bool &gzip) len = sizeof(outbuf) - stream.avail_out; if (len != 0) { - WriteFile (file, outbuf, len, &len, NULL); + whichfile->CompressedSize += len; + WriteFile (ziphandle, outbuf, len, &wrote, NULL); stream.next_out = outbuf; stream.avail_out = sizeof(outbuf); } @@ -1167,14 +1302,17 @@ static HANDLE MakeTar (bool &gzip) } } deflateEnd (&stream); - memcpy (gzipheader, &crc, 4); - memcpy (gzipheader+4, &totalsize, 4); - WriteFile (file, gzipheader, 8, &len, NULL); } - return file; -} -static WNDPROC StdStaticProc; + // Fill in fields we didn't know when we wrote the local header. + SetFilePointer (ziphandle, whichfile->ZipOffset + 14, NULL, FILE_BEGIN); + k = LittleLong(whichfile->CRC32); + WriteFile (ziphandle, &k, 4, &wrote, NULL); + k = LittleLong(whichfile->CompressedSize); + WriteFile (ziphandle, &k, 4, &wrote, NULL); + + SetFilePointer (ziphandle, 0, NULL, FILE_END); +} //========================================================================== // @@ -2301,7 +2439,6 @@ struct UploadParmStruct HWND hDlg; HANDLE hThread; HANDLE hFile; - bool gzipped; const char *UserSummary; }; @@ -2394,10 +2531,6 @@ static DWORD WINAPI UploadProc (LPVOID lpParam) sizeof(MultipartBinaryHeader)-1 + sizeof(MultipartHeaderGZip)-1 + fileLen + sizeof(MultipartFooter)-1; - if (!parm->gzipped) - { - contentLength -= sizeof(MultipartHeaderGZip) - sizeof(MultipartHeaderNoGZip); - } headerLen = sprintf (xferbuf, PostHeader, contentLength); bytesSent = send (sock, xferbuf, headerLen, 0); if (bytesSent != headerLen) @@ -2463,8 +2596,7 @@ static DWORD WINAPI UploadProc (LPVOID lpParam) } // Send the report file. - headerLen = sprintf (xferbuf, "%s%s", MultipartBinaryHeader, - parm->gzipped ? MultipartHeaderGZip : MultipartHeaderNoGZip); + headerLen = sprintf (xferbuf, "%s%s", MultipartBinaryHeader, MultipartHeaderZip); bytesSent = send (sock, xferbuf, headerLen, 0); if (bytesSent == SOCKET_ERROR) @@ -2684,9 +2816,9 @@ static BOOL CALLBACK BoingDlgProc (HWND hDlg, UINT message, WPARAM wParam, LPARA // //========================================================================== -static BOOL UploadReport (HANDLE file, bool gzipped) +static BOOL UploadReport (HANDLE file) { - UploadParmStruct uploadParm = { 0, 0, file, gzipped, UserSummary }; + UploadParmStruct uploadParm = { 0, 0, file, UserSummary }; BOOL res = (BOOL)DialogBoxParam (g_hInst, MAKEINTRESOURCE(IDD_BOING), NULL, (DLGPROC)BoingDlgProc, (LPARAM)&uploadParm); if (UserSummary != NULL) { @@ -2706,7 +2838,7 @@ static BOOL UploadReport (HANDLE file, bool gzipped) // //========================================================================== -static void SaveReport (HANDLE file, bool gzipped) +static void SaveReport (HANDLE file) { OPENFILENAME ofn = { #ifdef OPENFILENAME_SIZE_VERSION_400 @@ -2717,16 +2849,8 @@ static void SaveReport (HANDLE file, bool gzipped) , }; char filename[256]; - if (gzipped) - { - ofn.lpstrFilter = "GZipped Tarball (*.tar.gz)\0*.tar.gz\0"; - strcpy (filename, "CrashReport.tar.gz"); - } - else - { - ofn.lpstrFilter = "Tarball (*.tar)\0*.tar\0"; - strcpy (filename, "CrashReport.tar"); - } + ofn.lpstrFilter = "Zip file (*.pk3)\0*.pk3\0"; + strcpy (filename, "CrashReport.pk3"); ofn.lpstrFile = filename; ofn.nMaxFile = sizeof(filename); @@ -2780,7 +2904,6 @@ void DisplayCrashLog () { HINSTANCE riched; HANDLE file; - bool gzipped = false; if (NumFiles == 0 || (riched = LoadLibrary ("riched20.dll")) == NULL) { @@ -2805,16 +2928,16 @@ void DisplayCrashLog () INT_PTR result = DialogBox (g_hInst, MAKEINTRESOURCE(IDD_CRASHDIALOG), NULL, (DLGPROC)CrashDlgProc); if (result == IDYES) { -#if 0 // Server-side support is not done yet - file = MakeTar (gzipped); - UploadReport (file, gzipped); +#if 0 // Server-side support is not done yet because I am too lazy. + file = MakeZip (); + UploadReport (file); CloseHandle (file); #endif } else if (result == IDC_SAVEREPORT) { - file = MakeTar (gzipped); - SaveReport (file, gzipped); + file = MakeZip (); + SaveReport (file); CloseHandle (file); } FreeLibrary (riched); diff --git a/src/win32/i_main.cpp b/src/win32/i_main.cpp index 410d2d857a..74444512de 100644 --- a/src/win32/i_main.cpp +++ b/src/win32/i_main.cpp @@ -67,11 +67,9 @@ LRESULT CALLBACK WndProc (HWND, UINT, WPARAM, LPARAM); -void CreateCrashLog (HANDLE process, DWORD processID, DWORD threadID, LPEXCEPTION_POINTERS exceptionRecord, char *custominfo, DWORD customsize); +void CreateCrashLog (char *custominfo, DWORD customsize); extern void DisplayCrashLog (); extern EXCEPTION_POINTERS CrashPointers; -extern EXCEPTION_RECORD MyExceptionRecord; -extern CONTEXT MyContextRecord; // Will this work with something besides VC++? // Answer: yes it will. @@ -479,15 +477,7 @@ LONG WINAPI ExitMessedUp (LPEXCEPTION_POINTERS foo) ExitProcess (1000); } -void CALLBACK TimeToDie (ULONG_PTR dummy) -{ - SetUnhandledExceptionFilter (ExitMessedUp); - I_ShutdownHardware (); - SetWindowPos (Window, NULL, 0, 0, 0, 0, SWP_HIDEWINDOW); - exit (-1); -} - -void CALLBACK SpawnFailed (ULONG_PTR dummy) +void CALLBACK ExitFatally (ULONG_PTR dummy) { SetUnhandledExceptionFilter (ExitMessedUp); I_ShutdownHardware (); @@ -496,30 +486,8 @@ void CALLBACK SpawnFailed (ULONG_PTR dummy) exit (-1); } -void GetInfo (PEXCEPTION_POINTERS dst, PEXCEPTION_POINTERS src) -{ - dst->ExceptionRecord = &MyExceptionRecord; - dst->ContextRecord = &MyContextRecord; - MyExceptionRecord = *src->ExceptionRecord; - MyContextRecord = *src->ContextRecord; -} - LONG WINAPI CatchAllExceptions (LPEXCEPTION_POINTERS info) { - static bool caughtsomething = false; - - if (caughtsomething) return EXCEPTION_EXECUTE_HANDLER; - caughtsomething = true; - - char *custominfo = (char *)HeapAlloc (GetProcessHeap(), 0, 16384); - PAPCFUNC apcfunc; - char cmdline[128]; - char modname[_MAX_PATH]; - STARTUPINFO startup = { sizeof(startup) }; - PROCESS_INFORMATION newproc; - - GetInfo (&CrashPointers, info); - #ifdef _DEBUG if (info->ExceptionRecord->ExceptionCode == EXCEPTION_BREAKPOINT) { @@ -527,41 +495,27 @@ LONG WINAPI CatchAllExceptions (LPEXCEPTION_POINTERS info) } #endif + static bool caughtsomething = false; + + if (caughtsomething) return EXCEPTION_EXECUTE_HANDLER; + caughtsomething = true; + + char *custominfo = (char *)HeapAlloc (GetProcessHeap(), 0, 16384); + CrashPointers = *info; DoomSpecificInfo (custominfo); - - // Spawn off a new copy of ourself to handle the information gathering and reporting. - wsprintf (cmdline, "IDidCrash:OuchieBooboo %x %x %p %p %lx", GetCurrentProcessId(), - GetCurrentThreadId(), &CrashPointers, custominfo, strlen(custominfo)); - GetModuleFileName (NULL, modname, _MAX_PATH); - modname[_MAX_PATH-1] = 0; - if (!CreateProcess (modname, cmdline, NULL, NULL, FALSE, 0, NULL, NULL, &startup, &newproc)) - { - // CreateProcess failed. We'll have to do everything ourself! - CreateCrashLog (GetCurrentProcess(), GetCurrentProcessId(), GetCurrentThreadId(), &CrashPointers, custominfo, (DWORD)strlen(custominfo)); - apcfunc = SpawnFailed; - QueueUserAPC (SpawnFailed, MainThread, 0); - } - else - { - // Give our new debugger process a chance to get up and running and attach - // itself to us. Calling SuspendThread (so that we wait only as long as actually - // needed) does not work, because it interferes with the receipt of the - // exception information. I'm not sure why. - Sleep (1000); - apcfunc = TimeToDie; - } + CreateCrashLog (custominfo, (DWORD)strlen(custominfo)); // If the main thread crashed, then make it clean up after itself. // Otherwise, put the crashing thread to sleep and signal the main thread to clean up. if (GetCurrentThreadId() == MainThreadID) { - info->ContextRecord->Eip = (DWORD_PTR)apcfunc; + info->ContextRecord->Eip = (DWORD_PTR)ExitFatally; } else { info->ContextRecord->Eip = (DWORD_PTR)SleepForever; - QueueUserAPC (apcfunc, MainThread, 0); + QueueUserAPC (ExitFatally, MainThread, 0); } return EXCEPTION_CONTINUE_EXECUTION; } @@ -572,63 +526,15 @@ int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE nothing, LPSTR cmdline, int n InitCommonControls (); // Be pretty under XP - if (strncmp (GetCommandLine(), "IDidCrash:OuchieBooboo ", 23) == 0 && __argc == 6) - { // We are being run to handle a crash in another instance of ourself. - // argv[1] is the process ID of the instance that crashed. - // argv[2] is the thread ID of the crasher. - // argv[3] is a pointer to the exception information in that process's address space. - // argv[4] is a pointer to a text buffer for ZDoom-specific info. - // argv[5] is the length of argv[4]'s buffer - DWORD pid, tid; - LPEXCEPTION_POINTERS info; - char *info2; - DWORD info2len; - BOOL debugging; - HANDLE debuggee; - DEBUG_EVENT debugev; - - pid = strtoul (__argv[1], NULL, 16); - tid = strtoul (__argv[2], NULL, 16); - sscanf (__argv[3], "%p", &info); - sscanf (__argv[4], "%p", &info2); - info2len = strtoul (__argv[5], NULL, 16); - - // DebugActiveProcess will suspend all threads in the other process, - // so that's why we call it. When it resumes, it will shut down - // as cleanly as it can. - debugging = DebugActiveProcess (pid); -// if (MessageBox (NULL, GetCommandLine(), "foo", MB_OKCANCEL) == IDCANCEL) exit (0); - debuggee = OpenProcess (PROCESS_ALL_ACCESS, FALSE, pid); - CreateCrashLog (debuggee, pid, tid, info, info2, info2len); - if (debugging) - { // Now let the crashing process resume. Process debug events until the process - // has exited. We don't care about the events, we just wanted the debugger status - // so that the process would be suspended while we gathered info on it. - while (WaitForDebugEvent (&debugev, INFINITE)) - { - ContinueDebugEvent(debugev.dwProcessId, debugev.dwThreadId, DBG_CONTINUE); - if (debugev.dwDebugEventCode == EXIT_PROCESS_DEBUG_EVENT) - { - // In this case, ContinueDebugEvent closes the process handle for us, - // so we don't need to (and we must be sure not to reference it again). - break; - } - } - } - DisplayCrashLog (); - exit (0); - } #if !defined(__GNUC__) && defined(_DEBUG) if (__argc == 2 && strcmp (__argv[1], "TestCrash") == 0) { - EXCEPTION_POINTERS info; __try { *(int *)0 = 0; } - __except(GetInfo (&info, GetExceptionInformation()), - CreateCrashLog (GetCurrentProcess(), GetCurrentProcessId(), GetCurrentThreadId(), &info, __argv[1], 9), - EXCEPTION_EXECUTE_HANDLER) + __except(CrashPointers = *GetExceptionInformation(), + CreateCrashLog (__argv[1], 9), EXCEPTION_EXECUTE_HANDLER) { } DisplayCrashLog ();