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