Index: lldb/trunk/include/lldb/API/SBProcess.h =================================================================== --- lldb/trunk/include/lldb/API/SBProcess.h +++ lldb/trunk/include/lldb/API/SBProcess.h @@ -341,6 +341,10 @@ bool IsInstrumentationRuntimePresent(InstrumentationRuntimeType type); + // Save the state of the process in a core file (or mini dump on Windows). + lldb::SBError + SaveCore(const char *file_name); + protected: friend class SBAddress; friend class SBBreakpoint; Index: lldb/trunk/include/lldb/API/SBTarget.h =================================================================== --- lldb/trunk/include/lldb/API/SBTarget.h +++ lldb/trunk/include/lldb/API/SBTarget.h @@ -180,6 +180,9 @@ bool stop_at_entry, lldb::SBError& error); + SBProcess + LoadCore (const char *core_file); + //------------------------------------------------------------------ /// Launch a new process with sensible defaults. /// @@ -215,9 +218,6 @@ Launch (SBLaunchInfo &launch_info, SBError& error); SBProcess - LoadCore (const char *core_file); - - SBProcess Attach (SBAttachInfo &attach_info, SBError& error); //------------------------------------------------------------------ Index: lldb/trunk/packages/Python/lldbsuite/test/functionalities/process_save_core/Makefile =================================================================== --- lldb/trunk/packages/Python/lldbsuite/test/functionalities/process_save_core/Makefile +++ lldb/trunk/packages/Python/lldbsuite/test/functionalities/process_save_core/Makefile @@ -0,0 +1,6 @@ +LEVEL = ../../make + +CXX_SOURCES := main.cpp + +include $(LEVEL)/Makefile.rules + Index: lldb/trunk/packages/Python/lldbsuite/test/functionalities/process_save_core/TestProcessSaveCore.py =================================================================== --- lldb/trunk/packages/Python/lldbsuite/test/functionalities/process_save_core/TestProcessSaveCore.py +++ lldb/trunk/packages/Python/lldbsuite/test/functionalities/process_save_core/TestProcessSaveCore.py @@ -0,0 +1,58 @@ +""" +Test saving a core file (or mini dump). +""" + +from __future__ import print_function + +import os, time +import lldb +from lldbsuite.test.lldbtest import * + +class ProcessSaveCoreTestCase(TestBase): + + mydir = TestBase.compute_mydir(__file__) + + @not_remote_testsuite_ready + @skipUnlessWindows + def test_cannot_save_core_unless_process_stopped(self): + """Test that SaveCore fails if the process isn't stopped.""" + self.build() + exe = os.path.join(os.getcwd(), "a.out") + core = os.path.join(os.getcwd(), "core.dmp") + target = self.dbg.CreateTarget(exe) + process = target.LaunchSimple(None, None, self.get_process_working_directory()) + self.assertNotEqual(process.GetState(), lldb.eStateStopped) + error = process.SaveCore(core) + self.assertTrue(error.Fail()) + + @not_remote_testsuite_ready + @skipUnlessWindows + def test_save_windows_mini_dump(self): + """Test that we can save a Windows mini dump.""" + self.build() + exe = os.path.join(os.getcwd(), "a.out") + core = os.path.join(os.getcwd(), "core.dmp") + try: + target = self.dbg.CreateTarget(exe) + breakpoint = target.BreakpointCreateByName("bar") + process = target.LaunchSimple(None, None, self.get_process_working_directory()) + self.assertEqual(process.GetState(), lldb.eStateStopped) + self.assertTrue(process.SaveCore(core)) + self.assertTrue(os.path.isfile(core)) + self.assertTrue(process.Kill().Success()) + + # To verify, we'll launch with the mini dump, and ensure that we see + # the executable in the module list. + target = self.dbg.CreateTarget(None) + process = target.LoadCore(core) + files = [target.GetModuleAtIndex(i).GetFileSpec() for i in range(0, target.GetNumModules())] + paths = [os.path.join(f.GetDirectory(), f.GetFilename()) for f in files] + self.assertTrue(exe in paths) + + finally: + # Clean up the mini dump file. + self.assertTrue(self.dbg.DeleteTarget(target)) + if (os.path.isfile(core)): + os.unlink(core) + + Index: lldb/trunk/packages/Python/lldbsuite/test/functionalities/process_save_core/main.cpp =================================================================== --- lldb/trunk/packages/Python/lldbsuite/test/functionalities/process_save_core/main.cpp +++ lldb/trunk/packages/Python/lldbsuite/test/functionalities/process_save_core/main.cpp @@ -0,0 +1,21 @@ +int global = 42; + +int +bar(int x) +{ + int y = 4*x + global; + return y; +} + +int +foo(int x) +{ + int y = 2*bar(3*x); + return y; +} + +int +main() +{ + return 0 * foo(1); +} Index: lldb/trunk/scripts/interface/SBProcess.i =================================================================== --- lldb/trunk/scripts/interface/SBProcess.i +++ lldb/trunk/scripts/interface/SBProcess.i @@ -398,6 +398,9 @@ bool IsInstrumentationRuntimePresent(lldb::InstrumentationRuntimeType type); + lldb::SBError + SaveCore(const char *file_name); + %pythoncode %{ def __get_is_alive__(self): '''Returns "True" if the process is currently alive, "False" otherwise''' Index: lldb/trunk/source/API/SBProcess.cpp =================================================================== --- lldb/trunk/source/API/SBProcess.cpp +++ lldb/trunk/source/API/SBProcess.cpp @@ -19,6 +19,7 @@ #include "lldb/Core/Debugger.h" #include "lldb/Core/Log.h" #include "lldb/Core/Module.h" +#include "lldb/Core/PluginManager.h" #include "lldb/Core/State.h" #include "lldb/Core/Stream.h" #include "lldb/Core/StreamFile.h" @@ -1425,3 +1426,27 @@ return runtime_sp->IsActive(); } + +lldb::SBError +SBProcess::SaveCore(const char *file_name) +{ + lldb::SBError error; + ProcessSP process_sp(GetSP()); + if (!process_sp) + { + error.SetErrorString("SBProcess is invalid"); + return error; + } + + Mutex::Locker api_locker(process_sp->GetTarget().GetAPIMutex()); + + if (process_sp->GetState() != eStateStopped) + { + error.SetErrorString("the process is not stopped"); + return error; + } + + FileSpec core_file(file_name, false); + error.ref() = PluginManager::SaveCore(process_sp, core_file); + return error; +} Index: lldb/trunk/source/Commands/CommandObjectTarget.cpp =================================================================== --- lldb/trunk/source/Commands/CommandObjectTarget.cpp +++ lldb/trunk/source/Commands/CommandObjectTarget.cpp @@ -256,7 +256,7 @@ { if (!symfile.Readable()) { - result.AppendErrorWithFormat("symbol file '%s' is not readable", core_file.GetPath().c_str()); + result.AppendErrorWithFormat("symbol file '%s' is not readable", symfile.GetPath().c_str()); result.SetStatus (eReturnStatusFailed); return false; } Index: lldb/trunk/source/Plugins/ObjectFile/PECOFF/CMakeLists.txt =================================================================== --- lldb/trunk/source/Plugins/ObjectFile/PECOFF/CMakeLists.txt +++ lldb/trunk/source/Plugins/ObjectFile/PECOFF/CMakeLists.txt @@ -1,3 +1,4 @@ add_lldb_library(lldbPluginObjectFilePECOFF ObjectFilePECOFF.cpp + WindowsMiniDump.cpp ) Index: lldb/trunk/source/Plugins/ObjectFile/PECOFF/ObjectFilePECOFF.h =================================================================== --- lldb/trunk/source/Plugins/ObjectFile/PECOFF/ObjectFilePECOFF.h +++ lldb/trunk/source/Plugins/ObjectFile/PECOFF/ObjectFilePECOFF.h @@ -95,6 +95,11 @@ lldb_private::ModuleSpecList &specs); static bool + SaveCore (const lldb::ProcessSP &process_sp, + const lldb_private::FileSpec &outfile, + lldb_private::Error &error); + + static bool MagicBytesMatch (lldb::DataBufferSP& data_sp); bool Index: lldb/trunk/source/Plugins/ObjectFile/PECOFF/ObjectFilePECOFF.cpp =================================================================== --- lldb/trunk/source/Plugins/ObjectFile/PECOFF/ObjectFilePECOFF.cpp +++ lldb/trunk/source/Plugins/ObjectFile/PECOFF/ObjectFilePECOFF.cpp @@ -8,6 +8,7 @@ //===----------------------------------------------------------------------===// #include "ObjectFilePECOFF.h" +#include "WindowsMiniDump.h" #include "llvm/Support/COFF.h" @@ -24,6 +25,7 @@ #include "lldb/Core/Timer.h" #include "lldb/Core/UUID.h" #include "lldb/Symbol/ObjectFile.h" +#include "lldb/Target/Process.h" #include "lldb/Target/SectionLoadList.h" #include "lldb/Target/Target.h" @@ -42,7 +44,8 @@ GetPluginDescriptionStatic(), CreateInstance, CreateMemoryInstance, - GetModuleSpecifications); + GetModuleSpecifications, + SaveCore); } void @@ -148,6 +151,14 @@ return specs.GetSize() - initial_count; } +bool +ObjectFilePECOFF::SaveCore(const lldb::ProcessSP &process_sp, + const lldb_private::FileSpec &outfile, + lldb_private::Error &error) +{ + return SaveMiniDump(process_sp, outfile, error); +} + bool ObjectFilePECOFF::MagicBytesMatch (DataBufferSP& data_sp) Index: lldb/trunk/source/Plugins/ObjectFile/PECOFF/WindowsMiniDump.h =================================================================== --- lldb/trunk/source/Plugins/ObjectFile/PECOFF/WindowsMiniDump.h +++ lldb/trunk/source/Plugins/ObjectFile/PECOFF/WindowsMiniDump.h @@ -0,0 +1,24 @@ +//===-- WindowsMiniDump.h ---------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_WindowsMiniDump_h_ +#define liblldb_WindowsMiniDump_h_ + +#include "lldb/Target/Process.h" + +namespace lldb_private { + +bool +SaveMiniDump(const lldb::ProcessSP &process_sp, + const lldb_private::FileSpec &outfile, + lldb_private::Error &error); + +} // namespace lldb_private + +#endif Index: lldb/trunk/source/Plugins/ObjectFile/PECOFF/WindowsMiniDump.cpp =================================================================== --- lldb/trunk/source/Plugins/ObjectFile/PECOFF/WindowsMiniDump.cpp +++ lldb/trunk/source/Plugins/ObjectFile/PECOFF/WindowsMiniDump.cpp @@ -0,0 +1,55 @@ +//===-- WindowsMiniDump.cpp -------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +// This function is separated out from ObjectFilePECOFF.cpp to name avoid name +// collisions with WinAPI preprocessor macros. + +#include "WindowsMiniDump.h" +#include "lldb/Host/FileSpec.h" +#include "llvm/Support/ConvertUTF.h" + +#ifdef _WIN32 +#include "lldb/Host/windows/windows.h" +#include // for MiniDumpWriteDump +#endif + +namespace lldb_private { + +bool +SaveMiniDump(const lldb::ProcessSP &process_sp, + const lldb_private::FileSpec &outfile, + lldb_private::Error &error) +{ + if (!process_sp) return false; +#ifdef _WIN32 + HANDLE process_handle = ::OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, process_sp->GetID()); + const std::string file_name = outfile.GetCString(); + std::wstring wide_name; + wide_name.resize(file_name.size() + 1); + char * result_ptr = reinterpret_cast(&wide_name[0]); + const UTF8 *error_ptr = nullptr; + if (!llvm::ConvertUTF8toWide(sizeof(wchar_t), file_name, result_ptr, error_ptr)) { + error.SetErrorString("cannot convert file name"); + return false; + } + HANDLE file_handle = ::CreateFileW(wide_name.c_str(), GENERIC_WRITE, FILE_SHARE_READ, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); + const auto result = ::MiniDumpWriteDump(process_handle, process_sp->GetID(), file_handle, MiniDumpNormal, NULL, NULL, NULL); + ::CloseHandle(file_handle); + ::CloseHandle(process_handle); + if (!result) + { + error.SetError(::GetLastError(), lldb::eErrorTypeWin32); + return false; + } + return true; +#endif + return false; +} + +} // namesapce lldb_private