Determining the size of C++ vtables (original) (raw)
Ioi Lam ioi.lam at oracle.com
Wed Mar 1 02:37:16 UTC 2017
- Previous message: RFR[S] 8005165 Platform-independent C++ vtables for CDS
- Next message: Determining the size of C++ vtables
- Messages sorted by: [ date ] [ thread ] [ subject ] [ author ]
On 2/24/17 11:52 PM, Thomas Stüfe wrote:
Hi ioi,
On Fri, Feb 24, 2017 at 7:50 PM, Ioi Lam <ioi.lam at oracle.com_ _<mailto:ioi.lam at oracle.com>> wrote:
On 2/23/17 11:17 PM, Thomas Stüfe wrote: Hi Ioi, After reading through the original mailthread referenced in the bug report, the only reason the "just copy enough (200) slots from the vtable in libjvm.so to the vtable in the image" approach does not work for you is that you are afraid of hitting unmapped memory? If I understand it correctly. If that is the case, why not copy 200 slots with SafeFetch and stop at the first error? That should not incur much additional costs as long as you do not hit an unmapped area, in which case you'd stop anyway. Oh, that's a good simplification.I hadn't thought about using SafeFetch :-( One issue with it is the 200 slots are too big (ConstantPool needs just 10 slots), so finding the exact size is a little more efficient and saves a few KBs. How many vtables are you copying? Does it really matter? I am copying only 8 vtables, so it doesn't really matter.
As I said, I think your approach is quite elegant, but not terribly portable. We already make some assumptions (that vtables are organized as one continuous block of function pointers) and you add some more assumptions about the order of the methods in the vtable.
I think even if you can make sure that it works now with our set of platforms and compilers, this adds the risk that a future compiler update may change the vtable layout and break the code. E.g. a vtable dumped with a VM compilers by an older compiler could not be loaded with the newly compiled VM. This shouldn't be a problem with usual compiler upgrades:
You can load a CDS image only with the VM that generates it (we have a VM version string check), so if you have upgraded the compiler (or enabled RTTI and rebuilt the VM), so you have larger vtables, you won't be able to load any old CDS image anyway.
However, if your compiler completely changes the way it implements vtables (e.g., adding one level of indirection to vptr), then, things will break, but the existing CDS vtable code also has the same problem.
Also, you set the bar a bit higher for future ports, because you add requirements for the C++ compiler.
But I may be too cautious here and I am waiting for others to chime in. As a side note, it would make sense to write a tiny C++ test which copies vtables around using your technique and then checks if they still work; such a test we could use on out platforms to check if our compilers can cope. Such a test would also be a good sanity check to add to the VM initialization if CDS is active. I've taken your suggestion to use SafeFetchN instead of trying to figure out the vtable size. That simplifies the product-build a bit.
I put my vtable sizing code in debug builds. It's not strictly portable, but it probably fail quickly on an esoteric c++ compiler so you can figure out what's wrong. It will also assert if you have added too many new virtual method to overflow the current limit of 150.
Please reply your comments to the RFR e-mail thread.
Thanks
- Ioi
Kind Regards, Thomas
Thanks - Ioi
That way you make yourself a bit less dependent on compiler internals. I agree that your approach is more elegant, though. Kind Regards, Thomas On Fri, Feb 24, 2017 at 4:55 AM, Ioi Lam <ioi.lam at oracle.com_ _<mailto:ioi.lam at oracle.com>> wrote:
On 2/23/17 7:47 PM, Ioi Lam wrote: Hi, I am working on https://bugs.openjdk.java.net/browse/JDK-8005165 <https://bugs.openjdk.java.net/browse/JDK-8005165> (Remove CPU-dependent code in self-patching vtables), I need a way find out the size of a C++ vtable. I ended up doing this: // Objects of the Metadata types (such as Klass and ConstantPool) have C++ vtables. // (In GCC this is the field ::vptr, i.e., first word in the object.) // // Addresses of the vtables and the methods may be different across JVM runs, // if libjvm.so is dynamically loaded at a different base address. // // To ensure that the Metadata objects in the CDS archive always have the correct vtable: // // + at dump time: we redirect the vptr to point to our own vtables inside // the CDS image // + at run time: we clone the actual contents of the vtables from libjvm.so // into our own tables. // // To determine the size of the vtable for each type, we use the following // trick by declaring 2 subclasses: // // class CppVtabTesterA: public InstanceKlass { // virtual int lastvirtualmethod() {return 1;} // }; // class CppVtabTesterB: public InstanceKlass { // virtual void* lastvirtualmethod() {return NULL}; // }; // // CppVtabTesterA and CppVtabTesterB's vtables have the following properties: // - Their size (N+1) is exactly one more than the size of InstanceKlass's vtable (N) // - The first N entries have are exactly the same as in InstanceKlass's vtable. // - Their last entry is different. // // So to determine the value of N, we just walk CppVtabTesterA and CppVtabTesterB's tables // and find the first entry that's different Could anyone comment if this is acceptable? I know it's not 100% portable (C++ doesn't specify where to find the vtable, or what's inside), but my assumptions is the same as the existing code. I.e., vptr is a pointer located at offset 0 of the object, and it points to a one-dimensional array. So at least it's not any worse than before? Thanks - Ioi By the way, I first tried having only a single "tester" class and walk the vtable to look for &lastvirtualmethod, but the C++ compiler told me that taking the address of a non-static function is not allowed ..... so I ended up creating two tester classes and checking their differences.
- Previous message: RFR[S] 8005165 Platform-independent C++ vtables for CDS
- Next message: Determining the size of C++ vtables
- Messages sorted by: [ date ] [ thread ] [ subject ] [ author ]