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)
     {