Skip to content

Commit

Permalink
1. More 4548: more correct stack on x86, source file/line in stack & …
Browse files Browse the repository at this point in the history
…main dialog.

2. Process forward slash in UnquoteExternal() too.
  • Loading branch information
alabuzhev committed Feb 20, 2016
1 parent 3e9f717 commit d5e5c75
Show file tree
Hide file tree
Showing 8 changed files with 176 additions and 71 deletions.
8 changes: 7 additions & 1 deletion far/changelog
@@ -1,4 +1,10 @@
w17 20.02.2016 15:03:22 +0300 - build 4556
drkns 20.02.2016 15:56:08 +0200 - build 4557

1. Продолжение 4548: показываем более правильный стек на x86, показываем source file/line в стеке и в основном диалоге.

2. В UnquoteExternal() учтём прямой слеш тоже.

w17 20.02.2016 15:03:22 +0300 - build 4556

1. Уточнение 4523. Учтём "D:\Path Name"\ в UnquoteExternal()

Expand Down
31 changes: 15 additions & 16 deletions far/farexcpt.cpp
Expand Up @@ -59,9 +59,8 @@ void CreatePluginStartupInfo(const Plugin *pPlugin, PluginStartupInfo *PSI, FarS

#define LAST_BUTTON 14

static void ShowStackTraceImpl(const std::vector<void*>& Trace)
static void ShowStackTraceImpl(const std::vector<string>& Symbols)
{
const auto Symbols = tracer::GetSymbols(Trace);
if (Global && Global->WindowManager && !Global->WindowManager->ManagerIsDown())
{
Message(MSG_WARNING | MSG_LEFTALIGN, MSG(MExcTrappedException), Symbols, make_vector<string>(MSG(MOk)));
Expand All @@ -79,7 +78,7 @@ static void ShowStackTraceImpl(const std::vector<void*>& Trace)
template<class T>
void ShowStackTrace(const T& e)
{
return ShowStackTraceImpl(tracer::GetInstance()->get(e));
return ShowStackTraceImpl(tracer::get(e));
}

intptr_t ExcDlgProc(Dialog* Dlg,intptr_t Msg,intptr_t Param1,void* Param2)
Expand Down Expand Up @@ -131,7 +130,7 @@ intptr_t ExcDlgProc(Dialog* Dlg,intptr_t Msg,intptr_t Param1,void* Param2)
return FALSE;

case 12: // stack
ShowStackTrace(reinterpret_cast<const EXCEPTION_RECORD*>(Dlg->SendMessage(DM_GETDLGDATA, 0, nullptr)));
ShowStackTrace(reinterpret_cast<const EXCEPTION_POINTERS*>(Dlg->SendMessage(DM_GETDLGDATA, 0, nullptr)));
return FALSE;
}
}
Expand All @@ -156,24 +155,24 @@ enum reply
reply_ignore,
};

static reply ExcDialog(const string& ModuleName, LPCWSTR Exception, const EXCEPTION_RECORD* xr)
static reply ExcDialog(const string& ModuleName, LPCWSTR Exception, const EXCEPTION_POINTERS* xp)
{
// TODO: Far Dialog is not the best choice for exception reporting
// replace with something trivial

const auto strAddr = L"0x" + to_hex_wstring(reinterpret_cast<uintptr_t>(xr->ExceptionAddress));
const auto strAddr = tracer::get(xp->ExceptionRecord->ExceptionAddress);

FarDialogItem EditDlgData[]=
{
{DI_DOUBLEBOX,3,1,76,8,0,nullptr,nullptr,0,MSG(MExcTrappedException)},
{DI_TEXT, 5,2, 17,2,0,nullptr,nullptr,0,MSG(MExcException)},
{DI_TEXT, 18,2, 70,2,0,nullptr,nullptr,0,Exception},
{DI_TEXT, 18,2, 75,2,0,nullptr,nullptr,0,Exception},
{DI_TEXT, 5,3, 17,3,0,nullptr,nullptr,0,MSG(MExcAddress)},
{DI_TEXT, 18,3, 70,3,0,nullptr,nullptr,0,strAddr.data()},
{DI_EDIT, 18,3, 75,3,0,nullptr,nullptr,DIF_READONLY|DIF_SELECTONENTRY,strAddr.data()},
{DI_TEXT, 5,4, 17,4,0,nullptr,nullptr,0,MSG(MExcFunction)},
{DI_TEXT, 18,4, 70,4,0,nullptr,nullptr,0,gFrom},
{DI_TEXT, 18,4, 75,4,0,nullptr,nullptr,0,gFrom},
{DI_TEXT, 5,5, 17,5,0,nullptr,nullptr,0,MSG(MExcModule)},
{DI_EDIT, 18,5, 70,5,0,nullptr,nullptr,DIF_READONLY|DIF_SELECTONENTRY,ModuleName.data()},
{DI_EDIT, 18,5, 75,5,0,nullptr,nullptr,DIF_READONLY|DIF_SELECTONENTRY,ModuleName.data()},
{DI_TEXT, -1,6, 0,6,0,nullptr,nullptr,DIF_SEPARATOR,L""},
{DI_BUTTON, 0,7, 0,7,0,nullptr,nullptr,DIF_DEFAULTBUTTON|DIF_FOCUS|DIF_CENTERGROUP, MSG(PluginModule? MExcUnload : MExcTerminate)},
{DI_BUTTON, 0,7, 0,7,0,nullptr,nullptr,DIF_CENTERGROUP,MSG(MExcDebugger)},
Expand All @@ -182,7 +181,7 @@ static reply ExcDialog(const string& ModuleName, LPCWSTR Exception, const EXCEPT
{DI_BUTTON, 0,7, 0,7,0,nullptr,nullptr,DIF_CENTERGROUP,MSG(MIgnore)},
};
auto EditDlg = MakeDialogItemsEx(EditDlgData);
const auto Dlg = Dialog::create(EditDlg, ExcDlgProc, const_cast<void*>(reinterpret_cast<const void*>(xr)));
const auto Dlg = Dialog::create(EditDlg, ExcDlgProc, const_cast<void*>(reinterpret_cast<const void*>(xp)));
Dlg->SetDialogMode(DMODE_WARNINGSTYLE|DMODE_NOPLUGINS);
Dlg->SetPosition(-1, -1, 80, 10);
Dlg->Process();
Expand All @@ -201,9 +200,9 @@ static reply ExcDialog(const string& ModuleName, LPCWSTR Exception, const EXCEPT
}
}

static void ExcDump(const string& ModuleName, LPCWSTR Exception, const EXCEPTION_RECORD* xr)
static void ExcDump(const string& ModuleName, LPCWSTR Exception, const EXCEPTION_POINTERS* xp)
{
const auto strAddr = L"0x" + to_hex_wstring(reinterpret_cast<uintptr_t>(xr->ExceptionAddress));
const auto strAddr = tracer::get(xp->ExceptionRecord->ExceptionAddress);

string Msg[4];
if (LanguageLoaded())
Expand All @@ -229,7 +228,7 @@ static void ExcDump(const string& ModuleName, LPCWSTR Exception, const EXCEPTION

std::wcerr << Dump << std::endl;

ShowStackTrace(xr);
ShowStackTrace(xp);
}

template<char c0, char c1, char c2, char c3>
Expand Down Expand Up @@ -429,12 +428,12 @@ static bool ProcessSEHExceptionImpl(EXCEPTION_POINTERS *xp)

if (Global && Global->WindowManager && !Global->WindowManager->ManagerIsDown())
{
MsgCode = ExcDialog(strFileName, Exception, xr);
MsgCode = ExcDialog(strFileName, Exception, xp);
ShowMessages=TRUE;
}
else
{
ExcDump(strFileName, Exception, xr);
ExcDump(strFileName, Exception, xp);
}

if (ShowMessages && !PluginModule)
Expand Down
33 changes: 24 additions & 9 deletions far/imports.cpp
Expand Up @@ -85,7 +85,6 @@ ImportedFunctions::ImportedFunctions():
INIT_IMPORT(m_kernel32, TzSpecificLocalTimeToSystemTime),
INIT_IMPORT(m_kernel32, AddVectoredExceptionHandler),
INIT_IMPORT(m_kernel32, RemoveVectoredExceptionHandler),
INIT_IMPORT(m_kernel32, RtlCaptureStackBackTrace),

INIT_IMPORT(m_shell32, SHCreateAssociationRegistration),

Expand All @@ -104,9 +103,12 @@ ImportedFunctions::ImportedFunctions():
INIT_IMPORT(m_netapi32, NetDfsGetInfo),

INIT_IMPORT(m_dbghelp, MiniDumpWriteDump),
INIT_IMPORT(m_dbghelp, StackWalk64),
INIT_IMPORT(m_dbghelp, SymInitialize),
INIT_IMPORT(m_dbghelp, SymCleanup),
INIT_IMPORT(m_dbghelp, SymFromAddr)
INIT_IMPORT(m_dbghelp, SymFromAddr),
INIT_IMPORT(m_dbghelp, SymSetOptions),
INIT_IMPORT(m_dbghelp, SymGetLineFromAddr64)

#undef INIT_IMPORT
{
Expand Down Expand Up @@ -301,13 +303,6 @@ ULONG WINAPI ImportedFunctions::stub_RemoveVectoredExceptionHandler(PVOID Handle
return 0;
}

USHORT WINAPI ImportedFunctions::stub_RtlCaptureStackBackTrace(ULONG FramesToSkip, ULONG FramesToCapture, PVOID BackTrace, PULONG BackTraceHash)
{
// TODO: log
SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
return 0;
}

// shell32
HRESULT STDAPICALLTYPE ImportedFunctions::stub_SHCreateAssociationRegistration(REFIID riid, void ** ppv)
{
Expand Down Expand Up @@ -388,6 +383,12 @@ BOOL WINAPI ImportedFunctions::stub_MiniDumpWriteDump(HANDLE Process, DWORD Proc
return FALSE;
}

BOOL WINAPI ImportedFunctions::stub_StackWalk64(DWORD MachineType, HANDLE Process, HANDLE Thread, LPSTACKFRAME64 StackFrame, PVOID ContextRecord, PREAD_PROCESS_MEMORY_ROUTINE64 ReadMemoryRoutine, PFUNCTION_TABLE_ACCESS_ROUTINE64 FunctionTableAccessRoutine, PGET_MODULE_BASE_ROUTINE64 GetModuleBaseRoutine, PTRANSLATE_ADDRESS_ROUTINE64 TranslateAddress)
{
SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
return FALSE;
}

BOOL WINAPI ImportedFunctions::stub_SymInitialize(HANDLE Process, PCSTR UserSearchPath, BOOL InvadeProcess)
{
SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
Expand All @@ -405,3 +406,17 @@ BOOL WINAPI ImportedFunctions::stub_SymFromAddr(HANDLE Process, DWORD64 Address,
SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
return FALSE;
}

DWORD WINAPI ImportedFunctions::stub_SymSetOptions(DWORD SymOptions)
{
SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
return 0;
}

BOOL WINAPI ImportedFunctions::stub_SymGetLineFromAddr64(HANDLE Process, DWORD64 Addr, PDWORD Displacement, PIMAGEHLP_LINE64 Line)
{
SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
return FALSE;
}


5 changes: 3 additions & 2 deletions far/imports.hpp
Expand Up @@ -110,7 +110,6 @@ public: const unique_function_pointer<decltype(&ImportedFunctions::stub_##NAME),
DECLARE_IMPORT_FUNCTION(BOOL, WINAPI, TzSpecificLocalTimeToSystemTime, const TIME_ZONE_INFORMATION* TimeZoneInformation, const SYSTEMTIME* LocalTime, LPSYSTEMTIME UniversalTime);
DECLARE_IMPORT_FUNCTION(PVOID, WINAPI, AddVectoredExceptionHandler, ULONG First, PVECTORED_EXCEPTION_HANDLER Handler);
DECLARE_IMPORT_FUNCTION(ULONG, WINAPI, RemoveVectoredExceptionHandler, PVOID Handler);
DECLARE_IMPORT_FUNCTION(USHORT, WINAPI, RtlCaptureStackBackTrace, ULONG FramesToSkip, ULONG FramesToCapture, PVOID BackTrace, PULONG BackTraceHash);

// shell32
DECLARE_IMPORT_FUNCTION(HRESULT, STDAPICALLTYPE, SHCreateAssociationRegistration, REFIID riid, void** ppv);
Expand All @@ -135,10 +134,12 @@ public: const unique_function_pointer<decltype(&ImportedFunctions::stub_##NAME),

// dbghelp
DECLARE_IMPORT_FUNCTION(BOOL, WINAPI, MiniDumpWriteDump, HANDLE Process, DWORD ProcessId, HANDLE File, MINIDUMP_TYPE DumpType, PMINIDUMP_EXCEPTION_INFORMATION ExceptionParam, PMINIDUMP_USER_STREAM_INFORMATION UserStreamParam, PMINIDUMP_CALLBACK_INFORMATION CallbackParam);
DECLARE_IMPORT_FUNCTION(BOOL, WINAPI, StackWalk64, DWORD MachineType, HANDLE Process, HANDLE Thread, LPSTACKFRAME64 StackFrame, PVOID ContextRecord, PREAD_PROCESS_MEMORY_ROUTINE64 ReadMemoryRoutine, PFUNCTION_TABLE_ACCESS_ROUTINE64 FunctionTableAccessRoutine, PGET_MODULE_BASE_ROUTINE64 GetModuleBaseRoutine, PTRANSLATE_ADDRESS_ROUTINE64 TranslateAddress);
DECLARE_IMPORT_FUNCTION(BOOL, WINAPI, SymInitialize, HANDLE Process, PCSTR UserSearchPath, BOOL InvadeProcess);
DECLARE_IMPORT_FUNCTION(BOOL, WINAPI, SymCleanup, HANDLE Process);
DECLARE_IMPORT_FUNCTION(BOOL, WINAPI, SymFromAddr, HANDLE Process, DWORD64 Address, PDWORD64 Displacement, PSYMBOL_INFO Symbol);

DECLARE_IMPORT_FUNCTION(DWORD,WINAPI, SymSetOptions, DWORD SymOptions);
DECLARE_IMPORT_FUNCTION(BOOL, WINAPI, SymGetLineFromAddr64, HANDLE Process, DWORD64 Addr, PDWORD Displacement, PIMAGEHLP_LINE64 Line);
#undef DECLARE_IMPORT_FUNCTION

private:
Expand Down
2 changes: 1 addition & 1 deletion far/strmix.cpp
Expand Up @@ -525,7 +525,7 @@ void UnquoteExternal(string &strStr)
strStr.pop_back();
strStr.erase(0, 1);
}
else if (len >= 3 && strStr[len-1] == L'\\' && strStr[len-2] == L'\"') // '"D:\Path Name"\'
else if (len >= 3 && IsSlash(strStr[len-1]) && strStr[len-2] == L'\"') // '"D:\Path Name"\'
{
strStr.erase(len-2, 1);
strStr.erase(0, 1);
Expand Down
140 changes: 108 additions & 32 deletions far/tracer.cpp
Expand Up @@ -34,35 +34,67 @@ THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include "tracer.hpp"
#include "imports.hpp"

static std::vector<void*> GetBackTrace()
static std::vector<const void*> GetBackTrace(const EXCEPTION_POINTERS* ExceptionInfo)
{
// ## Windows Server 2003 and Windows XP:
// ## The sum of the FramesToSkip and FramesToCapture parameters must be less than 63.
const int MaxCallers = 62;
std::vector<void*> Result(MaxCallers);
auto FramesCount = Imports().RtlCaptureStackBackTrace(0, MaxCallers, Result.data(), nullptr);
Result.resize(FramesCount);
std::vector<const void*> Result;

// StackWalk64() may modify context record passed to it, so we will use a copy.
auto ContextRecord = *ExceptionInfo->ContextRecord;
STACKFRAME64 StackFrame = {};
#if defined(_WIN64)
int machine_type = IMAGE_FILE_MACHINE_AMD64;
StackFrame.AddrPC.Offset = ContextRecord.Rip;
StackFrame.AddrFrame.Offset = ContextRecord.Rbp;
StackFrame.AddrStack.Offset = ContextRecord.Rsp;
#else
int machine_type = IMAGE_FILE_MACHINE_I386;
StackFrame.AddrPC.Offset = ContextRecord.Eip;
StackFrame.AddrFrame.Offset = ContextRecord.Ebp;
StackFrame.AddrStack.Offset = ContextRecord.Esp;
#endif
StackFrame.AddrPC.Mode = AddrModeFlat;
StackFrame.AddrFrame.Mode = AddrModeFlat;
StackFrame.AddrStack.Mode = AddrModeFlat;

block_ptr<SYMBOL_INFO> Symbol(sizeof(SYMBOL_INFO) + 256);
Symbol->MaxNameLen = 255;
Symbol->SizeOfStruct = sizeof(SYMBOL_INFO);

while (Imports().StackWalk64(machine_type, GetCurrentProcess(), GetCurrentThread(), &StackFrame, &ContextRecord, nullptr, nullptr, nullptr, nullptr))
{
Result.push_back((const void*)StackFrame.AddrPC.Offset);
}

return Result;
}

std::vector<string> tracer::GetSymbols(const std::vector<void*>& BackTrace)
static std::vector<string> GetSymbols(const std::vector<const void*>& BackTrace)
{
std::vector<string> Result;

const auto Process = GetCurrentProcess();
if (!Imports().SymInitialize(Process, nullptr, TRUE))
return Result;
SCOPE_EXIT{ Imports().SymCleanup(Process); };

const auto MaxNameLen = 2047;
const auto MaxNameLen = MAX_SYM_NAME;
block_ptr<SYMBOL_INFO> Symbol(sizeof(SYMBOL_INFO) + MaxNameLen + 1);
Symbol->SizeOfStruct = sizeof(SYMBOL_INFO);
Symbol->MaxNameLen = MaxNameLen;

Imports().SymSetOptions(SYMOPT_LOAD_LINES);
IMAGEHLP_LINE64 Line = { sizeof(Line) };
DWORD Displacement;

std::wostringstream Stream;
for (size_t i = 0, size = BackTrace.size(); i != size; ++i)
FOR(const auto i, BackTrace)
{
Imports().SymFromAddr(Process, reinterpret_cast<DWORD_PTR>(BackTrace[i]), nullptr, Symbol.get());
Stream << i << ": " << BackTrace[i] << " " << Symbol->Name << " - 0x" << Symbol->Address;
Stream << L"0x" << i;
const auto Address = reinterpret_cast<DWORD_PTR>(i);
if (Imports().SymFromAddr(Process, Address, nullptr, Symbol.get()))
{
Stream << " " << Symbol->Name;
}
if (Imports().SymGetLineFromAddr64(Process, Address, &Displacement, &Line))
{
Stream << L" (" << Line.FileName << L":" << Line.LineNumber << L")";
}
Result.emplace_back(Stream.str());
Stream.str(string());
}
Expand All @@ -71,11 +103,11 @@ std::vector<string> tracer::GetSymbols(const std::vector<void*>& BackTrace)

LONG WINAPI StackLogger(EXCEPTION_POINTERS *xp)
{
if (xp->ExceptionRecord->ExceptionCode == 0xE06D7363) // MS C++ exception
tracer::GetInstance()->store(*reinterpret_cast<const std::exception*>(xp->ExceptionRecord->ExceptionInformation[1]), GetBackTrace());
else
tracer::GetInstance()->store(xp->ExceptionRecord, GetBackTrace());

static const auto MSCPPExceptionCode = 0xE06D7363;
if (xp->ExceptionRecord->ExceptionCode == MSCPPExceptionCode)
{
tracer::GetInstance()->store(*reinterpret_cast<const std::exception*>(xp->ExceptionRecord->ExceptionInformation[1]), xp);
}
return EXCEPTION_CONTINUE_SEARCH;
}

Expand All @@ -97,27 +129,71 @@ tracer::~tracer()
sTracer = nullptr;
}


void tracer::store(const EXCEPTION_RECORD* Record, std::vector<void*>&& BackTrace)
void tracer::store(const std::exception& e, const EXCEPTION_POINTERS* ExceptionInfo)
{
SCOPED_ACTION(CriticalSectionLock)(m_CS);
m_SehMap.insert(std::make_pair(Record, std::move(BackTrace)));
exception_context Context = { *ExceptionInfo->ExceptionRecord, *ExceptionInfo->ContextRecord };
m_StdMap.insert(std::make_pair(&e, Context));
}

void tracer::store(const std::exception& e, std::vector<void*>&& BackTrace)
bool tracer::get_context(const std::exception& e, exception_context& Context) const
{
SCOPED_ACTION(CriticalSectionLock)(m_CS);
m_StdMap.insert(std::make_pair(&e, std::move(BackTrace)));
auto Iter = m_StdMap.find(&e);
if (Iter == m_StdMap.end())
{
return false;
}
Context = Iter->second;
return true;
}

std::vector<void*> tracer::get(const EXCEPTION_RECORD* Record)
std::vector<string> tracer::get(const std::exception& e)
{
SCOPED_ACTION(CriticalSectionLock)(m_CS);
return m_SehMap[Record];
exception_context Context;
if (!tracer::GetInstance()->get_context(e, Context))
{
return std::vector<string>();
}
EXCEPTION_POINTERS xp = { &Context.ExceptionRecord, &Context.ContextRecord };

SymInitialise();
SCOPE_EXIT{ SymCleanup(); };

return GetSymbols(GetBackTrace(&xp));
}

std::vector<void*> tracer::get(const std::exception& e)
std::vector<string> tracer::get(const EXCEPTION_POINTERS* e)
{
SCOPED_ACTION(CriticalSectionLock)(m_CS);
return m_StdMap[&e];
SymInitialise();
SCOPE_EXIT{ SymCleanup(); };

return GetSymbols(GetBackTrace(e));
}

string tracer::get(const void* Address)
{
SymInitialise();
SCOPE_EXIT{ SymCleanup(); };

return GetSymbols(make_vector<const void*>(Address)).front();
}

bool tracer::m_SymInitialised;

bool tracer::SymInitialise()
{
if (!m_SymInitialised)
{
m_SymInitialised = Imports().SymInitialize(GetCurrentProcess(), nullptr, TRUE) != FALSE;
}
return m_SymInitialised;
}

void tracer::SymCleanup()
{
if (m_SymInitialised)
{
m_SymInitialised = !Imports().SymCleanup(GetCurrentProcess());
}
}

0 comments on commit d5e5c75

Please sign in to comment.