Add QUtil::get_max_memory_usage for testing

This commit is contained in:
Jay Berkenbilt 2022-08-31 12:49:29 -04:00
parent 0adfd74f8b
commit 0a54247652
7 changed files with 127 additions and 1 deletions

View File

@ -131,6 +131,7 @@
"esize",
"eval",
"extlibdir",
"fclose",
"fdict",
"ffield",
"fghij",
@ -268,6 +269,7 @@
"maxdepth",
"maxobjectid",
"mdash",
"memstream",
"mindepth",
"mkdir",
"mkinstalldirs",

View File

@ -525,7 +525,17 @@ namespace QUtil
wchar_t const* const argv[],
std::function<int(int, char const* const[])> realmain);
#endif // QPDF_NO_WCHAR_T
}; // namespace QUtil
// Try to return the maximum amount of memory allocated by the
// current process and its threads. Return 0 if unable to
// determine. This is Linux-specific and not implemented to be
// completely reliable. It is used during development for
// performance testing to detect changes that may significantly
// change memory usage. It is not recommended for use for other
// purposes.
QPDF_DLL
size_t get_max_memory_usage();
}; // namespace QUtil
inline bool
QUtil::is_hex_digit(char ch)

View File

@ -375,6 +375,29 @@ int main(int argc, char* argv[]) {
endif()
endfunction()
check_c_source_compiles(
"#include <malloc.h>
#include <stdio.h>
int main(int argc, char* argv[]) {
malloc_info(0, stdout);
return 0;
}"
HAVE_MALLOC_INFO)
check_c_source_compiles(
"#include <stdio.h>
#include <stdlib.h>
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)

View File

@ -37,6 +37,9 @@
# include <sys/stat.h>
# include <unistd.h>
#endif
#ifdef HAVE_MALLOC_INFO
# include <malloc.h>
#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<std::string, std::string> 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
}

View File

@ -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}"

View File

@ -134,3 +134,5 @@ D:20210209191925Z
2021-02-09T19:19:25Z
---- is_long_long
done
---- memory usage
memory usage okay

View File

@ -703,6 +703,18 @@ is_long_long_test()
std::cout << "done" << std::endl;
}
void
memory_usage_test()
{
auto u1 = QUtil::get_max_memory_usage();
if (u1 > 0) {
auto x = QUtil::make_shared_array<int>(10 << 20);
auto u2 = QUtil::get_max_memory_usage();
assert(u2 > u1);
}
std::cout << "memory usage okay" << std::endl;
}
int
main(int argc, char* argv[])
{
@ -739,6 +751,8 @@ main(int argc, char* argv[])
timestamp_test();
std::cout << "---- is_long_long" << std::endl;
is_long_long_test();
std::cout << "---- memory usage" << std::endl;
memory_usage_test();
} catch (std::exception& e) {
std::cout << "unexpected exception: " << e.what() << std::endl;
}