Better createdump error messages (#69663)
authorMike McLaughlin <mikem@microsoft.com>
Thu, 26 May 2022 18:39:45 +0000 (11:39 -0700)
committerGitHub <noreply@github.com>
Thu, 26 May 2022 18:39:45 +0000 (11:39 -0700)
Better createdump error messages

Redirect stderr when launching createdump to get any error text.

Added a new generate core dump ipc message that allows a error message string
to be returned for more detail on createdump errors.

Update test Microsoft.Diagnostics.NETCore.Client with new generate dump command

26 files changed:
src/coreclr/debug/createdump/crashinfo.cpp
src/coreclr/debug/createdump/crashinfomac.cpp
src/coreclr/debug/createdump/crashinfounix.cpp
src/coreclr/debug/createdump/crashreportwriter.cpp
src/coreclr/debug/createdump/createdump.h
src/coreclr/debug/createdump/createdumpwindows.cpp
src/coreclr/debug/createdump/dumpwriter.cpp
src/coreclr/debug/createdump/dumpwriterelf.cpp
src/coreclr/debug/createdump/dumpwritermacho.cpp
src/coreclr/debug/createdump/main.cpp
src/coreclr/debug/createdump/threadinfomac.cpp
src/coreclr/debug/createdump/threadinfounix.cpp
src/coreclr/pal/inc/pal.h
src/coreclr/pal/src/thread/process.cpp
src/coreclr/vm/eventing/eventpipe/ds-rt-coreclr.h
src/coreclr/vm/excep.cpp
src/coreclr/vm/excep.h
src/coreclr/vm/gcenv.ee.cpp
src/mono/mono/eventpipe/ds-rt-mono.h
src/native/eventpipe/ds-dump-protocol.c
src/native/eventpipe/ds-dump-protocol.h
src/native/eventpipe/ds-rt.h
src/native/eventpipe/ds-types.h
src/tests/tracing/eventpipe/common/Microsoft.Diagnostics.NETCore.Client/DiagnosticsClient/DiagnosticsClient.cs
src/tests/tracing/eventpipe/common/Microsoft.Diagnostics.NETCore.Client/DiagnosticsIpc/IpcCommands.cs
src/tests/tracing/eventpipe/common/Microsoft.Diagnostics.NETCore.Client/DiagnosticsIpc/IpcMessage.cs

index c256173..a4a4dc4 100644 (file)
@@ -67,7 +67,7 @@ CrashInfo::~CrashInfo()
         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
@@ -219,6 +219,27 @@ CrashInfo::GatherCrashInfo(MINIDUMP_TYPE minidumpType)
     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
 //
@@ -241,31 +262,31 @@ CrashInfo::InitializeDAC()
         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:
@@ -302,7 +323,7 @@ CrashInfo::EnumerateMemoryRegionsWithDAC(MINIDUMP_TYPE minidumpType)
         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");
@@ -324,7 +345,7 @@ CrashInfo::EnumerateManagedModules()
         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;
         }
 
index bb25da5..ae77509 100644 (file)
@@ -3,6 +3,8 @@
 
 #include "createdump.h"
 
+int g_readProcessMemoryResult = KERN_SUCCESS;
+
 bool
 CrashInfo::Initialize()
 {
@@ -12,7 +14,10 @@ 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;
@@ -37,14 +42,14 @@ CrashInfo::EnumerateAndSuspendThreads()
     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;
     }
 
@@ -57,7 +62,7 @@ CrashInfo::EnumerateAndSuspendThreads()
         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
@@ -105,7 +110,7 @@ CrashInfo::EnumerateMemoryRegions()
         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",
@@ -382,8 +387,9 @@ CrashInfo::ReadProcessMemory(void* address, void* buffer, size_t size, size_t* r
         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;
index 0d54f56..4a9c2d4 100644 (file)
@@ -3,6 +3,8 @@
 
 #include "createdump.h"
 
+int g_readProcessMemoryErrno = 0;
+
 bool GetStatus(pid_t pid, pid_t* ppid, pid_t* tgid, std::string* name);
 
 bool
@@ -14,7 +16,17 @@ CrashInfo::Initialize()
     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
@@ -58,7 +70,7 @@ CrashInfo::EnumerateAndSuspendThreads()
     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;
     }
 
@@ -76,7 +88,7 @@ CrashInfo::EnumerateAndSuspendThreads()
             }
             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;
             }
@@ -102,7 +114,7 @@ CrashInfo::GetAuxvEntries()
     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;
@@ -159,7 +171,7 @@ CrashInfo::EnumerateModuleMappings()
     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
@@ -394,8 +406,9 @@ CrashInfo::ReadProcessMemory(void* address, void* buffer, size_t size, size_t* r
 
     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;
@@ -413,7 +426,7 @@ GetStatus(pid_t pid, pid_t* ppid, pid_t* tgid, std::string* name)
     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;
     }
 
index b643fa8..f18ba6d 100644 (file)
@@ -271,7 +271,7 @@ CrashReportWriter::OpenWriter(const char* fileName)
     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");
index 2066120..f687887 100644 (file)
@@ -109,6 +109,7 @@ typedef int T_CONTEXT;
 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, ...);
 
index ebcd6fc..42036d5 100644 (file)
@@ -20,12 +20,12 @@ CreateDump(const char* dumpPathTemplate, int pid, const char* dumpType, MINIDUMP
     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))
@@ -37,7 +37,7 @@ CreateDump(const char* dumpPathTemplate, int pid, const char* dumpType, MINIDUMP
     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;
     }
 
@@ -52,9 +52,9 @@ CreateDump(const char* dumpPathTemplate, int pid, const char* dumpType, MINIDUMP
         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;
             }
         }
index 5946c37..ec9101e 100644 (file)
@@ -26,7 +26,7 @@ DumpWriter::OpenDump(const char* dumpFileName)
     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;
@@ -46,7 +46,7 @@ DumpWriter::WriteData(int fd, const void* buffer, size_t length)
         } 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;
index 5e6e249..70da2f5 100644 (file)
@@ -3,6 +3,8 @@
 
 #include "createdump.h"
 
+extern int g_readProcessMemoryErrno;
+
 // Write the core dump file:
 //   ELF header
 //   Single section header (Shdr) for 64 bit program header count
@@ -161,7 +163,7 @@ DumpWriter::WriteDump()
     // 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);
@@ -189,13 +191,13 @@ DumpWriter::WriteDump()
                 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;
                 }
 
index fc354d5..31e347d 100644 (file)
@@ -4,6 +4,8 @@
 #include "createdump.h"
 #include "specialthreadinfo.h"
 
+extern int g_readProcessMemoryResult;
+
 //
 // Write the core dump file
 //
@@ -56,7 +58,7 @@ DumpWriter::WriteDump()
     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);
@@ -264,13 +266,13 @@ DumpWriter::WriteSegments()
                 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;
                 }
 
index 7d60670..89178b5 100644 (file)
@@ -34,7 +34,6 @@ const char* g_help = "createdump [options] pid\n"
 
 FILE *g_logfile = nullptr;
 FILE *g_stdout = stdout;
-FILE *g_stderr = stderr;
 bool g_diagnostics = false;
 bool g_diagnosticsVerbose = false;
 #ifdef HOST_UNIX
@@ -157,11 +156,10 @@ int __cdecl main(const int argc, const char* argv[])
                 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);
@@ -183,8 +181,8 @@ int __cdecl main(const int argc, const char* 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)
@@ -205,10 +203,10 @@ int __cdecl main(const int argc, const char* argv[])
         }
 
         fflush(g_stdout);
-        fflush(g_stderr);
 
         if (g_logfile != nullptr)
         {
+            fflush(g_logfile);
             fclose(g_logfile);
         }
     }
@@ -224,6 +222,41 @@ int __cdecl main(const int argc, const char* argv[])
     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, ...)
 {
@@ -243,12 +276,19 @@ printf_error(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);
 }
 
index 7357946..cc4e23e 100644 (file)
@@ -23,7 +23,7 @@ ThreadInfo::~ThreadInfo()
     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);
     }
 }
 
@@ -38,7 +38,7 @@ ThreadInfo::Initialize()
     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;
     }
 
@@ -46,7 +46,7 @@ ThreadInfo::Initialize()
     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__)
@@ -54,7 +54,7 @@ ThreadInfo::Initialize()
     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;
     }
 
@@ -62,7 +62,7 @@ ThreadInfo::Initialize()
     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
index 8474fcb..c9ccc7f 100644 (file)
@@ -71,7 +71,7 @@ ThreadInfo::GetRegistersWithPTrace()
     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);
@@ -82,7 +82,7 @@ ThreadInfo::GetRegistersWithPTrace()
 #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
     }
@@ -91,7 +91,7 @@ ThreadInfo::GetRegistersWithPTrace()
 #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__)
@@ -102,7 +102,7 @@ ThreadInfo::GetRegistersWithPTrace()
 
     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
index a441752..db7d114 100644 (file)
@@ -451,7 +451,9 @@ PALAPI
 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,
index 94668de..9f0a179 100644 (file)
@@ -2371,24 +2371,56 @@ Function:
 (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
@@ -2399,16 +2431,36 @@ PROCCreateCrashDump(std::vector<const char*>& argv)
         {
             // 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;
@@ -2509,7 +2561,9 @@ BOOL
 PAL_GenerateCoreDump(
     LPCSTR dumpName,
     INT dumpType,
-    ULONG32 flags)
+    ULONG32 flags,
+    LPSTR errorMessageBuffer,
+    INT cbErrorMessageBuffer)
 {
     std::vector<const char*> argvCreateDump;
 
@@ -2526,7 +2580,7 @@ PAL_GenerateCoreDump(
     BOOL result = PROCBuildCreateDumpCommandLine(argvCreateDump, &program, &pidarg, dumpName, nullptr, dumpType, flags);
     if (result)
     {
-        result = PROCCreateCrashDump(argvCreateDump);
+        result = PROCCreateCrashDump(argvCreateDump, errorMessageBuffer, cbErrorMessageBuffer);
     }
     free(program);
     free(pidarg);
@@ -2578,7 +2632,7 @@ PROCCreateCrashDumpIfEnabled(int signal)
             argv.push_back(nullptr);
         }
 
-        PROCCreateCrashDump(argv);
+        PROCCreateCrashDump(argv, nullptr, 0);
 
         free(signalArg);
         free(crashThreadArg);
index a62c563..221aaf0 100644 (file)
@@ -188,7 +188,11 @@ ds_rt_config_value_get_default_port_suspend (void)
 
 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;
 
@@ -196,15 +200,17 @@ ds_rt_generate_core_dump (DiagnosticsDumpCommandId commandId, DiagnosticsGenerat
        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);
index 53b5724..9040281 100644 (file)
@@ -4101,7 +4101,10 @@ LaunchCreateDump(LPCWSTR lpCommandLine)
         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
@@ -4176,7 +4179,9 @@ InitializeCrashDump()
 bool GenerateDump(
     LPCWSTR dumpName,
     INT dumpType,
-    ULONG32 flags)
+    ULONG32 flags,
+    LPSTR errorMessageBuffer,
+    INT cbErrorMessageBuffer)
 {
 #ifdef TARGET_UNIX
     MAKE_UTF8PTR_FROMWIDE_NOTHROW (dumpNameUtf8, dumpName);
@@ -4186,7 +4191,7 @@ bool GenerateDump(
     }
     else
     {
-        return PAL_GenerateCoreDump(dumpNameUtf8, dumpType, flags);
+        return PAL_GenerateCoreDump(dumpNameUtf8, dumpType, flags, errorMessageBuffer, cbErrorMessageBuffer);
     }
 #else // TARGET_UNIX
     return GenerateCrashDump(dumpName, dumpType, flags & GenerateDumpFlagsLoggingEnabled);
index b76839b..7b61eb6 100644 (file)
@@ -208,7 +208,7 @@ enum
 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);
index 1e20632..02537f0 100644 (file)
@@ -1695,7 +1695,7 @@ void GCToEEInterface::AnalyzeSurvivorsFinished(size_t gcIndex, int condemnedGene
             {
                 EX_TRY
                 {
-                    GenerateDump (GENAWARE_DUMP_FILE_NAME, 2, GenerateDumpFlagsNone);
+                    GenerateDump (GENAWARE_DUMP_FILE_NAME, 2, GenerateDumpFlagsNone, nullptr, 0);
                 }
                 EX_CATCH {}
                 EX_END_CATCH(SwallowAllExceptions);
index d3ed4f0..f674dfa 100644 (file)
@@ -172,7 +172,11 @@ ds_rt_config_value_get_default_port_suspend (void)
 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;
index fad5680..42ee6a2 100644 (file)
@@ -8,6 +8,8 @@
 #include "ds-dump-protocol.h"
 #include "ds-rt.h"
 
+const ep_char16_t empty_string [1] = { 0 };
+
 /*
  * Forward declares of all static functions.
  */
@@ -94,6 +96,102 @@ dump_protocol_helper_unknown_command (
 }
 
 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,
@@ -115,9 +213,17 @@ dump_protocol_helper_generate_core_dump (
                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);
@@ -146,6 +252,7 @@ ds_dump_protocol_helper_handle_ipc_message (
        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:
index 8065e8c..20bb40c 100644 (file)
@@ -61,5 +61,27 @@ ds_dump_protocol_helper_handle_ipc_message (
        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__ */
index 8de7157..d2e96ff 100644 (file)
@@ -87,7 +87,7 @@ ds_rt_config_value_get_default_port_suspend (void);
 
 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.
index fba8ba8..29baf22 100644 (file)
@@ -20,6 +20,7 @@ typedef struct _DiagnosticsStartupProfilerCommandPayload DiagnosticsStartupProfi
 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;
@@ -45,6 +46,7 @@ typedef enum {
        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;
 
index 181ccc4..2d58ef8 100644 (file)
@@ -121,9 +121,7 @@ namespace Microsoft.Diagnostics.NETCore.Client
         /// <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>
@@ -134,15 +132,22 @@ namespace Microsoft.Diagnostics.NETCore.Client
         /// <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);
             }
         }
 
@@ -153,11 +158,9 @@ namespace Microsoft.Diagnostics.NETCore.Client
         /// <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>
@@ -169,15 +172,22 @@ namespace Microsoft.Diagnostics.NETCore.Client
         /// <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);
             }
         }
 
@@ -522,13 +532,13 @@ namespace Microsoft.Diagnostics.NETCore.Client
             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)
@@ -552,8 +562,13 @@ namespace Microsoft.Diagnostics.NETCore.Client
         {
             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:
@@ -562,18 +577,36 @@ namespace Microsoft.Diagnostics.NETCore.Client
                                 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.");
             }
@@ -585,6 +618,7 @@ namespace Microsoft.Diagnostics.NETCore.Client
             None = 0x0,
             UnknownCommandReturnsFalse = 0x1,
             InvalidArgumentIsRequiresSuspension = 0x2,
+            ErrorMessageReturned = 0x4,
         }
     }
 }
index 516cf30..241acae 100644 (file)
@@ -35,6 +35,7 @@ namespace Microsoft.Diagnostics.NETCore.Client
     {
         GenerateCoreDump = 0x01,
         GenerateCoreDump2 = 0x02,
+        GenerateCoreDump3 = 0x03,
     }
 
     internal enum ProfilerCommandId : byte
index 9aa4d25..9a771b8 100644 (file)
@@ -16,7 +16,9 @@ namespace Microsoft.Diagnostics.NETCore.Client
     /// </summary>
     internal enum DiagnosticsIpcError : uint
     {
+        Fail                  = 0x80004005,
         InvalidArgument       = 0x80070057,
+        NotSupported          = 0x80131515,
         ProfilerAlreadyActive = 0x8013136A,
         BadEncoding           = 0x80131384,
         UnknownCommand        = 0x80131385,