/* [<][>][^][v][top][bottom][index][help] */
//
// Copyright (C) 2005, 2006, 2007, 2008, 2009, 2010 Free Software
// Foundation, Inc
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
//
// This class is a memory allocation tracker used to optimize
// the memory usage and find memory leaks.
//
#ifdef HAVE_CONFIG_H
#include "gnashconfig.h"
#endif
// If we don't have support for mallinfo(), this code is useless
#if HAVE_MALLINFO
#include "gmemory.h"
#include <vector>
#include <iostream>
#include "log.h"
#include "getclocktime.hpp"
using namespace std;
namespace gnash {
RcInitFile& rcfile = gnash::RcInitFile::getDefaultInstance();
const int DATALOG_SIZE = 1024;
Memory::Memory()
: _collecting(false),
_info(0),
_size(DATALOG_SIZE),
_index(0)
{
// GNASH_REPORT_FUNCTION;
}
Memory::Memory(size_t size)
: _collecting(false)
{
// GNASH_REPORT_FUNCTION;
_size = size;
_info = new struct small_mallinfo[_size];
reset();
}
Memory::~Memory()
{
// GNASH_REPORT_FUNCTION;
if (_info) {
delete[] _info;
}
_index = 0;
_size = 0;
}
// Erase all collected data and reset collections.
void
Memory::reset()
{
// GNASH_REPORT_FUNCTION;
if (_info) {
memset(_info, 0, _size);
}
_index = 0;
}
void
Memory::startStats()
{
// GNASH_REPORT_FUNCTION;
_collecting = true;
if (_info == 0) {
log_debug("Allocating buffer for %d data samples", _size);
_info = new struct small_mallinfo[_size];
reset();
addStats();
}
}
int
Memory::addStats(int line)
{
// GNASH_REPORT_FUNCTION;
if (_info) {
struct small_mallinfo *ptr = _info + _index;
addStats(ptr, line);
}
return _index;
}
int
Memory::addStats()
{
// GNASH_REPORT_FUNCTION;
if (_info) {
struct small_mallinfo *ptr = _info + _index;
addStats(ptr, 0);
}
return _index;
}
int
Memory::addStats(struct small_mallinfo *ptr)
{
// GNASH_REPORT_FUNCTION;
return addStats(ptr, 0);
}
int
Memory::addStats(struct small_mallinfo *ptr, int line)
{
// GNASH_REPORT_FUNCTION;
struct mallinfo mal = mallinfo();
int yy = static_cast<int>(_size);
// dump(&mal);
if ((ptr) && (_index < yy)) {
ptr->line = line;
clock_gettime (CLOCK_REALTIME, &ptr->stamp);
ptr->arena = mal.arena;
ptr->uordblks = mal.uordblks;
ptr->fordblks = mal.fordblks;
_index++;
}
return _index;
}
// return true if we haven't leaked any memory
bool
Memory::endCheckpoint()
{
// GNASH_REPORT_FUNCTION;
_checkpoint[1] = mallinfo();
if (_checkpoint[1].uordblks == _checkpoint[0].uordblks) {
return true;
}
return false;
}
// Dump the differences of bytes allocated between two samples
int
Memory::diffStats()
{
// GNASH_REPORT_FUNCTION;
return diffStats(_index - 1, _index - 2);
}
int
Memory::diffStats(int x, int y)
{
// GNASH_REPORT_FUNCTION;
int yy = static_cast<int>(_size);
if ((_info) && (x < DATALOG_SIZE) && (y < yy)) {
return (_info[x].uordblks - _info[y].uordblks);
}
return -1;
}
// Dump the differences between two samples's timestamp
int
Memory::diffStamp()
{
// GNASH_REPORT_FUNCTION;
return diffStamp(_index - 1, _index - 2);
}
int
Memory::diffStamp(int x, int y)
{
// GNASH_REPORT_FUNCTION;
int yy = static_cast<int>(_size);
if ((_info) && (x < DATALOG_SIZE) && (y < yy)) {
return (_info[x].stamp.tv_nsec - _info[y].stamp.tv_nsec);
}
return -1;
}
// Analyze memory usage
bool
Memory::analyze()
{
// GNASH_REPORT_FUNCTION;
int accumulate_allocated = 0;
int accumulate_freed = 0;
// System memory is what we get from brk(), the lowest level
// system call used by both malloc() or new().
cerr << endl << "System memory allocated in bytes: "
<< _info->arena << endl;
int diff_arena = (_info + _index - 1)->arena - _info->arena;
if (diff_arena) {
cerr << "System memory change in bytes: " << diff_arena << endl;
}
int total_allocated = (_info + _index - 1)->uordblks - _info->uordblks;
cerr << "Total bytes allocated: " << total_allocated << endl;
if (_index > 1) {
for (int i=1; i<_index; i++) {
struct small_mallinfo *ptr = _info + i;
// // Get the time stamp
// int diff_stamp_sec = (ptr->stamp.tv_sec) - (ptr - 1)->stamp.tv_sec;
int diff_stamp_nsec = (ptr->stamp.tv_nsec) - (ptr - 1)->stamp.tv_nsec;
// if ((diff_stamp_sec > 0) || (diff_stamp_nsec > 0)) {
// if (ptr->line && (ptr - 1)->line) {
// if (diff_stamp_sec > 0) {
// cerr << "Difference in seconds is: " << diff_stamp_sec;
// cerr << ", nanoseconds is: "<< diff_stamp_nsec;
// }
// else {
// cerr << "Difference in nanoseconds is: "<< diff_stamp_nsec;
// }
// cerr << "\tbetween lines: " << (ptr - 1)->line
// << " and " << ptr->line << endl;
// } else {
// cerr << "Difference in seconds is: " << diff_stamp_sec
// << ", nanoseconds is: "<< diff_stamp_nsec << endl;
// }
// }
// See what was allocated between samples
int diff_allocated = (ptr->uordblks) - (ptr - 1)->uordblks;
if (diff_allocated > 0) {
accumulate_allocated += diff_allocated;
if (ptr->line && (ptr - 1)->line) {
cerr << "Allocated " << diff_allocated
<< " bytes\tbetween lines: " << (ptr - 1)->line
<< " and " << ptr->line;
} else {
cerr << "Allocated bytes: " << diff_allocated;
}
// same as diff_freed
// } else {
// if (diff_allocated != 0) {
// cerr << "\tnew heap bytes: " << diff_allocated << endl;
// }
}
// See what was freed between samples
int diff_freed = ptr->fordblks - (ptr - 1)->fordblks;
if (diff_freed > 0) {
accumulate_freed += diff_freed;
if (ptr->line && (ptr - 1)->line) {
cerr << "Freed " << diff_freed
<< " bytes between lines: " << (ptr - 1)->line
<< " and " << ptr->line;
} else {
cerr << "Freed bytes: " << diff_freed;
}
// Same as diif_allocated
// } else {
// if (diff_freed != 0) {
// cerr << "\tnuked heap bytes: " << diff_freed << endl;
// }
}
if (diff_freed || diff_allocated) {
cerr << ", and took " << diff_stamp_nsec << " nanoseconds";
} else {
cerr << "no allocations, time difference is " << diff_stamp_nsec << " nanoseconds";
if (ptr->line && (ptr - 1)->line) {
cerr << " between lines: " << (ptr - 1)->line
<< " and " << ptr->line;
}
}
cerr << endl;
}
} else {
cerr << "Only have one sample" << endl;
dump();
}
// Sanity check on our calculations
if (total_allocated != (accumulate_allocated - accumulate_freed)) {
log_error("Calculations don't equal");
} else {
log_debug("Zero memory leaks for this program");
}
if ((_checkpoint[0].uordblks != 0) && (_checkpoint[1].uordblks != 0)) {
if (_checkpoint[1].uordblks == _checkpoint[0].uordblks) {
cerr << "The last checkpoint status was: "
<< ((_checkpoint[1].uordblks == _checkpoint[0].uordblks)
? "passed" : "failed") << endl;
}
}
return true;
}
// Dump the vector of stored classes
void
Memory::dump(struct mallinfo *ptr)
{
// GNASH_REPORT_FUNCTION;
cerr << "\tstruct mallinfo: Non-mmapped space allocated from system is: \""
<< ptr->arena << "\"" << endl;
cerr << "\tstruct mallinfo: Total allocated space is: \""
<< ptr->uordblks << "\"" << endl;
cerr << "\tstruct mallinfo: Total free space is: \""
<< ptr->fordblks << "\"" << endl;
}
// Dump the vector of stored classes
void
Memory::dump(struct small_mallinfo *ptr)
{
// GNASH_REPORT_FUNCTION;
cerr << "\tLine number of sample: " << ptr->line << endl;
cout.fill('0');
cout.width(9);
cerr << "\tTimestamp number of sample: " << ptr->stamp.tv_sec
<< ":" << ptr->stamp.tv_nsec << endl;
cout.fill(' ');
cout.width(1);
cerr << "\tNon-mmapped space allocated from system is: \""
<< ptr->arena << "\"" << endl;
cerr << "\tTotal allocated space is: \""
<< ptr->uordblks << "\"" << endl;
cerr << "\tTotal free space is: \""
<< ptr->fordblks << "\"" << endl;
}
void
Memory::dump()
{
// GNASH_REPORT_FUNCTION;
for (int i=0; i<_index; i++) {
cerr << "Mallinfo index: " << i << endl;
dump(_info + i);
}
}
void
Memory::dumpCSV()
{
// GNASH_REPORT_FUNCTION;
struct small_mallinfo *ptr;
cerr << "linenum,seconds,nanoseconds,arena,allocated,freed" << endl;
for (int i=0; i<_index; i++) {
ptr = _info + i;
cerr << ptr->line << ","
<< ptr->stamp.tv_sec << ","
<< ptr->stamp.tv_nsec << ","
<< ptr->arena << ","
<< ptr->uordblks << ","
<< ptr->fordblks << endl;
}
}
} // end of gnash namespace
#endif // end of HAVE_MALLINFO
// Local Variables:
// mode: C++
// indent-tabs-mode: t
// End: