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 | |