Re: [diamon-discuss] Follow up on SFrame in JIT applications

Brian Robbins <[email protected]> Thu, 28 Sep 2023 17:39:07 -0000
Newsgroups dev.linux.lists.diamon-discuss
Message-ID <20230928173904.GA22912@linuxonhyperv3.guj3yctzbm1etfxqx2vob5hsef.xx.internal.cloudapp.net>
> Hi Indu,
>=20
> On 9/26/23 22:24, Indu Bhagat wrote:
>=20
> > I would like to follow up on your question around using SFrame in
> > JITed code.  I recall you mentioning that looking at MS exception
> > handling may give us some insights.  Unfortunately, I dont recall
> > other pointers/references made in that conversation.  Can you send me
> > a brief summary and any pointers that you may have around this so I can
> read up?
>=20
> Thanks for following up on my Tracing Summit question. I am adding the
> diamon-discuss mailing list in CC so we can keep a trace of this conversa=
tion,
> hopefully it's OK with you.
>=20
> I am also adding collaborators from Microsoft to this discussion. Those w=
ere
> present in a 2019 meeting held at Microsoft Redmond offices where I've be=
en
> told about the shortcomings of the way integration between JITs and the
> backtrace infrastructure is done in Windows. This was discussed in the
> context of improving the backtrace infrastructure on Linux.
>=20
> Here is a link to your presentation abstract as reference to others:
>=20
> -
> https://nam06.safelinks.protection.outlook.com/?url=3Dhttps%3A%2F%2Ftracin
> gsummit.org%2Fts%2F2023%2Fsframe%2F&data=3D05%7C01%7Cbrianrob%40
> microsoft.com%7Cd3d3f9d6f1864e61e02808dbbf382d7c%7C72f988bf86f141a
> f91ab2d7cd011db47%7C1%7C0%7C638314020881735122%7CUnknown%7CT
> WFpbGZsb3d8eyJWIjoiMC4wLjAwMDAiLCJQIjoiV2luMzIiLCJBTiI6Ik1haWwiLCJ
> XVCI6Mn0%3D%7C3000%7C%7C%7C&sdata=3Dfp2ECCRPbnsCP9WvNmV4GM%
> 2Bd1pkkG8vc8dUtFqNQGEw%3D&reserved=3D0 (abstract)
> -
> https://nam06.safelinks.protection.outlook.com/?url=3Dhttps%3A%2F%2Ftracin
> gsummit.org%2Fts%2F2023%2Ffiles%2FSFrame_TracingSummit2023.pdf&dat
> a=3D05%7C01%7Cbrianrob%40microsoft.com%7Cd3d3f9d6f1864e61e02808dbb
> f382d7c%7C72f988bf86f141af91ab2d7cd011db47%7C1%7C0%7C6383140208
> 81743792%7CUnknown%7CTWFpbGZsb3d8eyJWIjoiMC4wLjAwMDAiLCJQIjoi
> V2luMzIiLCJBTiI6Ik1haWwiLCJXVCI6Mn0%3D%7C3000%7C%7C%7C&sdata=3DwI
> JwUw4fktvyJ5Rcnn%2F5j38TcRFr8wSWSr5omF8OlQU%3D&reserved=3D0
> (slides)
>=20
> For context, I've been discussing with Indu how we should extend the SFra=
me
> section (generated by GNU binutils, currently designed to augment the ELF
> binaries with additional sections which contain relevant frame information
> permitting fast backtrace without frame pointers) to JITs.
> Or at least define an ABI between applications, libraries, and the Linux =
kernel,
> which allows populating frame description information efficiently.
>=20
> I recall from our discussion at Microsoft that the way it was done on Win=
dows
> ended up being a performance bottleneck for JITs. If my memory serves me
> right, every time the JIT emits a function, it pretty much calls into the=
 kernel to
> register its existence immediately.
>=20
> I would rather see a mechanism on Linux which is similar to the rseq(2),
> io_uring(2), and perf_open(2) system calls. Those are implemented with a
> memory mapping between kernel and user-space. We could do something
> similar for a JITted sframe section: Populate an empty memory mapping of a
> given size, with a header which describes how many entries are populated.
> Then as the JIT appends a functions, it increments the populated size wit=
h a
> store-release semantic. Mutual exclusion between multiple producers
> updating a mapping would be left to userspace.
>=20
> I wonder if it would be acceptable for JITs to successively provide the
> functions in increasing order of memory addresses within a mapping, so th=
ey
> are already sorted.
>=20
> We would also probably want to plan a tombstone bit that would be set by
> the JIT when it needs to reclaim a function.
>=20
> Hopefully our collaborators at Microsoft will be able to provide more
> background information on that topic, especially references to the Windows
> side of things.
>=20

Your recollection is correct that there are win32 APIs that are used to reg=
ister the existence of new jitted code in order for the OS unwinder to know=
 how to handle the code.  The functions that get used are RtlAddGrowableFun=
ctionTable [1],  RtlGrowFunctionTable [2], RtlDeleteGrowableFunctionTable [=
3].

The idea is that you create a growable function table in userspace that is =
an array of structures that point to unwind information.  When you do this,=
 you're registering the array with the operating system and a range of memo=
ry in the process that does/will contain code.  Then, when you JIT new func=
tions, you tell the OS about them by calling RtlGrowFunctionTable.  As long=
 as the new functions are located at memory addresses that are higher than =
the last entry, then you can just initialize the next structure in the arra=
y and call RtlGrowFunctionTable.  If you do an out-of-order insert, then yo=
u have to delete the old table, create a new one, sort the data, and regist=
er the new one with the OS.  This sounds somewhat similar to the proposal y=
ou've specified above.

One thing that is worth calling out is that a process can effectively call =
RtlAddGrowableFunctionTable to create as many tables as it wants.  This (or=
 something that replaces this functionality) is important when it comes to =
thinking about the lifetime of dynamically jitted code.  For jitted code th=
at represents code stored in assemblies (DLLs) on disk, the lifetime of the=
 growable function table is generally long, and the table often doesn't hav=
e to be re-created.  However, there are cases, such as dynamically generate=
d code, where the lifetime of the jitted code is quite short, and oftentime=
s, the address range gets re-used over and over by newly generated code.  I=
n this case, it is often worthwhile to create a new growable function table=
 for the method or set of methods that were dynamically generated together.=
  This allows the process to delete the table when the code destroyed witho=
ut having to re-create a new table for code that wasn't destroyed but happe=
ned to be part of the table.  All this to say, the design point of supporti=
ng multiple tables per process is an important one.

If you'd like to see how the .NET runtime does this, here's a pointer to th=
e code that adds new entries to the existing function table: https://github=
=2Ecom/dotnet/runtime/blob/7e6bd94c52b6f4d75f8e5098e814b4ffacdd5e02/src/cor=
eclr/vm/codeman.cpp#L228.

Happy to answer any questions.

--Brian

> Thanks!
>=20
> Mathieu
>=20
> --
> Mathieu Desnoyers
> EfficiOS Inc.
> https://nam06.safelinks.protection.outlook.com/?url=3Dhttps%3A%2F%2Fwww.
> efficios.com%2F&data=3D05%7C01%7Cbrianrob%40microsoft.com%7Cd3d3f9d
> 6f1864e61e02808dbbf382d7c%7C72f988bf86f141af91ab2d7cd011db47%7C1
> %7C0%7C638314020881749505%7CUnknown%7CTWFpbGZsb3d8eyJWIjoiMC
> 4wLjAwMDAiLCJQIjoiV2luMzIiLCJBTiI6Ik1haWwiLCJXVCI6Mn0%3D%7C3000%
> 7C%7C%7C&sdata=3Dv6XfK7SLrOAn50bL1xt78Ul1uCzcsky0vGFt3sP6vmU%3D&r
> eserved=3D0

[1] https://learn.microsoft.com/en-us/windows/win32/api/winnt/nf-winnt-rtla=
ddgrowablefunctiontable
[2] https://learn.microsoft.com/en-us/windows/win32/api/winnt/nf-winnt-rtlg=
rowfunctiontable
[3] https://learn.microsoft.com/en-us/windows/win32/api/winnt/nf-winnt-rtld=
eletegrowablefunctiontable