From 0a54247652e49ce384dcf0d8df078201aa106089 Mon Sep 17 00:00:00 2001 From: Jay Berkenbilt Date: Wed, 31 Aug 2022 12:49:29 -0400 Subject: Add QUtil::get_max_memory_usage for testing --- libqpdf/CMakeLists.txt | 23 ++++++++++++++ libqpdf/QUtil.cc | 73 +++++++++++++++++++++++++++++++++++++++++++ libqpdf/qpdf/qpdf-config.h.in | 2 ++ 3 files changed, 98 insertions(+) (limited to 'libqpdf') diff --git a/libqpdf/CMakeLists.txt b/libqpdf/CMakeLists.txt index cf807f6d..106292a3 100644 --- a/libqpdf/CMakeLists.txt +++ b/libqpdf/CMakeLists.txt @@ -375,6 +375,29 @@ int main(int argc, char* argv[]) { endif() endfunction() +check_c_source_compiles( +"#include +#include +int main(int argc, char* argv[]) { + malloc_info(0, stdout); + return 0; +}" + HAVE_MALLOC_INFO) + +check_c_source_compiles( +"#include +#include +int main(int argc, char* argv[]) { + char* buf; + size_t size; + FILE* f; + f = open_memstream(&buf, &size); + fclose(f); + free(buf); + return 0; +}" + HAVE_OPEN_MEMSTREAM) + qpdf_check_ll_fmt("%lld" fmt_lld) qpdf_check_ll_fmt("%I64d" fmt_i64d) qpdf_check_ll_fmt("%I64lld" fmt_i64lld) diff --git a/libqpdf/QUtil.cc b/libqpdf/QUtil.cc index d565ece0..98a8f318 100644 --- a/libqpdf/QUtil.cc +++ b/libqpdf/QUtil.cc @@ -37,6 +37,9 @@ # include # include #endif +#ifdef HAVE_MALLOC_INFO +# include +#endif // First element is 24 static unsigned short pdf_doc_low_to_unicode[] = { @@ -1968,3 +1971,73 @@ QUtil::call_main_from_wmain( } #endif // QPDF_NO_WCHAR_T + +size_t +QUtil::get_max_memory_usage() +{ +#if defined(HAVE_MALLOC_INFO) && defined(HAVE_OPEN_MEMSTREAM) + static std::regex tag_re("<(/?\\w+)([^>]*?)>"); + static std::regex attr_re("(\\w+)=\"(.*?)\""); + + char* buf; + size_t size; + FILE* f = open_memstream(&buf, &size); + if (f == nullptr) { + return 0; + } + malloc_info(0, f); + fclose(f); + if (QUtil::get_env("QPDF_DEBUG_MEM_USAGE")) { + fprintf(stderr, "%s", buf); + } + + // Warning: this code uses regular expression to extract data from + // an XML string. This is generally a bad idea, but we're going to + // do it anyway because QUtil.hh warns against using this function + // for other than development/testing, and if this function fails + // to generate reasonable output during performance testing, it + // will be noticed. + + // This is my best guess at how to interpret malloc_info. Anyway + // it seems to provide useful information for detecting code + // changes that drastically change memory usage. + size_t result = 0; + try { + std::cregex_iterator m_begin(buf, buf + size, tag_re); + std::cregex_iterator cr_end; + std::sregex_iterator sr_end; + + int in_heap = 0; + for (auto m = m_begin; m != cr_end; ++m) { + std::string tag(m->str(1)); + if (tag == "heap") { + ++in_heap; + } else if (tag == "/heap") { + --in_heap; + } else if (in_heap == 0) { + std::string rest = m->str(2); + std::map attrs; + std::sregex_iterator a_begin(rest.begin(), rest.end(), attr_re); + for (auto m2 = a_begin; m2 != sr_end; ++m2) { + attrs[m2->str(1)] = m2->str(2); + } + if (tag == "total") { + if (attrs.count("size") > 0) { + result += QIntC::to_size( + QUtil::string_to_ull(attrs["size"].c_str())); + } + } else if (tag == "system" && attrs["type"] == "max") { + result += QIntC::to_size( + QUtil::string_to_ull(attrs["size"].c_str())); + } + } + } + } catch (...) { + // ignore -- just return 0 + } + free(buf); + return result; +#else + return 0; +#endif +} diff --git a/libqpdf/qpdf/qpdf-config.h.in b/libqpdf/qpdf/qpdf-config.h.in index 8a22b875..500f55cc 100644 --- a/libqpdf/qpdf/qpdf-config.h.in +++ b/libqpdf/qpdf/qpdf-config.h.in @@ -21,6 +21,8 @@ #cmakedefine HAVE_LOCALTIME_R 1 #cmakedefine HAVE_RANDOM 1 #cmakedefine HAVE_TM_GMTOFF 1 +#cmakedefine HAVE_MALLOC_INFO 1 +#cmakedefine HAVE_OPEN_MEMSTREAM 1 /* printf format for long long */ #cmakedefine LL_FMT "${LL_FMT}" -- cgit v1.2.3-54-g00ecf