CodeQL / Analyze (actions) (push) Canceled after 0s
CodeQL / Analyze (python) (push) Canceled after 0s
pre-commit / pre-commit (push) Canceled after 0s
Multi-Arch CI / setup (push) Canceled after 0s
Publish build_manylinux_x86_64 images / publish_build_manylinux_x86_64 (push) Canceled after 0s
Setup (Multi-Arch) / setup (push) Canceled after 0s
Multi-Arch CI / Linux::${{ fromJSON(needs.setup.outputs.linux_build_config || '{}').build_variant_label || 'skip' }} (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Publish no_rocm_image_ubi10 image / publish_no_rocm_image_ubi10 (push) Canceled after 0s
Multi-Arch CI / Windows::${{ fromJSON(needs.setup.outputs.windows_build_config || '{}').build_variant_label || 'skip' }} (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Multi-Arch CI / Manifest Diff (push) Canceled after 0s
TheRock Manifest Diff Report / Generate Manifest Diff Report (push) Canceled after 0s
Multi-Arch CI / CI Summary (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04 (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04_ocl_rt images / publish_no_rocm_image_ubuntu24_04_ocl_rt (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_rocgdb (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04_rocgdb images / publish_no_rocm_image_ubuntu24_04_rocgdb (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_ocl_rt (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Unit Tests / Unit Tests :: ubuntu-24.04 (push) Canceled after 0s
Unit Tests / Unit Tests :: windows-2022 (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_media (push) Canceled after 0s
Unit Tests / Unit Tests Summary (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_openmpi (push) Canceled after 0s
603 lines
17 KiB
C++
603 lines
17 KiB
C++
/*
|
|
* See the dyninst/COPYRIGHT file for copyright information.
|
|
*
|
|
* We provide the Paradyn Tools (below described as "Paradyn")
|
|
* on an AS IS basis, and do not warrant its validity or performance.
|
|
* We reserve the right to update, modify, or discontinue this
|
|
* software at any time. We shall have no obligation to supply such
|
|
* updates or modifications or any other form of support to you.
|
|
*
|
|
* By your use of Paradyn, you understand and agree that we (or any
|
|
* other person or entity with proprietary rights in Paradyn) are
|
|
* under no obligation to provide either maintenance services,
|
|
* update services, notices of latent defects, or correction of
|
|
* defects for Paradyn.
|
|
*
|
|
* This library is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU Lesser General Public
|
|
* License as published by the Free Software Foundation; either
|
|
* version 2.1 of the License, or (at your option) any later version.
|
|
*
|
|
* This library is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
* Lesser General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU Lesser General Public
|
|
* License along with this library; if not, write to the Free Software
|
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
|
*/
|
|
|
|
#include <algorithm>
|
|
#include "memcache.h"
|
|
#include "int_process.h"
|
|
#include <string.h>
|
|
|
|
using namespace std;
|
|
|
|
memEntry::memEntry() :
|
|
addr(0),
|
|
buffer(NULL),
|
|
had_error(false),
|
|
size(0),
|
|
operation_num(0),
|
|
clean_buffer(false),
|
|
is_read(false),
|
|
is_write(false),
|
|
token_type(token_none)
|
|
{
|
|
}
|
|
|
|
memEntry::memEntry(Dyninst::Address remote, void *local, unsigned long size_, bool is_read_, memCache *cache) :
|
|
addr(remote),
|
|
buffer((char *) local),
|
|
had_error(false),
|
|
size(size_),
|
|
operation_num(cache->operation_num),
|
|
clean_buffer(false),
|
|
is_read(is_read_),
|
|
is_write(!is_read_),
|
|
token_type(token_none)
|
|
{
|
|
}
|
|
|
|
memEntry::memEntry(token_t t) :
|
|
addr(0),
|
|
buffer(NULL),
|
|
had_error(false),
|
|
size(0),
|
|
operation_num(0),
|
|
clean_buffer(false),
|
|
is_read(false),
|
|
is_write(false),
|
|
token_type(t)
|
|
{
|
|
}
|
|
|
|
memEntry::memEntry(const memEntry *me, char *b) :
|
|
addr(me->addr),
|
|
buffer(b),
|
|
had_error(me->had_error),
|
|
size(me->size),
|
|
operation_num(me->operation_num),
|
|
clean_buffer(true),
|
|
is_read(me->is_read),
|
|
is_write(me->is_write),
|
|
token_type(me->token_type)
|
|
{
|
|
}
|
|
|
|
memEntry::~memEntry()
|
|
{
|
|
if (buffer && clean_buffer)
|
|
free(buffer);
|
|
buffer = NULL;
|
|
}
|
|
|
|
Address memEntry::getAddress() const
|
|
{
|
|
return addr;
|
|
}
|
|
|
|
char *memEntry::getBuffer() const
|
|
{
|
|
return buffer;
|
|
}
|
|
|
|
unsigned long memEntry::getSize() const
|
|
{
|
|
return size;
|
|
}
|
|
|
|
bool memEntry::isRead() const
|
|
{
|
|
return is_read;
|
|
}
|
|
|
|
bool memEntry::isWrite() const
|
|
{
|
|
return is_write;
|
|
}
|
|
|
|
bool memEntry::isToken() const
|
|
{
|
|
return token_type != token_none;
|
|
}
|
|
|
|
bool memEntry::operator==(const memEntry &b) const
|
|
{
|
|
if (is_read != b.is_read || is_write != b.is_write ||
|
|
token_type != b.token_type || addr != b.addr || size != b.size)
|
|
{
|
|
return false;
|
|
}
|
|
if (is_write && (memcmp(buffer, b.buffer, size) != 0))
|
|
return false;
|
|
return true;
|
|
}
|
|
|
|
void memEntry::invalidate() {
|
|
is_read = false;
|
|
is_write = false;
|
|
token_type = token_none;
|
|
}
|
|
|
|
void onContinueMemCache(int_thread *thr)
|
|
{
|
|
thr->llproc()->getMemCache()->clear();
|
|
}
|
|
|
|
memCache::memCache(int_process *p) :
|
|
proc(p),
|
|
block_size(0),
|
|
word_cache(0),
|
|
word_cache_addr(0),
|
|
word_cache_valid(false),
|
|
pending_async(false),
|
|
have_writes(false),
|
|
sync_handle(false),
|
|
operation_num(0)
|
|
{
|
|
static bool registeredMemCacheClear = false;
|
|
if (!registeredMemCacheClear) {
|
|
registeredMemCacheClear = true;
|
|
int_thread::addContinueCB(onContinueMemCache);
|
|
}
|
|
last_operation = mem_cache.end();
|
|
}
|
|
|
|
memCache::~memCache()
|
|
{
|
|
clear();
|
|
}
|
|
|
|
void memCache::getPendingAsyncs(set<response::ptr> &resps)
|
|
{
|
|
for (mcache_t::iterator i = mem_cache.begin(); i != mem_cache.end(); i++) {
|
|
memEntry *entry = *i;
|
|
if (!entry->resp && !entry->res_resp)
|
|
continue;
|
|
assert(entry->resp || entry->res_resp);
|
|
response::ptr resp;
|
|
if (entry->resp)
|
|
resp = entry->resp;
|
|
else
|
|
resp = entry->res_resp;
|
|
if (resp->isReady() || resp->hasError())
|
|
continue;
|
|
resps.insert(resp);
|
|
}
|
|
for (map<int_thread *, allreg_response::ptr>::iterator j = regs.begin(); j != regs.end(); j++) {
|
|
response::ptr resp = j->second;
|
|
if (resp->isReady() || resp->hasError())
|
|
continue;
|
|
resps.insert(resp);
|
|
}
|
|
|
|
}
|
|
|
|
void memCache::setSyncHandling(bool b)
|
|
{
|
|
sync_handle = b;
|
|
}
|
|
|
|
void memCache::markToken(token_t tk)
|
|
{
|
|
if (!proc->plat_needsAsyncIO())
|
|
return;
|
|
|
|
mcache_t::iterator i;
|
|
for (i = mem_cache.begin(); i != mem_cache.end(); i++) {
|
|
if ((*i)->token_type != tk)
|
|
continue;
|
|
last_operation = i;
|
|
return;
|
|
}
|
|
memEntry *newEntry = new memEntry(tk);
|
|
mem_cache.push_back(newEntry);
|
|
last_operation = mem_cache.end();
|
|
last_operation--;
|
|
}
|
|
|
|
void memCache::updateReadCacheWithWrite(Address dest, char *src, unsigned long size)
|
|
{
|
|
Address start_block = dest - (dest % block_size);
|
|
for (Address cur = start_block; cur < dest+size; cur += block_size) {
|
|
Address write_start = dest;
|
|
Address write_end = dest + size;
|
|
for (mcache_t::iterator i = mem_cache.begin(); i != mem_cache.end(); i++) {
|
|
if (!(*i)->isRead())
|
|
continue;
|
|
|
|
Address read_start = (*i)->getAddress();
|
|
Address read_end = read_start + (*i)->getSize();
|
|
if (write_start >= read_end || read_start >= write_end)
|
|
continue;
|
|
|
|
//Compute intersection of write and read
|
|
Address intersect_start = write_start > read_start ? write_start : read_start;
|
|
Address intersect_end = write_end < read_end ? write_end : read_end;
|
|
|
|
char *target_mem = (*i)->getBuffer() + (intersect_start - cur);
|
|
char *src_mem = src + (intersect_start - dest);
|
|
unsigned long copy_size = intersect_end - intersect_start;
|
|
|
|
memcpy(target_mem, src_mem, copy_size);
|
|
}
|
|
}
|
|
}
|
|
|
|
void memCache::condense()
|
|
{
|
|
if (!proc->plat_needsAsyncIO())
|
|
return;
|
|
|
|
mcache_t::iterator i;
|
|
for (i = mem_cache.begin(); i != mem_cache.end(); i++) {
|
|
memEntry *ent = *i;
|
|
if (ent->isToken()) {
|
|
ent->invalidate();
|
|
}
|
|
else if (ent->isWrite()) {
|
|
updateReadCacheWithWrite(ent->addr, ent->buffer, ent->size);
|
|
ent->invalidate();
|
|
}
|
|
}
|
|
have_writes = false;
|
|
}
|
|
|
|
async_ret_t memCache::doOperation(memEntry *me, int_thread *op_thread)
|
|
{
|
|
bool result = false;
|
|
response::ptr resp;
|
|
unsigned size = me->getSize();
|
|
char *buffer = (char *) malloc(size);
|
|
if (me->isRead()) {
|
|
pthrd_printf("Performing async read memory in memCache\n");
|
|
assert(!me->getBuffer());
|
|
me->buffer = buffer;
|
|
me->resp = mem_response::createMemResponse(buffer, size);
|
|
if (sync_handle) me->resp->markSyncHandled();
|
|
result = proc->readMem(me->getAddress(), me->resp, op_thread);
|
|
resp = me->resp;
|
|
}
|
|
else if (me->isWrite()) {
|
|
assert(me->isWrite());
|
|
pthrd_printf("Performing async write memory in memCache\n");
|
|
memcpy(buffer, me->getBuffer(), size);
|
|
me->buffer = buffer;
|
|
me->operation_num = ++operation_num;
|
|
me->res_resp = result_response::createResultResponse();
|
|
if (sync_handle) me->res_resp->markSyncHandled();
|
|
result = proc->writeMem(buffer, me->getAddress(), size,
|
|
me->res_resp, op_thread);
|
|
resp = me->res_resp;
|
|
}
|
|
else {
|
|
assert(0);
|
|
}
|
|
|
|
if (!result || resp->hasError()) {
|
|
pthrd_printf("Error accessing memory in memCache\n");
|
|
return aret_error;
|
|
}
|
|
|
|
mem_cache.push_back(new memEntry(me, me->buffer));
|
|
last_operation = mem_cache.end();
|
|
last_operation--;
|
|
|
|
if (!resp->isReady()) {
|
|
return aret_async;
|
|
}
|
|
|
|
return aret_success;
|
|
}
|
|
|
|
async_ret_t memCache::getExistingOperation(mcache_t::iterator i, memEntry *orig)
|
|
{
|
|
memEntry *me = *i;
|
|
response::ptr resp;
|
|
|
|
if (me->isRead()) {
|
|
resp = me->resp;
|
|
orig->resp = me->resp;
|
|
orig->buffer = me->buffer;
|
|
}
|
|
else if (me->isWrite()) {
|
|
resp = me->res_resp;
|
|
orig->res_resp = me->res_resp;
|
|
}
|
|
else {
|
|
assert(0);
|
|
}
|
|
|
|
last_operation = i;
|
|
if (resp->hasError()) {
|
|
pthrd_printf("Previous entry had error accessing memory in memCache\n");
|
|
return aret_error;
|
|
}
|
|
if (!resp->isReady()) {
|
|
return aret_async;
|
|
}
|
|
return aret_success;
|
|
}
|
|
|
|
async_ret_t memCache::lookupAsync(memEntry *me, int_thread *op_thread)
|
|
{
|
|
if (last_operation == mem_cache.end()) {
|
|
pthrd_printf("Async request is first in empty cache\n");
|
|
return doOperation(me, op_thread);
|
|
}
|
|
|
|
mcache_t::iterator matching_entry = last_operation;
|
|
bool first = true;
|
|
for (;;) {
|
|
if (matching_entry == last_operation && !first)
|
|
break;
|
|
first = false;
|
|
|
|
if (matching_entry == mem_cache.end()) {
|
|
//If there aren't any writes in the memory cache then
|
|
// go ahead and search the whole thing, there aren't
|
|
// going to be any consistency issues.
|
|
if (have_writes) {
|
|
break;
|
|
}
|
|
matching_entry = mem_cache.begin();
|
|
continue;
|
|
}
|
|
|
|
if (**matching_entry == *me) {
|
|
/*pthrd_printf("Memcache: Found existing operation: %s of %lx, size %lu\n",
|
|
me->is_read ? "read" : "write", me->getAddress(), me->getSize());*/
|
|
return getExistingOperation(matching_entry, me);
|
|
}
|
|
matching_entry++;
|
|
}
|
|
|
|
pthrd_printf("Async request not found in cache. Triggering.\n");
|
|
return doOperation(me, op_thread);
|
|
}
|
|
|
|
async_ret_t memCache::readMemoryAsync(void *dest, Address src, unsigned long size,
|
|
set<mem_response::ptr> &resps,
|
|
int_thread *reading_thread)
|
|
{
|
|
pending_async = false;
|
|
Address start_block = src - (src % block_size);
|
|
mcache_t::size_type cache_start_size = mem_cache.size();
|
|
|
|
for (Address cur = start_block; cur < src+size; cur += block_size) {
|
|
memEntry me(cur, NULL, block_size, true, this);
|
|
async_ret_t result = lookupAsync(&me, reading_thread);
|
|
if (result == aret_success) {
|
|
char *target_mem;
|
|
unsigned long copy_size;
|
|
char *src_mem;
|
|
|
|
if (cur == start_block)
|
|
target_mem = (char *) dest;
|
|
else
|
|
target_mem = ((char *) dest) + (cur - src);
|
|
|
|
Address me_start = me.addr;
|
|
Address me_end = me.addr + block_size;
|
|
Address src_start = src;
|
|
Address src_end = src + size;
|
|
|
|
//Compute intersection of i & curr
|
|
Address intersect_start = me_start > src_start ? me_start : src_start;
|
|
Address intersect_end = me_end < src_end ? me_end : src_end;
|
|
assert(intersect_start <= intersect_end);
|
|
|
|
copy_size = intersect_end - intersect_start;
|
|
src_mem = me.buffer + (intersect_start - me_start);
|
|
|
|
memcpy(target_mem, src_mem, copy_size);
|
|
}
|
|
else if (result == aret_error) {
|
|
pthrd_printf("Error return from memCache::readMemAsync\n");
|
|
return aret_error;
|
|
}
|
|
else if (result == aret_async) {
|
|
pending_async = true;
|
|
}
|
|
}
|
|
|
|
if (pending_async) {
|
|
mcache_t::size_type cache_end_size = mem_cache.size();
|
|
//assert(cache_start_size != cache_end_size);
|
|
for (mcache_t::size_type i = cache_start_size; i != cache_end_size; i++) {
|
|
assert(mem_cache[i]->resp);
|
|
resps.insert(mem_cache[i]->resp);
|
|
}
|
|
return aret_async;
|
|
}
|
|
|
|
return aret_success;
|
|
}
|
|
|
|
async_ret_t memCache::readMemorySync(void *buffer, Address addr, unsigned long size,
|
|
int_thread *reading_thread)
|
|
{
|
|
if (size != 1) {
|
|
mem_response::ptr memresult = mem_response::createMemResponse((char *) buffer, size);
|
|
bool result = proc->readMem(addr, memresult, reading_thread);
|
|
if (!result) {
|
|
pthrd_printf("Failed to read memory for proc reader\n");
|
|
return aret_error;
|
|
}
|
|
result = memresult->isReady();
|
|
assert(result);
|
|
return aret_success;
|
|
}
|
|
|
|
//Try to optimially handle a case where the calling code
|
|
// reads a string one char at a time. This is mostly for
|
|
// ptrace platforms, but won't harm any others.
|
|
assert(size == 1);
|
|
Address aligned_addr = addr - (addr % sizeof(word_cache));
|
|
if (!word_cache_valid || aligned_addr != word_cache_addr) {
|
|
mem_response::ptr memresult = mem_response::createMemResponse((char *) &word_cache, sizeof(word_cache));
|
|
bool result = proc->readMem(aligned_addr, memresult, reading_thread);
|
|
if (!result) {
|
|
pthrd_printf("Failed to read memory for proc reader\n");
|
|
return aret_error;
|
|
}
|
|
result = memresult->isReady();
|
|
assert(result);
|
|
word_cache_addr = aligned_addr;
|
|
word_cache_valid = true;
|
|
}
|
|
*((char *) buffer) = ((char *) &word_cache)[addr - aligned_addr];
|
|
return aret_success;
|
|
}
|
|
|
|
async_ret_t memCache::writeMemoryAsync(Dyninst::Address dest, void *src, unsigned long size,
|
|
std::set<result_response::ptr> &resps,
|
|
int_thread *writing_thread)
|
|
{
|
|
memEntry me(dest, (char *) src, size, false, this);
|
|
pending_async = false;
|
|
async_ret_t result = lookupAsync(&me, writing_thread);
|
|
have_writes = true;
|
|
if (result == aret_async) {
|
|
mcache_t::iterator i = mem_cache.end();
|
|
i--;
|
|
assert((*i)->res_resp);
|
|
resps.insert((*i)->res_resp);
|
|
pending_async = true;
|
|
|
|
bool found_token = false;
|
|
for (i = mem_cache.begin(); i != mem_cache.end(); i++) {
|
|
if ((*i)->token_type != token_none) {
|
|
found_token = true;
|
|
break;
|
|
}
|
|
}
|
|
assert(found_token);
|
|
}
|
|
return result;
|
|
}
|
|
|
|
async_ret_t memCache::writeMemorySync(Dyninst::Address dest, void *src, unsigned long size,
|
|
int_thread *write_thread)
|
|
{
|
|
result_response::ptr resp = result_response::createResultResponse();
|
|
bool result = proc->writeMem(src, dest, size, resp, write_thread);
|
|
if (!result) {
|
|
pthrd_printf("Error writing memory at %lx/%lu on %d\n",
|
|
dest, size, proc->getPid());
|
|
return aret_error;
|
|
}
|
|
result = resp->isReady();
|
|
assert(result);
|
|
return aret_success;
|
|
}
|
|
|
|
async_ret_t memCache::readMemory(void *dest, Address src, unsigned long size,
|
|
set<mem_response::ptr> &resps, int_thread *thrd)
|
|
{
|
|
// Normalize addresses
|
|
if (proc->getAddressWidth() == 4) {
|
|
src &= 0xffffffff;
|
|
}
|
|
|
|
if (proc->plat_needsAsyncIO()) {
|
|
if (!block_size)
|
|
block_size = proc->plat_getRecommendedReadSize();
|
|
return readMemoryAsync(dest, src, size, resps, thrd);
|
|
}
|
|
else
|
|
return readMemorySync(dest, src, size, thrd);
|
|
}
|
|
|
|
async_ret_t memCache::writeMemory(Dyninst::Address dest, void *src, unsigned long size,
|
|
std::set<result_response::ptr> &resps, int_thread *thrd)
|
|
{
|
|
// Normalize addresses
|
|
if (proc->getAddressWidth() == 4) {
|
|
dest &= 0xffffffff;
|
|
}
|
|
|
|
if (proc->plat_needsAsyncIO()) {
|
|
if (!block_size)
|
|
block_size = proc->plat_getRecommendedReadSize();
|
|
return writeMemoryAsync(dest, src, size, resps, thrd);
|
|
}
|
|
else
|
|
return writeMemorySync(dest, src, size, thrd);
|
|
}
|
|
|
|
async_ret_t memCache::getRegisters(int_thread *thr, int_registerPool &pool)
|
|
{
|
|
allreg_response::ptr resp;
|
|
bool result = true;
|
|
map<int_thread *, allreg_response::ptr>::iterator i = regs.find(thr);
|
|
if (i == regs.end()) {
|
|
int_registerPool *new_pool = new int_registerPool();
|
|
resp = allreg_response::createAllRegResponse(new_pool);
|
|
regs[thr] = resp;
|
|
if (sync_handle) resp->markSyncHandled();
|
|
result = thr->getAllRegisters(resp);
|
|
}
|
|
else {
|
|
resp = i->second;
|
|
}
|
|
|
|
if (!result || resp->hasError()) {
|
|
pending_async = false;
|
|
return aret_error;
|
|
}
|
|
else if (!resp->isReady()) {
|
|
pending_async = true;
|
|
return aret_async;
|
|
}
|
|
pool = *resp->getRegPool();
|
|
pending_async = false;
|
|
return aret_success;
|
|
}
|
|
|
|
void memCache::clear()
|
|
{
|
|
pthrd_printf("Clearing memCache\n");
|
|
|
|
for (map<int_thread *, allreg_response::ptr>::iterator i = regs.begin(); i != regs.end(); i++) {
|
|
delete i->second->getRegPool();
|
|
}
|
|
for (mcache_t::iterator j = mem_cache.begin(); j != mem_cache.end(); j++) {
|
|
delete *j;
|
|
}
|
|
mem_cache.clear();
|
|
regs.clear();
|
|
|
|
last_operation = mem_cache.end();
|
|
word_cache_valid = false;
|
|
pending_async = false;
|
|
have_writes = false;
|
|
}
|
|
|
|
bool memCache::hasPendingAsync() {
|
|
return pending_async;
|
|
}
|