Thanks to Godmar Back for suggestion.
tests/vm_TESTS = $(addprefix tests/vm/,pt-grow-stack pt-grow-pusha \
pt-grow-bad pt-big-stk-obj pt-bad-addr pt-bad-read pt-write-code \
pt-write-code2 pt-grow-stk-sc page-linear page-parallel page-merge-seq \
-page-merge-par page-shuffle mmap-read mmap-close mmap-unmap \
-mmap-overlap mmap-twice mmap-write mmap-exit mmap-shuffle mmap-bad-fd \
-mmap-clean mmap-inherit mmap-misalign mmap-null mmap-over-code \
-mmap-over-data mmap-over-stk mmap-remove mmap-zero)
+page-merge-par page-merge-stk page-merge-mm page-shuffle mmap-read \
+mmap-close mmap-unmap mmap-overlap mmap-twice mmap-write mmap-exit \
+mmap-shuffle mmap-bad-fd mmap-clean mmap-inherit mmap-misalign \
+mmap-null mmap-over-code mmap-over-data mmap-over-stk mmap-remove \
+mmap-zero)
tests/vm_PROGS = $(tests/vm_TESTS) $(addprefix tests/vm/,child-linear \
-child-sort child-mm-wrt child-inherit)
+child-sort child-qsort child-qsort-mm child-mm-wrt child-inherit)
tests/vm/pt-grow-stack_SRC = tests/vm/pt-grow-stack.c tests/arc4.c \
tests/cksum.c tests/lib.c tests/main.c
tests/vm/page-parallel_SRC = tests/vm/page-parallel.c tests/lib.c tests/main.c
tests/vm/page-merge-seq_SRC = tests/vm/page-merge-seq.c tests/arc4.c \
tests/lib.c tests/main.c
-tests/vm/page-merge-par_SRC = tests/vm/page-merge-par.c tests/arc4.c \
-tests/lib.c tests/main.c
+tests/vm/page-merge-par_SRC = tests/vm/page-merge-par.c \
+tests/vm/parallel-merge.c tests/arc4.c tests/lib.c tests/main.c
+tests/vm/page-merge-stk_SRC = tests/vm/page-merge-stk.c \
+tests/vm/parallel-merge.c tests/arc4.c tests/lib.c tests/main.c
+tests/vm/page-merge-mm_SRC = tests/vm/page-merge-mm.c \
+tests/vm/parallel-merge.c tests/arc4.c tests/lib.c tests/main.c
tests/vm/page-shuffle_SRC = tests/vm/page-shuffle.c tests/arc4.c \
tests/cksum.c tests/lib.c tests/main.c
tests/vm/mmap-read_SRC = tests/vm/mmap-read.c tests/lib.c tests/main.c
tests/vm/mmap-zero_SRC = tests/vm/mmap-zero.c tests/lib.c tests/main.c
tests/vm/child-linear_SRC = tests/vm/child-linear.c tests/arc4.c tests/lib.c
+tests/vm/child-qsort_SRC = tests/vm/child-qsort.c tests/vm/qsort.c tests/lib.c
+tests/vm/child-qsort-mm_SRC = tests/vm/child-qsort-mm.c tests/vm/qsort.c \
+tests/lib.c
tests/vm/child-sort_SRC = tests/vm/child-sort.c tests/lib.c
tests/vm/child-mm-wrt_SRC = tests/vm/child-mm-wrt.c tests/lib.c tests/main.c
tests/vm/child-inherit_SRC = tests/vm/child-inherit.c tests/lib.c tests/main.c
tests/vm/page-parallel_PUTFILES = tests/vm/child-linear
tests/vm/page-merge-seq_PUTFILES = tests/vm/child-sort
tests/vm/page-merge-par_PUTFILES = tests/vm/child-sort
+tests/vm/page-merge-stk_PUTFILES = tests/vm/child-qsort
+tests/vm/page-merge-mm_PUTFILES = tests/vm/child-qsort-mm
tests/vm/mmap-clean_PUTFILES = tests/vm/sample.txt
tests/vm/mmap-inherit_PUTFILES = tests/vm/sample.txt tests/vm/child-inherit
tests/vm/mmap-misalign_PUTFILES = tests/vm/sample.txt
3 page-shuffle
5 page-merge-seq
5 page-merge-par
+5 page-merge-mm
+5 page-merge-stk
- Test "mmap" system call.
2 mmap-read
--- /dev/null
+/* Mmaps a 128 kB file "sorts" the bytes in it, using quick sort,
+ a multi-pass divide and conquer algorithm. */
+
+#include <debug.h>
+#include <syscall.h>
+#include "tests/lib.h"
+#include "tests/main.h"
+#include "tests/vm/qsort.h"
+
+const char *test_name = "child-qsort-mm";
+
+int
+main (int argc UNUSED, char *argv[])
+{
+ int handle;
+ unsigned char *p = (unsigned char *) 0x10000000;
+
+ quiet = true;
+
+ CHECK ((handle = open (argv[1])) > 1, "open \"%s\"", argv[1]);
+ CHECK (mmap (handle, p) != MAP_FAILED, "mmap \"%s\"", argv[1]);
+ qsort_bytes (p, 1024 * 128);
+
+ return 80;
+}
--- /dev/null
+/* Reads a 128 kB file onto the stack and "sorts" the bytes in
+ it, using quick sort, a multi-pass divide and conquer
+ algorithm. The sorted data is written back to the same file
+ in-place. */
+
+#include <debug.h>
+#include <syscall.h>
+#include "tests/lib.h"
+#include "tests/main.h"
+#include "tests/vm/qsort.h"
+
+const char *test_name = "child-qsort";
+
+int
+main (int argc UNUSED, char *argv[])
+{
+ int handle;
+ unsigned char buf[128 * 1024];
+ size_t size;
+
+ quiet = true;
+
+ CHECK ((handle = open (argv[1])) > 1, "open \"%s\"", argv[1]);
+
+ size = read (handle, buf, sizeof buf);
+ qsort_bytes (buf, sizeof buf);
+ seek (handle, 0);
+ write (handle, buf, size);
+ close (handle);
+
+ return 72;
+}
-/* Reads a 128 kB file and "sorts" the bytes in it, using
- counting sort. The sorted data is written back to the same
- file in-place. */
+/* Reads a 128 kB file into static data and "sorts" the bytes in
+ it, using counting sort, a single-pass algorithm. The sorted
+ data is written back to the same file in-place. */
#include <debug.h>
#include <syscall.h>
--- /dev/null
+#include "tests/main.h"
+#include "tests/vm/parallel-merge.h"
+
+void
+test_main (void)
+{
+ parallel_merge ("child-qsort-mm", 80);
+}
--- /dev/null
+# -*- perl -*-
+use strict;
+use warnings;
+use tests::tests;
+check_expected (IGNORE_EXIT_CODES => 1, [<<'EOF']);
+(page-merge-mm) begin
+(page-merge-mm) init
+(page-merge-mm) sort chunk 0
+(page-merge-mm) sort chunk 1
+(page-merge-mm) sort chunk 2
+(page-merge-mm) sort chunk 3
+(page-merge-mm) sort chunk 4
+(page-merge-mm) sort chunk 5
+(page-merge-mm) sort chunk 6
+(page-merge-mm) sort chunk 7
+(page-merge-mm) wait for child 0
+(page-merge-mm) wait for child 1
+(page-merge-mm) wait for child 2
+(page-merge-mm) wait for child 3
+(page-merge-mm) wait for child 4
+(page-merge-mm) wait for child 5
+(page-merge-mm) wait for child 6
+(page-merge-mm) wait for child 7
+(page-merge-mm) merge
+(page-merge-mm) verify
+(page-merge-mm) success, buf_idx=1,048,576
+(page-merge-mm) end
+EOF
+pass;
-/* Generates about 1 MB of random data that is then divided into
- 16 chunks. A separate subprocess sorts each chunk; the
- subprocesses run in parallel. Then we merge the chunks and
- verify that the result is what it should be. */
-
-#include <stdio.h>
-#include <syscall.h>
-#include "tests/arc4.h"
-#include "tests/lib.h"
#include "tests/main.h"
-
-#define CHUNK_SIZE (128 * 1024)
-#define CHUNK_CNT 8 /* Number of chunks. */
-#define DATA_SIZE (CHUNK_CNT * CHUNK_SIZE) /* Buffer size. */
-
-unsigned char buf1[DATA_SIZE], buf2[DATA_SIZE];
-size_t histogram[256];
-
-/* Initialize buf1 with random data,
- then count the number of instances of each value within it. */
-static void
-init (void)
-{
- struct arc4 arc4;
- size_t i;
-
- msg ("init");
-
- arc4_init (&arc4, "foobar", 6);
- arc4_crypt (&arc4, buf1, sizeof buf1);
- for (i = 0; i < sizeof buf1; i++)
- histogram[buf1[i]]++;
-}
-
-/* Sort each chunk of buf1 using a subprocess. */
-static void
-sort_chunks (void)
-{
- pid_t children[CHUNK_CNT];
- size_t i;
-
- for (i = 0; i < CHUNK_CNT; i++)
- {
- char fn[128];
- char cmd[128];
- int handle;
-
- msg ("sort chunk %zu", i);
-
- /* Write this chunk to a file. */
- snprintf (fn, sizeof fn, "buf%zu", i);
- create (fn, CHUNK_SIZE);
- quiet = true;
- CHECK ((handle = open (fn)) > 1, "open \"%s\"", fn);
- write (handle, buf1 + CHUNK_SIZE * i, CHUNK_SIZE);
- close (handle);
-
- /* Sort with subprocess. */
- snprintf (cmd, sizeof cmd, "child-sort %s", fn);
- CHECK ((children[i] = exec (cmd)) != -1, "exec \"%s\"", cmd);
- quiet = false;
- }
-
- for (i = 0; i < CHUNK_CNT; i++)
- {
- char fn[128];
- int handle;
-
- CHECK (wait (children[i]) == 123, "wait for child %zu", i);
-
- /* Read chunk back from file. */
- quiet = true;
- snprintf (fn, sizeof fn, "buf%zu", i);
- CHECK ((handle = open (fn)) > 1, "open \"%s\"", fn);
- read (handle, buf1 + CHUNK_SIZE * i, CHUNK_SIZE);
- close (handle);
- quiet = false;
- }
-}
-
-/* Merge the sorted chunks in buf1 into a fully sorted buf2. */
-static void
-merge (void)
-{
- unsigned char *mp[CHUNK_CNT];
- size_t mp_left;
- unsigned char *op;
- size_t i;
-
- msg ("merge");
-
- /* Initialize merge pointers. */
- mp_left = CHUNK_CNT;
- for (i = 0; i < CHUNK_CNT; i++)
- mp[i] = buf1 + CHUNK_SIZE * i;
-
- /* Merge. */
- op = buf2;
- while (mp_left > 0)
- {
- /* Find smallest value. */
- size_t min = 0;
- for (i = 1; i < mp_left; i++)
- if (*mp[i] < *mp[min])
- min = i;
-
- /* Append value to buf2. */
- *op++ = *mp[min];
-
- /* Advance merge pointer.
- Delete this chunk from the set if it's emptied. */
- if ((++mp[min] - buf1) % CHUNK_SIZE == 0)
- mp[min] = mp[--mp_left];
- }
-}
-
-static void
-verify (void)
-{
- size_t buf_idx;
- size_t hist_idx;
-
- msg ("verify");
-
- buf_idx = 0;
- for (hist_idx = 0; hist_idx < sizeof histogram / sizeof *histogram;
- hist_idx++)
- {
- while (histogram[hist_idx]-- > 0)
- {
- if (buf2[buf_idx] != hist_idx)
- fail ("bad value %d in byte %zu", buf2[buf_idx], buf_idx);
- buf_idx++;
- }
- }
-
- msg ("success, buf_idx=%'zu", buf_idx);
-}
+#include "tests/vm/parallel-merge.h"
void
-test_main (void)
+test_main (void)
{
- init ();
- sort_chunks ();
- merge ();
- verify ();
+ parallel_merge ("child-sort", 123);
}
--- /dev/null
+#include "tests/main.h"
+#include "tests/vm/parallel-merge.h"
+
+void
+test_main (void)
+{
+ parallel_merge ("child-qsort", 72);
+}
--- /dev/null
+# -*- perl -*-
+use strict;
+use warnings;
+use tests::tests;
+check_expected (IGNORE_EXIT_CODES => 1, [<<'EOF']);
+(page-merge-stk) begin
+(page-merge-stk) init
+(page-merge-stk) sort chunk 0
+(page-merge-stk) sort chunk 1
+(page-merge-stk) sort chunk 2
+(page-merge-stk) sort chunk 3
+(page-merge-stk) sort chunk 4
+(page-merge-stk) sort chunk 5
+(page-merge-stk) sort chunk 6
+(page-merge-stk) sort chunk 7
+(page-merge-stk) wait for child 0
+(page-merge-stk) wait for child 1
+(page-merge-stk) wait for child 2
+(page-merge-stk) wait for child 3
+(page-merge-stk) wait for child 4
+(page-merge-stk) wait for child 5
+(page-merge-stk) wait for child 6
+(page-merge-stk) wait for child 7
+(page-merge-stk) merge
+(page-merge-stk) verify
+(page-merge-stk) success, buf_idx=1,048,576
+(page-merge-stk) end
+EOF
+pass;
--- /dev/null
+/* Generates about 1 MB of random data that is then divided into
+ 16 chunks. A separate subprocess sorts each chunk; the
+ subprocesses run in parallel. Then we merge the chunks and
+ verify that the result is what it should be. */
+
+#include "tests/vm/parallel-merge.h"
+#include <stdio.h>
+#include <syscall.h>
+#include "tests/arc4.h"
+#include "tests/lib.h"
+#include "tests/main.h"
+
+#define CHUNK_SIZE (128 * 1024)
+#define CHUNK_CNT 8 /* Number of chunks. */
+#define DATA_SIZE (CHUNK_CNT * CHUNK_SIZE) /* Buffer size. */
+
+unsigned char buf1[DATA_SIZE], buf2[DATA_SIZE];
+size_t histogram[256];
+
+/* Initialize buf1 with random data,
+ then count the number of instances of each value within it. */
+static void
+init (void)
+{
+ struct arc4 arc4;
+ size_t i;
+
+ msg ("init");
+
+ arc4_init (&arc4, "foobar", 6);
+ arc4_crypt (&arc4, buf1, sizeof buf1);
+ for (i = 0; i < sizeof buf1; i++)
+ histogram[buf1[i]]++;
+}
+
+/* Sort each chunk of buf1 using SUBPROCESS,
+ which is expected to return EXIT_STATUS. */
+static void
+sort_chunks (const char *subprocess, int exit_status)
+{
+ pid_t children[CHUNK_CNT];
+ size_t i;
+
+ for (i = 0; i < CHUNK_CNT; i++)
+ {
+ char fn[128];
+ char cmd[128];
+ int handle;
+
+ msg ("sort chunk %zu", i);
+
+ /* Write this chunk to a file. */
+ snprintf (fn, sizeof fn, "buf%zu", i);
+ create (fn, CHUNK_SIZE);
+ quiet = true;
+ CHECK ((handle = open (fn)) > 1, "open \"%s\"", fn);
+ write (handle, buf1 + CHUNK_SIZE * i, CHUNK_SIZE);
+ close (handle);
+
+ /* Sort with subprocess. */
+ snprintf (cmd, sizeof cmd, "%s %s", subprocess, fn);
+ CHECK ((children[i] = exec (cmd)) != -1, "exec \"%s\"", cmd);
+ quiet = false;
+ }
+
+ for (i = 0; i < CHUNK_CNT; i++)
+ {
+ char fn[128];
+ int handle;
+
+ CHECK (wait (children[i]) == exit_status, "wait for child %zu", i);
+
+ /* Read chunk back from file. */
+ quiet = true;
+ snprintf (fn, sizeof fn, "buf%zu", i);
+ CHECK ((handle = open (fn)) > 1, "open \"%s\"", fn);
+ read (handle, buf1 + CHUNK_SIZE * i, CHUNK_SIZE);
+ close (handle);
+ quiet = false;
+ }
+}
+
+/* Merge the sorted chunks in buf1 into a fully sorted buf2. */
+static void
+merge (void)
+{
+ unsigned char *mp[CHUNK_CNT];
+ size_t mp_left;
+ unsigned char *op;
+ size_t i;
+
+ msg ("merge");
+
+ /* Initialize merge pointers. */
+ mp_left = CHUNK_CNT;
+ for (i = 0; i < CHUNK_CNT; i++)
+ mp[i] = buf1 + CHUNK_SIZE * i;
+
+ /* Merge. */
+ op = buf2;
+ while (mp_left > 0)
+ {
+ /* Find smallest value. */
+ size_t min = 0;
+ for (i = 1; i < mp_left; i++)
+ if (*mp[i] < *mp[min])
+ min = i;
+
+ /* Append value to buf2. */
+ *op++ = *mp[min];
+
+ /* Advance merge pointer.
+ Delete this chunk from the set if it's emptied. */
+ if ((++mp[min] - buf1) % CHUNK_SIZE == 0)
+ mp[min] = mp[--mp_left];
+ }
+}
+
+static void
+verify (void)
+{
+ size_t buf_idx;
+ size_t hist_idx;
+
+ msg ("verify");
+
+ buf_idx = 0;
+ for (hist_idx = 0; hist_idx < sizeof histogram / sizeof *histogram;
+ hist_idx++)
+ {
+ while (histogram[hist_idx]-- > 0)
+ {
+ if (buf2[buf_idx] != hist_idx)
+ fail ("bad value %d in byte %zu", buf2[buf_idx], buf_idx);
+ buf_idx++;
+ }
+ }
+
+ msg ("success, buf_idx=%'zu", buf_idx);
+}
+
+void
+parallel_merge (const char *child_name, int exit_status)
+{
+ init ();
+ sort_chunks (child_name, exit_status);
+ merge ();
+ verify ();
+}
--- /dev/null
+#ifndef TESTS_VM_PARALLEL_MERGE
+#define TESTS_VM_PARALLEL_MERGE 1
+
+void parallel_merge (const char *child_name, int exit_status);
+
+#endif /* tests/vm/parallel-merge.h */
--- /dev/null
+#include "tests/vm/qsort.h"
+#include <stdbool.h>
+#include <debug.h>
+#include <random.h>
+
+/* Picks a pivot for the quicksort from the SIZE bytes in BUF. */
+static unsigned char
+pick_pivot (unsigned char *buf, size_t size)
+{
+ ASSERT (size >= 1);
+ return buf[random_ulong () % size];
+}
+
+/* Checks whether the SIZE bytes in ARRAY are divided into an
+ initial LEFT_SIZE elements all less than PIVOT followed by
+ SIZE - LEFT_SIZE elements all greater than or equal to
+ PIVOT. */
+static bool
+is_partitioned (const unsigned char *array, size_t size,
+ unsigned char pivot, size_t left_size)
+{
+ size_t i;
+
+ for (i = 0; i < left_size; i++)
+ if (array[i] >= pivot)
+ return false;
+
+ for (; i < size; i++)
+ if (array[i] < pivot)
+ return false;
+
+ return true;
+}
+
+/* Swaps the bytes at *A and *B. */
+static void
+swap (unsigned char *a, unsigned char *b)
+{
+ unsigned char t = *a;
+ *a = *b;
+ *b = t;
+}
+
+/* Partitions ARRAY in-place in an initial run of bytes all less
+ than PIVOT, followed by a run of bytes all greater than or
+ equal to PIVOT. Returns the length of the initial run. */
+static size_t
+partition (unsigned char *array, size_t size, int pivot)
+{
+ size_t left_size = size;
+ unsigned char *first = array;
+ unsigned char *last = first + left_size;
+
+ for (;;)
+ {
+ /* Move FIRST forward to point to first element greater than
+ PIVOT. */
+ for (;;)
+ {
+ if (first == last)
+ {
+ ASSERT (is_partitioned (array, size, pivot, left_size));
+ return left_size;
+ }
+ else if (*first >= pivot)
+ break;
+
+ first++;
+ }
+ left_size--;
+
+ /* Move LAST backward to point to last element no bigger
+ than PIVOT. */
+ for (;;)
+ {
+ last--;
+
+ if (first == last)
+ {
+ ASSERT (is_partitioned (array, size, pivot, left_size));
+ return left_size;
+ }
+ else if (*last < pivot)
+ break;
+ else
+ left_size--;
+ }
+
+ /* By swapping FIRST and LAST we extend the starting and
+ ending sequences that pass and fail, respectively,
+ PREDICATE. */
+ swap (first, last);
+ first++;
+ }
+}
+
+/* Returns true if the SIZE bytes in BUF are in nondecreasing
+ order, false otherwise. */
+static bool
+is_sorted (const unsigned char *buf, size_t size)
+{
+ size_t i;
+
+ for (i = 1; i < size; i++)
+ if (buf[i - 1] > buf[i])
+ return false;
+
+ return true;
+}
+
+/* Sorts the SIZE bytes in BUF into nondecreasing order, using
+ the quick-sort algorithm. */
+void
+qsort_bytes (unsigned char *buf, size_t size)
+{
+ if (!is_sorted (buf, size))
+ {
+ int pivot = pick_pivot (buf, size);
+
+ unsigned char *left_half = buf;
+ size_t left_size = partition (buf, size, pivot);
+ unsigned char *right_half = left_half + left_size;
+ size_t right_size = size - left_size;
+
+ if (left_size <= right_size)
+ {
+ qsort_bytes (left_half, left_size);
+ qsort_bytes (right_half, right_size);
+ }
+ else
+ {
+ qsort_bytes (right_half, right_size);
+ qsort_bytes (left_half, left_size);
+ }
+ }
+}
--- /dev/null
+#ifndef TESTS_VM_QSORT_H
+#define TESTS_VM_QSORT_H 1
+
+#include <stddef.h>
+
+void qsort_bytes (unsigned char *buf, size_t size);
+
+#endif /* tests/vm/qsort.h */