mirror of
https://gitea.wildfiregames.com/0ad/0ad
synced 2026-06-17 05:44:08 -07:00
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:
parent
2d9e473483
commit
0d23e3f333
32 changed files with 575 additions and 1069 deletions
|
|
@ -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));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -381,7 +381,7 @@ private:
|
|||
lfh_dst += size;
|
||||
lfh_bytes_remaining -= size;
|
||||
|
||||
return INFO::CONTINUE;
|
||||
return INFO::OK;
|
||||
}
|
||||
|
||||
mutable u8* lfh_dst;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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())
|
||||
{
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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";
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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)" },
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
||||
|
|
|
|||
|
|
@ -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*
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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)));
|
||||
}
|
||||
};
|
||||
|
|
|
|||
|
|
@ -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
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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];
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue