vktrace: set PMB default to false.
Also: change segv handler in trace layer to not save
address list in order to avoid malloc calls.
Change-Id: I13911be7c4bc6f4293b35426f803e7540fbfa751
diff --git a/vktrace/src/vktrace_layer/vktrace_lib_trace.cpp b/vktrace/src/vktrace_layer/vktrace_lib_trace.cpp
index 9b8e726..bf24749 100644
--- a/vktrace/src/vktrace_layer/vktrace_lib_trace.cpp
+++ b/vktrace/src/vktrace_layer/vktrace_lib_trace.cpp
@@ -144,45 +144,26 @@
// small window between the reading of this file and when the dirty bits
// are cleared in which some other thread might modify mapped memory.
// We trap these memory writes by setting mapped memory pages to read-only and
-// setting the signal handler for SIGSEGV. The signal handler creates a list
-// of the addresses that caused a SIGSEGV. We keep the list in sighAddrList.
-// We used the semaphore sighAddrListSem to protect access to this list.
+// setting the signal handler for SIGSEGV. We keep retrying the dirty
+// page query and clear until the signal handler is not called.
-static std::list<void *> sighAddrList;
-static vktrace_sem_id sighAddrListSem = NULL;
+volatile static bool sigSegvOccurred;
static void segvHandler(int sig, siginfo_t *si, void *ununsed)
{
size_t pageSize = getpagesize();
- // Note: we use sem_wait and mprotect inside a signal handler.
- // This is not POSIX compliant, but works on Linux.
+ // TODO: Only set sigSegvOcccured if this page has not previously marked
+ // dirty.
- vktrace_sem_wait(sighAddrListSem);
+ // TODO: Check to see if address that caused sigsegv is in the mapped memory
+ // range. If it isn't, pass signal on to original sigsegv handler.
- if (!sighAddrList.empty() && sighAddrList.front() == si->si_addr)
- {
- // SIGSEGV was generated on the same address twice in a row.
- // This could happen if an attempt is made to execute an
- // instruction at an address without execute permission, i.e. a
- // bad function pointer. This could also happen if two threads
- // attempt to write to the same mapped address at the same
- // time. This is a race condition that an app should avoid,
- // so we'll consider it a fatal error.
- VKTRACE_FATAL_ERROR("SIGSEGV repeated on identical addresses.");
- }
-
- // Add the addr that caused the segv to sighAddrList.
- // It may not be in a mapped page we are tracking. If it
- // isn't, we'll detect it later and generate an error then.
- sighAddrList.emplace_front(si->si_addr);
-
- vktrace_sem_post(sighAddrListSem);
+ sigSegvOccurred = true;
// Change protection of this page to allow the write to proceed
if (0 != mprotect((void *)((uint64_t)si->si_addr & ~(pageSize-1)), pageSize, PROT_READ|PROT_WRITE))
- // If we're calling VKTRACE_FATAL_ERROR here, there's a bug in the trace layer.
- // Calling VKTRACE_FATAL_ERROR involves potentially doing a malloc, writing to the
+ // Calling VKTRACE_FATAL_ERROR here involves potentially doing a malloc, writing to the
// trace file, and writing to stdout -- operations that may not work from a
// signal handler. But we're about to exit anyway.
VKTRACE_FATAL_ERROR("mprotect sys call failed.");
@@ -206,125 +187,119 @@
size_t pageSize = getpagesize();
size_t readLen;
VKAllocInfo *pEntry;
- struct sigaction sigAction;
+ struct sigaction sigAction, sigActionOld;
static int pmFd = -1;
static std::vector<uint64_t> pageEntries;
-
+
// If pageguard isn't enabled, we don't need to do anythhing
if (!getPageGuardEnableFlag())
return;
vktrace_enter_critical_section(&g_memInfoLock);
- // Open pagefile, initialize sighAddrList semaphore, and set the SIGSEGV signal handler
+ // Open pagefile
if (pmFd == -1)
{
pmFd = open("/proc/self/pagemap", O_RDONLY);
if (pmFd < 0)
VKTRACE_FATAL_ERROR("Failed to open pagemap file.");
-
- if (!vktrace_sem_create(&sighAddrListSem, 1))
- VKTRACE_FATAL_ERROR("Failed to create sighAddrListSem.");
-
- sigAction.sa_sigaction = segvHandler;
- sigfillset(&sigAction.sa_mask);
- sigAction.sa_flags = SA_SIGINFO;
- if (0 != sigaction(SIGSEGV, &sigAction, NULL))
- VKTRACE_FATAL_ERROR("sigaction sys call failed.");
}
- // Iterate through all mapped memory allocations.
- // Check dirty bits of each page in each mapped memory allocation.
- for (std::unordered_map< VkDeviceMemory, PageGuardMappedMemory >::iterator it = getPageGuardControlInstance().getMapMemory().begin();
- it != getPageGuardControlInstance().getMapMemory().end();
- it++)
- {
- pMappedMem = &(it->second);
- mappedMemory = pMappedMem->getMappedMemory();
- pEntry=find_mem_info_entry(mappedMemory);
- addr=pEntry->pData;
- if (!addr)
- continue;
- alignedAddrStart = (PBYTE)((uint64_t)addr & ~(pageSize-1));
- alignedAddrEnd = (PBYTE)(((uint64_t)addr + pEntry->rangeSize + pageSize - 1) & ~(pageSize-1));
- nPages = (alignedAddrEnd - alignedAddrStart ) / pageSize;
+ // Set SIGSEGV handler
+ sigAction.sa_sigaction = segvHandler;
+ sigfillset(&sigAction.sa_mask);
+ sigAction.sa_flags = SA_SIGINFO;
+ if (0 != sigaction(SIGSEGV, &sigAction, &sigActionOld))
+ VKTRACE_FATAL_ERROR("sigaction sys call failed.");
- // Make pages in this memory allocation non-writable so we get a SIGSEGV
- // if some other thread writes to this memory while we are examining
- // and clearing its dirty bit.
- if (0 != mprotect(alignedAddrStart, (size_t)(alignedAddrEnd - alignedAddrStart), PROT_READ))
- VKTRACE_FATAL_ERROR("mprotect sys call failed.");
+ // Determine which mapped memory pages changed
+ do {
- // Read all the pagemap entries for this mapped memory allocation
- if (pageEntries.size() < nPages)
- pageEntries.resize(nPages);
- pmOffset = (off_t)((uint64_t)alignedAddrStart/(uint64_t)pageSize) * 8;
- lseek(pmFd, pmOffset, SEEK_SET);
- readLen = read(pmFd, &pageEntries[0], 8*nPages);
- assert(readLen==8*nPages);
- if (readLen != 8*nPages)
- VKTRACE_FATAL_ERROR("Failed to read from pagemap file.");
+ // Keep querying until no changes to mapped memory
+ // are detected during this loop. We do this because
+ // some other thread may write to a mapped page
+ // between when we read pagemaps and clear the dirty
+ // bits using a write to clear_refs. If a signal
+ // occurs, somebody modified mapped memory, so we'll
+ // keep trying until we are sure nobody modified
+ // mapped memory.
+ sigSegvOccurred = false;
- // Examine the dirty bits in each pagemap entry
- addr = alignedAddrStart;
- for (uint64_t i=0; i<nPages; i++)
- {
- if ((pageEntries[i]&((uint64_t)1<<55)) != 0)
- {
- index = pMappedMem->getIndexOfChangedBlockByAddr(addr);
- if (index >= 0)
- pMappedMem->setMappedBlockChanged(index, true, BLOCK_FLAG_ARRAY_CHANGED);
- }
- addr += pageSize;
- }
- }
-
- // Clear all dirty bits for this process
-#if !defined(ANDROID)
- getPageGuardControlInstance().pageRefsDirtyClear();
-#endif
-
- // Re-enable write permission for all mapped memory
- for (std::unordered_map< VkDeviceMemory, PageGuardMappedMemory >::iterator it = getPageGuardControlInstance().getMapMemory().begin();
- it != getPageGuardControlInstance().getMapMemory().end();
- it++)
- {
- pMappedMem = &(it->second);
- mappedMemory = pMappedMem->getMappedMemory();
- pEntry=find_mem_info_entry(mappedMemory);
- addr=pEntry->pData;
- if (!addr)
- continue;
- alignedAddrStart = (PBYTE)((uint64_t)addr & ~(pageSize-1));
- alignedAddrEnd = (PBYTE)(((uint64_t)addr + pEntry->rangeSize + pageSize - 1) & ~(pageSize-1));
- if (0 != mprotect(alignedAddrStart, (size_t)(alignedAddrEnd - alignedAddrStart), PROT_READ|PROT_WRITE))
- VKTRACE_FATAL_ERROR("mprotect sys call failed.");
- }
-
- // Loop through addresses that caused a segv and mark those pages dirty
- vktrace_sem_wait(sighAddrListSem);
- while (!sighAddrList.empty())
- {
- addr = (PBYTE)sighAddrList.front();
- bool foundIt=false;
+ // Iterate through all mapped memory allocations.
+ // Check dirty bits of each page in each mapped memory allocation.
for (std::unordered_map< VkDeviceMemory, PageGuardMappedMemory >::iterator it = getPageGuardControlInstance().getMapMemory().begin();
it != getPageGuardControlInstance().getMapMemory().end();
it++)
{
pMappedMem = &(it->second);
- index = pMappedMem->getIndexOfChangedBlockByAddr(addr);
- if (index >= 0)
+ mappedMemory = pMappedMem->getMappedMemory();
+ pEntry=find_mem_info_entry(mappedMemory);
+ addr=pEntry->pData;
+ if (!addr)
+ continue;
+ alignedAddrStart = (PBYTE)((uint64_t)addr & ~(pageSize-1));
+ alignedAddrEnd = (PBYTE)(((uint64_t)addr + pEntry->rangeSize + pageSize - 1) & ~(pageSize-1));
+ nPages = (alignedAddrEnd - alignedAddrStart ) / pageSize;
+
+ // Make pages in this memory allocation non-writable so we get a SIGSEGV
+ // if some other thread writes to this memory while we are examining
+ // and clearing its dirty bit.
+ if (0 != mprotect(alignedAddrStart, (size_t)(alignedAddrEnd - alignedAddrStart), PROT_READ))
+ VKTRACE_FATAL_ERROR("mprotect sys call failed.");
+
+ // Read all the pagemap entries for this mapped memory allocation
+ if (pageEntries.size() < nPages)
+ pageEntries.resize(nPages);
+ pmOffset = (off_t)((uint64_t)alignedAddrStart/(uint64_t)pageSize) * 8;
+ lseek(pmFd, pmOffset, SEEK_SET);
+ readLen = read(pmFd, &pageEntries[0], 8*nPages);
+ assert(readLen==8*nPages);
+ if (readLen != 8*nPages)
+ VKTRACE_FATAL_ERROR("Failed to read from pagemap file.");
+
+ // Examine the dirty bits in each pagemap entry
+ addr = alignedAddrStart;
+ for (uint64_t i=0; i<nPages; i++)
{
- pMappedMem->setMappedBlockChanged(index, true, BLOCK_FLAG_ARRAY_CHANGED);
- foundIt=true;
- break;
+ if ((pageEntries[i]&((uint64_t)1<<55)) != 0)
+ {
+ index = pMappedMem->getIndexOfChangedBlockByAddr(addr);
+ if (index >= 0)
+ pMappedMem->setMappedBlockChanged(index, true, BLOCK_FLAG_ARRAY_CHANGED);
+ }
+ addr += pageSize;
}
}
- if (!foundIt)
- VKTRACE_FATAL_ERROR("Received signal SIGSEGV on non-mapped memory.");
- sighAddrList.pop_front();
- }
- vktrace_sem_post(sighAddrListSem);
+
+ // Clear all dirty bits for this process
+ #if !defined(ANDROID)
+ getPageGuardControlInstance().pageRefsDirtyClear();
+ #endif
+
+
+ // Re-enable write permission for all mapped memory
+ for (std::unordered_map< VkDeviceMemory, PageGuardMappedMemory >::iterator it = getPageGuardControlInstance().getMapMemory().begin();
+ it != getPageGuardControlInstance().getMapMemory().end();
+ it++)
+ {
+ pMappedMem = &(it->second);
+ mappedMemory = pMappedMem->getMappedMemory();
+ pEntry=find_mem_info_entry(mappedMemory);
+ addr=pEntry->pData;
+ if (!addr)
+ continue;
+ alignedAddrStart = (PBYTE)((uint64_t)addr & ~(pageSize-1));
+ alignedAddrEnd = (PBYTE)(((uint64_t)addr + pEntry->rangeSize + pageSize - 1) & ~(pageSize-1));
+ if (0 != mprotect(alignedAddrStart, (size_t)(alignedAddrEnd - alignedAddrStart), PROT_READ|PROT_WRITE))
+ VKTRACE_FATAL_ERROR("mprotect sys call failed.");
+ }
+
+ } while (sigSegvOccurred);
+
+ // Restore SIGSEGV handler
+ if (0 != sigaction(SIGSEGV, &sigActionOld, &sigAction))
+ VKTRACE_FATAL_ERROR("sigaction restore sys call failed.");
+
vktrace_leave_critical_section(&g_memInfoLock);
}
#endif
diff --git a/vktrace/src/vktrace_trace/vktrace.cpp b/vktrace/src/vktrace_trace/vktrace.cpp
index f59bc96..22d9598 100644
--- a/vktrace/src/vktrace_trace/vktrace.cpp
+++ b/vktrace/src/vktrace_trace/vktrace.cpp
@@ -46,7 +46,7 @@
{ "o", "OutputTrace", VKTRACE_SETTING_STRING, { &g_settings.output_trace }, { &g_default_settings.output_trace }, TRUE, "Path to the generated output trace file."},
{ "s", "ScreenShot", VKTRACE_SETTING_STRING, { &g_settings.screenshotList }, { &g_default_settings.screenshotList }, TRUE, "Comma separated list of frames to take a snapshot of."},
{ "ptm", "PrintTraceMessages", VKTRACE_SETTING_BOOL, { &g_settings.print_trace_messages }, { &g_default_settings.print_trace_messages }, TRUE, "Print trace messages to vktrace console."},
- { "P", "PMB", VKTRACE_SETTING_BOOL, { &g_settings.enable_pmb }, { &g_default_settings.enable_pmb }, TRUE, "Optimize tracing of persistently mapped buffers, default is TRUE."},
+ { "P", "PMB", VKTRACE_SETTING_BOOL, { &g_settings.enable_pmb }, { &g_default_settings.enable_pmb }, TRUE, "Optimize tracing of persistently mapped buffers, default is FALSE."},
#if _DEBUG
{ "v", "Verbosity", VKTRACE_SETTING_STRING, { &g_settings.verbosity }, { &g_default_settings.verbosity }, TRUE, "Verbosity mode. Modes are \"quiet\", \"errors\", \"warnings\", \"full\", \"debug\"."},
#else
@@ -156,7 +156,7 @@
g_default_settings.output_trace = vktrace_allocate_and_copy("vktrace_out.vktrace");
g_default_settings.verbosity = "errors";
g_default_settings.screenshotList = NULL;
- g_default_settings.enable_pmb = true;;
+ g_default_settings.enable_pmb = false;;
if (vktrace_SettingGroup_init(&g_settingGroup, NULL, argc, argv, &g_settings.arguments) != 0)
{