157 lines
5.1 KiB
C++
157 lines
5.1 KiB
C++
/**
|
|
* @file llallocator_heap_profile.cpp
|
|
* @brief Implementation of the parser for tcmalloc heap profile data.
|
|
* @author Brad Kittenbrink
|
|
*
|
|
* $LicenseInfo:firstyear=2009&license=viewerlgpl$
|
|
* Second Life Viewer Source Code
|
|
* Copyright (C) 2010, Linden Research, Inc.
|
|
*
|
|
* This library is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU Lesser General Public
|
|
* License as published by the Free Software Foundation;
|
|
* version 2.1 of the License only.
|
|
*
|
|
* This library 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
|
|
* Lesser General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU Lesser General Public
|
|
* License along with this library; if not, write to the Free Software
|
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
|
*
|
|
* Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
|
|
* $/LicenseInfo$
|
|
*/
|
|
|
|
// <FS:ND> Disable some warnings on newer GCC versions.
|
|
// This might also trigger on something like 4.8, but I did not such a GCC to test anything lower than 4.9 and higher than 4.6
|
|
//<FS:TS> It does trigger on 4.8. Not sure about 4.7.
|
|
#if LL_LINUX
|
|
#pragma GCC diagnostic ignored "-Wuninitialized"
|
|
#if (__GNUC__ * 10000 + __GNUC_MINOR__ * 100 + __GNUC_PATCHLEVEL__ ) >= 40800
|
|
#pragma GCC diagnostic ignored "-Wmaybe-uninitialized"
|
|
#endif
|
|
#endif
|
|
// </FS:ND>
|
|
|
|
#include "linden_common.h"
|
|
#include "llallocator_heap_profile.h"
|
|
|
|
#if LL_MSVC
|
|
// disable warning about boost::lexical_cast returning uninitialized data
|
|
// when it fails to parse the string
|
|
#pragma warning (disable:4701)
|
|
#pragma warning (disable:4702)
|
|
#endif
|
|
|
|
#include <boost/algorithm/string/split.hpp>
|
|
#include <boost/bind.hpp>
|
|
#include <boost/lexical_cast.hpp>
|
|
#include <boost/range/iterator_range.hpp>
|
|
|
|
static const std::string HEAP_PROFILE_MAGIC_STR = "heap profile:";
|
|
|
|
static bool is_separator(char c)
|
|
{
|
|
return isspace(c) || c == '[' || c == ']' || c == ':';
|
|
}
|
|
|
|
void LLAllocatorHeapProfile::parse(std::string const & prof_text)
|
|
{
|
|
// a typedef for handling a token in the string buffer
|
|
// it's a begin/end pair of string::const_iterators
|
|
typedef boost::iterator_range<std::string::const_iterator> range_t;
|
|
|
|
mLines.clear();
|
|
|
|
if(prof_text.compare(0, HEAP_PROFILE_MAGIC_STR.length(), HEAP_PROFILE_MAGIC_STR) != 0)
|
|
{
|
|
// *TODO - determine if there should be some better error state than
|
|
// mLines being empty. -brad
|
|
LL_WARNS() << "invalid heap profile data passed into parser." << LL_ENDL;
|
|
return;
|
|
}
|
|
|
|
std::vector< range_t > prof_lines;
|
|
|
|
std::string::const_iterator prof_begin = prof_text.begin() + HEAP_PROFILE_MAGIC_STR.length();
|
|
|
|
range_t prof_range(prof_begin, prof_text.end());
|
|
boost::algorithm::split(prof_lines,
|
|
prof_range,
|
|
boost::bind(std::equal_to<llwchar>(), '\n', _1));
|
|
|
|
std::vector< range_t >::const_iterator i;
|
|
for(i = prof_lines.begin(); i != prof_lines.end() && !i->empty(); ++i)
|
|
{
|
|
range_t const & line_text = *i;
|
|
|
|
std::vector<range_t> line_elems;
|
|
|
|
boost::algorithm::split(line_elems,
|
|
line_text,
|
|
is_separator);
|
|
|
|
std::vector< range_t >::iterator j;
|
|
j = line_elems.begin();
|
|
|
|
while(j != line_elems.end() && j->empty()) { ++j; } // skip any separator tokens
|
|
llassert_always(j != line_elems.end());
|
|
U32 live_count = boost::lexical_cast<U32>(*j);
|
|
++j;
|
|
|
|
while(j != line_elems.end() && j->empty()) { ++j; } // skip any separator tokens
|
|
llassert_always(j != line_elems.end());
|
|
U64 live_size = boost::lexical_cast<U64>(*j);
|
|
++j;
|
|
|
|
while(j != line_elems.end() && j->empty()) { ++j; } // skip any separator tokens
|
|
llassert_always(j != line_elems.end());
|
|
U32 tot_count = boost::lexical_cast<U32>(*j);
|
|
++j;
|
|
|
|
while(j != line_elems.end() && j->empty()) { ++j; } // skip any separator tokens
|
|
llassert_always(j != line_elems.end());
|
|
U64 tot_size = boost::lexical_cast<U64>(*j);
|
|
++j;
|
|
|
|
while(j != line_elems.end() && j->empty()) { ++j; } // skip any separator tokens
|
|
llassert(j != line_elems.end());
|
|
if (j != line_elems.end())
|
|
{
|
|
++j; // skip the '@'
|
|
|
|
mLines.push_back(line(live_count, live_size, tot_count, tot_size));
|
|
line & current_line = mLines.back();
|
|
|
|
for(; j != line_elems.end(); ++j)
|
|
{
|
|
if(!j->empty())
|
|
{
|
|
U32 marker = boost::lexical_cast<U32>(*j);
|
|
current_line.mTrace.push_back(marker);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
// *TODO - parse MAPPED_LIBRARIES section here if we're ever interested in it
|
|
}
|
|
|
|
void LLAllocatorHeapProfile::dump(std::ostream & out) const
|
|
{
|
|
for (const LLAllocatorHeapProfile::line& line : mLines)
|
|
{
|
|
out << line.mLiveCount << ": " << line.mLiveSize << '[' << line.mTotalCount << ": " << line.mTotalSize << "] @";
|
|
|
|
for (const stack_marker marker : line.mTrace)
|
|
{
|
|
out << ' ' << marker;
|
|
}
|
|
out << '\n';
|
|
}
|
|
out.flush();
|
|
}
|
|
|