This is a continuation of the review: https://reviews.llvm.org/D100181 Creates a new directory "libompd" under openmp. "TargetValue" provides operational access to the OpenMP runtime memory for OMPD APIs. With TargetValue, using "pointer" a user can do multiple operations from casting, dereferencing to accessing an element for structure. The member functions are designed to concatenate the operations that are needed to access values from structures. e.g., _a[6]->_b._c would read like : TValue(ctx, "_a").cast("A",2) .getArrayElement(6).access("_b").cast("B").access("_c") For example: If you have a pointer "ThreadHandle" of a running program then you can access/retrieve "threadID" from the memory using TargetValue as below. TValue(context, thread_handle->th) /*__kmp_threads[t]->th*/ .cast("kmp_base_info_t") .access("th_info") /*__kmp_threads[t]->th.th_info*/ .cast("kmp_desc_t") .access("ds") /*__kmp_threads[t]->th.th_info.ds*/ .cast("kmp_desc_base_t") .access("ds_thread") /*__kmp_threads[t]->th.th_info.ds.ds_thread*/ .cast("kmp_thread_t") .getRawValue(thread_id, 1); Reviewed By: @hbae Differential Revision: https://reviews.llvm.org/D100182
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;
|
|
}
|