i#2037: update memcache on re-takeover and query miss (#2870)

Adds a complete maps file walk to update the memcache on re-taking-over the
process for dr_api_start.  The memcache is cleared beforehand to avoid both
false positives and negatives in later queries.  This helps to solve issues
with a gap between dr_app_setup() and dr_app_start().

Does not update the executable areas or module list: they are more
difficult to re-walk, and existing lazy updates to those will suffice for
now, with a low risk of false positives.

Adds updating of the memcache on a query miss.  Previously we would just
continue to miss and walk the maps file every time.

Tested manually by disabling the i#2114 change so that a signal does a
query, adding signals to the burst_threads test, and calling dr_app_setup()
before creating the test's threads, causing queries to miss when delivering
signals.  It is difficult to create a regression test for this as the
consequences are performance degradations rather than correctness, and
these degradations only really show up at scale with hundreds of threads
whose missing stacks are queried at once with no caching.

Fixes #2037
This commit is contained in:
Derek Bruening
2018-03-07 16:38:06 -05:00
committed by GitHub
parent 5ed020292c
commit b06a702273
4 changed files with 42 additions and 22 deletions
+24 -14
View File
@@ -1,5 +1,5 @@
/* *******************************************************************************
* Copyright (c) 2010-2017 Google, Inc. All rights reserved.
* Copyright (c) 2010-2018 Google, Inc. All rights reserved.
* Copyright (c) 2011 Massachusetts Institute of Technology All rights reserved.
* Copyright (c) 2000-2010 VMware, Inc. All rights reserved.
* *******************************************************************************/
@@ -446,25 +446,18 @@ memcache_query_memory(const byte *pc, OUT dr_mem_info_t *out_info)
* are holes in all_memory_areas
*/
from_os_prot != MEMPROT_NONE) {
/* FIXME i#2037: today the start/stop API does not re-scan on start,
* so our cache is wrong. Worse than a false negative here, which we
* can handle as lazy lookup, is a false positive: we need to clear the
* cache at start. For now we just quiet the complaints here.
*/
DODEBUG({
if (!dr_api_entry) {
SYSLOG_INTERNAL_ERROR
("all_memory_areas is missing region " PFX"-"PFX"!",
from_os_base_pc, from_os_base_pc + from_os_size);
}
});
SYSLOG_INTERNAL_WARNING_ONCE
("all_memory_areas is missing regions including " PFX"-"PFX,
from_os_base_pc, from_os_base_pc + from_os_size);
DOLOG(4, LOG_VMAREAS, memcache_print(THREAD_GET, ""););
ASSERT(dr_api_entry);
/* be paranoid */
out_info->base_pc = from_os_base_pc;
out_info->size = from_os_size;
out_info->prot = from_os_prot;
out_info->type = DR_MEMTYPE_DATA; /* hopefully we won't miss an image */
/* Update our list to avoid coming back here again (i#2037). */
memcache_update_locked(from_os_base_pc, from_os_base_pc+from_os_size,
from_os_prot, -1, false/*!exists*/);
}
#else
/* We now have nested probes, but currently probing sometimes calls
@@ -585,3 +578,20 @@ memcache_handle_app_brk(byte *lowest_brk/*if known*/, byte *old_brk, byte *new_b
}
}
void
memcache_update_all_from_os(void)
{
memquery_iter_t iter;
LOG(GLOBAL, LOG_SYSCALLS, 1, "updating memcache from maps file\n");
memquery_iterator_start(&iter, NULL, true/*may alloc*/);
memcache_lock();
/* We clear the entire cache to avoid false positive queries. */
vmvector_reset_vector(GLOBAL_DCONTEXT, all_memory_areas);
while (memquery_iterator_next(&iter)) {
/* We do a heavyweight overlap check with everything. */
memcache_update_locked(iter.vm_start, iter.vm_end, iter.prot,
-1, false/*!exists*/);
}
memcache_unlock();
memquery_iterator_stop(&iter);
}
+4 -1
View File
@@ -1,5 +1,5 @@
/* **********************************************************
* Copyright (c) 2013-2015 Google, Inc. All rights reserved.
* Copyright (c) 2013-2018 Google, Inc. All rights reserved.
* **********************************************************/
/*
@@ -78,4 +78,7 @@ memcache_handle_mremap(dcontext_t *dcontext, byte *base, size_t size,
void
memcache_handle_app_brk(byte *lowest_brk/*if known*/, byte *old_brk, byte *new_brk);
void
memcache_update_all_from_os(void);
#endif /* _MEMCACHE_H_ */
+8 -1
View File
@@ -1,5 +1,5 @@
/* *******************************************************************************
* Copyright (c) 2010-2017 Google, Inc. All rights reserved.
* Copyright (c) 2010-2018 Google, Inc. All rights reserved.
* Copyright (c) 2011 Massachusetts Institute of Technology All rights reserved.
* Copyright (c) 2000-2010 VMware, Inc. All rights reserved.
* *******************************************************************************/
@@ -2597,6 +2597,13 @@ os_process_under_dynamorio_complete(dcontext_t *dcontext)
{
/* i#2161: only now do we un-ignore alarm signals. */
signal_reinstate_alarm_handlers(dcontext);
IF_NO_MEMQUERY({
/* Update the memory cache (i#2037) now that we've taken over all the
* threads, if there may have been a gap between setup and start.
*/
if (dr_api_entry)
memcache_update_all_from_os();
});
}
void
+6 -6
View File
@@ -2116,6 +2116,12 @@ vmvector_reset_vector(dcontext_t *dcontext, vm_area_vector_t *v)
});
/* with thread shared cache it is in fact possible to have no thread local vmareas */
if (v->buf != NULL) {
if (v->free_payload_func != NULL) {
int i;
for (i = 0; i < v->length; i++) {
v->free_payload_func(v->buf[i].custom.client);
}
}
/* FIXME: walk through and make sure frags lists are all freed */
global_heap_free(v->buf, v->size*sizeof(struct vm_area_t) HEAPACCT(ACCT_VMAREAS));
v->size = 0;
@@ -2136,12 +2142,6 @@ vmvector_free_vector(dcontext_t *dcontext, vm_area_vector_t *v)
void
vmvector_delete_vector(dcontext_t *dcontext, vm_area_vector_t *v)
{
if (v->free_payload_func != NULL) {
int i;
for (i = 0; i < v->length; i++) {
v->free_payload_func(v->buf[i].custom.client);
}
}
vmvector_free_vector(dcontext, v);
HEAP_TYPE_FREE(dcontext, v, vm_area_vector_t, ACCT_VMAREAS, PROTECTED);
}