Move GetStackTrace and Demangle to wpiutil, add Windows support (#1819)

For Windows, import the StackWalker source (https://github.com/JochenKalmbach/StackWalker)
plus PR 35 in that repo, with a few simplifications to StackWalker.h.
This commit is contained in:
Peter Johnson
2019-08-12 23:45:45 -07:00
committed by GitHub
parent ef037457e5
commit f1d71da8a9
15 changed files with 1809 additions and 73 deletions

View File

@@ -39,6 +39,7 @@ popper.js wpiutil/src/main/native/resources/popper-*
units wpiutil/src/main/native/include/units/units.h
Eigen wpiutil/src/main/native/include/Eigen/
wpiutil/src/main/native/include/unsupported/
StackWalker wpiutil/src/main/native/windows/StackWalker.*
==============================================================================
@@ -746,3 +747,32 @@ Exhibit B - "Incompatible With Secondary Licenses" Notice
This Source Code Form is "Incompatible With Secondary Licenses", as
defined by the Mozilla Public License, v. 2.0.
===================
StackWalker License
===================
Copyright (c) 2005-2013, Jochen Kalmbach
All rights reserved.
Redistribution and use in source and binary forms, with or without modification,
are permitted provided that the following conditions are met:
Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.
Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
Neither the name of Jochen Kalmbach nor the names of its contributors may be
used to endorse or promote products derived from this software without
specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

View File

@@ -1,5 +1,5 @@
/*----------------------------------------------------------------------------*/
/* Copyright (c) 2008-2018 FIRST. All Rights Reserved. */
/* Copyright (c) 2008-2019 FIRST. All Rights Reserved. */
/* Open Source Software - may be modified and shared by FRC teams. The code */
/* must be accompanied by the FIRST BSD license file in the root directory of */
/* the project. */
@@ -8,6 +8,7 @@
#include "frc/Error.h"
#include <wpi/Path.h>
#include <wpi/StackTrace.h>
#include "frc/DriverStation.h"
#include "frc/Timer.h"
@@ -84,7 +85,7 @@ void Error::Report() {
true, m_code, m_message,
m_function + wpi::Twine(" [") + wpi::sys::path::filename(m_filename) +
wpi::Twine(':') + wpi::Twine(m_lineNumber) + wpi::Twine(']'),
GetStackTrace(4));
wpi::GetStackTrace(4));
}
void Error::Clear() {

View File

@@ -20,6 +20,7 @@
#include <hal/HAL.h>
#include <wpi/Path.h>
#include <wpi/SmallString.h>
#include <wpi/StackTrace.h>
#include <wpi/raw_ostream.h>
#include "frc/ErrorBase.h"
@@ -47,7 +48,7 @@ bool wpi_assert_impl(bool conditionValue, const wpi::Twine& conditionText,
errorStream << "failed: " << message << "\n";
}
std::string stack = GetStackTrace(2);
std::string stack = wpi::GetStackTrace(2);
// Print the error and send it to the DriverStation
HAL_SendError(1, 1, 0, errorBuf.c_str(), locBuf.c_str(), stack.c_str(), 1);
@@ -86,7 +87,7 @@ void wpi_assertEqual_common_impl(const wpi::Twine& valueA,
errorStream << "failed: " << message << "\n";
}
std::string trace = GetStackTrace(3);
std::string trace = wpi::GetStackTrace(3);
// Print the error and send it to the DriverStation
HAL_SendError(1, 1, 0, errorBuf.c_str(), locBuf.c_str(), trace.c_str(), 1);
@@ -115,59 +116,3 @@ bool wpi_assertNotEqual_impl(int valueA, int valueB,
}
return valueA != valueB;
}
namespace frc {
#ifndef _WIN32
/**
* Demangle a C++ symbol, used for printing stack traces.
*/
static std::string demangle(char const* mangledSymbol) {
char buffer[256];
size_t length;
int32_t status;
if (std::sscanf(mangledSymbol, "%*[^(]%*[(]%255[^)+]", buffer)) {
char* symbol = abi::__cxa_demangle(buffer, nullptr, &length, &status);
if (status == 0) {
return symbol;
} else {
// If the symbol couldn't be demangled, it's probably a C function,
// so just return it as-is.
return buffer;
}
}
// If everything else failed, just return the mangled symbol
return mangledSymbol;
}
std::string GetStackTrace(int offset) {
void* stackTrace[128];
int stackSize = backtrace(stackTrace, 128);
char** mangledSymbols = backtrace_symbols(stackTrace, stackSize);
wpi::SmallString<1024> buf;
wpi::raw_svector_ostream trace(buf);
for (int i = offset; i < stackSize; i++) {
// Only print recursive functions once in a row.
if (i == 0 || stackTrace[i] != stackTrace[i - 1]) {
trace << "\tat " << demangle(mangledSymbols[i]) << "\n";
}
}
std::free(mangledSymbols);
return trace.str();
}
#else
static std::string demangle(char const* mangledSymbol) {
return "no demangling on windows";
}
std::string GetStackTrace(int offset) { return "no stack trace on windows"; }
#endif
} // namespace frc

View File

@@ -71,14 +71,3 @@ bool wpi_assertNotEqual_impl(int valueA, int valueB,
const wpi::Twine& valueBString,
const wpi::Twine& message, wpi::StringRef fileName,
int lineNumber, wpi::StringRef funcName);
namespace frc {
/**
* Get a stack trace, ignoring the first "offset" symbols.
*
* @param offset The number of symbols at the top of the stack to ignore
*/
std::string GetStackTrace(int offset);
} // namespace frc

View File

@@ -70,6 +70,7 @@ generatedFileExclude {
src/main/native/resources/
src/test/native/cpp/UnitsTest\.cpp$
src/test/native/cpp/llvm/
src/main/native/windows/StackWalker
}
licenseUpdateExclude {

View File

@@ -63,6 +63,8 @@ endif()
GENERATE_RESOURCES(src/main/native/resources generated/main/cpp WPI wpi wpiutil_resources_src)
file(GLOB_RECURSE wpiutil_native_src src/main/native/cpp/*.cpp)
file(GLOB_RECURSE wpiutil_unix_src src/main/native/unix/*.cpp)
file(GLOB_RECURSE wpiutil_windows_src src/main/native/windows/*.cpp)
file(GLOB uv_native_src src/main/native/libuv/src/*.cpp)
@@ -128,7 +130,7 @@ if (NOT USE_VCPKG_LIBUV)
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/src/main/native/libuv/include>
$<INSTALL_INTERFACE:${include_dest}/wpiutil>)
if(NOT MSVC)
target_sources(wpiutil PRIVATE ${uv_unix_src})
target_sources(wpiutil PRIVATE ${wpiutil_unix_src} ${uv_unix_src})
if (APPLE)
target_sources(wpiutil PRIVATE ${uv_darwin_src})
else()
@@ -136,7 +138,7 @@ if (NOT USE_VCPKG_LIBUV)
endif()
target_compile_definitions(wpiutil PRIVATE -D_GNU_SOURCE)
else()
target_sources(wpiutil PRIVATE ${uv_windows_src})
target_sources(wpiutil PRIVATE ${wpiutil_windows_src} ${uv_windows_src})
if(BUILD_SHARED_LIBS)
target_compile_definitions(wpiutil PRIVATE -DBUILDING_UV_SHARED)
endif()

View File

@@ -63,6 +63,16 @@ ext {
srcDirs 'src/main/native/include', 'src/main/native/libuv/include', 'src/main/native/libuv/src'
}
}
wpiutilUnixCpp(CppSourceSet) {
source {
srcDirs 'src/main/native/unix'
include '**/*.cpp'
}
exportedHeaders {
srcDirs 'src/main/native/include', 'src/main/native/cpp'
include '**/*.h'
}
}
}
}
if (it.targetPlatform.operatingSystem.isWindows()) {
@@ -79,6 +89,16 @@ ext {
srcDirs 'src/main/native/include', 'src/main/native/libuv/include', 'src/main/native/libuv/src'
}
}
wpiutilWindowsCpp(CppSourceSet) {
source {
srcDirs 'src/main/native/windows'
include '**/*.cpp'
}
exportedHeaders {
srcDirs 'src/main/native/include', 'src/main/native/cpp'
include '**/*.h'
}
}
}
} else if (it.targetPlatform.operatingSystem.isMacOsX()) {
it.sources {

View File

@@ -0,0 +1,25 @@
/*----------------------------------------------------------------------------*/
/* Copyright (c) 2008-2019 FIRST. All Rights Reserved. */
/* Open Source Software - may be modified and shared by FRC teams. The code */
/* must be accompanied by the FIRST BSD license file in the root directory of */
/* the project. */
/*----------------------------------------------------------------------------*/
#ifndef WPIUTIL_WPI_DEMANGLE_H_
#define WPIUTIL_WPI_DEMANGLE_H_
#include <string>
namespace wpi {
/**
* Demangle a C++ symbol.
*
* @param mangledSymbol the mangled symbol.
* @return The demangled symbol, or mangledSymbol if demangling fails.
*/
std::string Demangle(char const* mangledSymbol);
} // namespace wpi
#endif // WPIUTIL_WPI_DEMANGLE_H_

View File

@@ -0,0 +1,24 @@
/*----------------------------------------------------------------------------*/
/* Copyright (c) 2008-2019 FIRST. All Rights Reserved. */
/* Open Source Software - may be modified and shared by FRC teams. The code */
/* must be accompanied by the FIRST BSD license file in the root directory of */
/* the project. */
/*----------------------------------------------------------------------------*/
#ifndef WPIUTIL_WPI_STACKTRACE_H_
#define WPIUTIL_WPI_STACKTRACE_H_
#include <string>
namespace wpi {
/**
* Get a stack trace, ignoring the first "offset" symbols.
*
* @param offset The number of symbols at the top of the stack to ignore
*/
std::string GetStackTrace(int offset);
} // namespace wpi
#endif // WPIUTIL_WPI_STACKTRACE_H_

View File

@@ -0,0 +1,36 @@
/*----------------------------------------------------------------------------*/
/* Copyright (c) 2008-2019 FIRST. All Rights Reserved. */
/* Open Source Software - may be modified and shared by FRC teams. The code */
/* must be accompanied by the FIRST BSD license file in the root directory of */
/* the project. */
/*----------------------------------------------------------------------------*/
#include "wpi/Demangle.h"
#include <cxxabi.h>
#include <cstdio>
namespace wpi {
std::string Demangle(char const* mangledSymbol) {
char buffer[256];
size_t length;
int32_t status;
if (std::sscanf(mangledSymbol, "%*[^(]%*[(]%255[^)+]", buffer)) {
char* symbol = abi::__cxa_demangle(buffer, nullptr, &length, &status);
if (status == 0) {
return symbol;
} else {
// If the symbol couldn't be demangled, it's probably a C function,
// so just return it as-is.
return buffer;
}
}
// If everything else failed, just return the mangled symbol
return mangledSymbol;
}
} // namespace wpi

View File

@@ -0,0 +1,37 @@
/*----------------------------------------------------------------------------*/
/* Copyright (c) 2008-2019 FIRST. All Rights Reserved. */
/* Open Source Software - may be modified and shared by FRC teams. The code */
/* must be accompanied by the FIRST BSD license file in the root directory of */
/* the project. */
/*----------------------------------------------------------------------------*/
#include "wpi/StackTrace.h"
#include <execinfo.h>
#include "wpi/Demangle.h"
#include "wpi/SmallString.h"
#include "wpi/raw_ostream.h"
namespace wpi {
std::string GetStackTrace(int offset) {
void* stackTrace[128];
int stackSize = backtrace(stackTrace, 128);
char** mangledSymbols = backtrace_symbols(stackTrace, stackSize);
wpi::SmallString<1024> buf;
wpi::raw_svector_ostream trace(buf);
for (int i = offset; i < stackSize; i++) {
// Only print recursive functions once in a row.
if (i == 0 || stackTrace[i] != stackTrace[i - 1]) {
trace << "\tat " << Demangle(mangledSymbols[i]) << "\n";
}
}
std::free(mangledSymbols);
return trace.str();
}
} // namespace wpi

View File

@@ -0,0 +1,30 @@
/*----------------------------------------------------------------------------*/
/* Copyright (c) 2008-2019 FIRST. All Rights Reserved. */
/* Open Source Software - may be modified and shared by FRC teams. The code */
/* must be accompanied by the FIRST BSD license file in the root directory of */
/* the project. */
/*----------------------------------------------------------------------------*/
#include "wpi/Demangle.h"
#include <windows.h> // NOLINT(build/include_order)
#include <dbghelp.h>
#include "wpi/mutex.h"
#pragma comment(lib, "Dbghelp.lib")
namespace wpi {
std::string Demangle(char const* mangledSymbol) {
static wpi::mutex m;
std::scoped_lock lock(m);
char buffer[256];
DWORD sz = UnDecorateSymbolName(mangledSymbol, buffer, sizeof(buffer),
UNDNAME_COMPLETE);
if (sz == 0) return mangledSymbol;
return std::string(buffer, sz);
}
} // namespace wpi

View File

@@ -0,0 +1,45 @@
/*----------------------------------------------------------------------------*/
/* Copyright (c) 2008-2019 FIRST. All Rights Reserved. */
/* Open Source Software - may be modified and shared by FRC teams. The code */
/* must be accompanied by the FIRST BSD license file in the root directory of */
/* the project. */
/*----------------------------------------------------------------------------*/
#include "wpi/StackTrace.h"
#include "StackWalker.h"
#include "wpi/ConvertUTF.h"
#include "wpi/SmallString.h"
namespace {
class StackTraceWalker : public StackWalker {
public:
explicit StackTraceWalker(std::string& output) : m_output(output) {}
void OnOutput(LPCTSTR szText) override;
private:
std::string& m_output;
};
} // namespace
void StackTraceWalker::OnOutput(LPCTSTR szText) {
#ifdef _UNICODE
wpi::SmallString<128> utf8;
wpi::sys::windows::UTF16ToUTF8(szText, wcslen(szText), utf8);
m_output.append(utf8.data(), utf8.size());
#else
m_output.append(szText);
#endif
}
namespace wpi {
std::string GetStackTrace(int offset) {
// TODO: implement offset
std::string output;
StackTraceWalker walker(output);
return output;
}
} // namespace wpi

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,178 @@
/**********************************************************************
*
* StackWalker.h
*
*
*
* LICENSE (http://www.opensource.org/licenses/bsd-license.php)
*
* Copyright (c) 2005-2009, Jochen Kalmbach
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
*
* Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
* Neither the name of Jochen Kalmbach nor the names of its contributors may be
* used to endorse or promote products derived from this software without
* specific prior written permission.
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* **********************************************************************/
#pragma once
#include <windows.h>
#if _MSC_VER >= 1900
#pragma warning(disable : 4091)
#endif
class StackWalkerInternal; // forward
class StackWalker
{
public:
typedef enum StackWalkOptions
{
// No addition info will be retrieved
// (only the address is available)
RetrieveNone = 0,
// Try to get the symbol-name
RetrieveSymbol = 1,
// Try to get the line for this symbol
RetrieveLine = 2,
// Try to retrieve the module-infos
RetrieveModuleInfo = 4,
// Also retrieve the version for the DLL/EXE
RetrieveFileVersion = 8,
// Contains all the above
RetrieveVerbose = 0xF,
// Generate a "good" symbol-search-path
SymBuildPath = 0x10,
// Also use the public Microsoft-Symbol-Server
SymUseSymSrv = 0x20,
// Contains all the above "Sym"-options
SymAll = 0x30,
// Contains all options (default)
OptionsAll = 0x3F
} StackWalkOptions;
StackWalker(int options = OptionsAll, // 'int' is by design, to combine the enum-flags
LPCTSTR szSymPath = NULL,
DWORD dwProcessId = GetCurrentProcessId(),
HANDLE hProcess = GetCurrentProcess());
StackWalker(DWORD dwProcessId, HANDLE hProcess);
virtual ~StackWalker();
typedef BOOL(__stdcall* PReadProcessMemoryRoutine)(
HANDLE hProcess,
DWORD64 qwBaseAddress,
PVOID lpBuffer,
DWORD nSize,
LPDWORD lpNumberOfBytesRead,
LPVOID pUserData // optional data, which was passed in "ShowCallstack"
);
BOOL LoadModules();
BOOL ShowCallstack(
HANDLE hThread = GetCurrentThread(),
const CONTEXT* context = NULL,
PReadProcessMemoryRoutine readMemoryFunction = NULL,
LPVOID pUserData = NULL // optional to identify some data in the 'readMemoryFunction'-callback
);
BOOL ShowObject(LPVOID pObject);
protected:
enum
{
STACKWALK_MAX_NAMELEN = 1024
}; // max name length for found symbols
protected:
// Entry for each Callstack-Entry
typedef struct CallstackEntry
{
DWORD64 offset; // if 0, we have no valid entry
TCHAR name[STACKWALK_MAX_NAMELEN];
TCHAR undName[STACKWALK_MAX_NAMELEN];
TCHAR undFullName[STACKWALK_MAX_NAMELEN];
DWORD64 offsetFromSmybol;
DWORD offsetFromLine;
DWORD lineNumber;
TCHAR lineFileName[STACKWALK_MAX_NAMELEN];
DWORD symType;
LPCSTR symTypeString;
TCHAR moduleName[STACKWALK_MAX_NAMELEN];
DWORD64 baseOfImage;
TCHAR loadedImageName[STACKWALK_MAX_NAMELEN];
} CallstackEntry;
typedef enum CallstackEntryType
{
firstEntry,
nextEntry,
lastEntry
} CallstackEntryType;
virtual void OnSymInit(LPCTSTR szSearchPath, DWORD symOptions, LPCTSTR szUserName);
virtual void OnLoadModule(LPCTSTR img,
LPCTSTR mod,
DWORD64 baseAddr,
DWORD size,
DWORD result,
LPCTSTR symType,
LPCTSTR pdbName,
ULONGLONG fileVersion);
virtual void OnCallstackEntry(CallstackEntryType eType, CallstackEntry& entry);
virtual void OnDbgHelpErr(LPCTSTR szFuncName, DWORD gle, DWORD64 addr);
virtual void OnOutput(LPCTSTR szText);
StackWalkerInternal* m_sw;
HANDLE m_hProcess;
DWORD m_dwProcessId;
BOOL m_modulesLoaded;
LPTSTR m_szSymPath;
int m_options;
int m_MaxRecursionCount;
static BOOL __stdcall myReadProcMem(HANDLE hProcess,
DWORD64 qwBaseAddress,
PVOID lpBuffer,
DWORD nSize,
LPDWORD lpNumberOfBytesRead);
friend StackWalkerInternal;
}; // class StackWalker
// The following is defined for x86 (XP and higher), x64 and IA64:
#define GET_CURRENT_CONTEXT_STACKWALKER_CODEPLEX(c, contextFlags) \
do \
{ \
memset(&c, 0, sizeof(CONTEXT)); \
c.ContextFlags = contextFlags; \
RtlCaptureContext(&c); \
} while (0);