From b7485bfc0be8c961530e75a05d5d2d25ee39d305 Mon Sep 17 00:00:00 2001 From: Wilson Snyder Date: Wed, 30 Aug 2006 19:50:24 +0000 Subject: [PATCH] Fix and test for memory leaks git-svn-id: file://localhost/svn/verilator/trunk/verilator@768 77ca24e4-aefa-0310-84f0-b9a241c72d87 --- Changes | 4 +- include/verilated.cpp | 24 +++++++--- include/verilated.h | 11 ++--- test_regress/driver.pl | 17 +++---- test_regress/t/t_leak.cpp | 93 +++++++++++++++++++++++++++++++++++++++ test_regress/t/t_leak.pl | 21 +++++++++ test_regress/t/t_leak.v | 24 ++++++++++ 7 files changed, 174 insertions(+), 20 deletions(-) create mode 100644 test_regress/t/t_leak.cpp create mode 100755 test_regress/t/t_leak.pl create mode 100644 test_regress/t/t_leak.v diff --git a/Changes b/Changes index b977e01e0..f24a69b70 100644 --- a/Changes +++ b/Changes @@ -9,7 +9,9 @@ indicates the contributor was also the author of the fix; Thanks! *** Added `systemc_dtor for destructor extentions. [Allan Cochrane] -**** Declare tables static, to reduce D-Cache miss rate. +**** Declare optimized lookup tables as 'static', to reduce D-Cache miss rate. + +**** Fix memory leak when destroying modules. [John Stroebel] **** Fix $display %m name not matching Verilog name inside SystemC modules. diff --git a/include/verilated.cpp b/include/verilated.cpp index 6c4522152..709dee963 100644 --- a/include/verilated.cpp +++ b/include/verilated.cpp @@ -23,6 +23,7 @@ //========================================================================= #include "verilated.h" +#include #define VL_VALUE_STRING_MAX_WIDTH 1024 ///< Max static char array for VL_VALUE_STRING @@ -244,17 +245,28 @@ QData VL_FOPEN_WI(int fnwords, WDataInP filenamep, IData mode) { const char* Verilated::catName(const char* n1, const char* n2) { // Returns new'ed data // Used by symbol table creation to make module names - char* str = new char[strlen(n1)+strlen(n2)+2]; - strcpy(str,n1); - strcat(str,n2); - return str; + static char* strp = NULL; + static int len = -1; + int newlen = strlen(n1)+strlen(n2)+2; + if (newlen > len) { + if (strp) delete [] strp; + strp = new char[newlen]; + len = newlen; + } + strcpy(strp,n1); + strcat(strp,n2); + return strp; } //=========================================================================== // VerilatedModule:: Methods -VerilatedModule::VerilatedModule(const char* name) - : m_name(name) { +VerilatedModule::VerilatedModule(const char* namep) + : m_namep(strdup(namep)) { +} + +VerilatedModule::~VerilatedModule() { + if (m_namep) free((void*)m_namep); m_namep=NULL; } //=========================================================================== diff --git a/include/verilated.h b/include/verilated.h index 3857524f5..7de903128 100644 --- a/include/verilated.h +++ b/include/verilated.h @@ -57,12 +57,13 @@ class SpTraceVcdCFile; class VerilatedModule { private: - const char* m_name; ///< Module name - VerilatedModule() {} ///< N/A, always use named constructor below + const char* m_namep; ///< Module name + VerilatedModule(); ///< N/A, always use named constructor below + VerilatedModule(const VerilatedModule& ); ///< N/A, no copying modules public: - VerilatedModule(const char* name); ///< Create module with given hierarchy name - ~VerilatedModule() {} - const char* name() const { return m_name; } ///< Return name of module + VerilatedModule(const char* namep); ///< Create module with given hierarchy name + ~VerilatedModule(); + const char* name() const { return m_namep; } ///< Return name of module }; //========================================================================= diff --git a/test_regress/driver.pl b/test_regress/driver.pl index d2206430b..54ceed0dd 100755 --- a/test_regress/driver.pl +++ b/test_regress/driver.pl @@ -359,6 +359,7 @@ sub compile { "VM_PREFIX=$self->{VM_PREFIX}", ($param{make_main}?"":"MAKE_MAIN=0"), "$self->{VM_PREFIX}", # not default, as we don't need archive + ($param{make_flags}||""), ]); } } @@ -518,7 +519,7 @@ sub _make_main { print $fh "#include \"SpTraceVcdC.cpp\"\n" if $self->{trace}; print $fh "#include \"SpCoverage.cpp\"\n" if $self->{coverage}; - print $fh "$VM_PREFIX *top;\n"; + print $fh "$VM_PREFIX * topp;\n"; if (!$self->sp) { print $fh "unsigned int main_time = false;\n"; print $fh "double sc_time_stamp () {\n"; @@ -536,15 +537,15 @@ sub _make_main { print $fh " double sim_time = 1000;\n"; } print $fh " Verilated::debug(".($Opt_Verilated_Debug?1:0).");\n"; - print $fh " top = new $VM_PREFIX (\"TOP\");\n"; + print $fh " topp = new $VM_PREFIX (\"TOP\");\n"; my $set; if ($self->sp) { - print $fh " SP_PIN(top,fastclk,fastclk);\n" if $self->{inputs}{fastclk}; - print $fh " SP_PIN(top,clk,clk);\n" if $self->{inputs}{clk}; + print $fh " SP_PIN(topp,fastclk,fastclk);\n" if $self->{inputs}{fastclk}; + print $fh " SP_PIN(topp,clk,clk);\n" if $self->{inputs}{clk}; $set = ""; } else { - print $fh " top->eval();\n"; - $set = "top->"; + print $fh " topp->eval();\n"; + $set = "topp->"; } print $fh " ${set}fastclk = true;\n" if $self->{inputs}{fastclk}; print $fh " ${set}clk = true;\n" if $self->{inputs}{clk}; @@ -570,9 +571,9 @@ sub _make_main { print $fh " if (!Verilated::gotFinish()) {\n"; print $fh ' vl_fatal(__FILE__,__LINE__,"main", "%Error: Timeout; never got a $finish");',"\n"; print $fh " }\n"; - print $fh " top->final();\n"; + print $fh " topp->final();\n"; print $fh " SpCoverage::write(\"",$self->{coverage_filename},"\");\n" if $self->{coverage}; - print $fh " delete top;\n"; + print $fh " delete topp; topp=NULL;\n"; print $fh " exit(0L);\n"; print $fh "}\n"; $fh->close(); diff --git a/test_regress/t/t_leak.cpp b/test_regress/t/t_leak.cpp new file mode 100644 index 000000000..aabafbfe0 --- /dev/null +++ b/test_regress/t/t_leak.cpp @@ -0,0 +1,93 @@ +// $Id$ +// DESCRIPTION: Verilator: Verilog Test driver/expect definition +// +// Copyright 2003-2006 by Wilson Snyder. This program is free software; you can +// redistribute it and/or modify it under the terms of either the GNU +// General Public License or the Perl Artistic License. + +#include +#include +#include +#include "Vt_leak.h" + +unsigned int main_time = false; +double sc_time_stamp () { + return main_time; +} + +void vl_finish (const char* filename, int linenum, const char* hier) { + // Define it to not print a message + if (0 && filename && linenum && hier) {} + Verilated::gotFinish(true); +} + +long long get_memory_usage() { + // Return memory usage. Return 0 if the system doesn't look quite right. + +#if 0 // BSD only. + struct rusage usage; + getrusage(RUSAGE_SELF, &usage); + return usage.ru_ixrss + usage.ru_idrss + usage.ru_isrss; +#endif + + FILE* fp = fopen("/proc/self/stat", "r"); + if (!fp) return 0; + + int ps_ign; + long long ps_vsize, ps_rss; + int items = fscanf(fp, ("%d (%*[^) ]) %*1s %d %*d %*d %*d %*d %u" + " %u %u %u %u %d %d %d %d" + " %*d %*d %*u %*u %d %llu %llu "), + &ps_ign, &ps_ign, &ps_ign, + &ps_ign, &ps_ign, &ps_ign, &ps_ign, + &ps_ign, &ps_ign, &ps_ign, &ps_ign, + &ps_ign, &ps_vsize, &ps_rss); + fclose(fp); + if (items >= 14) { + return ps_vsize; + } else { + return 0; + } +} + +void make_and_destroy () { + Vt_leak* topp = new Vt_leak; + + Verilated::debug(0); + Verilated::gotFinish(0); + topp->eval(); + topp->clk = true; + while (!Verilated::gotFinish()) { + main_time+=5; + topp->clk=!topp->clk; + topp->eval(); + } + + delete topp; topp=NULL; +} + +int main (int argc, char *argv[]) { + long long firstUsage = get_memory_usage(); + + // Warmup phase + for (int i=0; i<1000; i++) { + make_and_destroy(); + } + firstUsage = get_memory_usage(); + printf("Memory size %lld bytes\n", firstUsage); + + int loops = 100*1000; + for (int left=loops; left>0;) { + for (int j=0; j<1000; j++, left--) { + make_and_destroy(); + } + } + + long long leaked = get_memory_usage() - firstUsage; + if (leaked > 64*1024) { // Have to allow some slop for this code. + printf ("Leaked %lld bytes, or ~ %lld bytes/construt\n", leaked, leaked/loops); + vl_fatal(__FILE__,__LINE__,"top", "Leaked memory\n"); + } + + printf ("*-* All Finished *-*\n"); +} diff --git a/test_regress/t/t_leak.pl b/test_regress/t/t_leak.pl new file mode 100755 index 000000000..ff37df609 --- /dev/null +++ b/test_regress/t/t_leak.pl @@ -0,0 +1,21 @@ +#!/usr/bin/perl +if (!$::Driver) { use FindBin; exec("./driver.pl", @ARGV, $0); die; } +# $Id$ +# DESCRIPTION: Verilator: Verilog Test driver/expect definition +# +# Copyright 2003 by Wilson Snyder. This program is free software; you can +# redistribute it and/or modify it under the terms of either the GNU +# General Public License or the Perl Artistic License. + +compile ( + make_top_shell => 0, + make_main => 0, + make_flags => "OPT_FAST=-DVL_USER_FINISH", + v_flags2 => ["--exe t/$Last_Self->{name}.cpp"], + ) if $Last_Self->{v3}; + +execute ( + check_finished=>1, + ) if $Last_Self->{v3}; +ok(1); +1; diff --git a/test_regress/t/t_leak.v b/test_regress/t/t_leak.v new file mode 100644 index 000000000..9a73eab0c --- /dev/null +++ b/test_regress/t/t_leak.v @@ -0,0 +1,24 @@ +// $Id$ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed into the Public Domain, for any use, +// without warranty, 2006 by Wilson Snyder. + +module t (clk); + + sub sub (); + + input clk; + integer cyc=1; + + always @ (posedge clk) begin + cyc <= cyc + 1; + if (cyc==2) begin + $finish; + end + end +endmodule + +module sub; + /* verilator public_module */ +endmodule