blob: d1a99636832426b027a28f3acd90c191c11cd149 [file] [log] [blame]
/*
* 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