post-alpha sync with work.

debug stack trace fixes, remove more asm, change CONTINUE/OK scheme to
OK/ALL_COMPLETE, fix tests

This was SVN commit r9871.
This commit is contained in:
janwas 2011-07-18 09:21:56 +00:00
parent 2d9e473483
commit 0d23e3f333
32 changed files with 575 additions and 1069 deletions

View file

@ -1,33 +1,50 @@
#include "precompiled.h"
#include "lib/allocators/unique_range.h"
#include "lib/bits.h" // is_pow2, round_up
#include "lib/sysdep/cpu.h" // cpu_AtomicAdd
#include "lib/sysdep/rtl.h" // rtl_FreeAligned
static void UniqueRangeDeleterNone(void* UNUSED(pointer), size_t UNUSED(size))
// (hardwired index avoids RegisterUniqueRangeDeleter overhead for
// this commonly used allocator.)
static const IdxDeleter idxDeleterAligned = 1;
static void FreeNone(void* UNUSED(pointer), size_t UNUSED(size))
{
// (introducing this do-nothing function avoids having to check whether deleter != 0)
// (providing a deleter function for idxDeleterNone avoids
// having to check whether deleters[idxDeleter] == 0)
}
static void UniqueRangeDeleterAligned(void* pointer, size_t UNUSED(size))
static void FreeAligned(void* pointer, size_t UNUSED(size))
{
return rtl_FreeAligned(pointer);
}
static UniqueRangeDeleter deleters[idxDeleterBits+1] = { UniqueRangeDeleterNone, UniqueRangeDeleterAligned };
static UniqueRangeDeleter deleters[allocationAlignment] = { FreeNone, FreeAligned };
static IdxDeleter numDeleters = 2;
IdxDeleter AddUniqueRangeDeleter(UniqueRangeDeleter deleter)
void RegisterUniqueRangeDeleter(UniqueRangeDeleter deleter, volatile IdxDeleter* idxDeleterOut)
{
ENSURE(deleter);
IdxDeleter idxDeleter = cpu_AtomicAdd(&numDeleters, 1);
if(!cpu_CAS(idxDeleterOut, idxDeleterNone, -1)) // not the first call for this deleter
{
// wait until an index has been assigned
while(*idxDeleterOut <= 0)
cpu_Pause();
return;
}
const IdxDeleter idxDeleter = cpu_AtomicAdd(&numDeleters, 1);
ENSURE(idxDeleter < (IdxDeleter)ARRAY_SIZE(deleters));
deleters[idxDeleter] = deleter;
return idxDeleter;
COMPILER_FENCE;
*idxDeleterOut = idxDeleter; // linearization point
}
@ -38,3 +55,19 @@ void CallUniqueRangeDeleter(void* pointer, size_t size, IdxDeleter idxDeleter) t
if(pointer)
deleters[idxDeleter](pointer, size);
}
UniqueRange AllocateAligned(size_t size, size_t alignment)
{
ENSURE(is_pow2(alignment));
alignment = std::max(alignment, allocationAlignment);
const size_t alignedSize = round_up(size, alignment);
const UniqueRange::pointer p = rtl_AllocateAligned(alignedSize, alignment);
static volatile IdxDeleter idxDeleterAligned;
if(idxDeleterAligned == 0)
RegisterUniqueRangeDeleter(FreeAligned, &idxDeleterAligned);
return RVALUE(UniqueRange(p, size, idxDeleterAligned));
}

View file

@ -2,6 +2,7 @@
#define INCLUDED_ALLOCATORS_UNIQUE_RANGE
#include "lib/lib_api.h"
#include "lib/alignment.h" // allocationAlignment
// we usually don't hold multiple references to allocations, so unique_ptr
// can be used instead of the more complex (ICC generated incorrect code on
@ -14,32 +15,32 @@
// bonus of no longer requiring a complete type at the invocation of ~unique_ptr.
// however, this inflates the pointer size to 3 words. if only a few allocator types
// are needed, we can replace the function pointer with an index stashed into the
// lower bits of the pointer (safe because allocations are always aligned to the
// word size).
// lower bits of the pointer (safe because all allocations' addresses are multiples
// of allocationAlignment).
typedef intptr_t IdxDeleter;
// no-op deleter (use when returning part of an existing allocation)
// must be zero because reset() sets address (which includes idxDeleter) to zero.
static const IdxDeleter idxDeleterNone = 0;
static const IdxDeleter idxDeleterAligned = 1;
// (temporary value to prevent concurrent calls to AddUniqueRangeDeleter)
static const IdxDeleter idxDeleterBusy = -IdxDeleter(1);
// governs the maximum number of IdxDeleter and each pointer's alignment requirements
static const IdxDeleter idxDeleterBits = 0x7;
typedef void (*UniqueRangeDeleter)(void* pointer, size_t size);
/**
* @return the next available IdxDeleter and associate it with the deleter.
* halts the program if the idxDeleterBits limit has been reached.
* register a deleter, returning its index within the table.
*
* thread-safe, but no attempt is made to detect whether the deleter has already been
* registered (would require a mutex). each allocator must ensure they only call this once.
* @param deleter function pointer. must be uniquely associated with
* the idxDeleter storage location.
* @param idxDeleter location where to store the next available index.
* if it is already non-zero, skip the call to this function to
* avoid overhead.
*
* thread-safe. idxDeleter is used for mutual exclusion between
* multiple callers for the same deleter. concurrent registration of
* different deleters is also safe due to atomic increments.
*
* halts the program if more than allocationAlignment deleters are
* to be registered.
**/
LIB_API IdxDeleter AddUniqueRangeDeleter(UniqueRangeDeleter deleter);
LIB_API void RegisterUniqueRangeDeleter(UniqueRangeDeleter deleter, volatile IdxDeleter* idxDeleter);
LIB_API void CallUniqueRangeDeleter(void* pointer, size_t size, IdxDeleter idxDeleter) throw();
@ -56,7 +57,7 @@ public:
UniqueRange()
{
Set(0, 0, idxDeleterNone);
Clear();
}
UniqueRange(pointer p, size_t size, IdxDeleter deleter)
@ -66,10 +67,7 @@ public:
UniqueRange(RVALUE_REF(UniqueRange) rvalue)
{
UniqueRange& lvalue = LVALUE(rvalue);
address_ = lvalue.address_;
size_ = lvalue.size_;
lvalue.address_ = 0;
Pilfer(LVALUE(rvalue));
}
UniqueRange& operator=(RVALUE_REF(UniqueRange) rvalue)
@ -78,9 +76,7 @@ public:
if(this != &lvalue)
{
Delete();
address_ = lvalue.address_;
size_ = lvalue.size_;
lvalue.address_ = 0;
Pilfer(lvalue);
}
return *this;
}
@ -92,12 +88,12 @@ public:
pointer get() const
{
return pointer(address_ & ~idxDeleterBits);
return pointer(address_ & ~(allocationAlignment-1));
}
IdxDeleter get_deleter() const
{
return IdxDeleter(address_ & idxDeleterBits);
return IdxDeleter(address_ % allocationAlignment);
}
size_t size() const
@ -109,14 +105,14 @@ public:
pointer release() // relinquish ownership
{
pointer ret = get();
address_ = 0;
Clear();
return ret;
}
void reset()
{
Delete();
address_ = 0;
Clear();
}
void reset(pointer p, size_t size, IdxDeleter deleter)
@ -139,8 +135,8 @@ public:
private:
void Set(pointer p, size_t size, IdxDeleter deleter)
{
ASSERT((uintptr_t(p) & idxDeleterBits) == 0);
ASSERT(deleter <= idxDeleterBits);
ASSERT((uintptr_t(p) % allocationAlignment) == 0);
ASSERT(size_t(deleter) < allocationAlignment);
address_ = uintptr_t(p) | deleter;
size_ = size;
@ -150,6 +146,20 @@ private:
ASSERT(this->size() == size);
}
void Clear()
{
Set(0, 0, idxDeleterNone);
}
void Pilfer(UniqueRange& victim)
{
const size_t size = victim.size();
const IdxDeleter idxDeleter = victim.get_deleter();
pointer p = victim.release();
Set(p, size, idxDeleter);
victim.Clear();
}
void Delete()
{
CallUniqueRangeDeleter(get(), size(), get_deleter());
@ -179,4 +189,6 @@ static inline void swap(UniqueRange& p1, RVALUE_REF(UniqueRange) p2)
}
LIB_API UniqueRange AllocateAligned(size_t size, size_t alignment);
#endif // #ifndef INCLUDED_ALLOCATORS_UNIQUE_RANGE

View file

@ -532,10 +532,12 @@ static bool ShouldSkipError(Status err)
ErrorReaction debug_OnError(Status err, atomic_bool* suppress, const wchar_t* file, int line, const char* func)
{
CACHE_ALIGNED u8 context[DEBUG_CONTEXT_SIZE];
(void)debug_CaptureContext(context);
if(ShouldSkipError(err))
return ER_CONTINUE;
void* context = 0;
const wchar_t* lastFuncToSkip = L"debug_OnError";
wchar_t buf[400];
wchar_t err_buf[200]; StatusDescription(err, err_buf, ARRAY_SIZE(err_buf));
@ -546,7 +548,9 @@ ErrorReaction debug_OnError(Status err, atomic_bool* suppress, const wchar_t* fi
ErrorReaction debug_OnAssertionFailure(const wchar_t* expr, atomic_bool* suppress, const wchar_t* file, int line, const char* func)
{
void* context = 0;
CACHE_ALIGNED u8 context[DEBUG_CONTEXT_SIZE];
(void)debug_CaptureContext(context);
const std::wstring lastFuncToSkip = L"debug_OnAssertionFailure";
wchar_t buf[400];
swprintf_s(buf, ARRAY_SIZE(buf), L"Assertion failed: \"%ls\"", expr);

View file

@ -39,6 +39,7 @@
#include "lib/lib_api.h"
#include "lib/types.h" // intptr_t
#include "lib/status.h"
#include "lib/alignment.h"
#include "lib/code_annotation.h"
#include "lib/code_generation.h"
@ -119,8 +120,7 @@ typedef volatile intptr_t atomic_bool;
* value for suppress flag once set by debug_DisplayError.
* rationale: this value is fairly distinctive and helps when
* debugging the symbol engine.
* initial value is 0 rather that another constant; this avoids
* allocating .rdata space.
* use 0 as the initial value to avoid allocating .rdata space.
**/
const atomic_bool DEBUG_SUPPRESS = 0xAB;
@ -175,6 +175,7 @@ enum ErrorReactionInternal
ERI_NOT_IMPLEMENTED
};
/**
* display an error dialog with a message and stack trace.
*
@ -192,11 +193,15 @@ enum ErrorReactionInternal
**/
LIB_API ErrorReaction debug_DisplayError(const wchar_t* description, size_t flags, void* context, const wchar_t* lastFuncToSkip, const wchar_t* file, int line, const char* func, atomic_bool* suppress);
/**
* convenience version, in case the advanced parameters aren't needed.
* macro instead of providing overload/default values for C compatibility.
**/
#define DEBUG_DISPLAY_ERROR(text) debug_DisplayError(text, 0, 0, L"debug_DisplayError", WIDEN(__FILE__),__LINE__,__func__, 0)
// simplified version for just displaying an error message
#define DEBUG_DISPLAY_ERROR(description)\
do\
{\
CACHE_ALIGNED u8 context[DEBUG_CONTEXT_SIZE];\
(void)debug_CaptureContext(context);\
(void)debug_DisplayError(description, 0, context, L"debug_DisplayError", WIDEN(__FILE__), __LINE__, __func__, 0);\
}\
while(0)
//
@ -417,23 +422,23 @@ namespace INFO
/**
* Maximum number of characters (including trailing \\0) written to
* Maximum number of characters (including null terminator) written to
* user's buffers by debug_ResolveSymbol.
**/
const size_t DBG_SYMBOL_LEN = 1000;
const size_t DBG_FILE_LEN = 100;
static const size_t DEBUG_SYMBOL_CHARS = 1000;
static const size_t DEBUG_FILE_CHARS = 100;
/**
* read and return symbol information for the given address.
*
* NOTE: the PDB implementation is rather slow (~500us).
* NOTE: the PDB implementation is rather slow (~500 us).
*
* @param ptr_of_interest address of symbol (e.g. function, variable)
* @param sym_name optional out; size >= DBG_SYMBOL_LEN chars;
* receives symbol name returned via debug info.
* @param file optional out; size >= DBG_FILE_LEN chars; receives
* base name only (no path; see rationale in wdbg_sym) of
* source file containing the symbol.
* @param sym_name optional out; holds at least DEBUG_SYMBOL_CHARS;
* receives symbol name returned via debug info.
* @param file optional out; holds at least DEBUG_FILE_CHARS;
* receives base name only (no path; see rationale in wdbg_sym) of
* source file containing the symbol.
* @param line optional out; receives source file line number of symbol.
*
* note: all of the output parameters are optional; we pass back as much
@ -443,6 +448,15 @@ const size_t DBG_FILE_LEN = 100;
**/
LIB_API Status debug_ResolveSymbol(void* ptr_of_interest, wchar_t* sym_name, wchar_t* file, int* line);
static const size_t DEBUG_CONTEXT_SIZE = 2048; // Win32 CONTEXT is currently 1232 bytes
/**
* @param context must point to an instance of the platform-specific type
* (e.g. CONTEXT) or CACHE_ALIGNED storage of DEBUG_CONTEXT_SIZE bytes.
**/
LIB_API Status debug_CaptureContext(void* context);
/**
* write a complete stack trace (including values of local variables) into
* the specified buffer.
@ -450,8 +464,10 @@ LIB_API Status debug_ResolveSymbol(void* ptr_of_interest, wchar_t* sym_name, wch
* @param buf Target buffer.
* @param maxChars Max chars of buffer (should be several thousand).
* @param context Platform-specific representation of execution state
* (e.g. Win32 CONTEXT). if not NULL, tracing starts there; this is useful
* for exceptions. Otherwise, tracing starts from the current call stack.
* (e.g. Win32 CONTEXT). either specify an SEH exception's
* context record or use debug_CaptureContext to retrieve the current state.
* Rationale: intermediates such as debug_DisplayError change the
* context, so it should be captured as soon as possible.
* @param lastFuncToSkip Is used for omitting error-reporting functions like
* debug_OnAssertionFailure from the stack trace. It is either 0 (skip nothing) or
* a substring of a function's name (this allows platform-independent

View file

@ -37,6 +37,7 @@
static const StatusDefinition debugStlStatusDefinitions[] = {
{ ERR::STL_CNT_UNKNOWN, L"Unknown STL container type_name" },
{ ERR::STL_CNT_UNSUPPORTED, L"Unsupported STL container" },
{ ERR::STL_CNT_INVALID, L"Container type is known but contents are invalid" }
};
STATUS_ADD_DEFINITIONS(debugStlStatusDefinitions);
@ -49,7 +50,7 @@ STATUS_ADD_DEFINITIONS(debugStlStatusDefinitions);
else if(!wcsncmp(src, (what), ARRAY_SIZE(what)-1))\
{\
src += ARRAY_SIZE(what)-1-1; /* see preincrement rationale*/\
wcscpy_s(dst, ARRAY_SIZE(what), (with));\
wcscpy_s(dst, ARRAY_SIZE(with), (with));\
dst += ARRAY_SIZE(with)-1;\
}
#define STRIP(what)\
@ -139,10 +140,12 @@ wchar_t* debug_stl_simplify_name(wchar_t* name)
}
REPLACE(L"std::_List_nod", L"list")
REPLACE(L"std::_Tree_nod", L"map")
REPLACE(L"std::basic_string<char,", L"string<")
REPLACE(L"std::basic_string<unsigned short,", L"wstring<")
STRIP(L"std::char_traits<char>,")
STRIP(L"std::char_traits<unsigned short>,")
REPLACE(L"std::basic_string<char, ", L"string<")
REPLACE(L"std::basic_string<__wchar_t, ", L"wstring<")
REPLACE(L"std::basic_string<unsigned short, ", L"wstring<")
STRIP(L"std::char_traits<char>, ")
STRIP(L"std::char_traits<unsigned short>, ")
STRIP(L"std::char_traits<__wchar_t>, ")
STRIP(L"std::_Tmap_traits")
STRIP(L"std::_Tset_traits")
STRIP_NESTED(L"std::allocator<")
@ -554,7 +557,7 @@ Status debug_stl_get_container_info(const wchar_t* type_name, const u8* p, size_
UNUSED2(el_count);
UNUSED2(el_iterator);
UNUSED2(it_mem);
return ERR::STL_CNT_UNKNOWN; // NOWARN
return ERR::STL_CNT_UNSUPPORTED; // NOWARN
#else
bool handled = false, IsValid = false;

View file

@ -31,8 +31,9 @@
namespace ERR
{
const Status STL_CNT_UNKNOWN = -100500;
const Status STL_CNT_UNSUPPORTED = -100501;
// likely causes: not yet initialized or memory corruption.
const Status STL_CNT_INVALID = -100501;
const Status STL_CNT_INVALID = -100502;
}
@ -45,7 +46,7 @@ namespace ERR
*
* @param name Buffer holding input symbol name; modified in-place.
* There is no length limit; must be large enough to hold typical STL
* strings. DBG_SYMBOL_LEN chars is a good measure.
* strings. DEBUG_SYMBOL_CHARS chars is a good measure.
* @return name for convenience.
**/
extern wchar_t* debug_stl_simplify_name(wchar_t* name);

View file

@ -121,9 +121,13 @@ enum DataKind
# pragma warning(disable:791) // calling convention specified more than once
#endif
#pragma pack(push, 8) // seems to be required
#define _NO_CVCONST_H // request SymTagEnum be defined
#include <dbghelp.h> // must come after win.h and the above definitions
#pragma pack(pop)
#if ICC_VERSION
# pragma warning(pop)
#endif

View file

@ -381,7 +381,7 @@ private:
lfh_dst += size;
lfh_bytes_remaining -= size;
return INFO::CONTINUE;
return INFO::OK;
}
mutable u8* lfh_dst;

View file

@ -116,7 +116,7 @@ void Stream::SetOutputBuffer(u8* out, size_t outSize)
Status Stream::Feed(const u8* in, size_t inSize)
{
if(m_outProduced == m_outputBufferManager.Size()) // output buffer full; must not call Process
return INFO::OK;
return INFO::ALL_COMPLETE;
size_t inConsumed, outProduced;
u8* const out = m_outputBufferManager.Buffer() + m_outProduced;
@ -125,7 +125,7 @@ Status Stream::Feed(const u8* in, size_t inSize)
m_inConsumed += inConsumed;
m_outProduced += outProduced;
return INFO::CONTINUE;
return INFO::OK;
}

View file

@ -32,18 +32,6 @@ STATUS_ADD_DEFINITIONS(ioStatusDefinitions);
namespace io {
UniqueRange Allocate(size_t size, size_t alignment)
{
ENSURE(is_pow2(alignment));
if(alignment <= (size_t)idxDeleterBits)
alignment = idxDeleterBits+1;
const size_t alignedSize = round_up(size, alignment);
const UniqueRange::pointer p = rtl_AllocateAligned(alignedSize, alignment);
return RVALUE(UniqueRange(p, size, idxDeleterAligned));
}
// this is just a thin wrapper on top of lowio and POSIX aio.
// note that the Windows aio implementation requires buffers, sizes and
// offsets to be sector-aligned.

View file

@ -31,6 +31,7 @@
#include "lib/config2.h"
#include "lib/alignment.h"
#include "lib/bits.h"
#include "lib/timer.h"
#include "lib/file/file.h"
#include "lib/sysdep/filesystem.h" // wtruncate
#include "lib/posix/posix_aio.h" // LIO_READ, LIO_WRITE
@ -51,7 +52,10 @@ namespace io {
//
// use this instead of the file cache for write buffers that are
// never reused (avoids displacing other items).
LIB_API UniqueRange Allocate(size_t size, size_t alignment = maxSectorSize);
static inline UniqueRange Allocate(size_t size, size_t alignment = maxSectorSize)
{
return RVALUE(AllocateAligned(size, alignment));
}
#pragma pack(push, 1)
@ -146,16 +150,14 @@ struct DefaultCompletedHook
{
/**
* called after a block I/O has completed.
*
* @return INFO::CONTINUE to proceed; any other value will
* be immediately returned by Run.
* @return Status (see RETURN_STATUS_FROM_CALLBACK).
*
* allows progress notification and processing data while waiting for
* previous I/Os to complete.
**/
Status operator()(const u8* UNUSED(block), size_t UNUSED(blockSize)) const
{
return INFO::CONTINUE;
return INFO::OK;
}
};
@ -164,16 +166,14 @@ struct DefaultIssueHook
{
/**
* called before a block I/O is issued.
*
* @return INFO::CONTINUE to proceed; any other value will
* be immediately returned by Run.
* @return Status (see RETURN_STATUS_FROM_CALLBACK).
*
* allows generating the data to write while waiting for
* previous I/Os to complete.
**/
Status operator()(aiocb& UNUSED(cb)) const
{
return INFO::CONTINUE;
return INFO::OK;
}
};
@ -245,7 +245,7 @@ static inline Status Run(const Operation& op, const Parameters& p = Parameters()
if(blocksIssued == numBlocks-1)
cb.aio_nbytes = round_up(size_t(op.size - blocksIssued * p.blockSize), size_t(p.alignment));
RETURN_IF_NOT_CONTINUE(issueHook(cb));
RETURN_STATUS_FROM_CALLBACK(issueHook(cb));
RETURN_STATUS_IF_ERR(Issue(cb, p.queueDepth));
}
@ -253,7 +253,7 @@ static inline Status Run(const Operation& op, const Parameters& p = Parameters()
aiocb& cb = controlBlockRingBuffer[blocksCompleted];
RETURN_STATUS_IF_ERR(WaitUntilComplete(cb, p.queueDepth));
RETURN_IF_NOT_CONTINUE(completedHook((u8*)cb.aio_buf, cb.aio_nbytes));
RETURN_STATUS_FROM_CALLBACK(completedHook((u8*)cb.aio_buf, cb.aio_nbytes));
}
return INFO::OK;
@ -275,7 +275,7 @@ static inline Status Run(const Operation& op, const Parameters& p = Parameters()
//-----------------------------------------------------------------------------
// Store
// efficient writing requires preallocation, and the resulting file is
// efficient writing requires preallocation; the resulting file is
// padded to the sector size and needs to be truncated afterwards.
// this function takes care of both.
template<class CompletedHook, class IssueHook>
@ -316,7 +316,7 @@ static inline Status Store(const OsPath& pathname, const void* data, size_t size
//-----------------------------------------------------------------------------
// Load
// convenience function provided for symmetry with Store
// convenience function provided for symmetry with Store.
template<class CompletedHook, class IssueHook>
static inline Status Load(const OsPath& pathname, void* buf, size_t size, const Parameters& p = Parameters(), const CompletedHook& completedHook = CompletedHook(), const IssueHook& issueHook = IssueHook())
{

View file

@ -23,39 +23,6 @@
#include "precompiled.h"
#include "lib/posix/posix.h"
#if ARCH_IA32
# include "lib/sysdep/arch/ia32/ia32_asm.h"
#endif
#if !HAVE_C99_MATH
size_t fpclassifyd(double d)
{
#if ARCH_IA32
return ia32_asm_fpclassifyd(d);
#else
// really sucky stub implementation; doesn't attempt to cover all cases.
if(d != d)
return FP_NAN;
else
return FP_NORMAL;
#endif
}
size_t fpclassifyf(float f)
{
#if ARCH_IA32
return ia32_asm_fpclassifyf(f);
#else
const double d = (double)f;
return fpclassifyd(d);
#endif
}
#endif // #if !HAVE_C99_MATH
#if EMULATE_WCSDUP
wchar_t* wcsdup(const wchar_t* str)

View file

@ -115,44 +115,18 @@ extern wchar_t* wcsdup(const wchar_t* str);
extern int wcscasecmp(const wchar_t* s1, const wchar_t* s2);
#endif
// fpclassify etc (too few/diverse to make separate HAVE_ for each)
#if HAVE_C99 || ICC_VERSION || GCC_VERSION
# define HAVE_C99_MATH 1
#else
# define HAVE_C99_MATH 0
#endif
#if !HAVE_C99_MATH
extern size_t fpclassifyf(float f);
extern size_t fpclassifyd(double d);
#define fpclassify(x) ( (sizeof(x) == sizeof(float))? fpclassifyf(x) : fpclassifyd(x) )
// these definitions "happen" to match IA32_FP_* and allow using
// ia32_fp_classify without having to translate the return value.
// we don't #define to IA32_FP_* to avoid dependency.
# define FP_NAN 0x0100
# define FP_NORMAL 0x0400
# define FP_INFINITE (FP_NAN | FP_NORMAL)
# define FP_ZERO 0x4000
# define FP_SUBNORMAL (FP_NORMAL | FP_ZERO)
# define isnan(d) (fpclassify(d) == FP_NAN)
# define isfinite(d) ((fpclassify(d) & FP_NAN) == 0)
# define isinf(d) (fpclassify(d) == (FP_NAN|FP_NORMAL))
# define isnormal(d) (fpclassify(d) == FP_NORMAL)
//# define signbit
#else // HAVE_C99_MATH
// Some systems have C99 support but in C++ they provide only std::isfinite
// and not isfinite. C99 specifies that isfinite is a macro, so we can use
// #ifndef and define it if it's not there already.
// We've included <cmath> above to make sure it defines that macro.
# ifndef isfinite
# define fpclassify std::fpclassify
#ifndef isfinite
# if MSC_VERSION
# define isfinite _finite
# define isnan _isnan
# else
# define isfinite std::isfinite
# define isnan std::isnan
# define isinf std::isinf
# define isnormal std::isnormal
# endif
#endif // HAVE_C99_MATH
#endif
#endif // #ifndef INCLUDED_POSIX

View file

@ -27,84 +27,6 @@
class TestPosix : public CxxTest::TestSuite
{
public:
template<typename T>
void do_fpclassify()
{
T zero = 0.f;
T one = 1.f;
T inf = std::numeric_limits<T>::infinity();
T ninf = -std::numeric_limits<T>::infinity();
T qnan = std::numeric_limits<T>::quiet_NaN();
T snan = std::numeric_limits<T>::signaling_NaN();
T min = std::numeric_limits<T>::min();
T sub = std::numeric_limits<T>::denorm_min();
T sub2 = std::numeric_limits<T>::min() / 2;
TS_ASSERT_EQUALS((int)fpclassify(zero), (int)FP_ZERO);
TS_ASSERT_EQUALS((int)fpclassify(one), (int)FP_NORMAL);
TS_ASSERT_EQUALS((int)fpclassify(inf), (int)FP_INFINITE);
TS_ASSERT_EQUALS((int)fpclassify(ninf), (int)FP_INFINITE);
TS_ASSERT_EQUALS((int)fpclassify(qnan), (int)FP_NAN);
TS_ASSERT_EQUALS((int)fpclassify(snan), (int)FP_NAN);
TS_ASSERT_EQUALS((int)fpclassify(min), (int)FP_NORMAL);
#ifndef OS_WIN // http://trac.wildfiregames.com/ticket/478
TS_ASSERT_EQUALS((int)fpclassify(sub), (int)FP_SUBNORMAL);
TS_ASSERT_EQUALS((int)fpclassify(sub2), (int)FP_SUBNORMAL);
#endif
TS_ASSERT(!isnan(zero));
TS_ASSERT(!isnan(one));
TS_ASSERT(!isnan(inf));
TS_ASSERT(!isnan(ninf));
TS_ASSERT(isnan(qnan));
TS_ASSERT(isnan(snan));
TS_ASSERT(!isnan(min));
TS_ASSERT(!isnan(sub));
TS_ASSERT(!isnan(sub2));
TS_ASSERT(isfinite(zero));
TS_ASSERT(isfinite(one));
TS_ASSERT(!isfinite(inf));
TS_ASSERT(!isfinite(ninf));
TS_ASSERT(!isfinite(qnan));
TS_ASSERT(!isfinite(snan));
TS_ASSERT(isfinite(min));
TS_ASSERT(isfinite(sub));
TS_ASSERT(isfinite(sub2));
TS_ASSERT(!isinf(zero));
TS_ASSERT(!isinf(one));
TS_ASSERT(isinf(inf));
TS_ASSERT(isinf(ninf));
TS_ASSERT(!isinf(qnan));
TS_ASSERT(!isinf(snan));
TS_ASSERT(!isinf(min));
TS_ASSERT(!isinf(sub));
TS_ASSERT(!isinf(sub2));
TS_ASSERT(!isnormal(zero));
TS_ASSERT(isnormal(one));
TS_ASSERT(!isnormal(inf));
TS_ASSERT(!isnormal(ninf));
TS_ASSERT(!isnormal(qnan));
TS_ASSERT(!isnormal(snan));
TS_ASSERT(isnormal(min));
#ifndef OS_WIN // http://trac.wildfiregames.com/ticket/478
TS_ASSERT(!isnormal(sub));
TS_ASSERT(!isnormal(sub2));
#endif
}
void test_fpclassifyf()
{
do_fpclassify<float>();
}
void test_fpclassifyd()
{
do_fpclassify<double>();
}
void test_wcsdup()
{
const wchar_t* a = L"test";

View file

@ -1009,8 +1009,8 @@ static Status snd_data_free(Handle& hsd)
* @param hsd Handle to SndData.
* @param al_buf buffer name.
* @return Status, most commonly:
* INFO::CONTINUE = buffer has been returned; more are expected to be available.
* INFO::OK = buffer has been returned but is the last one (EOF).
* INFO::OK = buffer has been returned; more are expected to be available.
* INFO::ALL_COMPLETE = buffer has been returned but is the last one (EOF).
*/
static Status snd_data_buf_get(Handle hsd, ALuint& al_buf)
{
@ -1029,7 +1029,7 @@ static Status snd_data_buf_get(Handle hsd, ALuint& al_buf)
const size_t size = (size_t)ret;
al_buf = al_buf_alloc(data, (ALsizei)size, sd->al_fmt, sd->al_freq);
return (size < maxBufferSize)? INFO::OK : INFO::CONTINUE;
return (size < maxBufferSize)? INFO::ALL_COMPLETE : INFO::OK;
}
@ -1651,7 +1651,7 @@ public:
ALuint al_buf;
Status ret = snd_data_buf_get(vs->hsd, al_buf);
RETURN_STATUS_IF_ERR(ret);
if(ret == INFO::OK) // no further buffers will be forthcoming
if(ret == INFO::ALL_COMPLETE) // no further buffers will be forthcoming
vs->flags |= VS_EOF;
alSourceQueueBuffers(vs->al_src, 1, &al_buf);

View file

@ -120,7 +120,6 @@ static const StatusDefinition statusDefs[] = {
{ INFO::OK, L"No error reported here" },
{ ERR::FAIL, L"Function failed (no details available)" },
{ INFO::CONTINUE, L"Continue (not an error)" },
{ INFO::SKIPPED, L"Skipped (not an error)" },
{ INFO::CANNOT_HANDLE, L"Cannot handle (not an error)" },
{ INFO::ALL_COMPLETE, L"All complete (not an error)" },

View file

@ -310,6 +310,21 @@ extern Status StatusFromErrno();
}\
while(0)
// if expression (typically the invocation of a callback) evaluates to:
// - INFO::OK, do nothing;
// - INFO::ALL_COMPLETE, return INFO::OK;
// - anything else, return that.
#define RETURN_STATUS_FROM_CALLBACK(expression)\
do\
{\
const Status status_ = (expression);\
if(status_ == INFO::ALL_COMPLETE)\
return INFO::OK;\
else if(status_ != INFO::OK)\
return status_;\
}\
while(0)
// return 0 if expression is negative. use in functions that return pointers.
#define RETURN_0_IF_ERR(expression)\
do\
@ -320,17 +335,6 @@ extern Status StatusFromErrno();
}\
while(0)
// return expression if it evaluates to something other than
// INFO::CONTINUE. use when invoking callbacks.
#define RETURN_IF_NOT_CONTINUE(expression)\
do\
{\
const Status status_ = (expression);\
if(status_ != INFO::CONTINUE)\
return status_;\
}\
while(0)
// warn if expression is false, i.e. zero.
#define WARN_IF_FALSE(expression)\
do\
@ -363,9 +367,6 @@ namespace INFO {
// note: these values are > 100 to allow multiplexing them with
// coroutine return values, which return completion percentage.
// the function (usually a callback) would like to be called again.
const Status CONTINUE = +100000;
// notify caller that nothing was done.
const Status SKIPPED = +100001;

View file

@ -30,122 +30,6 @@
#include "lib/sysdep/arch/ia32/ia32.h"
#include "lib/sysdep/arch/ia32/ia32_asm.h"
static const size_t maxInstructionLength = 15; // IA-32 limitation
static bool IsCall(void* ret_addr, void*& target)
{
target = 0; // (not always possible to determine)
// points to end of the CALL instruction (which is of unknown length)
const u8* c = (const u8*)ret_addr;
// this would allow for avoiding exceptions when accessing ret_addr
// close to the beginning of the code segment. it's not currently set
// because this is really unlikely and not worth the trouble.
const size_t len = maxInstructionLength;
// (most frequent cases first to reduce stack walk overhead:)
// CALL rel32 (E8 cd)
if(len >= 5 && c[-5] == 0xE8)
{
i32 offset;
memcpy(&offset, c-sizeof(offset), sizeof(offset));
target = (void*)(intptr_t(ret_addr) + intptr_t(offset));
return true;
}
// CALL r32 => FF D0-D7
if(len >= 2 && c[-2] == 0xFF && (c[-1] & 0xF8) == 0xD0)
return true;
// CALL [r32 + disp8] => FF 50-57(!54) disp8
if(len >= 3 && c[-3] == 0xFF && (c[-2] & 0xF8) == 0x50 && c[-2] != 0x54)
return true;
// CALL [disp32] => FF 15 disp32
if(len >= 6 && c[-6] == 0xFF && c[-5] == 0x15)
{
void** addr_of_target;
memcpy(&addr_of_target, c-sizeof(addr_of_target), sizeof(addr_of_target));
target = *addr_of_target;
// there are no meaningful checks we can perform: we're called from
// the stack trace code, so ring0 addresses may be legit.
// even if the pointer is 0, it's better to pass its value on
// (may help in tracking down memory corruption)
return true;
}
// CALL [r32 + r32*s] => FF 14 SIB
if(len >= 3 && c[-3] == 0xFF && c[-2] == 0x14)
return true;
// CALL [r32] => FF 00-3F(!14/15)
if(len >= 2 && c[-2] == 0xFF && c[-1] < 0x40 && c[-1] != 0x14 && c[-1] != 0x15)
return true;
// CALL [r32 + r32*s + disp8] => FF 54 SIB disp8
if(len >= 4 && c[-4] == 0xFF && c[-3] == 0x54)
return true;
// CALL [r32 + r32*s + disp32] => FF 94 SIB disp32
if(len >= 7 && c[-7] == 0xFF && c[-6] == 0x94)
return true;
// CALL [r32 + disp32] => FF 90-97(!94) disp32
if(len >= 6 && c[-6] == 0xFF && (c[-5] & 0xF8) == 0x90 && c[-5] != 0x94)
return true;
return false;
}
Status ia32_GetCallTarget(void* ret_addr, void*& target)
{
if(IsCall(ret_addr, target))
{
// follow the incremental linker's jump tables
const u8* c = (const u8*)target;
if(c && c[0] == 0xE9)
{
i32 offset;
memcpy(&offset, c+1, sizeof(offset));
target = (void*)(intptr_t(c)+5 + intptr_t(offset));
}
return INFO::OK;
}
const u8* const instructionWindow = (const u8*)ret_addr - maxInstructionLength;
if(memchr(instructionWindow, 0xCC, maxInstructionLength))
return ERR::AGAIN; // NOWARN (debugger has inserted a breakpoint)
// this shouldn't normally be reached but might happen if the
// call stack is corrupted. note that we mustn't warn, because
// this routine is already called from the stack dump chain.
debug_break();
return ERR::CPU_UNKNOWN_OPCODE; // NOWARN (see above)
}
void cpu_ConfigureFloatingPoint()
{
// no longer set 24 bit (float) precision by default: for
// very long game uptimes (> 1 day; e.g. dedicated server),
// we need full precision when calculating the time.
// if there's a spot where we want to speed up divides|sqrts,
// we can temporarily change precision there.
//ia32_asm_control87(IA32_PC_24, IA32_MCW_PC);
// to help catch bugs, enable as many floating-point exceptions as
// possible. unfortunately SpiderMonkey triggers all of them.
// note: passing a flag *disables* that exception.
ia32_asm_control87(IA32_EM_ZERODIVIDE|IA32_EM_INVALID|IA32_EM_DENORMAL|IA32_EM_OVERFLOW|IA32_EM_UNDERFLOW|IA32_EM_INEXACT, IA32_MCW_EM);
// no longer round toward zero (truncate). changing this setting
// resulted in much faster float->int casts, because the compiler
// could be told (via /QIfist) to use FISTP while still truncating
// the result as required by ANSI C. however, FPU calculation
// results were changed significantly, so it had to be disabled.
//ia32_asm_control87(IA32_RC_CHOP, IA32_MCW_RC);
}
#if MSC_VERSION
// VC 2008 and ICC 12 differ in their declaration of _Interlocked*

View file

@ -19,15 +19,11 @@
; TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
; SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
; optimized assembly code for IA-32. not provided as
; inline assembly because that's compiler-specific.
; assembly code for IA-32 (avoid inline assembly due to differing
; compiler support and/or syntax).
%include "ia32.inc"
; note: pure asm functions prevent inlining but also avoid redundant
; store/loads generated by VC inline asm (ugh).
;-------------------------------------------------------------------------------
; CPUID support
;-------------------------------------------------------------------------------
@ -54,128 +50,3 @@ sym(ia32_asm_cpuid):
pop edi
pop ebx
ret
;-------------------------------------------------------------------------------
; FPU
;-------------------------------------------------------------------------------
; extern "C" u32 __cdecl ia32_asm_control87(u32 new_cw, u32 mask);
global sym(ia32_asm_control87)
sym(ia32_asm_control87):
push eax
fnstcw [esp]
pop eax ; old_cw
mov ecx, [esp+4] ; new_val
mov edx, [esp+8] ; mask
and ecx, edx ; new_val & mask
not edx ; ~mask
and eax, edx ; old_cw & ~mask
or eax, ecx ; (old_cw & ~mask) | (new_val & mask)
push eax ; = new_cw
fldcw [esp]
pop eax
xor eax, eax ; return value
ret
; possible IA-32 FPU control word flags after FXAM: NAN|NORMAL|ZERO
FP_CLASSIFY_MASK equ 0x4500
; extern "C" size_t __cdecl ia32_asm_fpclassifyd(double d);
global sym(ia32_asm_fpclassifyd)
sym(ia32_asm_fpclassifyd):
fld qword [esp+4]
fxam
fnstsw ax
fstp st0
and eax, FP_CLASSIFY_MASK
ret
; extern "C" size_t __cdecl ia32_asm_fpclassifyf(float f);
global sym(ia32_asm_fpclassifyf)
sym(ia32_asm_fpclassifyf):
fld dword [esp+4]
fxam
fnstsw ax
fstp st0
and eax, FP_CLASSIFY_MASK
ret
;-------------------------------------------------------------------------------
; misc
;-------------------------------------------------------------------------------
; write the current execution state (e.g. all register values) into
; (Win32::CONTEXT*)pcontext (defined as void* to avoid dependency).
; optimized for size; this must be straight asm because ; extern "C"
; is compiler-specific and compiler-generated prolog code inserted before
; inline asm trashes EBP and ESP (unacceptable).
; extern "C" void ia32_asm_GetCurrentContext(void* pcontext);
global sym(ia32_asm_GetCurrentContext)
sym(ia32_asm_GetCurrentContext):
pushad
pushfd
mov edi, [esp+4+32+4] ; pcontext
; ContextFlags
mov eax, 0x10007 ; segs, int, control
stosd
; DRx and FloatSave
; rationale: we can't access the debug registers from Ring3, and
; the FPU save area is irrelevant, so zero them.
xor eax, eax
push byte 6+8+20
pop ecx
rep stosd
; CONTEXT_SEGMENTS
mov ax, gs
stosd
mov ax, fs
stosd
mov ax, es
stosd
mov ax, ds
stosd
; CONTEXT_INTEGER
mov eax, [esp+4+32-32] ; edi
stosd
xchg eax, esi
stosd
xchg eax, ebx
stosd
xchg eax, edx
stosd
mov eax, [esp+4+32-8] ; ecx
stosd
mov eax, [esp+4+32-4] ; eax
stosd
; CONTEXT_CONTROL
xchg eax, ebp ; ebp restored by POPAD
stosd
mov eax, [esp+4+32] ; return address
sub eax, 5 ; skip CALL instruction -> call site.
stosd
xor eax, eax
mov ax, cs
stosd
pop eax ; eflags
stosd
lea eax, [esp+32+4+4] ; esp
stosd
xor eax, eax
mov ax, ss
stosd
; ExtendedRegisters
xor ecx, ecx
mov cl, 512/4
rep stosd
popad
ret

View file

@ -30,45 +30,4 @@
struct x86_x64_CpuidRegs;
EXTERN_C void CALL_CONV ia32_asm_cpuid(x86_x64_CpuidRegs* regs);
/// control87
// FPU control word
// .. Precision Control:
const u32 IA32_MCW_PC = 0x0300;
const u32 IA32_PC_24 = 0x0000;
// .. Rounding Control:
const u32 IA32_MCW_RC = 0x0C00;
const u32 IA32_RC_NEAR = 0x0000;
const u32 IA32_RC_DOWN = 0x0400;
const u32 IA32_RC_UP = 0x0800;
const u32 IA32_RC_CHOP = 0x0C00;
// .. Exception Mask:
const u32 IA32_MCW_EM = 0x3F;
const u32 IA32_EM_INVALID = 0x01;
const u32 IA32_EM_DENORMAL = 0x02;
const u32 IA32_EM_ZERODIVIDE = 0x04;
const u32 IA32_EM_OVERFLOW = 0x08;
const u32 IA32_EM_UNDERFLOW = 0x10;
const u32 IA32_EM_INEXACT = 0x20;
/**
* for all 1-bits in mask, update the corresponding FPU control word bits
* with the bit values in new_val.
* @return 0 to indicate success.
**/
EXTERN_C u32 CALL_CONV ia32_asm_control87(u32 new_val, u32 mask);
/// POSIX fpclassify
#define IA32_FP_NAN 0x0100
#define IA32_FP_NORMAL 0x0400
#define IA32_FP_INFINITE (IA32_FP_NAN | IA32_FP_NORMAL)
#define IA32_FP_ZERO 0x4000
#define IA32_FP_SUBNORMAL (IA32_FP_NORMAL | IA32_FP_ZERO)
EXTERN_C size_t CALL_CONV ia32_asm_fpclassifyd(double d);
EXTERN_C size_t CALL_CONV ia32_asm_fpclassifyf(float f);
/**
* write the current execution state (e.g. all register values) into
* (Win32::CONTEXT*)pcontext (defined as void* to avoid dependency).
**/
EXTERN_C void CALL_CONV ia32_asm_GetCurrentContext(void* pcontext);
#endif // #ifndef INCLUDED_IA32_ASM

View file

@ -28,7 +28,6 @@
#include "lib/sysdep/arch/x86_x64/topology.h"
#include <set>
#include <bitset>
#include "lib/bits.h"
#include "lib/module_init.h"
@ -271,7 +270,7 @@ static Status InitCpuTopology()
// generate fake but legitimate APIC IDs
for(size_t processor = 0; processor < cpuTopology.numProcessors; processor++)
cpuTopology.apicIds[processor] = cpuTopology.sortedApicIds[processor] = processor;
cpuTopology.apicIds[processor] = cpuTopology.sortedApicIds[processor] = (ApicId)processor;
return INFO::OK;
}
}

View file

@ -44,10 +44,10 @@
# include <intrin.h> // __rdtsc
#endif
#define CPUID_INTRINSIC 0
#define HAVE_CPUIDEX 0
#if defined(_MSC_FULL_VER) && _MSC_FULL_VER >= 150030729 // __cpuidex available on VC10+ and VC9 SP1 (allows setting ecx beforehand)
# undef CPUID_INTRINSIC
# define CPUID_INTRINSIC 1
# undef HAVE_CPUIDEX
# define HAVE_CPUIDEX 1
#else
# if ARCH_AMD64
# include "lib/sysdep/arch/amd64/amd64_asm.h"
@ -72,7 +72,7 @@
static void cpuid(x86_x64_CpuidRegs* regs)
{
#if CPUID_INTRINSIC
#if HAVE_CPUIDEX
cassert(sizeof(regs->eax) == sizeof(int));
cassert(sizeof(*regs) == 4*sizeof(int));
__cpuidex((int*)regs, regs->eax, regs->ecx);

View file

@ -102,16 +102,4 @@ inline void cpu_Pause()
#endif
}
//-----------------------------------------------------------------------------
// misc
/**
* set the FPU control word to "desirable" values (see implementation)
**/
LIB_API void cpu_ConfigureFloatingPoint();
// NB: cpu_i64FromDouble et al. were faster than _ftol2, but are obsolete
// since VC8 and GCC (with -ffast-math) generate SSE instructions.
#endif // #ifndef INCLUDED_CPU

View file

@ -33,6 +33,7 @@
#include <map>
#include <stack>
#include "lib/bits.h"
#include "lib/sysdep/os/win/win.h" // HWND
#include "lib/sysdep/sysdep.h"
#include "lib/sysdep/os/win/wdbg_sym.h"
@ -45,7 +46,7 @@ static size_t numCallers;
static Status OnFrame(const _tagSTACKFRAME64* frame, uintptr_t UNUSED(cbData))
{
callers[numCallers++] = (void*)frame->AddrPC.Offset;
return INFO::CONTINUE;
return INFO::OK;
}
#pragma optimize("", off)
@ -56,7 +57,9 @@ static Status OnFrame(const _tagSTACKFRAME64* frame, uintptr_t UNUSED(cbData))
// while debug_ResolveSymbol for the function does not)
__declspec(noinline) static void Func1()
{
wdbg_sym_WalkStack(OnFrame, 0, 0, L"wdbg_sym_WalkStack");
CONTEXT context;
(void)debug_CaptureContext(&context);
wdbg_sym_WalkStack(OnFrame, 0, context);
}
__declspec(noinline) static void Func2()
@ -97,18 +100,23 @@ class TestWdbgSym : public CxxTest::TestSuite
wchar_t chars[] = { 'w','c','h','a','r','s',0 };
wchar_t many_wchars[1024]; memset(many_wchars, 'a', sizeof(many_wchars));
debug_printf(L"\n(dumping stack frames may result in access violations %.0d %.0c %.0c %.0g %.0g %.0d %.0d..)\n", ints[0], chars[0], many_wchars[0], large_array_of_large_structs[0].d1, small_array_of_large_structs[0].d1, large_array_of_small_structs[0].i1, small_array_of_small_structs[0].i1);
debug_printf(L"\n(dumping stack frames may result in access violations...)\n");
debug_printf(L" locals: %.0d %.0c %.0c %.0g %.0g %.0d %.0d\n", ints[0], chars[0], many_wchars[0], large_array_of_large_structs[0].d1, small_array_of_large_structs[0].d1, large_array_of_small_structs[0].i1, small_array_of_small_structs[0].i1);
// note: we don't want any kind of dialog to be raised, because
// this test now always runs. therefore, just make sure a decent
// amount of text (not just "(failed)" error messages) was produced.
ErrorMessageMem emm = {0};
const wchar_t* text = debug_BuildErrorMessage(L"dummy", 0,0,0, 0,L"debug_BuildErrorMessage", &emm);
CACHE_ALIGNED u8 context[DEBUG_CONTEXT_SIZE];
(void)debug_CaptureContext(context);
const wchar_t* text = debug_BuildErrorMessage(L"dummy", 0,0,0, context, L"m_test_array", &emm);
TS_ASSERT(wcslen(text) > 500);
#if 0
{
std::wofstream s(L"d:\\out.txt");
s << text;
}
#endif
debug_FreeErrorMessage(&emm);
debug_printf(L"(done dumping stack frames)\n");
@ -287,18 +295,27 @@ public:
void test_stack_walk()
{
Status ret;
Func3();
TS_ASSERT(numCallers >= 3);
size_t foundFunctionBits = 0;
void* funcAddresses[3] = { (void*)&Func1, (void*)&Func2, (void*)&Func3 };
for(size_t i = 0; i < 3; i++)
for(size_t idxCaller = 0; idxCaller < numCallers; idxCaller++)
{
wchar_t func1[DBG_SYMBOL_LEN], func2[DBG_SYMBOL_LEN];
Status ret;
ret = debug_ResolveSymbol(callers[i], func1, 0, 0);
wchar_t callerName[DEBUG_SYMBOL_CHARS];
ret = debug_ResolveSymbol(callers[idxCaller], callerName, 0, 0);
TS_ASSERT_OK(ret);
ret = debug_ResolveSymbol(funcAddresses[i], func2, 0, 0);
TS_ASSERT_OK(ret);
TS_ASSERT_WSTR_CONTAINS(func2, func1);
wchar_t funcName[DEBUG_SYMBOL_CHARS];
for(size_t idxFunc = 0; idxFunc < ARRAY_SIZE(funcAddresses); idxFunc++)
{
ret = debug_ResolveSymbol(funcAddresses[idxFunc], funcName, 0, 0);
TS_ASSERT_OK(ret);
if(wcsstr(funcName, callerName))
foundFunctionBits |= BIT(idxFunc);
}
}
TS_ASSERT(foundFunctionBits == bit_mask<size_t>(ARRAY_SIZE(funcAddresses)));
}
};

View file

@ -338,16 +338,16 @@ public:
Status NotifyOfCaller(uintptr_t pc)
{
if(!m_isRecordingKnownCallers)
return INFO::SKIPPED; // do not affect the stack walk
return INFO::SKIPPED;
// last 'known' function has been reached
if(pc == (uintptr_t)&CallerFilter::CallHeapFunctions)
return INFO::OK; // stop stack walk
return INFO::ALL_COMPLETE;
// pc is a 'known' function on the allocation hook's back-trace
// (e.g. _malloc_dbg and other helper functions)
m_knownCallers.Add(pc);
return INFO::CONTINUE;
return INFO::OK;
}
bool IsKnownCaller(uintptr_t pc) const
@ -675,7 +675,9 @@ public:
void Gather()
{
m_numCallers = 0;
(void)wdbg_sym_WalkStack(OnFrame_Trampoline, (uintptr_t)this);
CONTEXT context;
(void)debug_CaptureContext(&context);
(void)wdbg_sym_WalkStack(OnFrame_Trampoline, (uintptr_t)this, context);
std::fill(m_callers+m_numCallers, m_callers+maxCallers, 0);
}
@ -696,7 +698,7 @@ private:
// skip invalid frames
if(pc == 0)
return INFO::CONTINUE;
return INFO::OK;
Status ret = m_filter.NotifyOfCaller(pc);
// (CallerFilter provokes stack traces of heap functions; if that is
@ -706,11 +708,11 @@ private:
// stop the stack walk if frame storage is full
if(m_numCallers >= maxCallers)
return INFO::OK;
return INFO::ALL_COMPLETE;
if(!m_filter.IsKnownCaller(pc))
m_callers[m_numCallers++] = pc;
return INFO::CONTINUE;
return INFO::OK;
}
static Status OnFrame_Trampoline(const STACKFRAME64* frame, uintptr_t cbData)
@ -850,7 +852,7 @@ static void PrintCallStack(const uintptr_t* callers, size_t numCallers)
wdbg_printf(L"\n partial, unordered call stack:\n");
for(size_t i = 0; i < numCallers; i++)
{
wchar_t name[DBG_SYMBOL_LEN] = {'\0'}; wchar_t file[DBG_FILE_LEN] = {'\0'}; int line = -1;
wchar_t name[DEBUG_SYMBOL_CHARS] = {'\0'}; wchar_t file[DEBUG_FILE_CHARS] = {'\0'}; int line = -1;
Status err = debug_ResolveSymbol((void*)callers[i], name, file, &line);
wdbg_printf(L" ");
if(err != INFO::OK)
@ -944,7 +946,7 @@ static Status wdbg_heap_Init()
FindCodeSegment();
// load symbol information now (fails if it happens during shutdown)
wchar_t name[DBG_SYMBOL_LEN]; wchar_t file[DBG_FILE_LEN]; int line;
wchar_t name[DEBUG_SYMBOL_CHARS]; wchar_t file[DEBUG_FILE_CHARS]; int line;
(void)debug_ResolveSymbol(wdbg_heap_Init, name, file, &line);
int ret = _CrtSetReportHookW2(_CRT_RPTHOOK_INSTALL, ReportHook);

File diff suppressed because it is too large Load diff

View file

@ -27,19 +27,18 @@
#ifndef INCLUDED_WDBG_SYM
#define INCLUDED_WDBG_SYM
#include "lib/sysdep/os/win/win.h" // CONTEXT, EXCEPTION_POINTERS
struct _tagSTACKFRAME64;
struct _CONTEXT;
struct _EXCEPTION_POINTERS;
/**
* called for each stack frame found by wdbg_sym_WalkStack.
*
* @param frame the dbghelp stack frame (we can't just pass the
* instruction-pointer because dump_frame_cb needs the frame pointer to
* locate frame-relative variables)
* instruction-pointer because dump_frame_cb needs the frame pointer to
* locate frame-relative variables)
* @param cbData the user-specified value that was passed to wdbg_sym_WalkStack
* @return INFO::CONTINUE to continue, anything else to stop immediately
* and return that value to wdbg_sym_WalkStack's caller.
* @return Status (see RETURN_STATUS_FROM_CALLBACK).
**/
typedef Status (*StackFrameCallback)(const _tagSTACKFRAME64* frame, uintptr_t cbData);
@ -48,16 +47,16 @@ typedef Status (*StackFrameCallback)(const _tagSTACKFRAME64* frame, uintptr_t cb
*
* @param cb
* @param cbData
* @param pcontext Processor context from which to start (usually taken from
* an exception record), or 0 to walk the current stack.
* @param context Processor context from which to start (taken from
* an exception record or debug_CaptureContext).
* @param lastFuncToSkip
*
* @note It is safe to use ENSURE/debug_warn/WARN_RETURN_STATUS_IF_ERR even during a
* stack trace (which is triggered by ENSURE et al. in app code) because
* nested stack traces are ignored and only the error is displayed.
* stack trace (which is triggered by ENSURE et al. in app code) because
* nested stack traces are ignored and only the error is displayed.
**/
extern Status wdbg_sym_WalkStack(StackFrameCallback cb, uintptr_t cbData = 0, const _CONTEXT* pcontext = 0, const wchar_t* lastFuncToSkip = 0);
LIB_API Status wdbg_sym_WalkStack(StackFrameCallback cb, uintptr_t cbData, CONTEXT& context, const wchar_t* lastFuncToSkip = 0);
extern void wdbg_sym_WriteMinidump(_EXCEPTION_POINTERS* ep);
LIB_API void wdbg_sym_WriteMinidump(EXCEPTION_POINTERS* ep);
#endif // #ifndef INCLUDED_WDBG_SYM

View file

@ -82,7 +82,7 @@ static Status mmap_mem(void* start, size_t len, int prot, int flags, int fd, voi
ENSURE(fd == -1);
// .. if MAP_SHARED, writes are to change "the underlying [mapped]
// object", but there is none here (we're backed by the page file).
ENSURE(flags & MAP_PRIVATE);
ENSURE(!(flags & MAP_SHARED));
// see explanation at MAP_NORESERVE definition.
bool want_commit = (prot != PROT_NONE && !(flags & MAP_NORESERVE));
@ -120,33 +120,35 @@ static Status mmap_mem(void* start, size_t len, int prot, int flags, int fd, voi
// CreateFileMapping / MapViewOfFile. they only support read-only,
// read/write and copy-on-write, so we dumb it down to that and later
// set the correct (and more restrictive) permission via mprotect.
static Status mmap_file_access(int prot, int flags, DWORD& protect, DWORD& dwAccess)
static Status DecodeFlags(int prot, int flags, DWORD& protect, DWORD& access)
{
// assume read-only; other cases handled below.
protect = PAGE_READONLY;
dwAccess = FILE_MAP_READ;
// ensure exactly one of (MAP_SHARED, MAP_PRIVATE) is specified
switch(flags & (MAP_SHARED|MAP_PRIVATE))
{
case 0:
case MAP_SHARED|MAP_PRIVATE:
WARN_RETURN(ERR::INVALID_PARAM);
default:;
}
if(prot & PROT_WRITE)
{
// determine write behavior: (whether they change the underlying file)
switch(flags & (MAP_SHARED|MAP_PRIVATE))
if(flags & MAP_SHARED) // writes affect the file
{
// .. changes are written to file.
case MAP_SHARED:
protect = PAGE_READWRITE;
dwAccess = FILE_MAP_WRITE; // read and write
break;
// .. copy-on-write mapping; writes do not affect the file.
case MAP_PRIVATE:
protect = PAGE_WRITECOPY;
dwAccess = FILE_MAP_COPY;
break;
// .. either none or both of the flags are set. the latter is
// definitely illegal according to POSIX and some man pages
// say exactly one must be set, so abort.
default:
WARN_RETURN(ERR::INVALID_PARAM);
access = FILE_MAP_WRITE; // read and write
}
else // copy on write (file remains unchanged)
{
protect = PAGE_WRITECOPY;
access = FILE_MAP_COPY;
}
}
else
{
protect = PAGE_READONLY;
access = FILE_MAP_READ;
}
return INFO::OK;
@ -171,27 +173,24 @@ static Status mmap_file(void* start, size_t len, int prot, int flags, int fd, of
// choose protection and access rights for CreateFileMapping /
// MapViewOfFile. these are weaker than what PROT_* allows and
// are augmented below by subsequently mprotect-ing.
DWORD protect; DWORD dwAccess;
RETURN_STATUS_IF_ERR(mmap_file_access(prot, flags, protect, dwAccess));
DWORD protect; DWORD access;
RETURN_STATUS_IF_ERR(DecodeFlags(prot, flags, protect, access));
// enough foreplay; now actually map.
const HANDLE hMap = CreateFileMapping(hFile, 0, protect, 0, 0, 0);
// .. create failed; bail now to avoid overwriting the last error value.
if(!hMap)
WARN_RETURN(ERR::NO_MEM);
const DWORD ofs_hi = u64_hi(ofs), ofs_lo = u64_lo(ofs);
void* p = MapViewOfFileEx(hMap, dwAccess, ofs_hi, ofs_lo, (SIZE_T)len, start);
// .. make sure we got the requested address if MAP_FIXED was passed.
void* p = MapViewOfFileEx(hMap, access, u64_hi(ofs), u64_lo(ofs), (SIZE_T)len, start);
// ensure we got the requested address if MAP_FIXED was passed.
ENSURE(!(flags & MAP_FIXED) || (p == start));
// .. free the mapping object now, so that we don't have to hold on to its
// handle until munmap(). it's not actually released yet due to the
// reference held by MapViewOfFileEx (if it succeeded).
// free the mapping object now, so that we don't have to hold on to its
// handle until munmap(). it's not actually released yet due to the
// reference held by MapViewOfFileEx (if it succeeded).
CloseHandle(hMap);
// .. map failed; bail now to avoid "restoring" the last error value.
// map failed; bail now to avoid "restoring" the last error value.
if(!p)
WARN_RETURN(ERR::NO_MEM);
// slap on correct (more restrictive) permissions.
// enforce the desired (more restrictive) protection.
(void)mprotect(p, len, prot);
*pp = p;
@ -201,12 +200,7 @@ static Status mmap_file(void* start, size_t len, int prot, int flags, int fd, of
void* mmap(void* start, size_t len, int prot, int flags, int fd, off_t ofs)
{
if(len == 0) // POSIX says this must cause mmap to fail
{
DEBUG_WARN_ERR(ERR::LOGIC);
errno = EINVAL;
return MAP_FAILED;
}
ASSERT(len != 0);
void* p;
Status status;

View file

@ -38,8 +38,8 @@
#define MAP_PRIVATE 0x02 // writes do not affect the file (copy-on-write)
#define MAP_FIXED 0x04
// .. non-portable
#define MAP_ANONYMOUS 0x10
#define MAP_NORESERVE 0x20
#define MAP_ANONYMOUS 0x10 // backed by the pagefile; fd should be -1
#define MAP_NORESERVE 0x20 // see below
// note: we need a means of only "reserving" virtual address ranges
// for the fixed-address expandable array mechanism. the non-portable
@ -49,7 +49,7 @@
// doesn't commit mmap-ed regions anyway, but we specify this flag to
// make sure of that in the future.
#define MAP_FAILED ((void*)(intptr_t)-1L)
#define MAP_FAILED ((void*)intptr_t(-1))
extern void* mmap(void* start, size_t len, int prot, int flags, int fd, off_t offset);
extern int munmap(void* start, size_t len);

View file

@ -215,7 +215,7 @@ static const wchar_t* GetExceptionDescription(const EXCEPTION_POINTERS* ep,
// return location at which the exception <er> occurred.
// params: see debug_ResolveSymbol.
static void GetExceptionLocus(const EXCEPTION_POINTERS* ep,
static void GetExceptionLocus(EXCEPTION_POINTERS* ep,
wchar_t* file, int* line, wchar_t* func)
{
// HACK: <ep> provides no useful information - ExceptionAddress always
@ -270,9 +270,9 @@ long __stdcall wseh_ExceptionFilter(struct _EXCEPTION_POINTERS* ep)
// extract details from ExceptionRecord.
wchar_t descriptionBuf[150];
const wchar_t* description = GetExceptionDescription(ep, descriptionBuf, ARRAY_SIZE(descriptionBuf));
wchar_t file[DBG_FILE_LEN] = {0};
wchar_t file[DEBUG_FILE_CHARS] = {0};
int line = 0;
wchar_t func[DBG_SYMBOL_LEN] = {0};
wchar_t func[DEBUG_SYMBOL_CHARS] = {0};
GetExceptionLocus(ep, file, &line, func);
wchar_t message[500];

View file

@ -760,8 +760,6 @@ void EarlyInit()
// add all debug_printf "tags" that we are interested in:
debug_filter_add(L"TIMER");
cpu_ConfigureFloatingPoint();
timer_LatchStartTime();
FixLocales();

View file

@ -72,7 +72,7 @@ static Status BuildDirEntListCB(const VfsPath& pathname, const FileInfo& UNUSED(
jsval val = ToJSVal( CStrW(pathname.string()) );
JS_SetElement(s->cx, s->filename_array, s->cur_idx++, &val);
return INFO::CONTINUE;
return INFO::OK;
}