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;
+}