diff --git a/.gitignore b/.gitignore index 4ed5246f..fe9e6c3c 100644 --- a/.gitignore +++ b/.gitignore @@ -42,6 +42,7 @@ Makefile.in /test/extended /test/mosaic /test/parallel +/test/profile /test/query /test/test /test/try_open diff --git a/Makefile.am b/Makefile.am index b64e9cb7..a6e02ffc 100644 --- a/Makefile.am +++ b/Makefile.am @@ -92,7 +92,7 @@ noinst_HEADERS = \ noinst_PROGRAMS = test/test test/try_open test/parallel test/query \ - test/extended test/mosaic + test/extended test/mosaic test/profile noinst_SCRIPTS = test/driver CLEANFILES += test/driver EXTRA_DIST += test/driver.in @@ -118,6 +118,9 @@ test_extended_LDADD = src/libopenslide.la $(GLIB2_LIBS) test_mosaic_CPPFLAGS = $(GLIB2_CFLAGS) $(CAIRO_CFLAGS) -I$(top_srcdir)/src test_mosaic_LDADD = src/libopenslide.la $(GLIB2_LIBS) $(CAIRO_LIBS) +test_profile_CPPFLAGS = $(GLIB2_CFLAGS) $(VALGRIND_CFLAGS) -I$(top_srcdir)/src +test_profile_LDADD = src/libopenslide.la $(GLIB2_LIBS) + if CYGWIN_CROSS_TEST noinst_PROGRAMS += test/symlink test_symlink_LDADD = -lkernel32 diff --git a/README.txt b/README.txt index 05aedba3..1bba92eb 100644 --- a/README.txt +++ b/README.txt @@ -34,7 +34,8 @@ If you want to run the test suite, you will need PyYAML, python-requests, xdelta3, cjpeg and djpeg (from libjpeg), a Git checkout of OpenSlide, at least one installed font, and > 120 GB of disk space. Valgrind mode requires Valgrind, plus debug symbols for library dependencies (particularly -glib2) and Fontconfig. Coverage mode requires gcov and Doxygen. +glib2) and Fontconfig. Profile mode requires Valgrind. Coverage mode +requires gcov and Doxygen. Features diff --git a/test/driver.in b/test/driver.in index 35c6ff5a..3a137d7e 100644 --- a/test/driver.in +++ b/test/driver.in @@ -35,7 +35,7 @@ import struct import subprocess import sys import tarfile -from tempfile import mkdtemp, TemporaryFile +from tempfile import mkdtemp, TemporaryFile, NamedTemporaryFile import textwrap from time import time as curtime from urlparse import urljoin @@ -665,6 +665,29 @@ def time(pattern='*'): @_command +def profile(pattern='*', level=0): + '''Profile openslide_read_region() on the specified level for all + successful primary tests matching the specified pattern.''' + env = os.environ.copy() + env.update( + G_MESSAGES_DEBUG='', + OPENSLIDE_DEBUG='performance', + ) + line = '#' * 79 + for testname, slidefile in _successful_primary_tests(pattern): + print '%s\n# %s\n%s\n' % (line, testname, line) + with NamedTemporaryFile(prefix='openslide-callgrind-') as fh: + args = ['!!BUILDDIR!!/../libtool', '--mode=execute', + 'valgrind', '--quiet', '--error-exitcode=3', + '--tool=callgrind', '--callgrind-out-file=' + fh.name, + '--instr-atstart=no', + '!!BUILDDIR!!/profile', slidefile, str(level)] + if subprocess.call(args, env=env) == 0: + subprocess.check_call(['callgrind_annotate', + '--threshold=80', fh.name]) + + +@_command def clean(pattern='*'): '''Delete temporary slide data for tests matching the specified pattern.''' for testname in _list_tests(pattern): diff --git a/test/profile.c b/test/profile.c new file mode 100644 index 00000000..5fe09af0 --- /dev/null +++ b/test/profile.c @@ -0,0 +1,120 @@ +/* + * OpenSlide, a library for reading whole slide image files + * + * Copyright (c) 2007-2015 Carnegie Mellon University + * All rights reserved. + * + * OpenSlide 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. + * + * OpenSlide 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 OpenSlide. If not, see + * . + * + */ + +#include +#include +#include +#include +#include +#include + +#include "config.h" +#ifdef HAVE_VALGRIND +#include +#endif + +#define BUFWIDTH 1000 +#define BUFHEIGHT 1000 +#define MAXWIDTH 10000 +#define MAXHEIGHT 10000 + +static void fail(const char *str, ...) { + va_list ap; + + va_start(ap, str); + vfprintf(stderr, str, ap); + fprintf(stderr, "\n"); + va_end(ap); + exit(1); +} + +int main(int argc, char **argv) { + if (argc != 3) { + fail("Usage: %s ", argv[0]); + } + const char *path = argv[1]; + int level = atoi(argv[2]); + + openslide_t *osr = openslide_open(path); + if (!osr) { + fail("Couldn't open %s", path); + } + const char *err = openslide_get_error(osr); + if (err) { + fail("Open failed: %s", err); + } + if (level >= openslide_get_level_count(osr)) { + fail("No such level: %d", level); + } + + // Get dimensions + int64_t x = 0; + int64_t y = 0; + int64_t w, h; + openslide_get_level_dimensions(osr, level, &w, &h); + + // Read from active region, if denoted + const char *bounds_x = openslide_get_property_value(osr, OPENSLIDE_PROPERTY_NAME_BOUNDS_X); + const char *bounds_y = openslide_get_property_value(osr, OPENSLIDE_PROPERTY_NAME_BOUNDS_Y); + const char *bounds_w = openslide_get_property_value(osr, OPENSLIDE_PROPERTY_NAME_BOUNDS_WIDTH); + const char *bounds_h = openslide_get_property_value(osr, OPENSLIDE_PROPERTY_NAME_BOUNDS_HEIGHT); + if (bounds_x && bounds_y) { + x = g_ascii_strtoll(bounds_x, NULL, 10); + y = g_ascii_strtoll(bounds_y, NULL, 10); + } + if (bounds_w && bounds_h) { + double downsample = openslide_get_level_downsample(osr, level); + w = g_ascii_strtoll(bounds_w, NULL, 10) / downsample; + h = g_ascii_strtoll(bounds_h, NULL, 10) / downsample; + } + + w = MIN(w, MAXWIDTH); + h = MIN(h, MAXHEIGHT); + uint32_t *buf = g_new(uint32_t, BUFWIDTH * BUFHEIGHT); + + printf("Reading (%"PRId64", %"PRId64") in level %d for " + "%"PRId64" x %"PRId64"\n\n", x, y, level, w, h); + +#ifdef HAVE_VALGRIND + CALLGRIND_START_INSTRUMENTATION; +#endif + + for (int yy = 0; yy < h; yy += BUFHEIGHT) { + for (int xx = 0; xx < w; xx += BUFWIDTH) { + openslide_read_region(osr, buf, x + xx, y + yy, level, + MIN(BUFWIDTH, w - xx), MIN(BUFHEIGHT, h - yy)); + } + } + +#ifdef HAVE_VALGRIND + CALLGRIND_STOP_INSTRUMENTATION; +#endif + + err = openslide_get_error(osr); + if (err) { + fail("Read failed: %s", err); + } + + g_free(buf); + openslide_close(osr); + + return 0; +}