Commits

spencercw committed 5efb452

Add a basic stack trace to the debugger.

Comments (0)

Files changed (18)

GbDebuggerMsvs/EnumFrameInfo.rgs

+HKCR
+{
+	NoRemove CLSID
+	{
+		ForceRemove {99E79283-DB5D-4C9F-8002-9B0019298E4D} = s 'EnumFrameInfo Class'
+		{
+			InprocServer32 = s '%MODULE%'
+			{
+				val ThreadingModel = s 'Apartment'
+			}
+			TypeLib = s '{13703F40-0D7C-4E56-B8A5-85DB92D6944E}'
+			Version = s '1.0'
+		}
+	}
+}

GbDebuggerMsvs/GbDebuggerMsvs.idl

 	{
 		[default] interface IDebugProgramProvider2;
 	};
+	[
+		uuid(99E79283-DB5D-4C9F-8002-9B0019298E4D)		
+	]
+	coclass EnumFrameInfo
+	{
+		[default] interface IEnumDebugFrameInfo2;
+	};
+	[
+		uuid(0B9C415E-FB6E-4DAD-8470-53271D4C42E3)		
+	]
+	coclass GbVsStackFrame
+	{
+		[default] interface IDebugStackFrame2;
+	};
+	[
+		uuid(9E6F8AED-8DFE-451C-AED1-6CB7B68A0FD3)		
+	]
+	coclass DebugContext
+	{
+		[default] interface IDebugCodeContext2;
+	};
 };

GbDebuggerMsvs/GbDebuggerMsvs.rc

Binary file modified.

GbDebuggerMsvs/GbDebuggerMsvs.vcxproj

     <PostBuildEvent />
   </ItemDefinitionGroup>
   <ItemGroup>
+    <ClCompile Include="context.cpp" />
     <ClCompile Include="Engine.cpp" />
     <ClCompile Include="events.cpp" />
     <ClCompile Include="GbDebuggerMsvs.cpp" />
     </ClCompile>
     <ClCompile Include="Program.cpp" />
     <ClCompile Include="ProgramProvider.cpp" />
+    <ClCompile Include="stack_frame.cpp" />
     <ClCompile Include="stdafx.cpp">
       <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">Create</PrecompiledHeader>
       <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">Create</PrecompiledHeader>
   </ItemGroup>
   <ItemGroup>
     <ClInclude Include="com_enum_with_count.h" />
+    <ClInclude Include="context.h" />
     <ClInclude Include="Engine.h" />
     <ClInclude Include="events.h" />
     <ClInclude Include="GbDebuggerMsvs.h" />
     <ClInclude Include="Program.h" />
     <ClInclude Include="ProgramProvider.h" />
     <ClInclude Include="Resource.h" />
+    <ClInclude Include="stack_frame.h" />
     <ClInclude Include="stdafx.h" />
     <ClInclude Include="targetver.h" />
   </ItemGroup>
     <ResourceCompile Include="GbDebuggerMsvs.rc" />
   </ItemGroup>
   <ItemGroup>
+    <None Include="DebugContext.rgs" />
     <None Include="Engine.rgs" />
+    <None Include="EnumFrameInfo.rgs" />
     <None Include="GbDebuggerMsvs.rgs" />
+    <None Include="GbVsStackFrame.rgs" />
     <None Include="Program.rgs" />
     <None Include="ProgramProvider.rgs" />
   </ItemGroup>

GbDebuggerMsvs/GbDebuggerMsvs.vcxproj.filters

     <ClCompile Include="events.cpp">
       <Filter>Source Files</Filter>
     </ClCompile>
+    <ClCompile Include="stack_frame.cpp">
+      <Filter>Source Files</Filter>
+    </ClCompile>
+    <ClCompile Include="context.cpp">
+      <Filter>Source Files</Filter>
+    </ClCompile>
   </ItemGroup>
   <ItemGroup>
     <ClInclude Include="stdafx.h">
     <ClInclude Include="com_enum_with_count.h">
       <Filter>Header Files</Filter>
     </ClInclude>
+    <ClInclude Include="stack_frame.h">
+      <Filter>Header Files</Filter>
+    </ClInclude>
+    <ClInclude Include="context.h">
+      <Filter>Header Files</Filter>
+    </ClInclude>
   </ItemGroup>
   <ItemGroup>
     <ResourceCompile Include="GbDebuggerMsvs.rc">
     <None Include="ProgramProvider.rgs">
       <Filter>Resource Files</Filter>
     </None>
+    <None Include="EnumFrameInfo.rgs">
+      <Filter>Resource Files</Filter>
+    </None>
+    <None Include="GbVsStackFrame.rgs">
+      <Filter>Resource Files</Filter>
+    </None>
+    <None Include="DebugContext.rgs">
+      <Filter>Resource Files</Filter>
+    </None>
   </ItemGroup>
   <ItemGroup>
     <Midl Include="GbDebuggerMsvs.idl">

GbDebuggerMsvs/GbVsStackFrame.rgs

+HKCR
+{
+	NoRemove CLSID
+	{
+		ForceRemove {0B9C415E-FB6E-4DAD-8470-53271D4C42E3} = s 'GbVsStackFrame Class'
+		{
+			InprocServer32 = s '%MODULE%'
+			{
+				val ThreadingModel = s 'Apartment'
+			}
+			TypeLib = s '{13703F40-0D7C-4E56-B8A5-85DB92D6944E}'
+			Version = s '1.0'
+		}
+	}
+}

GbDebuggerMsvs/Program.cpp

 
 #include "Program.h"
 
+#include <iomanip>
 #include <fstream>
+#include <sstream>
 
 #include <boost/bind.hpp>
 
+#include "context.h"
 #include "events.h"
+#include "stack_frame.h"
+
+namespace fs = boost::filesystem;
+using std::hex;
+using std::setfill;
+using std::setw;
+using std::vector;
+using std::wostringstream;
+using std::wstring;
 
 CProgram *gProgram = NULL;
 
 	return E_NOTIMPL;
 }
 
-HRESULT CProgram::GetProgramName(BSTR *pbstrProgramName)
+HRESULT CProgram::GetProgramName(BSTR * /*pbstrProgramName*/)
 {
 	std::ofstream f("C:\\Users\\Chris\\gbem_debug.txt", std::ios_base::out | std::ios_base::binary | std::ios_base::app);
 	f << "CProgram::GetProgramName\n";
 	f.close();
 
-	*pbstrProgramName = SysAllocString(L"Hello");
-	return S_OK;
-	//return E_NOTIMPL;
+	return E_NOTIMPL;
 }
 
 HRESULT CProgram::GetHostName(DWORD /*dwHostNameType*/, BSTR * /*pbstrHostName*/)
 	return E_NOTIMPL;
 }
 
-HRESULT CProgram::GetName(BSTR * /*pbstrName*/)
+HRESULT CProgram::GetName(BSTR *pbstrName)
 {
-	std::ofstream f("C:\\Users\\Chris\\gbem_debug.txt", std::ios_base::out | std::ios_base::binary | std::ios_base::app);
-	f << "CProgram::GetName\n";
-	f.close();
-
-	return E_NOTIMPL;
+	fs::path module = gbd->modulePath();
+	module = module.filename();
+	*pbstrName = SysAllocString(module.c_str());
+	return S_OK;
 }
 
 HRESULT CProgram::GetProcess(IDebugProcess2 ** /*ppProcess*/)
 }
 
 // IDebugThread2
-HRESULT CProgram::EnumFrameInfo(
-	FRAMEINFO_FLAGS /*dwFieldSpec*/,
-	UINT /*nRadix*/,
-	IEnumDebugFrameInfo2 ** /*ppEnum*/)
+HRESULT CProgram::EnumFrameInfo(FRAMEINFO_FLAGS dwFieldSpec, UINT nRadix, IEnumDebugFrameInfo2 **ppEnum)
 {
 	std::ofstream f("C:\\Users\\Chris\\gbem_debug.txt", std::ios_base::out | std::ios_base::binary | std::ios_base::app);
-	f << "CProgram::EnumFrameInfo\n";
+	f << "CProgram::EnumFrameInfo " << dwFieldSpec << " " << nRadix << "\n";
 	f.close();
 
-	return E_NOTIMPL;
+	// Get the stack frames and allocate space for the VS format stack frames
+	vector<GbStackFrame> gbStackFrames = gbd->stackFrames();
+	size_t framesCount = gbStackFrames.size();
+
+	vector<FRAMEINFO> stackFrames(framesCount);
+	memset(&*stackFrames.begin(), 0, sizeof(FRAMEINFO) * framesCount);
+
+	// Copy the data
+	size_t i = 0;
+	for (vector<GbStackFrame>::const_iterator frame = gbStackFrames.begin(),
+		end = gbStackFrames.end(); frame != end; ++frame, ++i)
+	{
+		fs::path filename = frame->module.filename();
+		stackFrames[i].m_dwValidFields =
+			FIF_FUNCNAME | FIF_MODULE | FIF_STACKRANGE | FIF_FRAME | FIF_DEBUGINFO;
+
+		// Format the function name
+		if (dwFieldSpec & FIF_FUNCNAME_FORMAT)
+		{
+			stackFrames[i].m_dwValidFields |= FIF_FUNCNAME_FORMAT;
+		}
+
+		wostringstream oss;
+		oss << hex << setfill(L'0');
+		if (dwFieldSpec & FIF_FUNCNAME_MODULE)
+		{
+			stackFrames[i].m_dwValidFields |= FIF_FUNCNAME_MODULE;
+			oss << filename.native() << L"!";
+		}
+		oss << setw(4) << static_cast<unsigned>(frame->instructionAddr) << L"()";
+		wstring functionName = oss.str();
+
+		stackFrames[i].m_bstrFuncName = SysAllocString(functionName.c_str());
+		stackFrames[i].m_bstrModule = SysAllocString(filename.c_str());
+		stackFrames[i].m_addrMin = frame->stackAddrMin;
+		stackFrames[i].m_addrMax = frame->stackAddrMax;
+		stackFrames[i].m_fHasDebugInfo = FALSE;
+
+
+		CComObject<CGbVsStackFrame> *debugFrame;
+		CComObject<CGbVsStackFrame>::CreateInstance(&debugFrame);
+		debugFrame->AddRef();
+
+		stackFrames[i].m_pFrame = debugFrame;
+		
+		CComObject<CDebugContext> *context;
+		CComObject<CDebugContext>::CreateInstance(&context);
+		context->AddRef();
+		context->Init(frame->module, frame->instructionAddr);
+
+		debugFrame->Init(&stackFrames[i], context);
+		context->Release();
+	}
+
+	// Create the enumerator
+	CComObject<CEnumFrameInfo> *framesEnum;
+	CComObject<CEnumFrameInfo>::CreateInstance(&framesEnum);
+	framesEnum->AddRef();
+	framesEnum->Init(stackFrames);
+
+	*ppEnum = framesEnum;
+
+	return S_OK;
 }
 
 HRESULT CProgram::SetThreadName(const OLECHAR * /*pszName*/)

GbDebuggerMsvs/context.cpp

+/*  Copyright � 2011 Chris Spencer <spencercw@gmail.com>
+
+    This program is free software: you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation, either version 3 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
+
+#include "stdafx.h"
+
+#include "context.h"
+
+#include <iomanip>
+#include <fstream>
+#include <sstream>
+
+namespace fs = boost::filesystem;
+using std::hex;
+using std::setfill;
+using std::setw;
+using std::wostringstream;
+using std::wstring;
+
+// IDebugCodeContext2 methods
+
+HRESULT CDebugContext::GetDocumentContext(IDebugDocumentContext2 ** /*ppSrcCxt*/)
+{
+	std::ofstream f("C:\\Users\\Chris\\gbem_debug.txt", std::ios_base::out | std::ios_base::binary | std::ios_base::app);
+	f << "CDebugContext::GetDocumentContext\n";
+	f.close();
+
+	return E_NOTIMPL;
+}
+
+HRESULT CDebugContext::GetLanguageInfo(BSTR * /*pbstrLanguage*/, GUID * /*pguidLanguage*/)
+{
+	std::ofstream f("C:\\Users\\Chris\\gbem_debug.txt", std::ios_base::out | std::ios_base::binary | std::ios_base::app);
+	f << "CDebugContext::GetLanguageInfo\n";
+	f.close();
+
+	return E_NOTIMPL;
+}
+
+// IDebugMemoryContext2 methods
+
+HRESULT CDebugContext::GetName(BSTR * /*pbstrName*/)
+{
+	std::ofstream f("C:\\Users\\Chris\\gbem_debug.txt", std::ios_base::out | std::ios_base::binary | std::ios_base::app);
+	f << "CDebugContext::GetName\n";
+	f.close();
+
+	return E_NOTIMPL;
+}
+
+HRESULT CDebugContext::GetInfo(CONTEXT_INFO_FIELDS, CONTEXT_INFO *pInfo)
+{
+	wostringstream oss;
+	oss << hex << setfill(L'0') << L"0x" << setw(4) << address_;
+	wstring address = oss.str();
+
+	pInfo->dwFields = CIF_MODULEURL | CIF_ADDRESS;
+	pInfo->bstrModuleUrl = SysAllocString(module_.c_str());
+	pInfo->bstrAddress = SysAllocString(address.c_str());
+
+	return S_OK;
+}
+
+HRESULT CDebugContext::Add(UINT64 /*dwCount*/, IDebugMemoryContext2 ** /*ppMemCxt*/)
+{
+	std::ofstream f("C:\\Users\\Chris\\gbem_debug.txt", std::ios_base::out | std::ios_base::binary | std::ios_base::app);
+	f << "CDebugContext::Add\n";
+	f.close();
+
+	return E_NOTIMPL;
+}
+
+HRESULT CDebugContext::Subtract(UINT64 /*dwCount*/, IDebugMemoryContext2 ** /*ppMemCxt*/)
+{
+	std::ofstream f("C:\\Users\\Chris\\gbem_debug.txt", std::ios_base::out | std::ios_base::binary | std::ios_base::app);
+	f << "CDebugContext::Subtract\n";
+	f.close();
+
+	return E_NOTIMPL;
+}
+
+HRESULT CDebugContext::Compare(
+	CONTEXT_COMPARE /*compare*/,
+	IDebugMemoryContext2 ** /*rgpMemoryContextSet*/,
+	DWORD /*dwMemoryContextSetLen*/,
+	DWORD * /*pdwMemoryContext*/)
+{
+	std::ofstream f("C:\\Users\\Chris\\gbem_debug.txt", std::ios_base::out | std::ios_base::binary | std::ios_base::app);
+	f << "CDebugContext::Compare\n";
+	f.close();
+
+	return E_NOTIMPL;
+}
+
+// CDebugContext methods
+
+void CDebugContext::Init(const fs::path &module, uint16_t address)
+{
+	module_ = module;
+	address_ = address;
+}

GbDebuggerMsvs/context.h

+/*  Copyright � 2011 Chris Spencer <spencercw@gmail.com>
+
+    This program is free software: you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation, either version 3 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
+
+#pragma once
+
+#include <stdint.h>
+
+#include <atlbase.h>
+#include <atlcom.h>
+
+#include <boost/filesystem/path.hpp>
+
+#include "GbDebuggerMsvs_i.h"
+
+#if defined(_WIN32_WCE) && !defined(_CE_DCOM) && !defined(_CE_ALLOW_SINGLE_THREADED_OBJECTS_IN_MTA)
+#error "Single-threaded COM objects are not properly supported on Windows CE platform, such as the Windows Mobile platforms that do not include full DCOM support. Define _CE_ALLOW_SINGLE_THREADED_OBJECTS_IN_MTA to force ATL to support creating single-thread COM object's and allow use of it's single-threaded COM object implementations. The threading model in your rgs file was set to 'Free' as that is the only threading model supported in non DCOM Windows CE platforms."
+#endif
+
+using namespace ATL;
+
+class ATL_NO_VTABLE CDebugContext :
+	public CComObjectRootEx<CComSingleThreadModel>,
+	public CComCoClass<CDebugContext, &CLSID_DebugContext>,
+	public IDebugCodeContext2
+{
+public:
+	CDebugContext()
+	{
+	}
+
+DECLARE_REGISTRY_RESOURCEID(IDR_DEBUGCONTEXT)
+
+BEGIN_COM_MAP(CDebugContext)
+	COM_INTERFACE_ENTRY(IDebugCodeContext2)
+END_COM_MAP()
+
+	// IDebugCodeContext2 methods
+	STDMETHOD(GetDocumentContext)(IDebugDocumentContext2 **ppSrcCxt);
+	STDMETHOD(GetLanguageInfo)(BSTR *pbstrLanguage, GUID *pguidLanguage);
+
+	// IDebugMemoryContext2 methods
+	STDMETHOD(GetName)(BSTR *pbstrName);
+	STDMETHOD(GetInfo)(CONTEXT_INFO_FIELDS dwFields, CONTEXT_INFO *pInfo);
+	STDMETHOD(Add)(UINT64 dwCount, IDebugMemoryContext2 **ppMemCxt);
+	STDMETHOD(Subtract)(UINT64 dwCount, IDebugMemoryContext2 **ppMemCxt);
+	STDMETHOD(Compare)(
+		CONTEXT_COMPARE compare,
+		IDebugMemoryContext2 **rgpMemoryContextSet,
+		DWORD dwMemoryContextSetLen,
+		DWORD *pdwMemoryContext);
+
+	DECLARE_PROTECT_FINAL_CONSTRUCT()
+
+	HRESULT FinalConstruct()
+	{
+		return S_OK;
+	}
+
+	void FinalRelease()
+	{
+	}
+
+public:
+	void Init(const boost::filesystem::path &module, uint16_t address);
+
+protected:
+	boost::filesystem::path module_;
+	uint16_t address_;
+};
+
+OBJECT_ENTRY_AUTO(__uuidof(DebugContext), CDebugContext)

GbDebuggerMsvs/resource.h

Binary file modified.

GbDebuggerMsvs/stack_frame.cpp

+/*  Copyright � 2011 Chris Spencer <spencercw@gmail.com>
+
+    This program is free software: you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation, either version 3 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
+
+#include "stdafx.h"
+
+#include <fstream>
+
+#include "stack_frame.h"
+
+using std::vector;
+
+// IDebugStackFrame2 methods
+
+HRESULT CGbVsStackFrame::GetCodeContext(IDebugCodeContext2 **ppCodeCxt)
+{
+	*ppCodeCxt = codeContext_;
+	(*ppCodeCxt)->AddRef();
+
+	return S_OK;
+}
+
+HRESULT CGbVsStackFrame::GetDocumentContext(IDebugDocumentContext2 ** /*ppCxt*/)
+{
+	std::ofstream f("C:\\Users\\Chris\\gbem_debug.txt", std::ios_base::out | std::ios_base::binary | std::ios_base::app);
+	f << "CGbVsStackFrame::GetDocumentContext\n";
+	f.close();
+
+	return E_NOTIMPL;
+}
+
+HRESULT CGbVsStackFrame::GetName(BSTR * /*pbstrName*/)
+{
+	std::ofstream f("C:\\Users\\Chris\\gbem_debug.txt", std::ios_base::out | std::ios_base::binary | std::ios_base::app);
+	f << "CGbVsStackFrame::GetName\n";
+	f.close();
+
+	return E_NOTIMPL;
+}
+
+HRESULT CGbVsStackFrame::GetInfo(FRAMEINFO_FLAGS, UINT, FRAMEINFO *pFrameInfo)
+{
+	CopyFrameInfo(&frameInfo_, pFrameInfo);
+	return S_OK;
+}
+
+HRESULT CGbVsStackFrame::GetPhysicalStackRange(UINT64 * /*paddrMin*/, UINT64 * /*paddrMax*/)
+{
+	std::ofstream f("C:\\Users\\Chris\\gbem_debug.txt", std::ios_base::out | std::ios_base::binary | std::ios_base::app);
+	f << "CGbVsStackFrame::GetPhysicalStackRange\n";
+	f.close();
+
+	return E_NOTIMPL;
+}
+
+HRESULT CGbVsStackFrame::GetExpressionContext(IDebugExpressionContext2 ** /*ppExprCxt*/)
+{
+	std::ofstream f("C:\\Users\\Chris\\gbem_debug.txt", std::ios_base::out | std::ios_base::binary | std::ios_base::app);
+	f << "CGbVsStackFrame::GetExpressionContext\n";
+	f.close();
+
+	return E_NOTIMPL;
+}
+
+HRESULT CGbVsStackFrame::GetLanguageInfo(BSTR * /*pbstrLanguage*/, GUID * /*pguidLanguage*/)
+{
+	std::ofstream f("C:\\Users\\Chris\\gbem_debug.txt", std::ios_base::out | std::ios_base::binary | std::ios_base::app);
+	f << "CGbVsStackFrame::GetLanguageInfo\n";
+	f.close();
+
+	return E_NOTIMPL;
+}
+
+HRESULT CGbVsStackFrame::GetDebugProperty(IDebugProperty2 ** /*ppDebugProp*/)
+{
+	std::ofstream f("C:\\Users\\Chris\\gbem_debug.txt", std::ios_base::out | std::ios_base::binary | std::ios_base::app);
+	f << "CGbVsStackFrame::GetDebugProperty\n";
+	f.close();
+
+	return E_NOTIMPL;
+}
+
+HRESULT CGbVsStackFrame::EnumProperties(
+	DEBUGPROP_INFO_FLAGS /*dwFields*/,
+	UINT /*nRadix*/,
+	REFGUID /*guidFilter*/,
+	DWORD /*dwTimeout*/,
+	ULONG * /*pcelt*/,
+	IEnumDebugPropertyInfo2 ** /*ppepi*/)
+{
+	std::ofstream f("C:\\Users\\Chris\\gbem_debug.txt", std::ios_base::out | std::ios_base::binary | std::ios_base::app);
+	f << "CGbVsStackFrame::EnumProperties\n";
+	f.close();
+
+	return E_NOTIMPL;
+}
+
+HRESULT CGbVsStackFrame::GetThread(IDebugThread2 ** /*ppThread*/)
+{
+	std::ofstream f("C:\\Users\\Chris\\gbem_debug.txt", std::ios_base::out | std::ios_base::binary | std::ios_base::app);
+	f << "CGbVsStackFrame::GetThread\n";
+	f.close();
+
+	return E_NOTIMPL;
+}
+
+// CGbVsStackFrame methods
+
+void CGbVsStackFrame::FinalRelease()
+{
+	ReleaseFrameInfo(&frameInfo_);
+}
+
+void CGbVsStackFrame::Init(const FRAMEINFO *frameInfo, IDebugCodeContext2 *codeContext)
+{
+	CopyFrameInfo(frameInfo, &frameInfo_);
+	codeContext_ = codeContext;
+}
+
+void CGbVsStackFrame::CopyFrameInfo(const FRAMEINFO *src, FRAMEINFO *dest)
+{
+	*dest = *src;
+		
+	dest->m_bstrArgs       = SysAllocString(dest->m_bstrArgs);
+	dest->m_bstrFuncName   = SysAllocString(dest->m_bstrFuncName);
+	dest->m_bstrLanguage   = SysAllocString(dest->m_bstrLanguage);
+	dest->m_bstrModule     = SysAllocString(dest->m_bstrModule);
+	dest->m_bstrReturnType = SysAllocString(dest->m_bstrReturnType);
+		
+	if (dest->m_pFrame)
+		dest->m_pFrame->AddRef();
+	if (dest->m_pModule)
+		dest->m_pModule->AddRef();
+}
+
+void CGbVsStackFrame::ReleaseFrameInfo(const FRAMEINFO *frame)
+{
+	SysFreeString(frame->m_bstrArgs);
+	SysFreeString(frame->m_bstrFuncName);
+	SysFreeString(frame->m_bstrLanguage);
+	SysFreeString(frame->m_bstrModule);
+	SysFreeString(frame->m_bstrReturnType);
+
+	if (frame->m_pFrame)
+		frame->m_pFrame->Release();
+	if (frame->m_pModule)
+		frame->m_pModule->Release();
+}
+
+////////////////////////////////////////////////////////////////////////////////////////////////////
+
+// IEnumDebugFrameInfo2 methods
+HRESULT CEnumFrameInfo::Skip(ULONG /*celt*/)
+{
+	return E_NOTIMPL;
+}
+
+HRESULT CEnumFrameInfo::Clone(IEnumDebugFrameInfo2 ** /*ppEnum*/)
+{
+	return E_NOTIMPL;
+}
+
+HRESULT CEnumFrameInfo::Reset()
+{
+	position_ = 0;
+	return S_OK;
+}
+
+HRESULT CEnumFrameInfo::GetCount(ULONG *pcelt)
+{
+	*pcelt = static_cast<ULONG>(stackFrames_.size());
+	return S_OK;
+}
+
+HRESULT CEnumFrameInfo::Next(ULONG celt, FRAMEINFO *rgelt, ULONG *pceltFetched)
+{
+	*pceltFetched = static_cast<ULONG>(stackFrames_.size() - position_);
+	if (celt > *pceltFetched)
+	{
+		*pceltFetched = 0;
+		return S_FALSE;
+	}
+	else if (celt < *pceltFetched)
+	{
+		*pceltFetched = celt;
+	}
+
+	for (ULONG i = 0; i != *pceltFetched; ++i)
+	{
+		CGbVsStackFrame::CopyFrameInfo(&stackFrames_[i], &rgelt[i]);
+	}
+	position_ += *pceltFetched;
+
+	return S_OK;
+}
+
+// CEnumFrameInfo methods
+
+void CEnumFrameInfo::FinalRelease()
+{
+	for (vector<FRAMEINFO>::const_iterator frame = stackFrames_.begin(), end = stackFrames_.end();
+		frame != end; ++frame)
+	{
+		CGbVsStackFrame::ReleaseFrameInfo(&*frame);
+	}
+}
+
+void CEnumFrameInfo::Init(const vector<FRAMEINFO> &stackFrames)
+{
+	stackFrames_ = stackFrames;
+	position_ = 0;
+}

GbDebuggerMsvs/stack_frame.h

+/*  Copyright � 2011 Chris Spencer <spencercw@gmail.com>
+
+    This program is free software: you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation, either version 3 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
+
+#pragma once
+
+#include <vector>
+
+#include <atlbase.h>
+#include <atlcom.h>
+
+#include "GbDebuggerMsvs_i.h"
+
+#if defined(_WIN32_WCE) && !defined(_CE_DCOM) && !defined(_CE_ALLOW_SINGLE_THREADED_OBJECTS_IN_MTA)
+#error "Single-threaded COM objects are not properly supported on Windows CE platform, such as the Windows Mobile platforms that do not include full DCOM support. Define _CE_ALLOW_SINGLE_THREADED_OBJECTS_IN_MTA to force ATL to support creating single-thread COM object's and allow use of it's single-threaded COM object implementations. The threading model in your rgs file was set to 'Free' as that is the only threading model supported in non DCOM Windows CE platforms."
+#endif
+
+using namespace ATL;
+
+class ATL_NO_VTABLE CGbVsStackFrame:
+	public CComObjectRootEx<CComSingleThreadModel>,
+	public CComCoClass<CGbVsStackFrame, &CLSID_GbVsStackFrame>,
+	public IDebugStackFrame2
+{
+public:
+	CGbVsStackFrame()
+	{
+	}
+
+DECLARE_REGISTRY_RESOURCEID(IDR_GBVSSTACKFRAME)
+
+BEGIN_COM_MAP(CGbVsStackFrame)
+	COM_INTERFACE_ENTRY(IDebugStackFrame2)
+END_COM_MAP()
+
+	// IDebugStackFrame2 methods
+	STDMETHOD(GetCodeContext)(IDebugCodeContext2 **ppCodeCxt);
+	STDMETHOD(GetDocumentContext)(IDebugDocumentContext2 **ppCxt);
+	STDMETHOD(GetName)(BSTR *pbstrName);
+	STDMETHOD(GetInfo)(FRAMEINFO_FLAGS dwFieldSpec, UINT nRadix, FRAMEINFO *pFrameInfo);
+	STDMETHOD(GetPhysicalStackRange)(UINT64 *paddrMin, UINT64 *paddrMax);
+	STDMETHOD(GetExpressionContext)(IDebugExpressionContext2 **ppExprCxt);
+	STDMETHOD(GetLanguageInfo)(BSTR *pbstrLanguage, GUID *pguidLanguage);
+	STDMETHOD(GetDebugProperty)(IDebugProperty2 **ppDebugProp);
+	STDMETHOD(EnumProperties)(
+		DEBUGPROP_INFO_FLAGS dwFields,
+		UINT nRadix,
+		REFGUID guidFilter,
+		DWORD dwTimeout,
+		ULONG *pcelt,
+		IEnumDebugPropertyInfo2 **ppepi);
+	STDMETHOD(GetThread)(IDebugThread2 **ppThread);
+
+	DECLARE_PROTECT_FINAL_CONSTRUCT()
+
+	HRESULT FinalConstruct()
+	{
+		return S_OK;
+	}
+
+	void FinalRelease();
+
+public:
+	void Init(const FRAMEINFO *frameInfo, IDebugCodeContext2 *codeContext);
+
+	static void CopyFrameInfo(const FRAMEINFO *src, FRAMEINFO *dest);
+	static void ReleaseFrameInfo(const FRAMEINFO *frame);
+
+protected:
+	FRAMEINFO frameInfo_;
+	CComPtr<IDebugCodeContext2> codeContext_;
+};
+
+OBJECT_ENTRY_AUTO(__uuidof(GbVsStackFrame), CGbVsStackFrame)
+
+////////////////////////////////////////////////////////////////////////////////////////////////////
+
+class ATL_NO_VTABLE CEnumFrameInfo:
+	public CComObjectRootEx<CComSingleThreadModel>,
+	public CComCoClass<CEnumFrameInfo, &CLSID_EnumFrameInfo>,
+	public IEnumDebugFrameInfo2
+{
+public:
+	CEnumFrameInfo():
+	position_(0)
+	{
+	}
+
+DECLARE_REGISTRY_RESOURCEID(IDR_ENUMFRAMEINFO)
+
+BEGIN_COM_MAP(CEnumFrameInfo)
+	COM_INTERFACE_ENTRY(IEnumDebugFrameInfo2)
+END_COM_MAP()
+
+	// IEnumDebugFrameInfo2
+	STDMETHOD(Next)(ULONG celt, FRAMEINFO *rgelt, ULONG *pceltFetched);
+	STDMETHOD(Skip)(ULONG celt);
+	STDMETHOD(Reset)();
+	STDMETHOD(Clone)(IEnumDebugFrameInfo2 **ppEnum);
+	STDMETHOD(GetCount)(ULONG *pcelt);
+
+	DECLARE_PROTECT_FINAL_CONSTRUCT()
+
+	HRESULT FinalConstruct()
+	{
+		return S_OK;
+	}
+
+	void FinalRelease();
+
+public:
+	void Init(const std::vector<FRAMEINFO> &stackFrames);
+
+protected:
+	std::vector<FRAMEINFO> stackFrames_;
+	size_t position_;
+};
+
+OBJECT_ENTRY_AUTO(__uuidof(EnumFrameInfo), CEnumFrameInfo)

gb_emulator/defs.hpp

-/*  Copyright � 2011 Chris Spencer <spencercw@gmail.com>
-
-    This program is free software: you can redistribute it and/or modify
-    it under the terms of the GNU General Public License as published by
-    the Free Software Foundation, either version 3 of the License, or
-    (at your option) any later version.
-
-    This program is distributed in the hope that it will be useful,
-    but WITHOUT ANY WARRANTY; without even the implied warranty of
-    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-    GNU General Public License for more details.
-
-    You should have received a copy of the GNU General Public License
-    along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
-
-#ifndef DEFS_HPP_4E1047E0_FD6C_11E0_9C2D_0002A5D5C51B
-#define DEFS_HPP_4E1047E0_FD6C_11E0_9C2D_0002A5D5C51B
-
-#ifdef GB_EMULATOR_EXPORTS
-#define GB_EMULATOR_API __declspec(dllexport)
-#else
-#define GB_EMULATOR_API __declspec(dllimport)
-#endif
-
-#ifdef GB_DEBUGGER_EXPORTS
-#define GB_DEBUGGER_API __declspec(dllexport)
-#else
-#define GB_DEBUGGER_API __declspec(dllimport)
-#endif
-
-#endif

gb_emulator/include/gb_emulator/defs.hpp

+/*  Copyright � 2011 Chris Spencer <spencercw@gmail.com>
+
+    This program is free software: you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation, either version 3 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
+
+#ifndef DEFS_HPP_4E1047E0_FD6C_11E0_9C2D_0002A5D5C51B
+#define DEFS_HPP_4E1047E0_FD6C_11E0_9C2D_0002A5D5C51B
+
+#ifdef GB_EMULATOR_EXPORTS
+#define GB_EMULATOR_API __declspec(dllexport)
+#else
+#define GB_EMULATOR_API __declspec(dllimport)
+#endif
+
+#ifdef GB_DEBUGGER_EXPORTS
+#define GB_DEBUGGER_API __declspec(dllexport)
+#else
+#define GB_DEBUGGER_API __declspec(dllimport)
+#endif
+
+#endif

gb_emulator/include/gb_emulator/gb.hpp

 	bool gbc_;
 
 	// Filenames
+	boost::filesystem::path biosFilename_;
 	boost::filesystem::path romFilename_;
 	boost::filesystem::path saveFilename_;
 

gb_emulator/include/gb_emulator/gb_debugger.h

 #pragma warning(disable: 4251)
 #endif
 
+#include <stdint.h>
+
 #include <set>
+#include <vector>
 
 #include <boost/asio/io_service.hpp>
+#include <boost/filesystem/path.hpp>
 #include <boost/function.hpp>
+#include <boost/thread/condition_variable.hpp>
+#include <boost/thread/mutex.hpp>
 
 #include <gb_emulator/defs.hpp>
 
 class Gb;
 
+struct GB_EMULATOR_API GbStackFrame
+{
+	boost::filesystem::path module;
+	uint16_t stackAddrMin;
+	uint16_t stackAddrMax;
+	uint16_t instructionAddr;
+};
+
 class GB_EMULATOR_API GbDebugger
 {
 private:
 	void execute();
 	void suspend();
 
+	boost::filesystem::path modulePath();
+	std::vector<GbStackFrame> stackFrames();
+
 private:
 	Gb &gb_;
 	boost::asio::io_service ioService_;
 	boost::asio::io_service::work work_;
 	std::set<Listener *> listeners_;
 
+	boost::filesystem::path modulePath_;
+	boost::mutex modulePathMutex_;
+	boost::condition_variable modulePathCv_;
+
+	std::vector<GbStackFrame> stackFrames_;
+	boost::mutex stackFramesMutex_;
+	boost::condition_variable stackFramesCv_;
+
 	GbDebugger(Gb &gb);
 	~GbDebugger();
 
 	void doExecute();
 	void doSuspend();
 
+	void doModulePath();
+	void doStackFrames();
+
+	boost::filesystem::path getModulePath();
+
 	// Disabled operations
 	GbDebugger(const GbDebugger &);
 	GbDebugger & operator=(const GbDebugger &);

gb_emulator/src/gb.cpp

 	}
 
 	// Load the BIOS
-	fs::path path = biosPath;
-	path.make_preferred();
+	biosFilename_ = biosPath;
+	biosFilename_.make_preferred();
 
 	// Check the file
-	if (!fs::exists(path))
+	if (!fs::exists(biosFilename_))
 	{
-		throw runtime_error(path.string() + " does not exist");
+		throw runtime_error(biosFilename_.string() + " does not exist");
 	}
-	if (!fs::is_regular_file(path))
+	if (!fs::is_regular_file(biosFilename_))
 	{
-		throw runtime_error(path.string() + " is not a file");
+		throw runtime_error(biosFilename_.string() + " is not a file");
 	}
-	if (fs::file_size(path) != BIOS_SIZE)
+	if (fs::file_size(biosFilename_) != BIOS_SIZE)
 	{
 		ostringstream oss;
-		oss << path.string() << " is the wrong size; it must be " << BIOS_SIZE << " bytes";
+		oss << biosFilename_.string() << " is the wrong size; it must be " << BIOS_SIZE << " bytes";
 		throw runtime_error(oss.str());
 	}
 
 	// Read the file
-	fs::ifstream f(path, ios_base::in | ios_base::binary);
+	fs::ifstream f(biosFilename_, ios_base::in | ios_base::binary);
 	if (!f)
 	{
-		throw runtime_error("failed to open " + path.string() + " for reading");
+		throw runtime_error("failed to open " + biosFilename_.string() + " for reading");
 	}
 	bios_.resize(BIOS_SIZE);
 	if (!f.read(reinterpret_cast<char *>(&*bios_.begin()), BIOS_SIZE))
 	{
-		throw runtime_error("failed to read " + path.string());
+		throw runtime_error("failed to read " + biosFilename_.string());
 	}
 }
 

gb_emulator/src/gb_debugger.cpp

 #include <gb_emulator/gb_debugger.h>
 
 #include <boost/bind.hpp>
+#include <boost/thread/locks.hpp>
 
 #include <gb_emulator/gb.hpp>
 
+namespace fs = boost::filesystem;
+using boost::lock_guard;
+using boost::mutex;
+using boost::unique_lock;
 using std::set;
+using std::vector;
 
 GbDebugger::GbDebugger(Gb &gb):
 gb_(gb),
 	ioService_.post(boost::bind(&GbDebugger::doSuspend, this));
 }
 
+fs::path GbDebugger::modulePath()
+{
+	unique_lock<mutex> lock(modulePathMutex_);
+	modulePath_.clear();
+	ioService_.post(boost::bind(&GbDebugger::doModulePath, this));
+	
+	while (modulePath_.empty())
+	{
+		modulePathCv_.wait(lock);
+	}
+
+	return modulePath_;
+}
+
+vector<GbStackFrame> GbDebugger::stackFrames()
+{
+	unique_lock<mutex> lock(stackFramesMutex_);
+	stackFrames_.clear();
+	ioService_.post(boost::bind(&GbDebugger::doStackFrames, this));
+	
+	while (stackFrames_.empty())
+	{
+		stackFramesCv_.wait(lock);
+	}
+
+	return stackFrames_;
+}
+
 ////////////////////////////////////////////////////////////////////////////////////////////////////
 // Implementation functions
 
 		(*listener)->onSuspend();
 	}
 }
+
+void GbDebugger::doModulePath()
+{
+	// Get the module path
+	fs::path modulePath = getModulePath();
+	
+	// Notify the caller that the data is ready
+	lock_guard<mutex> lock(modulePathMutex_);
+	modulePath_ = modulePath;
+	modulePathCv_.notify_one();
+}
+
+void GbDebugger::doStackFrames()
+{
+	// Generate the stack trace
+	vector<GbStackFrame> stackFrames;
+	GbStackFrame frame;
+	frame.module = getModulePath();
+	frame.stackAddrMin = gb_.cpu_.sp;
+	frame.stackAddrMax = gb_.cpu_.sp;
+	frame.instructionAddr = gb_.cpu_.pc;
+	stackFrames.push_back(frame);
+	
+	// Notify the caller that the data is ready
+	lock_guard<mutex> lock(stackFramesMutex_);
+	stackFrames_ = stackFrames;
+	stackFramesCv_.notify_one();
+}
+
+fs::path GbDebugger::getModulePath()
+{
+	if (gb_.mem_.ioPorts[BLCK] && !gb_.romFilename_.empty())
+	{
+		return gb_.romFilename_;
+	}
+	else
+	{
+		return gb_.biosFilename_;
+	}
+}