Skip to content
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

[Hot Cold Splitting] Fix R2RDump to support dumping hot-cold parts together #1908

Closed
cshung opened this issue Jun 18, 2022 · 1 comment
Closed
Labels
area-hot-cold-splitting Hot cold splitting support in crossgen2

Comments

@cshung
Copy link
Member

cshung commented Jun 18, 2022

Right now, R2RDump will dump all the cold blocks as part of the last hot function, that is just wrong.

@cshung cshung added the area-hot-cold-splitting Hot cold splitting support in crossgen2 label Jun 22, 2022
@cshung
Copy link
Member Author

cshung commented Jul 20, 2022

All code reference in the comment comes from my private branch here. Note that the branch is updated from time to time, make sure you are referring to the latest one.

Functionality

When you look at the R2RDump for CoreLab, you will see this info:

...

Type:  RuntimeFunctions (102)
RelativeVirtualAddress: 0x0000180C
Size: 72 bytes
  Index | StartRVA |  EndRVA  | UnwindRVA
-----------------------------------------
      0 | 00001890 | 000018ad | 000017C0
      1 | 000018B0 | 000018cd | 000017C0
      2 | 00002430 | 00002449 | 000017E4
      3 | 00002449 | 00002471 | 000017F0
      4 | 00002480 | 00002499 | 000017E4
      5 | 00002499 | 000024c1 | 000017F0

...

Type:  Scratch (119)
RelativeVirtualAddress: 0x00001863
Size: 16 bytes
2,0
4,1

...

That means there are two methods:
The hot runtime function 0 is associated with the cold runtime function 2 and 3.
The hot runtime function 1 is associated with the cold runtime function 4 and 5.

Then we look further down and found this:

...

void CoreLab.Program.Test(string[])
Id: 0
StartAddress: 0x00001890

...

void CoreLab.Program.Main(string[])
Id: 1
StartAddress: 0x000018B0

...

void CoreLab.Program.Main(string[])
Id: 2
StartAddress: 0x00002430

...

First of all, we knew that the hot runtime function 0 at 0x00001890 is associated to the cold runtime function 0x00002430, so they should be the same method (in fact, they should both be void CoreLab.Program.Test(string[]))

Second, we would like to be able to see all runtime functions of a method together. The block for 0x00001890 and the 0x00002430 should be displayed together (in fact, all runtime function of the same method should be together).

This concludes, from the functionality point of view, what we needed.

Design

The entry point for the R2RDump tool is here. You can use the same technique as in here to make it possible to debug R2RDump.

The method DumpAllMethods here is the main method to dump all methods. As we can see, that method depends on NormalizedMethod, which is defined here and calling ReadyToRunReader.Methods, which is defined here. EnsureMethods here

Inside EnsureMethods, we used various information such as ParseMethodDefEntrypoints or ParseInstanceMethodEntrypoints to prepare a boolean array of whether or not the runtime function is an entry point, then we use CountRuntimeFunctions to determine the length of the block, assuming a method is always a contiguous sequence of RuntimeFunctions. This invariant is broken when we have hot-cold splitting and it needs to be changed.

Potential solution

To begin with, we need to have the Scratch map handy. One way of doing it is to keep it in a form that is easy to query, such as a dictionary from the hot runtime function index to the associated cold runtime function indices (Maybe a range?). Here is some example code showing how we can obtain the scratch map data in raw form.

Then when we run CountRuntimeFunctions, we should have the runtime function ID for the entry point, the entry point is always hot, so if the function is split (it might not), you should be able to search in the scratch dictionary the corresponding cold runtime function indices

Then we compute the lengths (in terms of number of runtime functions) for the hot block and the cold block.

After computing the values, we need to store them somewhere. Right now, a ReadyToRunMethod only contains EntryPointRuntimeFunctionId and RuntimeFunctionCount. We will need to introduce the ColdRuntimeFunctionId and ColdRuntimeFunctionCount so that we store those information.

The EntryPointRuntimeFunctionId is used for ParseRuntimeFunctions here to create a list of RuntimeFunctions. Right now it is only iterating through the list of functions starting from EntryPointRuntimeFunctionId up to RuntimeFunctionCount times. In case we have split, we will also need to loop through
ColdRuntimeFunctionId for ColdRuntimeFunctionCount times.

If you have implemented the functionality correctly, you should find yourselves only need to change these files:

  • ReadyToRunReader.cs, changing the EnsureMethods and CountRuntimeFunctions.
  • ReadyToRunMethods.cs, adding the ColdRuntimeFunctionId and ColdRuntimeFunctionCount properties and changing the ParseRuntimeFunctions only very slightly, there is an easy trick that allows us to iterate through the hot and the cold in the same loop to avoid code duplication.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
area-hot-cold-splitting Hot cold splitting support in crossgen2
Projects
None yet
Development

No branches or pull requests

1 participant