-
Notifications
You must be signed in to change notification settings - Fork 4.9k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[cDAC] Implement basic stackwalking #111759
base: main
Are you sure you want to change the base?
Changes from 25 commits
a213ec2
8f2b7e7
6f2f4ab
0259f83
cd8854c
17e34fd
22a3c9a
5d9317a
2a1e410
da73082
a794374
3d22034
18e63a3
25f25c2
831fb14
4f97647
721042c
2887c4c
1df3093
3696838
373f9ef
5edb98d
1d1beda
a1703a0
71d5590
fb4109c
4fc3f22
bfb63ee
1961e4d
ce13fdc
1d3799e
4e40652
af93b5d
0d2c3d9
82e9ece
333a932
bcdf2e4
d3fdf0b
a246775
90fd607
93a8345
746f584
bd41361
da0425a
c7ad322
bf1743b
0a03272
0295a95
fa50b1c
54e3cb3
ba2a8ab
4945be7
cd15192
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,67 @@ | ||
# Contract StackWalk | ||
|
||
This contract encapsulates support for StackWalking managed threads. | ||
|
||
## APIs of contract | ||
|
||
```csharp | ||
public interface IStackWalkHandle { }; | ||
public interface IStackDataFrameHandle { }; | ||
``` | ||
|
||
```csharp | ||
// Creates a stack walk and returns a handle | ||
IStackWalkHandle CreateStackWalk(ThreadData threadData); | ||
// Iterates the stackWalkHandle to the next frame. If successful, returns true. Otherwise false. | ||
bool Next(IStackWalkHandle stackWalkHandle); | ||
// Gets the current frame from a stack walk and returns a IStackDataFrameHandle to it. | ||
IStackDataFrameHandle GetCurrentFrame(IStackWalkHandle stackWalkHandle); | ||
max-charlamb marked this conversation as resolved.
Show resolved
Hide resolved
|
||
// Gets the thread context at the given stack dataframe. | ||
byte[] GetRawContext(IStackDataFrameHandle stackDataFrameHandle); | ||
// Gets the Frame address at the given stack dataframe. Returns TargetPointer.Null if the current dataframe does not have a valid Frame. | ||
TargetPointer GetFrameAddress(IStackDataFrameHandle stackDataFrameHandle); | ||
``` | ||
|
||
## Version 1 | ||
|
||
The `StackWalk` contract provides an interface for walking the stack of a managed thread. It includes methods to create a stack walk, move to the next frame, get the current frame, retrieve the raw context, and get the frame address. | ||
|
||
### CreateStackWalk | ||
|
||
```csharp | ||
IStackWalkHandle CreateStackWalk(ThreadData threadData); | ||
``` | ||
|
||
Creates a stack walk handle for the given thread data. This initializes the context and frame iterator for the stack walk. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I don't see documentation about how to do all of this. We need that before we can actually merge this change, not just C# implementation in the actual cDAC codebase. Where we use the a well documented standard like the Win32 unwinder, you can just drop in a link, but for everything else... it needs correct documentation/algorithms |
||
|
||
### Next | ||
|
||
```csharp | ||
bool Next(IStackWalkHandle stackWalkHandle); | ||
``` | ||
|
||
Moves to the next frame in the stack walk. Returns `true` if successfully moved to the next frame. Otherwise returns `false`. | ||
|
||
### GetCurrentFrame | ||
|
||
```csharp | ||
IStackDataFrameHandle GetCurrentFrame(IStackWalkHandle stackWalkHandle); | ||
``` | ||
|
||
Gets the current frame in the stack walk. Returns a handle to the stack data frame. | ||
|
||
### GetRawContext | ||
|
||
```csharp | ||
byte[] GetRawContext(IStackDataFrameHandle stackDataFrameHandle); | ||
``` | ||
|
||
Retrieves the raw Windows thread context of the current frame as a byte array. The size and shape of the context is platform dependent. See [CONTEXT structure](https://learn.microsoft.com/en-us/windows/win32/api/winnt/ns-winnt-context) for more info. | ||
|
||
### GetFrameAddress | ||
|
||
```csharp | ||
TargetPointer GetFrameAddress(IStackDataFrameHandle stackDataFrameHandle); | ||
``` | ||
|
||
Gets the frame address of the current frame. Returns `TargetPointer.Null` if the frame is not valid. |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -367,7 +367,7 @@ extends: | |
- windows_x64 | ||
jobParameters: | ||
templatePath: 'templates-official' | ||
buildArgs: -s tools+libs -pack -c $(_BuildConfig) /p:TestAssemblies=false /p:TestPackages=true | ||
buildArgs: -s tools.illink+libs -pack -c $(_BuildConfig) /p:TestAssemblies=false /p:TestPackages=true | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Discussed with @jkoritzinsky , cDAC should not currently be built in the |
||
nameSuffix: Libraries_WithPackages | ||
isOfficialBuild: ${{ variables.isOfficialBuild }} | ||
postBuildSteps: | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -20,5 +20,6 @@ | |
"PrecodeStubs": 1, | ||
"ReJIT": 1, | ||
"RuntimeTypeSystem": 1, | ||
"StackWalk": 1, | ||
"Thread": 1 | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,31 +1,92 @@ | ||
include_directories(BEFORE ${VM_DIR}) | ||
include_directories(BEFORE ${VM_DIR}/${ARCH_SOURCES_DIR}) | ||
include_directories(BEFORE ${CMAKE_CURRENT_SOURCE_DIR}) | ||
include_directories(BEFORE ${CLR_DIR}/unwinder) | ||
include_directories(${CLR_DIR}/debug/ee) | ||
include_directories(${CLR_DIR}/gc) | ||
include_directories(${CLR_DIR}/gcdump) | ||
include_directories(${CLR_DIR}/debug/daccess) | ||
|
||
set(UNWINDER_SOURCES | ||
baseunwinder.cpp | ||
) | ||
|
||
# Include platform specific unwinder for applicable (native and cross-target) builds. | ||
include_directories(${ARCH_SOURCES_DIR}) | ||
list(APPEND UNWINDER_SOURCES | ||
${ARCH_SOURCES_DIR}/unwinder.cpp | ||
) | ||
|
||
convert_to_absolute_path(UNWINDER_SOURCES ${UNWINDER_SOURCES}) | ||
|
||
if(CLR_CMAKE_HOST_UNIX) | ||
add_library_clr(unwinder_wks OBJECT ${UNWINDER_SOURCES}) | ||
target_include_directories(unwinder_wks BEFORE PRIVATE ${VM_DIR}) | ||
target_include_directories(unwinder_wks BEFORE PRIVATE ${VM_DIR}/${ARCH_SOURCES_DIR}) | ||
max-charlamb marked this conversation as resolved.
Show resolved
Hide resolved
|
||
target_include_directories(unwinder_wks BEFORE PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}) | ||
target_include_directories(unwinder_wks BEFORE PRIVATE ${CLR_DIR}/unwinder) | ||
target_include_directories(unwinder_wks PRIVATE ${CLR_DIR}/debug/ee) | ||
target_include_directories(unwinder_wks PRIVATE ${CLR_DIR}/gc) | ||
target_include_directories(unwinder_wks PRIVATE ${CLR_DIR}/gcdump) | ||
target_include_directories(unwinder_wks PRIVATE ${CLR_DIR}/debug/daccess) | ||
target_include_directories(unwinder_wks PRIVATE ${ARCH_SOURCES_DIR}) | ||
add_dependencies(unwinder_wks eventing_headers) | ||
endif(CLR_CMAKE_HOST_UNIX) | ||
|
||
add_library_clr(unwinder_dac ${UNWINDER_SOURCES}) | ||
target_include_directories(unwinder_dac BEFORE PRIVATE ${VM_DIR}) | ||
target_include_directories(unwinder_dac BEFORE PRIVATE ${VM_DIR}/${ARCH_SOURCES_DIR}) | ||
target_include_directories(unwinder_dac BEFORE PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}) | ||
target_include_directories(unwinder_dac BEFORE PRIVATE ${CLR_DIR}/unwinder) | ||
target_include_directories(unwinder_dac PRIVATE ${CLR_DIR}/debug/ee) | ||
target_include_directories(unwinder_dac PRIVATE ${CLR_DIR}/gc) | ||
target_include_directories(unwinder_dac PRIVATE ${CLR_DIR}/gcdump) | ||
target_include_directories(unwinder_dac PRIVATE ${CLR_DIR}/debug/daccess) | ||
target_include_directories(unwinder_dac PRIVATE ${ARCH_SOURCES_DIR}) | ||
add_dependencies(unwinder_dac eventing_headers) | ||
set_target_properties(unwinder_dac PROPERTIES DAC_COMPONENT TRUE) | ||
target_compile_definitions(unwinder_dac PRIVATE FEATURE_NO_HOST) | ||
|
||
# Helper function for platform specific cDAC uwninder builds. | ||
function(create_platform_unwinder) | ||
set(oneValueArgs TARGET ARCH) | ||
set(multiValueArgs DESTINATIONS) | ||
cmake_parse_arguments(TARGETDETAILS "" "${oneValueArgs}" "${multiValueArgs}" ${ARGN}) | ||
|
||
if(TARGETDETAILS_ARCH STREQUAL "x64") | ||
set(ARCH_SOURCES_DIR amd64) | ||
elseif((TARGETDETAILS_ARCH STREQUAL "arm") OR (TARGETDETAILS_ARCH STREQUAL "armel")) | ||
set(ARCH_SOURCES_DIR arm) | ||
elseif(TARGETDETAILS_ARCH STREQUAL "x86") | ||
set(ARCH_SOURCES_DIR i386) | ||
elseif(TARGETDETAILS_ARCH STREQUAL "arm64") | ||
set(ARCH_SOURCES_DIR arm64) | ||
else() | ||
clr_unknown_arch() | ||
endif() | ||
|
||
set(UNWINDER_SOURCES | ||
baseunwinder.cpp | ||
${ARCH_SOURCES_DIR}/unwinder.cpp | ||
) | ||
|
||
convert_to_absolute_path(UNWINDER_SOURCES ${UNWINDER_SOURCES}) | ||
|
||
add_library_clr(${TARGETDETAILS_TARGET} | ||
SHARED | ||
${UNWINDER_SOURCES} | ||
) | ||
|
||
target_include_directories(${TARGETDETAILS_TARGET} BEFORE PRIVATE ${VM_DIR}) | ||
target_include_directories(${TARGETDETAILS_TARGET} BEFORE PRIVATE ${VM_DIR}/${ARCH_SOURCES_DIR}) | ||
target_include_directories(${TARGETDETAILS_TARGET} BEFORE PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}) | ||
target_include_directories(${TARGETDETAILS_TARGET} BEFORE PRIVATE ${CLR_DIR}/unwinder) | ||
target_include_directories(${TARGETDETAILS_TARGET} PRIVATE ${CLR_DIR}/debug/ee) | ||
target_include_directories(${TARGETDETAILS_TARGET} PRIVATE ${CLR_DIR}/gc) | ||
target_include_directories(${TARGETDETAILS_TARGET} PRIVATE ${CLR_DIR}/gcdump) | ||
target_include_directories(${TARGETDETAILS_TARGET} PRIVATE ${CLR_DIR}/debug/daccess) | ||
target_include_directories(${TARGETDETAILS_TARGET} PRIVATE ${ARCH_SOURCES_DIR}) | ||
|
||
target_link_libraries(${TARGETDETAILS_TARGET} PRIVATE ${STATIC_MT_CRT_LIB} ${STATIC_MT_VCRT_LIB}) | ||
|
||
# add the install targets | ||
install_clr(TARGETS ${TARGETDETAILS_TARGET} DESTINATIONS ${TARGETDETAILS_DESTINATIONS} COMPONENT debug INSTALL_ALL_ARTIFACTS) | ||
|
||
# Set the target to be built for the specified OS and ARCH | ||
set_target_definitions_to_custom_os_and_arch(TARGET ${TARGETDETAILS_TARGET} OS win ARCH ${TARGETDETAILS_ARCH}) | ||
|
||
target_compile_definitions(${TARGETDETAILS_TARGET} PRIVATE FEATURE_NO_HOST FEATURE_CDAC_UNWINDER) | ||
endfunction() | ||
|
||
# TODO: Support building cDAC unwinders on other platforms | ||
# https://github.com/dotnet/runtime/issues/112272#issue-2838611496 | ||
if(CLR_CMAKE_TARGET_WIN32 AND CLR_CMAKE_TARGET_ARCH_AMD64) | ||
create_platform_unwinder(TARGET unwinder_cdac_amd64 ARCH x64 DESTINATIONS cdaclibs) | ||
create_platform_unwinder(TARGET unwinder_cdac_arm64 ARCH arm64 DESTINATIONS cdaclibs) | ||
endif(CLR_CMAKE_TARGET_WIN32 AND CLR_CMAKE_TARGET_ARCH_AMD64) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't like this naming/meaning. Looking closer at this, this concept of "module" isn't used anywhere else in the documentation. Other parts of the documentation refer to modules as the IL concept, but in this case it refers to the concept of a module which is either a PE binary which holds R2R code, or a CodeHeap which may have code from many different IL modules within it. Possibly change the name here to something like "GetUnwindInfoBaseAddress" and document that it serves as the base pointer for the RUNTIME_FUNCTION found in the GetUnwindInfo api?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I renamed
GetModuleBaseAddress
->GetUnwindInfoBaseAddress
. It's documented as "returning the base address that the unwind info for a givenCodeBlockHandle
is relative to."I changed all of cDAC to reference the base address using
unwindInfoBase
but left the pre-existing native references tomoduleBase
andimageBase
.