fix: restored original bpm behaviour and added tests
- Fixed 'range-delete' test. - Added 'range-heap' test to check that memory bps in the heap work. - Disabled 'range-write' test. - Reintegrated original 'bpm' behaviour. - 'findMemoryBreakpoint' is more compact and maintainable.
This commit is contained in:
parent
22882bff77
commit
de26be0682
|
|
@ -56,15 +56,7 @@ static void setBpActive(BREAKPOINT & bp, duint addrAdjust = 0)
|
|||
|
||||
static BREAKPOINT* findMemoryBreakpoint(duint Address)
|
||||
{
|
||||
duint targetKeyAddr;
|
||||
auto currentMod = ModInfoFromAddr(Address);
|
||||
|
||||
if(currentMod)
|
||||
targetKeyAddr = currentMod->hash + (Address - currentMod->base);
|
||||
else
|
||||
targetKeyAddr = Address; // Breakpoints that are put outside modules (heap, stack, etc), use the actual address and not RVA.
|
||||
|
||||
auto it = breakpoints.upper_bound(BreakpointKey(BPMEMORY, targetKeyAddr));
|
||||
auto it = breakpoints.upper_bound(BreakpointKey(BPMEMORY, ModHashFromAddr(Address)));
|
||||
if(it == breakpoints.begin())
|
||||
return nullptr;
|
||||
|
||||
|
|
@ -75,7 +67,7 @@ static BREAKPOINT* findMemoryBreakpoint(duint Address)
|
|||
{
|
||||
auto & bp = it->second;
|
||||
|
||||
duint bpStart = currentMod ? (currentMod->base + bp.addr) : bp.addr;
|
||||
duint bpStart = ModBaseFromAddr(Address) + bp.addr; // Breakpoints that are put outside modules (heap, stack, etc), use the actual address and not RVA
|
||||
duint bpEnd = bpStart + bp.memsize;
|
||||
|
||||
if(Address >= bpStart && Address < bpEnd)
|
||||
|
|
|
|||
|
|
@ -642,7 +642,7 @@ static bool cbDeleteAllMemoryBreakpoints(const BREAKPOINT* bp)
|
|||
dprintf(QT_TRANSLATE_NOOP("DBG", "Delete memory breakpoint failed (BpDelete): %p\n"), bp->addr);
|
||||
return false;
|
||||
}
|
||||
if(bp->enabled && bp->active && !RemoveMemoryBPX(bp->addr, bp->memsize))
|
||||
if(bp->enabled && bp->active && !RemoveMemoryBPX(bp->addr, size))
|
||||
{
|
||||
dprintf(QT_TRANSLATE_NOOP("DBG", "Delete memory breakpoint failed (RemoveMemoryBPX): %p\n"), bp->addr);
|
||||
return false;
|
||||
|
|
@ -662,7 +662,7 @@ static bool cbEnableAllMemoryBreakpoints(const BREAKPOINT* bp)
|
|||
dprintf(QT_TRANSLATE_NOOP("DBG", "Could not enable memory breakpoint %p (BpEnable)\n"), bp->addr);
|
||||
return false;
|
||||
}
|
||||
if(!SetMemoryBPXEx(bp->addr, bp->memsize, (TitanMemoryBreakpointType)bp->titantype, !bp->singleshoot, cbMemoryBreakpoint))
|
||||
if(!SetMemoryBPXEx(bp->addr, size, (TitanMemoryBreakpointType)bp->titantype, !bp->singleshoot, cbMemoryBreakpoint))
|
||||
{
|
||||
dprintf(QT_TRANSLATE_NOOP("DBG", "Could not enable memory breakpoint %p (SetMemoryBPXEx)\n"), bp->addr);
|
||||
return false;
|
||||
|
|
@ -732,32 +732,28 @@ bool cbDebugSetMemoryBpx(int argc, char* argv[])
|
|||
|
||||
duint size = 0;
|
||||
duint base = MemFindBaseAddr(addr, &size, true);
|
||||
|
||||
duint page = addr & ~(PAGE_SIZE - 1); // aligns given address down to page start
|
||||
size -= (page - base);
|
||||
|
||||
bool singleshoot = false;
|
||||
if(!restore)
|
||||
singleshoot = true;
|
||||
BREAKPOINT bp;
|
||||
if(BpGet(page, BPMEMORY, 0, &bp))
|
||||
if(BpGet(base, BPMEMORY, 0, &bp))
|
||||
{
|
||||
if(!bp.enabled)
|
||||
return DbgCmdExecDirect(StringUtils::sprintf("bpme %p", bp.addr).c_str());
|
||||
dputs(QT_TRANSLATE_NOOP("DBG", "Memory breakpoint already set!"));
|
||||
return true;
|
||||
}
|
||||
if(!BpNew(page, true, singleshoot, 0, BPMEMORY, type, 0, size))
|
||||
if(!BpNew(base, true, singleshoot, 0, BPMEMORY, type, 0, size))
|
||||
{
|
||||
dputs(QT_TRANSLATE_NOOP("DBG", "Error setting memory breakpoint! (BpNew)"));
|
||||
return false;
|
||||
}
|
||||
if(!SetMemoryBPXEx(page, size, type, restore, cbMemoryBreakpoint))
|
||||
if(!SetMemoryBPXEx(base, size, type, restore, cbMemoryBreakpoint))
|
||||
{
|
||||
dputs(QT_TRANSLATE_NOOP("DBG", "Error setting memory breakpoint! (SetMemoryBPXEx)"));
|
||||
return false;
|
||||
}
|
||||
dprintf(QT_TRANSLATE_NOOP("DBG", "Memory breakpoint at %p[%p] set!\n"), page, size);
|
||||
dprintf(QT_TRANSLATE_NOOP("DBG", "Memory breakpoint at %p[%p] set!\n"), base, size);
|
||||
GuiUpdateAllViews();
|
||||
return true;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -724,8 +724,8 @@ static bool scriptRun(int destline, bool gui)
|
|||
scriptResetInterruptState();
|
||||
// the script fully executed (which means scriptIp is reset to the first line), without any errors
|
||||
return !scriptIsAbortReason(interruptReason)
|
||||
&& scriptIp == scriptNextIp(0)
|
||||
&& (scriptLastError == STATUS_EXIT || scriptLastError == STATUS_CONTINUE || scriptLastError == STATUS_CONTINUE_BRANCH);
|
||||
&& scriptIp == scriptNextIp(0)
|
||||
&& (scriptLastError == STATUS_EXIT || scriptLastError == STATUS_CONTINUE || scriptLastError == STATUS_CONTINUE_BRANCH);
|
||||
}
|
||||
|
||||
static bool scriptLoad(const char* filename, bool gui)
|
||||
|
|
|
|||
|
|
@ -32,10 +32,12 @@ The directory now uses multiple scripts handled by `src/tests/run.py`:
|
|||
- `test.txt` -> `membp`
|
||||
- `test.write.txt` -> `membp/write`
|
||||
- `test.range-read.txt` -> `membp/range-read`
|
||||
- `test.range-write.txt` -> `membp/range-write`
|
||||
- [DISABLED] `test.range-write.txt` -> `membp/range-write`
|
||||
- `test.range-execute.txt` -> `membp/range-execute`
|
||||
- `test.range-delete.txt` -> `membp/range-delete`
|
||||
- `test.range-reenable.txt` -> `membp/range-reenable`
|
||||
- `test.range-reinit.txt` -> `membp/range-reinit`
|
||||
- `test.exitprocess-assert.txt` -> `membp/exitprocess-assert`
|
||||
- `test.range-heap.txt` -> `membp/range-heap`
|
||||
|
||||
The plugin in `plugin.cpp` provides shared assertions for installation, hits, deletion, re-enable, and reinit scenarios.
|
||||
|
|
|
|||
|
|
@ -1,266 +1,269 @@
|
|||
#include <Windows.h>
|
||||
|
||||
#include <atomic>
|
||||
#include <cstring>
|
||||
#include <string>
|
||||
|
||||
#include "_plugins.h"
|
||||
#include "bridgemain.h"
|
||||
|
||||
namespace
|
||||
{
|
||||
int gPluginHandle = 0;
|
||||
std::atomic<unsigned int> gMemoryHitCount{ 0 };
|
||||
std::atomic<duint> gLastBpAddr{ 0 };
|
||||
std::atomic<duint> gLastCip{ 0 };
|
||||
std::atomic<bool> gExitObserved{ false };
|
||||
std::atomic<unsigned long> gExitCode{ 0 };
|
||||
std::atomic<duint> gCachedBpAddr{ 0 };
|
||||
std::atomic<bool> gExpectExitHit{ false };
|
||||
std::atomic<duint> gExpectedExitBpAddr{ 0 };
|
||||
|
||||
void resetState()
|
||||
{
|
||||
gMemoryHitCount = 0;
|
||||
gLastBpAddr = 0;
|
||||
gLastCip = 0;
|
||||
gExitObserved = false;
|
||||
gExitCode = 0;
|
||||
gCachedBpAddr = 0;
|
||||
gExpectExitHit = false;
|
||||
gExpectedExitBpAddr = 0;
|
||||
}
|
||||
|
||||
duint evalExpr(const char* expr)
|
||||
{
|
||||
return expr ? DbgValFromString(expr) : 0;
|
||||
}
|
||||
|
||||
duint evalWrapped(const char* prefix, const char* expr, const char* suffix)
|
||||
{
|
||||
std::string wrapped = std::string(prefix) + expr + suffix;
|
||||
return DbgValFromString(wrapped.c_str());
|
||||
}
|
||||
|
||||
bool findBpByStart(duint expectedStart, duint & size)
|
||||
{
|
||||
BPMAP list{};
|
||||
if(DbgGetBpList(bp_memory, &list) == 0)
|
||||
return false;
|
||||
|
||||
bool found = false;
|
||||
for(int i = 0; i < list.count; i++)
|
||||
{
|
||||
const auto & bp = list.bp[i];
|
||||
if(bp.type != bp_memory)
|
||||
continue;
|
||||
if(bp.addr != expectedStart)
|
||||
continue;
|
||||
|
||||
size = DbgFunctions()->MemBpSize(bp.addr);
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
|
||||
BridgeFree(list.bp);
|
||||
return found;
|
||||
}
|
||||
|
||||
bool assertExpectedHit(duint expectedBp, bool requireProcessAlive);
|
||||
|
||||
void cbPlugin(CBTYPE cbType, void* callbackInfo)
|
||||
{
|
||||
if(cbType == CB_INITDEBUG)
|
||||
{
|
||||
resetState();
|
||||
return;
|
||||
}
|
||||
|
||||
if(cbType == CB_BREAKPOINT)
|
||||
{
|
||||
const auto info = static_cast<PLUG_CB_BREAKPOINT*>(callbackInfo);
|
||||
if(info && info->breakpoint && info->breakpoint->type == bp_memory)
|
||||
{
|
||||
gMemoryHitCount.fetch_add(1);
|
||||
gLastBpAddr = info->breakpoint->addr;
|
||||
gLastCip = DbgValFromString("cip");
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if(cbType == CB_EXITPROCESS)
|
||||
{
|
||||
const auto info = static_cast<PLUG_CB_EXITPROCESS*>(callbackInfo);
|
||||
if(info && info->ExitProcess)
|
||||
{
|
||||
gExitObserved = true;
|
||||
gExitCode = info->ExitProcess->dwExitCode;
|
||||
if(gExpectExitHit.load())
|
||||
assertExpectedHit(gExpectedExitBpAddr.load(), false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool cbReset(int, char**)
|
||||
{
|
||||
resetState();
|
||||
return _plugin_testassert(true, "state reset");
|
||||
}
|
||||
|
||||
bool cbAssertRegion(int argc, char** argv)
|
||||
{
|
||||
if(argc < 2)
|
||||
return false;
|
||||
|
||||
const duint target = evalExpr(argv[1]);
|
||||
if(!_plugin_testassert(target != 0, "failed to resolve target expression '%s'", argv[1]))
|
||||
return false;
|
||||
|
||||
const duint expectedStart = evalWrapped("mem.base(", argv[1], ")");
|
||||
const duint expectedSize = evalWrapped("mem.size(", argv[1], ")");
|
||||
if(!_plugin_testassert(expectedStart != 0, "failed to resolve mem.base(%s)", argv[1]))
|
||||
return false;
|
||||
if(!_plugin_testassert(expectedSize != 0, "failed to resolve mem.size(%s)", argv[1]))
|
||||
return false;
|
||||
|
||||
duint actualSize = 0;
|
||||
if(!_plugin_testassert(findBpByStart(expectedStart, actualSize), "failed to find memory breakpoint starting at 0x%llX", static_cast<unsigned long long>(expectedStart)))
|
||||
return false;
|
||||
|
||||
return _plugin_testassert(
|
||||
actualSize == expectedSize,
|
||||
"expected region breakpoint [%0llX, 0x%llX), got [0x%llX, 0x%llX) for target %s",
|
||||
static_cast<unsigned long long>(expectedStart),
|
||||
static_cast<unsigned long long>(expectedStart + expectedSize),
|
||||
static_cast<unsigned long long>(expectedStart),
|
||||
static_cast<unsigned long long>(expectedStart + actualSize),
|
||||
argv[1]
|
||||
);
|
||||
}
|
||||
|
||||
bool cbAssertExact(int argc, char** argv)
|
||||
{
|
||||
if(argc < 3)
|
||||
return false;
|
||||
|
||||
const duint expectedStart = evalExpr(argv[1]);
|
||||
const duint expectedSize = evalExpr(argv[2]);
|
||||
if(!_plugin_testassert(expectedStart != 0, "failed to resolve target expression '%s'", argv[1]))
|
||||
return false;
|
||||
if(!_plugin_testassert(expectedSize != 0, "failed to resolve size expression '%s'", argv[2]))
|
||||
return false;
|
||||
|
||||
duint actualSize = 0;
|
||||
if(!_plugin_testassert(findBpByStart(expectedStart, actualSize), "failed to find memory breakpoint starting at 0x%llX", static_cast<unsigned long long>(expectedStart)))
|
||||
return false;
|
||||
|
||||
return _plugin_testassert(
|
||||
actualSize == expectedSize,
|
||||
"expected exact breakpoint [%0llX, 0x%llX), got [0x%llX, 0x%llX)",
|
||||
static_cast<unsigned long long>(expectedStart),
|
||||
static_cast<unsigned long long>(expectedStart + expectedSize),
|
||||
static_cast<unsigned long long>(expectedStart),
|
||||
static_cast<unsigned long long>(expectedStart + actualSize)
|
||||
);
|
||||
}
|
||||
|
||||
bool cbCacheBp(int argc, char** argv)
|
||||
{
|
||||
if(argc < 2)
|
||||
return false;
|
||||
const duint expectedBp = evalExpr(argv[1]);
|
||||
if(!_plugin_testassert(expectedBp != 0, "failed to resolve breakpoint expression '%s'", argv[1]))
|
||||
return false;
|
||||
gCachedBpAddr = expectedBp;
|
||||
return _plugin_testassert(true, "cached breakpoint address 0x%llX", static_cast<unsigned long long>(expectedBp));
|
||||
}
|
||||
|
||||
bool assertExpectedHit(duint expectedBp, bool requireProcessAlive)
|
||||
{
|
||||
if(!_plugin_testassert(expectedBp != 0, "failed to resolve expected breakpoint address"))
|
||||
return false;
|
||||
if(!_plugin_testassert(gMemoryHitCount.load() == 1, "expected exactly 1 memory breakpoint callback, got %u", gMemoryHitCount.load()))
|
||||
return false;
|
||||
if(!_plugin_testassert(gLastBpAddr.load() == expectedBp, "expected memory breakpoint address 0x%llX, got 0x%llX", static_cast<unsigned long long>(expectedBp), static_cast<unsigned long long>(gLastBpAddr.load())))
|
||||
return false;
|
||||
if(!requireProcessAlive)
|
||||
return true;
|
||||
return _plugin_testassert(!gExitObserved.load(), "process exited before the memory breakpoint assertion, exitCode=%lu, lastCip=0x%llX", gExitCode.load(), static_cast<unsigned long long>(gLastCip.load()));
|
||||
}
|
||||
|
||||
bool cbAssertHit(int argc, char** argv)
|
||||
{
|
||||
duint expectedBp = 0;
|
||||
if(argc >= 2)
|
||||
expectedBp = evalExpr(argv[1]);
|
||||
if(expectedBp == 0)
|
||||
expectedBp = gCachedBpAddr.load();
|
||||
return assertExpectedHit(expectedBp, true);
|
||||
}
|
||||
|
||||
bool cbAssertNoHit(int argc, char** argv)
|
||||
{
|
||||
if(argc < 2)
|
||||
return false;
|
||||
|
||||
const duint expectedExitCode = evalExpr(argv[1]);
|
||||
if(!_plugin_testassert(gMemoryHitCount.load() == 0, "expected no memory breakpoint callbacks, got %u", gMemoryHitCount.load()))
|
||||
return false;
|
||||
if(!_plugin_testassert(gExitObserved.load(), "expected process exit to be observed"))
|
||||
return false;
|
||||
return _plugin_testassert(gExitCode.load() == expectedExitCode, "expected exit code 0x%llX, got 0x%lX", static_cast<unsigned long long>(expectedExitCode), gExitCode.load());
|
||||
}
|
||||
|
||||
bool cbExpectExitHit(int argc, char** argv)
|
||||
{
|
||||
duint expectedBp = 0;
|
||||
if(argc >= 2)
|
||||
expectedBp = evalExpr(argv[1]);
|
||||
if(expectedBp == 0)
|
||||
expectedBp = gCachedBpAddr.load();
|
||||
if(!_plugin_testassert(expectedBp != 0, "failed to resolve expected exit breakpoint address"))
|
||||
return false;
|
||||
gExpectedExitBpAddr = expectedBp;
|
||||
gExpectExitHit = true;
|
||||
return _plugin_testassert(true, "expecting exit-time memory breakpoint assertion for 0x%llX", static_cast<unsigned long long>(expectedBp));
|
||||
}
|
||||
|
||||
bool cbReenable(int argc, char** argv)
|
||||
{
|
||||
if(argc < 2)
|
||||
return false;
|
||||
const auto disableCommand = std::string("membpd ") + argv[1];
|
||||
const auto enableCommand = std::string("membpe ") + argv[1];
|
||||
if(!_plugin_testassert(DbgCmdExecDirect(disableCommand.c_str()), "membpd failed for %s", argv[1]))
|
||||
return false;
|
||||
return _plugin_testassert(DbgCmdExecDirect(enableCommand.c_str()), "membpe failed for %s", argv[1]);
|
||||
}
|
||||
}
|
||||
|
||||
extern "C" __declspec(dllexport) bool pluginit(PLUG_INITSTRUCT* initStruct)
|
||||
{
|
||||
initStruct->pluginVersion = 1;
|
||||
initStruct->sdkVersion = PLUG_SDKVERSION;
|
||||
strncpy_s(initStruct->pluginName, sizeof(initStruct->pluginName), "MembpRegression", _TRUNCATE);
|
||||
gPluginHandle = initStruct->pluginHandle;
|
||||
_plugin_registercallback(gPluginHandle, CB_INITDEBUG, cbPlugin);
|
||||
_plugin_registercallback(gPluginHandle, CB_BREAKPOINT, cbPlugin);
|
||||
_plugin_registercallback(gPluginHandle, CB_EXITPROCESS, cbPlugin);
|
||||
_plugin_registercommand(gPluginHandle, "mbreset", cbReset, false);
|
||||
_plugin_registercommand(gPluginHandle, "mbassertregion", cbAssertRegion, false);
|
||||
_plugin_registercommand(gPluginHandle, "mbassertexact", cbAssertExact, false);
|
||||
_plugin_registercommand(gPluginHandle, "mbcachebp", cbCacheBp, false);
|
||||
_plugin_registercommand(gPluginHandle, "mbasserthit", cbAssertHit, false);
|
||||
_plugin_registercommand(gPluginHandle, "mbassertnohit", cbAssertNoHit, false);
|
||||
_plugin_registercommand(gPluginHandle, "mbexpectexithit", cbExpectExitHit, false);
|
||||
_plugin_registercommand(gPluginHandle, "mbreenable", cbReenable, false);
|
||||
return true;
|
||||
}
|
||||
|
||||
extern "C" __declspec(dllexport) void plugstop()
|
||||
{
|
||||
}
|
||||
|
||||
extern "C" __declspec(dllexport) void plugsetup(PLUG_SETUPSTRUCT*)
|
||||
{
|
||||
}
|
||||
#include <Windows.h>
|
||||
|
||||
#include <atomic>
|
||||
#include <cstring>
|
||||
#include <string>
|
||||
|
||||
#include "_plugins.h"
|
||||
#include "bridgemain.h"
|
||||
|
||||
namespace
|
||||
{
|
||||
int gPluginHandle = 0;
|
||||
std::atomic<unsigned int> gMemoryHitCount{ 0 };
|
||||
std::atomic<duint> gLastBpAddr{ 0 };
|
||||
std::atomic<duint> gLastCip{ 0 };
|
||||
std::atomic<bool> gExitObserved{ false };
|
||||
std::atomic<unsigned long> gExitCode{ 0 };
|
||||
std::atomic<duint> gCachedBpAddr{ 0 };
|
||||
std::atomic<bool> gExpectExitHit{ false };
|
||||
std::atomic<duint> gExpectedExitBpAddr{ 0 };
|
||||
|
||||
void resetState()
|
||||
{
|
||||
gMemoryHitCount = 0;
|
||||
gLastBpAddr = 0;
|
||||
gLastCip = 0;
|
||||
gExitObserved = false;
|
||||
gExitCode = 0;
|
||||
gCachedBpAddr = 0;
|
||||
gExpectExitHit = false;
|
||||
gExpectedExitBpAddr = 0;
|
||||
}
|
||||
|
||||
duint evalExpr(const char* expr)
|
||||
{
|
||||
return expr ? DbgValFromString(expr) : 0;
|
||||
}
|
||||
|
||||
duint evalWrapped(const char* prefix, const char* expr, const char* suffix)
|
||||
{
|
||||
std::string wrapped = std::string(prefix) + expr + suffix;
|
||||
return DbgValFromString(wrapped.c_str());
|
||||
}
|
||||
|
||||
bool findBpByStart(duint expectedStart, duint & size)
|
||||
{
|
||||
BPMAP list{};
|
||||
if(DbgGetBpList(bp_memory, &list) == 0)
|
||||
return false;
|
||||
|
||||
bool found = false;
|
||||
for(int i = 0; i < list.count; i++)
|
||||
{
|
||||
const auto & bp = list.bp[i];
|
||||
if(bp.type != bp_memory)
|
||||
continue;
|
||||
if(bp.addr != expectedStart)
|
||||
continue;
|
||||
|
||||
size = DbgFunctions()->MemBpSize(bp.addr);
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
|
||||
BridgeFree(list.bp);
|
||||
return found;
|
||||
}
|
||||
|
||||
bool assertExpectedHit(duint expectedBp, bool requireProcessAlive, duint count);
|
||||
|
||||
void cbPlugin(CBTYPE cbType, void* callbackInfo)
|
||||
{
|
||||
if(cbType == CB_INITDEBUG)
|
||||
{
|
||||
resetState();
|
||||
return;
|
||||
}
|
||||
|
||||
if(cbType == CB_BREAKPOINT)
|
||||
{
|
||||
const auto info = static_cast<PLUG_CB_BREAKPOINT*>(callbackInfo);
|
||||
if(info && info->breakpoint && info->breakpoint->type == bp_memory)
|
||||
{
|
||||
gMemoryHitCount.fetch_add(1);
|
||||
gLastBpAddr = info->breakpoint->addr;
|
||||
gLastCip = DbgValFromString("cip");
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if(cbType == CB_EXITPROCESS)
|
||||
{
|
||||
const auto info = static_cast<PLUG_CB_EXITPROCESS*>(callbackInfo);
|
||||
if(info && info->ExitProcess)
|
||||
{
|
||||
gExitObserved = true;
|
||||
gExitCode = info->ExitProcess->dwExitCode;
|
||||
if(gExpectExitHit.load())
|
||||
assertExpectedHit(gExpectedExitBpAddr.load(), false, 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool cbReset(int, char**)
|
||||
{
|
||||
resetState();
|
||||
return _plugin_testassert(true, "state reset");
|
||||
}
|
||||
|
||||
bool cbAssertRegion(int argc, char** argv)
|
||||
{
|
||||
if(argc < 2)
|
||||
return false;
|
||||
|
||||
const duint target = evalExpr(argv[1]);
|
||||
if(!_plugin_testassert(target != 0, "failed to resolve target expression '%s'", argv[1]))
|
||||
return false;
|
||||
|
||||
const duint expectedStart = evalWrapped("mem.base(", argv[1], ")");
|
||||
const duint expectedSize = evalWrapped("mem.size(", argv[1], ")");
|
||||
if(!_plugin_testassert(expectedStart != 0, "failed to resolve mem.base(%s)", argv[1]))
|
||||
return false;
|
||||
if(!_plugin_testassert(expectedSize != 0, "failed to resolve mem.size(%s)", argv[1]))
|
||||
return false;
|
||||
|
||||
duint actualSize = 0;
|
||||
if(!_plugin_testassert(findBpByStart(expectedStart, actualSize), "failed to find memory breakpoint starting at 0x%llX", static_cast<unsigned long long>(expectedStart)))
|
||||
return false;
|
||||
|
||||
return _plugin_testassert(
|
||||
actualSize == expectedSize,
|
||||
"expected region breakpoint [%0llX, 0x%llX), got [0x%llX, 0x%llX) for target %s",
|
||||
static_cast<unsigned long long>(expectedStart),
|
||||
static_cast<unsigned long long>(expectedStart + expectedSize),
|
||||
static_cast<unsigned long long>(expectedStart),
|
||||
static_cast<unsigned long long>(expectedStart + actualSize),
|
||||
argv[1]
|
||||
);
|
||||
}
|
||||
|
||||
bool cbAssertExact(int argc, char** argv)
|
||||
{
|
||||
if(argc < 3)
|
||||
return false;
|
||||
|
||||
const duint expectedStart = evalExpr(argv[1]);
|
||||
const duint expectedSize = evalExpr(argv[2]);
|
||||
if(!_plugin_testassert(expectedStart != 0, "failed to resolve target expression '%s'", argv[1]))
|
||||
return false;
|
||||
if(!_plugin_testassert(expectedSize != 0, "failed to resolve size expression '%s'", argv[2]))
|
||||
return false;
|
||||
|
||||
duint actualSize = 0;
|
||||
if(!_plugin_testassert(findBpByStart(expectedStart, actualSize), "failed to find memory breakpoint starting at 0x%llX", static_cast<unsigned long long>(expectedStart)))
|
||||
return false;
|
||||
|
||||
return _plugin_testassert(
|
||||
actualSize == expectedSize,
|
||||
"expected exact breakpoint [%0llX, 0x%llX), got [0x%llX, 0x%llX)",
|
||||
static_cast<unsigned long long>(expectedStart),
|
||||
static_cast<unsigned long long>(expectedStart + expectedSize),
|
||||
static_cast<unsigned long long>(expectedStart),
|
||||
static_cast<unsigned long long>(expectedStart + actualSize)
|
||||
);
|
||||
}
|
||||
|
||||
bool cbCacheBp(int argc, char** argv)
|
||||
{
|
||||
if(argc < 2)
|
||||
return false;
|
||||
const duint expectedBp = evalExpr(argv[1]);
|
||||
if(!_plugin_testassert(expectedBp != 0, "failed to resolve breakpoint expression '%s'", argv[1]))
|
||||
return false;
|
||||
gCachedBpAddr = expectedBp;
|
||||
return _plugin_testassert(true, "cached breakpoint address 0x%llX", static_cast<unsigned long long>(expectedBp));
|
||||
}
|
||||
|
||||
bool assertExpectedHit(duint expectedBp, bool requireProcessAlive, duint count)
|
||||
{
|
||||
if(!_plugin_testassert(expectedBp != 0, "failed to resolve expected breakpoint address"))
|
||||
return false;
|
||||
if(!_plugin_testassert(gMemoryHitCount.load() == count, "expected exactly %lu memory breakpoint callback, got %u", count, gMemoryHitCount.load()))
|
||||
return false;
|
||||
if(!_plugin_testassert(gLastBpAddr.load() == expectedBp, "expected memory breakpoint address 0x%llX, got 0x%llX", static_cast<unsigned long long>(expectedBp), static_cast<unsigned long long>(gLastBpAddr.load())))
|
||||
return false;
|
||||
if(!requireProcessAlive)
|
||||
return true;
|
||||
return _plugin_testassert(!gExitObserved.load(), "process exited before the memory breakpoint assertion, exitCode=%lu, lastCip=0x%llX", gExitCode.load(), static_cast<unsigned long long>(gLastCip.load()));
|
||||
}
|
||||
|
||||
bool cbAssertHit(int argc, char** argv)
|
||||
{
|
||||
duint expectedBp = 0;
|
||||
duint count = 1;
|
||||
if(argc >= 2)
|
||||
expectedBp = evalExpr(argv[1]);
|
||||
if(argc >= 3)
|
||||
count = evalExpr(argv[2]);
|
||||
if(expectedBp == 0)
|
||||
expectedBp = gCachedBpAddr.load();
|
||||
return assertExpectedHit(expectedBp, true, count);
|
||||
}
|
||||
|
||||
bool cbAssertNoHit(int argc, char** argv)
|
||||
{
|
||||
if(argc < 2)
|
||||
return false;
|
||||
|
||||
const duint expectedExitCode = evalExpr(argv[1]);
|
||||
if(!_plugin_testassert(gMemoryHitCount.load() == 0, "expected no memory breakpoint callbacks, got %u", gMemoryHitCount.load()))
|
||||
return false;
|
||||
if(!_plugin_testassert(gExitObserved.load(), "expected process exit to be observed"))
|
||||
return false;
|
||||
return _plugin_testassert(gExitCode.load() == expectedExitCode, "expected exit code 0x%llX, got 0x%lX", static_cast<unsigned long long>(expectedExitCode), gExitCode.load());
|
||||
}
|
||||
|
||||
bool cbExpectExitHit(int argc, char** argv)
|
||||
{
|
||||
duint expectedBp = 0;
|
||||
if(argc >= 2)
|
||||
expectedBp = evalExpr(argv[1]);
|
||||
if(expectedBp == 0)
|
||||
expectedBp = gCachedBpAddr.load();
|
||||
if(!_plugin_testassert(expectedBp != 0, "failed to resolve expected exit breakpoint address"))
|
||||
return false;
|
||||
gExpectedExitBpAddr = expectedBp;
|
||||
gExpectExitHit = true;
|
||||
return _plugin_testassert(true, "expecting exit-time memory breakpoint assertion for 0x%llX", static_cast<unsigned long long>(expectedBp));
|
||||
}
|
||||
|
||||
bool cbReenable(int argc, char** argv)
|
||||
{
|
||||
if(argc < 2)
|
||||
return false;
|
||||
const auto disableCommand = std::string("membpd ") + argv[1];
|
||||
const auto enableCommand = std::string("membpe ") + argv[1];
|
||||
if(!_plugin_testassert(DbgCmdExecDirect(disableCommand.c_str()), "membpd failed for %s", argv[1]))
|
||||
return false;
|
||||
return _plugin_testassert(DbgCmdExecDirect(enableCommand.c_str()), "membpe failed for %s", argv[1]);
|
||||
}
|
||||
}
|
||||
|
||||
extern "C" __declspec(dllexport) bool pluginit(PLUG_INITSTRUCT* initStruct)
|
||||
{
|
||||
initStruct->pluginVersion = 1;
|
||||
initStruct->sdkVersion = PLUG_SDKVERSION;
|
||||
strncpy_s(initStruct->pluginName, sizeof(initStruct->pluginName), "MembpRegression", _TRUNCATE);
|
||||
gPluginHandle = initStruct->pluginHandle;
|
||||
_plugin_registercallback(gPluginHandle, CB_INITDEBUG, cbPlugin);
|
||||
_plugin_registercallback(gPluginHandle, CB_BREAKPOINT, cbPlugin);
|
||||
_plugin_registercallback(gPluginHandle, CB_EXITPROCESS, cbPlugin);
|
||||
_plugin_registercommand(gPluginHandle, "mbreset", cbReset, false);
|
||||
_plugin_registercommand(gPluginHandle, "mbassertregion", cbAssertRegion, false);
|
||||
_plugin_registercommand(gPluginHandle, "mbassertexact", cbAssertExact, false);
|
||||
_plugin_registercommand(gPluginHandle, "mbcachebp", cbCacheBp, false);
|
||||
_plugin_registercommand(gPluginHandle, "mbasserthit", cbAssertHit, false);
|
||||
_plugin_registercommand(gPluginHandle, "mbassertnohit", cbAssertNoHit, false);
|
||||
_plugin_registercommand(gPluginHandle, "mbexpectexithit", cbExpectExitHit, false);
|
||||
_plugin_registercommand(gPluginHandle, "mbreenable", cbReenable, false);
|
||||
return true;
|
||||
}
|
||||
|
||||
extern "C" __declspec(dllexport) void plugstop()
|
||||
{
|
||||
}
|
||||
|
||||
extern "C" __declspec(dllexport) void plugsetup(PLUG_SETUPSTRUCT*)
|
||||
{
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,35 +1,51 @@
|
|||
#include <Windows.h>
|
||||
|
||||
#pragma section(".mbd2", read, write)
|
||||
#pragma section(".mbd3", read, write)
|
||||
|
||||
extern "C"
|
||||
{
|
||||
__declspec(allocate(".mbd2")) volatile unsigned char Padding[0x2000] = { 2 };
|
||||
__declspec(allocate(".mbd3")) __declspec(dllexport) volatile signed char ReadTarget = -1;
|
||||
__declspec(allocate(".mbd3")) __declspec(dllexport) volatile signed char WriteTarget = 0;
|
||||
|
||||
#pragma code_seg(push, membp_code, ".mbc")
|
||||
__declspec(dllexport) __declspec(noinline) void ReadSequence()
|
||||
{
|
||||
const auto value = ReadTarget;
|
||||
ExitProcess(static_cast<UINT>(static_cast<unsigned char>(value)));
|
||||
}
|
||||
|
||||
__declspec(dllexport) __declspec(noinline) void WriteSequence()
|
||||
{
|
||||
WriteTarget = 0x5A;
|
||||
ExitProcess(0x5A);
|
||||
}
|
||||
|
||||
__declspec(dllexport) __declspec(noinline) void ExecSequence()
|
||||
{
|
||||
ExitProcess(0x33);
|
||||
}
|
||||
|
||||
__declspec(noinline) void start()
|
||||
{
|
||||
ExitProcess(0);
|
||||
}
|
||||
#pragma code_seg(pop, membp_code)
|
||||
}
|
||||
#include <Windows.h>
|
||||
|
||||
#pragma section(".mbd2", read, write)
|
||||
#pragma section(".mbd3", read, write)
|
||||
|
||||
extern "C"
|
||||
{
|
||||
__declspec(allocate(".mbd2")) volatile unsigned char Padding[0x2000] = { 2 };
|
||||
__declspec(allocate(".mbd3")) __declspec(dllexport) volatile signed char ReadTarget = -1;
|
||||
__declspec(allocate(".mbd3")) __declspec(dllexport) volatile signed char WriteTarget = 0;
|
||||
__declspec(allocate(".mbd3")) __declspec(dllexport) volatile unsigned char* HeapPointer = nullptr;
|
||||
|
||||
#pragma code_seg(push, membp_code, ".mbc")
|
||||
__declspec(dllexport) __declspec(noinline) void ReadSequence()
|
||||
{
|
||||
const auto value = ReadTarget;
|
||||
ExitProcess(static_cast<UINT>(static_cast<unsigned char>(value)));
|
||||
}
|
||||
|
||||
__declspec(dllexport) __declspec(noinline) void WriteSequence()
|
||||
{
|
||||
WriteTarget = 0x5A;
|
||||
ExitProcess(0x5A);
|
||||
}
|
||||
|
||||
__declspec(dllexport) __declspec(noinline) void ExecSequence()
|
||||
{
|
||||
ExitProcess(0x33);
|
||||
}
|
||||
|
||||
__declspec(noinline) void start()
|
||||
{
|
||||
ExitProcess(0);
|
||||
}
|
||||
|
||||
__declspec(dllexport) __declspec(noinline) void WriteHeap()
|
||||
{
|
||||
*HeapPointer = 0xBC;
|
||||
|
||||
ExitProcess(static_cast<UINT>(*HeapPointer));
|
||||
}
|
||||
|
||||
__declspec(dllexport) __declspec(noinline) void StartHeap()
|
||||
{
|
||||
HANDLE hHeap = GetProcessHeap();
|
||||
HeapPointer = (unsigned char*)HeapAlloc(hHeap, HEAP_ZERO_MEMORY, 1024);
|
||||
|
||||
WriteHeap();
|
||||
}
|
||||
#pragma code_seg(pop, membp_code)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
settingset Events, EntryBreakpoint, 0
|
||||
settingset Events, EntryBreakpoint, 1
|
||||
init tests/membp.exe
|
||||
run
|
||||
mbreset
|
||||
cip=membp:WriteSequence
|
||||
bpmrange membp:WriteTarget, 1, w
|
||||
|
|
|
|||
|
|
@ -0,0 +1,15 @@
|
|||
settingset Events, EntryBreakpoint, 1
|
||||
init tests/membp.exe
|
||||
run
|
||||
mbreset
|
||||
cip=membp:StartHeap
|
||||
bpmrange membp:WriteHeap, 1, x
|
||||
run
|
||||
bpmc membp:WriteHeap
|
||||
push r12
|
||||
mov r12, [membp:HeapPointer]
|
||||
bpmrange r12, 1, a
|
||||
run
|
||||
mbasserthit r12, 2
|
||||
pop r12
|
||||
stop
|
||||
|
|
@ -1,5 +1,6 @@
|
|||
settingset Events, EntryBreakpoint, 0
|
||||
settingset Events, EntryBreakpoint, 1
|
||||
init tests/membp.exe
|
||||
run
|
||||
mbreset
|
||||
mbcachebp membp:WriteTarget
|
||||
cip=membp:WriteSequence
|
||||
|
|
@ -7,3 +8,4 @@ bpmrange membp:WriteTarget, 1, w
|
|||
mbassertexact membp:WriteTarget, 1
|
||||
run
|
||||
mbasserthit
|
||||
stop
|
||||
|
|
@ -1,7 +1,7 @@
|
|||
#include <windows.h>
|
||||
|
||||
int main()
|
||||
{
|
||||
Sleep(10);
|
||||
return 0;
|
||||
}
|
||||
#include <windows.h>
|
||||
|
||||
int main()
|
||||
{
|
||||
Sleep(10);
|
||||
return 0;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,83 +1,83 @@
|
|||
#include <Windows.h>
|
||||
|
||||
#include <atomic>
|
||||
#include <cstring>
|
||||
#include <string>
|
||||
|
||||
#include "_plugins.h"
|
||||
#include "bridgemain.h"
|
||||
|
||||
namespace
|
||||
{
|
||||
int gPluginHandle = 0;
|
||||
std::atomic<unsigned int> gOuterDispatchCount{ 0 };
|
||||
std::atomic<unsigned int> gInnerDispatchCount{ 0 };
|
||||
|
||||
bool pluginDirectModeEnabled()
|
||||
{
|
||||
return DbgValFromString("$plugin_direct_mode") != 0;
|
||||
}
|
||||
|
||||
duint resolveAddress(const char* expression)
|
||||
{
|
||||
return DbgValFromString(expression);
|
||||
}
|
||||
|
||||
bool dispatchScriptCallback(const char* label, std::atomic<unsigned int>& counter)
|
||||
{
|
||||
counter.fetch_add(1);
|
||||
const auto command = std::string("scriptcmd call ") + label;
|
||||
return _plugin_testassert(DbgCmdExecDirect(command.c_str()), "DbgCmdExecDirect failed for %s", label);
|
||||
}
|
||||
|
||||
void cbPlugin(CBTYPE cbType, void* callbackInfo)
|
||||
{
|
||||
if(cbType == CB_INITDEBUG)
|
||||
{
|
||||
gOuterDispatchCount = 0;
|
||||
gInnerDispatchCount = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
if(cbType != CB_BREAKPOINT || !pluginDirectModeEnabled())
|
||||
return;
|
||||
|
||||
const auto info = static_cast<PLUG_CB_BREAKPOINT*>(callbackInfo);
|
||||
if(info == nullptr || info->breakpoint == nullptr || info->breakpoint->type != bp_normal)
|
||||
return;
|
||||
|
||||
const auto outerAddress = resolveAddress("scriptcmd_call:OuterHit");
|
||||
const auto innerAddress = resolveAddress("scriptcmd_call:InnerHit");
|
||||
if(info->breakpoint->addr == outerAddress)
|
||||
dispatchScriptCallback("onouter", gOuterDispatchCount);
|
||||
else if(info->breakpoint->addr == innerAddress)
|
||||
dispatchScriptCallback("oninner", gInnerDispatchCount);
|
||||
}
|
||||
|
||||
bool cbPluginDirectAssert(int, char**)
|
||||
{
|
||||
if(!_plugin_testassert(gOuterDispatchCount.load() == 1, "expected exactly 1 plugin-direct outer dispatch, got %u", gOuterDispatchCount.load()))
|
||||
return false;
|
||||
return _plugin_testassert(gInnerDispatchCount.load() == 1, "expected exactly 1 plugin-direct inner dispatch, got %u", gInnerDispatchCount.load());
|
||||
}
|
||||
}
|
||||
|
||||
extern "C" __declspec(dllexport) bool pluginit(PLUG_INITSTRUCT* initStruct)
|
||||
{
|
||||
initStruct->pluginVersion = 1;
|
||||
initStruct->sdkVersion = PLUG_SDKVERSION;
|
||||
strncpy_s(initStruct->pluginName, sizeof(initStruct->pluginName), "ScriptCmdCallPlugin", _TRUNCATE);
|
||||
gPluginHandle = initStruct->pluginHandle;
|
||||
_plugin_registercallback(gPluginHandle, CB_INITDEBUG, cbPlugin);
|
||||
_plugin_registercallback(gPluginHandle, CB_BREAKPOINT, cbPlugin);
|
||||
_plugin_registercommand(gPluginHandle, "plugindirectassert", cbPluginDirectAssert, false);
|
||||
return true;
|
||||
}
|
||||
|
||||
extern "C" __declspec(dllexport) void plugstop()
|
||||
{
|
||||
}
|
||||
|
||||
extern "C" __declspec(dllexport) void plugsetup(PLUG_SETUPSTRUCT*)
|
||||
{
|
||||
}
|
||||
#include <Windows.h>
|
||||
|
||||
#include <atomic>
|
||||
#include <cstring>
|
||||
#include <string>
|
||||
|
||||
#include "_plugins.h"
|
||||
#include "bridgemain.h"
|
||||
|
||||
namespace
|
||||
{
|
||||
int gPluginHandle = 0;
|
||||
std::atomic<unsigned int> gOuterDispatchCount{ 0 };
|
||||
std::atomic<unsigned int> gInnerDispatchCount{ 0 };
|
||||
|
||||
bool pluginDirectModeEnabled()
|
||||
{
|
||||
return DbgValFromString("$plugin_direct_mode") != 0;
|
||||
}
|
||||
|
||||
duint resolveAddress(const char* expression)
|
||||
{
|
||||
return DbgValFromString(expression);
|
||||
}
|
||||
|
||||
bool dispatchScriptCallback(const char* label, std::atomic<unsigned int> & counter)
|
||||
{
|
||||
counter.fetch_add(1);
|
||||
const auto command = std::string("scriptcmd call ") + label;
|
||||
return _plugin_testassert(DbgCmdExecDirect(command.c_str()), "DbgCmdExecDirect failed for %s", label);
|
||||
}
|
||||
|
||||
void cbPlugin(CBTYPE cbType, void* callbackInfo)
|
||||
{
|
||||
if(cbType == CB_INITDEBUG)
|
||||
{
|
||||
gOuterDispatchCount = 0;
|
||||
gInnerDispatchCount = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
if(cbType != CB_BREAKPOINT || !pluginDirectModeEnabled())
|
||||
return;
|
||||
|
||||
const auto info = static_cast<PLUG_CB_BREAKPOINT*>(callbackInfo);
|
||||
if(info == nullptr || info->breakpoint == nullptr || info->breakpoint->type != bp_normal)
|
||||
return;
|
||||
|
||||
const auto outerAddress = resolveAddress("scriptcmd_call:OuterHit");
|
||||
const auto innerAddress = resolveAddress("scriptcmd_call:InnerHit");
|
||||
if(info->breakpoint->addr == outerAddress)
|
||||
dispatchScriptCallback("onouter", gOuterDispatchCount);
|
||||
else if(info->breakpoint->addr == innerAddress)
|
||||
dispatchScriptCallback("oninner", gInnerDispatchCount);
|
||||
}
|
||||
|
||||
bool cbPluginDirectAssert(int, char**)
|
||||
{
|
||||
if(!_plugin_testassert(gOuterDispatchCount.load() == 1, "expected exactly 1 plugin-direct outer dispatch, got %u", gOuterDispatchCount.load()))
|
||||
return false;
|
||||
return _plugin_testassert(gInnerDispatchCount.load() == 1, "expected exactly 1 plugin-direct inner dispatch, got %u", gInnerDispatchCount.load());
|
||||
}
|
||||
}
|
||||
|
||||
extern "C" __declspec(dllexport) bool pluginit(PLUG_INITSTRUCT* initStruct)
|
||||
{
|
||||
initStruct->pluginVersion = 1;
|
||||
initStruct->sdkVersion = PLUG_SDKVERSION;
|
||||
strncpy_s(initStruct->pluginName, sizeof(initStruct->pluginName), "ScriptCmdCallPlugin", _TRUNCATE);
|
||||
gPluginHandle = initStruct->pluginHandle;
|
||||
_plugin_registercallback(gPluginHandle, CB_INITDEBUG, cbPlugin);
|
||||
_plugin_registercallback(gPluginHandle, CB_BREAKPOINT, cbPlugin);
|
||||
_plugin_registercommand(gPluginHandle, "plugindirectassert", cbPluginDirectAssert, false);
|
||||
return true;
|
||||
}
|
||||
|
||||
extern "C" __declspec(dllexport) void plugstop()
|
||||
{
|
||||
}
|
||||
|
||||
extern "C" __declspec(dllexport) void plugsetup(PLUG_SETUPSTRUCT*)
|
||||
{
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,20 +1,20 @@
|
|||
#include <windows.h>
|
||||
|
||||
static volatile int gValue = 0;
|
||||
|
||||
extern "C" __declspec(dllexport) __declspec(noinline) void OuterHit()
|
||||
{
|
||||
gValue += 1;
|
||||
}
|
||||
|
||||
extern "C" __declspec(dllexport) __declspec(noinline) void InnerHit()
|
||||
{
|
||||
gValue += 2;
|
||||
}
|
||||
|
||||
int main()
|
||||
{
|
||||
OuterHit();
|
||||
InnerHit();
|
||||
return gValue == 3 ? 0 : 1;
|
||||
}
|
||||
#include <windows.h>
|
||||
|
||||
static volatile int gValue = 0;
|
||||
|
||||
extern "C" __declspec(dllexport) __declspec(noinline) void OuterHit()
|
||||
{
|
||||
gValue += 1;
|
||||
}
|
||||
|
||||
extern "C" __declspec(dllexport) __declspec(noinline) void InnerHit()
|
||||
{
|
||||
gValue += 2;
|
||||
}
|
||||
|
||||
int main()
|
||||
{
|
||||
OuterHit();
|
||||
InnerHit();
|
||||
return gValue == 3 ? 0 : 1;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,44 +1,44 @@
|
|||
#include <windows.h>
|
||||
|
||||
static HANDLE gGoEvent = nullptr;
|
||||
static volatile LONG gMainCounter = 0;
|
||||
static volatile LONG gWorkerCounter = 0;
|
||||
|
||||
extern "C" __declspec(dllexport) __declspec(noinline) void MainThreadOuterHit()
|
||||
{
|
||||
InterlockedIncrement(&gMainCounter);
|
||||
}
|
||||
|
||||
extern "C" __declspec(dllexport) __declspec(noinline) void WorkerThreadInnerHit()
|
||||
{
|
||||
InterlockedExchangeAdd(&gWorkerCounter, 2);
|
||||
}
|
||||
|
||||
static DWORD WINAPI WorkerThreadProc(void*)
|
||||
{
|
||||
WaitForSingleObject(gGoEvent, INFINITE);
|
||||
WorkerThreadInnerHit();
|
||||
return 0;
|
||||
}
|
||||
|
||||
int main()
|
||||
{
|
||||
gGoEvent = CreateEventW(nullptr, TRUE, FALSE, nullptr);
|
||||
if(gGoEvent == nullptr)
|
||||
return 1;
|
||||
|
||||
auto thread = CreateThread(nullptr, 0, WorkerThreadProc, nullptr, 0, nullptr);
|
||||
if(thread == nullptr)
|
||||
{
|
||||
CloseHandle(gGoEvent);
|
||||
return 1;
|
||||
}
|
||||
|
||||
MainThreadOuterHit();
|
||||
SetEvent(gGoEvent);
|
||||
|
||||
WaitForSingleObject(thread, INFINITE);
|
||||
CloseHandle(thread);
|
||||
CloseHandle(gGoEvent);
|
||||
return 0;
|
||||
}
|
||||
#include <windows.h>
|
||||
|
||||
static HANDLE gGoEvent = nullptr;
|
||||
static volatile LONG gMainCounter = 0;
|
||||
static volatile LONG gWorkerCounter = 0;
|
||||
|
||||
extern "C" __declspec(dllexport) __declspec(noinline) void MainThreadOuterHit()
|
||||
{
|
||||
InterlockedIncrement(&gMainCounter);
|
||||
}
|
||||
|
||||
extern "C" __declspec(dllexport) __declspec(noinline) void WorkerThreadInnerHit()
|
||||
{
|
||||
InterlockedExchangeAdd(&gWorkerCounter, 2);
|
||||
}
|
||||
|
||||
static DWORD WINAPI WorkerThreadProc(void*)
|
||||
{
|
||||
WaitForSingleObject(gGoEvent, INFINITE);
|
||||
WorkerThreadInnerHit();
|
||||
return 0;
|
||||
}
|
||||
|
||||
int main()
|
||||
{
|
||||
gGoEvent = CreateEventW(nullptr, TRUE, FALSE, nullptr);
|
||||
if(gGoEvent == nullptr)
|
||||
return 1;
|
||||
|
||||
auto thread = CreateThread(nullptr, 0, WorkerThreadProc, nullptr, 0, nullptr);
|
||||
if(thread == nullptr)
|
||||
{
|
||||
CloseHandle(gGoEvent);
|
||||
return 1;
|
||||
}
|
||||
|
||||
MainThreadOuterHit();
|
||||
SetEvent(gGoEvent);
|
||||
|
||||
WaitForSingleObject(thread, INFINITE);
|
||||
CloseHandle(thread);
|
||||
CloseHandle(gGoEvent);
|
||||
return 0;
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue