kern_return_t result = ::mach_port_deallocate(mach_task_self(), m_task);
if (result != KERN_SUCCESS)
{
- printf_error("~CrashInfo: mach_port_deallocate FAILED %x %s\n", result, mach_error_string(result));
+ printf_error("Internal error: mach_port_deallocate FAILED %s (%x)\n", mach_error_string(result), result);
}
}
#endif
return true;
}
+static const char*
+GetHResultString(HRESULT hr)
+{
+ switch (hr)
+ {
+ case E_FAIL:
+ return "The operation has failed";
+ case E_INVALIDARG:
+ return "Invalid argument";
+ case E_OUTOFMEMORY:
+ return "Out of memory";
+ case CORDBG_E_UNCOMPATIBLE_PLATFORMS:
+ return "The operation failed because debuggee and debugger are on incompatible platforms";
+ case CORDBG_E_MISSING_DEBUGGER_EXPORTS:
+ return "The debuggee memory space does not have the expected debugging export table";
+ case CORDBG_E_UNSUPPORTED:
+ return "The specified action is unsupported by this version of the runtime";
+ }
+ return "";
+}
+
//
// Enumerate all the memory regions using the DAC memory region support given a minidump type
//
m_hdac = LoadLibraryA(dacPath.c_str());
if (m_hdac == nullptr)
{
- printf_error("LoadLibraryA(%s) FAILED %d\n", dacPath.c_str(), GetLastError());
+ printf_error("InitializeDAC: LoadLibraryA(%s) FAILED %s\n", dacPath.c_str(), GetLastErrorString().c_str());
goto exit;
}
pfnCLRDataCreateInstance = (PFN_CLRDataCreateInstance)GetProcAddress(m_hdac, "CLRDataCreateInstance");
if (pfnCLRDataCreateInstance == nullptr)
{
- printf_error("GetProcAddress(CLRDataCreateInstance) FAILED %d\n", GetLastError());
+ printf_error("InitializeDAC: GetProcAddress(CLRDataCreateInstance) FAILED %s\n", GetLastErrorString().c_str());
goto exit;
}
hr = pfnCLRDataCreateInstance(__uuidof(ICLRDataEnumMemoryRegions), dataTarget, (void**)&m_pClrDataEnumRegions);
if (FAILED(hr))
{
- printf_error("CLRDataCreateInstance(ICLRDataEnumMemoryRegions) FAILED %08x\n", hr);
+ printf_error("InitializeDAC: CLRDataCreateInstance(ICLRDataEnumMemoryRegions) FAILED %s (%08x)\n", GetHResultString(hr), hr);
goto exit;
}
hr = pfnCLRDataCreateInstance(__uuidof(IXCLRDataProcess), dataTarget, (void**)&m_pClrDataProcess);
if (FAILED(hr))
{
- printf_error("CLRDataCreateInstance(IXCLRDataProcess) FAILED %08x\n", hr);
+ printf_error("InitializeDAC: CLRDataCreateInstance(IXCLRDataProcess) FAILED %s (%08x)\n", GetHResultString(hr), hr);
goto exit;
}
}
else
{
- TRACE("InitializeDAC: coreclr not found; not using DAC\n");
+ printf_error("InitializeDAC: coreclr not found; not using DAC\n");
}
result = true;
exit:
HRESULT hr = m_pClrDataEnumRegions->EnumMemoryRegions(this, minidumpType, CLRDATA_ENUM_MEM_DEFAULT);
if (FAILED(hr))
{
- printf_error("EnumMemoryRegions FAILED %08x\n", hr);
+ printf_error("EnumMemoryRegions FAILED %s (%08x)\n", GetHResultString(hr), hr);
return false;
}
TRACE("EnumerateMemoryRegionsWithDAC: Memory enumeration FINISHED\n");
TRACE("EnumerateManagedModules: Module enumeration STARTED\n");
if (FAILED(hr = m_pClrDataProcess->StartEnumModules(&enumModules))) {
- printf_error("StartEnumModules FAILED %08x\n", hr);
+ printf_error("StartEnumModules FAILED %s (%08x)\n", GetHResultString(hr), hr);
return false;
}
#include "createdump.h"
+int g_readProcessMemoryResult = KERN_SUCCESS;
+
bool
CrashInfo::Initialize()
{
kern_return_t result = ::task_for_pid(mach_task_self(), m_pid, &m_task);
if (result != KERN_SUCCESS)
{
- printf_error("task_for_pid(%d) FAILED %x %s\n", m_pid, result, mach_error_string(result));
+ // Regardless of the reason (invalid process id or invalid signing/entitlements) it always returns KERN_FAILURE (5)
+ printf_error("Invalid process id: task_for_pid(%d) FAILED %s (%x)\n"
+ "This failure may be because createdump or the application is not properly signed and entitled.\n",
+ m_pid, mach_error_string(result), result);
return false;
}
return true;
kern_return_t result = ::task_suspend(Task());
if (result != KERN_SUCCESS)
{
- printf_error("task_suspend(%d) FAILED %x %s\n", m_pid, result, mach_error_string(result));
+ printf_error("Problem suspending process: task_suspend(%d) FAILED %s (%x)\n", m_pid, mach_error_string(result), result);
return false;
}
result = ::task_threads(Task(), &threadList, &threadCount);
if (result != KERN_SUCCESS)
{
- printf_error("task_threads(%d) FAILED %x %s\n", m_pid, result, mach_error_string(result));
+ printf_error("Problem enumerating threads: task_threads(%d) FAILED %s (%x)\n", m_pid, mach_error_string(result), result);
return false;
}
result = ::thread_info(threadList[i], THREAD_IDENTIFIER_INFO, (thread_info_t)&tident, &tident_count);
if (result != KERN_SUCCESS)
{
- TRACE("%d thread_info(%x) FAILED %x %s\n", i, threadList[i], result, mach_error_string(result));
+ TRACE("%d thread_info(%x) FAILED %s (%x)\n", i, threadList[i], mach_error_string(result), result);
tid = (int)threadList[i];
}
else
if (result != KERN_SUCCESS) {
// Iteration can be ended on a KERN_INVALID_ADDRESS
// Allow other kernel errors to continue too so we can get at least part of a dump
- TRACE("mach_vm_region_recurse for address %016llx %08llx FAILED %x %s\n", address, size, result, mach_error_string(result));
+ TRACE("mach_vm_region_recurse for address %016llx %08llx FAILED %s (%x)\n", address, size, mach_error_string(result), result);
break;
}
TRACE_VERBOSE("%016llx - %016llx (%06llx) %08llx %s %d %d %d %c%c%c %02x\n",
kern_return_t result = ::vm_read_overwrite(Task(), addressAligned, PAGE_SIZE, (vm_address_t)data, &bytesRead);
if (result != KERN_SUCCESS || bytesRead != PAGE_SIZE)
{
- TRACE_VERBOSE("ReadProcessMemory(%p %d): vm_read_overwrite failed bytesLeft %d bytesRead %d from %p: %x %s\n",
- address, size, bytesLeft, bytesRead, (void*)addressAligned, result, mach_error_string(result));
+ g_readProcessMemoryResult = result;
+ TRACE_VERBOSE("ReadProcessMemory(%p %d): vm_read_overwrite failed bytesLeft %d bytesRead %d from %p: %s (%x)\n",
+ address, size, bytesLeft, bytesRead, (void*)addressAligned, mach_error_string(result), result);
break;
}
ssize_t bytesToCopy = PAGE_SIZE - offset;
#include "createdump.h"
+int g_readProcessMemoryErrno = 0;
+
bool GetStatus(pid_t pid, pid_t* ppid, pid_t* tgid, std::string* name);
bool
m_fd = open(memPath, O_RDONLY);
if (m_fd == -1)
{
- printf_error("open(%s) FAILED %d (%s)\n", memPath, errno, strerror(errno));
+ int err = errno;
+ const char* message = "Problem accessing memory";
+ if (err == EPERM || err == EACCES)
+ {
+ message = "The process or container does not have permissions or access";
+ }
+ else if (err == ENOENT)
+ {
+ message = "Invalid process id";
+ }
+ printf_error("%s: open(%s) FAILED %s (%d)\n", message, memPath, strerror(err), err);
return false;
}
// Get the process info
DIR* taskDir = opendir(taskPath);
if (taskDir == nullptr)
{
- printf_error("opendir(%s) FAILED %s\n", taskPath, strerror(errno));
+ printf_error("Problem enumerating threads: opendir(%s) FAILED %s (%d)\n", taskPath, strerror(errno), errno);
return false;
}
}
else
{
- printf_error("ptrace(ATTACH, %d) FAILED %s\n", tid, strerror(errno));
+ printf_error("Problem suspending threads: ptrace(ATTACH, %d) FAILED %s (%d)\n", tid, strerror(errno), errno);
closedir(taskDir);
return false;
}
int fd = open(auxvPath, O_RDONLY, 0);
if (fd == -1)
{
- printf_error("open(%s) FAILED %s\n", auxvPath, strerror(errno));
+ printf_error("Problem reading aux info: open(%s) FAILED %s (%d)\n", auxvPath, strerror(errno), errno);
return false;
}
bool result = false;
FILE* mapsFile = fopen(mapPath, "r");
if (mapsFile == nullptr)
{
- printf_error("fopen(%s) FAILED %s\n", mapPath, strerror(errno));
+ printf_error("Problem reading maps file: fopen(%s) FAILED %s (%d)\n", mapPath, strerror(errno), errno);
return false;
}
// linuxGateAddress is the beginning of the kernel's mapping of
if (*read == (size_t)-1)
{
- int readErrno = errno;
- TRACE_VERBOSE("ReadProcessMemory FAILED, addr: %" PRIA PRIx ", size: %zu, ERRNO %d: %s\n", address, size, readErrno, strerror(readErrno));
+ // Preserve errno for the ELF dump writer call
+ g_readProcessMemoryErrno = errno;
+ TRACE_VERBOSE("ReadProcessMemory FAILED addr: %" PRIA PRIx " size: %zu error: %s (%d)\n", address, size, strerror(g_readProcessMemoryErrno), g_readProcessMemoryErrno);
return false;
}
return true;
FILE *statusFile = fopen(statusPath, "r");
if (statusFile == nullptr)
{
- printf_error("GetStatus fopen(%s) FAILED\n", statusPath);
+ printf_error("GetStatus fopen(%s) FAILED %s (%d)\n", statusPath, strerror(errno), errno);
return false;
}
m_fd = open(fileName, O_WRONLY|O_CREAT|O_TRUNC, S_IWUSR | S_IRUSR);
if (m_fd == -1)
{
- printf_error("Could not create json file %s: %d %s\n", fileName, errno, strerror(errno));
+ printf_error("Could not create json file '%s': %s (%d)\n", fileName, strerror(errno), errno);
return false;
}
Write("{\n");
extern bool FormatDumpName(std::string& name, const char* pattern, const char* exename, int pid);
extern bool CreateDump(const char* dumpPathTemplate, int pid, const char* dumpType, MINIDUMP_TYPE minidumpType, bool crashReport, int crashThread, int signal);
+extern std::string GetLastErrorString();
extern void printf_status(const char* format, ...);
extern void printf_error(const char* format, ...);
hProcess = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, pid);
if (hProcess == NULL)
{
- printf_error("Invalid process id '%d' error %d\n", pid, GetLastError());
+ printf_error("Invalid process id '%d' - %s\n", pid, GetLastErrorString().c_str());
goto exit;
}
if (GetModuleBaseNameA(hProcess, NULL, pszName, MAX_LONGPATH) <= 0)
{
- printf_error("Get process name FAILED %d\n", GetLastError());
+ printf_error("Get process name FAILED - %s\n", GetLastErrorString().c_str());
goto exit;
}
if (!FormatDumpName(dumpPath, dumpPathTemplate, pszName, pid))
hFile = CreateFileA(dumpPath.c_str(), GENERIC_READ | GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
if (hFile == INVALID_HANDLE_VALUE)
{
- printf_error("Invalid dump path '%s' error %d\n", dumpPath.c_str(), GetLastError());
+ printf_error("Invalid dump path '%s' - %s\n", dumpPath.c_str(), GetLastErrorString().c_str());
goto exit;
}
else
{
int err = GetLastError();
- if (err != HRESULT_FROM_WIN32(ERROR_PARTIAL_COPY))
+ if (err != ERROR_PARTIAL_COPY)
{
- printf_error("Write dump FAILED 0x%08x\n", err);
+ printf_error("MiniDumpWriteDump - %s\n", GetLastErrorString().c_str());
break;
}
}
m_fd = open(dumpFileName, O_WRONLY|O_CREAT|O_TRUNC, S_IWUSR | S_IRUSR);
if (m_fd == -1)
{
- printf_error("Could not open output %s: %d %s\n", dumpFileName, errno, strerror(errno));
+ printf_error("Could not create output file '%s': %s (%d)\n", dumpFileName, strerror(errno), errno);
return false;
}
return true;
} while (written == -1 && errno == EINTR);
if (written < 1) {
- printf_error("WriteData FAILED %d %s\n", errno, strerror(errno));
+ printf_error("Error writing data to dump file: %s (%d)\n", strerror(errno), errno);
return false;
}
done += written;
#include "createdump.h"
+extern int g_readProcessMemoryErrno;
+
// Write the core dump file:
// ELF header
// Single section header (Shdr) for 64 bit program header count
// and then laydown the memory blocks
if (finalNoteAlignment > 0) {
if (finalNoteAlignment > sizeof(m_tempBuffer)) {
- printf_error("finalNoteAlignment %zu > sizeof(m_tempBuffer)\n", finalNoteAlignment);
+ printf_error("Internal error: finalNoteAlignment %zu > sizeof(m_tempBuffer)\n", finalNoteAlignment);
return false;
}
memset(m_tempBuffer, 0, finalNoteAlignment);
size_t read = 0;
if (!m_crashInfo.ReadProcessMemory((void*)address, m_tempBuffer, bytesToRead, &read)) {
- printf_error("ReadProcessMemory(%" PRIA PRIx64 ", %08zx) FAILED\n", address, bytesToRead);
+ printf_error("Error reading memory at %" PRIA PRIx64 " size %08zx FAILED %s (%d)\n", address, bytesToRead, strerror(g_readProcessMemoryErrno), g_readProcessMemoryErrno);
return false;
}
// This can happen if the target process dies before createdump is finished
if (read == 0) {
- printf_error("ReadProcessMemory(%" PRIA PRIx64 ", %08zx) returned 0 bytes read\n", address, bytesToRead);
+ printf_error("Error reading memory at %" PRIA PRIx64 " size %08zx returned 0 bytes read: %s (%d)\n", address, bytesToRead, strerror(g_readProcessMemoryErrno), g_readProcessMemoryErrno);
return false;
}
#include "createdump.h"
#include "specialthreadinfo.h"
+extern int g_readProcessMemoryResult;
+
//
// Write the core dump file
//
if (alignment > 0)
{
if (alignment > sizeof(m_tempBuffer)) {
- printf_error("Segment alignment %llu > sizeof(m_tempBuffer)\n", alignment);
+ printf_error("Internal error: segment alignment %llu > sizeof(m_tempBuffer)\n", alignment);
return false;
}
memset(m_tempBuffer, 0, alignment);
size_t read = 0;
if (!m_crashInfo.ReadProcessMemory((void*)address, m_tempBuffer, bytesToRead, &read)) {
- printf_error("ReadProcessMemory(%" PRIA PRIx64 ", %08zx) FAILED\n", address, bytesToRead);
+ printf_error("Error reading memory at %" PRIA PRIx64 " size %08zx FAILED %s (%x)\n", address, bytesToRead, mach_error_string(g_readProcessMemoryResult), g_readProcessMemoryResult);
return false;
}
// This can happen if the target process dies before createdump is finished
if (read == 0) {
- printf_error("ReadProcessMemory(%" PRIA PRIx64 ", %08zx) returned 0 bytes read\n", address, bytesToRead);
+ printf_error("Error reading memory at %" PRIA PRIx64 " size %08zx returned 0 bytes read: %s (%x)\n", address, bytesToRead, mach_error_string(g_readProcessMemoryResult), g_readProcessMemoryResult);
return false;
}
FILE *g_logfile = nullptr;
FILE *g_stdout = stdout;
-FILE *g_stderr = stderr;
bool g_diagnostics = false;
bool g_diagnosticsVerbose = false;
#ifdef HOST_UNIX
g_logfile = fopen(logFilePath, "w");
if (g_logfile == nullptr)
{
- printf_error("Can not create log file %s: %d %s\n", logFilePath, errno, strerror(errno));
+ printf_error("Can not create log file '%s': %s (%d)\n", logFilePath, strerror(errno), errno);
return errno;
}
g_stdout = g_logfile;
- g_stderr = g_logfile;
}
else {
pid = atoi(*argv);
{
if (::GetTempPathA(MAX_LONGPATH, tmpPath) == 0)
{
- printf_error("GetTempPath failed (0x%08x)", ::GetLastError());
- return ::GetLastError();
+ printf_error("GetTempPath failed %s", GetLastErrorString().c_str());
+ return -1;
}
exitCode = strcat_s(tmpPath, MAX_LONGPATH, DEFAULT_DUMP_TEMPLATE);
if (exitCode != 0)
}
fflush(g_stdout);
- fflush(g_stderr);
if (g_logfile != nullptr)
{
+ fflush(g_logfile);
fclose(g_logfile);
}
}
return exitCode;
}
+std::string
+GetLastErrorString()
+{
+ DWORD error = GetLastError();
+ std::string result;
+#ifdef HOST_WINDOWS
+ LPSTR messageBuffer;
+ DWORD length = FormatMessage(
+ FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
+ NULL,
+ error,
+ MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
+ (LPTSTR)&messageBuffer,
+ 0,
+ NULL);
+ if (length > 0)
+ {
+ result.append(messageBuffer, length);
+ LocalFree(messageBuffer);
+
+ // Remove the \r\n at the end of the system message. Assumes that the \r is first.
+ size_t found = result.find_last_of('\r');
+ if (found != std::string::npos)
+ {
+ result.erase(found);
+ }
+ result.append(" ");
+ }
+#endif
+ char buffer[64];
+ sprintf(buffer, "(%d)", error);
+ result.append(buffer);
+ return result;
+}
+
void
printf_status(const char* format, ...)
{
{
va_list args;
va_start(args, format);
- if (g_logfile == nullptr)
+
+ // Log error message to file
+ if (g_logfile != nullptr)
{
- fprintf(g_stderr, "[createdump] ");
+ va_list args2;
+ va_copy(args2, args);
+ vfprintf(g_logfile, format, args2);
+ fflush(g_logfile);
}
- vfprintf(g_stderr, format, args);
- fflush(g_stderr);
+ // Always print errors on stderr
+ fprintf(stderr, "[createdump] ");
+ vfprintf(stderr, format, args);
+ fflush(stderr);
va_end(args);
}
kern_return_t result = ::mach_port_deallocate(mach_task_self(), m_port);
if (result != KERN_SUCCESS)
{
- printf_error("~ThreadInfo: mach_port_deallocate FAILED %x %s\n", result, mach_error_string(result));
+ printf_error("Internal error: ~ThreadInfo: mach_port_deallocate FAILED %s (%x)\n", mach_error_string(result), result);
}
}
kern_return_t result = ::thread_get_state(Port(), x86_THREAD_STATE64, (thread_state_t)&m_gpRegisters, &stateCount);
if (result != KERN_SUCCESS)
{
- printf_error("thread_get_state(%x) FAILED %x %s\n", m_tid, result, mach_error_string(result));
+ printf_error("thread_get_state(%x) FAILED %s (%x)\n", m_tid, mach_error_string(result), result);
return false;
}
result = ::thread_get_state(Port(), x86_FLOAT_STATE64, (thread_state_t)&m_fpRegisters, &stateCount);
if (result != KERN_SUCCESS)
{
- printf_error("thread_get_state(%x) FAILED %x %s\n", m_tid, result, mach_error_string(result));
+ printf_error("thread_get_state(%x) FAILED %s (%x)\n", m_tid, mach_error_string(result), result);
return false;
}
#elif defined(__aarch64__)
kern_return_t result = ::thread_get_state(Port(), ARM_THREAD_STATE64, (thread_state_t)&m_gpRegisters, &stateCount);
if (result != KERN_SUCCESS)
{
- printf_error("thread_get_state(%x) FAILED %x %s\n", m_tid, result, mach_error_string(result));
+ printf_error("thread_get_state(%x) FAILED %s (%x)\n", m_tid, mach_error_string(result), result);
return false;
}
result = ::thread_get_state(Port(), ARM_NEON_STATE64, (thread_state_t)&m_fpRegisters, &stateCount);
if (result != KERN_SUCCESS)
{
- printf_error("thread_get_state(%x) FAILED %x %s\n", m_tid, result, mach_error_string(result));
+ printf_error("thread_get_state(%x) FAILED %s (%x)\n", m_tid, mach_error_string(result), result);
return false;
}
#else
struct iovec gpRegsVec = { &m_gpRegisters, sizeof(m_gpRegisters) };
if (ptrace((__ptrace_request)PTRACE_GETREGSET, m_tid, NT_PRSTATUS, &gpRegsVec) == -1)
{
- printf_error("ptrace(PTRACE_GETREGSET, %d, NT_PRSTATUS) FAILED %d (%s)\n", m_tid, errno, strerror(errno));
+ printf_error("ptrace(PTRACE_GETREGSET, %d, NT_PRSTATUS) FAILED %s (%d)\n", m_tid, strerror(errno), errno);
return false;
}
assert(sizeof(m_gpRegisters) == gpRegsVec.iov_len);
#if defined(__arm__)
// Some aarch64 kernels may not support NT_FPREGSET for arm processes. We treat this failure as non-fatal.
#else
- printf_error("ptrace(PTRACE_GETREGSET, %d, NT_FPREGSET) FAILED %d (%s)\n", m_tid, errno, strerror(errno));
+ printf_error("ptrace(PTRACE_GETREGSET, %d, NT_FPREGSET) FAILED %s (%d)\n", m_tid, strerror(errno), errno);
return false;
#endif
}
#if defined(__i386__)
if (ptrace((__ptrace_request)PTRACE_GETFPXREGS, m_tid, nullptr, &m_fpxRegisters) == -1)
{
- printf_error("ptrace(GETFPXREGS, %d) FAILED %d (%s)\n", m_tid, errno, strerror(errno));
+ printf_error("ptrace(GETFPXREGS, %d) FAILED %s (%d)\n", m_tid, strerror(errno), errno);
return false;
}
#elif defined(__arm__) && defined(__VFP_FP__) && !defined(__SOFTFP__)
if (ptrace((__ptrace_request)PTRACE_GETVFPREGS, m_tid, nullptr, &m_vfpRegisters) == -1)
{
- printf_error("ptrace(PTRACE_GETVFPREGS, %d) FAILED %d (%s)\n", m_tid, errno, strerror(errno));
+ printf_error("ptrace(PTRACE_GETVFPREGS, %d) FAILED %s (%d)\n", m_tid, strerror(errno), errno);
return false;
}
#endif
PAL_GenerateCoreDump(
IN LPCSTR dumpName,
IN INT dumpType,
- IN ULONG32 flags);
+ IN ULONG32 flags,
+ LPSTR errorMessageBuffer,
+ INT cbErrorMessageBuffer);
typedef VOID (*PPAL_STARTUP_CALLBACK)(
char *modulePath,
(no return value)
--*/
BOOL
-PROCCreateCrashDump(std::vector<const char*>& argv)
+PROCCreateCrashDump(
+ std::vector<const char*>& argv,
+ LPSTR errorMessageBuffer,
+ INT cbErrorMessageBuffer)
{
+ _ASSERTE(argv.size() > 0);
+ _ASSERTE(errorMessageBuffer == nullptr || cbErrorMessageBuffer > 0);
+
+ int pipe_descs[2];
+ if (pipe(pipe_descs) == -1)
+ {
+ if (errorMessageBuffer != nullptr)
+ {
+ sprintf_s(errorMessageBuffer, cbErrorMessageBuffer, "Problem launching createdump: pipe() FAILED %s (%d)\n", strerror(errno), errno);
+ }
+ return false;
+ }
+ // [0] is read end, [1] is write end
+ int parent_pipe = pipe_descs[0];
+ int child_pipe = pipe_descs[1];
+
// Fork the core dump child process.
pid_t childpid = fork();
// If error, write an error to trace log and abort
if (childpid == -1)
{
- ERROR("PROCCreateCrashDump: fork() FAILED %d (%s)\n", errno, strerror(errno));
+ if (errorMessageBuffer != nullptr)
+ {
+ sprintf_s(errorMessageBuffer, cbErrorMessageBuffer, "Problem launching createdump: fork() FAILED %s (%d)\n", strerror(errno), errno);
+ }
+ close(pipe_descs[0]);
+ close(pipe_descs[1]);
return false;
}
else if (childpid == 0)
{
- // Child process
+ // Close the read end of the pipe, the child doesn't need it
+ close(parent_pipe);
+
+ // Only dup the child's stderr if there is error buffer
+ if (errorMessageBuffer != nullptr)
+ {
+ dup2(child_pipe, STDERR_FILENO);
+ }
+ // Execute the createdump program
if (execve(argv[0], (char**)argv.data(), palEnvironment) == -1)
{
- ERROR("PROCCreateCrashDump: execve() FAILED %d (%s)\n", errno, strerror(errno));
- return false;
+ fprintf(stderr, "Problem launching createdump (may not have execute permissions): execve(%s) FAILED %s (%d)\n", argv[0], strerror(errno), errno);
+ exit(-1);
}
}
else
{
// Ignore any error because on some CentOS and OpenSUSE distros, it isn't
// supported but createdump works just fine.
- ERROR("PROCCreateCrashDump: prctl() FAILED %d (%s)\n", errno, strerror(errno));
+ ERROR("PROCCreateCrashDump: prctl() FAILED %s (%d)\n", errno, strerror(errno));
}
#endif // HAVE_PRCTL_H && HAVE_PR_SET_PTRACER
+ close(child_pipe);
+
+ // Read createdump's stderr messages (if any)
+ if (errorMessageBuffer != nullptr)
+ {
+ // Read createdump's stderr
+ int bytesRead = 0;
+ int count = 0;
+ while ((count = read(parent_pipe, errorMessageBuffer + bytesRead, cbErrorMessageBuffer - bytesRead)) > 0)
+ {
+ bytesRead += count;
+ }
+ errorMessageBuffer[bytesRead] = 0;
+ if (bytesRead > 0)
+ {
+ fputs(errorMessageBuffer, stderr);
+ }
+ }
+ close(parent_pipe);
+
// Parent waits until the child process is done
int wstatus = 0;
int result = waitpid(childpid, &wstatus, 0);
if (result != childpid)
{
- ERROR("PROCCreateCrashDump: waitpid() FAILED result %d wstatus %d errno %d (%s)\n",
- result, wstatus, errno, strerror(errno));
+ ERROR("PROCCreateCrashDump: waitpid() FAILED result %d wstatus %d errno %s (%d)\n",
+ result, wstatus, strerror(errno), errno);
return false;
}
return !WIFEXITED(wstatus) || WEXITSTATUS(wstatus) == 0;
PAL_GenerateCoreDump(
LPCSTR dumpName,
INT dumpType,
- ULONG32 flags)
+ ULONG32 flags,
+ LPSTR errorMessageBuffer,
+ INT cbErrorMessageBuffer)
{
std::vector<const char*> argvCreateDump;
BOOL result = PROCBuildCreateDumpCommandLine(argvCreateDump, &program, &pidarg, dumpName, nullptr, dumpType, flags);
if (result)
{
- result = PROCCreateCrashDump(argvCreateDump);
+ result = PROCCreateCrashDump(argvCreateDump, errorMessageBuffer, cbErrorMessageBuffer);
}
free(program);
free(pidarg);
argv.push_back(nullptr);
}
- PROCCreateCrashDump(argv);
+ PROCCreateCrashDump(argv, nullptr, 0);
free(signalArg);
free(crashThreadArg);
static
ds_ipc_result_t
-ds_rt_generate_core_dump (DiagnosticsDumpCommandId commandId, DiagnosticsGenerateCoreDumpCommandPayload *payload)
+ds_rt_generate_core_dump (
+ DiagnosticsDumpCommandId commandId,
+ DiagnosticsGenerateCoreDumpCommandPayload *payload,
+ ep_char8_t *errorMessageBuffer,
+ int32_t cbErrorMessageBuffer)
{
STATIC_CONTRACT_NOTHROW;
EX_TRY
{
uint32_t flags = ds_generate_core_dump_command_payload_get_flags(payload);
- if (commandId == DS_DUMP_COMMANDID_GENERATE_CORE_DUMP)
- {
- // For the old commmand, this payload field is a bool of whether to enable logging
- flags = flags != 0 ? GenerateDumpFlagsLoggingEnabled : 0;
+ if (commandId == DS_DUMP_COMMANDID_GENERATE_CORE_DUMP)
+ {
+ // For the old commmand, this payload field is a bool of whether to enable logging
+ flags = flags != 0 ? GenerateDumpFlagsLoggingEnabled : 0;
}
- if (GenerateDump (reinterpret_cast<LPCWSTR>(ds_generate_core_dump_command_payload_get_dump_name (payload)),
- static_cast<int32_t>(ds_generate_core_dump_command_payload_get_dump_type (payload)),
- flags))
+ LPCWSTR dumpName = reinterpret_cast<LPCWSTR>(ds_generate_core_dump_command_payload_get_dump_name (payload));
+ int32_t dumpType = static_cast<int32_t>(ds_generate_core_dump_command_payload_get_dump_type (payload));
+ if (GenerateDump(dumpName, dumpType, flags, errorMessageBuffer, cbErrorMessageBuffer))
+ {
result = DS_IPC_S_OK;
+ }
}
EX_CATCH {}
EX_END_CATCH(SwallowAllExceptions);
if (WszCreateProcess(NULL, lpCommandLine, NULL, NULL, TRUE, 0, NULL, NULL, &StartupInfo, &processInformation))
{
WaitForSingleObject(processInformation.hProcess, INFINITE);
- fSuccess = true;
+
+ DWORD exitCode = 0;
+ GetExitCodeProcess(processInformation.hProcess, &exitCode);
+ fSuccess = exitCode == 0;
}
}
EX_CATCH
bool GenerateDump(
LPCWSTR dumpName,
INT dumpType,
- ULONG32 flags)
+ ULONG32 flags,
+ LPSTR errorMessageBuffer,
+ INT cbErrorMessageBuffer)
{
#ifdef TARGET_UNIX
MAKE_UTF8PTR_FROMWIDE_NOTHROW (dumpNameUtf8, dumpName);
}
else
{
- return PAL_GenerateCoreDump(dumpNameUtf8, dumpType, flags);
+ return PAL_GenerateCoreDump(dumpNameUtf8, dumpType, flags, errorMessageBuffer, cbErrorMessageBuffer);
}
#else // TARGET_UNIX
return GenerateCrashDump(dumpName, dumpType, flags & GenerateDumpFlagsLoggingEnabled);
void InitializeCrashDump();
void CreateCrashDumpIfEnabled(bool stackoverflow = false);
#endif
-bool GenerateDump(LPCWSTR dumpName, INT dumpType, ULONG32 flags);
+bool GenerateDump(LPCWSTR dumpName, INT dumpType, ULONG32 flags, LPSTR errorMessageBuffer, INT cbErrorMessageBuffer);
// Generates crash dumps if enabled for both Windows and Linux
void CrashDumpAndTerminateProcess(UINT exitCode);
{
EX_TRY
{
- GenerateDump (GENAWARE_DUMP_FILE_NAME, 2, GenerateDumpFlagsNone);
+ GenerateDump (GENAWARE_DUMP_FILE_NAME, 2, GenerateDumpFlagsNone, nullptr, 0);
}
EX_CATCH {}
EX_END_CATCH(SwallowAllExceptions);
static
inline
ds_ipc_result_t
-ds_rt_generate_core_dump (DiagnosticsDumpCommandId commandId, DiagnosticsGenerateCoreDumpCommandPayload *payload)
+ds_rt_generate_core_dump (
+ DiagnosticsDumpCommandId commandId,
+ DiagnosticsGenerateCoreDumpCommandPayload *payload,
+ ep_char8_t *errorMessageBuffer,
+ int32_t cbErrorMessageBuffer)
{
// TODO: Implement.
return DS_IPC_E_NOTSUPPORTED;
#include "ds-dump-protocol.h"
#include "ds-rt.h"
+const ep_char16_t empty_string [1] = { 0 };
+
/*
* Forward declares of all static functions.
*/
}
static
+void
+dump_protocol_generate_core_dump_response_init(
+ DiagnosticsGenerateCoreDumpResponsePayload *payload,
+ ds_ipc_result_t error,
+ const ep_char8_t *errorText)
+{
+ EP_ASSERT (payload != NULL);
+
+ payload->error = error;
+ // If this conversion failures it will set error_message to NULL which will send an empty message
+ payload->error_message = ep_rt_utf8_to_utf16_string (errorText, -1);
+}
+
+static
+void
+dump_protocol_generate_core_dump_response_fini(
+ DiagnosticsGenerateCoreDumpResponsePayload *payload)
+{
+ ep_rt_utf16_string_free (payload->error_message);
+}
+
+static
+uint16_t
+dump_protocol_generate_core_dump_response_get_size (
+ DiagnosticsGenerateCoreDumpResponsePayload *payload)
+{
+ EP_ASSERT (payload != NULL);
+
+ size_t size = sizeof(payload->error);
+
+ size += sizeof(uint32_t);
+ size += (payload->error_message != NULL) ? (ep_rt_utf16_string_len (payload->error_message) + 1) * sizeof(ep_char16_t) : 0;
+
+ EP_ASSERT (size <= UINT16_MAX);
+ return (uint16_t)size;
+}
+
+static
+bool
+dump_protocol_generate_core_dump_response_flatten (
+ void *payload,
+ uint8_t **buffer,
+ uint16_t *size)
+{
+ DiagnosticsGenerateCoreDumpResponsePayload *response = (DiagnosticsGenerateCoreDumpResponsePayload*)payload;
+
+ EP_ASSERT (payload != NULL);
+ EP_ASSERT (buffer != NULL);
+ EP_ASSERT (*buffer != NULL);
+ EP_ASSERT (size != NULL);
+ EP_ASSERT (dump_protocol_generate_core_dump_response_get_size (response) == *size);
+
+ bool success = true;
+
+ // ds_ipc_result_t size error
+ memcpy (*buffer, &response->error, sizeof (response->error));
+ *buffer += sizeof (response->error);
+ *size -= sizeof (response->error);
+
+ // LPCWSTR error message - if there is no error_message (NULL) then write an empty string
+ success &= ds_ipc_message_try_write_string_utf16_t (buffer, size, response->error_message != NULL ? response->error_message : empty_string);
+
+ // Assert we've used the whole buffer we were given
+ EP_ASSERT(*size == 0);
+
+ return success;
+}
+
+static
+void
+dump_protocol_generate_core_dump_response (
+ DiagnosticsIpcStream *stream,
+ ds_ipc_result_t error,
+ const ep_char8_t * errorText)
+{
+ DiagnosticsGenerateCoreDumpResponsePayload payload;
+ DiagnosticsIpcMessage message;
+ ds_ipc_message_init (&message);
+
+ dump_protocol_generate_core_dump_response_init(&payload, error, errorText);
+
+ bool result = ds_ipc_message_initialize_buffer (
+ &message,
+ ds_ipc_header_get_generic_error (),
+ &payload,
+ dump_protocol_generate_core_dump_response_get_size(&payload),
+ dump_protocol_generate_core_dump_response_flatten);
+
+ if (result)
+ ds_ipc_message_send (&message, stream);
+
+ ds_ipc_message_fini (&message);
+ dump_protocol_generate_core_dump_response_fini (&payload);
+}
+
+static
bool
dump_protocol_helper_generate_core_dump (
DiagnosticsIpcMessage *message,
ep_raise_error ();
}
- ipc_result = ds_rt_generate_core_dump (commandId, payload);
+ ep_char8_t errorMessage[1024];
+ errorMessage[0] = '\0';
+
+ ipc_result = ds_rt_generate_core_dump (commandId, payload, errorMessage, sizeof(errorMessage));
if (ipc_result != DS_IPC_S_OK) {
- ds_ipc_message_send_error (stream, ipc_result);
+ if (commandId == DS_DUMP_COMMANDID_GENERATE_CORE_DUMP3) {
+ dump_protocol_generate_core_dump_response (stream, ipc_result, errorMessage);
+ }
+ else {
+ ds_ipc_message_send_error (stream, ipc_result);
+ }
ep_raise_error ();
} else {
ds_ipc_message_send_success (stream, ipc_result);
switch ((DiagnosticsDumpCommandId)ds_ipc_header_get_commandid (ds_ipc_message_get_header_ref (message))) {
case DS_DUMP_COMMANDID_GENERATE_CORE_DUMP:
case DS_DUMP_COMMANDID_GENERATE_CORE_DUMP2:
+ case DS_DUMP_COMMANDID_GENERATE_CORE_DUMP3:
result = dump_protocol_helper_generate_core_dump (message, stream);
break;
default:
DiagnosticsIpcMessage *message,
DiagnosticsIpcStream *stream);
+/*
+* DiagnosticsGenerateCoreDumpResponsePayload
+*/
+
+#if defined(DS_INLINE_GETTER_SETTER) || defined(DS_IMPL_DUMP_PROTOCOL_GETTER_SETTER)
+struct _DiagnosticsGenerateCoreDumpResponsePayload {
+#else
+struct _DiagnosticsGenerateCoreDumpResponsePayload_Internal {
+#endif
+ // uint = 4 little endian bytes
+ // string = (array<char> where the last char must = 0) or (length = 0)
+
+ ds_ipc_result_t error;
+ ep_char16_t *error_message;
+};
+
+#if !defined(DS_INLINE_GETTER_SETTER) && !defined(DS_IMPL_DUMP_PROTOCOL_GETTER_SETTER)
+struct _DiagnosticsGenerateCoreDumpResponsePayload {
+ uint8_t _internal [sizeof (struct _DiagnosticsGenerateCoreDumpResponsePayload_Internal)];
+};
+#endif
+
#endif /* ENABLE_PERFTRACING */
#endif /* __DIAGNOSTICS_DUMP_PROTOCOL_H__ */
static
ds_ipc_result_t
-ds_rt_generate_core_dump (DiagnosticsDumpCommandId commandId, DiagnosticsGenerateCoreDumpCommandPayload *payload);
+ds_rt_generate_core_dump (DiagnosticsDumpCommandId commandId, DiagnosticsGenerateCoreDumpCommandPayload *payload, ep_char8_t *errorMessageBuffer, int32_t cbErrorMessageBuffer);
/*
* DiagnosticsIpc.
typedef struct _DiagnosticsConnectPort DiagnosticsConnectPort;
typedef struct _DiagnosticsEnvironmentInfoPayload DiagnosticsEnvironmentInfoPayload;
typedef struct _DiagnosticsGenerateCoreDumpCommandPayload DiagnosticsGenerateCoreDumpCommandPayload;
+typedef struct _DiagnosticsGenerateCoreDumpResponsePayload DiagnosticsGenerateCoreDumpResponsePayload;
typedef struct _DiagnosticsSetEnvironmentVariablePayload DiagnosticsSetEnvironmentVariablePayload;
typedef struct _DiagnosticsGetEnvironmentVariablePayload DiagnosticsGetEnvironmentVariablePayload;
typedef struct _DiagnosticsIpcHeader DiagnosticsIpcHeader;
DS_DUMP_COMMANDID_RESERVED = 0x00,
DS_DUMP_COMMANDID_GENERATE_CORE_DUMP = 0x01,
DS_DUMP_COMMANDID_GENERATE_CORE_DUMP2 = 0x02,
+ DS_DUMP_COMMANDID_GENERATE_CORE_DUMP3 = 0x03,
// future
} DiagnosticsDumpCommandId;
/// <param name="logDumpGeneration">When set to true, display the dump generation debug log to the console.</param>
public void WriteDump(DumpType dumpType, string dumpPath, bool logDumpGeneration = false)
{
- IpcMessage request = CreateWriteDumpMessage(dumpType, dumpPath, logDumpGeneration);
- IpcMessage response = IpcClient.SendMessage(_endpoint, request);
- ValidateResponseMessage(response, nameof(WriteDump));
+ WriteDump(dumpType, dumpPath, logDumpGeneration ? WriteDumpFlags.LoggingEnabled : WriteDumpFlags.None);
}
/// <summary>
/// <param name="flags">logging and crash report flags. On runtimes less than 6.0, only LoggingEnabled is supported.</param>
public void WriteDump(DumpType dumpType, string dumpPath, WriteDumpFlags flags)
{
- IpcMessage request = CreateWriteDumpMessage2(dumpType, dumpPath, flags);
+ IpcMessage request = CreateWriteDumpMessage(DumpCommandId.GenerateCoreDump3, dumpType, dumpPath, flags);
IpcMessage response = IpcClient.SendMessage(_endpoint, request);
- if (!ValidateResponseMessage(response, nameof(WriteDump), ValidateResponseOptions.UnknownCommandReturnsFalse))
+ if (!ValidateResponseMessage(response, "Write dump", ValidateResponseOptions.UnknownCommandReturnsFalse | ValidateResponseOptions.ErrorMessageReturned))
{
- if ((flags & ~WriteDumpFlags.LoggingEnabled) != 0)
+ request = CreateWriteDumpMessage(DumpCommandId.GenerateCoreDump2, dumpType, dumpPath, flags);
+ response = IpcClient.SendMessage(_endpoint, request);
+ if (!ValidateResponseMessage(response, "Write dump", ValidateResponseOptions.UnknownCommandReturnsFalse))
{
- throw new ArgumentException($"Only {nameof(WriteDumpFlags.LoggingEnabled)} flag is supported by this runtime version", nameof(flags));
+ if ((flags & ~WriteDumpFlags.LoggingEnabled) != 0)
+ {
+ throw new ArgumentException($"Only {nameof(WriteDumpFlags.LoggingEnabled)} flag is supported by this runtime version", nameof(flags));
+ }
+ request = CreateWriteDumpMessage(dumpType, dumpPath, logDumpGeneration: (flags & WriteDumpFlags.LoggingEnabled) != 0);
+ response = IpcClient.SendMessage(_endpoint, request);
+ ValidateResponseMessage(response, "Write dump");
}
- WriteDump(dumpType, dumpPath, logDumpGeneration: (flags & WriteDumpFlags.LoggingEnabled) != 0);
}
}
/// <param name="dumpPath">Full path to the dump to be generated. By default it is /tmp/coredump.{pid}</param>
/// <param name="logDumpGeneration">When set to true, display the dump generation debug log to the console.</param>
/// <param name="token">The token to monitor for cancellation requests.</param>
- public async Task WriteDumpAsync(DumpType dumpType, string dumpPath, bool logDumpGeneration, CancellationToken token)
+ public Task WriteDumpAsync(DumpType dumpType, string dumpPath, bool logDumpGeneration, CancellationToken token)
{
- IpcMessage request = CreateWriteDumpMessage(dumpType, dumpPath, logDumpGeneration);
- IpcMessage response = await IpcClient.SendMessageAsync(_endpoint, request, token).ConfigureAwait(false);
- ValidateResponseMessage(response, nameof(WriteDumpAsync));
+ return WriteDumpAsync(dumpType, dumpPath, logDumpGeneration ? WriteDumpFlags.LoggingEnabled : WriteDumpFlags.None, token);
}
/// <summary>
/// <param name="token">The token to monitor for cancellation requests.</param>
public async Task WriteDumpAsync(DumpType dumpType, string dumpPath, WriteDumpFlags flags, CancellationToken token)
{
- IpcMessage request = CreateWriteDumpMessage2(dumpType, dumpPath, flags);
+ IpcMessage request = CreateWriteDumpMessage(DumpCommandId.GenerateCoreDump3, dumpType, dumpPath, flags);
IpcMessage response = await IpcClient.SendMessageAsync(_endpoint, request, token).ConfigureAwait(false);
- if (!ValidateResponseMessage(response, nameof(WriteDumpAsync), ValidateResponseOptions.UnknownCommandReturnsFalse))
+ if (!ValidateResponseMessage(response, "Write dump", ValidateResponseOptions.UnknownCommandReturnsFalse | ValidateResponseOptions.ErrorMessageReturned))
{
- if ((flags & ~WriteDumpFlags.LoggingEnabled) != 0)
+ request = CreateWriteDumpMessage(DumpCommandId.GenerateCoreDump2, dumpType, dumpPath, flags);
+ response = await IpcClient.SendMessageAsync(_endpoint, request, token).ConfigureAwait(false);
+ if (!ValidateResponseMessage(response, "Write dump", ValidateResponseOptions.UnknownCommandReturnsFalse))
{
- throw new ArgumentException($"Only {nameof(WriteDumpFlags.LoggingEnabled)} flag is supported by this runtime version", nameof(flags));
+ if ((flags & ~WriteDumpFlags.LoggingEnabled) != 0)
+ {
+ throw new ArgumentException($"Only {nameof(WriteDumpFlags.LoggingEnabled)} flag is supported by this runtime version", nameof(flags));
+ }
+ request = CreateWriteDumpMessage(dumpType, dumpPath, logDumpGeneration: (flags & WriteDumpFlags.LoggingEnabled) != 0);
+ response = await IpcClient.SendMessageAsync(_endpoint, request, token).ConfigureAwait(false);
+ ValidateResponseMessage(response, "Write dump");
}
- await WriteDumpAsync(dumpType, dumpPath, logDumpGeneration: (flags & WriteDumpFlags.LoggingEnabled) != 0, token);
}
}
return new IpcMessage(DiagnosticsServerCommandSet.Dump, (byte)DumpCommandId.GenerateCoreDump, payload);
}
- private static IpcMessage CreateWriteDumpMessage2(DumpType dumpType, string dumpPath, WriteDumpFlags flags)
+ private static IpcMessage CreateWriteDumpMessage(DumpCommandId command, DumpType dumpType, string dumpPath, WriteDumpFlags flags)
{
if (string.IsNullOrEmpty(dumpPath))
throw new ArgumentNullException($"{nameof(dumpPath)} required");
byte[] payload = SerializePayload(dumpPath, (uint)dumpType, (uint)flags);
- return new IpcMessage(DiagnosticsServerCommandSet.Dump, (byte)DumpCommandId.GenerateCoreDump2, payload);
+ return new IpcMessage(DiagnosticsServerCommandSet.Dump, (byte)command, payload);
}
private static ProcessInfo GetProcessInfoFromResponse(IpcResponse response, string operationName)
{
switch ((DiagnosticsServerResponseId)responseMessage.Header.CommandId)
{
+ case DiagnosticsServerResponseId.OK:
+ return true;
+
case DiagnosticsServerResponseId.Error:
uint hr = BitConverter.ToUInt32(responseMessage.Payload, 0);
+ int index = sizeof(uint);
+ string message = null;
switch (hr)
{
case (uint)DiagnosticsIpcError.UnknownCommand:
return false;
}
throw new UnsupportedCommandException($"{operationName} failed - Command is not supported.");
+
case (uint)DiagnosticsIpcError.ProfilerAlreadyActive:
throw new ProfilerAlreadyActiveException($"{operationName} failed - A profiler is already loaded.");
+
case (uint)DiagnosticsIpcError.InvalidArgument:
if (options.HasFlag(ValidateResponseOptions.InvalidArgumentIsRequiresSuspension))
{
throw new ServerErrorException($"{operationName} failed - The runtime must be suspended for this command.");
}
throw new UnsupportedCommandException($"{operationName} failed - Invalid command argument.");
+
+ case (uint)DiagnosticsIpcError.NotSupported:
+ message = $"{operationName} - Not supported by this runtime.";
+ break;
+
+ default:
+ break;
}
- throw new ServerErrorException($"{operationName} failed - HRESULT: 0x{hr:X8}");
- case DiagnosticsServerResponseId.OK:
- return true;
+ // Check if the command can return an error message and if the payload is big enough to contain the
+ // error code (uint) and the string length (uint).
+ if (options.HasFlag(ValidateResponseOptions.ErrorMessageReturned) && responseMessage.Payload.Length >= (sizeof(uint) * 2))
+ {
+ message = IpcHelpers.ReadString(responseMessage.Payload, ref index);
+ }
+ if (string.IsNullOrWhiteSpace(message))
+ {
+ message = $"{operationName} failed - HRESULT: 0x{hr:X8}.";
+ }
+ throw new ServerErrorException(message);
+
default:
throw new ServerErrorException($"{operationName} failed - Server responded with unknown response.");
}
None = 0x0,
UnknownCommandReturnsFalse = 0x1,
InvalidArgumentIsRequiresSuspension = 0x2,
+ ErrorMessageReturned = 0x4,
}
}
}
{
GenerateCoreDump = 0x01,
GenerateCoreDump2 = 0x02,
+ GenerateCoreDump3 = 0x03,
}
internal enum ProfilerCommandId : byte
/// </summary>
internal enum DiagnosticsIpcError : uint
{
+ Fail = 0x80004005,
InvalidArgument = 0x80070057,
+ NotSupported = 0x80131515,
ProfilerAlreadyActive = 0x8013136A,
BadEncoding = 0x80131384,
UnknownCommand = 0x80131385,