1// Licensed to the .NET Foundation under one or more agreements.
2// The .NET Foundation licenses this file to you under the MIT license.
3// See the LICENSE file in the project root for more information.
4//*****************************************************************************
5
6//
7// File: DacDbiInterface.inl
8//
9// Inline functions for DacDbiStructures.h
10//
11//*****************************************************************************
12
13#ifndef DACDBISTRUCTURES_INL_
14#define DACDBISTRUCTURES_INL_
15
16//-----------------------------------------------------------------------------------
17// DacDbiArrayList member function implementations
18//-----------------------------------------------------------------------------------
19
20// constructor--sets list to empty state
21// Arguments: none
22// Notes: this allocates no memory, so the list will not be ready to use
23template<class T>
24inline
25DacDbiArrayList<T>::DacDbiArrayList():
26 m_pList(NULL),
27 m_nEntries(0)
28 {
29 }
30
31// conversion constructor--takes a list of type T and a count and converts to a
32// DacDbiArrayList
33// Arguments:
34// input: list - a consecutive list (array) of elements of type T
35// count - the number of elements in list
36// Notes: - Allocates memory and copies the elements of list into "this"
37// - It is assumed that the list does NOT already have memory allocated; if it does,
38// calling Init will cause a leak.
39// - the element copy relies on the assignment operator for T
40// - may throw OOM
41template<class T>
42inline
43DacDbiArrayList<T>::DacDbiArrayList(const T * pList, int count):
44 m_pList(NULL),
45 m_nEntries(0)
46{
47 Init(pList, count);
48}
49
50// destructor: deallocates memory and sets list back to empty state
51// Arguments: none
52template<class T>
53inline
54DacDbiArrayList<T>::~DacDbiArrayList()
55{
56 Dealloc();
57}
58
59// explicitly deallocate the list and set it back to the empty state
60// Arguments: none
61// Notes: - Dealloc can be called multiple times without danger, since it
62// checks first that memory has been allocated
63template<class T>
64inline
65void DacDbiArrayList<T>::Dealloc()
66{
67 CONTRACT_VOID
68 {
69 NOTHROW;
70 }
71 CONTRACT_END;
72
73 if (m_pList != NULL)
74 {
75 delete [] m_pList;
76 m_pList = NULL;
77 }
78 m_nEntries = 0;
79 RETURN;
80}
81
82// Alloc and Init are very similar. Both preallocate the array; but Alloc leaves the
83// contents unintialized while Init provides initial values. The array contents are always
84// mutable.
85
86// allocate space for the list--in some instances, we'll know the count first, and then
87// we'll compute the elements one at a time. This (along with the array access operator
88// overload) allows us to handle that situation
89// Arguments:
90// input: nElements - number of elements of type T for which we need space
91// Notes:
92// - Alloc can be called multiple times and will free previous arrays.
93// - May throw OOM
94// - The array is not expandable, so you must allocate for all the elements at once.
95// - requesting an allocation of 0 or fewer bytes will not cause an error, but no memory is
96// allocated
97template<class T>
98inline
99void DacDbiArrayList<T>::Alloc(int nElements)
100{
101 Dealloc();
102 if (nElements > 0)
103 {
104 m_pList = new(forDbi) T[(size_t)nElements];
105 m_nEntries = nElements;
106 }
107}
108
109// allocate and initialize a DacDbiArrayList from a list of type T and a count
110// Arguments:
111// input: list - consecutive list (array) of elements of type T to be copied into
112// "this"
113// count - number of elements in list
114// Notes:
115// - May throw OOM
116// - Can be called multiple times with different lists, since this will deallocate
117// previous arrays.
118template<class T>
119inline
120void DacDbiArrayList<T>::Init(const T * pList, int count)
121{
122 _ASSERTE((m_pList == NULL) && (m_nEntries == 0));
123 if (count > 0)
124 {
125 Alloc(count);
126 m_nEntries = count;
127 for (int index = 0; index < count; ++index)
128 {
129 m_pList[index] = pList[index];
130 }
131 }
132}
133
134// read-only list element access
135template<class T>
136inline
137const T & DacDbiArrayList<T>::operator [](int i) const
138{
139 _ASSERTE(m_pList != NULL);
140 _ASSERTE((i >= 0) && (i < m_nEntries));
141 return m_pList[i];
142}
143
144// writeable list element access
145template<class T>
146inline
147T & DacDbiArrayList<T>::operator [](int i)
148{
149 _ASSERTE(m_pList != NULL);
150 _ASSERTE((i >= 0) && (i < m_nEntries));
151 return m_pList[i];
152}
153
154// get the number of elements in the list
155template<class T>
156inline
157int DacDbiArrayList<T>::Count() const
158{
159 return m_nEntries;
160}
161
162//-----------------------------------------------------------------------------
163// Target Buffer functions
164//-----------------------------------------------------------------------------
165
166// Default ctor
167inline
168TargetBuffer::TargetBuffer()
169{
170 this->pAddress = NULL;
171 this->cbSize = 0;
172}
173
174// Convenience Ctor to initialize around an (Address, size).
175inline
176TargetBuffer::TargetBuffer(CORDB_ADDRESS pBuffer, ULONG cbSizeInput)
177{
178 this->pAddress = pBuffer;
179 this->cbSize = cbSizeInput;
180}
181
182// Convenience Ctor to initialize around an (Address, size).
183inline
184TargetBuffer::TargetBuffer(void * pBuffer, ULONG cbSizeInput)
185{
186 this->pAddress = PTR_TO_CORDB_ADDRESS(pBuffer);
187 this->cbSize = cbSizeInput;
188}
189
190// Return a sub-buffer that's starts at byteOffset within this buffer and runs to the end.
191//
192// Arguments:
193// byteOffset - offset in bytes within this buffer that the new buffer starts at.
194//
195// Returns:
196// A new buffer that's a subset of the existing buffer.
197inline
198TargetBuffer TargetBuffer::SubBuffer(ULONG byteOffset) const
199{
200 _ASSERTE(byteOffset <= cbSize);
201 return TargetBuffer(pAddress + byteOffset, cbSize - byteOffset);
202}
203
204// Return a sub-buffer that starts at byteOffset within this buffer and is byteLength long.
205//
206// Arguments:
207// byteOffset - offset in bytes within this buffer that the new buffer starts at.
208// byteLength - length in bytes of the new buffer.
209//
210// Returns:
211// A new buffer that's a subset of the existing buffer.
212inline
213TargetBuffer TargetBuffer::SubBuffer(ULONG byteOffset, ULONG byteLength) const
214{
215 _ASSERTE(byteOffset + byteLength <= cbSize);
216 return TargetBuffer(pAddress + byteOffset, byteLength);
217}
218
219// Sets address to NULL and size to 0
220inline
221void TargetBuffer::Clear()
222{
223 pAddress = NULL;
224 cbSize = 0;
225}
226
227// Initialize fields
228inline
229void TargetBuffer::Init(CORDB_ADDRESS address, ULONG size)
230{
231 pAddress = address;
232 cbSize = size;
233}
234
235
236// Returns true iff the buffer is empty.
237inline
238bool TargetBuffer::IsEmpty() const
239{
240 return (this->cbSize == 0);
241}
242
243//-----------------------------------------------------------------------------
244// NativeVarData member function implementations
245//-----------------------------------------------------------------------------
246
247// Initialize a new instance of NativeVarData
248inline NativeVarData::NativeVarData() :
249 m_allArgsCount(0),
250 m_fInitialized(false)
251{
252}
253
254// destructor
255inline NativeVarData::~NativeVarData()
256{
257 m_fInitialized = false;
258 }
259
260// initialize the list of native var information structures, including the starting address of the list, the number of
261// entries and the number of fixed args.
262inline void NativeVarData::InitVarDataList(ICorDebugInfo::NativeVarInfo * pListStart,
263 int fixedArgCount,
264 int entryCount)
265{
266 m_offsetInfo.Init(pListStart, entryCount);
267 m_fixedArgsCount = fixedArgCount;
268 m_fInitialized = true;
269}
270
271//-----------------------------------------------------------------------------
272// SequencePoints member function implementations
273//-----------------------------------------------------------------------------
274
275// initializing constructor
276inline SequencePoints::SequencePoints() :
277 m_mapCount(0),
278 m_lastILOffset(0),
279 m_fInitialized(false)
280{
281}
282
283// destructor
284inline SequencePoints::~SequencePoints()
285{
286 m_fInitialized = false;
287 }
288
289// Initialize the m_pMap data member to the address of an allocated chunk
290// of memory (or to NULL if the count is zero). Set m_count as the
291// number of entries in the map.
292inline void SequencePoints::InitSequencePoints(ULONG32 count)
293{
294 m_map.Alloc(count),
295 m_fInitialized = true;
296}
297
298//
299// Map the given native offset to IL offset and return the mapping type.
300//
301// Arguments:
302// dwNativeOffset - the native offset to be mapped
303// pMapType - out parameter; return the mapping type
304//
305// Return Value:
306// Return the IL offset corresponding to the given native offset.
307// For a prolog, return 0.
308// For an epilog, return the IL offset of the last sequence point before the epilog.
309// If we can't map to an IL offset, then return 0, with a mapping type of MAPPING_NO_INFO.
310//
311// Assumptions:
312// The sequence points are sorted.
313//
314
315inline
316DWORD SequencePoints::MapNativeOffsetToIL(DWORD dwNativeOffset,
317 CorDebugMappingResult *pMapType)
318{
319 //_ASSERTE(IsInitialized());
320 if (!IsInitialized())
321 {
322 (*pMapType) = MAPPING_NO_INFO;
323 return 0;
324 }
325
326 _ASSERTE(pMapType != NULL);
327
328 int i;
329
330 for (i = 0; i < (int)m_mapCount; ++i)
331 {
332 // Check to determine if dwNativeOffset is within this sequence point. Checking the lower bound is trivial--
333 // we just make sure that dwNativeOffset >= m_map[i].nativeStartOffset.
334 // Checking to be sure it's before the end of the range is a little trickier. We can have
335 // m_map[i].nativeEndOffset = 0 for two reasons:
336 // 1. We use an end offset of 0 to signify that this end offset is also the end of the method.
337 // 2. We could also have an end offset of 0 if the IL prologue doesn't translate to any native
338 // instructions. Thus, the first native instruction (which will not be in the prologue) is at an offset
339 // of 0. The end offset is always set to the start offset of the next sequence point, so this means
340 // that both the start and end offsets of the (non-existent) native instruction range for the
341 // prologue is also 0.
342 // If the end offset is 0, we want to check if we're in the prologue before concluding that the
343 // value of dwNativeOffset is out of range.
344 if ((dwNativeOffset >= m_map[i].nativeStartOffset) &&
345 (((m_map[i].nativeEndOffset == 0) && (m_map[i].ilOffset != (ULONG)ICorDebugInfo::PROLOG)) ||
346 (dwNativeOffset < m_map[i].nativeEndOffset)))
347 {
348 ULONG uILOffset = m_map[i].ilOffset;
349
350 if (m_map[i].ilOffset == (ULONG)ICorDebugInfo::PROLOG)
351 {
352 uILOffset = 0;
353 (*pMapType) = MAPPING_PROLOG;
354 }
355 else if (m_map[i].ilOffset == (ULONG)ICorDebugInfo::NO_MAPPING)
356 {
357 uILOffset = 0;
358 (*pMapType) = MAPPING_UNMAPPED_ADDRESS;
359 }
360 else if (m_map[i].ilOffset == (ULONG)ICorDebugInfo::EPILOG)
361 {
362 uILOffset = m_lastILOffset;
363 (*pMapType) = MAPPING_EPILOG;
364 }
365 else if (dwNativeOffset == m_map[i].nativeStartOffset)
366 {
367 (*pMapType) = MAPPING_EXACT;
368 }
369 else
370 {
371 (*pMapType) = MAPPING_APPROXIMATE;
372 }
373 return uILOffset;
374 }
375 }
376
377 (*pMapType) = MAPPING_NO_INFO;
378 return 0;
379}
380
381//
382// Copy data from the VM map data to our own map structure and sort. The
383// information comes to us in a data structure that differs slightly from the
384// one we use out of process, so we have to copy it to the right-side struct.
385// Arguments
386// input
387// mapCopy sequence points
388// output
389// pSeqPoints.m_map is initialized with the correct right side representation of sequence points
390
391inline
392void SequencePoints::CopyAndSortSequencePoints(const ICorDebugInfo::OffsetMapping mapCopy[])
393{
394 // copy information to pSeqPoint and set end offsets
395 int i;
396
397 ULONG32 lastILOffset = 0;
398
399 const DWORD call_inst = (DWORD)ICorDebugInfo::CALL_INSTRUCTION;
400 for (i = 0; i < m_map.Count(); i++)
401 {
402 m_map[i].ilOffset = mapCopy[i].ilOffset;
403 m_map[i].nativeStartOffset = mapCopy[i].nativeOffset;
404
405 if (i < m_map.Count() - 1)
406 {
407 // We need to not use CALL_INSTRUCTION's IL start offset.
408 int j = i + 1;
409 while ((mapCopy[j].source & call_inst) == call_inst && j < m_map.Count()-1)
410 j++;
411
412 m_map[i].nativeEndOffset = mapCopy[j].nativeOffset;
413 }
414
415 m_map[i].source = mapCopy[i].source;
416
417 // need to cast the offsets to signed values first because we do actually use
418 // special negative offsets such as ICorDebugInfo::PROLOG
419 if ((m_map[i].source & call_inst) != call_inst)
420 lastILOffset = max((int)lastILOffset, (int)m_map[i].ilOffset);
421 }
422
423 if (m_map.Count() >= 1)
424 {
425 m_map[i - 1].nativeEndOffset = 0;
426 m_map[i - 1].source =
427 (ICorDebugInfo::SourceTypes)(m_map[i - 1].source | ICorDebugInfo::NATIVE_END_OFFSET_UNKNOWN);
428 }
429
430 // sort the map
431 MapSortILMap mapSorter(&m_map[0], m_map.Count());
432 mapSorter.Sort();
433
434
435 m_mapCount = m_map.Count();
436 while (m_mapCount > 0 && (m_map[m_mapCount-1].source & (call_inst)) == call_inst)
437 m_mapCount--;
438
439 SetLastILOffset(lastILOffset);
440} // CopyAndSortSequencePoints
441
442//-----------------------------------------------------------------------------
443// member function implementations for MapSortILMap class to sort sequence points
444// by IL offset
445//-----------------------------------------------------------------------------
446
447// secondary key comparison--if two IL offsets are the same,
448// we determine order based on native offset
449
450inline
451int SequencePoints::MapSortILMap::CompareInternal(DebuggerILToNativeMap *first,
452 DebuggerILToNativeMap *second)
453{
454 LIMITED_METHOD_CONTRACT;
455
456 if (first->nativeStartOffset == second->nativeStartOffset)
457 return 0;
458 else if (first->nativeStartOffset < second->nativeStartOffset)
459 return -1;
460 else
461 return 1;
462}
463
464//Comparison operator
465inline
466int SequencePoints::MapSortILMap::Compare(DebuggerILToNativeMap * first,
467 DebuggerILToNativeMap * second)
468{
469 LIMITED_METHOD_CONTRACT;
470 const DWORD call_inst = (DWORD)ICorDebugInfo::CALL_INSTRUCTION;
471
472 //PROLOGs go first
473 if (first->ilOffset == (ULONG) ICorDebugInfo::PROLOG &&
474 second->ilOffset == (ULONG) ICorDebugInfo::PROLOG)
475 {
476 return CompareInternal(first, second);
477 }
478 else if (first->ilOffset == (ULONG) ICorDebugInfo::PROLOG)
479 {
480 return -1;
481 }
482 else if (second->ilOffset == (ULONG) ICorDebugInfo::PROLOG)
483 {
484 return 1;
485 }
486 // call_instruction goes at the very very end of the table.
487 else if ((first->source & call_inst) == call_inst
488 && (second->source & call_inst) == call_inst)
489 {
490 return CompareInternal(first, second);
491 } else if ((first->source & call_inst) == call_inst)
492 {
493 return 1;
494 } else if ((second->source & call_inst) == call_inst)
495 {
496 return -1;
497 }
498 //NO_MAPPING go last
499 else if (first->ilOffset == (ULONG) ICorDebugInfo::NO_MAPPING &&
500 second->ilOffset == (ULONG) ICorDebugInfo::NO_MAPPING)
501 {
502 return CompareInternal(first, second);
503 }
504 else if (first->ilOffset == (ULONG) ICorDebugInfo::NO_MAPPING)
505 {
506 return 1;
507 }
508 else if (second->ilOffset == (ULONG) ICorDebugInfo::NO_MAPPING)
509 {
510 return -1;
511 }
512 //EPILOGs go next-to-last
513 else if (first->ilOffset == (ULONG) ICorDebugInfo::EPILOG &&
514 second->ilOffset == (ULONG) ICorDebugInfo::EPILOG)
515 {
516 return CompareInternal(first, second);
517 }
518 else if (first->ilOffset == (ULONG) ICorDebugInfo::EPILOG)
519 {
520 return 1;
521 }
522 else if (second->ilOffset == (ULONG) ICorDebugInfo::EPILOG)
523 {
524 return -1;
525 }
526 //normal offsets compared otherwise
527 else if (first->ilOffset < second->ilOffset)
528 {
529 return -1;
530 }
531 else if (first->ilOffset == second->ilOffset)
532 {
533 return CompareInternal(first, second);
534 }
535 else
536 {
537 return 1;
538 }
539}
540
541//-----------------------------------------------------------------------------
542// NativeCodeFunctionData member function implementations
543// (for getting native code regions)
544//-----------------------------------------------------------------------------
545
546inline
547CodeBlobRegion & operator++(CodeBlobRegion & rs)
548{
549 return rs = CodeBlobRegion(rs + 1);
550}
551
552// Convert the data in an instance of DebuggerIPCE_JITFUncData to an instance of NativeCodeFunctionData.
553// We need to have this latter type to look up or create a new CordbNativeCode object, but the stack walker is
554// using the former type to gather information.
555// Arguments:
556// Input:
557// source - an initialized instance of DebuggerIPCE_JITFuncData containing the information to
558// be copied into this instance of NativeCodeFunctionData
559// @dbgtodo dlaw: Once CordbThread::RefreshStack is fully DAC-ized, we can change the data structure that it uses
560// to have a member of type NativeCodeFunctionData which we can pass without copying. At that point,
561// this method can disappear.
562inline
563NativeCodeFunctionData::NativeCodeFunctionData(DebuggerIPCE_JITFuncData * source)
564{
565 // copy the code region information
566 m_rgCodeRegions[kHot].Init(CORDB_ADDRESS(source->nativeStartAddressPtr), (ULONG)source->nativeHotSize);
567 m_rgCodeRegions[kCold].Init(CORDB_ADDRESS(source->nativeStartAddressColdPtr), (ULONG)source->nativeColdSize);
568
569 // copy the other function information
570 isInstantiatedGeneric = source->isInstantiatedGeneric;
571 vmNativeCodeMethodDescToken = source->vmNativeCodeMethodDescToken;
572 encVersion = source->enCVersion;
573}
574
575
576// set all fields to default values (NULL, FALSE, or zero as appropriate)
577inline
578NativeCodeFunctionData::NativeCodeFunctionData()
579{
580 Clear();
581}
582
583inline
584void NativeCodeFunctionData::Clear()
585{
586 isInstantiatedGeneric = FALSE;
587 encVersion = CorDB_DEFAULT_ENC_FUNCTION_VERSION;
588 for (CodeBlobRegion region = kHot; region < MAX_REGIONS; ++region)
589 {
590 m_rgCodeRegions[region].Clear();
591 }
592}
593
594//-----------------------------------------------------------------------------------
595// ClassInfo member functions
596//-----------------------------------------------------------------------------------
597
598inline
599ClassInfo::ClassInfo():
600 m_objectSize(0)
601 {}
602
603// clear all fields
604inline
605void ClassInfo::Clear()
606{
607 m_objectSize = 0;
608 m_fieldList.Dealloc();
609}
610
611inline
612ClassInfo::~ClassInfo()
613{
614 m_fieldList.Dealloc();
615}
616
617//-----------------------------------------------------------------------------------
618// FieldData member functions
619//-----------------------------------------------------------------------------------
620#ifndef RIGHT_SIDE_COMPILE
621
622// initialize various fields of an instance of FieldData from information retrieved from a FieldDesc
623inline
624void FieldData::Initialize(BOOL fIsStatic, BOOL fIsPrimitive, mdFieldDef mdToken)
625{
626 ClearFields();
627 m_fFldIsStatic = (fIsStatic == TRUE);
628 m_fFldIsPrimitive = (fIsPrimitive == TRUE);
629 // This is what tells the right side the field is unavailable due to EnC.
630 m_fldMetadataToken = mdToken;
631}
632#endif
633
634// clear various fields for a new instance of FieldData
635inline
636void FieldData::ClearFields()
637{
638 m_fldSignatureCache = NULL;
639 m_fldSignatureCacheSize = 0;
640 m_fldInstanceOffset = 0;
641 m_pFldStaticAddress = NULL;
642}
643
644typedef ULONG_PTR SIZE_T;
645
646inline
647BOOL FieldData::OkToGetOrSetInstanceOffset()
648{
649 return (!m_fFldIsStatic && !m_fFldIsRVA && !m_fFldIsTLS &&
650 m_fFldStorageAvailable && (m_pFldStaticAddress == NULL));
651}
652
653// If this is an instance field, store its offset
654inline
655void FieldData::SetInstanceOffset(SIZE_T offset)
656{
657 _ASSERTE(!m_fFldIsStatic);
658 _ASSERTE(!m_fFldIsRVA);
659 _ASSERTE(!m_fFldIsTLS);
660 _ASSERTE(m_fFldStorageAvailable);
661 _ASSERTE(m_pFldStaticAddress == NULL);
662 m_fldInstanceOffset = offset;
663}
664
665inline
666BOOL FieldData::OkToGetOrSetStaticAddress()
667{
668 return (m_fFldIsStatic && !m_fFldIsTLS &&
669 m_fFldStorageAvailable && (m_fldInstanceOffset == 0));
670}
671
672// If this is a "normal" static, store its absolute address
673inline
674void FieldData::SetStaticAddress(TADDR addr)
675{
676 _ASSERTE(m_fFldIsStatic);
677 _ASSERTE(!m_fFldIsTLS);
678 _ASSERTE(m_fFldStorageAvailable);
679 _ASSERTE(m_fldInstanceOffset == 0);
680 m_pFldStaticAddress = TADDR(addr);
681}
682
683// Get the offset of a field
684inline
685SIZE_T FieldData::GetInstanceOffset()
686{
687 _ASSERTE(!m_fFldIsStatic);
688 _ASSERTE(!m_fFldIsRVA);
689 _ASSERTE(!m_fFldIsTLS);
690 _ASSERTE(m_fFldStorageAvailable);
691 _ASSERTE(m_pFldStaticAddress == NULL);
692 return m_fldInstanceOffset;
693}
694
695// Get the static address for a field
696inline
697TADDR FieldData::GetStaticAddress()
698{
699 _ASSERTE(m_fFldIsStatic);
700 _ASSERTE(!m_fFldIsTLS);
701 _ASSERTE(m_fFldStorageAvailable || (m_pFldStaticAddress == NULL));
702 _ASSERTE(m_fldInstanceOffset == 0);
703 return m_pFldStaticAddress;
704}
705
706//-----------------------------------------------------------------------------------
707// EnCHangingFieldInfo member functions
708//-----------------------------------------------------------------------------------
709
710inline
711void EnCHangingFieldInfo::Init(VMPTR_Object pObject,
712 SIZE_T offset,
713 mdFieldDef fieldToken,
714 CorElementType elementType,
715 mdTypeDef metadataToken,
716 VMPTR_DomainFile vmDomainFile)
717 {
718 m_vmObject = pObject;
719 m_offsetToVars = offset;
720 m_fldToken = fieldToken;
721 m_objectTypeData.elementType = elementType;
722 m_objectTypeData.metadataToken = metadataToken;
723 m_objectTypeData.vmDomainFile = vmDomainFile;
724 }
725
726
727
728#endif // DACDBISTRUCTURES_INL_
729