| 1 | /* |
| 2 | * TargetValue.cpp -- Access to target values using OMPD callbacks |
| 3 | */ |
| 4 | |
| 5 | //===----------------------------------------------------------------------===// |
| 6 | // |
| 7 | // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
| 8 | // See https://llvm.org/LICENSE.txt for license information. |
| 9 | // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
| 10 | // |
| 11 | //===----------------------------------------------------------------------===// |
| 12 | |
| 13 | #include "TargetValue.h" |
| 14 | #include "Debug.h" |
| 15 | #include <cstring> |
| 16 | #include <fstream> |
| 17 | #include <iostream> |
| 18 | #include <sstream> |
| 19 | |
| 20 | const ompd_callbacks_t *TValue::callbacks = NULL; |
| 21 | ompd_device_type_sizes_t TValue::type_sizes; |
| 22 | |
| 23 | inline int ompd_sizeof(ompd_target_prim_types_t t) { |
| 24 | assert(t != ompd_type_max && "ompd_type_max should not be used anywhere" ); |
| 25 | assert(t != ompd_type_invalid && "request size of invalid type" ); |
| 26 | |
| 27 | return (((char *)&TValue::type_sizes)[(int)t]); |
| 28 | } |
| 29 | |
| 30 | TType &TTypeFactory::getType(ompd_address_space_context_t *context, |
| 31 | const char *typeName, ompd_addr_t segment) { |
| 32 | TType empty(true); |
| 33 | |
| 34 | if (ttypes.find(x: context) == ttypes.end()) { |
| 35 | std::map<const char *, TType> empty; |
| 36 | ttypes[context] = empty; |
| 37 | } |
| 38 | |
| 39 | auto t = ttypes.find(x: context); |
| 40 | auto i = t->second.find(x: typeName); |
| 41 | if (i == t->second.end()) |
| 42 | i = t->second.insert( |
| 43 | position: i, x: std::make_pair(x&: typeName, y: TType(context, typeName, segment))); |
| 44 | else |
| 45 | i->second.context = context; |
| 46 | |
| 47 | return i->second; |
| 48 | } |
| 49 | |
| 50 | TType::TType(ompd_address_space_context_t *_context, const char *_typeName, |
| 51 | ompd_addr_t _segment) |
| 52 | : typeSize(0), fieldOffsets(), descSegment(_segment), typeName(_typeName), |
| 53 | context(_context), isvoid(false) {} |
| 54 | |
| 55 | ompd_rc_t TType::getSize(ompd_size_t *size) { |
| 56 | ompd_rc_t ret = ompd_rc_ok; |
| 57 | if (typeSize == 0) { |
| 58 | ompd_address_t symbolAddr; |
| 59 | ompd_size_t tmpSize; |
| 60 | std::stringstream ss; |
| 61 | ss << "ompd_sizeof__" << typeName; |
| 62 | |
| 63 | ret = TValue::callbacks->symbol_addr_lookup(context, NULL, ss.str().c_str(), |
| 64 | &symbolAddr, NULL); |
| 65 | if (ret != ompd_rc_ok) { |
| 66 | dout << "missing symbol " << ss.str() |
| 67 | << " add this to ompd-specific.h:\nOMPD_SIZEOF(" << typeName |
| 68 | << ") \\" << std::endl; |
| 69 | return ret; |
| 70 | } |
| 71 | |
| 72 | symbolAddr.segment = descSegment; |
| 73 | |
| 74 | ret = TValue::callbacks->read_memory( |
| 75 | context, NULL, &symbolAddr, 1 * TValue::type_sizes.sizeof_long_long, |
| 76 | &(tmpSize)); |
| 77 | if (ret != ompd_rc_ok) |
| 78 | return ret; |
| 79 | ret = TValue::callbacks->device_to_host( |
| 80 | context, &tmpSize, TValue::type_sizes.sizeof_long_long, 1, &(typeSize)); |
| 81 | } |
| 82 | *size = typeSize; |
| 83 | return ret; |
| 84 | } |
| 85 | |
| 86 | ompd_rc_t TType::getBitfieldMask(const char *fieldName, |
| 87 | uint64_t *bitfieldmask) { |
| 88 | ompd_rc_t ret = ompd_rc_ok; |
| 89 | auto i = bitfieldMasks.find(x: fieldName); |
| 90 | if (i == bitfieldMasks.end()) { |
| 91 | uint64_t tmpMask, bitfieldMask; |
| 92 | ompd_address_t symbolAddr; |
| 93 | std::stringstream ss; |
| 94 | ss << "ompd_bitfield__" << typeName << "__" << fieldName; |
| 95 | ret = TValue::callbacks->symbol_addr_lookup(context, NULL, ss.str().c_str(), |
| 96 | &symbolAddr, NULL); |
| 97 | if (ret != ompd_rc_ok) { |
| 98 | dout << "missing symbol " << ss.str() |
| 99 | << " add this to ompd-specific.h:\nOMPD_BITFIELD(" << typeName << "," |
| 100 | << fieldName << ") \\" << std::endl; |
| 101 | return ret; |
| 102 | } |
| 103 | symbolAddr.segment = descSegment; |
| 104 | |
| 105 | ret = TValue::callbacks->read_memory( |
| 106 | context, NULL, &symbolAddr, 1 * TValue::type_sizes.sizeof_long_long, |
| 107 | &(tmpMask)); |
| 108 | if (ret != ompd_rc_ok) |
| 109 | return ret; |
| 110 | ret = TValue::callbacks->device_to_host(context, &(tmpMask), |
| 111 | TValue::type_sizes.sizeof_long_long, |
| 112 | 1, &(bitfieldMask)); |
| 113 | if (ret != ompd_rc_ok) { |
| 114 | return ret; |
| 115 | } |
| 116 | i = bitfieldMasks.insert(position: i, x: std::make_pair(x&: fieldName, y&: bitfieldMask)); |
| 117 | } |
| 118 | *bitfieldmask = i->second; |
| 119 | return ret; |
| 120 | } |
| 121 | |
| 122 | ompd_rc_t TType::getElementOffset(const char *fieldName, ompd_size_t *offset) { |
| 123 | ompd_rc_t ret = ompd_rc_ok; |
| 124 | auto i = fieldOffsets.find(x: fieldName); |
| 125 | if (i == fieldOffsets.end()) { |
| 126 | ompd_size_t tmpOffset, fieldOffset; |
| 127 | ompd_address_t symbolAddr; |
| 128 | std::stringstream ss; |
| 129 | ss << "ompd_access__" << typeName << "__" << fieldName; |
| 130 | |
| 131 | ret = TValue::callbacks->symbol_addr_lookup(context, NULL, ss.str().c_str(), |
| 132 | &symbolAddr, NULL); |
| 133 | if (ret != ompd_rc_ok) { |
| 134 | dout << "missing symbol " << ss.str() |
| 135 | << " add this to ompd-specific.h:\nOMPD_ACCESS(" << typeName << "," |
| 136 | << fieldName << ") \\" << std::endl; |
| 137 | return ret; |
| 138 | } |
| 139 | symbolAddr.segment = descSegment; |
| 140 | |
| 141 | ret = TValue::callbacks->read_memory( |
| 142 | context, NULL, &symbolAddr, 1 * TValue::type_sizes.sizeof_long_long, |
| 143 | &(tmpOffset)); |
| 144 | if (ret != ompd_rc_ok) |
| 145 | return ret; |
| 146 | ret = TValue::callbacks->device_to_host(context, &(tmpOffset), |
| 147 | TValue::type_sizes.sizeof_long_long, |
| 148 | 1, &fieldOffset); |
| 149 | if (ret != ompd_rc_ok) { |
| 150 | return ret; |
| 151 | } |
| 152 | i = fieldOffsets.insert(position: i, x: std::make_pair(x&: fieldName, y&: fieldOffset)); |
| 153 | } |
| 154 | *offset = i->second; |
| 155 | return ret; |
| 156 | } |
| 157 | |
| 158 | ompd_rc_t TType::getElementSize(const char *fieldName, ompd_size_t *size) { |
| 159 | ompd_rc_t ret = ompd_rc_ok; |
| 160 | auto i = fieldSizes.find(x: fieldName); |
| 161 | if (i == fieldSizes.end()) { |
| 162 | ompd_size_t tmpOffset, fieldSize; |
| 163 | ompd_address_t symbolAddr; |
| 164 | std::stringstream ss; |
| 165 | ss << "ompd_sizeof__" << typeName << "__" << fieldName; |
| 166 | |
| 167 | ret = TValue::callbacks->symbol_addr_lookup(context, NULL, ss.str().c_str(), |
| 168 | &symbolAddr, NULL); |
| 169 | if (ret != ompd_rc_ok) { |
| 170 | dout << "missing symbol " << ss.str() |
| 171 | << " add this to ompd-specific.h:\nOMPD_ACCESS(" << typeName << "," |
| 172 | << fieldName << ") \\" << std::endl; |
| 173 | return ret; |
| 174 | } |
| 175 | symbolAddr.segment = descSegment; |
| 176 | |
| 177 | ret = TValue::callbacks->read_memory( |
| 178 | context, NULL, &symbolAddr, 1 * TValue::type_sizes.sizeof_long_long, |
| 179 | &(tmpOffset)); |
| 180 | if (ret != ompd_rc_ok) |
| 181 | return ret; |
| 182 | ret = TValue::callbacks->device_to_host(context, &tmpOffset, |
| 183 | TValue::type_sizes.sizeof_long_long, |
| 184 | 1, &fieldSize); |
| 185 | if (ret != ompd_rc_ok) { |
| 186 | return ret; |
| 187 | } |
| 188 | i = fieldSizes.insert(position: i, x: std::make_pair(x&: fieldName, y&: fieldSize)); |
| 189 | } |
| 190 | *size = i->second; |
| 191 | return ret; |
| 192 | } |
| 193 | |
| 194 | TValue::TValue(ompd_address_space_context_t *_context, |
| 195 | ompd_thread_context_t *_tcontext, const char *_valueName, |
| 196 | ompd_addr_t segment) |
| 197 | : errorState(ompd_rc_ok), type(&nullType), pointerLevel(0), |
| 198 | context(_context), tcontext(_tcontext), fieldSize(0) { |
| 199 | errorState.errorCode = callbacks->symbol_addr_lookup( |
| 200 | context, tcontext, _valueName, &symbolAddr, NULL); |
| 201 | symbolAddr.segment = segment; |
| 202 | } |
| 203 | |
| 204 | TValue::TValue(ompd_address_space_context_t *_context, |
| 205 | ompd_thread_context_t *_tcontext, ompd_address_t addr) |
| 206 | : errorState(ompd_rc_ok), type(&nullType), pointerLevel(0), |
| 207 | context(_context), tcontext(_tcontext), symbolAddr(addr), fieldSize(0) { |
| 208 | if (addr.address == 0) |
| 209 | errorState.errorCode = ompd_rc_bad_input; |
| 210 | } |
| 211 | |
| 212 | TValue &TValue::cast(const char *typeName) { |
| 213 | if (gotError()) |
| 214 | return *this; |
| 215 | type = &tf.getType(context, typeName, segment: symbolAddr.segment); |
| 216 | pointerLevel = 0; |
| 217 | assert(!type->isVoid() && "cast to invalid type failed" ); |
| 218 | return *this; |
| 219 | } |
| 220 | |
| 221 | TValue &TValue::cast(const char *typeName, int _pointerLevel, |
| 222 | ompd_addr_t segment) { |
| 223 | if (gotError()) |
| 224 | return *this; |
| 225 | type = &tf.getType(context, typeName, segment: symbolAddr.segment); |
| 226 | pointerLevel = _pointerLevel; |
| 227 | symbolAddr.segment = segment; |
| 228 | assert(!type->isVoid() && "cast to invalid type failed" ); |
| 229 | return *this; |
| 230 | } |
| 231 | |
| 232 | TValue TValue::dereference() const { |
| 233 | if (gotError()) |
| 234 | return *this; |
| 235 | ompd_address_t tmpAddr; |
| 236 | assert(!type->isVoid() && "cannot work with void" ); |
| 237 | assert(pointerLevel > 0 && "cannot dereference non-pointer" ); |
| 238 | TValue ret = *this; |
| 239 | ret.pointerLevel--; |
| 240 | ret.errorState.errorCode = callbacks->read_memory( |
| 241 | context, tcontext, &symbolAddr, 1 * TValue::type_sizes.sizeof_pointer, |
| 242 | &(tmpAddr.address)); |
| 243 | if (ret.errorState.errorCode != ompd_rc_ok) |
| 244 | return ret; |
| 245 | |
| 246 | ret.errorState.errorCode = callbacks->device_to_host( |
| 247 | context, &(tmpAddr.address), TValue::type_sizes.sizeof_pointer, 1, |
| 248 | &(ret.symbolAddr.address)); |
| 249 | if (ret.errorState.errorCode != ompd_rc_ok) { |
| 250 | return ret; |
| 251 | } |
| 252 | if (ret.symbolAddr.address == 0) |
| 253 | ret.errorState.errorCode = ompd_rc_unsupported; |
| 254 | return ret; |
| 255 | } |
| 256 | |
| 257 | ompd_rc_t TValue::getAddress(ompd_address_t *addr) const { |
| 258 | *addr = symbolAddr; |
| 259 | if (symbolAddr.address == 0) |
| 260 | return ompd_rc_unsupported; |
| 261 | return errorState.errorCode; |
| 262 | } |
| 263 | |
| 264 | ompd_rc_t TValue::getRawValue(void *buf, int count) { |
| 265 | if (errorState.errorCode != ompd_rc_ok) |
| 266 | return errorState.errorCode; |
| 267 | ompd_size_t size; |
| 268 | errorState.errorCode = type->getSize(size: &size); |
| 269 | if (errorState.errorCode != ompd_rc_ok) |
| 270 | return errorState.errorCode; |
| 271 | |
| 272 | errorState.errorCode = |
| 273 | callbacks->read_memory(context, tcontext, &symbolAddr, size, buf); |
| 274 | return errorState.errorCode; |
| 275 | } |
| 276 | |
| 277 | ompd_rc_t TValue::getString(const char **buf) { |
| 278 | *buf = 0; |
| 279 | if (gotError()) |
| 280 | return getError(); |
| 281 | |
| 282 | TValue strValue = dereference(); |
| 283 | if (strValue.gotError()) { |
| 284 | return strValue.getError(); |
| 285 | } |
| 286 | |
| 287 | if (!callbacks) { |
| 288 | return ompd_rc_error; |
| 289 | } |
| 290 | ompd_rc_t ret; |
| 291 | #define BUF_LEN 512 |
| 292 | char *string_buffer; |
| 293 | |
| 294 | // Allocate an extra byte, but pass only BUF_LEN to the tool |
| 295 | // so that we can detect truncation later. |
| 296 | ret = callbacks->alloc_memory(BUF_LEN + 1, (void **)&string_buffer); |
| 297 | if (ret != ompd_rc_ok) { |
| 298 | return ret; |
| 299 | } |
| 300 | string_buffer[BUF_LEN] = '\0'; |
| 301 | |
| 302 | // TODO: if we have not read in the complete string, we need to realloc |
| 303 | // 'string_buffer' and attempt reading again repeatedly till the entire string |
| 304 | // is read in. |
| 305 | ret = callbacks->read_string(context, tcontext, &strValue.symbolAddr, BUF_LEN, |
| 306 | (void *)string_buffer); |
| 307 | *buf = string_buffer; |
| 308 | // Check for truncation. The standard specifies that if a null byte is not |
| 309 | // among the first 'nbytes' bytes, the string placed in the buffer is not |
| 310 | // null-terminated. 'nbytes' is BUF_LEN in this case. |
| 311 | if (ret == ompd_rc_ok && strlen(s: string_buffer) == BUF_LEN) { |
| 312 | return ompd_rc_error; |
| 313 | } |
| 314 | return ret; |
| 315 | } |
| 316 | |
| 317 | TBaseValue TValue::castBase(const char *varName) { |
| 318 | ompd_size_t size; |
| 319 | errorState.errorCode = |
| 320 | tf.getType(context, typeName: varName, segment: symbolAddr.segment).getSize(size: &size); |
| 321 | return TBaseValue(*this, size); |
| 322 | } |
| 323 | |
| 324 | TBaseValue TValue::castBase() const { |
| 325 | if (pointerLevel > 0) |
| 326 | return TBaseValue(*this, type_sizes.sizeof_pointer); |
| 327 | return TBaseValue(*this, fieldSize); |
| 328 | } |
| 329 | |
| 330 | TBaseValue TValue::castBase(ompd_target_prim_types_t baseType) const { |
| 331 | return TBaseValue(*this, baseType); |
| 332 | } |
| 333 | |
| 334 | TValue TValue::access(const char *fieldName) const { |
| 335 | if (gotError()) |
| 336 | return *this; |
| 337 | TValue ret = *this; |
| 338 | assert(pointerLevel < 2 && "access to field element of pointer array failed" ); |
| 339 | if (pointerLevel == 1) // -> operator |
| 340 | ret = ret.dereference(); |
| 341 | // we use *this for . operator |
| 342 | ompd_size_t offset; |
| 343 | ret.errorState.errorCode = type->getElementOffset(fieldName, offset: &offset); |
| 344 | ret.errorState.errorCode = type->getElementSize(fieldName, size: &(ret.fieldSize)); |
| 345 | ret.symbolAddr.address += offset; |
| 346 | |
| 347 | return ret; |
| 348 | } |
| 349 | |
| 350 | ompd_rc_t TValue::check(const char *bitfieldName, ompd_word_t *isSet) const { |
| 351 | if (gotError()) |
| 352 | return getError(); |
| 353 | int bitfield; |
| 354 | uint64_t bitfieldmask; |
| 355 | ompd_rc_t ret = this->castBase(baseType: ompd_type_int).getValue(buf: &bitfield, count: 1); |
| 356 | if (ret != ompd_rc_ok) |
| 357 | return ret; |
| 358 | ret = type->getBitfieldMask(fieldName: bitfieldName, bitfieldmask: &bitfieldmask); |
| 359 | *isSet = ((bitfield & bitfieldmask) != 0); |
| 360 | return ret; |
| 361 | } |
| 362 | |
| 363 | TValue TValue::getArrayElement(int elemNumber) const { |
| 364 | if (gotError()) |
| 365 | return *this; |
| 366 | TValue ret; |
| 367 | if (pointerLevel > 0) { |
| 368 | ret = dereference(); |
| 369 | } else { |
| 370 | ret = *this; |
| 371 | } |
| 372 | if (ret.pointerLevel == 0) { |
| 373 | ompd_size_t size; |
| 374 | ret.errorState.errorCode = type->getSize(size: &size); |
| 375 | ret.symbolAddr.address += elemNumber * size; |
| 376 | } else { |
| 377 | ret.symbolAddr.address += elemNumber * type_sizes.sizeof_pointer; |
| 378 | } |
| 379 | return ret; |
| 380 | } |
| 381 | |
| 382 | TValue TValue::getPtrArrayElement(int elemNumber) const { |
| 383 | if (gotError()) { |
| 384 | return *this; |
| 385 | } |
| 386 | assert(pointerLevel > 0 && "This only works on arrays of pointers" ); |
| 387 | TValue ret = *this; |
| 388 | ret.symbolAddr.address += elemNumber * type_sizes.sizeof_pointer; |
| 389 | return ret; |
| 390 | } |
| 391 | |
| 392 | TBaseValue::TBaseValue(const TValue &_tvalue, |
| 393 | ompd_target_prim_types_t _baseType) |
| 394 | : TValue(_tvalue), baseTypeSize(ompd_sizeof(t: _baseType)) {} |
| 395 | TBaseValue::TBaseValue(const TValue &_tvalue, ompd_size_t _baseTypeSize) |
| 396 | : TValue(_tvalue), baseTypeSize(_baseTypeSize) {} |
| 397 | |
| 398 | ompd_rc_t TBaseValue::getValue(void *buf, int count) { |
| 399 | if (errorState.errorCode != ompd_rc_ok) |
| 400 | return errorState.errorCode; |
| 401 | errorState.errorCode = callbacks->read_memory(context, tcontext, &symbolAddr, |
| 402 | count * baseTypeSize, buf); |
| 403 | if (errorState.errorCode != ompd_rc_ok) |
| 404 | return errorState.errorCode; |
| 405 | errorState.errorCode = |
| 406 | callbacks->device_to_host(context, buf, baseTypeSize, count, buf); |
| 407 | return errorState.errorCode; |
| 408 | } |
| 409 | |