mirror of
git://git.sv.gnu.org/emacs.git
synced 2026-03-15 11:21:19 -07:00
New unit
test system hatched Copied from Perforce Change: 18174 ServerID: perforce.ravenbrook.com
This commit is contained in:
parent
6a09783743
commit
0f52ec25fc
18 changed files with 1896 additions and 0 deletions
87
mps/qa/test/qa
Normal file
87
mps/qa/test/qa
Normal file
|
|
@ -0,0 +1,87 @@
|
|||
#!/usr/local/bin/perl -w
|
||||
#
|
||||
# First, we'll set up @INC so we can find the other
|
||||
# perl scripts we require. This program will be in the
|
||||
# test directory, and all the other scripts will be
|
||||
# in the script subdirectory.
|
||||
#
|
||||
# $0 contains the name of this program, as it was run
|
||||
# this means it may not be the full path, but should be
|
||||
# enough of the path for us to use it to locate other
|
||||
# perl scripts.
|
||||
# We assume the separator is / and the program name is
|
||||
# made only of A-Za-z0-9 and _.
|
||||
|
||||
$test_dir = $0;
|
||||
|
||||
$0 = "qa testing"; # this will show up in ps
|
||||
|
||||
$test_dir =~ s/\/\w+$//;
|
||||
|
||||
$script_dir = $test_dir."/script";
|
||||
$testlib_dir = $test_dir."/testlib"; &use($testlib_dir);
|
||||
|
||||
$platform_class = `class`;
|
||||
chop($platform_class);
|
||||
$obj_dir = $test_dir."/obj/".$platform_class;
|
||||
unless (-e $obj_dir) {
|
||||
mkdir($obj_dir,0777)
|
||||
|| die "Couldn't find or create obj dir: $obj_dir.";
|
||||
}
|
||||
$report_dir = $test_dir."/report"; &use($report_dir);
|
||||
|
||||
foreach (@INC) {
|
||||
if ($_ ne '.') { push(@newINC, $_) }
|
||||
}
|
||||
push(@newINC, $script_dir);
|
||||
@INC = @newINC;
|
||||
|
||||
# Now we must process the command-line arguments
|
||||
# to qa. These specify:
|
||||
# 0: which qa command to run
|
||||
# 1,2,3.. it's parameters
|
||||
# except that -V, -I, -L (or lower-case) can be used to
|
||||
# define $MMQA_VERSION, $MPS_LINK_OBJ and $MPS_INCLUDE_DIR
|
||||
# which otherwise will be taken from environment variables.
|
||||
#
|
||||
# we'll set the relevant vars and pass the rest on in @qa_args
|
||||
|
||||
@qa_args = ();
|
||||
|
||||
%qa_options = ();
|
||||
$qa_options{"v"} = "MMQA_VERSION";
|
||||
$MMQA_VERSION = "MMQA_VERS_MO"; &use($MMQA_VERSION);
|
||||
$qa_options{"i"} = "MPS_INCLUDE_DIR";
|
||||
$qa_options{"l"} = "MPS_LINK_OBJ";
|
||||
|
||||
while ($_ = shift(@ARGV)) {
|
||||
if (/^\-(.*)$/i) {
|
||||
unless ($qa_options{$1}) {
|
||||
die "Unrecognised option: $1.\n";
|
||||
}
|
||||
$val = shift(@ARGV); &use($val);
|
||||
$tetete="$".$qa_options{$1}." = \$val";
|
||||
eval $tetete;
|
||||
} else {
|
||||
push(@qa_args, $_);
|
||||
}
|
||||
}
|
||||
|
||||
$qa_command = shift(@qa_args);
|
||||
|
||||
foreach (keys %qa_options) {
|
||||
unless (eval("$".$qa_options{$_})) {
|
||||
die "Error: you must specify $qa_options{$_} (use the -$_ option).\n";
|
||||
}
|
||||
}
|
||||
|
||||
unless (-e "$script_dir/commands/".$qa_command) {
|
||||
die "MM QA: unknown command '".$qa_command."'";
|
||||
}
|
||||
do "commands/".$qa_command;
|
||||
if ($@) {print $@};
|
||||
|
||||
#################
|
||||
|
||||
sub use {}
|
||||
|
||||
22
mps/qa/test/script/commands/clib
Normal file
22
mps/qa/test/script/commands/clib
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
#!/usr/local/bin/perl -w
|
||||
#
|
||||
# Compile all the .c files in $testlib_dir,
|
||||
# putting the resulting .o files in $obj_dir.
|
||||
#
|
||||
|
||||
print "Compiling test libraries in $testlib_dir.\n";
|
||||
|
||||
require "compile";
|
||||
|
||||
while ($tlfile = <$testlib_dir/*.c>) {
|
||||
$tlobj = $tlfile;
|
||||
$tlobj =~ s/\.c/\.o/;
|
||||
$tlobj =~ s/$testlib_dir/$obj_dir/;
|
||||
print"$tlfile -> $tlobj";
|
||||
|
||||
if (&compile($tlfile, $tlobj)) {
|
||||
print " ok\n";
|
||||
} else {
|
||||
print " failed\n";
|
||||
}
|
||||
}
|
||||
26
mps/qa/test/script/commands/try
Normal file
26
mps/qa/test/script/commands/try
Normal file
|
|
@ -0,0 +1,26 @@
|
|||
#!/usr/local/bin/perl
|
||||
#
|
||||
# run tests interactively (allows for user input, immediate results,
|
||||
# &c, but doesn't give results in standard format)
|
||||
|
||||
require "headread";
|
||||
require "compile";
|
||||
|
||||
foreach $testfile (@qa_args) {
|
||||
|
||||
print "Test file: $testfile.\n";
|
||||
|
||||
&readheader($testfile);
|
||||
unless ($test_header{"language"} eq "c") {
|
||||
die "Don't know how to run test in this language.\n";
|
||||
}
|
||||
|
||||
$linkfiles = " ".$test_header{"link"};
|
||||
$linkfiles =~ s/ +/ $obj_dir\//g;
|
||||
$objfile = "$obj_dir/tmp_test";
|
||||
unless (&compile_and_link($testfile, $objfile, $linkfiles)) {
|
||||
die "Compilation failed on test ".$testfile.".\n";
|
||||
}
|
||||
system("sh -c \"$objfile\"");
|
||||
}
|
||||
|
||||
49
mps/qa/test/script/compile
Normal file
49
mps/qa/test/script/compile
Normal file
|
|
@ -0,0 +1,49 @@
|
|||
#!/usr/local/perl
|
||||
#
|
||||
# provides subroutines to compile or compile and link
|
||||
# tests and test libraries.
|
||||
#
|
||||
# In future, this will probably do some clever stuff
|
||||
# depending on the platform &c, but for now it's just
|
||||
# what works on SunOS.
|
||||
#
|
||||
|
||||
1;
|
||||
|
||||
sub compiler_settings {
|
||||
$cc_command = "gcc";
|
||||
$cc_opts = "-ansi -pedantic -Wall -Wstrict-prototypes \\
|
||||
-Winline -Waggregate-return -Wnested-externs -Wcast-qual \\
|
||||
-Wshadow -Wmissing-prototypes -Wredundant-decls -Wcast-align \\
|
||||
-O -g -ggdb3 -D$MMQA_VERSION";
|
||||
$cc_link = "$obj_dir/platform.o";
|
||||
$cc_include = "-I$testlib_dir -I$MPS_INCLUDE_DIR";
|
||||
}
|
||||
|
||||
sub compile {
|
||||
local($srcfile, $objfile) = @_;
|
||||
|
||||
&compiler_settings;
|
||||
$command = "$cc_command -c $cc_opts -o $objfile $srcfile $cc_include";
|
||||
|
||||
if (system($command)) {
|
||||
return 0;
|
||||
} else {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
sub compile_and_link {
|
||||
local($srcfile, $exefile, $linkfiles) = @_;
|
||||
|
||||
&compiler_settings;
|
||||
$command = "$cc_command $cc_opts -o $exefile $srcfile \\
|
||||
$linkfiles $MPS_LINK_OBJ $cc_link $cc_include";
|
||||
|
||||
if (system($command)) {
|
||||
return 0;
|
||||
} else {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
92
mps/qa/test/script/headread
Normal file
92
mps/qa/test/script/headread
Normal file
|
|
@ -0,0 +1,92 @@
|
|||
#!/usr/local/bin/perl
|
||||
#
|
||||
# subroutines to assist in
|
||||
# 1. reading test headers
|
||||
# 2. reading test output
|
||||
# 3. making pass/fail decision
|
||||
#
|
||||
# [returns 1 to make perl happy]
|
||||
1;
|
||||
|
||||
# Example header:
|
||||
#
|
||||
# ... TEST_HEADER
|
||||
# summary=try lots of allocation to provoke errors
|
||||
# language=c; link=testlib.o
|
||||
# OUTPUT_SPEC
|
||||
# alloc=OK
|
||||
# size1>20
|
||||
# END_HEADER ...
|
||||
#
|
||||
# header information is stored in associative arrays:
|
||||
# %test_header
|
||||
# %spec_output
|
||||
# %spec_rel
|
||||
#
|
||||
# $test_header{key} = value;
|
||||
# $spec_output{key} = value;
|
||||
# $spec_rel{key} = relation;
|
||||
#
|
||||
|
||||
sub readheader {
|
||||
local($infile) = @_;
|
||||
|
||||
unless (open(IN, $infile)) {
|
||||
die "File ".$infile." not found.";
|
||||
}
|
||||
|
||||
$_ = "";
|
||||
while (! /TEST_HEADER/) {
|
||||
($_ = <IN>) || die "Couldn't find start of test header.\n";
|
||||
}
|
||||
s/.*TEST_HEADER//;
|
||||
$line = $_;
|
||||
while (! /END_HEADER/) {
|
||||
($_ = <IN> || die "Couldn't find end of test header.\n");
|
||||
chop;
|
||||
$line = $line."; ".$_;
|
||||
}
|
||||
$line =~ s/END_HEADER.*//;
|
||||
|
||||
if ($line =~ /OUTPUT_SPEC/) {
|
||||
$line =~ /(.*)OUTPUT_SPEC(.*)/;
|
||||
$header = $1;
|
||||
$outspec = $2;
|
||||
} else {
|
||||
print "No output specification -- assuming result=pass required.\n";
|
||||
$header = $line;
|
||||
$outspec = "result=pass";
|
||||
}
|
||||
|
||||
&readvals($header, "=");
|
||||
%test_header = %keyvalues;
|
||||
|
||||
&readvals($outspec, "=|<|>|<=|>=");
|
||||
%spec_output = %keyvalues;
|
||||
%spec_rel = %keyrelations;
|
||||
|
||||
close(IN);
|
||||
}
|
||||
|
||||
sub readvals {
|
||||
local ($_, $relations) = @_;
|
||||
|
||||
%keyvalues = ();
|
||||
%keyrelations = ();
|
||||
|
||||
s/([^\/]);/\1;;/g;
|
||||
|
||||
foreach (split(/\s*;;\s*/)) {
|
||||
s/\\(\\|;)/\1/g;
|
||||
|
||||
if (m/^\W*(\w+)\s*($relations)\s*(.+)\s*/) {
|
||||
$keyvalues{$1} = $3;
|
||||
$keyrelations{$1} = $2;
|
||||
} else {
|
||||
unless (m/^\W*/) {
|
||||
print "Bad header item: ".$_."\n";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
35
mps/qa/test/testlib/arg.h
Normal file
35
mps/qa/test/testlib/arg.h
Normal file
|
|
@ -0,0 +1,35 @@
|
|||
/* arg.h
|
||||
useful things for arg-err tests
|
||||
*/
|
||||
|
||||
#ifndef arg_h
|
||||
#define arg_h
|
||||
|
||||
#include "mps.h"
|
||||
|
||||
#define UNALIGNED ((mps_addr_t) (((char *) NULL) + 1))
|
||||
|
||||
#define MPS_RANK_MIN 0
|
||||
#define MPS_RANK_MAX 3
|
||||
|
||||
#define MPS_RM_MIN 0
|
||||
#define MPS_RM_MAX 3
|
||||
|
||||
#define MPS_ALIGN_MIN 1
|
||||
|
||||
/* possibly nasty values with high bits set */
|
||||
|
||||
#define HIGHBIT_CHAR (~(((unsigned char) -1) >> 1))
|
||||
#define HIGHBIT_INT (~(((unsigned int) -1) >> 1))
|
||||
#define HIGHBIT_SHORT (~(((unsigned short) -1) >> 1))
|
||||
#define HIGHBIT_LONG (~(((unsigned long) -1) >> 1))
|
||||
#define HIGHBIT_SIZE (~((~ (size_t) 0) >> 1))
|
||||
|
||||
/* n.b. the last line above will work in ansi C because
|
||||
size_t is an unsigned type. In sos libs, size_t, however
|
||||
is a signed type; it still works tho', because in sos,
|
||||
>> on a negative value is a logical (not arithmetical) shift.
|
||||
*/
|
||||
|
||||
#endif
|
||||
|
||||
195
mps/qa/test/testlib/lofmt.c
Normal file
195
mps/qa/test/testlib/lofmt.c
Normal file
|
|
@ -0,0 +1,195 @@
|
|||
/* lofmt.c
|
||||
A format for pool class LO
|
||||
*/
|
||||
|
||||
#include "mps.h"
|
||||
#include "testlib.h"
|
||||
#include "lofmt.h"
|
||||
#include <string.h>
|
||||
|
||||
/* some options on the format are controlled by global
|
||||
variables. Of course for efficiency we'd do it in the
|
||||
pre-processor, but that would require recompilation...
|
||||
|
||||
variable default function
|
||||
|
||||
alloclocomments comment on allocation (0)
|
||||
allowlocopies allow objects to be copied (1)
|
||||
*/
|
||||
|
||||
int alloclocomments=0;
|
||||
int allowlocopies=1;
|
||||
|
||||
long int nextid=0x2000000;
|
||||
|
||||
/* a cell can be one of four things, depending on its type:
|
||||
LOpadsingle - a single pad item, MPS_PF_ALIGN in size
|
||||
LOpadmulti - a longer pad item, at least 2*MPS_PF_ALIGN in size
|
||||
LOheart - a broken heart, aka forwarding object
|
||||
LOdata - a real object
|
||||
*/
|
||||
|
||||
static mps_res_t myscan(mps_ss_t ss, mps_addr_t base, mps_addr_t limit);
|
||||
static mps_addr_t myskip(mps_addr_t object);
|
||||
static void myfwd(mps_addr_t object, mps_addr_t to);
|
||||
static mps_addr_t myisfwd(mps_addr_t object);
|
||||
static void mycopy(mps_addr_t object, mps_addr_t to);
|
||||
static void mypad(mps_addr_t base, size_t size);
|
||||
|
||||
struct mps_fmt_A_s fmtLO =
|
||||
{
|
||||
MPS_PF_ALIGN,
|
||||
&myscan,
|
||||
&myskip,
|
||||
&mycopy,
|
||||
&myfwd,
|
||||
&myisfwd,
|
||||
&mypad
|
||||
};
|
||||
|
||||
/* in the following, size is the size_t of the data element
|
||||
of the lodata structure you want the object to have; i.e.
|
||||
the number of bytes of storage you get in the object on top
|
||||
of the headers.
|
||||
*/
|
||||
|
||||
locell *alloclo(mps_ap_t ap, size_t size) {
|
||||
mps_addr_t p;
|
||||
locell *q;
|
||||
size_t bytes;
|
||||
size_t alignment;
|
||||
|
||||
bytes = offsetof(struct lodata, data) + size;
|
||||
|
||||
alignment = MPS_PF_ALIGN; /* needed to make it as wide as size_t */
|
||||
|
||||
/* twiddle the value of size to make it aligned */
|
||||
bytes = (bytes+alignment-1) & ~(alignment-1);
|
||||
|
||||
do {
|
||||
die(mps_reserve(&p, ap, bytes), "Reserve: ");
|
||||
q=p;
|
||||
q->data.tag = LOdata;
|
||||
q->data.id = nextid;
|
||||
q->data.copycount = 0;
|
||||
q->data.size = bytes;
|
||||
}
|
||||
while (!mps_commit(ap, p, bytes));
|
||||
commentif(alloclocomments, "allocated id %li at %p.", nextid, q);
|
||||
nextid += 1;
|
||||
|
||||
return q;
|
||||
}
|
||||
|
||||
static mps_res_t myscan(mps_ss_t ss, mps_addr_t b, mps_addr_t l) {
|
||||
error("Scan method in LO format called!");
|
||||
return MPS_RES_OK;
|
||||
}
|
||||
|
||||
static mps_addr_t myskip(mps_addr_t object) {
|
||||
locell *obj = object;
|
||||
|
||||
switch(obj->tag)
|
||||
{
|
||||
case LOpadsingle:
|
||||
return (mps_addr_t) ((char *) obj + MPS_PF_ALIGN);
|
||||
case LOpadmany:
|
||||
return obj->padmulti.next;
|
||||
case LOheart:
|
||||
return (mps_addr_t) ((char *) obj + (obj->heart.size));
|
||||
case LOdata:
|
||||
return (mps_addr_t) ((char *) obj + (obj->data.size));
|
||||
default:
|
||||
asserts(0, "loskip: bizarre obj tag at %p.", obj);
|
||||
return 0; /* not reached */
|
||||
}
|
||||
}
|
||||
|
||||
static void mycopy(mps_addr_t object, mps_addr_t to)
|
||||
{
|
||||
locell *boj = object;
|
||||
locell *toj = to;
|
||||
|
||||
asserts(allowlocopies, "locopy on LO object");
|
||||
asserts(boj->tag = LOdata, "locopy: non-data object");
|
||||
|
||||
memmove(to, object, boj->data.size);
|
||||
toj->data.copycount = (toj->data.copycount)+1;
|
||||
}
|
||||
|
||||
/* pad stores not its size but a pointer to the next object,
|
||||
because we know we'll never be asked to copy it
|
||||
*/
|
||||
|
||||
static void mypad(mps_addr_t base, size_t size)
|
||||
{
|
||||
locell *obj = base;
|
||||
|
||||
asserts(size >= MPS_PF_ALIGN, "pad: size too small.");
|
||||
|
||||
if (size == MPS_PF_ALIGN)
|
||||
{
|
||||
asserts(sizeof(obj->padsingle) <= MPS_PF_ALIGN, "impossible pad");
|
||||
obj->padsingle.tag = LOpadsingle;
|
||||
}
|
||||
else
|
||||
{
|
||||
asserts(size >= sizeof(struct lopadmulti), "pad: awkward size.");
|
||||
obj->padmulti.tag = LOpadmany;
|
||||
obj->padmulti.next = (mps_addr_t) ((char *) base + size);
|
||||
}
|
||||
}
|
||||
|
||||
static mps_addr_t myisfwd(mps_addr_t object)
|
||||
{
|
||||
locell *obj = object;
|
||||
|
||||
if (obj->tag != LOheart)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
else
|
||||
{
|
||||
return obj->heart.obj;
|
||||
}
|
||||
}
|
||||
|
||||
static void myfwd(mps_addr_t object, mps_addr_t to)
|
||||
{
|
||||
locell *obj = object;
|
||||
size_t size;
|
||||
|
||||
asserts(obj->tag == LOdata || obj->tag == LOheart,
|
||||
"lofwd: unexpected object tag at %p.", obj);
|
||||
|
||||
if (obj->tag == LOdata)
|
||||
{
|
||||
size = obj->data.size;
|
||||
}
|
||||
else /* obj->tag == LOheart */
|
||||
{
|
||||
size = obj->heart.size;
|
||||
}
|
||||
obj->data.tag = LOheart;
|
||||
obj->heart.obj = to;
|
||||
obj->heart.size = size;
|
||||
}
|
||||
|
||||
long int getloid(locell *obj)
|
||||
{
|
||||
asserts(obj->tag = LOdata, "getloid: non-data object.");
|
||||
return obj->data.id;
|
||||
}
|
||||
|
||||
long int getlocopycount(locell *obj)
|
||||
{
|
||||
asserts(obj->tag = LOdata, "getlocopycount: non-data object.");
|
||||
return obj->data.copycount;
|
||||
}
|
||||
|
||||
long int getlosize(locell *obj)
|
||||
{
|
||||
asserts(obj->tag = LOdata, "getlosize: non-data object.");
|
||||
return obj->data.size - offsetof(struct lodata, data);
|
||||
}
|
||||
|
||||
61
mps/qa/test/testlib/lofmt.h
Normal file
61
mps/qa/test/testlib/lofmt.h
Normal file
|
|
@ -0,0 +1,61 @@
|
|||
/* lofmt.h
|
||||
A format for the LO pool class. We have to allow for copying
|
||||
and forwarding, but scan should never be called.
|
||||
*/
|
||||
|
||||
#ifndef lofmt_h
|
||||
#define lofmt_h
|
||||
|
||||
#include "mps.h"
|
||||
|
||||
extern int alloclocomments;
|
||||
extern int allowlocopies;
|
||||
|
||||
/* the object format is visible so tests that want to
|
||||
can hack around with it
|
||||
*/
|
||||
|
||||
#define MAXSIZE 10000
|
||||
|
||||
enum {LOpadsingle=(int) 0xBAD51497, LOpadmany=(int) 0xBAD3A41,
|
||||
LOheart=(int) 0x8EA62, LOdata=(int) 0x7EAFDA2A};
|
||||
|
||||
typedef union locell locell;
|
||||
|
||||
typedef int tag;
|
||||
|
||||
struct lopadsingle {tag tag;};
|
||||
|
||||
struct lopadmulti {tag tag; mps_addr_t next;};
|
||||
|
||||
struct loheart {tag tag; size_t size; mps_addr_t obj;};
|
||||
|
||||
struct lodata
|
||||
{
|
||||
tag tag;
|
||||
size_t size;
|
||||
long int id;
|
||||
long int copycount;
|
||||
size_t len;
|
||||
char data[MAXSIZE];
|
||||
};
|
||||
|
||||
union locell
|
||||
{
|
||||
tag tag;
|
||||
struct lopadsingle padsingle;
|
||||
struct lopadmulti padmulti;
|
||||
struct loheart heart;
|
||||
struct lodata data;
|
||||
};
|
||||
|
||||
extern struct mps_fmt_A_s fmtLO;
|
||||
|
||||
locell *alloclo(mps_ap_t ap, size_t bytes);
|
||||
|
||||
long int getloid(locell *obj);
|
||||
long int getlocopycount(locell *obj);
|
||||
long int getlosize(locell *obj);
|
||||
|
||||
#endif
|
||||
|
||||
220
mps/qa/test/testlib/myfmt.c
Normal file
220
mps/qa/test/testlib/myfmt.c
Normal file
|
|
@ -0,0 +1,220 @@
|
|||
/* myfmt.c
|
||||
a format for scannable objects
|
||||
*/
|
||||
|
||||
#include "mps.h"
|
||||
#include "testlib.h"
|
||||
#include "myfmt.h"
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
|
||||
enum {MCpadsingle, MCpadmany, MCheart, MCdata};
|
||||
|
||||
/* some options on the format are controlled by global
|
||||
variables. Of course for efficiency we'd do it in the
|
||||
pre-processor, but that would require recompilation...
|
||||
|
||||
variable default function
|
||||
|
||||
formatcomments 1 print comments on scanning, fixing, copying
|
||||
copysurplus 1 copy the surplus space in objects when moving
|
||||
|
||||
*/
|
||||
|
||||
int formatcomments=1;
|
||||
int copysurplus=1;
|
||||
|
||||
/* we don't have a separate type for leaf nodes;
|
||||
instead the scanning function doesn't fix null refs
|
||||
|
||||
the words after ref[1] are copied by mycopy,
|
||||
(so you can use them to store data) as long as copysurplus=1
|
||||
*/
|
||||
|
||||
static mps_res_t myscan(mps_ss_t ss, mps_addr_t base, mps_addr_t limit);
|
||||
static mps_addr_t myskip(mps_addr_t object);
|
||||
static void myfwd(mps_addr_t object, mps_addr_t to);
|
||||
static mps_addr_t myisfwd(mps_addr_t object);
|
||||
static void mycopy(mps_addr_t object, mps_addr_t to);
|
||||
static void mypad(mps_addr_t base, size_t size);
|
||||
|
||||
struct mps_fmt_A_s fmtA =
|
||||
{
|
||||
MPS_PF_ALIGN,
|
||||
&myscan,
|
||||
&myskip,
|
||||
&mycopy,
|
||||
&myfwd,
|
||||
&myisfwd,
|
||||
&mypad
|
||||
};
|
||||
|
||||
mycell *allocone(mps_ap_t ap, mps_word_t data,
|
||||
mycell *ref0, mycell *ref1, size_t size)
|
||||
{
|
||||
mps_addr_t p;
|
||||
mycell *q;
|
||||
size_t align;
|
||||
|
||||
align = MPS_PF_ALIGN; /* makes it long enough for ~ to work */
|
||||
|
||||
if (size < sizeof(mycell))
|
||||
{
|
||||
error("Tried to allocate too small an object.");
|
||||
}
|
||||
|
||||
/* twiddle the value of size to make it aligned */
|
||||
size = (size+align-1) & ~(align-1);
|
||||
|
||||
do
|
||||
{
|
||||
die(mps_reserve(&p, ap, size), "Reserve: ");
|
||||
q=p;
|
||||
q->tag = MCdata;
|
||||
q->data = data;
|
||||
q->size = size;
|
||||
q->ref[0] = ref0;
|
||||
q->ref[1] = ref1;
|
||||
}
|
||||
while (!mps_commit(ap, p, size));
|
||||
return q;
|
||||
}
|
||||
|
||||
mps_res_t myscan(mps_ss_t ss, mps_addr_t base, mps_addr_t limit)
|
||||
{
|
||||
MPS_SCAN_BEGIN(ss)
|
||||
{
|
||||
while (base < limit)
|
||||
{
|
||||
mycell *obj = base;
|
||||
mps_res_t res;
|
||||
|
||||
if (formatcomments) printf("Scan %p.\n", (void *)base);
|
||||
switch (obj->tag)
|
||||
{
|
||||
case MCpadsingle:
|
||||
base = (mps_addr_t) ((mps_word_t) obj + MPS_PF_ALIGN);
|
||||
break;
|
||||
case MCpadmany:
|
||||
base = (mps_addr_t) (obj->data);
|
||||
break;
|
||||
case MCdata:
|
||||
/* actual scanning is done in here */
|
||||
|
||||
if (obj->ref[0] != NULL)
|
||||
{
|
||||
if (formatcomments) printf("Fix: %x.\n", (int) &(obj->ref[0]));
|
||||
res = MPS_FIX(ss, (mps_addr_t *) &(obj->ref[0])); /* pun! */
|
||||
if (res != MPS_RES_OK)
|
||||
{
|
||||
return res;
|
||||
}
|
||||
}
|
||||
if (obj->ref[1] != NULL)
|
||||
{
|
||||
if (formatcomments) printf("Fix: %x.\n", (int) &(obj->ref[1]));
|
||||
res = MPS_FIX(ss, (mps_addr_t *) &(obj->ref[1])); /* pun! */
|
||||
if (res != MPS_RES_OK)
|
||||
{
|
||||
return res;
|
||||
}
|
||||
}
|
||||
/* \/ fall through \/ */
|
||||
|
||||
case MCheart:
|
||||
base = (mps_addr_t) ((char *) obj + (obj->size));
|
||||
}
|
||||
}
|
||||
asserts(base == limit, "base <> limit in scan!");
|
||||
}
|
||||
MPS_SCAN_END(ss);
|
||||
return MPS_RES_OK;
|
||||
}
|
||||
|
||||
mps_addr_t myskip(mps_addr_t object)
|
||||
{
|
||||
mycell *obj = object;
|
||||
|
||||
switch(obj->tag)
|
||||
{
|
||||
case MCpadsingle:
|
||||
return (mps_addr_t) ((mps_word_t) obj+MPS_PF_ALIGN);
|
||||
case MCpadmany:
|
||||
return (mps_addr_t) (obj->data);
|
||||
case MCheart: case MCdata:
|
||||
return (mps_addr_t) ((char *) obj + (obj->size));
|
||||
default:
|
||||
asserts(0, "skip: bizarre obj tag at %p.", obj);
|
||||
return 0; /* just to satisfy the compiler! */
|
||||
}
|
||||
}
|
||||
|
||||
void mycopy(mps_addr_t object, mps_addr_t to)
|
||||
{
|
||||
mycell *boj = object;
|
||||
/* mycell *toj = to;
|
||||
*/
|
||||
|
||||
if (formatcomments) printf("copy! %x -> %x\n", (int) object, (int) to);
|
||||
|
||||
/* this line is bad, because the objects might overlap,
|
||||
and then C doesn't guarantee to do the right thing!
|
||||
|
||||
*toj = *boj;
|
||||
*/
|
||||
|
||||
asserts(boj->tag == MCdata, "Bad object tag in copy");
|
||||
|
||||
if (copysurplus)
|
||||
{
|
||||
memmove(to, object, boj->size);
|
||||
}
|
||||
else
|
||||
{
|
||||
memmove(to, object, sizeof(mycell));
|
||||
}
|
||||
|
||||
/* it's guaranteed that we won't have to copy a pad, so we
|
||||
don't have to worry about fiddling the pointer
|
||||
*/
|
||||
|
||||
}
|
||||
|
||||
void mypad(mps_addr_t base, size_t size)
|
||||
{
|
||||
mycell *obj = base;
|
||||
|
||||
asserts(size >= MPS_PF_ALIGN, "size too small for pad");
|
||||
if (size == MPS_PF_ALIGN)
|
||||
{
|
||||
obj->tag = MCpadsingle;
|
||||
}
|
||||
else
|
||||
{
|
||||
obj->tag = MCpadmany;
|
||||
obj->data = ((mps_word_t) base) + size;
|
||||
}
|
||||
}
|
||||
|
||||
mps_addr_t myisfwd(mps_addr_t object)
|
||||
{
|
||||
mycell *obj = object;
|
||||
|
||||
if (obj->tag != MCheart)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
else
|
||||
{
|
||||
return (mps_addr_t) obj->data;
|
||||
}
|
||||
}
|
||||
|
||||
void myfwd(mps_addr_t object, mps_addr_t to)
|
||||
{
|
||||
mycell *obj = object;
|
||||
|
||||
obj->tag = MCheart;
|
||||
obj->data = (mps_word_t) to;
|
||||
}
|
||||
|
||||
46
mps/qa/test/testlib/myfmt.h
Normal file
46
mps/qa/test/testlib/myfmt.h
Normal file
|
|
@ -0,0 +1,46 @@
|
|||
/* myfmt.h
|
||||
a format for scannable objects
|
||||
*/
|
||||
|
||||
#ifndef myfmt_h
|
||||
#define myfmt_h
|
||||
|
||||
#include "mps.h"
|
||||
#include "testlib.h"
|
||||
|
||||
/* some options on the format are controlled by global
|
||||
variables. Of course for efficiency we'd do it in the
|
||||
pre-processor, but that would require recompilation...
|
||||
|
||||
variable default function
|
||||
|
||||
formatcomments 1 print comments on scanning, fixing, copying
|
||||
copysurplus 1 copy the surplus space in objects when moving
|
||||
|
||||
*/
|
||||
|
||||
extern int formatcomments;
|
||||
extern int copysurplus;
|
||||
|
||||
typedef struct mycell
|
||||
{
|
||||
mps_word_t tag;
|
||||
mps_word_t data;
|
||||
mps_word_t size;
|
||||
struct mycell *ref[2];
|
||||
} mycell;
|
||||
|
||||
/* we don't have a separate type for leaf nodes;
|
||||
instead the scanning function doesn't fix null refs
|
||||
|
||||
the words after ref[1] are copied by mycopy,
|
||||
(so you can use them to store data) as long as copysurplus=1
|
||||
*/
|
||||
|
||||
extern struct mps_fmt_A_s fmtA;
|
||||
|
||||
mycell *allocone(mps_ap_t ap, mps_word_t data,
|
||||
mycell *ref0, mycell *ref1, size_t size);
|
||||
|
||||
#endif
|
||||
|
||||
526
mps/qa/test/testlib/newfmt.c
Normal file
526
mps/qa/test/testlib/newfmt.c
Normal file
|
|
@ -0,0 +1,526 @@
|
|||
/* newfmt.c
|
||||
My attempt to write a format using unions &c to avoid
|
||||
nasty casting all over the place
|
||||
*/
|
||||
|
||||
#include "mps.h"
|
||||
#include "testlib.h"
|
||||
#include "newfmt.h"
|
||||
#include <string.h>
|
||||
|
||||
/* some options on the format are controlled by global
|
||||
variables. Of course for efficiency we'd do it in the
|
||||
pre-processor, but that would require recompilation...
|
||||
|
||||
variable default function
|
||||
|
||||
formatcomments 1 print comments on scanning, fixing, copying
|
||||
checkcomments 1 print comments on checking
|
||||
|
||||
*/
|
||||
|
||||
int formatcomments=0;
|
||||
int checkcomments=0;
|
||||
int countcomments=1;
|
||||
int alloccomments=0;
|
||||
int fixcomments=0;
|
||||
|
||||
long int nextid=0x1000000;
|
||||
|
||||
long int checkobjcount=0;
|
||||
|
||||
enum
|
||||
{
|
||||
SCANCALL_COUNT,
|
||||
SCANOBJ_COUNT, /* = #objects scanned (real ones, that is) */
|
||||
SCANPS_COUNT, /* = #pad singles scanned */
|
||||
SCANPM_COUNT, /* etc ... */
|
||||
SCANHEART_COUNT,
|
||||
COPY_COUNT,
|
||||
SKIP_COUNT,
|
||||
FWD_COUNT,
|
||||
ISFWD_COUNT,
|
||||
RESERVE_COUNT,
|
||||
ALLOC_COUNT,
|
||||
PAD_SINGLE_COUNT,
|
||||
PAD_MULTI_COUNT
|
||||
};
|
||||
|
||||
int counters[PAD_MULTI_COUNT+1];
|
||||
int prevcounters[PAD_MULTI_COUNT+1];
|
||||
int maxcounters[PAD_MULTI_COUNT+1] = {0};
|
||||
|
||||
static long int maxcopy = 0;
|
||||
static int freeze=0;
|
||||
|
||||
#define INCCOUNT(c) do {if(!freeze) counters[c]+=1;} while (0)
|
||||
|
||||
/* a cell can be one of four things, depending on its type:
|
||||
MCpadsingle - a single pad item, MPS_PF_ALIGN in size
|
||||
MCpadmulti - a longer pad item, at least 2*MPS_PF_ALIGN in size
|
||||
MCheart - a broken heart, aka forwarding object
|
||||
MCdata - a real object
|
||||
*/
|
||||
|
||||
/* the scanning function doesn't try to fix null refs
|
||||
|
||||
size is the total size of the object in bytes
|
||||
every other word from ref onwards, including the word
|
||||
at ref, is a reference; after each one comes the id
|
||||
of the object referenced, so we can check the graph is
|
||||
correct later!
|
||||
|
||||
nb: This way of doing things is not really guaranteed to work
|
||||
on all systems. It would be more correct to use an array
|
||||
of structures, or an array of unions.
|
||||
*/
|
||||
|
||||
static mps_res_t myscan(mps_ss_t ss, mps_addr_t base, mps_addr_t limit);
|
||||
static mps_addr_t myskip(mps_addr_t object);
|
||||
static void myfwd(mps_addr_t object, mps_addr_t to);
|
||||
static mps_addr_t myisfwd(mps_addr_t object);
|
||||
static void mycopy(mps_addr_t object, mps_addr_t to);
|
||||
static void mypad(mps_addr_t base, size_t size);
|
||||
|
||||
struct mps_fmt_A_s fmtA =
|
||||
{
|
||||
MPS_PF_ALIGN,
|
||||
&myscan,
|
||||
&myskip,
|
||||
&mycopy,
|
||||
&myfwd,
|
||||
&myisfwd,
|
||||
&mypad
|
||||
};
|
||||
|
||||
/* in the following, size is the number of refs you want
|
||||
the allocated object to have
|
||||
*/
|
||||
|
||||
mycell *allocdumb(mps_ap_t ap, size_t size)
|
||||
{
|
||||
mps_addr_t p;
|
||||
mycell *q;
|
||||
size_t bytes;
|
||||
size_t alignment;
|
||||
|
||||
bytes = offsetof(struct data, ref) + size;
|
||||
|
||||
alignment = MPS_PF_ALIGN; /* needed to make it as wide as size_t */
|
||||
|
||||
/* twiddle the value of size to make it aligned */
|
||||
bytes = (bytes+alignment-1) & ~(alignment-1);
|
||||
|
||||
do
|
||||
{
|
||||
die(mps_reserve(&p, ap, bytes), "Reserve: ");
|
||||
INCCOUNT(RESERVE_COUNT);
|
||||
q=p;
|
||||
q->data.tag = MCdata;
|
||||
q->data.id = nextid;
|
||||
q->data.copycount = 0;
|
||||
q->data.numrefs = 0;
|
||||
q->data.checkedflag = 0;
|
||||
q->data.size = bytes;
|
||||
}
|
||||
while (!mps_commit(ap, p, bytes));
|
||||
INCCOUNT(ALLOC_COUNT);
|
||||
commentif(alloccomments, "allocated id %li at %p.", nextid, q);
|
||||
nextid += 1;
|
||||
|
||||
return q;
|
||||
}
|
||||
|
||||
mycell *allocone(mps_ap_t ap, int size)
|
||||
{
|
||||
mps_addr_t p;
|
||||
mycell *q;
|
||||
int i;
|
||||
size_t bytes;
|
||||
size_t alignment;
|
||||
|
||||
bytes = offsetof(struct data, ref) + size*sizeof(struct refitem);
|
||||
|
||||
alignment = MPS_PF_ALIGN; /* needed to make it as wide as size_t */
|
||||
|
||||
/* twiddle the value of size to make it aligned */
|
||||
bytes = (bytes+alignment-1) & ~(alignment-1);
|
||||
|
||||
do
|
||||
{
|
||||
die(mps_reserve(&p, ap, bytes), "Reserve: ");
|
||||
INCCOUNT(RESERVE_COUNT);
|
||||
q=p;
|
||||
q->data.tag = MCdata;
|
||||
q->data.id = nextid;
|
||||
q->data.copycount = 0;
|
||||
q->data.numrefs = size;
|
||||
q->data.checkedflag = 0;
|
||||
q->data.size = bytes;
|
||||
|
||||
for(i=0; i<size; i+=1)
|
||||
{
|
||||
q->data.ref[i].addr = NULL;
|
||||
q->data.ref[i].id = 0;
|
||||
}
|
||||
}
|
||||
while (!mps_commit(ap, p, bytes));
|
||||
INCCOUNT(ALLOC_COUNT);
|
||||
commentif(alloccomments, "allocated id %li at %p.", nextid, q);
|
||||
nextid += 1;
|
||||
|
||||
return q;
|
||||
}
|
||||
|
||||
static mps_res_t myscan(mps_ss_t ss, mps_addr_t base, mps_addr_t limit)
|
||||
{
|
||||
int i;
|
||||
|
||||
INCCOUNT(SCANCALL_COUNT);
|
||||
MPS_SCAN_BEGIN(ss)
|
||||
{
|
||||
while (base < limit)
|
||||
{
|
||||
mycell *obj = base;
|
||||
mps_res_t res;
|
||||
mps_addr_t p;
|
||||
|
||||
switch (obj->tag)
|
||||
{
|
||||
case MCpadsingle:
|
||||
INCCOUNT(SCANPS_COUNT);
|
||||
base = (mps_addr_t) ((char *) obj + MPS_PF_ALIGN);
|
||||
break;
|
||||
case MCpadmany:
|
||||
INCCOUNT(SCANPM_COUNT);
|
||||
base = (obj->padmulti.next);
|
||||
break;
|
||||
case MCdata:
|
||||
INCCOUNT(SCANOBJ_COUNT);
|
||||
/* actual scanning is done in here */
|
||||
|
||||
asserts(obj->data.id != MCerrorid, "scan on error object");
|
||||
commentif(formatcomments, "scan %li at %p.", obj->data.id, base);
|
||||
for (i=0; i<(obj->data.numrefs); i++)
|
||||
{
|
||||
p = obj->data.ref[i].addr;
|
||||
if (p != NULL)
|
||||
{
|
||||
/* copy ref to p for fixing, to avoid a pun (although
|
||||
the pun would probably work fine almost everywhere)
|
||||
*/
|
||||
commentif(fixcomments, "fix %li[%i] -> %li",
|
||||
obj->data.id, i, obj->data.ref[i].id);
|
||||
res = MPS_FIX(ss, (mps_addr_t *) &p);
|
||||
if (res != MPS_RES_OK) return res;
|
||||
obj->data.ref[i].addr = p;
|
||||
}
|
||||
}
|
||||
base = (mps_addr_t) ((char *) obj + (obj->data.size));
|
||||
break;
|
||||
case MCheart:
|
||||
INCCOUNT(SCANHEART_COUNT);
|
||||
base = (mps_addr_t) ((char *) obj + (obj->heart.size));
|
||||
break;
|
||||
default:
|
||||
asserts(0, "scan: bizarre obj tag at %p.", obj);
|
||||
}
|
||||
}
|
||||
}
|
||||
MPS_SCAN_END(ss);
|
||||
return MPS_RES_OK;
|
||||
}
|
||||
|
||||
static mps_addr_t myskip(mps_addr_t object)
|
||||
{
|
||||
mycell *obj = object;
|
||||
|
||||
INCCOUNT(SKIP_COUNT);
|
||||
switch(obj->tag)
|
||||
{
|
||||
case MCpadsingle:
|
||||
return (mps_addr_t) ((char *) obj + MPS_PF_ALIGN);
|
||||
case MCpadmany:
|
||||
return obj->padmulti.next;
|
||||
case MCheart:
|
||||
return (mps_addr_t) ((char *) obj + (obj->heart.size));
|
||||
case MCdata:
|
||||
return (mps_addr_t) ((char *) obj + (obj->data.size));
|
||||
default:
|
||||
asserts(0, "skip: bizarre obj tag at %p.", obj);
|
||||
return 0; /* not reached */
|
||||
}
|
||||
}
|
||||
|
||||
static void mycopy(mps_addr_t object, mps_addr_t to)
|
||||
{
|
||||
mycell *boj = object;
|
||||
mycell *toj = to;
|
||||
|
||||
asserts(boj->tag = MCdata, "copy: non-data object");
|
||||
|
||||
INCCOUNT(COPY_COUNT);
|
||||
commentif(formatcomments, "copy: %li: %p -> %p\n",
|
||||
boj->data.id, object, to);
|
||||
|
||||
/* this line would be bad, because the objects might overlap,
|
||||
and then C doesn't guarantee to do the right thing!
|
||||
|
||||
*toj = *boj;
|
||||
*/
|
||||
|
||||
memmove(to, object, boj->data.size);
|
||||
if (!freeze)
|
||||
{
|
||||
toj->data.copycount = (toj->data.copycount)+1;
|
||||
if (toj->data.copycount > maxcopy) maxcopy = toj->data.copycount;
|
||||
}
|
||||
}
|
||||
|
||||
/* pad stores not its size but a pointer to the next object,
|
||||
because we know we'll never be asked to copy it
|
||||
*/
|
||||
|
||||
static void mypad(mps_addr_t base, size_t size)
|
||||
{
|
||||
mycell *obj = base;
|
||||
|
||||
asserts(size >= MPS_PF_ALIGN, "pad: size too small.");
|
||||
|
||||
if (size == MPS_PF_ALIGN)
|
||||
{
|
||||
INCCOUNT(PAD_SINGLE_COUNT);
|
||||
asserts(sizeof(obj->padsingle) <= MPS_PF_ALIGN, "impossible pad");
|
||||
obj->padsingle.tag = MCpadsingle;
|
||||
}
|
||||
else
|
||||
{
|
||||
asserts(size >= sizeof(struct padmulti), "pad: awkward size.");
|
||||
INCCOUNT(PAD_MULTI_COUNT);
|
||||
obj->padmulti.tag = MCpadmany;
|
||||
obj->padmulti.next = (mps_addr_t) ((char *) base + size);
|
||||
}
|
||||
}
|
||||
|
||||
static mps_addr_t myisfwd(mps_addr_t object)
|
||||
{
|
||||
mycell *obj = object;
|
||||
|
||||
INCCOUNT(ISFWD_COUNT);
|
||||
if (obj->tag != MCheart)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
else
|
||||
{
|
||||
return obj->heart.obj;
|
||||
}
|
||||
}
|
||||
|
||||
static void myfwd(mps_addr_t object, mps_addr_t to)
|
||||
{
|
||||
mycell *obj = object;
|
||||
size_t size;
|
||||
|
||||
asserts(obj->tag == MCdata || obj->tag == MCheart,
|
||||
"fwd: unexpected object tag at %p.", obj);
|
||||
INCCOUNT(FWD_COUNT);
|
||||
|
||||
if (obj->tag == MCdata)
|
||||
{
|
||||
size = obj->data.size;
|
||||
}
|
||||
else /* obj->tag == MCheart */
|
||||
{
|
||||
size = obj->heart.size;
|
||||
}
|
||||
obj->data.tag = MCheart;
|
||||
obj->heart.obj = to;
|
||||
obj->heart.size = size;
|
||||
}
|
||||
|
||||
|
||||
/* ---------------------------------------------------------------
|
||||
Access methods for mycell objects
|
||||
*/
|
||||
|
||||
/* set the nth reference of obj to to (n from 0 to size-1) */
|
||||
|
||||
void setref(mycell *obj, int n, mycell *to)
|
||||
{
|
||||
asserts(obj->tag = MCdata, "setref: from non-data object.");
|
||||
asserts((to==NULL)||(to->tag = MCdata), "setref: to non-data object.");
|
||||
asserts(obj->data.numrefs > n, "setref: access beyond object size.");
|
||||
|
||||
obj->data.ref[n].addr = to;
|
||||
obj->data.ref[n].id = (to==NULL ? 0 : to->data.id);
|
||||
}
|
||||
|
||||
mycell *getref(mycell *obj, int n)
|
||||
{
|
||||
asserts(obj->tag = MCdata, "getref: from non-data object.");
|
||||
asserts(obj->data.numrefs > n, "getref: access beyond object size.");
|
||||
return obj->data.ref[n].addr;
|
||||
}
|
||||
|
||||
mps_addr_t getdata(mycell *obj)
|
||||
{
|
||||
return (mps_addr_t) &(obj->data.ref[0]);
|
||||
}
|
||||
|
||||
long int getid(mycell *obj)
|
||||
{
|
||||
asserts(obj->tag = MCdata, "getid: non-data object.");
|
||||
return obj->data.id;
|
||||
}
|
||||
|
||||
long int getcopycount(mycell *obj)
|
||||
{
|
||||
asserts(obj->tag = MCdata, "getcopycount: non-data object.");
|
||||
return obj->data.copycount;
|
||||
}
|
||||
|
||||
long int getsize(mycell *obj)
|
||||
{
|
||||
asserts(obj->tag = MCdata, "getsize: non-data object.");
|
||||
return obj->data.numrefs;
|
||||
}
|
||||
|
||||
/* ---------------------------------------------------------------
|
||||
Now the useful things specially for checking the graph
|
||||
*/
|
||||
|
||||
/* recursively check the graph, starting at an object.
|
||||
We do the check twice, so as to restore the
|
||||
checkflags to zero.
|
||||
*/
|
||||
|
||||
static void checkloop(mycell *obj, mps_word_t dir)
|
||||
{
|
||||
mycell *toj;
|
||||
int tid;
|
||||
int i;
|
||||
|
||||
asserts(obj->tag = MCdata,
|
||||
"checkfrom: non data object in graph at %p.", obj);
|
||||
|
||||
if (obj->data.checkedflag != dir)
|
||||
{
|
||||
commentif(checkcomments, "checking %p = %li", obj, obj->data.id);
|
||||
|
||||
checkobjcount += 1;
|
||||
|
||||
obj->data.checkedflag = dir;
|
||||
|
||||
for (i=0; i<(obj->data.numrefs); i+=1)
|
||||
{
|
||||
if (obj->data.ref[i].addr != NULL)
|
||||
{
|
||||
toj = (obj->data.ref[i].addr);
|
||||
tid = (obj->data.ref[i].id);
|
||||
asserts(toj->data.id = tid,
|
||||
"checkfrom: corrupt graph at %p, %d.", obj, i);
|
||||
checkloop(toj, dir);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void checkfrom(mycell *obj)
|
||||
{
|
||||
int k;
|
||||
|
||||
freeze = 1; /* suspend counting while checking graph */
|
||||
checkobjcount = 0;
|
||||
checkloop(obj, 1);
|
||||
comment("checkfrom: %li live objects checked", checkobjcount);
|
||||
k = checkcomments;
|
||||
checkcomments = 0;
|
||||
checkloop(obj, 0);
|
||||
checkcomments = k;
|
||||
comment("checkfrom: graph ok from ID: %li.", obj->data.id);
|
||||
freeze = 0; /* resume counting */
|
||||
}
|
||||
|
||||
/* ----------------------------------------------------------
|
||||
Now things to reset and display the counters
|
||||
*/
|
||||
|
||||
void resetcounters(void)
|
||||
{
|
||||
int i;
|
||||
for (i=0; i < PAD_MULTI_COUNT+1; i++)
|
||||
{
|
||||
counters[i]=0;
|
||||
prevcounters[i]=0;
|
||||
maxcounters[i]=0;
|
||||
}
|
||||
maxcopy = 0;
|
||||
}
|
||||
|
||||
void updatecounters(void)
|
||||
{
|
||||
int i;
|
||||
for (i=0; i < PAD_MULTI_COUNT+1; i++)
|
||||
{
|
||||
if (counters[i]-prevcounters[i] > maxcounters[i])
|
||||
{
|
||||
maxcounters[i]=counters[i]-prevcounters[i];
|
||||
}
|
||||
prevcounters[i]=counters[i];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void d_c(int i, char *name)
|
||||
{
|
||||
comment("%10d %s", counters[i], name);
|
||||
}
|
||||
|
||||
static void d_mc(int i, char *name)
|
||||
{
|
||||
comment("%10d %s", maxcounters[i], name);
|
||||
}
|
||||
|
||||
void displaycounters(void)
|
||||
{
|
||||
comment("--------");
|
||||
comment("Counters:");
|
||||
d_c(SCANCALL_COUNT, "scan calls");
|
||||
d_c(SCANOBJ_COUNT, "object scans");
|
||||
d_c(SCANHEART_COUNT, "heart scans");
|
||||
d_c(SCANPS_COUNT, "pad scans (single word)");
|
||||
d_c(SCANPM_COUNT, "pad scans (multi word)");
|
||||
d_c(COPY_COUNT, "copys");
|
||||
d_c(SKIP_COUNT, "skips");
|
||||
d_c(FWD_COUNT, "fwds");
|
||||
d_c(ISFWD_COUNT, "isfwds");
|
||||
d_c(PAD_SINGLE_COUNT, "pads (single word)");
|
||||
d_c(PAD_MULTI_COUNT, "pads (multi word)");
|
||||
d_c(RESERVE_COUNT, "reserve calls");
|
||||
d_c(ALLOC_COUNT, "allocations");
|
||||
comment("--------");
|
||||
}
|
||||
|
||||
void displaymaxcounters(void)
|
||||
{
|
||||
comment("--------");
|
||||
comment("Maximum counter values:");
|
||||
d_mc(SCANCALL_COUNT, "scan calls");
|
||||
d_mc(SCANOBJ_COUNT, "object scans");
|
||||
d_mc(SCANHEART_COUNT, "heart scans");
|
||||
d_mc(SCANPS_COUNT, "pad scans (single word)");
|
||||
d_mc(SCANPM_COUNT, "pad scans (multi word)");
|
||||
d_mc(COPY_COUNT, "copys");
|
||||
d_mc(SKIP_COUNT, "skips");
|
||||
d_mc(FWD_COUNT, "fwds");
|
||||
d_mc(ISFWD_COUNT, "isfwds");
|
||||
d_mc(RESERVE_COUNT, "reserve calls");
|
||||
d_mc(ALLOC_COUNT, "allocations");
|
||||
d_mc(PAD_SINGLE_COUNT, "pads (single word)");
|
||||
d_mc(PAD_MULTI_COUNT, "pads (multi word)");
|
||||
comment("--------");
|
||||
comment("max copies of a single object: %li.", maxcopy);
|
||||
comment("--------");
|
||||
}
|
||||
|
||||
87
mps/qa/test/testlib/newfmt.h
Normal file
87
mps/qa/test/testlib/newfmt.h
Normal file
|
|
@ -0,0 +1,87 @@
|
|||
/* newfmt.h
|
||||
A nice format with graph checking and statistics
|
||||
*/
|
||||
|
||||
#ifndef newfmt_h
|
||||
#define newfmt_h
|
||||
|
||||
#include "mps.h"
|
||||
|
||||
extern int formatcomments;
|
||||
extern int checkcomments;
|
||||
extern int countcomments;
|
||||
extern int alloccomments;
|
||||
extern int fixcomments;
|
||||
|
||||
/* the object format is visible so tests that want to
|
||||
can hack around with it
|
||||
*/
|
||||
|
||||
#define MAXSIZE 10000
|
||||
|
||||
enum {MCpadsingle=(int) 0xBAD51497, MCpadmany=(int) 0xBAD3A41,
|
||||
MCheart=(int) 0x8EA62, MCdata=(int) 0xDA2A};
|
||||
|
||||
enum {MCerrorid=(int) 0xE660E};
|
||||
|
||||
/* n.b. MCerrorid < 0x1000000 so it won't clash with id of
|
||||
any ordinary object
|
||||
*/
|
||||
|
||||
typedef union mycell mycell;
|
||||
|
||||
typedef int tag;
|
||||
|
||||
struct padsingle {tag tag;};
|
||||
|
||||
struct padmulti {tag tag; mps_addr_t next;};
|
||||
|
||||
struct heart {tag tag; size_t size; mps_addr_t obj;};
|
||||
|
||||
struct data
|
||||
{
|
||||
tag tag;
|
||||
size_t size;
|
||||
long int id;
|
||||
long int copycount;
|
||||
long int numrefs;
|
||||
int checkedflag;
|
||||
struct refitem {mycell *addr; long int id;} ref[MAXSIZE];
|
||||
};
|
||||
|
||||
union mycell
|
||||
{
|
||||
tag tag;
|
||||
struct padsingle padsingle;
|
||||
struct padmulti padmulti;
|
||||
struct heart heart;
|
||||
struct data data;
|
||||
};
|
||||
|
||||
extern struct mps_fmt_A_s fmtA;
|
||||
|
||||
mycell *allocone(mps_ap_t ap, int size);
|
||||
mycell *allocdumb(mps_ap_t ap, size_t bytes);
|
||||
|
||||
mps_addr_t getdata(mycell *obj);
|
||||
void setref(mycell *obj, int n, mycell *to);
|
||||
mycell *getref(mycell *obj, int n);
|
||||
|
||||
long int getid(mycell *obj);
|
||||
long int getcopycount(mycell *obj);
|
||||
long int getsize(mycell *obj);
|
||||
|
||||
void checkfrom(mycell *obj);
|
||||
|
||||
#define RC resetcounters()
|
||||
#define UC updatecounters()
|
||||
#define DC displaycounters()
|
||||
#define DMC displaymaxcounters()
|
||||
|
||||
void resetcounters(void);
|
||||
void updatecounters(void);
|
||||
void displaycounters(void);
|
||||
void displaymaxcounters(void);
|
||||
|
||||
#endif
|
||||
|
||||
78
mps/qa/test/testlib/ossu.h
Normal file
78
mps/qa/test/testlib/ossu.h
Normal file
|
|
@ -0,0 +1,78 @@
|
|||
/* ==== SUNOS ANSI COMPATABILITY HEADER ====
|
||||
*
|
||||
* $HopeName: MMsrc!ossu.h(trunk.3) $
|
||||
*
|
||||
* Copyright (C) 1994,1995 Harlequin Group, all rights reserved
|
||||
*
|
||||
* This header defines some things which are part of the ANSI standard but
|
||||
* missing from the C compiler / environment.
|
||||
*
|
||||
* See also syscalls.h, which contains prototypes for system calls
|
||||
* which are not prototyped in include files
|
||||
*
|
||||
* This header was imported from the MLWorks runtime system, when it
|
||||
* had the following id:
|
||||
* src:OS:SunOS:ansi.h,v 1.2 1994/06/09 14:24:35 nickh
|
||||
*/
|
||||
|
||||
#ifndef ansi_h
|
||||
#define ansi_h
|
||||
|
||||
#include <stdarg.h>
|
||||
#include <stdio.h>
|
||||
#include <sys/types.h>
|
||||
#include <time.h>
|
||||
|
||||
/* on the Suns, the include files in /usr/include do not include
|
||||
declarations for a large number of ANSI functions. We remedy that
|
||||
here. */
|
||||
|
||||
/* stdio.h things */
|
||||
|
||||
extern int fclose (FILE *stream);
|
||||
extern int fflush (FILE *stream);
|
||||
extern int fgetc (FILE *stream);
|
||||
extern int ungetc (int c, FILE *stram);
|
||||
extern int fputc (int c, FILE *stream);
|
||||
extern int printf (const char *format, ...);
|
||||
extern int fprintf (FILE *stream, const char *format, ...);
|
||||
extern int vfprintf (FILE *stream, const char *format, va_list arg);
|
||||
extern int vsprintf (char *s, const char *format, va_list arg);
|
||||
extern int fputs (const char *s, FILE *stream);
|
||||
extern int fscanf (FILE *stream, const char *format, ...);
|
||||
extern int sscanf (const char *s, const char *format, ...);
|
||||
extern int fseek (FILE *stream, long int offset, int whence);
|
||||
extern size_t fread (void *ptr, size_t size, size_t nmemb, FILE *stream);
|
||||
extern size_t fwrite (const void *ptr, size_t size, size_t nmemb,
|
||||
FILE *stream);
|
||||
|
||||
/* these functions are used in the macro definitions of putc and getc
|
||||
but not declared in stdio.h */
|
||||
|
||||
extern int _filbuf(FILE *stream);
|
||||
|
||||
#ifdef __GNUC__
|
||||
extern int _flsbuf(unsigned char c, FILE *stream);
|
||||
#else
|
||||
extern int _flsbuf(FILE *stream);
|
||||
#endif
|
||||
|
||||
/* time.h things */
|
||||
|
||||
#ifndef __GNUC__
|
||||
typedef long clock_t;
|
||||
#endif
|
||||
extern size_t strftime (char *s, size_t maxsize, const char *format,
|
||||
const struct tm *timeptr);
|
||||
extern time_t time (time_t *timer);
|
||||
extern clock_t clock(void);
|
||||
|
||||
/* stdlib.h things */
|
||||
|
||||
extern int system(const char *string);
|
||||
|
||||
/* string.h things */
|
||||
|
||||
extern void *memset(void *, int, size_t);
|
||||
|
||||
#endif
|
||||
12
mps/qa/test/testlib/platform.c
Normal file
12
mps/qa/test/testlib/platform.c
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
#include "mps.h"
|
||||
#include "platform.h"
|
||||
|
||||
#ifdef MPS_OS_SU
|
||||
|
||||
void *memmove(void *to, void *from, size_t bytes)
|
||||
{
|
||||
bcopy((char *)from, (char *)to, (int)bytes);
|
||||
return to;
|
||||
}
|
||||
|
||||
#endif
|
||||
30
mps/qa/test/testlib/platform.h
Normal file
30
mps/qa/test/testlib/platform.h
Normal file
|
|
@ -0,0 +1,30 @@
|
|||
/* platform.h
|
||||
load appropriate header files to do platform-specific
|
||||
stuff.
|
||||
*/
|
||||
|
||||
#ifdef MPS_OS_SU
|
||||
|
||||
/* SunOS (4) doesn't have memmove, which would be handy for
|
||||
writing copy functions in formats. So...
|
||||
*/
|
||||
|
||||
/* (I copied ossu.h from the mps header files for
|
||||
thursday afternoon)
|
||||
*/
|
||||
|
||||
#include "ossu.h"
|
||||
|
||||
#define EXIT_FAILURE 1
|
||||
#define EXIT_SUCCESS 0
|
||||
|
||||
#define CLOCKS_PER_SEC 1000000
|
||||
|
||||
int scanf(const char *format, ...);
|
||||
|
||||
void bcopy(char* from, char* to, int bytes);
|
||||
|
||||
void *memmove(void *to, void *from, size_t bytes);
|
||||
|
||||
#endif
|
||||
|
||||
243
mps/qa/test/testlib/testlib.c
Normal file
243
mps/qa/test/testlib/testlib.c
Normal file
|
|
@ -0,0 +1,243 @@
|
|||
/* some useful functions for testing the MPS */
|
||||
|
||||
#include <stdio.h>
|
||||
#include <assert.h>
|
||||
#include <math.h>
|
||||
#include "testlib.h"
|
||||
|
||||
/* err_text
|
||||
mps_res_t -> textual description
|
||||
uses case rather than array lookup 'cos we don't want to
|
||||
rely on the representation, which might change
|
||||
*/
|
||||
|
||||
const char *err_text(mps_res_t err)
|
||||
{
|
||||
switch (err)
|
||||
{
|
||||
case MPS_RES_OK: return "OK";
|
||||
case MPS_RES_FAIL: return "FAIL";
|
||||
case MPS_RES_RESOURCE: return "RESOURCE";
|
||||
case MPS_RES_MEMORY: return "MEMORY";
|
||||
case MPS_RES_LIMIT: return "LIMIT";
|
||||
case MPS_RES_UNIMPL: return "UNIMPL";
|
||||
case MPS_RES_IO: return "IO";
|
||||
}
|
||||
asserts(0, "Unknown result code");
|
||||
return "*** Unknown result code ***";
|
||||
}
|
||||
|
||||
/* pass
|
||||
report result=pass and exit(0)
|
||||
*/
|
||||
|
||||
void pass(void)
|
||||
{
|
||||
report("result", "pass");
|
||||
exit(0);
|
||||
}
|
||||
|
||||
/* fail
|
||||
report result=fail and exit(1)
|
||||
*/
|
||||
|
||||
void fail(void)
|
||||
{
|
||||
report("result", "fail");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
/* report
|
||||
write a var=value line on stdout
|
||||
*/
|
||||
|
||||
void report(const char *str, const char *format, ...)
|
||||
{
|
||||
va_list args;
|
||||
|
||||
va_start(args, format);
|
||||
vreport(str, format, args);
|
||||
va_end(args);
|
||||
}
|
||||
|
||||
void vreport(const char *str, const char *format, va_list args)
|
||||
{
|
||||
fprintf(stdout, "!%s=", str);
|
||||
vfprintf(stdout, format, args);
|
||||
fprintf(stdout, "\n");
|
||||
}
|
||||
|
||||
/* cdie
|
||||
report mps result code, and exit if it's not ok
|
||||
*/
|
||||
|
||||
void cdie(mps_res_t err, const char *str)
|
||||
{
|
||||
if (err != MPS_RES_OK)
|
||||
{
|
||||
error("%s: %s\n", str, err_text(err));
|
||||
}
|
||||
else comment("%s: OK", str);
|
||||
}
|
||||
|
||||
/* die
|
||||
check mps result code it ok; it not, report and exit
|
||||
*/
|
||||
|
||||
void die(mps_res_t err, const char *str)
|
||||
{
|
||||
if (err != MPS_RES_OK)
|
||||
{
|
||||
error("%s: %s\n", str, err_text(err));
|
||||
}
|
||||
}
|
||||
|
||||
/* adie
|
||||
report mps result code as error, whatever it is
|
||||
*/
|
||||
|
||||
void adie(mps_res_t err, const char *str)
|
||||
{
|
||||
error("%s: %s\n", str, err_text(err));
|
||||
}
|
||||
|
||||
/* comment
|
||||
print comment on stdout
|
||||
*/
|
||||
|
||||
void comment(const char *format, ...)
|
||||
{
|
||||
va_list args;
|
||||
|
||||
va_start(args, format);
|
||||
vcomment(format, args);
|
||||
va_end(args);
|
||||
}
|
||||
|
||||
void vcomment(const char *format, va_list args)
|
||||
{
|
||||
fprintf(stdout, "%% ");
|
||||
vfprintf(stdout, format, args);
|
||||
fprintf(stdout, "\n");
|
||||
}
|
||||
|
||||
/* commentif(boolean, "comment")
|
||||
*/
|
||||
|
||||
void commentif(int cond, const char *format, ...)
|
||||
{
|
||||
va_list args;
|
||||
|
||||
if (cond)
|
||||
{
|
||||
va_start(args, format);
|
||||
vcomment(format, args);
|
||||
va_end(args);
|
||||
}
|
||||
}
|
||||
|
||||
/* error
|
||||
print error on stdout and exit
|
||||
*/
|
||||
|
||||
void error(const char *format, ...)
|
||||
{
|
||||
va_list args;
|
||||
|
||||
va_start(args, format);
|
||||
verror(format, args);
|
||||
va_end(args);
|
||||
}
|
||||
|
||||
void verror(const char *format, va_list args)
|
||||
{
|
||||
fprintf(stdout, "%% ERROR \n!error=true\n");
|
||||
fprintf(stdout, "!errtext=");
|
||||
vfprintf(stdout, format, args);
|
||||
fprintf(stdout, "\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
/* asserts(1=0, "Axiom violation.");
|
||||
assert, with textual message instead of expr printed
|
||||
*/
|
||||
|
||||
void asserts(int expr, const char *format, ...)
|
||||
{
|
||||
va_list args;
|
||||
|
||||
if (!expr)
|
||||
{
|
||||
va_start(args, format);
|
||||
fprintf(stdout, "%% ASSERTION FAILED \n!assert=true\n");
|
||||
fprintf(stdout, "!asserttext=");
|
||||
vfprintf(stdout, format, args);
|
||||
fprintf(stdout, "\n");
|
||||
va_end(args);
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
/* routines for easy use of the MPS */
|
||||
|
||||
/* easy_tramp
|
||||
simplified trampoline, for those who don't want to
|
||||
pass anything into or out of it -- it takes
|
||||
a function with no arguments returning nothing
|
||||
*/
|
||||
|
||||
static void *call_f(void *p, size_t s)
|
||||
{
|
||||
void (**f)(void) = p;
|
||||
|
||||
(**f)();
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
void easy_tramp(void (*f)(void))
|
||||
{
|
||||
void *result;
|
||||
|
||||
mps_tramp(&result, call_f, &f, (size_t)0);
|
||||
}
|
||||
|
||||
/* nabbed from "ML for the Working Programmer"
|
||||
* Originally from:
|
||||
* Stephen K Park & Keith W Miller (1988). Random number generators:
|
||||
* good ones are to find. Communications of the ACM, 31:1192-1201
|
||||
*/
|
||||
|
||||
static unsigned long rnd(void)
|
||||
{
|
||||
static unsigned long seed = 1;
|
||||
double s;
|
||||
s = seed;
|
||||
s *= 16807.0;
|
||||
s = fmod(s, 2147483647.0); /* 2^31 - 1 */
|
||||
seed = (unsigned long)s;
|
||||
return seed;
|
||||
}
|
||||
|
||||
unsigned long ranint(unsigned long x)
|
||||
{
|
||||
unsigned long y;
|
||||
unsigned long max;
|
||||
|
||||
asserts(x>0, "ranint needs positive parameter");
|
||||
|
||||
if (x==1) return 0;
|
||||
|
||||
max = (2147483647/x)*x;
|
||||
|
||||
do y = rnd();
|
||||
while (y>max-1);
|
||||
|
||||
return y%x;
|
||||
}
|
||||
|
||||
unsigned long ranrange(unsigned long min, unsigned long max)
|
||||
{
|
||||
return min+ranint(max-min);
|
||||
}
|
||||
|
||||
70
mps/qa/test/testlib/testlib.h
Normal file
70
mps/qa/test/testlib/testlib.h
Normal file
|
|
@ -0,0 +1,70 @@
|
|||
/* test_lib.h
|
||||
various handy things for reporting errors &c
|
||||
*/
|
||||
|
||||
#ifndef test_lib_h
|
||||
#define test_lib_h
|
||||
|
||||
#include "mps.h"
|
||||
#include "versind.h"
|
||||
#include "platform.h"
|
||||
|
||||
/* Give textual description of mps error code */
|
||||
|
||||
const char *err_text(mps_res_t err);
|
||||
|
||||
/* pass, fail: write result=pass/fail to stdout and
|
||||
exit (0 or 1 as appropriate)
|
||||
*/
|
||||
|
||||
void pass(void);
|
||||
void fail(void);
|
||||
|
||||
/* report: print variable and value to stdout
|
||||
*/
|
||||
|
||||
void report(const char *str, const char *format, ...);
|
||||
void vreport(const char *str, const char *format, va_list args);
|
||||
|
||||
/* adie: print text and err code to stderr by calling error
|
||||
die: as above, but if err is MPS_RES_OK do nothing
|
||||
cdie: print text and err code to stderr as comment, or as error
|
||||
if err isn't MPS_RES_OK
|
||||
*/
|
||||
|
||||
void cdie(mps_res_t err, const char *str);
|
||||
void die(mps_res_t err, const char *str);
|
||||
void adie(mps_res_t err, const char *str);
|
||||
|
||||
/* Prints text to stderr */
|
||||
|
||||
void comment(const char *format, ...);
|
||||
void vcomment(const char *format, va_list args);
|
||||
|
||||
/* Prints text to stderr if cond is true */
|
||||
|
||||
void commentif(int cond, const char *format, ...);
|
||||
|
||||
/* Prints text to stderr and aborts */
|
||||
|
||||
void error(const char *format, ...);
|
||||
void verror(const char *format, va_list args);
|
||||
|
||||
/* If exp is false, prints text to stderr and aborts */
|
||||
|
||||
void asserts(int expr, const char *format, ...);
|
||||
|
||||
/* Easy way of entering the trampoline, for when you don't
|
||||
want to pass any information in or out. If you have a
|
||||
function void foo(void), just go easy_tramp(foo).
|
||||
*/
|
||||
|
||||
void easy_tramp(void (*f)(void));
|
||||
|
||||
/* Random number from 0 to x-1
|
||||
*/
|
||||
|
||||
unsigned long ranint(unsigned long limit);
|
||||
unsigned long ranrange(unsigned long min, unsigned long max);
|
||||
|
||||
#endif
|
||||
17
mps/qa/test/testlib/versind.h
Normal file
17
mps/qa/test/testlib/versind.h
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
/* versind.h
|
||||
lead appropriate header files to do version-specific
|
||||
interface things
|
||||
|
||||
valid versions are:
|
||||
MO : modern -- i.e. as in thursday afternoon
|
||||
OS : oldstyle -- dylan.incr.patch.11
|
||||
*/
|
||||
|
||||
#if defined(MMQA_VERS_MO)
|
||||
#elif defined(MMQA_VERS_OS)
|
||||
|
||||
#include "oldstyle.h"
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
Loading…
Add table
Add a link
Reference in a new issue