| /* |
| * Licensed to the Apache Software Foundation (ASF) under one or more |
| * contributor license agreements. See the NOTICE file distributed with |
| * this work for additional information regarding copyright ownership. |
| * The ASF licenses this file to You under the Apache License, Version 2.0 |
| * (the "License"); you may not use this file except in compliance with |
| * the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| #define CDEV_CURRENT_FUNCTION _comment_ |
| /** |
| * @file |
| * @ingroup Port |
| * @brief Dump formatting |
| */ |
| #undef CDEV_CURRENT_FUNCTION |
| |
| #include <windows.h> |
| #include <winnt.h> |
| #include <stdlib.h> |
| #include <float.h> |
| |
| #include <process.h> |
| #include <dbghelp.h> |
| |
| #include "hyport.h" |
| #include "hysignal.h" |
| |
| typedef BOOL (WINAPI * PMINIDUMPWRITEDUMP) (IN HANDLE hProcess, |
| IN DWORD ProcessId, |
| IN HANDLE hFile, |
| IN MINIDUMP_TYPE DumpType, |
| IN CONST |
| PMINIDUMP_EXCEPTION_INFORMATION |
| ExceptionParam, |
| OPTIONAL IN CONST |
| PMINIDUMP_USER_STREAM_INFORMATION |
| UserStreamParam, |
| OPTIONAL IN CONST |
| PMINIDUMP_CALLBACK_INFORMATION |
| CallbackParam OPTIONAL); |
| |
| PMINIDUMPWRITEDUMP dump_fn; |
| |
| #define MINIDUMPWRITEDUMP "MiniDumpWriteDump" |
| #define DBGHELP_DLL "DBGHELP.DLL" |
| |
| typedef struct _WriteDumpFileArgs |
| { |
| PMINIDUMPWRITEDUMP dmp_function; |
| HANDLE hDumpFile; |
| MINIDUMP_EXCEPTION_INFORMATION *mdei; |
| } WriteDumpFileArgs, *PWriteDumpFileArgs; |
| |
| #define HYUSE_UNIQUE_DUMP_NAMES "HYUNIQUE_DUMPS" |
| #define DUMP_FNAME_KEY "SOFTWARE\\Microsoft\\DrWatson" |
| #define DUMP_FNAME_VALUE "CrashDumpFile" |
| |
| #define CDEV_CURRENT_FUNCTION _prototypes_private |
| |
| static HANDLE openFileInCWD (struct HyPortLibrary *portLibrary, |
| char *fileNameBuff, U_32 fileNameBuffSize); |
| |
| static HANDLE openFileFromEnvVar (struct HyPortLibrary *portLibrary, |
| char *envVarName, char *fileNameBuff, |
| U_32 fileNameBuffSize); |
| |
| static HANDLE openFileFromReg (const char *keyName, const char *valName, |
| char *fileNameBuff, U_32 fileNameBuffSize); |
| |
| static void writeDumpFile (PWriteDumpFileArgs args); |
| |
| static HINSTANCE loadDumpLib (const char *dllName); |
| |
| static PMINIDUMPWRITEDUMP linkDumpFn (HINSTANCE dllHandle, |
| const char *fnName); |
| |
| #undef CDEV_CURRENT_FUNCTION |
| |
| #define CDEV_CURRENT_FUNCTION hydump_create |
| /** |
| * Create a dump file of the OS state. |
| * |
| * @param[in] portLibrary The port library. |
| * @param[in] filename Buffer for filename optionally containing the filename where dump is to be output. |
| * @param[out] filename filename used for dump file or error message. |
| * @param[in] dumpType Type of dump to perform. |
| * @param[in] userData Implementation specific data. |
| * |
| * @return 0 on success, non-zero otherwise. |
| * |
| * @note filename buffer can not be NULL. |
| * @note user allocates and frees filename buffer. |
| * @note filename buffer length is platform dependent, assumed to be HyMaxPath/MAX_PATH |
| * |
| * @note if filename buffer is empty, a filename will be generated. |
| * @note if HYUNIQUE_DUMPS is set, filename will be unique. |
| */ |
| UDATA VMCALL |
| hydump_create (struct HyPortLibrary *portLibrary, char *filename, |
| char *dumpType, void *userData) |
| { |
| HANDLE hFile; |
| WriteDumpFileArgs args; |
| HINSTANCE dllHandle; |
| HANDLE hThread; |
| MINIDUMP_EXCEPTION_INFORMATION mdei; |
| EXCEPTION_POINTERS exceptionPointers; |
| HyWin32SignalInfo *info = (HyWin32SignalInfo *) userData; |
| |
| if (filename == NULL) |
| { |
| return 1; |
| } |
| |
| if (*filename == '\0') |
| { |
| /* no file name provided, so generate one and open the file */ |
| |
| hFile = |
| openFileFromReg (DUMP_FNAME_KEY, DUMP_FNAME_VALUE, filename, |
| HyMaxPath); |
| if (hFile == INVALID_HANDLE_VALUE) |
| { |
| hFile = |
| openFileFromEnvVar (portLibrary, "USERPROFILE", filename, |
| HyMaxPath); |
| if (hFile == INVALID_HANDLE_VALUE) |
| { |
| hFile = openFileInCWD (portLibrary, filename, HyMaxPath); |
| if (hFile == INVALID_HANDLE_VALUE) |
| { |
| hFile = |
| openFileFromEnvVar (portLibrary, "TEMP", filename, |
| HyMaxPath); |
| if (hFile == INVALID_HANDLE_VALUE) |
| { |
| hFile = |
| openFileFromEnvVar (portLibrary, "TMP", filename, |
| HyMaxPath); |
| } |
| } |
| } |
| } |
| } |
| else |
| { |
| /* use name provided */ |
| /* 0666 is a guess...0?, 0666??...want to grant write permissions */ |
| hFile = |
| (HANDLE) portLibrary->file_open (portLibrary, filename, |
| HyOpenWrite | HyOpenCreate | |
| HyOpenRead, 0666); |
| } |
| |
| if (hFile == (HANDLE) - 1) |
| { |
| /*make sure useful */ |
| portLibrary->str_printf (portLibrary, filename, HyMaxPath, |
| "Dump failed - could not create dump file:\n"); |
| return 1; |
| } |
| |
| /* Try to load JRE minidump module before the system one */ |
| dllHandle = loadDumpLib ("..\\bin\\" DBGHELP_DLL); |
| |
| dllHandle = loadDumpLib (DBGHELP_DLL); |
| |
| dump_fn = linkDumpFn (dllHandle, MINIDUMPWRITEDUMP); |
| |
| if (dump_fn == NULL) |
| { |
| portLibrary->str_printf (portLibrary, filename, HyMaxPath, |
| "Dump failed - could not link %s in %s\n", |
| MINIDUMPWRITEDUMP, DBGHELP_DLL); |
| return 1; |
| } |
| |
| /* collect exception data */ |
| if (info) |
| { |
| exceptionPointers.ExceptionRecord = info->ExceptionRecord; |
| exceptionPointers.ContextRecord = info->ContextRecord; |
| mdei.ThreadId = GetCurrentThreadId (); |
| mdei.ExceptionPointers = &exceptionPointers; |
| mdei.ClientPointers = TRUE; |
| } |
| |
| args.dmp_function = dump_fn; |
| args.hDumpFile = hFile; |
| args.mdei = info ? &mdei : NULL; |
| |
| /* call createCrashDumpFile on a different thread so we can walk the stack back to our code */ |
| hThread = (HANDLE) _beginthread (writeDumpFile, 0, &args); |
| |
| /* exception thread waits while crash dump is printed on other thread */ |
| if ((HANDLE) - 1 == hThread) |
| { |
| portLibrary->str_printf (portLibrary, filename, HyMaxPath, |
| "Dump failed - could not begin dump thread\n"); |
| return 1; |
| } |
| else |
| { |
| WaitForSingleObject (hThread, INFINITE); |
| } |
| |
| if ((portLibrary->file_close (portLibrary, (I_32) hFile)) == -1) |
| { |
| /*make sure useful */ |
| portLibrary->str_printf (portLibrary, filename, HyMaxPath, |
| "Dump failed - could not close dump file.\n"); |
| return 1; |
| } |
| return 0; |
| } |
| |
| #undef CDEV_CURRENT_FUNCTION |
| |
| #define CDEV_CURRENT_FUNCTION linkDumpFn |
| static PMINIDUMPWRITEDUMP |
| linkDumpFn (HINSTANCE dllHandle, const char *fnName) |
| { |
| return (PMINIDUMPWRITEDUMP) GetProcAddress (dllHandle, fnName); |
| } |
| |
| #undef CDEV_CURRENT_FUNCTION |
| |
| #define CDEV_CURRENT_FUNCTION loadDumpLib |
| /* will remove private from name once duplicate functionality removed from GPHandler */ |
| static HINSTANCE |
| loadDumpLib (const char *dllName) |
| { |
| HINSTANCE dbghelpDll = GetModuleHandle (dllName); |
| |
| if (dbghelpDll == NULL) |
| { |
| dbghelpDll = LoadLibrary (dllName); |
| } |
| |
| return dbghelpDll; |
| } |
| |
| #undef CDEV_CURRENT_FUNCTION |
| |
| #define CDEV_CURRENT_FUNCTION writeDumpFile |
| static void |
| writeDumpFile (PWriteDumpFileArgs args) |
| { |
| #define MAX_MINIDUMP_ATTEMPTS 10 |
| |
| HANDLE hFile = args->hDumpFile; |
| int i; |
| |
| for (i = 0; i < MAX_MINIDUMP_ATTEMPTS; i++) |
| { |
| BOOL ok = args->dmp_function (GetCurrentProcess (), |
| GetCurrentProcessId (), |
| hFile, |
| 2, |
| args->mdei, |
| NULL, |
| NULL); |
| |
| if (ok) |
| { |
| break; /* dump taken! */ |
| } |
| else |
| { |
| Sleep (100); |
| } |
| } |
| |
| _endthread (); |
| } |
| |
| #undef CDEV_CURRENT_FUNCTION |
| |
| #define CDEV_CURRENT_FUNCTION openFileFromEnvVar |
| static HANDLE |
| openFileFromEnvVar (struct HyPortLibrary *portLibrary, char *envVarName, |
| char *fileNameBuff, U_32 fileNameBuffSize) |
| { |
| HANDLE hFile; |
| I_32 retCode; |
| U_32 i; |
| char pidStore[25]; /* This is roughly the 3 * sizeof(int)+1 which is more than enough space */ |
| |
| if (portLibrary == NULL) |
| { |
| return INVALID_HANDLE_VALUE; |
| } |
| |
| retCode = |
| portLibrary->sysinfo_get_env (portLibrary, envVarName, fileNameBuff, |
| fileNameBuffSize); |
| if (retCode != 0) |
| { |
| return INVALID_HANDLE_VALUE; |
| } |
| |
| retCode = |
| portLibrary->sysinfo_get_env (portLibrary, HYUSE_UNIQUE_DUMP_NAMES, NULL, |
| 0); |
| if (-1 != retCode) |
| { |
| pidStore[0] = '-'; |
| /* this env var is set to append a unique identifier to the dump file name */ |
| _itoa (GetCurrentProcessId (), &pidStore[1], 10); |
| } |
| else |
| { |
| /* we aren't using this feature so make this a zero-length string */ |
| pidStore[0] = 0; |
| } |
| |
| i = strlen (fileNameBuff); |
| i += strlen (pidStore); |
| if (i + 8 > fileNameBuffSize) |
| { |
| return INVALID_HANDLE_VALUE; |
| } |
| strcat (fileNameBuff, "\\hy"); |
| strcat (fileNameBuff, pidStore); |
| strcat (fileNameBuff, ".dmp"); |
| |
| hFile = CreateFileA (fileNameBuff, |
| GENERIC_READ | GENERIC_WRITE, |
| 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); |
| |
| return hFile; |
| } |
| |
| #undef CDEV_CURRENT_FUNCTION |
| |
| #define CDEV_CURRENT_FUNCTION openFileFromReg |
| static HANDLE |
| openFileFromReg (const char *keyName, const char *valName, char *fileNameBuff, |
| U_32 fileNameBuffSize) |
| { |
| HANDLE hFile; |
| HKEY hKey; |
| LONG lRet; |
| U_32 buffUsed = fileNameBuffSize; |
| |
| lRet = RegOpenKeyEx (HKEY_LOCAL_MACHINE, |
| keyName, 0, KEY_QUERY_VALUE, &hKey); |
| if (lRet != ERROR_SUCCESS) |
| { |
| return INVALID_HANDLE_VALUE; |
| } |
| |
| lRet = RegQueryValueEx (hKey, |
| valName, |
| NULL, NULL, (LPBYTE) fileNameBuff, &buffUsed); |
| if (lRet != ERROR_SUCCESS) |
| { |
| return INVALID_HANDLE_VALUE; |
| } |
| |
| RegCloseKey (hKey); |
| |
| hFile = CreateFileA (fileNameBuff, |
| GENERIC_READ | GENERIC_WRITE, |
| 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); |
| |
| return hFile; |
| } |
| |
| #undef CDEV_CURRENT_FUNCTION |
| |
| #define CDEV_CURRENT_FUNCTION openFileInCWD |
| static HANDLE |
| openFileInCWD (struct HyPortLibrary *portLibrary, char *fileNameBuff, |
| U_32 fileNameBuffSize) |
| { |
| HANDLE hFile; |
| U_32 length; |
| |
| if (portLibrary == NULL) |
| { |
| return INVALID_HANDLE_VALUE; |
| } |
| |
| length = GetModuleFileName (NULL, fileNameBuff, fileNameBuffSize); |
| if (length == 0 || length > fileNameBuffSize || length < 3) |
| { |
| return INVALID_HANDLE_VALUE; |
| } |
| fileNameBuff[length - 1] = 'p'; |
| fileNameBuff[length - 2] = 'm'; |
| fileNameBuff[length - 3] = 'd'; |
| |
| hFile = CreateFileA (fileNameBuff, |
| GENERIC_READ | GENERIC_WRITE, |
| 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); |
| |
| return hFile; |
| } |
| |
| #undef CDEV_CURRENT_FUNCTION |