Description
In simulations where an iteration of the top modules is performed with vpi_iterate/vpi_scan the memory usage increases every time a new iterator is created and consumed. If too many calls are done, the system ram and cache is completely filled and the process is killed. In my machine such amount is around to ~5GB of ram.
Expected behaviour
I would have expected that a consumed iterator (when vpi_scan returns 0) frees all its associated memory.
How to reproduce?
To reproduce the issue the following vpi plugin is given:
```c :file: vpi_plugin.c
include
uint32_t iteration = 0;
PLI_INT32 start_cb(p_cb_data);
PLI_INT32 end_cb(p_cb_data);
PLI_INT32 rw_cb(p_cb_data);
PLI_INT32 ro_cb(p_cb_data);
PLI_INT32 delay_rw_cb(p_cb_data);
PLI_INT32 delay_ro_cb(p_cb_data);
void register_cb(PLI_INT32(*f)(p_cb_data),
PLI_INT32 reason,
int64_t cycles){
s_cb_data cbData;
s_vpi_time simuTime;
if (cycles < 0){
cbData.time = NULL;
} else {
cbData.time = &simuTime;
simuTime.type = vpiSimTime;
simuTime.high = (PLI_INT32) (cycles >> 32);
simuTime.low = (PLI_INT32) (cycles & 0xFFFFFFFF);
}
cbData.reason = reason;
cbData.cb_rtn = f;
cbData.user_data = 0;
cbData.value = 0;
vpi_register_cb(&cbData);
}
void entry_point_cb() {
register_cb(start_cb, cbStartOfSimulation, -1);
register_cb(end_cb, cbEndOfSimulation, -1);
register_cb(delay_ro_cb, cbAfterDelay, 0);
}
PLI_INT32 start_cb(p_cb_data data){
(void) data;
printf("Start of simulation \n");
return 0;
}
PLI_INT32 end_cb(p_cb_data data){
(void) data;
printf("End of simulation %u \n", iteration);
return 0;
}
PLI_INT32 rw_cb(p_cb_data data){
(void) data;
if(iteration < STOP_ITERATION) {
register_cb(delay_ro_cb, cbAfterDelay, 1);
} else {
vpi_control(vpiFinish, 0);
}
vpiHandle handle_iterator;
handle_iterator = vpi_iterate (vpiModule, NULL) ; // <---- Here the iterator is created
while(vpi_scan(handle_iterator)); // <---- Here the iterator is consumed
iteration++;
return 0;
}
PLI_INT32 ro_cb(p_cb_data data){
(void) data;
register_cb(delay_rw_cb, cbAfterDelay, 0);
return 0;
}
PLI_INT32 delay_rw_cb(p_cb_data data){
(void) data;
register_cb(rw_cb, cbReadWriteSynch, 0);
return 0;
}
PLI_INT32 delay_ro_cb(p_cb_data data){
(void) data;
register_cb(ro_cb, cbReadOnlySynch, 0);
return 0;
}
void (*vlog_startup_routines[]) () = {
entry_point_cb,
0
};
The VPI plugin generate a chain of callbacks that repeats
cbAfterDelay -> cbReadWriteSynch -> cbAfterDelay -> cbReadOnlySynch one billion times.
In cbReadWriteSynch the iterator is generated/consumed. Removing the iterator results in the memory leak to disappear.
A 'dummy' vhdl source(any other vhdl file can be used):
```vhd :file: adder.vhd
library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;
entity adder is
port
(
nibble1, nibble2 : in unsigned(3 downto 0);
sum : out unsigned(3 downto 0);
carry_out : out std_logic
);
end entity adder;
architecture behavioral of adder is
signal temp : unsigned(4 downto 0);
begin
temp <= ("0" & nibble1) + nibble2;
sum <= temp(3 downto 0);
carry_out <= temp(4);
end architecture behavioral;
A script file to execute the simulation
```sh :file: execute.sh
$GHDL -a adder.vhd
$GHDL --vpi-compile gcc -c vpi_plugin.c
$GHDL --vpi-link gcc -o vpi_plugin.vpi vpi_plugin.o -lc
$GHDL --elab-run adder --vpi=./vpi_plugin.vpi
to run the simulation use
```sh
GHDL=ghdl-mcode sh execute.sh
GHDL=ghdl-gcc execute.sh
GHDL=ghdl-llvm sh execute.sh
Context
Please, provide the following information:
Notes
This was discovered working on https://github.com/SpinalHDL/SpinalHDL/issues/146
(Sorry. I am really putting GHDL under the microscope XD)
Note that you are supposed to free the handler returned by vpi_scan.
I agree that the vpi_iterator handler should be automatically free.
And I also agree that in the verilog standard (at least 1364-2005), there is no call to vpi_free_object in the example for vpi_scan (27.36).
Good morning
Modifiying the above code with
vpiHandle handle_iterator;
vpiHandle handle_scan;
handle_iterator = vpi_iterate (vpiModule, NULL) ;
handle_scan=vpi_scan(handle_iterator);
while(handle_scan){
vpi_free_object(handle_scan);
handle_scan=vpi_scan(handle_iterator);
}
to manage the iterator, still results in a memory leak. However, as you said, there is few documentation out there about using vpi_free_object on handles generated by vpi_scan :) (also in the examples of The Verilog PLI Handbook ).
Yes, there was a memory leak because vpi_scan didn't free the iterator from vpi_iterate. I am testing the fix.
Most helpful comment
Yes, there was a memory leak because vpi_scan didn't free the iterator from vpi_iterate. I am testing the fix.