RFC: Storing additional information for op_arrays

php.internals

Stefan Esser

18 years ago
Hi everyone, I would like to use this list to address the major players in bytecode caching tools: Zend, APC, Xcache... One problem I and others have run into is that from time to time we need to store extra information for specific opcode arrays. For simple values it is possible to use one of the reserved slots in the op_array structure, but in the past that has been unrelieable because APC for example simply overwrote the first slots without asking the Zend Engine to reserve some space. The next problem is that the amount of data you can store is not that big. Leaving a pointer in the reserved field is also not a good idea, because this will break as soon the opcode array is shared among processes or was stored on the disk. Therefore it would be great if we can come up with a modification of the op_array structure that allows extensions to append arbitrary sized data to an op_array, that gets also cached by all the opcode cachers... What do you think? Stefan Esser

Derick Rethans

18 years ago
On Thu, 20 Sep 2007, Stefan Esser wrote:
> One problem I and others have run into is that from time to time we need > to store extra information for specific opcode arrays. For simple values > it is possible to use one of the reserved slots in the op_array > structure, but in the past that has been unrelieable because APC for > example simply overwrote the first slots without asking the Zend Engine > to reserve some space.
Have a pointer on how this is done? I sortof need/want to do this in Xdebug as well, however I don't want opcode caches to remember this value.
> The next problem is that the amount of data you can store is not that big. > Leaving a pointer in the reserved field is also not a good idea, because > this will break as soon the opcode array is shared among processes or > was stored on the disk. > > Therefore it would be great if we can come up with a modification of the > op_array structure that allows extensions to append arbitrary sized data > to an op_array, that gets also cached by all the opcode cachers... > > What do you think?
Sounds like a good idea to me - but the op code caches do need to some special trickery for this I guess. However, in my case I do *not* want an opcode cache to remember the stored data, so that perhaps needs to be taken into account as well. regards, Derick
-- Derick Rethans http://derickrethans.nl | http://ez.no | http://xdebug.org

Stefan Esser

18 years ago
Derick Rethans schrieb:
> On Thu, 20 Sep 2007, Stefan Esser wrote: > > >> One problem I and others have run into is that from time to time we need >> to store extra information for specific opcode arrays. For simple values >> it is possible to use one of the reserved slots in the op_array >> structure, but in the past that has been unrelieable because APC for >> example simply overwrote the first slots without asking the Zend Engine >> to reserve some space. >> > > Have a pointer on how this is done? I sortof need/want to do this in > Xdebug as well, however I don't want opcode caches to remember this > value. >
Well the "official" way to use the reserved area of an opcode array is to get an handle from the zend engine for reserved space. This is done by ZEND_API int zend_get_resource_handle(zend_extension *extension) it returns an index you can simply use. (I think meanwhile APC will honor this and will not overwrite the first bytes).
> Sounds like a good idea to me - but the op code caches do need to some > special trickery for this I guess. However, in my case I do *not* want > an opcode cache to remember the stored data, so that perhaps needs to be > taken into account as well
The basic idea would be to add API functions like zend_op_array_add_data(key, data, size, flags) zend_op_array_remove_data()... zend_op_array_get_data, zend_op_array_get_all_data In the flags one could choose: Needs to be cached etc... Well and this will be stored in a datastructure that will just be pointed at by the op_array struct. So op_arrays will not really grow (just a single pointer added) and the whole overhead is only in the extensions that actually require such extra data and in the opcode caches that need to cache this. And I think the opcode cachers would just need to traverse the list of all data and store it with the op_array. I doubt that would take much changed in the cacher software. And well if the cache authors speak up, PHP can pretty much implement all the API functions required for such a step... Stefan Esser

Derick Rethans

18 years ago
On Thu, 20 Sep 2007, Stefan Esser wrote:
> Derick Rethans schrieb: > > On Thu, 20 Sep 2007, Stefan Esser wrote: > > > > > >> One problem I and others have run into is that from time to time we need > >> to store extra information for specific opcode arrays. For simple values > >> it is possible to use one of the reserved slots in the op_array > >> structure, but in the past that has been unrelieable because APC for > >> example simply overwrote the first slots without asking the Zend Engine > >> to reserve some space. > >> > > > > Have a pointer on how this is done? I sortof need/want to do this in > > Xdebug as well, however I don't want opcode caches to remember this > > value. > > > Well the "official" way to use the reserved area of an opcode array is > to get an handle from the zend engine for reserved space. > This is done by > ZEND_API int zend_get_resource_handle(zend_extension *extension) > it returns an index you can simply use. (I think meanwhile APC will > honor this and will not overwrite the first bytes).
Yes, it does: void apc_zend_init(TSRMLS_D) { zend_extension dummy_ext; #ifdef ZEND_ENGINE_2 APCG(reserved_offset) = zend_get_resource_handle(&dummy_ext); assert(APCG(reserved_offset) == dummy_ext.resource_number); assert(APCG(reserved_offset) != -1); assert(sizeof(apc_opflags_t) <= sizeof(void*)); #endif For Xdebug I don't need to use the dummy ext though as it's a real zend extension. regards, Derick
-- Derick Rethans http://derickrethans.nl | http://ez.no | http://xdebug.org

Stanislav Malyshev

18 years ago
> The basic idea would be to add API functions like > zend_op_array_add_data(key, data, size, flags) > zend_op_array_remove_data()... zend_op_array_get_data, > zend_op_array_get_all_data > In the flags one could choose: Needs to be cached etc... > > Well and this will be stored in a datastructure that will just be > pointed at by the op_array struct.
Some questions here: 1. Do I understand right that that this is supposed to be arbitrary length list of (void *, size) pairs? 2. If opcode cache gets just size, that means the data structure would have to be in some kind of serialized position-independent form - which most of C structures that use any kind of pointers aren't. 3. What happens with this if there's no bytecode cache? How the data is supposed to be retrieved from the cache? 4. How modifications are handled - i.e., if extension modifies the data, bytecode cache is supposed to be notified of the change, or the data is supposed to be immutable once created and cached?
-- Stanislav Malyshev, Zend Software Architect stas@zend.com http://www.zend.com/ (408)253-8829 MSN: stas@zend.com

Stanislav Malyshev

18 years ago
> One problem I and others have run into is that from time to time we need > to store extra information for specific opcode arrays. For simple values > it is possible to use one of the reserved slots in the op_array > structure, but in the past that has been unrelieable because APC for > example simply overwrote the first slots without asking the Zend Engine > to reserve some space.
I think it's a bug in APC. Each extension has its own slot, by allocated slot number, and should never touch other slots (unless it has some protocol to talk to the extension which owns other slot). So touching slot that is not owned by the code is a bug like touching unallocated memory.
> The next problem is that the amount of data you can store is not that big. > Leaving a pointer in the reserved field is also not a good idea, because > this will break as soon the opcode array is shared among processes or > was stored on the disk.
Well, we can't have pointers to disk in memory :), so disk is another problem. As for shared memory, I don't see how one can support arbitrary length and structure data without using pointers in one way or another, so once you share that you'd have to use shared memory pointers anyway. Maybe I misunderstand something in what do you mean - could you tell some more on what would be the improvement you are thinking of?
> Therefore it would be great if we can come up with a modification of the > op_array structure that allows extensions to append arbitrary sized data > to an op_array, that gets also cached by all the opcode cachers...
Note that reserved space in op_array is meant for general extension usage, not just for bytecode caches. So at least some of that data doesn't have to be cached. Now, we could add some other data that would be cached, but here we need to understand how we can do arbitrary length and arbitrary structure data that can be stored in shared memory and concurrently used. It looks to me rather complex task, not sure it belongs to the engine even - unless I'm missing some obvious easy solution.
-- Stanislav Malyshev, Zend Software Architect stas@zend.com http://www.zend.com/ (408)253-8829 MSN: stas@zend.com

Stefan Esser

18 years ago
Morning,
> I think it's a bug in APC. Each extension has its own slot, by > allocated slot number, and should never touch other slots (unless it > has some protocol to talk to the extension which owns other slot). So > touching slot that is not owned by the code is a bug like touching > unallocated memory.
Yes it was a bug in APC. But the problem is that I doubt it is clearly documented how to get access to the "reserved resourced". Actually I believe Suhosin was the first open source solution that used these resources and the only closed source software that did it (and dit it long before Suhosin) are Zend tools.
> Well, we can't have pointers to disk in memory :), so disk is another > problem. As for shared memory, I don't see how one can support > arbitrary length and structure data without using pointers in one way > or another, so once you share that you'd have to use shared memory > pointers anyway. Maybe I misunderstand something in what do you mean - > could you tell some more on what would be the improvement you are > thinking of?
Well the problem I want to be solving is the following 1) Extension generated extra data that is linked to the op_array (f.e. information like: was generated by eval()'d code, does use variable XYZ, uses session functionality, ....) 2) Naive way is to reserve a slot and simply put a pointer in there 3) Naive way works when there is no opcode cache, because then the extra data has to be regenerated everytime anyway (SLOW!) 4) Naive way works when the opcode cache does only work on a per process/thread basis, because then the pointer stays valid 5) Naive way FAILS when opcode cache stores data on disk and loads it back (pointer wrong, or process never generated the extra data) 6) Naive way FAILS when opcode cache shares data between processes (pointer wrong, ...) Now the idea was to create a solution for this problem that is compatible with all opcode cachers that follow some rules. Correct me if I am wrong, but I believed that op_arrays in shared memory cannot be used directly and usually have to be copied into the own address space to work on... If this is true then maybe the simplest idea would be to have some kind of suspend and resume functionality for op_arrays. When you put the op_array into the shared memory you call a suspend hook() extensions can register and when you read it from shared memory you call the resume() hook. This would be the simplest way, because then the extension could maybe clear its pointer in the extra slot so that it is NULL for the other process and then it knows inside the other process that it has to regenerate it for the other process. Or it simply regenerates the information from within the resume_hook(). I think this idea is pretty simple to implement. But my original idea was a little bit more complicated. I wanted that the suspend hook creates a binary data package (by extension) and that when the opcode cacher caches the binary data package it copies the data of the suspend hook into shared memory/writes it onto disk. On the other hand the resume hook will decode the extra data... To make it simple for the opcode cachers PHP could internally handle the whole combine data of extensions and split up data by extension stuff. So for opcode cachers they simply have to: 1) zend_suspend_op_array(op_array, *buffer, *size); 2) Store the extra data in buffer of size bytes together with the op_array 3) zend_resume_op_array(op_array, buffer, size); I know that there are some op_array() hooks for zend_extensions but I believe right now there is no way to hook "I just came from some kind of shared memory (memory/disk/network)" please fix me up so that I still work in your process.... Stefan Esser
-- Stefan Esser SektionEins GmbH Tel. +49 175 6782326 Ober Buschweg 9a 50999 Köln stefan.esser@sektioneins.de www.sektioneins.de SektionEins GmbH, Standort Köln Firmensitz: Ober Buschweg 9a, 50999 Köln Registergericht: Amtsgericht Köln, HRB 59920 Geschäftsführer: Johann-Peter Hartmann