|  | /* | 
|  | * 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; | 
|  | } |