- Timestamp:
- 07/18/11 11:21:56 (13 years ago)
- Location:
- ps/trunk/source
- Files:
-
- 32 edited
-
lib/allocators/unique_range.cpp (modified) (3 diffs)
-
lib/allocators/unique_range.h (modified) (11 diffs)
-
lib/debug.cpp (modified) (2 diffs)
-
lib/debug.h (modified) (7 diffs)
-
lib/debug_stl.cpp (modified) (4 diffs)
-
lib/debug_stl.h (modified) (2 diffs)
-
lib/external_libraries/dbghelp.h (modified) (1 diff)
-
lib/file/archive/archive_zip.cpp (modified) (1 diff)
-
lib/file/archive/stream.cpp (modified) (2 diffs)
-
lib/file/io/io.cpp (modified) (1 diff)
-
lib/file/io/io.h (modified) (10 diffs)
-
lib/posix/posix.cpp (modified) (1 diff)
-
lib/posix/posix.h (modified) (1 diff)
-
lib/posix/tests/test_posix.h (modified) (1 diff)
-
lib/res/sound/snd_mgr.cpp (modified) (3 diffs)
-
lib/status.cpp (modified) (1 diff)
-
lib/status.h (modified) (3 diffs)
-
lib/sysdep/arch/ia32/ia32.cpp (modified) (1 diff)
-
lib/sysdep/arch/ia32/ia32_asm.asm (modified) (2 diffs)
-
lib/sysdep/arch/ia32/ia32_asm.h (modified) (1 diff)
-
lib/sysdep/arch/x86_x64/topology.cpp (modified) (2 diffs)
-
lib/sysdep/arch/x86_x64/x86_x64.cpp (modified) (2 diffs)
-
lib/sysdep/cpu.h (modified) (1 diff)
-
lib/sysdep/os/win/tests/test_wdbg_sym.h (modified) (6 diffs)
-
lib/sysdep/os/win/wdbg_heap.cpp (modified) (6 diffs)
-
lib/sysdep/os/win/wdbg_sym.cpp (modified) (69 diffs)
-
lib/sysdep/os/win/wdbg_sym.h (modified) (3 diffs)
-
lib/sysdep/os/win/wposix/wmman.cpp (modified) (4 diffs)
-
lib/sysdep/os/win/wposix/wmman.h (modified) (2 diffs)
-
lib/sysdep/os/win/wseh.cpp (modified) (2 diffs)
-
ps/GameSetup/GameSetup.cpp (modified) (1 diff)
-
ps/scripting/JSInterface_VFS.cpp (modified) (1 diff)
Legend:
- Unmodified
- Added
- Removed
-
ps/trunk/source/lib/allocators/unique_range.cpp
r9362 r9871 2 2 #include "lib/allocators/unique_range.h" 3 3 4 #include "lib/bits.h" // is_pow2, round_up 4 5 #include "lib/sysdep/cpu.h" // cpu_AtomicAdd 5 6 #include "lib/sysdep/rtl.h" // rtl_FreeAligned 6 7 7 8 8 static void UniqueRangeDeleterNone(void* UNUSED(pointer), size_t UNUSED(size)) 9 // (hardwired index avoids RegisterUniqueRangeDeleter overhead for 10 // this commonly used allocator.) 11 static const IdxDeleter idxDeleterAligned = 1; 12 13 14 static void FreeNone(void* UNUSED(pointer), size_t UNUSED(size)) 9 15 { 10 // (introducing this do-nothing function avoids having to check whether deleter != 0) 16 // (providing a deleter function for idxDeleterNone avoids 17 // having to check whether deleters[idxDeleter] == 0) 11 18 } 12 19 13 static void UniqueRangeDeleterAligned(void* pointer, size_t UNUSED(size))20 static void FreeAligned(void* pointer, size_t UNUSED(size)) 14 21 { 15 22 return rtl_FreeAligned(pointer); … … 17 24 18 25 19 static UniqueRangeDeleter deleters[ idxDeleterBits+1] = { UniqueRangeDeleterNone, UniqueRangeDeleterAligned };26 static UniqueRangeDeleter deleters[allocationAlignment] = { FreeNone, FreeAligned }; 20 27 21 28 static IdxDeleter numDeleters = 2; 22 29 23 30 24 IdxDeleter AddUniqueRangeDeleter(UniqueRangeDeleter deleter)31 void RegisterUniqueRangeDeleter(UniqueRangeDeleter deleter, volatile IdxDeleter* idxDeleterOut) 25 32 { 26 33 ENSURE(deleter); 27 IdxDeleter idxDeleter = cpu_AtomicAdd(&numDeleters, 1); 34 35 if(!cpu_CAS(idxDeleterOut, idxDeleterNone, -1)) // not the first call for this deleter 36 { 37 // wait until an index has been assigned 38 while(*idxDeleterOut <= 0) 39 cpu_Pause(); 40 return; 41 } 42 43 const IdxDeleter idxDeleter = cpu_AtomicAdd(&numDeleters, 1); 28 44 ENSURE(idxDeleter < (IdxDeleter)ARRAY_SIZE(deleters)); 29 45 deleters[idxDeleter] = deleter; 30 return idxDeleter; 46 COMPILER_FENCE; 47 *idxDeleterOut = idxDeleter; // linearization point 31 48 } 32 49 … … 39 56 deleters[idxDeleter](pointer, size); 40 57 } 58 59 60 UniqueRange AllocateAligned(size_t size, size_t alignment) 61 { 62 ENSURE(is_pow2(alignment)); 63 alignment = std::max(alignment, allocationAlignment); 64 65 const size_t alignedSize = round_up(size, alignment); 66 const UniqueRange::pointer p = rtl_AllocateAligned(alignedSize, alignment); 67 68 static volatile IdxDeleter idxDeleterAligned; 69 if(idxDeleterAligned == 0) 70 RegisterUniqueRangeDeleter(FreeAligned, &idxDeleterAligned); 71 72 return RVALUE(UniqueRange(p, size, idxDeleterAligned)); 73 } -
ps/trunk/source/lib/allocators/unique_range.h
r9362 r9871 3 3 4 4 #include "lib/lib_api.h" 5 #include "lib/alignment.h" // allocationAlignment 5 6 6 7 // we usually don't hold multiple references to allocations, so unique_ptr … … 15 16 // however, this inflates the pointer size to 3 words. if only a few allocator types 16 17 // are needed, we can replace the function pointer with an index stashed into the 17 // lower bits of the pointer (safe because all ocations are always aligned to the18 // word size).18 // lower bits of the pointer (safe because all allocations' addresses are multiples 19 // of allocationAlignment). 19 20 typedef intptr_t IdxDeleter; 20 21 21 22 // no-op deleter (use when returning part of an existing allocation) 22 // must be zero because reset() sets address (which includes idxDeleter) to zero.23 23 static const IdxDeleter idxDeleterNone = 0; 24 25 static const IdxDeleter idxDeleterAligned = 1;26 27 // (temporary value to prevent concurrent calls to AddUniqueRangeDeleter)28 static const IdxDeleter idxDeleterBusy = -IdxDeleter(1);29 30 // governs the maximum number of IdxDeleter and each pointer's alignment requirements31 static const IdxDeleter idxDeleterBits = 0x7;32 24 33 25 typedef void (*UniqueRangeDeleter)(void* pointer, size_t size); 34 26 35 27 /** 36 * @return the next available IdxDeleter and associate it with the deleter. 37 * halts the program if the idxDeleterBits limit has been reached. 28 * register a deleter, returning its index within the table. 38 29 * 39 * thread-safe, but no attempt is made to detect whether the deleter has already been 40 * registered (would require a mutex). each allocator must ensure they only call this once. 30 * @param deleter function pointer. must be uniquely associated with 31 * the idxDeleter storage location. 32 * @param idxDeleter location where to store the next available index. 33 * if it is already non-zero, skip the call to this function to 34 * avoid overhead. 35 * 36 * thread-safe. idxDeleter is used for mutual exclusion between 37 * multiple callers for the same deleter. concurrent registration of 38 * different deleters is also safe due to atomic increments. 39 * 40 * halts the program if more than allocationAlignment deleters are 41 * to be registered. 41 42 **/ 42 LIB_API IdxDeleter AddUniqueRangeDeleter(UniqueRangeDeleter deleter);43 LIB_API void RegisterUniqueRangeDeleter(UniqueRangeDeleter deleter, volatile IdxDeleter* idxDeleter); 43 44 44 45 LIB_API void CallUniqueRangeDeleter(void* pointer, size_t size, IdxDeleter idxDeleter) throw(); … … 57 58 UniqueRange() 58 59 { 59 Set(0, 0, idxDeleterNone);60 Clear(); 60 61 } 61 62 … … 67 68 UniqueRange(RVALUE_REF(UniqueRange) rvalue) 68 69 { 69 UniqueRange& lvalue = LVALUE(rvalue); 70 address_ = lvalue.address_; 71 size_ = lvalue.size_; 72 lvalue.address_ = 0; 70 Pilfer(LVALUE(rvalue)); 73 71 } 74 72 … … 79 77 { 80 78 Delete(); 81 address_ = lvalue.address_; 82 size_ = lvalue.size_; 83 lvalue.address_ = 0; 79 Pilfer(lvalue); 84 80 } 85 81 return *this; … … 93 89 pointer get() const 94 90 { 95 return pointer(address_ & ~ idxDeleterBits);91 return pointer(address_ & ~(allocationAlignment-1)); 96 92 } 97 93 98 94 IdxDeleter get_deleter() const 99 95 { 100 return IdxDeleter(address_ & idxDeleterBits);96 return IdxDeleter(address_ % allocationAlignment); 101 97 } 102 98 … … 110 106 { 111 107 pointer ret = get(); 112 address_ = 0;108 Clear(); 113 109 return ret; 114 110 } … … 117 113 { 118 114 Delete(); 119 address_ = 0;115 Clear(); 120 116 } 121 117 … … 140 136 void Set(pointer p, size_t size, IdxDeleter deleter) 141 137 { 142 ASSERT((uintptr_t(p) & idxDeleterBits) == 0);143 ASSERT( deleter <= idxDeleterBits);138 ASSERT((uintptr_t(p) % allocationAlignment) == 0); 139 ASSERT(size_t(deleter) < allocationAlignment); 144 140 145 141 address_ = uintptr_t(p) | deleter; … … 149 145 ASSERT(get_deleter() == deleter); 150 146 ASSERT(this->size() == size); 147 } 148 149 void Clear() 150 { 151 Set(0, 0, idxDeleterNone); 152 } 153 154 void Pilfer(UniqueRange& victim) 155 { 156 const size_t size = victim.size(); 157 const IdxDeleter idxDeleter = victim.get_deleter(); 158 pointer p = victim.release(); 159 Set(p, size, idxDeleter); 160 victim.Clear(); 151 161 } 152 162 … … 180 190 } 181 191 192 LIB_API UniqueRange AllocateAligned(size_t size, size_t alignment); 193 182 194 #endif // #ifndef INCLUDED_ALLOCATORS_UNIQUE_RANGE -
ps/trunk/source/lib/debug.cpp
r9545 r9871 533 533 ErrorReaction debug_OnError(Status err, atomic_bool* suppress, const wchar_t* file, int line, const char* func) 534 534 { 535 CACHE_ALIGNED u8 context[DEBUG_CONTEXT_SIZE]; 536 (void)debug_CaptureContext(context); 537 535 538 if(ShouldSkipError(err)) 536 539 return ER_CONTINUE; 537 540 538 void* context = 0;539 541 const wchar_t* lastFuncToSkip = L"debug_OnError"; 540 542 wchar_t buf[400]; … … 547 549 ErrorReaction debug_OnAssertionFailure(const wchar_t* expr, atomic_bool* suppress, const wchar_t* file, int line, const char* func) 548 550 { 549 void* context = 0; 551 CACHE_ALIGNED u8 context[DEBUG_CONTEXT_SIZE]; 552 (void)debug_CaptureContext(context); 553 550 554 const std::wstring lastFuncToSkip = L"debug_OnAssertionFailure"; 551 555 wchar_t buf[400]; -
ps/trunk/source/lib/debug.h
r9410 r9871 40 40 #include "lib/types.h" // intptr_t 41 41 #include "lib/status.h" 42 #include "lib/alignment.h" 42 43 #include "lib/code_annotation.h" 43 44 #include "lib/code_generation.h" … … 120 121 * rationale: this value is fairly distinctive and helps when 121 122 * debugging the symbol engine. 122 * initial value is 0 rather that another constant; this avoids 123 * allocating .rdata space. 123 * use 0 as the initial value to avoid allocating .rdata space. 124 124 **/ 125 125 const atomic_bool DEBUG_SUPPRESS = 0xAB; … … 175 175 ERI_NOT_IMPLEMENTED 176 176 }; 177 177 178 178 179 /** … … 193 194 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); 194 195 195 /** 196 * convenience version, in case the advanced parameters aren't needed. 197 * macro instead of providing overload/default values for C compatibility. 198 **/ 199 #define DEBUG_DISPLAY_ERROR(text) debug_DisplayError(text, 0, 0, L"debug_DisplayError", WIDEN(__FILE__),__LINE__,__func__, 0) 196 // simplified version for just displaying an error message 197 #define DEBUG_DISPLAY_ERROR(description)\ 198 do\ 199 {\ 200 CACHE_ALIGNED u8 context[DEBUG_CONTEXT_SIZE];\ 201 (void)debug_CaptureContext(context);\ 202 (void)debug_DisplayError(description, 0, context, L"debug_DisplayError", WIDEN(__FILE__), __LINE__, __func__, 0);\ 203 }\ 204 while(0) 200 205 201 206 … … 418 423 419 424 /** 420 * Maximum number of characters (including trailing \\0) written to425 * Maximum number of characters (including null terminator) written to 421 426 * user's buffers by debug_ResolveSymbol. 422 427 **/ 423 const size_t DBG_SYMBOL_LEN= 1000;424 const size_t DBG_FILE_LEN= 100;428 static const size_t DEBUG_SYMBOL_CHARS = 1000; 429 static const size_t DEBUG_FILE_CHARS = 100; 425 430 426 431 /** 427 432 * read and return symbol information for the given address. 428 433 * 429 * NOTE: the PDB implementation is rather slow (~500 us).434 * NOTE: the PDB implementation is rather slow (~500 us). 430 435 * 431 436 * @param ptr_of_interest address of symbol (e.g. function, variable) 432 * @param sym_name optional out; size >= DBG_SYMBOL_LEN chars;433 * receives symbol name returned via debug info.434 * @param file optional out; size >= DBG_FILE_LEN chars; receives435 * base name only (no path; see rationale in wdbg_sym) of436 * source file containing the symbol.437 * @param sym_name optional out; holds at least DEBUG_SYMBOL_CHARS; 438 * receives symbol name returned via debug info. 439 * @param file optional out; holds at least DEBUG_FILE_CHARS; 440 * receives base name only (no path; see rationale in wdbg_sym) of 441 * source file containing the symbol. 437 442 * @param line optional out; receives source file line number of symbol. 438 443 * … … 444 449 LIB_API Status debug_ResolveSymbol(void* ptr_of_interest, wchar_t* sym_name, wchar_t* file, int* line); 445 450 451 static const size_t DEBUG_CONTEXT_SIZE = 2048; // Win32 CONTEXT is currently 1232 bytes 452 453 /** 454 * @param context must point to an instance of the platform-specific type 455 * (e.g. CONTEXT) or CACHE_ALIGNED storage of DEBUG_CONTEXT_SIZE bytes. 456 **/ 457 LIB_API Status debug_CaptureContext(void* context); 458 459 446 460 /** 447 461 * write a complete stack trace (including values of local variables) into … … 451 465 * @param maxChars Max chars of buffer (should be several thousand). 452 466 * @param context Platform-specific representation of execution state 453 * (e.g. Win32 CONTEXT). if not NULL, tracing starts there; this is useful 454 * for exceptions. Otherwise, tracing starts from the current call stack. 467 * (e.g. Win32 CONTEXT). either specify an SEH exception's 468 * context record or use debug_CaptureContext to retrieve the current state. 469 * Rationale: intermediates such as debug_DisplayError change the 470 * context, so it should be captured as soon as possible. 455 471 * @param lastFuncToSkip Is used for omitting error-reporting functions like 456 472 * debug_OnAssertionFailure from the stack trace. It is either 0 (skip nothing) or -
ps/trunk/source/lib/debug_stl.cpp
r9447 r9871 38 38 static const StatusDefinition debugStlStatusDefinitions[] = { 39 39 { ERR::STL_CNT_UNKNOWN, L"Unknown STL container type_name" }, 40 { ERR::STL_CNT_UNSUPPORTED, L"Unsupported STL container" }, 40 41 { ERR::STL_CNT_INVALID, L"Container type is known but contents are invalid" } 41 42 }; … … 50 51 {\ 51 52 src += ARRAY_SIZE(what)-1-1; /* see preincrement rationale*/\ 52 wcscpy_s(dst, ARRAY_SIZE(w hat), (with));\53 wcscpy_s(dst, ARRAY_SIZE(with), (with));\ 53 54 dst += ARRAY_SIZE(with)-1;\ 54 55 } … … 140 141 REPLACE(L"std::_List_nod", L"list") 141 142 REPLACE(L"std::_Tree_nod", L"map") 142 REPLACE(L"std::basic_string<char,", L"string<") 143 REPLACE(L"std::basic_string<unsigned short,", L"wstring<") 144 STRIP(L"std::char_traits<char>,") 145 STRIP(L"std::char_traits<unsigned short>,") 143 REPLACE(L"std::basic_string<char, ", L"string<") 144 REPLACE(L"std::basic_string<__wchar_t, ", L"wstring<") 145 REPLACE(L"std::basic_string<unsigned short, ", L"wstring<") 146 STRIP(L"std::char_traits<char>, ") 147 STRIP(L"std::char_traits<unsigned short>, ") 148 STRIP(L"std::char_traits<__wchar_t>, ") 146 149 STRIP(L"std::_Tmap_traits") 147 150 STRIP(L"std::_Tset_traits") … … 555 558 UNUSED2(el_iterator); 556 559 UNUSED2(it_mem); 557 return ERR::STL_CNT_UN KNOWN; // NOWARN560 return ERR::STL_CNT_UNSUPPORTED; // NOWARN 558 561 #else 559 562 -
ps/trunk/source/lib/debug_stl.h
r9410 r9871 32 32 { 33 33 const Status STL_CNT_UNKNOWN = -100500; 34 const Status STL_CNT_UNSUPPORTED = -100501; 34 35 // likely causes: not yet initialized or memory corruption. 35 const Status STL_CNT_INVALID = -10050 1;36 const Status STL_CNT_INVALID = -100502; 36 37 } 37 38 … … 46 47 * @param name Buffer holding input symbol name; modified in-place. 47 48 * There is no length limit; must be large enough to hold typical STL 48 * strings. D BG_SYMBOL_LENchars is a good measure.49 * strings. DEBUG_SYMBOL_CHARS chars is a good measure. 49 50 * @return name for convenience. 50 51 **/ -
ps/trunk/source/lib/external_libraries/dbghelp.h
r8484 r9871 122 122 #endif 123 123 124 #pragma pack(push, 8) // seems to be required 125 124 126 #define _NO_CVCONST_H // request SymTagEnum be defined 125 127 #include <dbghelp.h> // must come after win.h and the above definitions 128 129 #pragma pack(pop) 126 130 127 131 #if ICC_VERSION -
ps/trunk/source/lib/file/archive/archive_zip.cpp
r9550 r9871 382 382 lfh_bytes_remaining -= size; 383 383 384 return INFO:: CONTINUE;384 return INFO::OK; 385 385 } 386 386 -
ps/trunk/source/lib/file/archive/stream.cpp
r9462 r9871 117 117 { 118 118 if(m_outProduced == m_outputBufferManager.Size()) // output buffer full; must not call Process 119 return INFO:: OK;119 return INFO::ALL_COMPLETE; 120 120 121 121 size_t inConsumed, outProduced; … … 126 126 m_inConsumed += inConsumed; 127 127 m_outProduced += outProduced; 128 return INFO:: CONTINUE;128 return INFO::OK; 129 129 } 130 130 -
ps/trunk/source/lib/file/io/io.cpp
r9447 r9871 32 32 33 33 namespace io { 34 35 UniqueRange Allocate(size_t size, size_t alignment)36 {37 ENSURE(is_pow2(alignment));38 if(alignment <= (size_t)idxDeleterBits)39 alignment = idxDeleterBits+1;40 41 const size_t alignedSize = round_up(size, alignment);42 const UniqueRange::pointer p = rtl_AllocateAligned(alignedSize, alignment);43 return RVALUE(UniqueRange(p, size, idxDeleterAligned));44 }45 46 34 47 35 // this is just a thin wrapper on top of lowio and POSIX aio. -
ps/trunk/source/lib/file/io/io.h
r9580 r9871 32 32 #include "lib/alignment.h" 33 33 #include "lib/bits.h" 34 #include "lib/timer.h" 34 35 #include "lib/file/file.h" 35 36 #include "lib/sysdep/filesystem.h" // wtruncate … … 52 53 // use this instead of the file cache for write buffers that are 53 54 // never reused (avoids displacing other items). 54 LIB_API UniqueRange Allocate(size_t size, size_t alignment = maxSectorSize); 55 static inline UniqueRange Allocate(size_t size, size_t alignment = maxSectorSize) 56 { 57 return RVALUE(AllocateAligned(size, alignment)); 58 } 55 59 56 60 … … 147 151 /** 148 152 * called after a block I/O has completed. 149 * 150 * @return INFO::CONTINUE to proceed; any other value will 151 * be immediately returned by Run. 153 * @return Status (see RETURN_STATUS_FROM_CALLBACK). 152 154 * 153 155 * allows progress notification and processing data while waiting for … … 156 158 Status operator()(const u8* UNUSED(block), size_t UNUSED(blockSize)) const 157 159 { 158 return INFO:: CONTINUE;160 return INFO::OK; 159 161 } 160 162 }; … … 165 167 /** 166 168 * called before a block I/O is issued. 167 * 168 * @return INFO::CONTINUE to proceed; any other value will 169 * be immediately returned by Run. 169 * @return Status (see RETURN_STATUS_FROM_CALLBACK). 170 170 * 171 171 * allows generating the data to write while waiting for … … 174 174 Status operator()(aiocb& UNUSED(cb)) const 175 175 { 176 return INFO:: CONTINUE;176 return INFO::OK; 177 177 } 178 178 }; … … 246 246 cb.aio_nbytes = round_up(size_t(op.size - blocksIssued * p.blockSize), size_t(p.alignment)); 247 247 248 RETURN_ IF_NOT_CONTINUE(issueHook(cb));248 RETURN_STATUS_FROM_CALLBACK(issueHook(cb)); 249 249 250 250 RETURN_STATUS_IF_ERR(Issue(cb, p.queueDepth)); … … 254 254 RETURN_STATUS_IF_ERR(WaitUntilComplete(cb, p.queueDepth)); 255 255 256 RETURN_ IF_NOT_CONTINUE(completedHook((u8*)cb.aio_buf, cb.aio_nbytes));256 RETURN_STATUS_FROM_CALLBACK(completedHook((u8*)cb.aio_buf, cb.aio_nbytes)); 257 257 } 258 258 … … 276 276 // Store 277 277 278 // efficient writing requires preallocation , andthe resulting file is278 // efficient writing requires preallocation; the resulting file is 279 279 // padded to the sector size and needs to be truncated afterwards. 280 280 // this function takes care of both. … … 317 317 // Load 318 318 319 // convenience function provided for symmetry with Store 319 // convenience function provided for symmetry with Store. 320 320 template<class CompletedHook, class IssueHook> 321 321 static inline Status Load(const OsPath& pathname, void* buf, size_t size, const Parameters& p = Parameters(), const CompletedHook& completedHook = CompletedHook(), const IssueHook& issueHook = IssueHook()) -
ps/trunk/source/lib/posix/posix.cpp
r9015 r9871 23 23 #include "precompiled.h" 24 24 #include "lib/posix/posix.h" 25 26 #if ARCH_IA3227 # include "lib/sysdep/arch/ia32/ia32_asm.h"28 #endif29 30 31 #if !HAVE_C99_MATH32 33 size_t fpclassifyd(double d)34 {35 #if ARCH_IA3236 return ia32_asm_fpclassifyd(d);37 #else38 // really sucky stub implementation; doesn't attempt to cover all cases.39 40 if(d != d)41 return FP_NAN;42 else43 return FP_NORMAL;44 #endif45 }46 47 size_t fpclassifyf(float f)48 {49 #if ARCH_IA3250 return ia32_asm_fpclassifyf(f);51 #else52 const double d = (double)f;53 return fpclassifyd(d);54 #endif55 }56 57 #endif // #if !HAVE_C99_MATH58 25 59 26 -
ps/trunk/source/lib/posix/posix.h
r9572 r9871 116 116 #endif 117 117 118 // fpclassify etc (too few/diverse to make separate HAVE_ for each)119 #if HAVE_C99 || ICC_VERSION || GCC_VERSION120 # define HAVE_C99_MATH 1121 #else122 # define HAVE_C99_MATH 0123 #endif124 125 #if !HAVE_C99_MATH126 extern size_t fpclassifyf(float f);127 extern size_t fpclassifyd(double d);128 #define fpclassify(x) ( (sizeof(x) == sizeof(float))? fpclassifyf(x) : fpclassifyd(x) )129 130 // these definitions "happen" to match IA32_FP_* and allow using131 // ia32_fp_classify without having to translate the return value.132 // we don't #define to IA32_FP_* to avoid dependency.133 # define FP_NAN 0x0100134 # define FP_NORMAL 0x0400135 # define FP_INFINITE (FP_NAN | FP_NORMAL)136 # define FP_ZERO 0x4000137 # define FP_SUBNORMAL (FP_NORMAL | FP_ZERO)138 139 # define isnan(d) (fpclassify(d) == FP_NAN)140 # define isfinite(d) ((fpclassify(d) & FP_NAN) == 0)141 # define isinf(d) (fpclassify(d) == (FP_NAN|FP_NORMAL))142 # define isnormal(d) (fpclassify(d) == FP_NORMAL)143 //# define signbit144 #else // HAVE_C99_MATH145 118 // Some systems have C99 support but in C++ they provide only std::isfinite 146 119 // and not isfinite. C99 specifies that isfinite is a macro, so we can use 147 120 // #ifndef and define it if it's not there already. 148 121 // We've included <cmath> above to make sure it defines that macro. 149 # ifndef isfinite 150 # define fpclassify std::fpclassify 122 #ifndef isfinite 123 # if MSC_VERSION 124 # define isfinite _finite 125 # define isnan _isnan 126 # else 151 127 # define isfinite std::isfinite 152 128 # define isnan std::isnan 153 # define isinf std::isinf154 # define isnormal std::isnormal155 129 # endif 156 #endif // HAVE_C99_MATH130 #endif 157 131 158 132 #endif // #ifndef INCLUDED_POSIX -
ps/trunk/source/lib/posix/tests/test_posix.h
r7513 r9871 28 28 { 29 29 public: 30 template<typename T>31 void do_fpclassify()32 {33 T zero = 0.f;34 T one = 1.f;35 T inf = std::numeric_limits<T>::infinity();36 T ninf = -std::numeric_limits<T>::infinity();37 T qnan = std::numeric_limits<T>::quiet_NaN();38 T snan = std::numeric_limits<T>::signaling_NaN();39 T min = std::numeric_limits<T>::min();40 T sub = std::numeric_limits<T>::denorm_min();41 T sub2 = std::numeric_limits<T>::min() / 2;42 43 TS_ASSERT_EQUALS((int)fpclassify(zero), (int)FP_ZERO);44 TS_ASSERT_EQUALS((int)fpclassify(one), (int)FP_NORMAL);45 TS_ASSERT_EQUALS((int)fpclassify(inf), (int)FP_INFINITE);46 TS_ASSERT_EQUALS((int)fpclassify(ninf), (int)FP_INFINITE);47 TS_ASSERT_EQUALS((int)fpclassify(qnan), (int)FP_NAN);48 TS_ASSERT_EQUALS((int)fpclassify(snan), (int)FP_NAN);49 TS_ASSERT_EQUALS((int)fpclassify(min), (int)FP_NORMAL);50 #ifndef OS_WIN // http://trac.wildfiregames.com/ticket/47851 TS_ASSERT_EQUALS((int)fpclassify(sub), (int)FP_SUBNORMAL);52 TS_ASSERT_EQUALS((int)fpclassify(sub2), (int)FP_SUBNORMAL);53 #endif54 55 TS_ASSERT(!isnan(zero));56 TS_ASSERT(!isnan(one));57 TS_ASSERT(!isnan(inf));58 TS_ASSERT(!isnan(ninf));59 TS_ASSERT(isnan(qnan));60 TS_ASSERT(isnan(snan));61 TS_ASSERT(!isnan(min));62 TS_ASSERT(!isnan(sub));63 TS_ASSERT(!isnan(sub2));64 65 TS_ASSERT(isfinite(zero));66 TS_ASSERT(isfinite(one));67 TS_ASSERT(!isfinite(inf));68 TS_ASSERT(!isfinite(ninf));69 TS_ASSERT(!isfinite(qnan));70 TS_ASSERT(!isfinite(snan));71 TS_ASSERT(isfinite(min));72 TS_ASSERT(isfinite(sub));73 TS_ASSERT(isfinite(sub2));74 75 TS_ASSERT(!isinf(zero));76 TS_ASSERT(!isinf(one));77 TS_ASSERT(isinf(inf));78 TS_ASSERT(isinf(ninf));79 TS_ASSERT(!isinf(qnan));80 TS_ASSERT(!isinf(snan));81 TS_ASSERT(!isinf(min));82 TS_ASSERT(!isinf(sub));83 TS_ASSERT(!isinf(sub2));84 85 TS_ASSERT(!isnormal(zero));86 TS_ASSERT(isnormal(one));87 TS_ASSERT(!isnormal(inf));88 TS_ASSERT(!isnormal(ninf));89 TS_ASSERT(!isnormal(qnan));90 TS_ASSERT(!isnormal(snan));91 TS_ASSERT(isnormal(min));92 #ifndef OS_WIN // http://trac.wildfiregames.com/ticket/47893 TS_ASSERT(!isnormal(sub));94 TS_ASSERT(!isnormal(sub2));95 #endif96 }97 98 void test_fpclassifyf()99 {100 do_fpclassify<float>();101 }102 103 void test_fpclassifyd()104 {105 do_fpclassify<double>();106 }107 108 30 void test_wcsdup() 109 31 { -
ps/trunk/source/lib/res/sound/snd_mgr.cpp
r9477 r9871 1010 1010 * @param al_buf buffer name. 1011 1011 * @return Status, most commonly: 1012 * INFO:: CONTINUE= buffer has been returned; more are expected to be available.1013 * INFO:: OK= buffer has been returned but is the last one (EOF).1012 * INFO::OK = buffer has been returned; more are expected to be available. 1013 * INFO::ALL_COMPLETE = buffer has been returned but is the last one (EOF). 1014 1014 */ 1015 1015 static Status snd_data_buf_get(Handle hsd, ALuint& al_buf) … … 1030 1030 al_buf = al_buf_alloc(data, (ALsizei)size, sd->al_fmt, sd->al_freq); 1031 1031 1032 return (size < maxBufferSize)? INFO:: OK : INFO::CONTINUE;1032 return (size < maxBufferSize)? INFO::ALL_COMPLETE : INFO::OK; 1033 1033 } 1034 1034 … … 1652 1652 Status ret = snd_data_buf_get(vs->hsd, al_buf); 1653 1653 RETURN_STATUS_IF_ERR(ret); 1654 if(ret == INFO:: OK) // no further buffers will be forthcoming1654 if(ret == INFO::ALL_COMPLETE) // no further buffers will be forthcoming 1655 1655 vs->flags |= VS_EOF; 1656 1656 -
ps/trunk/source/lib/status.cpp
r9545 r9871 121 121 { ERR::FAIL, L"Function failed (no details available)" }, 122 122 123 { INFO::CONTINUE, L"Continue (not an error)" },124 123 { INFO::SKIPPED, L"Skipped (not an error)" }, 125 124 { INFO::CANNOT_HANDLE, L"Cannot handle (not an error)" }, -
ps/trunk/source/lib/status.h
r9545 r9871 311 311 while(0) 312 312 313 // if expression (typically the invocation of a callback) evaluates to: 314 // - INFO::OK, do nothing; 315 // - INFO::ALL_COMPLETE, return INFO::OK; 316 // - anything else, return that. 317 #define RETURN_STATUS_FROM_CALLBACK(expression)\ 318 do\ 319 {\ 320 const Status status_ = (expression);\ 321 if(status_ == INFO::ALL_COMPLETE)\ 322 return INFO::OK;\ 323 else if(status_ != INFO::OK)\ 324 return status_;\ 325 }\ 326 while(0) 327 313 328 // return 0 if expression is negative. use in functions that return pointers. 314 329 #define RETURN_0_IF_ERR(expression)\ … … 318 333 if(status_ < 0)\ 319 334 return 0;\ 320 }\321 while(0)322 323 // return expression if it evaluates to something other than324 // INFO::CONTINUE. use when invoking callbacks.325 #define RETURN_IF_NOT_CONTINUE(expression)\326 do\327 {\328 const Status status_ = (expression);\329 if(status_ != INFO::CONTINUE)\330 return status_;\331 335 }\ 332 336 while(0) … … 363 367 // note: these values are > 100 to allow multiplexing them with 364 368 // coroutine return values, which return completion percentage. 365 366 // the function (usually a callback) would like to be called again.367 const Status CONTINUE = +100000;368 369 369 370 // notify caller that nothing was done. -
ps/trunk/source/lib/sysdep/arch/ia32/ia32.cpp
r9410 r9871 30 30 #include "lib/sysdep/arch/ia32/ia32.h" 31 31 #include "lib/sysdep/arch/ia32/ia32_asm.h" 32 33 34 static const size_t maxInstructionLength = 15; // IA-32 limitation35 36 static bool IsCall(void* ret_addr, void*& target)37 {38 target = 0; // (not always possible to determine)39 40 // points to end of the CALL instruction (which is of unknown length)41 const u8* c = (const u8*)ret_addr;42 // this would allow for avoiding exceptions when accessing ret_addr43 // close to the beginning of the code segment. it's not currently set44 // because this is really unlikely and not worth the trouble.45 const size_t len = maxInstructionLength;46 47 // (most frequent cases first to reduce stack walk overhead:)48 49 // CALL rel32 (E8 cd)50 if(len >= 5 && c[-5] == 0xE8)51 {52 i32 offset;53 memcpy(&offset, c-sizeof(offset), sizeof(offset));54 target = (void*)(intptr_t(ret_addr) + intptr_t(offset));55 return true;56 }57 58 // CALL r32 => FF D0-D759 if(len >= 2 && c[-2] == 0xFF && (c[-1] & 0xF8) == 0xD0)60 return true;61 62 // CALL [r32 + disp8] => FF 50-57(!54) disp863 if(len >= 3 && c[-3] == 0xFF && (c[-2] & 0xF8) == 0x50 && c[-2] != 0x54)64 return true;65 66 // CALL [disp32] => FF 15 disp3267 if(len >= 6 && c[-6] == 0xFF && c[-5] == 0x15)68 {69 void** addr_of_target;70 memcpy(&addr_of_target, c-sizeof(addr_of_target), sizeof(addr_of_target));71 target = *addr_of_target;72 // there are no meaningful checks we can perform: we're called from73 // the stack trace code, so ring0 addresses may be legit.74 // even if the pointer is 0, it's better to pass its value on75 // (may help in tracking down memory corruption)76 return true;77 }78 79 // CALL [r32 + r32*s] => FF 14 SIB80 if(len >= 3 && c[-3] == 0xFF && c[-2] == 0x14)81 return true;82 // CALL [r32] => FF 00-3F(!14/15)83 if(len >= 2 && c[-2] == 0xFF && c[-1] < 0x40 && c[-1] != 0x14 && c[-1] != 0x15)84 return true;85 // CALL [r32 + r32*s + disp8] => FF 54 SIB disp886 if(len >= 4 && c[-4] == 0xFF && c[-3] == 0x54)87 return true;88 // CALL [r32 + r32*s + disp32] => FF 94 SIB disp3289 if(len >= 7 && c[-7] == 0xFF && c[-6] == 0x94)90 return true;91 // CALL [r32 + disp32] => FF 90-97(!94) disp3292 if(len >= 6 && c[-6] == 0xFF && (c[-5] & 0xF8) == 0x90 && c[-5] != 0x94)93 return true;94 95 return false;96 }97 98 Status ia32_GetCallTarget(void* ret_addr, void*& target)99 {100 if(IsCall(ret_addr, target))101 {102 // follow the incremental linker's jump tables103 const u8* c = (const u8*)target;104 if(c && c[0] == 0xE9)105 {106 i32 offset;107 memcpy(&offset, c+1, sizeof(offset));108 target = (void*)(intptr_t(c)+5 + intptr_t(offset));109 }110 111 return INFO::OK;112 }113 114 const u8* const instructionWindow = (const u8*)ret_addr - maxInstructionLength;115 if(memchr(instructionWindow, 0xCC, maxInstructionLength))116 return ERR::AGAIN; // NOWARN (debugger has inserted a breakpoint)117 118 // this shouldn't normally be reached but might happen if the119 // call stack is corrupted. note that we mustn't warn, because120 // this routine is already called from the stack dump chain.121 debug_break();122 return ERR::CPU_UNKNOWN_OPCODE; // NOWARN (see above)123 }124 125 126 void cpu_ConfigureFloatingPoint()127 {128 // no longer set 24 bit (float) precision by default: for129 // very long game uptimes (> 1 day; e.g. dedicated server),130 // we need full precision when calculating the time.131 // if there's a spot where we want to speed up divides|sqrts,132 // we can temporarily change precision there.133 //ia32_asm_control87(IA32_PC_24, IA32_MCW_PC);134 135 // to help catch bugs, enable as many floating-point exceptions as136 // possible. unfortunately SpiderMonkey triggers all of them.137 // note: passing a flag *disables* that exception.138 ia32_asm_control87(IA32_EM_ZERODIVIDE|IA32_EM_INVALID|IA32_EM_DENORMAL|IA32_EM_OVERFLOW|IA32_EM_UNDERFLOW|IA32_EM_INEXACT, IA32_MCW_EM);139 140 // no longer round toward zero (truncate). changing this setting141 // resulted in much faster float->int casts, because the compiler142 // could be told (via /QIfist) to use FISTP while still truncating143 // the result as required by ANSI C. however, FPU calculation144 // results were changed significantly, so it had to be disabled.145 //ia32_asm_control87(IA32_RC_CHOP, IA32_MCW_RC);146 }147 148 32 149 33 #if MSC_VERSION -
ps/trunk/source/lib/sysdep/arch/ia32/ia32_asm.asm
r8985 r9871 20 20 ; SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 21 22 ; optimized assembly code for IA-32. not provided as23 ; inline assembly because that's compiler-specific.22 ; assembly code for IA-32 (avoid inline assembly due to differing 23 ; compiler support and/or syntax). 24 24 25 25 %include "ia32.inc" 26 27 ; note: pure asm functions prevent inlining but also avoid redundant28 ; store/loads generated by VC inline asm (ugh).29 30 26 31 27 ;------------------------------------------------------------------------------- … … 55 51 pop ebx 56 52 ret 57 58 59 ;-------------------------------------------------------------------------------60 ; FPU61 ;-------------------------------------------------------------------------------62 63 ; extern "C" u32 __cdecl ia32_asm_control87(u32 new_cw, u32 mask);64 global sym(ia32_asm_control87)65 sym(ia32_asm_control87):66 push eax67 fnstcw [esp]68 pop eax ; old_cw69 mov ecx, [esp+4] ; new_val70 mov edx, [esp+8] ; mask71 and ecx, edx ; new_val & mask72 not edx ; ~mask73 and eax, edx ; old_cw & ~mask74 or eax, ecx ; (old_cw & ~mask) | (new_val & mask)75 push eax ; = new_cw76 fldcw [esp]77 pop eax78 xor eax, eax ; return value79 ret80 81 82 ; possible IA-32 FPU control word flags after FXAM: NAN|NORMAL|ZERO83 FP_CLASSIFY_MASK equ 0x450084 85 ; extern "C" size_t __cdecl ia32_asm_fpclassifyd(double d);86 global sym(ia32_asm_fpclassifyd)87 sym(ia32_asm_fpclassifyd):88 fld qword [esp+4]89 fxam90 fnstsw ax91 fstp st092 and eax, FP_CLASSIFY_MASK93 ret94 95 ; extern "C" size_t __cdecl ia32_asm_fpclassifyf(float f);96 global sym(ia32_asm_fpclassifyf)97 sym(ia32_asm_fpclassifyf):98 fld dword [esp+4]99 fxam100 fnstsw ax101 fstp st0102 and eax, FP_CLASSIFY_MASK103 ret104 105 106 ;-------------------------------------------------------------------------------107 ; misc108 ;-------------------------------------------------------------------------------109 110 ; write the current execution state (e.g. all register values) into111 ; (Win32::CONTEXT*)pcontext (defined as void* to avoid dependency).112 ; optimized for size; this must be straight asm because ; extern "C"113 ; is compiler-specific and compiler-generated prolog code inserted before114 ; inline asm trashes EBP and ESP (unacceptable).115 ; extern "C" void ia32_asm_GetCurrentContext(void* pcontext);116 global sym(ia32_asm_GetCurrentContext)117 sym(ia32_asm_GetCurrentContext):118 pushad119 pushfd120 mov edi, [esp+4+32+4] ; pcontext121 122 ; ContextFlags123 mov eax, 0x10007 ; segs, int, control124 stosd125 126 ; DRx and FloatSave127 ; rationale: we can't access the debug registers from Ring3, and128 ; the FPU save area is irrelevant, so zero them.129 xor eax, eax130 push byte 6+8+20131 pop ecx132 rep stosd133 134 ; CONTEXT_SEGMENTS135 mov ax, gs136 stosd137 mov ax, fs138 stosd139 mov ax, es140 stosd141 mov ax, ds142 stosd143 144 ; CONTEXT_INTEGER145 mov eax, [esp+4+32-32] ; edi146 stosd147 xchg eax, esi148 stosd149 xchg eax, ebx150 stosd151 xchg eax, edx152 stosd153 mov eax, [esp+4+32-8] ; ecx154 stosd155 mov eax, [esp+4+32-4] ; eax156 stosd157 158 ; CONTEXT_CONTROL159 xchg eax, ebp ; ebp restored by POPAD160 stosd161 mov eax, [esp+4+32] ; return address162 sub eax, 5 ; skip CALL instruction -> call site.163 stosd164 xor eax, eax165 mov ax, cs166 stosd167 pop eax ; eflags168 stosd169 lea eax, [esp+32+4+4] ; esp170 stosd171 xor eax, eax172 mov ax, ss173 stosd174 175 ; ExtendedRegisters176 xor ecx, ecx177 mov cl, 512/4178 rep stosd179 180 popad181 ret -
ps/trunk/source/lib/sysdep/arch/ia32/ia32_asm.h
r9361 r9871 31 31 EXTERN_C void CALL_CONV ia32_asm_cpuid(x86_x64_CpuidRegs* regs); 32 32 33 /// control8734 // FPU control word35 // .. Precision Control:36 const u32 IA32_MCW_PC = 0x0300;37 const u32 IA32_PC_24 = 0x0000;38 // .. Rounding Control:39 const u32 IA32_MCW_RC = 0x0C00;40 const u32 IA32_RC_NEAR = 0x0000;41 const u32 IA32_RC_DOWN = 0x0400;42 const u32 IA32_RC_UP = 0x0800;43 const u32 IA32_RC_CHOP = 0x0C00;44 // .. Exception Mask:45 const u32 IA32_MCW_EM = 0x3F;46 const u32 IA32_EM_INVALID = 0x01;47 const u32 IA32_EM_DENORMAL = 0x02;48 const u32 IA32_EM_ZERODIVIDE = 0x04;49 const u32 IA32_EM_OVERFLOW = 0x08;50 const u32 IA32_EM_UNDERFLOW = 0x10;51 const u32 IA32_EM_INEXACT = 0x20;52 /**53 * for all 1-bits in mask, update the corresponding FPU control word bits54 * with the bit values in new_val.55 * @return 0 to indicate success.56 **/57 EXTERN_C u32 CALL_CONV ia32_asm_control87(u32 new_val, u32 mask);58 59 /// POSIX fpclassify60 #define IA32_FP_NAN 0x010061 #define IA32_FP_NORMAL 0x040062 #define IA32_FP_INFINITE (IA32_FP_NAN | IA32_FP_NORMAL)63 #define IA32_FP_ZERO 0x400064 #define IA32_FP_SUBNORMAL (IA32_FP_NORMAL | IA32_FP_ZERO)65 EXTERN_C size_t CALL_CONV ia32_asm_fpclassifyd(double d);66 EXTERN_C size_t CALL_CONV ia32_asm_fpclassifyf(float f);67 68 /**69 * write the current execution state (e.g. all register values) into70 * (Win32::CONTEXT*)pcontext (defined as void* to avoid dependency).71 **/72 EXTERN_C void CALL_CONV ia32_asm_GetCurrentContext(void* pcontext);73 74 33 #endif // #ifndef INCLUDED_IA32_ASM -
ps/trunk/source/lib/sysdep/arch/x86_x64/topology.cpp
r9870 r9871 29 29 30 30 #include <set> 31 #include <bitset>32 31 33 32 #include "lib/bits.h" … … 272 271 // generate fake but legitimate APIC IDs 273 272 for(size_t processor = 0; processor < cpuTopology.numProcessors; processor++) 274 cpuTopology.apicIds[processor] = cpuTopology.sortedApicIds[processor] = processor;273 cpuTopology.apicIds[processor] = cpuTopology.sortedApicIds[processor] = (ApicId)processor; 275 274 return INFO::OK; 276 275 } -
ps/trunk/source/lib/sysdep/arch/x86_x64/x86_x64.cpp
r9846 r9871 45 45 #endif 46 46 47 #define CPUID_INTRINSIC047 #define HAVE_CPUIDEX 0 48 48 #if defined(_MSC_FULL_VER) && _MSC_FULL_VER >= 150030729 // __cpuidex available on VC10+ and VC9 SP1 (allows setting ecx beforehand) 49 # undef CPUID_INTRINSIC50 # define CPUID_INTRINSIC149 # undef HAVE_CPUIDEX 50 # define HAVE_CPUIDEX 1 51 51 #else 52 52 # if ARCH_AMD64 … … 73 73 static void cpuid(x86_x64_CpuidRegs* regs) 74 74 { 75 #if CPUID_INTRINSIC75 #if HAVE_CPUIDEX 76 76 cassert(sizeof(regs->eax) == sizeof(int)); 77 77 cassert(sizeof(*regs) == 4*sizeof(int)); -
ps/trunk/source/lib/sysdep/cpu.h
r9410 r9871 103 103 } 104 104 105 106 //-----------------------------------------------------------------------------107 // misc108 109 /**110 * set the FPU control word to "desirable" values (see implementation)111 **/112 LIB_API void cpu_ConfigureFloatingPoint();113 114 // NB: cpu_i64FromDouble et al. were faster than _ftol2, but are obsolete115 // since VC8 and GCC (with -ffast-math) generate SSE instructions.116 117 105 #endif // #ifndef INCLUDED_CPU -
ps/trunk/source/lib/sysdep/os/win/tests/test_wdbg_sym.h
r9475 r9871 34 34 #include <stack> 35 35 36 #include "lib/bits.h" 36 37 #include "lib/sysdep/os/win/win.h" // HWND 37 38 #include "lib/sysdep/sysdep.h" … … 46 47 { 47 48 callers[numCallers++] = (void*)frame->AddrPC.Offset; 48 return INFO:: CONTINUE;49 return INFO::OK; 49 50 } 50 51 … … 57 58 __declspec(noinline) static void Func1() 58 59 { 59 wdbg_sym_WalkStack(OnFrame, 0, 0, L"wdbg_sym_WalkStack"); 60 CONTEXT context; 61 (void)debug_CaptureContext(&context); 62 wdbg_sym_WalkStack(OnFrame, 0, context); 60 63 } 61 64 … … 98 101 wchar_t many_wchars[1024]; memset(many_wchars, 'a', sizeof(many_wchars)); 99 102 100 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); 103 debug_printf(L"\n(dumping stack frames may result in access violations...)\n"); 104 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); 101 105 102 106 // note: we don't want any kind of dialog to be raised, because … … 104 108 // amount of text (not just "(failed)" error messages) was produced. 105 109 ErrorMessageMem emm = {0}; 106 const wchar_t* text = debug_BuildErrorMessage(L"dummy", 0,0,0, 0,L"debug_BuildErrorMessage", &emm); 110 CACHE_ALIGNED u8 context[DEBUG_CONTEXT_SIZE]; 111 (void)debug_CaptureContext(context); 112 const wchar_t* text = debug_BuildErrorMessage(L"dummy", 0,0,0, context, L"m_test_array", &emm); 107 113 TS_ASSERT(wcslen(text) > 500); 114 #if 0 108 115 { 109 116 std::wofstream s(L"d:\\out.txt"); 110 117 s << text; 111 118 } 119 #endif 112 120 debug_FreeErrorMessage(&emm); 113 121 … … 288 296 void test_stack_walk() 289 297 { 298 Status ret; 299 290 300 Func3(); 291 301 TS_ASSERT(numCallers >= 3); 302 size_t foundFunctionBits = 0; 292 303 void* funcAddresses[3] = { (void*)&Func1, (void*)&Func2, (void*)&Func3 }; 293 for(size_t i = 0; i < 3; i++) 294 { 295 wchar_t func1[DBG_SYMBOL_LEN], func2[DBG_SYMBOL_LEN]; 296 Status ret; 297 ret = debug_ResolveSymbol(callers[i], func1, 0, 0); 304 for(size_t idxCaller = 0; idxCaller < numCallers; idxCaller++) 305 { 306 wchar_t callerName[DEBUG_SYMBOL_CHARS]; 307 ret = debug_ResolveSymbol(callers[idxCaller], callerName, 0, 0); 298 308 TS_ASSERT_OK(ret); 299 ret = debug_ResolveSymbol(funcAddresses[i], func2, 0, 0); 300 TS_ASSERT_OK(ret); 301 TS_ASSERT_WSTR_CONTAINS(func2, func1); 302 } 309 310 wchar_t funcName[DEBUG_SYMBOL_CHARS]; 311 for(size_t idxFunc = 0; idxFunc < ARRAY_SIZE(funcAddresses); idxFunc++) 312 { 313 ret = debug_ResolveSymbol(funcAddresses[idxFunc], funcName, 0, 0); 314 TS_ASSERT_OK(ret); 315 if(wcsstr(funcName, callerName)) 316 foundFunctionBits |= BIT(idxFunc); 317 } 318 } 319 TS_ASSERT(foundFunctionBits == bit_mask<size_t>(ARRAY_SIZE(funcAddresses))); 303 320 } 304 321 }; -
ps/trunk/source/lib/sysdep/os/win/wdbg_heap.cpp
r9475 r9871 339 339 { 340 340 if(!m_isRecordingKnownCallers) 341 return INFO::SKIPPED; // do not affect the stack walk341 return INFO::SKIPPED; 342 342 343 343 // last 'known' function has been reached 344 344 if(pc == (uintptr_t)&CallerFilter::CallHeapFunctions) 345 return INFO:: OK; // stop stack walk345 return INFO::ALL_COMPLETE; 346 346 347 347 // pc is a 'known' function on the allocation hook's back-trace 348 348 // (e.g. _malloc_dbg and other helper functions) 349 349 m_knownCallers.Add(pc); 350 return INFO:: CONTINUE;350 return INFO::OK; 351 351 } 352 352 … … 676 676 { 677 677 m_numCallers = 0; 678 (void)wdbg_sym_WalkStack(OnFrame_Trampoline, (uintptr_t)this); 678 CONTEXT context; 679 (void)debug_CaptureContext(&context); 680 (void)wdbg_sym_WalkStack(OnFrame_Trampoline, (uintptr_t)this, context); 679 681 std::fill(m_callers+m_numCallers, m_callers+maxCallers, 0); 680 682 } … … 697 699 // skip invalid frames 698 700 if(pc == 0) 699 return INFO:: CONTINUE;701 return INFO::OK; 700 702 701 703 Status ret = m_filter.NotifyOfCaller(pc); … … 707 709 // stop the stack walk if frame storage is full 708 710 if(m_numCallers >= maxCallers) 709 return INFO:: OK;711 return INFO::ALL_COMPLETE; 710 712 711 713 if(!m_filter.IsKnownCaller(pc)) 712 714 m_callers[m_numCallers++] = pc; 713 return INFO:: CONTINUE;715 return INFO::OK; 714 716 } 715 717 … … 851 853 for(size_t i = 0; i < numCallers; i++) 852 854 { 853 wchar_t name[D BG_SYMBOL_LEN] = {'\0'}; wchar_t file[DBG_FILE_LEN] = {'\0'}; int line = -1;855 wchar_t name[DEBUG_SYMBOL_CHARS] = {'\0'}; wchar_t file[DEBUG_FILE_CHARS] = {'\0'}; int line = -1; 854 856 Status err = debug_ResolveSymbol((void*)callers[i], name, file, &line); 855 857 wdbg_printf(L" "); … … 945 947 946 948 // load symbol information now (fails if it happens during shutdown) 947 wchar_t name[D BG_SYMBOL_LEN]; wchar_t file[DBG_FILE_LEN]; int line;949 wchar_t name[DEBUG_SYMBOL_CHARS]; wchar_t file[DEBUG_FILE_CHARS]; int line; 948 950 (void)debug_ResolveSymbol(wdbg_heap_Init, name, file, &line); 949 951 -
ps/trunk/source/lib/sysdep/os/win/wdbg_sym.cpp
r9519 r9871 28 28 #include "lib/sysdep/os/win/wdbg_sym.h" 29 29 30 #include < stdlib.h>31 #include < stdio.h>30 #include <cstdlib> 31 #include <cstdio> 32 32 #include <set> 33 33 34 #include "lib/byte_order.h" 34 #include "lib/byte_order.h" // movzx_le64 35 35 #include "lib/module_init.h" 36 36 #include "lib/sysdep/cpu.h" 37 37 #include "lib/debug_stl.h" 38 38 #include "lib/app_hooks.h" 39 #if ARCH_IA3240 # include "lib/sysdep/arch/ia32/ia32.h"41 # include "lib/sysdep/arch/ia32/ia32_asm.h"42 #endif43 39 #include "lib/external_libraries/dbghelp.h" 44 40 #include "lib/sysdep/os/win/wdbg.h" 45 41 #include "lib/sysdep/os/win/wutil.h" 46 47 48 #if ARCH_IA32 && !CONFIG_OMIT_FP 49 # define IA32_STACK_WALK_ENABLED 1 50 #else 51 # define IA32_STACK_WALK_ENABLED 0 52 #endif 42 #include "lib/sysdep/os/win/winit.h" 43 44 WINIT_REGISTER_CRITICAL_INIT(wdbg_sym_Init); 45 46 static WUTIL_FUNC(pRtlCaptureContext, VOID, (PCONTEXT)); 47 48 static Status wdbg_sym_Init() 49 { 50 WUTIL_IMPORT_KERNEL32(RtlCaptureContext, pRtlCaptureContext); 51 return INFO::OK; 52 } 53 53 54 54 … … 57 57 //---------------------------------------------------------------------------- 58 58 59 // passed to all dbghelp symbol query functions. we're not interested in 60 // resolving symbols in other processes; the purpose here is only to 61 // generate a stack trace. if that changes, we need to init a local copy 62 // of these in dump_sym_cb and pass them to all subsequent dump_*. 59 // global for convenience (we only support a single process) 63 60 static HANDLE hProcess; 64 static uintptr_t mod_base; 65 66 #if !IA32_STACK_WALK_ENABLED 61 67 62 // for StackWalk64; taken from PE header by InitDbghelp. 68 63 static WORD machine; 69 #endif70 64 71 65 static Status InitDbghelp() … … 81 75 // - do not set directly - that would zero any existing flags. 82 76 DWORD opts = pSymGetOptions(); 77 //opts |= SYMOPT_DEBUG; // lots of debug spew in output window 83 78 opts |= SYMOPT_DEFERRED_LOADS; // the "fastest, most efficient way" 84 //opts |= SYMOPT_DEBUG; // lots of debug spew in output window79 opts |= SYMOPT_LOAD_LINES; 85 80 opts |= SYMOPT_UNDNAME; 86 81 pSymSetOptions(opts); … … 96 91 WARN_IF_FALSE(ok); 97 92 98 mod_base = (uintptr_t)pSymGetModuleBase64(hProcess, (u64)&InitDbghelp); 99 100 #if !IA32_STACK_WALK_ENABLED 101 IMAGE_NT_HEADERS* const header = pImageNtHeader((void*)(uintptr_t)mod_base); 93 HMODULE hModule = GetModuleHandle(0); 94 IMAGE_NT_HEADERS* const header = pImageNtHeader(hModule); 102 95 machine = header->FileHeader.Machine; 103 #endif104 96 105 97 return INFO::OK; … … 110 102 // (on-demand initialization allows handling exceptions raised before 111 103 // winit.cpp init functions are called) 104 // 105 // NB: this may take SECONDS if OS symbols are installed and 106 // symserv wants to access the internet. 112 107 static void sym_init() 113 108 { 114 109 static ModuleInitState initState; 115 110 ModuleInit(&initState, InitDbghelp); 111 } 112 113 114 static STACKFRAME64 PopulateStackFrame(CONTEXT& context) 115 { 116 STACKFRAME64 sf; 117 memset(&sf, 0, sizeof(sf)); 118 sf.AddrPC.Mode = AddrModeFlat; 119 sf.AddrFrame.Mode = AddrModeFlat; 120 sf.AddrStack.Mode = AddrModeFlat; 121 #if ARCH_AMD64 122 sf.AddrPC.Offset = context.Rip; 123 sf.AddrFrame.Offset = context.Rbp; 124 sf.AddrStack.Offset = context.Rsp; 125 #else 126 sf.AddrPC.Offset = context.Eip; 127 sf.AddrFrame.Offset = context.Ebp; 128 sf.AddrStack.Offset = context.Esp; 129 #endif 130 return sf; 131 } 132 133 134 static IMAGEHLP_STACK_FRAME PopulateImageStackFrame(const STACKFRAME64& sf) 135 { 136 IMAGEHLP_STACK_FRAME isf; 137 memset(&isf, 0, sizeof(isf)); 138 // apparently only PC, FP and SP are necessary, but 139 // we copy everything to be safe. 140 isf.InstructionOffset = sf.AddrPC.Offset; 141 isf.ReturnOffset = sf.AddrReturn.Offset; 142 isf.FrameOffset = sf.AddrFrame.Offset; 143 isf.StackOffset = sf.AddrStack.Offset; 144 isf.BackingStoreOffset = sf.AddrBStore.Offset; 145 isf.FuncTableEntry = (ULONG64)sf.FuncTableEntry; 146 // (note: array of different types, can't copy directly) 147 for(int i = 0; i < 4; i++) 148 isf.Params[i] = sf.Params[i]; 149 // isf.Reserved - already zeroed 150 isf.Virtual = sf.Virtual; 151 // isf.Reserved2 - already zeroed 152 return isf; 116 153 } 117 154 … … 132 169 struct TI_FINDCHILDREN_PARAMS2 133 170 { 134 TI_FINDCHILDREN_PARAMS2(DWORD num _children)171 TI_FINDCHILDREN_PARAMS2(DWORD numChildren) 135 172 { 136 173 p.Start = 0; 137 p.Count = std::min(num _children, MAX_CHILDREN);138 } 139 140 static const DWORD MAX_CHILDREN= 300;174 p.Count = std::min(numChildren, maxChildren); 175 } 176 177 static const DWORD maxChildren = 300; 141 178 TI_FINDCHILDREN_PARAMS p; 142 DWORD additional_children[MAX_CHILDREN-1];179 DWORD childrenStorage[maxChildren-1]; 143 180 }; 144 181 … … 153 190 154 191 const DWORD64 addr = (DWORD64)ptr_of_interest; 155 int successes = 0;192 size_t successes = 0; 156 193 157 194 WinScopedPreserveLastError s; // SymFromAddrW, SymGetLineFromAddrW64 … … 166 203 if(pSymFromAddrW(hProcess, addr, 0, sym)) 167 204 { 168 wcscpy_s(sym_name, D BG_SYMBOL_LEN, sym->Name);205 wcscpy_s(sym_name, DEBUG_SYMBOL_CHARS, sym->Name); 169 206 successes++; 170 207 } … … 188 225 // call site (full path is too long to display nicely). 189 226 const wchar_t* basename = path_name_only(line_info.FileName); 190 wcscpy_s(file, D BG_FILE_LEN, basename);227 wcscpy_s(file, DEBUG_FILE_CHARS, basename); 191 228 successes++; 192 229 } … … 208 245 } 209 246 210 // read and return symbol information for the given address. all of the211 // output parameters are optional; we pass back as much information as is212 // available and desired. return 0 iff any information was successfully213 // retrieved and stored.214 // sym_name and file must hold at least the number of chars above;215 247 // file is the base name only, not path (see rationale in wdbg_sym). 216 248 // the PDB implementation is rather slow (~500µs). … … 226 258 //---------------------------------------------------------------------------- 227 259 228 /* 229 Subroutine linkage example code: 230 231 push param2 232 push param1 233 call func 234 ret_addr: 235 [..] 236 237 func: 238 push ebp 239 mov ebp, esp 240 sub esp, local_size 241 [..] 242 243 Stack contents (down = decreasing address) 244 [param2] 245 [param1] 246 ret_addr 247 prev_ebp (<- current ebp points at this value) 248 [local_variables] 249 */ 250 251 252 /* 253 call func1 254 ret1: 255 256 func1: 257 push ebp 258 mov ebp, esp 259 call func2 260 ret2: 261 262 func2: 263 push ebp 264 mov ebp, esp 265 STARTHERE 266 267 */ 268 269 #if IA32_STACK_WALK_ENABLED 270 271 static Status ia32_walk_stack(_tagSTACKFRAME64* sf) 272 { 273 // read previous values from _tagSTACKFRAME64 274 void* prev_fp = (void*)(uintptr_t)sf->AddrFrame .Offset; 275 void* prev_ip = (void*)(uintptr_t)sf->AddrPC .Offset; 276 void* prev_ret = (void*)(uintptr_t)sf->AddrReturn.Offset; 277 if(!debug_IsStackPointer(prev_fp)) 278 WARN_RETURN(ERR::_11); 279 if(prev_ip && !debug_IsCodePointer(prev_ip)) 280 WARN_RETURN(ERR::_12); 281 if(prev_ret && !debug_IsCodePointer(prev_ret)) 282 WARN_RETURN(ERR::_13); 283 284 // read stack frame 285 void* fp = ((void**)prev_fp)[0]; 286 void* ret_addr = ((void**)prev_fp)[1]; 287 if(!fp) 288 return INFO::ALL_COMPLETE; 289 if(!debug_IsStackPointer(fp)) 290 WARN_RETURN(ERR::_14); 291 if(!debug_IsCodePointer(ret_addr)) 292 return ERR::FAIL; // NOWARN (invalid address) 293 294 void* target; 295 Status err = ia32_GetCallTarget(ret_addr, target); 296 RETURN_STATUS_IF_ERR(err); 297 if(target) // were able to determine it from the call instruction 298 { 299 if(!debug_IsCodePointer(target)) 300 return ERR::FAIL; // NOWARN (invalid address) 301 } 302 303 sf->AddrFrame .Offset = DWORD64(fp); 304 sf->AddrStack .Offset = DWORD64(prev_fp)+8; // +8 reverts effects of call + push ebp 305 sf->AddrPC .Offset = DWORD64(target); 306 sf->AddrReturn.Offset = DWORD64(ret_addr); 307 260 Status debug_CaptureContext(void* pcontext) 261 { 262 // there are 4 ways to do so, in order of preference: 263 // - RtlCaptureContext (only available on WinXP or above) 264 // - assembly language subroutine (complicates the build system) 265 // - intentionally raise an SEH exception and capture its context 266 // (causes annoying "first chance exception" messages and 267 // can't co-exist with WinScopedLock's destructor) 268 // - GetThreadContext while suspended (a bit tricky + slow). 269 // note: it used to be common practice to query the current thread 270 // context, but WinXP SP2 and above require it be suspended. 271 272 if(!pRtlCaptureContext) 273 return ERR::NOT_SUPPORTED; // NOWARN 274 275 CONTEXT* context = (CONTEXT*)pcontext; 276 cassert(sizeof(CONTEXT) <= DEBUG_CONTEXT_SIZE); 277 memset(context, 0, sizeof(CONTEXT)); 278 context->ContextFlags = CONTEXT_FULL; 279 pRtlCaptureContext(context); 308 280 return INFO::OK; 309 281 } 310 282 283 284 static Status CallStackWalk(STACKFRAME64& sf, CONTEXT& context) 285 { 286 WinScopedLock lock(WDBG_SYM_CS); 287 288 SetLastError(0); // StackWalk64 doesn't always SetLastError 289 const HANDLE hThread = GetCurrentThread(); 290 if(!pStackWalk64(machine, hProcess, hThread, &sf, &context, 0, pSymFunctionTableAccess64, pSymGetModuleBase64, 0)) 291 return ERR::FAIL; // NOWARN (no stack frames left) 292 293 // (the frame pointer can be zero despite StackWalk64 returning TRUE.) 294 if(sf.AddrFrame.Offset == 0) 295 return ERR::FAIL; // NOWARN (no stack frames left) 296 297 // huge WTF in x64 debug builds (dbghelp 6.12.0002.633): 298 // AddrFrame.Offset doesn't match the correct RBP value. 299 // StackWalk64 updates the context [http://bit.ly/lo1aqZ] and 300 // its Rbp is correct, so we'll use that. 301 #if ARCH_AMD64 302 sf.AddrFrame.Offset = context.Rbp; 311 303 #endif 312 304 313 314 // note: RtlCaptureStackBackTrace (http://msinilo.pl/blog/?p=40) 315 // is likely to be much faster than StackWalk64 (especially relevant 316 // for debug_GetCaller), but wasn't known during development and 317 // remains undocumented. 318 319 Status wdbg_sym_WalkStack(StackFrameCallback cb, uintptr_t cbData, const CONTEXT* pcontext, const wchar_t* lastFuncToSkip) 320 { 321 // to function properly, StackWalk64 requires a CONTEXT on 322 // non-x86 systems (documented) or when in release mode (observed). 323 // exception handlers can call wdbg_sym_WalkStack with their context record; 324 // otherwise (e.g. dump_stack from ENSURE), we need to query it. 325 CONTEXT context; 326 // .. caller knows the context (most likely from an exception); 327 // since StackWalk64 may modify it, copy to a local variable. 328 if(pcontext) 329 context = *pcontext; 330 // .. need to determine context ourselves. 331 else 332 { 333 // there are 4 ways to do so, in order of preference: 334 // - asm (easy to use but currently only implemented on IA32) 335 // - RtlCaptureContext (only available on WinXP or above) 336 // - intentionally raise an SEH exception and capture its context 337 // (causes annoying "first chance exception" messages and 338 // can't co-exist with WinScopedLock's destructor) 339 // - GetThreadContext while suspended (a bit tricky + slow). 340 // note: it used to be common practice to query the current thread 341 // context, but WinXP SP2 and above require it be suspended. 342 // 343 // this MUST be done inline and not in an external function because 344 // compiler-generated prolog code trashes some registers. 345 346 #if ARCH_IA32 347 ia32_asm_GetCurrentContext(&context); 348 #else 349 // we need to capture the context ASAP, lest more registers be 350 // clobbered. since sym_init is no longer called from winit, the 351 // best we can do is import the function pointer directly. 352 static WUTIL_FUNC(pRtlCaptureContext, VOID, (PCONTEXT)); 353 if(!pRtlCaptureContext) 354 { 355 WUTIL_IMPORT_KERNEL32(RtlCaptureContext, pRtlCaptureContext); 356 if(!pRtlCaptureContext) 357 return ERR::NOT_SUPPORTED; // NOWARN 358 } 359 memset(&context, 0, sizeof(context)); 360 context.ContextFlags = CONTEXT_CONTROL|CONTEXT_INTEGER; 361 pRtlCaptureContext(&context); 362 #endif 363 } 364 pcontext = &context; 365 366 _tagSTACKFRAME64 sf; 367 memset(&sf, 0, sizeof(sf)); 368 sf.AddrPC.Mode = AddrModeFlat; 369 sf.AddrFrame.Mode = AddrModeFlat; 370 sf.AddrStack.Mode = AddrModeFlat; 371 #if ARCH_AMD64 372 sf.AddrPC.Offset = pcontext->Rip; 373 sf.AddrFrame.Offset = pcontext->Rbp; 374 sf.AddrStack.Offset = pcontext->Rsp; 375 #else 376 sf.AddrPC.Offset = pcontext->Eip; 377 sf.AddrFrame.Offset = pcontext->Ebp; 378 sf.AddrStack.Offset = pcontext->Esp; 379 #endif 380 381 #if !IA32_STACK_WALK_ENABLED 305 return INFO::OK; 306 } 307 308 309 // NB: CaptureStackBackTrace may be faster (http://msinilo.pl/blog/?p=40), 310 // but wasn't known during development. 311 Status wdbg_sym_WalkStack(StackFrameCallback cb, uintptr_t cbData, CONTEXT& context, const wchar_t* lastFuncToSkip) 312 { 382 313 sym_init(); 383 #endif 384 385 // for each stack frame found: 386 Status ret = ERR::SYM_NO_STACK_FRAMES_FOUND; 387 for(;;) 388 { 389 // rationale: 390 // - provide a separate ia32 implementation so that simple 391 // stack walks (e.g. to determine callers of malloc) do not 392 // require firing up dbghelp. that takes tens of seconds when 393 // OS symbols are installed (because symserv is wanting to access 394 // the internet), which is entirely unacceptable. 395 // - VC7.1 sometimes generates stack frames despite /Oy ; 396 // ia32_walk_stack may appear to work, but it isn't reliable in 397 // this case and therefore must not be used! 398 // - don't switch between ia32_stack_walk and StackWalk64 when one 399 // of them fails: this needlessly complicates things. the ia32 400 // code is authoritative provided its prerequisite (FP not omitted) 401 // is met, otherwise totally unusable. 402 Status err; 403 #if IA32_STACK_WALK_ENABLED 404 err = ia32_walk_stack(&sf); 405 #else 406 { 407 WinScopedLock lock(WDBG_SYM_CS); 408 409 // note: unfortunately StackWalk64 doesn't always SetLastError, 410 // so we have to reset it and check for 0. *sigh* 411 SetLastError(0); 412 const HANDLE hThread = GetCurrentThread(); 413 const BOOL ok = pStackWalk64(machine, hProcess, hThread, &sf, (PVOID)pcontext, 0, pSymFunctionTableAccess64, pSymGetModuleBase64, 0); 414 err = ok? INFO::OK : ERR::FAIL; // NOWARN (no stack frames are left) 415 } 416 #endif 417 418 // no more frames found - abort. note: also test FP because 419 // StackWalk64 sometimes erroneously reports success. 420 void* const fp = (void*)(uintptr_t)sf.AddrFrame.Offset; 421 if(err != INFO::OK || !fp) 314 315 STACKFRAME64 sf = PopulateStackFrame(context); 316 317 wchar_t func[DEBUG_SYMBOL_CHARS]; 318 319 Status ret = ERR::SYM_NO_STACK_FRAMES_FOUND; 320 for(;;) // each stack frame: 321 { 322 if(CallStackWalk(sf, context) != INFO::OK) 422 323 return ret; 423 324 … … 425 326 { 426 327 void* const pc = (void*)(uintptr_t)sf.AddrPC.Offset; 427 wchar_t func[DBG_SYMBOL_LEN]; 428 err = debug_ResolveSymbol(pc, func, 0, 0); 429 if(err == INFO::OK) 328 if(debug_ResolveSymbol(pc, func, 0, 0) == INFO::OK) 430 329 { 431 if(wcsstr(func, lastFuncToSkip)) 330 if(wcsstr(func, lastFuncToSkip)) // this was the last one to skip 432 331 lastFuncToSkip = 0; 433 332 continue; … … 436 335 437 336 ret = cb(&sf, cbData); 438 // callback is allowing us to continue 439 if(ret == INFO::CONTINUE) 440 ret = INFO::OK; 441 // callback reports it's done; stop calling it and return that value. 442 // (can be either success or failure) 443 else 444 { 445 ENSURE(ret <= 0); // shouldn't return > 0 446 return ret; 447 } 448 } 449 } 450 451 452 // 453 // get address of Nth function above us on the call stack (uses wdbg_sym_WalkStack) 454 // 455 456 // called by wdbg_sym_WalkStack for each stack frame 457 static Status nth_caller_cb(const _tagSTACKFRAME64* sf, uintptr_t cbData) 458 { 459 void** pfunc = (void**)cbData; 460 461 // return its address 462 *pfunc = (void*)(uintptr_t)sf->AddrPC.Offset; 463 return INFO::OK; 464 } 337 RETURN_STATUS_FROM_CALLBACK(ret); 338 } 339 } 340 465 341 466 342 void* debug_GetCaller(void* pcontext, const wchar_t* lastFuncToSkip) 467 343 { 344 struct StoreAddress 345 { 346 static Status Func(const STACKFRAME64* sf, uintptr_t cbData) 347 { 348 const uintptr_t funcAddress = sf->AddrPC.Offset; 349 350 // store funcAddress in our `output parameter' 351 memcpy((void*)cbData, &funcAddress, sizeof(funcAddress)); 352 353 return INFO::OK; 354 } 355 }; 468 356 void* func; 469 Status ret = wdbg_sym_WalkStack(nth_caller_cb, (uintptr_t)&func, (const CONTEXT*)pcontext, lastFuncToSkip); 357 wdbg_assert(pcontext != 0); 358 Status ret = wdbg_sym_WalkStack(&StoreAddress::Func, (uintptr_t)&func, *(CONTEXT*)pcontext, lastFuncToSkip); 470 359 return (ret == INFO::OK)? func : 0; 471 360 } 472 361 473 362 474 475 363 //----------------------------------------------------------------------------- 476 364 // helper routines for symbol value dump … … 478 366 479 367 // infinite recursion has never happened, but we check for it anyway. 480 static const size_t MAX_INDIRECTION= 255;481 static const size_t MAX_LEVEL= 255;368 static const size_t maxIndirection = 255; 369 static const size_t maxLevel = 255; 482 370 483 371 struct DumpState … … 485 373 size_t level; 486 374 size_t indirection; 487 488 DumpState() 489 { 490 level = 0; 491 indirection = 0; 375 uintptr_t moduleBase; 376 LPSTACKFRAME64 stackFrame; 377 378 DumpState(uintptr_t moduleBase, LPSTACKFRAME64 stackFrame) 379 : level(0), indirection(0), moduleBase(moduleBase), stackFrame(stackFrame) 380 { 492 381 } 493 382 }; 383 494 384 495 385 //---------------------------------------------------------------------------- … … 653 543 654 544 // forward decl; called by dump_sequence and some of dump_sym_*. 655 static Status dump_sym(DWORD id, const u8* p, DumpState state);545 static Status dump_sym(DWORD id, const u8* p, DumpState& state); 656 546 657 547 // from cvconst.h 658 548 // 659 549 // rationale: we don't provide a get_register routine, since only the 660 // value of FP is known to dump_frame_cb (via _tagSTACKFRAME64).550 // value of FP is known to dump_frame_cb (via STACKFRAME64). 661 551 // displaying variables stored in registers is out of the question; 662 552 // all we can do is display FP-relative variables. … … 664 554 { 665 555 CV_REG_EBP = 22, 666 CV_AMD64_R SP = 335556 CV_AMD64_RBP = 334 667 557 }; 668 558 … … 700 590 701 591 702 // splitout of dump_sequence.592 // moved out of dump_sequence. 703 593 static Status dump_string(const u8* p, size_t el_size) 704 594 { … … 734 624 735 625 736 // splitout of dump_sequence.626 // moved out of dump_sequence. 737 627 static void seq_determine_formatting(size_t el_size, size_t el_count, bool* fits_on_one_line, size_t* num_elements_to_show) 738 628 { … … 760 650 761 651 762 static Status dump_sequence(DebugStlIterator el_iterator, void* internal, size_t el_count, DWORD el_type_id, size_t el_size, DumpState state)652 static Status dump_sequence(DebugStlIterator el_iterator, void* internal, size_t el_count, DWORD el_type_id, size_t el_size, DumpState& state) 763 653 { 764 654 const u8* el_p = 0; // avoid "uninitialized" warning … … 832 722 833 723 834 static Status dump_array(const u8* p, size_t el_count, DWORD el_type_id, size_t el_size, DumpState state)724 static Status dump_array(const u8* p, size_t el_count, DWORD el_type_id, size_t el_size, DumpState& state) 835 725 { 836 726 const u8* iterator_internal_pos = p; … … 839 729 } 840 730 841 842 static const _tagSTACKFRAME64* current_stackframe64;843 731 844 732 static Status CanHandleDataKind(DWORD dataKind) … … 880 768 if((flags & SYMFLAG_REGREL) == 0) 881 769 return false; 882 if(reg == CV_REG_EBP || reg == CV_AMD64_R SP)770 if(reg == CV_REG_EBP || reg == CV_AMD64_RBP) 883 771 return true; 884 772 return false; … … 903 791 } 904 792 905 static Status DetermineSymbolAddress(DWORD id, const SYMBOL_INFOW* sym, const u8** pp) 906 { 907 const _tagSTACKFRAME64* sf = current_stackframe64; 908 793 static Status DetermineSymbolAddress(DWORD id, const SYMBOL_INFOW* sym, const DumpState& state, const u8** pp) 794 { 909 795 DWORD dataKind; 910 if(!pSymGetTypeInfo(hProcess, mod_base, id, TI_GET_DATAKIND, &dataKind))796 if(!pSymGetTypeInfo(hProcess, state.moduleBase, id, TI_GET_DATAKIND, &dataKind)) 911 797 WARN_RETURN(ERR::SYM_TYPE_INFO_UNAVAILABLE); 912 798 Status ret = CanHandleDataKind(dataKind); … … 921 807 uintptr_t addr = (uintptr_t)sym->Address; 922 808 if(IsRelativeToFramePointer(sym->Flags, sym->Register)) 923 { 924 #if ARCH_AMD64 925 addr += (uintptr_t)sf->AddrStack.Offset; 926 #else 927 addr += (uintptr_t)sf->AddrFrame.Offset; 928 # if defined(NDEBUG) 929 // NB: the addresses of register-relative symbols are apparently 930 // incorrect [VC8, 32-bit Wow64]. the problem occurs regardless of 931 // IA32_STACK_WALK_ENABLED and with both ia32_asm_GetCurrentContext 932 // and RtlCaptureContext. the EBP, ESP and EIP values returned by 933 // ia32_asm_GetCurrentContext match those reported by the IDE, so 934 // the problem appears to lie in the offset values stored in the PDB. 935 if(sym->Flags & SYMFLAG_PARAMETER) 936 addr += sizeof(void*); 937 else 938 addr += sizeof(void*) * 2; 939 # endif 940 #endif 941 } 809 addr += (uintptr_t)state.stackFrame->AddrFrame.Offset; 942 810 else if(IsUnretrievable(sym->Flags)) 943 811 return ERR::SYM_UNRETRIEVABLE; // NOWARN … … 945 813 *pp = (const u8*)(uintptr_t)addr; 946 814 947 debug_printf(L"SYM| %ls at %p flags=%X dk=%d sym->addr=%I64X fp=%I64x\n", sym->Name, *pp, sym->Flags, dataKind, sym->Address, s f->AddrFrame.Offset);815 debug_printf(L"SYM| %ls at %p flags=%X dk=%d sym->addr=%I64X fp=%I64x\n", sym->Name, *pp, sym->Flags, dataKind, sym->Address, state.stackFrame->AddrFrame.Offset); 948 816 return INFO::OK; 949 817 } … … 959 827 // called by dump_sym; lock is held. 960 828 961 static Status dump_sym_array(DWORD type_id, const u8* p, DumpState state)829 static Status dump_sym_array(DWORD type_id, const u8* p, DumpState& state) 962 830 { 963 ULONG64 size _= 0;964 if(!pSymGetTypeInfo(hProcess, mod_base, type_id, TI_GET_LENGTH, &size_))965 WARN_RETURN(ERR::SYM_TYPE_INFO_UNAVAILABLE); 966 const size_t size = (size_t)size _;831 ULONG64 size64 = 0; 832 if(!pSymGetTypeInfo(hProcess, state.moduleBase, type_id, TI_GET_LENGTH, &size64)) 833 WARN_RETURN(ERR::SYM_TYPE_INFO_UNAVAILABLE); 834 const size_t size = (size_t)size64; 967 835 968 836 // get element count and size 969 837 DWORD el_type_id = 0; 970 if(!pSymGetTypeInfo(hProcess, mod_base, type_id, TI_GET_TYPEID, &el_type_id))838 if(!pSymGetTypeInfo(hProcess, state.moduleBase, type_id, TI_GET_TYPEID, &el_type_id)) 971 839 WARN_RETURN(ERR::SYM_TYPE_INFO_UNAVAILABLE); 972 840 // .. workaround: TI_GET_COUNT returns total struct size for 973 841 // arrays-of-struct. therefore, calculate as size / el_size. 974 842 ULONG64 el_size_; 975 if(!pSymGetTypeInfo(hProcess, mod_base, el_type_id, TI_GET_LENGTH, &el_size_))843 if(!pSymGetTypeInfo(hProcess, state.moduleBase, el_type_id, TI_GET_LENGTH, &el_size_)) 976 844 WARN_RETURN(ERR::SYM_TYPE_INFO_UNAVAILABLE); 977 845 const size_t el_size = (size_t)el_size_; … … 1000 868 1001 869 1002 static Status dump_sym_base_type(DWORD type_id, const u8* p, DumpState state)870 static Status dump_sym_base_type(DWORD type_id, const u8* p, DumpState& state) 1003 871 { 1004 872 DWORD base_type; 1005 if(!pSymGetTypeInfo(hProcess, mod_base, type_id, TI_GET_BASETYPE, &base_type))1006 WARN_RETURN(ERR::SYM_TYPE_INFO_UNAVAILABLE); 1007 ULONG64 size _= 0;1008 if(!pSymGetTypeInfo(hProcess, mod_base, type_id, TI_GET_LENGTH, &size_))1009 WARN_RETURN(ERR::SYM_TYPE_INFO_UNAVAILABLE); 1010 const size_t size = (size_t)size _;873 if(!pSymGetTypeInfo(hProcess, state.moduleBase, type_id, TI_GET_BASETYPE, &base_type)) 874 WARN_RETURN(ERR::SYM_TYPE_INFO_UNAVAILABLE); 875 ULONG64 size64 = 0; 876 if(!pSymGetTypeInfo(hProcess, state.moduleBase, type_id, TI_GET_LENGTH, &size64)) 877 WARN_RETURN(ERR::SYM_TYPE_INFO_UNAVAILABLE); 878 const size_t size = (size_t)size64; 1011 879 1012 880 // single out() call. note: we pass a single u64 for all sizes, … … 1024 892 break; 1025 893 if(i == size-1) 1026 goto display_as_hex; 894 { 895 out(L"(uninitialized)"); 896 return INFO::OK; 897 } 1027 898 } 1028 899 … … 1063 934 case btUInt: 1064 935 case btULong: 1065 display_as_hex:1066 936 if(size == 1) 1067 937 { … … 1134 1004 //----------------------------------------------------------------------------- 1135 1005 1136 static Status dump_sym_base_class(DWORD type_id, const u8* p, DumpState state)1006 static Status dump_sym_base_class(DWORD type_id, const u8* p, DumpState& state) 1137 1007 { 1138 1008 DWORD base_class_type_id; 1139 if(!pSymGetTypeInfo(hProcess, mod_base, type_id, TI_GET_TYPEID, &base_class_type_id))1009 if(!pSymGetTypeInfo(hProcess, state.moduleBase, type_id, TI_GET_TYPEID, &base_class_type_id)) 1140 1010 WARN_RETURN(ERR::SYM_TYPE_INFO_UNAVAILABLE); 1141 1011 … … 1144 1014 // and just not worth it. 1145 1015 DWORD vptr_ofs; 1146 if(pSymGetTypeInfo(hProcess, mod_base, type_id, TI_GET_VIRTUALBASEPOINTEROFFSET, &vptr_ofs))1016 if(pSymGetTypeInfo(hProcess, state.moduleBase, type_id, TI_GET_VIRTUALBASEPOINTEROFFSET, &vptr_ofs)) 1147 1017 return ERR::SYM_UNSUPPORTED; // NOWARN 1148 1018 … … 1153 1023 //----------------------------------------------------------------------------- 1154 1024 1155 static Status dump_sym_data(DWORD id, const u8* p, DumpState state)1025 static Status dump_sym_data(DWORD id, const u8* p, DumpState& state) 1156 1026 { 1157 1027 SYMBOL_INFO_PACKAGEW2 sp; 1158 1028 SYMBOL_INFOW* sym = &sp.si; 1159 if(!pSymFromIndexW(hProcess, mod_base, id, sym))1029 if(!pSymFromIndexW(hProcess, state.moduleBase, id, sym)) 1160 1030 RETURN_STATUS_IF_ERR(ERR::SYM_TYPE_INFO_UNAVAILABLE); 1161 1031 … … 1164 1034 __try 1165 1035 { 1166 RETURN_STATUS_IF_ERR(DetermineSymbolAddress(id, sym, &p));1036 RETURN_STATUS_IF_ERR(DetermineSymbolAddress(id, sym, state, &p)); 1167 1037 // display value recursively 1168 1038 return dump_sym(sym->TypeIndex, p, state); … … 1177 1047 //----------------------------------------------------------------------------- 1178 1048 1179 static Status dump_sym_enum(DWORD type_id, const u8* p, DumpState UNUSED(state))1180 { 1181 ULONG64 size _= 0;1182 if(!pSymGetTypeInfo(hProcess, mod_base, type_id, TI_GET_LENGTH, &size_))1183 WARN_RETURN(ERR::SYM_TYPE_INFO_UNAVAILABLE); 1184 const size_t size = (size_t)size _;1049 static Status dump_sym_enum(DWORD type_id, const u8* p, DumpState& state) 1050 { 1051 ULONG64 size64 = 0; 1052 if(!pSymGetTypeInfo(hProcess, state.moduleBase, type_id, TI_GET_LENGTH, &size64)) 1053 WARN_RETURN(ERR::SYM_TYPE_INFO_UNAVAILABLE); 1054 const size_t size = (size_t)size64; 1185 1055 1186 1056 const i64 enum_value = movsx_le64(p, size); 1187 1057 1188 1058 // get array of child symbols (enumerants). 1189 DWORD num _children;1190 if(!pSymGetTypeInfo(hProcess, mod_base, type_id, TI_GET_CHILDRENCOUNT, &num_children))1191 WARN_RETURN(ERR::SYM_TYPE_INFO_UNAVAILABLE); 1192 TI_FINDCHILDREN_PARAMS2 fcp(num _children);1193 if(!pSymGetTypeInfo(hProcess, mod_base, type_id, TI_FINDCHILDREN, &fcp))1194 WARN_RETURN(ERR::SYM_TYPE_INFO_UNAVAILABLE); 1195 num _children = fcp.p.Count; // was truncated to MAX_CHILDREN1059 DWORD numChildren; 1060 if(!pSymGetTypeInfo(hProcess, state.moduleBase, type_id, TI_GET_CHILDRENCOUNT, &numChildren)) 1061 WARN_RETURN(ERR::SYM_TYPE_INFO_UNAVAILABLE); 1062 TI_FINDCHILDREN_PARAMS2 fcp(numChildren); 1063 if(!pSymGetTypeInfo(hProcess, state.moduleBase, type_id, TI_FINDCHILDREN, &fcp)) 1064 WARN_RETURN(ERR::SYM_TYPE_INFO_UNAVAILABLE); 1065 numChildren = fcp.p.Count; // was truncated to maxChildren 1196 1066 const DWORD* children = fcp.p.ChildId; 1197 1067 1198 1068 // for each child (enumerant): 1199 for(size_t i = 0; i < num _children; i++)1069 for(size_t i = 0; i < numChildren; i++) 1200 1070 { 1201 1071 DWORD child_data_id = children[i]; … … 1207 1077 // already pulled in by e.g. OpenGL anyway. 1208 1078 VARIANT v; 1209 if(!pSymGetTypeInfo(hProcess, mod_base, child_data_id, TI_GET_VALUE, &v))1079 if(!pSymGetTypeInfo(hProcess, state.moduleBase, child_data_id, TI_GET_VALUE, &v)) 1210 1080 WARN_RETURN(ERR::SYM_TYPE_INFO_UNAVAILABLE); 1211 1081 if(VariantChangeType(&v, &v, 0, VT_I8) != S_OK) … … 1216 1086 { 1217 1087 const wchar_t* name; 1218 if(!pSymGetTypeInfo(hProcess, mod_base, child_data_id, TI_GET_SYMNAME, &name))1088 if(!pSymGetTypeInfo(hProcess, state.moduleBase, child_data_id, TI_GET_SYMNAME, &name)) 1219 1089 WARN_RETURN(ERR::SYM_TYPE_INFO_UNAVAILABLE); 1220 1090 out(L"%ls", name); … … 1235 1105 //----------------------------------------------------------------------------- 1236 1106 1237 static Status dump_sym_function(DWORD UNUSED(type_id), const u8* UNUSED(p), DumpState UNUSED(state))1107 static Status dump_sym_function(DWORD UNUSED(type_id), const u8* UNUSED(p), DumpState& UNUSED(state)) 1238 1108 { 1239 1109 return INFO::SYM_SUPPRESS_OUTPUT; … … 1243 1113 //----------------------------------------------------------------------------- 1244 1114 1245 static Status dump_sym_function_type(DWORD UNUSED(type_id), const u8* p, DumpState state)1115 static Status dump_sym_function_type(DWORD UNUSED(type_id), const u8* p, DumpState& state) 1246 1116 { 1247 1117 // this symbol gives class parent, return type, and parameter count. … … 1249 1119 // isn't exposed via TI_GET_SYMNAME, so we resolve it ourselves. 1250 1120 1251 wchar_t name[D BG_SYMBOL_LEN];1121 wchar_t name[DEBUG_SYMBOL_CHARS]; 1252 1122 Status err = ResolveSymbol_lk((void*)p, name, 0, 0); 1253 1123 … … 1311 1181 1312 1182 1313 static Status dump_sym_pointer(DWORD type_id, const u8* p, DumpState state)1314 { 1315 ULONG64 size _= 0;1316 if(!pSymGetTypeInfo(hProcess, mod_base, type_id, TI_GET_LENGTH, &size_))1317 WARN_RETURN(ERR::SYM_TYPE_INFO_UNAVAILABLE); 1318 const size_t size = (size_t)size _;1183 static Status dump_sym_pointer(DWORD type_id, const u8* p, DumpState& state) 1184 { 1185 ULONG64 size64 = 0; 1186 if(!pSymGetTypeInfo(hProcess, state.moduleBase, type_id, TI_GET_LENGTH, &size64)) 1187 WARN_RETURN(ERR::SYM_TYPE_INFO_UNAVAILABLE); 1188 const size_t size = (size_t)size64; 1319 1189 1320 1190 // read+output pointer's value. … … 1340 1210 // the responsible dump_sym* will erase "->", leaving only address. 1341 1211 out(L" -> "); 1342 if(!pSymGetTypeInfo(hProcess, mod_base, type_id, TI_GET_TYPEID, &type_id))1212 if(!pSymGetTypeInfo(hProcess, state.moduleBase, type_id, TI_GET_TYPEID, &type_id)) 1343 1213 WARN_RETURN(ERR::SYM_TYPE_INFO_UNAVAILABLE); 1344 1214 1345 1215 // prevent infinite recursion just to be safe (shouldn't happen) 1346 if(state.indirection >= MAX_INDIRECTION)1216 if(state.indirection >= maxIndirection) 1347 1217 WARN_RETURN(ERR::SYM_NESTING_LIMIT); 1348 1218 state.indirection++; … … 1354 1224 1355 1225 1356 static Status dump_sym_typedef(DWORD type_id, const u8* p, DumpState state)1357 { 1358 if(!pSymGetTypeInfo(hProcess, mod_base, type_id, TI_GET_TYPEID, &type_id))1226 static Status dump_sym_typedef(DWORD type_id, const u8* p, DumpState& state) 1227 { 1228 if(!pSymGetTypeInfo(hProcess, state.moduleBase, type_id, TI_GET_TYPEID, &type_id)) 1359 1229 WARN_RETURN(ERR::SYM_TYPE_INFO_UNAVAILABLE); 1360 1230 return dump_sym(type_id, p, state); … … 1368 1238 // useful for UDTs that contain typedefs describing their contents, 1369 1239 // e.g. value_type in STL containers. 1370 static Status udt_get_child_type(const wchar_t* child_name, ULONG num _children, const DWORD* children, DWORD* el_type_id, size_t* el_size)1240 static Status udt_get_child_type(const wchar_t* child_name, ULONG numChildren, const DWORD* children, const DumpState& state, DWORD* el_type_id, size_t* el_size) 1371 1241 { 1372 1242 const DWORD lastError = GetLastError(); … … 1375 1245 *el_size = 0; 1376 1246 1377 for(ULONG i = 0; i < num _children; i++)1247 for(ULONG i = 0; i < numChildren; i++) 1378 1248 { 1379 1249 const DWORD child_id = children[i]; … … 1381 1251 SYMBOL_INFO_PACKAGEW2 sp; 1382 1252 SYMBOL_INFOW* sym = &sp.si; 1383 if(!pSymFromIndexW(hProcess, mod_base, child_id, sym))1253 if(!pSymFromIndexW(hProcess, state.moduleBase, child_id, sym)) 1384 1254 { 1385 1255 // this happens for several UDTs; cause is unknown. … … 1402 1272 1403 1273 1404 static Status udt_dump_std(const wchar_t* type_name, const u8* p, size_t size, DumpState state, ULONG num_children, const DWORD* children)1274 static Status udt_dump_std(const wchar_t* type_name, const u8* p, size_t size, DumpState& state, ULONG numChildren, const DWORD* children) 1405 1275 { 1406 1276 Status err; … … 1411 1281 1412 1282 // check for C++ objects that should be displayed via udt_dump_normal. 1413 // STLcontainers are special-cased and the rest (apart from those here)1283 // C++03 containers are special-cased and the rest (apart from those here) 1414 1284 // are ignored, because for the most part they are spew. 1415 if(!wcsncmp(type_name, L"std::pair", 9)) 1285 if(!wcsncmp(type_name, L"std::pair", 9) || 1286 !wcsncmp(type_name, L"std::tr1::", 10)) 1416 1287 return INFO::CANNOT_HANDLE; 1417 1288 … … 1420 1291 DWORD el_type_id; 1421 1292 size_t el_size; 1422 err = udt_get_child_type(L"value_type", num _children, children, &el_type_id, &el_size);1293 err = udt_get_child_type(L"value_type", numChildren, children, state, &el_type_id, &el_size); 1423 1294 if(err != INFO::OK) 1424 1295 goto not_valid_container; … … 1443 1314 text = L""; 1444 1315 // .. not one of the containers we can analyse. 1445 if(err == ERR::STL_CNT_UNKNOWN) 1316 else if(err == ERR::STL_CNT_UNKNOWN) 1317 text = L"unknown "; 1318 else if(err == ERR::STL_CNT_UNSUPPORTED) 1446 1319 text = L"unsupported "; 1447 1320 // .. container of a known type but contents are invalid. 1448 if(err == ERR::STL_CNT_INVALID)1321 else if(err == ERR::STL_CNT_INVALID) 1449 1322 text = L"uninitialized/invalid "; 1450 1323 // .. some other error encountered 1451 1324 else 1452 1325 { 1453 swprintf_s(buf, ARRAY_SIZE(buf), L"error %d while analyzing ", err); 1326 wchar_t description[200]; 1327 (void)StatusDescription(err, description, ARRAY_SIZE(description)); 1328 swprintf_s(buf, ARRAY_SIZE(buf), L"error \"%ls\" while analyzing ", description); 1454 1329 text = buf; 1455 1330 } … … 1457 1332 // (debug_stl modifies its input string in-place; type_name is 1458 1333 // a const string returned by dbghelp) 1459 wchar_t type_name_buf[D BG_SYMBOL_LEN];1334 wchar_t type_name_buf[DEBUG_SYMBOL_CHARS]; 1460 1335 wcscpy_s(type_name_buf, ARRAY_SIZE(type_name_buf), type_name); 1461 1336 … … 1514 1389 1515 1390 1516 static Status udt_dump_suppressed(const wchar_t* type_name, const u8* UNUSED(p), size_t UNUSED(size), DumpState state, ULONG UNUSED(num _children), const DWORD* UNUSED(children))1391 static Status udt_dump_suppressed(const wchar_t* type_name, const u8* UNUSED(p), size_t UNUSED(size), DumpState state, ULONG UNUSED(numChildren), const DWORD* UNUSED(children)) 1517 1392 { 1518 1393 if(!udt_should_suppress(type_name)) … … 1566 1441 1567 1442 1568 static Status udt_dump_normal(const wchar_t* type_name, const u8* p, size_t size, DumpState state, ULONG num _children, const DWORD* children)1569 { 1570 const bool fits_on_one_line = udt_fits_on_one_line(type_name, num _children, size);1443 static Status udt_dump_normal(const wchar_t* type_name, const u8* p, size_t size, DumpState state, ULONG numChildren, const DWORD* children) 1444 { 1445 const bool fits_on_one_line = udt_fits_on_one_line(type_name, numChildren, size); 1571 1446 1572 1447 // prevent infinite recursion just to be safe (shouldn't happen) 1573 if(state.level >= MAX_LEVEL)1448 if(state.level >= maxLevel) 1574 1449 WARN_RETURN(ERR::SYM_NESTING_LIMIT); 1575 1450 state.level++; … … 1578 1453 1579 1454 bool displayed_anything = false; 1580 for(ULONG i = 0; i < num _children; i++)1455 for(ULONG i = 0; i < numChildren; i++) 1581 1456 { 1582 1457 const DWORD child_id = children[i]; … … 1585 1460 // (we only display data here, not e.g. typedefs) 1586 1461 DWORD ofs = 0; 1587 if(!pSymGetTypeInfo(hProcess, mod_base, child_id, TI_GET_OFFSET, &ofs))1462 if(!pSymGetTypeInfo(hProcess, state.moduleBase, child_id, TI_GET_OFFSET, &ofs)) 1588 1463 continue; 1589 1464 if(ofs >= size) … … 1626 1501 1627 1502 // remove trailing comma separator 1628 // note: we can't avoid writing it by checking if i == num _children-1:1503 // note: we can't avoid writing it by checking if i == numChildren-1: 1629 1504 // each child might be the last valid data member. 1630 1505 if(fits_on_one_line) … … 1638 1513 1639 1514 1640 static Status dump_sym_udt(DWORD type_id, const u8* p, DumpState state)1641 { 1642 ULONG64 size _= 0;1643 if(!pSymGetTypeInfo(hProcess, mod_base, type_id, TI_GET_LENGTH, &size_))1644 WARN_RETURN(ERR::SYM_TYPE_INFO_UNAVAILABLE); 1645 const size_t size = (size_t)size _;1515 static Status dump_sym_udt(DWORD type_id, const u8* p, DumpState& state) 1516 { 1517 ULONG64 size64 = 0; 1518 if(!pSymGetTypeInfo(hProcess, state.moduleBase, type_id, TI_GET_LENGTH, &size64)) 1519 WARN_RETURN(ERR::SYM_TYPE_INFO_UNAVAILABLE); 1520 const size_t size = (size_t)size64; 1646 1521 1647 1522 // get array of child symbols (members/functions/base classes). 1648 DWORD num _children;1649 if(!pSymGetTypeInfo(hProcess, mod_base, type_id, TI_GET_CHILDRENCOUNT, &num_children))1650 WARN_RETURN(ERR::SYM_TYPE_INFO_UNAVAILABLE); 1651 TI_FINDCHILDREN_PARAMS2 fcp(num _children);1652 if(!pSymGetTypeInfo(hProcess, mod_base, type_id, TI_FINDCHILDREN, &fcp))1653 WARN_RETURN(ERR::SYM_TYPE_INFO_UNAVAILABLE); 1654 num _children = fcp.p.Count; // was truncated to MAX_CHILDREN1523 DWORD numChildren; 1524 if(!pSymGetTypeInfo(hProcess, state.moduleBase, type_id, TI_GET_CHILDRENCOUNT, &numChildren)) 1525 WARN_RETURN(ERR::SYM_TYPE_INFO_UNAVAILABLE); 1526 TI_FINDCHILDREN_PARAMS2 fcp(numChildren); 1527 if(!pSymGetTypeInfo(hProcess, state.moduleBase, type_id, TI_FINDCHILDREN, &fcp)) 1528 WARN_RETURN(ERR::SYM_TYPE_INFO_UNAVAILABLE); 1529 numChildren = fcp.p.Count; // was truncated to maxChildren 1655 1530 const DWORD* children = fcp.p.ChildId; 1656 1531 1657 1532 const wchar_t* type_name; 1658 if(!pSymGetTypeInfo(hProcess, mod_base, type_id, TI_GET_SYMNAME, &type_name))1533 if(!pSymGetTypeInfo(hProcess, state.moduleBase, type_id, TI_GET_SYMNAME, &type_name)) 1659 1534 WARN_RETURN(ERR::SYM_TYPE_INFO_UNAVAILABLE); 1660 1535 … … 1663 1538 // suppressing UDTs, which tosses out most other C++ stdlib classes) 1664 1539 1665 ret = udt_dump_std (type_name, p, size, state, num _children, children);1540 ret = udt_dump_std (type_name, p, size, state, numChildren, children); 1666 1541 if(ret != INFO::CANNOT_HANDLE) 1667 1542 goto done; 1668 1543 1669 ret = udt_dump_suppressed(type_name, p, size, state, num _children, children);1544 ret = udt_dump_suppressed(type_name, p, size, state, numChildren, children); 1670 1545 if(ret != INFO::CANNOT_HANDLE) 1671 1546 goto done; 1672 1547 1673 ret = udt_dump_normal (type_name, p, size, state, num _children, children);1548 ret = udt_dump_normal (type_name, p, size, state, numChildren, children); 1674 1549 if(ret != INFO::CANNOT_HANDLE) 1675 1550 goto done; … … 1684 1559 1685 1560 1686 static Status dump_sym_vtable(DWORD UNUSED(type_id), const u8* UNUSED(p), DumpState UNUSED(state))1561 static Status dump_sym_vtable(DWORD UNUSED(type_id), const u8* UNUSED(p), DumpState& UNUSED(state)) 1687 1562 { 1688 1563 // unsupported (vtable internals are undocumented; too much work). … … 1694 1569 1695 1570 1696 static Status dump_sym_unknown(DWORD type_id, const u8* UNUSED(p), DumpState UNUSED(state))1571 static Status dump_sym_unknown(DWORD type_id, const u8* UNUSED(p), DumpState& state) 1697 1572 { 1698 1573 // redundant (already done in dump_sym), but this is rare. 1699 1574 DWORD type_tag; 1700 if(!pSymGetTypeInfo(hProcess, mod_base, type_id, TI_GET_SYMTAG, &type_tag))1701 WARN_RETURN(ERR::SYM_TYPE_INFO_UNAVAILABLE); 1702 1703 debug_printf(L"SYM |unknown tag: %d\n", type_tag);1575 if(!pSymGetTypeInfo(hProcess, state.moduleBase, type_id, TI_GET_SYMTAG, &type_tag)) 1576 WARN_RETURN(ERR::SYM_TYPE_INFO_UNAVAILABLE); 1577 1578 debug_printf(L"SYM: unknown tag: %d\n", type_tag); 1704 1579 out(L"(unknown symbol type)"); 1705 1580 return INFO::OK; … … 1709 1584 //----------------------------------------------------------------------------- 1710 1585 1586 typedef Status (*DumpFunc)(DWORD typeId, const u8* p, DumpState& state); 1587 1588 static DumpFunc DumpFuncFromTypeTag(DWORD typeTag) 1589 { 1590 switch(typeTag) 1591 { 1592 case SymTagArrayType: 1593 return dump_sym_array; 1594 case SymTagBaseType: 1595 return dump_sym_base_type; 1596 case SymTagBaseClass: 1597 return dump_sym_base_class; 1598 case SymTagData: 1599 return dump_sym_data; 1600 case SymTagEnum: 1601 return dump_sym_enum; 1602 case SymTagFunction: 1603 return dump_sym_function; 1604 case SymTagFunctionType: 1605 return dump_sym_function_type; 1606 case SymTagPointerType: 1607 return dump_sym_pointer; 1608 case SymTagTypedef: 1609 return dump_sym_typedef; 1610 case SymTagUDT: 1611 return dump_sym_udt; 1612 case SymTagVTable: 1613 return dump_sym_vtable; 1614 default: 1615 return dump_sym_unknown; 1616 } 1617 } 1618 1711 1619 1712 1620 // write name and value of the symbol <type_id> to the output buffer. 1713 1621 // delegates to dump_sym_* depending on the symbol's tag. 1714 static Status dump_sym(DWORD type_id, const u8* p, DumpState state)1622 static Status dump_sym(DWORD type_id, const u8* p, DumpState& state) 1715 1623 { 1716 1624 RETURN_STATUS_IF_ERR(out_check_limit()); 1717 1625 1718 DWORD type_tag; 1719 if(!pSymGetTypeInfo(hProcess, mod_base, type_id, TI_GET_SYMTAG, &type_tag)) 1720 WARN_RETURN(ERR::SYM_TYPE_INFO_UNAVAILABLE); 1721 switch(type_tag) 1722 { 1723 case SymTagArrayType: 1724 return dump_sym_array (type_id, p, state); 1725 case SymTagBaseType: 1726 return dump_sym_base_type (type_id, p, state); 1727 case SymTagBaseClass: 1728 return dump_sym_base_class (type_id, p, state); 1729 case SymTagData: 1730 return dump_sym_data (type_id, p, state); 1731 case SymTagEnum: 1732 return dump_sym_enum (type_id, p, state); 1733 case SymTagFunction: 1734 return dump_sym_function (type_id, p, state); 1735 case SymTagFunctionType: 1736 return dump_sym_function_type (type_id, p, state); 1737 case SymTagPointerType: 1738 return dump_sym_pointer (type_id, p, state); 1739 case SymTagTypedef: 1740 return dump_sym_typedef (type_id, p, state); 1741 case SymTagUDT: 1742 return dump_sym_udt (type_id, p, state); 1743 case SymTagVTable: 1744 return dump_sym_vtable (type_id, p, state); 1745 default: 1746 return dump_sym_unknown (type_id, p, state); 1747 } 1626 DWORD typeTag; 1627 if(!pSymGetTypeInfo(hProcess, state.moduleBase, type_id, TI_GET_SYMTAG, &typeTag)) 1628 WARN_RETURN(ERR::SYM_TYPE_INFO_UNAVAILABLE); 1629 const DumpFunc dumpFunc = DumpFuncFromTypeTag(typeTag); 1630 return dumpFunc(type_id, p, state); 1748 1631 } 1749 1632 … … 1752 1635 // stack trace 1753 1636 //----------------------------------------------------------------------------- 1754 1755 struct IMAGEHLP_STACK_FRAME2 : public IMAGEHLP_STACK_FRAME1756 {1757 IMAGEHLP_STACK_FRAME2(const _tagSTACKFRAME64* sf)1758 {1759 // apparently only PC, FP and SP are necessary, but1760 // we go whole-hog to be safe.1761 memset(this, 0, sizeof(IMAGEHLP_STACK_FRAME2));1762 InstructionOffset = sf->AddrPC.Offset;1763 ReturnOffset = sf->AddrReturn.Offset;1764 FrameOffset = sf->AddrFrame.Offset;1765 StackOffset = sf->AddrStack.Offset;1766 BackingStoreOffset = sf->AddrBStore.Offset;1767 FuncTableEntry = (ULONG64)sf->FuncTableEntry;1768 Virtual = sf->Virtual;1769 // (note: array of different types, can't copy directly)1770 for(int i = 0; i < 4; i++)1771 Params[i] = sf->Params[i];1772 }1773 };1774 1637 1775 1638 static bool ShouldSkipSymbol(const wchar_t* name) … … 1784 1647 // output the symbol's name and value via dump_sym*. 1785 1648 // called from dump_frame_cb for each local symbol; lock is held. 1786 static BOOL CALLBACK dump_sym_cb(SYMBOL_INFOW* sym, ULONG UNUSED(size), void* UNUSED(ctx))1649 static BOOL CALLBACK dump_sym_cb(SYMBOL_INFOW* sym, ULONG UNUSED(size), PVOID userContext) 1787 1650 { 1788 1651 if(ShouldSkipSymbol(sym->Name)) … … 1790 1653 1791 1654 out_latch_pos(); // see decl 1792 mod_base = (uintptr_t)sym->ModBase;1793 1655 const u8* p = (const u8*)(uintptr_t)sym->Address; 1794 DumpState state ;1656 DumpState state((uintptr_t)sym->ModBase, (LPSTACKFRAME64)userContext); 1795 1657 1796 1658 INDENT; … … 1806 1668 1807 1669 // called by wdbg_sym_WalkStack for each stack frame 1808 static Status dump_frame_cb(const _tagSTACKFRAME64* sf, uintptr_t UNUSED(cbData)) 1809 { 1810 current_stackframe64 = sf; 1670 static Status dump_frame_cb(const STACKFRAME64* sf, uintptr_t UNUSED(userContext)) 1671 { 1811 1672 void* func = (void*)(uintptr_t)sf->AddrPC.Offset; 1812 1673 1813 wchar_t func_name[D BG_SYMBOL_LEN]; wchar_t file[DBG_FILE_LEN]; int line;1674 wchar_t func_name[DEBUG_SYMBOL_CHARS]; wchar_t file[DEBUG_FILE_CHARS]; int line; 1814 1675 Status ret = ResolveSymbol_lk(func, func_name, file, &line); 1815 1676 if(ret == INFO::OK) … … 1822 1683 // note: the stdcall mangled name includes parameter size, which is 1823 1684 // different in 64-bit, so only check the first characters. 1824 if(!wcsncmp(func_name, L"_BaseProcessStart", 17)) 1685 if(!wcsncmp(func_name, L"_BaseProcessStart", 17) || 1686 !wcscmp(func_name, L"BaseThreadInitThunk")) 1687 return INFO::OK; 1688 1689 // skip any mainCRTStartup frames 1690 if(!wcscmp(func_name, L"__tmainCRTStartup")) 1691 return INFO::OK; 1692 if(!wcscmp(func_name, L"mainCRTStartup")) 1825 1693 return INFO::OK; 1826 1694 … … 1836 1704 // problem: debug info is scope-aware, so we won't see any variables 1837 1705 // declared in sub-blocks. we'd have to pass an address in that block, 1838 // which isn't worth the trouble. since 1839 IMAGEHLP_STACK_FRAME2 imghlp_frame(sf); 1840 const PIMAGEHLP_CONTEXT context = 0; // ignored 1841 pSymSetContext(hProcess, &imghlp_frame, context); 1706 // which isn't worth the trouble. 1707 IMAGEHLP_STACK_FRAME isf = PopulateImageStackFrame(*sf); 1708 const PIMAGEHLP_CONTEXT ic = 0; // ignored 1709 // NB: this sometimes fails for reasons unknown in a static 1710 // member function, possibly because the return address is in kernel32 1711 (void)pSymSetContext(hProcess, &isf, ic); 1842 1712 1843 1713 const ULONG64 base = 0; const wchar_t* const mask = 0; // use scope set by pSymSetContext 1844 pSymEnumSymbolsW(hProcess, base, mask, dump_sym_cb, 0);1714 pSymEnumSymbolsW(hProcess, base, mask, dump_sym_cb, (PVOID)sf); 1845 1715 1846 1716 if(GetLastError() == ERROR_NOT_SUPPORTED) // no debug info present? … … 1848 1718 1849 1719 out(L"\r\n"); 1850 return INFO:: CONTINUE;1720 return INFO::OK; 1851 1721 } 1852 1722 … … 1861 1731 ptr_reset_visited(); 1862 1732 1863 Status ret = wdbg_sym_WalkStack(dump_frame_cb, 0, (const CONTEXT*)pcontext, lastFuncToSkip); 1864 1733 wdbg_assert(pcontext != 0); 1734 Status ret = wdbg_sym_WalkStack(dump_frame_cb, 0, *(CONTEXT*)pcontext, lastFuncToSkip); 1735 1736 COMPILER_FENCE; 1865 1737 busy = 0; 1866 COMPILER_FENCE;1867 1738 1868 1739 return ret; -
ps/trunk/source/lib/sysdep/os/win/wdbg_sym.h
r9462 r9871 28 28 #define INCLUDED_WDBG_SYM 29 29 30 #include "lib/sysdep/os/win/win.h" // CONTEXT, EXCEPTION_POINTERS 31 30 32 struct _tagSTACKFRAME64; 31 struct _CONTEXT;32 struct _EXCEPTION_POINTERS;33 33 34 34 /** … … 36 36 * 37 37 * @param frame the dbghelp stack frame (we can't just pass the 38 * instruction-pointer because dump_frame_cb needs the frame pointer to39 * locate frame-relative variables)38 * instruction-pointer because dump_frame_cb needs the frame pointer to 39 * locate frame-relative variables) 40 40 * @param cbData the user-specified value that was passed to wdbg_sym_WalkStack 41 * @return INFO::CONTINUE to continue, anything else to stop immediately 42 * and return that value to wdbg_sym_WalkStack's caller. 41 * @return Status (see RETURN_STATUS_FROM_CALLBACK). 43 42 **/ 44 43 typedef Status (*StackFrameCallback)(const _tagSTACKFRAME64* frame, uintptr_t cbData); … … 49 48 * @param cb 50 49 * @param cbData 51 * @param pcontext Processor context from which to start (usuallytaken from52 * an exception record), or 0 to walk the current stack.50 * @param context Processor context from which to start (taken from 51 * an exception record or debug_CaptureContext). 53 52 * @param lastFuncToSkip 54 53 * 55 54 * @note It is safe to use ENSURE/debug_warn/WARN_RETURN_STATUS_IF_ERR even during a 56 * stack trace (which is triggered by ENSURE et al. in app code) because57 * nested stack traces are ignored and only the error is displayed.55 * stack trace (which is triggered by ENSURE et al. in app code) because 56 * nested stack traces are ignored and only the error is displayed. 58 57 **/ 59 extern Status wdbg_sym_WalkStack(StackFrameCallback cb, uintptr_t cbData = 0, const _CONTEXT* pcontext = 0, const wchar_t* lastFuncToSkip = 0);58 LIB_API Status wdbg_sym_WalkStack(StackFrameCallback cb, uintptr_t cbData, CONTEXT& context, const wchar_t* lastFuncToSkip = 0); 60 59 61 extern void wdbg_sym_WriteMinidump(_EXCEPTION_POINTERS* ep);60 LIB_API void wdbg_sym_WriteMinidump(EXCEPTION_POINTERS* ep); 62 61 63 62 #endif // #ifndef INCLUDED_WDBG_SYM -
ps/trunk/source/lib/sysdep/os/win/wposix/wmman.cpp
r9462 r9871 83 83 // .. if MAP_SHARED, writes are to change "the underlying [mapped] 84 84 // object", but there is none here (we're backed by the page file). 85 ENSURE( flags & MAP_PRIVATE);85 ENSURE(!(flags & MAP_SHARED)); 86 86 87 87 // see explanation at MAP_NORESERVE definition. … … 121 121 // read/write and copy-on-write, so we dumb it down to that and later 122 122 // set the correct (and more restrictive) permission via mprotect. 123 static Status mmap_file_access(int prot, int flags, DWORD& protect, DWORD& dwAccess) 124 { 125 // assume read-only; other cases handled below. 126 protect = PAGE_READONLY; 127 dwAccess = FILE_MAP_READ; 123 static Status DecodeFlags(int prot, int flags, DWORD& protect, DWORD& access) 124 { 125 // ensure exactly one of (MAP_SHARED, MAP_PRIVATE) is specified 126 switch(flags & (MAP_SHARED|MAP_PRIVATE)) 127 { 128 case 0: 129 case MAP_SHARED|MAP_PRIVATE: 130 WARN_RETURN(ERR::INVALID_PARAM); 131 default:; 132 } 128 133 129 134 if(prot & PROT_WRITE) 130 135 { 131 136 // determine write behavior: (whether they change the underlying file) 132 switch(flags & (MAP_SHARED|MAP_PRIVATE))137 if(flags & MAP_SHARED) // writes affect the file 133 138 { 134 // .. changes are written to file.135 case MAP_SHARED:136 139 protect = PAGE_READWRITE; 137 dwAccess = FILE_MAP_WRITE; // read and write138 break;139 // .. copy-on-write mapping; writes do not affect the file.140 case MAP_PRIVATE:140 access = FILE_MAP_WRITE; // read and write 141 } 142 else // copy on write (file remains unchanged) 143 { 141 144 protect = PAGE_WRITECOPY; 142 dwAccess = FILE_MAP_COPY; 143 break; 144 // .. either none or both of the flags are set. the latter is 145 // definitely illegal according to POSIX and some man pages 146 // say exactly one must be set, so abort. 147 default: 148 WARN_RETURN(ERR::INVALID_PARAM); 145 access = FILE_MAP_COPY; 149 146 } 147 } 148 else 149 { 150 protect = PAGE_READONLY; 151 access = FILE_MAP_READ; 150 152 } 151 153 … … 172 174 // MapViewOfFile. these are weaker than what PROT_* allows and 173 175 // are augmented below by subsequently mprotect-ing. 174 DWORD protect; DWORD dwAccess; 175 RETURN_STATUS_IF_ERR(mmap_file_access(prot, flags, protect, dwAccess)); 176 177 // enough foreplay; now actually map. 176 DWORD protect; DWORD access; 177 RETURN_STATUS_IF_ERR(DecodeFlags(prot, flags, protect, access)); 178 178 179 const HANDLE hMap = CreateFileMapping(hFile, 0, protect, 0, 0, 0); 179 // .. create failed; bail now to avoid overwriting the last error value.180 180 if(!hMap) 181 181 WARN_RETURN(ERR::NO_MEM); 182 const DWORD ofs_hi = u64_hi(ofs), ofs_lo = u64_lo(ofs); 183 void* p = MapViewOfFileEx(hMap, dwAccess, ofs_hi, ofs_lo, (SIZE_T)len, start); 184 // .. make sure we got the requested address if MAP_FIXED was passed. 182 void* p = MapViewOfFileEx(hMap, access, u64_hi(ofs), u64_lo(ofs), (SIZE_T)len, start); 183 // ensure we got the requested address if MAP_FIXED was passed. 185 184 ENSURE(!(flags & MAP_FIXED) || (p == start)); 186 // ..free the mapping object now, so that we don't have to hold on to its187 // handle until munmap(). it's not actually released yet due to the188 // reference held by MapViewOfFileEx (if it succeeded).185 // free the mapping object now, so that we don't have to hold on to its 186 // handle until munmap(). it's not actually released yet due to the 187 // reference held by MapViewOfFileEx (if it succeeded). 189 188 CloseHandle(hMap); 190 // ..map failed; bail now to avoid "restoring" the last error value.189 // map failed; bail now to avoid "restoring" the last error value. 191 190 if(!p) 192 191 WARN_RETURN(ERR::NO_MEM); 193 192 194 // slap on correct (more restrictive) permissions.193 // enforce the desired (more restrictive) protection. 195 194 (void)mprotect(p, len, prot); 196 195 … … 202 201 void* mmap(void* start, size_t len, int prot, int flags, int fd, off_t ofs) 203 202 { 204 if(len == 0) // POSIX says this must cause mmap to fail 205 { 206 DEBUG_WARN_ERR(ERR::LOGIC); 207 errno = EINVAL; 208 return MAP_FAILED; 209 } 203 ASSERT(len != 0); 210 204 211 205 void* p; -
ps/trunk/source/lib/sysdep/os/win/wposix/wmman.h
r7316 r9871 39 39 #define MAP_FIXED 0x04 40 40 // .. non-portable 41 #define MAP_ANONYMOUS 0x10 42 #define MAP_NORESERVE 0x20 41 #define MAP_ANONYMOUS 0x10 // backed by the pagefile; fd should be -1 42 #define MAP_NORESERVE 0x20 // see below 43 43 44 44 // note: we need a means of only "reserving" virtual address ranges … … 50 50 // make sure of that in the future. 51 51 52 #define MAP_FAILED ((void*) (intptr_t)-1L)52 #define MAP_FAILED ((void*)intptr_t(-1)) 53 53 54 54 extern void* mmap(void* start, size_t len, int prot, int flags, int fd, off_t offset); -
ps/trunk/source/lib/sysdep/os/win/wseh.cpp
r9475 r9871 216 216 // return location at which the exception <er> occurred. 217 217 // params: see debug_ResolveSymbol. 218 static void GetExceptionLocus( constEXCEPTION_POINTERS* ep,218 static void GetExceptionLocus(EXCEPTION_POINTERS* ep, 219 219 wchar_t* file, int* line, wchar_t* func) 220 220 { … … 271 271 wchar_t descriptionBuf[150]; 272 272 const wchar_t* description = GetExceptionDescription(ep, descriptionBuf, ARRAY_SIZE(descriptionBuf)); 273 wchar_t file[D BG_FILE_LEN] = {0};273 wchar_t file[DEBUG_FILE_CHARS] = {0}; 274 274 int line = 0; 275 wchar_t func[D BG_SYMBOL_LEN] = {0};275 wchar_t func[DEBUG_SYMBOL_CHARS] = {0}; 276 276 GetExceptionLocus(ep, file, &line, func); 277 277 -
ps/trunk/source/ps/GameSetup/GameSetup.cpp
r9819 r9871 761 761 debug_filter_add(L"TIMER"); 762 762 763 cpu_ConfigureFloatingPoint();764 765 763 timer_LatchStartTime(); 766 764 -
ps/trunk/source/ps/scripting/JSInterface_VFS.cpp
r9550 r9871 73 73 jsval val = ToJSVal( CStrW(pathname.string()) ); 74 74 JS_SetElement(s->cx, s->filename_array, s->cur_idx++, &val); 75 return INFO:: CONTINUE;75 return INFO::OK; 76 76 } 77 77
Note:
See TracChangeset
for help on using the changeset viewer.
