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