blob: 8818121cf4f3a1ec9e9e96fb9249a28bdc7568aa [file] [log] [blame]
/*
* 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;
}