409 lines
13 KiB
C++
409 lines
13 KiB
C++
/*
|
|
* TargetValue.cpp -- Access to target values using OMPD callbacks
|
|
*/
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
//
|
|
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
|
// See https://llvm.org/LICENSE.txt for license information.
|
|
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#include "TargetValue.h"
|
|
#include "Debug.h"
|
|
#include <cstring>
|
|
#include <fstream>
|
|
#include <iostream>
|
|
#include <sstream>
|
|
|
|
const ompd_callbacks_t *TValue::callbacks = NULL;
|
|
ompd_device_type_sizes_t TValue::type_sizes;
|
|
|
|
inline int ompd_sizeof(ompd_target_prim_types_t t) {
|
|
assert(t != ompd_type_max && "ompd_type_max should not be used anywhere");
|
|
assert(t != ompd_type_invalid && "request size of invalid type");
|
|
|
|
return (((char *)&TValue::type_sizes)[(int)t]);
|
|
}
|
|
|
|
TType &TTypeFactory::getType(ompd_address_space_context_t *context,
|
|
const char *typeName, ompd_addr_t segment) {
|
|
TType empty(true);
|
|
|
|
if (ttypes.find(context) == ttypes.end()) {
|
|
std::map<const char *, TType> empty;
|
|
ttypes[context] = empty;
|
|
}
|
|
|
|
auto t = ttypes.find(context);
|
|
auto i = t->second.find(typeName);
|
|
if (i == t->second.end())
|
|
i = t->second.insert(
|
|
i, std::make_pair(typeName, TType(context, typeName, segment)));
|
|
else
|
|
i->second.context = context;
|
|
|
|
return i->second;
|
|
}
|
|
|
|
TType::TType(ompd_address_space_context_t *_context, const char *_typeName,
|
|
ompd_addr_t _segment)
|
|
: typeSize(0), fieldOffsets(), descSegment(_segment), typeName(_typeName),
|
|
context(_context), isvoid(false) {}
|
|
|
|
ompd_rc_t TType::getSize(ompd_size_t *size) {
|
|
ompd_rc_t ret = ompd_rc_ok;
|
|
if (typeSize == 0) {
|
|
ompd_address_t symbolAddr;
|
|
ompd_size_t tmpSize;
|
|
std::stringstream ss;
|
|
ss << "ompd_sizeof__" << typeName;
|
|
|
|
ret = TValue::callbacks->symbol_addr_lookup(context, NULL, ss.str().c_str(),
|
|
&symbolAddr, NULL);
|
|
if (ret != ompd_rc_ok) {
|
|
dout << "missing symbol " << ss.str()
|
|
<< " add this to ompd-specific.h:\nOMPD_SIZEOF(" << typeName
|
|
<< ") \\" << std::endl;
|
|
return ret;
|
|
}
|
|
|
|
symbolAddr.segment = descSegment;
|
|
|
|
ret = TValue::callbacks->read_memory(
|
|
context, NULL, &symbolAddr, 1 * TValue::type_sizes.sizeof_long_long,
|
|
&(tmpSize));
|
|
if (ret != ompd_rc_ok)
|
|
return ret;
|
|
ret = TValue::callbacks->device_to_host(
|
|
context, &tmpSize, TValue::type_sizes.sizeof_long_long, 1, &(typeSize));
|
|
}
|
|
*size = typeSize;
|
|
return ret;
|
|
}
|
|
|
|
ompd_rc_t TType::getBitfieldMask(const char *fieldName,
|
|
uint64_t *bitfieldmask) {
|
|
ompd_rc_t ret = ompd_rc_ok;
|
|
auto i = bitfieldMasks.find(fieldName);
|
|
if (i == bitfieldMasks.end()) {
|
|
uint64_t tmpMask, bitfieldMask;
|
|
ompd_address_t symbolAddr;
|
|
std::stringstream ss;
|
|
ss << "ompd_bitfield__" << typeName << "__" << fieldName;
|
|
ret = TValue::callbacks->symbol_addr_lookup(context, NULL, ss.str().c_str(),
|
|
&symbolAddr, NULL);
|
|
if (ret != ompd_rc_ok) {
|
|
dout << "missing symbol " << ss.str()
|
|
<< " add this to ompd-specific.h:\nOMPD_BITFIELD(" << typeName << ","
|
|
<< fieldName << ") \\" << std::endl;
|
|
return ret;
|
|
}
|
|
symbolAddr.segment = descSegment;
|
|
|
|
ret = TValue::callbacks->read_memory(
|
|
context, NULL, &symbolAddr, 1 * TValue::type_sizes.sizeof_long_long,
|
|
&(tmpMask));
|
|
if (ret != ompd_rc_ok)
|
|
return ret;
|
|
ret = TValue::callbacks->device_to_host(context, &(tmpMask),
|
|
TValue::type_sizes.sizeof_long_long,
|
|
1, &(bitfieldMask));
|
|
if (ret != ompd_rc_ok) {
|
|
return ret;
|
|
}
|
|
i = bitfieldMasks.insert(i, std::make_pair(fieldName, bitfieldMask));
|
|
}
|
|
*bitfieldmask = i->second;
|
|
return ret;
|
|
}
|
|
|
|
ompd_rc_t TType::getElementOffset(const char *fieldName, ompd_size_t *offset) {
|
|
ompd_rc_t ret = ompd_rc_ok;
|
|
auto i = fieldOffsets.find(fieldName);
|
|
if (i == fieldOffsets.end()) {
|
|
ompd_size_t tmpOffset, fieldOffset;
|
|
ompd_address_t symbolAddr;
|
|
std::stringstream ss;
|
|
ss << "ompd_access__" << typeName << "__" << fieldName;
|
|
|
|
ret = TValue::callbacks->symbol_addr_lookup(context, NULL, ss.str().c_str(),
|
|
&symbolAddr, NULL);
|
|
if (ret != ompd_rc_ok) {
|
|
dout << "missing symbol " << ss.str()
|
|
<< " add this to ompd-specific.h:\nOMPD_ACCESS(" << typeName << ","
|
|
<< fieldName << ") \\" << std::endl;
|
|
return ret;
|
|
}
|
|
symbolAddr.segment = descSegment;
|
|
|
|
ret = TValue::callbacks->read_memory(
|
|
context, NULL, &symbolAddr, 1 * TValue::type_sizes.sizeof_long_long,
|
|
&(tmpOffset));
|
|
if (ret != ompd_rc_ok)
|
|
return ret;
|
|
ret = TValue::callbacks->device_to_host(context, &(tmpOffset),
|
|
TValue::type_sizes.sizeof_long_long,
|
|
1, &fieldOffset);
|
|
if (ret != ompd_rc_ok) {
|
|
return ret;
|
|
}
|
|
i = fieldOffsets.insert(i, std::make_pair(fieldName, fieldOffset));
|
|
}
|
|
*offset = i->second;
|
|
return ret;
|
|
}
|
|
|
|
ompd_rc_t TType::getElementSize(const char *fieldName, ompd_size_t *size) {
|
|
ompd_rc_t ret = ompd_rc_ok;
|
|
auto i = fieldSizes.find(fieldName);
|
|
if (i == fieldSizes.end()) {
|
|
ompd_size_t tmpOffset, fieldSize;
|
|
ompd_address_t symbolAddr;
|
|
std::stringstream ss;
|
|
ss << "ompd_sizeof__" << typeName << "__" << fieldName;
|
|
|
|
ret = TValue::callbacks->symbol_addr_lookup(context, NULL, ss.str().c_str(),
|
|
&symbolAddr, NULL);
|
|
if (ret != ompd_rc_ok) {
|
|
dout << "missing symbol " << ss.str()
|
|
<< " add this to ompd-specific.h:\nOMPD_ACCESS(" << typeName << ","
|
|
<< fieldName << ") \\" << std::endl;
|
|
return ret;
|
|
}
|
|
symbolAddr.segment = descSegment;
|
|
|
|
ret = TValue::callbacks->read_memory(
|
|
context, NULL, &symbolAddr, 1 * TValue::type_sizes.sizeof_long_long,
|
|
&(tmpOffset));
|
|
if (ret != ompd_rc_ok)
|
|
return ret;
|
|
ret = TValue::callbacks->device_to_host(context, &tmpOffset,
|
|
TValue::type_sizes.sizeof_long_long,
|
|
1, &fieldSize);
|
|
if (ret != ompd_rc_ok) {
|
|
return ret;
|
|
}
|
|
i = fieldSizes.insert(i, std::make_pair(fieldName, fieldSize));
|
|
}
|
|
*size = i->second;
|
|
return ret;
|
|
}
|
|
|
|
TValue::TValue(ompd_address_space_context_t *_context,
|
|
ompd_thread_context_t *_tcontext, const char *_valueName,
|
|
ompd_addr_t segment)
|
|
: errorState(ompd_rc_ok), type(&nullType), pointerLevel(0),
|
|
context(_context), tcontext(_tcontext), fieldSize(0) {
|
|
errorState.errorCode = callbacks->symbol_addr_lookup(
|
|
context, tcontext, _valueName, &symbolAddr, NULL);
|
|
symbolAddr.segment = segment;
|
|
}
|
|
|
|
TValue::TValue(ompd_address_space_context_t *_context,
|
|
ompd_thread_context_t *_tcontext, ompd_address_t addr)
|
|
: errorState(ompd_rc_ok), type(&nullType), pointerLevel(0),
|
|
context(_context), tcontext(_tcontext), symbolAddr(addr), fieldSize(0) {
|
|
if (addr.address == 0)
|
|
errorState.errorCode = ompd_rc_bad_input;
|
|
}
|
|
|
|
TValue &TValue::cast(const char *typeName) {
|
|
if (gotError())
|
|
return *this;
|
|
type = &tf.getType(context, typeName, symbolAddr.segment);
|
|
pointerLevel = 0;
|
|
assert(!type->isVoid() && "cast to invalid type failed");
|
|
return *this;
|
|
}
|
|
|
|
TValue &TValue::cast(const char *typeName, int _pointerLevel,
|
|
ompd_addr_t segment) {
|
|
if (gotError())
|
|
return *this;
|
|
type = &tf.getType(context, typeName, symbolAddr.segment);
|
|
pointerLevel = _pointerLevel;
|
|
symbolAddr.segment = segment;
|
|
assert(!type->isVoid() && "cast to invalid type failed");
|
|
return *this;
|
|
}
|
|
|
|
TValue TValue::dereference() const {
|
|
if (gotError())
|
|
return *this;
|
|
ompd_address_t tmpAddr;
|
|
assert(!type->isVoid() && "cannot work with void");
|
|
assert(pointerLevel > 0 && "cannot dereference non-pointer");
|
|
TValue ret = *this;
|
|
ret.pointerLevel--;
|
|
ret.errorState.errorCode = callbacks->read_memory(
|
|
context, tcontext, &symbolAddr, 1 * TValue::type_sizes.sizeof_pointer,
|
|
&(tmpAddr.address));
|
|
if (ret.errorState.errorCode != ompd_rc_ok)
|
|
return ret;
|
|
|
|
ret.errorState.errorCode = callbacks->device_to_host(
|
|
context, &(tmpAddr.address), TValue::type_sizes.sizeof_pointer, 1,
|
|
&(ret.symbolAddr.address));
|
|
if (ret.errorState.errorCode != ompd_rc_ok) {
|
|
return ret;
|
|
}
|
|
if (ret.symbolAddr.address == 0)
|
|
ret.errorState.errorCode = ompd_rc_unsupported;
|
|
return ret;
|
|
}
|
|
|
|
ompd_rc_t TValue::getAddress(ompd_address_t *addr) const {
|
|
*addr = symbolAddr;
|
|
if (symbolAddr.address == 0)
|
|
return ompd_rc_unsupported;
|
|
return errorState.errorCode;
|
|
}
|
|
|
|
ompd_rc_t TValue::getRawValue(void *buf, int count) {
|
|
if (errorState.errorCode != ompd_rc_ok)
|
|
return errorState.errorCode;
|
|
ompd_size_t size;
|
|
errorState.errorCode = type->getSize(&size);
|
|
if (errorState.errorCode != ompd_rc_ok)
|
|
return errorState.errorCode;
|
|
|
|
errorState.errorCode =
|
|
callbacks->read_memory(context, tcontext, &symbolAddr, size, buf);
|
|
return errorState.errorCode;
|
|
}
|
|
|
|
ompd_rc_t TValue::getString(const char **buf) {
|
|
*buf = 0;
|
|
if (gotError())
|
|
return getError();
|
|
|
|
TValue strValue = dereference();
|
|
if (strValue.gotError()) {
|
|
return strValue.getError();
|
|
}
|
|
|
|
if (!callbacks) {
|
|
return ompd_rc_error;
|
|
}
|
|
ompd_rc_t ret;
|
|
#define BUF_LEN 512
|
|
char *string_buffer;
|
|
|
|
// Allocate an extra byte, but pass only BUF_LEN to the tool
|
|
// so that we can detect truncation later.
|
|
ret = callbacks->alloc_memory(BUF_LEN + 1, (void **)&string_buffer);
|
|
if (ret != ompd_rc_ok) {
|
|
return ret;
|
|
}
|
|
string_buffer[BUF_LEN] = '\0';
|
|
|
|
// TODO: if we have not read in the complete string, we need to realloc
|
|
// 'string_buffer' and attempt reading again repeatedly till the entire string
|
|
// is read in.
|
|
ret = callbacks->read_string(context, tcontext, &strValue.symbolAddr, BUF_LEN,
|
|
(void *)string_buffer);
|
|
*buf = string_buffer;
|
|
// Check for truncation. The standard specifies that if a null byte is not
|
|
// among the first 'nbytes' bytes, the string placed in the buffer is not
|
|
// null-terminated. 'nbytes' is BUF_LEN in this case.
|
|
if (ret == ompd_rc_ok && strlen(string_buffer) == BUF_LEN) {
|
|
return ompd_rc_error;
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
TBaseValue TValue::castBase(const char *varName) {
|
|
ompd_size_t size;
|
|
errorState.errorCode =
|
|
tf.getType(context, varName, symbolAddr.segment).getSize(&size);
|
|
return TBaseValue(*this, size);
|
|
}
|
|
|
|
TBaseValue TValue::castBase() const {
|
|
if (pointerLevel > 0)
|
|
return TBaseValue(*this, type_sizes.sizeof_pointer);
|
|
return TBaseValue(*this, fieldSize);
|
|
}
|
|
|
|
TBaseValue TValue::castBase(ompd_target_prim_types_t baseType) const {
|
|
return TBaseValue(*this, baseType);
|
|
}
|
|
|
|
TValue TValue::access(const char *fieldName) const {
|
|
if (gotError())
|
|
return *this;
|
|
TValue ret = *this;
|
|
assert(pointerLevel < 2 && "access to field element of pointer array failed");
|
|
if (pointerLevel == 1) // -> operator
|
|
ret = ret.dereference();
|
|
// we use *this for . operator
|
|
ompd_size_t offset;
|
|
ret.errorState.errorCode = type->getElementOffset(fieldName, &offset);
|
|
ret.errorState.errorCode = type->getElementSize(fieldName, &(ret.fieldSize));
|
|
ret.symbolAddr.address += offset;
|
|
|
|
return ret;
|
|
}
|
|
|
|
ompd_rc_t TValue::check(const char *bitfieldName, ompd_word_t *isSet) const {
|
|
if (gotError())
|
|
return getError();
|
|
int bitfield;
|
|
uint64_t bitfieldmask;
|
|
ompd_rc_t ret = this->castBase(ompd_type_int).getValue(&bitfield, 1);
|
|
if (ret != ompd_rc_ok)
|
|
return ret;
|
|
ret = type->getBitfieldMask(bitfieldName, &bitfieldmask);
|
|
*isSet = ((bitfield & bitfieldmask) != 0);
|
|
return ret;
|
|
}
|
|
|
|
TValue TValue::getArrayElement(int elemNumber) const {
|
|
if (gotError())
|
|
return *this;
|
|
TValue ret;
|
|
if (pointerLevel > 0) {
|
|
ret = dereference();
|
|
} else {
|
|
ret = *this;
|
|
}
|
|
if (ret.pointerLevel == 0) {
|
|
ompd_size_t size;
|
|
ret.errorState.errorCode = type->getSize(&size);
|
|
ret.symbolAddr.address += elemNumber * size;
|
|
} else {
|
|
ret.symbolAddr.address += elemNumber * type_sizes.sizeof_pointer;
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
TValue TValue::getPtrArrayElement(int elemNumber) const {
|
|
if (gotError()) {
|
|
return *this;
|
|
}
|
|
assert(pointerLevel > 0 && "This only works on arrays of pointers");
|
|
TValue ret = *this;
|
|
ret.symbolAddr.address += elemNumber * type_sizes.sizeof_pointer;
|
|
return ret;
|
|
}
|
|
|
|
TBaseValue::TBaseValue(const TValue &_tvalue,
|
|
ompd_target_prim_types_t _baseType)
|
|
: TValue(_tvalue), baseTypeSize(ompd_sizeof(_baseType)) {}
|
|
TBaseValue::TBaseValue(const TValue &_tvalue, ompd_size_t _baseTypeSize)
|
|
: TValue(_tvalue), baseTypeSize(_baseTypeSize) {}
|
|
|
|
ompd_rc_t TBaseValue::getValue(void *buf, int count) {
|
|
if (errorState.errorCode != ompd_rc_ok)
|
|
return errorState.errorCode;
|
|
errorState.errorCode = callbacks->read_memory(context, tcontext, &symbolAddr,
|
|
count * baseTypeSize, buf);
|
|
if (errorState.errorCode != ompd_rc_ok)
|
|
return errorState.errorCode;
|
|
errorState.errorCode =
|
|
callbacks->device_to_host(context, buf, baseTypeSize, count, buf);
|
|
return errorState.errorCode;
|
|
}
|